Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
freemodbus
源码分析
STM32F103芯片的 freemodbus RTU的移植和使用。
发布于 2022-02-19 11:18:33 浏览:2873
订阅该版
文章目录 [toc] ------ STM32F103芯片的 freemodbus RTU的移植和使用。 **示例代码** 代码示例上传在 gitee上,仓库地址为[freemodbus移植示例](https://gitee.com/robot266/stm32f1xx_freemodbus) # 1 freemodbus介绍 Freemodbus是一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议。Modbus通信协议栈包括两层:Modbus应用层协议,该层定义了数据模式和功能;另外一层是网络层。本文以STM32F103ZET6的单片机为例,在Keil环境中讲述单片机作为从机实现 freeModbus RTU 模式的移植。源码的下载地址为https://www.embedded-experts.at/en/freemodbus-downloads/ ,源码主要包括 demo modbus doc tools 四个文件夹。Demo 文件夹中主要free modbus官方为我们新建好的各种平台的测试例程,加快我们的开发进度,其中包括 Win32平台、Linux平台、ARM平台等。本次使用的是STM32的平台,在源码中并没有STM32平台的示例,但是在 demo 中有一个 BARE 文件夹,我们可以在该文件夹的基础上进行源码的移植。Modbus文件夹下,主要放一些关于Modbus自身协议的源码,其中包括Modbus-Rtu、Modbus-Ascii、Modbus-Tcp等。doc主要放一些帮助和说明文件,tools就是放置一些需要的工具,在测试时可以使用Modbus Poll 和 Modbus Slave 进行调试测试。 # 2 freemodbus移植 由于freemodbus库没有在STM32上移植的示例,所以在移植STM32平台时只能在demo\BARE\port文件夹中进行从0开始的移植,在Keil工程中新建freemodbus文件夹,然后拷贝port文件夹和modbus文件夹下的所有内容到新建立的文件夹中,拷贝完成的文件夹如下图所示。 ![拷贝后的文件夹](https://img-blog.csdnimg.cn/41cc12118a724ca3b00f359fe373fc98.png) port文件夹下的文件是本次移植要修改的部分,其中 portserial.c 是串口的初始化和收发控制的实现,porttimer.c 是3.5T定时器的实现,portevent.c 无需修改。 ## 2.1 串口的移植 本次设计使用的是modbus RTU模式,数据传输依靠串口来实现,所以需要将freemodbus库文件的数据的输入和输出定位在单片机的串口中,串口的移植在 portserial.c 中实现,在文件中主要实现了串口的初始化,串口的中断接收和发送的功能。 串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备得RXD接口,在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。通讯帧格式如下图所示。 ![2.1.png](https://oss-club.rt-thread.org/uploads/20220219/e1ba21bada3e68fa8d6d5c04bc285661.png.webp) 串口是一种最为常见的通讯方式,因为串口是一种全双工的通讯方式,但是modbus是一种主从也就是半双工的通讯方式,所以在移植时要设置串口为半双工的工作方式,即接收时关闭发送,发送时关闭接收。 ## 2.2 定时器的移植 Modbus协议规定,在RTU模式中,消息的发送和接收至少以3.5个字符时间的停顿间隔为标志。实际使用中,网络设备不断侦测网络总线,计算字符间的停顿间隔时间,判断消息帧的起始点。当接收到第一个域(地址域)时,每个设备都进行解码以判断是否是发给自己的,在最后一个传输字符结束之后,一个至少3.5个字符时间的停顿标志标定了消息的结束,而一个新的消息可在此停顿后开始。初次之外Modbus还规定当串口的波特率小于9600bps时,两个数据帧之间至少有3.5个字符的时间间隔,当波特率大于等于19200bps时,两个数据帧的时间间隔以波特为19200bps时的3.5个字符长度为判断依据。 在freemodbus源码中,以定时器作为两个数据帧之间时间间隔的判断依据,源码的相关内容为: ```c /* If baudrate > 19200 then we should use the fixed timer values * t35 = 1750us. Otherwise t35 must be 3.5 times the character time. */ if( ulBaudRate > 19200 ) { usTimerT35_50us = 35; /* 1800us. */ } else { /* The timer reload value for a character is given by: * * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 ) * = 11 * Ticks_per_1s / Baudrate * = 220000 / Baudrate * The reload for t3.5 is 1.5 times this value and similary * for t3.5. */ usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate ); } if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE ) // 定时器初始化 { eStatus = MB_EPORTERR; } ``` 分析源码可以看出,源码要求初始化一个50*usTimerT35_50us的定时器,原因如下:源码展示了当波特率大于19200时,usTimerT35_50us赋值为35,这样赋值的原因是3.5个字符时35个比特位(每个字符包含一个起始位,8个数据位和一个停止位),所以3.5个字符所需要的发送时间为35*1/19200≈1823us。 # 3 源码解析 FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完整的报文后,对报文进行解析,然后响应主机,发送报文给主机,实现主机和从机之间的通信。为了更好的实现整个代码的设计,本文对FreeModbus源码进行简单的分析。在源码的demo.c中主要有三个函数分别是eMBInit(),eMBEnable()和eMBPoll(),接下来将对这三个函数分别进行解析。 源码解析图的源文件在仓库的 stm32f1xx_freemodbus/source_code_analysis 目录下 ## 3.1 eMBEnable 解析 eMBInit()主要实现的功能是实现RTU模式和ASCALL模式的协议栈初始化,完成协议栈核心函数指针的赋值,包括Modbus协议栈的使能和禁止、报文的接收和响应、3.5T定时器中断回调函数、串口发送和接收中断回调函数,eMBRTUInit完成RTU模式下串口和3.5T定时器的初始化,需依据不同的开发板平台进行移植,本次设计的串口的波特率位9600,从机地址为0x05,主要函数的调用关系如下图所示。 ![3.1.png](https://oss-club.rt-thread.org/uploads/20220219/0bbb63cc7c5429249ad3487027f5b8c9.png) ## 3.2 eMBInit 解析 eMBEnable()函数主要设置Modbus协议栈工作状态eMBState为STATE_ENABLED;调用pvMBFrameStartCur()函数激活协议栈,函数调用关系如下图所示。 ![3.2副本.png](https://oss-club.rt-thread.org/uploads/20220219/33de6f6793545fd4a58687768daa9491.png) ## 3.3 eMBPoll 解析 eMBPoll()函数主要查询事件的状态,根据不同的状态执行相应的处理函数,调用关系如下图所示。eMBPoll()函数在while(1)被不断的调用,使用switch来不断的对检测到的事件进行分类处理,当接收到一帧完整的数据时,此时在3.5T定时器中断中会将事件状态修改为EV_FRAME_RECEIVED,表示接收到一帧数据,然后会调用eMBRTUReceive()函数将数据从接收缓冲区中取出,判断接收的数据帧的地址是否和本机地址一致如果一致的情况下,则将事件状态修改为EV_EXECUTE,开始对数据帧进行解析处理。 在EV_EXECUTE状态下会首先遍历一个大小为16的结构体数组xFuncHandlers,寻找不同的功能码对应的执行函数,本文以读写保持寄存器为例进行解析,在eMBFuncReadHoldingRegister()函数中会对将数据帧进行拆分,取出功能码,寄存器地址,寄存器数量等数据,然后调用eMBRegHoldingCB()这个回调函数,这个函数源码库并没有实现,需要依据不同的功能进行编写,在编写时可以参考源码在其他平台下的移植实例,最终实现的效果是将数据从内存中取出放入对应的寄存器中,然后依据是否应答执行发送函数。 ![3.3.png](https://oss-club.rt-thread.org/uploads/20220219/2701cab747091bc88c1fe43ca60666dd.png) ## 3.4 定时器超时 解析 FreeModbus是通过定时器判断启动接收准备完成和一帧数据接收结束的,所有的处理均在定时器的中断中进行,定时器中断的处理流程如下图所示。定时器中断的入口函数是prvvTIMERExpiredISR(),最终调用的接口是xMBRTUTimerT35Expired(),在中断处理函数中依据接收状态机的状态,执行不同的处理流程。如果接收模式处于初始化状态则释放一个EV_REDAY信号,然后关闭定时器并将接收状态机修改为空闲模式;如果接收机处于STATE_RX_RCV状态,表示此时完成了一个数据帧的接收,此时将释放一个EV_FRAME_RECEIVED接收完成信号,然后关闭定时器并将接收状态机修改为空闲模式,待eMBPoll()轮询后发现接收完成事件,对接收的内容进行解析和处理。 ![3.4.png](https://oss-club.rt-thread.org/uploads/20220219/12d2c35741c9bc505668c4044570d307.png.webp) ## 3.5 发送流程 解析 本小节将详细说明发送过程的处理逻辑,发送的执行过程如下图所示。在eMBPoll()函数处理完接收的数据后开始调用发送函数peMBFrameSendCur()进行发送,最终调用的发送函数实际为eMBRTUSend(),在该发送函数中进行数据的组帧工作,同时修改发送状态机为eSndState为STATE_TX_XMIT,然后启动串口的发送字节函数xMBPortSerialPutByte()进行发送同时打开串口的发送功能关闭串口的接收功能,在每发送完一个字节的数据后会进入串口的发送中断处理函数中,最终调用的函数为xMBRTUTransmitFSM(),在该函数中会依据要发送的字节数循环的进行发送处理,待所有的要发送的内容发送完毕后,会释放EV_FRAME_SENT事件按,然后关闭串口的发送功能,打开串口的接收功能,等待接收下一个数据帧。 ![3.5副本.png](https://oss-club.rt-thread.org/uploads/20220219/e10a84dfe5a9994cf54a3f6e1fe22017.png) ## 3.6 接收一帧数据流程 解析 接收过程的流程如下图所示。在串口的接收中断处理函数中执行具体的接收过程,最终调用的接收处理函数为xMBRTUReceiveFSM(),在这个函数中会根据不同的接收状态机执行相应的函数。 因为在初始化时,调用了eMBEnable()函数,在这个函数中启动了3.5T定时器,然后定时器中断将eRcvState状态修改为STATE_RX_IDLE,所以收到第一个字节的数据后首先执行case STATE_RX_IDLE中的内容,在这个分支里将收到的第一个数据存储到了ucRTUBuf接收缓冲区的第一个位置,然后将接收状态机修改为STATE_RX_RCV,接着每产生一个接收中断,就将收到的数据依次存储在ucRTUBuf接收缓冲区中。待主机发送完所有的数据后,子机端因为3.5T定时器的作用在一段时间内没有收到数据产生一个定时器中断,定时器中断中将释放一个接收完成信号,将xEventInQueue修改为TRUE,待eMBPoll()轮询到接收完成事件后进行接收数据的解析和处理。 ![3.6.png](https://oss-club.rt-thread.org/uploads/20220219/f8b7ee5a4fe61494ed8a65dc157c44d2.png)
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
crystal266
嵌入式
文章
14
回答
547
被采纳
161
关注TA
发私信
相关文章
1
FREEMODBUS的设置问题
2
freemodbus tcp
3
freemodbus,写保持寄存器返回值一直是3,但是正确写入 ?
4
FreeModbus 从机该如何更新数据,菜鸟的不解.
5
RT thread freemobus tcp通讯问题
6
freemodbus主机读取从机保持寄存器命令后多两个0
7
freemodbus接收CRC校验疑问
8
freemodbus主机不通
9
freemodbus的主机任何指令都timeout
10
freemodbus 软件包主机有bug
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部