一、“玫瑰一号”虚拟开发板 (1)学习单片机RTOS的目的是为了在实际中应用它,而实际应用场景千差万别,因此就需要先抽象出一个开发模型来进行学习和研究,通常这个开发模型就被称为“虚拟开发板”。 (2)AI8051U单片机已经具有强大的性能,其典型应用场景是作为一个“工业控制主机”,通常这个工控机上首先要有若干个红红绿绿的指示灯来显示系统运行的状态,比如一闪一闪的生命灯,比如系统总电源开关指示灯和各个设备启停指示灯。其次这个工控机上要有一个数码管实时地显示系统的运行参数,最简单地是显示运行时间。最后这个工控机还应该通过网络与上位机通讯,向上位机传送各种运行状态信息和接收各种命令。 以下将这种最小工控机模型称为“玫瑰派一号”虚拟开发板。 (3)“玫瑰派一号”虚拟开发板具有8个LED灯、一个8位数码管和连接在P30P31上的供烧录和通讯用的串口UART1。 AI8051U实验箱是一款功能强大的学习和研究设备,上面正好有这三个独立的部分,因此正好可以抽象出一个“玫瑰派一号”虚拟开发板 下图是AI8051U实验箱实验箱的实物照片: 图中上部的8位数码管,中间一排的LED灯。
二、RTOS的每个实时任务相当于一个虚拟单片机 (4)金山151-RTOS具有5个实时任务,空任务的框架程序如下: 其中任务0~任务3为空任务,任务4是留守任务。 (5)从上面框架中可以看出,金山151-RTOS的每一个实时任务都是独立的,每一个实时任务都是同样的结构:从函数开始到“Main_Loop”标号之间是任务的初始化部分,比如第57行程序,然后从“Main_Loop”标号到无条件转移语句“goto Main_Loop;”之间是一个无限循环部分。这也说明每一个实时任务都是不允许退出的。 有人问笔者为什么不使用“while(1){...}”这种方式来实现无限循环结构,这是因为每个RTOS的实时任务函数或者裸机编程的“main()”函数都是不能退出的,退出可能产生系统崩溃的严重后果,采用上面的“goto”语句方法来实现无限循环,可以避免使用“break”语句不正确导致的意外退出主循环,进而退出任务函数的后果。 (6)对比实时任务的函数结构与单片机裸机编程的“main()”函数结构,两者都是一样的,因此我们可以把每个独立的实时任务抽象成为一个“虚拟单片机”,在RTOS任务调度系统的协调下多个虚拟单片机同时运行。比如对于高运算能力的AI8051U系列单片机,采用金山151-RTOS编程,就好像5台单片机同时运行,可以充分发挥AI8051U单片机的速度和内存优势。
三、RTOS的实时任务的独立编程方法 (7)对于一个RTOS系统,如果其中某一些设备是独立控制的,那么就可以把这些独立设备看做是一个单独的虚拟单片机,用一个实时任务来进行控制,对这个实时任务的编程就像裸机编程中的“main()”函数一样可以独立地编程。 (8)本文范例将AI8051U实验箱的右面4个LED(对应P04、P05、P06、P07)作为一个虚拟单片机用任务0来进行控制,程序见下图: 这是一个跑马灯程序。
(9)本文范例将AI8051U实验箱的左面4个LED(对应P00、P01、P02、P03)作为一个虚拟单片机用任务1来进行控制,程序见下图: 这是一个花样灯程序。 (10)任务0和任务1的编程方法称为“直写法”:将实现功能的程序按照节拍依次写出来,这也是最基本的编程方法。
(11)本文范例将AI8051U实验箱的左面4个数码管(第0、1、2、3号数码管)作为一个虚拟单片机用任务2来进行控制,程序见下图: 这是一个典型的独立任务的“main()”函数结构,第88行是变量定义,第90行是变量初始化,第91行到第103行是一个无限循环结构。 任务2是将十六进制数字在这个4个数码管上不停地向左滚动。 (12)任务2的编程方法是“制造循环法”。这个方法是找出整个控制过程的最大循环圈,把它作为程序的主循环过程,然后逐步地写出其中的过程。 (13)金山151的数码管驱动方式与AI8051U实验箱的不同,在数码管缓冲区“JS151_DRV_DIG8_DATA_BUF[0]” ~“JS151_DRV_DIG8_DATA_BUF[7]”中,每个字节存放对应数码管的7个笔画和小数点的二进制数据。采用这种形式存放,每个数码管就可以显示全部255种图案,把相邻的数码管组合起来,不但可以显示数字字母,而且还可以显示各种动画图形。 上面语句中的JS151_DRV_DIG8_SYMB_LUT[n]是从符号查找表中将第n号符号的笔画取出来。
(14)本文范例将AI8051U实验箱的右面4个数码管(第4、5、6、7号数码管)作为一个虚拟单片机用任务3来进行控制,程序见下图: 任务3是把右面4个数码管作为一个3位数的计数器来使用。其中第112行程序让4号数码管不亮,第115、118和121行分别显示百位、十位和个位。
下面是本范例四个独立任务运行的视频效果:
四、RTOS任务调度的基本工作原理 (15)上面4个独立的实时任务都是按照一定的节拍对设备进行控制,其中控制节拍节奏的函数都是“JS151_OS_Task_Sleep_MS(xxx)”。这个函数的名称是“任务休眠函数”,它的功能是通知RTOS系统本任务需要“休眠多少毫秒”,在休眠期间可以暂停本任务的执行,等待休眠到期,再唤醒本任务从下一行程序开始继续执行。 (16)通常对于没有RTOS的单任务程序,控制节奏节拍的是软件延迟函数,延时时软件做空循环等待。 对于RTOS系统,当一个任务进入休眠等待状态时,不需要再做无用的空循环,任务直接放弃CPU的执行权,让其他就绪的任务获得CPU的执行权去执行该任务。 这就是RTOS系统最基本的任务调度方法:高优先级的任务在休眠期间利用“休眠函数”主动放弃CPU执行权,让其他低优先级的任务得以继续执行。 (17)当高优先级任务开始休眠时,如果同时有好几个低优先级任务处于就绪状态时,RTOS通常让这些就绪任务中优先级最高的一个获得CPU执行权,这种方法称为“按优先级”进行任务调度。 (18)通常RTOS在每毫秒一次的系统节拍中断中处理休眠任务,系统把每个休眠任务的休眠毫秒时间减一,如果减到0,说明该任务的休眠已经到期了,就将其的任务状态设置为就绪状态。 (19)每当有休眠任务到期,RTOS系统就将到期任务的优先级与当前正在执行的任务的优先级进行比较,如果到期任务的优先级比当前正在执行的任务的优先级还高,那么RTOS的调度系统就剥夺当前任务的CPU执行权,将CPU执行权交给休眠到期的任务,从休眠函数的下一行开始继续执行程序。 这种任务调度方式称为“抢占式任务调度”。
附件1:STCIDE+金水151编译器 附件2:RTOS独立任务范例程序
|