本章目标
深入解析HAL_Delay()函数
分析使用的时候会出现的问题
精确定时的自实现方法案例代码
HAL_Delay()原理分析
HAL_Delay(1000);大家都很熟悉了,嵌入式开发阶段玛卡巴卡级别的元老了。
但是终归是神秘了一点,初次见面人际关系还不错,后面社交复杂了就变心了。
__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)
{
}
}
你可能会遇到卡死、时序错误、失灵等等bug.
下面来具体分析一下这个函数,首先找出名字不太好辨认的:
获得当前tick:
__weak uint32_t HAL_GetTick(void)//获取tick
{
return uwTick;
}
延时的最大值 :
#define HAL_MAX_DELAY 0xFFFFFFFFU
累次叠加时的步进值,可选:
typedef enum
{
HAL_TICK_FREQ_10HZ = 100U,
HAL_TICK_FREQ_100HZ = 10U,
HAL_TICK_FREQ_1KHZ = 1U,
HAL_TICK_FREQ_DEFAULT = HAL_TICK_FREQ_1KHZ
} HAL_TickFreqTypeDef;
HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT; /* 1KHz */
抽象了,用人话说就是,获取当前的时间,然后逐步加一点去逼近(当前时间+delay)这个数,每次前进的步伐大小根据需要选一个。
至于步伐HAL_TickFreqTypeDef为啥要这么定义,咱们这么想,在最终值大小一样的时候,频率越大,每次前进的值越小。
再通俗一点,跑800米,同样是3min到达,你步子小,就得迈得快。
HAL_Delay()引发的问题
平时使用这个函数的时候,大概都是在main函数里面while里面写HAL_Dekay(1000);
这就可能会出现刚刚前面说过的各种bug,别想了,你老板肯定不高兴。
仔细一看,好家伙,这巨大一个while死循环可,真是棒啊。
while ((HAL_GetTick() - tickstart) < wait){}
还记得自己是个单片机吧,也就是说,一旦程序进入这个流程,就得卡到while条件不符合退出,否则就每次都是执行一条空指令,返回while,再来,再返回……
对于实时性要求特别高的硬件,比如按钮、ADC、RS485通讯等等,就比较无力了。
问题解决方案
如果想要实现更灵活的延时可以参考上回用定时器实现按键检测。
自实现精确定时
那HAL_Delay()怎么看怎么不对劲,那毕竟人家姓HAL啊,咱还是得自己有活计才行。
per_count=SystemCoreClock/1000000;
void delay_us(uint16_t us)
{
uint32_t ctrl; //工具人
SysTick->LOAD=per_count*us; //装载值,每微秒的时钟脉冲数*需要us微秒
SysTick->VAL=0x00; //清零当前值寄存器
SysTick->CTRL=0x00000005; //控制和状态寄存器,启动SysTick定时器
do
{
ctrl=SysTick->CTRL;
}
while(temp&0x01&&!(temp&(1<<16))); //SysTick->CTRL的位[0] - ENABLE(使能位),位[1] - TICKINT(中断使能位),位[2] - CLKSOURCE(时钟源选择位),位[16] - COUNTFLAG(计数标志位)
SysTick->CTRL=0x00000004;
SysTick->VAL=0x00;
}
有了us计数,相信ms和s就不需要多说了。多来几个1000遍就可以。
下面解释一下per_count这个变量的来历:
以STM32F103C8举例:
最大72MHz,也就是说,计数一次1/72Ms,那么如果计数72次,就是1/1000000s=1us
假设我们需要延时10us,那么,就需要计数72*10次即可
而SystemCoreClock这个变量,是在系统初始化的时候改变的,根据你的芯片型号和最初在cubemax里面配置的systick来确定数值。