本文主要讲在FreeRTOS中使用消息队列传输串口接收到的不定长数据,主要传指针。串口方面使用DMA接收,然后串口空闲中断进行接收数据的管理。
原理:创建一个消息队列、创建串口数据缓冲区结构体数组。数组长度多少,队列长度就有多长。该结构体至少包含数据缓冲区和数据大小,后面有代码演示的。创建完成后让DMA接收的数据放在结构体里的数据缓冲区里面,然后用指针指向这整个结构体,最后通过消息队列发送出去
传指针的方法可以快速传输数据,哪怕有几千的字节,只要定义好保存数据的缓冲区,最后也不过是往消息队列里面发一个几字节大小的指针罢了。
一、CubeMX配置
1.1 配置串口
1.1.1 打开串口
1.1.2 打开接收DMA传输,设置优先级最高
1.1.3 打开串口全局中断
1.2 配置FreeRTOS
1.2.1 打开FreeRTOS,下面配置默认就好了,新手一般不用改什么,了解FreeRTOS的就根据你自己需要去更改
1.2.2 注意:因为FreeRTOS用了系统滴答时钟,所以这里更换一下系统时钟源
1.2.3 新建一个线程,用作接收和处理串口接收到的数据
1.2.4 创建一个消息队列
队列单个数据大小,我们直接用指针(STM32一般是4字节大小,你也可以直接填4)。因为一会传输的是指针,而不是串口接收的实际数据。
配置已完成,开始写代码。
二、Keil部分
2.1勾选下use micro lib
三、代码部分
分为图示和要添加的代码,不用你一个一个敲,都放在图示后面了。
3.1 在mian.h文件
这里的缓冲区大小和缓冲区数量你们自己按照实际的项目情况来定义。例如在你的项目中是单次少量数据,但是短时间会有很多次,那么这里可以把UART_BUFFER_SIZE改小点,节省内存,然后UART_BUFFER_QUANTITY改大些,最后别忘了改一下队列的长度!要和这个UART_BUFFER_QUANTITY同样。
以下为图示,后面可以直接复制。
只需添加以下代码:
/* 一、引用标准输入输出库 */
#include <stdio.h>
/* 二、定义串口数据缓冲区配置 */
#define UART_BUFFER_SIZE (256)
#define UART_BUFFER_QUANTITY (5)
/* 三、定义串口数据结构体,DMA传输的数据将保存在这里 */
typedef struct
{
uint8_t buffer[UART_BUFFER_SIZE]; /* 存放数据的空间 */
uint16_t size; /* 已存放数据的大小 */
}UART_RX_TypeDef;
3.2 在mian.c文件
添加变量。
添加处理函数:
要添加的代码:
/* 先定义好存放数据的空间,队列大小是5,这里就定义5个。 */
UART_RX_TypeDef uart_rx_data_t[UART_BUFFER_QUANTITY];
/* 定义一个变量用作分配上面定义的数据 */
uint8_t uart_buff_ctrl = 0;
/* 引用串口DMA handle */
extern DMA_HandleTypeDef hdma_usart1_rx;
/* 队列 handle。去freertos.c文件里面找CubeMX给你创建好的。 */
extern osMessageQId uart_queueHandle;
/* Redirect printf function */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
void USART1_DMAHandler(void)
{
if (RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) // 判断是否是空闲中断
{
UART_RX_TypeDef *pUartData; /* 定义指向创建串口数据的指针 */
__HAL_UART_CLEAR_IDLEFLAG(&huart1); // 清除空闲中断标志
HAL_UART_DMAStop(&huart1); // 停止本次DMA传输
/* 计算接收到的数据长度,放进串口数据结构体中 */
uart_rx_data_t[uart_buff_ctrl].size = UART_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
/* 将这个串口数据的结构体地址给指针 */
pUartData = &uart_rx_data_t[uart_buff_ctrl];
/* 把指向串口接收数据的指针放入消息队列。注意,这里传的是指针的地址,也就是指针串口数据结构体的指针的地址不能传pUartData本身 */
xQueueSendFromISR(uart_queueHandle, &pUartData, NULL);
/* 这里进行加一,使其下一次DMA传输时将接收的数据放到下一个串口缓冲区中 */
uart_buff_ctrl++;
/* 取余操作防止越界 */
uart_buff_ctrl %= UART_BUFFER_QUANTITY;
/* 重启开始DMA传输 */
HAL_UART_Receive_DMA(&huart1, uart_rx_data_t[uart_buff_ctrl].buffer, UART_BUFFER_SIZE);
}
}
在main函数中开启串口空闲中断和DMA传输。
这部分放main函数里面:
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, uart_rx_data_t[uart_buff_ctrl].buffer, UART_BUFFER_SIZE);
3.3 在stm32f4xx_it.c文件
如果你的是F1系列的,就去stm32f1xx_it.c里面去找,我这个就是f4。
引用外部函数声明
串口1中断调用处理函数
代码:
extern void USART1_DMAHandler(void);
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
USART1_DMAHandler();
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
3.4 在freertos文件
找到我们的两个线程。主要是Uart_Thread线程函数
void Uart_Thread(void const *argument)
{
/* USER CODE BEGIN Uart_Thread */
UART_RX_TypeDef *pRecvUartData; /* 定义指向串口数据的指针 */
printf("Uart Thread init. \r\n");
/* Infinite loop */
for (;;)
{
/* 一直等待直到有数据 */
/* 参数1:队列handle */
/* 参数2:放入接收消息的指针,也就是让它指向串口中断中已接收串口数据完成的结构体 */
/* 参数3:等待时间,这里是等待永久,直到有数据 */
if (xQueueReceive(uart_queueHandle, &pRecvUartData, portMAX_DELAY) == pdTRUE)
{
/* 打印或处理接收到的数据,这里为了演示做个串口回显 */
HAL_UART_Transmit(&huart1, pRecvUartData->buffer, pRecvUartData->size, 1000);
}
}
/* USER CODE END Uart_Thread */
}
添加完毕,现在可以Run了
四、演示
直接定时发送,有回显说明没问题了。