FreeModBus源码解析(1)---开篇
一、设计思想
任何通信协议的实现都是基于状态机的设计思想,就是来了一串数据判断是是干啥的在调用相应的处理函数只不过高手一般采用回调处理。
- 如果你熟悉了回调、源码里的状态机的实现又可以理解,那么恭喜你已经掌握了通信协议的实现方法。
- 如果你可以从源妈里体会到分层的设计思想,那么恭喜你已经触碰到了架构师的门槛。
本系列文章就是通过对FreeModeBus源码进行解析来掌握以上技能。
二、ModBus协议简介以及状态机的实现
为啥把ModBus协议简介与状态机的实现放在一起呢???
状态机编写当然是基于通信协议标准。
如果通信数据格式、功能码、处理流程都搞不清楚,你编出来的状态机也是你“个人版的通信协议标准”,与符合ModBus通信协议标准的设备也是通信不上的。
1、 ModBus 设备间通信处理流程如下:
- 主设备向从设备发送请求
- 从设备分析并处理主设备的请求,然后向主设备发送结果
- 如果出现任何差错,从设备将返回一个异常功能码
这里不对ModBus协议进行过多介绍,网上有很多资料。但有个疑点还是有必要搞清楚。在ModbusTCP里什么是主站(主设备)?什么是从站(从设备)??
我相信会有人不假思索地说主站不就是服务器,从站不就是客户端码???
哈哈,如果你仔细读了 ModBus 设备间通信处理流程的话,你可以搞懂的。俺老李就不老生常谈了。
2、状态机的实现解析
首先从网上下载modbus协议源码文件,打开文件夹如下:

首先使用Source Insight编辑器打开mb.c文件(一般重要的文件放在工程的根目录下相相当于main函数,这个和人类的编程习惯有关)。
打开之后。嗯分析文件之前先说下状态机,状态机的运行是要轮询poll是否建立modbus通信、判断通信数据是接收请求还是请求响应。
注意关键字poll在编辑器的左下角找到带有poll的函数。

就是eMBPoll函数,这里面就有实现设备间Modbus通信状态机源代码以及相应的简单注释如下所示:
/* Check if the protocol stack is ready. */
//判断协议栈硬件环境有没有准备。好比如ModBusTCP协议里基于W5500以太网芯片的初始化,sockt通信建立提供TCP服务。
if( eMBState != STATE_ENABLED )
{
return MB_EILLSTATE;
} /* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
//通过xMBPortEventGet函数处理如果有事件发生得到状态机的状态值并赋值给eEvent,
//然后通过switch语句运行状态机调用相应函数进行处理,当然这里用了函数指针、回调。
if( xMBPortEventGet( &eEvent ) == TRUE )
{
//
switch ( eEvent )
{
case EV_READY:
break; case EV_FRAME_RECEIVED:
eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
if( eStatus == MB_ENOERR )
{
/* Check if the frame is for us. If not ignore the frame. */
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
{
( void )xMBPortEventPost( EV_EXECUTE );
}
}
break; case EV_EXECUTE:
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
for( i = ; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if( xFuncHandlers[i].ucFunctionCode == )
{
break;
}
else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
{
eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
break;
}
} /* If the request was not sent to the broadcast address we
* return a reply. */
if( ucRcvAddress != MB_ADDRESS_BROADCAST )
{
if( eException != MB_EX_NONE )
{
/* An exception occured. Build an error frame. */
usLength = ;
ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
ucMBFrame[usLength++] = eException;
}
if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
{
vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
}
eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
}
break; case EV_FRAME_SENT:
break;
}
}
从程序源码里面解析框架图如下:

大家可以根据上图理解状态机的结构框架,本文解析到此结束。关于相关在状态机里面怎么回调相关处理函数的下章解析。
FreeModBus源码解析(1)---开篇的更多相关文章
- SuperSocket源码解析之开篇
一 简介 官方介绍:SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架.你无须了解如何使用 Socket, 如何维护 Socket 连接和 S ...
- SuperSocket源码解析之开篇 (转)
一 简介 官方介绍:SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架.你无须了解如何使用 Socket, 如何维护 Socket 连接和 S ...
- Spring源码解析系列汇总
相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...
- Maven 依赖调解源码解析(一):开篇
本文是系列文章<Maven 源码解析:依赖调解是如何实现的?>第一篇,主要做个开头介绍.并为后续的实验做一些准备.系列文章总目录参见:https://www.cnblogs.com/xia ...
- jQuery整体架构源码解析(转载)
jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...
- 给jdk写注释系列之jdk1.6容器(5)-LinkedHashMap源码解析
前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表).接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持 ...
- jQuery整体架构源码解析
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 顺序线性表 ---- ArrayList 源码解析及实现原理分析
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...
- .NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入
作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9998021.html 写在前面 上篇文章我给大家讲解了ASP.NET Core的概念及为什么使用它,接着 ...
随机推荐
- Docker系列四: 使用UI管理docker容器
一.什么是Portainer? Portainer是Docker的图形化管理工具,提供状态显示面板.应用模板快速部署.容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作).事件日志显示.容 ...
- crm项目-需求分析
############### crm需求分析 ############### 讲师和学生:1,批量生成上课记录,2,考勤点名,3,录入成绩,4,显示成绩5,上传作业,os模块,6,下载成绩, ...
- single-value grouping |limit grouping|cutpoint grouping|Lower class limit|Upper class limit|Class width|Class mark|rounding error or roundoff error|Histograms|Dotplots|Stem-and-Leaf
2.3 Organizing Quantitative Data group quantitative data: To organize quantitative data, we first gr ...
- 跨域问题与SpringBoot解决方案
什么是跨域? 定义:浏览器从一个域名的网页取请求另一个域名下的东西.通俗点说,浏览器直接从A域访问B域中的资源是不被允许的,如果想要访问,就需要进行一步操作,这操作就叫"跨域".例 ...
- linux 新添加的硬盘格式化并挂载到目录下方法
需求: 新增加一块硬盘sdb,将sdb分区,只分一个区,格式化,挂载到目录/ssd下.原文:https://www.cnblogs.com/ddbear/p/7009736.html 1. 查看现在 ...
- RadixSort(基数排序)原理及C++代码实现
基数排序是一种思想很值得学习的排序方法. 它突破了正常的排序思维:先排高位,如果高位相同再排次高位,直至最低.它的思想是利用稳定排序从低位开始排,最后再排最高位. 另外它用来划分的位不一定是一位一位的 ...
- mysql数据库-进阶-长期维护
############### 视图 ############## """ 1.视图 视图:是一个虚拟表,其内容由查询定义.同真实的表一样,视图包含一系列带有 ...
- cs231n spring 2017 lecture3 Loss Functions and Optimization
1. Loss function是用来量化评估当前预测的好坏,loss function越小表明预测越好. 几种典型的loss function: 1)Multiclass SVM loss:一般的S ...
- Kinect_V1在Debian testing的配置指北
在Linux下驱动Kinect V1现在有两种方式,一种是使用OpenNI + SensorKinect + Nite的方案,一种是使用OpenNI2 + libfreenect的方案,第一种我没有尝 ...
- Jsp入门EL表达式_学习笔记
1.EL表达式 [1] 简介 > JSP表达式 <%= %> 用于向页面中输出一个对象. > 到JSP2.0时,在我们的页面中不允许出现 JSP表达式和 脚本片段. > ...