【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****************************/
相关推荐
Rorsion5 分钟前
各种电机原理介绍
单片机·嵌入式硬件
善 .3 小时前
单片机的内存是指RAM还是ROM
单片机·嵌入式硬件
超级码农ProMax3 小时前
STM32——“SPI Flash”
stm32·单片机·嵌入式硬件
Asa3194 小时前
stm32点灯Hal库
stm32·单片机·嵌入式硬件
end_SJ5 小时前
初学stm32 --- 外部中断
stm32·单片机·嵌入式硬件
gantengsheng6 小时前
基于51单片机和OLED12864的小游戏《贪吃蛇》
单片机·嵌入式硬件·游戏·51单片机
嵌入式小强工作室7 小时前
stm32 查找进硬件错误方法
stm32·单片机·嵌入式硬件
委员8 小时前
基于NodeMCU的物联网窗帘控制系统设计
单片机·mcu·物联网·智能家居·iot
wenchm8 小时前
细说STM32F407单片机DMA方式读写SPI FLASH W25Q16BV
stm32·单片机·嵌入式硬件
南城花随雪。8 小时前
单片机:实现自动关机电路(附带源码)
单片机·嵌入式硬件