FreeModbus应用系列之一
FreeModbus應用總結系列之一
- FreeModbus簡介
- FreeModbus的獲取
- 硬件需求
- 移植
-
- 1. 物理層接口文件的修改
-
- 1.1portserial.c中函數的修改
- porttimer.c中函數的修改
- 2. 應用層回調函數的修改
- 3. 應用層初始化及協議訪問
- 初始化及運行
- FreeModbus啟動流程分析
- MODBUS主機協議移植
FreeModbus簡介
FreeMODBUS是一個奧地利人寫的Modbus協議。它是一個針對嵌入式應用的一個免費(自由)的通用MODBUS協議的移植。Modbus是一個工業制造環境中應用的一個通用協議。Modbus通信協議棧包括兩層:Modbus應用層協議,該層定義了數據模式和功能;另外一層是網絡層。
FreeModbus提供了RTU/ASCII 傳輸模式及TCP協議支持。
FreeModbus協議對硬件的需求非常少——基本上任何具有串行接口,并且有一些能夠容納modbus數據幀的RAM的微控制器都足夠了。
modbus協議鏈接:
https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
現支持如下功能碼:
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,可以通過官網下載,鏈接如下:
https://www.embedded-solutions.at/en/freemodbus-downloads/
可以下載壓縮包,也可以使用git工具來下載。
https://github.com/cwalter-at/freemodbus
硬件需求
FreeModbus協議對硬件的需求非常少——基本上任何具有串行接口,并且有一些能夠容納modbus數據幀的RAM的微控制器都足夠了。
1)一個異步串行接口,能夠支持接收緩沖區滿和發送緩存區空中斷。
2)一個能夠產生RTU傳輸所需要的t3.5字符超時定時器的時鐘。
對于軟件部分,僅僅需要一個簡單的事件隊列。在使用操作系統的處理器上,可通過單獨定義一個任務完成Modbus時間的查詢。小點的微控制器往往不允許使用操作系統,在那種情況下,可以使用一個全局變量來實現該事件隊列(Atmel AVR 移植使用這種方式實現)。
實際的存儲器需求決定于所使用的Modbus模塊的多少。下表列出了所支持的功能編譯后所需要的存儲器。ARM是使用GNUARM編譯器3.4.4使用-O1選項得到的。AVR項數值是使用WinAVR編譯器3.4.5使用-Os選項編譯得到的。
移植
1. 物理層接口文件的修改
在物理層,用戶只需完成串行口及超時定時器的配置即可。具體應修改接口文件portserial.c及porttimer.c。
1.1portserial.c中函數的修改
1) void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
此函數的功能為設置串口狀態。有兩個參數:xRxEnable及xTxEnable。當xRxEnable為真時,應使能串口接收及接收中斷。在RS485通訊系統中,還要注意將RS485接口芯片設為接收使能狀態;當xTxEnable為真時,應使能串口發送及發送中斷。在RS485通訊系統中,還要注意將RS485接口芯片設為發送使能狀態。
2) void vMBPortClose( void )
此函數的功能是關閉Modbus通訊端口,具體的,應在此函數中關閉通訊端口的發送使能及接收使能。
3) BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
此函數的功能是初始化串行通訊端口。有四個參數:ucPORT、ulBaudRate、ucDataBits及eParity。參數ucPORT可以忽略;參數ulBaudRate是通訊端口的波特率,應根據此數值設置所使用硬件端口的波特率;參數ucDataBits為通訊時所使用的數據位寬,注意,若使用RTU模式,則有ucDataBits=8,若使用ASCII模式,則有ucDataBits=7,應根據此參數設置所使用硬件端口的數據位寬;eParity為校驗方式,eParity=MB_PAR_NONE為無校驗,此時硬件端口應設置為無校驗方式及兩個停止位,eParity=MB_PAR_ODD為奇校驗,此時硬件端口應設置為奇校驗方式及一個停止位,eParity= MB_PAR_EVEN為偶校驗,此時硬件端口應設置為偶校驗方式及一個停止位。函數返回值務必為TRUE。
4) BOOL xMBPortSerialPutByte(CHAR ucByte)
此函數的功能為通訊端口發送一字節數據。參數為:ucByte,待發送的數據。應在此函數中編寫發送一字節數據的函數。注意,由于使用的是中斷發送,故只需將數據放到發送寄存器即可。函數返回值務必為TRUE。
5) BOOL xMBPortSerialGetByte( CHAR * pucByte )
此函數的功能為通訊端口接收一字節數據。參數為:* pucByte,接收到的數據。應在此函數中編寫接收的函數。注意,由于使用的是中斷接收,故只需將接收寄存器的值放到* pucByte即可。函數返回值務必為TRUE。
6) void prvvUARTTxReadyISR(void)
發送中斷函數。此函數無需修改。只需在用戶的發送中斷函數中調用此函數即可,同時,用戶應在調用此函數后,清除發送中斷標志位。
7) void prvvUARTRxISR(void)
接送中斷函數。此函數無需修改。只需在用戶的接收中斷函數中調用此函數即可,同時,用戶應在調用此函數后,清除接收中斷標志位。
porttimer.c中函數的修改
1) BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
此函數的功能為初始化超時定時器。參數為:usTim1Timerout50us,50us的個數。用戶應根據所使用的硬件初始化超時定時器,使之能產生中斷時間為usTim1Timerout50us*50us的中斷。函數返回值務必為TRUE。
2) void vMBPortTimersEnable( )
此函數的功能為使能超時定時器。用戶需在此函數中清除中斷標志位、清零定時器計數值,并重新使能定時器中斷。
3) void vMBPortTimersDisable( )
此函數的功能為關閉超時定時器。用戶需在此函數中清零定時器計數值,并關閉定時器中斷。
4) void TIMERExpiredISR( void )
定時器中斷函數。此函數無需修改。只需在用戶的定時器中斷中調用此函數即可,同時,用戶應在調用此函數后清除中斷標志位。
2. 應用層回調函數的修改
在應用層,用戶需要定義所需要使用的寄存器,并修改對應的回調函數?;睾瘮涤腥缦聨讉€:
1) eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
輸入寄存器回調函數。* pucRegBuffer為要添加到協議中的數據,usAddress為輸入寄存器地址,usNRegs為要讀取寄存器的個數。用戶應根據要訪問的寄存器地址usAddress將相應輸入寄存器的值按順序添加到pucRegBuffer中。
2) eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
保持寄存器回調函數。* pucRegBuffer為要協議中的數據,usAddress為輸入寄存器地址,usNRegs為訪問寄存器的個數,eMode為訪問類型(MB_REG_READ為讀保持寄存器,MB_REG_WRITE為寫保持寄存器)。用戶應根據要訪問的寄存器地址usAddress將相應輸入寄存器的值按順序添加到pucRegBuffer中,或將協議中的數據根據要訪問的寄存器地址usAddress放到相應保持寄存器中。
3) eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
讀寫線圈回調函數。* pucRegBuffer為要添加到協議中的數據,usAddress為線圈地址,usNCoils為要訪問線圈的個數,eMode為訪問類型(MB_REG_READ為讀線圈狀態,MB_REG_WRITE為寫線圈)。用戶應根據要訪問的線圈地址usAddress將相應線圈的值按順序添加到pucRegBuffer中,或將協議中的數據根據要訪問的線圈地址usAddress放到相應線圈中。
4) eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
讀離散線圈回調函數。* pucRegBuffer為要添加到協議中的數據,usAddress為線圈地址,usNDiscrete為要訪問線圈的個數。用戶應根據要訪問的線圈地址usAddress將相應線圈的值按順序添加到pucRegBuffer中。
3. 應用層初始化及協議訪問
用戶只需在主函數中調用協議初始化代碼,及消息處理函數即可。需用戶調用的函數有如下幾個:
1) eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
協議初始化函數。eMode為所要使用的模式,用戶可選MB_RTU(RTU模式)、MB_ASCII(ASCII模式)或MB_TCP(TCP模式);ucSlaveAddress為從機地址,用戶根據需要,取值為1~247(0為廣播地址,248 ~ 255協議保留);ulBaudRate為通信波特率,用戶根據需要選用,但務必使主機能支持此波特率;eParity為校驗方式,用戶根據需要選用,但務必使主機能支持此校驗方式。
2) eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning, UCHAR const pucAdditional, USHORT usAdditionalLen )
從機ID設置函數。注意,ID表示的是設備的類型,不同于ucSlaveAddress(從機地址)。對同一通訊系統中,可以有相同的ucSlaveID,但不可以有相同的ucSlaveAddress。ucSlaveID為一字節的設備ID號;xIsRunning為設備的運行狀態,0xFF為運行,0x00為停止; pucAdditional為設備的附加描述,根據需要添加;usAdditionalLen為附加描述的長度(按字節計算)。此函數不是必須調用的。但當一個Modbus通訊系統中有不同種設備時,應調用此函數添加對應設備的描述。
3) eMBErrorCode eMBPoll( void )
輪詢事件查詢處理函數。用戶需在主循環中調用此函數。對于使用操作系統的程序,應單獨創建一個任務,使操作系統能周期調用此函數。
初始化及運行
FreeModbus是基于消息隊列的協議。協議通過檢測相應的消息來完成對應功能。 協議棧的初始化及運行流程如下:
1) 首先調用eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )完成物理層設備的初始化, 主要包括:
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )串口初始化,設定波特率、數據位數、校驗方式;BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )定時器初始化,設定T35定時所需要的定時器常數。
2) 調用(此處非必需)eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,UCHAR const *pucAdditional, USHORT usAdditionalLen )指定設備ID。
3) 調用eMBErrorCode eMBEnable(void)使能協議棧 ,主要包括:static pvMBFrameStart pvMBFrameStartCur(函數指針)協議棧開始,將eRcvState設為STATE_RX_INIT狀態,調用void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能接收,調用void vMBPortTimersEnable( )使能超時定時器。
4) 在3中使能了超時定時器,故經過T35時間后,發生第一次超時中斷,在中斷中,向協議棧發送消息EV_READY(Startup finished),并調用void vMBPortTimersDisable( )關閉超時定時器,同時將eRcvState設為STATE_RX_IDLE。此時,協議棧可以接收串口數據。注意,此處首先啟用一次超時定時器是因為初始化完成時,串口有可能已經有數據,因為無法判斷第一個數據是請求的開始,故等待T35,接收下一幀請求。
5) 此時,主函數調用eMBErrorCode eMBPoll( void )檢測事件。
6) 若發生串口接收中斷,且eRcvState為STATE_RX_IDLE(4中已將eRcvState設為STATE_RX_IDLE),則向接收緩存中存入接收到的字符,同時將eRcvState設為STATE_RX_RCV狀態,并清零超時定時器。在下一個數據來到時,不斷將數據存入接收緩存,并清零超時定時器。
7) 如果沒有接收完成,則不可能發生超時中斷。發生超時中斷,說明T35時間內未收到新的串口數據,根據Modbus協議的規定,這指示著一幀請求數據接收完成。在中斷中,向協議棧發送消息EV_FRAME_RECEIVED(Frame received),等待協議棧處理此消息。
8) 主函數調用eMBErrorCode eMBPoll( void )檢測到事件EV_FRAME_RECEIVED后,調用static peMBFrameReceive peMBFrameReceiveCur簡單判斷請求幀數據,并向協議棧發送消息EV_EXECUTE(Execute function)。
9) 主函數調用eMBErrorCode eMBPoll( void )檢測到事件EV_EXECUTE后,根據相應的請求代碼查找處理該功能的函數指針來處理該功能。 若不是廣播消息,則調用static peMBFrameSend peMBFrameSendCur發送回復消息,在此函數中,只把要回復的數據復制到了串口緩存中,同時將eSndState設為STATE_TX_XMIT(Transmitter is in transfer state),并通過調用void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能發送中斷。注意,發送中斷使能后,由于串口發送寄存器本來就是空的,故在使能后將進入發送中斷中。
10) 發送中斷中,且eSndState為STATE_TX_XMIT(9中已將eSndState設為STATE_TX_XMIT),則將串口緩存中的數據發送出去,同時不斷對發送字符個數統計,當發送完成后,向協議棧發送消息EV_FRAME_SENT(Frame sent)。
11) 主函數調用eMBErrorCode eMBPoll( void )檢測到事件EV_FRAME_SENT后,不處理此消息。
12) 當串口接收到數據后,協議棧將重復6-11處理消息。
資料來源:
https://baike.baidu.com/item/freemodbus/7566841?fr=aladdin
FreeModbus啟動流程分析
資料來源:
http://emb.hqyj.com/Column/7504.html
移植資料:
STM32 HAL庫移植FreeModbus詳細步驟
https://blog.csdn.net/qq153471503/article/details/104840279
STM32 移植FreeModbus詳細過程
http://www.mcublog.cn/software/2020_03/stm32-freemodbus-yizhi/
移植FreeModbus到FreeRTOS系統上
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裸機移植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主機協議移植
https://blog.csdn.net/weixin_42462202/article/details/81232347
Modbus協議棧應用實例之一:Modbus RTU主站應用
https://www.cnblogs.com/foxclever/p/13251949.html
總結
以上是生活随笔為你收集整理的FreeModbus应用系列之一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVA - 725 Division-s
- 下一篇: 《C++ Primer》2.1.3节练习