STM32 RS485串口DMA发送问题记录及调试解决

文章讲述了在STM32F767IGT6芯片上使用RS485和MODBUSRTU协议进行通信时,遇到的数据接收和发送问题及解决方案。接收端利用串口超时中断和DMA循环模式处理数据,发送端通过调整485芯片方向转换时机解决了丢包问题。在DMA发送中,通过分析代码和中断处理,解决了发送两帧才应答一次的异常,最终成功实现了串口DMA的稳定通信。
摘要由CSDN通过智能技术生成

STM32 RS485串口DMA接收及发送,问题记录及调试解决

芯片型号:STM32F767IGT6、SP3485,如图1、图2所示。

image-20230622184446680 图1 主芯片型号
image-20230622185006024 图2 485芯片型号

开发环境:Keil uVision5、STM32CubeMX,如图3、图4所示。

image-20230622184317724 图3 Keil uVision版本信息
image-20230622184536168 图4 STM32CubeMX版本信息

之前与上层设备的通讯协议是基于MODBUS TCP进行地相应开发,但因为STM32F767IG芯片只有一个物理网口,并且其也不得不接入局域网。所以当局域网内有大量数据时可能导致网络阻塞,其与上层设备的通讯会不稳定,从而无法正常工作。于是乎便考虑使用基于MODBUS RTU的协议进行通讯。在开发过程中,碰到了些许问题,花费了不少时间解决,觉得有必要记录总结下。

好了,背景阐述完毕,咱们进入正题。

其实MODBUS协议从网口改为兼容串口通讯,协议部分的处理并不棘手,只要考虑如何兼容网口的MBAP报文头的处理,以及串口的CRC校验;串口通讯主要的难点在于命令帧之间的区分、应答的及时性,同时若使用RS485通讯,需要注意485芯片数据处理方向的转换,以及转换的时机

1.数据接收

如图5所示,在文档Modbus_over_serial_line_V1 2.5.1.1节阐述,在MODBUS RTU模式中,消息帧之间的最小间隔时间是3.5字符的时间(具体时间由串口比特率及单个字符包含的位数决定,如比特率为9600bits/s,单个字符包含1个起始位,8个数据位,0个校验位,1个停止位,则最小间隔时间具体值t3.5=1/9600*(1+8+0+1)*3.5≈3.65ms)。因此数据接收可以使用芯片自带的超时功能实现,如图6所示,除了要使能接收器超时中断外,同时也要使能接收器超时功能,并使能DMA接收的循环模式,即数据接收采用 串口接收器超时中断+DMA接收循环模式 的方式,在串口中断中判断是否是超时中断,若是则获取此次中断录入DMA的新数据长度值,数据长度值的存储采用10级的循环缓冲区实现,并在程序主循环中判断存储数据长度值的缓冲区是否为空,若不为空则根据存储的数据长度值读取DMA接收缓冲区相应的数据量,然后送入MODBUS协议解析函数中进行处理。

image-20230626090402165 图5 MODBUS Message RTU Framing
image-20230622190810356 图6 MODBUS通信

2.数据发送

2.1调用HAL_UART_Transmit()进行发送

计划是准备先通过标准函数HAL_UART_Transmit()调通后,再考虑使用DMA发送。现实却是一直有接收到正确的数据,但就是没有进行正确的应答。查了很长一段时间,甚至在Debug模式下一步步运行,程序都是在按设想地运行,最后弄得都有点怀疑人生了,才猛得意识到是没有正确地转换485芯片的方向。后续是在转换为发送方向前延时一定时间,再调用HAL_UART_Transmit(),然后再延时一定时间后,转换为接收方向,整个链路总算能正常工作了。

2.2调用HAL_UART_Transmit_DMA()进行发送

DMA发送配置的是正常模式(DMA发送循环模式还没来得及研究),开始是直接把HAL_UART_Transmit()替换成HAL_UART_Transmit_DMA(),数据是能够正常发送出去,但总是比应该应答的少一两个字节,然而在Debug模式下打断点,一步步运行又能正常应答,因此猜测是某处的时序不对,可身边没有示波器直接对信号进行监测;便上网搜索相应的解决方法,果然大家也遇到过同样的问题,经过一番查找与阅读,及修改程序再实测(在调用完HAL_UART_Transmit_DMA()后,延时时间加长,再转换为接收方向)。定位到正常运行时丢应答数据的本质原因是485方向转换的时机不对可以在调用完HAL_UART_Transmit_DMA()后,延时时间加长再转换485芯片为接收方向,来实现正常应答。但更稳妥以及更高效地处理,是通过串口发送完成中断来进行485芯片方向的转换。

待以为这样处理完后,就调通串口DMA发送了。没想到又遇到了新的问题:发送两帧才正常应答一次。当然又是一番网上搜索,发现网上大部分遇到的是只会发送一次,解决方法也是有多种,但通过仔细查阅,个人感觉只是打补丁,没有找到真正的原因。后来又是一步步仿真调试,发现是如图7标记所示的条件语句就满足了一次,后续huart->gState的值都是HAL_UART_STATE_BUSY_TX,导致后续调用HAL_UART_Transmit_DMA()实际没有正常执行

image-20230622201523659 图7 函数HAL_UART_Transmit_DMA部分代码

因此可在串口发送完成中断后重新赋值huart->gState = HAL_UART_STATE_READY就可以了。说来也是巧,之所以会遇到这个问题,是因为我刚好在调用串口中断函数HAL_UART_IRQHandler()前进行了__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC)清除串口发送完成中断的操作,不然如图8所示在HAL_UART_IRQHandler()中会调用UART_EndTransmit_IT(),如图9所示也会执行huart->gState = HAL_UART_STATE_READY操作,如此就不需要再手动赋值huart->gState = HAL_UART_STATE_READY。

image-20230622234515900 图8 函数HAL_UART_IRQHandler部分代码
image-20230622202142086 图9 函数UART_EndTransmit_IT部分代码

程序按照以上方法修改后,串口DMA发送也能正常应答了。但是我实际遇到的现象是发送两帧才正常应答一次,为了不给后续的开发埋下“炸弹”,便决定得找到真正的原因。又是通过一系列的断点,总算定位到问题了,如图10所示,是因为DMA产生了DMA_IT_FE中断,从而导致HAL_UART_Transmit_DMA()会执行如图11的语句。

image-20230622234748794 图10 函数HAL_DMA_IRQHandler部分代码
image-20230622203356432 图11 函数HAL_UART_Transmit_DMA部分代码
所以就如同huart->gState一直在进行自恢复,导致一直能每发送两帧,正常应答一次。好的,问题找到了,但解决方法就困惑了,因为我根本没有使能DMA FIFO模式,为什么会出现FIFO错误了,而且还是DMA发送导致的。这次是网上搜索了几番,花费了很多时间,很久也没有搜索到想要的解惑说明。经过一段漫长的修改关键词再搜索,总算是在以下链接处找到了问题的本质,如图12所示。

while-sending-data-on-to-usart1-i-always-get-feif-dma-errors-why

image-20230626084531685 图12 STMicroelectronics Community Solution

原来是我提前调用LL_USART_EnableDMAReq_TX(),使能了串口DMA的发送器导致的,如图13所示,技术文档AN4031 4.3节也有详细的说明。

image-20230622204239552 图13 Software sequence

按照以上要求进行修改后,程序终于能够按照开发的要求正常运行了。至此,MODBUS协议从网口到兼容串口通讯正式开发完成了。
经过此次开发经历,给我最大的感受就是 搜商 也是一件强有力的“利刃”。

  • 13
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一种高性能的32位微控制器,拥有丰富的外设资源和DMA技术,适合于各种应用场合。RS485是一种串行通信协议,可以用于远距离传输数据。在STM32上使用RS485协议进行通信,通常需要使用软件实现收发。而利用DMA技术可以提高数据传输效率,减轻CPU负担。 STM32提供了丰富的串口外设资源,包括USART、UART、SPI等。其中,USART支持RS485模式,可以方便地实现RS485通信,而UART则需要通过软件实现。在软件实现RS485收发时,通常采用半双工方式,即同一时刻只能进行发送或接收。为了实现快速、可靠的数据传输,可以使用DMA技术。 DMA(Direct Memory Access)直接存储器访问技术是一种硬件加速技术,可以使外设在不经过CPU的干预下将数据直接传输到内存中,或者将内存中的数据直接传输到外设中。在RS485通信中,可以使用DMA技术来实现数据缓存、数据收发等功能,从而提高数据传输效率和CPU利用率。 使用STM32进行RS485 DMA软件收发,通常需要完成以下步骤: 1.配置串口外设资源,包括USART或UART的初始化,波特率、数据位、停止位、校验位等设置。 2.配置DMA外设资源,包括DMA通道、数据传输方向、数据长度、传输模式(单次传输或循环传输)、中断控制器等设置。 3.实现DMA传输完成中断处理函数,以便在数据传输完成后对数据进行处理或者进行下一步操作。 4.在应用程序中实现接收数据发送数据的功能,包括数据的缓存、传输、校验和处理等。 5.使用半双工模式进行数据收发,以避免数据冲突和数据丢失等问题。 以上就是STM32 RS485 DMA软件收发的基本实现方法。使用DMA技术可以有效地提高数据传输效率,减轻CPU负担,避免数据丢失和冲突等问题,适用于对数据传输效率有较高要求的应用场合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值