百度解释
Modbus协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一种通用工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一个控制器请求访问其它设备的过程,如何回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。
当在同一Modbus网络上通信时,此协议决定了每个控制器需要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。如果需要回应,控制器将生成反馈信息并用Modbus协议发出。在其它网络上,包含了Modbus协议的消息转换为在此网络上使用的帧或包结构。这种转换也扩展了根据具体的网络解决节地址、路由路径及错误检测的方法。
此协议支持传统的RS-232、RS-422、RS-485和以太网设备。许多工业设备,包括PLC,DCS,智能仪表等都在使用Modbus协议作为他们之间的通信标准。
项目中经常需要和各式各样的PLC进行通讯,开始的时候是三菱Q系列的5u,用的是SLMP协议,后面又对接了安川的PLC,指不定以后会用哪家的PLC,所以考虑到是不是应该换一个通用性比较强的接口协议,可以省去大量时间和精力。
网上搜了一下,发现Modbus算是比较通用的协议了,也搜到了很多资料,因为对C++ 比较熟悉,所以搜的都是C++ 的Modbus资源。正好找到了libmodbus这个开源库,通读了一遍代码,觉得很不错,很强大,所以有了这篇文章。
在官网下载了代码,用vs2013编译了一下,我自己编译生成的libCSDN下载地址(2分),Gitee下载地址(免积分)。还是很容易的。
编译:
libmodbus/src/win32目录下的 modbus-9.sln 文件,直接用vs打开编译就可以了。
生成:
libmodbus/src/win32目录下生成了
modbus.dll和modbus.lib文件
引用:
工程包含头文件:modbus.h
modbus-rtu.h
modbus-tcp.h
modbus-version.h
库文件:
modbus.lib
程序目录下放入文件:
modbus.dll
引入到工程之后,就需要了解几个主要的函数了。
/*
以TCP的方式创建libmobus实例
char *ip:连接的IP地址
int port: 连接的IP端口
*/
modbus_t *modbus_new_tcp(const char *ip, int port);
/*
以串口的方式创建libmobus实例
onst char *device:连接的串口号,类似是这样'\\\\.\\COM10'
int baud: 波特率
char parity:奇偶校验
int data_bit:数据位
int stop_bit:停止位
*/
modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
/*
释放libmodbus实例,使用完libmodbus需要释放掉
modbus_t *ctx:libmodbus实例
*/
void modbus_free(modbus_t *ctx);
/*
读取线圈状态,可读取多个连续线圈的状态
modbus_t *ctx:Modbus实例
int addr: 线圈地址
int nb:读取线圈的个数
uint8_t *dest: 传出的状态值
*/
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
/*
读取输入状态,可读取多个连续输入的状态
modbus_t *ctx:Modbus实例
int addr:输入地址
int nb:读取输入的个数
uint8_t *dest:传出的状态值
*/
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
/*
读取输入寄存器的值,可读取多个连续输入输入寄存器
modbus_t *ctx:Modbus实例
int addr:输入地址
int nb:读取输入寄存器的个数
uint8_t *dest:传出的寄存器值
*/
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
/*
读取保持寄存器的值,可读取多个连续输入保持寄存器
modbus_t *ctx:Modbus实例
int addr:输入地址
int nb:读取保持寄存器的个数
uint8_t *dest:传出的寄存器值
*/
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
/*
写入单个线圈的状态
modbus_t *ctx:Modbus实例
int addr:线圈地址
int status:线圈状态
*/
int modbus_write_bit(modbus_t *ctx, int addr, int status);
/*
写入多个连续线圈的状态
modbus_t *ctx:Modbus实例
int addr:线圈地址
int nb:线圈个数
const uint8_t *src:多个线圈状态
*/
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
/*
写入单个寄存器
modbus_t *ctx:Modbus实例
int addr:寄存器地址
int value:寄存器的值
*/
int modbus_write_register(modbus_t *ctx, int addr, int value);
/*
写入多个连续寄存器
int addr:寄存器地址
int nb:寄存器的个数
const uint16_t *src:多个寄存器的值
*/
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
libmodbus下处理浮点型真的是异常方便,我之前用的协议都是需要我自己转换的,在得到的寄存器的数据后,发现PLC的数解析的时候,可能分16位和32位的,拿16位举例,每4位一组就像ABCD,我们需要解析出来的是DCBA。
后来我发现了libmodbus里面有这两个函数
float modbus_get_float(const uint16_t *src);
void modbus_set_float(float f, uint16_t *dest)
这个两个函数主要是将整型数据转换成float,和将float转换成整型的。
在用modbus_read_registers或者modbus_read_input_registers得到寄存器的值int16_t *dest,如果里面存的是浮点数,把dest当做参数传入到modbus_get_float里面,如果得到的值不对,你需要弄清楚PLC对应传出数据的模式是DCBA还是BADC,CDAB,ABCD。
libmodbus还提供了其他函数
modbus_get_float_abcd
modbus_get_float_badc
modbus_get_float_cdab
modbus_get_float_dcba
modbus_set_float_abcd
modbus_set_float_badc
modbus_set_float_cdab
modbus_set_float_dcba