久久婷婷香蕉热狠狠综合,精品无码国产自产拍在线观看蜜,寡妇房东在做爰3,中文字幕日本人妻久久久免费,国产成人精品三上悠亚久久

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > Freemodbus啟動流程(cheng)分析

Freemodbus啟動流程分析(xi) 時(shi)間(jian):2018-09-25      來源(yuan):未知

近項目(mu)有(you)用(yong)到modbus協(xie)議,于(yu)是在網(wang)上找了些資料成功將freemodbus移植(zhi)到m3,由于(yu)移植(zhi)過程較簡單(dan),網(wang)上教程也(ye)很(hen)多,這里我們就不再贅述(shu).我用(yong)到的freemodbus版本是V1.5,下面(mian)附上新(xin)的源(yuan)碼下載地址://www.freemodbus.org/index.php?idx=5

下(xia)(xia)面開始分析下(xia)(xia)freemodbus得啟動流程(cheng),老(lao)規矩我們還(huan)是從main()函數下(xia)(xia)手:

和freemodbus有關(guan)的函數只有三(san)個eMBInit(), eMBEnable(), eMBPoll().我們逐一來分析.

首先(xian)是eMBInit(),我(wo)們來看下(xia)源碼(ma):eMBErrorCode

eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort,

ULONG ulBaudRate, eMBParity eParity )

{

//錯誤狀態(tai)初始值

eMBErrorCode eStatus = MB_ENOERR;

//驗證從(cong)機地址

if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||

( ucSlaveAddress < MB_ADDRESS_MIN ) ||

( ucSlaveAddress > MB_ADDRESS_MAX ) )

{

eStatus = MB_EINVAL;

}

else

{

ucMBAddress = ucSlaveAddress;

switch ( eMode )

{

#if MB_RTU_ENABLED > 0

case MB_RTU:

pvMBFrameStartCur = eMBRTUStart;

pvMBFrameStopCur = eMBRTUStop;

peMBFrameSendCur = eMBRTUSend;

//報文(wen)接(jie)收(shou)函(han)數

peMBFrameReceiveCur = eMBRTUReceive;

pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;

//接(jie)收狀態機

pxMBFrameCBByteReceived = xMBRTUReceiveFSM;

//發送狀態機

pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;

//報(bao)文到(dao)達間隔檢查

 pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;

//初始(shi)化(hua)RTU

eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );

break;

#endif

#if MB_ASCII_ENABLED > 0

case MB_ASCII:

pvMBFrameStartCur = eMBASCIIStart;

pvMBFrameStopCur = eMBASCIIStop;

peMBFrameSendCur = eMBASCIISend;

peMBFrameReceiveCur = eMBASCIIReceive;

pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;

pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;

pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;

 pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;

eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );

break;

#endif

default:

eStatus = MB_EINVAL;

}

//

if( eStatus == MB_ENOERR )

{

if( !xMBPortEventInit() )

{

/* port dependent event module initalization failed. */

eStatus = MB_EPORTERR;

}

else

{

//設定當前狀態(tai)

eMBCurrentMode = eMode;

eMBState = STATE_DISABLED;

}

}

}

return eStatus;

}

我這(zhe)次用到的是RTU模式,所以我們(men)就只(zhi)分析(xi)RTU模式下得工作模式.上邊的代(dai)碼比較(jiao)容易理解, 大家好逐行分析(xi),我們(men)首先來看下都(dou)傳了什么參(can)數:

eMBInit(MB_RTU, 0x09, 0x01, 9600, MB_PAR_NONE);

附上函數的聲明:eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )

MB_RTU:使用的是modbusRTU模式.

0x09:從(cong)機得地(di)址

0x01:串口(kou)號,這(zhe)里我(wo)們使(shi)用的是(shi)串口(kou)1

9600:波特率

MB_PAR_NONE:無校驗和

這個(ge)(ge)函(han)數(shu)的(de)任務就是(shi)根據你的(de)參數(shu)來配置(zhi)串(chuan)口(kou),定時器和(he)modbus中的(de)ID號這個(ge)(ge)函(han)數(shu)另外一(yi)(yi)個(ge)(ge)重要的(de)工作就是(shi)將一(yi)(yi)些全(quan)局指(zhi)(zhi)針(zhen)變量(liang)指(zhi)(zhi)向對應得函(han)數(shu),將來方便進(jin)行回調。

另外一個事情(qing)就是將eMBCurrentMode賦(fu)值(zhi)為MB_RTU, eMBState 賦(fu)值(zhi)為STATE_DISABLED;

我們需要對(dui)這些狀(zhuang)態有一些印(yin)象,因為后邊還是會用到。

下(xia)面來分析eMBEnable():eMBErrorCode

eMBEnable( void )

{

eMBErrorCode eStatus = MB_ENOERR;

if( eMBState == STATE_DISABLED )

{

/* Activate the protocol stack. */

pvMBFrameStartCur( );

eMBState = STATE_ENABLED;

}

else

{

eStatus = MB_EILLSTATE;

}

return eStatus;

}

這一段代(dai)碼呢(ni)比(bi)較少,我(wo)們來看第6行的判(pan)斷,這里eMBState 賦值為(wei)STATE_DISABLED是在(zai)

eMBInit()執行,所以執行if分支,看第9行代碼,發現并(bing)沒有這個函數,奧,,還記(ji)得他是函數指(zhi)針吧,在eMBInit()中被指(zhi)向了函數eMBRTUStart( void ),我們(men)來看下真正的(de)啟動函數原型:void

eMBRTUStart( void )

{

ENTER_CRITICAL_SECTION( );

/* Initially the receiver is in the state STATE_RX_INIT. we start

* the timer and if no character is received within t3.5 we change

* to STATE_RX_IDLE. This makes sure that we delay startup of the

* modbus protocol stack until the bus is free.

*/

//eRcvState 初始化狀態

eRcvState = STATE_RX_INIT;

//使能接收,禁止發送

vMBPortSerialEnable( TRUE, FALSE );

//啟動定(ding)時器(qi)

vMBPortTimersEnable();

EXIT_CRITICAL_SECTION( );

}

以上(shang)代碼比較簡單我就不逐一(yi)分析了(le)(le),我們需要注(zhu)意下eRcvState被賦值為 STATE_RX_INIT;還將串口接收(shou)中斷使(shi)能,關閉串口發送(song)。開(kai)(kai)啟定(ding)時(shi)器,記得哦(e),開(kai)(kai)啟了(le)(le)定(ding)時(shi)器并開(kai)(kai)啟了(le)(le)中斷,你肯定(ding)會(hui)想定(ding)時(shi)器多久后(hou)觸發中斷?我們來分析一(yi)下,首先我們找到初(chu)始(shi)化(hua)定(ding)時(shi)器的地方,eMBInit()èeMBRTUInit(),我們來看一(yi)下源碼:eMBErrorCode

eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate,

eMBParity eParity )

{

eMBErrorCode eStatus = MB_ENOERR;

ULONG usTimerT35_50us;

( void )ucSlaveAddress;

ENTER_CRITICAL_SECTION();

/* Modbus RTU uses 8 Databits. */

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

{

eStatus = MB_EPORTERR;

}

else

{

/* If baudrate > 19200 then we should use the fixed timer values

* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.

*/

//如果波特率超過19200 使用固(gu)定的時(shi)間間隔(ge),1750us

//其他情況,則要進行(xing)計算。

if( ulBaudRate > 19200 )

{

usTimerT35_50us = 35; /* 1750us. */

}

else

{

/* The timer reload value for a character is given by:

*

* ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )

* = 11 * Ticks_per_1s / Baudrate

* = 220000 / Baudrate

* The reload for t3.5 is 1.5 times this value and similary

* for t3.5.

*/

usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );

}

//初始化定時器

if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )

{

eStatus = MB_EPORTERR;

}

}

EXIT_CRITICAL_SECTION( );

return eStatus;

}

串口(kou)初始化我(wo)們(men)就不多說了(le),接下來它在算出來了(le)一個數(shu)給了(le)usTimerT35_50us,然后調用xMBPortTimersInit( ( USHORT ) usTimerT35_50us ),這是(shi)在干嘛(ma)?

首先我們(men)來詳細說下(xia)usTimerT35_50us,這(zhe)里我首先要說下(xia)modbus的(de)協議(yi)規范了:

RTU方(fang)式的(de)MODBUS如何判別(bie)(bie)一幀(zhen)數據包(bao)哪?有的(de)協(xie)議(yi)里有開始(shi)標(biao)示、結束(shu)標(biao)示,通過判別(bie)(bie)開頭標(biao)示和(he)結束(shu)標(biao)示來(lai)表(biao)示一幀(zhen)完整的(de)數據幀(zhen)。但MODBUS RTU方(fang)式的(de)數據幀(zhen)并(bing)沒有開始(shi)標(biao)示和(he)結束(shu)標(biao)示,那MODBUS RTU怎(zen)么(me)判別(bie)(bie)一幀(zhen)數據幀(zhen)的(de)哪?我們還(huan)要看(kan)MDBUS協(xie)議(yi)棧(zhan)的(de)具體規定。先給大家(jia)看(kan)一個(ge)圖:

MODBUS協議里面說一幀數(shu)據(ju)(ju)和(he)下一幀數(shu)據(ju)(ju)之間的間隔至少(shao)是3.5個(ge)字符(fu)。好了,新的問題(ti)又來了3.5個(ge)字符(fu)是多長時間,我們來看(kan)段代碼中的說明:

* If baudrate > 19200 then we should use the fixed timer values

 * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.

*/

波(bo)特率大于(yu)(yu)19200時候(hou)我(wo)(wo)們(men)(men)(men)用固定時長來判(pan)斷(duan),小于(yu)(yu)19200時我(wo)(wo)們(men)(men)(men)還是(shi)要知道(dao)3.5個(ge)(ge)(ge)字(zi)符是(shi)多久,我(wo)(wo)們(men)(men)(men)知道(dao)他和波(bo)特率有關(guan),我(wo)(wo)們(men)(men)(men)假設波(bo)特率是(shi)9600,那1s能傳9600位(wei)(wei)(wei), 一個(ge)(ge)(ge)字(zi)符是(shi)11位(wei)(wei)(wei)(8個(ge)(ge)(ge)數據位(wei)(wei)(wei),1個(ge)(ge)(ge)起始位(wei)(wei)(wei),1個(ge)(ge)(ge)停止位(wei)(wei)(wei), 1個(ge)(ge)(ge)校驗(yan)位(wei)(wei)(wei)),那T3.5就等(deng)價于(yu)(yu)1000/(9600/11)*3.5(估算為4ms)。

Freemodbus實現t3.5的方法是在定(ding)時器設(she)置一個(ge)基(ji)準(zhun)時間(jian)50us(通過設(she)置預分頻的數值TIM_Prescaler),再根(gen)據T3.5的大小來計算出計數值(TIM_Period)。好了我(wo)們(men)這(zhe)里就點到(dao)為止(zhi),我(wo)們(men)只需要將定(ding)時器配(pei)置基(ji)準(zhun)時間(jian)為50us。

誒(e^),,,我們剛到哪里(li)了(le)?

剛(gang)調用了一個(ge)鉤子函數(shu)pvMBFrameStartCur( );隨后將eMBState狀態改編為STATE_ENABLED;

到這里我(wo)(wo)們總結一(yi)下(xia),剛(gang)(gang)剛(gang)(gang)我(wo)(wo)們已經執行了兩個函數(shu),分別(bie)是(shi)eMBInit(MB_RTU, 0x09, 0x01, 9600, MB_PAR_NONE)和(he)eMBEnable(); 有兩個狀態(tai)發生(sheng)了改變(bian)(bian),eMBState狀態(tai)改變(bian)(bian)為STATE_ENABLED,eRcvState 狀態(tai)改變(bian)(bian)為 STATE_RX_INIT; 并(bing)且(qie)在大約4ms后會(hui)產生(sheng)一(yi)次定時器中(zhong)斷。

接下(xia)來我(wo)們(men)先去看(kan)下(xia)這個定時器中斷(duan)都干了什(shen)么(me)?然后再去看(kan)eMBPoll();void TIM4_IRQHandler(void)

{

if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)

{

//清除定時器T4溢出(chu)中(zhong)斷標(biao)志位

TIM_ClearITPendingBit(TIM4, TIM_IT_Update);

prvvTIMERExpiredISR( );

}

}

判斷(duan)確定發生中斷(duan)以后,又調用(yong)了一(yi)個prvvTIMERExpiredISR( )è pxMBPortCBTimerExpired()這又是(shi)一(yi)個鉤子(zi)函數,我們來(lai)看下(xia)代碼:

BOOL

xMBRTUTimerT35Expired( void )

{

BOOL xNeedPoll = FALSE;

switch ( eRcvState )

{

/* Timer t35 expired. Startup phase is finished. */

//這(zhe)是一個啟(qi)動狀態,運行到這(zhe)里說明啟(qi)動狀態完成。

case STATE_RX_INIT:

xNeedPoll = xMBPortEventPost( EV_READY );

break;

/* A frame was received and t35 expired.

* Notify the listener that

* a new frame was received. */

case STATE_RX_RCV:

//發(fa)送事件(jian),接收到完整的modbus數據

xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );

break;

/* An error occured while receiving the frame. */

case STATE_RX_ERROR:

break;

/* Function called in an illegal state. */

default:

assert( ( eRcvState == STATE_RX_INIT ) ||

( eRcvState == STATE_RX_RCV ) ||

( eRcvState == STATE_RX_ERROR ) );

}

//禁(jin)止定時(shi)器(qi)

vMBPortTimersDisable( );

//串口接收狀態(tai) 變為空閑狀態(tai)。

eRcvState = STATE_RX_IDLE;

return xNeedPoll;

}

上來是(shi)(shi)根據eRcvState的值來運行對應分(fen)支(zhi)的,eRcvState的值又是(shi)(shi)什么呢?我們之前說過,在eMBRTUStart()中eRcvState = STATE_RX_INIT;那我們就(jiu)知(zhi)道要執行哪(na)些(xie)代碼了(le),首先調(diao)用了(le) xNeedPoll = xMBPortEventPost( EV_READY ); xMBPortEventPost()就(jiu)是(shi)(shi)將事(shi)(shi)件類型賦給eQueuedEvent,并將xEventInQueue置為TRUE代表(biao)有事(shi)(shi)件發生(sheng)。BOOL

xMBPortEventPost( eMBEventType eEvent )

{

//有事件標志更新

xEventInQueue = TRUE;

//設(she)定事件標志

eQueuedEvent = eEvent;

return TRUE;

}

我們繼續回到xMBRTUTimerT35Expired(),他在將對應的(de)事(shi)件(jian)發送(song)給(gei)eQueuedEvent后關(guan)閉了(le)定時器,并將eRcvState置為STATE_RX_IDLE

下(xia)面開(kai)始分析eMBPoll();static UCHAR *ucMBFrame;

static UCHAR ucRcvAddress;

static UCHAR ucFunctionCode;

static USHORT usLength;

static eMBException eException;

int i;

eMBErrorCode eStatus = MB_ENOERR;

eMBEventType eEvent;

/* Check if the protocol stack is ready. */

//eMBEnable()==> eMBState = STATE_ENABLED;

if( eMBState != STATE_ENABLED )

{

return MB_EILLSTATE;

}

/* Check if there is a event available.

If not return control to caller.

* Otherwise we will handle the event. */

//查詢事件

if( xMBPortEventGet( &eEvent ) == TRUE )

{

switch ( eEvent )

{

case EV_READY:

break;

case EV_FRAME_RECEIVED:

//接收報(bao)文函數,傳入參數從機(ji)地(di)址,報(bao)文指針,長度

//實際上調用(yong)了(le)eMBRTUReceive 位于mbrtu.c

eStatus = peMBFrameReceiveCur( &ucRcvAddress,

&ucMBFrame, &usLength );

if( eStatus == MB_ENOERR )

{

/* Check if the frame is for us.

If not ignore the frame. */

//驗證報文從機地(di)址

if( ( ucRcvAddress == ucMBAddress ) ||

( ucRcvAddress == MB_ADDRESS_BROADCAST ) )

{

//發送事件,報文(wen)到(dao)達,可以進行處理

( void )xMBPortEventPost( EV_EXECUTE );

}

}

break;

case EV_EXECUTE:

ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];

eException = MB_EX_ILLEGAL_FUNCTION;

for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )

{

/* No more function handlers registered. Abort. */

if( xFuncHandlers[i].ucFunctionCode == 0 )

{

break;

}

else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )

{

eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );

break;

}

}

/* If the request was not sent to the broadcast address we

* return a reply. */

if( ucRcvAddress != MB_ADDRESS_BROADCAST )

{

if( eException != MB_EX_NONE )

{

/* An exception occured. Build an error frame. */

usLength = 0;

ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );

ucMBFrame[usLength++] = eException;

}

if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )

{

vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );

}

eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );

}

break;

case EV_FRAME_SENT:

break;

}

}

return MB_ENOERR;

這里代碼比較多,我們(men)逐行(xing)分析(xi)下。

13-16行檢測eMBState是否為(wei)STATE_ENABLED?之前我們在eMBEnable()已經將eMBState置為(wei)STATE_ENABLED

23-89行檢測(ce)是(shi)否有(you)事件(jian)發(fa)生,并執行事件(jian)對應(ying)的處理函數。

eMBPoll()就(jiu)是(shi)一直在檢測是(shi)否有事件(jian),有就(jiu)處(chu)理。我們先來看(kan)xMBPortEventGet( &eEvent )BOOL

xMBPortEventGet( eMBEventType * eEvent )

{

BOOL xEventHappened = FALSE;

//若(ruo)有事件更(geng)新

if( xEventInQueue )

{

//獲(huo)得事件

*eEvent = eQueuedEvent;

xEventInQueue = FALSE;

xEventHappened = TRUE;

}

return xEventHappened;

}

代碼比較簡單,就是檢測是否(fou)有事件,并(bing)將(jiang)事件回傳給(gei)掉它的函數(shu)。剛(gang)剛(gang)我們發生了一個事件,還記(ji)得嗎?在第(di)一次(ci)定時器中斷的時候 執行(xing)了這樣一句話xNeedPoll = xMBPortEventPost( EV_READY );

可惜EV_READY事(shi)件(jian)只是告訴主程序協議棧(zhan)初(chu)始化成功,并沒(mei)有做實際(ji)的事(shi)情。

好了(le),到這里我們大概將freemodbus的啟(qi)動流程(cheng)分析(xi)了(le)一下(xia),我們來總結一下(xia)。

eMBInit():配置了定時器和串口,并規定了modbus的啟動、停止(zhi)、發送以及接收數(shu)據(ju)的函數(shu)具體(ti)形式。

eMBEnable():打開定(ding)時(shi)器并開啟中斷, 使能(neng)串口接收并開啟中斷。

eMBPoll():檢測(ce)是(shi)否有(you)事件發(fa)生,并處(chu)理對應的事件,包括接(jie)收到完(wan)整(zheng)數據(ju)幀(zhen)的事件,處(chu)理數據(ju)幀(zhen)的事件。

協議(yi)棧剩(sheng)下(xia)的工作就(jiu)是接(jie)收串口(kou)的數據并(bing)進行分析處理,并(bing)會通過(guo)串口(kou)返(fan)回對應(ying)的回應(ying)幀。我們將會在下(xia)面(mian)分析modbus RTU模式的接(jie)收發送機制分析。

上一篇:init可執行程序的結構

下一篇:TI藍牙4.0協議棧main函數分析

熱(re)點文章(zhang)推薦
華清學員就業榜單
高(gao)薪學員(yuan)經驗分享
熱(re)點新(xin)聞(wen)推薦
前(qian)臺專線:010-82525158 企(qi)業培訓(xun)洽談(tan)專線:010-82525379 院校合作洽談(tan)專線(xian):010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,,京公海網安備11010802025203號

回到頂部