【STM32】入门(十一):初识uCOS-III

【STM32】STM32单片机总目录

1、轮询、中断、多任务对比

在这里插入图片描述

2、什么是任务

如果您学过linux,那么任务可以理解为线程。在代码中的体现就是线程函数,一个函数中有个无限循环函数,并且永不返回。例如:

void Task (void *arg)
{
	while(1)
	{
		。。。
	}
}

3、任务栈

3.1 栈

栈stack是一块程序运行时用来存储临时变量的内存RAM空间。栈一般静态分配,并且后进先出,栈的生命周期从程序的起始直到程序结束。一个函数返回,其用到的栈空间就被释放给后续函数使用。

不带操作系统的裸机中,可以视为只有一个任务,任务栈也只有一个,可以在启动文件中看到相关代码:

Stack_Size		EQU     0x400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size

Stack栈的大小为:0x400(1024Byte),一个函数中定义的所有局部变量,加起来不能大于工程的栈大小,否则程序肯定会出现内存溢出,导致复位。

3.2 堆

与栈相类似的还有堆空间,当工程中使用了malloc动态分配内存空间时,这时分配的空间就为堆的空间,同样在启动代码中也能看到堆空间的分配的代码:

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

Heap堆的大小为:0x200(512Byte)

3.3 多任务中栈分配

在操作系统,如ucosIII中,每个任务都需要分配堆栈(如果不需要动态分配内存,可以不分配堆)。
栈空间的分配其实就是,创建一个数组,即连续的内存分配,例如:

__align(8)  CPU_STK  STkTask1[512];

宏CPU_STK其实就是 unsigned int ,源码如下

typedef  CPU_INT32U               CPU_STK;
typedef  unsigned  int         CPU_INT32U;

4、任务控制块TCB

任务函数写好后,uCOSIII系统如何调度我们写的任务函数?这就需要通过任务控制块TCB来让uCOSIII识别、调度任务。

任务控制块TCB相当于任务的身份证,里面存有任务的所有信息,比如任务的堆栈,任务名称,任务的形参等。

任务控制块TCB源码如下:
最主要的有两个任务函数指针 (CPU_STK *StkPtr)和任务栈大小(CPU_STK_SIZE StkSize;)

struct os_tcb {
    CPU_STK             *StkPtr;                            /* Pointer to current top of stack                        */
    void                *ExtPtr;                            /* Pointer to user definable data for TCB extension       */
    CPU_STK             *StkLimitPtr;                       /* Pointer used to set stack 'watermark' limit            */
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;                           /* Pointer to task name                                   */
#endif
    OS_TCB              *NextPtr;                           /* Pointer to next     TCB in the TCB list                */
    OS_TCB              *PrevPtr;                           /* Pointer to previous TCB in the TCB list                */
#if (OS_CFG_TICK_EN == DEF_ENABLED)
    OS_TCB              *TickNextPtr;
    OS_TCB              *TickPrevPtr;
#endif
#if ((OS_CFG_DBG_EN == DEF_ENABLED) || (OS_CFG_STAT_TASK_STK_CHK_EN == DEF_ENABLED) || (OS_CFG_TASK_STK_REDZONE_EN == DEF_ENABLED))
    CPU_STK             *StkBasePtr;                        /* Pointer to base address of stack                       */
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    OS_TLS               TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    OS_TASK_PTR          TaskEntryAddr;                     /* Pointer to task entry point address                    */
    void                *TaskEntryArg;                      /* Argument passed to task when it was created            */
#endif
    OS_TCB              *PendNextPtr;                       /* Pointer to next     TCB in pend list.                  */
    OS_TCB              *PendPrevPtr;                       /* Pointer to previous TCB in pend list.                  */
    OS_PEND_OBJ         *PendObjPtr;                        /* Pointer to object pended on.                           */
    OS_STATE             PendOn;                            /* Indicates what task is pending on                      */
    OS_STATUS            PendStatus;                        /* Pend status                                            */
    OS_STATE             TaskState;                         /* See OS_TASK_STATE_xxx                                  */
    OS_PRIO              Prio;                              /* Task priority (0 == highest)                           */
...

5、任务创建函数

任务控制块TCB就是一个结构体,需要封装了任务信息,uCOS提供一个函数将任务信息填充到TCP中,并将它注册到uCOS操作系统中去。让uCOS知道它的存在,并调度它。这个函数在uCOS中就是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,	/* 堆栈基地址 */
                    CPU_STK_SIZE   stk_limit,	/* 堆栈监测区 */
                    CPU_STK_SIZE   stk_size,	/* 堆栈空间大小 */
                    OS_MSG_QTY     q_size,		/* 本任务支持接受的最大消息数 */
                    OS_TICK        time_quanta, /* 设置时间片 */
                    void          *p_ext,		/* 堆栈空间大小 */ 
                    OS_OPT         opt,			/* 选择设置 */ 
                    OS_ERR        *p_err)		/* 错误返回值 */ 

6、任务就绪列表

所有任务都在一个列表中,供系统切换,这个列表叫做任务就绪列表:OSRdyList

任务就绪列表的定义:OS_RDY_LIST OSRdyList[OS_CFG_PRIO_MAX]
节点个数最大64个,也就是说,最多可以创建64个任务:#define OS_CFG_PRIO_MAX 64u
节点类型 OS_RDY_LIST 其实就是组成双向列表的结构体,原代码如下

typedef struct os_rdy_list OS_RDY_LIST;
struct os_rdy_list{
    OS_TCB  *HeadPtr;  /* Pointer to task that will run at selected priority */
    OS_TCB  *TailPtr;  /* Pointer to last task at selected priority */
};

7、任务初始化OSInit

系统初始化一般都是在硬件初始化完后再执行。代码如下,系统初始化函数OSInit在时钟初始化、外部设备初初始化以后运行

int main(void)
{
	OS_ERR  err;
	
	/* HAL库,MPU,Cache,时钟等系统初始化 */
	System_Init();

	/*初始化GPIO引脚*/	
	MX_GPIO_Init();

	/* 初始化uC/OS-III 内核 */
	OSInit(&err);  
	。。。
}

OSInit主要完成的工作:初始化全局变量、初始化任务就绪列表。

初始化的全局变量包括:

OSRunning 系统的运行状态,默认是停止状态OS_STATE_OS_STOPPED
OSTCBCurPtr:当前正在运行的任务TCB指针
OSTCBHighRdyPtr:任务就绪列表中,优先级最高的任务TCB指针

8、任务启动OSStart

先装载最高优先级任务到当前任务指针,然后执行任务切换函数:OSStartHighRdy

void  OSStart (OS_ERR  *p_err)
{
    OS_OBJ_QTY  kernel_task_cnt;
    kernel_task_cnt = 0u;                                       
    if (OSRunning == OS_STATE_OS_STOPPED) {
        OSPrioHighRdy   = OS_PrioGetHighest();                  
        OSPrioCur       = OSPrioHighRdy;
        OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
        OSTCBCurPtr     = OSTCBHighRdyPtr;
        OSRunning       = OS_STATE_OS_RUNNING;
        OSStartHighRdy();                                       
       *p_err           = OS_ERR_FATAL_RETURN;                  
    } else {
       *p_err           = OS_ERR_OS_RUNNING;                    
    }
}

9、任务切换OSStartHighRdy

任务切换函数OSStartHighRdy是汇编写的,在os_cpu_a.asm中,部分源码如下,本人不懂汇编,不在展开解释

主要完成的工作是:

保存上下文(将当前任务的各个CPU寄存器中值保存到任务栈中)
切换上下文(将下个任务栈中的内容加到CPU寄存器中)
OSStartHighRdy
    CPSID   I                                                   ; Prevent interruption during context switch
    MOV32   R0, NVIC_SYSPRI14                                   ; Set the PendSV exception priority
    MOV32   R1, NVIC_PENDSV_PRI
    STRB    R1, [R0]
。。。

10、任务调度

在每个任务函数的循环中会需要执行一个函数: OSTimeDly;
OSTimeDly中会调用OSSched;
OSSched会切换下一个任务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭老二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值