freertos队列学习

队列的概念

队列用于任务间通信的数据结构,通过消息队列服务,任务或中断服务将消息放入消息队列中。其他任务或者自身从消息队列中获得消息。实现队列可以在任务与任务间、中断和任务间传递信息。队列操作支持阻塞等待,向已经填满的队列发送数据或者从空队列读出数据,都会导致阻塞,时间自定义。消息队列的运作过程具如下:

队列创建

xQueueCreate()用于创建一个新的队列并返回可用于访问这个队列的句柄。队列句柄其实就是一个指向队列数据结构类型的指针。

  1. master_queue = xQueueCreate(50, sizeof(track_task_message_struct_t));

创建队列,占用50个单元,每个单元为sizeof(track_task_message_struct_t)字节,和 malloc比较类似。

其最终使用的函数是 xQueueGenericCreate(),后续信号量等也是使用它创建,只是最后的队列类型不同。

申请内存后,xQueueGenericReset再对其进行初始化,队列的结构体xQUEUE成员:

  1. typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */

  2. {

  3. int8_t * pcHead; /*< Points to the beginning of the queue storage area. */

  4. int8_t * pcWriteTo; /*< Points to the free next place in the storage area. */

  5. //类型

  6. union

  7. {

  8. QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */

  9. SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */

  10. } u;

  11. //当前向队列写数据阻塞的任务列表或者从队列取数阻塞的链表

  12. List_t xTasksWaitingToSend;

  13. List_t xTasksWaitingToReceive;

  14. //队列里有多少个单元被占用,应用中需要

  15. volatile UBaseType_t uxMessagesWaiting;

  16. UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */

  17. UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */

  18. /******/

  19. } xQUEUE;

队列删除

队列删除函数 vQueueDelete()需传入要删除的消息队列的句柄即可,删除之后这个消息队列的所有信息都会被系统回收清空,而且不能再次使用这个消息队列了。实际应用中很少使用。

向队列发送消息

任务或者中断服务程序都可以给消息队列发送消息,当发送消息时,如果队列未满或者允许覆盖入队,FreeRTOS 会将消息拷贝到消息队列队尾,否则,会根据用户指定的超时时间进行阻塞,消息发送接口很多,最简单的是 xQueueSend(),用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,该函数绝对不能在中断服务程序里面被调用,中断中必须使用带有中断保护功能的 xQueueSendFromISR()来代替。

BaseType_t xQueueSend(QueueHandle_t xQueue,const void* pvItemToQueue, TickType_t xTicksToWait);

用于向队列尾部发送一个队列消息

参数

xQueue 队列句柄

pvItemToQueue 指针,指向要发送到队列尾部的队列消息。

xTicksToWait 队列满时,等待队列空闲的最大超时时间。如果队列满并且xTicksToWait 被设置成 0,函数立刻返回。超时时间的单位为系统节拍周期 tick,延时为 portMAX_DELAY 将导致任务挂起(没有超时)。

返回值

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

xQueueSendToBack与xQueueSend完全相同, xQueueSendFromISR()与 xQueueSendToBackFromISR(),带FromISR表示只能在中断中使用,freeRTOS所以带这个后缀的都是这个含义。

xQueueSendToFront()和QueueSendToFrontFromISR()用于向队列队首发送一个消息。 这些在任务中发送消息的函数都是 xQueueGenericSend()展开的宏定义。

  1. BaseType_t xQueueGenericSend( QueueHandle_t xQueue, //任务句柄

  2. const void * const pvItemToQueue, //指向要发送到队列的数据项的指针

  3. TickType_t xTicksToWait, //函数将等待队列有空位的最长时间(以系统节拍为单位)

  4. const BaseType_t xCopyPosition ) //发送数据到消息队列的位置

一般使用xQueueSend和xQueueSendFromISR,如不确定当前运行的是系统服务,还是中断服务,一般ARM都支持查询中断状态寄存器判断,可以封装一层接口,只管发消息,内部判断是否使用支持中断嵌套的版本,UIS8910就是如此。

特殊情况下,如发送网络数据包未收到服务器响应,期望立刻入队再次发送它,可以xQueueSendToFront向队头发消息。

从队列读取消息

当任务试图读队列中的消息时,可以指定一个阻塞超时时间,当且仅当消息队列中有消息的时候,任务才能读取到消息。如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转为就绪态。当任务等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。所有的task主入口while循环体都是按这个执行。例如:

  1. static void track_master_task_main()

  2. {

  3. track_task_message_struct_t queue_item = {0};

  4. /****/

  5. while(1)

  6. {

  7. if(xQueueReceive(master_queue, &queue_item, portMAX_DELAY))//阻塞等待

  8. {

  9. track_master_task_msg_handler(&queue_item);

  10. }

  11. }

  12. }

xQueueReceive()用于从一个队列中接收消息并把消息从队列中删除。

如果不想删除消息的话,就调用 xQueuePeek()函数。

xQueueReceiveFromISR()与xQueuePeekFromISR()是中断版本,用于在中断服务程序中接收一个队列消息并把消息。这两个函数只能用于中断,是不带有阻塞机制的,实际项目没有使用。

查询队列使用情况

uxQueueMessagesWaiting()查询队列中存储的信息数目,具有中断保护的版本为uxQueueMessagesWaitingFromISR()。

查询队列的空闲数目uxQueueSpacesAvailable()。

队列使用注意点

使用队列函数需要注意以下几点:

1、中断中必须使用带FromISR后缀的接口;

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

  1. typedef struct

  2. {

  3. TaskHandle_t src_mod_id;

  4. int message_id;

  5. int32_t param;

  6. union

  7. {

  8. int32_t result;

  9. int32_t socket_id;

  10. };

  11. void* pvdata; //大数据使用动态申请内存保存,队列只传递指针

  12. } track_task_message_struct_t;

3、队列并不属于任何任务,所有任务都可以向同一队列写入和读出,一个队列可以由多任务或中断读写。 4、队列的深度要结合实际,可以多申请点,前提是每个队列单元尽可能小。 5、队列存在一定限制,在队头没有取出来之前,是无法取出第二个,和以前使用的STL存在差异。

创建任务和队列

#define UART_TASK_PRIO 1 //任务优先级

#define UART_TASK_STK_SIZE 80 //任务堆栈大小

TaskHandle_t UARTTaskHandler; //任务句柄

void UARTTaskFunc(void *pvParameters); //任务函数

#define TEST_TASK0_PRIO 2 //任务优先级

#define TEST_TASK0_STK_SIZE 50 //任务堆栈大小

TaskHandle_t TestTask0Handler; //任务句柄

void TestTask0Func(void *pvParameters); //任务函数

#define TEST_TASK1_PRIO 3 //任务优先级

#define TEST_TASK1_STK_SIZE 80 //任务堆栈大小

TaskHandle_t TestTask1Handler; //任务句柄

void TestTask1Func(void *pvParameters); //任务函数

#define QUEUE_TASK_PRIO 4 //任务优先级

#define QUEUE_TASK_STK_SIZE 80 //任务堆栈大小

TaskHandle_t QueueTaskHandler; //任务句柄

void QueueTaskFunc(void *pvParameters); //任务函数

//LCD显示任务当前计数,并把当前计数写入TaskToTaskQueue队列函数

void UARTTaskFunc(void *pvParameters);

//Test0任务函数,读取 TaskToTaskQueue的数据并打印

void TestTask0Func(void *pvParameters);

//Test1任务函数

void TestTask1Func(void *pvParameters);

//从队列读取串口收到的数据并打印任务函数

void QueueTaskFunc(void *pvParameters);

QueueHandle_t TaskToIrqQueue; //信息队列句柄

QueueHandle_t TaskToTaskQueue; //信息队列句柄

void MX_FREERTOS_Init(void) {

/* USER CODE BEGIN Init /

* /* USER CODE END Init */

/* USER CODE BEGIN RTOS_MUTEX /

* /* add mutexes, ... /

* /* USER CODE END RTOS_MUTEX */

/* USER CODE BEGIN RTOS_SEMAPHORES /

* /* add semaphores, ... /

* /* USER CODE END RTOS_SEMAPHORES */

/* USER CODE BEGIN RTOS_TIMERS /

* /* start timers, add new ones, ... /

* /* USER CODE END RTOS_TIMERS */

/* USER CODE BEGIN RTOS_QUEUES /

* /* add queues, ... /

* /* USER CODE END RTOS_QUEUES */

/* Create the thread(s) /

* /* creation of defaultTask */

//defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

/* USER CODE BEGIN RTOS_THREADS /

* /* add threads, ... */

BaseType_t ret;

TaskToIrqQueue=xQueueCreate(1,255); //创建一个队列,队列有1项,每个项长为255byte。

TaskToTaskQueue=xQueueCreate(5,2); //创建一个队列,队列有5项,每个项长为2byte

ret=xTaskCreate((TaskFunction_t )UARTTaskFunc, (const char* )"lcdtask", (uint16_t )UART_TASK_STK_SIZE,

(void* )NULL, (UBaseType_t )UART_TASK_PRIO, (TaskHandle_t* )&UARTTaskHandler);

ret=xTaskCreate((TaskFunction_t )TestTask0Func, (const char* )"testtask0", (uint16_t )TEST_TASK0_STK_SIZE,

(void* )NULL, (UBaseType_t )TEST_TASK0_PRIO, (TaskHandle_t* )&TestTask0Handler);

ret=xTaskCreate((TaskFunction_t )TestTask1Func, (const char* )"testtask1", (uint16_t )TEST_TASK1_STK_SIZE,

(void* )NULL,

(UBaseType_t )TEST_TASK1_PRIO,

(TaskHandle_t* )&TestTask1Handler);

ret=xTaskCreate((TaskFunction_t )QueueTaskFunc, (const char* )"queue task", (uint16_t )QUEUE_TASK_STK_SIZE,

(void* )NULL,

(UBaseType_t )QUEUE_TASK_PRIO,

(TaskHandle_t* )&QueueTaskHandler);

xTaskCreate(LED_Test,"LED",254,NULL,osPriorityNormal,&LED123);

//xTaskCreate(Key_Test,"KEY",254,NULL,osPriorityNone,&KEY123);

vTaskStartScheduler();

/* USER CODE END RTOS_THREADS */

/* USER CODE BEGIN RTOS_EVENTS /

* /* add events, ... /

* /* USER CODE END RTOS_EVENTS */

}

各个任务函数:

//显示任务当前计数,并把当前计数写入TaskToTaskQueue队列函数

void UARTTaskFunc(void *pvParameters)

{

static uint8_t i=0;

for(;;)

{

i++; xQueueSend(TaskToTaskQueue,&i,20);

vTaskDelay(500); //延时500ms,也就是500个时钟节拍 }

}

//Test0任务函数,读取 TaskToTaskQueue的数据并通过串口打印出来

void TestTask0Func(void *pvParameters)

{

uint8_t i;

char a[20];

while(1)

{ if(TaskToTaskQueue!=NULL) { xQueueReceive(TaskToTaskQueue,&i,portMAX_DELAY);

sprintf((char *)a,"%d",i);

printf("%s\r\n",a); }

}

}

//Test1任务函数

void TestTask1Func(void *pvParameters)

{

while(1)

{

printf("task1\r\n");

vTaskDelay(1000);

}

}

//从队列读取串口收到的数据并打印任务函数

void QueueTaskFunc(void *pvParameters)

{

for(;;)

{

if(TaskToIrqQueue!=NULL)

{

if(xQueueReceive(TaskToIrqQueue,rx_buf,10))

{

printf("serial:%s\r\n",rx_buf);

}

}

}

}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

HAL_UART_Receive_IT(&huart1,&rx_data,1);

rx_buf[rx_pointer++] = rx_data;

BaseType_t pxHigherPriorityTaskWoken=pdFALSE;

xQueueSendFromISR(TaskToIrqQueue,rx_buf,&pxHigherPriorityTaskWoken);

//向队列写入数据,如果写队列使某个任务就绪,并且这个任务是当前所有任务中优先级最高的,那么pxHigherPriorityTaskWoken将会被设置为pdTRUE

portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);

//如果pxHigherPriorityTaskWoken为真将进行任务切换

}

实现效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值