今天使用了OLED驱动代码,其中为了适应MCU较高速度而加入了Delay_us(4),后来再调用HAL库的HAL_Delay()出现了程序卡死在HAL库延迟函数之中。为此对延迟函数作进一步学习。
问题原因
首先是Delay_us()函数原型(代码来源江科大的代码)
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
该程序通过清空滴答定时器计数值后再从零计数来实现延迟。该过程直接操作、修改滴答定时器的句柄 SysTick 。而HAL库的逻辑是先记录当前的时间点(计数值)为start,阻塞等待计数值为start+wait_time。其中并未操作SysTick句柄,该句柄在初始化之初已配置好。
Delay_us函数最后关闭了定时器是一方面,另外还有SysTick->CTRL = 0x00000005。这个值对应的二进制是0101,即:
Bit 0 (ENABLE) = 1:启用 SysTick 定时器。
Bit 1 (TICKINT) = 0:禁用 SysTick 中断。
Bit 2 (CLKSOURCE) = 1:选择处理器时钟(HCLK)
函数结束后由于SysTick中断关闭,HAL全局计时变量 uwTick 无法得到更新,程序一直卡在HAL_Delay。即使最后 Delay_us 函数结束时恢复 CTRL 寄存器的值,HAL 库的中断逻辑可能已经被破坏,所以最好使用其他方法实现微妙延迟控制。HAL库SysTick句柄初始化配置具体如下:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks){
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
关于SysTick
SysTick句柄