参考资料:1. 正点原子
摘要:本篇文章首先讨论了Systick的配置过程、HAL_Delay()函数的实现原理并分析了中断服务函数不能使用延时函数的原因,其次讨论了正点原子的delay_us()函数实现原理以及在使用delay_us()函数时必须重写HAL_Delay()的原因。
作者水平有限,非常欢迎各路大仙批阅指正文章中可能会分析的不恰当之处!非常感谢!!
一、Systick初始化过程
1.1 Systick 寄存器简介
Systick系统滴答定时器是Cortex-M3架构的资源,参阅《Cortex-M3开发手册》,Systick有校准寄存器、重装载寄存器、当前计数值寄存器以及状态及控制寄存器,程序中只对其重装载寄存器SysTick -> LOAD; 当前计数值寄存器Systick -> VAL; 以及控制及状态寄存器Systick -> CTRL进行了配置。我们先来看一下这些寄存器的定义:
这里重点强调一下CTRL控制及状态寄存器的bit2,在STM32中,当配置Systick -> CTRL的bit2 = 0时,选择外部时钟源STCLK,而这里的STCLK即为CUBEMX时钟树上“To Cortex System timer(MHz)”,虽然CubeMX中可以选择是否配置八分频,但是实际上只要CTRL的bit2为0,STCLK的时钟就一定是HCLK的八分频,不管你配没配置八分频...(可能是bug吧)
如果配置Systick -> CTRL的bit2 = 1,则对应时钟树为FCLK:
综上,Systick的时钟源要么是HCLK(CTRL的bit2 = 1),要么是HCLK的八分频(CTRL的bit2 = 0)
注意!无论是在CUBEMX还是STM32手册里都明确说明了HSI固定为8M,但是在HAL_Init()程序的注释中表明HSI为16M!!!且后续Systick的配置均以16M为基准进行的(后续有分析)。因此我大胆猜测是STM32后续更新了HSI为16M但是没有更新CUBEMX和手册?
1.2 上电启动后至系统时钟初始化之前的阶段
在这个阶段,系统时钟由HSI提供。在CubeMX生成的代码中,会自动生成一个HAL_Init()函数
它的作用是初始化HAL库,而且必须在main中第一个被执行。其中一个很重要的功能是初始化Systick定时器(使用HSI时钟源(16M),并产生1ms的中断,中断分组为4)
HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
defined(STM32F102x6) || defined(STM32F102xB) || \
defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
defined(STM32F105xC) || defined(STM32F107xC)
/* Prefetch buffer is not available on value line devices */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}
其中SysTick的配置通过HAL_InitTick(TICK_INT_PRIORITY)配置,TICK_INT_PRIPORITY通过宏定义设置为15(在中断分组为4的情形下,为系统最低优先级)
我们来具体看一下这个函数:
简而言之这个函数就干了俩事:1. 配置SysTick产生1ms中断 2. 配置SYsTick优先级(抢占优先级为15)
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/* Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
{
return HAL_ERROR;
}
/* Configure the SysTick IRQ priority */
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
return HAL_ERROR;
}
/* Return function status */
return HAL_OK;
}
优先级配置我们在这里不做讨论,重点讨论SysTick的配置函数HAL_SYSTICK_Config()
其中传递的参数:
uint32_t SystemCoreClock = 16000000;(印证了HSI实际就是16M)
uwTickFreq = 1U;
那么计算传递的参数为:16M/(1000/1)= 16K
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
return SysTick_Config(Ticks