ImageVerifierCode 换一换
格式:DOCX , 页数:11 ,大小:20.93KB ,
资源ID:10286790      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/10286790.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(中断驱动多任务单片机MCU下的一种软件设计结构.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

中断驱动多任务单片机MCU下的一种软件设计结构.docx

1、中断驱动多任务单片机MCU下的一种软件设计结构中断驱动多任务- 单片机(MCU) 下的一种软件设计结构mcu由于内部资源的限制,软件设计有其特殊性,程序一般没有复杂的算法以及数据结构,代码量也不大, 通常不会使用 OS (Operating System),因为对于一个只有 若干K ROM, 一百多byte RAM 的 mcu 来说,一个简单OS也会吃掉大部分的资源。对于无 os 的系统,流行的设计是主程序(主循环 ) + (定时)中断,这种结构虽然符合自然想法,不过却有很多不利之处,首先是中断可以在主程序的任何地方发生,随意打断主程序。其次主程序与中断之间的耦合性(关联度)较大,这种做法 使

2、得主程序与中断缠绕在一起,必须仔细处理以防不测。那么换一种思路,如果把主程序全部放入(定时)中断中会怎么样?这么做至少可以立即看到几个好处: 系统可以处于低功耗的休眠状态,将由中断唤醒进入主程序; 如果程序跑飞,则中断可以拉回;没有了主从之分(其他中断另计),程序易于模块化。(题外话:这种方法就不会有何处喂狗的说法,也没有中断是否应该尽可能的简短的争论了)为了把主程序全部放入(定时)中断中,必须把程序化分成一个个的模块,即任务,每个任务完成一个特定的功能,例如扫描键盘并检测按键。 设定一个合理的时基 (tick), 例如5, 10 或 20 ms,每次定时中断,把所有任务执行一遍,为减少复杂性

3、,一般不做动态调度(最多使用固定数组以简化设计,做动态调度就接近 os 了),这实际上是一种无优先级时间片轮循的变种。来看看主程序的构成:void main()./ Initializewhile (true) IDLE;/sleep这里的 IDLE 是一条sleep 指令,让 mcu 进入低功耗模式。中断程序的构成void Timer_Interrupt() SetTimer();ResetStack();Enable_Timer_Interrupt;.进入中断后,首先重置Timer, 这主要针对8051, 8051 自动重装分频器只有 8-bit, 难以做到长时间定时;复位 stack ,

4、即把stack 指针赋值为栈顶或栈底(对于 pic, TI DSP 等使用循环栈的 mcu 来说,则无此必要),用以表示与过去决裂,而且不准备返回到中断点,保证不会保留程序在跑飞时stack 中的遗体。Enable_Timer_Interrupt 也主要是针对8051。8051 由于中断控制较弱,只有两级中断优先级,而且使用了如果中断程序不用 reti 返回,则不能响应同级中断这种偷懒方法,所以对于 8051, 必须调用一次 reti 来开放中断:_Enable_Timer_Interrupt: acall_reti_reti:reti下面就是任务的执行了,这里有几种方法。第一种是采用固定顺序

5、,由于mcu 程序复杂度不高,多数情况下可以采用这种方法:Enable_Timer_Interrupt;ProcessKey();RunTask2();RunTaskN();while (1) IDLE;可以看到中断把所有任务调用一遍,至于任务是否需要运行,由程序员自己控制。另一种做法是通过函数指针数组:#define CountOfArray(x) (sizeof(x)/sizeof(x0)typedef void (*FUNCTIONPTR)();const FUNCTIONPTR tasks = ProcessKey, RunTask2,RunTaskN;void Timer_Inter

6、rupt() SetTimer();ResetStack();Enable_Timer_Interrupt;for (i=0; iCountOfArray (tasks), i+) (*tasksi)();while (1) IDLE;使用const 是让数组内容位于 code segment (ROM) 而非 data segment (RAM) 中,8051 中使用 code 作为 const 的替代品。(题外话:关于函数指针赋值时是否需要取地址操作符 & 的问题,与数组名一样,取决于 compiler. 对于熟悉汇编的人来说,函数名和数组名都是常数地址,无需也不能取地址。对于不熟悉汇编的

7、人来说,用 & 取地址是理所当然的事情。Visual C+ 2005对此两者都支持)这种方法在汇编下表现为散转, 一个小技巧是利用 stack 获取跳转表入口: movA, stateacallMultiJumpajmpstate0ajmpstate1.MultiJump:popDPHpopDPLrlAjmpA+DPTR还有一种方法是把函数指针数组(动态数组,链表更好,不过在 mcu 中不适用)放在 data segment 中,便于修改函数指针以运行不同的任务,这已经接近于动态调度了:FUNCTIONPTRCOUNTOFTASKS tasks;tasks0 = ProcessKey;task

8、s0 = RunTaskM;tasks0 = NULL;.FUNCTIONPTR pFunc;for (i=0; i COUNTOFTASKS; i+)pFunc = tasksi);if (pFunc != NULL)(*pFunc)();通过上面的手段,一个中断驱动的框架形成了,下面的事情就是保证每个 tick 内所有任务的运行时间总和不能超过一个 tick 的时间。为了做到这一点,必须把每个任务切分成一个个的时间片,每个 tick 内运行一片。这里引入了状态机 (state machine) 来实现切分。关于 state machine,很多书中都有介绍, 这里就不多说了。(题外话:实践

9、升华出理论,理论再作用于实践。我很长时间不知道我一直沿用的方法就是state machine,直到学习UML/C+,书中介绍 tachniques for identifying dynamic behvior,方才豁然开朗。功夫在诗外,掌握 C+, 甚至C# JAVA, 对理解嵌入式程序设计,会有莫大的帮助)状态机的程序实现相当简单,第一种方法是用 swich-case 实现:void RunTaskN() switch (state) case 0: state0(); break;case 1: state1(); break;case M: stateM(); break;defaul

10、t:state = 0;另一种方法还是用更通用简洁的函数指针数组:const FUNCTIONPTR states = state0, state1, , stateM ; void RunTaskN() (*statesstate)(); 下面是 state machine 控制的例子:void state0() void state1() state+; /next state;void state2() state+=2; /go to state 4;void state3() state-; /go to previous state;void state4() delay = 10

11、0; state+; void state5() delay-; if (delay = 0) state+; /delay 100*tickvoid state6() state=0; /go to the first state一个小技巧是把第一个状态 state0 设置为空状态,即:void state0() 这样,state =0可以让整个task 停止运行,如果需要投入运行,简单的让 state = 1 即可。以下是一个键盘扫描的例子,这里假设 tick = 20 ms, ScanKeyboard() 函数控制口线的输出扫描,并检测输入转换为键码,利用每个state 之间 20 ms

12、 的间隔去抖动。enum EnumKey EnumKey_NoKey =0,;struct StructKey intkeyValue;boolkeyPressed; ; struct StructKeyProcess key;void ProcessKey() (*statesstate)(); void state0() void state1() key.keyPressed = false; state+; void state2() if (ScanKey() != EnumKey_NoKey) state+; /next state if a key pressedvoid sta

13、te3() /debouncing statekey.keyValue = ScanKey(); if (key.keyValue = EnumKey_NoKey)state-;else key.keyPressed = true;state+;void state4() if (ScanKey() = EnumKey_NoKey) state+; /next state if the key releasedvoid state5() ScanKey() = EnumKey_NoKey? state = 1 : state-; 上面的键盘处理过程显然比通常使用标志去抖的程序简洁清晰,而且没有

14、软件延时去抖的困扰。以此类推,各个任务都可以划分成一个个的state, 每个state 实际上占用不多的处理时间。某些任务可以划分成若干个子任务,每个子任务再划分成若干个状态。(题外话:对于常数类型,建议使用 enum 分类组织,避免使用大量 #define 定义常数)对于一些完全不能分割,必须独占的任务来说,比如我以前一个低成本应用中红外遥控器的软件解码任务,这时只能牺牲其他的任务了。两种做法:一种是关闭中断,完全的独占;void RunTaskN() Disable_Interrupt;Enable_Interrupt;第二种,允许定时中断发生,保证某些时基 register 得以更新;v

15、oid Timer_Interrupt() SetTimer();Enable_Timer_Interrupt;UpdateTimingRegisters();if (watchDogCounter = 0) ResetStack();for (i=0; iCountOfArray (tasks), i+) (*tasksi)();while (1) IDLE;else watchDogCounter-; 只要watchDogCounter 不为 0,那么中断正常返回到中断点,继续执行先前被中断的任务,否则,复位 stack, 重新进行任务循环。这种状况下,中断处理过程极短,对独占任务的影响也

16、有限。中断驱动多任务配合状态机的使用,我相信这是mcu 下无os 系统较好的设计结构。对于绝大多数 mcu 程序设计来说,可以极大的减轻程序结构的安排,无需过多的考虑各个任务之间的时间安排,而且可以让程序简洁易懂。缺点是,程序员必须花费一定的时间考虑如何切分任务。下面是一段用 C 改写的CD Player 中检测 disc 是否存在的伪代码,用以展示这种结构的设计技巧,原源代码为Z8 mcu 汇编, 基于 Sony 的 DSP, Servo and RF 处理芯片, 通过送出命令字来控制主轴/滑板/聚焦/寻迹电机,并读取状态以及 CD 的sub Q 码。这个处理任务只是一个大任务下用state

17、 machine切开的一个二级子任务,tick = 20 ms。state1() InitializeMotor(); state+; state2() if (innerSwitch != ON) SendCommand(EnumCommand_SlidingMotorBackward); timeout = MILLISECOND(10000);state+;/ 滑板电机向内运动, 直至触及最内开关。 elsestate +=2;state3() if (-timeout) = 0) /note: some C compliers do not support (-timeout) =Se

18、ndCommand(EnumCommand_SlidingMotorStop)systemErrorCode = EnumErrorCode_InnerSwitch;state = 0;/ 10 s 超时错误,else if (innerSwitch = ON) SendCommand(EnumCommand _SlidingMotorStop)timeout = MILLISECOND(200);/ 200ms电机停止时间state+;state4() if (-timeout) = 0) state+; /等待电机完全停止state5() SendCommand(EnumCommand_S

19、lidingMotorForward); timeout = MILLISECOND(2000);state+; / 滑板电机向外运动,脱离inner switchstate6() if (-timeout) = 0) SendCommand(EnumCommand_SlidingMotorStop)systemErrorCode = EnumErrorCode_InnerSwitch;state = 0;/ 2 s 超时错误,else if (innerSwitch = OFF) SendCommand(EnumCommand_SlidingMotorStop)timeout = MILLISECOND(200);

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1