找回密码
 立即注册
查看: 691|回复: 3

STC单片机uC/OS-II移植记(5):纠正原移植程序中将“RETI”与“RET”混用产生的错误

[复制链接]

该用户从未签到

63

主题

703

回帖

1万

积分

荣誉版主

积分
10906
发表于 2023-5-28 16:00:23 | 显示全部楼层 |阅读模式
本帖最后由 杨为民 于 2024-3-1 18:41 编辑

一、前  言
前面的文章介绍了STC8uC/OS-II移植版和该移植版参考的原始的陈是知移植版的基本情况,这两个版本在处理移植“任务调度开始函数”(OSStartHighRdy)和“非中断任务切换函数”(OSCtxSw)这两个接口函数时存在一个严重的错误:在结束C语言的函数调用时用中断返回指令“RETI”代替与结束“LCALL”调用配对的函数返回指令“RET”。
本文将详细地介绍这个错误的来源和纠正的方法。

二、用 RETI”代替 RET”的错误
1)在STC8上的uC/OS-II移植版中有三个(也只需要三个)移植接口函数:
Fig01_接口函数.jpg
它们的出口都使用了同一段程序:
Fig02_统一出口.jpg
2)这个统一出口对应的三个接口函数中第62行的“OSIntCtxSw”是在硬件中断的ISR中调用的,采用第216行的“RETI”指令可以理解,但是对于第60行和61行的头两个接口函数,程序是用“LCALL”指令访问的函数,为什么要用中断返回指令“RETI”而不用函数返回指令“RET”。

3)首先可以用实验来证明“OSCtxSw”跟中断没有关系的观点。下图是实验范例“05_uCOSII_任务级切换”中的“main()”函数,其中只是对前文“04_RTOS_基础测试程序”中“main()”函数进行了注释:
Fig03_非中断任务切换.jpg
作为实验程序,将其中有关中断设置的第4546行、第4950行,以及与中断有关的任务A的建立语句第56行全部注释掉,然后编译,烧录到开发板上,肉眼就可以看到,没有什么“系统中断”,任务B和任务C照样可以正常轮流运行,正常的进行任务切换。

4)上面的移植程序来源是陈是知的移植版,见下图:
Fig04_来源.jpg
为了查找上面疑问的来源,笔者找到了《uC/OS-II内核分析、移植与驱动程序开发》一书作者陈是知在其移植源程序中的解释,见下图:
Fig05_陈是知.jpg
其中提到书中的内容如下:
Fig06_理由.jpg
5)虽然目前的移植例子的在实际运行中并没有显示异常,但是对于原始移植者陈先生的见解笔者完全不赞同。
首先笔者从来没有在有关8051的书籍和STC官方手册看到RETRETI指令可以无条件混用,如果是有条件可以混用,笔者也没有看到这个条件是什么。
其次移植RTOS是给出一个系统平台,是提供给用户在其基础上来编写自己的应用程序,因此不能用几个具体的移植案例来证明移植的RTOS平台的可靠性和安全性。因此笔者认为这两个移植版本均存在错误,必须找出原因和加以纠正。
6)为什么移植者会采用这种混用的方法,笔者又查看了uC/OS-II发明者本尊的著作《嵌入式实时操作系统uC/OS-II(第2版)》和其中的移植例子,原著中关于移植接口函数“OSCtxSw”的程序见下图:
Fig07_DOS.jpg
其中居然也是用8086汇编语言的中断返回指令“IRET”来实现非中断任务切换的。为什么?

笔者又查看了原著中非中断任务切换函数“OS_Sched”对“OSCtxSw”的调用方式,见下图:
Fig08_调用方法.jpg
图中第30行具体实现任务切换,且语句“OS_TASK_SW();”看起来也像是函数调用,不过它不是真函数,是一个宏定义:
Fig09_中断调用.jpg
这是PC机上BC45C语言内嵌汇编指令,其中“asm”告诉编译器后面的是8086汇编语言,“INT 0x80”是产生一个80H号软中断,按中断的方式执行80H号中断服务程序。也就是说,用户任务程序中的非中断的任务切换,其实还是通过“中断”的方式来实现。
7)至此,笔者认为产生将RETIRET错误混用的根源来自于原始的被移植程序。
由于uC/OS-II发明者的著作和书中给出的例子是以将他的研究成果移植到PC机的“DOS”操作系统上来介绍移植接口的。在DOS系统下,整个uC/OS-II RTOS系统只是整体第作为一个用户程序在运行,而这时uC/OS-II的多个RTOS实时任务只是一个用户程序的不同程序片段(操作系统术语是线程)。
DOS操作系统中,保存一个线程现场,恢复现场切换到另一个线程,标准的方法就是通过软中断调用。因此uC/OS-II进行“非中断任务切换”时,是调用DOS80H号软中断来实现的,所以“OSCtxSw”任务级切换是一个8086CPU的中断过程,因此前面第55行使用“IRET”返回,对于DOS下的uC/OS-II这是正确的。
但是这里的移植目标机是STC8H裸机,“OSCtxSw”任务级切换程序是汇编语言写C语言函数,原始移植源中的“IRET”指令在移植时应该用“RET”来替代,不需要保存原样将RETIRET混用。

三、STC8H上的uC/OS-II的错误的纠正
8)根据上面找到的产生错误的原因,纠正错误的方法是不需要混用,将对应的函数的出口改为“RET”指令即可。为此特地将前文测试程序中合并在一起的三个接口函数分开来写,编写了“06_uCOSII_错误纠正程序”。
9)纠正错误的方法很简单,将“OSStartHighRdy()”和“OSCtxSw()”函数的出口的“RETI”换为“RET”就行。
10)对于RTOS运行由于系统中断和任务切换的过程是很快的、实时的,并且在时间上是互相关联的,很难用仿真跟踪来观察,因此为了证明错误已纠正,在程序中还使用P2端口连接一个8路逻辑分析仪来显示RTOS的运行状况。

其中第0通道和第1通道分别显示定时器0中断和定时器3中断,观测程序见下图:
Fig10_01通道.jpg
定时器中断的正常情况时是连续的正脉冲,进入中断时为高电平,退出中断时为低电平。

任务A和任务B的观测程序如下:
Fig11_任务AB.jpg
78P22每个循环翻转一次,正常情况是周期为200毫秒的方波。
P23显示任务B得到程序执行权的状态,高电平代表任务处于“执行态”,低电平代表任务处于“挂起(休眠)态”。P24显示连接P1端口的LED灯的闪烁状态。

任务C的观测程序如下图:
Fig12_任务C.jpg
程序中P25显示任务C得到程序执行权的状态,高电平代表任务处于“执行态”,低电平代表任务处于“挂起(休眠)态”。
116行的P26P3跑马灯每移一位时翻转一次,正常情况是周期为400毫秒的方波。

下图是纠正混用错误后的系统运行的逻辑分析仪观测截图,可以看到系统运行正常。
Fig13_运行.jpg
六、结束语
11)对于笔者的移植范例程序,纠正错误后运行正常。那么对于陈先生的移植范例程序,纠正后运行还会正常吗?我猜不会,所以陈先生经过试验才会不得已混用的。

两个移植范例的差别在于笔者范例是只用了连接端口的LED灯,对端口的操作都是单条汇编指令可以完成的。而陈先生的移植范例是采用串口中断作为例子,其串口程序十分复杂,下图是其中部分:
Fig14_陈是知.jpg
其中逻辑流程比较复杂,因此难免挂一漏万,也有可能对于他的具体范例两者必须都用“RETI”。
12)对于移植或者开发一个RTOS,笔者认为应该先用最简单的范例验证系统的正确性和可靠性,然后再用RTOS设备的规范去开发串口等外部设备驱动程序。

13)上面的观察程序能否一直正常运行下去呢?由于移植程序还存在一些问题和瑕疵,这就不好说了。这些移植中问题和瑕疵还有哪些,后续文件将继续介绍。

回复 送花

使用道具 举报

该用户从未签到

552

主题

9492

回帖

1万

积分

管理员

积分
14057
发表于 2023-5-28 16:20:37 | 显示全部楼层
我最近健忘的厉害,技术我已是似懂非懂
但 这个 【RET, RETI】 我是知到绝对有区别的,当年毕设,软件模拟硬件复位 !!!

关中断后,用了2次 RETI, 最后1次 RETI 到 0000H 就搞定软复位了。
8032有2级中断优先级,你不知道中断嵌套了几次,就中断优先级有2级,就 RETI 2次 !
RETI 可以清内部被中断挂起的中断优先级标志,无专门的指令,无专门的寄存器干这个事 !
如果误用 RETI / RET 很恐怖,不擅长 【二进制码和汇编】到杨老师这种级别的,
会不知道自己错在哪 ,中断优先级被搞乱了 !
其实我们内部最擅长的是 二进制码/汇编/Verilog和Linux


为这 RETI,我领导芯片设计时,首先就加了软件复位


杨老师是我敬重的技术权威,我也老了,但我们酒后讲的都是帮助8051世界一起进步的核心技术
1.png

回复 支持 反对 送花

使用道具 举报

  • TA的每日心情
    开心
    昨天 03:47
  • 签到天数: 146 天

    [LV.7]常住居民III

    39

    主题

    887

    回帖

    4122

    积分

    荣誉版主

    积分
    4122
    发表于 2023-10-25 02:50:46 | 显示全部楼层
    (11)对于笔者的移植范例程序,纠正错误后运行正常。那么对于陈先生的移植范例程序,纠正后运行还会正常吗?我猜不会,所以陈先生经过试验才会不得已混用的。

    ===========================================================================================

    RETI  是中断返回指针,  相当于同时执行 RET 和 清当前内部的中断级标志位(充许该中断级能再次响应中断).

    当 中断没有发生时,  RETI  的执行, 等同于 RET, 这是常识.

    因此, 在没有中断产生的情况下,  RETI  指令的执行,  等价于 RET,   陈是知先生移植版的混用, 完全没有任何问题.

    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    昨天 03:47
  • 签到天数: 146 天

    [LV.7]常住居民III

    39

    主题

    887

    回帖

    4122

    积分

    荣誉版主

    积分
    4122
    发表于 2023-10-25 02:57:22 | 显示全部楼层
    神农鼎 发表于 2023-5-28 16:20
    我最近健忘的厉害,技术我已是似懂非懂
    但 这个 【RET, RETI】 我是知到绝对有区别的,当年毕设,软件模拟 ...


    以前的 51有2级中断优先级,所以需要 用2次 RETI, 才能清除内部的2个中断级标志位(如没有响应中断,相当于空操作), 最后跳转到 0000H 才算正式软复位了。

    现在的 STC51, 有4级中断优先级,需要 用4次 RETI 才行。
    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-5-18 03:15 , Processed in 0.063088 second(s), 43 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表