【FreeRtos队列操作】

FreeRtos队列操作


前言

本篇分享的是FreeRtos中的队列基础知识,通过本次的学习对队列有了一定的认识,以及如何去创建队列。


一、队列介绍

队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之间要交流的数据保存在队列中,叫做队列项目。队列所能保存的最大数据项目数量叫做队列的长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列用来传递消息的,所以也称为消息队列。FreeRTOS 中的信号量的也是依据队列实现的!所以有必要深入的了解FreeRTOS 的队列。

1.1 数据存储

通常队列采用先进先出(FIFO)的存储缓冲机制,也就是往队列发送数据的时候(也叫入队)永远都是发送到队列的尾部,而从队列提取数据的时候(也叫出队)是从队列的头部提取的。但是也可以使用 LIFO 的存储缓冲,也就是后进先出,FreeRTOS 中的队列也提供了 LIFO 的存储缓
冲机制。
数据发送到队列中会导致数据拷贝,也就是将要发送的数据拷贝到队列中,这就意味着在队列中存储的是数据的原始值,而不是原数据的引用(即只传递数据的指针),这个也叫做值传递。学过 UCOS 的同学应该知道,UCOS 的消息队列采用的是引用传递,传递的是消息指针。
采用引用传递的话消息内容就必须一直保持可见性,也就是消息内容必须有效,那么局部变量这种可能会随时被删掉的东西就不能用来传递消息,但是采用引用传递会节省时间啊!因为不用进行数据拷贝。
采用值传递的话虽然会导致数据拷贝,会浪费一点时间,但是一旦将消息发送到队列中原始的数据缓冲区就可以删除掉或者覆写,这样的话这些缓冲区就可以被重复的使用。FreeRTOS中使用队列传递消息的话虽然使用的是数据拷贝,但是也可以使用引用来传递消息啊,我直接往队列中发送指向这个消息的地址指针不就可以了!这样当我要发送的消息数据太大的时候就可以直接发送消息缓冲区的地址指针,比如在网络应用环境中,网络的数据量往往都很大的,采用数据拷贝的话就不现实。

注意:

队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中
提取消息

1.2 出队阻塞

当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。出队就是就从队列中读取消息,出队阻塞是针对从队列中读取消息的任务而言的。比如任务 A 用于处理串口接收到的数据,串口接收到数据以后就会放到队列 Q 中,任务 A 从队列 Q 中读取数据。但是如果此时队列 Q 是空的,说明还没有数据,任务 A 这时候来读取的话肯定是获取不到任何东西,那该怎么办呢?任务 A 现在有三种选择,一:二话不说扭头就走,二:要不我在等等吧,等一会看看,说不定一会就有数据了,三:死等,死也要等到你有数据!选哪一个就是由这个阻塞时间决定的,这个阻塞时间单位是时钟节拍数。阻塞时间为 0 的话就是不阻塞,没有数据的话就马上返回任务继续执行接下来的代码,对应第一种选择。如果阻塞时间为 0~ portMAX_DELAY,当任务没有从队列中获取到消息的话就进入阻塞态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还没有接收到数据的话就退出阻塞态,返回任务接着运行下面的代码,如果在阻塞时间内接收到了数据就立即返回,执行任务中下面的代码,这种情况对应第二种选择。当阻塞时间设置为portMAX_DELAY 的话,任务就会一直进入阻塞态等待,直到接收到数据为止!这个就是第三种选择。

1.3 入队阻塞

入队说的是向队列中发送消息,将消息加入到队列中。和出队阻塞一样,当一个任务向队列发送消息的话也可以设置阻塞时间。比如任务 B 向消息队列 Q 发送消息,但是此时队列 Q 是满的,那肯定是发送失败的。此时任务 B 就会遇到和上面任务 A 一样的问题,这两种情况的处理过程是类似的,只不过一个是向队列 Q 发送消息,一个是从队列 Q 读取消息而已。

二、队列结构体

有一个结构体用于描述队列,叫做 Queue_t,这个结构体在文件 queue.c 中定义并且在新版本的FreeRtos中我们使用队列的试试名字是用的Queue_t。

typedef struct QueueDefinition 
{ 
 int8_t *pcHead; //指向队列存储区开始地址。 
 int8_t *pcTail; //指向队列存储区最后一个字节。 
 int8_t *pcWriteTo; //指向存储区中下一个空闲区域。 
 
 union 
 { 
 int8_t *pcReadFrom; //当用作队列的时候指向最后一个出队的队列项首地址 
 UBaseType_t uxRecursiveCallCount;//当用作递归互斥量的时候用来记录递归互斥量被 
//调用的次数。 
 } u; 
 
 List_t xTasksWaitingToSend; //等待发送任务列表,那些因为队列满导致入队失败而进 
//入阻塞态的任务就会挂到此列表上。 
 List_t xTasksWaitingToReceive; //等待接收任务列表,那些因为队列空导致出队失败而进 
//入阻塞态的任务就会挂到此列表上。 
 
 volatile UBaseType_t uxMessagesWaiting; //队列中当前队列项数量,也就是消息数 
 UBaseType_t uxLength; //创建队列时指定的队列长度,也就是队列中最大允许的 
//队列项(消息)数量 
 UBaseType_t uxItemSize; //创建队列时指定的每个队列项(消息)最大长度,单位字节 
 
 volatile int8_t cRxLock; //当队列上锁以后用来统计从队列中接收到的队列项数 
//量,也就是出队的队列项数量,当队列没有上锁的话此字 
//段为 queueUNLOCKED 
 
 volatile int8_t cTxLock; //当队列上锁以后用来统计发送到队列中的队列项数量, 
//也就是入队的队列项数量,当队列没有上锁的话此字 
//段为 queueUNLOCKED 
 
 #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) &&\ 
( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) 
 uint8_t ucStaticallyAllocated; //如果使用静态存储的话此字段设置为 pdTURE。 
 #endif 
 
 #if ( configUSE_QUEUE_SETS == 1 ) //队列集相关宏 
 struct QueueDefinition *pxQueueSetContainer; 
 #endif 
 
 #if ( configUSE_TRACE_FACILITY == 1 ) //跟踪调试相关宏 
 UBaseType_t uxQueueNumber; 
 uint8_t ucQueueType; 
 #endif 
 
} xQUEUE; 
 
typedef xQUEUE Queue_t; 

2.1 队列的动态创建

创建队列的方式有两种一种为动态创建一种为静态创建,这两种方式和我们最开始的创建任务的动态和静态差不多,动态会自动分配相应的空间,静态需要我们自己设置。所以在一般使用过程中我们都是用的动态创建的方法,这里也只进行动态创建的演示。

函数 xQueueCreate()

//创建函数都是用的宏定义实际的函数为后面定义的函数
#define xQueueCreate( uxQueueLength, uxItemSize )    xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )

//实际函数
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
                                       const UBaseType_t uxItemSize,
                                       const uint8_t ucQueueType )





参数: 
uxQueueLength: 要创建的队列的队列长度,这里是队列的项目数。 
uxItemSize: 队列中每个项目(消息)的长度,单位为字节 
 
返回值: 
其他值: 队列创捷成功以后返回的队列句柄! 
NULL: 队列创建失败

2.2 队列发送消息

创建好队列以后就可以向队列发送消息了,FreeRTOS 提供了 8 个向队列发送消息的 API 函数

在这里插入图片描述

函数 xQueueSend()、xQueueSendToBack()和 xQueueSendToFront()
这三个函数都是用于向队列中发送消息的,这三个函数本质都是宏,其中函数 xQueueSend()和 xQueueSendToBack()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数xQueueSendToToFront()是前向入队,即将新消息插入到队列的前面。然而!这三个函数最后都是调用的同一个函数:xQueueGenericSend()。这三个函数只能用于任务函数中,不能用于中断服务函数,中断服务函数有专用的函数,它们以“FromISR”结尾。

/*-----------------------函数原型-----------------------*/
BaseType_t xQueueSend( QueueHandle_t xQueue, 
 const void * pvItemToQueue, 
 TickType_t xTicksToWait); 
 
BaseType_t xQueueSendToBack(QueueHandle_t xQueue, 
 const void* pvItemToQueue, 
 TickType_t xTicksToWait); 
 
BaseType_t xQueueSendToToFront(QueueHandle_t xQueue, 
 const void *pvItemToQueue, 
 TickType_t xTicksToWait); 
参数: 
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的
队列句柄。 
pvItemToQueue:指向要发送的消息,发送时候会将这个消息拷贝到队列中。 
xTicksToWait: 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大
时间。如果为 0 的话当队列满的时候就立即返回;当为 portMAX_DELAY 的
话就会一直等待,直到队列有空闲的队列项,也就是死等,但是宏
INCLUDE_vTaskSuspend 必须为 1
返回值: 
pdPASS: 向队列发送消息成功! 
errQUEUE_FULL: 队列已经满了,消息发送失败。 

xQueueOverwrite()重写函数

此函数也是用于向队列发送数据的,当队列满了以后会覆写掉旧的数据,不管这个旧数据有没有被其他任务或中断取走。这个函数常用于向那些长度为 1 的队列发送消息,此函数也是一个宏,最终调用的也是函数 xQueueGenericSend()。

#define xQueueOverwrite( xQueue, pvItemToQueue ) \
    xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
//实际调用函数
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
                              const void * const pvItemToQueue,
                              TickType_t xTicksToWait,
                              const BaseType_t xCopyPosition )

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
pvItemToQueue:指向要发送的消息,发送的时候会将这个消息拷贝到队中
返回值:
pdPASS: 向队列发送消息成功,此函数也只会返回 pdPASS!因为此函数执行过程中不在乎队列满不满,满了的话我就覆写掉旧的数据,总之肯定能成功。

xQueueGenericSend() 函数

这个函数才是我们实际所调用的

BaseType_t xQueueGenericSend( QueueHandle_t xQueue, 
const void * const pvItemToQueue, 
TickType_t xTicksToWait, 
const BaseType_t xCopyPosition ) 

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
pvItemToQueue:指向要发送的消息,发送的过程中会将这个消息拷贝到队列中。
xTicksToWait: 阻塞时间。
xCopyPosition: 入队方式,有三种入队方式:
queueSEND_TO_BACK: 后向入队
queueSEND_TO_FRONT: 前向入队
queueOVERWRITE: 覆写入队。
上面讲解的入队 API 函数就是通过此参数来决定采用哪种入队方式的。
返回值:
pdTRUE: 向队列发送消息成功!
errQUEUE_FULL: 队列已经满了,消息发送失败

xQueueSendFromISR() xQueueSendToBackFromISR() xQueueSendToFrontFromISR()

这三个函数也是向队列中发送消息的,这三个函数用于中断服务函数中。这三个函数本质也宏,其中函数 xQueueSendFromISR ()和 xQueueSendToBackFromISR ()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数 xQueueSendToFrontFromISR ()是前向入队,即将新消息插入到队列的前面。
这三个函数同样调用同一个函数 xQueueGenericSendFromISR ()

/*--------------------函数原型----------------------*/
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, 
 const void * pvItemToQueue, 
 BaseType_t * pxHigherPriorityTaskWoken); 
 
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue, 
 const void * pvItemToQueue, 
 BaseType_t * pxHigherPriorityTaskWoken); 
 
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue, 
 const void * pvItemToQueue, 
 BaseType_t * pxHigherPriorityTaskWoken); 

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
pvItemToQueue:指向要发送的消息,发送的时候会将这个消息拷贝到队列中。
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdTRUE: 向队列中发送消息成功!
errQUEUE_FULL: 队列已经满了,消息发送失败。
我们注意观察,可以看出这些函数都没有设置阻塞时间值。原因很简单,这些函数都是在中断服务函数中调用的,并不是在任务中,所以也就没有阻塞这一说了!

2.3 从队列读取消息

有入队就有出队,出队就是从队列中获取队列项(消息),FreeRTOS中同样也提供了一些读取消息的函数。分为两类一种是任务机读取函数,一类是中断级读取函数。

在这里插入图片描述

函数 xQueueReceive()

此函数用于在任务中从队列中读取一条(请求)消息,读取成功以后就会将队列中的这条数据删除,此函数的本质是一个宏,真正执行的函数是 xQueueGenericReceive()。此函数在读取消息的时候是采用拷贝方式的,所以用户需要提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是创建队列的时候所设定的每个队列项目的长度。

/*--------------------函数原型---------------------*/
BaseType_t xQueueReceive(QueueHandle_t xQueue, 
 void * pvBuffer, 
 TickType_t xTicksToWait); 

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏INCLUDE_vTaskSuspend 必须为 1。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败

函数 xQueuePeek()

此函数用于从队列读取一条(请求)消息,只能用在任务中!此函数在读取成功以后不会将消息删除,此函数是一个宏,真正执行的函数是 xQueueGenericReceive()。此函数在读取消息的时候是采用拷贝方式的,所以用户需要提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是创建队列的时候所设定的每个队列项目的长度。

/*--------------------函数原型-----------------*/
BaseType_t xQueuePeek(QueueHandle_t xQueue, 
					void * pvBuffer, 
 				   TickType_t xTicksToWait); 

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY
的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏
INCLUDE_vTaskSuspend 必须为 1。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败

函数 xQueueGenericReceive()

不管是函数 xQueueReceive() 还 是 xQueuePeek() ,最终都是调用的函数
xQueueGenericReceive()。

/*-----------------------函数原型----------------------*/
BaseType_t xQueueGenericReceive(QueueHandle_t xQueue, 
 void* pvBuffer, 
 TickType_t xTicksToWait 
 BaseType_t xJustPeek) 

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏INCLUDE_vTaskSuspend 必须为 1。
xJustPeek: 标记当读取成功以后是否删除掉队列项,当为 pdTRUE 的时候就不用删除,也就是说你后面再调用函数 xQueueReceive()获取到的队列项是一样的。当为pdFALSE 的时候就会删除掉这个队列项。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

函数 xQueueReceiveFromISR()

/*-------------------------函数原型--------------------*/
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, 
 void* pvBuffer, 
 BaseType_t * pxTaskWoken); 

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
pxTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值是由函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值
为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败

xQueuePeekFromISR()

此函数是 xQueuePeek()的中断版本,此函数在读取成功以后不会将消息删除

BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, 
 void * pvBuffer)

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

三、队列Demo演示

3.1 任务需求

1)本实验主要有通过读取按键键值,将其键值发送到队列中(Key_Queue)
2)通过定时器和串口中断,串口中断读取数据并将读取的数据发送到另一个队列中,定时器中断每隔500ms去读取消息队列中的值Usart_Queue

3.2 代码演示

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "key.h"
#include "time.h"


//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO		2
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);


//按键处理任务

//任务优先级
#define KEY_TASK_PRIO		3
//任务堆栈大小	
#define KEY_STK_SIZE 		50  
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);


//创建队列
QueueHandle_t   Key_Queue;             //按键队列
#define  			  KEYMSG_Queue_NUM 1     //队列长度


QueueHandle_t   Usart_Queue;             //串口队列
#define  			  UsartMSG_Queue_NUM 4     //队列长度



int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_GPIO_INIT();            //按键初始化
	TIM2_Int_Init(5000-1,7200-1);           //定时器初始化  1s
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
		//创建队列
	  Key_Queue =  xQueueCreate(KEYMSG_Queue_NUM,sizeof(u8));
	  Usart_Queue = xQueueCreate(UsartMSG_Queue_NUM,USART_REC_LEN);  //USART_REC_LEN串口宏定义最大接收
		if(NULL == Key_Queue)
		{
			 //创建失败
			printf("Key_Queue Create Faile\r\n");
			
		}
		if(Usart_Queue == NULL)
		{
				printf("Usart_Queue Create Faile\r\n");
		}
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);   
    //创建LED1任务
    xTaskCreate((TaskFunction_t )key_task,     
                (const char*    )"key_task",   
                (uint16_t       )KEY_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )KEY_TASK_PRIO,
                (TaskHandle_t*  )&KEYTask_Handler);         
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}



void led0_task(void *pvParameters)
{
  u8 key; 
	BaseType_t err;	
	while(1)
    {
			key = KEY_Scan();
			if((Key_Queue != NULL) && (key))  //队列按键都有效
			{
				 //err = xQueueOverwrite(Key_Queue,&key);
				err = xQueueSend(Key_Queue,&key,10);
				if(err != pdTRUE)
				{
					printf("Queue Send Failed\r\n");
				}
			}
			LED0=~LED0;
      vTaskDelay(500);
			// printf("led1 is running\r\n");
    }
}   

//LED1任务函数
void key_task(void *pvParameters)
{
  u8 key ;
	BaseType_t err;
	while(1)
    {
				if(Key_Queue != NULL)
				{
						 err =  xQueueReceive(Key_Queue,&key,portMAX_DELAY);
						if(err == pdTRUE)  //获取成功
						{
							printf("Key Value = %d\r\n",key);
						}
															 
				}
				else
				{
					
				}
	
    }
}

串口中断读取数据

extern QueueHandle_t   Usart_Queue;  

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
	BaseType_t xHigherPriorityTaskWoken;
	BaseType_t err;

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 
		 
		 //向队列发送数据
			if(NULL != Usart_Queue ){
			 err = xQueueSendFromISR(Usart_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken);
			 if(err == NULL)
			 {
				 printf("Send Failed\r\n");
			 }
			 printf("Usart Send:%s\r\n",USART_RX_BUF);
			 USART_RX_STA = 0; //状态清零
			 memset(USART_RX_BUF,0,USART_REC_LEN); //缓存区清零
		 }
			 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
		 
} 

定时器中断从队列中读数据

//定时器 2 中断服务函数 
void TIM2_IRQHandler(void) 
{ 
	u8 Buffer[USART_REC_LEN];
	BaseType_t err; 
	BaseType_t pxTaskWoken = pdFALSE;
	
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中断 
	 { 
			//printf("TIME2 TEST\r\n");
		
		 if(Usart_Queue != NULL)
		 {
				
			 memset(Buffer,0,USART_REC_LEN);
				err = xQueueReceiveFromISR(Usart_Queue,Buffer,&pxTaskWoken);
			 if(err == pdTRUE)
			 {
				 printf("Receive:%s\r\n",Buffer);
				
			 }
			 else
			 {
				 printf("Receive Failed\r\n");
			 }
		 }
		  portYIELD_FROM_ISR(pxTaskWoken);
	 } 
	
	 TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志位 
} 

3.3 实验结果

在这里插入图片描述

在串口接收中断和定时器读取中有点问题,我发送的字符串只能读取到两个字符,在串口接收中断单独直接将接收的数据打印是正常的。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
FreeRTOS队列是一个在FreeRTOS实时操作系统中提供的一种数据结构,用于在任务之间进行通信和共享数据。队列是一个先进先出(FIFO)的数据结构,任务可以向队列中发送数据项,也可以从队列中接收数据项。 队列的主要功能是实现任务之间的同步和通信。通过使用队列,任务可以将数据安全地传递给其他任务,而不需要直接访问共享内存或全局变量。这种方式可以有效地避免竞态条件和数据一致性问题。 FreeRTOS提供了以下几种类型的队列: 1. 二进制信号量队列(binary semaphore queue):用于在任务之间传递二进制信号量,只有两个状态,已获取(signaled)和未获取(not signaled)。 2. 计数信号量队列(counting semaphore queue):用于在任务之间传递计数信号量,可以有多个状态值。 3. 互斥量队列(mutex queue):用于实现任务之间的互斥操作,只有一个任务可以同时访问共享资源。 4. 消息队列message queue):用于在任务之间传递消息或数据项,可以具有不同的数据类型和大小。 使用FreeRTOS队列时,任务可以通过发送到队列或从队列接收数据进行通信。发送和接收操作可以是阻塞或非阻塞的,具体取决于队列的配置和任务的需求。 总之,FreeRTOS队列是一种强大而灵活的机制,可用于实现任务之间的通信和数据共享,同时确保任务之间的同步和互斥操作

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小殷学长

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

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

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

打赏作者

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

抵扣说明:

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

余额充值