内容简介:在上一篇文章《我们简单的写一下 modbus rtu 下 响应客户端(主机)读4x 区保持寄存器的伪代码流程:上述代码中,大部分都是很容易理解的,modbus_receive 函数就是在上一篇文章中已经进行了分析,这里就不再赘述了,它最终是调用 _modbus_receive_msg 函数, 它采用 select 接收机制,而且当作为 从机使用时, select 的超时时间设定为 空,
在上一篇文章《 libmodbus源码分析(2)主机(客户端)功能源码分析 》 从 主机的角度 分析了 源码,本文以 从机(服务器)的角度分析一下源码。同样的,我们以 modbus rtu 协议的 4x区保持寄存器功能进行举例说明。
我们简单的写一下 modbus rtu 下 响应客户端(主机)读4x 区保持寄存器的伪代码流程:
int main(void) { modbus_t *ctx; modbus_mapping_t *mb_mapping; uint8_t *query; /* 创建并初始化 modbus_t 指针 */ ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); /* 设定本机设备地址 */ modbus_set_slave(ctx, SERVER_ID); /* 用于接收主机消息的 缓存申请 */ query = malloc(MODBUS_RTU_MAX_ADU_LENGTH); /* 0x、1x、3x、4x共4个区 寄存器的 缓存申请 */ mb_mapping = modbus_mapping_new_start_address( UT_BITS_ADDRESS, UT_BITS_NB, UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX, UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB); /* 根据自己需要 填充 4个区 寄存器内容,这一部分可以在另外一个单独线程中,循环刷新寄存器值*/ while(1){ /* 等待接收主机读,直到 读到 指令,会阻塞*/ do{ rc = modbus_receive(ctx, query); }while(rc == 0); /* 根据接收到的主机 指令 query 内容,自动的回复 主机想要的 数据包 */ rc = modbus_reply(ctx, query, rc, mb_mapping); } /* libmodbus 退出后,释放、关闭相关资源 */ modbus_mapping_free(mb_mapping); free(query); /* For RTU */ modbus_close(ctx); modbus_free(ctx); }
上述代码中,大部分都是很容易理解的,modbus_receive 函数就是在上一篇文章中已经进行了分析,这里就不再赘述了,它最终是调用 _modbus_receive_msg 函数, 它采用 select 接收机制,而且当作为 从机使用时, select 的超时时间设定为 空,
if (msg_type == MSG_INDICATION) { /* Wait for a message, we don't know when the message will be * received */ p_tv = NULL; }
这就意味着该函数会“阻塞“等待接收,直到有数据可接收,所以在写程序的时候,需要注意,可以考虑在一个单独线程中使用。
接下来,我们就分析一下 modbus_repley 函数的实现:
这里,我们再看看一下,libmodbus 是如何 知道主机要读那些寄存器,并且如何将主机想读的寄存器内容筛选打包的,源码如下:
case MODBUS_FC_READ_HOLDING_REGISTERS: case MODBUS_FC_READ_INPUT_REGISTERS: { /* 保持寄存器 or 输入寄存器判断 */ unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS); /* modbus 寄存器区 首地址 获取 */ int start_registers = is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers; /* modbus 寄存器区 寄存器总数量,比如 4x区寄存器数量 */ int nb_registers = is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers; /* 对应区 寄存器 缓存 首地址 */ uint16_t *tab_registers = is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers; /* 调试,没用 */ const char * const name = is_input ? "read_input_registers" : "read_registers"; /* 筛选出 主机想要读的 寄存器 数量 */ int nb = (req[offset + 3] << 8) + req[offset + 4]; /* The mapping can be shifted to reduce memory consumption and it doesn't always start at address zero. */ /* 计算出 主机要读的 寄存器起始地址 在 modbus 寄存器缓存中的 偏移地址 */ int mapping_address = address - start_registers; /* 主机发送命令中的 首地址、寄存器数量大小 合法性判断 */ if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) { rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, "Illegal nb of values %d in %s (max %d)\n", nb, name, MODBUS_MAX_READ_REGISTERS); } else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) { rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, "Illegal data address 0x%0X in %s\n", mapping_address < 0 ? address : address + nb, name); } else { /* 根据前面的 计算,将对应区的寄存器 数据从 modbus 缓存 拷贝到 rsp(回复给主机的数 * 据帧包) */ int i; rsp_length = ctx->backend->build_response_basis(&sft, rsp); rsp[rsp_length++] = nb << 1; for (i = mapping_address; i < mapping_address + nb; i++) { rsp[rsp_length++] = tab_registers[i] >> 8; rsp[rsp_length++] = tab_registers[i] & 0xFF; } } } break;
以上所述就是小编给大家介绍的《libmodbus源码分析(3)从机(服务端)功能源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
- [源码分析] kube-scheduler源码分析(五)之 PrioritizeNodes
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从规范出发的程序设计
[美] Carroll Morgan / 裘宗燕 / 机械工业出版社 / 2002-8 / 45.00元
本书详细论述了有关规范程序设计的内容,包括:程序和精化、谓词演算、选择、迭代、构造类型、模块和封装等,最后几章还包含了大量的实例研究和一些更高级的程序设计技术。本书提倡一种严格的程序开发方法,分析问题要用严格方式写出程序的规范,而后通过一系列具有严格理论基础的推导,最终得到可以运行的程序。 本书是被世界上许多重要大学采用的教材,适于计算机及相关专业的本科生和研究生使用。一起来看看 《从规范出发的程序设计》 这本书的介绍吧!