FreeModbus应用系列之一
FreeModbus應(yīng)用總結(jié)系列之一
- FreeModbus簡介
- FreeModbus的獲取
- 硬件需求
- 移植
-
- 1. 物理層接口文件的修改
-
- 1.1portserial.c中函數(shù)的修改
- porttimer.c中函數(shù)的修改
- 2. 應(yīng)用層回調(diào)函數(shù)的修改
- 3. 應(yīng)用層初始化及協(xié)議訪問
- 初始化及運(yùn)行
- FreeModbus啟動(dòng)流程分析
- MODBUS主機(jī)協(xié)議移植
FreeModbus簡介
FreeMODBUS是一個(gè)奧地利人寫的Modbus協(xié)議。它是一個(gè)針對(duì)嵌入式應(yīng)用的一個(gè)免費(fèi)(自由)的通用MODBUS協(xié)議的移植。Modbus是一個(gè)工業(yè)制造環(huán)境中應(yīng)用的一個(gè)通用協(xié)議。Modbus通信協(xié)議棧包括兩層:Modbus應(yīng)用層協(xié)議,該層定義了數(shù)據(jù)模式和功能;另外一層是網(wǎng)絡(luò)層。
FreeModbus提供了RTU/ASCII 傳輸模式及TCP協(xié)議支持。
FreeModbus協(xié)議對(duì)硬件的需求非常少——基本上任何具有串行接口,并且有一些能夠容納modbus數(shù)據(jù)幀的RAM的微控制器都足夠了。
modbus協(xié)議鏈接:
https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
現(xiàn)支持如下功能碼:
Read Input Register (0x04)
Read Holding Registers (0x03)
Write Single Register (0x06)
Write Multiple Registers (0x10)
Read/Write Multiple Registers (0x17)
Read Coils (0x01)
Write Single Coil (0x05)
Write Multiple Coils (0x0F)
Read Discrete Inputs (0x02)
Report Slave ID (0x11)
FreeModbus的獲取
目前,FreeModbus最新版本是V1.6,可以通過官網(wǎng)下載,鏈接如下:
https://www.embedded-solutions.at/en/freemodbus-downloads/
可以下載壓縮包,也可以使用git工具來下載。
https://github.com/cwalter-at/freemodbus
硬件需求
FreeModbus協(xié)議對(duì)硬件的需求非常少——基本上任何具有串行接口,并且有一些能夠容納modbus數(shù)據(jù)幀的RAM的微控制器都足夠了。
1)一個(gè)異步串行接口,能夠支持接收緩沖區(qū)滿和發(fā)送緩存區(qū)空中斷。
2)一個(gè)能夠產(chǎn)生RTU傳輸所需要的t3.5字符超時(shí)定時(shí)器的時(shí)鐘。
對(duì)于軟件部分,僅僅需要一個(gè)簡單的事件隊(duì)列。在使用操作系統(tǒng)的處理器上,可通過單獨(dú)定義一個(gè)任務(wù)完成Modbus時(shí)間的查詢。小點(diǎn)的微控制器往往不允許使用操作系統(tǒng),在那種情況下,可以使用一個(gè)全局變量來實(shí)現(xiàn)該事件隊(duì)列(Atmel AVR 移植使用這種方式實(shí)現(xiàn))。
實(shí)際的存儲(chǔ)器需求決定于所使用的Modbus模塊的多少。下表列出了所支持的功能編譯后所需要的存儲(chǔ)器。ARM是使用GNUARM編譯器3.4.4使用-O1選項(xiàng)得到的。AVR項(xiàng)數(shù)值是使用WinAVR編譯器3.4.5使用-Os選項(xiàng)編譯得到的。
移植
1. 物理層接口文件的修改
在物理層,用戶只需完成串行口及超時(shí)定時(shí)器的配置即可。具體應(yīng)修改接口文件portserial.c及porttimer.c。
1.1portserial.c中函數(shù)的修改
1) void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
此函數(shù)的功能為設(shè)置串口狀態(tài)。有兩個(gè)參數(shù):xRxEnable及xTxEnable。當(dāng)xRxEnable為真時(shí),應(yīng)使能串口接收及接收中斷。在RS485通訊系統(tǒng)中,還要注意將RS485接口芯片設(shè)為接收使能狀態(tài);當(dāng)xTxEnable為真時(shí),應(yīng)使能串口發(fā)送及發(fā)送中斷。在RS485通訊系統(tǒng)中,還要注意將RS485接口芯片設(shè)為發(fā)送使能狀態(tài)。
2) void vMBPortClose( void )
此函數(shù)的功能是關(guān)閉Modbus通訊端口,具體的,應(yīng)在此函數(shù)中關(guān)閉通訊端口的發(fā)送使能及接收使能。
3) BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
此函數(shù)的功能是初始化串行通訊端口。有四個(gè)參數(shù):ucPORT、ulBaudRate、ucDataBits及eParity。參數(shù)ucPORT可以忽略;參數(shù)ulBaudRate是通訊端口的波特率,應(yīng)根據(jù)此數(shù)值設(shè)置所使用硬件端口的波特率;參數(shù)ucDataBits為通訊時(shí)所使用的數(shù)據(jù)位寬,注意,若使用RTU模式,則有ucDataBits=8,若使用ASCII模式,則有ucDataBits=7,應(yīng)根據(jù)此參數(shù)設(shè)置所使用硬件端口的數(shù)據(jù)位寬;eParity為校驗(yàn)方式,eParity=MB_PAR_NONE為無校驗(yàn),此時(shí)硬件端口應(yīng)設(shè)置為無校驗(yàn)方式及兩個(gè)停止位,eParity=MB_PAR_ODD為奇校驗(yàn),此時(shí)硬件端口應(yīng)設(shè)置為奇校驗(yàn)方式及一個(gè)停止位,eParity= MB_PAR_EVEN為偶校驗(yàn),此時(shí)硬件端口應(yīng)設(shè)置為偶校驗(yàn)方式及一個(gè)停止位。函數(shù)返回值務(wù)必為TRUE。
4) BOOL xMBPortSerialPutByte(CHAR ucByte)
此函數(shù)的功能為通訊端口發(fā)送一字節(jié)數(shù)據(jù)。參數(shù)為:ucByte,待發(fā)送的數(shù)據(jù)。應(yīng)在此函數(shù)中編寫發(fā)送一字節(jié)數(shù)據(jù)的函數(shù)。注意,由于使用的是中斷發(fā)送,故只需將數(shù)據(jù)放到發(fā)送寄存器即可。函數(shù)返回值務(wù)必為TRUE。
5) BOOL xMBPortSerialGetByte( CHAR * pucByte )
此函數(shù)的功能為通訊端口接收一字節(jié)數(shù)據(jù)。參數(shù)為:* pucByte,接收到的數(shù)據(jù)。應(yīng)在此函數(shù)中編寫接收的函數(shù)。注意,由于使用的是中斷接收,故只需將接收寄存器的值放到* pucByte即可。函數(shù)返回值務(wù)必為TRUE。
6) void prvvUARTTxReadyISR(void)
發(fā)送中斷函數(shù)。此函數(shù)無需修改。只需在用戶的發(fā)送中斷函數(shù)中調(diào)用此函數(shù)即可,同時(shí),用戶應(yīng)在調(diào)用此函數(shù)后,清除發(fā)送中斷標(biāo)志位。
7) void prvvUARTRxISR(void)
接送中斷函數(shù)。此函數(shù)無需修改。只需在用戶的接收中斷函數(shù)中調(diào)用此函數(shù)即可,同時(shí),用戶應(yīng)在調(diào)用此函數(shù)后,清除接收中斷標(biāo)志位。
porttimer.c中函數(shù)的修改
1) BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
此函數(shù)的功能為初始化超時(shí)定時(shí)器。參數(shù)為:usTim1Timerout50us,50us的個(gè)數(shù)。用戶應(yīng)根據(jù)所使用的硬件初始化超時(shí)定時(shí)器,使之能產(chǎn)生中斷時(shí)間為usTim1Timerout50us*50us的中斷。函數(shù)返回值務(wù)必為TRUE。
2) void vMBPortTimersEnable( )
此函數(shù)的功能為使能超時(shí)定時(shí)器。用戶需在此函數(shù)中清除中斷標(biāo)志位、清零定時(shí)器計(jì)數(shù)值,并重新使能定時(shí)器中斷。
3) void vMBPortTimersDisable( )
此函數(shù)的功能為關(guān)閉超時(shí)定時(shí)器。用戶需在此函數(shù)中清零定時(shí)器計(jì)數(shù)值,并關(guān)閉定時(shí)器中斷。
4) void TIMERExpiredISR( void )
定時(shí)器中斷函數(shù)。此函數(shù)無需修改。只需在用戶的定時(shí)器中斷中調(diào)用此函數(shù)即可,同時(shí),用戶應(yīng)在調(diào)用此函數(shù)后清除中斷標(biāo)志位。
2. 應(yīng)用層回調(diào)函數(shù)的修改
在應(yīng)用層,用戶需要定義所需要使用的寄存器,并修改對(duì)應(yīng)的回調(diào)函數(shù)。回函數(shù)有如下幾個(gè):
1) eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
輸入寄存器回調(diào)函數(shù)。* pucRegBuffer為要添加到協(xié)議中的數(shù)據(jù),usAddress為輸入寄存器地址,usNRegs為要讀取寄存器的個(gè)數(shù)。用戶應(yīng)根據(jù)要訪問的寄存器地址usAddress將相應(yīng)輸入寄存器的值按順序添加到pucRegBuffer中。
2) eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
保持寄存器回調(diào)函數(shù)。* pucRegBuffer為要協(xié)議中的數(shù)據(jù),usAddress為輸入寄存器地址,usNRegs為訪問寄存器的個(gè)數(shù),eMode為訪問類型(MB_REG_READ為讀保持寄存器,MB_REG_WRITE為寫保持寄存器)。用戶應(yīng)根據(jù)要訪問的寄存器地址usAddress將相應(yīng)輸入寄存器的值按順序添加到pucRegBuffer中,或?qū)f(xié)議中的數(shù)據(jù)根據(jù)要訪問的寄存器地址usAddress放到相應(yīng)保持寄存器中。
3) eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
讀寫線圈回調(diào)函數(shù)。* pucRegBuffer為要添加到協(xié)議中的數(shù)據(jù),usAddress為線圈地址,usNCoils為要訪問線圈的個(gè)數(shù),eMode為訪問類型(MB_REG_READ為讀線圈狀態(tài),MB_REG_WRITE為寫線圈)。用戶應(yīng)根據(jù)要訪問的線圈地址usAddress將相應(yīng)線圈的值按順序添加到pucRegBuffer中,或?qū)f(xié)議中的數(shù)據(jù)根據(jù)要訪問的線圈地址usAddress放到相應(yīng)線圈中。
4) eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
讀離散線圈回調(diào)函數(shù)。* pucRegBuffer為要添加到協(xié)議中的數(shù)據(jù),usAddress為線圈地址,usNDiscrete為要訪問線圈的個(gè)數(shù)。用戶應(yīng)根據(jù)要訪問的線圈地址usAddress將相應(yīng)線圈的值按順序添加到pucRegBuffer中。
3. 應(yīng)用層初始化及協(xié)議訪問
用戶只需在主函數(shù)中調(diào)用協(xié)議初始化代碼,及消息處理函數(shù)即可。需用戶調(diào)用的函數(shù)有如下幾個(gè):
1) eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
協(xié)議初始化函數(shù)。eMode為所要使用的模式,用戶可選MB_RTU(RTU模式)、MB_ASCII(ASCII模式)或MB_TCP(TCP模式);ucSlaveAddress為從機(jī)地址,用戶根據(jù)需要,取值為1~247(0為廣播地址,248 ~ 255協(xié)議保留);ulBaudRate為通信波特率,用戶根據(jù)需要選用,但務(wù)必使主機(jī)能支持此波特率;eParity為校驗(yàn)方式,用戶根據(jù)需要選用,但務(wù)必使主機(jī)能支持此校驗(yàn)方式。
2) eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning, UCHAR const pucAdditional, USHORT usAdditionalLen )
從機(jī)ID設(shè)置函數(shù)。注意,ID表示的是設(shè)備的類型,不同于ucSlaveAddress(從機(jī)地址)。對(duì)同一通訊系統(tǒng)中,可以有相同的ucSlaveID,但不可以有相同的ucSlaveAddress。ucSlaveID為一字節(jié)的設(shè)備ID號(hào);xIsRunning為設(shè)備的運(yùn)行狀態(tài),0xFF為運(yùn)行,0x00為停止; pucAdditional為設(shè)備的附加描述,根據(jù)需要添加;usAdditionalLen為附加描述的長度(按字節(jié)計(jì)算)。此函數(shù)不是必須調(diào)用的。但當(dāng)一個(gè)Modbus通訊系統(tǒng)中有不同種設(shè)備時(shí),應(yīng)調(diào)用此函數(shù)添加對(duì)應(yīng)設(shè)備的描述。
3) eMBErrorCode eMBPoll( void )
輪詢事件查詢處理函數(shù)。用戶需在主循環(huán)中調(diào)用此函數(shù)。對(duì)于使用操作系統(tǒng)的程序,應(yīng)單獨(dú)創(chuàng)建一個(gè)任務(wù),使操作系統(tǒng)能周期調(diào)用此函數(shù)。
初始化及運(yùn)行
FreeModbus是基于消息隊(duì)列的協(xié)議。協(xié)議通過檢測相應(yīng)的消息來完成對(duì)應(yīng)功能。 協(xié)議棧的初始化及運(yùn)行流程如下:
1) 首先調(diào)用eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )完成物理層設(shè)備的初始化, 主要包括:
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )串口初始化,設(shè)定波特率、數(shù)據(jù)位數(shù)、校驗(yàn)方式;BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )定時(shí)器初始化,設(shè)定T35定時(shí)所需要的定時(shí)器常數(shù)。
2) 調(diào)用(此處非必需)eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,UCHAR const *pucAdditional, USHORT usAdditionalLen )指定設(shè)備ID。
3) 調(diào)用eMBErrorCode eMBEnable(void)使能協(xié)議棧 ,主要包括:static pvMBFrameStart pvMBFrameStartCur(函數(shù)指針)協(xié)議棧開始,將eRcvState設(shè)為STATE_RX_INIT狀態(tài),調(diào)用void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能接收,調(diào)用void vMBPortTimersEnable( )使能超時(shí)定時(shí)器。
4) 在3中使能了超時(shí)定時(shí)器,故經(jīng)過T35時(shí)間后,發(fā)生第一次超時(shí)中斷,在中斷中,向協(xié)議棧發(fā)送消息EV_READY(Startup finished),并調(diào)用void vMBPortTimersDisable( )關(guān)閉超時(shí)定時(shí)器,同時(shí)將eRcvState設(shè)為STATE_RX_IDLE。此時(shí),協(xié)議棧可以接收串口數(shù)據(jù)。注意,此處首先啟用一次超時(shí)定時(shí)器是因?yàn)槌跏蓟瓿蓵r(shí),串口有可能已經(jīng)有數(shù)據(jù),因?yàn)闊o法判斷第一個(gè)數(shù)據(jù)是請(qǐng)求的開始,故等待T35,接收下一幀請(qǐng)求。
5) 此時(shí),主函數(shù)調(diào)用eMBErrorCode eMBPoll( void )檢測事件。
6) 若發(fā)生串口接收中斷,且eRcvState為STATE_RX_IDLE(4中已將eRcvState設(shè)為STATE_RX_IDLE),則向接收緩存中存入接收到的字符,同時(shí)將eRcvState設(shè)為STATE_RX_RCV狀態(tài),并清零超時(shí)定時(shí)器。在下一個(gè)數(shù)據(jù)來到時(shí),不斷將數(shù)據(jù)存入接收緩存,并清零超時(shí)定時(shí)器。
7) 如果沒有接收完成,則不可能發(fā)生超時(shí)中斷。發(fā)生超時(shí)中斷,說明T35時(shí)間內(nèi)未收到新的串口數(shù)據(jù),根據(jù)Modbus協(xié)議的規(guī)定,這指示著一幀請(qǐng)求數(shù)據(jù)接收完成。在中斷中,向協(xié)議棧發(fā)送消息EV_FRAME_RECEIVED(Frame received),等待協(xié)議棧處理此消息。
8) 主函數(shù)調(diào)用eMBErrorCode eMBPoll( void )檢測到事件EV_FRAME_RECEIVED后,調(diào)用static peMBFrameReceive peMBFrameReceiveCur簡單判斷請(qǐng)求幀數(shù)據(jù),并向協(xié)議棧發(fā)送消息EV_EXECUTE(Execute function)。
9) 主函數(shù)調(diào)用eMBErrorCode eMBPoll( void )檢測到事件EV_EXECUTE后,根據(jù)相應(yīng)的請(qǐng)求代碼查找處理該功能的函數(shù)指針來處理該功能。 若不是廣播消息,則調(diào)用static peMBFrameSend peMBFrameSendCur發(fā)送回復(fù)消息,在此函數(shù)中,只把要回復(fù)的數(shù)據(jù)復(fù)制到了串口緩存中,同時(shí)將eSndState設(shè)為STATE_TX_XMIT(Transmitter is in transfer state),并通過調(diào)用void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能發(fā)送中斷。注意,發(fā)送中斷使能后,由于串口發(fā)送寄存器本來就是空的,故在使能后將進(jìn)入發(fā)送中斷中。
10) 發(fā)送中斷中,且eSndState為STATE_TX_XMIT(9中已將eSndState設(shè)為STATE_TX_XMIT),則將串口緩存中的數(shù)據(jù)發(fā)送出去,同時(shí)不斷對(duì)發(fā)送字符個(gè)數(shù)統(tǒng)計(jì),當(dāng)發(fā)送完成后,向協(xié)議棧發(fā)送消息EV_FRAME_SENT(Frame sent)。
11) 主函數(shù)調(diào)用eMBErrorCode eMBPoll( void )檢測到事件EV_FRAME_SENT后,不處理此消息。
12) 當(dāng)串口接收到數(shù)據(jù)后,協(xié)議棧將重復(fù)6-11處理消息。
資料來源:
https://baike.baidu.com/item/freemodbus/7566841?fr=aladdin
FreeModbus啟動(dòng)流程分析
資料來源:
http://emb.hqyj.com/Column/7504.html
移植資料:
STM32 HAL庫移植FreeModbus詳細(xì)步驟
https://blog.csdn.net/qq153471503/article/details/104840279
STM32 移植FreeModbus詳細(xì)過程
http://www.mcublog.cn/software/2020_03/stm32-freemodbus-yizhi/
移植FreeModbus到FreeRTOS系統(tǒng)上
https://blog.csdn.net/dmjkun/article/details/85564634?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-5.no_search_link&spm=1001.2101.3001.4242
stm32裸機(jī)移植FreeModbus
https://blog.csdn.net/qq_27508477/article/details/89280822?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-16.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-16.no_search_link
https://www.stmcu.org.cn/module/forum/forum.php?mod=viewthread&page=1&tid=604864
安富萊電子論壇:
http://www.armbbs.cn/
MODBUS主機(jī)協(xié)議移植
https://blog.csdn.net/weixin_42462202/article/details/81232347
Modbus協(xié)議棧應(yīng)用實(shí)例之一:Modbus RTU主站應(yīng)用
https://www.cnblogs.com/foxclever/p/13251949.html
總結(jié)
以上是生活随笔為你收集整理的FreeModbus应用系列之一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVA - 725 Division-s
- 下一篇: MediaTek发布天玑8300移动芯片