在 FreeModbus 中添加主机模式。
FreeModbus是一款开源的Modbus协议栈,但是只有从机开源,主机源码是需要收费的。同时网上也没有发现比较好的开源的Modbus主机协议栈,所以才开发这款支持主机模式的FreeModbus协议栈。本版FreeModbus版本号更改为V1.6,特性如下:
注:所有带_m后缀的文件为主机模式下必须使用的文件,如使用从机模式则无需这些文件。
对于协议栈的移植主要包括两个方面,硬件及软件。用户需要根据自己的需求进行自行选择。
注:以下所有说明都主要针对Modbus主机模式进行介绍。
软件方面支持基于裸机及实时操作系统的移植;支持单个主机与单个从机同时独立运行。另外用户也可以修改协议栈的事件回调接口,使主机请求的接口采用阻塞及非阻塞模式;主机资源等待方面,用户也可以设置等待超时时间等等,诸多功能将会一一介绍。
对于操作系统与裸机目前协议栈都是支持的,但个人更加推荐采用实时操作系统,因为这样会使得接口调用及接口移植变得更加简单。目前移植完成的操作系统包括国人的 [RT-Thread][1] (详见项目源码) 、 UCOS 及 FreeRTOS。
操作系统与裸机移植的过程中涉及的文件为FreeModbus\port\portevent_m.c
该文件主要有以下需要用户移植的接口
在 基于操作系统移植 时,主要用到操作系统线程同步方面的技术,Modbus 协议栈自身需要使用操作系统自带的事件机制来实现事件的发送通知与等待获取,同时用户请求 Modbus 功能的线程与 Modbus 协议栈自身线程(Modbus Poll 线程)需要通过事件机制实现两个线程的同步;主机协议栈还需要一个主机资源占用的信号量,初始化默认为1,采用信号量保证了多线程同时发送主机请求时,只有一个线程可以使用主机。
在 基于裸机移植 时,需要通过软件模拟方式实现事件通知机制,事件等待及资源等待都得采用用户自定义延时及标志变量来实现,实现起来比操作系统模式下的线程同步机制要复杂很多。
数据缓冲区定义的位置位于 FreeModbus\port\\user_mb_app_m.c 文件顶部,共计 4种 数据类型。
FreeModbus从机默认使用 一维数组 作为缓存区数据结构,主机可以存储所有网内从机的数据,所以主机采用 二维数组 对所有从机节点数据进行存储。二维数组的列号代表寄存器、线圈及离散量地址,行号代表从机节点ID,但需要做减一处理,例如usMRegHoldBuf[2][1]代表从机ID为 3,保持寄存器地址为 1 的从机数据。
Modbus 一共有4种不同的数据类型,所有的 Modbus 功能都围绕这些数据类型进行操作。由于不同的用户数据缓冲区结构可能有所不同,那么对应的 Modbus 数据处理方式也就存在差异,所以用户需要把每种数据类型对应的操作,按照自己的数据缓冲区结构进行定制实现。 所有的 Modbus 数据处理回调接口如下:
接口 功能描述 eMBMasterRegInputCB 输入寄存器回调接口 eMBMasterRegHoldingCB 保持寄存器回调接口 eMBMasterRegCoilsCB 线圈回调接口 eMBMasterRegDiscreteCB 离散输入回调接口对于数组形式的数据缓冲区结构,源码中已经做好了移植,直接使用即可。也可以使用 EasyDataManager 库,采用链表作为缓冲区,该库还支持事件驱动,做到数据变化自动通知应用层。
移植 FreeModbus 协议栈主机部分时,在硬件方面需要修改串口及定时器配置,文件位于port文件下,用户需要根据自己的CPU进行移植修改。
注:协议栈默认自带STM32F103X移植文件,用户可以参考移植
这里提一下基于操作系统设备驱动框架的移植,后期协议栈会增加对 [RT-Thread][1] 自带设备驱动框架的移植,只要是 RT-Thread 的 BSP 支持的 MCU,用户都无需考虑底层的移植过程,减低移植成本。
涉及到串口的移植文件位于FreeModbus\port\portserial_m.c,在这个文件中用户需要对以下接口方法进行修改
pxMBMasterFrameCBTransmitterEmpty方法即可
prvvUARTRxISR
串口接收中断服务程序接口,按照默认方式,直接引用pxMBMasterFrameCBByteReceived方法即可
还需要在文件末尾增加 CPU 的自带的串口服务程序,将上表中的发送及接收中断程序接口,放到对应的中断服务程序中去即可。
涉及到定时器的移植文件位于FreeModbus\port\porttimer_m.c,在这个文件中用户需要对以下接口方法进行修改
usPrescalerValue及usT35TimeOut50us
vMBMasterPortTimersT35Enable
设置定时器按照T3.5时间开始计数
vMBMasterPortTimersConvertDelayEnable
设置定时器按照广播帧的转换延时时间开始计数
vMBMasterPortTimersRespondTimeoutEnable
设置定时器按照响应超时时间开始计数
vMBMasterPortTimersDisable
失能定时器,定时器将停止计数
prvvTIMERExpiredISR
定时器中断服务程序接口,按照默认方式,直接引用pxMBMasterPortCBTimerExpired方法即可
注: 1、
usPrescalerValue及usT35TimeOut50us在文件顶部有定义 2、转换延时时间及响应超时时间在FreeModbus\modbus\include\mbconfig.h,用户可以根据自己系统的特点自行设置。
除上面接口方法外,用户需要在文件末尾增加 CPU 的自带的定时器中断服务程序,将上表中的定时器中断服务程序接口放进去。
Modbus 主机使用过程中与从机有很大不同,从机是需要被动等待主机请求,而主机则是主动发出请求,并接收处理从机响应。在主机发送广播请求的时候,从机不需要返回响应,所以广播请求适合主机的写从机数据命令,不适合读从机数据命令。 主机请求API中的所有方法的返回值格式都相同,返回值意义如下。
返回值 描述 MB_MRE_NO_ERR 正常,没错误 MB_MRE_NO_REG 寄存器、线圈或离散输入地址出错 MB_MRE_ILL_ARG 入参格式有误 MB_MRE_REV_DATA 接收数据出错 MB_MRE_TIMEDOUT 响应超时。主机在设定的时间内未收到从机响应。 MB_MRE_MASTER_BUSY 主机忙。在设定的时间内,没有请求没有被发送。 MB_MRE_EXE_FUN 主机收到响应后,执行Modbus方法(功能)出错。所有的主机请求方法都是 线程安全 的也是 阻塞模式 的。在使用过程中,只要在设定的超时时间内没有得到主机资源,就会返回主机忙;如果在设定的超时时间内得到主机资源,那么必须等待得到请求结果后才会返回。
往从机某个保持寄存器中写入数据
eMBMasterReqErrCode eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr,
USHORT usRegAddr,
USHORT usRegData,
LONG lTimeOut );
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usRegAddr
写寄存器的地址
usRegData
写寄存器的数据
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
往从机多个保持寄存器中写入数据。
eMBMasterReqErrCode eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usRegAddr,
USHORT usNRegs,
USHORT * pusDataBuffer,
LONG lTimeOut )
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usRegAddr
写寄存器的起始地址
usNRegs
写寄存器的总数
pusDataBuffer
写寄存器的数据
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
读取多个保持寄存器中的数据
eMBMasterReqErrCode eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr,
USHORT usRegAddr,
USHORT usNRegs,
LONG lTimeOut );
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usRegAddr
读寄存器的地址
usRegData
读寄存器的数量
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
先读多个寄存器,然后再写多个寄存器。
…
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usReadRegAddr
读寄存器的地址
usNReadRegs
读寄存器的数量
pusDataBuffer
写寄存器的数据
usWriteRegAddr
写寄存器的地址
usNWriteRegs
写寄存器的数量
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
读取多个输入寄存器中的数据
eMBMasterReqErrCode eMBMasterReqReadInputRegister( UCHAR ucSndAddr,
USHORT usRegAddr,
USHORT usNRegs,
LONG lTimeOut );
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usRegAddr
读寄存器的地址
usRegData
读寄存器的数量
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
往从机某个线圈中写入数据
eMBMasterReqErrCode eMBMasterReqWriteCoil( UCHAR ucSndAddr,
USHORT usCoilAddr,
USHORT usCoilData,
LONG lTimeOut )
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usCoilAddr
写线圈的地址
usCoilData
写线圈的数量
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
往从机多个线圈中写入数据。
eMBMasterReqErrCode eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
USHORT usCoilAddr,
USHORT usNCoils,
UCHAR * pucDataBuffer,
LONG lTimeOut)
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usCoilAddr
写线圈的起始地址
usNCoils
写线圈的总数
pucDataBuffer
写线圈的数据
lTimeOut
请求超时时间。支持永久等待,使用操作系统的永久等待参数即可。
读取多个线圈中的数据
eMBMasterReqErrCode eMBMasterReqReadCoils( UCHAR ucSndAddr,
USHORT usCoilAddr,
USHORT usNCoils ,
LONG lTimeOut )
参数
描述
ucSndAddr
请求的从机地址,0代表广播。
usCoilAddr
读线圈的地址
usNCoils
读线圈的数量
lTimeOu
暂无开放 Issues,或尚未同步最近议题。