delay_us函数
/**********************************************************/
该函数用来延时指定的 us,其参数nus为要延时的微秒数。该函数有使用OS和不使用OS
两个版本,这里我们分别介绍,首先是不使用 OS的时候,实现函数如下:
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
有了上面对SysTick 寄存器的描述,这段代码不难理解。其实就是先把要延时的 us数换算成 SysTick 的时钟数,然后写入LOAD寄存器。然后清空当前寄存器VAL的内容,再开启倒数功能。等到倒数结束,即延时了 nus。最后关闭 SysTick,清空 VAL 的值。实现一次延时 nus的操作,但是这里要注意 nus 的值,不能太大,必须保证 nus<=(2^24)/fac_us,否则将导致延时时间不准确。这里特别说明一下:temp&0x01,这一句是用来判断systick定时器是否还处于开启状态,可以防止 systick被意外关闭导致的死循环。
再来看看使用OS的时候,delay_us的实现函数如下:
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
delay_osschedlock(); //阻止 OS 调度,防止打断us延时
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //注意 SYSTICK是一个递减的计数器.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
};
delay_osschedunlock(); //恢复 OS 调度
}
这里就正是利用了我们前面提到的时钟摘取法,ticks 是延时 nus 需要等待的 SysTick 计数次数(也就是延时时间),told 用于记录最近一次的 SysTick->VAL 值,然后 tnow 则是当前的SysTick->VAL值,通过他们的对比累加,实现SysTick 计数次数的统计,统计值存放在tcnt里面,然后通过对比 tcnt 和 ticks,来判断延时是否到达,从而达到不修改 SysTick 实现 nus 的延时,从而可以和OS 共用一个SysTick。
上面的 delay_osschedlock 和 delay_osschedunlock 是 OS 提供的两个函数,用于调度上锁和解锁,这里为了防止 OS 在 delay_us 的时候打断延时,可能导致的延时不准,所以我们利用这两个函数来实现免打断,从而保证延时精度!同时,此时的delay_us,,可以实现最长2^32/fac_us,在 168M主频下,最大延时,大概是204秒。
/**********************************************************/
【立创商城】STM32F103CBT6 托盘 https://item.szlcsc.com/8796.html |