从零开始学习UCOSII操作系统13–系统移植理论篇「建议收藏」

从零开始学习UCOSII操作系统13–系统移植理论篇「建议收藏」从零开始学习UCOSII操作系统13–系统移植理论篇1、什么是系统移植?(1)UCOSII移植到不同的处理器上,所谓的移植就是将一个实时的内核能在其他的微处理器或者微控制器上运行。为了方便移植,UCOSII的大部分的代码都是C语言写的,因为不同机器的汇编代码是不一样的。这是由于UCOSII在设计的时候已经充分考虑到了可移植性这一点。但是仍然有一部分的代码是需要用C语言

大家好,又见面了,我是你们的朋友全栈君。

从零开始学习UCOSII操作系统13–系统移植理论篇

1、什么是系统移植?

(1)UCOSII移植到不同的处理器上,所谓的移植就是将一个实时的内核能在其他的微处理器或者微控制器上运行。

为了方便移植,UCOSII的大部分的代码都是C语言写的,因为不同机器的汇编代码是不一样的。这是由于UCOSII在设计的时候已经充分考虑到了可移植性这一点。

但是仍然有一部分的代码是需要用C语言和汇编语言写一些与处理器有关的代码。

(2)要使用UCOSII正常的运行,处理器必须满足以下的条件
2.1、处理器的C编译器能产生可重入型的代码

原因:如果不行的话,那么就不能在任务之间随意的切换,因为当你切换到别的任务的时候,该任务在这个函数的数据就会被破坏。

2.2、处理器支持中断,并能产生定时中断

2.3、用C语言就可以开关中断

原因:连中断都没有的话,是不可能进行任务切换的。

2.4、处理器能够支持一定数量的数据存储硬件堆栈,也就是栈

2.5、处理器有将堆栈指针以及其他的CPU的寄存器的内容读出,并存储到堆栈或者内存中去的指令。

原因:因为我们在任务切换的时候,需要将当前的CPU指针保存到刚刚执行的任务当中。然后切换到优先级更高的任务当中。

2、移植UCOSII实际上需要移植什么文件?

其实我们移植UCOSII额时候,大部分的代码是基于底层进行编写的,所以我们不需要进行移植。有一些是对这个UCOSII进行的配置的,所以我们不需要进行移植。

我们唯一需要关注的三个文件就是下面的三个文件。
OS_CPU.h
OS_CPU_A.ASM
OS_CPU_C.c

一般我们在开发的时候,我们会把所有的头文件定义在同一个头文件当中,因为这样我们只需要包含一个头文件就可以了,不会重复的包含多个头文件。

需要移植的几个文件:
INCLUDES.H
是一个主头文件,出现在每个.c 文件的第一行。

使得每个.c的文件中无需分别考虑它实际上需要哪些头文件,使用INCLUDE.H唯一的缺点就是,它可能包含一些与当前的编译的.c文件不相干的头文件。

OS_CPU.H
包含了用#define 语句定义的,与处理器相关的常数,宏以及类型。
OS_CPU.H的大体结构如程序清单所列。

这里面使用typedef用的比#define更加的好,因为#define仅仅相当于字符串的拷贝,但是typedef相当于命名了一个别名。

/*        数据类型       */

typedef unsigned char BOOLEAN;

typedef unsigned int OS_STK;   //堆栈入口的宽度为16位
typedef unsigned short OS_CPU_SR; //定义CPU状态寄存器的宽度为16位

/*         与处理器有关的代码    */
#define OS_ENTER_CRITICAL()     //进入临界区的代码
#define OS_EXIT_CRITICAL()      //跳出临界区的代码

#define OS_STK_GHOWTH   1  定义堆栈的方向:1=向下递减,0=向上递增

#define OS_TASK_SW()  ??  //定义软件任务切换的函数。

最关键的移植文件:CPU的文件
移植文件3:OS_CPU_C.c
UCOSII的移植范例要求用户编写10个简单的C函数:

OSTaskStkInit();
OSTaskCreateHook();
OSTaskDelHook();
OSTaskSwHook();
OSTaskIdleHook();
OSTaskStatHook();
OSTimeTickHook();
OSInitHookBegin();
OSInitHookend();
OSTCBInitHook();

PS:唯一必要的函数是OSTaskStkinit();其余的9个函数必须声明,但是并不一定要包含任何代码,这些函数的原型放在本章的末尾。

(1)因此堆栈看起来就像中断刚发生的一样,所有的寄存器都保存在堆栈中,OSTaskStkInit()的示例性代码:

OS_STK * OSTaskStkInit(void (*task)(void * pd),
                        void * pada,
                        OS_STK  *ptos,
                        INT16U  opt);
{
    pada = pada;
    模拟ISR向量;

    //按照预先设计的寄存器值初始化堆栈结构;
    //不断的在堆栈中相应的位置填入你要传递的参数
    //返回栈顶指针给调用该函数的函数

    //在这里假定堆栈是从上往下递减的,下面讨论同样适用于以相反方向从下到上递增的堆栈结构。
}

(2)OSTaskCreateHook():
每当添加任务的时候,OS_TCBInit()函数都会调用OSTaskCreateHook()函数,该函数允许扩展UCOSII的功能,当UCOSII设置完任务控制块OS_TCB初始化的绝大部分的工作后,但是在任务控制块被链接到相应的任务链中之前,以及在该任务就绪运行之前,UCOSII会调用OSTaskCreateHook(),该函数被调用的时候中断是打开的。

钩子函数就是为了检查相应的操作有没有成功的。

(3)OSTaskIdleHook()
很多微处理器都允许执行相应的指令,将CPU置于低功耗模式。而当接收到中断信号的时候,CPU就会退出低功耗模式,OSTaskIdle()函数可调用OSTaskIdleHook()函数,实现CPU的这种低功耗的模式:

PS:其实这里真的设计的非常的巧妙,因为你没有任何的任务进行调度的时候,应该是没有什么执行的,但是我们UCOSII系统,规定当没有任何的任务需要强占CPU的时候,我们应该让其进入低功耗的模式,真的设计的很好。

void OS_TaskIdle(void *pdata)
{
    pdata = pdata;
    for(;;)
    {
        OS_ENTER_CRITICAL();
        OSIdleCtr++;
        OS_EXIT_CRITICAL();
        OSTaskIdleHook();
    }
}

void OSTaskIdleHook(void)
{
    asm("STOP");
    //收到中断并完成中断服务。
}

OS_CPU_A.ASm
UCOSII的移植实例就是要求用户编写4个简单的汇编语言函数:
OSStartHighRdy(); //使得最高优先级的任务运行的函数
OSCtxSw(); //任务的切换的函数
OSIntCtxSw();
OSTickISR();

如果编译器支持插入行汇编代码就可以将所有的与处理器相关的代码放置到OS_CPU_C.c里面种,就不需要适用汇编文件了。

(1)OSStartHighRdy()

OSStart()函数调用OSStartHighRdy()来使得就绪太任务中最高优先级的任务开始运行,这个函数的示例性的代码

void OSStartHighRdy()
{
    调用用户定义的OSTaskSwHook();
    OSRunning = TRUE;
    //得到将要恢复运行的任务的堆栈指针。
    stack pointer = OSTcbHighRdy->OSTCBStkPtr;
    //从新的堆栈中恢复处理器的所有的寄存器,就是把刚刚切换的堆栈保存到别的地方当中
    //执行中断返回,然后跳转PC指针到别的地方中去。

}

(2)OSCtxSw():

任务级的切换是通过执行软中断指令,或者依据处理器的不同,执行TPAP陷阱指令执行的。中断服务子程序,陷阱或者异常处理的向量的地址必须指向OSCtxSw();

void OSCtxSw()
{
    保存处理器寄存器;
    在当前的任务的任务控制块中保存当前任务的堆栈指针;
    OSTCBCur->OSTCBStkPtr = stack pointer;
    OSTaskSwHook();
    OSTCBCur = OSTCBHighRdy;
    OSPrioCur = OSPrioHighRdy;

    //得到将要重新开始运行的任务的堆栈指针:
    stack pointer = OSTCBHighRdy->OSTCBstkPtr;
    //从新的任务堆栈中恢复所有的寄存器的值;
    //执行中断返回的指令。
}

(3)OSTickISR()
UCOSII要求用户提供一个周期性的时钟源,来实现时间延迟和超时功能,时钟节拍应该每秒发生10或者100次每秒,为了完成任务,可以使用硬件定时器,也可以从交流电中获得50~60Hz的时钟频率。

必须在开启多任务后,即调用OSStart()后,启动时钟节拍中断,但是由于OSStart()函数不会返回。不能在还没有运行第一个任务的时候,启动时钟节拍中断。会导致程序跑飞。

在没有执行OSStart()之前不能打开时钟节拍中断。千万不能在这里开中断。

因为UCOSII此时仍然处于未知的状态,所以一旦跳入中断,就会跑飞。

定时器允许用户被挂起一定的时间:

void OSTickISR(void)
{
    //保存处理器的寄存器
    //调用OSIntEnter或者直接给OSIntNesting加1
    if(OSIntNesting ==1)
    {
        OSTCBCur->OSTCBStkPtr = Stack Pointer;
    }
    //给产生中断的设备清中断
    //重新允许中断
    OSTimeTick();   //硬件的产生中断的原理
    OSIntExit();    //中断退出
    //恢复处理器寄存器
    //执行中断返回指令
}

OSIntCtxSw()

void OSintCtxSw(void)
{
    //调整堆栈指针
    OSintExit();
    OSINTCtxSw();
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/136016.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Centos7安装Postgresql 13 详细步骤(远程连接)

    Centos7安装Postgresql 13 详细步骤(远程连接)

    2021年6月4日
    154
  • smartctl用法心得

    smartctl用法心得SMART简介S.M.A.R.T.,全称为“Self-MonitoringAnalysisandReportingTechnology”,即“自我监测、分析及报告技术”。是一种自动的硬盘状态检测与预警系统和规范。通过在硬盘硬件内的检测指令对硬盘的硬件如磁头、盘片、马达、电路的运行情况进行监控、记录并与厂商所设定的预设安全值进行比较,若监控情况将或已超出预设安全值的安全范围,就可以通过主机的监控硬件或软件自动向用户作出警告并进行轻微的自动修复,以提前保障硬盘数据的安全。除一些出厂时间极早的硬盘外,现

    2022年10月8日
    4
  • Tensorflow分布式框架 解决Graph is finalized and cannot be modified问题

    Tensorflow分布式框架 解决Graph is finalized and cannot be modified问题Tensorflow 分布式框架解决 Graphisfinal 问题如果使用 MonitoredTra 创建 Session 不需要再初始化变量 错误示例 whilenotsess should stop sess run tf global variables initializer 并且注意也不能在 MonitoredTra 之后进行任何初始化操作 包括数据初始化或变量初始化 应该放

    2026年1月29日
    1
  • 降价的路上一路狂奔 阿里云又双叒叕降价了

    降价的路上一路狂奔 阿里云又双叒叕降价了

    2022年2月23日
    39
  • python优势有哪些?[通俗易懂]

    python优势有哪些?[通俗易懂]ol是英文orderedlist的缩写,翻译为中文是有序列表。有序列表是一种强调排列顺序的列表,使用标签定义,内部可以嵌套多个 标签。例如网页中常见的歌曲排行榜、游戏排行榜等都可以通过有序列表来定义。定义有序列表的基本语法格式如下: 列表项1 列表项2 列表项3 ……   在上面的语法中, 标签用于定义有序列表, 为具体的列表项,和无序列表类似,每对中 也至少应包含一对 。   在有序列表中,除了type属性之外,还可以为 定义start属..

    2022年10月5日
    4
  • python字符串删除指定字符「建议收藏」

    python字符串删除指定字符「建议收藏」python字符串中去除指定字符或字符串:1,Pythonstrip()方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。2,Pythonreplace()方法把字符串中的old(旧字符串)替换成new(新字符串),如果指定第三个参数max,则替换不超过max次。参考链接:https://blog.csdn.net/qq_31672701/article/details/100927305…

    2022年5月1日
    701

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号