stm32 中的 Arduino millis()
Arduino millis() in stm32
我正在尝试将一些 Arduino 库移植到 stm32。在 Arduino 中,millis()
returns 自启动以来的毫秒数。 stm32 中是否有等效的功能?我用的是stm32f0单片机。
您可以使用 HAL_GetTick(): this function gets current SysTick counter value (incremented in SysTick interrupt) used by peripherals drivers to handle timeouts.
SysTick
就是为此提供的ARM核心外设。根据您的需要进行调整:
首先,初始化它
// Initialise SysTick to tick at 1ms by initialising it with SystemCoreClock (Hz)/1000
volatile uint32_t counter = 0;
SysTick_Config(SystemCoreClock / 1000);
提供中断处理程序。您的编译器可能需要使用其他属性修饰中断处理程序。
SysTick_Handler(void) {
counter++;
}
这是您的 millis()
函数,再简单不过了:
uint32_t millis() {
return counter;
}
需要注意的一些注意事项。
SysTick 是一个 24 位计数器。它会在溢出时换行。请注意,当
您正在比较值或实施延迟方法。
SysTick 源自处理器核心时钟。如果你弄乱了核心时钟,例如为了省电而降低它的速度,那么也必须手动调整 SysTick 频率。
在某些情况下,您可能不会将 SysTick 用于此目的。例如,当使用 FreeRTOS 时,它已经实现了 SysTick。在这种情况下,运行 你的代码在一个任务中,你可以使用 vTaskDelay。根据您想做什么,软件计时器也可以解决问题:http://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html
如果您不能使用 SysTick,并且您没有 FreeRTOS 或类似软件,您可以设置一个带中断的定时器,并将您的代码放在适当的回调中。
好吧,我想我需要更多信息才能得到正确答案,但我会尽力而为。
您的应用程序需要考虑的一个因素是,如果您正在进行高分辨率测量(需要精确时间),您将需要提供外部 crystal 以确保准确性,因为MCU 不太准确。
考虑到这一点,您要做的是:
- 配置一个 TIM,使其每 1 毫秒执行一次中断
- 启用TIM中断,
- 在 TIM 中断中,增加一个计数器(这将是您的时间),该计数器被定义为该模块的全局变量。
void interrupt TIMX_interrupt(void){
counter++; // this will have the time count in MS.
}
- 创建一个名为
millis()
的函数,return 计数器类似于:
uint32_t millis(void){
return counter;
}
这样你就可以在这个MCU中使用同样的功能了。
我建议用定时器来做。这样你也可以获得 1us 步长,只需控制你的时间步长。无论如何,大多数 STM32 MCU 都有 8 个或更多定时器,因此在大多数情况下,您可以随意使用一个。
我将展示如何做的非常简单的基本想法。
只需创建计时器:
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);
}
在 IRQ 时你必须控制定时器计数器的溢出并更新你的全局时间
void TIM16_IRQHandler(void){
if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET){
TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
time +=65535;
}
}
实时时间为:
uint32_t real_time_us = time + (uint32_t)TIM16->CNT;
但如果你可以随意使用 32 位定时器,你甚至可以在没有 IRQ 的情况下使用它,只需简单地使用 time= TIMx->CNT。是的,这取决于计时器配置和您的需求,您还必须知道 uint32_t 时间变量也可能溢出,但这是一个细节,它可以轻松管理。
我建议设置您自己的计时器。
要制作定时器,您必须在 CubeMX 中或手动配置预分频器和周期。
Clock Configuration
因为我的时钟是 72MHz 这意味着我想要一个 72MHz / 1MHz - 1 = 71 的预分频器
因此我的周期将是 1MHz / 1kHz - 1 = 999 因为我希望中断每 1 毫秒触发一次。
Prescalar Calculation
这是在 CubeMX 中的样子。
Timer Configuration
那么你想在定时器回调函数中添加一个计数器增量。
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 */
}
您需要先初始化 SysTick,以 SystemCoreClock (Hz)/1000 初始化它以 1ms 为单位计时。
HAL 库使用定时器(您可以 select 在 STM32CubeMX 中使用它)来生成 Tick 计数器。每个 Tick 需要 1ms。您可以按如下方式使用它来测量经过的时间:
uint32_t startTime = HAL_GetTick();
.
.
.
uint32_t endTime = HAL_GetTick();
uint32_t elapsedTime = endTime-startTime; // in ms
我正在尝试将一些 Arduino 库移植到 stm32。在 Arduino 中,millis()
returns 自启动以来的毫秒数。 stm32 中是否有等效的功能?我用的是stm32f0单片机。
您可以使用 HAL_GetTick(): this function gets current SysTick counter value (incremented in SysTick interrupt) used by peripherals drivers to handle timeouts.
SysTick
就是为此提供的ARM核心外设。根据您的需要进行调整:
首先,初始化它
// Initialise SysTick to tick at 1ms by initialising it with SystemCoreClock (Hz)/1000
volatile uint32_t counter = 0;
SysTick_Config(SystemCoreClock / 1000);
提供中断处理程序。您的编译器可能需要使用其他属性修饰中断处理程序。
SysTick_Handler(void) {
counter++;
}
这是您的 millis()
函数,再简单不过了:
uint32_t millis() {
return counter;
}
需要注意的一些注意事项。
SysTick 是一个 24 位计数器。它会在溢出时换行。请注意,当 您正在比较值或实施延迟方法。
SysTick 源自处理器核心时钟。如果你弄乱了核心时钟,例如为了省电而降低它的速度,那么也必须手动调整 SysTick 频率。
在某些情况下,您可能不会将 SysTick 用于此目的。例如,当使用 FreeRTOS 时,它已经实现了 SysTick。在这种情况下,运行 你的代码在一个任务中,你可以使用 vTaskDelay。根据您想做什么,软件计时器也可以解决问题:http://www.freertos.org/FreeRTOS-Software-Timer-API-Functions.html
如果您不能使用 SysTick,并且您没有 FreeRTOS 或类似软件,您可以设置一个带中断的定时器,并将您的代码放在适当的回调中。
好吧,我想我需要更多信息才能得到正确答案,但我会尽力而为。
您的应用程序需要考虑的一个因素是,如果您正在进行高分辨率测量(需要精确时间),您将需要提供外部 crystal 以确保准确性,因为MCU 不太准确。
考虑到这一点,您要做的是:
- 配置一个 TIM,使其每 1 毫秒执行一次中断
- 启用TIM中断,
- 在 TIM 中断中,增加一个计数器(这将是您的时间),该计数器被定义为该模块的全局变量。
void interrupt TIMX_interrupt(void){
counter++; // this will have the time count in MS.
}
- 创建一个名为
millis()
的函数,return 计数器类似于:
uint32_t millis(void){
return counter;
}
这样你就可以在这个MCU中使用同样的功能了。
我建议用定时器来做。这样你也可以获得 1us 步长,只需控制你的时间步长。无论如何,大多数 STM32 MCU 都有 8 个或更多定时器,因此在大多数情况下,您可以随意使用一个。 我将展示如何做的非常简单的基本想法。
只需创建计时器:
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);
}
在 IRQ 时你必须控制定时器计数器的溢出并更新你的全局时间
void TIM16_IRQHandler(void){
if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET){
TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
time +=65535;
}
}
实时时间为:
uint32_t real_time_us = time + (uint32_t)TIM16->CNT;
但如果你可以随意使用 32 位定时器,你甚至可以在没有 IRQ 的情况下使用它,只需简单地使用 time= TIMx->CNT。是的,这取决于计时器配置和您的需求,您还必须知道 uint32_t 时间变量也可能溢出,但这是一个细节,它可以轻松管理。
我建议设置您自己的计时器。
要制作定时器,您必须在 CubeMX 中或手动配置预分频器和周期。
Clock Configuration
因为我的时钟是 72MHz 这意味着我想要一个 72MHz / 1MHz - 1 = 71 的预分频器
因此我的周期将是 1MHz / 1kHz - 1 = 999 因为我希望中断每 1 毫秒触发一次。
Prescalar Calculation
这是在 CubeMX 中的样子。
Timer Configuration
那么你想在定时器回调函数中添加一个计数器增量。
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 */
}
您需要先初始化 SysTick,以 SystemCoreClock (Hz)/1000 初始化它以 1ms 为单位计时。
HAL 库使用定时器(您可以 select 在 STM32CubeMX 中使用它)来生成 Tick 计数器。每个 Tick 需要 1ms。您可以按如下方式使用它来测量经过的时间:
uint32_t startTime = HAL_GetTick();
.
.
.
uint32_t endTime = HAL_GetTick();
uint32_t elapsedTime = endTime-startTime; // in ms