使用HAL库需要HAL_Delay()函数支持,离开这个函数,和这个函数有关的函数就无法调用了。遇到这个问题,我们肯定会否定HAL库,也确实它是有点差,但也有好的一面。
为什么HAL库初始化了SysTick定时器,也使能了相关中断,一旦调用HAL_Delay()函数就挂住了。大多数原因是在SysTick_Handler()中没有调用HAL_IncTick()函数,不清楚HAL库为什么没有写SysTick_Handler()中断服务程序,导致很多人认为HAL库会卡死。
__weak void HAL_IncTick(void)
{
uwTick += uwTickFreq;//uwTick计数器加1
}
__weak uint32_t HAL_GetTick(void)
{
return uwTick;//读uwTick计数器
}
__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 )
{//uwTick 不增加,(0-0)<wait,所以一值循环
}
}
如果uwTick 不增加,HAL_Delay()就一直循环,像是卡死了一样。
Source of time base is configured to generate interrupts at regular
time intervals. Care must be taken if HAL_Delay() is called from a
peripheral ISR process, the Tick interrupt line must have higher priority
(numerically lower) than the peripheral interrupt. Otherwise the caller
ISR process will be blocked.
使用“ctrl+shift+F”搜索“HAL_IncTick”
搜索结果如下:
添加程序如下:
//systick中断服务函数,1ms中断一次
//在调用HAL_Init()时,SysTick定时器初始化为使能中断
void SysTick_Handler(void)
{
HAL_IncTick();
//使能HAL_Delay()
//若SysTick定时器的中断优先级低于当前的中断服务程序,则该中断服务程序不能使用HAL_Delay;
}
如果在SysTick_Handler()中调用HAL_IncTick()函数,但是HAL_Delay()用于其它中断服务程序,原因是:SysTick定时器的中断优先级低于当前的中断服务程序,导致SysTick定时器无法进入中断,从而导致该中断服务程序不能使用HAL_Delay()。这就是HAL库为什么要将SysTick定时器的中断优先级设置为最高级别的原因。为了保证HAL_Delay()能在任何地方使用,HAL库实在有点霸道,主要还是HAL库的延时程序写得差。我在看到这个优先级配置,也有点懵逼了,想了很久,才知道原因所在。
解决方法:
有没有解决方法?
有,就是要少用HAL_Delay(),让HAL库独占这个函数。可以单独写自己的延时函数。如下:
static uint8_t fac_us=0; //us延时倍乘数
static uint16_t fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数
//函数功能:delay函数初始化
//在调用HAL_Init()时,已经对SysTick定时器初始化了,因此这里无需初始化
void delay_init(void)
{
uint32_t reload;
fac_us=SystemCoreClock/1000000;
//相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us
reload=SystemCoreClock/1000000;
//相当于将170MHz经过1000000分频用作SysTick的输入时钟,即周期为1us
reload*=1000;
//SysTick溢出时间为1000*1us=1ms
//reload为24位寄存器,最大值:16777216,在170MHz下,约合0.098s左右
fac_ms=1;
}
//函数功能:延时nus微妙
//nus:要延时的us数.
//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD;
//读取SysTick的LOAD寄存器的值
ticks=nus*fac_us; //计算需要的节拍数
told=SysTick->VAL;//刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;//读SYSTICK计数器值
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //SysTick定时器是一个递减的计数器.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
//函数功能:延时nms毫秒
//nms:要延时的ms数
//nms:0~65535
void delay_ms(uint32_t nms)
{
delay_us((uint32_t)(nms*1000));//普通方式延时
}