我们从接触的第一个跑马灯项目开始,就用到了delay函数,初学者只知道调用一下delay_ms,delay_us就可以进行体系延时。那么对于这个函数,你相识多少呢?下面让我们根据代码分析一下这个函数实现的过程。
首先,delay函数分为裸机版本和操作体系版本,如果你在操作体系的项目下面运行裸机版本的delay函数,那么步伐不会按你所想的方式运行,以是在操作体系下面我们应该修改delay函数为兼容体系的,下面我们以ucos体系为例,说说这两个版本的区别。
1.delay_init函数
第一个我们来分析初始化函数delay_init,宏定义SYSTEM_SUPPORT_OS为1的时间是支持操作体系,为0的时间是裸机步伐,SysTick 是 MDK 定义了的一个结构体(在 core_m4.h 里面),里面包罗 CTRL、LOAD、VAL、CALIB 4 个寄存器。
SysTick->CTRL 的各位定义:
SysTick-> LOAD 的定义:
SysTick-> VAL 的定义:
SysTick-> CALIB的话我们现在不用到,不做描述。
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);这行代码的意思就是SysTick的时钟选择为体系内核时钟,SysTick的时钟来源于HCLK,和体系时钟同样为180Mhz,SysTick-> VAL每减小1就过去了1/180us,fac_us=SYSCLK,这里我们传入的SYSCLK为180,意思就是延时1us需要180个 SysTick 时钟周期。
在不使用操作体系的时间,只要给fac_us写入周期数就可以了,但是在使用操作体系的时间,fac_us不被写入SysTick->LOAD 实现延时,fac_ms=1000/delay_ostickspersec; 代表操作体系可以延时的最少毫秒(当delay_ostickspersec=1000的时间,表示操作体系最少可以延迟1毫秒)
2.delay_us 函数
2.1带操作体系的delay_us函数
- //延时nus
- //nus:要延时的us数.
- //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
- 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调度
复制代码 此函数想对于不带操作体系的delay_us函数,多一个delay_osschedlock(); 来阻止OS调理,防止打断us延时,然后在延时函数结束之前,调用delay_osschedunlock();恢复OS调理。
2.2不带操作体系的delay_us函数
这里利用了时钟摘取法,ticks 是延时 nus 需要等待的 SysTick 计数次数,told 用于记录最近一次的 SysTick->VAL 值,然后 tnow 则是当前的SysTick->VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 tcnt 里面,通过对比 tcnt 和 ticks,来判定延时是否完成,实现 nus 的延时。
3. delay_ms
3.1带操作体系的delay_ms函数
- //延时nms
- //nms:要延时的ms数
- //nms:0~65535
- 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)); //普通方式延时
- }
复制代码 带操作体系的delay_ms函数,会判定需要延时的时间是否大于之前在init里面设置的最小时间周期fac_ms,如果大于fac_ms,就会进行nms/fac_ms次时间延时,并且这段时间操作体系进行正常的任务调理,最后计算nms%=fac_ms,如果存在余数表示最后差nms没有延时完成,以是又用delay_us补上剩余没有延时的时间,此时不进行任务调理。
3.2不带操作体系的delay_ms函数
这个没什么好说的,就是调用了n次delay_us实现。
4.总结
在裸机步伐中,delay_ms函数与delay_us函数正常使用即可,在操作体系中,使用delay_ms函数的时间,如果设置的延时时间大于操作体系设置的最小的调理时间,就会进行正常的任务调理拉起当前任务,执行下一个已停当的任务,直到剩余延时时间小于操作体系设置的最小的调理时间,就进行delay_us函数,留意的是,只要是delay_us函数被调用的时间,体系不会进行任务调理。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |