【FreeRTOS】【STM32】08 FreeRTOS 消息队列

简单来说

消息队列是一种数据结构

任务操作队列的基本描述

1.如果队列未满或者允许覆盖入队,FreeRTOS会将任务需要发送的消息添加到队列尾

2.如果队列满,任务会阻塞(等待)。

3.用户可以指定等待时间。

4.当其它任务从其等待的队列中读取入了数据(这时候队列未满了)

5.该任务(等待发送数据到队列的任务)将自动由阻塞态转移为就绪态。

6.超过了等待时间,即使队列还不允许访问,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULL。

消息队列使用注意事项

1、发送,接收消息前需要定义一个消息队列,并根据队列句柄进行操作

2、队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的据。当然也 FreeRTOS 也支持后进先出(LIFO)模式。

3、读取数据前需要定义读取buffer。

4、无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大,可以将消息的地址作为消息进行发送、接收。

5、 队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可以向同

一队列写入和读出。一个队列由多任务或中断写入是经常的事,但由多个任务读

出倒是用的比较少。

消息队列创建函数 xQueueCreate()

1.xQueueCreate函数是基于xQueueGenericCreate进行创建

2.使用xQueueCreate()创建队列时,使用的是动态内存分配,所以要想使用该函数必须在FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 来使能。FreeRTOSConfig.h 中这个宏定义默认1.

3.消息量API函数也是基于xQueueGenericCreate进行创建的

c 复制代码
 #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
 #define xQueueCreate( uxQueueLength, uxItemSize ) \ 
						 xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) 
 #endif

函数原型

c 复制代码
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
							 UBaseType_t uxItemSize );

参数:
uxQueueLength:队列能够存储的最大消息单元数目,即队列长度。
uxItemSize:队列中消息单元的大小,以字节为单位

返回一个句柄,句柄是一个指向队列数据结构类型的指针,RTOS中对函数以及数据结构的操作都是通过句柄。

使用示例

c 复制代码
 QueueHandle_t Test_Queue =NULL;//队列句柄

#define QUEUE_LEN 4 /* 队列的长度,最大可包含多少个消息 */
#define QUEUE_SIZE 4 /* 队列中每个消息大小(字节) */

BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */

 taskENTER_CRITICAL(); //进入临界区
 /* 创建 Test_Queue */ 
Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */ 
							(UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */ 
 if (NULL != Test_Queue) 
	 printf("创建 Test_Queue 消息队列成功!\r\n");
	 
 taskEXIT_CRITICAL(); //退出临界区

消息队列静态创建函数 xQueueCreateStatic()

与静态创建任务一样,静态创建消息队列,也需要自定义一块内存

函数原型

c 复制代码
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,
								 UBaseType_t uxItemSize,
								 uint8_t *pucQueueStorageBuffer,
								 StaticQueue_t *pxQueueBuffer );

参数
uxQueueLength:队列能够存储的最大单元数目,即队列深度
uxItemSize:队列中数据单元的长度,以字节为单位。
pucQueueStorageBuffer:指针,指向一个 uint8_t 类型的数组,数组的大小至少有uxQueueLength* uxItemSize 个字节。当 uxItemSize 为 0 时,pucQueueStorageBuffer 可以为 NULL。
pxQueueBuffer:指针,指向 StaticQueue_t 类型的变量,该变量用于存储队列的数据结构。

如果创建成功则返回一个队列句柄,用于访问创建的队列。如果创建不成功则返回NULL,可能原因是创建队列需要的 RAM 无法分配成功。

使用示例

c 复制代码
/* 创建一个可以最多可以存储 10 个 64 位变量的队列 */
#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof(uint64_t)

/* 该变量用于存储队列的数据结构 */
static StaticQueue_t xStaticQueue;

/* 该数组作为队列的存储区域,大小至少有 uxQueueLength * uxItemSize 个字节 */
uint8_t ucQueueStorageArea[ QUEUE_LENGTH * ITEM_SIZE ]; 
 
 void vATask( void *pvParameters )
 {
	 QueueHandle_t xQueue;
 
 /* 创建一个队列 */ 
 xQueue = xQueueCreateStatic( QUEUE_LENGTH, /* 队列深度 */ 
							 ITEM_SIZE, /* 队列数据单元的单位 */ 
							 ucQueueStorageArea,/* 队列的存储区域 */ 
							 &xStaticQueue ); /* 队列的数据结构 */ 

 /* 剩下的其他代码 */
 }

消息队列删除函数 vQueueDelete()

传入形参是消息队列的队柄

使用示例

c 复制代码
#define QUEUE_LENGTH 5
#define QUEUE_ITEM_SIZE 4

int main( void )
{
	QueueHandle_t xQueue;
	/* 创建消息队列 */
	xQueue = xQueueCreate( QUEUE_LENGTH, QUEUE_ITEM_SIZE );

	 if ( xQueue == NULL ) {
	 /* 消息队列创建失败 */
	 } else {
	 /* 删除已创建的消息队列 */ 
	 vQueueDelete( xQueue ); 
		}
 }

向消息队列发送消息函数

任务或者中断服务程序都可以给消息队列发送消息.

所有的发送消息函数都是基于xQueueGenericSend.

xQueueSend()与 xQueueSendToBack() 用于非中断程序

函数原型

c 复制代码
BaseType_t xQueueSend(QueueHandle_t xQueue,
					 const void * pvItemToQueue,
					 TickType_t xTicksToWait);

参数
xQueue:目标队列句柄
pvItemToQueue:指针,指向要发送到队列尾部的队列消息
xTicksToWait:队列满时,等待队列空闲的最大超时时间。如果队列满并 且xTicksToWait 被设置成 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务挂起(没有超时)。
返回值:消息发送成功成功返回 pdTRUE,否则返回 errQUEUE_FULL。

宏定义

c 复制代码
 #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
 			xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), \
							 ( xTicksToWait ), queueSEND_TO_BACK )

可以看出来xQueueSend()是一个宏,宏展开是调用函数 xQueueGenericSend().

c 复制代码
 #define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \
			 xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), \
 								( xTicksToWait ), queueSEND_TO_BACK )

可以看出来,xQueueSendToBack宏展开是调用函数 xQueueGenericSend().

xQueueSend() 等同于xQueueSendToBack()。

xQueueSend()用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,而不是以引用的形式。该函数绝对不能在中断服务程序里面被调用,中断中必须使用带有中断保护功能的 xQueueSendFromISR()来代替。

使用实例

Send_Task作为一个任务函数,功能是检测到按键之后发送消息到队列

这个实际任务也是任务的一个形参。 任务创建

c 复制代码
static void Send_Task(void* parameter)
{
	BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
	uint32_t send_data1 = 1;
	uint32_t send_data2 = 2;
	while (1) {
		if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) {
				/* K1 被按下 */
				printf("发送消息 send_data1!\n"); 
				 xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */ 
										&send_data1,/* 发送的消息内容 */ 
										 0 ); /* 等待时间 0 */ 
				if (pdPASS == xReturn) 
					 printf("消息 send_data1 发送成功!\n\n"); 
			 }

		if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {
				 /* K2 被按下 */
				printf("发送消息 send_data2!\n"); 
				xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */ 
									&send_data2,/* 发送的消息内容 */ 
									 0 ); /* 等待时间 0 */ 
			 if (pdPASS == xReturn) 
					 printf("消息 send_data2 发送成功!\n\n"); 
			 }
		 vTaskDelay(20);/* 延时 20 个 tick */
	}
 }

xQueueSendFromISR()与 xQueueSendToBackFromISR() 中断服务函数中发送消息

函数原型

c 复制代码
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
							 const void *pvItemToQueue,
							 BaseType_t *pxHigherPriorityTaskWoken);

参数
xQueue:目标队列句柄
pvItemToQueue:指针,指向要发送到队列尾部的消息
pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,则将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去执行被唤醒的优先级更高的任务 。从FreeRTOS V7.3.0 起pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。

返回值:

消息发送成功返回 pdTRUE,否则返回 errQUEUE_FULL。

xQueueSend函数相同,xQueueSendFromISR()xQueueSendToBackFromISR()也是宏定义。
xQueueSendFromISR()

c 复制代码
 #define xQueueSendToFrontFromISR(xQueue,pvItemToQueue,pxHigherPriorityTaskWoken) \
				xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ),\
								( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )

xQueueSendToBackFromISR 等同于 xQueueSendFromISR ()。
xQueueSendToBackFromISR()

c 复制代码
#define xQueueSendToBackFromISR(xQueue,pvItemToQueue,pxHigherPriorityTaskWoken) \
			 xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), \
						 ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
使用实例
c 复制代码
void vBufferISR( void )
{
	char cIn;
	BaseType_t xHigherPriorityTaskWoken; 

	/* 在 ISR 开始的时候,我们并没有唤醒任务 */
	xHigherPriorityTaskWoken = pdFALSE; 

	/* 直到缓冲区为空 */
	 do {
	 /* 从缓冲区获取一个字节的数据 */
	 cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
 
	 /* 发送这个数据 */ 
	 xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); 
 
	 } while ( portINPUT_BYTE( BUFFER_COUNT ) );
 
	 /* 这时候 buffer 已经为空,如果需要则进行上下文切换 */ 
	 if ( xHigherPriorityTaskWoken ) { 
	 /* 上下文切换,这是一个宏,不同的处理器,具体的方法不一样 */ 
	 taskYIELD_FROM_ISR (); 
	 } 
 }

我如何知道中断服务函数中有没有唤醒任务?

向队列首发送消息

xQueueSendToFront()

用法与xQueueSend()一致。用于task,中断中使用有中断保护的xQueueSendToFrontFromISR (),类似的xQueueSendToFrontFromISR也是宏定义,基于xQueueGenericSendFromISR

函数原型

c 复制代码
BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
 							const void * pvItemToQueue,
 							TickType_t xTicksToWait );

参数
xQueue:队列句柄
pvItemToQueue:指针,指向要发送到队首的消息。
xTicksToWait:队列满时,等待队列空闲的最大超时时间。如果队列满并 且xTicksToWait 被设置成 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时).

xQueueSendToFrontFromISR()

函数原型

c 复制代码
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
							 const void *pvItemToQueue,
							 BaseType_t *pxHigherPriorityTaskWoken);

参数:
xQueue:队列句柄
pvItemToQueue:指针,指向要发送到队首的消息
pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先高于当前被中断的任务,则将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去执行被唤醒的优先级更高的任务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。

返回值:队列项投递成功返回 pdTRUE,否则返回 errQUEUE_FULL。

从消息队列读取消息函数

xQueueReceive() 接收并从消息队列里删除数据

xQueueReceive() 是一个宏 , 宏展开是调用函数xQueueGenericReceive() 。xQueueReceive()用于从一个队列中接收消息并把消息从队列中删除。

接收的消息是以拷贝的形式进行的,所以我们必须提供一个足够大空间的缓冲区。

类似发送消息到队列,这个函数用于普通Task,中断服务函数中使用带有中断保护功能的 xQueueReceiveFromISR

函数原型

c 复制代码
BaseType_t xQueueReceive(QueueHandle_t xQueue,
 								void *pvBuffer,
 						TickType_t xTicksToWait);

参数
xQueue:队列句柄
pvBuffer:指针,指向接收到要保存的数据
xTicksToWait:队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。
返回值:队列项接收成功返回 pdTRUE,否则返回 pdFALSE。

xQueueReceive函数使用实例

Receive_Task是一个任务函数。

c 复制代码
static void Receive_Task(void* parameter)
{
	BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为 pdPASS */
	uint32_t r_queue; /* 定义一个接收消息的变量 */
	while (1) {
	xReturn = xQueueReceive( Test_Queue, /* 消息队列的句柄 */ 
							&r_queue, /* 接受的数据 */ 
							portMAX_DELAY); /* 等待时间 一直等 */ 
	if (pdTRUE== xReturn) 
		 printf("本次接收到的数据是:%d\n\n",r_queue); 
		 else 
		 printf("数据接收出错,错误代码: 0x%lx\n",xReturn); 
	 }
 }

xQueuePeek() 接收但不从消息队列里删除数据

xQueuePeek使用方法与xQueueReceive一致,但是不会从消息队列里删除数据。

xQueueReceiveFromISR()与 xQueuePeekFromISR()中断服务函数的接收消息版本

从队列中获取消息的中断服务函数版本。
xQueueReceiveFromISR函数原型

c 复制代码
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,
									 void *pvBuffer,
				 BaseType_t *pxHigherPriorityTaskWoken);

参数:
xQueue:队列句柄
pvBuffer:指针,指向存放接收到的数据buffer
pxHigherPriorityTaskWoken:任务在往队列投递信息时,如果队列满,则任务将阻塞在该队列上。如果 xQueueReceiveFromISR引起了一个TASK被唤醒则将 *pxHigherPriorityTaskWoken 设置为pdTRUE

//---------------------------------------------------------------------------------------------------
pxHigherPriorityTaskWoken对于这个参数的理解,联系xQueueReceiveFromISR()xQueuePeekFromISR()函数的区别,一个函数是可以从队列中删除数据,一个函数是不可以从队列中删除数据。删除了队列中的数据可能导致一个task被唤醒,中断服务函数在Task中中断执行的,如果引起了一个别的TASK,那么下一步如何执行这两个TASK呢?

//---------------------------------------------------------------------------------------------------
xQueuePeekFromISR函数原型

c 复制代码
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,
 									void *pvBuffer);

参数:
xQueue:队列句柄。
pvBuffer:指针,指向接收到要保存的数据。

xQueueReceiveFromISR使用范例
c 复制代码
QueueHandle_t xQueue;

/* 创建一个队列,并往队列里面发送一些数据 */
void vAFunction( void *pvParameters )
{
	char cValueToPost;
	const TickType_t xTicksToWait = ( TickType_t )0xff;

	/* 创建一个可以容纳 10 个字符的队列 */
	 xQueue = xQueueCreate( 10, sizeof( char ) );
	 if ( xQueue == 0 ) {
 		/* 队列创建失败 */
 		}
 
	 /* ... 任务其他代码 */
	 
	 /* 往队列里面发送两个字符 如果队列满了则等待 xTicksToWait 个系统节拍周期*/
	 cValueToPost = 'a';
	 xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
	 cValueToPost = 'b';
	 xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
	 
	 /* 继续往队列里面发送字符当队列满的时候该任务将被阻塞*/
	 cValueToPost = 'c';
	 xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
 }
 
 
 /* 中断服务程序:输出所有从队列中接收到的字符 */
 void vISR_Routine( void )
 {
	 BaseType_t xTaskWokenByReceive = pdFALSE;
	 char cRxedChar;
 
 while ( xQueueReceiveFromISR( xQueue, 
						 ( void * ) &cRxedChar, 
						 &xTaskWokenByReceive) )
		 { 
		 /* 接收到一个字符,然后输出这个字符 */
		 vOutputCharacter( cRxedChar );
 
		 /* 如果从队列移除一个字符串后唤醒了向此队列投递字符的任务,
			 那么参数 xTaskWokenByReceive 将会设置成 pdTRUE,这个循环无论重复				多少次,仅会有一个任务被唤醒 */}
 
 if ( xTaskWokenByReceive != pdFALSE ) { 
		 /* 我们应该进行一次上下文切换,当 ISR 返回的时候则执行另外一个任务 */ 
		 /* 这是一个上下文切换的宏,不同的处理器,具体处理的方式不一样 */ 
 				taskYIELD (); 
 			} 
 }

消息队列实例

c 复制代码
 /* 
 *************************************************************************
 * 包含的头文件
 *************************************************************************
 */
 /* FreeRTOS 头文件 */
 #include "FreeRTOS.h"
 #include "task.h"
 #include "queue.h"
 /* 开发板硬件 bsp 头文件 */
 #include "bsp_led.h"
 #include "bsp_usart.h"
 #include "bsp_key.h"
 /**************************** 任务句柄 ********************************/
 /*
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为 NULL。
 */
 static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
 static TaskHandle_t Receive_Task_Handle = NULL;/* LED 任务句柄 */
 static TaskHandle_t Send_Task_Handle = NULL;/* KEY 任务句柄 */
 
 /***************************** 内核对象句柄 *****************************/
 /*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 
 * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 *
 */
 QueueHandle_t Test_Queue =NULL;
 
 /*************************** 全局变量声明 *******************************/
 /*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */
 /*************************** 宏定义 ************************************/
 /*
 * 当我们在写应用程序的时候,可能需要用到一些宏定义。
 */
 #define QUEUE_LEN 4 /* 队列的长度,最大可包含多少个消息 */ 
 #define QUEUE_SIZE 4 /* 队列中每个消息大小(字节) */ 
 
 /*
 *************************************************************************
 * 函数声明
 *************************************************************************
 */
 static void AppTaskCreate(void);/* 用于创建任务 */
 static void Receive_Task(void* pvParameters);/* Receive_Task 任务实现 */
 static void Send_Task(void* pvParameters);/* Send_Task 任务实现 */
 static void BSP_Init(void);/* 用于初始化板载相关资源 */
 
 /*****************************************************************
 * @brief 主函数
 * @param 无
 * @retval 无
 * @note 第一步:开发板硬件初始化
 第二步:创建 APP 应用任务
 第三步:启动 FreeRTOS,开始多任务调度
 ****************************************************************/
 int main(void)
 {
	 BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
 
	 /* 开发板硬件初始化 */
	 BSP_Init();
	 printf("按下 KEY1 或者 KEY2 发送队列消息\n");
	 printf("Receive 任务接收到消息在串口回显\n\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(); //进入临界区
 
 /* 创建 Test_Queue */ 
 Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */ 
 							(UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */ 
 if (NULL != Test_Queue) 
 	printf("创建 Test_Queue 消息队列成功!\r\n"); 
 
 /* 创建 Receive_Task 任务 */
 xReturn = xTaskCreate((TaskFunction_t )Receive_Task,/* 任务入口函数 */
					 (const char* )"Receive_Task",/* 任务名字 */
					 (uint16_t )512, /* 任务栈大小 */
					 (void* )NULL, /* 任务入口函数参数 */
					 (UBaseType_t )2, /* 任务的优先级 */
					 (TaskHandle_t* )&Receive_Task_Handle);/*任务控制块指针*/
 if (pdPASS == xReturn)
 		printf("创建 Receive_Task 任务成功!\r\n");
 
 /* 创建 Send_Task 任务 */
 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 任务成功!\n\n");
 vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务 
 taskEXIT_CRITICAL(); //退出临界区
 }
 
 
 
 /**********************************************************************
 * @ 函数名 : Receive_Task
 * @ 功能说明: Receive_Task 任务主体
 * @ 参数 :
 * @ 返回值 : 无
 ********************************************************************/
 static void Receive_Task(void* parameter) 
 { 
		 BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为 pdTRUE */ 
		 uint32_t r_queue; /* 定义一个接收消息的变量 */ 
	 while (1) { 
		 xReturn = xQueueReceive( Test_Queue, /* 消息队列的句柄 */ 
							 	&r_queue, /* 发送的消息内容 */ 
 								portMAX_DELAY); /* 等待时间 一直等 */ 
 		if (pdTRUE == xReturn) 
		 printf("本次接收到的数据是%d\n\n",r_queue); 
			 else 
 		printf("数据接收出错,错误代码: 0x%lx\n",xReturn); 
			 } 
 } 
 
 /**********************************************************************
 * @ 函数名 : Send_Task
 * @ 功能说明: Send_Task 任务主体
 * @ 参数 :
 * @ 返回值 : 无
 ********************************************************************/
 static void Send_Task(void* parameter) 
 { 
	 BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */ 
	 uint32_t send_data1 = 1; 
	 uint32_t send_data2 = 2; 
 while (1) { 
 	if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) { 
				 /* KEY1 被按下 */ 
			 printf("发送消息 send_data1!\n"); 
 			xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */ 
 								&send_data1,/* 发送的消息内容 */ 
								 0 ); /* 等待时间 0 */ 
			 if (pdPASS == xReturn) 
 					printf("消息 send_data1 发送成功!\n\n"); 
 			} 
 if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) { 
 		/* KEY2 被按下 */ 
				 printf("发送消息 send_data2!\n"); 
 				 xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */ 
 										&send_data2,/* 发送的消息内容 */ 
 										0 ); /* 等待时间 0 */ 
				 if (pdPASS == xReturn) 
						 printf("消息 send_data2 发送成功!\n\n"); 
 			} 
 	vTaskDelay(20);/* 延时 20 个 tick */ 
		 } 
 } 
 
 /***********************************************************************
 * @ 函数名 : 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****************************/
相关推荐
小渔村的拉线工17 分钟前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip1 小时前
CAN与CANopen的本质区别
单片机
lilihewo2 小时前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET2 小时前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院3 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰4 小时前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥5 小时前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5
zcmodeltech5 小时前
农业教学实训沙盘物联网控制系统设计:基于STM32与Modbus RTU的“感知-控制-实训”一体化方案
分布式·stm32·嵌入式硬件·物联网
Hello_Damon_Nikola6 小时前
LIS3DH从入门到精通_三轴加速度计实战指南
单片机·嵌入式硬件·3d
zcmodeltech7 小时前
化工装置沙盘模型多设备协同控制系统设计:基于STM32与Modbus RTU的装置-流程-安全联动方案
大数据·stm32·嵌入式硬件·安全·unity·制造