1任务管理
1.1系统文件
delay
delay_ms会发起任务调度,最低可发起延时>5ms,从5ms开始不再使用任务调度
delay_us不发起任务调度。
Usart文件的差别在于中断服务函数
void USART1_IRQHandler(void)
{
u8 res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter(); //进入中断
#endif
...
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit(); //退出中断
#endif
}
os系统延时函数
//OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
OSTimeDlyHMSM(时,分,秒,毫秒,OS_OPT_TIME_HMSM_STRICT,&err)
1.2 任务基本概念
UCOSIII是一个可剥夺的多任务系统
任务就是程序实体。
UCOSIII中的任务由三部分组成:任务堆栈、任务控制块和任务函数。
任务堆栈:上下文切换的时候用来保存任务的工作环境,就是STM32的内部寄存器值。
任务控制块:任务控制块用来记录任务的各个属性。
任务函数:由用户编写的任务处理代码,是实实在在干活的,一般写法如下:
任务堆栈就是一个数组,任务控制是一个 OS_TCB 变量
1.2.1 UCOSII系统任务
UCOSIII默认有5个系统任务:
1、空闲任务:UCOSIII创建的第一个任务,UCOSIII必须创建的任务,此任务有UCoSIII自动创建,不需要用户手动创建。
2、时钟节拍任务:此任务也是必须创建的任务。
3、统计任务:可选任务,用来统计CPU使用率和各个任务的堆栈使用量。此任务是可选任务,由宏0S_CFG_STAT_TASK_EN控制是否使用此任务。
4、定时任务:用来向用户提供定时服务,也是可选任务,由宏0S_CFG_TVMR_E控制是否使用此任务。
5、中断服务管理任务:可选任务,由宏0S_CFG_ISR_POST_DEFERRED_EN控制是否使用此任务。
1.2.2 UCOSIII的任务状态
从用户的角度看,UCOSIII的任务一共有5种状态:
1、休眠态:任务已经在CPU的flash中了,但是还不受UCOSIII管理。
2、就绪态:系统为任务分配了任务控制块,并且任务已经在就绪表中登记,这时这个任务就具有了运行的条件,此时任务的状态就是就绪态。
3、运行态:任务获得CPU的使用权,正在运行。
4、等待态:正在运行的任务需要等待一段时间,或者等待某个事件,这个任务就进入了等待态,此时系统就会把CPU使用权转交给别的任务。
5、中断服务态:当发送中断,当前正在运行的任务会被挂起,cPU转而去执行中断服务函数,此时任务的任务状态叫做中断服务态。
1.3 任务堆栈
任务堆栈在RAM中按照先进先出原则,用于任务切换时保存上个任务运行状态时的CPU寄存器内容。
1.3.1 创建
#define STAR_STK_SIZE 512 //堆栈大小 512*4=2048个字节
CPU_STK STAR_TASK_STK[STAR_STK_SIZE ]; //定义一个数组作为任务堆栈
CPU_STK 是CPU_INT32U 无符号整型 unsigned int
1.3.2 初始化
在创建一个任务时,需先把启动这个任务所需的CPU寄存器值先存入任务堆栈,任务获得CPU使用权是就把任务堆栈内容复制进各个寄存器即可顺利运行,即任务堆栈的初始化。
初始化函数OSTaskStkInit,这个函数不会由用户自己调用,
由OSTaskCreate()调用。
1.3.3 使用堆栈
OSTaskCreate(...
(CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小
...
(OS_ERR * )&err); //存放该函数错误时的返回值
1.4 任务控制块结构
任务控制块是用来记录与任务相关的信息的数据结构,每个任务都要有自己的任务控制块。任务控制块由用户自行创建,如下代码为创建一个任务控制块:
0S_TCB StartTaskTCB;//创建一个任务控制块
0S_TCB为一个结构体,描述了任务控制块,任务控制块中的成员变量用户不能直接访问,更不可能改变他们。
0S_TCB为一个结构体,其中有些成员采用了条件编译的方式来确定
1.4.1 初始化
同堆栈,就是给结构体赋初值,基本都是0
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
....
(OS_ERR * )&err);
1.5 UCOSIII任务就绪表
1.5.1 优先级
数值越小优先级越大。
最低可用优先级是OS_CFG_PRIO_MAX-1
OS_CFG_PRIO_MAX是可用任务总个数,定义在os_cfg.h文件下
1.5.2就绪表
UCOSIII中就绪表由2部分组成:
1、优先级位映射表0SPrioTbl[]:用来记录哪个优先级下有任务就绪。
2、就绪任务列表0SRdyList[]:用来记录每一个优先级下所有就绪的任务。
CPU_DATA 0SPrioTbl[OS_PRIO_TBL_SIZE]
0SPrioTbl的数据类型是uint 32位,每个位对应一个优先级,当系统有64个优先级时OS_PRIO_TBL_SIZE=((64-1)/(4*8)+1)=2。
1.6 任务调度
1.6.1 可剥夺型任务调度
任务调度就是中止当前正在运行的任务转而去执行其他任务。
可剥夺型:当一个高优先级任务准备就绪,并发生了任务调度,高优先级任务就会执行。
任务调度器:任务级调度器和中断级调度器。
任务:OSSched()。在延时函数里
OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err); //延时200ms
中断: OSInitExit()。推出外部中断服务函数时使用中断级任务调度。
1.6.2 任务调度点
1、释放信号量或者发送消息,也可通过配置相应的参数不发生任务调度。
2、使用延时函数OSTimeDly()或者OSTimeDlylMSM()。(delay_ms)
3、任务等待的事情还没发生(等待信号量,消息队列等)。
4、任务取消等待。
5、创建任务。
6、删除任务。
7、删除一个内核对象。
8、任务改变自身的优先级或者其他任务的优先级。
9、任务通过调用0STaskSuspend0将自身挂起。
10、任务解挂某个挂起的任务。
11、退出所有的嵌套中断。
12、通过OSSchedUnlock()给调度器解锁。
13、任务调用OSSchedRoundRobinYield)放弃其执行时间片。
14、用产调用OSSched()。
1.6.3 调度器上锁解锁
OSSchedLock()对调度器上锁
OSSchedUnlock()解锁
1.6.4 时间片轮转调度
UCOSIII允许一个优先级下有多个任务,每个任务可以执行指定时间(时间片),然后轮到下一个任务,这个过程就是时间片轮转调度,任务不再运行后就可以放弃其时间片。
时间片轮转调度器: OS_SchedRoundRobin();
1.6.5 任务切换
UCOSIII需要切换到另一个任务时i,将保存当前任务的现场到当前任务的堆栈中,然后恢复新的现场并执行新的任务。
任务级切换函数OSCtxSw()。
中断级切换函数OSIntCtSw()。
1.7 UCOSIII系统初始化和启动
1.7.1 初始化
OSInit()函数完成初始化
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
MY_NVIC_PriorityGroupConfig(2); //中断分组配置
uart_init(72,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
while(1);
}
2 任务创建和删除
2.1创建
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 OPT TASK NONE 表示没有任何选项
//OS_OPT_TASK_STK_CHK 指定是否允许检测该任务的堆栈
//OSOPT_TASK_STK_CLR 指定是否清除该任务的堆栈
//OS_OPT_TASK_SAVE_F P指定是否存储浮点寄存器,CPU需要有浮点运算硬件并且有专用代码保存浮点寄存器。
OS_ERR *p_err) //保存创建任务失败导致的失败值
寄存器版本模板:
宏定义:
//任务优先级
#define START_TASK_PRIO 3
//任务堆栈大小
#define START_STK_SIZE 128
//任务控制块
OS_TCB StartTaskTCB;
//任务堆栈
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);
//任务优先级
#define TASK1_TASK_PRIO 5
//任务堆栈大小
#define TASK1_STK_SIZE 128
//任务控制块
OS_TCB Task1TaskTCB;
//任务堆栈
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
//任务函数
void task1_task(void *p_arg);
//任务优先级
#define TASK2_TASK_PRIO 6
//任务堆栈大小
#define TASK2_STK_SIZE 128
//任务控制块
OS_TCB Task2TaskTCB;
//任务堆栈
CPU_STK TASK2_TASK_STK[START_STK_SIZE];
//任务函数
void task2_task(void *p_arg);
主函数:
/
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72);
MY_NVIC_PriorityGroupConfig(2);
/
uart_init(72,115200);
LED_Init();
/
OSInit(&err); //Ucos初始化
OS_CRITICAL_ENTER(); //进入临界区
OSTaskCreate((OS_TCB* )&StartTaskTCB, //任务控制块
(CPU_CHAR* )"start_task", //任务名称
(OS_TASK_PTR )start_task, //任务函数
(void * )0, //声明任务名时的void *p_arg
(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,//任务内建消息队列
(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);
while(1);
}
开始任务:
///
//任务函数
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg=p_arg;//消除p_arg未使用警告
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
printf("进入临界区\r\n");
OSTaskCreate((OS_TCB* )&Task1TaskTCB, //任务控制块
(CPU_CHAR* )"task1_task", //任务名称
(OS_TASK_PTR )task1_task, //任务函数
(void * )0, //声明任务名时的void *p_arg
(OS_PRIO )TASK1_TASK_PRIO, //任务优先级
(CPU_STK * )&TASK1_TASK_STK[0], //任务堆栈的基地址
(CPU_STK_SIZE )TASK1_STK_SIZE/10, //栈深
(CPU_STK_SIZE )TASK1_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY )0,//任务内建消息队列
(OS_TICK )0,//时间编程
(void* )0,//
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR* )&err);
OSTaskCreate((OS_TCB* )&Task2TaskTCB, //任务控制块
(CPU_CHAR* )"task2_task", //任务名称
(OS_TASK_PTR )task2_task, //任务函数
(void * )0, //声明任务名时的void *p_arg
(OS_PRIO )TASK2_TASK_PRIO, //任务优先级
(CPU_STK * )&TASK2_TASK_STK[0], //任务堆栈的基地址
(CPU_STK_SIZE )TASK2_STK_SIZE/10, //栈深
(CPU_STK_SIZE )TASK2_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY )0,//任务内建消息队列
(OS_TICK )0,//时间编程
(void* )0,//
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR* )&err);
OS_CRITICAL_EXIT();//退出临界区
printf("创建完毕\n");
OSTaskDel((OS_TCB*)0,&err); //删除start_task任务自身
}
其他任务:
void task1_task(void *p_arg)
{
u8 task1_num=0;
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED_G_T;
task1_num++;
printf("任务1运行第%d次\n",task1_num);
if(task1_num==5)
{
OSTaskDel((OS_TCB *)&Task2TaskTCB,&err);
printf("任务1删除了任务2-------------\n");
}
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
}
}
void task2_task(void *p_arg)
{
u8 task2_num=0;
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED_R_T;
task2_num++;
printf("任务2运行D%d次\n",task2_num);
OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
}
}
2.2任务删除
void OSTaskDel(OS_TCB *p_tcb //任务控制块
OS_ERR *p_err); //错误信息
OSTaskDel((OS_TCB*)&Task2_TaskTCB,&err);
由于任务间可能会共享资源,因此尽量不要删除任务。
3 任务挂起与恢复
3.1 任务挂起
void OSTaskSuspend( OS_TCB *p_tcb,
OS_ERR *p_err);
3.2 任务恢复
void OSTaskResume(OS_TCB *p_tcb,
OS_ERR *p_err);
任务程序实例
void task1_task(void *p_arg)
{
u8 task1_num=0;
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED_G_T;
task1_num++;
printf("任务1运行第%d次\n",task1_num);
if(task1_num==5)
{
OSTaskSuspend((OS_TCB *)&Task2TaskTCB,&err);
printf("任务1挂起了任务2-------------\n");
}
if(task1_num==30)
{
OSTaskResume((OS_TCB *)&Task2TaskTCB,&err);
printf("任务1恢复了任务2-------------\n");
}
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
}
}
4 时间片轮转调度
1.宏定义 CPU_CFG_INT_DIS_MEAS_EN设置为1
2.运行 CPU_IntDisMeasMaxCurReset();放在开始任务中,打开所有任务之前
3.为需要时间片轮转调度的函数打开任务函数中分配时间片,每个任务时间片长度可以不同,但是需要注意每个任务运行需要的时间长度,不要让设定的时间片长度小于运行时间,时间片调度时前一个任务未执行完,会被强制打断,执行新的函数,等时间片轮转回到第一个函数时,才会继续上次未完成的程序。
设定时间片:
OSTaskCreate(...
(OS_TICK )2,//当使能时间片轮转时的时间片长度,为0时为默认长度,2=10ms
...
(OS_ERR* )&err);
程序输出实例: 任务1打印
123456789次**
123456789次
123456789次
123456789次–
等待1s,
任务2打印
ABCDEFG次**
ABCDEFG次
ABCDEFG次
ABCDEFG次–
等待1s
当时间片设定正常: 每次间隔1s
123456789次**
123456789次
123456789次
123456789次–
ABCDEFG次**
ABCDEFG次
ABCDEFG次
ABCDEFG次–
时间片设定过短
123456789次**
123456789次
1ABCDEFG次**
ABCDEFG次
ABCDEFG次
ABCDEFG次–
23456789次
123456789次–
5 系统内部任务
5.1 空闲任务
创建的第一个任务,必须创建的,优先级最低,总个数为64个时,优先级为63,OS_CFG_PRIO_MAX-1,空闲任务不可进入等待态。
里面有统计任务的参数,就是空闲任务执行的越多,统计任务的参数就越大,说明系统越空闲。
5.2 时钟节拍任务
用来追踪任务延时和任务等待超时,任务函数OS_TickTask。系统必须包含,任务优先级使用宏定义 OS_CFG_TICK_TASK_PRIO 定义,优先级设定为较高,设定为 1 ;
5.3 统计任务
用来统计CPU使用率,不是必须的。
1.宏定义 OS_CFG_STAT_TASK_EN 置1开启
2.在main函数中唯一的一个应用函数中使用,即start_task中
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
3.优先级宏定义 OS_CFG_STAT_TASK_PRIO ,一般设置为只比空闲任务高一级 64个任务时设定为62
OS_CFG_PRIO_MAX-2 ,倒数第二个优先级。
4.可以查看任务堆栈的使用情况 OSTaskStkChk
5.查看CPU使用率 OSStatTaskCPUUsage CPU 的使用率用一个0~10000 之间的整数表示(对应 0.00~100.00%),
5.4 定时任务
1.软件定时器,可选的
2.宏定义 OS_CFG_TMR_EN ,OSInit()中将调用函数OS_TmeInit()来创建定时任务
3.优先级通过 OS_CFG_TMR_TASK_PRIO 定义,默认设定为 2;
5.5 中断服务管理任务
1.使能宏定义 OS_CFG_ISR_POST_DEFERRED_EN
2.优先级:永远为0,不可更改
3.工作机制:将中断中的一部分功能移出中断,数据和目的地存入一个队列。
5.6 钩子函数
钩子函数一般主要是用来扩展其他函数(任务)功能的,钩子函数有如下几个:
1、OSIdleTaskHook(),空闲任务调用这个函数,可以用来让CPU进入低功耗模式
2、OSInitHook(),系统初始化函数OSInit()调用此函数。
3、OSStatTaskHook(),统计任务每秒中都会调用这个函数,此函数允许你向统计任务中添加自己的应用函数。
4、OSTaskCreateHook(),任务创建的钩子函数。
5、OSTaskDelHook(),任务删除的钩子函数。
6、OSTaskReturnHook(),任务意外返回时调用的钩子函数,比如删除某个任务
7、OSTaskSwHook(),任务切换时候调用的钩子函数。
8、OSTimeTickHook(),滴答定时器调用的钩子函数。
钩子函数会指向某个函数,需要更改钩子函数则更改指向的函数
6 中断和时间管理
6.1 中断
中断:应内部或外部异步事件的请求中止当前任务,而去处理异步事件所要求的任务的过程叫做中断。
中断服务函数:
void USART1_IRQHandler(void)
{
OSIntEnter();
//中断服务程序
OSIntExit();
}
void OSIntEnter (void)
{
if (OSRunning != OS_STATE_OS_RUNNING) {
return
}
if (OSIntNestingCtr >= (OS_NESTING_CTR)250u) {
return;
}
OSIntNestingCtr++;
}
OSIntNestingCtr来记录中断嵌套次数,UCOSIII最多支持250级的中断嵌套。退出中断服务函数时要调用函数OSIntExit()。
6.2 临界段代码保护
1.指一段代码必须完整运行,中间不可被打断的。
2.当宏OS_CFG_ISR_POST_DEFERRED_EN为0时,UCOSIII使用关中断的方式来保护临界段代码,
当设置为1的时候就会采用给调度器上锁的方式来保护临界段代码。
3.进入临界段代码的宏:OS_CRITICAL_ENTER()
退出临界段代码的宏:OS_CRITICAL_EXIT 和 OS_CRITICAL_EXIT_NO_SCHED()。
OS_CRITICAL_EXIT 有任务切换
OS_CRITICAL_EXIT_NO_SCHED 没有任务切换
4.如果需要临界代码区保护,必须加入函数 CPU_SR_ALLOC(); 。
6.3 时间管理
6.3.1 任务延时
UCOSIII的任务是无限循环且是一个抢占式内核,每个任务必须必须在合适的位置调用任务切换。
延时函数:OSTimeDly()和OSTimeDlyHMSM()
OSTimeDly()有三种工作模式:相对模式、周期模式和绝对模式。
OSTimeDlyHMSM()只有相对模式
OSTimeDly(参数1 时间片数,参数2 工作模式,参数3 错误码)
相对模式:OS_OPT_TIME_DLY 在系统负荷较重时有可能延时会少一个节拍,甚至偶尔差多个节拍,在周期
模式下,任务仍然可能会被推迟执行,但它总会和预期的“匹配值”同步。因此,推荐使用“周
期模式”来实现长时间运行的周期性延时
周期模式:OS_OPT_TIME_PERIODIC
绝对模式:OS_OPT_TIME_MATCH 可以用来在上电后指定的时间执行具体的动作,比如可以规定,上电 N 秒后
关闭某个外设
void OSTimeDlyHMSM (CPU_INT16U hours, //需要延时的小时数
CPU_INT16U minutes, //需要延时的分钟数
CPU_INT16U seconds, //需要延时的秒数
CPU_INT32U milli, //需要延时的毫秒数
OS_OPT opt, //选项
OS_ERR *p_err)
相比 OSTimeDly()函数多了两个选项 OS_OPT_TIME_HMSM_STRICT 和 OS_OPT_TIME_HMSM_NON_STRICT,其他四个选项都一样的。
使用 OS_OPT_TIME_HMSM_STRICT 选项的话将会检查延时参数,hours 的范围应该是 0~99, minutes 的范围应该是 0~59, seconds 的范围为 0~59,milli的范围为 0~999。
使用 OS_OPT_TIME_HMSM_NON_STRICT 选项的话, hours 的范围为 0~999,<minutes 的范围为 0~9999, seconds 的范围为 0~65535, mili 的范围为 0~4294967259。
7 软件定时器
7.1 简介
本质是递减计数器,减到0可以出发某种动作,动作内容在回调函数内。
数量可以任意。
分辨率由一个宏OS_CFG_TMR_TASK_RATE_HZ,单位为HZ,默认为100Hz,10ms。
注意:一定要避免在回调函数中使用阻塞调用或者可以阻塞或删除定时器任务的函数
宏定义使能 OS_CFG_TMR_EN 1u
回调函数:
A(&B){} B(){},则B称为A的回调函数。B函数作为指针传递给函数A
回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
7.2 函数
函数名 | 作用 |
---|---|
OSTmrCreate() | 创建定时器并制定运行模式 |
OSTmrDel() | 删除定时器 |
OSTmrRemainGet() | 获取定时器的剩余时间 |
OSTmrStart() | 启动定时器计数 |
OSTmrStateGet() | 获取当前定时器状态 |
OSTmrStop() | 停止计数器倒计时 |
创建定时器
void OSTmrCreate (OS_TMR *p_tmr,//指向定时器的指针,宏 OS_TMR 是一个结构体
CPU_CHAR *p_name,//定时器名称
OS_TICK dly,//初始化定时器的延迟值
OS_TICK period,//重复周期
OS_OPT opt,///定时器运行选项
//OS_OPT_TMR_ONE_SHOT 单次定时器
//OS_OPT_TMR_PERIODIC 周期定时器
OS_TMR_CALLBACK_PTR p_callback,//指向回调函数的名字。
void *p_callback_arg,//回调函数的参数。
OS_ERR *p_err)//调用此函数以后返回的错误码
OS_TMR tmr1; //定时器1
void tmr1_callback(void *p_tmr, void *p_arg); //定时器1回调函数
//创建定时器1
OSTmrCreate((OS_TMR *)&tmr1, //定时器1
(CPU_CHAR *)"tmr1", //定时器名字
(OS_TICK )200, //200*10=2000ms dly
(OS_TICK )50, //50*10=500ms
(OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式
(OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
(void *)0, //参数为0
(OS_ERR *)&err); //返回的错误码
停止寄存器
CPU_BOOLEAN OSTmrStop (OS_TMR *p_tmr, //定时器
OS_OPT opt,//选项
//OS_OPT_TMR_NONE 直接停止,无操作
//OS_OPT_TMR_CALLBACK 停止之后再调用一次回调函数
//OS_OPT_TMR_CALLBACK_ARG 给回调函数一个新的参数
void *p_callback_arg,
OS_ERR *p_err) //错误
///
OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err);
单次定时器 创建定时器,运行OSTmrStart() 之后才开始计时,计时结束后保持为0,OSTmrStart() 每次调用都会使定时器值返回dly。
周期定时器 创建定时器后运行OSTmrStart() 之后才开始计时,每次到达0后定时器值变为period
周期定时器有初始化延迟 (dly ),周期定时器中dly 只在第一个周期内有效。
流程:
1.创建定时器和回调函数:
OS_TMR tmr1; //定时器1
void tmr1_callback(void *p_tmr, void *p_arg); //定时器1回调函数
void tmr1_callback(void *p_tmr, void *p_arg) //定时器1回调函数
{
OS_ERR err;
static u8 tmr1_num=0;
printf("定时器1运行%d次\r\n",tmr1_num);
tmr1_num++; //定时器1执行次数加1
}
2.创建定时器,这是一个任务
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg=p_arg;//消除p_arg未使用警告
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
...
//创建定时器1
OSTmrCreate((OS_TMR *)&tmr1, //定时器1
(CPU_CHAR *)"tmr1", //定时器名字
(OS_TICK )200, //200*10=2000ms dly
(OS_TICK )50, //50*10=500ms
(OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式
(OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
(void *)0, //参数为0
(OS_ERR *)&err); //返回的错误码
...
OS_CRITICAL_EXIT();//退出临界区
OSTaskDel((OS_TCB*)0,&err); //删除start_task任务自身
}
3.启动定时器
OSTmrStart(&tmr1,&err); //开启定时器
4.关闭定时器
OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err);
8 信号量和互斥信号量
8.1信号量简介
信号量像是一种上锁机制,代码必须获得相应的钥匙才能继续执行,执行至被锁代码段则任务一直等待。
信号量用于控制对共享资源的保护,现在基本用于任务同步。
信号量的等待是有超市时间的,设定为0则一直等待,其他值超时后任务进入就绪态。
分类:二进制信号量和计数型信号量
二进制信号量只能取0和1两个值,计数器信号量的范围由宏定义OS_SEM_CTR决定,8位:0255;16位:065535;32位:0~4294967295。OS_SEM_CTR默认32位。二进制信号用于那种一次只能被一个任务使用的资源。
8.2信号量函数
OS_SEM XX 定义信号量变量
OSSemCreate() 创建信号量
OS_SEM_CTR OSSemPost() 释放或发出一个信号量
OS_SEM_CTR OSSemPend () 等待信号量
OS_OBJ_QTY OSSemPendAbort() 取消等待
OS_OBJ_QTY OSSemDel() 删除信号量
void OSSemSet() 强制设置一个信号量的值
/--------------------------------------OSSemCreate ------------------------------------------------/
详解:
void OSSemCreate (OS_SEM *p_sem, //信号量
CPU_CHAR *p_name, //信号量名称
OS_SEM_CTR cnt, //信号量初始值
OS_ERR *p_err); //错误
OS_OBJ_QTY OSSemDel (OS_SEM *p_sem, //信号量
OS_OPT opt, //选项
OS_ERR *p_err); //错误
选项:OS_OPT_DEL_NO_PEND 当没有任务请求信号量的时候
OS_OPT_DEL_ALWAYS 无条件删除
/------------------------------------OSSemPend -----------------------------------------------------/
OS_SEM_CTR OSSemPend (OS_SEM *p_sem, //信号量
OS_TICK timeout, //超时时间(时钟节拍)
OS_OPT opt, //选项
CPU_TS *p_ts, //时间戳 写0标识不需要时间戳
OS_ERR *p_err) //错误
选项: OS_OPT_PEND_BLOCKING //信号量无效时任务挂起
OS_OPT_PEND_NON_BLOCKING //信号量无效时任务直接返回
/--------------------------------------OSSemPendAbort -------------------------------------------/
OS_OBJ_QTY OSSemPendAbort (OS_SEM *p_sem, //信号量
OS_OPT opt, //选项
OS_ERR *p_err) //错误
选项:OS_OPT_PEND_ABORT_1 //只终止等待该信号量最高优先级的任务
OS_OPT_PEND_ABORT_ALL //终止所有等待该信号量的任务
OS_OPT_POST_NO_SCHED //禁止在本函数内执行任务调度
/--------------------------------------OSSemPost -------------------------------------------/
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,//信号量
OS_OPT opt, //选项
OS_ERR *p_err) //错误
选项:OS_OPT_POST_1 //只发送给等待该信号量最高优先级的任务
OS_OPT_POST_ALL //发送给所有等待该信号量的任务
OS_OPT_POST_NO_SCHED //禁止在本函数内执行任务调度
示例:
1.创建信号量变量
OS_SEM MY_SEM;
开始任务中创建信号量
OSSemCreate((OS_SEM* )&MY_SEM, //信号量
(CPU_CHAR* )"MY_SEM", //信号量名称
(OS_SEM_CTR )1, //信号量初始值
(OS_ERR* )&err); //错误
任务中等待信号量并释放信号量
void task2_task(void *p_arg)
{
OS_ERR err;
...
while(1)
{
OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err);//等待信号量
...
OSSemPost(&MY_SEM,OS_OPT_POST_1,&err); //释放信号量
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
}
}
8.3 互斥信号量
可剥夺内核中出现的优先级反转现象:
最低优先级的任务L运行中获得了信号量并执行,被最高优先级的任务H剥夺,运行至H等待信号量时切换回任务L,运行未结束被稍高级的任务M取得运行,M运行完毕后返回任务L,值任务H得到信号量才运行任务H,任务H从请求信号量到得到信号量中间出现了任务M运行优先于任务H,这段时间出现了优先级反转。
H ------- ---------------
M --------
L ------- — —
使用互斥信号量解决优先级反转问题,解决思路是在最高级等待信号量时将正在运行的最低任务的优先级提升到与等待信号量优先级一致的级别,防止较低优先级打断正在运行的任务。切换时间在最高H开始等待信号量的时刻。
H ------- ---------------
M --------
L ------- ------
8.4 互斥信号量函数
参数同普通信号量
void OSMutexCreate (OS_MUTEX *p_mutex,
CPU_CHAR *p_name,
OS_ERR *p_err);
OS_OBJ_QTY OSMutexDel (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err);
void OSMutexPend (OS_MUTEX *p_mutex,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err);
OS_OBJ_QTY OSMutexPendAbort (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err);
void OSMutexPost (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err);
示例:同信号量,注意替换函数,另外创建互斥信号量不需要参数初始化。
注意使用互斥信号量的低优先级任务运行时间不能太长。
8.5 任务内嵌信号量
每个任务都有自己的内嵌信号量,任务内只能等待本任务的信号量不能不等待其他任务的信号量,发布任务信号量没有限制。
函数:
OS_SEM_CTR OSTaskSemPend (OS_TICK timeout, //超时时间
OS_OPT opt,//选项
CPU_TS *p_ts,//时间戳
OS_ERR *p_err);//错误
选项: OS_OPT_PEND_BLOCKING //信号量无效时任务挂起
OS_OPT_PEND_NON_BLOCKING //信号量无效时任务直接返回
CPU_BOOLEAN OSTaskSemPendAbort (OS_TCB *p_tcb,//任务控制块
OS_OPT opt, //选项
OS_ERR *p_err);//错误
选项: OS_OPT_POST_NONE //无条件取消
OS_OPT_POST_NO_SCHED //无请求任务
OS_SEM_CTR OSTaskSemPost (OS_TCB *p_tcb,//任务控制块
OS_OPT opt, //选项
OS_ERR *p_err);//错误
选项: OS_OPT_POST_NONE //无条件
OS_OPT_POST_NO_SCHED //无请求任务
OS_SEM_CTR OSTaskSemSet (OS_TCB *p_tcb,//任务控制块
OS_SEM_CTR cnt,
OS_ERR *p_err);
实例:
任务内嵌信号量使用时不需要创建和变量,可直接使用
OSTaskSemPend() 等待信号量 //只能等待自己的信号量
void task1_task(void *p_arg)
{
OS_ERR err;
while(1)
{
OSTaskSemPend(0,OS_OPT_PEND_BLOCKING,0,&err);//只能等待自己的信号量
printf("任务一\r\n");
}
}
OSTaskSemPendAbort() 取消等待任务信号量
OSTaskSemPost() 发布任务信号量 //除参数与普通用法一样
void task2_task(void *p_arg)
{
OS_ERR err;
while(1)
{
if(KEY_Scan()==3)
{
printf("任务二\r\n");
OSTaskSemPost(&Task1TaskTCB,OS_OPT_POST_NONE,&err);
}
}
}
OSTaskSemSet() 强行设置任务信号量计数
OSTaskSemSet(&Task1TaskTCB,10,&err);//设定值
9 消息队列
struct os_q {
OS_OBJ_TYPE Type;
CPU_CHAR *NamePtr;
OS_PEND_LIST PendList;
OS_Q *DbgPrevPtr;
OS_Q *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
OS_MSG_Q MsgQ;
};
函数:
函数名 | 作用 |
---|---|
OSQCreate() | 创建一个消息 队列 |
OSQDel() | 删除一个消息队列 |
OSQFlush() | 清空消息队列 |
OSQPend() | 等待消息 |
OSQPendAbort() | 取消等待消息 |
OSQPost() | 向消息队列发布一则消息 |
1.void OSQCreate (OS_Q *p_q,//定义一个消息队列变量
CPU_CHAR *p_name,//命名
OS_MSG_QTY max_qty,//消息队列大小
OS_ERR *p_err); //错误码
2.OS_OBJ_QTY OSQDel (OS_Q *p_q, //消息队列变量
OS_OPT opt, //选项
OS_ERR *p_err); //错误码
选项:OS_OPT_DEL_NO_PEND //没有等待任务
OS_OPT_DEL_ALWAYS //直接删除
3.OS_MSG_QTY OSQFlush (OS_Q *p_q, //消息队列变量
OS_ERR *p_err);//错误码
4.void *OSQPend (OS_Q *p_q,//消息队列变量
OS_TICK timeout,//超时时间
OS_OPT opt,//选项
OS_MSG_SIZE *p_msg_size,//接收到的消息大小是多少
CPU_TS *p_ts,//时间戳
OS_ERR *p_err);//错误码
选项: OS_OPT_PEND_BLOCKING //信号量无效时任务挂起
OS_OPT_PEND_NON_BLOCKING //信号量无效时任务直接返回
5.OS_OBJ_QTY OSQPendAbort (OS_Q *p_q,//消息队列变量
OS_OPT opt,//选项
OS_ERR *p_err);//错误码
选项:OS_OPT_PEND_ABORT_1 //只终止等待该信号量最高优先级的任务
OS_OPT_PEND_ABORT_ALL //终止所有等待该信号量的任务
OS_OPT_POST_NO_SCHED //禁止在本函数内执行任务调度
6.void OSQPost (OS_Q *p_q,//消息队列变量
void *p_void,//消息内容
OS_MSG_SIZE msg_size,//发送的消息的大小(字节)
OS_OPT opt, //选项, 组合使用
OS_ERR *p_err);//错误码
选项:OS_OPT_POST_ALL //将消息发送给所有等待消息队列的任务
OS_OPT_POST_FIFO //将消息保存在消息队列的末尾
OS_OPT_POST_LIFO //将消息保存在消息队列的开头
OS_OPT_POST_NO_SCHED //不做任务切换
9.1 消息队列应用实例
1.创建变量
OS_Q Q_DATA; //数据队列
#define Q_DATA_MAX 5 //数据队列长度
2.创建消息队列
//开始任务中
OSQCreate ((OS_Q* )&Q_DATA,//定义一个消息队列变量
(CPU_CHAR* )"Q_DATA",//命名
(OS_MSG_QTY)Q_DATA_MAX,//消息队列大小
(OS_ERR* )&err); //错误码
3.发送队列
static char Uchars[]="msg_num=0\r\n";
sprintf(Uchars,"msg_num=%d\r\n",msg_num);
msg_num++;
OSQPost((OS_Q* )&Q_DATA,
(void* )Uchars,
(OS_MSG_SIZE)12,
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR* )&err);
if(err!=OS_ERR_NONE)//有可能是消息队列被放满了
{
OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err); //停止定时器1 此处让本段程序下次不再运行
printf("TMR1 STOP!\r\n");
}
4.请求消息队列
u8 *p;
OS_MSG_SIZE size;
OS_ERR err;
while(1)
{
p=OSQPend((OS_Q* )&Q_DATA,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_MSG_SIZE* )&size,
(CPU_TS* )0,
(OS_ERR* )&err);
printf("消息:%s",p);
printf("消息队列剩余%d,消息队列长度:%d\r\n",Q_DATA.MsgQ.NbrEntriesSize-Q_DATA.MsgQ.NbrEntries,Q_DATA.MsgQ.NbrEntriesSize);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时
}
9.2 任务内建消息队列
使能宏定义 OS_CFG_TASK_Q_EN 使能
函数
函数名 | 作用 |
---|---|
OSTaskQPend() | 等待消息 |
OSTaskQPendAbort() | 取消等待消息 |
OSTaskQPost() | 向任务发布一则消息 |
OSTaskQFlush() | 清空任务的消息队列 |
实例:
1.定义消息队列大小
#define TASK_Q_NUM 4 //发任务内建消息队列的长度
2.创建任务时加入消息队列长度
OSTaskCreate((OS_TCB * )&Msgdis_TaskTCB,
(CPU_CHAR * )"Msgdis task",
(OS_TASK_PTR )msgdis_task,
(void * )0,
(OS_PRIO )MSGDIS_TASK_PRIO,
(CPU_STK * )&MSGDIS_TASK_STK[0],
(CPU_STK_SIZE)MSGDIS_STK_SIZE/10,
(CPU_STK_SIZE)MSGDIS_STK_SIZE,
(OS_MSG_QTY )TASK_Q_NUM, //任务Msgdis_task需要使用内建消息队列,消息队列长度为4
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
3.发送消息队列
不再向队列发送,向任务发送。
u8 *pbuf;
OSTaskQPost((OS_TCB* )&Msgdis_TaskTCB, //向任务Msgdis发送消息
(void* )pbuf,
(OS_MSG_SIZE)10,
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR* )&err);
4.等待消息队列
u8 *p;
OS_MSG_SIZE size;
//请求消息
p=OSTaskQPend((OS_TICK )0, //自建消息队列
(OS_OPT )OS_OPT_PEND_BLOCKING, //选项,无消息挂起
(OS_MSG_SIZE* )&size,//接收到了多少字节
(CPU_TS* )0, //时间戳
(OS_ERR* )&err );//错误
5.带内存管理的消息队列
void USART1_IDLE_task(void *p_arg)
{
OS_ERR err;
u8 *pbuf;
OS_MSG_SIZE size;
while(1)
{
pbuf = mymalloc(SRAMIN,USART1_RX_STA); //申请 字节
//请求消息
pbuf=OSTaskQPend((OS_TICK )0, //自建消息队列
(OS_OPT )OS_OPT_PEND_BLOCKING, //选项,无消息挂起
(OS_MSG_SIZE* )&size,//接收到了多少字节
(CPU_TS* )0,//时间戳
(OS_ERR* )&err );//错误
USART1_Send_Len(pbuf,USART1_RX_STA);
printf("size%d",size);
myfree(SRAMIN,pbuf); //释放内存
OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_HMSM_STRICT,&err); //延时
}
}
10 事件标志组
有时候一个任务需要与多个事件同步,这个时候就需要使用事件标志组。事件标志组与任务之间有两种同步机制:“或”同步和“与”同步。
“或”同步:等待多个事件时,任何一个事件发生 ,任务都被同步,这个就称为“或”同步。
“与”同步:当所有的事件都发生时任务才被同步,这种同步机制被称为“与”同步。
在UCOSIII中事件标志组为OS_FLAG_GRP,如果需要使用事件标志组的时候需要将宏OS_CFG_FLAG_EN置1,
函数:
函数名 | 作用 |
---|---|
OSFlagCreate() | 创建事件标志组 |
OSFlagDel() | 删除事件标志组 |
OSFlagPend() | 等待事件标志组 |
OSFlagPendAbort() | 取消等待事件标志 |
OSFlagPendGetFlagsRdy() | 获取使任务就绪的事件标志 |
OSFlagPost() | 向事件标志组发布标志 |
函数详解:
1.void OSFlagCreate (OS_FLAG_GRP *p_grp,//指向事件标志组
CPU_CHAR *p_name,//事件标志组的名字
OS_FLAGS flags,//定义事件标志组的初始值
OS_ERR *p_err);//错误码
2.OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp,//指向事件标志组
OS_FLAGS flags,//bit 序列,任务需要等待事件标志组的哪个位就把这个序列对应的位置 1, bit0和 bit1 时(无论是等待置位还是清零), flag 是的值就为 0X03。
OS_TICK timeout,//超时时间(时钟节拍数)
OS_OPT opt,决定任务等待的条件
CPU_TS *p_ts,//时间戳
OS_ERR *p_err);//错误码
等待的条件 :
OS_OPT_PEND_FLAG_CLR_ALL //等待事件标志组所有的位清零
OS_OPT_PEND_FLAG_CLR_ANY //等待事件标志组中任意一个标志清零
OS_OPT_PEND_FLAG_SET_ALL //等待事件标志组中所有的位置位
OS_OPT_PEND_FLAG_SET_ANY //等待事件标志组中任意一个标志置位
调用上面四个选项的时候还可以搭配下面三个选项
OS_OPT_PEND_FLAG_CONSUME //用来设置是否继续保留该事件标志的状态。满足条件后对应位清零
OS_OPT_PEND_NON_BLOCKING //标志组不满足条件时不挂起任务。
OS_OPT_PEND_BLOCKING //标志组不满足条件时挂起任务。
3.OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp,//指向事件标志组
OS_FLAGS flags,//决定对哪些位清零和置位
OS_OPT opt,//决定对标志位的操作
OS_ERR *p_err);//错误码
操作
OS_OPT_POST_FLAG_SET 对标志位进行置位操作
OS_OPT_POST_FLAG_CLR 对标志位进行清零操作
10.1 事件标志位程序实例
1.定义事件标志位变量
OS_FLAG_GRP FLAG_KEY;
2.创建事件标志位
OSFlagCreate((OS_FLAG_GRP* )&FLAG_KEY,//指向事件标志组
(CPU_CHAR* )"FLAG_KEY",//事件标志组的名字
(OS_FLAGS )0,//定义事件标志组的初始值
(OS_ERR* )&err);//错误码
3.向事件标志组发布标志
OSFlagPost ((OS_FLAG_GRP*)&FLAG_KEY,//指向事件标志组
(OS_FLAGS )key,//决定对哪些位清零和置位
(OS_OPT )OS_OPT_POST_FLAG_SET,//决定对标志位的操作
(OS_ERR* )&err);//错误码
4.等待事件标志组
OSFlagPend((OS_FLAG_GRP* )&FLAG_KEY,//指向事件标志组
(OS_FLAGS )0x03,//bit 序列,任务需要等待事件标志组的哪个位就把这个序列对应的位置 1, bit0和 bit1 时(无论是等待置位还是清零), flag 是的值就为 0X03。
(OS_TICK )100,//超时时间(时钟节拍数)
(OS_OPT )OS_OPT_PEND_FLAG_SET_ALL+OS_OPT_PEND_FLAG_CONSUME,//决定任务等待的条件 等待事件标志组中所有的位置位+满足条件后对应位清零
(CPU_TS* )0,//时间戳
(OS_ERR* )&err);//错误码
if(err==OS_ERR_TIMEOUT) ///报错
{
printf("超时 %X\r\n",FLAG_KEY.Flags);
}
else
{
printf("OK %X\r\n",FLAG_KEY.Flags);
}
10.2 等待多个事件标志
等待单个内核对象,包括:信号量、互斥信号量、消息队列和事件标志组。
在UCOSIII中允许任务同时等待多个信号量和多个消息队列,也就是说,UCOSIII不支持同时等待多个事件标志组或互斥信号量。
一个任务可以等待任意数量的信号量和消息队列,第一个信号量或消息队列的发布会导致该任务进入就绪态。
使能宏定义 OS_CFG_PEND_MULTI_EN
函数:
OS_OBJ_QTY OSPendMulti (OS_PEND_DATA*p_pend_data_tbl,//指向 OS_PEND_DATA 表的指针
OS_OBJ_QTY tbl_size,//表 p_pend_data_tbl 的大小,也就是所等待的内核对象数量
OS_TICK timeout,//超时值(时钟节拍数)
OS_OPT opt,//是否使用阻塞模式
OS_ERR *p_err);//错误码
opt:
OS_OPT_PEND_BLOCKING //如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。
OS_OPT_PEND_NON_BLOCKING //如果消息队列没有任何消息的话任务就直接返回。
p_pend_data_tbl :
调用该函数的时候首先必须初始化 OS_PEND_DATA 表中的每个元素的PendObjPtr,使得各个指针指向被等待的对象。
10.3 等待多个事件标志实例
相当于只有一个使用的函数 OSPendMulti 就在合适的位置上调用这个函数就可以了,注意先初始化数组(OS_PEND_DATA* )pend_multi_tbl
1.使能宏定义 OS_CFG_PEND_MULTI_EN
2.定义内核对象个数
#define CORE_OBJ_NUM 3
3.函数内定义一个OS_PEND_DATA数组,数组元素为事件
OS_PEND_DATA pend_multi_tbl[CORE_OBJ_NUM];
pend_multi_tbl[0].PendObjPtr=(OS_PEND_OBJ*)&Sem1;
pend_multi_tbl[1].PendObjPtr=(OS_PEND_OBJ*)&Sem2;
pend_multi_tbl[2].PendObjPtr=(OS_PEND_OBJ*)&Test_Q;
4.等待
OS_OBJ_QTY index;
index=OSPendMulti((OS_PEND_DATA* )pend_multi_tbl,
(OS_OBJ_QTY )CORE_OBJ_NUM, //内核数量
(OS_TICK )20,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_ERR* )&err);
5.完整函数
void task2_task(void *p_arg)
{
OS_ERR err;
OS_OBJ_QTY index;
OS_PEND_DATA pend_multi_tbl[CORE_OBJ_NUM];
pend_multi_tbl[0].PendObjPtr=(OS_PEND_OBJ*)&Sem1;
pend_multi_tbl[1].PendObjPtr=(OS_PEND_OBJ*)&Sem2;
pend_multi_tbl[2].PendObjPtr=(OS_PEND_OBJ*)&Test_Q;
while(1)
{
index=OSPendMulti((OS_PEND_DATA* )pend_multi_tbl,
(OS_OBJ_QTY )CORE_OBJ_NUM, //内核数量
(OS_TICK )20,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(OS_ERR* )&err);
if(err==OS_ERR_TIMEOUT)
{
printf("超时 index=%d\r\n",index);
}
else
{
printf("%d个内核准备好了\r\n",index);
}
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时
}
}