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

STC单片机uC/OS-II移植记(4):RTOS基础测试程序

[复制链接]

该用户从未签到

63

主题

703

回帖

1万

积分

荣誉版主

积分
10908
发表于 2023-5-10 20:52:26 | 显示全部楼层 |阅读模式
本帖最后由 杨为民 于 2024-3-1 00:28 编辑

一、前  
无论是学习、研究、测试和移植RTOS,只要是想达到掌握RTOS编程的熟练水平,都应该深入研究一个或一组基础的测试程序。就像要取得驾照,就要通过精心设计的N个科目的考试。
uC/OS-II中有两个途径进行任务调度,一种是在中断过程中进行的,这时调用“OSIntExit()”函数来执行调度任务(称系统级调度),另一种是在实时任务中进行,这时调用“OS_Sched()”函数来执行调度任务(称任务级调度)。因此基础测试程序必须包括这两任务调度方法编程。
根据RTOS工作原理,在有中断嵌套时进行任务切换时十分不安全的,被高优先级中断嵌套的中断服务程序(简称ISR)现场在被中断的当前任务系统堆栈中,如果这时当前任务被切换走,则被嵌套的ISR就得不到继续执行了,只有下次当前任务再获得CPU执行权时该ISR才会被等到执行。这就带来两个不安全因素,首先如果迟迟不发布“RETI”指令,该中断以及比该中断优先级低的中断就被阻塞了,其次如果ISR没有执行完前,程序意外地先发布了“RETI”指令(比如在目前移植到STC8uC/OS-II使用串口中断的情况),则再次发生该中断时ISR就被重入,将会产生不可预料的错误。
因此在STC单片机RTOS程序设计中,如何规范地处理中断服务程序十分重要,RTOS测试程序除了系统中断外,还应该包括其他用户中断程序。
本文结合将uC/OS-II移植到STC单片机上的过程,介绍一个ROTS的基础测试程序,该程序包括了实时任务的系统级调度和任务级调度,包括了系统中断和用户中断服务程序。该程序是开源的,相信读者可以从中学习到许多RTOS的知识和RTOS的规范编程方法。
二、端口初始化和软件延时函数
STC单片机RTOS程序设计中,端口初始化和软件延时是最基本和最重要的方法,下图是本测试程序采用的端口初始化和软件延时函数(在OS_CPU_C.c程序文件中):
Fig01_软件延时函数.jpg
1)对于STC8H等系列STC单片机,单片机加电时为防止端口电平处于不期望的状态,除P3.0P3.1外所有端口都处于高阻输入状态,由用户根据具体的电路要求进行初始化。在端口的4种工作模式中,准双向口模式与传统的8051端口模式最相像,可以直接驱动下拉的LED灯和数码管等。另外在许多情况下,需要将端口内部的数据寄存器置为高电平才能进行正确的数字输入输出操作。所以在没有特别要求的情况下,STC单片机端口初始化程序(第297316行程序)将所有的端口都设置为准双向口和高电平。
2)同一段用C语言编写的软件延时函数程序的延时时间不但与烧录的主频有关,还与编译器的优化等级和内存模式有关,尤其在RTOS程序中还与该函数是否可以重入有关。因此通用空循环延时程序中内循环的次数都要根据同一类程序具体调试。

上面的程序中第291行是计算每毫秒的内循环次数,其中的系数“34000”是笔者采用下面测试程序具体调试的结果。
Fig02_软件延时测试.jpg
其中第32行到第42行是软件延时测试程序。如果注释掉第37行,打开第36行程序(如图)它是一个无限循环程序结构,测试时程序就在这一段中重复执行,后面的程序不会得到执行。
测试完成后将注释掉第36行,打开第37行程序这段测试程序就成了一个LED闪三次的系统启动LOGO程序。这个LOGO对于RTOS基础测试程序很有用,如果系统跑飞(比如访问了一个未定义的函数)造成单片机系统从0000H地址重启,你就可以这个闪烁的LOGO,知道系统重启了。

下图是用逻辑分析仪对P2端口的测试结果:
Fig03_测试结果.jpg
下面是本软件延时测试时的效果视频:
视频1 软件延时
三、系统级调度的编程方法

3)系统级调度方法。在本测试程序中任务A是采用了系统级调度方法,下图是测试该任务的程序:
Fig04_系统级调度.jpg
在上面任务A程序中第77行程序中用户任务函数“OSTimeDly”是进行系统级调度程序的典型接口函数,该函数设置了要延时的Tick数后,就挂起任务自己,等待延时完成后被唤醒。
在上面“main”函数中第57行和58行程序被注释后,只建立了任务A,下面的视频显示了任务A单独运行的情况:

视频2任务A
4)在uC/OS-II中每次系统中断发生时,会调用系统时钟函数“OSTimeTick OS_CORE.C文件第889行到第958行程序)将所有等待延时的任务的延时Tick数减一,减到0的时候,就将该任务状态设置为就绪状态。
5)系统级调度是指在各种中断中(不限于系统中断),调用系统调度(中断中任务切换)函数“OSIntExit”(OS_CORE.C文件第663行到第691行程序)进行任务调度。下图是该函数程序:
Fig05_系统级调度.jpg
该函数首先将中断嵌套计数减1(第674行),如果还有中断嵌套,则退出该函数,不进行任务调度,如果没有中断嵌套则进行任务调度,先找出当前就绪任务中优先级最高的任务(第678行),然后将CPU控制权切换给该任务(第685行)。
6)按照uC/OS-II系统级调度编程规范,只会在每次发生中断时才会检测当前就绪的最高优先级任务并进行任务调度,因此即使实时任务条件满足了,也要等到下次中断发生时才会进行任务切换。这样系统级调度的实时任务响应时间平均为各种定时周期中断中最小的周期间隔的二分之一。
比如RTOS包含一个异步串口中断(字符间的周期可能很长)和一个系统中断,系统中断的周期为20毫秒,则系统级调度的平均实时任务的响应时间为10毫秒。
四、任务级调度的编程方法
7uC/OS-II RTOS可以在没有系统中断的情况下进行任务调度,实现多个任务的切换,这时实时任务的响应时间甚至可以比在系统中断中进行系统级调度还要快。
比如极端情况下程序可以用循环的方式不停地检测实时任务的切换条件,如果条件成立就立即进行任务调度,不需等待,这种情况下实时任务的响应时间可以小到微秒量级。

8)本测试程序的任务B和任务C之间就是采用了任务级调度。首先注释掉所有中断设置语句和任务A,这样就不会有任何中断发生了,任务调度只能是任务级调度了,注释程序见下图:
Fig06_禁止中断.jpg
9)任务B和任务C的程序见下图:
Fig07_任务级调度.jpg
上面程序中用户任务函数 OSTaskSuspend(优先级数)”和“OSTaskResume(优先级数)”是典型的任务级调度接口函数。
10)在任务B中,第88行到93行将连接在P1端口的8LED分两半左右闪烁3次。
94行“OSTaskResume(4)”是将任务C(优先级为4)设置为就绪状态,然后进行任务级调度函数,由于当前任务B(优先级为4)的优先级比任务C高,因此继续执行下一行程序。
95行程序“OSTaskSuspend(OS_PRIO_SELF);”将任务B自己挂起(任务状态为延时非就绪),将程序控制权交给当前就绪任务中优先级最高的任务执行。由于上一行已经将任务C设置为就绪了,因此任务C继续执行。
11)任务C106行到第111行是将连接在P32P37上的6LED灯依次点亮。
任务C的第112行程序使得任务B就绪,然后进行任务级调度。由于任务B的优先级比任务C高,因此这时任务C被挂起在第112行,而控制权交给任务B,开始执行任务B上次挂起点的下一行第96行程序,重新执行P1灯闪烁。
等待任务C再次获得执行权后,就从挂起点的下一行第113行继续执行程序,然后又在第112行挂起,最终形成任务B和任务C轮流执行的情况。
下面是任务B和任务C在没有任何中断的情况下依靠任务级调度轮流执行的效果视频:

视频3任务级调度
12uCOS-II是一个可裁剪的开源程序,要进行任务级调度,首先要设置系统配置头文件“OS_CFG.H”中的任务挂起允许标志“OS_TASK_SUSPEND_EN”为1。见下图:
Fig08_任务挂起允许.jpg
五、uC/OS-II中断服务程序的规范编程方法

13)作为RTOS测试程序,中断的设置程序也要可以根据研究的目的进行多种模式的设置。对于用户中断,本测试程序选择的定时器3中断,这样做的理由是定时器中断是自动准确定时产生的,可以方便进行测试和研究。下图是系统中断定时器0和定时器3的设置程序:
Fig09_中断设置.jpg
其中主频的定义“MAIN_Fosc”是33.1776MHz,定时器0有两种模式可以选择,如图打开第324行,则其工作在模式3——不可屏蔽模式(意思是只有ET0被打开后,则定时器0的中断就与总中断控制EAET0无关了,即使将EA关闭EA=0ET0=0,定时器0的中断照样产生。在这个模式下,想要控制定时器0的中断,只能用计数允许TR0来控制了),如果注释掉第324行,则定时器0工作在模式0——16位自动重装模式。总中断允许EA可以控制定时器0的中断发生。
14)第331行到334行是定时器3的中断频率定义,此处图中选择的是10KHz——每100微秒产生一次中断。

15)按照uC/OS的不在中断嵌套时进行任务调度的要求,每个中断服务程序都有进行进入和退出中断登记,用户自己定义的中断也不例外,因此uC/OS-II的中断不能使用C语言用中断矢量关键字来定义,必须在汇编语言中逐个定义。下面是本测试程序的ISR
Fig10_ISR.jpg
其中第428行到第439行是定时器3ISR,该中断什么也不做,只是是uC/OS-II规范的ISR框架。
430行与第438行是用P20端口电平显示进入和退出该中断。第432行与第436行是中断是当前任务的寄存器现场保存与恢复。第433行与第435行是在内核中进行中断嵌入等级。

下图是第433行访问的中断内核进入函数:
Fig11_中断进入.jpg
其中第638行的条件对于STC目前的单片机是多余的,任何程序中断最大的嵌套层次为单片机中断的总数,肯定没有255个。中断嵌入层数计数“OSIntNesting”会在系统调度函数“OSIntExit”中作为是否进行调度的判断标志用。
用户中断的第435行调用不但会进行中断嵌套退出,也同样会进行系统级调度。因此对于本测试程序,系统级任务调度的响应时间为50微秒。
16)第400行到第425行是定时器0ISR,其用P21端口电平显示进入和退出中断。而第416行与第418行是用来保护时间节拍系统程序得到完整的执行。
下面是完整测试程序运行的视频效果:

视频4运行效果
从中可以看到任务B和任务C是交替执行的,而任务A的优先级最高,优先得到执行,由于任务A的休眠的时间较长,在其休眠时时,任务B和任务C能够得到执行,因此整体视频效果是任务A与任务B和任务C同时进行。
如果将任务A的任务休眠函数“OSTimeDly”换成软件延时“Delay_MS”,延迟期间不放弃程序执行权,那么视频的效果将是任务A独自执行了,有兴趣的读者可以自行试试。
六、结束语

本开源测试程序包括uC/OS-II的基本过程,可以作为用户实际RTOS程序的框架范例参考。本文程序也可以作为测试RTOS性能和研究用,笔者将在下一篇文章中介绍。
下面是本测试程序开源代码:
0704_RTOS_基础测试程序.rar (497.59 KB, 下载次数: 70)


回复 送花

使用道具 举报

  • TA的每日心情
    无聊
    半小时前
  • 签到天数: 153 天

    [LV.7]常住居民III

    18

    主题

    534

    回帖

    1306

    积分

    金牌会员

    积分
    1306
    发表于 2023-5-10 21:53:46 | 显示全部楼层
    顶一个,极度好文!学习学习,谢谢!
    不停地学习
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    1 小时前
  • 签到天数: 56 天

    [LV.5]常住居民I

    4

    主题

    26

    回帖

    288

    积分

    中级会员

    积分
    288
    发表于 2024-4-10 10:32:32 | 显示全部楼层
    看了杨工的详细解说,迷糊中, 先程序下载看看, 对 uC/OS-II 一点不了解, 需要看什么书或者资料吗? 认真学习下。
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    25 分钟前
  • 签到天数: 42 天

    [LV.5]常住居民I

    0

    主题

    56

    回帖

    134

    积分

    注册会员

    积分
    134
    发表于 2024-5-7 22:58:34 | 显示全部楼层
    受益匪浅,好文章
    回复 支持 反对 送花

    使用道具 举报

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

    本版积分规则

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

    GMT+8, 2024-5-20 08:57 , Processed in 0.087886 second(s), 44 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

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