摘要:
stm32串口的使用,可以说是这个芯片的灵魂了,DMA、IDLE空闲中断、不定长接收,这些都让stm32的串口产生异常强大的力量。如何用好这些特性,并避免踩坑,本文对几点主要配置和逻辑做了整理。感谢CSDN上各位大虾的无偿分享,本文因知识点太零散,所以无法一一做好文章引用,如果哪位大虾发现本文的某些内容是来自您的文章,请私信我添加您的链接,这里先行谢过了。
正文:
先提供一份测试完全没问题的代码,后面详解一下,以下是在中断文件中的回调函数。,2023年5月更新,DMA与IDLE配合的函数为 HAL_UARTEx_ReceiveToIdle_DMA。
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef* huart, uint16_t Size)
{
if(( Size> 3) && (Size!= USART_RX_MAX)){
rx_f = 1;
}
HAL_UARTEx_ReceiveToIdle_DMA(huart,rx_buf,USART_RX_MAX); //重新打开DMA与IDLE接收
}
下面是串口中断函数配置,2023年5月更新
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
然后,我们从头开始说起。
一、串口配置问题
串口配置属于比较基础的,推荐使用stm32cubeide,简直是懒人神器,配置时注意几个点即可。
1、串口接收引脚要上拉
这个一般不会有什么影响,但是有时485芯片电压不稳的时候,会产生非常多的接收信号干扰,造成程序跑飞,做了上拉,就可以避免这个问题。
2、串口根据波特率设置对应的引脚速度
一般9600及以下选择LOW即可,19200~115200选择MEDIUM, 再往上的直接用HIGH就行。这里要注意,有一个output level,这个是设置GPIO初始电平高低的,串口输入尽量配置LOW。
3、串口驱动方式
用推挽PUSH PULL比较合适,开漏OPEN Drain有时会有问题,这个跟电路设计有关系。
4、采样率分频(对应图中1、2)
SMT32的串口一般使用16倍过采样,这就需要分频要设置合理范围,STM32CUBEIDE一般默认设置为1,在大多数情况下,不需要修改这个数值。
如果实在需要修改,可以参考下面的计算方法来粗略估算一下:
如19200bps,每个bit位约52us,主频64M来说,每次采集间隔为1/64us,16倍过采样,则对应1/4us,对于串口接收的一个bit来说,16倍采样点尽量落在前1/2处,相对应需要1/2us。用52us来除1/2us,得到最大的分频率为104,所以,19200bps如果使用128分频,可能会造成接收信号判断错误,引起串口接收异常。
5、DMA配置
1)接收启用DMA,发送尽量不用DMA。可以使用发送中断的非阻塞函数HAL_UART_Transmit_IT,使用起来也很方便,并且可以释放一部分运算时间。
2)DMA与USART的初始化顺序问题,有时配置串口时,发现接收一直为0,反反复复找不到原因所在,最终找到原因,原来是DMA的初始化放到了USART后面。这里的顺序导致接收不到的原因是,DMA的时钟开启,必须在USART之前,否则USART是无法使用DMA进行数据接收的。所以,遇到DMA串口接收,没有数据进入的情况,首先查一下DMA与USART的初始化顺序是否正确。以下为正确的顺序:
MX_DMA_Init();
MX_USART1_UART_Init();
6、串口初始化
使用stm32cubeide有一个优势,它会自动帮你把外设用较好的顺序进行了初始化,这样就不会因为时钟初始化顺序问题,引起一些莫名其妙的问题。
但是串口中断及DMA初始化,一般还是需要单独做的。一般是先使用IDLE,后使能DMA,有很多文章提到要先初始化DMA,后初始化IDLE,以防一上电就产生接收,我觉得这个不是问题,只要做好接收数据判断即可。但是先使能DMA会造成串口干扰导致中断频发。
__HAL_UART_ENABLE_IT(&USART, UART_IT_IDLE); //使能IDLE中断
HAL_UARTEx_ReceiveToIdle_DMA(&USART, rx_buf, USART_RX_MAX);
7、高级特性设置(对应图中3)
之前遇到过一个特殊的现象,串口在板子刚上电启动时,可以正常收发,但是到了一定时间后,就会出现无法完整接收数据的现象,反复很多次,最终发现,是高级特性惹的祸。
串口配置中,Advanced features,有两个是默认enable的,一个是overrun,一个是dma or rx error。这两个本意是好的,意在增强串口的健壮性,但是在串口可能有干扰信号的情况下,这两个特性会造成串口异常。省力的方法就是关闭(disable)这两个特性。麻烦的方法,是在串口中断接收程序中,做overrun和rx error判断,清除对应的错误位,或者重写void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)函数,这样才能让串口不会出现接收异常的情况。
经过试验测试,即使关闭了这个高级特性,串口接收错误仍然有可能发生,并导致串口一直带着错误位在跑,子程序使用串口不当时,就会发生硬件错误使主程序挂掉。
转载一篇对overrun说的比较清楚的文章:STM32串口溢出错误Overrun使用不当导致的串口死机_TrueLink的博客-CSDN博客
但,这里要千万注意,errorCallback中需要重启的中断,必须是你使用的中断。比如使用了IDLE和DMA来配置的,那就__HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE)。如果未使用IDLE和DMA,那就按照上述博客中提到的,HAL_UART_Receive_IT(&huart1,buf,1),否则,轻则程序卡顿,重则串口又出现假死状态。
二、串口DMA接收的配置
很多文章和帖子中都做了配置说明,经过长时间的试错,可以确认以下几件事情,避免让大家进坑。
1,DMA是否需要开中断?
答案是,不需要开中断。 DMA传输只要配置到串口接收端口上,在串口数据(一般会使用IDLE空闲中断)来临会自动调用DMA程序,DMA负责把串口上的数据转移到片上RAM中,记得,DMA转移时不需要DMA中断动作的,如果加了DMA中断,轻则程序不丝滑,重则程序卡死而找不到原因。
2,DMA与串口是否有对应关系?
答案是,看具体芯片而定。对于stm32F系列芯片来说,很多都需要做对应关系,但是对于stm32G系列芯片来说,已经完全不需要做对应关系了。这个前期去芯片手册中查找清楚即可。
3,DMA优先级如何设置?
答案是,一般不需要特殊配置,从LOW到HIGH,配置起来问题都不大。重点是,需要调用DMA的外设,尽量避免同时持续不断的进行数据传输,一般来说,串口或者ADC通过DMA转移到片内RAM的时间,是以指令数来计的,对于48M以上的stm32时间一般在us级以下,所以,优先级在使用少量DMA的时候,完全不是问题。只有在DMA使用超过5个以上,且外设数据持续不停、通信速率又非常高的时候,才会发生抢占优先级的问题。
三、程序解析
1、串口中断,为什么把回调函数放在系统函数后面?,2023年5月更新
HAL_UART_IRQHandler(&huart1);
// if(0 != __HAL_UART_GET_FLAG(&USART_BUS, UART_FLAG_IDLE))
// usart_rx_cb(&USART_BUS);
这里之前对函数理解不到位,多做了一次无用功。实际上,串口中断这里是不需要额外增加回调函数的。
在HAL_UART_IRQHhandler函数中,就已经包含了回调函数HAL_UARTEx_RxEventCallback,在IDLE产生中断并进入HAL_UART_IRQHhandler函数后,可以直接调用HAL_UARTEx_RxEventCallback 函数进行数据处理。
这里只需要在回调函数内增加一个接收标志,并重新启动接收中断即可。
2、串口异常的处理
串口异常处理在HAL_UART_IRQHhandler函数也已经包含,不需要额外再做错误处理了。