BabyOS Modbus主从机

最近开发者Haimeng发现解析从机回复的数据时出现Hardfault异常;其根因是代码实现没有考虑非对齐访问,导致M0内核的芯片会出现异常。于是对代码进行优化,并使用hc32l136和stm32l053两个M0的MCU进行验证。

下面介绍使用BabyOS时如何快速开发Modbus主从机。

1 创建协议实例

首先创建协议实例,红色标记部分是用户需要实现提供给协议服务使用。

协议名:b_srv_protocol 根据协议名查找对应的解析和组包接口放在实例中。

get_info: 解析或者组包时需要向外获取的信息。

cb: 应用层处理解析结果的回调函数。

modbus_instance

2 Modbus主机

以modbus主机读取从机寄存器为例说明:

modbus_instance

3 Modbus从机

以从机被读寄存器为例说明:

modbus_instance

从机不会主动发送数据,因此不需要组包接口,解析主机数据的同时将待回复数据组装完成。

开发者ZHANGMIN增加了读写权限的想法,如果能从get_info获取到读写权限配置表,那么在解析到读或者写时都进行权限判断。

4 需要注意的点

4.1 modbus主机组包时,用于组包的信息从哪里传入?

这里table有两个作用,调用接口时存放待组包信息,接口执行完毕则是存放着组包结果。

        bModbusMasterRead_t *pread = (bModbusMasterRead_t *)table;
        pread->base_reg            = 0;
        pread->reg_num             = 2;
        pread->slave_addr          = 1;
        len = bProtSrvPackage(modbus_protocol_id, B_MODBUS_CMD_READ_REG, table, sizeof(table));

4.2 modbus解析结果是什么数据结构?

modbus解析结果数据结构统一如下所示:

typedef struct
{
    uint8_t   slave_id;
    uint8_t   func_code;
    uint16_t  base_reg;   // Little endian
    uint16_t  reg_num;    // Little endian
    uint16_t *reg_value;  // Little endian
} bModbusCbParm_t;

4.3 如何通过get_info获取寄存器信息

这里的buf,既可以作为从机地址存放的位置,也可以作为寄存器值的存放位置。

int modbus_slave_get_info(bProtoInfoType_t type, uint8_t *buf, uint16_t buf_len)
{
    int ret = -1;
    if (type == B_PROTO_INFO_MODBUS_REG_VALUE)
    {
        uint16_t reg_id    = ((uint16_t *)buf)[0];
        uint16_t tmp_value = g_slave_reg_value[reg_id];
        b_log("r:%d %x\r\n", reg_id, tmp_value);
        ((uint16_t *)buf)[0] = tmp_value;
        ret                  = 0;
    }
    else if (type == B_PROTO_INFO_MODBUS_REG_PERMISSION)
    {
        bModbusPerm_t *perm = &g_slave_reg_perm;
        memcpy(buf, &perm, sizeof(perm));
        ret = 0;
    }
    return ret;
}

4.4 如何创建读写权限表

创建空表:MODBUS_PERM_CREATE_TABLE(g_slave_reg_perm);

设置权限:

    // 全部可读,但只有寄存器0可读写
    for (int i = 0; i < MY_DEVICE_MODBUS_REG_NUM; i++)
    {
        MODBUS_PERM_CREATE_SET_STATE(&g_slave_reg_perm, i, MODBUS_PERM_READABLE);
    }
    MODBUS_PERM_CREATE_SET_STATE(&g_slave_reg_perm, 0, MODBUS_PERM_READWRITE);

代码已经在主分支,欢迎各位开发者体验!