首页|资源下载
登录|注册

您现在的位置是:首页 > 技术阅读 >  延时功能进化论之V2.5~V2.7(鱼鹰强烈建议)

延时功能进化论之V2.5~V2.7(鱼鹰强烈建议)

时间:2022-09-30


导读:有没有一种实现方式能解决V2.0~V2.3所描述的问题?不会被所谓的查询频率所限制呢?当然有。

是否存在一种没有查询频率限制的实现方法呢?换句话说,我们不是根据时刻判定延时时间的到达,而是通过时长判断呢?即1~2倍超时时间内都可以认为超时时间到,而不是刚好就是超时时间呢?

延时实现V2.5这里介绍一种有意思的公式(这个公式的妙处可看鱼鹰 FIFO 相关笔记  和 ):
(尾-头+表长) % 表长这个公式是用来计算队列中的元素个数,受它启发,我们可以利用这个公式计算时长:
Duration= (CurrentTime – Time + 256) % 256(注意不是255CurrentTime 为随时间不断递增的变量,Time 为记录的一个时刻点。
因为 CurrentTime 递增到256后自动清零的特性,可简化公式如下:Duration= CurrentTime – Time由此我们得到了和 V1.5 类似的代码:
uint8_t lcd_show_time;uint8_t show_time;
void SysTick_Handler(){ // 1 ms 中断 lcd_show_time++;}
int main (void){ USRAT_Init (9600); //必须,进入调试模式后点击全速运行
while (1) { if((uint8_t)(lcd_show_time - show_time) > (30 - 1)) { show_time = lcd_show_time; printf("微*信*公*众*号:鱼鹰谈单片机 ""lcd_show_time = %u\n",lcd_show_time); lcd_show(); //执行刷新任务 } }}这里特别注意需要强制转换成8位无符号整数,这是因为相减的时候默认采用有符号计算,我们需要让结果强制变成无符号8位整数(如果变量类型为无符号32位整数则不需要如此),还有判断条件是大于29,而不是30。
可以看看延时结果:我们关注的重点在于变量溢出后是否还能准确延时30 ms。事实证明,即使溢出,延时仍然准确无误。
延时实现V2.7上述延时版本的实现是一种飞跃,极大的简化延时代码的实现,但如果没有接下来这个版本的演进,你可能会如此实现各种功能的延时:每一个延时功能,都使用两个变量按上述V2.5实现各种延时。
如果延时任务不多,确实没有问题,但是如果延时任务很多,会带来什么问题?第一:中断函数处理的延时变量很多,增加中断负担;第二:需要的延时变量很多。
那么是否有更好解决方式呢?
利用V2.5实现两种以上延时功能后,你会发现,中断中的延时变量完全可以由一个变量承担计时任务。
比如延时 200 ms 任务和 LCD 刷新任务:
uint8_t current_time;uint8_t delay_time = 0,show_time;
void SysTick_Handler(){ // 1 ms 中断 current_time++;}
int main (void){ USRAT_Init (9600); //必须,进入调试模式后点击全速运行
while (1) { if((uint8_t)(current_time - show_time) > (30 - 1)) { show_time = current_time; printf("微*信*公*众*号:鱼鹰谈单片机 ""show_time = %u\n",current_time); lcd_show(); //执行刷新任务 } if((uint8_t)(current_time - delay_time) > (200 - 1)) { delay_time = current_time; printf("微*信*公*众*号:鱼鹰谈单片机 ""delay_time = %u\n",current_time); } }}可以看到,通过上述代码可以实现两种延时任务,互不干扰。
而以上代码才是鱼鹰这篇长文的核心!也是鱼鹰愿意花大量笔墨去写这样一个通用功能的原因所在,也是鱼鹰特别希望各位道友掌握的一个延时技能(到此全文笔记已接近尾声,V3.x 版本更多的是用于提高关于延时使用的认知)。
那么现在来分析这样的代码实现有什么好处:1、可重入,也就是说你在任何函数中采用这种延时方式,原则上不会影响延时的准确性(最终延时时间大于等于需要延时时间)。2、中断执行代码极少,各个延时功能几乎没有耦合关系。3、延时精度高,在这里延时精度为1 ms。4、可实现超长延时,如果把计时变量改成32位整数,延时时间超长(这里还是建议使用DWT,用于精确延时)。5、变量少,一个延时任务只需要一个变量记录上一次延时到达的时刻即可。6、延时变量类型可根据延时长度自行选择。比如说延时100 ms 使用8位整数,延时1 s 使用16位整数,延时 1天使用32位整数(当然判断代码需要根据情况修正才能准确延时,不懂的话可以留言讨论)。7、非阻塞式延时,执行效率高。
讨论了这么多优点,难道就没有任何缺点吗?一番思考下,鱼鹰想到了一个缺点,或者说另一个使用隐患。
我们想象这样一个场景,8位的计时变量,延时250 ms,上一次记录的超时时刻是0,原本应该在250这个时刻判断为超时,但有一个任务执行时间10 ms,导致在即将到达判断条件前执行了这个10 ms任务(假设在249这个时刻执行了这个任务,249 + 10 = 259,溢出变成3,查询时3 – 0 > 249不成立,但是250~255这些值是成立的),那么最终导致下一个250才是超时时刻,也就是说最终延时250 * 2 ms(如果不凑巧下一次又在249附近这里运行了这个任务,那么后果……),这肯定不是我们想看到的。那么从这个例子中我们可以总结一个防止延时时间错误的方法(没有理论依据,鱼鹰自己想的一个公式):
延时时间 + 最大查询时间 < 最大可延时时间在上面例子中,延时时间为 250,查询时间暂且认为是 10(不包含其他任务情况下),最大可延时时间 255。代入公式发现不满足条件,这也就是为什么会出现延时错误(延时错误指的是超出延时时间两倍以上)的原因。
最后,关于领取版本 V3.x 笔记的条件可点击底部阅读原文,需要注意的是,很多道友以为包含 02-03 那天满足条件即可,但鱼鹰的本意是在这天之前满足条件的道友可以取,因为在这天之后满足条件的道友在后续活动中会有别的笔记相送(具体什么笔记还不清楚)。还有要说明一点的是,条件具有时效性,不是说你有次获得满足了条件后,能把所有的笔记都获取,每次条件满足只能获取一种笔记。比如在 19-10-09 到20-02-02 期间分享文章到朋友圈,那么你可以选择领取 keil 调试笔记或者这次的延时笔记,但你不能把两份笔记都获取了,这损害了早期读者的利益。总之一句话,道友越是持续支持本公众号的发展,获得的个人笔记也越多


推荐阅读:





-THE END-


如果觉得文章对你有帮助,欢迎转发、分享给朋友,感谢你的支持!


如果对本文有问题,欢迎留言!即使没有问题也可以留下走心评论。


如需转载请联系我。


微信公众号「鱼鹰谈单片机

每周一更单片机知识

长按后识别图中二维码关注