BabyOS Modbus主从机
最近开发者Haimeng发现解析从机回复的数据时出现Hardfault异常;其根因是代码实现没有考虑非对齐访问,导致M0内核的芯片会出现异常。于是对代码进行优化,并使用hc32l136和stm32l053两个M0的MCU进行验证。
下面介绍使用BabyOS时如何快速开发Modbus主从机。
1 创建协议实例
首先创建协议实例,红色标记部分是用户需要实现提供给协议服务使用。
协议名:b_srv_protocol 根据协议名查找对应的解析和组包接口放在实例中。
get_info: 解析或者组包时需要向外获取的信息。
cb: 应用层处理解析结果的回调函数。
2 Modbus主机
以modbus主机读取从机寄存器为例说明:
3 Modbus从机
以从机被读寄存器为例说明:
从机不会主动发送数据,因此不需要组包接口,解析主机数据的同时将待回复数据组装完成。
开发者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);
代码已经在主分支,欢迎各位开发者体验!