趙艷明 曾培峰
文章編號(hào): 2095-2163(2018)03-0050-05中圖分類(lèi)號(hào): 文獻(xiàn)標(biāo)志碼: A
摘要: 關(guān)鍵詞: (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是一種在工業(yè)現(xiàn)場(chǎng)廣泛使用的布網(wǎng)方法\[1\]。RS485總線(xiàn)上最多支持255個(gè)從站,然而在實(shí)際應(yīng)用場(chǎng)景中從站的數(shù)量遠(yuǎn)遠(yuǎn)大于這個(gè)數(shù)量??刹捎脭U(kuò)展Modbus地址域,或者數(shù)據(jù)域中添入物理地址來(lái)增加從站的數(shù)量\[2\]。
常見(jiàn)的Modbus通信方法有2種。一種是主站根據(jù)要請(qǐng)求的數(shù)據(jù)信息,封裝相應(yīng)的請(qǐng)求幀,并生成對(duì)應(yīng)的定時(shí)器,通信程序通過(guò)定時(shí)器來(lái)執(zhí)行Modbus通話(huà)\[3\]。這種方法存在明顯的缺點(diǎn),如果定時(shí)器時(shí)間設(shè)置過(guò)短容易發(fā)生數(shù)據(jù)碰撞,過(guò)長(zhǎng)則導(dǎo)致通信效率低。另外一種是主站一次只允許發(fā)起一個(gè)通話(huà),當(dāng)從站回復(fù)數(shù)據(jù)或者從站在規(guī)定時(shí)間內(nèi)不回復(fù)數(shù)據(jù),主站才可以發(fā)起下一次通話(huà)\[4\],這種方法和第一種沒(méi)有本質(zhì)上的區(qū)別。
STM32芯片常見(jiàn)的數(shù)據(jù)接收方法有串口中斷\[5\]和DMA中斷 \[6-7\]。使用串口中斷接收數(shù)據(jù)存在2個(gè)缺點(diǎn)。其一是接收效率太低,其二是通信雙方要設(shè)置既定的結(jié)束符。使用DMA中斷能夠提高數(shù)據(jù)接收效率,前提條件是接收的長(zhǎng)度固定。然而掛載在同一根RS485總線(xiàn)上的從站回復(fù)的數(shù)據(jù)幀長(zhǎng)度是不固定的,此時(shí)無(wú)法使用DMA接收中斷。
本文采用面向?qū)ο蟮乃枷雽odbus通話(huà)封裝成任務(wù),任務(wù)中增加了通話(huà)對(duì)應(yīng)的端口地址,使用這種方式使得主站可以訪(fǎng)問(wèn)從站的數(shù)量超過(guò)255個(gè)。將實(shí)現(xiàn)數(shù)據(jù)收發(fā)的串口封裝成端口,一個(gè)端口對(duì)應(yīng)一個(gè)對(duì)象,任務(wù)對(duì)象和端口對(duì)象通過(guò)消息建立聯(lián)系。通信程序使用狀態(tài)機(jī)管理Modbus通信,并將通信過(guò)程中的異常狀況以日志的形式記錄下來(lái)。通過(guò)對(duì)日志提取分析當(dāng)前網(wǎng)絡(luò)狀態(tài),動(dòng)態(tài)調(diào)整任務(wù)調(diào)度。此外,本文基于STM32芯片,未配置DMA接收中斷實(shí)現(xiàn)一種DMA接收長(zhǎng)度不固定的Modbus數(shù)據(jù)幀的方法。
1系統(tǒng)介紹
使用STM32芯片的串口實(shí)現(xiàn)RS485通信需結(jié)合RS485收發(fā)器。系統(tǒng)結(jié)構(gòu)如圖1所示,圖中每個(gè)I/O端口連接一條RS485總線(xiàn)。
在系統(tǒng)中,主站和從站的串口設(shè)置,包括:波特率、停止位、奇偶校驗(yàn)等重要匹配。RS485總線(xiàn)上的Modbus協(xié)議使用半雙工主從方式通信,主站發(fā)起Modbus通話(huà)向從站發(fā)出請(qǐng)求,從站則處于接收狀態(tài),一直等待主站發(fā)送的數(shù)據(jù)。
2系統(tǒng)中的對(duì)象
在RS485總線(xiàn)下主站通過(guò)Modbus請(qǐng)求數(shù)據(jù)幀訪(fǎng)問(wèn)從站,通信程序中如果直接為每個(gè)功能設(shè)計(jì)寫(xiě)定一段請(qǐng)求幀,雖然程序看起來(lái)比較直觀(guān),但是這種方式喪失了通用性,且可維護(hù)性差。在系統(tǒng)移植時(shí),需要變更程序內(nèi)請(qǐng)求幀。因此要將運(yùn)算時(shí)的變量和實(shí)際操作變量的函數(shù)分離。在系統(tǒng)中的Modbus通話(huà)是有限的,可將通話(huà)封裝成任務(wù)。將通信系統(tǒng)中的所有任務(wù)的描述,以XML配置文件的方式得到保存。XML文件可用一個(gè)專(zhuān)門(mén)的上位機(jī)生成,當(dāng)需要增加、刪除、修改功能時(shí),就可通過(guò)上位機(jī)來(lái)重新生成配置文件,并將配置文件導(dǎo)入到單片機(jī)中,單片機(jī)通過(guò)文件解析程序構(gòu)建任務(wù)對(duì)象。任務(wù)對(duì)象定義可見(jiàn)表1。
屬性名稱(chēng)描述屬性名稱(chēng)描述Index任務(wù)的編號(hào)cmd數(shù)組,存放請(qǐng)求數(shù)據(jù)幀PortIndex任務(wù)對(duì)應(yīng)的端口號(hào)Cmdlen數(shù)據(jù)幀長(zhǎng)度DeviceAddr設(shè)備地址rcvCmd指針,指向接收數(shù)組Function功能碼RcvLen接收數(shù)據(jù)長(zhǎng)度RegAddr寄存器地址TErrorCount超時(shí)錯(cuò)誤計(jì)數(shù)Interval任務(wù)的掃描周期SErrorCount發(fā)送錯(cuò)誤計(jì)數(shù)Timeout任務(wù)超時(shí)時(shí)間RErrorCount接收錯(cuò)誤計(jì)數(shù)Timer任務(wù)的執(zhí)行時(shí)間Traffic任務(wù)執(zhí)行次數(shù)計(jì)數(shù)status枚舉,任務(wù)狀態(tài)Enable任務(wù)使能開(kāi)關(guān)同理,將每個(gè)物理串口映射到唯一的邏輯端口,每個(gè)端口都是一個(gè)對(duì)象。端口對(duì)象的定義則可見(jiàn)表2。
任務(wù)的調(diào)度借助一個(gè)循環(huán)鏈表,任務(wù)可以加入到鏈表的前提是任務(wù)使能開(kāi)關(guān)被打開(kāi)。系統(tǒng)經(jīng)過(guò)初始化后,開(kāi)始依次執(zhí)行鏈表中的任務(wù)。周期性的任務(wù)常存在鏈表中;非周期任務(wù),則在每次任務(wù)輪詢(xún)前插入到鏈表,或者從鏈表中刪除。當(dāng)串口連接了RS485總線(xiàn),并且總線(xiàn)上掛載了從站時(shí),將打開(kāi)串口對(duì)應(yīng)的邏輯端口的使能開(kāi)關(guān),從而激活端口對(duì)象。
屬性名稱(chēng)描述屬性名稱(chēng)描述TaskAddr指針,指向任務(wù)Sendlen發(fā)送長(zhǎng)度status枚舉,端口狀態(tài)RcvBuf數(shù)組,接收數(shù)據(jù)Timer端口工作時(shí)間Rcvlen接收長(zhǎng)度Timeout端口超時(shí)時(shí)間Enable端口使能開(kāi)關(guān)SendBuf指針,指向發(fā)送數(shù)組通信網(wǎng)絡(luò)需具備自診斷與自適應(yīng)的功能,實(shí)現(xiàn)這一功能借助于系統(tǒng)日志,通過(guò)日志及時(shí)發(fā)現(xiàn)并處理通信異常狀況。通信雙方的串口設(shè)置不匹配或者從站出現(xiàn)異常的現(xiàn)象是:從站對(duì)應(yīng)任務(wù)的Traffic統(tǒng)計(jì)中,RErrorCount數(shù)值偏高,而與該任務(wù)使用同一端口的其它任務(wù)RErrorCount數(shù)值正常。此時(shí)可以減少對(duì)該任務(wù)的輪詢(xún)。當(dāng)物理鏈路發(fā)生損壞時(shí),會(huì)導(dǎo)致端口發(fā)送出錯(cuò)誤的請(qǐng)求命令,可設(shè)計(jì)硬件回送功能再次檢查發(fā)送的數(shù)據(jù)。鏈路損壞的現(xiàn)象是:當(dāng)前任務(wù)和該任務(wù)使用同一個(gè)端口的其它任務(wù)的Traffic統(tǒng)計(jì)中SErrorCount都偏高,此時(shí)停止執(zhí)行該端口對(duì)應(yīng)下的所有任務(wù)。
3DMA數(shù)據(jù)收發(fā)
如果單純使用串口接收中斷的方式實(shí)現(xiàn)數(shù)據(jù)接收,頻繁地觸發(fā)串口接收中斷將會(huì)嚴(yán)重影響CPU的執(zhí)行效率。STM32芯片提供了將串口復(fù)用成DMA的功能。DMA發(fā)送數(shù)據(jù)時(shí),發(fā)送數(shù)據(jù)的長(zhǎng)度是已知的,可配置DMA發(fā)送完成中斷來(lái)告知數(shù)據(jù)發(fā)送完成。但是在接收數(shù)據(jù)時(shí),數(shù)據(jù)的長(zhǎng)度是未知的。因此使用DMA接收數(shù)據(jù)需要解決2個(gè)問(wèn)題,分別是:
(1)如何判斷有數(shù)據(jù)接收。
(2)如何從接收的數(shù)據(jù)中解析出一條完整的指令。
首先定義4個(gè)標(biāo)志位,也就是:curPos、lastPos、startPos、 prev。定義一個(gè)和時(shí)間相關(guān)的變量LastRcvTime。其中,curPos指向當(dāng)前接收數(shù)據(jù)位置;lastPos指向上次數(shù)據(jù)接收的位置;startPos指向開(kāi)始接收數(shù)據(jù)的位置;prev指向數(shù)據(jù)接收完畢的位置;LastRcvTime記錄最近一次接收數(shù)據(jù)的時(shí)間。數(shù)據(jù)接收狀態(tài)如圖2所示。圖2數(shù)據(jù)接收狀態(tài)
Fig. 2Data receiving state
將DMA配置成循環(huán)模式,并設(shè)置隊(duì)列長(zhǎng)度RXLen。DMA中的CNDTR寄存器會(huì)記錄數(shù)據(jù)的接收位置(curPos)。使用STM32芯片提供的SysTick_Handler滴答計(jì)時(shí)器定時(shí)查看curPos是否發(fā)生變化,結(jié)合Modbus協(xié)議特點(diǎn),如果3.5個(gè)字節(jié)(記為frameTime)內(nèi)curPos一直不發(fā)生變化,認(rèn)為DMA接收數(shù)據(jù)完畢。具體流程如下。
(1)當(dāng)準(zhǔn)備接收數(shù)據(jù)前、系統(tǒng)新近開(kāi)始接收數(shù)據(jù)或者上一次命令接收結(jié)束后。置4個(gè)標(biāo)志位為同一位置(RXLen-CNDTR)。
(2)通過(guò)SysTick_Handler函數(shù)定時(shí)查看curPos是否發(fā)生改變。如果發(fā)生改變,則將LastRcvTime賦值為當(dāng)前系統(tǒng)時(shí)間,并且將lastPos賦值為curPos。
(3)當(dāng)curPos數(shù)值和lastPos數(shù)值相等時(shí),可認(rèn)為當(dāng)前數(shù)據(jù)可能接收完畢。
(4)frameTime內(nèi)curPos不發(fā)生改變,即系統(tǒng)當(dāng)前時(shí)間systemTime和LastRcvTime的差值超過(guò)frameTime,認(rèn)為當(dāng)前數(shù)據(jù)接收完畢,將prev賦值為curPos。此時(shí)認(rèn)為startPos和prev之間接收為一條待驗(yàn)證的Modbus命令。
4狀態(tài)機(jī)模型建立
4.1端口狀態(tài)機(jī)
在端口對(duì)象定義中,status是一個(gè)枚舉類(lèi)型,包含了端口7個(gè)狀態(tài),端口的狀態(tài)轉(zhuǎn)換如圖3所示。
圖3中,CRC(Sending(msg))表示對(duì)回送的數(shù)據(jù)做CRC校驗(yàn),檢驗(yàn)結(jié)果正確時(shí)值為0,否則值不為0。任務(wù)與端口的關(guān)系是多對(duì)一的,并且主站可以訪(fǎng)問(wèn)同一從站的不同寄存器。因此Prase(Receiving(msg))不僅對(duì)接收的數(shù)據(jù)啟用CRC校驗(yàn),也會(huì)對(duì)設(shè)備地址和功能碼進(jìn)行檢查。端口狀態(tài)機(jī)的處理流程描述如下。
(1)當(dāng)端口對(duì)象處于Port_Idle狀態(tài),并且端口的TaskAddr指針指向?yàn)榭諘r(shí),此時(shí)端口可被占用。
(2)當(dāng)端口對(duì)象被占用之后,端口對(duì)象將指針變量TaskAddr指向當(dāng)前任務(wù)。通過(guò)端口,該指針可獲得任務(wù)對(duì)象存儲(chǔ)發(fā)送的命令的數(shù)組的地址和命令的長(zhǎng)度。端口開(kāi)始發(fā)送數(shù)據(jù),跳轉(zhuǎn)到Port_Sending狀態(tài)。
(3)對(duì)回送數(shù)據(jù)進(jìn)行CRC校驗(yàn)。如果檢驗(yàn)失敗,跳轉(zhuǎn)到Port_SendError狀態(tài);如果校驗(yàn)正確,跳轉(zhuǎn)到Port_Wait狀態(tài)。
(4)如果沒(méi)有數(shù)據(jù)接收,則繼續(xù)等待,并開(kāi)始計(jì)時(shí),等待超時(shí)則跳轉(zhuǎn)到Port_Timeout狀態(tài)。如果發(fā)現(xiàn)有數(shù)據(jù)接收,跳轉(zhuǎn)到Port_ Receiving狀態(tài)。
(5)判斷當(dāng)前數(shù)據(jù)是否接收完畢。數(shù)據(jù)接收完畢后對(duì)數(shù)據(jù)進(jìn)行解析。解析包括CRC校驗(yàn)、設(shè)備地址的檢查。如果解析失敗,則跳轉(zhuǎn)到Port_ReceiveError;如果解析成功,則將數(shù)據(jù)的接收地址和接收的長(zhǎng)度通過(guò)任務(wù)指針告知給任務(wù)對(duì)象。
(6)當(dāng)端口處于Port_SendError、Port_Timeout、Port_ReceiveError異常狀態(tài)時(shí),會(huì)將狀態(tài)反饋給任務(wù)。
4.2任務(wù)狀態(tài)機(jī)
任務(wù)對(duì)象定義中,status是一個(gè)枚舉類(lèi)型,任務(wù)包含了10個(gè)狀態(tài),任務(wù)狀態(tài)轉(zhuǎn)換如圖4所示。
設(shè)計(jì)任務(wù)狀態(tài)機(jī)時(shí)有一個(gè)前提條件:正確的命令可能會(huì)收到正確的回復(fù)數(shù)據(jù),不正確的命令一定收不到正確的回復(fù)數(shù)據(jù)。因此在任務(wù)狀態(tài)機(jī)中增加Task_Retry狀態(tài),盡可能保證發(fā)出的請(qǐng)求命令是正確的。圖4中,rescheduleTime表示任務(wù)再次發(fā)起的時(shí)間,該值為任務(wù)開(kāi)始進(jìn)入Task_Retry的系統(tǒng)時(shí)間systemTime加掃描周期Interval。任務(wù)狀態(tài)機(jī)的處理流程可分述如下。
(1)開(kāi)始任務(wù)對(duì)象處于Task_Idle狀態(tài)。當(dāng)有任務(wù)執(zhí)行時(shí),跳轉(zhuǎn)到Task_Check狀態(tài),檢查要綁定的是否被占用。
(2)如果端口被占用,放棄當(dāng)前任務(wù),跳轉(zhuǎn)到Task_Idle狀態(tài)。如果沒(méi)有被占用,則跳轉(zhuǎn)到Task_Bind狀態(tài)。
(3)任務(wù)會(huì)把命令數(shù)組的地址和長(zhǎng)度告知給端口,跳轉(zhuǎn)到Task_Schedule狀態(tài)。
(4)當(dāng)任務(wù)對(duì)象接收到端口Port_Timeout、Port_ReceiveError異常狀況反饋時(shí),相應(yīng)跳轉(zhuǎn)到Task_Timeout狀態(tài)、Task_ReceiveError狀態(tài),同時(shí)TErrorCount、SErrorCount的數(shù)值加1,將異常狀況保存到日志當(dāng)中,并跳轉(zhuǎn)到Task_Unbind狀態(tài)。
(5)任務(wù)得到接收的地址和長(zhǎng)度,跳轉(zhuǎn)到Task_Receive狀態(tài)。將得到的數(shù)據(jù)返回給Modbus主站后,跳轉(zhuǎn)到Task_Unbind狀態(tài)。
(6)當(dāng)任務(wù)接收到端口Port_SendError反饋,首先跳轉(zhuǎn)到Task_SendError狀態(tài),RErrorCount數(shù)值加1,把異常狀況保存到日志當(dāng)中。然后判斷任務(wù)的執(zhí)行時(shí)間是否已經(jīng)結(jié)束。如果已經(jīng)結(jié)束,跳轉(zhuǎn)到Task_Unbind狀態(tài)。如果還未結(jié)束,則跳轉(zhuǎn)到Task_Retry狀態(tài)。
(7)任務(wù)處于Task_Retry狀態(tài),需要等到掃描周期才能再次發(fā)起任務(wù)。在掃描周期內(nèi),任務(wù)一直處于該狀態(tài)。一旦等到掃描周期,則跳轉(zhuǎn)到Task_Schedule狀態(tài)。
(8)任務(wù)處于Task_Unbind狀態(tài)時(shí),無(wú)論任務(wù)執(zhí)行成功,或是失敗,都會(huì)解除對(duì)端口的占用。
5結(jié)束語(yǔ)
實(shí)現(xiàn)面向?qū)ο蠓椒瓤杀苊庵貜?fù)性代碼的編寫(xiě),又容易維護(hù)。作為一種思想,這是不受編程語(yǔ)言限制的,本文利用面向?qū)ο蠛蜖顟B(tài)機(jī)設(shè)計(jì)研發(fā)的RS485-Modbus通信方法具備通用性。同時(shí),將通信異常狀況以日志的形式記錄下來(lái),通信網(wǎng)絡(luò)借助日志使得自身具備自診斷性和自適應(yīng)性。文中提出的DMA接收數(shù)據(jù)方法亦可使用于同特點(diǎn)的其它系統(tǒng)中。
參考文獻(xiàn)
[1] 顏河恒, 王曉華, 佟為明. Modbus關(guān)鍵技術(shù)分析及節(jié)點(diǎn)開(kāi)發(fā)\[J\]. 自動(dòng)化技術(shù)與應(yīng)用, 2006, 25(5):49-51.
[2] 周海洋. 大規(guī)??刂乒?jié)點(diǎn)群管理系統(tǒng)設(shè)計(jì)\[D\]. 天津:天津大學(xué), 2015.
[3] 李年鎖, 顏罡, 郭彥每. 基于Modbus協(xié)議的RS485總線(xiàn)通信在內(nèi)電混合工程車(chē)中的設(shè)計(jì)及實(shí)現(xiàn)\[J\]. 電力機(jī)車(chē)與城軌車(chē)輛, 2017,40(3):44-47.
[4] 陳科, 蔣軍. 基于STM32的MODBUS協(xié)議的實(shí)現(xiàn)與應(yīng)用\[J\]. 視聽(tīng), 2013(4):9-10.
[5] 張永偉, 康興無(wú). 基于STM32和Modbus的串口服務(wù)器系統(tǒng)\[J\]. 電子設(shè)計(jì)工程, 2017, 25(16):108-111,116.
[6] 魏琳, 田波. 基于STM32F4系列的串口DMA數(shù)據(jù)處理傳輸研究\[J\]. 自動(dòng)化應(yīng)用, 2016(8):92-93.
[7] 孫景龍, 王業(yè)成, 陳銳. STM32F4xx利用DMA實(shí)現(xiàn)異步多串口高速通信設(shè)計(jì)\[J\]. 黑龍江科技信息, 2013(27):36.