STM32 HAL库 使用DMA同时收发串口数据

STM32 HAL库 使用DMA同时收发串口数据

发现最近随便转载的很多啊,未经授权禁止转载!抄袭!!否则转载者死全家!!另外这是我的笔记,不是教程,难免会有错误,不具有很高的参考性,望周知。

在上个文章里,我介绍了使用串口空闲中断的方法来接收任意长度的数据,但是在我调试串口屏的的时候,发现了一个严重的问题。我是用的是北京迪文科技的串口屏,当向串口屏发送写入数据指令的时候,串口屏会返回一个应答,正是这个应答就导致了STM32在运行的时候会卡死。

于是我开始在网络上查找资料(顺便吐槽一下,网络上的资料大部分都是转载,原创资料少之又少,就连老子的文章也被哪个阉人转载到了其他地方),发现HAL库有一个锁定机制,如果串口在发送数据的时候又接收数据,就会导致程序锁住,表现为卡死。于是我用STLink调试跟踪,发现最终程序死在了HAL_UART_IRQHandler函数中。网友大多表示要么放弃HAL库,要么就自己实现接收函数。我为了保证整个程序的移植性,最后决定继续使用HAL库,尝试使用其他办法解决串口同时收发的问题。

我突然想到之前有一个项目里使用的DMA来接收串口数据,哪个项目里串口是可以同时收发数据且多个串口都是可以同时使用的。于是我在此项目中使用DMA接收数据,发现程序运行良好,不再卡死。

使用DMA需要在CubeMX中串口的选项中添加DMA通道:

添加DMA通道

接着打开串口接收中断,如果不打开接收中断DMA无法接收到数据。

打开串口中断

为了防止DMA的中断优先级与滴答定时器冲突,更改一下DMA的优先级,并保持滴答定时器的优先级最高。

调整优先级

在代码中,删除原来的接收回调函数,接收工作全部交给DMA去完成,所以不需要自己处理接收函数。然后在main函数中开启DMA接收,指定DMA接收的缓冲区。由于串口是否接收完毕还是靠串口空闲中断来完成,所以仍然需要打开串口空闲中断。

1
2
3
4
5
6
7
8
int main()
{
...;
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart2, (uint8_t *)RxBuffer2, sizeof(RxBuffer2));
while(1)
...;
}

在串口服务函数中,添加串口中断处理部分的代码。当串口进入空闲中断时(表示数据接收完毕),关闭DMA接收,并将接收标志置1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */

/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
RxFlag2 = 1;
}
/* USER CODE END USART2_IRQn 1 */
}

在回到main函数中,根据接收标志去处理接收到的串口数据就好了,最后不要忘记处理完了还要将缓冲区清空并再打开DMA接收并将接收标志置0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
while(1)
{
if(RxFlag2)
{
printf("uart2 dma :%s\r\n", RxBuffer2);
RxFlag2 = 0;
memset(RxBuffer2, 0, sizeof(RxBuffer2));
HAL_UART_Receive_DMA(&huart2, (uint8_t *)RxBuffer2, sizeof(RxBuffer2));
}
}

}

最后测试,串口可以同时收发数据,不会发生卡死的情况了。
如果发送的时候不想占用CPU资源,也可以使用DMA来发送数据,在CubeMX里面添加串口发送的DMA通道,并设置优先级,这里的优先级可以设置与接收相同。发送时只需要调用HAL_UART_Transmit_DMA这个函数就可以发送了。

HAL_UART_Transmit_DMA(&huart2, “uart2 send test\r\n”, sizeof(“uart2 send test\r\n”));

比如fputc函数可以这样改:

1
2
3
4
5
6
7
8
9
10
11
12
int fputc(int ch,FILE *f)
{
uint8_t temp[1]={ch};

#ifdef USE_HAL_DRIVER
HAL_UART_Transmit_DMA(&huart1,temp,1); //UartHandle是串口的句柄
#else
USART1->DR = temp[0];
while((USART1->SR & 0x40) == 0);
#endif
return 0;
}

坚持原创技术分享,您的支持是我前进的动力!