一,简介
modbus协议是现在国内工业领域用的最多的协议,不只国内的PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备,凡是厂家没有能力设计协议的,都会直接上modbus协议。这个协议也是经过市场验证,经久不衰的协议。modbus分两种,一类是通过串口(232 485 422)采集的modbus RTU协议,一类是通过网口采集的modbus TCP协议,我们项目中用的是modbus TCP。
modbus 都是主从式问答,主机发送查询指令,从机响应;主机发送设置指令,主机响应。项目里我们的程序作为主机,发送指令查询。从机我们用modbus slave软件模拟,可以模拟任意设备,任意地址。
二,函数接口
modbus_t* modbus_new_tcp(const char *ip, int port)
功能:以TCP方式创建Modbus实例,并初始化
参数:
ip :ip地址
port:端口号
返回值:成功:Modbus实例
失败:NULL
int modbus_set_slave(modbus_t *ctx, int slave)
功能:设置从机ID
参数:
ctx :Modbus实例
slave:从机ID
返回值:成功:0
失败:-1
int modbus_connect(modbus_t *ctx)
功能:和从机(slave)建立连接
参数:
ctx:Modbus实例
返回值:成功:0
失败:-1
void modbus_free(modbus_t *ctx)
功能:释放Modbus实例
参数:ctx:Modbus实例
void modbus_close(modbus_t *ctx)
功能:关闭套接字
参数:ctx:Modbus实例
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01)
参数:
ctx :Modbus实例
addr :寄存器起始地址
nb :寄存器个数
dest :得到的状态值
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
参数:
ctx :Modbus实例
addr :寄存器起始地址
nb :寄存器个数
dest :得到的状态值
返回值:成功:返回nb的值
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03)
参数:
ctx :Modbus实例
addr :寄存器起始地址
nb :寄存器个数
dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
失败:-1
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
参数:
ctx :Modbus实例
addr :寄存器起始地址
nb :寄存器个数
dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
失败:-1
int modbus_write_bit(modbus_t *ctx, int addr, int status);
功能:写入单个线圈的状态(对应功能码为0x05)
参数:
ctx :Modbus实例
addr :线圈地址
status:线圈状态
返回值:成功:0
失败:-1
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
功能:写入多个连续线圈的状态(对应功能码为15)
参数:
ctx :Modbus实例
addr :线圈地址
nb :线圈个数
src :多个线圈状态
返回值:成功:0
失败:-1
int modbus_write_register(modbus_t *ctx, int addr, int value);
功能: 写入单个寄存器(对应功能码为0x06)
参数:
ctx :Modbus实例
addr :寄存器地址
value :寄存器的值
返回值:成功:0
失败:-1
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
功能:写入多个连续寄存器(对应功能码为16)
参数:
ctx :Modbus实例
addr :寄存器地址
nb :寄存器的个数
src :多个寄存器的值
返回值:成功:0
失败:-1
三,类型
typedef struct _modbus_mapping_t {
int nb_bits; //线圈寄存器的数量
int start_bits; //线圈寄存器的起始地址
int nb_input_bits; //离散输入寄存器的数量
int start_input_bits; //离散输入寄存器的起始地址
int nb_input_registers; //输入寄存器的数量
int start_input_registers; //输入寄存器的起始地址
int nb_registers; //保持寄存器的数量
int start_registers; //保持寄存器的起始地址
uint8_t *tab_bits; //指向线圈寄存器的值
uint8_t *tab_input_bits; //指向离散输入寄存器的值
uint16_t *tab_input_registers; //指向输入寄存器的值
uint16_t *tab_registers; //指向保持寄存器的值
} modbus_mapping_t;
四,读写
//读取线圈或者离散量输出状态(功能码 0x1)
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
//读取离散量输入值(功能码 0x2)
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
//读取保持寄存器(功能码 0x3)
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
//读取输入寄存器(功能码 0x4)
MODBUS_API int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
//写单个线圈或者单个离散量(功能码 0x5)
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
//写单个保持寄存器(功能码 0x6)
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
//写多个线圈(功能码 0xF)
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
//写多个保持寄存器(功能码 0x10)
五,uint ?_t解析
根据POSIX标准,对应的*T类型:1字节uint8ut,2字节uint16ut,4字节uint32ut,8字节Uint64ut,POSIX代表UNIX的可移植操作系统接口(简称POSIX)。POSIX标准定义了操作系统应该为应用程序提供的接口标准。它是IEEE为运行在各种UNIX操作系统上的软件定义的一系列API标准的总称。它的正式名称是IEEE1003,而国际标准名称是ISO/IEC9945。
uint8和uint16是什么数据类型?
Int8是一个8位无符号整数,uint16是一个16位无符号整数。
整数有两种类型:有符号和无符号。默认情况下,声明的整数变量是有符号的(char有点特殊)。如果需要声明一个未签名的类型,则需要在该类型之前添加signed。当不可能取负值时,可以将其定义为无符号。在一些底层嵌入式编程中,数据通常是无符号的。
uint8是什么类型?
这是一个C99整数类型,包括header和ltinttypes。H>//此文件中定义的整数类型uint8utu=>unsignedintinteger8是8位的Unit8ut它相当于一个char,Unit8ut可以是32位,但编译器将只使用它的8位,这是由C99和类似的intleast32ut定义的,至少是32位的整数。具体位数取决于编译器。它保证整数将被分配给您至少32位。
uint8_t就是typedefunsignedintuint8_t?
是
uint8ut实际上是一个char无符号字符,char是一个数据类型
定义:typedefunsignedcharuint8ut[T
注意:uint8ut类型变量的输出将输出什么,如下面的代码?
uint8utid=67
cout
结果是:LD=C而不是LD=67
这是因为typedefunsignedcharuint8ut
uint8ut实际上是一个char
cout
所以,输出uint8ut类型的变量的实际输出是它对应的字符,而不是实数
输出67,您可以这样做:
cout
(typedefunsignedshortintuint16ut)
结果是:LD=67
六,通过读取字节数组来设置位
void modbus_set_bits_from_bytes(uint8_t *dest, int index, unsigned int nb_bits, const uint8_t *tab_byte);
% 本函数的功能是通过读取字节数组来设置位,从tab_byte数组的第1个开始的所有字节都会被写到dest数组中从index索引开始的位置;
七,DCBA顺序获取浮点值float
modbus_get_float_dcba(const uint16_t *src);
DCBA顺序存储浮点值
void modbus_set_float_dcba(float f, uint16_t *dest)
float modbus_get_float(const uint16_t *src);
void modbus_set_float(float f, uint16_t *des
这个两个函数主要是将整型数据转换成float,和将float转换成整型的。
在用modbus_read_registers或者modbus_read_input_registers得到寄存器的值int16_t *dest,如果里面存的是浮点数,把dest当做参数传入到modbus_get_float里面,如果得到的值不对,你需要弄清楚PLC对应传出数据的模式是DCBA还是BADC,CDAB,ABC