打印
[STM32F4]

【stm32】delay详解

[复制链接]
楼主: kqh11a
手机看帖
扫描二维码
随时随地手机跟帖
21
kqh11a|  楼主 | 2022-5-31 16:51 | 只看该作者 回帖奖励 |倒序浏览
3.2 不使用ucos的delay_us

该函数用来延时指定的 us,其参数 nus 为要延时的微秒数。
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;                                               //清空计数器         
}

使用特权

评论回复
22
kqh11a|  楼主 | 2022-5-31 16:51 | 只看该作者
实现一次延时 nus的操作:

先把要延时的 us 数换算成 SysTick 的时钟数,然后写入 LOAD 寄存器。
然后清空当前寄存器 VAL 的内容,再开启倒数功能。
等到倒数结束,即延时了 nus。
最后关闭 SysTick,清空 VAL 的值。

使用特权

评论回复
23
kqh11a|  楼主 | 2022-5-31 16:52 | 只看该作者
nus 的值,不能太大,必须保证 nus<=(2^24)/fac_us,否则将导致延时时间不准确。 temp&0x01,这一句是用来判断 systick 定时器是否还处于开启状态,可以防止 systick 被意外关闭导致的死循环

使用特权

评论回复
24
kqh11a|  楼主 | 2022-5-31 16:53 | 只看该作者
3.3 ucos下的delay_us
void delay_us(u32 nus)
{               
        u32 ticks;
        u32 told,tnow,tcnt=0;
        u32 reload=SysTick->LOAD;                                        //LOAD的值                     
        ticks=nus*fac_us;                                                         //需要的节拍数                           
        tcnt=0;
        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调度                                                                            
}

使用特权

评论回复
25
kqh11a|  楼主 | 2022-5-31 16:54 | 只看该作者
利用时钟摘取法,ticks 是延时 nus 需要等待的 SysTick 计数次数(也就是延时时间),told 用于记录最近一次的 SysTick->VAL 值,然后 tnow 则是当前的SysTick->VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 tcnt 里面,然后通过对比 tcnt 和 ticks,来判断延时是否到达,从而达到不修改 SysTick 实现 nus 的延时,从而可以和 OS 共用一个 SysTick。

使用特权

评论回复
26
kqh11a|  楼主 | 2022-5-31 16:54 | 只看该作者
delay_osschedlock 和 delay_osschedunlock 是 OS 提供的两个函数,用于调度上锁和解锁,这里为了防止 OS 在 delay_us 的时候打断延时,可能导致的延时不准,所以我们利用这两个函数来实现免打断,从而保证延时精度!同时,此时的 delay_us,可以实现最长 2^32us 的延时,大概是 4294 秒。

使用特权

评论回复
27
kqh11a|  楼主 | 2022-5-31 16:55 | 只看该作者
3.4 不使用ucos的delay_ms
void delay_ms(u16 nms)
{                                     
        u32 temp;                  
        SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)
        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;                                               //清空计数器                      
}

使用特权

评论回复
28
kqh11a|  楼主 | 2022-5-31 16:55 | 只看该作者
注意:单次延时时间不能超过1864ms,原因如下:
同delay_us大致一样,LOAD 仅仅是一个 24bit 的寄存器,延时的 ms 数不能太长。否则超出了 LOAD 的范围,高位会被舍去,导致延时不准。最大延迟 ms 数可以通过公式:nms<=0xffffff81000/SYSCLK 计算。SYSCLK 单位为 Hz,nms 的单位为 ms。如果时钟为 72M,那么 nms 的最大值为 1864ms。超过这个值,建议通过多次调用 delay_ms 实现,否则就会导致延时不准确。

使用特权

评论回复
29
kqh11a|  楼主 | 2022-5-31 16:56 | 只看该作者
3.5 ucos下的delay_ms
void delay_ms(u16 nms)
{       
        if(delay_osrunning&&delay_osintnesting==0)        //如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)            
        {                 
                if(nms>=fac_ms)                                                        //延时的时间大于OS的最少时间周期
                {
                           delay_ostimedly(nms/fac_ms);                //OS延时
                }
                nms%=fac_ms;                                                        //OS已经无法提供这么小的延时了,采用普通方式延时   
        }
        delay_us((u32)(nms*1000));                                        //普通方式延时  
}

使用特权

评论回复
30
kqh11a|  楼主 | 2022-5-31 16:57 | 只看该作者
该函数中,delay_osrunning 是 OS 正在运行的标志,delay_osintnesting 则是 OS 中断嵌套次数,必须 delay_osrunning 为真,且 delay_osintnesting 为 0 的时候,才可以调用 OS 自带的延时函数进行延时(可以进行任务调度),delay_ostimedly 函数就是利用 OS 自带的延时函数,实现任务级延时的,其参数代表延时的时钟节拍数(假设 delay_ostickspersec=200 ,那么delay_ostimedly (1),就代表延时 5ms)。

使用特权

评论回复
31
kqh11a|  楼主 | 2022-5-31 16:57 | 只看该作者
当 OS 还未运行的时候,我们的 delay_ms 就是直接由 delay_us 实现的,OS 下的 delay_us可以实现很长的延时而不溢出!,所以放心的使用 delay_us 来实现 delay_ms,不过由于 delay_us的时候,任务调度被上锁了,所以还是建议不要用 delay_us 来延时很长的时间,否则影响整个系统的性能。

使用特权

评论回复
32
kqh11a|  楼主 | 2022-5-31 16:59 | 只看该作者
当 OS 运行的时候,我们的 delay_ms 函数将先判断延时时长是否大于等于 1 个 OS 时钟节拍(fac_ms),当大于这个值的时候,我们就通过调用 OS 的延时函数来实现(此时任务可以调度),不足 1 个时钟节拍的时候,直接调用 delay_us 函数实现(此时任务无法调度)。

使用特权

评论回复
33
koala889| | 2022-6-3 08:36 | 只看该作者
各种延时在应用上的差别有哪些

使用特权

评论回复
34
sheflynn| | 2022-6-6 12:54 | 只看该作者
如何实现us的延时呢?

使用特权

评论回复
35
averyleigh| | 2022-6-21 20:24 | 只看该作者
这个精度很高吗  

使用特权

评论回复
36
andygirl| | 2022-6-21 20:41 | 只看该作者
不单单是delay了

使用特权

评论回复
37
pixhw| | 2022-6-22 21:39 | 只看该作者
习惯使用定时器延时了。   

使用特权

评论回复
38
saservice| | 2022-6-23 20:27 | 只看该作者
delay函数太复杂了。   

使用特权

评论回复
39
selongli| | 2022-6-24 12:04 | 只看该作者
这个是正点原子的代码吧。   

使用特权

评论回复
40
aspoke| | 2022-6-24 14:01 | 只看该作者
ucos有关系吗?  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则