目录
消息队列:
消息列队相关的API函数
创建消息队列:
等待消息列队:
向消息列队发送消息:
消息队列实验
任务内嵌消息队列:
任务内建消息队列的API函数
等待任务内建消息:
发送任务内建消息:
任务内建消息队列实验:
一个任务或者中断有时候需要和另一个任务交流信息,这个就是消息传递的过程就叫做任务间通信,任务之间的信息传递有两种途径,一是使用全局变量,二是通过发布消息。
使用全局变量的时候,每一个任务或者中断服务程序都必须保证气全局变量的独占访问,(通常解决方式:关中断、临界区、信号量)。消息也可以通过消息队列作为中介发布给任务。
消息队列:
下边是正点原子的解释:
中断服务程序中只能使用OSPost()函数!!!在UCOSIII中对于消息队列的读取可以使先进先出(FIFO)也可以是后进先出(LIFO)的方式。接收消息的任务旁边的小沙漏表示任务可以指定一个超时时间,如果任务在这段时间内没有接收到消息的话就会唤醒任务,并且返回一个错误码告诉UCOSIII超时,任务是因为接收消息超时而被唤醒的,不是因为接收到了消息。如果将这个超时时
间指定为0的话,那么任务就会一直等待下去,直到接收到消息。消息列队中有一个列表,记录了所有正在等待获取得消息的任务,
消息列队相关的API函数
有关消息列队的API函数:
我们经常用的关于消息队列的函数主要有三个,创建消息队列函数OSQCreate(),向消息队列发送消息函数OSQPost()和等待消息队列函数OSQPend()。
创建消息队列:
OSQCreate函数用来创建一个消息队列,创建信号列队之前要先定义一个信号列队对象OS_Q + 列队对象名字;,消息队列使得任务或者中断服务程序可以向一个或者多个任务发送消息,函数原型如下:
p_q:指向一个消息队列,消息队列的存储空间必须由应用程序分配。
p_name:消息队列的名字。
max_qty: 指定消息队列的长度,必须大于 0。当然,如果 OS_MSGs 缓冲池中没有足够多的 OS_MSGs 可用,那么发送消息将会失败,并且返回相应的错误码,指明当前没有可用的 OS_MSGs。
p_err:保存调用此函数后返回的错误码
等待消息列队:
当一个任务想要从消息队列中接收一个消息的话就需要使用函数 OSQPend()。当任务调用这个函数的时候,如果消息队列中有至少一个消息时,这些消息就会返回给函数调用者。函数原型如下:
p_q:指向一个消息队列。
timeout:等待消息的超时时间,如果在指定的时间没有接收到消息的话,任务就会被唤醒,接着运行。这个参数也可以设置为0,表示任务将一直等待下去,直到接收到消息。
opt:用来选择是否使用阻塞模式,有两个选项可以选择 OS_OPT_PEND_BLOCKING 如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。 OS_OPT_PEND_NON_BLOCKING 如果消息队列没有任何消息的话任务就直接返回。
p_msg_size: 指向一个变量用来表示接收到的消息长度(字节数)。
p_ts:指向一个时间戳,表明什么时候接收到消息。如果这个指针被赋值为 NULL的话,说明用户没有要求时间戳。
p_err:用来保存调用此函数后返回的错误码。如果消息队列中没有任何消息,并且参数 opt 为OS_OPT_PEND_NON_BLOCKING 时,那么调用 OSQPend()函数的任务就会被挂起,直到接收到消息或者超时。如果有消息发送给消息队列,但是同时有多个任务在等待这个消息,那么 UCOSIII 将恢复等待中的最高优先级的任务。
向消息列队发送消息:
可以通过函数 OSQPost()向消息队列发送消息,如果消息队列是满的,则函数 OSQPost()就
会立刻返回,并且返回一个特定的错误代码,函数原型如下:
如果有多个任务在等待消息队列的话,那么优先级最高的任务将获得这个消息。如果等待消息的任务优先级比发送消息的任务优先级高,则系统会执行任务调度,等待消息的任务立即恢复运行,而发送消息的任务被挂起。可以通过 opt 设置消息队列是FIFO 还是 LIFO。如果有多个任务在等待消息队列的消息,则 OSQPost()函数可以设置仅将消息发送给等待任务中优先级最高的任务(opt 设置为 OS_OPT_POST_FIF 或者 OS_OPT_POST_LIFO),也可以将消息发送给所有等待的任务(opt 设置为OS_OPT_POST_ALL) 。 如 果 opt 设 置OS_OPT_POST_NO_SCHED,则在发送完消息后,会进行任务调度。
p_q:指向一个消息队列。
p_void:指向实际发送的内容,p_void 是一个执行 void 类型的指针,其具体含义由用户程序的决定。
msg_size: 设定消息的大小,单位为字节数。
opt:用来选择消息发送操作的类型,基本的类型可以有下面四种。
OS_OPT_POST_ALL将消息发送给所有等待该消息队列的任务,需要和选项 OS_OPT_POST_FIFO 或者
OS_OPT_POST_LIFO 配合使用。
OS_OPT_POST_FIFO待发送消息保存在消息队列的末尾
OS_OPT_POST_LIFO待发送的消息保存在消息队列的开头
OS_OPT_POST_NO_SCHED禁止在本函数内执行任务调度。
我们可以使用上面四种基本类型来组合出其他几种类型,如下:
OS_OPT_POST_FIFO + OS_OPT_POST_ALL
OS_OPT_POST_LIFO + OS_OPT_POST_ALL
OS_OPT_POST_FIFO + OS_OPT_POST_NO_SCHED
OS_OPT_POST_LIFO + OS_OPT_POST_NO_SCHED OS_OPT_POST_FIFO + OS_OPT_POST_ALL +
OS_OPT_POST_NO_SCHED
OS_OPT_POST_LIFO + OS_OPT_POST_ALL + OS_OPT_POST_NO_SCHEDp_err:用来保存调用此函数后返回的错误码。
消息队列实验
设计一个应用程序,该程序有 4 任务、两个消息队列和一个定时器。任务A 用于创建其他 3 个任务。任务B 为主任务,用于检测按键,并且将按键的值通过消息队列KEY_Msg 发送给任任务C,任务B 任务还用于检测消息队列 DATA_Msg的总大小和剩余空间大小,并且控制 LED0 的闪烁。任务C获取KEY_Msg内的消息,根据不同的消息做出相应的处理,定时器1的回调函数tmr1_callback通过消息队列DATA_Msg,将定时器1的运行次数作为信息发给任务D,任务D将DATA_Msg中的消息通过串口打印出来。
实验代码:
#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "includes.h" #include "os_app_hooks.h" #include "key.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //创建任务A、、开始任务 //定义任务优先级 #define TASK_A_PRIO 3 //定义任务控制块 OS_TCB TASK_A_TCB; //定义任务堆栈大小 #define TASK_A_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_A_STK[TASK_A_STK_SIZE]; //定义任务函数 void TASK_A(void *arg); //创建任务B //定义任务优先级 #define TASK_B_PRIO 4 //定义任务控制块 OS_TCB TASK_B_TCB; //定义任务堆栈大小 #define TASK_B_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_B_STK[TASK_B_STK_SIZE]; //定义任务函数 void TASK_B(void *arg); //创建任务C //定义任务优先级 #define TASK_C_PRIO 5 //定义任务控制块 OS_TCB TASK_C_TCB; //定义任务堆栈大小 #define TASK_C_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_C_STK[TASK_C_STK_SIZE]; //定义任务函数 void TASK_C(void *arg); //创建任务C //定义任务优先级 #define TASK_D_PRIO 6 //定义任务控制块 OS_TCB TASK_D_TCB; //定义任务堆栈大小 #define TASK_D_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_D_STK[TASK_D_STK_SIZE]; //定义任务函数 void TASK_D(void *arg); 共享资源 //u8 share_res[256]; 定义事件标志 //#define KEY0_FLAG 0x01//0000 0001 //#define KEY1_FLAG 0x02//0000 0010 定义事件标志组 //OS_FLAG_GRP EventFlag; //先定义两个消息队列和一个定时器的宏 消息队列/// #define KEYMSG_Q_NUM 1 //按键消息队列数量 #define DATAMSG_Q_NUM 4 //发送数据的消息队列的数量 OS_Q KEY_Msg; //定义一个消息队列,用于按键的消息传递模拟消息邮箱 OS_Q DATA_Msg; //定义一个消息列队用于发送数据 定时器/// u8 tmrlsta = 0;//标记定时器的工作状态 OS_TMR tmr1;//定义一个定时器 void tmr1_callback(void *p_arg); //定时器1回调函数 void check_msg_queue(u8 *p) { CPU_SR_ALLOC(); u8 msgq_remain_size; //消息队列剩余大小 OS_CRITICAL_ENTER(); //进入临界段 msgq_remain_size = DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries; sprintf((char*)p,"Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); //显示DATA_Msg消息队列总的大小 printf("Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); sprintf((char*)p,"Remain Size:%d",msgq_remain_size); //显示DATA_Msg剩余大小 printf("Remain Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); OS_CRITICAL_EXIT(); //退出临界段 } int main(void) { OS_ERR err1;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 //硬件初始化 delay_init(); //延时初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 uart_init(115200); //串口波特率设置 LED_Init(); KEY_Init(); OSInit(&err1);//初始化UCOSIII OS_CRITICAL_ENTER();//进入临界区代码 //创建开始任务A OSTaskCreate((OS_TCB * )&TASK_A_TCB, //任务控制块 (CPU_CHAR * )"main TASKA", //任务名字 (OS_TASK_PTR )TASK_A, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_A_PRIO, //任务优先级 (CPU_STK * )&TASK_A_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_A_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_A_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 * )&err1); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 OSStart(&err1);//开启UCOSIII while(1); } //开始任务函数 void TASK_A(void *arg) { OS_ERR err2_3;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 arg = arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err2_3); //统计任务 #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,&err2_3); #endif //#if OS_CFG_APP_HOOKS_EN //使用钩子函数 // App_OS_SetAllHooks(); //#endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //使用时间片轮转调度功能,时间片长度为一个时钟节拍即1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif OS_CRITICAL_ENTER();//进入临界区代码 //创建消息队列KEY_Msg OSQCreate ((OS_Q* )&KEY_Msg, //消息队列 (CPU_CHAR* )"KEY Msg", //消息队列名称 (OS_MSG_QTY )KEYMSG_Q_NUM, //消息队列长度,这里设置为1 (OS_ERR* )&err2_3); //错误码 //创建消息队列DATA_Msg OSQCreate ((OS_Q* )&DATA_Msg, (CPU_CHAR* )"DATA Msg", (OS_MSG_QTY )DATAMSG_Q_NUM, (OS_ERR* )&err2_3); //创建定时器1 OSTmrCreate((OS_TMR *)&tmr1, //定时器1 (CPU_CHAR *)"tmr1", //定时器名字 (OS_TICK )0, //0ms (OS_TICK )50, //50*10=500ms (OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式 (OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数 (void *)0, //参数为0 (OS_ERR *)&err2_3); //返回的错误码 //创建开始任务B 主函数 OSTaskCreate((OS_TCB * )&TASK_B_TCB, //任务控制块 (CPU_CHAR * )"main TASKB", //任务名字 (OS_TASK_PTR )TASK_B, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_B_PRIO, //任务优先级 (CPU_STK * )&TASK_B_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_B_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_B_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 * )&err2_3); //存放该函数错误时的返回值 //创建开始任务C 创建按键扫描任务函数 OSTaskCreate((OS_TCB * )&TASK_C_TCB, //任务控制块 (CPU_CHAR * )"main TASKC", //任务名字 (OS_TASK_PTR )TASK_C, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_C_PRIO, //任务优先级 (CPU_STK * )&TASK_C_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_C_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_C_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 * )&err2_3); //存放该函数错误时的返回值 //创建开始任务D 创建MSGDIS任务 OSTaskCreate((OS_TCB * )&TASK_D_TCB, //任务控制块 (CPU_CHAR * )"main TASKD", //任务名字 (OS_TASK_PTR )TASK_D, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_D_PRIO, //任务优先级 (CPU_STK * )&TASK_D_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_D_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_D_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 * )&err2_3); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 //任务一执行完函数之后删掉自身 OSTaskDel((OS_TCB *)0,&err2_3); } //任务B任务函数 用于检测按键 void TASK_B(void *arg) { OS_ERR err_B; u8 key; OS_FLAGS num_flag; while(1) { key = KEY_Scan(0); //扫描按键 if(key) { OSQPost((OS_Q* )&KEY_Msg, (void* )&key, (OS_MSG_SIZE)1, (OS_OPT )OS_OPT_POST_FIFO, (OS_ERR* )&err_B);//消息发送 printf("任务B发送信息\r\n"); } LED1 = ~LED1; OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时10ms } } //任务C任务函数 按键处理任务的任务函数 void TASK_C(void *arg) { u8 num; u8 *key; OS_MSG_SIZE size; OS_ERR err_C; while(1) { //请求消息KEY_Msg key=OSQPend((OS_Q* )&KEY_Msg, (OS_TICK )0, (OS_OPT )OS_OPT_PEND_BLOCKING, (OS_MSG_SIZE* )&size, (CPU_TS* )0, (OS_ERR* )&err_C); printf("任务C请求消息\r\n"); printf("任务C接收到的信号值:%d\r\n",*key); switch(*key) { case WKUP_PRES: //KEY_UP控制LED1 LED1 = ~LED1; break; case KEY0_PRES: //KEY0刷新LCD背景 num++; break; case KEY1_PRES: //KEY1控制定时器1 tmrlsta = !tmrlsta; if(tmrlsta) { OSTmrStart(&tmr1,&err_C); printf("定时器1开启\r\n"); } else { OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err_C); //停止定时器1 printf("定时器1关闭\r\n"); } break; } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_C); //延时1s } } //任务D任务函数 显示消息队列中的消息 void TASK_D(void *arg) { u8 *p; OS_MSG_SIZE size; OS_ERR err; while(1) { //请求消息 p=OSQPend((OS_Q* )&DATA_Msg, (OS_TICK )0, (OS_OPT )OS_OPT_PEND_BLOCKING, (OS_MSG_SIZE* )&size, (CPU_TS* )0, (OS_ERR* )&err); printf("任务D请求消息\r\n"); printf("任务D接收到的信号值:%d\r\n",*p); OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err); //延时1s } } //定时器1回调函数 void tmr1_callback(void *p_arg) { static u8 num = 0; OS_ERR err; num++; OSQPost((OS_Q* )&DATA_Msg, (void* )&num, (OS_MSG_SIZE)1, (OS_OPT )OS_OPT_POST_FIFO, (OS_ERR* )&err);//消息发送 printf("定时器发送信号:%d\r\n",num); }
上面有四个函数:TASK_A/B/C/D,这四个函数分别为定时器 1的回调函数、主任务的任务函数、按键处理任务的任务函数和显示任务的任务函数。tmrl callback0()函数是定时器1的回调函数,在TASK_A任务中我们是创建了一个定时器tmrl的,tmrl是一个周期定时器,定时周期为500ms。在 tmrl 的回调函数 tmrl callback0()中通过函数 OSQPost0向消息队列 DATA Msg 发送消息,这里向消息队列发送数据采用的是 FIFO方式,当发送失败的话就释放相应的内存并关闭定时器。TASK_B函数为主任务的任务函数,在这个函数中我们不断的扫描按键的键值,然后将键值发到消息队列 KEY Msg 中,这里向消息队列发送数据采用的是 FIFO 方式。
TASK_C为按键处理任务,在 main task 务中我们将按键值发送到了消息队列KEY Msg 中,在本函数中我们调用OSQPend0函数从消息队列 KEY Msg 中取消息,也就是按键值,然后根据不同的键值做出相应的处理。KEY UP 控制 LEDKEY0用来控制刷新 ;KEY1 控制 tmr1 的开关。
TASK_D()通过调用OSOPend0函数获得消息队列 DATA Msg 中的数据,并将获得到的
消息显示在 串口上。实验现象:
任务内嵌消息队列:
和信号量一样,UCOSIII中每个任务也都有其内建消息队列,这样的话用户就不需要使用外部的消息队列就可以直接向任务发布消息,这个特性不仅简化了代码,而且比使用外部消息队列更加有效。
根据函数原型可知使用前应该现将OS_CFG_TASK_Q_EN置1,
任务内建消息队列的API函数
等待任务内建消息:
函数 OSTaskQPend()用来请求消息,该函数让任务直接接收从其他任务或者 ISR 中发送来的消息,不需要经过中间的消息队列,函数原型如下:
timeout:等待消息的超时时间,如果在指定的时间没有接收到消息的话,任务就会被唤醒,
接着运行。这个参数也可以设置为 0,表示任务将一直等待下去,直到接收到消息。opt:用来选择是否使用阻塞模式,有两个选项可以选择。
OS_OPT_PEND_BLOCKING如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。
OS_OPT_PEND_NON_BLOCKING 如果消息队列没有任何消息的话任务就直接返回。p_msg_size: 指向存放消息大小的变量。
p_ts:指向一个时间戳,表明什么时候接收到消息。如果这个指针被赋值为 NULL的话,说明用户没有要求时间戳。
p_err:用来保存调用此函数后返回的错误码。
发送任务内建消息:
函数 OSTaskQPost()可以通过一个任务的内建消息队列向这个任务发送一条消息,同外置的
消息队列一样,一条消息就是一个指针,函数原型如下:
p_tcb:指向接收消息的任务的 TCB,可以通过指定一个 NULL 指针或该函数调用者的TCB 地址来向该函数的调用者自己发送一条消息。
p_void:发送给一个任务的消息。
msg_size:指定发送的消息的大小(字节数)。
opt:指定发送操作的类型,LIFO 和 FIFO 只能二选一。
OS_OPT_POST_FIFO待发送消息保存在消息队列的末尾(先进先出)
OS_OPT_POST_LIFO待发送的消息保存在消息队列的开头(后进先出)上面两个选项可以搭配下面这个选项一起使用。OS_OPT_POST_NO_SCHED指定该选项时,在发送后不会进行任务调度,因此,该函数的调用者还可以继续运行。
p_err:用来保存调用此函数后返回的错误码。
任务内建消息队列实验:
设计一个应用程序,该程序有 3 任务和一个定时器。任务 TASK_A用于创建其他 2 个任务。TASK_B任务为主任务,用于检测按键,当检测到按键 KWY_UP 被按下的时候就开启或关闭定时器 1, TASK_B任务还用于检测 任务C任务的内建消息队列的总大小和剩余空间大小,并且控制 LED0 的闪烁。
定时器 1 的回调函数 tmr1_callback 通过任务 任务C内建的消息队列将定时器 1 的运行次数作为信息发送给任务C,任务C将自带的队列中的消息显示在 串口上。#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "includes.h" #include "os_app_hooks.h" #include "key.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //创建任务A、、开始任务 //定义任务优先级 #define TASK_A_PRIO 3 //定义任务控制块 OS_TCB TASK_A_TCB; //定义任务堆栈大小 #define TASK_A_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_A_STK[TASK_A_STK_SIZE]; //定义任务函数 void TASK_A(void *arg); //创建任务B //定义任务优先级 #define TASK_B_PRIO 4 //定义任务控制块 OS_TCB TASK_B_TCB; //定义任务堆栈大小 #define TASK_B_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_B_STK[TASK_B_STK_SIZE]; //定义任务函数 void TASK_B(void *arg); //创建任务C //定义任务优先级 #define TASK_C_PRIO 5 //定义任务控制块 OS_TCB TASK_C_TCB; //定义任务堆栈大小 #define TASK_C_STK_SIZE 128 //定义任务堆栈 CPU_STK TASK_C_STK[TASK_C_STK_SIZE]; //定义任务函数 void TASK_C(void *arg); 共享资源 //u8 share_res[256]; 定义事件标志 //#define KEY0_FLAG 0x01//0000 0001 //#define KEY1_FLAG 0x02//0000 0010 定义事件标志组 //OS_FLAG_GRP EventFlag; //先定义两个消息队列和一个定时器的宏 //消息队列/// //#define KEYMSG_Q_NUM 1 //按键消息队列数量 //#define DATAMSG_Q_NUM 4 //发送数据的消息队列的数量 //OS_Q KEY_Msg; //定义一个消息队列,用于按键的消息传递模拟消息邮箱 //OS_Q DATA_Msg; //定义一个消息列队用于发送数据 //指定内建消息队列大小 #define TASK_Q_NUM 4 定时器/// u8 tmrlsta = 0;//标记定时器的工作状态 OS_TMR tmr1;//定义一个定时器 void tmr1_callback(void *p_arg); //定时器1回调函数 //void check_msg_queue(u8 *p) //{ // CPU_SR_ALLOC(); // u8 msgq_remain_size; //消息队列剩余大小 // OS_CRITICAL_ENTER(); //进入临界段 // msgq_remain_size = DATA_Msg.MsgQ.NbrEntriesSize-DATA_Msg.MsgQ.NbrEntries; // sprintf((char*)p,"Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); //显示DATA_Msg消息队列总的大小 // printf("Total Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); // sprintf((char*)p,"Remain Size:%d",msgq_remain_size); //显示DATA_Msg剩余大小 // printf("Remain Size:%d",DATA_Msg.MsgQ.NbrEntriesSize); // OS_CRITICAL_EXIT(); //退出临界段 //} int main(void) { OS_ERR err1;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 //硬件初始化 delay_init(); //延时初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置 uart_init(115200); //串口波特率设置 LED_Init(); KEY_Init(); OSInit(&err1);//初始化UCOSIII OS_CRITICAL_ENTER();//进入临界区代码 //创建开始任务A OSTaskCreate((OS_TCB * )&TASK_A_TCB, //任务控制块 (CPU_CHAR * )"main TASKA", //任务名字 (OS_TASK_PTR )TASK_A, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_A_PRIO, //任务优先级 (CPU_STK * )&TASK_A_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_A_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_A_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 * )&err1); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 OSStart(&err1);//开启UCOSIII while(1); } //开始任务函数 void TASK_A(void *arg) { OS_ERR err2_3;//错误码变量 CPU_SR_ALLOC();//定义临界区需要的变量 arg = arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err2_3); //统计任务 #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,&err2_3); #endif //#if OS_CFG_APP_HOOKS_EN //使用钩子函数 // App_OS_SetAllHooks(); //#endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //使用时间片轮转调度功能,时间片长度为一个时钟节拍即1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err2_3); #endif OS_CRITICAL_ENTER();//进入临界区代码 //创建定时器1 OSTmrCreate((OS_TMR *)&tmr1, //定时器1 (CPU_CHAR *)"tmr1", //定时器名字 (OS_TICK )0, //0ms (OS_TICK )50, //50*10=500ms (OS_OPT )OS_OPT_TMR_PERIODIC, //周期模式 (OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数 (void *)0, //参数为0 (OS_ERR *)&err2_3); //返回的错误码 //创建开始任务B 主函数 OSTaskCreate((OS_TCB * )&TASK_B_TCB, //任务控制块 (CPU_CHAR * )"main TASKB", //任务名字 (OS_TASK_PTR )TASK_B, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_B_PRIO, //任务优先级 (CPU_STK * )&TASK_B_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_B_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_B_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 * )&err2_3); //存放该函数错误时的返回值 //创建开始任务C 任务内建信号量 OSTaskCreate((OS_TCB * )&TASK_C_TCB, //任务控制块 (CPU_CHAR * )"main TASKC", //任务名字 (OS_TASK_PTR )TASK_C, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )TASK_C_PRIO, //任务优先级 (CPU_STK * )&TASK_C_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)TASK_C_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)TASK_C_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 * )&err2_3); //存放该函数错误时的返回值 OS_CRITICAL_EXIT();//退出临界区代码 //任务一执行完函数之后删掉自身 OSTaskDel((OS_TCB *)0,&err2_3); } //任务B任务函数 用于检测按键 主任务 void TASK_B(void *arg) { OS_ERR err_B; u8 key=0,num=0; while(1) { key = KEY_Scan(0); //扫描按键 if(key==WKUP_PRES) { tmrlsta = !tmrlsta; if(tmrlsta) { OSTmrStart(&tmr1,&err_B); printf("定时器1开启\r\n"); } else { OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err_B); //停止定时器1 printf("定时器1关闭\r\n"); } } num++; if(num%10==0) { LED1 = ~LED1; } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时10ms } } //任务C任务函数 任务内建信号量任务 void TASK_C(void *arg) { u8 *num; OS_MSG_SIZE size; OS_ERR err_C; while(1) { //请求消息KEY_Msg num=OSTaskQPend((OS_TICK )0, (OS_OPT )OS_OPT_PEND_BLOCKING, (OS_MSG_SIZE* )&size, (CPU_TS* )0, (OS_ERR* )&err_C); printf("任务C请求消息\r\n"); printf("任务C接收到的信号值:%d\r\n",*num); OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_C); //延时1s } } //定时器1回调函数 void tmr1_callback(void *p_arg) { static u8 num = 0; OS_ERR err; num++; OSTaskQPost((OS_TCB* )&TASK_C_TCB, (void* )&num, (OS_MSG_SIZE)1, (OS_OPT )OS_OPT_POST_FIFO, (OS_ERR* )&err);//消息发送 printf("定时器发送信号:%d\r\n",num); }
实验现象:刚开始的时候只会看到led的闪烁,当按下KEY_UP之后,定时器打开每个一段时间发送信息,然后任务C每隔一秒接收一次并打印。