MODBUS-RTU主从站C语言面向对象框架
MODBUS-RTU主从站C语言面向对象框架
本着学习Modbus协议,研究了FreeModbus 在其框架上魔改,C语言面向对象的方式重写了RTU
与平台代码分离,可移植在任意设备上
现在是第三版了,比较完善,用了很多数据结构(哈希Map,无锁队列,时间轮定时器),提升性能
1.代码下载
完整项目
https://gitee.com/xin___yue/XinYueC整个克隆下来推荐VS打开
2.Modbus串口配置

1.这个文件是我写的Modbus测试 目前是测试485 8路8入 IO模块
2.我这边是COM2口38400波特率 ,m_portNum 就填2,m_baudRate 38400
3.抽象了输入输出,所以不仅仅可以用在串口 ,只需要对XIODeviceBase类派生即可用在不同的设备或平台上
4.如下timerT35Expired和timerSendExpired 可以为空,会启用全局的时间轮定时器,在单片机上如果CPU占用过高,可以使用XTimerBase类继承后使用单片机的定时器解放CPU一般只要timerT35Expired即可(这个调用比较频繁)
XModbus* XModbus_create_RTU_SerialPort(XSerialPortBase* serial, XTimerBase* timerT35Expired, XTimerBase* timerSendExpired)
3.打开运行测试



如果运行不显示这些就去XDataFrameCommConfig.h 和XModbusConfig.h 开启宏显示
modbus继承自XDataFrameComm所以一些配置在XDataFrameCommConfig.h
4.移植接口介绍
如果是移植到其他平台 就继承下面的XSerialPortBase 类 重写一些输入输出
如下所示Windows平台和STM32F407标准库我都移植好了,可直接使用
#ifndef XSERIALPORTBASE_H
#define XSERIALPORTBASE_H
#ifdef __cplusplus
extern "C" {
#endif
#include<stdint.h>
#include<stdbool.h>
#include"XIODeviceBase.h"
//XSerialPortDevice虚函数表
extern XVtable* XSerialPortVtable;
#define XSERIALPORT_VTABLE_SIZE (XCLASS_VTABLE_GET_SIZE(XIODeviceBase)) //XSerialPortDevice容器虚函数表大小
// 跨平台停止位枚举
typedef enum
{
SP_ST_One, // 1位停止位
SP_ST_OnePointFive, // 1.5位停止位
SP_ST_Two, // 2位停止位
SP_ST_ZeroPointFive//0.5位停止位
}XSerialPortBaseStopBits;
// 跨平台数据位枚举
typedef enum
{
SP_DB_Five = 5, // 5位数据位
SP_DB_Six = 6, // 6位数据位
SP_DB_Seven = 7, // 7位数据位
SP_DB_Eight = 8, // 8位数据位
SP_DB_Nine = 9 // 9位数据位
}XSerialPortBaseDataBits;
/*! \brief 串口传输校验位类型 */
typedef enum
{
SP_PAR_NONE = 0, /*!< 无校验 */
SP_PAR_ODD, /*!< 奇校验 */
SP_PAR_EVEN, /*!< 偶校验 */
SP_PAR_Mark, // 标记校验(始终为1)
SP_PAR_Space // 空格校验(始终为0)
} XSerialPortBaseParity;
// 流控制类型枚举
typedef enum
{
SP_FC_None, // 无流控制(默认)
SP_FC_Hardware, // 硬件流控制(RTS/CTS)
SP_FC_Software, // 软件流控制(XON/XOFF)
SP_FC_Both // 同时使用硬件和软件流控制
}XSerialPortBaseFlowControl;
//串口设备抽象类
typedef struct XSerialPortBase
{
XIODeviceBase m_parent;//父对象
uint8_t m_portNum;//端口号
uint32_t m_baudRate;//波特率
XSerialPortBaseDataBits m_dataBits;//数据位
XSerialPortBaseStopBits m_stopBits;//停止位
XSerialPortBaseParity m_parity;//校验
XSerialPortBaseFlowControl m_flowControl;//流控制
}XSerialPortBase;//串口
//初始化类
XVtable* XSerialPortBase_class_init();
XSerialPortBase* XSerialPortBase_create(XVtable* vtable);
void XSerialPortBase_init(XSerialPortBase* serial, XVtable* vtable);
bool XSerialPortBase_open_base(XSerialPortBase* serial, XIODeviceBaseMode mode, uint8_t portNum, uint32_t baudRate, XSerialPortBaseParity parity);
#define XSerialPortBase_delete_base XIODeviceBase_delete_base
#define XSerialPortBase_setWriteBuffer_base XIODeviceBase_setWriteBuffer_base
#define XSerialPortBase_setReadBuffer_base XIODeviceBase_setReadBuffer_base
#define XSerialPortBase_setDevice_base XIODeviceBase_setDevice_base
#define XSerialPortBase_write_base XIODeviceBase_write_base
#define XSerialPortBase_read_base XIODeviceBase_read_base
#define XSerialPortBase_getBytesAvailable_base XIODeviceBase_getBytesAvailable_base
#define XSerialPortBase_getBytesToWrite_base XIODeviceBase_getBytesToWrite_base
#define XSerialPortBase_isOpen XIODeviceBase_isOpen
#define XSerialPortBase_close_base XIODeviceBase_close_base
#define XSerialPortBase_poll_base XIODeviceBase_poll_base
#define XSerialPortBase_writeFull_base XIODeviceBase_writeFull_base
//以下是平台的具体实现
#ifdef WIN32
#include"XSerialPortWin32.h"
#elif defined(USE_STDPERIPH_DRIVER)
#include"XSerialPortSTM32.h"
#endif
#ifdef __cplusplus
}
#endif
#endif
5.使用示例
#include"XProtocolStackTest.h"
#include"XModbus.h"
#include"XSerialPortBase.h"
#include"XMemory.h"
#include"XCrc.h"
#include"XModbusFrame.h"
#include"XTimerGroupBase.h"
#include"XModbusRegister.h"
#include"XModbusDigitalSwitch.h"
#include"XSwitchDeviceModbus.h"
static XSwitchDeviceModbus* SW;
static void StateChangeCallback0(XSwitchDeviceBase* sw)
{
printf("in0:%s\n",sw->m_state?"true":"false");
XSwitchDeviceBase_setState_base(SW, sw->m_state);
}
static void StateChangeCallback1(XSwitchDeviceBase* sw)
{
printf("in1:%s\n", sw->m_state ? "true" : "false");
}
static void StateChangeCallback2(XSwitchDeviceBase* sw)
{
printf("in2:%s\n", sw->m_state ? "true" : "false");
}
void XModbusTest()
{
XSerialPortBase* serial = XSerialPortWin32_create();
serial->m_baudRate = 38400;
serial->m_portNum = 2;
XModbus* modbus = XModbus_create_RTU_SerialPort(serial,NULL,NULL);
XModbus_setAddress(modbus,2);
XModbus_setMode(modbus, XMB_RTU_MASTER);
//XModbus_setRecvHandMode(modbus, XMB_RecvHand_CodeOnly);
XModbusDigitalSwitch* ds = XModbusDigitalSwitch_create(modbus,0x01,8,8);
//XModbusDigitalSwitch_bindModbus_RTU(ds,modbus);
XModbusDigitalSwitch_setScanningPeriod(ds,50);
{
XSwitchDeviceModbus* sw0 = XSwitchDeviceModbus_create(ds, 0);
XSwitchDeviceBase_open_base(sw0, XIODeviceBase_WriteOnly);
XSwitchDeviceBase_setState_base(sw0, true);
SW = sw0;
XSwitchDeviceModbus* sw1 = XSwitchDeviceModbus_create(ds, 1);
XSwitchDeviceBase_open_base(sw1, XIODeviceBase_WriteOnly);
XSwitchDeviceBase_setState_base(sw1, true);
XSwitchDeviceModbus* sw2 = XSwitchDeviceModbus_create(ds, 2);
XSwitchDeviceBase_open_base(sw2, XIODeviceBase_WriteOnly);
XSwitchDeviceBase_setState_base(sw2, true);
}
{
XSwitchDeviceModbus* sw0 = XSwitchDeviceModbus_create(ds, 0);
XSwitchDeviceBase_open_base(sw0, XIODeviceBase_ReadOnly);
XSwitchDeviceBase_setStateChangeCallback(sw0, StateChangeCallback0);
XSwitchDeviceModbus* sw1 = XSwitchDeviceModbus_create(ds, 1);
XSwitchDeviceBase_open_base(sw1, XIODeviceBase_ReadOnly);
XSwitchDeviceBase_setStateChangeCallback(sw1, StateChangeCallback1);
XSwitchDeviceModbus* sw2 = XSwitchDeviceModbus_create(ds, 2);
XSwitchDeviceBase_open_base(sw2, XIODeviceBase_ReadOnly);
XSwitchDeviceBase_setStateChangeCallback(sw2, StateChangeCallback2);
}
XModbusRegister* Register=XModbusRegister_create(16);
//设置从站的功能码回调函数
{
XModbus_addRecvHand_CodeOnly(modbus, MB_FUNC_READ_HOLDING_REGISTER, XModbusRegister_0x03_RTU_slaveRecvHandCb, Register);
}
{
XModbus_addRecvHand_CodeOnly(modbus, MB_FUNC_WRITE_REGISTER, XModbusRegister_0x06_RTU_slaveRecvHandCb, Register);
}
{//发送一帧数据
XVector* frame = XVector_Create(uint8_t);
char buff[] = {0x00,0x01};
XModbusFrameRTU_setFrameData_0x06_request(frame, 0x01, 0x01, buff);
//XModbus_sendData_base(modbus, frame);
//XModbus_sendDataPeriodicMaster_base(modbus, frame,50);
}
{//发送一帧数据
// printf("%d\n",frame->recvHandle->timeout);
char buff[] = { 0x00,0x01 };
//XModbusFrameRTU_setFrameData_0x06_request(frame, 0x01, 0x00, buff);
//XModbusBase_sendFrame(modbus, frame);
//printf("发送\n");
//XModbusBase_sendFrameRegularlyMaster(modbus, frame,50);
XVector* frame = XVector_Create(uint8_t);
uint8_t State =0;
XMODBUS_UINT8_SET_BITS(&State, 0, 1);
XMODBUS_UINT8_SET_BITS(&State, 2, 1);
XMODBUS_UINT8_SET_BITS(&State,3,1);
XMODBUS_UINT8_SET_BITS(&State, 7, 1);
XModbusFrameRTU_setFrameData_0x0F_request(frame, 0x01,0x0, 8, &State);
//XModbus_sendData_base(modbus, frame);
}
{
XVector* frame = XVector_Create(uint8_t);
XModbusFrameRTU_setFrameData_0x01_request(frame, 0x01, 0x0, 8);
//XModbus_sendDataPeriodicMaster_base(modbus, frame,500);
}
//使能打开Modbus
// XModbus_enable(modbus);
XModbus_connect_base(modbus);
// XModbusTest_threadReceiveCreate(modbus);
//开始轮询
while (true)
{
XModbus_poll_base(modbus);
XTimerGroupBase_global_poll();
// XModbus_poll(modbus);
// XModbusTest_SerialPoll(modbus);
}
}
6.RTU帧构建api
#ifndef XMODBUSFRAME_RTU_H
#define XMODBUSFRAME_RTU_H
/*针对RTU的数据帧构建*/
#ifdef __cplusplus
extern "C" {
#endif
#include"XModbusConfig.h"
#if MB_RTU_ENABLED
#include<stdint.h>
#include<stdbool.h>
#include"XModbusEnum.h"
#include"XTypes.h"
//RTU模式下的数据
typedef struct XModbusFrameRTU
{
uint8_t address;//地址
uint8_t funcCode;//功能码
union {
uint16_t coilsAddress;//线圈地址
uint16_t discAddress;//离散地址
uint16_t regAddress;//寄存器地址
//uint8_t error;//错误码
};
union {
uint16_t coilsCount;//线圈数量
uint16_t discCount;//离散数量
uint16_t regCount;//寄存器数量
};
uint16_t crc16;//校验码
union {
XVector* data;//数据 ---线圈/离散/寄存器
XModbusException exception;//错误码
};
}XModbusFrameRTU;
XModbusFrameRTU* XModbusFrameRTU_create();
void XModbusFrameRTU_delete(XModbusFrameRTU* data);
/* -------------------------------------- 线圈与离散输入-------------------------------------*/
#define XMODBUS_COILS_STATE_ON 0xFF00//线圈打开状态
#define XMODBUS_COILS_STATE_OFF 0x0000//线圈关闭状态
/*
* @brief 一个字节中按偏移量设置一个比特位
* @param ByteBuff:uint8_t* 类型 指向要操作的字节
* @param Offset:偏移量(0~7)
* @param State:要设置的状态(0或1)
* @retval
*/
#define XMODBUS_UINT8_SET_BITS(ByteBuff,Offset,State) (*(ByteBuff)=((State<<Offset)|(*(ByteBuff))))
/*
* @brief 一个字节中按偏移量查看一个比特位
* @param ByteBuff:uint8_t* 类型 指向要查看的字节
* @param Offset:偏移量(0~7)
* @retval 状态(0或1)
*/
#define XMODBUS_UINT8_GET_BITS(ByteBuff,Offset) (((*(ByteBuff))>>Offset)&0x1)
/* --------------------------------- RTU模式初始化数据帧-------------------------------------*/
/*
* @brief 初始化RTU格式数据帧 主机地址(1)+功能码(1)+数据(dataSIze)+crc16(2)
* @param frame:XModbus帧数据
* @param address:从机地址
* @param funcCode:功能码
* @param dataSIze:功能码和CRC16之间的数据大小(字节)
* @retval
*/
void XModbusFrameRTU_initDataFrame(XByteArray* frameData, uint8_t address, uint8_t funcCode,uint16_t dataSIze);
/* ----------------------- RTU模式根据功能码构建数据帧-------------------------------------*/
/*
* @brief 构建数据帧0x01功能码请求帧 读线圈状态.
* @param frame:XModbus帧数据
* @param address:从机地址
* @param coilsAddress:线圈地址(数据帧中的地址)
* @param coilsCount:线圈数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x01_request(XByteArray* frameData, uint8_t address, uint16_t coilsAddress, const uint16_t coilsCount);
/*
* @brief 构建数据帧0x01功能码响应帧 读线圈状态
* @param frame:XModbus帧数据
* @param address:主机地址
* @param coilsData:线圈数据缓冲区
* @param coilsCount:线圈数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x01_reply(XByteArray* frameData, uint8_t address, uint8_t* coilsData, const uint16_t coilsCount);
/*
* @brief 构建数据帧0x02功能码请求帧 读离散输入状态.
* @param frame:XModbus帧数据
* @param address:从机地址
* @param discAddress:离散输入地址(数据帧中的地址)
* @param discCount:离散输入数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x02_request(XByteArray* frameData, uint8_t address, uint16_t discAddress, const uint16_t discCount);
/*
* @brief 构建数据帧0x01功能码响应帧 读离散输入状态
* @param frame:XModbus帧数据
* @param address:主机地址
* @param discAddress:离散输入数据缓冲区
* @param discCount:离散输入数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x02_reply(XByteArray* frameData, uint8_t address, uint8_t* discAddress, const uint16_t discCount);
/*
* @brief 构建数据帧0x03功能码请求帧 读保持寄存器.
* @param frame:XModbus帧数据
* @param address:从机地址
* @param regAddress:寄存器地址(数据帧中的地址)
* @param regCount:寄存器数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x03_request(XByteArray* frameData, uint8_t address, uint16_t regAddress,const uint16_t regCount);
/*
* @brief 构建数据帧0x03功能码响应帧 读保持寄存器
* @param frame:XModbus帧数据
* @param address:主机地址
* @param regData:寄存器数据缓冲区
* @param regCount:寄存器数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x03_reply(XByteArray* frameData, uint8_t address, uint16_t* regData, const uint16_t regCount);
/*
* @brief 构建数据帧0x04功能码请求帧 读输入寄存器.
* @param frame:XModbus帧数据
* @param address:从机地址
* @param regAddress:寄存器地址(数据帧中的地址)
* @param regCount:寄存器数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x04_request(XByteArray* frameData, uint8_t address, uint16_t regAddress,const uint16_t regCount);
/*
* @brief 构建数据帧0x04功能码响应帧 读输入寄存器
* @param frame:XModbus帧数据
* @param address:主机地址
* @param regData:寄存器数据缓冲区(数据内存位置)
* @param regCount:寄存器数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x04_reply(XByteArray* frameData, uint8_t address, uint16_t* regData,uint16_t regCount);
/*
* @brief 构建数据帧0x05功能码请求帧 写单个线圈
* @param frame:XModbus帧数据
* @param address:从机地址
* @param coilsAddress:线圈地址(数据帧中的地址)
* @param coilsState:指向要写入的线圈状态 值填 XMODBUS_COILS_STATE_ON(开) XMODBUS_COILS_STATE_OFF(关)
* @retval
*/
void XModbusFrameRTU_setFrameData_0x05_request(XByteArray* frameData, uint8_t address, uint16_t coilsAddress, uint16_t coilsState);
/*
* @brief 构建数据帧0x05功能码响应帧 写单个线圈(实际成功直接转发请求帧)
* @param frame:XModbus帧数据
* @param address:主机地址
* @param coilsAddress:线圈地址(数据帧中的地址)
* @param coilsState:指向要写入的线圈状态 值填 XMODBUS_COILS_STATE_ON(开) XMODBUS_COILS_STATE_OFF(关)
* @retval
*/
void XModbusFrameRTU_setFrameData_0x05_reply(XByteArray* frameData, uint8_t address, uint16_t coilsAddress, uint16_t coilsState);
/*
* @brief 构建数据帧0x06功能码请求帧 写寄存器
* @param frame:XModbus帧数据
* @param address:从机地址
* @param regAddress:寄存器地址(数据帧中的地址)
* @param regData:指向要写入的寄存器数据的缓冲区(2字节)
* @retval
*/
void XModbusFrameRTU_setFrameData_0x06_request(XByteArray* frameData, uint8_t address, uint16_t regAddress,const uint16_t* regData);
/*
* @brief 构建数据帧0x06功能码响应帧 写寄存器(实际成功直接转发请求帧)
* @param frame:XModbus帧数据
* @param address:主机地址
* @param regAddress:寄存器地址(数据帧中的地址)
* @param regData:指向要写入的寄存器数据的缓冲区(2字节)
* @retval
*/
void XModbusFrameRTU_setFrameData_0x06_reply(XByteArray* frameData, uint8_t address, uint16_t regAddress,const uint16_t* regData);
/*
* @brief 构建数据帧0x10功能码请求帧 写多个寄存器
* @param frame:XModbus帧数据
* @param address:从机地址
* @param regAddress:寄存器起始地址(数据帧中的地址)
* @param regCount:要写入的寄存器数量
* @param regData:指向要写入的寄存器数据的缓冲区(大小不小于regCount*2 否则会出错)传入数据
* @retval
*/
void XModbusFrameRTU_setFrameData_0x10_request(XByteArray* frameData, uint8_t address, uint16_t regAddress,uint16_t regCount, uint16_t* regData);
/*
* @brief 构建数据帧0x10功能码响应帧 写多个寄存器
* @param frame:XModbus帧数据
* @param address:主机地址
* @param regAddress:寄存器地址(数据帧中的地址)
* @param regCount:写入的寄存器数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x10_reply(XByteArray* frameData, uint8_t address, uint16_t regAddress, uint16_t regCount);
/*
* @brief 构建数据帧0x0F功能码请求帧 写多个线圈
* @param frame:XModbus帧数据
* @param address:从机地址
* @param coilsAddress:线圈地址(数据帧中的地址)
* @param coilsCount:线圈数量
* @param coilsData:指向要写入的线圈数据的缓冲区()传入数据
* @retval
*/
void XModbusFrameRTU_setFrameData_0x0F_request(XByteArray* frameData, uint8_t address, uint16_t coilsAddress, uint16_t coilsCount, uint8_t* coilsData);
/*
* @brief 构建数据帧0x0F功能码响应帧 写多个线圈
* @param frame:XModbus帧数据
* @param address:主机地址
* @param coilsAddress:线圈地址(数据帧中的地址)
* @param coilsCount:线圈数量
* @retval
*/
void XModbusFrameRTU_setFrameData_0x0F_reply(XByteArray* frameData, uint8_t address, uint16_t coilsAddress, uint16_t coilsCount);
/*
* @brief 构建数据帧0x8X功能码响应帧 异常响应
* @param frame:XModbus帧数据
* @param address:主机地址
* @param funcCode:正常功能码(将自动组装成异常功能码)
* @param exception:错误码
* @retval
*/
void XModbusFrameRTU_setFrameData_0x8X_reply(XByteArray* frameData, uint8_t address, uint8_t funcCode, XModbusException exception);
/* ----------------------- RTU模式解析数据帧-------------------------------------*/
//将一个数据解析成RTU 请求帧
bool XModbusFrameRTU_parseData_request(XModbusFrameRTU* frame, XByteArray* frameData);
//将一个数据解析成RTU 响应帧
bool XModbusFrameRTU_parseData_reply(XModbusFrameRTU* frame, XByteArray* frameData);
//解析RTU模式下数据帧中主机地址
uint8_t XModbusFrameRTU_parseAddress(XByteArray* frameData);
//解析RTU模式下数据帧中功能码
uint8_t XModbusFrameRTU_parseFuncCode(XByteArray* frameData);
//转16进制显示
XString* XModbusFrameRTU_to16HexString(XByteArray* frameData);
#endif // MB_RTU_ENABLED
#ifdef __cplusplus
}
#endif
#endif
7. RTU功能码处理
1.线圈和离散的功能码处理
#ifndef XMODBUSCOILS_H
#define XMODBUSCOILS_H
#ifdef __cplusplus
extern "C" {
#endif
#include"XModbusDeviceObject.h"
#include<stdint.h>
#include<stdbool.h>
//功能描述: 处理0x01(读线圈)、0x02(读离散)、0x05(写单个线圈)、0x0F(写多个线圈)功能码,操作线圈状态
typedef struct XModbusCoilsDisc
{
XModbusDeviceObject parent;
uint16_t count;//数量
}XModbusCoilsDisc;
//创建线圈或离散功能类 要创建几个离散或线圈
XModbusCoilsDisc* XModbusCoilsDisc_create(uint16_t count);
void XModbusCoilsDisc_delete(XModbusCoilsDisc* pRegHandler);
bool XModbusCoilsDisc_write_bool(XModbusCoilsDisc* pRegHandler, uint16_t address, bool state);
//写入线圈或离散
bool XModbusCoilsDisc_write(XModbusCoilsDisc* pRegHandler, uint16_t address, uint16_t count, const uint8_t* writeArray);
// 读取线圈或离散
bool XModbusCoilsDisc_read(XModbusCoilsDisc* pRegHandler, uint16_t address, uint16_t count, uint8_t* readArray, uint16_t readArraySize);
//获取指定地址寄存器的线圈或离散状态
bool XModbusCoilsDisc_at(XModbusCoilsDisc* pRegHandler, uint16_t regAddress);
/* 以下都是功能码回调函数*/
//0x01读线圈接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusCoilsDisc_0x01_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x02读离散接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusCoilsDisc_0x02_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x01读线圈接收回调函数 从站接收是请求报文
void XModbusCoilsDisc_0x01_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x02读离散接收回调函数 从站接收是请求报文
void XModbusCoilsDisc_0x02_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x05写单个线圈接收回调函数 从站接收是请求报文
void XModbusCoilsDisc_0x05_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x0F写多个线圈接收回调函数 从站接收是请求报文
void XModbusCoilsDisc_0x0F_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
#ifdef __cplusplus
}
#endif
#endif // !XModbusFuncCode_H
2.寄存器的功能码处理
#ifndef XMODBUSREGISTER_H
#define XMODBUSREGISTER_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct XModbus XModbus;
#include"XModbusDeviceObject.h"
#include<stdint.h>
#include<stdbool.h>
//功能描述: 处理0x03(读)0x04(只读)、0x06(写单个)、0x10(写多个)功能码,操作保持寄存器
//寄存器功能类
typedef struct XModbusRegister
{
XModbusDeviceObject parent;
}XModbusRegister;
//创建保持寄存器功能类 要创建几个保持寄存器
XModbusRegister* XModbusRegister_create(uint16_t regCount);
void XModbusRegister_delete(XModbusRegister* pRegFunc);
//写入寄存器
bool XModbusRegister_write_uint16_t(XModbusRegister* regFunc, uint16_t regAddress,uint16_t value);
//写入寄存器 数组的方式
bool XModbusRegister_write(XModbusRegister* regFunc, uint16_t regAddress, uint16_t regCount,const uint8_t* writeArray);
// 读取寄存器
bool XModbusRegister_read(XModbusRegister* regFunc, uint16_t regAddress, uint16_t regCount, uint8_t* readArray, uint16_t readArraySize);
//获取指定地址寄存器的缓冲区地址
uint16_t* XModbusRegister_at(XModbusRegister* regFunc, uint16_t regAddress);
//以下都是功能码回调函数
//0x03读保持寄存器接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusRegister_0x03_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x04读输入寄存器接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusRegister_0x04_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x06写保持寄存器接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusRegister_0x06_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x03读保持寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x03_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x04读输入寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x04_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x06写保持寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x06_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x10写多个保持寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x10_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
#ifdef __cplusplus
}
#endif
#endif // !XModbusFuncCode_H
8.其他用法
使用XModbusDigitalSwitch类可以吧 远程IO的输入输出点映射成XSwitchDeviceBase类(开关类)
在测试代码中就使用了这功能
#ifndef XSWITCHDEVICEBASE_H
#define XSWITCHDEVICEBASE_H
#ifdef __cplusplus
extern "C" {
#endif
#include"XIODeviceBase.h"
#define XSWITCHDEVICEBASE_VTABLE_SIZE (XCLASS_VTABLE_GET_SIZE(XSwitchDeviceBase)) //XSwitchDeviceBase容器虚函数表大小
//XSwitchDevice虚函数表枚举
XCLASS_DEFINE_BEGING(XSwitchDeviceBase)
XCLASS_DEFINE_ENUM(XSwitchDeviceBase,SetState) = XCLASS_VTABLE_GET_SIZE(XIODeviceBase),
XCLASS_DEFINE_ENUM(XSwitchDeviceBase,GetState),
XCLASS_DEFINE_END(XSwitchDeviceBase)
typedef enum//触发方式
{
XSwitchDeviceBase_Trigger_High,//高电平触发
XSwitchDeviceBase_Trigger_Low,//低电平触发
XSwitchDeviceBase_Trigger_None,//无触发
}XSwitchDeviceBaseTriggerMode;
//开关设备
typedef struct XSwitchDeviceBase
{
XIODeviceBase m_parent;//父对象
bool m_buffer;//存储状态
bool m_state;//状态 开或关
XSwitchDeviceBaseTriggerMode m_triggerMode;//触发方式
void (*m_stateChangeCallback)(XSwitchDeviceBase* io);//状态改变回调函数
}XSwitchDeviceBase;
//初始化类
XVtable* XSwitchDeviceBase_class_init();
//开关设备
XSwitchDeviceBase* XSwitchDeviceBase_create(XVtable* vtable);
//初始化
void XSwitchDeviceBase_init(XSwitchDeviceBase* sw, XVtable* vtable);
//设置状态改变回调函数
void XSwitchDeviceBase_setStateChangeCallback(XSwitchDeviceBase* sw, void (*callback)(XSwitchDeviceBase* io));
//设置触发方式 设备开的状态时候的电平
void XSwitchDeviceBase_setTriggerMode(XSwitchDeviceBase* sw, XSwitchDeviceBaseTriggerMode mode);
XSwitchDeviceBaseTriggerMode XSwitchDeviceBase_getTriggerMode(XSwitchDeviceBase* sw);
//设置开关设备状态
void XSwitchDeviceBase_setState_base(XSwitchDeviceBase* sw,bool state);
//获取状态
bool XSwitchDeviceBase_getState_base(XSwitchDeviceBase* sw);
#define XSwitchDeviceBase_isOpen XIODeviceBase_isOpen
#define XSwitchDeviceBase_open_base XIODeviceBase_open_base
#define XSwitchDeviceBase_close_base XIODeviceBase_close_base
#define XSwitchDeviceBase_setDevice_base XIODeviceBase_setDevice_base
#define XSwitchDeviceBase_delete_base XIODeviceBase_delete_base
#define XSwitchDeviceBase_poll_base XIODeviceBase_poll_base
#if defined(USE_STDPERIPH_DRIVER)
#include"XSwitchDeviceSTM32.h"
#endif
#ifdef __cplusplus
}
#endif
#endif
#ifndef XMODBUSREGISTER_H
#define XMODBUSREGISTER_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct XModbus XModbus;
#include"XModbusDeviceObject.h"
#include<stdint.h>
#include<stdbool.h>
//功能描述: 处理0x03(读)0x04(只读)、0x06(写单个)、0x10(写多个)功能码,操作保持寄存器
//寄存器功能类
typedef struct XModbusRegister
{
XModbusDeviceObject parent;
}XModbusRegister;
//创建保持寄存器功能类 要创建几个保持寄存器
XModbusRegister* XModbusRegister_create(uint16_t regCount);
void XModbusRegister_delete(XModbusRegister* pRegFunc);
//写入寄存器
bool XModbusRegister_write_uint16_t(XModbusRegister* regFunc, uint16_t regAddress,uint16_t value);
//写入寄存器 数组的方式
bool XModbusRegister_write(XModbusRegister* regFunc, uint16_t regAddress, uint16_t regCount,const uint8_t* writeArray);
// 读取寄存器
bool XModbusRegister_read(XModbusRegister* regFunc, uint16_t regAddress, uint16_t regCount, uint8_t* readArray, uint16_t readArraySize);
//获取指定地址寄存器的缓冲区地址
uint16_t* XModbusRegister_at(XModbusRegister* regFunc, uint16_t regAddress);
//以下都是功能码回调函数
//0x03读保持寄存器接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusRegister_0x03_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x04读输入寄存器接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusRegister_0x04_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x06写保持寄存器接收回调函数 主站是响应报文 模板并不完整,返回帧并没有告诉有几个线圈
void XModbusRegister_0x06_RTU_masterRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x03读保持寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x03_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x04读输入寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x04_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x06写保持寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x06_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
//0x10写多个保持寄存器接收回调函数 从站接收是请求报文
void XModbusRegister_0x10_RTU_slaveRecvHandCb(XModbusRecvMatch* math, XModbus* modbus, XModbusFrame* recvFrame, XModbusDeviceObject* hand);
#ifdef __cplusplus
}
#endif
#endif // !XModbusFuncCode_H
9.结束
1.实际我是为单片机准备的,单片机上要实现动态内存分配,可以通过XMemory.h中的方法设置内存管理方法,全局生效,操作系统的话有没有都行,有的话使用更方便
2.可右键Cmakelist.txt 安装,将导出目标平台动态和静态库并将所有使用到的头文件和源文件也一并导出(方便使用源代码移植到单片机)
目前仅实现了RTU 的主从站,数据收发 远程IO映射
主站发送数据,回调函数的方式响应接收
定时发送采用了时间轮(XTimerGroupWheel),功能码采用了哈希查找(XHashMap)
效率相比第一版提升了很多
更多推荐



所有评论(0)