Modbus通信协议应用——风速传感器HS-FS01(串口显示)

news/2024/11/26 6:41:09/

风速传感器HS-FS01 485型采用Modbus-Rtu通信协议,本代码部分基于正点原子f1战舰V3。

首先,了解一下什么是Modbus协议

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。

Modbus通讯协议是应用层报文传输协议(OSI模型第7层),它定义了一个与通信层无关的协议数据单元(PDU),即PDU=功能码+数据域。Modbus协议只能主站和从站之间通信,从站和从站之间不能通信。Modbus某些特征是固定的,如信息帧结构,帧顺序,通信错误,异常情况处理,及所执行的功能码。其他特征是可选的(传输介质、波特率、奇偶校验、停止位个数、参数字址定义)。
Modbus协议能够应用在不同类型的总线或网络。对应不同的总线或网络,Modbus协议引入一些附加域映射成应用数据单元(ADU),即ADU=附加域+PDU。目前,Modbus有下列三种通信方式:
1. 以太网,对应的通信模式是MODBUS TCP。
2. 异步串行传输(各种介质如有线
Rs232-/422/485/;光纤、无线等),对应的通信模式是MODBUS RTU或MODBUS ASCII。
3. 高速令牌传递网络,对应的通信模式是Modbus PLUS

Modbus通信协议得到广泛应用的原因是:

  •  公开发表并且无版权要求
  • 易于部署和维护
  • 对供应商来说,修改移动本地的比特或字节没有很多限制

在介绍通信协议之前,首先要明白通信协议三要素:

  1. 通信接口标准(即硬件协议,如电平高低)
  2. 通信格式      (异步通信中,双方必须统一设置的参数,数据的传送方式、传送数据的长度、校验的方法,和传输速率)   
  3. 通信数据格式 (规定传输的内容,数据帧的结构)

本片内容只介绍Modbus三种通信方式的第二条,异步串行传输(Rs485)所对应的两种通信模式MODBUS  ASCII 模式和MODBUS RTU 模式。两种模式在通讯上有所不同。

  • MODBUS ASCII

主要优点是允许字符之间的时间间隔长达1s,也不会出现错误。同时也是其缺点,传输速度较慢。

ASCII模式通信格式约定

  1. 起始位 :1位
  2. 数据长度 : 7位 ,低位先送
  3. 校验位 : 1位(有校验)  , 0位(无校验)
  4. 停止位 : 1位(有校验) ,2位(无校验)
  5. 波特率 : 可选

ASCII模式数据格式约定

信息编码 : 16进制。

LRC校验码(参与校验的位置从地址码到数据区

算法:(8位校验码) 相邻2个16进制符相加求和。 取其和的低八位的补码为校验码。

例:

求和: H01+H03+H21+H02+H00+H02   =   H29  求补码 : H29的补码为 :HD7   则LRC校验码为HD7

ASCII模式数据传送约定

ASCII模式数据传送约定在数据格式中每个16进制字符都转换成ASCII码发送。

如上图则转换为:    :      0      1      0      3      2      1    ········································

对应的ASCII码 :  (0x) 30    31    30    33    32    31   ········································

 

  • MODBUS RTU

​​​​​​​主要优点是在相同波特率下其传输的字符的密度高于ASCII模式,每个信息必须连续传输。同时也是其缺点,传输过程中更容易出错。

RTU模式通信格式约定

  1. 起始位 : 1位
  2. 数据长度 : 8位 ,低位先送
  3. 校验位 : 1位(有校验) ,0位(无校验)
  4. 停止位 : 1位(有检验) ,2位(无校验)
  5. 波特率 : 可选

RTU模式数据格式约定

信息编码 : 16进制

CRC校验码(​​​​​​​参与校验的位置从地址码到数据区

算法 :(16位校验码) 由于算法比较复杂,附相关代码 (附一)

RTU模式数据传送约定

RTU模式数据传送约定按数据格式中16进制字符进行连续发送,如果发送帧期间,出现大于1.5个字符的停止时间时,则信息会出现错误。

发送实例:

/*******************************************************************************************************************************************/

  • 常用功能码

风速传感器HS-FS01使用的功能码。其他功能码请参考  MODBUS功能码

modbus.c 和modbus.h  (CRC校验函数在附一中)

modbus.h
/*****************************************************************/
#ifndef __MODBUS_H__
#define __MODBUS_H__
#include "stm32f10x.h"
#define T_R_Mode  PDout(7)typedef struct mod{u8 address; //设备地址u8 sendbuf[64];//发送缓冲区u8 recbuf[64]; //接受缓冲区u8 timflag;  //数据接收时间u8 timrun;   //定时器启动标志u8 reflag;   //接收到一帧数据的标志u8 recount;  //接受的数据个数u8 secount;  //发送的数据个数}Modbus;void modbus_init(void);
void modbus_fun3(u8 add,u16 readd,u16 renum);
void modbus_display(void);
void modbus_event(void);#endif/******************************************************************/modbus.c
/******************************************************************/#include "modbus.h"
#include  "sys.h"
#include "misc.h"
#include "ModbusCRC.h"
#include "stdio.h"extern vu8 temp;
Modbus modbus;void modbus_init()
{GPIO_InitTypeDef GPIO_InitTypeStruct;USART_InitTypeDef USART_InitTypeStruct;NVIC_InitTypeDef NVIC_InitTypeStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_7;GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOD,&GPIO_InitTypeStruct);   //    收发控制GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_2;GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitTypeStruct);   //PA2 USART2 TXGPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_3;GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitTypeStruct);    //PA3 USART2 RXUSART_InitTypeStruct.USART_BaudRate=9600;USART_InitTypeStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_InitTypeStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;USART_InitTypeStruct.USART_Parity=USART_Parity_No;USART_InitTypeStruct.USART_StopBits=USART_StopBits_2;USART_InitTypeStruct.USART_WordLength=USART_WordLength_8b;USART_Init(USART2,&USART_InitTypeStruct);USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);NVIC_InitTypeStruct.NVIC_IRQChannel=USART2_IRQn;NVIC_InitTypeStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitTypeStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitTypeStruct.NVIC_IRQChannelSubPriority=2;NVIC_Init(&NVIC_InitTypeStruct);USART_Cmd(USART2,ENABLE);T_R_Mode=1;  //默认为发送模式}void modbus_fun3(u8 add,u16 readd,u16 renum)
{u16 crc,i;modbus.secount=0;modbus.sendbuf[modbus.secount++]=add; //读取设备地址modbus.sendbuf[modbus.secount++]=0x03; //功能码modbus.sendbuf[modbus.secount++]=readd/256; //读取寄存器高地址modbus.sendbuf[modbus.secount++]=readd%256; //读取寄存器低地址modbus.sendbuf[modbus.secount++]=renum/256; //读取寄存器数量高八位modbus.sendbuf[modbus.secount++]=renum%256;  //读取寄存器数量低八位crc=crc16(modbus.sendbuf,modbus.secount);   //计算CRC校验码modbus.sendbuf[modbus.secount++]=crc/256;  //校验码高八位modbus.sendbuf[modbus.secount++]=crc%256;  //校验码低八位T_R_Mode=1; //sendfor(i=0;i<modbus.secount;i++){USART_SendData(USART2,modbus.sendbuf[i]);while(!USART_GetFlagStatus(USART2,USART_FLAG_TC));}T_R_Mode=0;
}void modbus_display()
{u8 i;printf("----------------------------------\r\n");printf("-----------发送的命令为-----------\r\n");for(i=0;i<modbus.secount;i++){printf("%2X  ",modbus.sendbuf[i]);}printf("\r\n");printf("------------接收的命令为----------\r\n");for(i=0;i<modbus.recount;i++){printf("%2X  ",modbus.recbuf[i]);}printf("\r\n");printf("------------------------------------\r\n");printf("*************************************\r\n");
}void modbus_event()
{u16 crc,rccrc;if(modbus.reflag==0)  return ;  //没有数据crc=crc16(modbus.recbuf,modbus.recount);rccrc=modbus.recbuf[modbus.recount-2]*256+modbus.recbuf[modbus.recount-1];if(crc==rccrc){modbus_display();           /*使用HS-FS01风速传感器时直接显示不检查校验码*/}modbus.secount=0;modbus.recount=0;modbus.reflag=0; 
}void USART2_IRQHandler()
{if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET){temp=USART_ReceiveData(USART2);if(modbus.reflag==1) return ;  //有数据正在处理modbus.recbuf[modbus.recount++]=temp;modbus.timflag=0; //清零计时位if(modbus.recount==1)   //主机发送的一个数据的第一字节modbus.timrun=1;  //启动定时}
}/******************************************************************/

tim.c 和 tim.h

tim.h
/**************************************************************/#ifndef __TIM_H__
#define __TIM_H__
#include "stm32f10x.h"void tim3_init(u16 arr,u16 psc);#endif
/***************************************************************/tim.c
/****************************************************************/
#include "tim.h"
#include "modbus.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "misc.h"extern  Modbus modbus;void tim3_init(u16 arr,u16 psc)  //arr 999 psc 71
{NVIC_InitTypeDef NVIC_InitTypeStruct;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruct;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); // tim3 enableTIM_TimeBaseInitTypeStruct.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitTypeStruct.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitTypeStruct.TIM_Period=arr;TIM_TimeBaseInitTypeStruct.TIM_Prescaler=psc;TIM_TimeBaseInitTypeStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitTypeStruct);   // tim3 base setTIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);   //tim3 it setNVIC_InitTypeStruct.NVIC_IRQChannel=TIM3_IRQn;NVIC_InitTypeStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitTypeStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitTypeStruct.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitTypeStruct);   //tim3 nvic  setTIM_Cmd(TIM3,ENABLE);  //tim3 cmd set}void TIM3_IRQHandler()   //tim it 
{if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET){if(modbus.timrun==1) //定时器开始计时{modbus.timflag++; if(modbus.timflag>=8)   //8ms 计时   默认8ms接收完成{modbus.timrun=0;  //关闭计时modbus.reflag=1;   //接收到一帧数据标志 } 	}		}TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}/******************************************************************/

main.c

main.c#include "stm32f10x.h"
#include "tim.h"
#include "modbus.h"
#include "misc.h"                 /*使用HS-FS01风速传感器时直接显示,不检查校验码*/
#include "usart.h"                /*请自行修改modbus_event(); 函数*/vu8 temp;int main()
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);uart_init(72);tim3_init(999,71);modbus_init();while(1){modbus_fun3(0x02,0x2A,0x01);  //发送指令modbus_event();  //处理数据}
}

附一:

/* CRC 高位字节值表 */
const uchar 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, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 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,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 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, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,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, 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, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
} ;
/* CRC低位字节值表*/
const uchar auchCRCLo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40
} ;/******************************************************************
功能: CRC16校验
输入:
输出:
******************************************************************/
uint crc16( uchar *puchMsg, uint usDataLen )
{uchar uchCRCHi = 0xFF ; // 高CRC字节初始化uchar uchCRCLo = 0xFF ; // 低CRC 字节初始化unsigned long uIndex ; 		// CRC循环中的索引while ( usDataLen-- ) 	// 传输消息缓冲区{uIndex = uchCRCHi ^ *puchMsg++ ; 	// 计算CRCuchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;uchCRCLo = auchCRCLo[uIndex] ;}return ( uchCRCHi << 8 | uchCRCLo ) ;
}

 


http://www.ppmy.cn/news/162747.html

相关文章

转-HD7 刷机教程 硬起 进三色屏详解教程

转-HD7 刷机教程 硬起 进三色屏详解教程 前言、 1、由于本帖中含有大量刷机基础和术语&#xff0c;因此对于没有任何刷机经验的人请先在论坛多多学习后再刷机 2、刷机步骤目录&#xff1a; 1&#xff09;选择、下载ROM&#xff1b;手机充电至50%以上 2&#xff09;开始刷机&…

MODBUS通信协议学习(二):通信方式

目录 前言MODBUS通信模式串行链路Modbus ASCII通信格式数据格式LRC Modbus RTU通信格式数据格式CRC 网络链路通信格式TCPModbus TCP/IP 数据格式MBAP报头文&#xff1a; 前言 初步了解了链接: MODBUS协议.现在具体学习通信模式。 这篇文章在学习过程中作为总结&#xff0c;如有…

【存储数据恢复】HP EVA存储误删除VDISK的数据恢复案例

存储数据恢复环境&#xff1a; 一台HP EVA某型号存储&#xff0c;2组扩展柜&#xff1b; 12块FATA磁盘10个FC磁盘&#xff0c;不确定LUN数量&#xff1b; WINDOWS操作系统&#xff0c;存储历史案例审理材料。 存储故障原因不明。 存储故障初检&分析&#xff1a; 1、通过和管…

在VMWare虚拟机中安装HMC V7R770

在VMWare虚拟机中安装HMC V7R770 环境&#xff1a; HMC version&#xff1a;V7R77.0 SP0 build level 20130116.1 VMware: VMware Workstation 15 Pro 15.5.2 build-15785246 Host OS: Windows 10 Enterprise 版本 10.0.18363 版本 18363 (1909) Host HW: LENOVO_MT_20LE_BU_Th…

AES_128加密解密算法,verilog实现。完整代码

具体的理论知识&#xff0c;本人不在详述。网上已经有很多了 AES128加密算法完整实现_u013605322的博客-CSDN博客_aes128加密算法 AES加密 - block2016 - 博客园 AES算法简介_Jimmy.li的博客-CSDN博客_aes算法 密码算法详解——AES - ReadingLover - 博客园 以上内容都对a…

hbase table region 按条件自动批量合并

环境信息&#xff1a; HBase 1.2.2&#xff0c;Hadoop 2.7.2 使用需求&#xff1a; 为什么需要合并Region呢&#xff1f;这个需要从Region的Split来说。当一个Region被不断的写数据&#xff0c;达到Region的Split的阀值时&#xff08;由属性hbase.hregion.max.filesize来决定…

MooseFS 集群安装部署

节点信息 主机名ip地址软件备注名mfsmaster192.168.100.20moosefs-master moosefs-cgi moosefs-cgiserv moosefs-cliMaster Serversmetaloggers192.168.100.20moosefs-metaloggerMetaloggers,本实验安装于一起mfschunkserver01192.168.100.21moosefs-chunkserverChunkserversm…

手机屏幕技术浅析

对手机本身不是特别关心的人对自己手机的屏幕采用何种材质并不在意&#xff0c;不过现在手机屏幕材质的多样化导致在不同手机上图像、文本显示的显著差异。——那些三星的粉丝整天宣扬他们的A屏或者SA屏有多牛叉。使用iPhone的同人是否了解你们的屏幕材质相对三星又有怎样的差异…