STM32-HAL库快速入门:UART不定长收发DMA最终解决方案
一、DMA实现不定长接收字符串原理
DMA,可以看作一个CPU的秘书,可以在程序配置好后,不经过CPU的干预,直接做数据搬移的操作,来大大节省CPU的资源消耗
原理:
我们知道,串口通信开始为空闲位,然后发送方先发送起始位,再发送字符,最后发送校验位和停止位,最后再变为空闲位,那么当数据连续收发时,如果我们像上一篇文章一样,接收一个字符就触发一次中断,那是不是中断的太频繁了呢?就像你对妹子说I love you,可是你每说一个字母就摇动对方一下,确认对方听清了没,我估计你没说到第5个字符对方就扭头走了吧。所以,这篇文章实现了DMA+串口ilde中断(空闲中断)的方式来实现不定长接收,类似于,你一次性说出i love you然后再确认对方有没有听到。
空闲中断:当串口连续通信时,每发送两个字符之间是没有空闲位的,只有当全部字符发送完毕才进入空闲位,然后触发空闲中断。
同样,我们先定义一个数组,如rx_buffer[100],已知,当触发DMA接收时,DMA计数器自动设置为100,每收到一个字节,计数器减1,所以,我们可以获取这个计数器数a,然后100-a,就是已经接收到的字符了。再把数据从数组中拿出来,这样就实现了不定长收发功能。
二、cubemx配置
第一步:打开串口,设置为异步模式
第二步:配置串口参数
第三步:打开串口中断
第四步:打开DMA
三、keil代码编写
#define MAX_Size_rx_buffer 100
uint8_t rx_buffer[MAX_Size_rx_buffer];
uint8_t rx_error[] = "The data is too long\r\n";
HAL_UART_Receive_DMA(&huart1,rx_buffer,MAX_Size_rx_buffer);//开启串口DMA接收
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//开启空闲中断
extern uint8_t rx_buffer[MAX_Size_rx_buffer];
uint16_t len = 0;
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1);
len = MAX_Size_rx_buffer - __HAL_DMA_GET_COUNTER(huart1.hdmarx);
if(len>=MAX_Size_rx_buffer)
{
HAL_UART_Transmit_DMA(&huart1,rx_error,22);
}
else
{
HAL_UART_Transmit_DMA(&huart1,rx_buffer,len);
}
HAL_UART_Receive_DMA(&huart1,rx_buffer,100);
}
第一步:定义接收缓存区
main.h
main.c
第二步:while前开启DMA接收和空闲中断
第三步:由于工程没有空闲中断的回调函数,所以我们要在串口1的中断服务函数来编写
先外部声明一下主函数用到的接收缓存区数组,再定义一下用来记录接收数据长度的变量len
stm32f4_it.c
第四步:在串口1中断服务函数编写不定长收发代码
先获取空闲中断标志位是否发生了,若发生则先清楚标志位再停止DMA,然后判断出来接受了多少字符串,判断长度是否大于等于最大数量,100个字符的最大数量,若点击了发送新行,则还会有\R和\N,最后,重新开启DMA接收
四、现象
注意:
由于hal库bug,第一次发送时候不要超过max_size,因为程序第一次启动后,第一次暂停DMA之后,使用dma模式发出字符串
有任何bug请直接发在评论区我会第一时间更改
作者:ZTL-chd