stm32f1xx-freemodbus-RTU 移植


STM32F103芯片的 freemodbus RTU的移植和使用。

1 示例代码

  代码示例上传在 gitee上,仓库地址为freemodbus移植示例

2 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文件夹下的所有内容到新建立的文件夹中,拷贝完成的文件夹如下图所示。
拷贝后的文件夹
  port文件夹下的文件是本次移植要修改的部分,其中 portserial.c 是串口的初始化和收发控制的实现,porttimer.c 是3.5T定时器的实现,portevent.c 无需修改。

2.1 串口的移植

  本次设计使用的是modbus RTU模式,数据传输依靠串口来实现,所以需要将freemodbus库文件的数据的输入和输出定位在单片机的串口中,串口的移植在 portserial.c 中实现,在文件中主要实现了串口的初始化,串口的中断接收和发送的功能。
  串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备得RXD接口,在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。通讯帧格式如下图所示。
串口通讯帧格式
  串口是一种最为常见的通讯方式,因为串口是一种全双工的通讯方式,但是modbus是一种主从也就是半双工的通讯方式,所以在移植时要设置串口为半双工的工作方式,即接收时关闭发送,发送时关闭接收。

2.2 定时器的移植

  Modbus协议规定,在RTU模式中,消息的发送和接收至少以3.5个字符时间的停顿间隔为标志。实际使用中,网络设备不断侦测网络总线,计算字符间的停顿间隔时间,判断消息帧的起始点。当接收到第一个域(地址域)时,每个设备都进行解码以判断是否是发给自己的,在最后一个传输字符结束之后,一个至少3.5个字符时间的停顿标志标定了消息的结束,而一个新的消息可在此停顿后开始。初次之外Modbus还规定当串口的波特率小于9600bps时,两个数据帧之间至少有3.5个字符的时间间隔,当波特率大于等于19200bps时,两个数据帧的时间间隔以波特为19200bps时的3.5个字符长度为判断依据。
  在freemodbus源码中,以定时器作为两个数据帧之间时间间隔的判断依据,源码的相关内容为:

/* 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;
}

  分析源码可以看出,源码要求初始化一个50usTimerT35_50us的定时器,原因如下:源码展示了当波特率大于19200时,usTimerT35_50us赋值为35,这样赋值的原因是3.5个字符时35个比特位(每个字符包含一个起始位,8个数据位和一个停止位),所以3.5个字符所需要的发送时间为351/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.2 eMBInit 解析

  eMBEnable()函数主要设置Modbus协议栈工作状态eMBState为STATE_ENABLED;调用pvMBFrameStartCur()函数激活协议栈,函数调用关系如下图所示。
在这里插入图片描述

3.3 eMBPoll 解析

  eMBPoll()函数主要查询事件的状态,根据不同的状态执行相应的处理函数,调用关系如下图所示。eMBPoll()函数在while(1)被不断的调用,使用switch来不断的对检测到的事件进行分类处理,当接收到一帧完整的数据时,此时在3.5T定时器中断中会将事件状态修改为EV_FRAME_RECEIVED,表示接收到一帧数据,然后会调用eMBRTUReceive()函数将数据从接收缓冲区中取出,判断接收的数据帧的地址是否和本机地址一致如果一致的情况下,则将事件状态修改为EV_EXECUTE,开始对数据帧进行解析处理。
  在EV_EXECUTE状态下会首先遍历一个大小为16的结构体数组xFuncHandlers,寻找不同的功能码对应的执行函数,本文以读写保持寄存器为例进行解析,在eMBFuncReadHoldingRegister()函数中会对将数据帧进行拆分,取出功能码,寄存器地址,寄存器数量等数据,然后调用eMBRegHoldingCB()这个回调函数,这个函数源码库并没有实现,需要依据不同的功能进行编写,在编写时可以参考源码在其他平台下的移植实例,最终实现的效果是将数据从内存中取出放入对应的寄存器中,然后依据是否应答执行发送函数。
在这里插入图片描述

3.4 定时器超时 解析

  FreeModbus是通过定时器判断启动接收准备完成和一帧数据接收结束的,所有的处理均在定时器的中断中进行,定时器中断的处理流程如下图所示。定时器中断的入口函数是prvvTIMERExpiredISR(),最终调用的接口是xMBRTUTimerT35Expired(),在中断处理函数中依据接收状态机的状态,执行不同的处理流程。如果接收模式处于初始化状态则释放一个EV_REDAY信号,然后关闭定时器并将接收状态机修改为空闲模式;如果接收机处于STATE_RX_RCV状态,表示此时完成了一个数据帧的接收,此时将释放一个EV_FRAME_RECEIVED接收完成信号,然后关闭定时器并将接收状态机修改为空闲模式,待eMBPoll()轮询后发现接收完成事件,对接收的内容进行解析和处理。
在这里插入图片描述

3.5 发送流程解析

  本小节将详细说明发送过程的处理逻辑,发送的执行过程如下图所示。在eMBPoll()函数处理完接收的数据后开始调用发送函数peMBFrameSendCur()进行发送,最终调用的发送函数实际为eMBRTUSend(),在该发送函数中进行数据的组帧工作,同时修改发送状态机为eSndState为STATE_TX_XMIT,然后启动串口的发送字节函数xMBPortSerialPutByte()进行发送同时打开串口的发送功能关闭串口的接收功能,在每发送完一个字节的数据后会进入串口的发送中断处理函数中,最终调用的函数为xMBRTUTransmitFSM(),在该函数中会依据要发送的字节数循环的进行发送处理,待所有的要发送的内容发送完毕后,会释放EV_FRAME_SENT事件按,然后关闭串口的发送功能,打开串口的接收功能,等待接收下一个数据帧。在这里插入图片描述

3.6 接收一帧数据流程 解析

  接收过程的流程如下图所示。在串口的接收中断处理函数中执行具体的接收过程,最终调用的接收处理函数为xMBRTUReceiveFSM(),在这个函数中会根据不同的接收状态机执行相应的函数。
  因为在初始化时,调用了eMBEnable()函数,在这个函数中启动了3.5T定时器,然后定时器中断将eRcvState状态修改为STATE_RX_IDLE,所以收到第一个字节的数据后首先执行case STATE_RX_IDLE中的内容,在这个分支里将收到的第一个数据存储到了ucRTUBuf接收缓冲区的第一个位置,然后将接收状态机修改为STATE_RX_RCV,接着每产生一个接收中断,就将收到的数据依次存储在ucRTUBuf接收缓冲区中。待主机发送完所有的数据后,子机端因为3.5T定时器的作用在一段时间内没有收到数据产生一个定时器中断,定时器中断中将释放一个接收完成信号,将xEventInQueue修改为TRUE,待eMBPoll()轮询到接收完成事件后进行接收数据的解析和处理。

在这里插入图片描述

### 回答1: STM32是一种广泛应用于嵌入式系统开发的微控制器,而MODBUS是一种串行通信协议,广泛应用于工业自动化领域。下面将介绍如何在STM32上成功移植MODBUS。 首先,我们需要准备相应的硬件和软件资源。硬件方面,我们需要一款STM32开发板,例如STM32F4系列、STM32F7系列等。同时,我们还需要一台PC机,用于开发和调试。软件方面,我们需要安装Keil MDK集成开发环境,并准备好对应的STM32 HAL库和MODBUS协议库。 接下来,我们需要编写相关的代码进行移植。首先,我们需要在Keil MDK中创建一个新的项目,并选择对应的STM32型号。然后,我们需要在项目中添加STM32 HAL库和MODBUS协议库的相关文件。 在编写代码的过程中,我们需要了解MODBUS协议的基本原理和通信规则。MODBUS协议分为RTU和ASCII两种通信格式,我们可以根据实际需求选择合适的方式。我们需要编写相应的函数来完成MODBUS的数据读写、错误处理等功能。 移植MODBUS还需要配置串口通信参数,例如波特率、数据位、校验位等。我们可以使用STM32的UART模块来实现串口通信,并配置对应的寄存器。 在完成代码编写后,我们需要进行编译、连接和下载等步骤,将代码烧录到STM32开发板中。然后,我们可以使用PC机上的MODBUS客户端软件进行测试。 在测试过程中,我们可以通过读写寄存器、读取输入寄存器等功能进行数据交互。如果出现问题,我们可以通过调试工具和日志查找错误原因,并进行相应的修复和优化。 总的来说,STM32移植MODBUS需要了解MODBUS协议的原理和使用方式,以及熟悉STM32的开发环境和相关库函数。在编码和调试过程中,需要耐心和细心地进行,以确保代码的正确性和可靠性。移植成功后,我们可以在工业自动化领域中广泛应用MODBUS通信。 ### 回答2: STM32是一种高性能32位微控制器系列,有着广泛的应用领域。而Modbus是一种通信协议,常用于工业自动化领域。移植Modbus协议到STM32上,可以实现与其他Modbus设备之间的通信。 首先,需要在STM32上选择一个适合的串口作为通信接口。然后,在STM32的开发环境中,创建一个工程并添加Modbus通信相关的库文件。 接下来,需要根据具体需求配置STM32的串口参数,例如波特率、数据位、校验位和停止位等。根据Modbus协议规范,还需要设置一个从站地址,用于识别STM32Modbus网络中的身份。 在代码编写方面,可以使用库函数或者原生的寄存器操作进行开发。首先,需要初始化串口配置和Modbus相关参数。然后,使用Modbus协议规定的函数进行读写操作,例如读取寄存器或写入寄存器的数值。 在通信过程中,需要注意Modbus协议的报文格式。例如,读取寄存器时需要构建读取请求报文,并通过串口发送到目标设备。然后,接收目标设备返回的响应报文,并解析其中的数据。 最后,进行测试和调试。可以通过与其他Modbus设备进行通信,验证STM32是否能够正常收发数据。如果出现问题,可以利用调试工具进行故障排查,例如使用串口调试助手查看串口发送和接收的数据。 总之,移植Modbus协议到STM32上需要进行串口配置、Modbus参数设置、代码编写和功能测试等步骤。随着不断的开发和调试,最终可以实现STM32与其他Modbus设备之间的可靠通信。 ### 回答3: 要在STM32移植Modbus协议,需要进行以下步骤: 1. 确定Modbus协议类型:根据实际需求确定使用的Modbus协议类型,包括Modbus RTUModbus ASCII或Modbus TCP/IP等。 2. 引入相关库文件:从官方网站或第三方提供的源码库中下载与所选协议类型兼容的Modbus协议库文件,如Modbus Slave Library等。 3. 配置串口或网络接口:根据所选协议类型的不同,配置相应的串口或网络接口,如使用Modbus RTU协议,需要配置UART通信;使用Modbus TCP/IP协议,则需要配置以太网接口。 4. 初始化Modbus库:根据所选协议类型的库文件提供的接口,进行Modbus库的初始化设置,包括波特率、数据位、校验位等参数的配置。 5. 注册Modbus功能码:根据项目需求,注册相关的Modbus功能码,包括读取寄存器、写入寄存器、读取线圈状态等,以便后续进行数据交互操作。 6. 处理Modbus请求和响应:在主循环中不断监听来自Modbus主机(如上位机或PLC)的请求,并根据请求的功能码执行相应的操作,如读取或写入寄存器值。 7. 构建Modbus响应:根据请求的功能码和相关参数,构建相应的Modbus响应,并发送给Modbus主机,确保数据的正确传输和处理。 8. 完善错误处理和容错机制:在移植过程中,要注意处理错误情况,如通信超时、校验错误等,确保系统具备一定的容错能力。 9. 调试和验证:完成移植后,进行系统测试和验证,检查Modbus协议的功能是否正常,并进行必要的调试和修复工作。 总之,移植Modbus协议到STM32芯片需要进行协议类型选择、库文件引入、接口配置、功能码注册、请求响应处理等一系列操作。在完成移植后,需要进行测试和验证,确保协议的正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值