一、libmodbus 下载
二、更改modbus.c和modbus.h文件
1、首先在modbus.h文件中增加读写文件记录的函数声明。
//fileNumber 文件名
//startRecordNumber 起始文件记录号
//fileData 需要写入文件的数据
//length 写入文件的数据长度
MODBUS_API int modbus_write_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
const uint16_t *fileData, uint8_t length);
//fileNumber 文件名
//startRecordNumber 起始文件记录号
//length 需要读取文件数据的长度
//dest 读取到的文件数据存放目标
MODBUS_API int modbus_read_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
uint16_t length, uint16_t *dest);
2、modbus.h文件中增加读写文件宏定义。
#define MODBUS_FC_READ_FILE_RECORD 0x14
#define MODBUS_FC_WRITE_FILE_RECORD 0x15
3、modbus.c文件计算读取数据长度函数中增加文件读写的处理。
if (function <= MODBUS_FC_READ_INPUT_REGISTERS ||
function == MODBUS_FC_REPORT_SLAVE_ID ||
function == MODBUS_FC_WRITE_AND_READ_REGISTERS ||
function == MODBUS_FC_READ_FILE_RECORD ||
function == MODBUS_FC_WRITE_FILE_RECORD)
{
length = msg[ctx->backend->header_length + 1];
}
else
{
length = 0;
}
4、在modbus.c文件计算响应数据长度函数中增加。
case MODBUS_FC_WRITE_FILE_RECORD:
length = 9 + 2 * (req[offset + 7] << 8 | req[offset + 8]);
break;
case MODBUS_FC_READ_FILE_RECORD:
length = 4 + 2 * (req[offset + 7] << 8 | req[offset + 8]);
break;
由协议文档可得,写文件收到的回应数据长度应为:
(Record length Hi << 8 | Record length Lo) * 2 + 前面9个除文件数据以外的固定回复数据。
由协议文档可得,读文件收到的回应数据长度应为:
(Record length Hi << 8 | Record length Lo) * 2 + 前面4个除文件数据以外的固定回复数据。5、在校验函数中增加读写文件的处理。
case MODBUS_FC_READ_FILE_RECORD:
/* 保证req_nb_value 和 rsp_nb_value 计算后的值相等,才会通过校验 */
req_nb_value = (req[offset + 7] << 8) + req[offset + 8];
rsp_nb_value = ((rsp[offset + 2] - 1) / 2);
break;
case MODBUS_FC_WRITE_FILE_RECORD:
req_nb_value = (req[offset + 7] << 8) + req[offset + 8];
rsp_nb_value = (rsp[offset + 7] << 8) | rsp[offset + 8];
break;
这里写文件发送和回复的数据长度是一样的。
读文件回复数据长度 = (File resp.length - 1) / 2
三、写文件函数定义
int modbus_write_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
const uint16_t *fileData, uint8_t length)
{
int rc;
int req_length;
uint8_t req[MAX_MESSAGE_LENGTH];
if (ctx == NULL || length <= 0) {
errno = EINVAL;
return -1;
}
if(fileNumber == 0)
{
errno = EINVAL;
return -1;
}
//起始文件记录号范围是 0000-9999
if(startRecordNumber > 0x270F)
{
errno = EINVAL;
return -1;
}
//文件传输一次最多传输122个数据
if( length > 122 )
{
errno = EINVAL;
return -1;
}
req_length = ctx->backend->build_request_basis(ctx,
MODBUS_FC_WRITE_FILE_RECORD,
0,0,req);
req[2] = length*2 + 7; //request Data length
req[3] = 0x06; //reference type must be specified as 6
req[4] = fileNumber >> 8; //File Number High
req[5] = fileNumber & 0x00ff; //File Number Low
req[6] = startRecordNumber >> 8; //The starting record number high within the file
req[7] = startRecordNumber & 0x00ff; //The starting record number low within the file
req[8] = length >> 8; //The length of the record to be written
req[9] = length & 0x00ff; //The length of the record to be written
for(int i = 0; i < length; i++)
{
req[2*i + 10] = fileData[i] >> 8;
req[2*i + 11] = fileData[i] & 0x00ff;
}
rc = send_msg(ctx, req, length*2 + 10);
if (rc > 0)
{
uint8_t rsp[MAX_MESSAGE_LENGTH];
rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
if (rc == -1)
return -1;
rc = check_confirmation(ctx, req, rsp, rc);
if (rc == -1)
return -1;
}
return rc;
}
四、读文件函数定义
int modbus_read_file_record(modbus_t *ctx, uint16_t fileNumber, uint16_t startRecordNumber,
uint16_t length, uint16_t *dest)
{
int rc;
int req_length;
uint8_t req[MAX_MESSAGE_LENGTH];
if (ctx == NULL || length <= 0)
{
errno = EINVAL;
return -1;
}
if(fileNumber == 0)
{
errno = EINVAL;
return -1;
}
//起始文件记录号范围是 0000-9999
if(startRecordNumber > 0x270F)
{
errno = EINVAL;
return -1;
}
req_length = ctx->backend->build_request_basis(ctx,
MODBUS_FC_READ_FILE_RECORD,
0,0,req);
req[2] = 7; //request Data length
req[3] = 0x06; //reference type must be specified as 6
req[4] = fileNumber >> 8; //File Number High
req[5] = fileNumber & 0x00ff; //File Number Low
req[6] = startRecordNumber >> 8; //The starting record number high within the file
req[7] = startRecordNumber & 0x00ff; //The starting record number low within the file
req[8] = length >> 8; //The length of the record to be written
req[9] = length & 0x00ff; //The length of the record to be written
rc = send_msg(ctx, req, 10);
if (rc > 0)
{
int offset;
int i;
uint8_t rsp[MAX_MESSAGE_LENGTH];
rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
if (rc == -1)
return -1;
rc = check_confirmation(ctx, req, rsp, rc);
if (rc == -1)
return -1;
offset = ctx->backend->header_length;
for (i = 0; i < rc; i++)
{
/* shift reg hi_byte to temp OR with lo_byte */
dest[i] = (rsp[offset + 4 + (i << 1)] << 8) |
rsp[offset + 5 + (i << 1)];
}
}
return rc;
}
五、函数接口用法简单示例
1、写文件。
uint16_t modbusData[122];
for(int i = 0; i < 122; i++)
{
modbusData[i] = i;
}
modbus_write_file_record(modbus, 1, 1, modbusData,2);
2、读文件。
uint16_t modbusData[100];
modbus_read_file_record(modbus, 1, 1, 2, modbusData)
for(int i = 0; i < 2; i++)
{
cout << i << ":" << modbusData[i] << endl;
}
六、用串口助手测试
1、写文件测试。
附上写文件回复的示例数据:
01 15 0D 06 00 01 00 01 00 03 4F 60 59 7D FF 01 19 8B2、读文件测试。
附上读文件回复的示例数据:
01 14 06 05 06 4F 60 59 7D 05 71
七、也可以直接下载我改完的modbus.c和modbus.h文件,替换掉原来的文件就可以直接用了