UCOSIII任务堆栈、控制块及就绪表

发布者:EtherealGrace最新更新时间:2019-04-25 来源: eefocus关键字:UCOSIII  任务堆栈  控制块  就绪表 手机看文章 扫描二维码
随时随地手机看文章

一、UCOSIII任务堆栈


1、任务堆栈的创建


堆栈是在RAM中按照“先进先出(FIFO)”的原则组织的一块连续的存储空间。为了满足任务切换和响应中断时保存CPU寄存器中的内容及任务调用其它函数时的需要,每个任务都应该有自己的堆栈。


如何创建?


#define START_STK_SIZE           512  //堆栈大小


CPU_STK START_TASK_STK[START_STK_SIZE];   //定义一个数组来作为任务堆栈


可查看main.c的29行,跳转可知堆栈大小:


CPU_STK为CPU_INT32U类型,也就是unsigned int类型,为4字节的,那么任务堆栈START_TASK_STK的大小就为:512 X 4=2048字节!


2、任务堆栈的初始化


任务如何才能切换回上一个任务并且还能接着从上次被中断的地方开始运行?


恢复现场即可,现场就是CPU的内部各个寄存器。因此在创建一个新任务时,必须把系统启动这个任务时所需的CPU各个寄存器初始值事先存放在任务堆栈中。这样当任务获得CPU使用权时,就把任务堆栈的内容复制到CPU的各个寄存器,从而可以任务顺利地启动并运行。


把任务初始数据存放到任务堆栈的工作就叫做任务堆栈的初始化,UCOSIII提供了完成堆栈初始化的函数:OSTaskStkInit():


CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,

                         void          *p_arg,

                         CPU_STK       *p_stk_base,

                         CPU_STK       *p_stk_limit,

                         CPU_STK_SIZE   stk_size,

                         OS_OPT         opt)

用户一般不会直接操作堆栈初始化函数,任务堆栈初始化函数由任务创建函数OSTaskCreate()调用。不同的CPU对于的寄存器和对堆栈的操作方式不同,因此在移植UCOSIII的时候需要用户根据各自所选的CPU来编写任务堆栈初始化函数。


可查看UCOSIII_PORT中的os_cpu_c.c中227行


3、如何使用创建的任务堆栈?


作为任务创建函数OSTaskCreate()的参数,函数OSTaskCreate()如下:


void  OSTaskCreate (OS_TCB         *p_tcb,             //任务控制块

                    CPU_CHAR     *p_name,         //任务名字

                    OS_TASK_PTR   p_task,             //任务函数

                    void           *p_arg,             //传递给任务函数的参数

                    OS_PRIO        prio,               //任务优先级

                    CPU_STK       *p_stk_base,    //任务堆栈基地址 一般0

                    CPU_STK_SIZE   stk_limit,   //任务堆栈栈深 一般堆栈大小/10

                    CPU_STK_SIZE   stk_size,    //任务堆栈大小            前面创建的

                    OS_MSG_QTY   q_size,

                    OS_TICK        time_quanta,

                    void           *p_ext,      //用户补充的存储区

                    OS_OPT        opt,

                    OS_ERR        *p_err)      //存放该函数错误时的返回值

可查看main.c中79行的任务开始函数


4、堆栈增长方式


函数OSTaskCreate()中的参数p_stk_base如何确定?


根据堆栈的增长方式,堆栈有两种增长方式:


向上增长:堆栈的增长方向从低地址向高地址增长。


向下增长:堆栈的增长方向从高地址向低地址增长。


函数OSTaskCreate()中的参数p_stk_base是任务堆栈基地址,那么如果CPU的堆栈是向上增长的话那么基地址就&START_TASK_STK[0],如果CPU堆栈是向下增长的话基地址就是&START_TASK_STK[START_STK_SIZE-1]STM32的堆栈是向下增长的!


二、UCOSIII任务控制块


1、任务控制块结构


任务控制块是用来记录与任务相关的信息的数据结构,每个任务都要有自己的任务控制块。任务控制块由用户自行创建,如下代码为创建一个任务控制块:


OS_TCB StartTaskTCB;  //创建一个任务控制块


OS_TCB为一个结构体,描述了任务控制块,任务控制块中的成员变量用户不能直接访问,更不可能改变他们。


OS_TCB为一个结构体,其中有些成员采用了条件编译的方式来确定


struct os_tcb

 

{

 

    CPU_STK             *StkPtr;  //指向当前任务堆栈栈顶                         

 

    void                *ExtPtr; //指向用户可定义的数据取                           

 

    CPU_STK             *StkLimitPtr;//可指向任务堆栈中的某个位置

 

    OS_TCB              *NextPtr; //NexPtr和PrevPtr用于在任务就绪表建立OS_TCB

 

    OS_TCB              *Prev

 

    ……                              //此处省略N个成员变量

 

 #if OS_CFG_DBG_EN > 0u

 

    OS_TCB              *DbgPrevPtr;//下面三个成员变量用于调试

 

    OS_TCB              *DbgNextPtr;

 

    CPU_CHAR            *DbgNamePtr;

 

 #endif

 

}

可查看main.c中26行进行跳转,里面有很多的成员变量


2、任务控制块初始化


函数OSTaskCreate()在创建任务的时候会对任务的任务控制块进行初始化。


函数OS_TaskInitTCB()用与初始化任务控制块。用户不需要自行初始化任务控制块。


可查看os_task.c中341行


三、UCOSIII任务就绪表


1、优先级:


UCOSIII中任务优先级数由宏OS_CFG_PRIO_MAX来配置,UCOSIII中数值越小,优先级越高,最低可用优先级就是OS_CFG_PRIO_MAX-1。


可查看UCOSIII_CONFIG中os_cfg.c中47行


2、就绪表


    UCOSIII中就绪表由2部分组成:


    1、优先级位映射表OSPrioTbl[]:用来记录哪个优先级下有任务就绪。


    2、就绪任务列表OSRdyList[]:用来记录每一个优先级下所有就绪的任务。


OSPrioTbl[]在os_prio.c中有定义:


CPU_DATA   OSPrioTbl[OS_PRIO_TBL_SIZE]; //UCOSIII_CORE中os_prio.c中41行


跳转可知:


在STM32中CPU_DATA为unsigned int,有4个字节,32位。因此表OSPrioTbl每个参数有32位,其中每个位对应一个优先级。


OS_PRIO_TBL_SIZE=((OS_CFG_PRIO_MAX - 1u) / DEF_INT_CPU_NBR_BITS)+ 1)


其中OS_CFG_PRIO_MAX由用户自行定义,默认为64。


DEF_INT_CPU_NBR_BITS= CPU_CFG_DATA_SIZE * DEF_OCTET_NBR_BITS


CPU_CFG_DATA_SIZE=CPU_WORD_SIZE_32=4。


DEF_OCTET_NBR_BITS=8。


所以,当系统有64个优先级的时候:


OS_PRIO_TBL_SIZE=((64-1)/(4*8)+1)=2。


2.1、如何找到已经就绪了的最高优先级的任务?


函数OS_PrioGetHighest()用于找到就绪了的最高优先级的任务:源码在UCOSIII_CORE中os_prio.c中85行


OS_PRIO  OS_PrioGetHighest (void)

 

{

 

    CPU_DATA  *p_tbl;

 

    OS_PRIO    prio;

 

    prio  = (OS_PRIO)0;

 

    p_tbl = &OSPrioTbl[0];//从OSProTb[0]开始扫描映射表,一直遇到非零项

 

while (*p_tbl == (CPU_DATA)0) {

 

//当数组OSProTb[]中某个元素为0时,就继续扫描下一个素组元素,prio加DEF_INT_CPU_NBR_BITS位

 

       prio += DEF_INT_CPU_NBR_BITS;

 

       p_tbl++;// p_tbl加一,继续寻找OSProTb[]中下一个元素

 

}

 

//一旦找到一个非0项,再加上该项的前导0数量就找到了最高优先级任务了

 

    prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl); //计算前导0:计算0的个数

 

    return (prio);

2.2就绪任务列表


通过上一步我们已经知道了哪个优先级的任务已经就绪了,但是UCOSIII支持时间片轮转调度,同一个优先级下可以有多个任务,因此我们还需要在确定是优先级下的哪个任务就绪了


struct  os_rdy_list {

 

    OS_TCB           *HeadPtr    //用于创建链表,指向链表头

 

    OS_TCB           *TailPtr;   //用于创建链表,指向链表尾

 

    OS_OBJ_QTY       NbrEntries; //此优先级下的任务数量

 

};     //全局变量查找:在os.h中1184行  

     

同一优先级下如果有多个任务的话,最先运行的永远是HeadPtr所指向的任务!达到左右任务轮询执行。


UCOSIII内部使用的几个函数:

关键字:UCOSIII  任务堆栈  控制块  就绪表 引用地址:UCOSIII任务堆栈、控制块及就绪表

上一篇:UCOSIII系统初始化和启动
下一篇:UCOSIII任务管理中的几个关键名词解释(任务及任务状态)

推荐阅读最新更新时间:2024-11-08 12:02

S7-GRAPH FB参数设置和顺序控制解读
S7-GRAPH FB参数设置包括4种类型: 1. Minimum , FB只包括SQ_ INIT 启动参数,如果用户的程序仅仅会运行在自动模式,并且不需要其它的控制及监控功能,可以选择此模式 2. Standard, FB包括默认参数,如果用户希望程序运行在各种模式,并提供反馈及确认消息功能。可以选择此模式。 3. um (S7-GRAPH 版本=》 V4.x) FB 包括默认参数,扩展参数,提供更多的控制,监控参数 4. User-defined (S7-GRAPH 版本=》 V5. x)包括默认参数,扩展参数,提供更多的控制,监控参数 顺序步
[机器人]
西门子SCL编程实例—气缸控制函数
气缸是工业现场使用较多的一种执行器,它使用压缩空气作为动力,有两个进气孔,通过控制进气与排气,推动气缸往复运动。一些安全系数较高的气缸还配有抱闸。默认情况下抱闸闭合,气缸不能运动。在通入压缩空气后抱闸打开,气缸可自由运动。今天这篇文章我们来编写一个用于控制气缸往复运动的函数块——FB5022_Cyclinder,它也可用于液压缸的类似场合。 函数块FB5022_Cyclinder有如下一些特点: 支持自动模式和维护(手动)模式。在自动模式下,气缸会运动某个指定的时间,直到时间到达或者触发前进/后退到位反馈信号;在维护(手动)模式下,气缸运动跟随按钮信号,按下按钮运动,松开按钮停止; 气缸运动时会使能抱闸松开信号; 当同时触发前
[嵌入式]
西门子SCL编程实例—气缸<font color='red'>控制</font>函数<font color='red'>块</font>
详解μC/OS-II如何检测任务堆栈实际使用情况
不少屌丝同学都有类似经历吧,在使用ucosii创建任务时,关于任务堆栈大小设为多大合适搞的不清不楚,郁闷之下就随便整个数,比如就1024吧,呵呵,反正也没见得出问题,那就不多想了。 我想大多数同学都是这样做的吧。这样只是因为在一般情况下,1024确实已经足够大了,堆栈溢出的可能性很小而已。那么,如果你任务实际使用率只有很小的 百分之几,一旦被你知道了,你会痛心不?我想你不痛心, C/OS-II也会痛心的,它会觉得这个coder真是浪费啊,哈哈!顺便提醒下大家,堆和栈是 完全不同的两个概念,出于国内习惯,还是称之为堆栈罢了! 下面,我就来告诉大家怎么知道运行中任务的堆栈实际使用情况,然后就知道应该分配多少堆栈大小合适了!开始正题。
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved