UCOSIII学习笔记(二)任务管理、Hook函数、中断

UCOSIII学习笔记(二)

一、UCOSIII任务管理

1、任务的基本概念

实时应用中一般将工作拆分为多个任务,每个任务都需要是可靠的。使用uC/OS-III 可以轻松地解决这个问题。任务(也叫做线程)是简单的程序。单CPU 中,在任何时刻只能是一个任务被执行。

UCOSIII中的任务由三部分组成:任务堆栈、任务控制块和任务函数

  • 任务堆栈:上下文切换的时候用来保存任务的工作环境,就是STM32的内部寄存器值。
  • 任务控制块:任务控制块用来记录任务的各个属性。
  • 任务函数:由用户编写的任务处理代码。

2、UCOSIII系统任务

UCOSIII默认有5个系统任务:

  1. 空闲任务:UCOSIII创建的第一个任务,UCOSIII必须创建的任务,此任务有UCOSIII自动创建,不需要用户手动创建,优先级总是为OS_CFG_PRIO_MAK-1(最低)。空闲任务中不能调用任何可使空闲任务进入等待态的函数

  2. 时钟节拍任务:此任务也是必须创建的任务。时钟节拍任务用来跟踪任务延时和任务等待超时,任务优先级用宏OS_CFG_TICK_TASK_PRIO来定义 ,一般时钟节拍任务的任务应该设置一个相对较高的优先级

  3. 统计任务:可选任务,用来统计CPU使用率和各个任务的堆栈使用量。此任务是可选任务,由宏OS_CFG_STAT_TASK_EN控制是否使用此任务。默认情况下统计任务是不会创建的,如果要开启统计任务的话需要做如下步骤:

    1、将宏OS_CFG_STAT_TASK_EN置1。

    2、必须在main函数创建的以一个任务也是唯一的一个应用任务里面调用函数OSStatTaskCPUUsageInit()。

    3、统计任务的优先级通过宏OS_CFG_STAT_TASK_PRIO来设置,一般设置OS_CFG_PRIO_MAX-2(倒数第二)。

  4. 定时任务:用来向用户提供定时服务,也是可选任务,由宏OS_CFG_TMR_EN控制是否使用此任务。在OSInit()中将会调用函数OS_TmrInit()来创建定时任务。定时任务的优先级通过宏OS_CFG_TMR_TASK_PRIO定义。

  5. 中断服务管理任务:可选任务,由宏OS_CFG_ISR_POST_DEFERRED_EN控制是否使用此任务。使用中断服务管理任务的好处就是可以减少中断关闭的时间(定时器中断也会关闭,影响UCOSIII系统时钟),中断服务管理任务的优先级永远为0(最高),不可更改!

3、UCOSIII的任务的状态

任务状态描述
休眠态任务已经在CPU的flash中了,但是还不受UCOSIII管理。
就绪态系统为任务分配了任务控制块,并且任务已经在就绪表中登记,这时这个任务就具有了运行的条件,此时任务的状态就是就绪态。
运行态任务获得CPU的使用权,正在运行。
等待态正在运行的任务需要等待一段时间,或者等待某个事件,这个任务就进入了等待态,此时系统就会把CPU使用权转交给别的任务。
中断服务态当发送中断,当前正在运行的任务会被挂起,CPU转而去执行中断服务函数,此时任务的任务状态叫做中断服务态。

4、任务控制块

任务控制块OS_TCB,用来记录与任务相关的信息的数据结构,每个任务都要有自己的任务控制块。任务控制块由用户自行创建。OS_TCB为一个结构体,描述了任务控制块,任务控制块中的成员变量用户不能直接访问,更不可能改变他们。函数OSTaskCreate()在创建任务的时候会对任务的任务控制块进行初始化。函数OS_TaskInitTCB()用来初始化任务控制块。用户不需要自行初始化任务控制块。

5、任务堆栈

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

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

堆栈大小:CPU_STK为4字节,堆栈大小 = 4 * START_STK_SIZE。

UCOSIII提供了堆栈初始化的函数:OSTaskStkInit() ,用户一般不会直接操作堆栈初始化函数,任务堆栈初始化函数由任务创建函数OSTaskCreate()调用。在移植UCOSIII的时候需要根据不同CPU来编写任务堆栈初始化函数。

6、任务就绪表

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

1、优先级位映射表OSPrioTbl[]:用来记录哪个优先级下有任务就绪。函数OS_PrioGetHighest()用于找到就绪了的最高优先级的任务。

​ UCOSIII中任务优先级数由宏OS_CFG_PRIO_MAX来配置。

src="UCOSIII%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0(%E4%BA%8C).assets/001.jpg" alt="001" style="zoom:70%;" />

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

​ 同一优先级下如果有多个任务的话最先运行的永远是HeadPtr所指向的任务!

7、任务调度和切换

1、任务调度

UCOSIII中的任务调度是由任务调度器来完成的,任务调度器有2种:任务级调度器和中断级调度器。

  • 任务级调度器为函数OSSched()。

  • 中断级调度器为函数OSIntExit(),当退出外部中断服务函数的时候使用中断级任务调度。

  • OSSchedLock()对调度器加锁。

  • OSSchedUnlock()给已经上锁的任务调度器解锁。

    时间片轮转调度

    UCOSIII允许同一个优先级下有多个任务,每个任务可以执行指定的时间(时间片),然后轮到下一个任务,这个过程就是时间片轮转调度,当一个任务不想在运行的时候就可以放弃其时间片。要使用这个功能需定义 OS_CFG_SCHED_ROUND_ROBIN_EN为1。使用时间片轮转需要先进行配置,调用OSSchedRoundRobinCfg()函数对时间片轮转进行配置。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1fhpb1oR-1602231811206)(UCOSIII%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0(%E4%BA%8C)].assets/Snipaste_2020-10-06_13-38-34.png)

2、任务切换

当UCOSIII需要切换到另外一个任务时,它将保存当前任务的现场到当前任务的堆栈中,主要是CPU寄存器值,然后恢复新的现场并且执行新的任务,这个过程就是任务切换。任务切换分为两种:任务级切换和中断级切换。

任务级切换函数为:OSCtxSw()。

中断级切换函数为:OSIntCtxSw()。

任务调度函数OSSched()先确定是否已准备好新的高优先级任务,如果有则执行任务切换函数;任务切换OSCtxSw()的作用时进行上下文切换。

8、UCOSIII的任务管理常用API

创建任务OSTaskCreate
#include "includes.h"												//UCOSIII头文件

CPU_SR_ALLOC();														//为了保存/恢复中断状态。通过#define变量定义,用于保存SR状态。只有定义此变量,才可以使用临界区函数OS_CRITICAL_ENTER()

//任务创建所需参数、全局变量
#define START_TASK_PRIO		3										//任务优先级
#define START_STK_SIZE 		128										//任务堆栈大小
CPU_STK START_TASK_STK[START_STK_SIZE];								//任务堆栈
OS_TCB StartTaskTCB;												//任务控制块	
void start_task(void *p_arg);										//任务函数声明

int main()
{
	OS_ERR err;														//创建任务需使用的局部变量,存放返回错误类型。引用时需要取地址,&err。否则UCOSIII会跑飞。																											 
	
	delay_init(168);
	USART1_init(115200);
	
	OSInit(&err);		    										//初始化UCOSIII,初始化前完成各外设初始化
	OS_CRITICAL_ENTER();											//进入临界区

//创建开始任务
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,						//任务控制块
							 (CPU_CHAR	* )"start task", 			//任务名字
               (OS_TASK_PTR )start_task, 							//任务函数
               (void		* )0,									//传递给任务函数的参数
               (OS_PRIO	  )START_TASK_PRIO,   						//任务优先级
               (CPU_STK   * )&START_TASK_STK[0],					//任务堆栈基地址
               (CPU_STK_SIZE)START_STK_SIZE/10,						//任务堆栈深度限位
               (CPU_STK_SIZE)START_STK_SIZE,						//任务堆栈大小
               (OS_MSG_QTY  )0,										//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
               (OS_TICK	  )0,										//当使能时间片轮转时的时间片长度,为0时为默认长度
               (void   	* )0,										//用户补充的存储区
               (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, 	//任务选项
               (OS_ERR 	* )&err);									//存放该函数错误时的返回值
	
	OS_CRITICAL_EXIT();												//退出临界区			 
	OSStart(&err);      											//开启UCOSIII
}
删除任务OSTaskDel
OSTaskDel((OS_TCB*)0,&err);											//删除任务自身
OSTaskDel(&Task1_TaskTCB,&err);										//删除任务1;Task1_TaskTCB任务1的任务控制块
任务挂起OSTaskSuspend
OSTaskSuspend(&Task3_TaskTCB,&err);									//挂起任务3
任务恢复OSTaskResume
OSTaskResume(&Task3_TaskTCB,&err);									//恢复任务3

一个任务可以多次挂起,对一个任务挂起了n次,必须再对该任务恢复n次才可以成功恢复任务。

时间片轮转调度配置OSSchedRoundRobinCfg

os_cfg_app.h文件中的OS_CFG_TICK_RATE_HZ来调整时钟节拍的频率,默认为200Hz(5ms)。

#if	OS_CFG_SCHED_ROUND_ROBIN_EN  									//当使用时间片轮转的时候
		OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);					//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms  
#endif

//创建任务时,将 (OS_TICK)time_quanta 设为该任务所需时间片数
OSTaskCreate((OS_TCB 	* )&Task3_TaskTCB,			
             (CPU_CHAR	* )"Task3 task", 		
             (OS_TASK_PTR )task3_task, 			
             (void		* )0,					
             (OS_PRIO	  )TASK3_TASK_PRIO,     
             (CPU_STK   * )&TASK3_TASK_STK[0],	
             (CPU_STK_SIZE)TASK3_STK_SIZE/10,	
             (CPU_STK_SIZE)TASK3_STK_SIZE,		
             (OS_MSG_QTY  )0,					
             (OS_TICK	  )1,										//时间片数为1,任务所需时间为1*5ms			
             (void   	* )0,					
             (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
             (OS_ERR 	* )&err);	
放弃本次时间片OSSchedRoundRobinYield
OSSchedRoundRobinYield(&err);										//该任务放弃本次时间片

二、UCOSIII的Hook函数

钩子函数一般主要是用来扩展其他函数(任务)功能的,发生相关状态时自动调用,钩子函数有如下几个:

函数名功能
OSIdleTaskHook()空闲任务调用这个函数,可以用来让CPU进入低功耗模式。
OSInitHook()系统初始化函数OSInit()调用此函数。
OSStatTaskHook()统计任务每秒中都会调用这个函数,此函数允许你向统计任务中添加自己的应用函数。
OSTaskCreateHook()任务创建的钩子函数。
OSTaskDelHook()任务删除的钩子函数。
OSTaskReturnHook()任务意外返回时调用的钩子函数,比如删除某个任务
OSTaskSwHook()任务切换时候调用的钩子函数。
OSTimeTickHook()滴答定时器调用的钩子函数。

三、UCOSIII中断

UCOSIII中,在CPU 执行中断服务程序的时候有可能有更高优先级的任务就绪,那么当退出中断服务程序的时候,CPU 就会直接执行这个高优先级的任务。UCOSIII 支持中断嵌套,既高优先级的中断可以打断低优先级的中断,在 UCOSIII 中使用OSIntNestingCtr 来记录中断嵌套次数,最大支持250 级的中断嵌套,每进入一次中断服务函数OSIntNestingCtr 就会加1,当退出中断服务函数的时候OSIntNestingCtr 就会减1。

在编写UCOSIII的中断服务程序的时候需要使用到两个中断级任务调度器函数OSIntEnter()和OSIntExit()。

void XXX_IRQHandler(void)													//中断服务模板
{
		OSIntEnter();
    ...																		//具体中断服务程序
		OSIntExit();
}  

UCOSIII 对从中断发布消息或者信号的处理有两种模式:直接发布和延迟发布两种方式。宏 OS_CFG_ISR_POST_DEFERRED_EN 在 os_cfg.h 文件中有定义,当定义为 0 时使用直接发布模式,定义为 1 的时候使用延迟发布模式

1、直接发布

使用直接发布模式的话,UCOSIII 会对临界段代码采用关闭中断的保护措施,这样就会延长中断的响应时间。虽然UCOSIII 已经采用了所有可能的措施来降低中断关闭时间,但仍然有一些复杂的功能会使得中断关闭相对较长的时间。

2、延迟发布

UCOSIII 通过给任务调度器上锁的方法来保护临界段代码。在延迟发布模式下,UCOSIII 在访问中断队列时,仍然需要关闭中断,但这个时间是非常短的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值