uCOSIII实时操作系统 九 信号量和互斥信号量

目录

信号量:

创建信号量:

请求信号量:

发送信号量:

直接访问共享资源区实验

使用信号量访问共享资源区实验

任务同步实验:

优先级反转

优先级反转实验

互斥信号量

互斥信号量的API

创建互斥型信号量:

请求互斥信号量:

发送互斥信号量

互斥信号量实验

任务内嵌信号

等待任务信号量:

发布任务信号量:

任务内嵌信号量实验:


信号量:

先粘出正点原子对信号量的解释:

使用信号量的最初目的是为了给共享资源设立一个标志。我们可以将其想象成我们裸机中经常使用的标志位。只不过该标志位代表的是共享资源被占用的情况罢了。

互斥型信号量:所谓互斥是同一时刻只能有一个线程可以对共享资源进行访问其他线程无法进行访问。

创建信号量:

想要使用信号量前提肯定是要创建,使用函数OSSemCreate()来创建信号量,函数原型如下:

参数:

 p_sem,指向信号量的指针

 p_name,信号量名字

cnt,信号量的初值,如果此值是1 ,代表此信号量为二进制信号量,大于1的话就代表此信号为计数型信号量

 p_err,返回错误码,没有错误的就返回OS_ERR_NONE

请求信号量:

当一个任务需要独占式的访问某个特定的系统资源时,需要与其他任务或中断服务程序同步,或者需要等待某个事件的发生,应该调用函数 OSSemPend()请求信号量,函数原型如下:

参数:

 p_sem,指向信号量的指针

timeout,超时时间,默认写0,一直等待

 opt,设置是否使用阻塞方式,默认写OS_OPT_PEND_BLOCKING,阻塞等待

两个选项:

OS_OPT_PEND_BLOCKING                        指定信号量无效时,任务挂起以等待信号量

OS_OPT_PEND_NON_BLOCKING              信号无效时直接返回        

p_ts,用于记录等待信号量花了多长时间,默认写0/NULL,不记录。 

p_err,返回错误码,没有错误的就返回OS_ERR_NONE

发送信号量:

任务获得信号量之后就可以访问共享资源了,在任务访问完共享资源后必须释放掉信号量,释放信号量也叫发送信号量,使用函数OSSemPost发送信号量。如果没有任务等待的话 OSSemPost()函数只是简单的将信号量加 1,然后返回到调用该函数的任务中继续执行。

如果有一个或者多个任务在等待这个信号量,则优先级最高的任务将获得这个信号量,然后由调度器来判定刚获得信号量的任务是否为系统中优先级最高的就绪任务,如果是,则系统将进行任务切换,运行这个就绪任务,函数原型如下:

 p_sem        信号量对象(指向信号量的指针)

opt              用来选择信号量的发送方式。

        OS_OPT_POST_1                          仅向等待该信号量的优先级最高的任务发送信号量

        OS_OPT_POST_ALL                     像等待该信号量的所有任务发送信号量

        OS_OPT_POST_NO_SCHED        该选项禁止在本函数内执行任务调度操作。即使该函数使得最高优先级的任务结束挂起进入就绪状态也不会执行任务调度,而是会在其他后续函数中完成任务调度。

p_err         返回错误码,没有错误的就返回OS_ERR_NONE

返回值:当前信号量的计数值

在此之前提到的信号量都是主要用于访问共享文件和进行任务同步

直接访问共享资源区实验

实验要求:

创建 3 个任务,任务 A 用于创建其他两个任务,任务 A 执行一次后就会被删除掉。任务 B 和任务 C 都可以访问作为共享资源,D,任务 B 和 C 对于共享资源 D 是直接访问的,观察直接访问共享资源会造成什么要的后果。

实验源码:

#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];

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();//进入临界区代码
	//创建开始任务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 task1_str[]="Shared resource area testing TASK_B!";
	while(1)
	{
		printf("\r\n----任务1:----\r\n");
		memcpy(share_res,task1_str,sizeof(task1_str));//向共享资源区内拷贝数据
		delay_ms(200);
		printf("%s\r\n",share_res); //串口输出共享资源区数据
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_B);//延时10ms
	}
	
}

//任务C任务函数
void TASK_C(void *arg)
{

	OS_ERR err_C;
	u8 task2_str[]="Shared resource area testing TASK_C!";
	while(1)
	{
		printf("\r\n----任务2:----\r\n");
		memcpy(share_res,task2_str,sizeof(task2_str));//向共享资源区内拷贝数据
		delay_ms(200);
		printf("%s\r\n",share_res); //串口输出共享资源区数据
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C);//延时10ms
	}
	
}

预计实验结果:

实验结果分析:

在任务B任务函数向资源共享区share_res拷贝数据"Shared resource area testing TASK_B!";,之后又因为delay_ms()函数系统进行任务切换。任务C开始运行这时任务C中又进行了一次拷贝"Shared resource area testing TASK_C!",而任务C也因为delay_ms()函数发生任务切换任务B接着运行,但是这时候的资源共享区已经被修改为"Shared resource area testing TASK_C!"因此我们输出就和预计的不一样了。这个就是多任务共享区带来的问题!!!所以在任务访问共享资源的时候我们要对其进行保护。

使用信号量访问共享资源区实验

直接访问共享资源区实验中我们对共享资源区的访问并没有进行保护,从而导致错误的发生。

使用信号量解决该问题:

逻辑图:

在上个实验的代码基础上修改。

先定义一个信号量,

在任务A即开始任务中调用函数OSSemCreate()函数创建一个信号量,

根据上逻辑题可知,下一步B请求信号量,然后读取打印,然后释放信号量

任务3同理:

实验现象:

任务同步实验:

信号量现在更多的是被用来实现任务的同步以及任务和ISR(中断)间的同步,下边是正点原子的解释我感觉非常的形象

现在就用一个实验来证实:

实验内容:创建3个任务任务A用来创建

任务 A 用于创建其他两个任务和一个初始值为 0 的信号量,任务 C 必须征得任务 B 的同意(按键按下一次表示同意一次)才能执行一次操作。

实验源码:

#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];

//创建信号量对象
OS_SEM MY_SEM;

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();//进入临界区代码
	
		
	//创建一个信号量
	OSSemCreate((OS_SEM* )&MY_SEM,
								(CPU_CHAR* )"MY_SEM",
								(OS_SEM_CTR)0,//cnt初始值
								(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)
{
	u8 key;
	OS_ERR err_B;
	
	
	while(1)
	{
		
//		//请求信号量
//		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err_B);
//	
		printf("\r\n----任务A:----\r\n");
		key = KEY_Scan(0);
		//释放(发送)信号量
		if(key==KEY0_PRES)
		{
			OSSemPost(&MY_SEM,OS_OPT_POST_1,&err_B);
			printf("B:MY_SEM.Ctr=%d",MY_SEM.Ctr);
		}
			
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_B);//延时10ms
	}
	
}

//任务C任务函数
void TASK_C(void *arg)
{
	u8 num;
	OS_ERR err_C;
	while(1)
	{
		//请求信号量
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err_C);
		
		printf("\r\n----任务C:----\r\n");
		num++;
		printf("C:MY_SEM.Ctr=%d",MY_SEM.Ctr);
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C);//延时10ms
	}
	
}

实验现象:

优先级反转

优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这样的现象!!!

这样会破坏任务的预期顺序

优先级反转的例子:

(1)任务 H 和任务M处于挂起状态,等待某一事件的发生,任务 L正在运行

(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。

(3) 任务 L获得信号量并开始使用该共享资源。
(4)由于任务H 优先级高,它等待的事件发生后便剥夺了务L的 CPU 使用权。
(5)任务H开始运行。
(6)任务H运行过程中也要使用任务L 正在使用着的资源,由于该资源的信号量还被任务L 占用着,任务H只能进入挂起状态,等待任务 L释放该信号量。

(7)任务L继续运行。
(8)由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥了任务L的CPU使用权。
(9)任务M 处理该处理的事。
(10)任务 M 执行完毕后,将 CPU 使用权归还给任务L。
(11)任务L继续运行。
(12)最终任务L完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。
(13)任务H得到该信号量并接着运行。

在这种情况下,任务 H 的优先级实际上降到了任务L的优先级水平。因为任务 H 要一直等待直到任务 L释放其占用的那个共享资源。由于任务 M 剥夺了任务L的 CPU 使用权,使得任务H的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级反转。

优先级反转实验

实验要求:

创建 4 个任务,任务 A 用于创建 B、C 和 D 这三个任务,A 还创建了一个初始值为 1的信号量 TEST_SEM,任务 B 和 D 都请求信号量 TEST_SEM,其中任务优先级从高到底分别为:B、C、D。

实验源码:

#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);

//创建任务D
//定义任务优先级
#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];

//创建信号量对象
OS_SEM MY_SEM;

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();//进入临界区代码
	
		
	//创建一个信号量
	OSSemCreate((OS_SEM* )&MY_SEM,
								(CPU_CHAR* )"MY_SEM",
								(OS_SEM_CTR)1,//cnt初始值
								(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
	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)
{
//	u8 key;
	OS_ERR err_B;
	
	
	while(1)
	{
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err_B); //延时500ms

		printf("TASK_B task Pend Sem\r\n");
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err_B); //请求信号量
		printf("TASK_B task Running!\r\n");
		LED0 = ~LED0;
		OSSemPost(&MY_SEM,OS_OPT_POST_1,&err_B); //释放信号量
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err_B); //延时500ms
	}
	
}

//任务C任务函数  中优先级
void TASK_C(void *arg)
{
//	u8 num;
	OS_ERR err_C;
	while(1)
	{
		printf("TASK_C task Running!\r\n");
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}

//任务D任务函数
void TASK_D(void *arg)
{
	static u32 times;
	OS_ERR err_C;
	while(1)
	{
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err_C); //请求信号量
		printf("TASK_D task Running!\r\n");
		for(times=0;times<20000000;times++)
		{
			OSSched(); //发起任务调度
		}
		OSSemPost(&MY_SEM,OS_OPT_POST_1,&err_C);
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}

程序流程贴图:

互斥信号量

因为实时操作系统不支持优先级反转,否则会破坏任务的预期顺序,可能会造成严重的后果,所以为避免这个问题,UCOSIII支持一种特殊的二进制信号量,互斥信号量,取值只能是0或者1。也就说只能有一个任务访问的独占资源,应采用互斥信号量来管理。独占资源在系统中是非常常见的,例如各种IO端口如串口、USB设备、网络设备等。为了保证系统的实时性,拒绝优先级反转,对互斥信号量的管理采用了优先级继承机制。用它可解决优先级反转问题。

我认为的互斥信号量就是,高优先级的任务等待低优先级的任务发送信号量时候,为避免优先级反转,现将低优先级任务的优先级提高,等释放信号量之后再将该任务恢复为原来的优先级。

注意 :只有任务才能进行互斥中断服务程序不可以

互斥信号量的API

与普通信号量一样,对于互斥信号量也有很多操作,在文件os_mutex.c中。

创建互斥型信号量:

使用函数OSMutexCreate()函数原型如下:

p_mutex : 指向互斥型信号量控制块。互斥型信号量必须有用户应用程序进行实际的分配 

p_name  : 互斥量的名字

p_err       :调用此函数返回的错误码

请求互斥信号量:

当一个任务需要对资源进行独占式访问的时候就可以使用函数OSMutexPend(),如果该互斥信号量正在被其他的任务使用,那么UCOSIII就会将请求信号量的任务放在这个互斥信号量等待表当中。任务会一直等待,直到这个互斥信号量被释放掉,或者设定的超时时间到达为止。如果在设定的超时时间到达之前信号量被释放,UCOSIII将恢复所有等待这个信号量的任务中优先级最高的任务。

注意:如果占用该信号量的任务比当前申请的该互斥信号量的优先级低的话,OSMutexPend()函数将会占用该互斥信号量的任务的优先级提升到和当前申请的高优先级任务的优先级一样。当占用该互斥信号量的任务释放掉该互斥信号量以后,恢复到之前优先级。(细品请细品)

OSMutexPend()函数原型:

p_mutex :指向互斥信号量的指针 

timeout:   指定等待互斥信号量的时间(时钟节拍),如果在指定的时间内互斥信号量没有释放,允许任务恢复执行。该值如果设置为0 的话,表示该任务会被一直等待下去直到信号量被释放掉。

opt:          用于是否选择阻塞模式。

                OS_OPT_PEND_BLOCKING      :指定互斥信号量被占用的时,任务挂起等待该任务互斥信号量

                OS OPT PEND NON BLOCKING 指定当互斥信号量被占用时,直接返回任务

注意!当设置为OS OPT PEND NON BLOCKING,是timeout 参数就没有意义了,应该设置为 0。

p_ts:指向一个时间戳,记录发送、终止或删除互斥信号量的时刻。
p_err:调用此函数返回的错误码

发送互斥信号量

通过调用函数OSMutexPost()来释放互斥信号量,只有之前调用过函数OSMutexPend()获取信号量,才调用OSMutexPost()函数来释放信号量,函数原型如下:

p_mutex:指向互斥信号量。
opt:用来指定是否进行任务调度操作

        OS_OPT_POST_NONE     不指定特定的选项

        OS_OPT_POST_NO_SCHED        禁止在本函数内执行任务调度操作
p_err:用来保存调用此函数返回的错误码。

互斥信号量实验

实验要求:

将上一节实验优先级反转中的普通信号量换成互斥信号量

步骤:先定一个一个互斥信号量对象,然后在开始函数创建互斥信号量,然后再任务中执行。

实验源码:

#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);

//创建任务D
//定义任务优先级
#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];

//创建互斥信号量对象
OS_MUTEX MY_MUTEX;

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();//进入临界区代码
	
		
	//创建一个互斥信号量
	OSMutexCreate((OS_MUTEX* )&MY_MUTEX,
								(CPU_CHAR* )"MY_MUTEX",
								(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
	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)
{
//	u8 key;
	OS_ERR err_B;
	
	
	while(1)
	{
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err_B); //延时500ms

		printf("TASK_B task Pend Sem\r\n");
		OSMutexPend(&MY_MUTEX,0,OS_OPT_PEND_BLOCKING,0,&err_B); //请求互斥信号量
		printf("TASK_B task Running!\r\n");
		LED0 = ~LED0;
		OSMutexPost(&MY_MUTEX,OS_OPT_POST_1,&err_B); //释放信号量
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err_B); //延时500ms
	}
	
}

//任务C任务函数  中优先级
void TASK_C(void *arg)
{
//	u8 num;
	OS_ERR err_C;
	while(1)
	{
		printf("TASK_C task Running!\r\n");
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}

//任务D任务函数
void TASK_D(void *arg)
{
	static u32 times;
	OS_ERR err_C;
	while(1)
	{
		OSMutexPend(&MY_MUTEX,0,OS_OPT_PEND_BLOCKING,0,&err_C); //请求互斥信号量
		printf("TASK_D task Running!\r\n");
		for(times=0;times<10000000;times++)
		{
			OSSched(); //发起任务调度
		}
		OSMutexPost(&MY_MUTEX,OS_OPT_POST_1,&err_C); //释放信号量
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}

运行逻辑图:

任务内嵌信号

前边使用的信号量都是需要先创建一个信号量,不过在UCOSIII中每个任务都有自己的内嵌信号量,这种功能不仅仅可以简化代码,而且要比使用独立信号量更有效。任务信号量是直接内嵌在UCOSIII中的,任务信号量相关代码在os_task.c中。任务内嵌信号量相关函数都有:

等待任务信号量:

等待任务内嵌信号量使用函数OSTaskSemPend(),OSTaskSemPend()允许一个任务等待由其他任务或者ISR(中断)直接发送信号,使用的基本过程和独立信号量相同。

函数原型:

timeout:如果在指定的节拍数内没有收到信号量任务就会因为等待超时而恢复运行,如果 timeout 为 0 的话任务就会一直等待,直到收到信号量。

opt:用于选择是否使用阻塞模式

        OS_OPT_PEND_BLOCKING 指定互斥信号量被占用时,任务挂起等待该互斥信号量。
        OS_OPT_PEND_NON_BLOCKING 指定当互斥信号量被占用时,直接返回任务。

注意!当设置为 OS_OPT_PEND_NON_BLOCKING,是 timeout 参数就没有意义了,应该设置为 0。

p_ts:指向一个时间戳,记录发送、终止或删除互斥信号量的时刻。

p_err:调用此函数后返回的错误码。

发布任务信号量:

OSTaskSemPost()可以通过一个任务的内置信号量向某个任务发送一个信号量

函数原型:

p_tcb:指向要用信号通知的任务的 TCB,当设置为 NULL (0)的时候可以向自己发送信量。
opt:用来指定是否进行任务调度操作
                OS_OPT_POST_NONE不指定特定的选项
                OS_OPT_POST_NO_SCHED禁止在本函数内执行任务调度操作。
p_err:调用此函数后返回的错误码。

任务内嵌信号量实验:

创建 3 个任务,任务 任务A 用于创建其他两个任务,任务 任务B主要用于扫描按键,当检测到 KWY_UP 按下以后就向任务 任务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];

创建互斥信号量对象
//OS_MUTEX MY_MUTEX;

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();//进入临界区代码
	
		
//	//创建一个互斥信号量
//	OSMutexCreate((OS_MUTEX* )&MY_MUTEX,
//								(CPU_CHAR* )"MY_MUTEX",
//								(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)
{
//	u8 key;
	OS_ERR err_B;
	u8 key;
  u8 num;

	
	while(1)
	{
		
		key = KEY_Scan(0); //扫描按键
		
		printf("TASK_B task Pend Sem\r\n");
		if(key==WKUP_PRES)
		{
			OSTaskSemPost(&TASK_C_TCB,OS_OPT_POST_NONE,&err_B); //使用系统内建信号量向任务task2发送信号量
			printf("发送内嵌信号量!\r\n");
		}
		num++;
		if(num==50)
		{
			num=0;
			LED0 = ~LED0;
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err_B); //延时500ms
	}
	
}

//任务C任务函数  中优先级
void TASK_C(void *arg)
{
	u8 num;
	OS_ERR err_C;
	while(1)
	{
		printf("TASK_C task Running!!!!!!!!!!!!!!!\r\n");
		OSTaskSemPend(0,OS_OPT_PEND_BLOCKING,0,&err_C); //请求任务内建的信号量
		printf("TASK_C task test2\r\n");
		num++;
		printf("TASK_C_TCB.SemCtr=%ud\r\n",TASK_C_TCB.SemCtr);
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err_C); //延时1s
	}
	
}


实验现象:

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值