单片机的非OS的事件驱动思考

上传人:仙*** 文档编号:28009487 上传时间:2021-08-22 格式:DOC 页数:9 大小:40.01KB
收藏 版权申诉 举报 下载
单片机的非OS的事件驱动思考_第1页
第1页 / 共9页
单片机的非OS的事件驱动思考_第2页
第2页 / 共9页
单片机的非OS的事件驱动思考_第3页
第3页 / 共9页
资源描述:

《单片机的非OS的事件驱动思考》由会员分享,可在线阅读,更多相关《单片机的非OS的事件驱动思考(9页珍藏版)》请在装配图网上搜索。

1、单片机的非OS的事件驱动思考很多单片机项目恐怕都是没有操作系统的前后台结构,就是main函数里用while无限循环各种任务,中断处理紧急任务。这种结构最简单,上手很容易,可是当项目比较大时,这种结构就不那么适合了,编写代码前你必须非常小心的设计各个模块和全局变量,否则最终会使整个代码结构杂乱无序,不利于维护,而且往往会因为修改了某部分代码而莫名其妙的影响到其他功能,而使调试陷入困境。改变其中局面的最有效措施当然是引入嵌入式操作系统,但是大多数的操作系统都是付费的(特别是商业项目)。我们熟悉的uc-os/II如果你应用于非商业项目它是免费的,而应用于商业项目的话则要付费,而且价格不菲。我们也可以

2、自己编写一套嵌入式OS,这当然最好了。可要编写一套完整的OS并非易事,而且当项目并不是非常复杂的话也不需要一个完整的os支持。我们只要用到OS最基本的任务调度和上下文切换就够了。正是基于这样的想法,最近的一个项目中我就尝试采用事件驱动的思想重新构建了代码架构,实际使用的效果还不错,在这里做个总结。本质上新架构仍然是前后台结构,只不过原来的函数直接调用改成通过指向函数的指针来调用。实际上这也是嵌入式OS任务调度的一个核心。C语言中可以定义指向函数的指针: void (*handle)(void);这里的handle就是一个指向函数的指针,我们只要将某函数的函数名赋给该指针,就能通过实现函数的调用

3、了: void func1(void) / Code handle = func1; (*handle)(); / 实现func1的调用有了这个函数调用新方法,我们就可以想办法将某个事件与某个函数关联,实现所谓的事件驱动。例如,按键1按下就是一个事件,func1响应按键1按下事件。但是,如果是单纯的调用方法替代又有什么意义呢?这又怎么会是事件驱动呢?关键就在于使用函数指针调用方法可以使模块和模块之间的耦合度将到最低。一个例子来说明这个问题,一个按键检测模块用于检测按键,一个电源模块处理按键1动作。传统的前后台处理方法:main.c void main() . while(1) . keySca

4、n(); if(flagKeyPress) keyHandle(); / 检测到按键就设置flagKeyPress标志,进入处理函数 key.c void keyHandle(void) switch (_keyName) / 存放按键值的全局变量 . case KEY1: pwrOpen(); break; case KEY2: pwrClose(); break; power.c void pwrOpen(void) . void pwrClose(void) . 这样的结构的缺点在哪里呢?1. key代码中直接涉及到power代码的函数,如果power代码里的函数变更,将引起key代码的

5、变更2. 一个按键值对应一个处理函数,如果要增加响应处理函数就要再次修改key代码3. 当项目越来越大时,引入的全局变量会越来越多,占用过多的内存很显然key模块与其他模块的耦合程度太高了,修改其他模块的代码都势必去修改key代码。理想的状态是key模块只负责检测按键,并触发一个按键事件,至于这个按键被哪个模块处理,它压根不需要知道,大大减少模块之间的耦合度,也减少出错的几率。这不正好是事件驱动的思想吗?接下来,该如何实现呢?事件驱动的实现。需要一个事件队列: u16 _eventMAX_EVENT_QUEUE;它是一个循环队列,保存事件编号,我们可以用一个16位数为各种事件编号,可以定义65

6、535个事件足够使用了。一个处理函数队列: typedef struct u16 event; / 事件编号 void (*handle)(void); / 处理函数 handleType; handleType _handleMAX_HANDLE_QUEUE;它实际是一个数组,每个元素保存事件编号和对应的处理函数指针。一个驱动函数: void eventProc(void) u16 event; u8 i; if(_eventHead!=_eventTail) | _eventFull) / 事件队列里有事件时才处理 event = _eq_eventHead; _event _eventH

7、ead+ = 0; / 清除事件 if(_eventHead= MAX_EVENT_QUEUE) _eventHead= 0; / 循环队列 / 依次比较,执行与事件编号相对应的函数 for(i=0; i_handleTail; i+) if(_handlei.event = event) (*_handlei.handle)(); main函数可以精简成这样: void main(void) . while(1) eventProc(); 这样代码的缺陷是需要为处理函数队列分配一个非常大的内存空间,项目越复杂分配的空间越大,显然不可取。需要做个变通,减少内存空间的使用量。变通方法就是将各模块

8、的散转处理保存在模块内部,而不再写入到处理函数队列中,具体实现如下:事件队列不需要修改。修改处理函数队列: typedef struct void (*handle)(u16 event); / 仅保存模块总的散转函数 handleType; handleType _handleMAX_HANDLE_QUEUE;修改驱动函数: void eventProc(void) u16 event; u8 i; if(_eventHead!=_eventTail) | _eventFull) / 事件队列里有事件时才处理 . for(i=0; i= MAX_HANDLE_QUEUE) _handleFu

9、ll= TRUE; 每个模块都定义各自的散转处理,然后在初始化的时候将该函数存入处理事件队列中,即能实现事件处理又不会占用很多的内存空间。加入到事件队列需要封装成统一的函数dispatchEven,由各模块直接调用。例如,key模块就可以dispatchEvent(EVENT_KEY1_PRESS)来触发一个事件 void dispatchEvent(u16 event) u8 i; bool canDispatch; canDispatch = TRUE; if(!_eventFull) / 为了避免同一事件被多次加入到事件队列中 for(i=_eventHead; i!=_eventTai

10、l;) if(_eventi = event) canDispatch = FALSE; break; i+; if(i = MAX_EVENT_QUEUE) i = 0; if(canDispatch) _event_eventTail+ = event; if(_eventTail= MAX_EVENT_QUEUE) _eventTail= 0; if(_eventTail= _eventHead) _eventFull = TRUE; 对于与时间相关的事件(循环事件和延时处理事件)需要做进一步处理。首先要设定系统Tick,可以用一个定时器来生成,例如配置一个定时器,每10ms中断一次。定

11、义一个时间事件队列: typedef struct u8 type; / 事件类别,循环事件还是延时事件 u16 event; / 触发的事件编号 u16 timer; / 延时或周期时间计数器 u16 timerBackup; / 用于周期事件的时间计数备份 timerEventType; timerEventType _timerEventMAX_TIMER_EVENT_Q;在定时器Tick中断中将时间事件转换成系统事件: void SysTickHandler(void) . for(i=0; i_timerEventTail; i+) _timerEventi.timer-; if(_

12、timerEventi.timer = 0) dispatchEvent(_timerEventi.event); if(_timerEventi.type = CYCLE_EVENT) / 循环事件,重新计数 _timerEventi.timer = _timerEventi.timerBackup; else / 延时事件,触发后删除 delTimerEvent(_timerEventi.event); 将增加和删除时间事件封装成函数,便以调用: void addTimerEvent(u8 type, u16 event, u16 timer) _timerEvent_timerEvent

13、Tail.type = type; _timerEvent_timerEventTail.event = event; _timerEvent_timerEventTail.timer = timer; / 时间单位是系统Tick间隔时间 _timerEvent_timerEventTail.timerBackup = timer; / 延时事件并不使用 _timerEventTail+; void delTimerEvent(u16 event) . for(i=0; i_timerEventTail; i+) if(_timerEventi.event = event) for(j=i; j_timerEventTail; j+) _timerEventj = _timerEventj+1; _timerEventFull= FALSE; _timerEventTail-; 对于延时处理,用事件驱动的方法并不理想,因为这可能需要将一段完整的代码拆成两个函数,破坏了代码的完整性。解决的方法需要采用OS的上下文切换,这就涉及到程序堆栈问题,用纯C代码不容易实现。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!