搜索
bottom↓
回复: 19

开源PLC学习笔记16续(FREEMODBUS eMBPoll侦听)——2013_12_8

[复制链接]

出0入0汤圆

发表于 2013-12-8 13:17:39 | 显示全部楼层 |阅读模式
本帖最后由 oldbeginner 于 2013-12-8 20:25 编辑

FreeModbus果然不是免费的,要想理解它就要花很多精力。看了这么多,基本上明白,如果应用中,只是用modbus控制几个线圈或寄存器,还是不要用freemodbus,用笔记15中的例子就非常简单和实用。举一反三,如果觉得论文写得一目了然,导师看不上眼,那么控制几个线圈时就使用freemodbus。


笔记16中使用的两个例子目前看来不适合入门,现在改为下面这个例子,avr mega128可以利用PROTEUS和modbus poll进行通信。
http://www.cnblogs.com/worldsing/category/502885.html


这个例子改得比较简练,和笔记16中我想做的差不多,这样就很省力气和时间,直接理解这个例子作为入门还是不错的。



理解FREEMODBUS的突破口就是eMBPoll函数了

http://www.cnblogs.com/worldsing/category/502885.html
直接摘录
eMbPoll()的作用是FreeMod协议通信过程中不断查询事件对列有无完速数据桢,并进行地址和CRC验证,最后运行和回复主机。

改进说明:

1、eMbPoll()调用一次即可运行功能码和回复主机;

2、省去独立的接收函数peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); 而直接操作,(其实里面对算出数据桢的启始位置、和长度);

3、省去发送函数peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); 而直接操作;

4、省去返回值,因为调用处没有使用;

5、对功能的遍历i改成unsigned char类型,省去ucRcvAddress和eMBErrorCode    eStatus = MB_ENOERR; 变量,

6、功能兼容原版本。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-12-8 14:18:51 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-12-8 19:32 编辑

void eMBPoll( void )
{
        变量定义();

        桢事件判断();


                最小桢判断();

                CRC判断();

                地址判断();


                        执行功能码();

                        回复主机();


                                错误码判断();
        
                                报文发送();

}

*******************************
    变量定义();
  static UCHAR   *ucMBFrame;
  static UCHAR    ucFunctionCode;
  static USHORT   usLength;
  static eMBException eException;
  eMBEventType    eEvent;
  UCHAR i;
  USHORT usCRC16;

变量名字前面加uc的意思应该是,uchar变量,MB表示modbus,所以ucMBFrame表示 uchar变量的modbus帧,前面加*,是一个指针。

ucFunctionCode应该是uchar变量的功能码,应该类似散转函数,只不过freemodbus使用了大量#ifdef .....#endif等,各种标志位来回绕几圈,现在还不清楚怎么调用。

usLength应该是ushort变量的帧长度。

static eMBException eException(ModBusRTU.h);
需要查看eMBException的定义,
    typedef enum
{
    MB_EX_NONE = 0x00,
    MB_EX_ILLEGAL_FUNCTION = 0x01,
    MB_EX_ILLEGAL_DATA_ADDRESS = 0x02,
    MB_EX_ILLEGAL_DATA_VALUE = 0x03,
    MB_EX_SLAVE_DEVICE_FAILURE = 0x04,
    MB_EX_ACKNOWLEDGE = 0x05,
    MB_EX_SLAVE_BUSY = 0x06,
    MB_EX_MEMORY_PARITY_ERROR = 0x08,
    MB_EX_GATEWAY_PATH_FAILED = 0x0A,
    MB_EX_GATEWAY_TGT_FAILED = 0x0B
} eMBException;

  e表示enum,MB同上,Exception表示异常状况,共有10种异常状况,暂不展开(原程序也没有解释,估计不太重要)。

上面四种变量都是static型,只能用在ModBusRTU.c中。

然后,
eMBEventType    eEvent;
查看定义(ModBusRTU.h),
typedef enum
{
    EV_READY,                   /*!< Startup finished. */
    EV_FRAME_RECEIVED,          /*!< Frame received. */
    EV_EXECUTE,                 /*!< Execute function. */
    EV_FRAME_SENT               /*!< Frame sent. */
} eMBEventType;

这个变量好理解,可以用来表示状态,共有4种状态。

  UCHAR i;
执行功能代码时,要用到的循环变量。

  USHORT usCRC16;
ushort类型的检验码。
      
在avr中 USHORT (unsigned short) 是16位的。
*****************************************************

桢事件判断();
if(xMBPortEventGet( &eEvent) == TRUE ){    
      if(eEvent == EV_FRAME_RECEIVED){

调用了函数

xMBPortEventGet
查看一下定义(ModBusPort.c),
//出队列
BOOL xMBPortEventGet( eMBEventType * eEvent ){
  
    BOOL xEventHappened = FALSE;
    if( xEventInQueue ){
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
        xEventHappened = TRUE;
    }
    return xEventHappened;
}

另外还要查看
static eMBEventType eQueuedEvent;
static BOOL     xEventInQueue;


首先理解
xEventInQueue,x表示返回类型布尔值,直译:队列中的事件。如果它为真,则函数返回值为真。


xEventInQueue赋值的函数有两个,
//对列初始化
BOOL xMBPortEventInit( void )
{
    xEventInQueue = FALSE;
    return TRUE;
}

//进入队列
BOOL xMBPortEventPost( eMBEventType eEvent )
{
    xEventInQueue = TRUE;
    eQueuedEvent = eEvent;
    return TRUE;
}

因为初始化只一次,所以表示进入队列的函数在某个地方应该被调用了,否则出队列返回值一直为false。

再看
xMBPortEventGet中的

*eEvent = eQueuedEvent;

eQueuedEvent是eMBEventType类型,直译:队列中的事件。

//进入队列
BOOL xMBPortEventPost( eMBEventType eEvent ){
    xEventInQueue = TRUE;
    eQueuedEvent = eEvent;
    return TRUE;
}


当从事件队列中提取的事件为EV_FRAME_RECEIVED时,表示接收帧状态,就会继续执行。
if(eEvent == EV_FRAME_RECEIVED),即

eQueuedEvent == EV_FRAME_RECEIVED


********************************************************************




出0入0汤圆

 楼主| 发表于 2013-12-8 15:18:08 | 显示全部楼层
oldbeginner 发表于 2013-12-8 14:18
void eMBPoll( void )
{
        变量定义();

最小桢判断();

      if(usRcvBufferPos < MB_SER_PDU_SIZE_MIN)                        //最小桢判断
        return;

首先查看usRcvBufferPos 定义(ModBusRTU.c),
static volatile USHORT usRcvBufferPos;

变量定义中含有volatile,就应该马上想到该变量是多任务共享的(笔记12)或是中断修改的标志位。
us表示ushort变量,Receive Buffer Position,直译:接收缓存位置(接收数组下标),是用来定位接收的缓存。

另外
#define MB_SER_PDU_SIZE_MIN     4       /*!< Minimum size of a Modbus RTU frame. */

帧最小长度为何是4,地址,命令和CRC校验,正好4个字节。

所以最小帧判断, if ( usRcvBufferPos < 4 ) return;

********************************************************************

CRC判断();

      if(usMBCRC16((UCHAR *)ucRTUBuf, usRcvBufferPos ) != 0)          //CRC判断
        return;

调用了函数usMBCRC16 (ModBusRTU.c),查看一下

USHORT usMBCRC16( UCHAR * pucFrame, USHORT usLen )
{
    UCHAR           ucCRCHi = 0xFF;
    UCHAR           ucCRCLo = 0xFF;
    USHORT          iIndex;

    while( usLen-- ){
        iIndex = ucCRCLo ^ *( pucFrame++ );
        ucCRCLo = ( UCHAR )( ucCRCHi ^ aucCRCHi[iIndex] );
        ucCRCHi = aucCRCLo[iIndex];
    }
    return ( USHORT )( ucCRCHi << 8 | ucCRCLo );
}

和以前处理CRC函数一样,不再展开,只使用。
暂时只需要知道该函数判断是否CRC正确,用到了两个参数,一个是刚理解过的usRcvBufferPos ,另一个是ucRTUBuf,
找到定义,
volatile UCHAR  ucRTUBuf[MB_SER_PDU_SIZE_MAX];
其中,
#define MB_SER_PDU_SIZE_MAX     256     /*!< Maximum size of a Modbus RTU frame. */

即volatile UCHAR ucRTUBuf[256];
就是接收缓存(数组)

******************************************************************************

地址判断();

      if(IS_VALID_ADD){                                               //地址
        
        ucMBFrame = (UCHAR *) &ucRTUBuf[MB_SER_PDU_PDU_OFF];
        
        usLength = (USHORT)( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC);

        ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];

        eException = MB_EX_ILLEGAL_FUNCTION;

有点长,主要是名称太长了,这样的长名字会让我想到安娜·阿尔卡迪耶夫娜·卡列尼娜;不太熟悉,作者是列夫·尼古拉耶维奇·托尔斯泰

IS_VALID_ADD是一个宏,
查看定义,
#define IS_VALID_ADD      ((ucRTUBuf[MB_SER_PDU_ADDR_OFF] == ucMBAddress) ||      \
                           (ucRTUBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST))

#define MB_SER_PDU_ADDR_OFF     0       /*!< Offset of slave address in Ser-PDU. */

ucRTUBuf[0] == ucMBAddress 或 ucRTUBuf[0] == MB_ADDRESS_BROADCAST
现在大致可以看出,就是判断从机地址,或者广播地址
ucRTUBuf[0] == 从机地址 或 ucRTUBuf[0] == 广播地址

从机地址在ModBusRTU.c中定义,
static UCHAR    ucMBAddress;
在主函数中被代入数值,

上图表示从机设备地址为0x01。

广播地址
#define MB_ADDRESS_BROADCAST    ( 0 )   /*! Modbus broadcast address. */


ucRTUBuf[0] == 1 或 ucRTUBuf[0] == 0
就是有效地址

ucMBFrame = (UCHAR *) &ucRTUBuf[MB_SER_PDU_PDU_OFF];
其中
#define MB_SER_PDU_PDU_OFF      1       /*!< Offset of Modbus-PDU in Ser-PDU. */

ucMBFrame = (UCHAR *) &ucRTUBuf[1];
应该是
ucMBFrame 指向 命令码;

usLength = (USHORT)( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC);
其中,
#define MB_SER_PDU_SIZE_CRC     2       /*!< Size of CRC field in PDU. */
即,
usLength = (USHORT)( usRcvBufferPos - 1 -2 )


ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
其中,
#define MB_PDU_FUNC_OFF     0   /*!< Offset of function code in PDU. */
即,
ucFunctionCode = ucMBFrame[0];
即,
ucFunctionCode = 命令码,
真绕

eException = MB_EX_ILLEGAL_FUNCTION;
赋值。

************************************************************************************

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-12-8 15:49:28 | 显示全部楼层
支持

出0入0汤圆

 楼主| 发表于 2013-12-8 16:05:52 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-12-8 16:29 编辑
oldbeginner 发表于 2013-12-8 15:18
最小桢判断();

      if(usRcvBufferPos < MB_SER_PDU_SIZE_MIN)                        //最小桢判 ...

执行功能码();

        for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ){                  //执行功能码
          if( xFuncHandlers.ucFunctionCode == 0 ){
            return;
          }
          else if( xFuncHandlers.ucFunctionCode == ucFunctionCode ){
            eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength );
            break;                              
          }
      }

其中ModBusConfig.h定义了
#define MB_FUNC_HANDLERS_MAX                    ( 16 )//使用的功能码数量
即,
for(i = 0; i < 16 ; i++ ) {

再看xFuncHandlers是什么?它的生成方法,我是第一次看到,为了简短,这里只列出2种#if情况
先看定义
typedef struct
{
    UCHAR           ucFunctionCode;
    pxMBFunctionHandler pxHandler;
} xMBFunctionHandler;
其中,
typedef  eMBException(*pxMBFunctionHandler) (UCHAR* pucFrame, USHORT * pusLength );
第二个成员变量是执行异常处理的指针。

再看该结构数组的生成,
/* 定义功能码与功能处理函数列表 */
static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
#if MB_FUNC_READ_INPUT_ENABLED > 0
    {MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
    {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
#endif
};

先看第一个#if
#define MB_FUNC_READ_INPUT_ENABLED              (  1 ) //读输入寄存器功能
这样的话,
#if总是成立,
{MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister}就会成为数组成员,

其中,
#define MB_FUNC_READ_INPUT_REGISTER           (  4 )
在ModBusFun.c中,
eMBException eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
eMBFuncReadInputRegister是一个函数,暂不展开。
功    能: 0x04(4) 读取输入寄存器。

xMBFunctionHandler 数组的成员变量是一个结构体,结构体的第一个变量是命令码,后一个变量是命令码对应的函数名。

这样看来,应该和开源PLC的函数散转同一功能,感觉开源PLC的写法更好理解。

****************************
          if( xFuncHandlers.ucFunctionCode == 0 ){
            return;
          }

如果第一个变量对应的命令码是0,则不执行。

**********************************
          else if( xFuncHandlers.ucFunctionCode == ucFunctionCode ){
            eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength );
            break;                              
          }


再回顾一下,
ucFunctionCode = ucMBFrame[0];
而 ucMBFrame = (UCHAR *) &ucRTUBuf[1];

即,
xFuncHandlers.ucFunctionCode == ucRTUBuf[1];
即,
接收缓存的命令码和功能码数组第一个变量名一样的话,就调用
xFuncHandlers.pxHandler( ucMBFrame, &usLength );
pxHandle是第二个变量名,代入时换成实际的函数名称,例如
eMBFuncReadInputRegister( ucMBFrame, &usLength );
这样就完成了函数调用。

(太绕了,看了几天才第一次理解到散转函数,可读性较差,真心怀疑FREEMODBUS是好程序还是坏程序)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-12-8 17:23:13 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-12-8 19:33 编辑
oldbeginner 发表于 2013-12-8 16:05
执行功能码();

        for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ){                  //执行功能码 ...

回复主机();

if(IS_NOT_BROADCAST){
查看定义
#define IS_NOT_BROADCAST  (ucRTUBuf[MB_SER_PDU_ADDR_OFF] != MB_ADDRESS_BROADCAST)

#define IS_NOT_BROADCAST  (ucRTUBuf[0] != 0)
只要地址不是0。

********************************************************

错误码处理();

        if( eException != MB_EX_NONE ){                            //错误码         
          usLength = 0;
          ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
          ucMBFrame[usLength++] = eException;
        }
首先,
eException是执行功能后的返回值,
eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength );
如果返回值是MB_EX_NONE,表示正常。

否则修改ucMBFrame,
          ucMBFrame[0] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
          ucMBFrame[1] = eException;


***********************************************

报文发送();


        if(eRcvState == STATE_RX_IDLE){                            //发送
          pucSndBufferCur = ( UCHAR * ) ucMBFrame - 1;
          pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucMBAddress;
          usSndBufferCount = usLength + 1;      
          usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
          ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
          ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );   
          eSndState = STATE_TX_XMIT;
          vMBPortSerialEnable( FALSE, TRUE );
          }

其中,
extern volatile eMBRcvState eRcvState;
接收状态标志位,
利用笔记16中的状态图来理解


只有当eRcvState处于 STATE_RX_IDLE 状态时,才能发送。

发送的函数比较好理解,就是生成报文并发送。

首先,
static volatile UCHAR *pucSndBufferCur;
p表示指针,uc表示uchar,Send Buffer Current ,直译就是发送数组目前下标。

          pucSndBufferCur = ( UCHAR * ) ucMBFrame - 1;
因为ucMBFram指向功能码,-1就是指向地址,即 pucSndBufferCur指向ucBufRTU。

          pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucMBAddress;
          usSndBufferCount = usLength + 1;      
          usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
          ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
          ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );   
生成报文,和笔记15类似。

         eSndState = STATE_TX_XMIT;
设定发送状态,


最后调用 vMBPortSerialEnable( FALSE, TRUE );
查看,
/* ------------------------------- 串口操作 -----------------------------------*/
//串口收发控制
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ){
  
    ENTER_CRITICAL_SECTION();
    if( xRxEnable ){
        USART_RX_ENABLE();
        RS485SWITCH_TO_RECEIVE();
    }
    else{
       USART_RX_DISABLE();
       RS485SWITCH_TO_SEND();
    }
    if( xTxEnable ){
       USART_TX_ENABLE();
    }
    else{
       USART_TX_DISABLE();
    }
    EXIT_CRITICAL_SECTION();
}
在笔记16中专门理解过,复习一下
执行的是
if( xTxEnable ){
       USART_TX_ENABLE();

再查看
#define USART_TX_ENABLE()   SetBit(UCSR1B, UDRIE1)
其中,
#define SetBit(port, bitn)           (port |=  (1<<(bitn)))  

************************************
发送应该是通过串口中断进行的,
//发送中断子程序  
#pragma vector = USART1_UDRE_vect
__interrupt void TX1_isr( void ){
  
  xMBRTUTransmitFSM();
}  

IAR中定义中断函数的格式是
/////////////////////////////////
#pragma vector=中断向量
__interrupt void 中断服务程序(void)
{
//中断处理程序
}
********************************************

//发送状态机由发送中断调用
void xMBRTUTransmitFSM( void )
{
    assert( eRcvState == STATE_RX_IDLE );
    switch ( eSndState )
    {
    case STATE_TX_IDLE:
        vMBPortSerialEnable( TRUE, FALSE );
        break;
    case STATE_TX_XMIT:
        if( usSndBufferCount != 0 ){
            xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
            pucSndBufferCur++;  
            usSndBufferCount--;
        }
        else{
            xMBPortEventPost( EV_FRAME_SENT );
            vMBPortSerialEnable( TRUE, FALSE );
            eSndState = STATE_TX_IDLE;
        }
        break;
    }
}

关注
    case STATE_TX_XMIT:
        if( usSndBufferCount != 0 ){
            xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
            pucSndBufferCur++;  
            usSndBufferCount--;
        }
因为设置了标志位STATE_TX_XMIT,调用了函数xMBPortSerialPutByte,直到待发送数为0 。

其中,
#define xMBPortSerialPutByte(ucByte)  ((UART_DR) = (ucByte))   

这样就完成了eMBPoll的第一遍理解。
***************************

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-12-8 17:52:15 | 显示全部楼层

出0入0汤圆

发表于 2013-12-8 18:06:44 | 显示全部楼层
来膜拜楼主~

出0入0汤圆

 楼主| 发表于 2013-12-8 22:42:01 | 显示全部楼层
本帖最后由 oldbeginner 于 2013-12-8 22:56 编辑
oldbeginner 发表于 2013-12-8 17:23
回复主机();

if(IS_NOT_BROADCAST){








本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-12-9 12:46:17 | 显示全部楼层

第二遍理解eMBPoll









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-12-9 12:54:12 | 显示全部楼层
有意思,顶一个

出0入0汤圆

 楼主| 发表于 2013-12-9 15:55:06 | 显示全部楼层

复习发送报文的过程




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-12-9 16:46:49 | 显示全部楼层
oldbeginner 发表于 2013-12-8 16:05
执行功能码();

        for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ){                  //执行功能码 ...

调度器,感觉好理解。
http://www.amobbs.com/thread-3757688-1-1.html

直接摘录
所有的C函数都可以看作是状态机,哪怕是一个最简单的函数,也可以看成是一个只有一个状态的
状态机。因此,最简单的状态机原型可以是大家常用的:
void FSMExample(void);

为了方便控制状态机的启动和关闭,我修改了最基本的原型:
BOOL FSMExample(void);

这就是最简单的双态状态机:
当函数返回TRUE,表明该状态机仍然“希望”处于运行状态;
当函数返回FALSE,表明该状态机已经完成,希望终止。

根据这一规定,如果通过函数指针把所有的状态机连接起来,就可以形成下面的简单调度器:
typedef struct Process
{
    BOOL (*Proc)(void);                                 //返回False,自动关闭任务
    volatile BOOL IfProcAlive;
}PROCESS;

typedef BOOL (*PROC_FUNCTION)(void);

void Process_Task(void)
{
    static uint8 n = 0;
   
    if (ProcPCB[n].IfProcAlive)                             //处理进程
    {
        ProcPCB[n].IfProcAlive = (*ProcPCB[n].Proc)();
    }        
   
    n ++;
    if (n >= g_cCOSPROCCounter)
    {
        n = 0;
        //g_cScheduleTest = MIN(g_cScheduleTest + 1,254);
    }
}

这个调度器很简单,就是检测一个注册了的顶层状态机其Alive状态是否为TRUE,
如果为TRUE,就调用;同时在调用后将状态机的返回值重新赋给ALive属性,这样
状态机就可以通过在函数中返回TRUE或者FALSE来控制自己的运行状态。

出0入0汤圆

 楼主| 发表于 2013-12-10 16:05:38 | 显示全部楼层
oldbeginner 发表于 2013-12-9 15:55
复习发送报文的过程

进行了很多尝试,终于理解FREEMODBUS大概思路。

理解freemodbus后,虽然只是刚学习单片机几个月,但感觉freemodbus滥用状态机(比如最关键状态是完整的帧收到了,然后再开始处理数据并反馈主机,顺序执行好好的,非要假设主机在从机发送的时候又送来命令;这种处理应该放在主机上,而不是从机的职责),把整体结构搞得复杂难懂,明明不是操作系统,非得往上靠。

从机就应该把重点放在从机的职责上(接收命令执行,然后反馈),而不是老想着主机发命令超过我的处理能力怎么办,这种想一劳永逸的从机不仅让自己的结构复杂了好多倍,导致犯错的机会也增加好多倍,本来想节约时间的通用程序反而成了麻烦制造机。

对Freemodbus的反思:

1、有很多人移植,而且热烈讨论,并没有怀疑它本身定位的问题,让我一开始误认为FREEMODBUS是一贴灵药。

2、Freemodbus的定位问题,它适合谁使用?适合哪种情况下使用?目前根本找不到相关信息。我的理解是,Freemodbus定位不准确,完全是按照主机的方式在处理。

3、很多不必要的信息,增加了复杂度。比如Errorcode问题,因为modbus协议很成熟,从机不厌其烦地判断是地址错误还是什么类型错误的?都有MBPoll这类软件帮助调试,从机完全没有必要增加这类信息。

4、最关键的状态就是字符串接收完毕,这点在程序中并没有体现,反而搞出了很多状态和它并列,而其它状态根本不是从机应该关注的,这些状态要用主机来解决。

5、这种大而全,有点向操作系统靠拢,不适合具体的实际应用。

出0入0汤圆

发表于 2013-12-17 12:57:32 | 显示全部楼层
free modbus 划分的文件比较多   目的是为了支持各种传输方式      用了接口的概念  
在51里面使用的确比较蛋疼         
可以把不相关的功能都去掉        整合起来看    实时性很不错的

出0入0汤圆

发表于 2013-12-17 14:05:09 | 显示全部楼层
楼主威武

出0入0汤圆

发表于 2013-12-24 20:54:31 | 显示全部楼层
顶一下~谢谢

出0入0汤圆

发表于 2014-1-12 11:33:26 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2014-3-19 11:07:40 | 显示全部楼层
好东西,学习了!

出0入0汤圆

发表于 2014-5-9 14:06:36 | 显示全部楼层
支持一下啊,感觉高大上啊
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-3 01:07

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表