FreeRTOS任务通知(用任务通知来模拟时间标志组)

本示例改写用事件标志组来进行任务同步,用任务通知来代替事件标志组,其余代码与原示例代码基本相同。

1.任务函数

#include "stm32f10x.h"                  // Device header
#include "appTask.h"

#include "Delay.h"
#include "Serial.h"


static char pcToPrint[80];    //待打印内容缓冲区
xQueueHandle xQueuePrint;			//消息队列句柄





static TaskHandle_t LED0TaskHandle = NULL;//任务LED0Task任务句柄
TaskHandle_t LED1TaskHandle = NULL;//任务LED1task任务句柄
static TaskHandle_t printTaskHandle = NULL;//任务printTask任务句柄
static TaskHandle_t KeyTaskHandle = NULL;//任务按键句柄


/**********************************************************************
函 数 名:LED0Task
功    能:LED0闪烁,指示程序正在运行
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	3
**********************************************************************/
static void LED0Task(void *pvParameters)
{
	while(1)
	{
		GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));	//LED0闪烁
		vTaskDelay(pdMS_TO_TICKS(500));//每秒闪烁1次
	}
}
/**********************************************************************
函 数 名:LED1Task
功		能:检测事件标志位,bit2,bit1和bit0同时置位时点亮LED1,并输出串口信息
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	3
**********************************************************************/
static void LED1Task(void *pvParameters)
{
	
	uint32_t uNotifyValue;   //用于保存任务通知值
	uint8_t evnFlag1,evnFlag2,evnFlag3;   //3个事件到来的标志
	 evnFlag1=evnFlag2=evnFlag3= 0;
	
	
	
	while(1)
	{
		GPIO_WriteBit(GPIOA,GPIO_Pin_5,Bit_SET);	//LED1熄灭
		
		/*检测事件标志组bit2,bit1和bit0,均置位点亮LED1并发送相应的信息到串口,3个事件标志位同时置位函数才返回,不满足则无限制阻塞任务,
		退出函数时清除3个事件标志位*/
		xTaskNotifyWait(0x00,0xffffffff,&uNotifyValue,portMAX_DELAY);
		if(uNotifyValue & 0x01) evnFlag1=1; 
		if(uNotifyValue & 0x02) evnFlag2=1; 
		if(uNotifyValue & 0x04) evnFlag3=1; 
		if(evnFlag1 &&  evnFlag2 && evnFlag3)
		{
			evnFlag1=evnFlag2=evnFlag3=0;
				//生成待打印出信息
				sprintf(pcToPrint,"按键0事件,按键1事件,TIM2中断事件均发生   \r\n\r\n");
				//打印信息,所有发送串口的信息不能直接输出,通过队列发送给串口守护任务
				xQueueSendToBack(xQueuePrint,pcToPrint,0);
			}	
					GPIO_WriteBit(GPIOA,GPIO_Pin_5,Bit_RESET);	//LED1点亮
				vTaskDelay(pdMS_TO_TICKS(1000));//点亮1s
	}
}
/**********************************************************************
函 数 名:printTask
功能说明:串口守护任务使用了一个FreeRTOS队列来对串口实现串行化访问,该守护任务是唯一能够直接访问串口的任务。
				  串口守护任务大部分时间都在阻塞态等待队列中有消息到来,当一个消息到达时,
				  串口守护任务仅简单地将接收到的消息发送到串口上,然后又返回阻塞态,继续等待下一条消息的到来。
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	3
**********************************************************************/
void printTask(void *pvParameters)
{
	char pcTowrite[80];  //缓存从队列接受到的数据
	while(1)
	{
	
		/*当队列为空,即没有字符需要输出时间,阻塞超时时间为portMAX_DELAY,任务将进入无期限等待
		状态,可以不检测队列读取函数的返回值*/
		xQueueReceive(xQueuePrint,pcTowrite,portMAX_DELAY);
		printf("%s",pcTowrite);
	}
	
}
/**********************************************************************
函 数 名:KeyTask
功能说明:高优先级任务,任务运行需要获取信号量,并将信息送往串口
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	4
**********************************************************************/
static void KeyTask(void *pvParameters)
{
	uint8_t keyValue; //键值
	
	
	
	while(1)
	{
		keyValue = Key_getNum(); //获取键值
		if(keyValue == 1)
		{
			sprintf(pcToPrint,"按键0按下,置位bit0......   \r\n\r\n");
				//打印信息,所有发送串口的信息不能直接输出,通过队列发送给串口守护任务
				xQueueSendToBack(xQueuePrint,pcToPrint,0);
			
			//设置事件标志组bit0位为1
			xTaskNotify(LED1TaskHandle,0x01,eSetBits);
		}
		else if(keyValue == 2)
		{
			sprintf(pcToPrint,"按键1按下,置位bit1......   \r\n\r\n");
				//打印信息,所有发送串口的信息不能直接输出,通过队列发送给串口守护任务
				xQueueSendToBack(xQueuePrint,pcToPrint,0);
			
			//设置事件标志组bit1位为1
			xTaskNotify(LED1TaskHandle,0x02,eSetBits);
		}
		else if(keyValue == 3)
		{
			//Key2按下,在TIM2中断中,设置事件标志组bit2为1
			TIM_Cmd(TIM2,ENABLE);//开启定时器
			sprintf(pcToPrint,"按键2按下,启动定时器2 ......   \r\n\r\n");
				//打印信息,所有发送串口的信息不能直接输出,通过队列发送给串口守护任务
				xQueueSendToBack(xQueuePrint,pcToPrint,0);
			
		}
		
		vTaskDelay(pdMS_TO_TICKS(100)); //阻塞100ms
			
	}
}

2.TIM函数

#include "stm32f10x.h"                  // Device header
#include "Timer.h"
#include "appTask.h"
#include "Serial.h"



BaseType_t xHighPriorityTaskWoken = pdFALSE ;  //退出中断后进行任务切换
extern TaskHandle_t LED1TaskHandle ;//任务LED1task任务句柄
void Timer_Init()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	TIM_InternalClockConfig(TIM2);//开启内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=9999 ;//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=7200 -1 ;//PSC
	//100ms

	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//中断使能  更新中断
	
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清除更新中断请求位
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);
	
	
	//TIM_Cmd(TIM2,ENABLE);//开启定时器
	
}

void TIM2_IRQHandler(void)
{
	
	if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)
	{
		
		//中断发生后,事件标志组bit2置1
		xTaskNotifyFromISR(LED1TaskHandle,0x04,eSetBits,&xHighPriorityTaskWoken);
		//如果有更高优先级的任务,则退出中断后执行任务切换
		portYIELD_FROM_ISR(xHighPriorityTaskWoken);
		//关闭定时器2
		TIM_Cmd(TIM2,DISABLE);
		TIM_ClearFlag(TIM2,TIM_FLAG_Update);
		
	}
}

 3.任务创建

/**********************************************************************
函 数 名:appStartTask
功能说明:任务开始函数,用于创建其他函数并且开启调度器
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
**********************************************************************/
void appStartTask(void)
{
	
	//创建一个长度为2,队列项大小可以容纳待输出字符的队列
	xQueuePrint = xQueueCreate(2,sizeof(pcToPrint));
	
		
				
			if(xQueuePrint)
			{
				
				taskENTER_CRITICAL();   /*进入临界段,关中断*/
	
				xTaskCreate(LED0Task,"LED0Task",128,NULL,3,&LED0TaskHandle);
				xTaskCreate(LED1Task,"LED1Task",128,NULL,3,&LED1TaskHandle);
				xTaskCreate(KeyTask,"KeyTask",128,NULL,3,&KeyTaskHandle);
				xTaskCreate(printTask,"printTask",128,NULL,4,&printTaskHandle);
				
				taskEXIT_CRITICAL(); 	/*退出临界段,关中断*/
				vTaskStartScheduler();/*开启调度器*/	
		}
		
}

4.下载测试

 测试结果与之前的示例一样。

5.总结

使用任务通知可以提高运行速度和减少RAM的消耗,可用于轻量级的使用场所下替换二值信号量,计数信号量,事件标志组等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值