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


    【STM32】STM32单片机总目录

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

    在这里插入图片描述

    2、什么是任务

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

    void Task (void *arg)
    {
    	while(1)
    	{
    		。。。
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、任务栈

    3.1 栈

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

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

    Stack_Size		EQU     0x400
    
                    AREA    STACK, NOINIT, READWRITE, ALIGN=3
    Stack_Mem       SPACE   Stack_Size
    
    • 1
    • 2
    • 3
    • 4

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    3.3 多任务中栈分配

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

    __align(8)  CPU_STK  STkTask1[512];
    
    • 1

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

    typedef  CPU_INT32U               CPU_STK;
    typedef  unsigned  int         CPU_INT32U;
    
    • 1
    • 2

    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)                           */
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    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)		/* 错误返回值 */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7、任务初始化OSInit

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

    int main(void)
    {
    	OS_ERR  err;
    	
    	/* HAL库,MPU,Cache,时钟等系统初始化 */
    	System_Init();
    
    	/*初始化GPIO引脚*/	
    	MX_GPIO_Init();
    
    	/* 初始化uC/OS-III 内核 */
    	OSInit(&err);  
    	。。。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    初始化的全局变量包括:

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

    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;                    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    9、任务切换OSStartHighRdy

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

    主要完成的工作是:

    保存上下文(将当前任务的各个CPU寄存器中值保存到任务栈中)
    切换上下文(将下个任务栈中的内容加到CPU寄存器中)
    
    • 1
    • 2
    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]
    。。。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    10、任务调度

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

  • 相关阅读:
    Linux操作系统作业
    kafka消费/发送消息,消息过大报错解决whose size is larger than the fetch size 1048576
    设计模式-观察者模式在Java中的使用示例-环境监测系统
    一种通用的项目开发规范
    Linux和Windows之间文件自动同步
    nodejs+vue绿植花草预订与销售系统epxress
    2021年软件测试面试题大全
    一文带你读懂PyQt:用Python做出与C++一样的GUI界面应用程序
    WPF音乐播放器 零基础4个小时左右
    Spark基础【介绍、入门WordCount案例】
  • 原文地址:https://blog.csdn.net/u010168781/article/details/126534488