提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
今天我学习了FreeRTOS的新知识消息队列
一、消息队列是什么?
FreeRTPS消息队列产生就是为了解决任务与任务,任务与中断之间的消息传递,一般我们在任务和任务直接消息的传递是使用一个全局变量来实现的,但是我们使用这种方法也存在一些问题。所以在FreeRTOS中就出现了消息队列。他就是相当于把任务和任务直接传递的信息拷贝到队列里面,然后在调用队列里面的信息,这样就实现了任务与任务之间消息的传递
队列不属于某一个任务,是所以任务都可以调用的
入队阻塞:当wait等待设置为0时就是没有发送数据马上执行其他任务,当wait等待为我们设定的时间,则没有发送数据时要阻塞我们设定的时间,当wait等待设置为portMax_Delay,则一直阻塞知道发送了消息,出队阻塞同理。
二、相关API
2.1 创建队列
/*
动态创建队列首先打开configSUPPORT_DYNAMIC_ALLOCATION的宏
形参 uxQueueLength 值列队消息项
uxItemSize 指定发送的数据类型(字节)
xQueueCreate是带BaseType_t的返回值函数
队列创建成功返回:该句柄
失败:0
*/
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
/*
使用静态创建队列打开configSUPPORT_STATIC_ALLOCATION 的宏
形参: uxQueueLength 队列的消息项数目
uxItemSize 指定发送的数据类型(字节)
pucQueueStorage 消息存储区,必须是一个uint8_t的数组,存储区大小要大于uxItemSize
pxQueueBuffer 保存队列结构体
xQueueCreateStatic是带BaseType_t的返回值函数
队列创建成功返回:该句柄
失败:NULL
*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
#define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) )
#endif /* configSUPPORT_STATIC_ALLOCATION */
2.2 像队列发送数据
重点介绍下xQueueSend();函数,其他函数请看正点原子FreeRTOS文档第十三章队列213页
/*
发送消息到队列
xQueue 队列句柄
pvItemToQueue 要发送的消息,记住这个字节要与自己定义的xQueueCreate()的字节类型一样
xTicksToWait 阻塞时间 类型:0 没有数据发送马上执行其他的任务,x值 阻塞xms等待数据发送,xms运行后还是没有发送则马上执行其他任务,portMax_Delay 一直阻塞直到数据发送成功
该函数是带BaseTepy_t的返回值。
成功:pdTRUE
失败:errQUEUE_FULL
*/
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
2.3 从队列读取数据
重点介绍xQueueReceive();函数,其他函数请看正点原子FreeRTOS文档第十三章队列225页
/*
从队列里读取消息
xQueue 队列句柄
pvBuffer 接收的消息存放地址
xTicksToWait 阻塞时间 类型:0 没有数据接收马上执行其他的任务,x值 阻塞xms等待数据发送,xms运行后还是没有数据接收则马上执行其他任务,portMax_Delay 一直阻塞直到有数据接收成功
该函数是带BaseTepy_t的返回值。
成功:pdTRUE
失败:errQUEUE_FULL
*/
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait )
三、实战
源码 main.c
重点看Task1Function和Task2Function以及Task4Function
补充:注意在使用队列时要添加 queue.h库
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stdio.h"
#ifdef __GNUC__ //printf重定向
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
return ch;
}
TaskHandle_t HandlerTask1;
TaskHandle_t Task_Name4;
BaseType_t xReturn;
#define Queue_item_Number 10 //队列里的项数量
#define Queue_size sizeof(uint32_t) //队列的数据类型
QueueHandle_t uTaskQueue1; //队列句柄
void SystemClock_Config(void);
StackType_t xTask3Static[128];
StaticTask_t xTaskTCB;
StackType_t xIdle3Static[128];
StaticTask_t xIdleTCB;
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize ){
*ppxIdleTaskTCBBuffer=&xIdleTCB;
*ppxIdleTaskStackBuffer=xIdle3Static;
*pulIdleTaskStackSize=128;
}
void Task1Function( void * param){ //像队列里发送消息
uint32_t dat=50; //发送的数据
BaseType_t err;
for(;;){
err=xQueueSend(uTaskQueue1,&dat,10);
if(err==pdTRUE){
printf("Send to successfully\r\n");
}else{
printf("Send to err\r\n");
}
vTaskDelay(100);
}
}
void Task2Function(void* param){//从队列里接收消息
uint32_t dat_Re; //接收数据
BaseType_t err_re;
for(;;)
{
err_re=xQueueReceive(uTaskQueue1,&dat_Re,0);
if(err_re==pdTRUE){
printf("Receive to successfully\r\n");
printf("Queue->Receive:%d\r\n",dat_Re);
}else{
printf("Receive to err\r\n");
}
vTaskDelay(100);
}
}
void Task3Funtion(void* param){
TickType_t st_time=xTaskGetTickCount();
while(1){
uint32_t num =1;
xTaskDelayUntil(&st_time,30);
}
}
void Task4Funtion(void* param){
taskENTER_CRITICAL();
uTaskQueue1=xQueueCreate(Queue_item_Number,Queue_size); //创建队列
xReturn=xTaskCreate(Task1Function,"Task1",128,NULL,2,&HandlerTask1);
xTaskCreate(Task2Function,"Task2",128,NULL,2,NULL);
xTaskCreateStatic(Task3Funtion,"Task3",128,NULL,2,xTask3Static,&xTaskTCB);
if(xReturn == pdPASS){
uint8_t buffS[20]="Task1 Create OK..\r\n";
HAL_UART_Transmit(&huart1,(uint8_t*)buffS,strlen(buffS)*sizeof(char),HAL_MAX_DELAY);
}
vTaskDelete(Task_Name4);
taskEXIT_CRITICAL();
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
xTaskCreate(Task4Funtion,"Task4",600,NULL,1,&Task_Name4);
vTaskStartScheduler();
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */
效果展示:
总结
队列说白了就是用来任务与任务之间的数据传递。只要了解了队列创建、队列添加消息、队列取出消息,然后还有中断就OK了
身无彩凤双飞翼,心有灵犀一点通