目录

一、本文讨论内容

二、工具与源码

三、Modbus概述

四、Modbus-RTU通讯协议

五、完成Modbus输入输出代码

六、完成Modbus逻辑功能

七、测试与验证


一、本文讨论内容

本文简明概要的介绍Modbus通讯协议,并在STM32上实现Modbus协议,通过与Modbus Poll(一种Modbus上位机工具)联合调试以验证程序的正确性。

        本文B站有视频讲解!均为作者原创。up主名字:“芯心智库”。

        视频链接:芯心智库的个人空间_哔哩哔哩_bilibili

二、工具与源码

工具与源码下载方式:微信公众号关注“芯心智库”,回复“modbus”即可获取。

三、Modbus概述

Modbus是主从方式通信,一个总线上只有一个主机,但可以有多个从机(这一点类似于IIC的通讯方式)。简单来说,也就是主机问从机答。问啥呢?主要问两个问题--从机的寄存器(线圈)的数值是多少、主机要求从机修改一下寄存器(线圈)的数值并回答。

Modbus还可以细分很多种协议,但是主要就是四种:

1、Modbus-RTU(设备必须要有RTU协议!这是Modbus协议上规定的,且默认模式必须是RTU)

2、Modbus-ASCII(人类可读的,冗长的表示方式)

3、Modbus-TCP(以太网作为介质进行传输)

4、Modbus-PLUS(高速现场总线网络)

其实从本质来讲,这四个都是差不多的,你只要掌握一种,其余三种都可以轻而易举地掌握。本文就讲解Modbus-RTU协议,这个是最常使用的协议。

四、Modbus-RTU通讯协议

结合前面所讲,再来理解一下Modbus通讯协议,我们可以把通讯过程理解为主机叫哪个从机,做什么事情,怎么做,最后主机检查自己描述的对不对。一共四个步骤,也就是每次通讯都需要包含这四个步骤,每一次通讯都需要发送一个帧结构。所以就有 帧结构 = 地址 + 功能码 + 数据 + CRC校验。地址就是相当于哪个从机,功能码就是要干的事情,数据就是要具体怎么做,最后来个CRC校验确保数据是对的。值得注意的是地址: 地址的有效范围是1-247,其他有特殊用途,比如255是广播地址(广播地址就是应答所有地址,正常的需要两个设备的地址一样才能进行查询和回复),而且RTU没有帧头和帧尾,所以协议里明确两帧之间要大于3.5个字节时间间隔,作为一帧结束的判断依据。

功能码有很多,但是最主要用到的是03,06,16,而且软件Modbus Poll也主要是使用这几个功能码,掌握这三个功能码,其余码的使用也是大同小异。

接着我们理解一下这三个功能码的区别:功能码03是查询寄存器,功能码06修改单个寄存器,功能码16是修改连续的多个寄存器。

下面直接实战理解一下。

①功能码03实战:

主机发送: 01 03 00 00 00 01 84 0A

从机回复: 01 03 02 01 02 35 15

解析:/*主机发送解析*/01-地址03-功能码,代表查询功能,其他功能后面再说00 00-代表查询的起始寄存器地址.说明从0x0000开始查询.(这里需要说明以下,Modbus把数据存放在寄存器中,通过查询寄存器来得到不同变量的值,一个寄存器地址对应2字节数据;)00 01-代表查询了一个寄存器.结合前面的00 00,意思就是查询从0开始的1个寄存器值;84 0A-循环冗余校验,是modbus的校验公式,从首个字节开始到84前面为止;(这里新手可能不懂,这个校验就是保证数据传输过程没有错误的一种手段,不同的协议这种校验公式不一样,只需了解这个就足够了,具体怎么求的,可以直接在输出数据得到结果,地址为:http://www.ip33.com/crc.html)/*从机回复解析*/01-地址03-功能码02-代表后面数据的字节数,因为上面说到,一个寄存器有2个字节,所以后面的字节数肯定是2*查询的寄存器个数;01 02-寄存器的值是0x0102,结合发送的数据看出,01这个寄存器的值为0x010235 15-循环冗余校验

②功能码06实战:

主机发送: 01 06 00 00 12 34 84 BD

从机回复: 01 06 00 00 12 34 84 BD

解析:/* 主机发送解析 */01-主机要查的地址06-功能码,代表修改单个寄存器功能00 00-代表修改的起始寄存器地址.说明从0x0000开始.12 34-代表修改的值为0x12 34.结合前面的00 00,意思就是修改0号寄存器值为0x1234;84 BD -循环冗余校验,是Modbus的校验公式,从首个字节开始到84前面为止;/*从机回复解析*/01-从机返回的地址,说明这就是主机查的从机06-功能码,代表修改单个寄存器功能;00 00-代表修改的起始寄存器地址.说明是0x0000.12 34-代表修改的值为0x1234.结合前面的00 00,意思就是修改0号寄存器值为0x1234;84 BD -循环冗余校验,是Modbus的校验公式,从首个字节开始到84前面为止;

③功能码16实战:

主机发送: 01 10 00 00 00 02 04 00 11 22 33 FB 1F

从机回复: 01 10 00 00 00 02 41 C8

解析:/*主机发送解析*/01-主机要查的地址10-功能码,0x10=16D代表修改多个寄存器功能;00 00-代表修改的起始寄存器地址.说明从0x0000开始.00 02-代表修改的寄存器数量,这里开始于0x06的修改不同;04 -表示修改的总字节数,由于修改了2个寄存器,所以数据要有4个字节;00 11-表示修改的值,结合上面,就是从第0000寄存器开始修改第一个寄存器值为0x0011,就是把0000寄存器改为0x0011;22 33-表示修改的值,结合上面,就是从第0000寄存器开始修改第二个寄存器值为0x22 33,就是把0001寄存器改为0x2233;FB 1F -循环冗余校验,是Modbus的校验公式,从首个字节开始到22前面为止;/*从机回复解析*/01-从机返回的地址,说明这就是主机查的从机10-功能码00 00-代表修改的起始寄存器地址.说明是0x0000.00 02-代表修改的寄存器数量,只需要回复这么多久足够了,从机告诉主机,你修改了哪几个寄存器就足够了;41 C8-循环冗余校验;

五、完成Modbus输入输出代码

我们使用正点原子的战舰开发板,程序的模板基于正点原子的“实验8 定时器中断实验(HAL库)”进行开发,以串口(USART1)作为介质与上位机Modbus Poll通讯。

先说一下输出部分的程序,这个就最简单的,直接调用HAL库的函数就可以了。代码如下:

/* 发送数据函数,buff为发送内容,len表示发送字节数 */void modbus_send_data(u8 *buff,u8 len){HAL_UART_Transmit(&UART1_Handler,(uint8_t*)buff,len,1000); //发送数据while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);//等待发送结束}

然后输入部分,不知道读者记不得前文说的“两帧之间要大于3.5个字节时间间隔,作为一帧结束的判断依据”这句话,那么字节时间怎么计算呢?如果波特率为9600,代表1秒可以传送9600 bits的数据,以业内常用的格式是8位数据,无校验位,1位停止位,加上一个必须的1 bit的起始位,那么一字节的串口数据一共是10位。所以每个字节时间为1/(9600/10)秒,也就是1.04ms。两帧之间要大于3.5个字节时间间隔,那么间隔时间设置为4ms即可。

在程序中,我们定义一个变量modbus_time作为间隔时间的标志位,把modbus_time放入定时器中断服务函数(周期:1ms)中,让其一直增加数值,在串口中断函数中只要收到1Byte消息便会把modbus_time清零,当modbus_time大于4(字节间隔了4ms)且串口曾收到数据但没有接收到新的字节便认为一个帧结构数据接收完成。

举个栗子,有01 02 03数据要发送过来,当数据01发送过来时,串口中断清零modbus_time,此时modbus_time=0,然后过了1.04秒后,数据02发了过来,此时modbus_time=1,但是串口中断清零modbus_time,此时modbus_time又变成0,然后过了1.04秒后,数据03发了过来,此时modbus_time=1,但是串口中断清零modbus_time,此时modbus_time又变成0,最后没有数据再发送过来了,此时modbus_time大于4(字节间隔了4ms)且串口曾收到数据但没有接收到新的字节便认为一个帧结构数据接收完成。

此外我们需要修改串口中断服务函数,把接收状态标记USART_RX_STA的接收完成标志bit15相关功能屏蔽(因为正点原子的原本例程是串口收到0x0d 与 0x0a表示收到完整的串口数据),然后将接收状态标记USART_RX_STA的接收完成标志bit15写在定时器中断函数中作为Modbus接收完成标志位。代码如下

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){if(huart->Instance==USART1)//如果是串口1{if((USART_RX_STA&0x8000)==0)//接收未完成{//                  if(USART_RX_STA&0x4000)//接收到了0x0d//                  {//                         if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始//                         else USART_RX_STA|=0x8000;    //接收完成了//                  }//                  else //还没收到0X0D//                  {     //                         if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;//                         else//                         {USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;USART_RX_STA++;modbus_time = 0;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收   //                         }            //                  }}}}
//回调函数,定时器中断服务函数调用void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){if(htim==(&TIM3_Handler)){modbus_time++;if((modbus_time>4) &&((modbus_time & 0x3fff)!=0)){USART_RX_STA|=0x8000;}}}

六、完成Modbus逻辑功能

这一部分考验C语言的功底,只要清楚Modbus-RTU协议并且C语言功底扎实,相信读者自己也可以办到。详细部分看笔者的源码实现。

七、测试与验证

打开上位机Modbus Poll,然后按下图设置便将上位机连接到板卡。

连接成功后,读者会发现10个寄存器的值会发生改变。这十个数值是程序初设设定的寄存器数值。如下图。

然后我们双击上图数值,便可进入快捷修改寄存器数值界面。然后便可以对功能码06和功能码16进行测试,测试结果如下:

另外点击右侧的“TC”可以自定义写入数据,我们将教程的数据写入测试可以得到下图结果。而且点击类似查找的按钮可以查看当前发送消息和接收消息内容(可以结合快捷修改寄存器数值界面使用)。

STM32移植modbus通讯协议简明教程相关推荐

  1. 移植modbus通讯协议232转换485

    232的modbus协议该位485时,需要注意: 1.portserial.c中的基本配置函数 2.中断中的配置函数 3.最重要的一点,mbrtu.c中(这个是重点) eMBRTUSend( UCHA ...

  2. Modbus通讯协议的原文和详解

    这个协议很早以前就被公开发表了,现在Modbus已经成为工业领域通信协议的业界标准,有网站专门进行协议的整理和管理. 这里必须要吐槽一下,我就想查查协议发布时的原始文档,或者这个协议的英文原文和官方版 ...

  3. Modbus通讯协议的C语言实现

    刚写完一个modbus通讯协议,调试完,整理出来了,软件代码只能为大家提供一个思路,仅供参考. //communication.h #include "H01_Global\_Define. ...

  4. 基于modbus协议的工业自动化网络规范_工控学堂:解读Modbus通讯协议「宜收藏」...

    作为工控人,Modbus通讯协议想必都不陌生,Modbus通讯协议可以说是工业自动化领域应用最为广泛的通讯协议,因为他的开放性.可扩充性和标准化使他成为通用工业标准. 1979年施耐德电气制定了一个用 ...

  5. 一文搞懂物联网Modbus通讯协议

    简介: 一般来说,常见的物联网通讯协议众多,如蓝牙.Zigbee.WiFi.ModBus.PROFINET.EtherCAT.蜂窝等.而在众多的物联网通讯协议中,Modbus是当前非常流行的一种通讯协 ...

  6. 协议:Modbus通讯协议详细

    1.Modbus通讯协议详细解释 https://blog.csdn.net/rxiang12/article/details/79125813 2.Modbus通信协议详解 https://blog ...

  7. modbus通讯协议详解

    1.Modbus协议简介 Modbus协议是一种广泛应用于当今工业控制领域的通用通信协议.通过此协议,控制器相互之间.或者控制器经由网路(如以太网)可以和其他设备之间进行通信.Modbus协议使用的是 ...

  8. Modbus通讯协议学习文档

    1 什么是Modbus通讯协议 Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表.Mo ...

  9. 485之modbus通讯协议学习笔记

    485之modbus通讯协议学习笔记 这里主要探讨两个寄存器 03 读保持寄存器 04读输入寄存器 输入寄存器其中的温度值和湿度值分别各占两个字节,一个字节八位使用十六进制00 01表示两个字节的寄存 ...

最新文章

  1. 物流企业信息化 准确定位应该如何做
  2. linux下eclipse cdt引用函数库设置问题
  3. 克隆人不只出现在科幻电影里?阿里虚拟美女“俪知”亮相
  4. Relation(NOIP模拟赛)(二分图染色)
  5. java控制器_Java用户界面 模型-视图-控制器(MVC)模式
  6. Jquery 实现动态加入table tr 和删除tr 以及checkbox的全选 和 获取加入TR删除TR后的数据...
  7. 学习C++编程的优势有哪些?
  8. 孟子模型 | 当Bert炼丹不是玄学而是哲学
  9. mysql 两表关联查询 group by having
  10. win10的任务管理器显示所占内存,比实际占用内存小的原因
  11. 传感器相关 MPU9250
  12. 万达商管冲刺香港上市:上半年净赚20亿元,已提前“套现”约63亿
  13. 与京东物流合作,能不能补全东方甄选的最后一块拼图?
  14. element 表格,实现选择的行变色
  15. 计算机机箱架硬盘托架是什么,机械硬盘托架 笔记本光驱位硬盘托架到底靠不靠谱?有人说会烧主板,我都害怕了,......
  16. 归一法的计算方法讲解_初三化学计算题中归一法,XY法,拆分法,分配法,K值法是怎样的,最好有具体的例子...
  17. 平衡小车c语言程序,【全部开源】两轮平衡小车(原理图、PCB、程序源码、BOM等)...
  18. 真是太有用了——整理央视《家有妙招》不看别后悔幺
  19. 为什么微软推出“阉割版”onenote?一篇文章快速入门onenote uwp
  20. 在Python中,val、exec和 compile 有什么区别?

热门文章

  1. (有码)NSURLSession
  2. Webpack之Tree Shaking与Scope Hoisting
  3. 反汇编吸星大法-IDA神器特殊之处
  4. C# Queue队列的介绍
  5. linux no mail for root,Linux定时任务Crontab使用 提示no crontab for root
  6. 词性标注,句法分析中NN,NP是什么意思
  7. 火爆Github的大厂Java 基础高频面试题,真的无解
  8. KNN算法实现鸢尾花数据集分类
  9. 《谋圣鬼谷子》陷三角恋 段奕宏萝莉御姐难抉择
  10. 百度地图SDK for iOS v2.1.1全新发布