Reputation:
I am trying to port some Arduino library to stm32. In Arduino, millis()
returns the number of milliseconds since boot. Is there an equivalent function in stm32? I am using stm32f0 MCU.
Upvotes: 12
Views: 27260
Reputation: 11
HAL library use a Timer(you can select it in the STM32CubeMX) to generate a Tick counter. every Tick takes 1ms. you can use it as follow to measure elapsed time:
uint32_t startTime = HAL_GetTick();
.
.
.
uint32_t endTime = HAL_GetTick();
uint32_t elapsedTime = endTime-startTime; // in ms
Upvotes: 1
Reputation: 1269
You could use HAL_GetTick(): this function gets current SysTick counter value (incremented in SysTick interrupt) used by peripherals drivers to handle timeouts.
Upvotes: 18
Reputation: 11
You need to initialise SysTick first, to tick at 1ms by initialising it with SystemCoreClock (Hz)/1000.
Upvotes: 1
Reputation: 113
I would suggest setting up your own timer.
To make a timer you must configure a prescalar and period in CubeMX or manually.
Clock Configuration
Because my clock is 72MHz this means I want a prescalar of 72MHz / 1MHz - 1 = 71
Consequently my period will be 1MHz / 1kHz - 1 = 999 because I want the interrupt to trigger once every 1 millisecond.
Prescalar Calculation
Here is how that looks in CubeMX.
Timer Configuration
Then you want to add a counter increment in the timer callback function.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
if (htim->Instance == TIM10) {
counter++;
}
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
Upvotes: 2
Reputation: 104
I would suggest to do it with timer. In this way you also can get 1us step, just control your time step size. Anyway most of STM32 MCU's has 8 or more timers, so in most cases you are free to take one. I'll show very simple basic idea how to do it.
Just create timer:
uint32_t time = 0;
void enable_timer(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM16, ENABLE);
TIM_TimeBaseInitTypeDef timerInitStructure;
/* if MCU frequency 48 MHz, prescaler of value 48 will make 1us counter steps
timerInitStructure.TIM_Prescaler = 48;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
/*how often you'll get irq*/
timerInitStructure.TIM_Period = 0xFFFF; // will get irq each 65535us on TIMx->CNT overflow
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM16, &timerInitStructure);
TIM_Cmd(TIM16, ENABLE);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM16_IRQn;
/*for more accuracy irq priority should be higher, but now its default*/
NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
TIM_ClearITPendingBit(TIM16,TIM_IT_Update);
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM16,TIM_IT_Update,ENABLE);
}
at IRQ you have to control overflows of timer counter and update your global time
void TIM16_IRQHandler(void){
if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET){
TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
time +=65535;
}
}
And real time will be :
uint32_t real_time_us = time + (uint32_t)TIM16->CNT;
But if you are free to use 32 bit timer you can even do it without IRQ, just simply time= TIMx->CNT. Yea it depends on timer configuration and your needs, also you have to know that uint32_t time variable also can overflow, but that's a details, it can be managed easily.
Upvotes: 3
Reputation: 21
Well, I think I need more information for a proper answer, but I will give you my best shot.
A consideration you need to have for your application, if you are making a high resolution measurements (need exact time), you will need to provide an external crystal for the accuracy since the internal clocks of the MCU are not quite accurate.
Having that in mind, what you shall do is the following:
void interrupt TIMX_interrupt(void){
counter++; // this will have the time count in MS.
}
millis()
that return the counter something like:uint32_t millis(void){
return counter;
}
In this way you could use the same function in this MCU.
Upvotes: 0
Reputation: 929
In some situations you may not use SysTick for this purpose. For example when using FreeRTOS, which already implements SysTick. In this case, run your code in a task, and you can use vTaskDelay. Depending on what you would like to do, a software timer can also make the trick: http://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html
In case you cannot use SysTick, and you don't have FreeRTOS or similar, you can setup a Timer with an interrupt and place your code in the appropriate callback.
Upvotes: 0
Reputation: 12999
SysTick
is an ARM core peripheral provided for this purpose. Adapt this to your needs:
Firstly, initialise it
// Initialise SysTick to tick at 1ms by initialising it with SystemCoreClock (Hz)/1000
volatile uint32_t counter = 0;
SysTick_Config(SystemCoreClock / 1000);
Provide an interrupt handler. Your compiler may need interrupt handlers to be decorated with additional attributes.
SysTick_Handler(void) {
counter++;
}
Here's your millis()
function, couldn't be simpler:
uint32_t millis() {
return counter;
}
Some caveats to be aware of.
SysTick is a 24 bit counter. It will wrap on overflow. Be aware of that when you're comparing values or implementing a delay method.
SysTick is derived from the processor core clock. If you mess with the core clock, for example slowing it down to save power then the SysTick frequency must be manually adjusted as well.
Upvotes: 17