色偷偷偷亚洲综合网另类,亚洲欧美另类在线观看,欧美午夜激情在线,久久久精品一区

當(dāng)前位置:首頁(yè) > 學(xué)習(xí)資源 > 講師博文 > Freemodbus啟動(dòng)流程分析

Freemodbus啟動(dòng)流程分析 時(shí)間:2018-09-25      來源:華清遠(yuǎn)見

近項(xiàng)目有用到modbus協(xié)議,于是在網(wǎng)上找了些資料成功將freemodbus移植到m3,由于移植過程較簡(jiǎn)單,網(wǎng)上教程也很多,這里我們就不再贅述.我用到的freemodbus版本是V1.5,下面附上新的源碼下載地址:http://www.freemodbus.org/index.php?idx=5

下面開始分析下freemodbus得啟動(dòng)流程,老規(guī)矩我們還是從main()函數(shù)下手:

和freemodbus有關(guān)的函數(shù)只有三個(gè)eMBInit(), eMBEnable(), eMBPoll().我們逐一來分析.

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

eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort,

ULONG ulBaudRate, eMBParity eParity )

{

//錯(cuò)誤狀態(tài)初始值

eMBErrorCode eStatus = MB_ENOERR;

//驗(yàn)證從機(jī)地址

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;

//報(bào)文接收函數(shù)

peMBFrameReceiveCur = eMBRTUReceive;

pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;

//接收狀態(tài)機(jī)

pxMBFrameCBByteReceived = xMBRTUReceiveFSM;

//發(fā)送狀態(tài)機(jī)

pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;

//報(bào)文到達(dá)間隔檢查

pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;

//初始化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

{

//設(shè)定當(dāng)前狀態(tài)

eMBCurrentMode = eMode;

eMBState = STATE_DISABLED;

}

}

}

return eStatus;

}

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

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

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

MB_RTU:使用的是modbusRTU模式.

0x09:從機(jī)得地址

0x01:串口號(hào),這里我們使用的是串口1

9600:波特率

MB_PAR_NONE:無校驗(yàn)和

這個(gè)函數(shù)的任務(wù)就是根據(jù)你的參數(shù)來配置串口,定時(shí)器和modbus中的ID號(hào)這個(gè)函數(shù)另外一個(gè)重要的工作就是將一些全局指針變量指向?qū)?yīng)得函數(shù),將來方便進(jìn)行回調(diào)。

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

我們需要對(duì)這些狀態(tài)有一些印象,因?yàn)楹筮呥是會(huì)用到。

下面來分析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;

}

這一段代碼呢比較少,我們來看第6行的判斷,這里eMBState 賦值為STATE_DISABLED是在

eMBInit()執(zhí)行,所以執(zhí)行if分支,看第9行代碼,發(fā)現(xiàn)并沒有這個(gè)函數(shù),奧,,還記得他是函數(shù)指針吧,在eMBInit()中被指向了函數(shù)eMBRTUStart( void ),我們來看下真正的啟動(dòng)函數(shù)原型: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 初始化狀態(tài)

eRcvState = STATE_RX_INIT;

//使能接收,禁止發(fā)送

vMBPortSerialEnable( TRUE, FALSE );

//啟動(dòng)定時(shí)器

vMBPortTimersEnable();

EXIT_CRITICAL_SECTION( );

}

以上代碼比較簡(jiǎn)單我就不逐一分析了,我們需要注意下eRcvState被賦值為 STATE_RX_INIT;還將串口接收中斷使能,關(guān)閉串口發(fā)送。開啟定時(shí)器,記得哦,開啟了定時(shí)器并開啟了中斷,你肯定會(huì)想定時(shí)器多久后觸發(fā)中斷?我們來分析一下,首先我們找到初始化定時(shí)器的地方,eMBInit()èeMBRTUInit(),我們來看一下源碼: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 使用固定的時(shí)間間隔,1750us

//其他情況,則要進(jìn)行計(jì)算。

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 );

}

//初始化定時(shí)器

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

{

eStatus = MB_EPORTERR;

}

}

EXIT_CRITICAL_SECTION( );

return eStatus;

}

串口初始化我們就不多說了,接下來它在算出來了一個(gè)數(shù)給了usTimerT35_50us,然后調(diào)用xMBPortTimersInit( ( USHORT ) usTimerT35_50us ),這是在干嘛?

首先我們來詳細(xì)說下usTimerT35_50us,這里我首先要說下modbus的協(xié)議規(guī)范了:

RTU方式的MODBUS如何判別一幀數(shù)據(jù)包哪?有的協(xié)議里有開始標(biāo)示、結(jié)束標(biāo)示,通過判別開頭標(biāo)示和結(jié)束標(biāo)示來表示一幀完整的數(shù)據(jù)幀。但MODBUS RTU方式的數(shù)據(jù)幀并沒有開始標(biāo)示和結(jié)束標(biāo)示,那MODBUS RTU怎么判別一幀數(shù)據(jù)幀的哪?我們還要看MDBUS協(xié)議棧的具體規(guī)定。先給大家看一個(gè)圖:

MODBUS協(xié)議里面說一幀數(shù)據(jù)和下一幀數(shù)據(jù)之間的間隔至少是3.5個(gè)字符。好了,新的問題又來了3.5個(gè)字符是多長(zhǎng)時(shí)間,我們來看段代碼中的說明:

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

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

*/

波特率大于19200時(shí)候我們用固定時(shí)長(zhǎng)來判斷,小于19200時(shí)我們還是要知道3.5個(gè)字符是多久,我們知道他和波特率有關(guān),我們假設(shè)波特率是9600,那1s能傳9600位, 一個(gè)字符是11位(8個(gè)數(shù)據(jù)位,1個(gè)起始位,1個(gè)停止位, 1個(gè)校驗(yàn)位),那T3.5就等價(jià)于1000/(9600/11)*3.5(估算為4ms)。

Freemodbus實(shí)現(xiàn)t3.5的方法是在定時(shí)器設(shè)置一個(gè)基準(zhǔn)時(shí)間50us(通過設(shè)置預(yù)分頻的數(shù)值TIM_Prescaler),再根據(jù)T3.5的大小來計(jì)算出計(jì)數(shù)值(TIM_Period)。好了我們這里就點(diǎn)到為止,我們只需要將定時(shí)器配置基準(zhǔn)時(shí)間為50us。

誒,,,我們剛到哪里了?

剛調(diào)用了一個(gè)鉤子函數(shù)pvMBFrameStartCur( );隨后將eMBState狀態(tài)改編為STATE_ENABLED;

到這里我們總結(jié)一下,剛剛我們已經(jīng)執(zhí)行了兩個(gè)函數(shù),分別是eMBInit(MB_RTU, 0x09, 0x01, 9600, MB_PAR_NONE)和eMBEnable(); 有兩個(gè)狀態(tài)發(fā)生了改變,eMBState狀態(tài)改變?yōu)镾TATE_ENABLED,eRcvState 狀態(tài)改變?yōu)?STATE_RX_INIT; 并且在大約4ms后會(huì)產(chǎn)生一次定時(shí)器中斷。

接下來我們先去看下這個(gè)定時(shí)器中斷都干了什么?然后再去看eMBPoll();void TIM4_IRQHandler(void)

{

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

{

//清除定時(shí)器T4溢出中斷標(biāo)志位

TIM_ClearITPendingBit(TIM4, TIM_IT_Update);

prvvTIMERExpiredISR( );

}

}

判斷確定發(fā)生中斷以后,又調(diào)用了一個(gè)prvvTIMERExpiredISR( )è pxMBPortCBTimerExpired()這又是一個(gè)鉤子函數(shù),我們來看下代碼:

BOOL

xMBRTUTimerT35Expired( void )

{

BOOL xNeedPoll = FALSE;

switch ( eRcvState )

{

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

//這是一個(gè)啟動(dòng)狀態(tài),運(yùn)行到這里說明啟動(dòng)狀態(tài)完成。

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:

//發(fā)送事件,接收到完整的modbus數(shù)據(jù)

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 ) );

}

//禁止定時(shí)器

vMBPortTimersDisable( );

//串口接收狀態(tài) 變?yōu)榭臻e狀態(tài)。

eRcvState = STATE_RX_IDLE;

return xNeedPoll;

}

上來是根據(jù)eRcvState的值來運(yùn)行對(duì)應(yīng)分支的,eRcvState的值又是什么呢?我們之前說過,在eMBRTUStart()中eRcvState = STATE_RX_INIT;那我們就知道要執(zhí)行哪些代碼了,首先調(diào)用了 xNeedPoll = xMBPortEventPost( EV_READY ); xMBPortEventPost()就是將事件類型賦給eQueuedEvent,并將xEventInQueue置為TRUE代表有事件發(fā)生。BOOL

xMBPortEventPost( eMBEventType eEvent )

{

//有事件標(biāo)志更新

xEventInQueue = TRUE;

//設(shè)定事件標(biāo)志

eQueuedEvent = eEvent;

return TRUE;

}

我們繼續(xù)回到xMBRTUTimerT35Expired(),他在將對(duì)應(yīng)的事件發(fā)送給eQueuedEvent后關(guān)閉了定時(shí)器,并將eRcvState置為STATE_RX_IDLE

下面開始分析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:

//接收?qǐng)?bào)文函數(shù),傳入?yún)?shù)從機(jī)地址,報(bào)文指針,長(zhǎng)度

//實(shí)際上調(diào)用了eMBRTUReceive 位于mbrtu.c

eStatus = peMBFrameReceiveCur( &ucRcvAddress,

&ucMBFrame, &usLength );

if( eStatus == MB_ENOERR )

{

/* Check if the frame is for us.

If not ignore the frame. */

//驗(yàn)證報(bào)文從機(jī)地址

if( ( ucRcvAddress == ucMBAddress ) ||

( ucRcvAddress == MB_ADDRESS_BROADCAST ) )

{

//發(fā)送事件,報(bào)文到達(dá),可以進(jìn)行處理

( 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;

這里代碼比較多,我們逐行分析下。

13-16行檢測(cè)eMBState是否為STATE_ENABLED?之前我們?cè)趀MBEnable()已經(jīng)將eMBState置為STATE_ENABLED

23-89行檢測(cè)是否有事件發(fā)生,并執(zhí)行事件對(duì)應(yīng)的處理函數(shù)。

eMBPoll()就是一直在檢測(cè)是否有事件,有就處理。我們先來看xMBPortEventGet( &eEvent )BOOL

xMBPortEventGet( eMBEventType * eEvent )

{

BOOL xEventHappened = FALSE;

//若有事件更新

if( xEventInQueue )

{

//獲得事件

*eEvent = eQueuedEvent;

xEventInQueue = FALSE;

xEventHappened = TRUE;

}

return xEventHappened;

}

代碼比較簡(jiǎn)單,就是檢測(cè)是否有事件,并將事件回傳給掉它的函數(shù)。剛剛我們發(fā)生了一個(gè)事件,還記得嗎?在第一次定時(shí)器中斷的時(shí)候 執(zhí)行了這樣一句話xNeedPoll = xMBPortEventPost( EV_READY );

可惜EV_READY事件只是告訴主程序協(xié)議棧初始化成功,并沒有做實(shí)際的事情。

好了,到這里我們大概將freemodbus的啟動(dòng)流程分析了一下,我們來總結(jié)一下。

eMBInit():配置了定時(shí)器和串口,并規(guī)定了modbus的啟動(dòng)、停止、發(fā)送以及接收數(shù)據(jù)的函數(shù)具體形式。

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

eMBPoll():檢測(cè)是否有事件發(fā)生,并處理對(duì)應(yīng)的事件,包括接收到完整數(shù)據(jù)幀的事件,處理數(shù)據(jù)幀的事件。

協(xié)議棧剩下的工作就是接收串口的數(shù)據(jù)并進(jìn)行分析處理,并會(huì)通過串口返回對(duì)應(yīng)的回應(yīng)幀。我們將會(huì)在下面分析modbus RTU模式的接收發(fā)送機(jī)制分析。

上一篇:init可執(zhí)行程序的結(jié)構(gòu)

下一篇:TI藍(lán)牙4.0協(xié)議棧main函數(shù)分析

戳我查看嵌入式每月就業(yè)風(fēng)云榜

點(diǎn)我了解華清遠(yuǎn)見高校學(xué)霸學(xué)習(xí)秘籍

猜你關(guān)心企業(yè)是如何評(píng)價(jià)華清學(xué)員的

干貨分享
相關(guān)新聞
前臺(tái)專線:010-82525158 企業(yè)培訓(xùn)洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2024 北京華清遠(yuǎn)見科技發(fā)展有限公司 版權(quán)所有 ,京ICP備16055225號(hào)-5京公海網(wǎng)安備11010802025203號(hào)

回到頂部

色偷偷偷亚洲综合网另类,亚洲欧美另类在线观看,欧美午夜激情在线,久久久精品一区
主站蜘蛛池模板: 91精品久久久久久久久久| 久久免费视频观看| 欧美黑人xxxx| 国产成人在线亚洲欧美| 亚洲精品成人久久电影| 日韩中文字幕在线视频播放| 久久国产精品影视| 国模精品系列视频| 中文字幕日韩欧美| 7m精品福利视频导航| 91中文字幕一区| 伊人青青综合网站| 欧美性猛交xxxx乱大交极品| 国产成人激情视频| 久久91亚洲精品中文字幕| 久久久久久国产精品久久| 亚洲护士老师的毛茸茸最新章节| 2019精品视频| 久久天天躁狠狠躁老女人| 国产精品91免费在线| 欧美日韩美女在线观看| 91精品国产综合久久香蕉| 久久久久久久久亚洲| 久久精品国产亚洲一区二区| 国产91ⅴ在线精品免费观看| 亚洲精品午夜精品| 日韩欧美国产网站| 最近中文字幕日韩精品 | 91在线精品播放| 久久国产精品久久精品| 亚洲欧美日韩一区二区三区在线| 亚洲91av视频| 亚洲天堂网站在线观看视频| 欧美日韩中文字幕| 日韩日本欧美亚洲| 亚洲网址你懂得| 亚洲精品成人免费| 亚洲tv在线观看| 国产精品普通话| 欧美超级免费视 在线| 亚洲人精选亚洲人成在线|