本帖最后由 杨为民 于 2024-3-1 18:41 编辑
一、前 言 前面的文章介绍了STC8的uC/OS-II移植版和该移植版参考的原始的陈是知移植版的基本情况,这两个版本在处理移植“任务调度开始函数”(OSStartHighRdy)和“非中断任务切换函数”(OSCtxSw)这两个接口函数时存在一个严重的错误:在结束C语言的函数调用时用中断返回指令“RETI”代替与结束“LCALL”调用配对的函数返回指令“RET”。 本文将详细地介绍这个错误的来源和纠正的方法。
二、用 “RETI”代替 “RET”的错误 (1)在STC8上的uC/OS-II移植版中有三个(也只需要三个)移植接口函数: 它们的出口都使用了同一段程序: (2)这个统一出口对应的三个接口函数中第62行的“OSIntCtxSw”是在硬件中断的ISR中调用的,采用第216行的“RETI”指令可以理解,但是对于第60行和61行的头两个接口函数,程序是用“LCALL”指令访问的函数,为什么要用中断返回指令“RETI”而不用函数返回指令“RET”。
(3)首先可以用实验来证明“OSCtxSw”跟中断没有关系的观点。下图是实验范例“05_uCOSII_任务级切换”中的“main()”函数,其中只是对前文“04_RTOS_基础测试程序”中“main()”函数进行了注释: 作为实验程序,将其中有关中断设置的第45和46行、第49和50行,以及与中断有关的任务A的建立语句第56行全部注释掉,然后编译,烧录到开发板上,肉眼就可以看到,没有什么“系统中断”,任务B和任务C照样可以正常轮流运行,正常的进行任务切换。
(4)上面的移植程序来源是陈是知的移植版,见下图: 为了查找上面疑问的来源,笔者找到了《uC/OS-II内核分析、移植与驱动程序开发》一书作者陈是知在其移植源程序中的解释,见下图: 其中提到书中的内容如下: (5)虽然目前的移植例子的在实际运行中并没有显示异常,但是对于原始移植者陈先生的见解笔者完全不赞同。 首先笔者从来没有在有关8051的书籍和STC官方手册看到RET和RETI指令可以无条件混用,如果是有条件可以混用,笔者也没有看到这个条件是什么。 其次移植RTOS是给出一个系统平台,是提供给用户在其基础上来编写自己的应用程序,因此不能用几个具体的移植案例来证明移植的RTOS平台的可靠性和安全性。因此笔者认为这两个移植版本均存在错误,必须找出原因和加以纠正。 (6)为什么移植者会采用这种混用的方法,笔者又查看了uC/OS-II发明者本尊的著作《嵌入式实时操作系统uC/OS-II(第2版)》和其中的移植例子,原著中关于移植接口函数“OSCtxSw”的程序见下图:
其中居然也是用8086汇编语言的中断返回指令“IRET”来实现非中断任务切换的。为什么?
笔者又查看了原著中非中断任务切换函数“OS_Sched”对“OSCtxSw”的调用方式,见下图: 图中第30行具体实现任务切换,且语句“OS_TASK_SW();”看起来也像是函数调用,不过它不是真函数,是一个宏定义: 这是PC机上BC45的C语言内嵌汇编指令,其中“asm”告诉编译器后面的是8086汇编语言,“INT 0x80”是产生一个80H号软中断,按中断的方式执行80H号中断服务程序。也就是说,用户任务程序中的非中断的任务切换,其实还是通过“中断”的方式来实现。 (7)至此,笔者认为产生将RETI与RET错误混用的根源来自于原始的被移植程序。 由于uC/OS-II发明者的著作和书中给出的例子是以将他的研究成果移植到PC机的“DOS”操作系统上来介绍移植接口的。在DOS系统下,整个uC/OS-II RTOS系统只是整体第作为一个用户程序在运行,而这时uC/OS-II的多个RTOS实时任务只是一个用户程序的不同程序片段(操作系统术语是线程)。 在DOS操作系统中,保存一个线程现场,恢复现场切换到另一个线程,标准的方法就是通过软中断调用。因此uC/OS-II进行“非中断任务切换”时,是调用DOS的80H号软中断来实现的,所以“OSCtxSw”任务级切换是一个8086CPU的中断过程,因此前面第55行使用“IRET”返回,对于DOS下的uC/OS-II这是正确的。 但是这里的移植目标机是STC8H裸机,“OSCtxSw”任务级切换程序是汇编语言写C语言函数,原始移植源中的“IRET”指令在移植时应该用“RET”来替代,不需要保存原样将RETI与RET混用。
三、STC8H上的uC/OS-II的错误的纠正 (8)根据上面找到的产生错误的原因,纠正错误的方法是不需要混用,将对应的函数的出口改为“RET”指令即可。为此特地将前文测试程序中合并在一起的三个接口函数分开来写,编写了“06_uCOSII_错误纠正程序”。 (9)纠正错误的方法很简单,将“OSStartHighRdy()”和“OSCtxSw()”函数的出口的“RETI”换为“RET”就行。 (10)对于RTOS运行由于系统中断和任务切换的过程是很快的、实时的,并且在时间上是互相关联的,很难用仿真跟踪来观察,因此为了证明错误已纠正,在程序中还使用P2端口连接一个8路逻辑分析仪来显示RTOS的运行状况。
其中第0通道和第1通道分别显示定时器0中断和定时器3中断,观测程序见下图: 定时器中断的正常情况时是连续的正脉冲,进入中断时为高电平,退出中断时为低电平。
任务A和任务B的观测程序如下: 第78行P22每个循环翻转一次,正常情况是周期为200毫秒的方波。 P23显示任务B得到程序执行权的状态,高电平代表任务处于“执行态”,低电平代表任务处于“挂起(休眠)态”。P24显示连接P1端口的LED灯的闪烁状态。
任务C的观测程序如下图: 程序中P25显示任务C得到程序执行权的状态,高电平代表任务处于“执行态”,低电平代表任务处于“挂起(休眠)态”。 第116行的P26在P3跑马灯每移一位时翻转一次,正常情况是周期为400毫秒的方波。
下图是纠正混用错误后的系统运行的逻辑分析仪观测截图,可以看到系统运行正常。 六、结束语 (11)对于笔者的移植范例程序,纠正错误后运行正常。那么对于陈先生的移植范例程序,纠正后运行还会正常吗?我猜不会,所以陈先生经过试验才会不得已混用的。
两个移植范例的差别在于笔者范例是只用了连接端口的LED灯,对端口的操作都是单条汇编指令可以完成的。而陈先生的移植范例是采用串口中断作为例子,其串口程序十分复杂,下图是其中部分: 其中逻辑流程比较复杂,因此难免挂一漏万,也有可能对于他的具体范例两者必须都用“RETI”。 (12)对于移植或者开发一个RTOS,笔者认为应该先用最简单的范例验证系统的正确性和可靠性,然后再用RTOS设备的规范去开发串口等外部设备驱动程序。
(13)上面的观察程序能否一直正常运行下去呢?由于移植程序还存在一些问题和瑕疵,这就不好说了。这些移植中问题和瑕疵还有哪些,后续文件将继续介绍。
|