modbus在rtthread上的应用

描述

1 背景
最近有一个modbus通信协议的需求,借此机会总结一下modbus在rtthread上的应用。

2 RS485
2.1 简介
RS485通信接口一般应用在物联网自动化场景,只有2根线,工作在半双工模式。

RS485

2.2 与RS232对比

RS485

2.3 正点原子开发板上的应用电路

RS485

RS485低电平是接收模式,高电平是发送模式,在发送函数中发送之前切换为发送状态,发送完后切换为接收状态

3 modbus
3.1 1.5T和3.5T
modbus协议规定字节间隔为1.5个字符,帧间隔为3.5个字符若串口通信参数设置为(注:开始位固定为1):数据位8,奇偶校验位1,停止位1,波特率9600bps,
则传输一个字符(即1个字节)的时间为:(1+8+1+1)/9600=0.00114583s=1.1454583ms
1.5字符间隔=1.5x1.1454583ms=1.71818745ms
3.5字符间隔=3.5x1.1454583ms=4.00910405ms

38400bps,则传输一个字符(即1个字节)的时间为:
(1+8+1+1)/38400=0.00028645=0.286ms
1.5字符间隔=1.5x0.286ms=0.4ms
3.5字符间隔=3.5x0.286ms=1ms

3.2 libmodbus
libmodbus是一个基于C语言实现的Modbus驱动库,作者是Stephane,支持Linux, Mac OS X, FreeBSD, QNX and Win32操作系统,主要应用在PC上,用来开发上位机,也可以对源代码进行交叉编译,以适配更多的平台,比如ARM Linux。 源代码开源,遵循 LGPL-2.1 许可。

4 相关代码
该版本不需要支持select和poll机制
4.1 宏定义
#define _RESPONSE_TIMEOUT 500000
#define _BYTE_TIMEOUT 5000
#define HAVE_DECL_TIOCM_RTS 1

4.2 初始化

int rc = 0; 
    uint8_t mb_reply[MODBUS_TCP_MAX_ADU_LENGTH];
    uint16_t tab_reg[64] = {0};
    char dev_name[32] ="/dev/uart2";
    
    #ifndef RT_USING_POSIX_STDIO
        sprintf(dev_name,"%s","uart2");
    #endif
    
    ctx = modbus_new_rtu(dev_name, 115200, 'N', 8, 1);
    rt_kprintf("ctx =[%x]\\n",ctx);
    modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
    modbus_rtu_set_rts(ctx, RS485_RE, MODBUS_RTU_RTS_UP);
    modbus_set_slave(ctx, CONFIG_SLAVE_ID); /* 设置从机地址 */
    modbus_set_debug(ctx,1);
    modbus_set_response_timeout(ctx, 0, 1000000);
    mb_mapping = modbus_mapping_new(0, 0, CONFIG_REG_HOLD_MAX, 0);
    if (mb_mapping == RT_NULL)
    {
        rt_kprintf("modbus_mapping_new failed! \\n");
        modbus_free(ctx);
        return;
    }

    mb_mapping->tab_registers[0] = 'R';
    mb_mapping->tab_registers[1] = 'T';
    mb_mapping->tab_registers[2] = '-';
    mb_mapping->tab_registers[3] = 'T';
    mb_mapping->tab_registers[4] = 'h';
    mb_mapping->tab_registers[5] = 'r';
    mb_mapping->tab_registers[6] = 'e';
    mb_mapping->tab_registers[7] = 'a';
    mb_mapping->tab_registers[8] = 'd';
    mb_mapping->tab_registers[0x0b] = 0x1234;
    #ifndef RT_USING_POSIX_STDIO
    rt_sem_init(&ctx->rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    #endif
    modbus_connect(ctx);
    int num = 0;

4.3 接收处理
_modbus_receive_msg
兼容了两种不同的方式

4.3.1 方式1 检查串口队列是否有数据或者是否满
select实际上是让线程进入睡眠,直到有事件响应就唤醒,同时检查串口队列中是否有数据。
4.3.1.1 接收处理函数

int rc;
    #ifdef RT_USING_POSIX_STDIO
    fd_set rset;
    #else
    uint8_t *rset;
    #endif
    struct timeval tv;
    struct timeval *p_tv;
    int length_to_read;
    int msg_length = 0;
    _step_t step;


    if (ctx->debug) {
        if (msg_type == MSG_INDICATION) {
            rt_kprintf("Waiting for a indication...\\n");
        } else {
            rt_kprintf("Waiting for a confirmation...\\n");
        }
    }

    /* Add a file descriptor to the set */
    #ifdef RT_USING_POSIX_STDIO
    FD_ZERO(&rset);
    FD_SET(ctx->s, &rset);
    #endif

    /* We need to analyse the message step by step.  At the first step, we want
     * to reach the function code because all packets contain this
     * information. */
    step = _STEP_SLAVE_ID;

    length_to_read = ctx->backend->header_length + 1;

    if (msg_type == MSG_INDICATION) 
    {
        /* Wait for a message, we don't know when the message will be
         * received */
        p_tv = NULL;
    } 
    else 
    {
        tv.tv_sec = ctx->response_timeout.tv_sec;
        tv.tv_usec = ctx->response_timeout.tv_usec;
        p_tv = &tv;
    }

     length_to_read = ctx->backend->header_length + 1;


    if (msg_type == MSG_INDICATION) {
        /* Wait for a message, we don't know when the message will be
         * received */
        p_tv = NULL;
    } else {
        tv.tv_sec = ctx->response_timeout.tv_sec;
        tv.tv_usec = ctx->response_timeout.tv_usec;
        p_tv = &tv;
    }


    while (length_to_read != 0) 
    {
        uint32_t get_tick = rt_tick_get();

        rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read);

        rt_kprintf("takes ms=[%d]\\n",rt_tick_get() - get_tick);

        if (rc == -1) 
        {
            _error_print(ctx, "select");

            if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) 
            {
                int saved_errno = errno;


                if (errno == ETIMEDOUT) {
                    _sleep_response_timeout(ctx);
                    modbus_flush(ctx);
                } else if (errno == EBADF) {
                    modbus_close(ctx);
                    modbus_connect(ctx);
                }
                errno = saved_errno;
            }

            return -1;
        } 

        rc = ctx->backend->recv(ctx, msg + msg_length, length_to_read);

        if (rc == 0) 
        {
            errno = ECONNRESET;
            rc = -1;
        }


        if (rc == -1) {
            _error_print(ctx, "read");
            if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
                (errno == ECONNRESET || errno == ECONNREFUSED ||
                 errno == EBADF)) {
                int saved_errno = errno;
                modbus_close(ctx);
                modbus_connect(ctx);
                /* Could be removed by previous calls */
                errno = saved_errno;
            }
            return -1;
        }


        /* Display the hex code of each character received */
        if (ctx->debug) {
            int i;
            for (i=0; i < rc; i++)
                printf("<%.2X>", msg[msg_length + i]);
        }
        
        /* Sums bytes received */
        msg_length += rc;
        /* Computes remaining bytes */
        length_to_read -= rc;

        if (length_to_read == 0) 
        {
            switch (step) 
            {
            case _STEP_SLAVE_ID:
            {
                if (CONFIG_SLAVE_ID != msg[0])
                {
                    break;  
                }
                else
                {
                    step = _STEP_FUNCTION;
                }
            }
            case _STEP_FUNCTION:
            {
                length_to_read = compute_meta_length_after_function(
                    msg[ctx->backend->header_length],
                    msg_type);
                if (length_to_read != 0) {
                    step = _STEP_META;
                } /* else switches straight to the next step */
            }
            break;

            case _STEP_META:
                length_to_read = compute_data_length_after_meta(
                    ctx, msg, msg_type);
                if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) {
                    errno = EMBBADDATA;
                    _error_print(ctx, "too many data");
                    return -1;
                }
                step = _STEP_DATA;
                break;
            default:
                break;
            }
        }


        if (length_to_read > 0 &&
            (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) {
            /* If there is no character in the buffer, the allowed timeout
               interval between two consecutive bytes is defined by
               byte_timeout */
            tv.tv_sec = ctx->byte_timeout.tv_sec;
            tv.tv_usec = ctx->byte_timeout.tv_usec;
            p_tv = &tv;
        } 
        /* else timeout isn't set again, the full response must be read before
           expiration of response timeout (for CONFIRMATION only) */
    }

    if (ctx->debug)
        rt_kprintf("\\n");

    return ctx->backend->check_integrity(ctx, msg, msg_length);

4.3.1.2从串口设备读数据

4.3.1.2 从串口设备读数据_modbus_rtu_recv
#if defined(_WIN32)
    return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
#else
#ifdef RT_USING_POSIX_STDIO
        return read(ctx->s, rsp, rsp_length);
#else
        return rt_device_read(ctx->dev, 0,rsp, rsp_length);
#endif

4.3.1.3 检查串口队列

int poll_get(modbus_t *ctx)
{
    int mask = 0;
            rt_base_t level;
            struct rt_serial_rx_fifo* rx_fifo;

            //rt_poll_add(&(device->wait_queue), req);
        struct rt_serial_device *serial; 
        serial = (struct rt_serial_device *)ctx->dev;

            rx_fifo = (struct rt_serial_rx_fifo*) serial->serial_rx;


            level = rt_hw_interrupt_disable();
            if ((rx_fifo->get_index != rx_fifo->put_index) || (rx_fifo->get_index == rx_fifo->put_index && rx_fifo->is_full == RT_TRUE))
            {
              mask = 1;
            } 

            rt_hw_interrupt_enable(level);
              
    return mask;
}


static int _modbus_rtu_select(modbus_t *ctx, void *rset,
                              struct timeval *tv, int length_to_read)
{
    int s_rc;
#if defined(_WIN32)
    s_rc = win32_ser_select(&((modbus_rtu_t *)ctx->backend_data)->w_ser,
                            length_to_read, tv);
    if (s_rc == 0) {
        errno = ETIMEDOUT;
        return -1;
    }


    if (s_rc < 0) {
        return -1;
    }
#else
#ifdef RT_USING_POSIX_STDIO
    fd_set *new_rset = (fd_set *)rset;
    while ((s_rc = select(ctx->s+1, new_rset, NULL, NULL, tv)) == -1) {
        if (errno == EINTR) {
            if (ctx->debug) {
                fprintf(stderr, "A non blocked signal was caught\\n");
            }
            /* Necessary after an error */
            FD_ZERO(new_rset);
            FD_SET(ctx->s, new_rset);
        } else {
            return -1;
        }
    }


#else
    uint32_t msec = 0;

    if (tv)
    {
        msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
    }
    else
    {
        msec = 1000;
    }

    uint32_t ms_delay_ivt = 10;

    uint32_t get_tick = rt_tick_get();

    if (msec < ms_delay_ivt)
    { 
        ms_delay_ivt = msec;
    }

    while (1)
    {
        s_rc = poll_get(ctx); 

        if ((s_rc) || ((rt_tick_get() - get_tick)>msec))
        {
            break;  
        }
        rt_thread_mdelay(ms_delay_ivt); 
    } 
#endif 
    if (s_rc == 0) 
    {
        /* Timeout */
        errno = ETIMEDOUT;
        return -1;
    }
#endif

4.3.2 方式2
4.3.2.1 直接等待接收数据
不使用先select后rec的方式,而是直接等待接收串口数据

int rc;
    #ifdef RT_USING_POSIX_STDIO
    fd_set rset;
    #else
    uint8_t *rset;
    #endif
    struct timeval tv;
    struct timeval *p_tv;
    int length_to_read;
    int msg_length = 0;
    _step_t step;


    if (ctx->debug) {
        if (msg_type == MSG_INDICATION) {
            rt_kprintf("Waiting for a indication...\\n");
        } else {
            rt_kprintf("Waiting for a confirmation...\\n");
        }
    }

    /* Add a file descriptor to the set */
    #ifdef RT_USING_POSIX_STDIO
    FD_ZERO(&rset);
    FD_SET(ctx->s, &rset);
    #endif

    /* We need to analyse the message step by step.  At the first step, we want
     * to reach the function code because all packets contain this
     * information. */
    step = _STEP_SLAVE_ID;

    length_to_read = ctx->backend->header_length + 1;

    if (msg_type == MSG_INDICATION) 
    {
        /* Wait for a message, we don't know when the message will be
         * received */
        p_tv = NULL;
    } 
    else 
    {
        tv.tv_sec = ctx->response_timeout.tv_sec;
        tv.tv_usec = ctx->response_timeout.tv_usec;
        p_tv = &tv;
    }

     length_to_read = ctx->backend->header_length + 1;


    if (msg_type == MSG_INDICATION) {
        /* Wait for a message, we don't know when the message will be
         * received */
        p_tv = NULL;
    } else {
        tv.tv_sec = ctx->response_timeout.tv_sec;
        tv.tv_usec = ctx->response_timeout.tv_usec;
        p_tv = &tv;
    }


    while (length_to_read != 0) 
    {


          uint32_t msec = 0;

           if (p_tv)
          {
            msec = (p_tv->tv_sec * 1000) + (p_tv->tv_usec / 1000);
          } 
          else
          {
             msec = 500; 
          } 
            uint32_t i = 0; 
            uint32_t tick = rt_tick_get(); 
            while (rt_tick_get() <= (tick + rt_tick_from_millisecond(msec)) && i < (length_to_read))
            {
                i += _rym_read_data(ctx, msg + msg_length,length_to_read);
                //rt_thread_mdelay(5);
            }

            rt_kprintf("i=%d\\r\\n",i);
            rc = i;  

        if (rc == 0) 
        {
            errno = ECONNRESET;
            rc = -1;
        }


        if (rc == -1) {
            _error_print(ctx, "read");
            if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
                (errno == ECONNRESET || errno == ECONNREFUSED ||
                 errno == EBADF)) {
                int saved_errno = errno;
                modbus_close(ctx);
                modbus_connect(ctx);
                /* Could be removed by previous calls */
                errno = saved_errno;
            }
            return -1;
        }


        /* Display the hex code of each character received */
        if (ctx->debug) {
            int i;
            for (i=0; i < rc; i++)
                printf("<%.2X>", msg[msg_length + i]);
        }


        /* Sums bytes received */
        msg_length += rc;
        /* Computes remaining bytes */
        length_to_read -= rc;

        if (length_to_read == 0) 
        {
            switch (step) 
            {
            case _STEP_SLAVE_ID:
            {
                if (CONFIG_SLAVE_ID != msg[0])
                {
                    break;  
                }
                else
                {
                    step = _STEP_FUNCTION;
                }
            }
            case _STEP_FUNCTION:
            {
                length_to_read = compute_meta_length_after_function(
                    msg[ctx->backend->header_length],
                    msg_type);
                if (length_to_read != 0) {
                    step = _STEP_META;
                } /* else switches straight to the next step */
            }
            break;

            case _STEP_META:
                length_to_read = compute_data_length_after_meta(
                    ctx, msg, msg_type);
                if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) {
                    errno = EMBBADDATA;
                    _error_print(ctx, "too many data");
                    return -1;
                }
                step = _STEP_DATA;
                break;
            default:
                break;
            }
        }


        if (length_to_read > 0 &&
            (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) {
            /* If there is no character in the buffer, the allowed timeout
               interval between two consecutive bytes is defined by
               byte_timeout */
            tv.tv_sec = ctx->byte_timeout.tv_sec;
            tv.tv_usec = ctx->byte_timeout.tv_usec;
            p_tv = &tv;
        } 
        /* else timeout isn't set again, the full response must be read before
           expiration of response timeout (for CONFIRMATION only) */
    }

    if (ctx->debug)
        rt_kprintf("\\n");

    return ctx->backend->check_integrity(ctx, msg, msg_length);

4.3.2.2 超时等待接收

static rt_size_t _rym_read_data(modbus_t *ctx,rt_uint8_t *buf,rt_size_t len)
{
    /* we should already have had the code */ 
    rt_size_t readlen = 0;


    do
    {
        readlen += rt_device_read(ctx->dev,
                                  0, buf + readlen, len - readlen);
        if (readlen >= len)
            return readlen;
    }
    while (rt_sem_take(&ctx->rx_sem, 5) == RT_EOK);


    return readlen;
}

4.4 应用部分

while (1)
    {
        if (0 == send_type)
        {
            rc = modbus_receive(ctx, mb_reply);

            if (rc > 0)
            {
                modbus_reply(ctx, mb_reply, rc, mb_mapping);
                uint8_t idx=0;
                rt_kprintf("check [",mb_mapping->tab_registers[0x0b]);
                for(idx=0; idx<0xFC; idx++)
                {
                    rt_kprintf("[%04x] ",mb_mapping->tab_registers[idx]);

                    if (idx%16==0)
                    {
                       rt_kprintf("\\n");
                    }
                }
                rt_kprintf("]\\n");
           }
        }
        else
        {
            memset(tab_reg, 0, 64 * 2);
            int regs = modbus_read_registers(ctx, 0, 20, tab_reg);
            rt_kprintf("[%4d][read num = %d]", num, regs);
            num++;
            int i;
            for (i = 0; i < 20; i++)
            {
                rt_kprintf("<%#x>", tab_reg[i]);
            }
            rt_kprintf("\\n");
            rt_thread_mdelay(2000);
        }
    }
    modbus_close(ctx);
    modbus_free(ctx);
}


static int rtu_test_init(void)
{
    rt_pin_mode(RS485_RE, PIN_MODE_OUTPUT);
    rt_thread_t tid;
    tid = rt_thread_create("mb_test",
                           mb_slave_thread, RT_NULL,
                           2048,
                           12, 10);
    if (tid != RT_NULL)
        rt_thread_startup(tid);


    return RT_EOK;
}


INIT_APP_EXPORT(rtu_test_init);


int cmd_modbus_send(int argc, char **argv)
{
        uint16_t set_addr = strtoul(argv[1], 0, 16); 
        uint16_t set_data = strtoul(argv[2], 0, 16); 

        send_type = 1;

         //int 
        int res = modbus_write_register(ctx, set_addr,set_data);

        rt_kprintf("res = [%d]\\n",res);


    return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_modbus_send, mod,mod [addr][data]);

5 测试
5.1 从机测试
5.1.1 上位机
使用modbus poll上位机进行测试,从机地址是0x30,读取保持寄存器地址是0x100,个数是100

RS485

RS485

5.1.2 日志

Sending request using RTS signal
check [[0052] 
[0054] [002d] [0054] [0068] [0072] [0065] [0061] [0064] [0000] [0000] [1234] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] 
[0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] [0000] ]
Waiting for a indication...
takes ms=[710]
takes ms=[0]
takes ms=[0]
<30><03><01><00><00><64><41><30><03><01><00><00><64><41>
[30][03][C8][00][52][00][54][00][2D][00][54][00][68][00][72][00][65][00][61][00][64][00][00][00][00][12][34][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][F2][BC]
Sending request using RTS signal

5.2 主机测试
5.2.1 上位机
使用Modbus Slave上位机,从机地址是0x30,读取保持寄存器地址是0,个数是20

RS485

RS485

5.2.2 日志

rst rc=[20]
[ 629][read num = 20]<1><100><3><4><5><6><7><8><9><77><0><0><0><0><0><0><0><0><0>
[30][03][00][00][00][14][41][E4]
Sending request using RTS signal
Waiting for a confirmation...
takes ms=[237]
takes ms=[0]
takes ms=[1]
<00><61><30><03><28><00><01><01><00><00><03><00><04><00><05><00><06><00><07><00><08><00><09><00><0A><00><7B><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><72>
rst rc=[20]
[ 630][read num = 20]<1><100><3><4><5><6><7><8><9><7b><0><0><0><0><0><0><0><0><0>
[30][03][00][00][00][14][41][E4]
Sending request using RTS signal
Waiting for a confirmation...
takes ms=[226]
takes ms=[0]
takes ms=[1]
<30><03><28><00><01><01><00><00><03><00><04><00><05><00><06><00><07><00><08><00><09><00><0A><00><80><00><00><00><00><00><00><00>
rst rc=[20]
[ 631][read num = 20]<1><100><3><4><5><6><7><8><9><80><0><0><0><0><0><0><0><0><0>
[30][03][00][00][00][14][41][E4]
Sending request using RTS signal
Waiting for a confirmation...
takes ms=[227]
takes ms=[0]
<00><00><00><00><00><00><00><00><00><00><00><30><03><28>takes ms=[0]
<00><01><01><00><00><03><00><04><00><05><00><06><00><07><00><08><00><09><00><0A><00><84><00><00><00><00><00><00><00><00><00><00>
rst rc=[20]
[ 632][read num = 20]<1><100><3><4><5><6><7><8><9><84><0><0><0><0><0><0><0><0><0>
[30][03][00][00][00][14][41][E4]
Sending request using RTS signal
Waiting for a confirmation...
takes ms=[236]
takes ms=[0]
takes ms=[0]
<00><00><00><00><00><00><00><00><56><30><03><28><00><01><01><00><00><03><00><04><00><05><00><06><00><07><00><08><00><09><00><0A><00><89><00><00><00><00><00><00><00><00><00><00><00><00><00>
rst rc=[20]
[ 633][read num = 20]<1><100><3><4><5><6><7><8><9><89><0><0><0><0><0><0><0><0><0>

6 总结
6.1modbus的应用场景非常广泛,无论在哪个领域都会有它的身影,所以掌握它是很有必要的,还是需要多做点项目多应用,多多刻意地练习。

6.2 主机模式下接收不稳定,有时校验不通过
看了下代码,应该是解码方式的问题,后续再优化一下解码步骤

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分