ModBus-RTU解析时一些心得

2019-07-21 06:53发布

下面我的心得,有不对的地方,还请纠正,谢谢:

----
30岁之前,一直闷头写单片机程序,对高级语言的设计模式(经典的23种),数据结构等等都缺乏学习。现在我决心补上这一课。
使用8位单片机时没想过用封装,从面向过程转向面向对象,而在32位机时,考虑到容量大了,速度快了,利用结构体、联合体、函数指针(乃至以后再链表插入删除),将一些事务封装起来,从而达到更方便的调用。

下面是接收到消息并执行:
主机单独写35H设备的地址0002H(DO2)这个继电器为闭合ON串口数据为0x35,0x05,0x00,0x02,0xFF,0x00,0x29,0x8E(其中CRC16=0x8E29)
(1)测试一,不带函数指针的操作在头文件里简单的封装typedef struct _strModbusRtuAio{    uint8 num;  //隐性地址(0,1,2,3,4,5)    uint16 addr; //显性地址(0-990-999)    uint16 value;//数据}strModbusRtuAio,*pstrModbusRtuAio; //ModbusRTU格式寄存器联合体typedef union _unModbusRtuAio{    strModbusRtuAio smra;    uint8 buff[5];    //buff[0]=smra.num;                       //buff[1]=smra.addr_h;                      //buff[2]=smra.addr_l;                      //buff[3]=smra.value_h;                      //buff[4]=smra.value_l;}unModbusRtuAio,*punModbusRtuAio;
然后在源文件里做了如下处理:
    strModbusRtuDio m_jdq[4];
   
    void strModbusRtuDio_set(strModbusRtuDio* pdio,uint8 num,uint16 addr,uint16 status)
    {
        pdio->num=num;
        pdio->addr=addr;
        pdio->status=status;
    }
   
    void jdq_init(void)
    {
        //初始化里假设继电器jdq02的显性地址0x0002,初始化状态为关0x0000
        strModbusRtuDio_set(&m_jdq[0],0,0x0000,0x0000);
        strModbusRtuDio_set(&m_jdq[1],0,0x0001,0x0000);
        strModbusRtuDio_set(&m_jdq[2],0,0x0002,0x0000);
        strModbusRtuDio_set(&m_jdq[3],0,0x0003,0x0000);   
    }
   
    void test(void)
    {
        //假设下面这个数据包符合本规约,其中[2]-[5]是要解析的数据
        uint8 recv_buff[8]={0x35,0x05,0x00,0x02,0xFF,0x00,0x29,0x8E};
        //假设一个联合体,用于变量间数据类型转换
        unModbusRtuDio un1;
        uint8 i;
   
        jdq_init();
   
        //将收到的串口数据recv_buff传递到联合体中
        //recv_buff[2,3,4,5]---->un1.buuf[1,2,3,4],同时也就传给了un1.smrd.addr,un1.smrd.status.
        for(i=1;i<5;i++)
        {
            un1.buff=recv_buff[i+1];
        }
   
        //判断联合体中的地址是否与jdq02地址相同
        //相同则将状态FF00(吸合)赋给jdq02
        for(i=0;i<4;i++)
        {
            if(m_jdq.addr==un1.smrd.addr)
            {
                m_jdq.status=un1.smrd.status;
            }
        }
    }

执行结果:m_jdq[2].status=0xFF00;继电器将吸合。

下面来封装函数,面向对象。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
1条回答
em78447
2019-07-21 10:28
还可以对数据结构做进一步的封装,将MCU端口操作或MCU寄存器操作封装进来,做到面向对象。

    //////////////////////////////////////////////////////////////////////////////////////////
    //----port.h----//
    sbit JDQ00=P1^0;
    sbit JDQ01=P1^1;
    sbit JDQ02=P1^2;
    sbit JDQ03=P1^3;
   
    typedef void (*jdq_fun)(uint8);
   
    //ModbusRTU格式开关量结构体
    typedef struct _strModbusRtuDio
    {
        uint8 num;    //隐性地址(0,1,2,3,4,5)
        uint16 addr;  //显性地址(0-99或0-999)
        uint16 status;//各位的状态或数据
        void (*jdq_fun)(uint8); //操作函数
    }strModbusRtuDio,*pstrModbusRtuDio;
   
    //ModbusRTU格式开关量联合体
    typedef union _unModbusRtuDio
    {
        strModbusRtuDio smrd;
        uint8 buff[6];   //buff[0]=smrd.num;
                      //buff[1]=smrd.addr_h;
                      //buff[2]=smrd.addr_l;
                      //buff[3]=smrd.status_h;
                      //buff[4]=smrd.status_l;
                      //buff[5]=smrd.control_fun(uint8);
          //8位CPU时1个指针1字节,16位CPU时1个指针2字节,32位CPU时1个指针4字节
          //8位CPU时寄存器是8位,因此指针的参数只能是uint8的
    }unModbusRtuDio,*punModbusRtuDio;
   
    //////////////////////////////////////////////////////////////////////////////////////////
    //----port.c----//
    void jdq00_fun(uint8 sta)
    {
        if(0==sta){JDQ00=0;}
        else{JDQ00=1;}
    }
   
    void jdq01_fun(uint8 sta)
    {
        if(0==sta){JDQ01=0;}
        else{JDQ01=1;}
    }
   
    void jdq02_fun(uint8 sta)
    {
        if(0==sta){JDQ02=0;}
        else{JDQ02=1;}
    }
   
    void jdq03_fun(uint8 sta)
    {
        if(0==sta){JDQ03=0;}
        else{JDQ03=1;}
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //----bsp.c----//
    void strModbusRtuDio_set(strModbusRtuDio* pdio,uint8 num,uint16 addr,uint16 status,jdq_fun fun)
    {
        pdio->num=num;
        pdio->addr=addr;
        pdio->status=status;
        pdio->jdq_fun=fun;
    }
   
    void jdq_init(void)
    {
        strModbusRtuDio_set(&m_jdq[0],0,0x0000,0x0000,jdq00_fun);
        strModbusRtuDio_set(&m_jdq[1],0,0x0001,0x0000,jdq01_fun);
        strModbusRtuDio_set(&m_jdq[2],0,0x0002,0x0000,jdq02_fun);
        strModbusRtuDio_set(&m_jdq[3],0,0x0003,0x0000,jdq03_fun);   
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //----app.c----//
    strModbusRtuDio m_jdq[4];
   
    void test(void)
    {
        uint8 recv_buff[8]={0x35,0x05,0x00,0x02,0xFF,0x00,0x29,0x8E};
        unModbusRtuDio un1;
        uint8 i;
   
        jdq_init();
   
        for(i=1;i<5;i++)
        {
            un1.buff[i]=recv_buff[i+1];
        }
   
        for(i=0;i<4;i++)
        {
            if(m_jdq[i].addr==un1.smrd.addr)
            {
                m_jdq[i].status=un1.smrd.status;
                if(0xFF00==m_jdq[i].status){m_jdq[i].jdq_fun(1);}
                else{m_jdq[i].jdq_fun(0);}
            }
        }
    }

执行结果:跳转到jdq02_fun函数里,执行了JDQ02=1;

一周热门 更多>