最近一个项目用到了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 - ;
uint16_t crc_result = 0xffff;
int crc_num =;
int xor_flag=;
for(int i=;i<len;i++)
{
crc_result ^= Rxbuff[i];
crc_num = (crc_result&0x0001);
for(int m=;m<;m++)
{
if(crc_num==)
xor_flag = ;
else
xor_flag = ;
crc_result >>= ;
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 []={};
uint8_t Robot_add=0x01;
uint8_t Robot = Get_Robot_Num(); //获取预先保存好的设备地址
uint16_t crc=Crc(Rxbuff,len);
uint16_t Rx_crc = Rxbuff[len-]<< | Rxbuff[len-];
if((Robot==Rxbuff[] ||Rxbuff[] ==0xff) && Rx_crc==crc) //判断设备地址是否正确,预留在不知道地址的时候,使用虚拟地址0xff,防止忘记设备地址后,用虚拟地址可以修改
{
switch(Rxbuff[])
{
case 0x01: //操作ID
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,ID_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,ID_Flash_Add);
break;
case 0x02: //操作名称
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Name_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Name_Flash_Add);
break;
case 0x03: //操作环节
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Link_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Link_Flash_Add);
break;
case 0x04: //操作型号
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Model_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Model_Flash_Add);
break;
case 0x05: //操作品牌
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Brand_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Brand_Flash_Add);
break;
case 0x06: //操作行数
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Linage_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Linage_Flash_Add);
break;
case 0x07: //操作幅宽
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Wite_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Wite_Flash_Add);
break;
case 0x08: //操作高度
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Hight_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Hight_Flash_Add);
break;
case 0x09: //操作马力
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Soup_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Soup_Flash_Add);
break;
case 0x0a: //操作合作社ID
if(Rxbuff[]==0x00) //读
Read(MCU_Rx_buff,Artel_Flash_Add);
else if(Rxbuff[]==0x01) //写
Write(Rxbuff,len,Artel_Flash_Add);
break;
case 0x21: //修改波特率
if(Rxbuff[]==0x00) //读
{
Get_Boud_num();
}
else if(Rxbuff[]==0x01) //写
{
Boud_Set(Rxbuff);
HAL_UART_Transmit(&huart1,Rxbuff,len,0x1000);
}
break;
case 0x22: //操作设备地址
if(Rxbuff[]==0x00) //读
{
Read_Robot[] = Get_Robot_Num();
HAL_UART_Transmit(&huart1,Read_Robot,,0x1000);
}
else if(Rxbuff[]==0x01) //写
{
Robot_add=Rxbuff[];
Robot_Num_Init(Robot_add);
HAL_UART_Transmit(&huart1,Rxbuff,len,0x1000);
}
break;
}
}
}

  最后直接在串口接收处理函数里调用MODBUS函数即可,代码如下:

/***
函数名:void Usart_RX(void)
说 明:接收数据处理
传入值:无
传出值:无
**/
void Usart_RX(void)
{
if(Rx_End_flag==)
{
modbus(Rx_buffer,Rx_len); //调用MODBUS协议处理函数
memset(Rx_buffer,,sizeof(Rx_buffer)); //清空数组
Rx_len=;
Rx_End_flag=;//清除接收结束标志位
}
HAL_UART_Receive_DMA(&huart1,Rx_buffer,BUFFER_SIZE);
}

  只要经过这些步骤,就可以在STM32上简单运用 MODBUS 协议了,其实 MODBUS 协议说难不难,它就跟我们平时自己定义的串口通信协议类似,有数据头、数据内容、数据尾,只不过更加科学规范罢了。

  参考链接:https://wenku.baidu.com/view/9cdb001533687e21af45a9e2.html

STM32 在串口通信时运用MODBUS协议的更多相关文章

  1. (三)stm32之串口通信DMA传输完成中断

    一.DMA功能简介 首先唠叨一下DMA的基本概念,DMA的出现大大减轻了CPU的工作量.在硬件系统中,主要由CPU(内核).外设.内存(SRAM).总线等结构组成,数据经常要在内存和外设之间,外设和外 ...

  2. STM32的串口通信

    本篇文章主要讲解一个在开发过程中经常使用到的一个外设---串口. 串口是绝大多数 MCU 中不可或缺的一个外设,同时也是我们开发中经常使用的一种调试手段,所以在STM32的学习中,串口的配置使用也是必 ...

  3. STM32之串口通信

    一.RS232通信协议 1.概念 个人计算机上的通讯接口之一,由电子工业协会(Electronic Industries Association,EIA) 所制定的异步传输标准接口. 2.电气特性 逻 ...

  4. Modbus 协议

    转载:https://www.cnblogs.com/DreamRecorder/p/9081127.html 一.Modbus 协议简介     Modbus 协议是应用于电子控制器上的一种通用语言 ...

  5. Modbus协议和应用开发介绍

    因业务需要了解Modbus协议的使用,因此对Modbus的协议,以及相应的C#处理应用进行了解,针对协议的几种方式(RTU.ASCII.TCPIP)进行了封装,以及对Modbus的各种功能码的特点进行 ...

  6. 基于FPGA的红外遥控解码与PC串口通信

    基于FPGA的红外遥控解码与PC串口通信 zouxy09@qq.com http://blog.csdn.net/zouxy09 这是我的<电子设计EDA>的课程设计作业(呵呵,这个月都拿 ...

  7. 通信协议之Modbus协议(一)

    Modbus通信协议: 简介:Modbus协议是应用于电子控制器上的一种通用语言 通过此协议,控制器相互之间,控制器经由网络(例如以太网) 和其他设备之间可以通信,他已经成为一种通用工业标准,有啦它 ...

  8. BluetoothChat用于蓝牙串口通信的修改方法

    本人最近在研究嵌入式的串口通信,任务是要写一个手机端的遥控器用来遥控双轮平衡小车.界面只用了一个小时就写好了,重要的问题是如何与板子所带的SPP-CA蓝牙模块进行通信. SPP-CA模块自带代码,在这 ...

  9. QT 串口通信 数据16进制发送

    在QT中进行串口通信时,很多情况要用到发送16进制的数据.从网上找来了一段代码测试能用: static QByteArray QString2Hex(QString str) { QByteArray ...

随机推荐

  1. [19/05/26-星期日] JavaScript_ 基本语法_运算符

    一.概念 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

  2. AtCoder Beginner Contest 133-C - Remainder Minimization 2019

    https://atcoder.jp/contests/abc133/tasks/abc133_c 思路:由于L,R区间太大,所以不能暴力枚举.由于求(i*j)%2019的最小值,那么2019的倍数对 ...

  3. spring aop实现数据库的读写分离

    为了减轻数据库的压力,一般会使用数据库主从(master/slave)的方式,但是这种方式会给应用程序带来一定的麻烦,比如说,应用程序如何做到把数据写到master库,而读取数据的时候,从slave库 ...

  4. Runnable和Thread区别和比较

    在很多博客中用这样一个例子来说明 Runnable更容易实现资源共享,能多个线程同时处理一个资源. 看代码: public static void main(String[] args) { new ...

  5. VS中发布并调试IIS程序

    1.创建本地IIS站点 2.修改配置 .net framework 右击项目属性,服务器修改为本地IIS,并且项目URL修改为相对应的站点即可 .net core 右键属性,进入调试栏新建一个配置 选 ...

  6. Could not load file or assembly system.data.sqlite.dll or one for it's depedencies

    最近做一个winform项目,因为数据库用的表很少,所以用的是轻量级数据库sqlite.sqlite的优点很多,但是他要分两个版本,32位或者64位,不能同时兼容. 我遇到的问题是,我在开发端用的是. ...

  7. opengl 单缓冲与双缓冲

    1.说明 GLUT_SINGLE  指定单缓存窗口 GLUT_DOUBLE  指定双缓存窗口 应用程序使用单缓冲绘图时可能会存在图像闪烁的问题. 这是因为生成的图像不是一下子被绘制出来的,而是按照从左 ...

  8. 【vue】vue不足 待补强

    83719279 9:56:03尤其是路由 声明周期 父子通信 组件通信 以及钩子函数83719279 9:56:17这些我都不敢用,只能用最原始的方法83719279 9:56:32还有es6 js ...

  9. 进程间的mutex

    设两个进程共用一个临界资源的互斥信号量mutex=1,当mutex=-1时表示(). 一个进程进入了临界区,另一个进程等待 没有一个进程进入临界区 两个进程都进入临界区 两个进程都在等待 互斥信号量不 ...

  10. wangeditor 支持上传视频版

    1.关于使用哪个富文本编辑器. 简单的要求,不要求发布出来的文章排版要求很高.  可用wangediter.(简单,体积小,不可修改上传图片的尺寸大小) 转载 来源: https://blog.csd ...