精妙的单片机非阻塞延时程序设计
http://blog.chinaunix.net/uid-29673749-id-4425603.html
对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。
但对于绝大多数人,我们在最最初让灯闪烁起来时大约都会用到阻塞延时实现,会像如下代码的样子:
- while(1)
- {
- LED =OFF;
- Delay_ms(500);
- LED = ON;
- Delay_ms(500);
- }
然后,在我们接触到定时器,我们会发现,原来用定时中断来处理会更好。比如我们可以500ms中断一次,让灯亮或灭,其余的时间系统还可以做非常之多的事情,效率一下提升了很多。
这时我们就会慢慢意识到,第一种(阻塞延时)方法效率很低,让芯片在那儿空运行几百毫米,什么也不做,真是莫大的浪费,尤其在芯片频率较高,任务又很多时,这样做就像在平坦宽阔的高速公路上挖了一大坑,出现事故可想而知。
但一个单片机中的定时器毕竟有限,如果我需要几十个或者更多不同时间的定时中断,每一个时间到都完成不同的处理动作,如何去做呢。一般我们会想到在一个定时中断函数中再定义static 变量继续定时,到了所需时间,做不同的动作。而这样又会导致在一个中断里做了很多不同的事情,会抢占主轮询更多时间,有时甚至喧宾夺主,并也不是很如的思维逻辑。
那么有没有更好的方法来实现呢,答案是肯定的。下面介绍我在一个项目中偶遇,一个精妙设计的非阻塞定时延时软件的设计(此设计主要针对于无操作系统的裸机程序)。
在上篇文章中有对 systick的介绍,比如我要设置其10ms中断一次,如何实现呢?
也很简单,只需调用 core_cm3.h文件中 SysTick_Config 函数 ,当系统时钟为72MHZ,则设置成如下 即可 SysTick_Config(720000 ); (递减计数720000次后中断一次) 。此时SysTick_Handler中断函数就会10ms进入一次;
任务定时用软件是如何设计的呢 ?
且先看其数据结构,这也是精妙所在之处,在此作自顶向下的介绍:
其定义结构体类型如:
- typedef struct
- {
- uint8_t Tick10Msec;
- Char_Field Status;
- } Timer_Struct;
其中Char_Field 为一联合体,设计如下:
- typedef union
- {
- unsigned char byte;
- Timer_Bit field;
- } Char_Field
而它内部的Timer_Bit是一个可按位访问的结构体:
- typedef struct
- {
- unsigned char bit0: 1;
- unsigned char bit1: 1;
- unsigned char bit2: 1;
- unsigned char bit3: 1;
- unsigned char bit4: 1;
- unsigned char bit5: 1;
- unsigned char bit6: 1;
- unsigned char bit7: 1;
- } Timer_Bit
此联合体的这样设计的目的将在后面的代码中体现出来。
如此结构体的设计就完成了。
然后我们定义的一全局变量,Timer_Struct gTimer;
并在头文件中宏定义如下:
- #define bSystem10Msec gTimer.Status.field.bit0
- #define bSystem50Msec gTimer.Status.field.bit1
- #define bSystem100Msec gTimer.Status.field.bit2
- #define bSystem1Sec gTimer.Status.field.bit3
- #define bTemp10Msec gTimer.Status.field.bit4
- #define bTemp50Msec gTimer.Status.field.bit5
- #define bTemp100Msec gTimer.Status.field.bit6
- #define bTemp1Sec gTimer.Status.field.bit
另外为了后面程序清晰,再定义一状态指示:
- typedef enum
- {
- TIMER_RESET = 0,
- TIMER_SET = 1,
- } TimerStatus;
至此,准备工作就完成了。下面我们就开始大显神通了!
首先,10ms定时中断处理函数如,可以看出,每到达10ms 将把 bTemp10Msec置1, 每50ms 将把 bTemp50Msec 置1, 每100ms 将把 bTemp100Msec 置1, 每1s 将把 bTemp1Sec 置1,
- void SysTick_Handler(void)
- {
- bTemp10Msec = TIMER_SET;
- ++gTimer.Tick10Msec;
- if (0 == (gTimer.Tick10Msec % 5))
- {
- bTemp50Msec = TIMER_SET;
- }
- if (0 == (gTimer.Tick10Msec % 10))
- {
- bTemp100Msec = TIMER_SET;
- }
- if (100 == gTimer.Tick10Msec)
- {
- gTimer.Tick10Msec = 0;
- bTemp1Sec = TIMER_SET;
- }
- }
而这又有什么用呢 ?
这时,我们需在主轮询while(1)内最开始调用一个定时处理函数如下:
- void SysTimer _Process(void)
- {
- gTimer.Status.byte &= 0xF0;
- if (bTemp10Msec)
- {
- bSystem10Msec = TIMER_SET;
- }
- if (bTemp50Msec)
- {
- bSystem50Msec = TIMER_SET;
- }
- if (bTemp100Msec)
- {
- bSystem100Msec = TIMER_SET;
- }
- if (bTemp1Sec)
- {
- bSystem1Sec = TIMER_SET;
- }
- gTimer.Status.byte &= 0x0F;
- }
此函数开头与结尾两句
- gTimer.Status.byte &= 0xF0;
- gTimer.Status.byte &= 0x0F
就分别巧妙的实现了bSystemXXX (低4位) 和 bTempXXX (高4位)的清零工作,不用再等定时到达后还需手动把计数值 清零。此处 清零工作用到了联合体中的变量共用一个起始存储空间的特性。
但要保证while(1)轮询时间要远小于10ms,否则将导致定时延时不准确。这样,在每 轮询一次,就先把 bSystemXXX ,再根据 bTempXXX判断是否时间到达,并把对应的 bSystemXXX 置1,而后面所有的任务就都可以通过bSystemXXX 来进行定时延时,在最后函数退出时,又会把 bTempXXX清零,为下一次时间到达后查询判断作好了准备。
说了这么多,举例说明一下如何应用:
- void Task_A_Processing(void)
- {
- if(TIMER_SET == bSystem50Msec){
- //do something
- }
- }
- void Task_B_Processing(void)
- {
- if(TIMER_SET == bSystem100Msec){
- //do something
- }
- }
- void Task_C_Processing(void)
- {
- static uint8_t ticks = 0;
- if(TIMER_SET == bSystem100Msec){
- ticks ++ ;
- }
- if(5 == ticks){
- ticks = 0;
- //do something
- }
- }
- void Task_D_Processing(void)
- {
- if(TIMER_SET == bSystem1Sec){
- //do something
- }
- }
以上示例四个任务进程,
在主轮询里可进行如下处理:
- int main(void)
- {
- while(1)
- {
- SysTimer _Process();
- Task_A_Processing();
- Task_B_Processing();
- Task_C_Processing();
- Task_D_Processing();
- }
- }
这样,就可以轻松且清晰实现了多个任务,不同时间内处理不同事件。(但注意,每个任务处理中不要有阻塞延时,也不要处理过多的事情,以致处理时间较长。可设计成状态机来处理不同任务。)
精妙的单片机非阻塞延时程序设计相关推荐
- 单片机阻塞延时与非阻塞延时(1)
一.延时函数的定义 1.个人理解:通过执行一些无意义的指令,来达到拖延运行下一条指令时间的函数. 二.阻塞延时函数 1.独占性的延时函数,其在运行时,整个系统都将处于停滞的状态.不利于多任务并行,因为 ...
- Qtime非阻塞延时和Sleep阻塞延时
1.Qtime非阻塞延时 (1)在头文件(.h文件)声明 QTimer public:QTimer *updateTimer; (2)再声明一个槽函数 public slots:void update ...
- 单片机非阻塞串口中断收发数据
下面的程序使用 CH32V103C8T6评估板测试没有问题,使用中断完成了串口的非阻塞式收发,编程思路参考了51单片机非阻塞串口中断收发数据 usart_buf.h /** usart_buf.h** ...
- linux C非阻塞延时,linux 非阻塞式socket编程求助。。
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 一下客户端: #include #include #include #include #include #include #include #includ ...
- QCC512x QCC302x 延时函数 (阻塞与非阻塞)
ADK4 与 ADK6 都提供了获取系统本地时间的函数,单位是 us 获取系统当前时间(阻塞) 函数原型: rtime_t SystemClockGetTimerTime(void) {return ...
- 《UNIXLinux程序设计教程》一3.7 非阻塞I/O
3.7 非阻塞I/O 前面几节已介绍了完成各种I/O的系统调用,如read().write().open()等,这些系统调用在默认情形下均是阻塞的,也就是说,调用必须等待操作完成,即读写到数据,才能返 ...
- 从0到1写RT-Thread内核——空闲线程与阻塞延时的实现
在之前写的另外一篇文章--<从0到1写RT-Thread内核--线程定义及切换的实现>中线程体内的延时使用的是软件延时,即还是让CPU空等来达到延时的效果.RTOS中的延时叫阻塞延时,即线 ...
- php程序阻塞与非阻塞的区别,完美起航
很多单片机的初学者容易掉入阻塞式编程的陷阱.因为阻塞式编程符合我们对现实世界的理解,一个人在一段时间内,只能做一件事情.例如要是实现1Hz的闪灯程序,那么先让单片机端口拉高500ms,然后再拉低500 ...
- Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结
同步和异步:与消息的通知机制有关. 本质区别 现实例子 同步模式 由处理消息者自己去等待消息是否被触发 我去银行办理业务,选择排队等,排到头了就办理. 异步模式 由触发机制来通知处理消息者 我去银行办 ...
最新文章
- Python第三方库jieba(中文分词)入门与进阶(官方文档)
- 从谷歌辞职后,作为独立开发者的第三年,我从年入 3 万做到了年入 40 万
- HD 2602 Bone Collector (0-1背包)
- Hadoop Hive替换自带的derby数据库为MySQL具体步骤
- SQLite 表达式(http://www.w3cschool.cc/sqlite/sqlite-expressions.html)
- WPF DataGrid根据内容设置行颜色
- Python标准库socketserver实现UDP协议时间服务器
- 百度统计挂了,分布式数据库异常引起,数据显示为空!
- Atitit nlp自然语言处理类库(java python nodejs c#net) 目录 1.1. Python snownlp	1 1.2. NLP.js一个nodejs/javascri
- delphi xe 服务器发消息,Delphi XE IdTCPClient和IdTCPServer 发送和接收文件
- 21天学通java不好_21天学通Java_怎么样学好Java,学Java-IT培训网
- 客户要管理,就靠crm吗?
- 超实数与实数本是同根生
- Android平台上的PacketSender
- 【音频处理】视频加速音频不变调
- 如何有效地设置QTextEdit的背景
- linux如何使用帮助文档快速学习
- OSG读取obj模型坐标变化的问题
- 搜索引擎优化:常用的SEO六个指标
- 第五届信息科学、电气与自动化工程国际学术会议(ISEAE 2023)
热门文章
- 基于STC8H1K28的双轴机械臂驱动模块:步进电机42HS348E,BH32角度传感器
- vue cli 添加html,vue-cli创建的项目,配置多页面的实现方法
- 虚拟摄像头 安卓版_林俊杰 ft. M.E.,联同视效大厂数字王国加码虚拟偶像
- git 可视化工具_最值得推荐的8个git/github项目数据分析工具
- 计算机软考高级论文,【2019年软考高项论文一定要避开这些坑】- 环球网校
- 【FPGA】双端口RAM的设计(同步读写)
- 为什么在用Vivado生成存储器配置文件时找不到相应型号的Flash芯片呢?
- 【 Vivado 】在工程模式下通过jou文件来学习 Tcl 命令
- system generator学习笔记【02】
- 《VMware vCAT权威指南:成功构建云环境的核心技术和方法》一3.6 vCloud计量