基于stm32的UART高效接收DMA+IDLE编程HAL库
本文目标:基于stm32的UART高效接收DMA+IDLE编程HAL库
按照本文的描述,应该可以在对应的硬件上通实验并举一反三。
先决条件:拥有C语言基础,装有编译和集成的开发环境,比如:Keil uVision5
使用外设:USART2、GPIO
HAL库版本:STM32F1xx HAL Driver version number 1.8.5
STMCubeMX版本:6.10.0
Keil uVision5版本:V5.38.0.0
实验目的
新手学习UART实验,使用串口的DMA+IDLE空闲中断进行接收数据。
使用场景原理图
在我的应用场景中,设计一个实验进行自发的实验,其中我这里使用串口2进行实验
其中涉及使用的HAL库API如下:
//查询方式:
//发送:
HAL_UART_Transmit
//接收:
HAL_UART_Receive
//中断方式:
//发送:
HAL_UART_Transmit_IT
HAL_UART_TxCpltCallback
//接收:
HAL_UART_Receive_IT
HAL_UART_RxCpltCallback
//DMA方式:
//发送:
HAL_UART_Transmit_DMA
HAL_UART_TxHalfCpltCallback
HAL_UART_TxCpltCallback
//接收:
HAL_UART_Receive_DMA
HAL_UART_RxHalfCpltCallback
HAL_UART_RxCpltCallback
// 错误
HAL_UART_ErrorCallback
HAL_UART_ErrorCallback
IDLE
IDLE,空闲的定义是:总线上在一个字节的时间内没有再接收到数据。UART 的 IDLE 中断何时发生?RxD 引脚一开始就是空闲的啊,难道 IDLE 中断一直产生?不是的。当我们使能 IDLE 中断后,它并不会立刻产生,而是:至少收到 1 个数据后,发现在一个字节的时间里,都没有接收到新数据,才会产生 IDLE 中断。我们使用 DMA 接收数据时,确实可以提高 CPU 的效率,但是“无法预知要接收多少数据”,而我们想尽快处理接收到的数据。怎么办?比如我想读取 100 字节的数据,但是接收到 60 字节后对方就不再发送数据了,怎么办?我们怎么判断数据传输中止了?可以使用IDLE 中断。在这种情况下,DMA 传输结束的条件有 3:
① 接收完指定数量的数据了,比如收到了 100 字节的数据了,HAL_UART_RxCpltCallback被调用
② 总线空闲了:HAL_UARTEx_RxEventCallback 被调用
③ 发生了错误:HAL_UART_ErrorCallback 被调用
使用 IDLE 状态来接收的函数有:
//查询方式:
//接收:
HAL_UARTEx_ReceiveToIdle
//回调函数:
//根据返回参数 RxLen 判断是否接收完毕,还是因为空闲而返回
//中断方式:
//接收:
HAL_UARTEx_ReceiveToIdle_IT
//回调函数:
完毕:HAL_UART_RxCpltCallback
因为空闲而中止:
HAL_UARTEx_RxEventCallback
//DMA方式:
//接收:
HAL_UARTEx_ReceiveToIdle_DMA
//回调函数:
传输一半:
HAL_UART_RxHalfCpltCallback
完毕:
HAL_UART_RxCpltCallback
因为空闲而中止:
HAL_UARTEx_RxEventCallback
// 错误
HAL_UART_ErrorCallback
程序设计
① 使用 DMA+IDLE 中断的方式接收数据,使用全局的数组来进行接收;
② main函数中将接收到的数据发回上位机。
串口配置
打开配置工具,进行串口配置
配置中断
配置DMA
代码片段
按照上述的配置进行底层配置之后,我们写点代码,进行接收数据,我们简单粗暴的写点函数,如下的代码片段
// 接收完成进入
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2)
{
g_uart2_rx_complete = 1;
Uart2ReciveDataSize = UART2_RECIVE_MAX;
HAL_UART_Receive_DMA(&huart2,Uart2ReciveBuff,UART2_RECIVE_MAX); // 重新启动接收
}
}
// 空闲中断进入
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart == &huart2)
{
g_uart2_rx_complete = 1;
Uart2ReciveDataSize = Size;
/* re-start DMA+IDLE rx */
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, Uart2ReciveBuff, UART2_RECIVE_MAX);
}
}
接收错误进入
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2)
{
/* re-start DMA+IDLE rx */
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, Uart2ReciveBuff, UART2_RECIVE_MAX);
}
}
main函数中代码片段
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart2,Uart2ReciveBuff,UART2_RECIVE_MAX); // 开启接收
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if(g_uart2_rx_complete == 1)
{
g_uart2_rx_complete = 0;
HAL_UART_Transmit_DMA(&huart2,Uart2ReciveBuff,Uart2ReciveDataSize); // 将接收的数据进行回显
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
编译、烧写、运行,可以看到开发板的上的串口能成功接收数据,将接收到的数据进行回显,实验设计成功。
工程实验成功,后续将会继续记录项目中的实验,感谢关注。
本文中使用的测试工程
https://download.csdn.net/download/weixin_44317448/89208845