Modbus通信协议应用——风速传感器HS-FS01(串口显示)
风速传感器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通信协议得到广泛应用的原因是:
- 公开发表并且无版权要求
- 易于部署和维护
- 对供应商来说,修改移动本地的比特或字节没有很多限制
在介绍通信协议之前,首先要明白通信协议三要素:
- 通信接口标准(即硬件协议,如电平高低)
- 通信格式 (异步通信中,双方必须统一设置的参数,数据的传送方式、传送数据的长度、校验的方法,和传输速率)
- 通信数据格式 (规定传输的内容,数据帧的结构)
本片内容只介绍Modbus三种通信方式的第二条,异步串行传输(Rs485)所对应的两种通信模式MODBUS ASCII 模式和MODBUS RTU 模式。两种模式在通讯上有所不同。
- MODBUS ASCII
主要优点是允许字符之间的时间间隔长达1s,也不会出现错误。同时也是其缺点,传输速度较慢。
ASCII模式通信格式约定
- 起始位 :1位
- 数据长度 : 7位 ,低位先送
- 校验位 : 1位(有校验) , 0位(无校验)
- 停止位 : 1位(有校验) ,2位(无校验)
- 波特率 : 可选
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位
- 数据长度 : 8位 ,低位先送
- 校验位 : 1位(有校验) ,0位(无校验)
- 停止位 : 1位(有检验) ,2位(无校验)
- 波特率 : 可选
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 ) ;
}
Modbus通信协议应用——风速传感器HS-FS01(串口显示)相关推荐
- Modbus通信协议+Modbus串口调试工具+Java版协议解析源码
网络与串口二合一调试助手TCPCOM: https://download.csdn.net/download/liuyuan_java/87454762 Modbus调试工具,模拟串口调试工具 htt ...
- 大神带你秒懂Modbus通信协议
摘要:昨天有小伙伴在群里问关于Modbus通信协议的,大家都比较积极地解答,所以今天果子哥总结一下关于Modbus相关的知识,适合正在入门的小伙伴"食用".同样还是理论+实战的方式 ...
- 【知识分享】Modbus通信协议详解
协议 这里分两部分,Modbus和协议,首先什么是协议?百度解释下就是:意思是共同计议,协商:经过谈判.协商而制定的共同承认.共同遵守的文件.比如大学毕业找工作的时候,一般要签一份叫"三方协 ...
- 干货|手把手带你搞懂Modbus通信协议
摘要:昨天有小伙伴在群里问关于Modbus通信协议的,大家都比较积极地解答,所以今天果子哥总结一下关于Modbus相关的知识,适合正在入门的小伙伴"食用".同样还是理论+实战的方式 ...
- 编写一个字节数的rtu C语言校验程序,Modbus通信协议中CRC校验的快速C语言算法
Modbus通信协议中CRC校验的快速C语言算法 2004年第11期 福 建 电 脑 63 Modbus通信协议中CRC校验的快速C语言算法 孟开元 (西安石油大学计算机学院陕西西安710065) [ ...
- 详解Modbus通信协议---清晰易懂
本文旨在让您对Modbus有一个很直观的了解,总结关于Modbus相关的知识,非常适合初学的同学,同时如有错误,欢迎修改意见和建议,将在第一时间修改 什么是协议 什么是RS-485 RS-232 Mo ...
- STM32:RS485通信和Modbus通信协议汇总
RS485通信和Modbus通信协议汇总 1. 主从模式 RS-485上的软件层协议ModBus主要依赖于主从模式.主从模式是指在半双工通讯方式上,2个或者2个以上的设备组成的通讯系统中: (1) 至 ...
- RS-485总线和Modbus通信协议的关系
一.RS-485总线 RS-485总线技术只是规定了接口的电气标准,并没有规定RS-485接口的电缆,插件以及通信协议,只是OSI规范中物理层的一个标准,RS-485总线采用差分平衡传输方式.由于RS ...
- 干货分享:RS485通信和Modbus通信协议汇总
https://zhuanlan.zhihu.com/p/24134130 在工业控制.电力通讯.智能仪表等领域,通常情况下是采用串口通信的方式进行数据交换.最初采用的方式是RS232接口,由于工业现 ...
最新文章
- 幼儿园语言活动包括哪几类_幼儿园小班语言游戏教案你问我答对话活动教学【幼儿教师教案】...
- python计算csv文件内的数据_Python利用pandas计算多个CSV文件数据值的实例
- OXite解读(1)----- 概述
- 心情随笔20180620
- 如何使用Appverifier ?
- java中CyclicBarrier的使用
- 以下是ECMAScript 2016、2017和2018中所有新增功能的示例
- system函数的返回值和执行脚本的返回值
- Geodatabase概述(翻译)
- linux下文件的相关信息
- 用Maven创建第一个web项目Struts2项目
- K8s集群部署(四)------ Flannel网络部署
- 2018暑假第五周总结(8.6-8.12)
- 打开客户端出现 sorry this application cannot run under a virtual machine错误
- 如何将网页转为html文件,Chrome怎样保存网页为mhtml格式
- python爬虫登录微博账号_python模拟登录新浪微博 python新浪微博爬虫
- [LOJ#3124][CTS2019]氪金手游(概率 + 树形 DP + 容斥)
- 【转】模糊测试(fuzzing)是什么
- Unity3D教程:触发器实现简单的场景跳转
- a1502配置 macbook_a1502的macbook pro 苹果系统和… - Apple 社区