在工控设备中,modbus协议是应用非常广泛的,所以我们的demo集成modbus,并通过modbus进行参数的设置及存储。
一.基于led的工程进行,modbus RTU功能的实现。
1.打开RT-thread settings功能设置,添加freemodbus 软件包,设置为从机模式
我们先使能一下demo,看下是如何实现的,我们再进行相应的优化,完成自己的modbus 从机模式,实现参数的设置。
2.扩展板的232接口使用的是usart6,使能串口6,并将modbus的接口改为6
查看一下rtconfig.h配置文件,确保更改成功。
3.PC打开modbus POLL
注意上面两点,demo中默认配置是这样的。
通讯成功。
二.Freemodbus协议解析
studio带给了我们很大的便利性,但同样我们关注的少了,理解就不够深入了,在项目级的开发中,简单配置得到的效果肯定是不满足要求的,我们还需要针对这个工程进行优化,例如将串口改为DMA的方式,查询的方式改为中断阻塞触发的方式,提高效率,减少资源占用。
首先我们对freeModbus进行一个简单的了解,其实modbus并不复杂,我们只需要了解它的各个接口部分,知道怎么优化,怎么和其他组件对接,怎么扩展功能就好,一些基础性的介绍,直接网上搜索就好了。
1.modbus基础概念
FreeMODBUS是一个奥地利人写的Modbus协议。从机开源,主机模式收费,armink 大神开发了这款支持主机模式的 FreeModbus 协议栈。
支持RTU,ASCII,TCP等模式。
https://github.com/RT-Thread-packages/freemodbus
详细可以查看上面地址。
2.modbus基本数据格式
这个是必须了解的,不止freemodbus,所有的modbus协议都要遵从这个结构。
3.常用的功能码:
01 读取单个/多个线圈状态(类似DO:数字输出)
02 读取单个/多个离散输入(类似DI:数字输入)
03 读取单个/多个保存寄存器
04 读取单个/多个输入寄存器(类似AI:模拟输入)
05 写单个线圈状态
06 写单个保存寄存器
15 写多个线圈
16 写多个保存寄存器
这个功能码就是我们常用的,我们经常也会说0x,1x,3x,4x寄存器
0x (01,05/15功能码),只有0,1状态,可读可写,主要当做开关,使能标志等控制
1x (02功能码),只读的,可以表示灯的状态,故障状态等,也只有0,1。
3x (04功能码),只读的,可以用于数据值的显示等包含很多中数据类型。
4x (03,06/16功能码),读写,主要用于各种类型的数据的参数设置
4.接口解析
打开从机实例(sample_mb_slave.c):
可以看到核心的接口
(1)eMBInit(MB_RTU, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
初始化RTU模式,从站ID,串口号,波特率,校验位
这样就很明确了,我们初始化配置就是在这个函数实现,之后进行优化,肯定也要进行相应初始化代码的修改及添加。
(2)eMBEnable();
1:设置Modbus协议栈工作状态eMBState为STATE_ENABLED;
2:调用pvMBFrameStartCur()函数激活协议栈
(3)eMBPoll();
· 1:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
· *2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
初始化,使能,轮询状态,就是上面三个函数的功能。
其实不单单是freemodbus,假如让你自己实现个通讯协议,你会怎么实现呢?、
首先初始化一个串口,打开串口发送和接收中断,在串口接收中断中,一个一个的接收数据并放到一个缓冲区中,当接收满一个包之后,置个标志,然后解析函数中检测到包满标志后,进行数据的解析,根据指令返回组包返回数据。
那么怎么判断包结束??
我们一般习惯使用一个定时器,在一定的时间内没有接收到数据,我们就认为包结束了。
其实这就是一个完整的接收解析过程,freemodbus无非是更完善,一些机制更合理而已。
想要找freemodbus的接口在哪里?
肯定是在eMBInit()里啊。
case MB_RTU:
pvMBFrameStartCur = eMBRTUStart; /*使能modbus协议栈*/
pvMBFrameStopCur = eMBRTUStop; /*禁用modbus协议栈*/
peMBFrameSendCur = eMBRTUSend; /*modbus从机响应函数*/
peMBFrameReceiveCur = eMBRTUReceive; /*modbus报文接收函数*/
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
/*串口接收中断最终调用此函数接收数据*/
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
/*串口发送中断最终调用此函数发送数据*/
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;//报文到达间隔检查
//初始化RTU,对接底层串口配置
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
了解接收机制:xMBRTUReceiveFSM
了解发送机制: xMBRTUTransmitFSM
帧数据完成,状态迁移:xMBRTUTimerT35Expired
具体的解释可以参考下面的博客,很详细:
https://blog.youkuaiyun.com/u014748120/article/details/80313215
这里我们需要注意的是modbus超时时间的配置,也就是一包完成的判断。
这里通过波特率进行的计算,还是比较合理的,不然很容易出现连包错包。
其实我们在平时的项目中,一个字节一个字节的接收,并且需要任务中不断轮询标志的这种方式是比较占用资源的。我们还习惯使用串口空闲中断+DMA的方式,当串口空闲后判断为接收包完成,发送队列或者信号量,触发任务解析,可以很大的节省系统资源。
下一节,我们对freemodbus进行简单的优化,生成我们的实例版本,方便后期直接使用该模板快速使用modbus进行参数的设置和存储。