1、简介
在裸机开发时候,一般都是设定一个全局变量来数据修改传递等,但是在Freertos系统中,如果设定全局变量,当任务1对全局变量a进行操作,可能此时任务2也在对变量a进行操作,会出现问题,此时就需要队列。
队列可以用于在不同的任务之间或者任务和中断服务程序之间传递数据。
2、队列主要参数
2.1 出队、入队阻塞
当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队。
阻塞时间如果设置: 0:直接返回不会等待;
0~port_MAX_DELAY:等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不再等待;
port_MAX_DELAY:死等,一直等到可以入队为止。
2.2 创建队列
API:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
uxQueueLength:队列可同时容纳的最大项目数 。
uxItemSize:存储队列中的每个数据项所需的大小(以字节为单位)。
返回值: 如果队列创建成功,则返回所创建队列的句柄 。 如果创建队列所需的内存无法分配 ,则返回 NULL。
2.3 写队列
写队列共有以下函数:
xQueueSend() //往队列的尾部写入消息
xQueueSendToBack() //往队列的尾部写入消息
xQueueSendToFront() //往队列的头部写入消息
xQueueOverwrite()//覆写队列消息(只用于队列长度为 1 的情况)
xQueueSendFromISR() //在中断中往队列的尾部写入消息
xQueueSendToBackFromISR() //在中断中往队列的尾部写入消息
xQueueSendToFrontFromISR() //在中断中往队列的头部写入消息
xQueueOverwriteFromISR() //在中断中覆写队列消息(只用于队列长度为 1 的情况)
API:
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
xQueue:队列的句柄,数据项将发送到此队列。
pvItemToQueue:待写入数据
xTicksToWait:阻塞超时时间
返回值:如果成功写入数据,返回 pdTRUE,否则返回 errQUEUE_FULL。
2.4 读队列
读队列共有以下函数:
xQueueReceive() //从队列头部读取消息,并删除消息
xQueuePeek() //从队列头部读取消息,但是不删除消息
xQueueReceiveFromISR() //在中断中从队列头部读取消息,并删除消息
xQueuePeekFromISR() //在中断中从队列头部读取消息
API:
BaseType_t xQueueReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait
);
xQueue:待读取的队列 pvItemToQueue:
数据读取缓冲区 xTicksToWait:阻塞超时时间
返回值: 成功返回 pdTRUE,否则返回 pdFALSE。
3、测试需求
创建一个队列, 通过串口调试助手,发送A,向队列发送数据,发送B 向队列读取数据。
4、STM32CubeMX配置
4.1 打开串口中断
4.3 中断接收测试
首先在main.c、main.h进行函数变量的添加:
char buf;
uint8_t sign_1;
extern char buf;
extern uint8_t sign_1;
接着在MainThread.cpp中进行中断接收函数以及中断回调函数:
/**
******************************************************************************
* File Name : MainThread.cpp
* Description : 主控制线程
******************************************************************************
* @attention
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "MainThread.h"
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数
{
if(huart1.Instance == USART1)
{
if(buf=='A'){
sign_1=1;
}
else if(buf=='B'){
sign_1=2;
}
else{
sign_1=0;
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&buf, sizeof(buf));
}
/* Functions -----------------------------------------------------------------*/
/**
* @brief 主线程函数(应用层)
* @retval None
*/
void MainTask(void *argument){
while(1){
HAL_UART_Receive_IT(&huart1, (uint8_t *)&buf, sizeof(buf));
osDelay(1);
}
}
/**
* @brief 主控制线程初始化
* @retval None
*/
void MainThread_Init(void){
const osThreadAttr_t MainTask_attributes = {"MainTask",0,0,0,0,128,(osPriority_t) osPriorityNormal};
osThreadNew(MainTask, NULL, &MainTask_attributes);//创建主线程
}
/**********************************END OF FILE*********************************/
接着在freertos.c在任务1、2中进行条件判断:
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for(;;)
{
if(sign_1==1){
HAL_UART_Transmit(&huart1,(uint8_t *)"任务1\r\n",5,0xFFFF);
sign_1=0;
}
osDelay(1);
}
/* USER CODE END StartDefaultTask */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
if(sign_1==2){
HAL_UART_Transmit(&huart1,(uint8_t *)"任务2\r\n",5,0xFFFF);
sign_1=0;
}
osDelay(1);
}
/* USER CODE END StartTask02 */
}
测试结果如下:
4.4 建立队列
Tasks and Queues -> Add -> OK
然后生成代码。
5、Keil 5 程序设计
5.1 串口重映射
为了便于测试对串口进行重映射,首先在main.h中添加队列头文件:
#include "queue.h"
#include "string.h"
#include "stdio.h"
接着在usart.c最下方加入重映射程序:
int fputc(int ch,FILE *f)
{
uint8_t temp[1] = {ch};
//采用轮询方式发送1字节数据
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
5.2 freertos.c测试代码设计
任务1发送队列:
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
uint16_t buf = 100;
BaseType_t status;
/* Infinite loop */
for(;;)
{
if(sign_1==1){
status = xQueueSend(myQueue01Handle, &buf, 0);
if (status == pdTRUE){
printf("写入队列成功,写入值为%d\r\n", buf);
}
else{
printf("写入队列失败\r\n");
}
sign_1=0;
}
osDelay(1);
}
/* USER CODE END StartDefaultTask */
任务2接收队列:
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
uint16_t buf;
BaseType_t status;
/* Infinite loop */
for(;;)
{
if(sign_1==2){
status = xQueueReceive(myQueue01Handle, &buf, 0);
if (status == pdTRUE){
printf("队列数据读取成功,读出值为%d\r\n", buf);
}
else{
printf("队列读取失败\r\n");
}
sign_1=0;
}
osDelay(1);
}
/* USER CODE END StartTask02 */
}
此时编译会出现如下错误:
error: #35: #error directive: “include FreeRTOS.h” must appear in source files before “include event
解决办法:
这个错误其实是提示我们,如果我们调用了#include "queue.h"头文件,那还必须在调用FreeRTOS.h才行。
再次编译无误!
5.3 测试
从测试数据可以看到写入队列两次,读取两次成功,第三次失败,证明测试无误!
上文如有错误恳请各位大佬指正!!!