赵艳明 曾培峰
文章编号: 2095-2163(2018)03-0050-05中图分类号: 文献标志码: A
摘要: 关键词: (School of Computer Science and Technology, Donghua University, Shanghai 201620, China)
Abstract: Based on STM32 chip, a Modbus-RS485 communication method is proposed. The method uses the object-oriented technology to manage Modbus calls and serial ports is universal, which can be easily portable to other systems with the same characteristics. After that, the paper elaborates using a state machine to manage Modbus communication combined with logs, so that the communication network has self-diagnosis and adaptive function. In combination with the characteristics of STM32 chip and Modbus protocol, a DMA receiving data method is proposed which is independent of the Modbus command length. This method can be used as a reference scheme to solve the low communication efficiency and slow data transmission of Modbus-RS485.
Key words:
引言
Modbus-RS485是一種在工业现场广泛使用的布网方法\[1\]。RS485总线上最多支持255个从站,然而在实际应用场景中从站的数量远远大于这个数量。可采用扩展Modbus地址域,或者数据域中添入物理地址来增加从站的数量\[2\]。
常见的Modbus通信方法有2种。一种是主站根据要请求的数据信息,封装相应的请求帧,并生成对应的定时器,通信程序通过定时器来执行Modbus通话\[3\]。这种方法存在明显的缺点,如果定时器时间设置过短容易发生数据碰撞,过长则导致通信效率低。另外一种是主站一次只允许发起一个通话,当从站回复数据或者从站在规定时间内不回复数据,主站才可以发起下一次通话\[4\],这种方法和第一种没有本质上的区别。
STM32芯片常见的数据接收方法有串口中断\[5\]和DMA中断 \[6-7\]。使用串口中断接收数据存在2个缺点。其一是接收效率太低,其二是通信双方要设置既定的结束符。使用DMA中断能够提高数据接收效率,前提条件是接收的长度固定。然而挂载在同一根RS485总线上的从站回复的数据帧长度是不固定的,此时无法使用DMA接收中断。
本文采用面向对象的思想将Modbus通话封装成任务,任务中增加了通话对应的端口地址,使用这种方式使得主站可以访问从站的数量超过255个。将实现数据收发的串口封装成端口,一个端口对应一个对象,任务对象和端口对象通过消息建立联系。通信程序使用状态机管理Modbus通信,并将通信过程中的异常状况以日志的形式记录下来。通过对日志提取分析当前网络状态,动态调整任务调度。此外,本文基于STM32芯片,未配置DMA接收中断实现一种DMA接收长度不固定的Modbus数据帧的方法。
1系统介绍
使用STM32芯片的串口实现RS485通信需结合RS485收发器。系统结构如图1所示,图中每个I/O端口连接一条RS485总线。
在系统中,主站和从站的串口设置,包括:波特率、停止位、奇偶校验等重要匹配。RS485总线上的Modbus协议使用半双工主从方式通信,主站发起Modbus通话向从站发出请求,从站则处于接收状态,一直等待主站发送的数据。
2系统中的对象
在RS485总线下主站通过Modbus请求数据帧访问从站,通信程序中如果直接为每个功能设计写定一段请求帧,虽然程序看起来比较直观,但是这种方式丧失了通用性,且可维护性差。在系统移植时,需要变更程序内请求帧。因此要将运算时的变量和实际操作变量的函数分离。在系统中的Modbus通话是有限的,可将通话封装成任务。将通信系统中的所有任务的描述,以XML配置文件的方式得到保存。XML文件可用一个专门的上位机生成,当需要增加、删除、修改功能时,就可通过上位机来重新生成配置文件,并将配置文件导入到单片机中,单片机通过文件解析程序构建任务对象。任务对象定义可见表1。
属性名称描述属性名称描述Index任务的编号cmd数组,存放请求数据帧PortIndex任务对应的端口号Cmdlen数据帧长度DeviceAddr设备地址rcvCmd指针,指向接收数组Function功能码RcvLen接收数据长度RegAddr寄存器地址TErrorCount超时错误计数Interval任务的扫描周期SErrorCount发送错误计数Timeout任务超时时间RErrorCount接收错误计数Timer任务的执行时间Traffic任务执行次数计数status枚举,任务状态Enable任务使能开关同理,将每个物理串口映射到唯一的逻辑端口,每个端口都是一个对象。端口对象的定义则可见表2。
任务的调度借助一个循环链表,任务可以加入到链表的前提是任务使能开关被打开。系统经过初始化后,开始依次执行链表中的任务。周期性的任务常存在链表中;非周期任务,则在每次任务轮询前插入到链表,或者从链表中删除。当串口连接了RS485总线,并且总线上挂载了从站时,将打开串口对应的逻辑端口的使能开关,从而激活端口对象。
属性名称描述属性名称描述TaskAddr指针,指向任务Sendlen发送长度status枚举,端口状态RcvBuf数组,接收数据Timer端口工作时间Rcvlen接收长度Timeout端口超时时间Enable端口使能开关SendBuf指针,指向发送数组通信网络需具备自诊断与自适应的功能,实现这一功能借助于系统日志,通过日志及时发现并处理通信异常状况。通信双方的串口设置不匹配或者从站出现异常的现象是:从站对应任务的Traffic统计中,RErrorCount数值偏高,而与该任务使用同一端口的其它任务RErrorCount数值正常。此时可以减少对该任务的轮询。当物理链路发生损坏时,会导致端口发送出错误的请求命令,可设计硬件回送功能再次检查发送的数据。链路损坏的现象是:当前任务和该任务使用同一个端口的其它任务的Traffic统计中SErrorCount都偏高,此时停止执行该端口对应下的所有任务。
3DMA数据收发
如果单纯使用串口接收中断的方式实现数据接收,频繁地触发串口接收中断将会严重影响CPU的执行效率。STM32芯片提供了将串口复用成DMA的功能。DMA发送数据时,发送数据的长度是已知的,可配置DMA发送完成中断来告知数据发送完成。但是在接收数据时,数据的长度是未知的。因此使用DMA接收数据需要解决2个问题,分别是:
(1)如何判断有数据接收。
(2)如何从接收的数据中解析出一条完整的指令。
首先定义4个标志位,也就是:curPos、lastPos、startPos、 prev。定义一个和时间相关的变量LastRcvTime。其中,curPos指向当前接收数据位置;lastPos指向上次数据接收的位置;startPos指向开始接收数据的位置;prev指向数据接收完毕的位置;LastRcvTime记录最近一次接收数据的时间。数据接收状态如图2所示。图2数据接收状态
Fig. 2Data receiving state
将DMA配置成循环模式,并设置队列长度RXLen。DMA中的CNDTR寄存器会记录数据的接收位置(curPos)。使用STM32芯片提供的SysTick_Handler滴答计时器定时查看curPos是否发生变化,结合Modbus协议特点,如果3.5个字节(记为frameTime)内curPos一直不发生变化,认为DMA接收数据完毕。具体流程如下。
(1)当准备接收数据前、系统新近开始接收数据或者上一次命令接收结束后。置4个标志位为同一位置(RXLen-CNDTR)。
(2)通过SysTick_Handler函数定时查看curPos是否发生改变。如果发生改变,则将LastRcvTime赋值为当前系统时间,并且将lastPos赋值为curPos。
(3)当curPos数值和lastPos数值相等时,可认为当前数据可能接收完毕。
(4)frameTime内curPos不发生改变,即系统当前时间systemTime和LastRcvTime的差值超过frameTime,认为当前数据接收完毕,将prev赋值为curPos。此时认为startPos和prev之间接收为一条待验证的Modbus命令。
4状态机模型建立
4.1端口状态机
在端口对象定义中,status是一个枚举类型,包含了端口7个状态,端口的状态转换如图3所示。
图3中,CRC(Sending(msg))表示对回送的数据做CRC校验,检验结果正确时值为0,否则值不为0。任务与端口的关系是多对一的,并且主站可以访问同一从站的不同寄存器。因此Prase(Receiving(msg))不仅对接收的数据启用CRC校验,也会对设备地址和功能码进行检查。端口状态机的处理流程描述如下。
(1)当端口对象处于Port_Idle狀态,并且端口的TaskAddr指针指向为空时,此时端口可被占用。
(2)当端口对象被占用之后,端口对象将指针变量TaskAddr指向当前任务。通过端口,该指针可获得任务对象存储发送的命令的数组的地址和命令的长度。端口开始发送数据,跳转到Port_Sending状态。
(3)对回送数据进行CRC校验。如果检验失败,跳转到Port_SendError状态;如果校验正确,跳转到Port_Wait状态。
(4)如果没有数据接收,则继续等待,并开始计时,等待超时则跳转到Port_Timeout状态。如果发现有数据接收,跳转到Port_ Receiving状态。
(5)判断当前数据是否接收完毕。数据接收完毕后对数据进行解析。解析包括CRC校验、设备地址的检查。如果解析失败,则跳转到Port_ReceiveError;如果解析成功,则将数据的接收地址和接收的长度通过任务指针告知给任务对象。
(6)当端口处于Port_SendError、Port_Timeout、Port_ReceiveError异常状态时,会将状态反馈给任务。
4.2任务状态机
任务对象定义中,status是一个枚举类型,任务包含了10个状态,任务状态转换如图4所示。
设计任务状态机时有一个前提条件:正确的命令可能会收到正确的回复数据,不正确的命令一定收不到正确的回复数据。因此在任务状态机中增加Task_Retry状态,尽可能保证发出的请求命令是正确的。图4中,rescheduleTime表示任务再次发起的时间,该值为任务开始进入Task_Retry的系统时间systemTime加扫描周期Interval。任务状态机的处理流程可分述如下。
(1)开始任务对象处于Task_Idle状态。当有任务执行时,跳转到Task_Check状态,检查要绑定的是否被占用。
(2)如果端口被占用,放弃当前任务,跳转到Task_Idle状态。如果没有被占用,则跳转到Task_Bind状态。
(3)任务会把命令数组的地址和长度告知给端口,跳转到Task_Schedule状态。
(4)当任务对象接收到端口Port_Timeout、Port_ReceiveError异常状况反馈时,相应跳转到Task_Timeout状态、Task_ReceiveError状态,同时TErrorCount、SErrorCount的数值加1,将异常状况保存到日志当中,并跳转到Task_Unbind状态。
(5)任务得到接收的地址和长度,跳转到Task_Receive状态。将得到的数据返回给Modbus主站后,跳转到Task_Unbind状态。
(6)当任务接收到端口Port_SendError反馈,首先跳转到Task_SendError状态,RErrorCount数值加1,把异常状况保存到日志当中。然后判断任务的执行时间是否已经结束。如果已经结束,跳转到Task_Unbind状态。如果还未结束,则跳转到Task_Retry状态。
(7)任务处于Task_Retry状态,需要等到扫描周期才能再次发起任务。在扫描周期内,任务一直处于该状态。一旦等到扫描周期,则跳转到Task_Schedule状态。
(8)任务处于Task_Unbind状态时,无论任务执行成功,或是失败,都会解除对端口的占用。
5结束语
实现面向对象方法既可避免重复性代码的编写,又容易维护。作为一种思想,这是不受编程语言限制的,本文利用面向对象和状态机设计研发的RS485-Modbus通信方法具备通用性。同时,将通信异常状况以日志的形式记录下来,通信网络借助日志使得自身具备自诊断性和自适应性。文中提出的DMA接收数据方法亦可使用于同特点的其它系统中。
参考文献
[1] 颜河恒, 王晓华, 佟为明. Modbus关键技术分析及节点开发\[J\]. 自动化技术与应用, 2006, 25(5):49-51.
[2] 周海洋. 大规模控制节点群管理系統设计\[D\]. 天津:天津大学, 2015.
[3] 李年锁, 颜罡, 郭彦每. 基于Modbus协议的RS485总线通信在内电混合工程车中的设计及实现\[J\]. 电力机车与城轨车辆, 2017,40(3):44-47.
[4] 陈科, 蒋军. 基于STM32的MODBUS协议的实现与应用\[J\]. 视听, 2013(4):9-10.
[5] 张永伟, 康兴无. 基于STM32和Modbus的串口服务器系统\[J\]. 电子设计工程, 2017, 25(16):108-111,116.
[6] 魏琳, 田波. 基于STM32F4系列的串口DMA数据处理传输研究\[J\]. 自动化应用, 2016(8):92-93.
[7] 孙景龙, 王业成, 陈锐. STM32F4xx利用DMA实现异步多串口高速通信设计\[J\]. 黑龙江科技信息, 2013(27):36.