一种基于C51的多任务机制及应用
- 格式:pdf
- 大小:114.74 KB
- 文档页数:4
试论51单片机多任务编程设计及应用51单片机是一种基于哈弗曼编码技术的单片机,广泛应用于嵌入式系统中。
随着嵌入式系统复杂度的不断提高,多任务编程设计已经成为了一种必要的需求。
本文将试论51单片机多任务编程设计及应用。
多任务设计可以使嵌入式系统在同时运行多个任务时,具有更高的系统效率和响应速度。
在多任务环境中,各任务可同时执行,每个任务都具有自己的优先级和独立的数据空间。
对于51单片机而言,要实现多任务编程设计,需要考虑以下几个方面:1.任务划分首先需要将任务划分为多个单独的任务。
根据任务的优先级和时间限制,进行合理的划分和安排。
2.任务调度在多任务环境中,需要进行任务调度,确定任务执行的优先级和时间片。
一般采用时间片轮转方式,即每个任务分配一个时间片,时间到了就切换到下一个任务。
3.任务间通信不同任务之间需要共享数据和通信,可采用共享变量或消息机制等方式进行任务间通信。
在实际应用中,多任务编程设计可应用于多种场景,例如自动控制、机器人控制、传感器采集等。
下面以自动控制为例,介绍51单片机多任务编程设计及应用。
自动控制系统通常包括数据采集、数据处理和执行控制三个任务。
数据采集任务负责采集传感器数据,数据处理任务负责对采集数据进行处理和分析,执行控制任务负责根据处理结果执行相应的控制操作。
在这个模块中,执行控制任务的优先级最高,数据采集任务的优先级最低。
首先应进行任务划分,将数据采集、数据处理和执行控制分别划分为独立的任务。
接着需要确定任务调度的方式和时间限制,可采用时间片轮转方式,每个任务分配一个时间片,执行控制任务的时间片最短,数据采集任务的时间片最长。
最后需要进行任务间通信,数据采集任务将采集到的数据存入共享变量中,数据处理任务通过共享变量获取数据进行处理,执行控制任务通过共享变量获取处理结果进行操作。
综上所述,对于51单片机的多任务编程设计及应用,需要进行任务划分、任务调度和任务间通信。
在实际应用中,多任务设计可提高嵌入式系统的效率和响应速度,降低系统复杂度。
多任务机制在单片机系统中的应用邓生根摘要:提出了一种崭新的基于任务机制的单片机系统程序结构,讨论了具体实现方法,并给出应用实例。
关键词:多任务机制单片机系统中断机制传统的单片机系统监控程序通常是基于单任务机制的。
这种机制具有简单直观,易于控制的优点。
然而由于程序只能按单一的线索顺序执行,缺乏灵活性,在复杂系统中难以胜任。
为了在更广泛的领域应用单片机系统,必须对传统的单任务机制进行改进。
多任务机制是现代操作系统才具有的突出优点。
在这种机制下,CPU的运行时间被划分为许多小的时间片,由某种调度算法按不同优先级别分配给不同的应用程序。
多个应用程序分别在自已的时间片内访问CPU,从而造成微观上轮流运行,宏观上并发运行的多任务效果。
在单片机系统中引入多任务机制,可以有效改善程序结构,满足复杂系统的要求。
任何多任务本质上都借助于中断机制。
通用单片机中都允许使用中断,从而保证了在单片机系统中实现多任务的切实可行。
而单片机运算能力的有限要求多任务调度算法必须简洁。
1 两种机制的比较一个典型的单片机监控系统通常包括输入、输出控制,数据处理,显示以及键盘管理。
在传统的单任务机制下,程序采用循环方式,其流程图如图1所示。
由流程可知,在单任务机制下,各功能模块按固定顺序构成一个整体,作为一个任务得到执行。
而在实际应用中,各个模块要求的执行频率往往不一致,如输入采样频率可能要求很高,而单位时间内键盘扫描的次数则相对较少,系统这种复杂的定时要求在单任务机制下难以满足。
另外在这种结构下程序一旦建立,各模块的执行顺序即已固定,对于需在运行时动态改变执行结构的系统,程序需用许多条件判断和分枝转移语句进行控制,增加了程序的复杂性。
程序作为一个整体而存在可读性和可维护性很差,调试不便,对已有程序进行扩充,也需先了解整个程序结构,增加了扩充难度。
考虑到单片机完成的系统功能往往可以分解为若干相对独立的模块,我们可以将这些模块理解为子任务,并引进多任务机制进行管理,从而形成一种崭新的程序结构。
51单片机多任务编程设计及应用作者:李鹏来源:《科技风》2016年第16期摘要:在51单片机上实现多任务处理主要借鉴现代操作系统的分时处理方法,有几种不同的实现策略。
本文将基于此背景,讨论51单片机多任务编程的设计和应用,详细阐述其实现策略。
关键词:51单片机;多任务编程;操作系统单片机技术经历几十年的发展逐步走向成熟,被广泛应用于各行业。
51单片机指兼容Intel8031系统的单片机系统,在智能控制领域有广泛的引用[ 1 ]。
一、多任务执行原理51单片机的多任务执行机制主要借鉴现代操作系统的分时处理方法。
事实上,多任务执行并非多个任务同时运行,而是CPU在不同的任务之间不停的切换,每次执行一个任务的一小部分,之后迅速切换至下一个任务,并执行这个任务的一小部分,然后在切换至另一个任务,以此循环往复。
从宏观上来看,就好像多个任务在同时执行。
系统通过合理的调度,将CPU的运行时间合理分配给各个任务,每个任务轮流占用一小部分时间。
这就是现代操作系统分时机制的原理[ 3 ],也是51单片机多任务执行的基本方法。
二、实现策略51单片机多任务执行机制针对不同的应用场景和不同的单片机型号,在具体实现上有所区别,但从根本上来说都是以现代操作系统分时理论为基础来实现的。
下面将详细讲解具体如何使用时间片分配机制来实现51单片机的多任务执行。
基于时间片分配机制来实现51单片机多任务执行主要涉及三项内容。
一是待执行程序,以列表形式组织,二是运算资源,也就是CPU,三是调度器,用于统筹安排待执行程序的执行顺序,合理给各待执行程序分配运算资源。
具体的运行机制如下。
首先,在初始化阶段,待执行任务被组织为列表,然后调度器根据具体情况为各个任务分配不同数量的时间片。
然后在调度器的组织下,各个任务依次占用CPU,占用时间为各自对应数量的时间片。
通常来说,根据具体情况不同,各任务占用的时间片数目有所区别,但总数量都不会很多,CPU只执行任务的一小部分,然后迅速切换至下一个任务。
这个操作系统很给力一种裸奔多任务模型一个网友的总结:stateMachine + timerTick + queue。
在RTOS环境下的多任务模型:任务通常阻塞在一个OS调用上(比如从消息队列取数据)。
外部如果想让该任务运转,就要向消息队列发送消息。
任务收到消息时,根据当前状态,决定如何处理消息。
这就是状态机。
任务将消息队列中的消息处理完毕后,重新进入阻塞状态。
任务在处理中,有时要延时一段时间,然后才继续工作:为了充分使用CPU,可以通过OS调用让其它任务去工作。
OS通常会提供一个taskDelay调用。
当任务调用taskDelay时,即进入阻塞状态,直到超时,才重新进入可工作状态(就绪状态)。
下面说说裸奔环境下的多任务模型:裸奔也可以多任务,但调度是由用户自主控制。
在RTOS环境下,一般提供抢占式调度。
在裸奔时,一般是任务在处理告一段落后,主动结束处理。
RTOS环境下的任务,一般处于一个while(1)循环中。
while(1){从消息队列接收消息。
如果没有,将阻塞。
处理消息。
}裸奔下的任务,一般采用查询方式:{查询是否有待处理的事件。
如果没有,返回。
如果有,根据任务的当前状态,进行处理。
处理完毕后,可能返回,也可能将待处理事件全部处理完毕后再返回。
}裸奔任务其实也处于一个while(1)循环中,只不过这个循环在任务外部。
main(){A_taskInit(); //任务的初始化B_taskInit();...while(1){A_taskProc(); //任务的处理B_taskProc();}}状态机既适用于OS环境,也适用于裸奔环境。
但在裸奔环境下,状态可能被切分得更细。
例如后面讲的如何在裸奔环境实现taskDelay()。
消息队列既适用于OS环境,也适用于裸奔环境。
在OS环境下,消息队列机制由OS提供。
在裸奔环境下,消息队列要自己来实现。
如果对队列的概念不清楚,可参考《数据结构》教材。
这个队列机制,可做成通用模块,在不同的程序中复用。
C51编程:多任务程序设计的结构,纯属个人观点,希望大家借签一下,提出更好的意见。
[小师⊕] [156次] 01-7-31 下午 08:48:27C51的一些特征技巧可供利用:1.时间的模糊性.在大多数情况下,时间是具有模糊性的.象秒,分钟,小时..,从长的时间角度,即使你计秒的时间被退后0.5秒,在大多数情况下都是允许的,包括一些显示.还有象扫描键盘,你可在20MS去抖,也可在30,30MS时间去抖,这个时间范围是有一定弹性的.又如闪烁要求400MS,你可在410MS去刷新,下次在2*400MS,只要保证长的周期定时是准确的,个别时间是可推迟的。
这样的情形会在许多地方发生,这就给设计多任务程序提供了一个基础.2.消息的周期循环性.消息指系统函数(定时类的),模块之间有状态变化,模块内部有状态请求而相应产生的标志数据或变量数据,它的特点是它的遍历整个模块,直到有模块接收它后让它消失,没有模块接收时,循环一周被自身消失.举个例,有T0计数器0.1MS产生一个中断,让其他所有模块都知道,模块不能消灭它,它只能被自己消灭:void timer0(void) interrupt 1 /*T0中断*/{fSYS_100us=1;}bit fSYS_TimeNow;#define Timer0_MainLoop() {fSYS_TimeNow=0;if(fSYS_100us){fSYS_TimeNow=1;fSYS_100us=0;}}unsigned char uCount;main(){init();uCount=100;while(1){Time0_MainLoop();Task0();if(fSYS_TimeNow)Task1();Task2();if(fSYS_TimeNow){uCount--;if(uCount==0){uCount=100;Task3();}}}}这样消息具有自我生成消失发布的能力,而且使模块具有独立性(Time0_MainLoop();可放在WHILE中的任何地方而不影响它的作用).而象键盘之类产生的消息,常常是每个模块接收到它后,就使它消失,避免其他模块也接收.消息在多任务程序中的作用:相当与桥梁,使模块间既相互独立又相互连接。
一种基于C51的多任务机制及应用摘要:本文介绍了一种在MCS51单片机程序中实现多任务机制的简单方法,并给出了源代码和一个应用实例。
通过中断进行实时任务切换,具有结构简单清晰、代码量少、不需使用汇编等优点。
该方法亦可应用于其他单片机系统。
关键词:多任务系统 单片机 C51 中断 安防系统引言传统的单片机程序一般采用单任务机制,单任务系统具有简单直观、易于控制的优点。
然而由于程序只能按顺序依次执行,缺乏灵活性,只能使用中断函数实时地处 理一些较短的任务,在较复杂的应用中使用极为不便。
嵌入式多任务操作系统的出现解决了这个问题。
在多任务系统中,可以同时执行多个并行任务,任务之间可以 相互跳转。
但是嵌入式操作系统在提供强大功能的同时,也带来了代码量大、结构复杂、对硬件要求较高、开发难度大且成本高等问题。
而很多时候只需要实现简单 的多任务操作就可以满足实际需要,本文设计的这种简单的多任务机制,在只增加极少量C语言代码的前提下,不需使用汇编,无需对原本的程序进行大改动,就可 以实现多任务操作。
μ实时操作系统RTOS的核心是中断,利用中断进行任务切换。
在大部分RTOS如C/OS-II 中,每个任务都有自己的堆栈,用来保存任务的一些信息, 任务之间通过信号量、邮箱、消息队列等传递信息。
在很多情况下并不需要这些功能,只需要使单片机在接收到控制信号后,切换到不同的工作状态,也就是只要进 行任务切换,不需要保存任务的相关信息。
舍弃这些复杂的功能可以使程序结构变得简洁易用。
两种机制在应用实例中的比较下面用一个应用实例来说明本设计的思路。
要设计一个智能安防系统,它的功能包括:当有人入侵时执行报警工作;用户可以通过键盘板进行功能设置;主板能与管 理中心进行通讯,当发生火灾、地震等灾情时,管理中心能通知用户。
其结构如图1所示。
平时状态下,主板的CPU不断地扫描各个传感器的状态。
当检测到传感 器的异常信号(有人闯入)时,CPU进入入侵报警状态,执行响警铃、拨打户主电话、通知管理中心等工作。
当发生火灾地震时,管理中心发送一个串口代码给主 板CPU,使CPU进入灾难报警状态,执行响警铃、语音报警等操作。
用户需要进行功能设置时可以通过键盘板使主板CPU进入功能设置状态。
因此主板的 CPU有4种不同的工作状态。
图1 智能安防系统结构示意图如果采用单任务机制, 主板的程序流程如图2所示。
在主函数中循环检测传感器状态,如有异常则调用报警函数,灾难报警和功能设置在串口中断中完成。
这种单任务结构有两个缺点。
首先,在各种非平时状态中,程序需要不停地检测是否收到撤除信号,这个要求在程序代码量大、执行工作较多的情况下很难实现。
其次,各状态之间的切换十分困 难,用C 语言写的程序为求模块化,一般函数数量较多,函数调用的嵌套层数也多,要从一个较深的嵌套立刻跳出到主函数,是非常困难的。
一般的解决方法或是使 用C51的库函数setjmp()和longjmp()实现长跳转,但是这两个函数在中断函数内部是无能为力的;再或是在C函数中嵌入汇编指令。
虽然用汇 编指令可以实现程序的长距离跳转,但是这种方法的调试过程十分烦琐,而且程序的可移植性差。
对于习惯用C51编程而不想用汇编的设计者,该部分程序是一个 难题。
图2 单任务机制程序流程实现多任务机制的程序结构本文提供了一种方法,可以在完全不使用汇编指令的前提下实现可移植性强的多任务程序,程序流程如图3所示。
图3 多任务结构程序流程实现这个多任务机制的完整源代码如下:word idata PC_Value, SP_Value; file://储存中断返回点、SP初值的全局变量byte idata Ctrl_Code; file://控制任务切换的全局变量,在中断函数里被赋值void main(){Initial(); file://初始化函数,与程序结构无关SP_Value=SP; file://获取SP的初始值PC_Value=Get_Next_PC(); file://获取下一条指令的地址EA=1; file://获取PC、SP初值后再开中断保证稳定性if(Ctrl_Code!=0)SP=SP_Value; file://重置堆栈指针,防止堆栈溢出switch( Ctrl_Code) file://任务入口地址,即中断的返回点{case 1: goto TASK1;case 2: goto TASK2;case 3: goto TASK3;default: break;}TASK1: for( ; ; ){ file://任务1代码 }TASK2: for( ; ; ){ file://任务2代码 }TASK3: for( ; ; ){ file://任务2代码 }}word Get_Next_PC(void) file://获取下一条指令的地址{word address;address=*((unsigned char *)SP); file://PC 的高字节address <<= 8;address+=*((unsigned char *)(SP-1)); file://PC 的低字节return address+4; file://查看反汇编代码,计算所得}void Chuan_Kou_Interrupt(void) interrupt 4 using 0{byte a1,a2;a1=a1*a2;*((unsigned char *)(SP-5))=PC_Value>>8;*((unsigned char *)(SP-6))=PC_Value & 0x00ff;{file://接收串口代码并根据代码修改Ctrl_Code的值file://其他操作}}任务调度原理与实现程序的整体思路是在主函数main中依次放置几个死循环作为任务框架,即每个任务都是一个死循环,利用中断进行任务切换。
以刚才所说的安防系统为例,由于 主板、键盘、管理中心之间是通过串口通讯的,因此串口是用来触发任务切换的理想中断源。
程序为所有任务设置一个总入口并放在主函数中,串口中断每次返回时 必须先经过这个总入口,在总入口处检查任务控制变量(全局变量)的值,任务控制变量已在串口中断中被赋值,其值决定要切换到哪个任务。
设计中可以把平时状态、入侵报警状态、危机报警状态、功能设置状态分别作为任务1、任务2、任务3、任务4。
主板CPU平常工作在平时状态,即任务1; 当串口收到管理中心的危机代码,在串口中断函数中令Ctrl_Code = 3,中断返回后会切换到任务3;同样,接收到键盘的功能设置代码后,会切换到任务4;由于入侵检测是由主板CPU自己负责,因此如果检测到有人入侵需要切 换到入侵报警状态时,可以借由键盘中转产生串口中断,即向键盘发送一串口数据并要求键盘回送。
这样就实现了各个状态的切换。
实现任务调度需要解决3个关键问题: ①获取任务入口点的程序地址。
由于使用C语言不能直接获取和修改程序计数器PC的值,而在调用函数时会将PC值入栈,利用这个特点在任务入口处之前调用 Get_Next_PC 函数即可从堆栈中获得入口地址。
Get_Next_PC中,SP为堆栈指针,得到的PC值要加4才是任务入口地址,因为查看反汇编 窗口可知,将函数返回值传给全局变量PC_Value需要两条2字节长的mov指令。
②修改中断返回地址。
修改中断返回地址的操作与获取PC值类似,都是通过修改堆栈中的内容实现。
但是由于编译器自身的特点,在进入中断时,编译器除了把返回 地址入栈外,还会计算自身及它所调用的函数对寄存器ACC、 B、 DPH、 DPL、 PSW、 R0 ~R7的改变,并将它认为被改变了的寄存器也入栈保护。
如果堆栈结构会随中断函数内容改变而变化,就没办法计算中断返回地址堆栈中的位置。
解决方法是,在中 断函数定义时加上关键字using 0 告诉编译器中断函数及其调用的函数将使用寄存器组0,这样工作寄存器R0~R7将不会被保存。
ACC、PSW、DPH、DPL在对PC_Value操作时 已经用到,在中断函数开头定义两个变量a1、b1并令它们相乘,使B寄存器也被入栈,这样堆栈的结构就是固定的了。
③防止堆栈溢 出。
由于在调用函数时编译器会将当前地址入栈,返回时再出栈,当任务切换即中断多次发生在函数调用过程中时,堆栈会因为只入不出而最终导致溢出。
这是不能 容许的。
因此,应在主函数开头初始化后立刻将SP值保存,再在每次任务切换后都将SP恢复为初值,这可以有效防止堆栈溢出。
结语 根据以上的比较与分析可以看出这种实现多任务机制的方法具有如下优点:与采用单任务机制的程序相比,其结构简单清晰,易于控制;利用中断和堆栈实现任务切换时的长跳转,完全不需使用汇编语言,可移植性强;增加的代码量极小,实时性好,节省程序开发时间。
以上介绍的方法已经通过测试并应用于几个实际项目中,包括智能小区安防系统、汽车CAN总线控制系统等,取得了良好效果。
只要根据具体的硬件与编译环境稍作修改,亦可应用于其他的单片机系统中。
参考文献1. 张培仁. 基于C语言编程MCS-51单片机原理与应用. 北京:清华大学出版社, 2003.1.2. 胡大可等. 基于单片机8051的嵌入式开发指南. 北京:电子工业出版社, 2003.1。