目录
两种时基
在STM32的裸机开发中,没有操作系统这个概念,特别是在用CUBEMX配置生成项目时,默认都是选择了systick做为hal的时基。而当用了FREE RT OS时则强行绑定了systick做为其任务调度的时基。
在操作系统中,时基被用做计数,systick是1ms一个脉冲,vTaskDelay使若任务挂起等若干内核级的函数用的就是这个。(内核封装了很多层,最终可以找到systick的中断回调就是为OS提供"心跳")
在HAL库中,用到时基的地方,目前我在开发中还只知道在延时函数HAL_Delay() 有用到HAL库的时基。 HAL库时基的频率默认是1KHZ,1ms产生定时中断计数值加1。
在cubeMX配置TIM6作为HAL库时基时。默认生成的代码是预分频系数为系统时钟频率-1,装载为999,每1us计数值加1,1ms产生一次中断。全局维护一个u32类型的变量 utick。在中断函数中调用
HAL_IncTick();使得utick自增1。
在以TIM6充当时基时:TIM6的初始化
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock, uwAPB1Prescaler = 0U;
uint32_t uwPrescalerValue = 0U;
uint32_t pFLatency;
HAL_StatusTypeDef status = HAL_OK;
/* Enable TIM6 clock */
__HAL_RCC_TIM6_CLK_ENABLE();
/* Get clock configuration */
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
/* Get APB1 prescaler */
uwAPB1Prescaler = clkconfig.APB1CLKDivider;
/* Compute TIM6 clock */
if (uwAPB1Prescaler == RCC_HCLK_DIV1)
{
uwTimclock = HAL_RCC_GetPCLK1Freq();
}
else
{
uwTimclock = 2UL * HAL_RCC_GetPCLK1Freq();
}
/* Compute the prescaler value to have TIM6 counter clock equal to 1MHz */
uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);
/* Initialize TIM6 */
htim6.Instance = TIM6;
/* Initialize TIMx peripheral as follow:
+ Period = [(TIM6CLK/1000) - 1]. to have a (1/1000) s time base.
+ Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
+ ClockDivision = 0
+ Counter direction = Up
*/
htim6.Init.Period = (1000000U / 1000U) - 1U;
htim6.Init.Prescaler = uwPrescalerValue;
htim6.Init.ClockDivision = 0;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
status = HAL_TIM_Base_Init(&htim6);
if (status == HAL_OK)
{
/* Start the TIM time Base generation in interrupt mode */
status = HAL_TIM_Base_Start_IT(&htim6);
if (status == HAL_OK)
{
/* Enable the TIM6 global Interrupt */
HAL_NVIC_EnableIRQ(TIM6_IRQn);
/* Configure the SysTick IRQ priority */
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
/* Configure the TIM IRQ priority */
HAL_NVIC_SetPriority(TIM6_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
status = HAL_ERROR;
}
}
}
/* Return function status */
return status;
}
HAL_Delay会至少延时1个时钟周期。
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}while ((HAL_GetTick() - tickstart) < wait)
{
}
}
定时器重写ms和us级函数
delay_ms
void delay_ms(u32 ms){
__HAL_TIM_ENABLE(&htim6);
HAL_Delay(ms);
__HAL_TIM_DISABLE(&htim6);
}
delay_us
这里更为直接,CUBEMX配置后,默认就是1MHZ的时钟频率。也就是1us计数值加1。那么可以先将计数值CNT赋值为0等待计数则大功告成。这在日常用IIC,DHT温度传感器以及一些时序中经常使用。
void delay_us(u32 nus)
{
uint32_t cnt=0;
__HAL_TIM_ENABLE(&htim6);
__HAL_TIM_SetCounter(&htim6,0);
while( cnt < nus){
cnt=__HAL_TIM_GetCounter(&htim6);
}
__HAL_TIM_DISABLE(&htim6);
}
Systick重写us ms级延时函数
通过滴答计数值tick结合hal_delay()实现软件延时.精度比不上用上面的定时器.
void delay_ms(uint32_t ms)
{
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_Delay(ms-1);
}
void delay_us(uint32_t us)
{
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000000);
HAL_Delay(us-1);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
}