STM32 在串口通信时运用MODBUS协议

发布者:Lihua521最新更新时间:2025-02-18 来源: cnblogs关键字:STM32  串口通信  MODBUS协议 手机看文章 扫描二维码
随时随地手机看文章

  最近一个项目用到了MODBUS协议,就学习了一下,这里做一下记录以免后续忘记。

  要用到MODBUS肯定要先知道是MOBUS协议,这里呢我们就又要先理解协议的含义了。

  所谓的协议是什么?就是互相之间的约定嘛,如果不让别人知道那就是暗号。现在就来定义一个新的最简单协议。

  例如:  协议:  “A”--“LED灭”  “B”--“报警”  “C”--“LED亮” 。

  单片机接收到“A”控制一个LED灭,单片机接收到“B”控制报警,单片机接收到“A”控制一个LED亮。那么当收到对应的信息就执行相应的动作,这就是协议。

  MODBUS 一包数据主要组成有 :   设备地址 功能码  数据长度  数据  CRC 

  先来简单分析一条MODBUS-RTU报文,例如:

  这个包数据的意思是往设备地址为 0x01 的设备中,执行 0x1f 代号的功能 ,这里假设 0x1f 代号功能码代表的是保存数据再Flash,那这包数据的意思就是,控制 0x01 设备,执行 0x1f 代号功能保存后面4位数据到 MCU 的 Flash 中,其中数据长度是指后面数据字节数的大小,最后两个 CRC 校验是除了最后两位 CRC 前面所有数据经过CRC运算出来的校验码,用来保证数据的准确性。

  协议大概就是这么些内容,因为只是简单的应用,就没有深入去研究了,知道这些运用到STM32上已经够了,下面的是CRC校验代码(重点):


/***

 函数名:uint16_t Crc(uint8_t Rxbuff[],uint8_t Rx_len)

 说  明:Modbus协议CRC校验

 传入值:Rxbuff[] 串口接收的数据,len串口接收的数据长度

 传出值:返回两个字节的CRC校验码,高位在前,低位在后

**/

uint16_t Crc(uint8_t Rxbuff[],uint8_t Rx_len)

{

    uint8_t len = Rx_len - 2;

    uint16_t crc_result = 0xffff;

    int crc_num =0;

    int xor_flag=0;

    for(int i=0;i    {

        crc_result ^= Rxbuff[i];

        crc_num = (crc_result&0x0001);

        for(int m=0;m<8;m++)

        {

            if(crc_num==1)

                xor_flag = 1;

            else

                xor_flag = 0;

            crc_result >>= 1;

            if(xor_flag)

                crc_result ^= 0xa001;

            crc_num = (crc_result & 0x0001);

        }

    }

    return crc_result;

}


下面的是MODBUS运用的举例代码,这里我对MODBUS协议做了些修改,在 “功能码 ” 跟 “数据长度 ” 间多加了个 “读写标志位”,实际项目时可以根据自己项目需求做一下修改也无可厚非:


/***

 函数名:void modbus(uint8_t Rxbuff[],uint8_t len)

 说  明:Modbus协议处理

 传入值:Rxbuff[] 串口接收的数据,len串口接收的数据长度

 传出值:无

**/

void modbus(uint8_t Rxbuff[],uint8_t len)

{

    uint8_t Read_Robot [1]={0};

    uint8_t Robot_add=0x01;

    uint8_t Robot = Get_Robot_Num();  //获取预先保存好的设备地址

    uint16_t crc=Crc(Rxbuff,len);

    uint16_t Rx_crc = Rxbuff[len-2]<<8 | Rxbuff[len-1];

    if((Robot==Rxbuff[0] ||Rxbuff[0] ==0xff) && Rx_crc==crc) //判断设备地址是否正确,预留在不知道地址的时候,使用虚拟地址0xff,防止忘记设备地址后,用虚拟地址可以修改

    {

        switch(Rxbuff[1])

        {

            case 0x01: //操作ID

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,ID_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,ID_Flash_Add);

                break;

            case 0x02: //操作名称

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Name_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Name_Flash_Add);

                break;

            case 0x03: //操作环节

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Link_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Link_Flash_Add);

                break;

            case 0x04: //操作型号

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Model_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Model_Flash_Add);

            break;

            case 0x05: //操作品牌

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Brand_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Brand_Flash_Add);                

            break;

            case 0x06: //操作行数

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Linage_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Linage_Flash_Add);    

                break;

            case 0x07: //操作幅宽

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Wite_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Wite_Flash_Add);    

                break;

            case 0x08: //操作高度

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Hight_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Hight_Flash_Add);                    

                break;

            case 0x09: //操作马力

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Soup_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Soup_Flash_Add);                    

                break;

            case 0x0a: //操作合作社ID

                if(Rxbuff[2]==0x00) //读

                        Read(MCU_Rx_buff,Artel_Flash_Add);

                else if(Rxbuff[2]==0x01) //写

                         Write(Rxbuff,len,Artel_Flash_Add);                    

                break;

            case 0x21: //修改波特率

                if(Rxbuff[2]==0x00) //读

                {

                        Get_Boud_num();

                }

                else if(Rxbuff[2]==0x01) //写

                {

                      Boud_Set(Rxbuff);

                        HAL_UART_Transmit(&huart1,Rxbuff,len,0x1000);                    

                }

                break;

            case 0x22: //操作设备地址

                if(Rxbuff[2]==0x00) //读

                {

                        Read_Robot[0] = Get_Robot_Num();

                      HAL_UART_Transmit(&huart1,Read_Robot,1,0x1000);

                }

                else if(Rxbuff[2]==0x01) //写

                {

                    Robot_add=Rxbuff[5]; 

                    Robot_Num_Init(Robot_add);

                    HAL_UART_Transmit(&huart1,Rxbuff,len,0x1000);

                }                    

[1] [2]
关键字:STM32  串口通信  MODBUS协议 引用地址:STM32 在串口通信时运用MODBUS协议

上一篇:STM32 HAL库关于串口中断烧录程序后可以正常运行
下一篇:STM32 实现内部Flash的读写(HAL库版)

推荐阅读最新更新时间:2026-03-24 16:28

STM32串口通信时运用MODBUS协议
  最近一个项目用到了MODBUS协议,就学习了一下,这里做一下记录以免后续忘记。   要用到MODBUS肯定要先知道是MOBUS协议,这里呢我们就又要先理解协议的含义了。   所谓的协议是什么?就是互相之间的约定嘛,如果不让别人知道那就是暗号。现在就来定义一个新的最简单协议。   例如:  协议:  “A”--“LED灭”  “B”--“报警”  “C”--“LED亮” 。   单片机接收到“A”控制一个LED灭,单片机接收到“B”控制报警,单片机接收到“A”控制一个LED亮。那么当收到对应的信息就执行相应的动作,这就是协议。   MODBUS 一包数据主要组成有 :   设备地址 功能码  数据长度  数据  CRC    先
[单片机]
<font color='red'>STM32</font> 在<font color='red'>串口通信</font>时运用<font color='red'>MODBUS协议</font>
单片机 STM32 HAL modbus协议
#include modbus.h /*CRC高位字节值表*/ const uint8_t auchCRCHi = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0
[单片机]
STM32与树莓派串口通信实现(简易教程,无需安装micro-ROS版)
零、预设: 本次实验是为了给ROS2模块小车的通信打基础,我们选择stm32f407和树莓派4B进行电机控制和数据处理,二者之间需要进行串口通信,找到一个比较方便的方法(无需安装micro-ROS) 一、基本思路: 和普通的串口通信其实都差不多,只是stm32发送的数据要用树莓派的ros系统上来接收,我们这次只是为了验证能否正常通信,于是树莓派的ros系统上我们就用cutecom串口收发助手来进行接收 二、具体操作: 1、接线: 我们使用TTL转串口来进行,注意stm32引出的串口RX、TX要和TTL转串口的TX、RX交错连接,引脚电平3v3,别忘了GND。 2、stm32端的代码编
[单片机]
<font color='red'>STM32</font>与树莓派<font color='red'>串口通信</font>实现(简易教程,无需安装micro-ROS版)
STM32 USART串口通信深度解析(第9篇)
一、通信协议介绍 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。 当STM32想要实现一些功能,但是需要外挂一些其他模块才能实现,这就需要在两个设备之间连接上一根或多跟通信线,通过通信线路发送或者接收数据,完成数据交换,从而实现控制外挂模块和读取外挂模块数据的目的。 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发。 单工:只允许数据单向传输; 双工:指通信双方能够进行双向通信; 半双工:发送数据和接收数据不能同时传输; 全双工:可以同时进行发送数据和接受数据,一般有两根通信线,一根发送数据,一根接收数据,发送线路和接收线路互不影响。 时钟特性分为同步和异步。 同步:一般有时钟线连接,接收设备可以在时钟
[单片机]
<font color='red'>STM32</font> USART<font color='red'>串口通信</font>深度解析(第9篇)
STM32学习:USART串口通信
一、STM32F1的USART介绍 (一)串口通信简介 串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线等,按位进行传输数据的一种通信方式,属于串行通信方式。串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。 (1)接口标准 串口通信的接口标准有很多,有RS-232C、RS-232、RS-422A、RS-485等。 常用的就是RS-232和RS-485。RS-232其实是RS-232C的改进,原理是一样的。 RS-232C是EIA(美国电子工业协会)1969年修订RS-232C标准。 RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的
[单片机]
STM32串口通信过程详解
按照数据传送方向分类: 单工:数据传输只支持数据在一个方向上传输; 半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口; 全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。 分别如下图中的a、b、c所示: 按照通信方式分类: 同步通信:带时钟同步信号传输。比如:SPI,IIC通信接口; 异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线; 在同步通讯中,收发设备上方会使用一根信号线传输信号,在时钟信号的驱动下双方进行协调,同步数
[单片机]
<font color='red'>STM32</font><font color='red'>串口通信</font>过程详解
STM32串口通信的原理
通信接口背景知识 设备之间通信的方式 一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种。并行与串行通信的区别如下表所示。 串行通信的分类 1、按照数据传送方向,分为: 单工:数据传输只支持数据在一个方向上传输; 半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。 全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。 2、按照通信方式,分为: 同步通信:带时钟同步信号传输。比如:SPI,IIC通信接口。 异步通信:不带时钟同步信号
[单片机]
<font color='red'>STM32</font><font color='red'>串口通信</font>的原理
STM32串口通信的重要性
刚开始学单片机的你,是不是会因用程序把LED点亮而感到高兴,会因用程序把数码管点亮而感到高兴。这是好事,这也是想继续学习下去的动力。 但是数据相关的实验是学习单片机和STM32的一道坎,此时就需要用好本文要说的串口这个调试工具。 串口通信介绍 关于串口通信的介绍,也可以点击阅读之前发的一篇文章。 STM32串口通信基本原理 串口通信是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。..。..这种太过理论了,看似懂了,但又不懂。还是用我笔者自己的话来说吧。 串口通信就是可以把程序在单片机或者STM32芯片中运行的结果发送到电脑的一种通信方式。 如何使用串口通讯,你需要知道的几个重要的知识点
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved