0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

modbus在rtthread上的应用

CHANBAEK 来源:小陈学不停 作者:小陈学不停 2023-01-12 16:45 次阅读

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

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

图片

2.2 与RS232对比

图片

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

图片

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

图片

图片

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

图片

图片

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

  • 通信协议
    +关注

    关注

    28

    文章

    846

    浏览量

    40226
  • RS485
    +关注

    关注

    39

    文章

    1135

    浏览量

    82097
  • MODBUS
    +关注

    关注

    28

    文章

    1759

    浏览量

    76779
  • RS232
    +关注

    关注

    13

    文章

    733

    浏览量

    94204
  • RTThread
    +关注

    关注

    8

    文章

    132

    浏览量

    40789
收藏 人收藏

    评论

    相关推荐

    使用rtthread移植qboot工程的学习笔记

    学习rtthread,配置qboot时的过程;记录一下自己使用rtthread studio配置qboot和app工程的制作过程.
    的头像 发表于 06-12 09:55 7030次阅读
    使用<b class='flag-5'>rtthread</b>移植qboot工程的学习笔记

    RT-Thread Studio配置rtthread CANFD驱动来控制M3508电机

    本文旨在RT-Thread Studio配置rtthread CANFD驱动来控制M3508电机,不涉及任何原理 开发环境:RT-Thread Studio v2.2.6
    发表于 10-08 11:44 1216次阅读
    <b class='flag-5'>在</b>RT-Thread Studio<b class='flag-5'>上</b>配置<b class='flag-5'>rtthread</b> CANFD驱动来控制M3508电机

    linux下搭建rtthread_qemu系统

    RT-Thread源码的获取方式有多种,可以是官网浏览器下载、云盘下载、git获取,强烈推荐git,因为使用git可以很方便的切换各种版本的rtthread
    的头像 发表于 11-21 14:40 1573次阅读
    <b class='flag-5'>在</b>linux下搭建<b class='flag-5'>rtthread</b>_qemu系统

    介绍Modbus协议STM32平台的移植

    协议STM32平台的移植。1.1 freemodbus介绍  freemodbus是一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议
    发表于 01-14 06:57

    rtthread-nano3.1.3添加pm组件FinSH控制台就输入不了怎么解决

    rtthread-nano3.1.3版本添加了pm组件,但是添加之后FinSH 控制台就输入不了了,未添加pm组件之前都是可以用的
    发表于 04-21 09:32

    rtthread smart可以使用device框架吗

    rtthread smart发布出来很久了,一直没时间玩,最近在rtthread开发使用device框架感觉太爽太省心了。然后突然想到,如果在rt smart跑同样这套代码,dev
    发表于 05-05 14:14

    介绍一个支持从主机的多实例modbus

    SMALL_MODBUS_PORT_H#define SMALL_MODBUS_PORT_H/**RTTHREAD PORT*/#define SMALL_MODBUS_RTTHREAD
    发表于 08-17 14:42

    有没有哪位道友RTThread内核移植modbus_tcp成功的啊

    有没有哪位道友,成功的使用过agile_modbus协议栈的modbus_tcp啊,我看官方给的DEMO都是基于posix接口写的,但是本人不太清除posix接口。有没有哪位道友RTThre
    发表于 11-09 14:27

    Modbus TCPARM工控板的使用

    Modbus TCPARM工控板的使用 本文主要介绍ZLG/Modbus TCP 用户接口及具体使用方法
    发表于 04-07 16:41 31次下载

    Modbus UARTARM工控板的使用

    Modbus UARTARM工控板的使用本文主要介绍ZLG/Modbus UART 用户接口及具体使用方法
    发表于 04-07 16:44 71次下载

    Modbus TCPARM工控板的使用

    Modbus 协议是应用于电子控制器的一种通用语言。Modbus/TCP 则是运行在TCP/IPModbus 报文传输协议。通过此协议
    发表于 07-19 15:03 256次下载

    RTThread操作系统的调度设计原理是怎样的

    电子发烧友网站提供《RTThread操作系统的调度设计原理是怎样的.pdf》资料免费下载
    发表于 11-26 17:24 26次下载
    <b class='flag-5'>RTThread</b>操作系统的调度设计原理是怎样的

    华大单片机移植RTThread操作系统

    3.1.新建华大单片机最小系统工程模板,这里不展开3.2.Keil MDK加入Rtthread代码3.3添加rtthread源码到工程中3.4 添加完成的样子3.5 移植后需要修改部分东西才能让系统真正跑起来。。4.测试例子5
    发表于 11-17 17:21 53次下载
    华大单片机移植<b class='flag-5'>RTThread</b>操作系统

    rtthread套娃移植

    和大家分享下将基于rtthread的项目移植到其他平台的经验。背景最近做了一个物联网项目移植。原先的项目使用的硬件平台为stm32f401+sim800c(mcu + 2G modem),软件平台为
    发表于 12-20 19:45 13次下载
    <b class='flag-5'>rtthread</b>套娃移植