Modbus 调试


背景

大量的工业设备基本都支持 RS485 串行协议,包括绝大部分工业传感器,在 RS485 的基础上,支持 ModbusRTU 协议,所以有必要对Modbus进行支持。

Freemodbus

Freemodbus 是 armink 大神移植的 Freemodbus 协议栈。同时支持主机和从机的功能。FreeModbus 是一款开源的 Modbus 协议栈,但是只有从机开源,主机源码是需要收费的。同时网上也没有发现比较好的开源的 Modbus 主机协议栈,所以才开发这款支持主机模式的 FreeModbus 协议栈。本版 FreeModbus版本号更改为V1.6,特性如下:

  • 新增加的主机源码与原有从机的风格及接口保持一致;
  • 支持主机与从机在同一协议栈运行;
  • 支持实时操作系统及裸机移植;
  • 为应用提供多种请求模式,用户可以选择阻塞还是非阻塞模式,自定义超时时间等,方便应用层灵活调用;
  • 支持所有常用的Modbus方法。

获取方式

使用 Freemodbus 软件包 需要在 RT-Thread 的包管理器中选择它,具体路径如下:

RT-Thread online packages
    IoT - internet of things --->
        [*] FreeModbus: Modbus master and slave stack  --->
             [*]   Master mode  --->
             [*]   Slave mode  --->

配置 Freemodbus

使用 Env 工具配置 Freemodbus 软件包,使能 Master 模式也就是主机模式,一般的传感器设备都是从机,所以这里使用主机模式去测试。配置从机的地址,也就是 RS485 地址,配置使用 uart2,配置波特率为9600,使能 sample 示例。

修改测试函数

打开 sample_mb_master.c文件,这个是示例代码。

测试函数如下:

static int mb_master_samlpe(int argc, char **argv)
{
    static rt_uint8_t is_init = 0;
    rt_thread_t tid1 = RT_NULL, tid2 = RT_NULL;

    if (is_init > 0)
    {
        rt_kprintf("sample is running\n");
        return -RT_ERROR;
    }
    tid1 = rt_thread_create("md_m_poll", mb_master_poll, RT_NULL, 512, MB_POLL_THREAD_PRIORITY, 10);
    if (tid1 != RT_NULL)
    {
        rt_thread_startup(tid1);
    }
    else
    {
        goto __exit;
    }

    tid2 = rt_thread_create("md_m_send", read_thread_entry, RT_NULL, 512, MB_SEND_THREAD_PRIORITY, 10);
    if (tid2 != RT_NULL)
    {
        rt_thread_startup(tid2);
    }
    else
    {
        goto __exit;
    }

    is_init = 1;
    return RT_EOK;

__exit:
    if (tid1)
        rt_thread_delete(tid1);
    if (tid2)
        rt_thread_delete(tid2);

    return -RT_ERROR;
}
MSH_CMD_EXPORT(mb_master_samlpe, run a modbus master sample);

首先运行md_m_poll进程,入口函数是mb_master_poll

static void mb_master_poll(void *parameter)
{
    eMBMasterInit(MB_RTU, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
    eMBMasterEnable();

    while (1)
    {
        eMBMasterPoll();
        rt_thread_mdelay(MB_POLL_CYCLE_MS);
    }
}

此函数调用eMBMasterInit方法初始化 Modbus 主机协议栈,主机涉及到的一些硬件就在这个时候做了初始化,然后调用eMBMasterEnable方法启动Modbus主机。轮询的周期由MB_POLL_CYCLE_MS定义,轮询周期决定了命令的相应时间。

随后,运行了md_m_send线程,这里我修改了入口函数为read_thread_entry,因为我需要做的是读取测试,自带的代码中是写入(随时时间参数)测试。

static void read_thread_entry(void *parameter)
{
    eMBMasterReqErrCode error_code = MB_MRE_NO_ERR;
    rt_uint16_t error_count = 0;
    while (1)
    {
        /* Test Modbus Master */

        error_code = eMBMasterReqReadHoldingRegister( SLAVE_ADDR, 
                                                     MB_SEND_REG_START,
                                                     1,
                                                     500 );
        error_code = eMBMasterReqReadHoldingRegister( 2, 
                                                     MB_SEND_REG_START,
                                                     1,
                                                     500 );
        printf("sensor1: %d    sensor2: %d  \t",usMRegHoldBuf[0][0],usMRegHoldBuf[1][0]);
        printf("error_code : %d \n",error_code);
        /* Record the number of errors */
        if (error_code != MB_MRE_NO_ERR)
        {
            error_count++;
        }

    }
}

此函数调用了eMBMasterReqReadHoldingRegister来读取保持寄存器,函数原型如下,其它 API 函数可详见仓库 readme 文档。

eMBMasterReqErrCode eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, 
                                                     USHORT usRegAddr,
                                                     USHORT usNRegs,
                                                     LONG lTimeOut );
参数 描述
ucSndAddr 请求的从机地址,0代表广播。
usRegAddr 读寄存器的地址
usRegData 读寄存器的数量
lTimeOut 请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。

完成读取后我直接用printf显示出来,方便测试。根据 readme 的描述,读取的数据是同意存放在数据缓存区的,代码中已经提供了默认的缓存区,也可以在 Modbus 数据处理回调接口中处理自定义的缓存区。

数据缓冲区

数据缓冲区定义的位置位于 FreeModbus\port\user_mb_app_m.c 文件顶部,共计 4种 数据类型。 FreeModbus从机默认使用 一维数组 作为缓存区数据结构,主机可以存储所有网内从机的数据,所以主机采用 二维数组 对所有从机节点数据进行存储。二维数组的列号代表寄存器、线圈及离散量地址,行号代表从机节点ID,但需要做减一处理,例如usMRegHoldBuf[2][1]代表从机ID为 3,保持寄存器地址为 1 的从机数据。

Modbus 数据处理回调接口

Modbus 一共有4种不同的数据类型,所有的 Modbus 功能都围绕这些数据类型进行操作。由于不同的用户数据缓冲区结构可能有所不同,那么对应的 Modbus 数据处理方式也就存在差异,所以用户需要把每种数据类型对应的操作,按照自己的数据缓冲区结构进行定制实现。 所有的 Modbus 数据处理回调接口如下:

接口 功能描述
eMBMasterRegInputCB 输入寄存器回调接口
eMBMasterRegHoldingCB 保持寄存器回调接口
eMBMasterRegCoilsCB 线圈回调接口
eMBMasterRegDiscreteCB 离散输入回调接口

所以我预计读取从机地址1、2的两个设备的各自的第一个保持寄存器。

测试

如果手头没有 Modbus 设备实物,可以使用 Modbus Slave 这个软件来模拟,功能非常齐全,价格是99刀/单个授权,我仅用于研究用途,就使用了研究用途版本。

连接USB-TTL, TTL 端连接单片机的 uart2,打开软件后点击 Conection,设置串口参数,OK 即可。

默认打开了一个从机,点击 File -> New 新建一个,默认 F = 03 即为保持寄存器,分别双击设置保存寄存器0的值为 1234 和 5678。

打开终端,输入测试命令。

可以看到已经成功读取保存寄存器。

可以实时地对保存寄存器的值进行修改

  • 分享:
评论
还没有评论
    发表评论 说点什么