ModBus入门学习篇(含MB-RTU从机stm32工程)
Modbus是一种比较常用的工业通讯协议(协议也可以称为约定),它对传输介质的要求并不高,基本上可用实现通讯链接就可以(rs232、rs485、网线等电器通讯均可),这可能也是它比较常用的原因之一,重要的是这个协议它是免费的而且也是一个普遍认可的协议,所有学习一下还是有必要的。
学习modbus(以下简称MB)之前需要搞清楚一个概念“协议”,协议到底是什么呢?
说到协议一般来说大家应该都听说过TCP/IP这个网络协议,tcp/ip是一个大型网络协议相对来说是比较复杂的,但是并不代表协议就是一个复杂的东西。
协议其实就是一种约定。
例如:甲方跟乙方约定好,当乙方接到甲方的信息aa时,乙方给甲方寄出一个东西并回复一个信息bb,这就是协议的模型,协议就是双方都能按照规定来进行交流和执行。
下面以单片机通讯做一简单的协议
协议:1、规定数据格式: 帧头-0x3e 地址 指令 数据 帧尾-0x3f,这个是通讯双方的通讯数据包格式。
2、规定功能指令:A–控制电机正转 B–控制电机反转 C–电机停止转动。
当单片机接收到数据:0x3e 0x01 A 03 0x3f 根据以上规定的协议,接收到这帧数据之后单片机将会执行正转3圈的动作,这就是一个简单的协议。
对协议有一个初步的理解之后,再看看modbus协议。
一、Modbus简介
Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。
此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。
当在一Modbus网络上通信时,此协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。如果需要回应,控制器将生成反馈信息并用Modbus协议发出。在其它网络上,包含了Modbus协议的消息转换为在此网络上使用的帧或包结构。这种转换也扩展了根据具体的网络解决节地址、路由路径及错误检测的方法。
ModBus是一种主从问询式的通讯方式,由主机(或者client)发起询问从机接收到问询之后响应主机服务。
二、传输方式与数据帧格式
MB有两种传输模式:ASCII或RTU
ASCII消息帧格式
使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。
RTU消息帧格式
这两个传输模式的区别:
1、数据帧的格式不同,ASCII模式下有帧头、帧尾而RTU模式下是没有帧头和帧尾的,RTU模式下数据帧的第一个数据就是地址。
2、数据校验的方式不一样,ASCII模式使用的是LRC校验 ,RTU使用的是crc校验
3、数据的表示方式不同,ASCII使用一个ASCII字符表示一位十六进制数,RTU模式下是用4bit二进制表示一位16进制数,因此RTU模式的数据帧长度是要小于ASCII模式的,所有RTU模式的传输速率大于ASCII模式。
以上是关于modbus协议的一个简介,详细介绍网上很多这里就不多讲了,具体内容了解可以看Modbus协议中文版(GB),和Modbus协议详解这个博客。
STM32-modbus rtu 从机程序
modbu功能处理c文件
#include "modbus.h"
#include "usart.h"
#include "modbus_timer.h"
#include "modbus_crc.h"
MODBUS modbus;
u16 DataReg[30] ={
0}; //¶¨Òå30¸ö16λµÄ¼Ä´æÆ÷
u8 OX[20]; //¶¨ÒåÊä³öÏßȦ±äÁ¿
void modbus_err_response(u8 cmd, u8 err)
{
u16 crc;
u8 i=0;
modbus.tx_buf[i++] = modbus.local_addr;//±¾»úµØÖ·
modbus.tx_buf[i++] = cmd|0x80; //·µ»Ø¹¦ÄÜÂë=¹¦ÄÜÂë+0x80
modbus.tx_buf[i++] = err;
crc = Modbus_CRC16(modbus.tx_buf,i); //¼ÆËãCRC
modbus.tx_buf[i++] = crc/256;
modbus.tx_buf[i++] = crc%256;
Modbus_Uart_Send(modbus.tx_buf,i);
}
// Modbus³õʼ»¯º¯Êý
void Modbus_Init(void)
{
u8 i;
modbus.local_addr = 0x01; //É豸µØַΪ0x01
modbus.rx_flag = 0;
Modbus_Uart_Init(9600);
Modbus_TIM_Init();
for(i=0;i<30;i++) DataReg[i]=i;
for(i=0;i<20;i++) OX[i]=i;
}
//Modbus 1ºÅ¹¦ÄÜÂ뺯Êý£¬¶ÁÏßȦ״̬
void Modbus_Fun1(void)
{
u16 ox_addr,ox_cnt,crc;
u8 i,j;
ox_addr = modbus.rx_buf[2]*256+modbus.rx_buf[3];
ox_cnt = modbus.rx_buf[4]*256+modbus.rx_buf[5];
if(ox_cnt<1 || ox_cnt>0x7d0) //ÅжÏÏßȦ¸öÊýÊÇ·ñÔڹ涨ÇøÓòÄÚ£¬Èç¹û²»ÔÚ·¶Î§ÄÚÔò·µ»ØÒì³£3
{
modbus_err_response(modbus.rx_buf[1],ERROR_2);
return;
}
if( ox_addr>1600)
{
modbus_err_response(modbus.rx_buf[1],ERROR_3);
return;
}
i=0;
modbus.tx_buf[i++] = modbus.local_addr; //·¢Ëͱ¾»úµØÖ·
modbus.tx_buf[i++] = 0x01; //¹¦ÄÜÂë
if(ox_cnt%8 != 0) //Åж϶ÁÈ¡ÏßȦ¸öÊýÊÇ·ñÊÇ8µÄÕûÊý±¶£¬Èç¹û²»ÊÇ8µÄÕûÊý±¶Ôò·µ»Øox_cnt/8+1¸ö×Ö½ÚÊý¾Ý
{
modbus.tx_buf[i++] = (u8)(ox_cnt/8)+1; //·µ»Øox_cnt/8+1×Ö½ÚÊý¾Ý
if(ox_addr%8) //ÅжϵØÖ·ÊÇ·ñÊÇ8µÄÕûÊý±¶£¬Ò»¸öu8µÄ±äÁ¿´æ´¢8¸öÏßȦµÄ¿ª¹ØÖµ£¬Èç¹û²»ÊÇÕûÊý±¶ÔòÐèÒªÒÆλÀ´½øÐÐλѰַ
{
for(j=0;j<ox_cnt/8;j++)
{
modbus.tx_buf[i++]=(OX[ox_addr/8+j]>>(ox_addr%8))|(OX[ox_addr/8+j+1]<<(8-ox_addr%8)); //¶ÁÈ¡ÏßȦ¿ª¹ØÖµ
}
modbus.tx_buf[i++]=((OX[ox_addr/8+j]>>(ox_addr%8))|(OX[ox_addr/8+j+1]<<(8-ox_addr%8)))&(0xff>>(8-ox_cnt%8));
}
else //ÈôÊÇÕûÊý±¶Ôò°´×Ö½ÚÑ°Ö·¼È¿É£¬´ÓOX[20]ÖжÁÈ¡Õû¸ö±äÁ¿·µ»Ø¼´¿É
{
for(j=0;j<(ox_cnt/8);j++)
{
modbus.tx_buf[i++]=OX[ox_addr/8+j];
}
modbus.tx_buf[i++] = OX[ox_addr/8+j] & (0xff>>(8-ox_cnt%8));
}
}
else //¶ÁÈ¡¸öÊýÕýºÃÊÇ8µÄÕûÊý±¶Ôò·µ»Øox_cnt/8¸ö×Ö½ÚµÄÊý¾Ý
{
modbus.tx_buf[i++] = ox_cnt/8;
if(ox_addr%8 != 0)
{
for(j=0;j<ox_cnt/8;j++)
{
modbus.tx_buf[i++]=(OX[ox_addr/8+j]>>(ox_addr%8))|(OX[ox_addr/8+j+1]<<(8-ox_addr%8));
}
}
else
{
for(j=0;j<(ox_cnt/8);j