µCOS-III从入门到精通 第十二章(任务信号量及队列)

参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili

一、任务信号量

1、任务信号量简介

(1)任务内嵌信号量本质上就是一个信号量,任务信号量是分配于每一个任务的任务控制块结构体中的,因此每一个任务都有独自的任务内嵌信号量。

(2)任务内嵌信号量只能被该任务获取,这也意味着任务内嵌信号量无法广播给多个任务,但是可以由其它任务或者中断释放。

2、任务信号量相关API函数介绍

(1)任务信号量相关API函数概览:

函数

描述

OSTaskSemPend

获取任务信号量

OSTaskSemPendAbort

终止任务挂起等待任务信号量

OSTaskSemPost

释放指定任务的任务信号量

OSTaskSemSet

强制设置指定的任务信号量为指定值

①释放任务信号量API函数可以用于任务和中断服务函数中。

②接收任务信号量API函数只能用在任务中。

(2)OSTaskSemPend函数:

OS_SEM_CTR OSTaskSemPend   //返回任务内嵌信号量更新后的资源数
(
    OS_TICK 	timeout,
	OS_OPT 	opt,
	CPU_TS* 	p_ts,
	OS_ERR* 	p_err
) 

形参

描述

timeout 

任务挂起等待任务内嵌信号量的最大允许时间

opt 

OS_OPT_PEND_BLOCKING:如果信号量没有资源的话就阻塞任务

OS_OPT_PEND_NON_BLOCKING:如果信号量没有资源任务就直接返回

p_ts 

指向接收任务内嵌信号量接收时的时间戳的变量的指针

p_err 

指向接收错误代码变量的指针

(3)OSTaskSemPost函数:

OS_SEM_CTR OSTaskSemPost   //返回任务内嵌信号量更新后的资源数
(
    OS_TCB* 	p_tcb,
	OS_OPT 	opt,
	OS_ERR* 	p_err
) 

形参

描述

p_tcb 

指向任务控制块的指针

opt 

OS_OPT_POST_NONE:不指定特定的选项
OS_OPT_POST_NO_SCHED :禁止在本函数内执行任务调度

p_err 

指向接收错误代码变量的指针

(4)OSTaskSemSet函数:

OS_SEM_CTR OSTaskSemSet   //返回任务内嵌信号量设置前的资源数
(
    OS_TCB* 		p_tcb,
	OS_SEM_CTR 	cnt,
	OS_ERR* 		p_err
) 

形参

描述

p_tcb 

指向任务控制块的指针

cnt 

指定的信号量资源数

p_err 

指向接收错误代码变量的指针

二、任务信号量实验

1、原理图与实验目标

(1)原理图:

(2)实验目标:

①设计4个任务——start_task、task1、task2、task3:

[1]start_task:用于创建task1、task2和task3任务。

[2]task1:当获取到LED1的硬件资源后,控制LED1约每500ms完成亮暗翻转的状态切换,每完成一次即将资源释放。

[3]task2:当获取到LED2的硬件资源后,控制LED2约每1000ms完成亮暗翻转的状态切换,每完成一次即将资源释放。

[4]task3:按下按键1,获取(或者说霸占)LED1和LED2的硬件资源;按下按键2,释放LED1和LED2的硬件资源。

②预期实验现象:

[1]程序下载到板子上后,两个LED灯闪烁。

[2]按下按键1,LED1和LED2停止闪烁(允许有1秒左右的延迟,这是因为task3一直在释放硬件资源,当task3停止释放时,task1和task2会收到最后一次任务通知,但它们此时可能处在自我阻塞引起的阻塞态,当从阻塞态中解脱时,它们会消耗这最后一次任务通知进行一次LED状态翻转操作)。

[3]按下按键2,LED1和LED2恢复闪烁。

2、实验步骤

(1)将“二值信号量实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)将UCOS_experiment.c文件中关于信号量的代码全部移除,并更改task1、task2和task3函数的实现。

①task1函数的思路:需要使用LED的硬件资源时调用OSTaskSemPend函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED1的状态。

②task2函数的思路:需要使用LED的硬件资源时调用OSTaskSemPend函数等待任务通知,并且必须等待到通知(同时将通知值清零)才可进行下一步操作——翻转LED2的状态。

③task3函数的思路:

[1]task1和task2需要task3的通知才能使用LED硬件资源,与二值信号量实验不同,二值信号量实验有单独的两个“队列”分别管理两个LED硬件资源,而本实验则是把task3当作了二值信号量队列管理员,两个二值信号量分别由task1和task2的TCB的任务通知相关成员代替。

[2]task1和task2不断获取和释放(获取实际上指的是接收通知,而释放在程序中并没有体现,可认为发生在下一次调用接收通知函数的前一时刻)硬件资源,那么task3也要不断处理task1和task2的申请,要不断地管理资源的分配,而不是等待按键事件到来的一刻才做一次资源分配操作,于是原本的算法需要做变更。

[3]在未按下任何按键时,task3可以一直做释放LED硬件资源给task1和task2的操作,即使手上没有LED资源也不会陷入阻塞,而是直接执行下一条语句(当然,在实际项目中通常不建议这么做,此处仅仅是为了功能演示),task1和task2哪个先结束阻塞,哪个就先调用OSTaskSemPend函数等待任务通知,或者已经调用OSTaskSemPend函数但之前未等待到通知而进入无限阻塞,但task3一旦分配资源以后就能立刻被唤醒。

[4]在按下按键1之后,task3不做资源分配操作,task1和task2只能无限等待任务通知,无法执行后续的任何操作。

[5]在按下按键2之后(以下执行流程图的情形为按下按键1后再按下按键2),task3可以继续一直做释放LED硬件资源给task1和task2的操作。

void task1(void){
	OS_ERR err;
	while(1){
		OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, NULL, &err);
		LED1_Turn();      //LED1状态翻转
		OSTimeDly(500, OS_OPT_TIME_DLY, &err);
	}
}
void task2(void){
	OS_ERR err;
	while(1){
		OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, NULL, &err); 
		LED2_Turn();      //LED2状态翻转
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
	}
}
void task3(void){
	OS_ERR err;
	uint8_t Key_memory = 0, key = 0;
	
	//OSTaskSemPost释放的是计数型信号量,没有上限,需注意资源释放频率
	uint16_t task1_blocktime = 0, task2_blocktime = 0;
	
	while(1){
		key = Key_GetNum();    //读取按键键值
		if(key != 0) Key_memory = key;
		if(Key_memory == 1) ;
		if(Key_memory == 2 || Key_memory == 0)
		{
			task1_blocktime++;	task2_blocktime++;
			if(task1_blocktime >= 50)
			{
				OSTaskSemPost(&task1_tcb, OS_OPT_POST_NONE, &err);
				task1_blocktime = 0;
			}
			if(task2_blocktime >= 100)
			{
				OSTaskSemPost(&task2_tcb, OS_OPT_POST_NONE, &err);
				task2_blocktime = 0;
			}
		}		
		OSTimeDly(10, OS_OPT_TIME_DLY, &err);      //自我阻塞10ms
	}
}

(3)程序完善好后点击“编译”,然后将程序下载到开发板上,根据程序注释进行调试。

三、任务队列

1、任务队列简介

(1)任务内嵌消息队列本质上就是一个消息队列,任务队列是分配于每一个任务的任务控制块结构体中的,因此每一个任务都有独自的任务内嵌消息队列。

(2)任务内嵌队列只能被该任务获取消息,这也意味着任务内嵌队列中的消息无法广播给多个任务,但是其中的消息可以由其它任务或者中断写入。

2、任务队列相关API函数介绍

(1)任务队列相关API函数概览:

函数

描述

OSTaskQFlush

清空任务内嵌消息队列中的所有消息

OSTaskQPend

获取任务内嵌消息队列中的消息

OSTaskQPendAbort

终止任务挂起等待任务内嵌消息队列

OSTaskQPost

发送消息到任务内嵌消息队列

①发送任务消息队列API函数可以用于任务和中断服务函数中。

②接收任务消息队列API函数只能用在任务中。

(2)OSTaskQPend函数:

void *OSTaskQPend   //指向获取到的消息的万能指针
(
	OS_TICK 		timeout,
	OS_OPT 		opt,
	OS_MSG_SIZE* 	p_msg_size,
	CPU_TS* 		p_ts,
	OS_ERR*		p_err
) 

形参

描述

timeout 

任务挂起等待任务内嵌消息队列的最大允许时间

opt 

OS_OPT_PEND_BLOCKING:如果任务队列中没有消息的话就阻塞任务

OS_OPT_PEND_NON_BLOCKING:如果任务队列中没有消息的话就直接返回

p_msg_size 

指向用于接收消息大小变量的指针

p_ts 

指向接收消息队列接收时的时间戳的变量的指针

p_err 

指向接收错误代码变量的指针

(3)OSTaskQPost函数:

void OSTaskQPost
(
 	OS_TCB* 		p_tcb,
	void* 			p_void,
	OS_MSG_SIZE 	msg_size,
	OS_OPT 		opt,
	OS_ERR* 		p_err
) 

形参

描述

p_tcb 

指向任务控制块的指针

p_void 

指向消息的指针

msg_size 

消息的大小,单位:字节

opt 

OS_OPT_POST_FIFO :将发送的消息保存在任务队列的末尾
OS_OPT_POST_LIFO :将发送的消息保存在任务队列的开头

OS_OPT_POST_NO_SCHED :禁止在本函数内执行任务调度(以上的几种类型可以与该宏进行组合使用)

p_err 

指向接收错误代码变量的指针

四、任务队列实验

1、原理图与实验目标

(1)原理图(按键未画出,接法与任务信号量实验相同):

(2)实验目标:

①设计3个任务——start_task、task1、task2:

[1]start_task:用于创建task1和task2任务。

[2]task1:用于按键扫描,并将键值发送到task2任务消息队列。

[3]task2:用于接收任务消息队列,并打印相关提示信息。

2、实验步骤

(1)将“任务创建与删除实验”的工程文件夹复制一份,在拷贝版中进行实验。

(2)在stm32教程中找到“串口发送(单片机通过串口向电脑发送数据)”实验,复制其中的Serial.c文件和Serial.h文件,将其添加到本实验的项目中,如下图所示,并在主函数中将串口初始化。

int main(void)
{
	/*模块初始化*/
	LED_Init();   //LED初始化
	Key_Init();   	 //按键初始化
	Serial_Init(); 	//串口初始化
	
	UCOS_Test();
	
	while (1)
	{
		
	}
}

(3)修改UCOS_experiment.c文件,删除task3任务的相关内容,并修改task1和task2的实现。

void task1(void)
{
	OS_ERR err;
	uint8_t key = 0;
	while(1)
	{
		key = Key_GetNum();
        if(key == 1 || key == 2)    //如果按下按键1或按键2
        {
            Serial_Printf("发送键值!\r\n");
            OSTaskQPost(&task2_tcb, &key, sizeof(key), OS_OPT_POST_FIFO, &err);
        }
        OSTimeDly(10,OS_OPT_TIME_DLY,&err);
	}
}

void task2(void)
{
	OS_ERR err;
	uint8_t * key;
    OS_MSG_SIZE size = 0;
    while(1)
    {
        key = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &size, NULL, &err);       
        Serial_Printf("接收到的键值为:%d\r\n", *key);
        Serial_Printf("接收到数据长度为:%d字节\r\n", size);
    }
}

(4)程序完善好后点击“编译”,然后将程序下载到开发板上,根据程序注释进行调试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值