51单片机最简单的多任务操作系统
51—TinyOS51嵌入式操作系统微小内核
与中止函数about()和退出函数exit()相比,初看 起来,goto语句处理异常更可行,但是,goto语句 只能在函数内部跳转,即不能从一个函数直接跳转 到另一个函数。
为此,标准C函数库提供了setjmp()和longjmp() 函数,setjmp()函数相当于非局部标号,longjmp() 函数相当于goto的作用,从而解决了从一个函数直 接跳转到另一个函数的问题,即非局部跳转。头文 件<setjmp.h>申明了这些函数及同时所需的jmp_buf 数据类型。
如下图所示:
8
第5章 TinyOS51嵌入式操作系统 时间轮询调度为每个任务提供同份额的cpu执行
时间。由于纯粹的时间轮询调度不能满足实时性系统 要求,取而代之的是基于优先级抢占式调度扩充时间 轮询调度,即对同样优先级的任务使用时间片获得相 等的cpu分配时间,不同优先级的具有抢占权。
9
第5章 TinyOS51嵌入式操作系统
就绪状态 ;运行状态; 阻塞状态
10
第5章 TinyOS51嵌入式操作系统
就绪状态:当一个任务创立并准备运行时,内核将其放入 就绪状态。但不能运行,因为有一个更高优先级的任务在 执行,内核调度器根据优先级决定哪个任务先迁移到运行 状态,但处于就绪状态的任务不能直接迁移到阻塞状态。
运行状态:操作系统可让处于运行状态的低优先级任务暂 停运行,转而执行另一个处于就绪状态的高优先级任务, 这样正运行的任务就从运行状态迁移到了就绪状态。
SP
变化。
addr15~addr8
addr7~addr0
图 5.7
上下文信息 23
第5章 TinyOS51嵌入式操作系统
51单片机最小系统-(最新版)
单片机最小系统,或者称为最小应用系统,是指用最少的元件组成的单片机可以工作的系统.对51系列单片机来说,最小系统一般应该包括:单片机、晶振电路、复位电路.下面给出一个51单片机的最小系统电路图.说明复位电路:由电容串联电阻构成,由图并结合"电容电压不能突变"的性质,可以知道,当系统一上电,RST脚将会出现高电平,并且,这个高电平持续的时间由电路的RC值来决定.典型的5 1单片机当RST脚的高电平持续两个机器周期以上就将复位,所以,适当组合RC的取值就可以保证可靠的复位.一般教科书推荐 C 取10u,R取.当然也有其他取法的,原则就是要让RC组合可以在RST脚上产生不少于2个机周期的高电平.至于如何具体定量计算,可以参考电路分析相关书籍.晶振电路:典型的晶振取(因为可以准确地得到9600波特率和19200波特率,用于有串口通讯的场合)/12MHz(产生精确的uS级时歇,方便定时操作)单片机:一片AT89S51/52或其他51系列兼容单片机特别注意:对于31脚(EA/Vpp),当接高电平时,单片机在复位后从内部ROM的0000H开始执行;当接低电平时,复位后直接从外部ROM的0000H开始执行.这一点是初学者容易忽略的.复位电路:一、复位电路的用途单片机复位电路就好比电脑的重启部分,当电脑在使用中出现死机,按下重启按钮电脑内部的程序从头开始执行。
单片机也一样,当单片机系统在运行中,受到环境干扰出现程序跑飞的时候,按下复位按钮内部的程序自动从头开始执行。
单片机复位电路如下图:二、复位电路的工作原理在书本上有介绍,51单片机要复位只需要在第9引脚接个高电平持续2US就可以实现,那这个过程是如何实现的呢?在单片机系统中,系统上电启动的时候复位一次,当按键按下的时候系统再次复位,如果释放后再按下,系统还会复位。
所以可以通过按键的断开和闭合在运行的系统中控制其复位。
开机的时候为什么为复位在电路图中,电容的的大小是10uF,电阻的大小是10k。
RTX51Tiny实时内核理解
RTX51 Tiny 实时内核理解声明:以下来自网络整理而来并非本人作品,觉得挺容易懂所以放入博客以便后来学习者参考RTX51 Tiny中容易混淆的问题RTX51 Tiny是 Keil uVision中自带的一个小型嵌入式RTOS,具有小巧、速度快、系统开销小、使用方便等优点。
使用RTX51 Tiny能够提高系统的稳定性,优化程序的性能;而且它是为51单片机专门定制的,所以在51单片机上的运行效率比其它一些通用的RTOS性能也要好一些。
但是,由于RTX51 Tiny的相关资料和书籍比较少,大部分只是对程序自带帮助文件的简单翻译,很少进行深入探讨。
下面就RTX51 Tiny使用中经常遇到的一些问题进行探讨。
1 关于时间片的问题RTX51 Tiny使用的是无优先级时间片轮询法,每个任务使用相同大小的时间片,但是时间片是怎样确定的呢?RTX51 Tiny的配置参数(Conf_tny.a51文件中)中有INT_CLOCK和TIMESHARING两个参数。
这两个参数决定了每个任务使用时间片的大小:INT_CLOCK是时钟中断使用的周期数,也就是基本时间片;TIMESHARING是每个任务一次使用的时间片数目。
两者决定了一个任务一次使用的最大时间片。
如假设一个系统中INT_CLOCK设置为10000,即10ms,那么TIMESHARING=1时,一个任务使用的最大时间片是 10ms;TIMESHARING=2时,任务使用最大的时间片是20ms;TIMESHARING=5时,任务使用最大的时间片是50ms;当 TIMESHARING设置为0时,系统就不会进行自动任务切换了,这时需要用os_switch_task函数进行任务切换。
这部分功能是RTX51 Tiny 2.0中新增加的。
2 关于os_wait延时的问题os_wait 是RTX51 Tiny中的基本函数之一。
它的功能是将当前任务挂起来,等待一个启动信号(K_SIG)或超时信号(K_TMO)或周期信号(K_IVL)或者是它们之间的组合。
实时多任务操作系统在MCS-51单片机中的应用
) 22 R X 5 . T 一 1任 务状态
R X 5 Leabharlann y的 用 户 任 务 有 5种 状 态 , 表 1 示 。 T 一 1Tn 如 所 某 一 时 刻 用 户 任 务 处 在 某 个 状 态 , 一 定 条 件 下 , 务 状 在 任
1 引 言
文献标 识码 : A
文章编 号 :6 4 58 (0 0 0 — 10 0 17 — 7 7 2 1 )5 0 6 — 3
义:
传统 的单 片机程 序多 为单任 务 系统 .其业 务逻 辑顺 序安排 在主 函数 中 , 函数是 整个程 序 的人 1 一般 为死 主 : 3. 循 环 , 环过 程 中通 过调用 函数 未完 成相 应 的操作 . 循 而对
于 一 些 较 短 的 实 时 任 务 则 通 过 中 断 方 式 进 行 处 理 此 种 程 序 结 构 简 单 、 观 , 于 实 现 . 对 于 较 复 杂 的 应 用 此 直 易 但
vi tsn m (od t k B m o ak a evi)_a u d s n n 是任务 号 , ul 取值 为 O 1 。t k a 一 5 a nme是任 务的名称 。 s 下
运 行 状 态
2 RX 5 T 一 1简 介
R X一 1是 德 国 K i公 司 开 发 的 适 用 于 MC 一 1 T 5 el S5 单 片 机 的 实 时 多 任 务 操 作 系 统 . T 一 1 R X 5 ul R X 5 有 T 一 1F l 和 R X 1Tn 个 版 本 本 文 以 R X 5 iy为 例 介 绍 . T 5 iy两 T 一 1Tn 它
Keil_C51开发系统基本知识
Keil_C51开发系统基本知识Keil C51开发系统基本知识1. 第一节系统概述Keil C51是美国Keil Software公司出品的51系列兼容单片机C 语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。
用过汇编语言后再使用C 来开发,体会更加深刻。
Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。
另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。
在开发大型软件时更能体现高级语言的优势。
下面详细介绍Keil C51开发系统各部分功能和使用。
2. 第二节 Keil C51单片机软件开发系统的整体结构C51工具包的整体结构,如图(1)所示,其中uVision与Ishell分别是C51 for Windows和for Dos的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。
开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。
然后分别由C51及A51编译器编译生成目标文件(.OBJ)。
目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.ABS)。
ABS文件由OH51转换成标准的Hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。
图(1) C51工具包整体结构图3. 第三节 Keil C51工具包的安装1. 1. C51 for Dos在Windows下直接运行软件包中DOS\C51DOS.exe然后选择安装目录即可。
完毕后欲使系统正常工作须进行以下操作(设C:\C51为安装目录):修改Autoexec.bat,加入path=C:\C51\BinSet C51LIB=C:\C51\LIBSet C51INC=C:\C51\INC然后运行Autoexec.bat2. 2. C51 for Windows的安装及注意事项:在Windows下运行软件包中WIN\Setup.exe,最好选择安装目录与C51 for Dos相同,这样设置最简单(设安装于C:\C51目录下)。
8051单片机实时操作系统RTX51 Tiny总结
RTX51 Tiny介绍μVision是德国K eil公司开发的单片机IDE软件,最初主要用于8051系列单片机,RTX51是其自带的运行于8051系列单片机上的小型多任务实时操作系统,可用来设计具有实时性要求的多任务软件。
RTx51有2个版本:RTX51 Tiny和RTX51 Full。
RTX51 Tiny是RTX51 Full的子集。
RTX51 Tiny 自身仅占用900字节左右的程序存储空间,可以很容易地运行在没有外部扩展存储器的8051单片机系统上。
它完全集成在Keil C5l编译器中,具有运行速度快、对硬件要求不高、使用方便灵活等优点,因此越来越广泛地应用到单片机的软件开发中。
它可以在单个CPU上管理几个作业(任务),同时可以在没有扩展外部存储器的单片机系统上运行。
目前在8051系列单片机上使用多任务实时操作系统,RTX51 Tiny也就成为了首选。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ RTX51 TINY允许同时“准并行”地执行多个任务:各个任务并非持续运行,而是在预先设定的时间片(time slice)内执行。
CPU执行时间被划分为若干时间片,RTX51 TINY为每个任务分配一个时间片,在一个时间片内允许执行某个任务,然后RTX51 TINY切换到另一个就绪的任务并允许它在其规定的时间片内执行。
由于各个时间片非常短,通常只有几ms,因此各个任务看起来似乎就是被同时执行了。
基于KeilC51嵌入式系统多任务实现技术
就必须选择此启动程序 STARTUP.A51。
启动 文 件 STARTUP.A51 中 包 含 目 标 板 启 动 代 码 , 可 手 动
加入这个文件, 只要复位, 则该文件立即执行, 其功能包括:
1) 定义内部 RAM 大小、外部 RAM 大小、可重入堆栈位
2) 清除内部、外部或者以此页为单元的外部存储器
放到寄存器中, 可以被多个任务调用。但是如果局部变量太多,
Keil C51 在编 译 时仍 然 会 把 局 部 变 量 放 到 内 部 RAM 中 , 造 成
不同函数的局部变量覆盖。
在 Progect- >Option for Target- >C51 的 优 化 选 项 中 选 择 4
Register Variable。这样编译后的局部变量都尽量( 局部变量在 8
不同的任务不能调用同一个函数, 否则第二任务调用函数时会
修 改 第 一 个 任 务 调 用 此 函 数 后 的 局 部 变 量 。这 种 方 法 实 际 上 就
是把函数的局部变量变成了全局变量。
在 Keil C51 实现变量覆盖的方法如下:
在 Progect- >Option for Target- >BL51 Misc- >Overlay 里填入:
实例验证了其有效性和正确性。
关键词: 嵌入式操作系统; Keil C51; 局部变量; 全局变bstr act:Program based on Keil C51 embedded operation system, which divides whole program into multitask, can decrease the error
有一点不同, 放在内部 RAM 中 或 外部 RAM 中 的 局部 变 量 可 以 被 其 他 函 数 的 局 部 变 量 所 覆 盖 , 也 就 是 说 , 在 C51 下 的 所 有函数的局部变 量 都 放在 RAM 的 一 片共 同 的 区域 里 , 形 成局 部 函 数 变 量 区 。这 就 给 实 时 操 作 系 统 下 的 应 用 程 序 开 发 带 来 一
第9章多任务实时操作系统
RTX-51是Keil公司开发的一款应用于 是 公司开发的一款应用于80C51 公司开发的一款应用于 系列单片机的实时多任务操作系统。采用 系列单片机的实时多任务操作系统。采用RTX-51 可简化复杂的软件设计,缩短项目周期。RTX-51 可简化复杂的软件设计,缩短项目周期。 使得复杂的多任务程序设计变得简单, 使得复杂的多任务程序设计变得简单,因此在 80C51系列单片机嵌入式系统中应用很广泛。 系列单片机嵌入式系统中应用很广泛。 系列单片机嵌入式系统中应用很广泛
4.RTX-51实时多任务 . 实时多任务
RTX-51TINY允许“准并行”同时执行几个任务。各个任务并非持续运行, 允许“准并行”同时执行几个任务。各个任务并非持续运行, 允许 CPU执行时间被划分为若干时间片(time slice),每一个任务在预先定义 执行时间被划分为若干时间片( ),每一个任务在预先定义 执行时间被划分为若干时间片 ), 好的时间片内得以执行。时间到使正在执行的任务挂起, 好的时间片内得以执行。时间到使正在执行的任务挂起,并使另一个任务开 始执行。当使用RTX-51 TINY时,为每个任务建立独立的任务函数。 始执行。当使用 时 为每个任务建立独立的任务函数。 例如: 例如 void check_serial_io_task(void) _task_ 1
例如 void main (void){ while(1) /*永远重复 永远重复*/ 永远重复 { do_something(); /*执行 do_something“任务”*/ 任务” 执行 任务 } }
在这个例子里, 函数可以认为是一个单任务, 在这个例子里,do_something函数可以认为是一个单任务,由 函数可以认为是一个单任务 于仅有一个任务在执行, 于仅有一个任务在执行,所以没有必要进行多任务处理或使用多 任务操作系统。 任务操作系统。
Keil C51使用详解
Keil C51使用详解第一章Keil C51开发系统基本知识 (6)第一节系统概述 (6)第二节Keil C51单片机软件开发系统的整体结构 (6)第三节Keil C51工具包的安装 (7)1. C51 for Dos 72. C51 for Windows的安装及注意事项: (7)第四节Keil C51工具包各部分功能及使用简介 (7)1. C51与A51. 72. L51和BL51. 83. DScope51,Tscope51及Monitor51. 84. Ishell及uVision. 9第二章Keil C51软件使用详解 (10)第一节Keil C51编译器的控制指令 (10)1. 源文件控制类 (10)2. 目标文件(Object)控制类: (10)3. 列表文件(listing)控制类: (10)第二节dScope51的使用 (11)1. dScope51 for Dos 112. dScope for Windows 12第三节Monitor51及其使用 (13)1. Monitor51对硬件的要求 (13)2. Mon51的使用 (13)3. MON51的配置 (13)4. 串口连接图: (13)5. MON51命令及使用 (14)第四节集成开发环境(IDE)的使用 (14)1. Ishell for Dos的使用 (14)2. uVision for windows的使用 (15)第三章Keil C51 vs 标准C.. 15第一节Keil C51扩展关键字 (15)第二节内存区域(Memory Areas): (16)1. Pragram Area: (16)2. Internal Data Memory: 163. External Data Memory. 164. Speciac Function Register Memory. 16第三节存储模式 (16)1. Small模式 (16)2. Compact模式 (17)3. large模式 (17)第四节存储类型声明 (17)第五节变量或数据类型 (17)第六节位变量与声明 (17)1. bit型变量 (17)2. 可位寻址区说明20H-2FH.. 18第七节Keil C51指针 (18)1. 一般指针 (18)2. 存储器指针 (18)3. 指针转换 (18)第八节Keil C51函数 (19)1. 中断函数声明: (19)2. 通用存储工作区 (19)3. 选通用存储工作区由using x声明,见上例。
rtx51 tiny原理
rtx51 tiny原理RTX51 Tiny是一款基于RTX51内核的微型嵌入式操作系统。
本文将介绍RTX51 Tiny的原理及其应用。
一、RTX51 Tiny的原理RTX51 Tiny是由Keil公司开发的一款嵌入式实时操作系统。
它的设计目标是在51系列单片机上提供简单、灵活、高效的多任务管理和资源调度功能。
RTX51 Tiny使用了一种基于优先级的抢占式调度算法,能够实现多个任务之间的快速切换,从而提高系统的响应速度和并发处理能力。
RTX51 Tiny的核心是一个可重入的内核,它提供了任务管理、时间管理、资源管理和通信机制等基本功能。
任务管理器负责任务的创建、删除和切换,时间管理器实现了系统时钟的管理和定时器的功能,资源管理器用于管理共享资源的访问,通信机制则提供了任务间的消息传递和事件通知功能。
RTX51 Tiny的任务是用户定义的函数,可以是独立的任务或者中断服务函数。
每个任务都有一个优先级,优先级高的任务会优先执行。
当系统启动时,RTX51 Tiny会自动创建一个空闲任务,它的优先级最低,用于处理系统空闲时的任务。
RTX51 Tiny采用了一种事件驱动的方式进行任务调度。
当一个任务完成了它的工作或者等待某个事件发生时,它会主动让出CPU,将控制权交给调度器。
调度器会从就绪队列中选择优先级最高的任务执行,直到它完成了工作或者时间片用完。
RTX51 Tiny还提供了一些常用的服务函数,如延时函数、信号量函数、邮箱函数等,方便用户进行任务的同步与通信。
用户可以通过这些服务函数来实现任务间的协作和数据交换。
二、RTX51 Tiny的应用RTX51 Tiny广泛应用于各种嵌入式系统中,特别是对实时性要求较高的应用场景。
以下是一些常见的应用领域:1. 工业自动化:RTX51 Tiny可以用于控制系统中的任务调度和数据处理,实现复杂的自动控制算法和实时监控功能。
2. 智能家居:RTX51 Tiny可以用于家庭自动化系统中的任务管理和设备控制,实现智能家居的各种功能,如安防、照明和能源管理等。
第3章 MCS-51单片机指令系统
(1)内部数据存储器的低128个字节单元 (00H~7FH)。例: MOV A, 40H ,表示把内 部RAM 40H单元的内容传送给A。 假设40H单元中的内容为2BH,结果是将直接地 址40H单元中的数据2BH传送到累加器A中。
第三章 MCS-51单片机指令系统
3.2.2 直接寻址
(2)特殊功能寄存器。 特殊功能寄存器只能用直接寻址方式进行访问。 对于特殊功能寄存器,在助记符指令中可以直接 用符号来代替地址。例: MOV A, P0 ,表示把 P0口(地址为80H)的内容传送给A。
3.3.1 内部数据存储器传送指令
1.立即数传送指令
MOV A,#data ;A←data MOV Rn,#data ;Rn←data,n=0~7 MOV direct,#data ;direct←data MOV @Ri,#data ;(Ri)←data,i=0,1 MOV DPTR,#data16 ;DPTR←data16 前四条指令将8位立即数传送到指定的存储单元中。 最后一条指令将16位立即数传送到数据指针 DPTR中,其中高8位送入DPH,低8位送入DPL。
(3)三字节指令 编码格式为:
例如数据传送指令MOV 20H,#3AH的编码格式为:
3.2 寻址方式
第三章 MCS-51单片机指令系统
所谓寻址,就是寻找操作数的真正地址,寻址方 式,就是指寻找操作数地址的方式。
在用汇编语言编程时,数据的存放、传送、运算 都要通过指令来完成。 编程者必须自始至终都要 十分清楚操作数的位置, 以及如何将它们传送到适 当的寄存器去参与运算。每一种计算机都具有多 种寻址方式。寻址方式的多少是反映指令系统优 劣的主要指标之一。
2. 汇编语言
汇编语言就是用助记符表示的指令,汇编语言与 机器语言一一对应。用汇编语言编写程序,每条 指令的意义一目了然,给程序的编写、阅读和修 改带来很大方便。而且用汇编语言编写的程序占 用内存少,执行速度快,尤其适用于实时应用场 合的程序设计。因此,在单片机应用系统中主要 是用汇编语言来编写程序。 汇编语言的缺点:缺乏通用性,程序不易移植, 是一种面向机器的低级语言。使用汇编语言编写 程序时,必须熟悉机器的指令系统、寻址方式、 寄存器的设置和使用方法。每种计算机系统都有 它自己的汇编语言。不同计算机的汇编语言之间 不能通用。
《单片机》教学课件51单片机的指令系统
MOV Rn ,direct
;(direct)→Rn ,n =0~7
MOV Rn ,#data
;#data→Rn ,n =0~7
把源操作数送入当前寄存器区的R0~R7中的某一寄存器。
3.以直接地址direct为目的操作数的指令
MOV direct,A
; (A)→direct
MOV direct,Rn
寻址空间内快速地找到指定的地址单元。 下面介绍指令系统7种寻址方式。
5
1.寄存器寻址方式
指令中的操作数为某一寄存器的内容。
例如:MOV A,Rn
;(Rn)→A,n =0~7
把Rn中的源操作数送入到累加器A中。由于指令指定了从寄存 器Rn中取得源操作数,所以称为寄存器寻址方式。
本寻址方式的寻址范围:
4.以寄存器间接地址为目的操作数的指令
MOV @Ri,A
;(A)→((Ri)), i=0,1
MOV @Ri,direct ;(direct)→((Ri)),i=0,1
MOV @Ri,#data ;#data→((Ri)), i=0,1
功能是把源操作数内容送入R0或R1指定的存储单元中。
5.16位数传送指令
内部RAM的00H~7FH共128个单元。 6.堆栈操作指令 内部RAM中设定一个后进先出(LIFO,Last In First Out)
的区域,称为堆栈。在特殊功能寄存器中有一个堆栈指针 SP,指示堆栈的栈顶位置。堆栈操作有进栈和出栈两种, 因此,在指令系统中相应有两条堆栈操作指令。
22
(1)进栈指令 PUSH direct
(1)4组通用工作寄存区共32个工作寄存器。但只对当前工 作寄存器区的8个工作寄存器寻址,指令中的寄存器名称只 能是R0~R7。
51单片机简单的多任务调度例子
51单⽚机简单的多任务调度例⼦看⼤家都在学操作系统,我也想学学。
所以想⽤51写⼀个来玩玩,发现⽐较郁闷。
弄了⼏下,不想再弄了,51弄这个没啥意思。
我⽤的89S52,除了速度慢,RAM资源太少之外,其它都还过得去。
弄了⼀点代码出来,放在那也没啥⽤,不如拿上来给新⼿看看,⼀个任务调度的雏形是什么样⼦的~~~~~~~~~这些代码没有经过优化,我只求实现任务切换的功能。
利⽤定时器2产⽣10mS的定时中断作为时钟节拍,任务切换时保存⼯作寄存器等操作嵌⼊了汇编指令,因此Task_Switch.C⽂件要做相应的设置才能编译通过。
受硬件资源和编译器的限制,有很多⽆奈。
程序只好这样写了,不管怎么说,到底是能调度起来了。
注:这⾥是⽼版本,后⾯⼜改动的新版本。
/*******************************************************本程序只供学习使⽤,未经作者允许,不能⽤于其它任何⽤途AT89S52 MCU 使⽤24M晶振时钟节拍设置为10mSmain.c fileCreated by Computer-lov.Date: 2005.10.27Copyright(C) Computer-lov 2005-2015All rigths reserved******************************************************/#include <at89x52.h>#include "task_switch.h"#include "MAIN.H"//灯#define LED1 P1_7#define LED2 P1_6#define LED3 P1_5#define LED4 P1_4#define LED5 P0_1#define LED6 P3_7#define ON_LED1() LED1=0#define OFF_LED1() LED1=1#define ON_LED2() LED2=0#define OFF_LED2() LED2=1#define ON_LED3() LED3=0#define OFF_LED3() LED3=1#define ON_LED4() LED4=0#define OFF_LED4() LED4=1#define ON_LED5() LED5=0#define OFF_LED5() LED5=1#define ON_LED6() LED6=0#define OFF_LED6() LED6=1//按钮#define KEY1 P1_0#define KEY2 P1_1#define KEY3 P1_2#define KEY4 P1_3//OS运⾏标志unsigned char OS_running;//堆栈申请unsigned char idata Stack[MAX_TASK][S_DEPTH];//运⾏时间unsigned int Running_Time;//程序控制块PCB pcb[MAX_TASK];//当前运⾏任务的ID号unsigned char Current_ID;/调⽤该函数使任务延时t个时钟节拍/ 输⼊参数:0<t<256 /// ⼀个时钟节拍为10mS ///void OS_Delay(unsigned char t){EA=0; //关中pcb[Current_ID].Suspend=1; //任务挂起pcb[Current_ID].Delay=t; //设置延迟节拍数EA=1; //开中task_switch(); //任务切换}///挂起任务/*void OS_Suspend(void){EA=0;pcb[Current_ID].Suspend=1; //任务挂起EA=1;task_switch(); //任务切换}*//创建⼀个任务///函数⼊⼝:Task_ID 分配给任务的唯⼀ID号 //// Task_Priority 任务优先级 //// Task_p 任务⼊⼝地址/// Stack_p 任务堆栈栈低地址 ///void Task_Create(unsigned char Task_ID,unsigned char Task_Priority,unsigned int Task_p,unsigned char Stack_p) {unsigned char i;for(i=0;i<S_DEPTH;i++){((unsigned char idata *)Stack_p)[i]=0; //初始化清空堆栈}((unsigned char idata *)Stack_p)[0]=Task_p; //将任务⼊⼝地址保存在堆栈((unsigned char idata *)Stack_p)[1]=Task_p>>8;pcb[Task_ID].Task_SP=Stack_p+Num_PUSH_bytes+1; //设置好堆栈指针pcb[Task_ID].Priority=Task_Priority; //设置任务优先级pcb[Task_ID].Suspend=0; //任务初始不挂起pcb[Task_ID].Delay=0; //任务初始不延时}//空闲任务,优先级最低///⼆个LED不停的闪烁 //void task_idle(void){static unsigned long int i; //使⽤static申明局部变量,避免临时变量使⽤相同地址while(1){ON_LED1(); //LED1亮for(i=0;i<0x2000;i++) //延迟{}OFF_LED1(); //LED1关for(i=0;i<0x2000;i++){ON_LED6(); //LED6闪烁很快,看起来是⼀直亮的OFF_LED6();}}}///任务1 检测按钮1 并控制LED2亮灭//void task_1(void){// static unsigned int j;while(1){ON_LED2();while(KEY1)OS_Delay(6); //等待KEY1按键按下while(!KEY1)OS_Delay(6); //等待KEY1释放OFF_LED2();while(KEY1)OS_Delay(6);while(!KEY1)OS_Delay(6);}}/任务2 检测按钮2 并控制LED3亮灭//void task_2(void){// static unsigned int j;while(1){ON_LED3();while(KEY2)OS_Delay(5);while(!KEY2)OS_Delay(5);OFF_LED3();while(KEY2)OS_Delay(5);while(!KEY2)OS_Delay(5);}}/任务3 检测按钮3 并控制LED4亮灭//void task_3(void){// static unsigned int j;while(1){ON_LED4();while(KEY3)OS_Delay(5);while(!KEY3)OS_Delay(5);OFF_LED4();while(KEY3)OS_Delay(5);while(!KEY3)OS_Delay(5);}}/任务4 控制LED5每秒闪⼀次//void task_4(void){// static unsigned int j;while(1){ON_LED5();OS_Delay(100); //LED5每隔1S闪⼀次OFF_LED5();OS_Delay(100);}}///主函数//void main(void){EA=0; //关中ET2=1; //定时器2开中断T2CON=0x00; //定时器⾃动重装模式T2MOD=0x00; //如果提⽰这⾥编译通不过,可将本⾏删除;或⾃⼰将定义添上 //因为keil⾃带的at89x52.h中没有T2MOD的定义RCAP2H=0xB1;RCAP2L=0xE0; //定时时间为10msTask_Create(0,5,(unsigned int)(void *)(&task_idle),(unsigned char)Stack[0]); //任务0初始化Task_Create(1,4,(unsigned int)(void *)(&task_1),(unsigned char)Stack[1]); //任务1初始化Task_Create(2,3,(unsigned int)(void *)(&task_2),(unsigned char)Stack[2]); //任务2初始化Task_Create(3,2,(unsigned int)(void *)(&task_3),(unsigned char)Stack[3]); //任务3初始化Task_Create(4,1,(unsigned int)(void *)(&task_4),(unsigned char)Stack[4]); //任务4初始化OS_running=0; //任务未开始运⾏Current_ID=MAX_TASK-1; //当前任务为最后⼀个任务pcb[Current_ID].Task_SP-=Num_PUSH_bytes; //调整任务堆栈指针,因为这时任务还未开始调度 //第⼀次进⼊中断时,会压栈。
基于RTX51实时操作系统的交通灯控制系统的设计
基于RTX51实时操作系统的交通灯控制系统的设计O 引言一个高效的单片机智能控制系统,不仅要求系统能够同时执行多个任务,对每个任务作出实时响应,而且要求系统能够及时响应随机发生的外部事件,并对其作出快速处理。
对于这样的系统应用,采用实时操作系统RTOS(Real-time-Operating System)作为系统软件设计平台是一个良好的选择,它可以灵活地安排系统资源,简化复杂的软件设计,加快软件的开发效率,大大缩短了项目的开发周期。
道路交通灯是最常见的一种多任务控制系统,本文以此为倒,详细阐述了51嵌入式实时操作系统RTX51开发软件的方法和步骤。
1 系统硬件电路设计交通信号灯控制系统主要实现以下三个功能:(1)信号灯指示,即完成十字路口红、黄、绿交通信号灯的控制。
(2)时间显示,各个信号灯持续的时间显示。
(3)紧急情况响应,当系统出现故障或者有紧急情况是能够及时响应。
根据以上功能要求,完整的交通灯控制系统硬件电路如图l所示,主要由三部分模块组成:单片机最小系统模块、红绿色显示模块、倒计时显示模块、紧急中断模块。
(1)单片机最小系统:包括时钟电路和开关复位电路。
单片机选用具有成本低廉且具有串口ISP下载功能的STC89C52单片机,晶振选用12 MHz。
(2)信号灯指示电路:东西南北四个方向分别有红、绿、黄三个状态指示的灯,其中南北方向的红绿黄发光二极管分别连接到P1.O~P1.2,东西方向的红绿黄发光二极管分别连接到P1.3~P1.5。
(3)倒计时显示:每个交通灯状态倒计时时间由两位共阳数码管显示,八位段码分别连接到P0.0~P2.7,两位位选通过反向器分别连接到P3.4、P3.5。
(4)紧急中断:开关K1为紧急中断开关,当有特殊情况时按下K1,K1连接到单片机P3.2外部中断O输入端。
2 基于RTX51的软件设计近年来,利用嵌入式实时操作系统来开发嵌入式系统的软件已是大势所趋。
这是因为传统的这类设计中,大多采用了中断结合单任务的顺序机制进行,这种设计方法虽然比较直观,但是也带来了诸如稳定性差、不便于调试等问题。
51单片机最小系统原理
51单片机最小系统原理
51单片机最小系统是指由51单片机芯片、时钟电路、复位电路和电
源电路等组成的最基本的硬件系统。
它是进行51单片机软件开发和运行
的基础,对于学习和应用51单片机技术来说非常重要。
下面将详细介绍
51单片机最小系统的原理。
1.51单片机芯片
51单片机是由英特尔公司推出的一种8位微控制器,是指基于哈佛
结构、具有复杂存储器结构和指令集的通用型单片机。
51单片机具有很
强的通用性,广泛应用于各种嵌入式系统和控制系统中。
常用的51单片
机芯片有AT89C51、AT89S52等。
2.时钟电路
时钟电路是指为51单片机提供稳定的时钟信号的电路。
由于51单片
机是以时序为基础进行工作的,因此时钟信号对于单片机的运行至关重要。
一般来说,时钟电路采用晶体振荡器作为时钟源,晶体振荡器的频率一般
为11.0592MHz。
时钟电路还包括电容和电阻等元件,用于保持晶体振荡
器的稳定性。
3.复位电路
复位电路是指对51单片机进行复位操作的电路。
当51单片机上电或
按下复位按钮时,复位电路会向单片机的复位引脚发送一个复位信号,使
单片机回到初始状态。
复位电路一般由电源滤波电路、复位电容和复位电
阻等元件组成。
4.电源电路
电源电路是指为51单片机提供稳定的电源电压的电路。
由于51单片机对电源电压的要求较高,一般在3.3V至5V之间,因此电源电路需要将输入的电源电压进行适当的处理,使其保持在合适的范围内。
电源电路一般由稳压电路、电容和电阻等元件组成。
51单片机操作系统
嵌入式实时操作系统,可以充分的利用单片机的资源,提高CPU使用效率。
操作系统最主要就是实现任务的调度、管理。
同时对于实时操作系统来说响应时间很重要。
操作系统编写最主要就是用到了堆栈SP于RET指令。
这两个东西怎么用呢?其实在我们每次调用函数的时候都会自动将函数的断点地址(执行函数调用时的PC)压入到SP中,而从函数中返回时其实是利用RET指令将断点弹回到PC(程序指针)中。
所以利用堆栈和RET指令就可以实现简单的任务的切换。
这么说肯定挺模糊的,接下来一步一步解释。
首先,要知道任务是一个死循环。
如下面所示,可以看出两个任务都是死循环,按照以往的情况,程序是跳不出来的,只能在while(1)中无限执行。
那怎么才可以实现从task0到task1的切换呢?其实如果我们能够改变PC的值是不是就可以改变程序执行顺序了。
任务的调度切换就是利用改变PC的值来改变程序执行顺序的。
其次,就是要解决如何实现PC值的正确变换问题,如何让PC指向我们需要执行的地方。
这就是通过堆栈来实现的。
我们可以为每个任务建立一个堆栈用于保存任务PC的值,以及任务寄存器的值。
这样每次进行任务切换时只要从相应的堆栈中取出PC和寄存器的值就可以实现任务的调度了。
在程序中于寄存器相关的程序使用在C语言中嵌入汇编来实现的。
因为直接使用C语言不能直接控制寄存器。
在本程序中,入栈和出栈是通过汇编实现的。
一个简单的操作系统如下所示,只能实现简单的任务调度,延时。
必须注意,空闲任务(Idle)必须建立,否则会出错。
#include<reg52.h>#define OSEnterCritical() EA=0#define OSExitCritical() EA=1#define EnterInt() EA=0;#define uint unsigned short int#define uchar unsigned char#define MAX_Tasks 3#define False 0#define Ture 1#define MaxPrio 2#define IdlePrio MaxPrio#define OS_Task_Create_Error 1#define OS_Delet_Task_Error 2#define OS_Delet_Task_Not_Exit 3#define OS_Resume_Idle_Error 4#define OS_Resume_Task_Error 5typedef struct{uchar OSStackTop; //SPuchar OSSuspend;uchar OSTCBDly; //delay time}OSTCB;uchar code OSMapTbl[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};OSTCB OSTCBTbl[MAX_Tasks];volatile uchar OSRdyTbl;volatile uchar OSIntNesting; //用于中断锁死volatile uchar OSSchNesting; //任务切换上锁volatile uchar OSRuning=False;volatile uchar OSStartStack[MAX_Tasks][20];volatile uchar OSPoint[MAX_Tasks][2];volatile uchar OSPrioCur;//volatile uchar OSTaskPend;OSInit(){// uchar i;EA=0;ET0=1;TMOD=0x01;TH0=0xB1;TL0=0xE0;OSRdyTbl=0;OSIntNesting=0;OSSchNesting=0;}//PCL,PCH,ACC ,B,DPL,DPH,PSW,R0-R7uchar *OSStackInit(uint task,uchar *ptr,uchar OSPrio){uchar* stk;stk=ptr;OSPoint[OSPrio][0]=task;OSPoint[OSPrio][1]=task>>8;*(stk++)= OSPoint[OSPrio][0];*(stk++)= OSPoint[OSPrio][1];*(stk++)= 0x00; //ACC*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk++)= 0x00;*(stk) = 0x00;return stk;}uchar OSTaskCreate(uint task,uchar *ptr,uchar OSPrio){uchar* psp;OSEnterCritical();if(OSPrio<=MaxPrio) //创建的任务优先级有效{psp=OSStackInit(task,ptr,OSPrio); //初始化堆栈OSRdyTbl|=OSMapTbl[OSPrio];OSTCBTbl[OSPrio].OSStackTop=psp;OSTCBTbl[OSPrio].OSSuspend=0;OSTCBTbl[OSPrio].OSTCBDly=0;}else{OSExitCritical();return OS_Task_Create_Error;}OSExitCritical();}/*===================================================== 任务调度函数入口参数:无函数说明:进入函数后,先进行堆栈保护,然后查找最高优先级任务运行======================================================*/void OSSchedule(){uchar i;OSEnterCritical();#pragma asmPUSH ACCPUSH BPUSH DPHPUSH DPLPUSH PSWPUSH 0PUSH 7PUSH 1PUSH 2PUSH 3PUSH 4PUSH 5PUSH 6#pragma endasmOSTCBTbl[OSPrioCur].OSStackTop=SP;if(OSRdyTbl) //如果就续表中有任务{for(i=0; i<MAX_Tasks;i++){if((OSRdyTbl & OSMapTbl[i])&&(!OSTCBTbl[i].OSSuspend)) //任务优先级最高且未被挂起{OSPrioCur=i;break;}}}SP=OSTCBTbl[OSPrioCur].OSStackTop;#pragma asmPOP 6;POP 5;POP 4;POP 3;POP 2;POP 1;POP 7;POP 0;POP PSW;POP DPL;POP DPH;POP B;POP ACC;#pragma endasmOSExitCritical();}void OSStart(){TR0=1;EA=1;while(1);}/*=========================================================延时若干个系统时钟入口参数:延时系统时间个数===========================================================*/ void OSDelay(uchar time){if(time==0)//延时为0,返回return;OSEnterCritical();OSTCBTbl[OSPrioCur].OSTCBDly=time;OSTCBTbl[OSPrioCur].OSSuspend=1;OSExitCritical();OSSchedule();}/*=========================================================任务删除函数入口参数:为被删除任务优先级函数说明:将任务从就绪表中删除===========================================================*/ uchar OSTaskDelet(uchar priority){OSEnterCritical();if(priority>=IdlePrio){OSExitCritical();return OS_Delet_Task_Error;}if(!(OSRdyTbl & OSMapTbl[priority])){OSExitCritical();return OS_Delet_Task_Not_Exit;}OSRdyTbl &= ~(OSMapTbl[priority]);OSExitCritical();if(priority<OSPrioCur){OSSchedule();}}/*=========================================================任务恢复函数入口参数:恢的任务优先级函数说明:恢复被OSTaskDelet()删除的任务===========================================================*/ uchar OSTaskResume(uchar priority){OSEnterCritical();if(priority==IdlePrio)//恢复的任务不能为空闲任务,为空闲任务返回错误标志{OSExitCritical();return OS_Resume_Idle_Error;}if((!(OSRdyTbl & OSMapTbl[priority])) && (priority>=0)){OSRdyTbl |= (OSMapTbl[priority]);}else //返回的任务不存在,返回错误标志{OSExitCritical();return OS_Resume_Task_Error;}OSExitCritical();if(priority<OSPrioCur){OSSchedule();}}/*=============================================================== 定时器0用于产生系统时钟,这里每过20ms中断一次。
单片机实时多任务操作系统分析
乡堪 V A L 鑫毅
单片机 实时多任 务操作 系统分析
王甲深 冯立杰
(西安武替工程学院 陕西 西安 710086)
〔 要]简要介绍美国 e l 公司开发的 摘 K i 应用于 1 系列单片机的实时多 就55 任务操作系统R 1 的墓本情况和使用方法: 分析这个内核的任务管理和内 Tx5 存管理的 运
行机制及其对硬件的要求,给出其在软件设计中应注意的问题。
[关键词] 单片机
C l RX s T 51
文章编号: 1671一7597 ( 20 8) 0 10 13一 0 4 0 01
中图分类号: TGS 文献标识码: A 一、橄述
一般说来,单片机的应用系统中都需要同时执行很多任务,我们可以
利用实时操作系统来灵活地安排系统资源。R l 是美国K l 公司开发的一 Txs ei 种小型的面向袱55 系列单片机的实时多任务操作系统,它可以工作于 1 805 单片机以及派生家族中,简化了复杂的软件设计,缩短了项目 1 周期. 二、RTXS, 介绍 T 5 R X 有2个模式: RTX 完全模式和最小模式. R 5 最小模式版是 1 1 5 TX 1 R 5 完全版的一个子集,可以很容易地运行在805 系统上,而不需要外部 TX 1 1 R M(D TA) . R 5 完全模式有4个任务优先级,可以和中断函数并行处 A A x TX 1 理,各个任务之间通过使用 “ 邮箱”系统来进行信号和消息的传递,可以 动态地申 请和释放内存: 同时,可以强制一个任务停止执行,等待一个中 断,或者是其它中断传来的信号量或者消息。
( 一) RTX51任务
R 5 包括2类任务: 快速任务和标准任务。快速任务有很快的响应速 TX 1 度,每个快速任务使用805 一个单独的寄存器组,井且有自己的堆栈区 1 域。R 5 支持最大同时有3个快速任务。标准任务需要多一点的时间来进 TX 1 行任务切换,因此使用的内部R M A 相对快速任务要少,所有的标准任务共用 1个寄存器组和堆栈。当任务切换的时候. 当前任务的寄存器状态和堆栈内 容转移到外部存储器中。R 5 支持最大1 个标准任务. TX 1 6 ( 二) R 51任务状态 TX
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
/*
51单片机最简单的多任务操作系统
其实只有个任务调度切换,把说它是OS有点牵强,但它对于一些简单的开发应用来说,简单也许就是最好的.尽情的扩展它吧.别忘了把你的成果分享给大家.
这是一个最简单的OS,一切以运行效率为重,经测试,切换一次任务仅个机器周期,也就是在标准(工作于M晶振)上uS.
而为速度作出的牺牲是,为了给每个任务都分配一个私有堆栈,而占用了较多的内存.作为补偿,多任务更容易安排程序逻辑,从而可以节省一些用于控制的变量.
任务槽越多,占用内存越多,但任务也越好安排,以实际需求合理安排任务数目.一般来说,4个已足够.况且可以拿一个槽出来作为活动槽,换入换入一些临时任务.
task_load(函数名,任务槽号)
装载任务
os_start(任务槽号)
启动任务表.参数必须指向一个装载了的任务,否则系统会崩溃.
task_switch()
切换到其它任务
.编写任务函数注意事项:
KEIL C编译器是假定用户使用单任务环境,所以在变量的使用上都未对多任务进行处理,编写任务时应注意变量覆盖和代码重入问题.
1.覆盖:编译器为了节省内存,会给两个没用调用关系的函数分配同一内存地址作为变量空间.这在单任务下是很合理的,但对于多任务来说,两个进程会互相干扰对方.
解决的方法是:凡作用域内会跨越task_switch()的变量,都使用static前辍,保证其地址空间分配时的唯一性.
2.重入:重入并不是多任务下独有的问题,在单任务时,函数递归同样会导致重入,即,一个函数的不同实例(或者叫作"复本")之间的变量覆盖问题.
解决的方法是:使用reentrant函数后辍(例如:void function1() reentrant{...}).当然,根本的办法还是避免重入,因为重入会带来巨大的目标代码量,并极大降低运行效率.
3.额外提醒一句,在本例中,任务函数必须为一个死循环.退出函数会导致系统崩溃.
.任务函数如果是用汇编写成或内嵌汇编,切换任务时应该注意什么问题?
由于KEIL C编译器在处理函数调用时的约定规则为"子函数有可能修改任务寄存器",因此编译器在调用前已释放所有寄存器,子函数无需考虑保护任何寄存器.
这对于写惯汇编的人来说有点不习惯: 汇编习惯于在子程序中保护寄存器.
请注意一条原则:凡是需要跨越task_switch()的寄存器,全部需要保护(例如入栈).根本解决办法还是,不要让寄存器跨越任务切换函数task_switch()
事实上这里要补充一下,正如前所说,由于编译器存在变量地址覆盖优化,因此凡是非静态变量都不得跨越
task_switch().
任务函数的书写:
void 函数名(void){//任务函数必须定义为无参数型
while(1){//任务函数不得返回,必须为死循环
//....这里写任务处理代码
task_switch();//每执行一段时间任务,就释放CPU一下,让别的任务有机会运行.
}
}
任务装载:
task_load(函数名,任务槽号)
装载函数的动作可发生在任意时候,但通常是在main()中.要注意的是,在本例中由于没考虑任务换出,
所以在执行os_start()前必须将所有任务槽装满.之后可以随意更换任务槽中的任务.
启动任务调度器:
os_start(任务槽号)
调用该宏后,将从参数指定的任务槽开始执行任务调度.本例为每切换一次任务需额外开销个机器周期,用于迁移堆栈.
*/
#include<reg51.h>
/*============================以下为任务管理器代码============================*/
#define MAX_TASKS 3//任务槽个数.在本例中并未考虑任务换入换出,所以实际运行的任务有多少个,就定义多少个任务槽,不可多定义或少定义
//任务的栈指针
unsigned char idata task_sp[MAX_TASKS];
#define MAX_TASK_DEP 12 //最大栈深.最低不得少于个,保守值为.
//预估方法:以为基数,每增加一层函数调用,加字节.如果其间可能发生中断,则还要再加上中断需要的栈深.
//减小栈深的方法:1.尽量少嵌套子程序2.调子程序前关中断.
unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈.
unsigned char task_id;//当前活动任务号
//任务切换函数(任务调度器)
void task_switch(){
task_sp[task_id] = SP;
if(++task_id == MAX_TASKS)
task_id = 0;
SP = task_sp[task_id];
}
//任务装入函数.将指定的函数(参数)装入指定(参数)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但
系统本身不会发生错误.
void task_load(unsigned int fn, unsigned char tid){
task_sp[tid] = task_stack[tid] + 1;
task_stack[tid][0] = (unsigned int)fn & 0xff;
task_stack[tid][1] = (unsigned int)fn >> 8;
}
//从指定的任务开始运行任务调度.调用该宏后,将永不返回.
#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}
/*============================以下为测试代码============================*/
unsigned char stra[3], strb[3];//用于内存块复制测试的数组.
//测试任务:复制内存块.每复制一个字节释放CPU一次
void task1(){
//每复制一个字节释放CPU一次,控制循环的变量必须考虑覆盖
static unsigned char i;//如果将这个变量前的static去掉,会发生什么事?
i = 0;
while(1){//任务必须为死循环,不得退出函数,否则系统会崩溃
stra[i] = strb[i];
if(++i == sizeof(stra))
i = 0;
//变量i在这里跨越了task_switch(),因此它必须定义为静态(static),否则它将会被其它进程修改,因为在另一个进程里也会用到该变量所占用的地址.
task_switch();//释放CPU一会儿,让其它进程有机会运行.如果去掉该行,则别的进程永远不会被调用到
}
}
//测试任务:复制内存块.每复制一个字节释放CPU一次.
void task2(){
//每复制一个字节释放CPU一次,控制循环的变量必须考虑覆盖
static unsigned char i;//如果将这个变量前的static去掉,将会发生覆盖问题.task1()和task2()会被编译器分配到同一个内存地址上,当两个任务同时运行时,i的值就会被两个任务改来改去
i = 0;
while(1){//任务必须为死循环,不得退出函数,否则系统会崩溃
stra[i] = strb[i];
if(++i == sizeof(stra))
i = 0;
//变量i在这里跨越了task_switch(),因此它必须定义为静态(static),否则它将会被其它进程修改,因为在另一个进程里也会用到该变量所占用的地址.
task_switch();//释放CPU一会儿,让其它进程有机会运行.如果去掉该行,则别的进程永远不会被调
用到
}
}
//测试任务:复制内存块.复制完所有字节后释放CPU一次.
void task3(){
//复制全部字节后才释放CPU,控制循环的变量不须考虑覆盖
unsigned char i;//这个变量前不需要加static,因为在它的作用域内并没有释放过CPU
while(1){//任务必须为死循环,不得退出函数,否则系统会崩溃
i = sizeof(stra);
do{
stra[i-1] = strb[i-1];
}while(--i);
//变量i在这里已完成它的使命,所以无需定义为静态.你甚至可以定义为寄存器型(regiter)
task_switch();//释放CPU一会儿,让其它进程有机会运行.如果去掉该行,则别的进程永远不会被调用到
}
}
void main(){
//在这个示例里并没有考虑任务的换入换出,所以任务槽必须全部用完,否则系统会崩溃.
//这里装载了三个任务,因此在定义MAX_TASKS时也必须定义为
task_load(task1, 0);//将task1函数装入号槽
task_load(task2, 1);//将task2函数装入号槽
task_load(task3, 2);//将task3函数装入号槽
os_start(0);//启动任务调度,并从号槽开始运行.参数改为,则首先运行号槽.
//调用该宏后,程序流将永不再返回main(),也就是说,该语句行之后的所有语句都不被执行到.
}。