FreeModbus开源协议栈的移植和详解(三)- RTU协议代码分析
FreeModbus开源协议栈的移植和详解(三)
目录
概述
一、RTU文件夹的文件
二、mbrtu.c文件
2.1数据类型说明
2.2函数说明
2.1eMBRTUInit()函数
2.2eMBRTUStart()函数
2.3eMBRTUStop()函数
2.4eMBRTUReceive()函数
2.5eMBRTUSend()函数
2.6xMBRTUReceiveFSM()函数
2.7xMBRTUTransmitFSM()函数
2.8xMBRTUTimerT35Expired()函数
三、总结
概述
上篇文章分析了mb.c文件,我们知道,mb.c只是实现modbus协议的一个框架,具体的RTU、ASCII和TCP相关的代码再具体的文件夹中。这里我们以使用比较多的RTU为例,来分析一下RTU的实现。
一、RTU文件夹的文件
文件名称 | 说明 |
---|---|
mbcrc.c | 这个文件只包含一个函数,就是标准的CRC16校验函数 |
mbcrc.h | 包含CRC校验函数的函数声明 |
mbrtu.c | 实现RTU协议的具体函数,rtu协议相关的实现函数都在这个文件中 |
mbrtu.h | 头文件,包含rtu函数的声明 |
二、mbrtu.c文件
/* * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.* Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at>* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:* 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* 3. The name of the author may not be used to endorse or promote products* derived from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.**//* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"/* ----------------------- Platform includes --------------------------------*/
#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbrtu.h"
#include "mbframe.h"#include "mbcrc.h"
#include "mbport.h"/* ----------------------- Defines ------------------------------------------*/
#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
#define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus RTU frame. */
#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. *//* ----------------------- Type definitions ---------------------------------*/
typedef enum
{STATE_RX_INIT, /*!< Receiver is in initial state. */STATE_RX_IDLE, /*!< Receiver is in idle state. */STATE_RX_RCV, /*!< Frame is beeing received. */STATE_RX_ERROR /*!< If the frame is invalid. */
} eMBRcvState;typedef enum
{STATE_TX_IDLE, /*!< Transmitter is in idle state. */STATE_TX_XMIT /*!< Transmitter is in transfer state. */
} eMBSndState;/* ----------------------- Static variables ---------------------------------*/
static volatile eMBSndState eSndState;
static volatile eMBRcvState eRcvState;volatile UCHAR ucRTUBuf[MB_SER_PDU_SIZE_MAX];static volatile UCHAR *pucSndBufferCur;
static volatile USHORT usSndBufferCount;static volatile USHORT usRcvBufferPos;/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{eMBErrorCode eStatus = MB_ENOERR;ULONG usTimerT35_50us;( void )ucSlaveAddress;ENTER_CRITICAL_SECTION( );/* Modbus RTU uses 8 Databits. */if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE ){eStatus = MB_EPORTERR;}else{/* 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;}}EXIT_CRITICAL_SECTION( );return eStatus;
}void
eMBRTUStart( void )
{ENTER_CRITICAL_SECTION( );/* Initially the receiver is in the state STATE_RX_INIT. we start* the timer and if no character is received within t3.5 we change* to STATE_RX_IDLE. This makes sure that we delay startup of the* modbus protocol stack until the bus is free.*/eRcvState = STATE_RX_INIT;vMBPortSerialEnable( TRUE, FALSE );vMBPortTimersEnable( );EXIT_CRITICAL_SECTION( );
}void
eMBRTUStop( void )
{ENTER_CRITICAL_SECTION( );vMBPortSerialEnable( FALSE, FALSE );vMBPortTimersDisable( );EXIT_CRITICAL_SECTION( );
}eMBErrorCode
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{BOOL xFrameReceived = FALSE;eMBErrorCode eStatus = MB_ENOERR;ENTER_CRITICAL_SECTION( );assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );/* Length and CRC check */if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )&& ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ){/* Save the address field. All frames are passed to the upper layed* and the decision if a frame is used is done there.*/*pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus* size of address field and CRC checksum.*/*pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );/* Return the start of the Modbus PDU to the caller. */*pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];xFrameReceived = TRUE;}else{eStatus = MB_EIO;}EXIT_CRITICAL_SECTION( );return eStatus;
}eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{eMBErrorCode eStatus = MB_ENOERR;USHORT usCRC16;ENTER_CRITICAL_SECTION( );/* Check if the receiver is still in idle state. If not we where to* slow with processing the received frame and the master sent another* frame on the network. We have to abort sending the frame.*/if( eRcvState == STATE_RX_IDLE ){/* First byte before the Modbus-PDU is the slave address. */pucSndBufferCur = ( UCHAR * ) pucFrame - 1;usSndBufferCount = 1;/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;usSndBufferCount += usLength;/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );/* Activate the transmitter. */eSndState = STATE_TX_XMIT;vMBPortSerialEnable( FALSE, TRUE );}else{eStatus = MB_EIO;}EXIT_CRITICAL_SECTION( );return eStatus;
}BOOL
xMBRTUReceiveFSM( void )
{BOOL xTaskNeedSwitch = FALSE;UCHAR ucByte;assert( eSndState == STATE_TX_IDLE );/* Always read the character. */( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );switch ( eRcvState ){/* If we have received a character in the init state we have to* wait until the frame is finished.*/case STATE_RX_INIT:vMBPortTimersEnable( );break;/* In the error state we wait until all characters in the* damaged frame are transmitted.*/case STATE_RX_ERROR:vMBPortTimersEnable( );break;/* In the idle state we wait for a new character. If a character* is received the t1.5 and t3.5 timers are started and the* receiver is in the state STATE_RX_RECEIVCE.*/case STATE_RX_IDLE:usRcvBufferPos = 0;ucRTUBuf[usRcvBufferPos++] = ucByte;eRcvState = STATE_RX_RCV;/* Enable t3.5 timers. */vMBPortTimersEnable( );break;/* We are currently receiving a frame. Reset the timer after* every character received. If more than the maximum possible* number of bytes in a modbus frame is received the frame is* ignored.*/case STATE_RX_RCV:if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ){ucRTUBuf[usRcvBufferPos++] = ucByte;}else{eRcvState = STATE_RX_ERROR;}vMBPortTimersEnable( );break;}return xTaskNeedSwitch;
}BOOL
xMBRTUTransmitFSM( void )
{BOOL xNeedPoll = FALSE;assert( eRcvState == STATE_RX_IDLE );switch ( eSndState ){/* We should not get a transmitter event if the transmitter is in* idle state. */case STATE_TX_IDLE:/* enable receiver/disable transmitter. */vMBPortSerialEnable( TRUE, FALSE );break;case STATE_TX_XMIT:/* check if we are finished. */if( usSndBufferCount != 0 ){xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );pucSndBufferCur++; /* next byte in sendbuffer. */usSndBufferCount--;}else{xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );/* Disable transmitter. This prevents another transmit buffer* empty interrupt. */vMBPortSerialEnable( TRUE, FALSE );eSndState = STATE_TX_IDLE;}break;}return xNeedPoll;
}BOOL
xMBRTUTimerT35Expired( void )
{BOOL xNeedPoll = FALSE;switch ( eRcvState ){/* Timer t35 expired. Startup phase is finished. */case STATE_RX_INIT:xNeedPoll = xMBPortEventPost( EV_READY );break;/* A frame was received and t35 expired. Notify the listener that* a new frame was received. */case STATE_RX_RCV:xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );break;/* An error occured while receiving the frame. */case STATE_RX_ERROR:break;/* Function called in an illegal state. */default:assert( ( eRcvState == STATE_RX_INIT ) ||( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );}vMBPortTimersDisable( );eRcvState = STATE_RX_IDLE;return xNeedPoll;
}
2.1数据类型说明
该文件主要实现了数据的接收和发送的相关函数。
首先,用枚举定义了接收器和发送器的状态,列表说明如下:
接收器状态说明:
接收器状态 | 说明 |
---|---|
STATE_RX_INIT | 接收器已初始化 |
STATE_RX_IDLE | 接收器空闲 |
STATE_RX_RCV | 接收器正在接收 |
STATE_RX_ERROR | 接收器错误 |
发送器状态 | 说明 |
---|---|
STATE_TX_IDLE | 发送器空闲 |
STATE_TX_XMIT | 正在发送 |
接着定义了一个数据缓存区、发送缓存指针、发送缓存长度、数据接收长度。
2.2函数说明
2.1eMBRTUInit()
函数
2.2eMBRTUStart()
函数
该函数比较简单,将接收器的状态设置为初始化状态,然后使能UART接收中断,禁止发送中断,使能定时器中断。
2.3eMBRTUStop()
函数
该函数和eMBRTUStart()
函数功能相反,禁止UART接收和发送中断,禁止定时器中断,不再相应UART发送过来的数据。
2.4eMBRTUReceive()
函数
2.5eMBRTUSend()
函数
该函数主要工作就是在发送PDU前面填充从机地址,后面填充CRC校验值,更新发送数据的长度,使能UART发送中断。
2.6xMBRTUReceiveFSM()
函数
这个函数是UART串口中断接收处理函数和2.7中的发送函数都是在串口中断函数中调用的。其功能就是将串口接收到的数据放在ucRTUBuf数据缓冲区当中。
2.7xMBRTUTransmitFSM()
函数
这个函数是串口发送中断处理函数,函数的主要功能就是将缓冲区中的数据发送出去。
2.8xMBRTUTimerT35Expired()
函数
这个是超时处理函数,当接收超时时,说明接收完成,发送一个接收完成事件,然后关闭超时中断。
三、总结
FreeModbus开源协议栈的移植和详解(三)- RTU协议代码分析相关推荐
- FreeModbus开源协议栈的移植和详解(一)- FreeModbus的下载和文件结构
FreeModbus开源协议栈的移植和详解(一) 引言 一.FreeModbus的获取 二.FreeModbus文件夹的介绍 三.Modbus文件夹介绍 四.小结 引言 很多做单片机或者嵌入式的朋友对 ...
- 短视频美颜sdk人脸编辑技术详解、美颜sdk代码分析
短视频美颜sdk中人脸编辑技术可以将人像风格进行转变,小编认为这也是未来的美颜sdk的一个重要发展方向,下文小编将为大家讲解一下短视频美颜sdk中人脸编辑的关键点. 一.人脸编辑的细分关键点 1.年龄 ...
- PackageManagerService启动详解(三)之开始初始化阶段流程分析
PKMS启动详解(三)之BOOT_PROGRESS_PMS_START阶段流程分析 Android PackageManagerService系列博客目录: PKMS启动详解系列博客概要 PKMS ...
- 主线剧情03-NXP-i.MX系列的u-boot移植基础详解
u-boot 移植基础详解 本文系广泛撷取.借鉴和整理(相关的内容在网络上有很多,但很多相互抄,或者是版本太老,或者就是不通用的非常有平台针对性的步骤,碎片化泛滥,甚至就是有待分拣的垃圾厂,当然也有一 ...
- EasyPR中文开源车牌识别系统 开发详解
在上篇文档中作者已经简单的介绍了EasyPR,现在在本文档中详细的介绍EasyPR的开发过程. 正如淘宝诞生于一个购买来的LAMP系统,EasyPR也有它诞生的原型,起源于CSDN的taotao ...
- TCP/IP 协议栈及 OSI 参考模型详解
TCP/IP 协议栈及 OSI 参考模型详解 转载地址:http://www.codeceo.com/article/tcp-ip-osi-model.html OSI参考模型 OSI RM:开放系统 ...
- Java开源项目Hibernate包作用详解
Java开源项目Hibernate包作用详解 本文引自:http://hi.baidu.com/nick6610/blog/item/70b58afa0d0eab9259ee90f7.html Jav ...
- linux 进程间通信 dbus-glib【实例】详解三 数据类型和dteeth(类型签名type域)(层级结构:服务Service --> Node(对象、object) 等 )(附代码)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- Android init.rc文件解析过程详解(三)
Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...
最新文章
- JAVA(IO流)知识整理
- Runtime 系列 3-- 给 category 添加属性
- 无法更改域名 php网页,WordPress更改新域名后网站无法正常运行怎么办?
- 小明分享|8ms平台下工程源码分析
- js处理异常try{}catch(e){}
- 服务器维护,日志分析常用命令
- KMP算法中next数组的理解与算法的实现(java语言)
- LeetCode 1727. 重新排列后的最大子矩阵(前缀和+排序)
- 《追风筝的人》卡勒德·胡赛尼著小说成长的故事
- 在CentOS7环境下部署TiDB
- 认知觉醒是成长的首因,送3本硬核认知提升书
- PuttyPsftp
- 【UEFI实战】HII之uni文件
- 【人工智能】Google I/O 2023:让 AI 对每个人都更有帮助 Making AI more helpful for everyone
- centos光盘修复引导_CentOs7 修复 引导启动
- 微型计算机点火系统的摘要,丰田汽车点火系统故障诊断与排除的毕业论文教案.doc...
- 一般论文投稿应该注意哪些要求
- ISPRS2018/云检测:Cloud/shadow detection based on spectral indices for multi/hyp基于光谱指数的多/高光谱光学遥感成像仪云/影检测
- 解决Windows11/10本地账户改用Microsoft账户登录显示“发生了错误”的问题
- laravel框架下载指定版本
热门文章
- 2022年RPA将从IT领域继续扩展至非IT领域,非IT领域5大场景RPA应用
- 可编程的SQL是什么样的?
- Python零基础自学要多久?编程入门该怎么学?
- 云端软件平台与虚拟系统大比拼
- 职场PPT达人装酷的13条秘诀
- c++windows openSSL编译(ActivePerl-5.8.8.822-MSWin32-x86-280952.msi+openssl-1.0.2)
- 使用致远OA系统,打开Excel类型文件,提示office编辑程序未安装
- onenote2007 设置行间距
- 第二十三课.扩散模型
- sortable的基本属性