手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的初始化过程(二)

本章重点讲解空闲任务的建立过程。

任务建立函数定义如下:

 1 INT8U  OSTaskCreate (void   (*task)(void *p_arg),
 2                      void    *p_arg,
 3                      OS_STK  *ptos,
 4                      INT8U    prio)
 5 {
 6     OS_STK    *psp;
 7     INT8U      err;
 8 #if OS_CRITICAL_METHOD == 3u                 /* Allocate storage for CPU status register               */
 9     OS_CPU_SR  cpu_sr = 0u;
10 #endif
11
12
13
14 #ifdef OS_SAFETY_CRITICAL_IEC61508
15     if (OSSafetyCriticalStartFlag == OS_TRUE) {
16         OS_SAFETY_CRITICAL_EXCEPTION();
17     }
18 #endif
19
20 #if OS_ARG_CHK_EN > 0u
21     if (prio > OS_LOWEST_PRIO) {             /* Make sure priority is within allowable range           */
22         return (OS_ERR_PRIO_INVALID);
23     }
24 #endif
25     OS_ENTER_CRITICAL();
26     if (OSIntNesting > 0u) {                 /* Make sure we don‘t create the task from within an ISR  */
27         OS_EXIT_CRITICAL();
28         return (OS_ERR_TASK_CREATE_ISR);
29     }
30     if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn‘t already exist at this priority  */
31         OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ...  */
32                                              /* ... the same thing until task is created.              */
33         OS_EXIT_CRITICAL();
34         psp = OSTaskStkInit(task, p_arg, ptos, 0u);             /* Initialize the task‘s stack         */
35         err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);
36         if (err == OS_ERR_NONE) {
37             if (OSRunning == OS_TRUE) {      /* Find highest priority task if multitasking has started */
38                 OS_Sched();
39             }
40         } else {
41             OS_ENTER_CRITICAL();
42             OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others                 */
43             OS_EXIT_CRITICAL();
44         }
45         return (err);
46     }
47     OS_EXIT_CRITICAL();
48     return (OS_ERR_PRIO_EXIST);
49 }

21~23行判断我们传递进来的优先级是否满足限定条件(不大于63),如果不满足,直接退出。

26~29行判断当前系统的状态,变量OSIntNesting的意义之前讲过,如果它大于0,那就代表目前处于中断服务函数中,在中断中是不允许建立新任务的。

30行首先判断任务是否已经存在,如果已经存在则跳出,数组OSTCBPrioTbl[prio]的元素是优先级,内容是任务的相关信息,如果该任务已建立,那么其内容必然不等于0.

31行当任务不存在时,首先随便赋个值给这个任务的管理数组,相当于是在这个元素上做个标志,告诉系统这个位置要保留下来不允许别人动,直到这个任务真正建立成功。

34行所调用的函数是初始化任务的堆栈,我们详细看看内部的处理,到底是如何进行初始化的:

函数OSTaskStkInit定义如下:

 1 OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
 2 {
 3     OS_STK *stk;
 4
 5
 6     (void)opt;                                   /* ‘opt‘ is not used, prevent warning                 */
 7     stk       = ptos;                            /* Load stack pointer                                 */
 8
 9                                                  /* Registers stacked as if auto-saved on exception    */
10     *(stk)    = (INT32U)0x01000000L;             /* xPSR                                               */
11     *(--stk)  = (INT32U)task;                    /* Entry Point                                        */
12     *(--stk)  = (INT32U)0xFFFFFFFEL;             /* R14 (LR) (init value will cause fault if ever used)*/
13     *(--stk)  = (INT32U)0x12121212L;             /* R12                                                */
14     *(--stk)  = (INT32U)0x03030303L;             /* R3                                                 */
15     *(--stk)  = (INT32U)0x02020202L;             /* R2                                                 */
16     *(--stk)  = (INT32U)0x01010101L;             /* R1                                                 */
17     *(--stk)  = (INT32U)p_arg;                   /* R0 : argument                                      */
18
19                                                  /* Remaining registers saved on process stack         */
20     *(--stk)  = (INT32U)0x11111111L;             /* R11                                                */
21     *(--stk)  = (INT32U)0x10101010L;             /* R10                                                */
22     *(--stk)  = (INT32U)0x09090909L;             /* R9                                                 */
23     *(--stk)  = (INT32U)0x08080808L;             /* R8                                                 */
24     *(--stk)  = (INT32U)0x07070707L;             /* R7                                                 */
25     *(--stk)  = (INT32U)0x06060606L;             /* R6                                                 */
26     *(--stk)  = (INT32U)0x05050505L;             /* R5                                                 */
27     *(--stk)  = (INT32U)0x04040404L;             /* R4                                                 */
28
29     return (stk);
30 }

这个函数该怎么解释呢?

它其实并不是真正的去初始化了CPU的栈空间,只是对一个模拟栈进行了初始化。

做过单片机程序的同学都很清楚,在程运行汇总遇到中断发生,必须要跳进中断中运行,但在跳进中断服务函数之前,它必须要做一些处理,比如保存现场,将当前的数据和寄存器值押入栈中,然后在去执行中断函数,因为只有这样做了在执行完中断函数以后才能找回原点继续执行剩下的代码。

单片机裸机程序是单线程,代码的执行逻辑始终处于一条时间线,就算是发生了中断嵌套,但在某一个具体的时间点,它也是单线的,只需要保存一个现场就可以回到原点,。

但在操作系统中,系统中同时执行了多个任务,每个任务之间都有可能发生切换,任务与任务之间不存在调用的关系,是相对独立的存在,任务的这个切换可以理解为单片机的中断,而且切换的顺序并不是线性的,一旦在任务过多的情况下,如果依然使用CPU自己的栈空间,那这样就会导致栈空间不够用,而且非常不方便。

所以系统创建了一个数组来模拟栈空间,比如这个空闲任务,我们跟踪它的参数可以知道:

OS_EXT  OS_STK            OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE];      /* Idle task stack                */
#define OS_TASK_IDLE_STK_SIZE   128u   /* Idle       task stack size (# of OS_STK wide entries)        */

系统定义了一个128byte的数组来模拟空闲任务的栈空间,专门用来存在和这个任务有关的数据,当我们从空闲任务跳出去的时候,就会把相应的信息保存在里面,等需要跳回的时候,就会从里面取出相应的信息,功能和CPU的栈完全一样。

原文地址:https://www.cnblogs.com/han-bing/p/9025847.html

时间: 2024-10-08 20:05:13

手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的初始化过程(二)的相关文章

从头开始编写一个实时嵌入式操作系统的内核(二)

一.RTOS里面的重要数据结构----链表 很多RTOS包括Linux的内核在内,内核里面都大量使用了链表这一种数据结构.内核的链表一般都是双向循环链表,这是因为双向循环链表的效率是最高的,找头节点.尾节点,直接前驱.直接后继时间复杂度都是O(1),这是使用单链表.单向循环链表或其他形式的链表是不能完成的.我们平时上课所学的链表一般都是指针域和数据域,但是如果有研究过Linux内核里面链表的人应该知道和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.Linux内核链表在linux源

关于嵌入式操作系统的小总结

嵌入式操作系统   摘要: 通过回顾嵌入式操作系统的发展历史,分析了嵌入式操作系统的特点,并且从嵌入式操作系统的市场和技术的发展着手,探讨了嵌入式系统的未来发展趋势. 正文: (一)嵌入式操作系统的简介 嵌入式操作系统(Embedded Operating System,简称:EOS)是指用于嵌入式系统的操作系统.嵌入式操作系统是一种用途广泛的系统软件,通常包括与硬件相关的底层驱动软件.系统内核.设备驱动接口.通信协议.图形界面.标准化浏览器等. 目前典型的嵌入式操作系统有:嵌入式实时操作系统μ

嵌入式操作系统的未来会是怎样?

随着计算机嵌入式的普遍应用,越来越多的人开始接受并认可嵌入式,而任何计算机操作都离不开操作系统的运用,今天尚观教育的小编就来给大家讲讲嵌入式操作系统的未来会是怎样? 嵌入式操作系统将是未来嵌入式系统中必不可少的组件,其未来发展趋势包括: 1.定制化:嵌入式操作系统将面向特定应用提供简化型系统调用接口,专门支持一种或一类嵌入式应用.嵌入式操作系统同将具备可伸缩性.可裁减的系统体系结构,提供多层次的系统体系结构.嵌入式操作系统将包含各种即插即用的设备驱动接口; 2.节能化:嵌入式操作系统继续采用微内

为什么要有uboot?带你全面分析嵌入式linux系统启动过程中uboot的作用

1.为什么要有uboot 1.1.计算机系统的主要部件 (1)计算机系统就是以CPU为核心来运行的系统.典型的计算机系统有:PC机(台式机+笔记本).嵌入式设备(手机.平板电脑.游戏机).单片机(家用电器像电饭锅.空调) (2)计算机系统的组成部件非常多,不同的计算机系统组成部件也不同.但是所有的计算机系统运行时需要的主要核心部件都是3个东西: CPU + 外部存储器(Flash/硬盘) + 内部存储器(DDR SDRAM/SDRAM/SRAM) 1.2.PC机的启动过程 (1)部署:典型的PC

嵌入式 Linux进程间通信(二)——exec族函数

嵌入式 Linux进程间通信(二)--exec族函数 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件.这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件. exec族函数包含如下函数: #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int exec

关于Linux操作系统的开机过程详解

由 于操作系统正在变得越来越复杂,所以开机引导和关机下电的过程也越来越智能化.从简单的DOS系统转移到 Windows NT系统,人们已经亲身感受到了这些变化--这已不仅仅是核心操作系统的启动引导和关闭了,还包括必须要同时启动或者关闭相当数量的服务项目.类似于 Windows NT,Linux系统启动过程需要打开的服务项目也是数量极大的. 这里,我们假设大家已经熟悉其它操作系统的引导过程,了解硬件的自检引导步骤,就只从Linux操作系统的引导加载程序(对个人电脑而言通常是LILO)开始,介绍Li

【转】android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)

原文网址:http://www.cnblogs.com/zdz8207/archive/2012/11/27/android-ndk-install.html android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创) 一直想搞NDK开发却一直给其他事情耽搁了,参考了些网上的资料今天终于把环境搭建起来了,把过程记录下来分享给大家. 内容目录: 1.默认基础环境 2.NDK下载与配置 3.安装Cygwin 4.用NDK编译 5.安装

(转)嵌入式Linux引导过程-----从BootRom到Xloader

--by FeCen 在开始看Xloader_Entry的代码之前,我想先总结一下从芯片上电到开始运行Xloader的代码的过程,这是我目前理解的一个过程,可能有所出入,待以后继续完善.当系统上电之后,首先会将PC寄存器设置成BootRom里面的代码对应的一个地址.BootRom是芯片内部集成的一块很小的存储区,里面一般会固化一段启动代码.至于BootRom所占用的地址空间,每个芯片的定义可能会有所不同,具体的可以参考芯片的用户手册中的Memory Map部分的说明.在spearplus中,Bo

嵌入式视频处理基础(二)

引言: 作为消费者,我们对于各种形式的视频系统都已经非常熟悉了.但是从嵌入式开发人员的角度来看,视频就好像是一张纷繁复杂的网络,里面充满了各种不同的分辨率.格式.标准与显示等. 隔行扫描和逐行扫描: 隔行扫描起源于早起的模拟电视广播,那时候需要按顺序将图像快速刷新,以减小视觉上的闪烁,但是当时的技术还无法做到这么快速的刷新整个屏幕.因此,就将每一帧进行交织处理,即分为两场,一个由奇数扫描行组成,另一个由偶数扫描行组成. 隔行扫描 帧分为奇数和偶数场 NTSC(PAL)的帧刷新率大约为30(25)