【UCOSIII】UCOSIII的同时等待多个内核对象

UCOSIII同时等待多个内核对象

前面讲述了UCOSIII的信号量(一个任务与另一个任务同步)、事件标志组(一个任务与多个任务同步),它们都可以完成任务的同步。同时,信号量(保证全局变量)、消息队列,它们都可以完成消息的传递。

但是,它们描述的情况都是任务如何等待单个对象,比如信号量、互斥信号量、消息队列和时间标志组等。本文我们就讲解一下UCOSIII如何同时等待多个内核对象,在UCOSIII中只支持同时等待多个信号量和消息队列,不支持同时等待多个事件标志组和互斥信号量。

UCOSIII中一个任务可以同时等待任意数量的信号量或者消息队列,当只要等到其中的任意一个的时候就会导致该任务进入就绪态,如下图所示:

需要注意:在事件标志组中,用户可以自主设定究竟是等待多个任务同时置1还是同时清零,或者是只要有置1,只要有清零多种情况下的任务同步。但是在UCOSIII同时等待多个内核对象的情况下,只能等到任何一个信号量或者消息队列就被同步,进入就绪态。只有这一种情况。

 

UCOSIII同时等待多个内核对象API函数

首先需要注意的是:同时等待多个内核对象并不需要和之前的信号量、消息队列、事件标志组一样,先声明一个对象,在通过xxxCreate()函数定义出来……它只有一个函数,直接用的函数!

OSPendMulti()函数

函数OSPendMulti()用来等待多个内核对象,调用OSPendMulti()时,如果这些对象中有多个可用,则所有可用的信号量和消息都将返回给调用者;如果没有任何对象可用,则OSPendMulti()将挂起当前任务,直到以下任一情况发生:

  • 对象变为可用;
  • 到达设定的超时时间;
  • 一个或多个任务被删除或被终止;
  • 一个或多个对象被删除。

如果一个对象变为可用,并且有多个任务在等待这个对象,则UCOSIII将恢复优先级最高的那个任务函数OSPendMulti()原型如下:

OS_OBJ_QTY  OSPendMulti (OS_PEND_DATA  *p_pend_data_tbl,                //指向OS_PEND_DATA表的指针
                         OS_OBJ_QTY     tbl_size,                    //所等待的内核对象数量
                         OS_TICK        timeout,                        //设定一个等待超时值(时钟节拍数)
                         OS_OPT         opt,                            //来选择是否使用阻塞模式
                         OS_ERR        *p_err)
{
    CPU_BOOLEAN   valid;
    OS_OBJ_QTY    nbr_obj_rdy;
    CPU_SR_ALLOC();

    valid = OS_PendMultiValidate(p_pend_data_tbl,           /* -------- Validate objects to be OS_SEM or OS_Q ------- */
                                 tbl_size);
    if (valid == DEF_FALSE) {
       *p_err = OS_ERR_OBJ_TYPE;                            /* Invalid, not OS_SEM or OS_Q                            */
        return ((OS_OBJ_QTY)0);
    }

    CPU_CRITICAL_ENTER();
    nbr_obj_rdy = OS_PendMultiGetRdy(p_pend_data_tbl,       /* --------- SEE IF OBJECT(s) HAVE BEEN POSTED ---------- */
                                     tbl_size);
    if (nbr_obj_rdy > (OS_OBJ_QTY)0) {
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_NONE;
        return ((OS_OBJ_QTY)nbr_obj_rdy);
    }

    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_PEND_WOULD_BLOCK;                    /* No                                                     */
        return ((OS_OBJ_QTY)0);
    } else {
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_SCHED_LOCKED;
            return ((OS_OBJ_QTY)0);
        }
    }
    OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */
                                                            /* ------ NO OBJECT READY, PEND ON MULTIPLE OBJECTS ----- */
    OS_PendMultiWait(p_pend_data_tbl,                       /* Suspend task until object posted or timeout occurs     */
                     tbl_size,
                     timeout);

    OS_CRITICAL_EXIT_NO_SCHED();

    OSSched();                                              /* Find next highest priority task ready                  */

    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                             /* We got one of the objects posted to                    */
            *p_err = OS_ERR_NONE;
             break;

        case OS_STATUS_PEND_ABORT:                          /* Indicate that the multi-pend was aborted               */
            *p_err = OS_ERR_PEND_ABORT;
             break;

        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get semaphore within timeout   */
            *p_err = OS_ERR_TIMEOUT;
             break;

        case OS_STATUS_PEND_DEL:                            /* Indicate that an object pended on has been deleted     */
            *p_err = OS_ERR_OBJ_DEL;
            break;

        default:
            *p_err = OS_ERR_STATUS_INVALID;
             break;
    }

    OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
    CPU_CRITICAL_EXIT();

    return ((OS_OBJ_QTY)1);
}

p_pend_data_tbl:指向OS_PEND_DATA表的指针,调用者通过该表来查询函数的调用结果。调用该函数的时候首先必须初始化OS_PEND_DATA表中的每个元素的PendObjPtr,使得各个指针指向被等待的对象。

tbl_siae:表p_pend_data_tbl的大小,也就是所等待的内核对象数量。

timeout:设定一个等待超时值(时钟节拍数),用来设置任务等待对象发送的时间,如果为0,表示这个任务将一直等待下去,直到对象被发送。

opt:来选择是否使用阻塞模式,有两个选项可以选择。OS_OPT_PEND_BLOCKING:如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息;OS_OPT_PEND_NON_BLOCKING:如果消息队列没有任何消息的话任务就直接返回。

本函数的返回值为等待的多个内核对象中哪些是可用的。

OS_PEND_DATA

虽然我们不需要定义一个同时等待多个内核对象,但是我们需要定义一个数组,用来将需要等待的内核对象放入其中:也就是OS_PEND_DATA类型的数组:

struct  os_pend_data {
    OS_PEND_DATA        *PrevPtr;
    OS_PEND_DATA        *NextPtr;
    OS_TCB              *TCBPtr;
    OS_PEND_OBJ         *PendObjPtr;            //指向需要等待的内核对象
    OS_PEND_OBJ         *RdyObjPtr;
    void                *RdyMsgPtr;
    OS_MSG_SIZE          RdyMsgSize;
    CPU_TS               RdyTS;
};

在这个结构体中,PendObjPtr就是指向需要等待的内核对象。所以,我们需要定义一个OS_PEND_DATA类型的数组,再向每个数组元素的PendObjPtr成员对象赋需要等待的内核对象的地址。比如:

OS_SEM	Test_Sem1;			//信号量1
OS_SEM	Test_Sem2;			//信号量2
OS_Q	Test_Q;				//消息队列

OS_PEND_DATA pend_multi_tbl[CORE_OBJ_NUM];      //定义数组		
pend_multi_tbl[0].PendObjPtr=(OS_PEND_OBJ*)&Test_Sem1;
pend_multi_tbl[1].PendObjPtr=(OS_PEND_OBJ*)&Test_Sem2;
pend_multi_tbl[2].PendObjPtr=(OS_PEND_OBJ*)&Test_Q;

 

 

UCOSIII实际例程

同时等待多个内核对象

例程要求:设计一个应用程序,该程序有3任务、2个信号量和1个消息队列。任务A用于创建其他2个任务、2个信号量和1个消息队列。B任务为任务1,用于检测按键,当检测到按键KEY1被按下就发送信号量1,当KEY2被按下就发送信号量2,当KEY_UP被按下就发送消息队列,任务1还用来控制LED0的闪烁。任务C调用函数OSPendMulti()来同时等待2个信号量和1个消息队列。

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "sram.h"
#include "malloc.h"
#include "includes.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()

//任务优先级
#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		4
//任务堆栈大小	
#define TASK1_STK_SIZE 		128
//任务控制块
OS_TCB Task1_TaskTCB;
//任务堆栈	
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
void task1_task(void *p_arg);

//任务优先级
#define TASK2_TASK_PRIO		5
//任务堆栈大小	
#define TASK2_STK_SIZE 		128
//任务控制块
OS_TCB Task2_TaskTCB;
//任务堆栈	
CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
void task2_task(void *p_arg);

//任务优先级
#define MULTI_TASK_PRIO		6
//任务堆栈大小	
#define MULTI_STK_SIZE 		128
//任务控制块
OS_TCB Multi_TaskTCB;
//任务堆栈	
CPU_STK MULTI_TASK_STK[MULTI_STK_SIZE];
void multi_task(void *p_arg);

//LCD刷屏时使用的颜色
int lcd_discolor[14]={	WHITE, BLACK, BLUE,  BRED,      
						GRED,  GBLUE, RED,   MAGENTA,       	 
						GREEN, CYAN,  YELLOW,BROWN, 			
						BRRED, GRAY };

OS_SEM	Test_Sem1;			//信号量1		
OS_SEM	Test_Sem2;			//信号量2
OS_Q	Test_Q;				//消息队列						
#define QUEUE_NUM		10	//消息队列长度
#define CORE_OBJ_NUM	3	//内核对象个数,一共3个:2个信号量和一个消息队列											

int main(void)                        //主函数
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  	//时钟初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
	uart_init(115200);  //串口初始化
	LED_Init();         //LED初始化	
	LCD_Init();			//LCD初始化	
	KEY_Init();			//按键初始化
	FSMC_SRAM_Init();	//初始化SRAM
	my_mem_init(SRAMIN);//初始化内部RAM
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 13-1");
	LCD_ShowString(30,50,200,16,16,"Pend Multi");
	LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,90,200,16,16,"2015/5/20");
	
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(5,110,234,314);	
	LCD_DrawLine(5,130,234,130);
	POINT_COLOR = RED;
	LCD_ShowString(50,111,200,16,16,"ObjRdy_NUM: 0");
	POINT_COLOR = BLUE;
	
	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
}

void start_task(void *p_arg)                    //开始任务函数
{
	OS_ERR err;
	CPU_SR_ALLOC();
	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();	//进入临界区

	OSSemCreate ((OS_SEM*	)&Test_Sem1,            	//创建信号量Test_Sem1
                 (CPU_CHAR*	)"Test_Sem1",
                 (OS_SEM_CTR)0,		
                 (OS_ERR*	)&err);

	OSSemCreate ((OS_SEM*	)&Test_Sem2,            	//创建信号量Test_Sem2
                 (CPU_CHAR*	)"Test_Sem2",
                 (OS_SEM_CTR)0,		
                 (OS_ERR*	)&err);
	//创建消息队列
	OSQCreate ((OS_Q*		)&Test_Q,	//消息队列
                (CPU_CHAR*	)"KEY Msg",	//消息队列名称
                (OS_MSG_QTY	)QUEUE_NUM,	//消息队列长度
                (OS_ERR*	)&err);		//错误码

	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		        	//创建TASK1任务
				 (CPU_CHAR	* )"Task1 task", 		
                 (OS_TASK_PTR )task1_task, 			
                 (void		* )0,					
                 (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 	* )&Multi_TaskTCB,			//创建MULTI测试任务
				 (CPU_CHAR	* )"Multi task", 		
                 (OS_TASK_PTR )multi_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )MULTI_TASK_PRIO,     
                 (CPU_STK   * )&MULTI_TASK_STK[0],	
                 (CPU_STK_SIZE)MULTI_STK_SIZE/10,	
                 (CPU_STK_SIZE)MULTI_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();	//退出临界区
	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
}

void task1_task(void *p_arg)            //任务1的任务函数
{
	u8 key;
	OS_ERR err;
	u8 num;
	u8 *pbuf;
	static u8 msg_num;
	pbuf=mymalloc(SRAMIN,10);	//申请内存
	while(1)
	{
		key = KEY_Scan(0);  //扫描按键
		switch(key)
		{
			case KEY1_PRES:
				OSSemPost(&Test_Sem1,OS_OPT_POST_1,&err);//发送信号量1
				break;
			case KEY0_PRES:
				OSSemPost(&Test_Sem2,OS_OPT_POST_1,&err);//发送信号量2
			case WKUP_PRES:
				msg_num++;
				sprintf((char*)pbuf,"ALIENTEK %d",msg_num);
		
				//发送消息
				OSQPost((OS_Q*		)&Test_Q,		
						(void*		)pbuf,
						(OS_MSG_SIZE)10,
						(OS_OPT		)OS_OPT_POST_FIFO,
						(OS_ERR*	)&err);
				break;
		}
		num++;
		if(num==50)
		{
			num=0;
			LED0=~LED0;
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
	}
}

void multi_task(void *p_arg)                    //等待多个内核对象的任务函数
{	
	u8 num;
	OS_ERR err;
	OS_OBJ_QTY index;
	OS_PEND_DATA pend_multi_tbl[CORE_OBJ_NUM];	
	
	pend_multi_tbl[0].PendObjPtr=(OS_PEND_OBJ*)&Test_Sem1;
	pend_multi_tbl[1].PendObjPtr=(OS_PEND_OBJ*)&Test_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		   	)0,		
						  (OS_OPT         	)OS_OPT_PEND_BLOCKING,
						  (OS_ERR*			)&err);
		LCD_ShowNum(147,111,index,1,16);  					//显示当前有几个内核对象准备好了
		num++;
		LCD_Fill(6,131,233,313,lcd_discolor[num%14]);		//刷屏
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   //延时1s
	}
}
	//创建信号量Test_Sem1
                 (CPU_CHAR*	)"Test_Sem1",
                 (OS_SEM_CTR)0,		
                 (OS_ERR*	)&err);

	OSSemCreate ((OS_SEM*	)&Test_Sem2,            	//创建信号量Test_Sem2
                 (CPU_CHAR*	)"Test_Sem2",
                 (OS_SEM_CTR)0,		
                 (OS_ERR*	)&err);
	//创建消息队列
	OSQCreate ((OS_Q*		)&Test_Q,	//消息队列
                (CPU_CHAR*	)"KEY Msg",	//消息队列名称
                (OS_MSG_QTY	)QUEUE_NUM,	//消息队列长度
                (OS_ERR*	)&err);		//错误码

	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		        	//创建TASK1任务
				 (CPU_CHAR	* )"Task1 task", 		
                 (OS_TASK_PTR )task1_task, 			
                 (void		* )0,					
                 (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 	* )&Multi_TaskTCB,			//创建MULTI测试任务
				 (CPU_CHAR	* )"Multi task", 		
                 (OS_TASK_PTR )multi_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )MULTI_TASK_PRIO,     
                 (CPU_STK   * )&MULTI_TASK_STK[0],	
                 (CPU_STK_SIZE)MULTI_STK_SIZE/10,	
                 (CPU_STK_SIZE)MULTI_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();	//退出临界区
	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
}

void task1_task(void *p_arg)            //任务1的任务函数
{
	u8 key;
	OS_ERR err;
	u8 num;
	u8 *pbuf;
	static u8 msg_num;
	pbuf=mymalloc(SRAMIN,10);	//申请内存
	while(1)
	{
		key = KEY_Scan(0);  //扫描按键
		switch(key)
		{
			case KEY1_PRES:
				OSSemPost(&Test_Sem1,OS_OPT_POST_1,&err);//发送信号量1
				break;
			case KEY0_PRES:
				OSSemPost(&Test_Sem2,OS_OPT_POST_1,&err);//发送信号量2
			case WKUP_PRES:
				msg_num++;
				sprintf((char*)pbuf,"ALIENTEK %d",msg_num);
		
				//发送消息
				OSQPost((OS_Q*		)&Test_Q,		
						(void*		)pbuf,
						(OS_MSG_SIZE)10,
						(OS_OPT		)OS_OPT_POST_FIFO,
						(OS_ERR*	)&err);
				break;
		}
		num++;
		if(num==50)
		{
			num=0;
			LED0=~LED0;
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
	}
}

void multi_task(void *p_arg)                    //等待多个内核对象的任务函数
{	
	u8 num;
	OS_ERR err;
	OS_OBJ_QTY index;
	OS_PEND_DATA pend_multi_tbl[CORE_OBJ_NUM];	
	
	pend_multi_tbl[0].PendObjPtr=(OS_PEND_OBJ*)&Test_Sem1;
	pend_multi_tbl[1].PendObjPtr=(OS_PEND_OBJ*)&Test_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		   	)0,		
						  (OS_OPT         	)OS_OPT_PEND_BLOCKING,
						  (OS_ERR*			)&err);
		LCD_ShowNum(147,111,index,1,16);  					//显示当前有几个内核对象准备好了
		num++;
		LCD_Fill(6,131,233,313,lcd_discolor[num%14]);		//刷屏
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   //延时1s
	}
}

 

相关推荐
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页