目录
2、函数 xQueueCreateStatic():静态创建队列
1、函数 xQueueSend()、 xQueueSendToBack()和 xQueueSendToFront()
2、函数 xQueueGenericSend():任务调用底层函数
xQueueSendToFrontFromISR()函数:在中断里面操作函数
4、函数 xQueueOverwriteFromISR():中断底层调用函数
队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中
断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之
间要交流的数据保存在队列中,叫做队列项目。队列所能保存的最大数据项目数量叫做队列的
长度,创建队列的时候会指定数据项目的大小和队列的长度。 由于队列用来传递消息的,所以
也称为消息队列。 FreeRTOS 中的信号量的也是依据队列实现的! 所以有必要深入的了解
FreeRTOS 的队列。
队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中
提取消息。
因为FreeRTOS是多任务的,里面的会有一些局部变量,需要供给其他任务使用,使用引出这个队列的定义,就是为了实现它们之间的交流。
当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任
务从队列中读取消息无效的时候任务阻塞的时间。 出队就是就从队列中读取消息,出队阻塞是
针对从队列中读取消息的任务而言的。 比如任务 A 用于处理串口接收到的数据, 串口接收到数
据以后就会放到队列 Q 中, 任务 A 从队列 Q 中读取数据。但是如果此时队列 Q 是空的,说明
还没有数据,任务 A 这时候来读取的话肯定是获取不到任何东西,那该怎么办呢?任务 A 现在
有三种选择,一:二话不说扭头就走,二:要不我在等等吧,等一会看看,说不定一会就有数
据了,三:死等,死也要等到你有数据!选哪一个就是由这个阻塞时间决定的,这个阻塞时间
单位是时钟节拍数。 阻塞时间为 0 的话就是不阻塞,没有数据的话就马上返回任务继续执行接
下来的代码, 对应第一种选择。 如果阻塞时间为 0~ portMAX_DELAY, 当任务没有从队列中获
取到消息的话就进入阻塞态,阻塞时间指定了任务进入阻塞态的时间,当阻塞时间到了以后还
没有接收到数据的话就退出阻塞态,返回任务接着运行下面的代码,如果在阻塞时间内接收到
了数据就立即返回,执行任务中下面的代码,这种情况对应第二种选择。当阻塞时间设置为
portMAX_DELAY 的话,任务就会一直进入阻塞态等待,直到接收到数据为止! 这个就是第三种选择。
将消息加入到队列中。和读取队列一样,当一个任务向队列发送消息的话也可以设置阻塞时间。比如任务 B 向消息队列 Q 发送消息,但是此时队列 Q 是满的,那肯定是发送失败的。此时任务B 就会遇到和上面任务 A 一样的问题,这两种情况的处理过程是类似的,只不过一个是向队列 Q 发送消息,一个是从队列 Q 读取消息而已。
有一个结构体用于描述队列,叫做 Queue_t,这个结构体在文件 queue.c 中定义如下

在使用队列之前必须先创建队列, 有两种创建队列的方法,一种是静态的,使用函数
xQueueCreateStatic();另一个是动态的,使用函数 xQueueCreate()



![]()



中断入队函数,就是在定时器中断、外部中断、串口中断服务函数里面所调用的API函数。

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

参数

返回值

此函数才是真正干活的,上面讲的所有的任务级入队函数最终都是调用的此函数,此函数
也是我们后面重点要讲解的
函数原型:

参数

返回值

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

参数

返回值

注:我们注意观察,可以看出这些函数都没有设置阻塞时间值。原因很简单,这些函数都是在
中断服务函数中调用的, 并不是在任务中,所以也就没有阻塞这一说了!
此函数是 xQueueOverwrite()的中断级版本, 用在中断服务函数中,在队列满的时候自动覆
写掉旧的数据,此函数也是一个宏,实际调用的也是函数 xQueueGenericSendFromISR(),前面三个函数也都是调用这个函数来使用。
函数原型

![]()
参数

返回值


此函数用于在任务中从队列中读取一条(请求)消息, 读取成功以后就会将队列中的这条数
据删除。
函数原型

参数

返回值

此函数用于从队列读取一条(请求)消息, 只能用在任务中! 此函数在读取成功以后不会将
消息删除。
函数原型
![]()

参数

返回值

不 管 是 函 数 xQueueReceive() 还 是 xQueuePeek() , 最 终 都 是 调 用 的 函 数
xQueueGenericReceive(),此函数是真正干事的。
函数原型

参数

返回值

此函数是 xQueueReceive()的中断版本, 用于在中断服务函数中从队列中读取(请求)一条消息,读取成功以后就会将队列中的这条数据删除。
函数原型

参数

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

参数

返回值

接下来我就介绍一下具体的实际操作,也就是调用上面那几个函数,来完成一些变量传递的功能。
实验内容:
我找到一个光照传感器BH1750,建立四个任务,一个任务是开始任务,这个任务作用就是创建其他三个任务,创建三个任务完成,会删除这个任务。一个任务是LED闪烁,表示功能正常运行,一个读取BH1750的关照数值,另一个任务把关照数值传进来,然后判断是否达到阈值,然后利用蜂鸣器报警。
操作流程:
先创建一个队列和三个任务;
然后调用入队函数(向队列中传入参数);
然后调用读取函数(向队列中读取参数);
- #include "sys.h"
- #include "usart.h"
- #include "gpio.h"
- #include "delay.h"
- #include "lcd.h"
- #include "FreeRTOS.h"
- #include "task.h"
- #include "bh1750.h"
- #include "queue.h"
-
- //LCD刷屏时使用的颜色
-
-
- QueueHandle_t LxQueue;
-
- //任务优先级
- #define START_TASK_PRIO 4
- //任务堆栈大小
- #define START_STK_SIZE 128
- //任务句柄
- TaskHandle_t StartTask_Handler;
- //任务函数
- void start_task(void *pvParameters);
-
- //任务优先级
- #define LED1_TASK_PRIO 0
- //任务堆栈大小
- #define LED1_STK_SIZE 20
- //任务句柄
- TaskHandle_t LED1Task_Handler;
- //任务函数
- void led1_task(void *pvParameters);
-
-
- //任务优先级
- #define LxTask_TASK_PRIO 0
- //任务堆栈大小
- #define LxTask_STK_SIZE 128
- //任务句柄
- TaskHandle_t LxTaskTask_Handler;
- //任务函数
- void LxTask(void *pvParameters);
-
-
- //任务优先级
- #define Beep_TASK_PRIO 0
- //任务堆栈大小
- #define Beep_STK_SIZE 128
- //任务句柄
- TaskHandle_t BeepTask_Handler;
- //任务函数
- void BeepState(void *pvParameters);
-
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
- delay_init(); //延时函数初始化
- My_USART_Init(); //初始化串口
- MX_GPIO_Init(); //初始化GPIO
- LCD_Init();
- LCD_Clear(BLACK);
- BACK_COLOR=BLACK;
- POINT_COLOR=RED;
- bh1750_init();
-
- //创建开始任务
- 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(); //进入临界区
-
- LxQueue=xQueueCreate(1,sizeof(float)); //创建队列
- //创建LED1任务
- xTaskCreate((TaskFunction_t )led1_task,
- (const char* )"led1_task",
- (uint16_t )LED1_STK_SIZE,
- (void* )NULL,
- (UBaseType_t )LED1_TASK_PRIO,
- (TaskHandle_t* )&LED1Task_Handler);
-
- //创建光照读取任务
- xTaskCreate((TaskFunction_t) LxTask,
- (const char* ) "LxTask",
- (uint16_t ) LxTask_STK_SIZE,
- (void* ) NULL,
- (UBaseType_t ) LxTask_TASK_PRIO,
- (TaskHandle_t* ) &LxTaskTask_Handler);
- //创建蜂鸣器任务
- xTaskCreate((TaskFunction_t) BeepState,
- (const char* ) "BeepState",
- (uint16_t ) Beep_STK_SIZE,
- (void* ) NULL,
- (UBaseType_t ) Beep_TASK_PRIO,
- (TaskHandle_t* ) &BeepTask_Handler);
-
- vTaskDelete(StartTask_Handler); //删除开始任务
- taskEXIT_CRITICAL(); //退出临界区
- }
-
- /* jwiw */
- //LED1任务函数
- void led1_task(void *pvParameters)
- {
- while(1)
- {
- LED1=~LED1;
- vTaskDelay(800);
- }
- }
-
- void LxTask(void *pvParameters)
- {
- float temp;
- while(1)
- {
- temp=BH1750_Measure();//读取光照强度
-
- if(LxQueue!=NULL)
- xQueueSend(LxQueue,&temp,10);//把数据发送到队列中
-
- LcdSprintf(20,240,200,30,24,"Temp_BUFF:%.2f",temp); //在LCD上显示
- vTaskDelay(300);
- }
- }
-
- float Lx;
- void BeepState(void *pvParameters)
- {
- while(1)
- {
- if(LxQueue!=NULL)
- xQueueReceive(LxQueue,&Lx,10);//读取队列中的数据
-
- if(Lx>300)
- {
- LED0=1;
- GPIO_ResetBits(GPIOB,GPIO_Pin_8);//蜂鸣器任务
- }
- else
- {
- LED0=0;
- GPIO_SetBits(GPIOB,GPIO_Pin_8);
- }
-
- LcdSprintf(20,280,200,30,24,"Lx_Buff:%.2f",Lx); //LCD显示是否和上面显示一致
- vTaskDelay(300);
- }
-
- }
-
-
