FreeRTOS-应用-任务通知-学习

文章基于野火与正点原子记录

1-任务通知-介绍

什么是任务通知?

  • FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,每个任务都有一个 32 位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件组,也可以替代 长度为 1 的队列(可以保存一个 32位整数或指针值)。
  • 任务通知的使用无需创建队列。想要使用任务通知, 必须将 FreeRTOSConfig.h 中的宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1,其实 FreeRTOS 默认是为 1 的,所以任务通知是默认使能的。

  • FreeRTOS 提供以下几种方式发送通知给任务
  • 发送通知给任务, 如果有通知未读,不覆盖通知值。
  • 发送通知给任务,直接覆盖通知值。
  • 发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。
  • 发送通知给任务,递增通知值,可以当做计数信号量使用。

 通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的信号量, 队列、事件组等。

  • 当然,凡是都有利弊,不然的话 FreeRTOS 还要内核的 IPC 通信机制干嘛,消息通知 虽然处理更快,RAM 开销更小,但也有以下限制 :
  •  只能有一个任务接收通知消息,因为必须指定接收通知的任务。
  •  只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会因为发 送失败而进入阻塞态。

任务通知的运作机制 :

        任务通知是属于任务中附带的资源,所以在任务被创建的时候,任务通知 也被初始化的,而在分析队列和信号量的章节中,我们知道在使用队列、信号量前,必须 先创建队列和信号量,目的是为了创建队列数据结构。比如使用 xQueueCreate()函数创建 队列,用 xSemaphoreCreateBinary()函数创建二值信号量等等。再来看任务通知,由于任务 通知的数据结构包含在任务控制块中,只要任务存在,任务通知数据结构就已经创建完毕, 可以直接使用,所以使用的时候很是方便,任务通知可以在任务中向指定任务发送通知,也可以在中断中向指定任务发送通知只有在任务中可以等待通知,而不允许在中断中等待通知。如果任务在 等待的通知暂时无效,任务会根据用户指定的阻塞超时时间进入阻塞状态。


发送任务通知:

1-任务通知-函数(2个通用函数+6个特化函数)


任务级任务通知通用发送函数

xTaskGenericNotify()

3 个任务级任务通知发送函数:xTaskNotify()、xTaskNotifyGive() 和 xTaskNotifyAndQuery(),这三个函数最终调用的都是函数 xTaskGenericNotify()!此函数在文 件 tasks.c 中有如下定义,缩减后的函数如下:

中断级任务通知发送函数 

中 断 级 任 务 通 知 发 送 函 数 也 有 三 个 , 分 别 为 : xTaskNotifyFromISR() 、 xTaskNotifyAndQueryFromISR()和 vTaskNotifyGiveFromISR()。其中函数 xTaskNotifyFromISR() 和 xTaskNotifyAndQueryFromISR()最终调用的都是函数 xTaskGenericNotifyFromISR(),此函数 的原型如下:


6个任务发送通知函数: 

 1xTaskNotify()

此函数用于发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正 执行的函数 xTaskGenericNotify()。

 

2、xTaskNotifyFromISR()

此函数用于发送任务通知,是函数 xTaskNotify()的中断版本,此函数是个宏,真正执行的 是函数 xTaskGenericNotifyFromISR(),此函数原型如下:

 

3xTaskNotifyGive()

 发送任务通知,相对于函数 xTaskNotify(),此函数发送任务通知的时候不带有通知值。此 函数只是将任务通知值简单的加一,此函数是个宏,真正执行的是函数 xTaskGenericNotify(), 此函数原型如下:

4vTaskNotifyGiveFromISR() 

 此函数为 xTaskNotifyGive()的中断版本,用在中断服务函数中,函数原型如下:

 

 5xTaskNotifyAndQuery()

 此函数和 xTaskNotify()很类似,此函数比 xTaskNotify()多一个参数,此参数用来保存更新 前的通知值。此函数是个宏,真正执行的是函数 xTaskGenericNotify(),此函数原型如下:

6xTaskNotifyAndQueryFromISR()  

此函数为 xTaskNorityAndQuery()的中断版本,用在中断服务函数中。此函数同样为宏,真 正执行的是函数 xTaskGenericNotifyFromISR(),此函数的原型如下:

 


获取任务通知:

1ulTaskNotifyTake() 

此函数为获取任务通知函数,当任务通知用作二值信号量或者计数型信号量的时候可以使 用此函数来获取信号量,函数原型如下:

 

2xTaskNotifyWait()

此函数也是用来获取任务通知的,不过此函数比 ulTaskNotifyTake()更为强大,不管任务通 知用作二值信号量、计数型信号量、队列和事件标志组中的哪一种,都可以使用此函数来获取 任务通知。但是当任务通知用作位置信号量和计数型信号量的时候推荐使用函数 ulTaskNotifyTake()。此函数原型如下:

 


简单实验部分:

所有实验都是在移植完FreeRTOS操作系统基础上完成,移植部分后续更新,这里给出所用到的外设的.c与.h文件(野火提供),还有硬件平台为STM32F103RCT6,标准库开发。

bsp_key.c

#include "bsp_key.h"  

/**
  * @brief  配置按键用到的I/O口
  * @param  无
  * @retval 无
  */
void Key_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/*开启按键端口的时钟*/
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
	
	//选择按键的引脚
	GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN; 
	// 设置按键的引脚为浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	//使用结构体初始化按键
	GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
	
	//选择按键的引脚
	GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN; 
	//设置按键的引脚为浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	//使用结构体初始化按键
	GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);	
}

 /*
 * 函数名:Key_Scan
 * 描述  :检测是否有按键按下
 * 输入  :GPIOx:x 可以是 A,B,C,D或者 E
 *		     GPIO_Pin:待读取的端口位 	
 * 输出  :KEY_OFF(没按下按键)、KEY_ON(按下按键)
 */
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{			
	/*检测是否有按键按下 */
	if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON )  
	{	 
		/*等待按键释放 */
		while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);   
		return 	KEY_ON;	 
	}
	else
		return KEY_OFF;
}
/*********************************************END OF FILE**********************/

bsp_key.h

#ifndef __KEY_H
#define	__KEY_H


#include "stm32f10x.h"

//  引脚定义
#define    KEY1_GPIO_CLK     RCC_APB2Periph_GPIOA
#define    KEY1_GPIO_PORT    GPIOA			   
#define    KEY1_GPIO_PIN		 GPIO_Pin_0

#define    KEY2_GPIO_CLK     RCC_APB2Periph_GPIOC
#define    KEY2_GPIO_PORT    GPIOC		   
#define    KEY2_GPIO_PIN		  GPIO_Pin_13


 /** 按键按下标置宏
	*  按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
	*  若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 即可
	*/
#define KEY_ON	1
#define KEY_OFF	0

void Key_GPIO_Config(void);
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);


#endif /* __KEY_H */

bsp_led.c

#include "bsp_led.h"   

 /**
  * @brief  初始化控制LED的IO
  * @param  无
  * @retval 无
  */
void LED_GPIO_Config(void)
{		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;

		/*开启LED相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK , ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;	

		/*设置引脚模式为通用推挽输出*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

		/*设置引脚速率为50MHz */   
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);		

		/* 关闭所有led灯	*/
		GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
		
		/* 关闭所有led灯	*/
		GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
}

/*********************************************END OF FILE**********************/

bsp_led.h

#ifndef __LED_H
#define	__LED_H


#include "stm32f10x.h"


/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define LED1_GPIO_PORT    	GPIOC		              /* GPIO端口 */
#define LED1_GPIO_CLK 	    RCC_APB2Periph_GPIOC		/* GPIO端口时钟 */
#define LED1_GPIO_PIN			GPIO_Pin_2			        

#define LED2_GPIO_PORT    	GPIOC			              /* GPIO端口 */
#define LED2_GPIO_CLK 	    RCC_APB2Periph_GPIOC		/* GPIO端口时钟 */
#define LED2_GPIO_PIN		GPIO_Pin_3			        



/** the macro definition to trigger the led on or off 
  * 1 - off
  *0 - on
  */
#define ON  0
#define OFF 1

/* 使用标准的固件库控制IO*/
#define LED1(a)	if (a)	\
					GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
					else		\
					GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)

#define LED2(a)	if (a)	\
					GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
					else		\
					GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)




/* 直接操作寄存器的方法控制IO */
#define	digitalHi(p,i)		 {p->BSRR=i;}	 //输出为高电平		
#define digitalLo(p,i)		 {p->BRR=i;}	 //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态


/* 定义控制IO的宏 */
#define LED1_TOGGLE		 digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF		   digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON			   digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)

#define LED2_TOGGLE		 digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF		   digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON			   digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)



void LED_GPIO_Config(void);

#endif /* __LED_H */

bsp_usart.c

#include "bsp_usart.h"

 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
	
	for(i=0; i<num; i++)
  {
	    /* 发送一个字节数据到USART */
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	/* 等待发送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

bsp_usart.h

#ifndef __USART_H
#define	__USART_H


#include "stm32f10x.h"
#include <stdio.h>

/** 
  * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
	* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
	* 2-修改GPIO的宏
  */
	
// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler


// 串口2-USART2
//#define  DEBUG_USARTx                   USART2
//#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

//#define  DEBUG_USART_IRQ                USART2_IRQn
//#define  DEBUG_USART_IRQHandler         USART2_IRQHandler

// 串口3-USART3
//#define  DEBUG_USARTx                   USART3
//#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOB)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOB   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOB
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

//#define  DEBUG_USART_IRQ                USART3_IRQn
//#define  DEBUG_USART_IRQHandler         USART3_IRQHandler

// 串口4-UART4
//#define  DEBUG_USARTx                   UART4
//#define  DEBUG_USART_CLK                RCC_APB1Periph_UART4
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

//#define  DEBUG_USART_IRQ                UART4_IRQn
//#define  DEBUG_USART_IRQHandler         UART4_IRQHandler


// 串口5-UART5
//#define  DEBUG_USARTx                   UART5
//#define  DEBUG_USART_CLK                RCC_APB1Periph_UART5
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOD
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2

//#define  DEBUG_USART_IRQ                UART5_IRQn
//#define  DEBUG_USART_IRQHandler         UART5_IRQHandler


void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

#endif /* __USART_H */

任务通知模拟二值信号量实验:

main.c

这里的FreeRTOS.h与task.h后续补充

根据 FreeRTOS 官方的统计,使用任务通知替代二值信号量的时候任务解除阻 塞的时间要快 45%,并且需要的 RAM 也更少。其实通过我们上面分析任务通知发送和获取函 数的过程可以看出,任务通知的代码量很少,所以执行时间与所需的 RAM 也就相应的会减少。 二值信号量就是值最大为 1 的信号量,这也是名字中“二值”的来源。当任务通知用于替 代二值信号量的时候任务通知值就会替代信号量值,函数 ulTaskNotifyTake()就可以替代信号量 获取函数 xSemaphoreTake(),函数 ulTaskNotifyTake()的参数 xClearCountOnExit 设置为 pdTRUE这样在每次获取任务通知的时候模拟的信号量值就会清零。函数 xTaskNotifyGive()和 vTaskNotifyGiveFromISR()用于替代函数 xSemaphoreGive()和 xSemaphoreGiveFromISR()。接下 来我们通过一个实验来演示一下任务通知是如何用作二值信号量的。

/*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/ 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/*Semaphore接受发送(获取释放)任务句柄*/
static TaskHandle_t Recevie_Task_Handle = NULL;
static TaskHandle_t Send_Task_Handle = NULL;

/********************************** 内核对象句柄 *********************************/
/*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 * 
 */
/*信号量句柄*/
/******************************* 全局变量声明 ************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */


/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void Recevie_Task(void *parameter);/*接收任务*/
static void Send_Task(void *parameter);/*发送任务*/

static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

  /* 开发板硬件初始化 */
  BSP_Init();
  
  printf("这是一个-FreeRTOS-任务通知-模拟二值信号量任务!\r\n");
   /* 创建AppTaskCreate任务 */ //任务创建完不会进行调度,必须启动任务调度函数
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1; 
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  /* 创建LED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Recevie_Task, /* 任务入口函数 */
                        (const char*    )"Recevie_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Recevie_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
   printf("创建Recevie_Task任务成功!\r\n");
  
  xReturn = xTaskCreate((TaskFunction_t )Send_Task, /* 任务入口函数 */
                        (const char*    )"Send_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )3,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Send_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
  printf("创建Send_Task任务成功!\r\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}



/**********************************************************************
  * @ 函数名  : Recevie_Task
  * @ 功能说明: Recevie_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Recevie_Task(void *parameter)
{
  BaseType_t R_return;
  while(1)
  {
    R_return=ulTaskNotifyTake(pdPASS,portMAX_DELAY);//参数1:pdfalse:退出任务通知值减1,pdpass:退出任务通知值清0 参数2:阻塞时间
    if(R_return == pdPASS)
    {
      LED1_ON;
      printf("任务通知获取成功!\r\n");
    }else
    {
      LED1_OFF;
      printf("任务通知值获取失败! \r\n");
    }   
  }
  
}

/**********************************************************************
  * @ 函数名  : Send_Task
  * @ 功能说明: Send_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Send_Task(void *parameter)
{
  BaseType_t S_return;
  while(1)
  {
    if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
    {
      S_return  = xTaskNotifyGive(Recevie_Task_Handle);//任务通知发送 ,参数:指定任务句柄
      if(S_return == pdPASS)
      {
        printf("任务通知发送成功!!\r\n");
      }
      else
      {
        printf("任务通知发送失败!!\r\n");
      }   
    }

    if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON)
    {
      S_return  = xTaskNotifyGive(Recevie_Task_Handle);//任务通知发送 ,参数:指定任务句柄
       if(S_return == pdPASS)
      {
        printf("任务通知发送成功!!\r\n");
      }
      else
      {
        printf("任务通知发送失败!!\r\n");
      }   
    }
    vTaskDelay(20);//增加20ms的阻塞时间
    /* code */
  }
  
}
/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
  /*按键初始化*/
  Key_GPIO_Config();
}

/********************************END OF FILE****************************/

任务通知模拟计数型信号量实验:

不同与二值信号量,计数型信号量值可以大 1,这个最大值在创建信号量的时候可以设置。 当计数型信号量有效的时候任务可以获取计数型信号量,信号量值只要大于 0 就表示计数型信 号量有效。 当任务通知用作计数型信号量的时候获取信号量相当于获取任务通知值,使用函数 ulTaskNotifyTake()来替代函数 xSemaphoreTake()。函数 ulTaskNotifyTake()的参数 xClearOnExit 要设置为 pdFLASE,这样每次获取任务通知成功以后任务通知值就会减一。使用任务通知发送 函 数 xTaskNotifyGive() 和 vTaskNotifyGiveFromISR() 来替代计数型信号量释放函数 xSemaphoreGive()和 xSemaphoreGiveFromISR()。下面通过一个实验来演示一下任务通知是如何 用作计数型信号量。

main.c

/*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/ 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/*Semaphore接受发送(获取释放)任务句柄*/
static TaskHandle_t Recevie_Task_Handle = NULL;
static TaskHandle_t Send_Task_Handle = NULL;

/********************************** 内核对象句柄 *********************************/
/*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 * 
 */
/*信号量句柄*/
/******************************* 全局变量声明 ************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */


/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void Recevie_Task(void *parameter);/*接收任务*/
static void Send_Task(void *parameter);/*发送任务*/

static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

  /* 开发板硬件初始化 */
  BSP_Init();
  
  printf("这是一个-FreeRTOS-二值信号量任务!\r\n");
   /* 创建AppTaskCreate任务 */ //任务创建完不会进行调度,必须启动任务调度函数
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1; 
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  

  /* 创建LED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Recevie_Task, /* 任务入口函数 */
                        (const char*    )"Recevie_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Recevie_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
   printf("创建Recevie_Task任务成功!\r\n");
  
  xReturn = xTaskCreate((TaskFunction_t )Send_Task, /* 任务入口函数 */
                        (const char*    )"Send_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )3,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Send_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
  printf("创建Send_Task任务成功!\r\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}



/**********************************************************************
  * @ 函数名  : Recevie_Task
  * @ 功能说明: Recevie_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Recevie_Task(void *parameter)
{
  BaseType_t R_return;
  while(1)
  {
    if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON)
    {
    R_return = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);//pdfalse:通知值减1;
    if(R_return>0)
    {
    printf("当前任务通知值为%d\r\n",(int)R_return-1);
    }
    } 
  }
  
}

/**********************************************************************
  * @ 函数名  : Send_Task
  * @ 功能说明: Send_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Send_Task(void *parameter)
{
  BaseType_t S_return;
  while(1)
  {
    if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
    {
      S_return = xTaskNotifyGive(Recevie_Task_Handle);//任务通知值加1
      LED1_TOGGLE;
      if(S_return != NULL)
      {
        printf("任务通知值加1\r\n");
      }
    }
    vTaskDelay(20);//增加20ms的阻塞时间
    /* code */
  }
  
}
/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
  /*按键初始化*/
  Key_GPIO_Config();
}

/********************************END OF FILE****************************/

任务通知模拟事件标志组实验:

任务通知代替事件组实验是在事件标志组实验基础上进行修改,实验任务通知替代事 件实现事件类型的通信,该实验是在 FreeRTOS 中创建了两个任务,一个是发送事件通知 任务,一个是等待事件通知任务,两个任务独立运行,发送事件通知任务通过检测按键的 按下情况设置不同的通知值位,等待事件通知任务则获取这任务通知值,并且根据通知值 判断两个事件是否都发生,如果是则输出相应信息,LED 进行翻转。等待事件通知任务的 等待时间是 portMAX_DELAY,一直在等待事件通知的发生,等待获取到事件之后清除对 应的任务通知值的位。

/*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/ 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
 /* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t task1_Handle = NULL;
static TaskHandle_t task2_Handle = NULL;

/********************************** 内核对象句柄 *********************************/
/*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 * 
 */
/*定义一个事件标志组句柄*/

/******************************* 全局变量声明 ************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */


/*******************************宏定义 ************************************/
#define Event_BIT_0  (1<<0)
#define Event_BIT_1  (1<<1)
/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void Task1(void *parameter);/*接收任务*/
static void Task2(void *parameter);/*发送任务*/

static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */

  /* 开发板硬件初始化 */
  BSP_Init();
  
  printf("这是一个-FreeRTOS-任务通知-模拟事件标志组任务!\r\n");
   /* 创建AppTaskCreate任务 */ //任务创建完不会进行调度,必须启动任务调度函数
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1; 
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
  
  
  /* 创建LED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Task1, /* 任务入口函数 */
                        (const char*    )"Task1",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&task1_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
   printf("创建Task1任务成功!\r\n");
  
  xReturn = xTaskCreate((TaskFunction_t )Task2, /* 任务入口函数 */
                        (const char*    )"Task2",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )3,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&task2_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
  printf("创建Task2任务成功!\r\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}



/**********************************************************************
  * @ 函数名  : Task1
  * @ 功能说明: Task1k任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Task1(void *parameter)
{
  while(1)
  {
    if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
    {
     xTaskNotify(task2_Handle,Event_BIT_0,eSetBits);
    printf("事件1被触发!!\r\n");
    }
    if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON)
    {
      xTaskNotify(task2_Handle,Event_BIT_1,eSetBits);
      printf("事件2被触发!!\r\n");
    }
    vTaskDelay(10);
  }
  
}

/**********************************************************************
  * @ 函数名  : Task2
  * @ 功能说明: Task2任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Task2(void *parameter)
{
  uint32_t r_return;
  BaseType_t _return;
  while(1)
  {
    _return = xTaskNotifyWait(0x0,                 //
                              0xffffffff,           //任务通知值清零
                              &r_return,            //存储通知值
                              portMAX_DELAY);         //阻塞时间
    if (_return == pdPASS) //把数据保留下来
    {
      /* code */
      printf("触发的事件标志位为%d\r\n",r_return); 
      if(r_return == Event_BIT_0)
        {
            LED1_TOGGLE;
            printf("事件1触发成功!!\r\n");
        }
        else if(r_return == Event_BIT_1)
        {
            LED2_TOGGLE;
            printf("事件2触发成功!!\r\n");
        }
    }
    
                                  
    
  }
  
}
/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
  /*按键初始化*/
  Key_GPIO_Config();
}

/********************************END OF FILE****************************/

任务通知模拟消息队列:

任务通知代替消息队列是在 FreeRTOS 中创建了三个任务,其中两个任务是用于接收 任务通知,另一个任务发送任务通知。三个任务独立运行,发送消息任务是通过检测按键 的按下情况来发送消息通知,另两个任务获取消息通知,在任务通知中没有可用的通知之 前就一直等待消息,一旦获取到消息通知就把消息打印在串口调试助手里:

/*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/ 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/*********************************************任务句柄*********************************************/
static TaskHandle_t APPTaskCreat_Handle = NULL;
static TaskHandle_t Receive_Task_Handle = NULL;
static TaskHandle_t Send_Task_Handle = NULL;
/*********************************************内核对象*********************************************/
/*********************************************宏定义*********************************************/
/*********************************************函数声明*********************************************/
static void BSP_Init(void);//硬件外设初始化
static void APPTaskCreat(void);//任务创建
static void Send_Task(void *parameter);//创建send_Task任务
static void Receive_Task(void *parameter);//创建Receive_Task任务

int main(void)
{	
      BaseType_t xReturn = pdPASS;

      BSP_Init();//硬件外设初始化
      printf("这是一个任务通知-模拟消息队列实验!\n");
     

      xReturn  =  xTaskCreate((TaskFunction_t) APPTaskCreat,//任务对象
                                ( char *  ) "APPTaskCreat",//任务名称
                                ( uint16_t) 512,//任务栈大小
                                (void *   ) NULL,//任务入口参数
                                (UBaseType_t   )1, //优先级
                                (TaskHandle_t * ) &APPTaskCreat_Handle );//任务句柄
        if(xReturn == pdPASS)
        {
          printf("APPTaskCreat任务创建成功!\n");
          vTaskStartScheduler();//开启任务调度
        }
        else
        {
         return  -1;
        }

        while(1);
      

}

/*任务创建任务*/
/*为方便管理把所有的任务都放在这个函数下*/
static void APPTaskCreat(void)
{
        BaseType_t xReturn = pdPASS;//存取任务的返回值
     
        taskENTER_CRITICAL();//进入临界区,不会被中断打扰

        
        /*创建消息队列接受函数*/
        xReturn  =  xTaskCreate((TaskFunction_t) Receive_Task,//任务对象
                                ( char *  ) "Receive_Task",//任务名称
                                ( uint16_t) 512,//任务栈大小
                                (void *   ) NULL,//任务入口参数
                                (UBaseType_t   )2, //优先级
                                (TaskHandle_t * ) &Receive_Task_Handle );//任务句柄
        if(xReturn == pdPASS)
        {
          printf("Receive_Task任务创建成功!\n");
        }
        else
        {
          printf("Receive_Task任务创建失败!\n");
        }

        /*创建消息队列接受函数*/
        xReturn  =  xTaskCreate((TaskFunction_t) Send_Task,//任务对象
                                ( char *  ) "Send_Task",//任务名称
                                ( uint16_t) 512,//任务栈大小
                                (void *   ) NULL,//任务入口参数
                                (UBaseType_t   )3, //优先级
                                (TaskHandle_t * ) &Send_Task_Handle );//任务句柄
        if(xReturn == pdPASS)
        {
          printf("Send_Task任务创建成功!\n");
        }
        else
        {
          printf("Send_Task任务创建失败!\n");
        }

        vTaskDelete(APPTaskCreat_Handle);//删除自身,注意是删除句柄;
        taskEXIT_CRITICAL();//退出临界区

}

/*接收任务*/
static void Receive_Task(void *parameter)
{
  BaseType_t xReturn  = pdPASS;//存取返回值
  char *r_char;
  //uint32_t r_data;//创建接收缓冲区
  while (1)
  {
    xReturn  = xTaskNotifyWait(0x0,
                     0xffffffff,   
                     (uint32_t *) &r_char,
                      portMAX_DELAY
                     );
    if (xReturn == pdPASS)
    {
      /* code */
      printf("接收到的数据为 %s\r\n",r_char);
    }
    
    
  }

}

/*发送任务*/
static void Send_Task(void *parameter)
{
  BaseType_t _return;
  #if 1
    char BUF[] = "hello world!";
  #else
    uint32_t send_data;
  #endif
  while (1)
  {
    if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==KEY_ON)
    {
      _return = xTaskNotify(Receive_Task_Handle, //发送的任务句柄
                      (uint32_t)&BUF,               //数据缓冲
                      eSetValueWithoutOverwrite);//覆写
      if(_return == pdPASS)
      {
        printf("消息发送成功!!\r\n");
      }
    }

     vTaskDelay(20);//阻塞20ms
  }
 
}

/*外设初始化*/
static void BSP_Init(void)
{
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);/*分组优先级必须为4*/

  /*LED初始化*/
  LED_GPIO_Config();

  /*key初始化*/
  Key_GPIO_Config();

  /*USART初始化*/
  USART_Config();

}
/********************************END OF FILE****************************/

总结:

任务通知可以代替消息队列、事件标志组、信号量使用,同时节省了一些ram空间,但也有缺点,比如任务通知只能一对一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值