UCOS-III笔记(四)
作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习
擅长领域:驱动开发,嵌入式软件开发,BSP开发
作者主页:一个平凡而乐于分享的小比特的个人主页
文章收录专栏:UCOS-III,本专栏为UCOS-III学习记录
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
UCOS-III笔记(四)
任务调度
UCOSIII的初始化
初始化UCOSIII函数:OSInit ( )
函数内部实现:
- 对一些全局变量赋初始值
- 初始化就绪列表以及就绪任务优先级
- 创建三个任务:空闲任务(必须创建),统计任务(条件创建),软件定时器任务(条件创建)
- 空闲任务:任务优先级最低31,当系统无其他的就绪任务,那么空闲任务将会执行,注意空闲任务不能被阻塞
- 统计任务:任务优先级为30,用来统计CPU使用率和各个任务的堆栈使用量
- 软件定时器任务:任务优先级为29,主要用于在特定的时间段内处理单次或周期性的软件定时器
开启任务调度器
开启任务调度器函数:OSStart ( )
函数内部实现:
- 进行一些安全关键代码判断
- 获取当前最高优先级任务
- 将调度器运行状态标志设置为开启状态
- 获取最高优先级任务的任务控制块
- 调用函数OSStartHighRdy( ) ,启动第一个任务
前导置零指令:CPU_CntLeadZeros( )
本质:计算一个 32位数,头部 0 的个数
CPU_CntLeadZeros ( OSPrioTbl[0]) ,其中OSPrioTbl[0]为32位的变量
eg1:

CPU_CntLeadZeros ( OSPrioTbl[0]) ) = 3
eg2:

CPU_CntLeadZeros ( OSPrioTbl[0]) ) = 1
函数OS_PrioGetHighest ( ) 通过前导置零指令获得最高优先级
启动第一个任务
启动第一个任务:OSStartHighRdy ()
函数内部实现:
- 屏蔽中断,防止在启动第一个任务时被打断
- 将PendSV设置为最低优先级
- 将PSP清0,PSP将用于任务栈
- 将MSP设置为OS_CPU_ExceptStkBas,指向异常栈的栈底(按 8 字节对齐)
- 获取当前最高优先级的任务它的任务优先级,以及任务控制块
- 将最高优先级任务的任务栈出栈
- 开中断,并跳转到第一个运行任务的任务函数执行
任务创建后对应任务的堆栈情况

- 寄存器xPSR被初始为0x01000000,其中bit24被置1,表示使用Thumb指令
- 寄存器PC被初始化为任务函数指针vTask_A,这样当某次任务切换后,任务A获得CPU控制权,任务函数vTask_A被出栈到PC寄存器,之后会执行任务A的代码
- LR寄存器初始化为函数指针OS_TaskReturn ,这是由移植层提供的一个出错处理函数
- 子函数的调用通过寄存器R0~R3传递参数,创建任务时,我们传入的参数被保存到R0中,用来向任务函数传递参数
EXC_RETURN合法值
R14 是链接寄存器 LR,在 ISR 中,它记录了异常返回值 EXC_RETURN

注意:在M4、M7系列中,bit4用于判断是否使用浮点单元,1不使用,0使用,M3未使用浮点单元,bit4为0
EXC_RETURN 只有 6 个合法的值(M4、M7),如下表所示:

MSP与PSP
程序在运行过程中需要一定的栈空间来保存局部变量等一些信息。当有信息保存到栈中时,MCU 会自动更新 SP 指针,ARM Cortex-M 内核提供了两个栈空间
-
主堆栈指针(MSP):它由 OS 内核、异常服务例程以及所有需要特权访问的应用程序代码来使用
-
进程堆栈指针(PSP):用于常规的应用程序代码(不处于异常服务例程中时)
在UCOS-III中,中断使用MSP(主堆栈),中断以外使用PSP(进程堆栈)
任务切换
本质:CPU寄存器的切换
当由任务A切换到任务B,主要分为两步:
- 需暂停任务A的执行,并将此时任务A的寄存器保存到任务堆栈,这个过程叫做保存现场;
- 将任务B的各个寄存器值(被存于任务堆栈中)恢复到CPU寄存器中,这个过程叫做恢复现场;
对任务A保存现场,对任务B恢复现场,这个整体的过程称之为:上下文切换
注意:任务切换的过程在PendSV中断服务函数里边完成

PendSV中断触发
执行UCOS-III提供的相关API函数:OSCtxSw()和 OSIntCtxSw()
本质:通过向中断控制和状态寄存器 ICSR 的bit28 写入 1 挂起 PendSV 来启动 PendSV 中断

触发PendSV中断的两个函数由以下API函数调用:
- OSSched():任务中使用
- OSIntExit():中断中使用
