FreeModbus源碼分析

轉載自:http://blog.sina.com.cn/s/blog_4aca3d5d01011a1d.html

 

1.   FreeModbus協議分析

協議必須首先調用初始化功能eMBinit()函數。後調用eMBEnable(),最後,在循環體或者單獨一個任務中調用eMBPoll()函數。

2.   應用層協議

2.1.     系統的啓動

2.1.1.   eMBInit()函數的源碼分析

以RTU方式爲例,首先,檢查調用的地址是否合法。如不合法,返回錯誤。如果合法則繼續執行,

首先,針對RTU方式還是ASCII方式,選擇不同的編譯模塊。

對需要調用的函數指針進行復制。如果移植需要改變其他用途,則要修改相應的指針,包括如下賦值:

            pvMBFrameStartCur = eMBRTUStart;

            pvMBFrameStopCur = eMBRTUStop;

            peMBFrameSendCur = eMBRTUSend;

            peMBFrameReceiveCur = eMBRTUReceive;

            pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;

            pxMBFrameCBByteReceived = xMBRTUReceiveFSM;

            pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;

            pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;

然後調用eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );具體初始化通訊端口。

2.1.2.   eMBRTUInit

eMBRTUInit這個函數主要幹兩件事:

第一,   初始化串口:   

if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )

{

   eStatus = MB_EPORTERR;

}

這個函數在portserial.c中,需要用戶在移植的時候根據自己的處理器編寫。

第二,   初始化計時器:首先要根據波特率計算一下是3.5~5.0個字節週期的時間,然後再調用xMBPortTimersInit( ( USHORT ) usTimerT35_50us ),初始化計時器。這個函數在porttimer.c中,需要用戶在移植的時候根據自己的處理器編寫。

2.1.3.   eMBEnable源碼分析

首先,看看Modbus功能是否是被關閉的,如果不是被關閉(可能是沒有被初始化或者已經打開),就返回錯誤。

如果是disable狀態,就幹下面兩件事:

l  調用pvMBFrameStartCur()。由於這是個函數指針,在模塊eMBInit中,指向了eMBRTUStart函數

n  在源代碼中有這樣一段註釋:,意思是,首先設置成STATE_RX_INIT,然後打開計時器,等待t3.5以後,進入STATE_RX_IDLE狀態。

n  看源代碼中,首先有設置Receiver的狀態,後調用vMBPortSerialEnable,設置接收狀態,然後打開定時器。

n  當定時器中斷後,自動調用中斷服務程序,在中斷服務程序中,只調用了pxMBPortCBTimerExpired,而這是一個函數指針,在RTU方式初始化時,被指向了xMBRTUTimerT35Expired()函數。

n  xMBRTUTimerT35Expired函數在mbrtu.c中,在這裏,我們只看第一種方式,就是進入初始化狀態,在t35時間以後,只調用了一個xNeedPoll = xMBPortEventPost( EV_READY );

n  xMBPortEventPost函數就是在事件隊列里加了一個EV_RDY事件。

l  然後,將eMB狀態改爲使能狀態,

l  初始化結束。

2.2.     總線偵聽eMBPoll()

首先,判斷系統是否被使能,如果沒有,則返回錯誤值。

然後,檢查是否有事件發生,如果有,則根據不同類型的事件響應:

l  如果是EV_RDY,表示系統剛剛進入偵聽狀態,則什麼都不做;

l  如果狀態爲EV_FRAME_RECEIVED,也就是接收到完整的幀,做下面兩件事情:

n  調用eStatus=peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength )。這是一個函數指針,在eMBInit中,被初始化指向eMBRTUReceive。

n  eMBRTUReceive這個函數首先校驗幀的長度和CRC,然後從協議中解析出地址、數據和長度。

n  然後檢查地址,如果是廣播地址或者是本機地址,就調用xMBPortEventPost( EV-EXECUTE),將接收器的狀態更改爲EV_EXECUTE。

l  如果狀態爲EV_EXECUTE,就在函數列表中檢查,有沒有與命令字段相符合的函數來解析相應則執行該函數,否則返回非法功能代碼。

2.3.  數據發送

發送數據通過指針eMBRTUSend,調用eMBRTUSend函數。

2.3.1.   eMBRTUSend函數

這個函數的作用就是打包,將數據打包成幀。

l  首先,檢查接收狀態。因爲MODBUS是基於RS-485半雙工通訊,所以當正在接收數據時,不發送該幀。

l  如果總線空,就將數據打包,將地址和CRC加入數據幀

l  將總線狀態改爲發送。

2.4.  功能註冊

l  對於指定的功能代碼,需要一個功能回調函數來處理,格式如下。

eMBException eMXXXXXX ( UCHAR * pucFrame, USHORT * usLen )

l  需要通過函數eMBRegisterCB(功能代碼,函數名)加到處理代碼中。具體源碼分析從略。

2.4.1.   prvvUARTTxReadyISR()

總線狀態改爲發送後,會在發送緩衝時,自動調用prvvUARTTxReadyISR()中斷服務程序。prvvUARTTxReadyISR()只調用了一個函數,就是pxMBFrameCBTransmitterEmpty ()。

2.4.2.   pxMBFrameCBByteReceived ()

pxMBFrameCBTransmitterEmpty ()是一個指針,指向了xMBRTUTransmitFSM函數。

3.   數據鏈路層協議

數據鏈路層是最基本的打包部分,將數據打包成幀,送到應用層。在數據鏈路層協議中,使用中斷方式來接受。那麼每次接收到字符就自動調用接收字符的ISR程序。按照規定,應該將中斷服務程序安裝給prvvUARTRxISR(void)函數。實際上這個函數只調用了一個函數:

pxMBFrameCBByteReceived(),這個指針調用了xMBRTUReceiveFSM函數。

3.1.  xMBRTUReceiveFSM()函數

函數首先檢查是不是處於發送狀態。如果處於發送狀態,直接退出。

l  首先調用xMBPortSerialGetByte( ( CHAR * ) & ucByte ),獲取從串口讀到的字符。

l  然後檢查接受狀態:

n  如果是錯誤狀態或者處於初始化狀態,那麼直接等待,錯過該幀。

n  如果是STATE_RX_IDLE空閒狀態,則將指針重置,將收到的第一個字節存儲到緩衝區,並將狀態改爲STATE_RX_RCV狀態。

n  如果處於接收狀態,就判斷,如果緩衝區未滿,就將收到的字節放入緩衝區,否則改爲錯誤狀態。

l  不管在任何狀態,最後都開啓了t35計時器。在t35結束的時候,通過指針調用了xMBRTUTimerT35Expired()函數。

l  xMBRTUTimerT35Expired()函數檢查狀態,如果是接收狀態那就表明,已經有t35這麼長的時間裏,沒有收到任新字節,當前的幀結束。在隊列裏增加一個EV_FRAME_RECEIVED事件。

l  如果是錯誤狀態,什麼都不做。

l  然後關掉計時器,將狀態改爲空閒。

3.2.  xMBRTUTransmitFSM()函數

xMBRTUTransmitFSM首先判斷總線是否忙,如果忙,則終止。如果不忙,則繼續,根據發送狀態變量:

l  如果當前爲STATE_TX_IDLE(空閒)狀態,則打開端口發送

l  如果當前狀態爲STATE_TX_XMIT,則進一步判斷髮送隊列是否爲空,

n  如果不空,則發送下一個字符

n  如果空,說明發送完成,關閉發送端口,改爲偵聽,並將狀態改爲空閒。

4.   傳輸控制

除了傳輸控制以外,還有傳輸控制的若干函數。通過下面幾個指針來調用:

         pvMBFrameStopCur()

         pvMBFrameCloseCur()

4.1.  pvMBFrameStopCur()函數

pvMBFrameStopCur是一個函數指針,在RTU方式下,它指向eMBRTUStop()函數。該函數做下面幾件事情:

l  關閉偵聽和發送

l  關閉定時器

4.2.  pvMBFrameCloseCur()函數

這個指針指向一個叫做vMBPortClose()的函數,該函數目前只有在mbport.h中的聲明,而沒有實現。需要等到後面的版本再實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章