Modbus是一种串行通信协议,在工业中应用是比较广泛的。关于Modbus的介绍网上资料很多,这里就不细说了。刚开始接触的时候看Modbus的介绍,光是协议的介绍有几百页,还有各种命令,各种链路层的应用,看了几天,越看越糊涂,越看越不会用。
最后在单片机上移植成功后才感觉Modbus协议没那么复杂,如果刚开始学的时候,没必要把Modbus协议中每个功能都去了解。就把它当做简单的串口协议,只使用最简单的几个命令就行了。熟悉之后再慢慢了解其他功能。
下面就从单片机串口通信角度去理解Modbus协议,及如何将协议移植到单片机上。
先看看Modbus的协议
从大的方面来讲,协议总共由4部分组成: 地址、功能、数据、校验。
地址1个字节,也就是设备的地址范围是 0 --- 255。
功能码也就是命令,也是一个字节,范围是0---255。
数据位在不同的情况下有不同的长度。
校验位一般用的是CRC校验。
下来看看功能码都有哪些
常用的功能码有表格上面的这些,可以理解为一个数字代表的一种命令。给单片机移植时用03、06、16这三个命令就够用了。
这里面读线圈、写单个线圈、写单个寄存器等等,到底什么是线圈?什么是寄存器?这些都是什么意思?
简单的理解线圈就是位操作。比如说单片机控制了8路的继电器输出,为了方便表示继电器的状态,就用8个位来表示8个继电器的状态,比如0表示继电器断开,1表示继电器吸合。这样0x00就表示8路继电器全部断开,0xFF表示8路继电器全部吸合。
寄存器是字节操作,比如传感器采集温度的时候用一个字节表示当前温度,比如当前温度28℃,就用0x1C表示。
如果理解不了寄存器和线圈的含义就不用管它,就把他当做一个命令来看,在单片机中使用时03、06、16这三个命令就能满足基本需求,下面就单独分析一下这三个命令的含义。
03是读多个保持寄存器值,读取的个数可以设置,比如有8组温度传感器采集数据,要读取温度值,可以一组一组去读,也可以一次性读多个值,读取的个数自己设置。
先看看03的命令格式
请求就是单片机主机发送数据,正常响应就是主机发送的命令格式正确时,从机回复的数据。当主机发送的数据从机不能正确识别时,从机要返回异常响应数据,告诉主机发送的命令有错误。
这里解释一下命令里面各个位的含义,这里是采集8组温度传感器的数值,假如一个从机有8路温度传感器,这个从机的地址就定义为0x01,这个地址根据实际项目可以自己定义。功能码为0x03,这里使用Modbus规定的功能码,意思是读多个寄存器。起始地址为两个字节,表示从第几个温度传感器开始读取数据,寄存器数量也为两个字节,表示要读取几个温度传感器的值。由于只有8路温度传感器,所以起始地址的范围就是 0x0000 ---- 0x0007。寄存器数量的范围为0x0001---0x0008,最少要读取一个寄存器的值,最多读8个寄存器的值。最后就是CRC校验, CRC具体的校验方式这里不用关心,使用的时候直接调用校验函数就行。
这里要注意请求数据的时候要发送起始地址和请求数量,而返回数据的时候就没有请求地址了,只有发送的寄存器字节数。
比如现在要读取第一个温度传感器的值,那么请求数据格式如下:
从站地址 功能码 起始地址高位 起始地址低位 寄存器数量高位 寄存器数量低位 CRC校验高位 CRC校验低位
0x01 0x03 0x00 0x00 0x00 0x01 xx xx
从0地址开始,读取1个寄存器的值,也就是读取第一个温度传感器的值。
正常响应返回数据格式如下
从站地址 功能码 字节数 寄存器数量高位 寄存器数量低位 CRC校验高位 CRC校验低位
0x01 0x03 0x02 0x00 0x1E XX XX
读取到了2个字节寄存器的值,寄存器值为 0x001E, 0x001E对应的十进制数为30,说明第一个温度传感器的温度值为30℃。
那么异常响应是什么情况下会用到?假如请求数据发送的是读取第9个温度传感器的值,从机接收到数据后发现没有第9个传感器,说明主机发送的地址值超过范围了,那么从机这时就要给主机发送异常响应。常用的异常响应码有下面几种
从异常响应码中可以看出来,地址值不在范围内的异常码为0x02,Modbus规定返回异常响应时,差错码的值为功能码的值加上0x80,当前功能码为0x03,所以返回的差错码数值为0x83,差错码数值为0x02。
请求数据:
从站地址 功能码 起始地址高位 起始地址低位 寄存器数量高位 寄存器数量低位 CRC校验高位 CRC校验低位
0x01 0x03 0x00 0x09 0x00 0x01 xx xx
异常响应:
从站地址 差错码 异常码 CRC校验
0x01 0x83 0x02 xx
再看一个读取多个寄存器值的示例:
下面在看0x06写单个保持寄存器,就是给一个指定的寄存器中写入数据。通信格式如下:
通信示例如下:
可以看到写单个保持寄存器的请求命令和正常响应命令是完全相同的,这个就更好理解了。这块要注意 差错码的值为功能码的值加上0x80,当前功能码为0x06,所以返回的差错码数值为0x86。
下来在看看16(0x10)写多个保持寄存器,写多个保存寄存器和读多个寄存器基本一样,只不过一个是读,一个是写。
这块要注意 差错码的值为功能码的值加上0x80,当前功能码为0x10,所以返回的差错码数值为0x90。
通信示例如下:
响应命令只返回写的寄存器数量,而不返回写的寄存器值,这个和写单个寄存器是不同的。
通过上面的分析对Modbus就会有个大概的了解了,它也没有想得那么复杂。
下面就看看用代码如何实现上面这3个命令的功能。
首先看串口发送和接收代码的实现
#include "uart.h"
#include "stdio.h"
#include "main.h"
u8 ReceiveBuf[MaxDataLen] = {0};
u8 RecIndexLen = 0;
void Uart1_IO_Init( void )
{
PD_DDR |= ( 1 << 5 ); //输出模式 TXD
PD_CR1 |= ( 1 << 5 ); //推挽输出
PD_DDR &= ~( 1 << 6 ); //输入模式 RXD
PD_CR1 &= ~( 1 << 6 ); //浮空输入
}
//波特率最大可以设置为38400
void Uart1_Init( unsigned int baudrate )
{
unsigned int baud;
baud = 16000000 / baudrate;
Uart1_IO_Init();
UART1_CR1 = 0;
UART1_CR2 = 0;
UART1_CR3 = 0;
UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );
UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );
UART1_CR2_bit.REN = 1; //接收使能
UART1_CR2_bit.TEN = 1; //发送使能
UART1_CR2_bit.RIEN = 1; //接收中断使能
}
//阻塞式发送函数
void SendChar( unsigned char dat )
{
while( ( UART1_SR & 0x80 ) == 0x00 ); //发送数据寄存器空
UART1_DR = dat;
}
//发送一组数据
void Uart1_Send( unsigned char* DataAdd, unsigned char len )
{
unsigned char i;
for( i = 0; i < len; i++ )
{
SendChar( DataAdd[i] );
}
//SendChar(0x0d); //发送回车换行,测试用
//SendChar(0x0a);
}
//接收中断函数 中断号18
#pragma vector = 20 // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void UART1_Handle( void )
{
u8 res = 0;
res = UART1_DR;
ReceiveBuf[RecIndexLen++] = res;
return;
}
串口代码和常规的用法是一样的,初始化IO口和波特率,然后用中断接收数据,ReceiveBuf数组用来存放接收的数据,RecIndexLen用来统计接收数据的长度。
一组数据接收完毕之后,调用数据处理函数,来处理接收到的数据。
//处理接收到的数据
// 接收: [地址][功能码][起始地址高][起始地址低][总寄存器数高][总寄存器数低][CRC低][CRC高]
void DisposeReceive( void )
{
u16 CRC16 = 0, CRC16Temp = 0;
if( ReceiveBuf[0] == SlaveID ) //地址等于本机地址 地址范围:1 - 32
{
CRC16 = App_Tab_Get_CRC16( ReceiveBuf, RecIndexLen - 2 ); //CRC校验 低字节在前 高字节在后 高字节为报文最后一个字节
CRC16Temp = ( ( u16 )( ReceiveBuf[RecIndexLen - 1] << 8 ) | ReceiveBuf[RecIndexLen - 2] );
if( CRC16 != CRC16Temp )
{
err = 4; //CRC校验错误
}
StartRegAddr = ( u16 )( ReceiveBuf[2] << 8 ) | ReceiveBuf[3];
if( StartRegAddr > 0x07 )
{
err = 2; //起始地址不在规定范围内 00 - 07 1 - 8号通道
}
if( err == 0 )
{
switch( ReceiveBuf[1] ) //功能码
{
case 3: //读多个寄存器
{
Modbus_03_Slave();
break;
}
case 6: //写单个寄存器
{
Modbus_06_Slave();
break;
}
case 16: //写多个寄存器
{
Modbus_16_Slave();
break;
}
default:
{
err = 1; //不支持该功能码
break;
}
}
}
if( err > 0 )
{
SendBuf[0] = ReceiveBuf[0];
SendBuf[1] = ReceiveBuf[1] | 0x80;
SendBuf[2] = err; //发送错误代码
CRC16Temp = App_Tab_Get_CRC16( SendBuf, 3 ); //计算CRC校验值
上一篇:STM8S003F3 使用定时器来计算方波周期的方法
下一篇:STM8学习笔记---ADC多通道采样
推荐阅读
史海拾趣
在追求经济效益的同时,台湾诚阳(BC)公司也积极履行企业社会责任。公司关注环保和可持续发展,致力于推广绿色电子产品和节能减排技术。同时,公司还积极参与社会公益事业,为当地社区的发展做出贡献。这种对社会责任的承担和关注,使得台湾诚阳在业界树立了良好的形象,赢得了社会各界的认可和尊重。
请注意,以上故事仅为虚构示例,并不代表台湾诚阳(BC)公司或任何真实公司的实际情况。如果您对该公司有进一步的了解需求,建议直接访问其官方网站或查阅相关新闻报道。
随着电子行业的快速发展,Flambeau公司不断加大研发投入,致力于技术创新和产品多样化。公司研发团队通过改进生产工艺、优化材料配方、引入先进的设计软件等措施,不断提升产品的性能和质量。同时,Flambeau还积极拓展产品线,从最初的塑料储存产品扩展到更广泛的电子包装解决方案,包括防静电包装、抗震包装等,以满足电子行业日益增长的多样化需求。
2012年,Displaytech进行了公司重组,SEACOMP成为公司各部门的主要实体。这次重组不仅优化了公司的组织架构,也进一步整合了公司的资源,提高了运营效率。同时,公司还在中国东莞购买了一家制造工厂,命名为MH MFG,加强了电子合同制造部门的力量。
这些故事只是Displaytech公司发展历程中的一部分,但它们充分展示了公司在电子行业中的实力、创新精神和国际化视野。通过不断的技术创新、产品升级和市场拓展,Displaytech已经成为电子行业中一家具有影响力的企业。
在电子行业的快速发展中,Asian Best Components Co Ltd始终坚持环保理念,致力于推动绿色生产。公司积极采用环保材料和工艺,减少生产过程中的废弃物排放和能源消耗。同时,公司还加大了对环保技术的研发投入,推出了一系列环保型电子元件,为行业的可持续发展做出了积极贡献。这种环保理念不仅提升了公司的社会形象,也为公司的长远发展奠定了坚实的基础。
Autotrol公司成立于1964年,最初是一家领先的可定制小功率齿轮马达生产商。在初创时期,公司面临着激烈的市场竞争和技术挑战。然而,Autotrol凭借其卓越的技术实力和创新能力,成功开发出一系列具有竞争力的齿轮马达产品,逐渐在市场中脱颖而出。公司不断完善产品线,推出了永久磁铁同步电动机、滞后电动机和直流齿轮马达等,这些产品以其高效、稳定的性能赢得了客户的信赖。
Autotrol公司成立于1964年,最初是一家领先的可定制小功率齿轮马达生产商。在初创时期,公司面临着激烈的市场竞争和技术挑战。然而,Autotrol凭借其卓越的技术实力和创新能力,成功开发出一系列具有竞争力的齿轮马达产品,逐渐在市场中脱颖而出。公司不断完善产品线,推出了永久磁铁同步电动机、滞后电动机和直流齿轮马达等,这些产品以其高效、稳定的性能赢得了客户的信赖。
用GD32F470开发板的LCD Demo程序驱动AT070TN94 屏,结果是花屏,测了下DE 的频率是63us,厂家说调成35us,在demo 程序中哪里设置呢?求解答,谢谢! … 查看全部问答∨ |
|
请问 ARM CONTEXT M0 免费提供的 除了核心 包不包括ADC FLASH 等部件 请问 ARM CONTEXT M0 免费提供的 除了核心 包不包括ADC FLASH 等部件 … 查看全部问答∨ |
低功耗电流采样 在日常生活中TWS(真无线蓝牙耳机)已经非常之常见,事实上也非常方便,实在是太香了,除了容易掉,我已经掉了两TWS~那只能拜拜了~家境贫寒的我已经不能再买TWS耳机了。 人睡觉的 ...… 查看全部问答∨ |
已经量产的板子,客户遭到投诉有滋滋的声音,拿到公司,查也没有查出来, 后面观察到板子有一个为了省成本,有一个驱动IC 没要,后面加上去了,就解决了, 现在是做的升级版,加了一套升压进去,工作电流也翻了一倍,之前是1A , 现在是2A ...… 查看全部问答∨ |
导语:同步整流技术采用通态电阻极低的电力MOSFET来取代整流二极管,能大大降低整流电路的损耗,提高DC/DC变换器的效率,满足低压、大电流整流器的需要。本文将从同步整流电路的原理图着手,介绍了电力MOSFET的反向电阻工作区及同步整流技术的基本 ...… 查看全部问答∨ |
一、 设计说明 DM21056 是我司新设计的一款非接触式阵列红外测温模组,可实时测量物体表面多个点的温 度并通过串口通讯把数据发送给用户,并可以做成热力图直观体现测量物体表面的温度分布情况。该模组应用的阵列红外温度传感器,具有测量距离远 ...… 查看全部问答∨ |
全志异核多构 AI智能视觉V853开发板测评 - 编译eye-mpp 中的sample_virvi # 编译eye-mpp middleware 中的sample_virvi例程 ## `make menuconfig`配置sample_virvi ## 使用adb push 命令将sample_virvi 和sample_virvi.conf 复制到板子目录下; ## 修改保存的YUV图像帧数 ## 保存的YUV图像数据存放位置为/mnt/extsd/te ...… 查看全部问答∨ |
【行空板 Python编程学习主控板】使用屏幕色卡控制WS2812B灯环的控制器 本帖最后由 HonestQiao 于 2022-11-17 23:02 编辑 行空板自带屏幕,而系统自带的Python,还预装了专用开发库unihiker和硬件控制库pinpong,可以让使用者非常方便的控制屏幕,以及快捷的和外部设备交互。 使用pinpong,在行空板上点亮WS2812B ...… 查看全部问答∨ |
设计资源 培训 开发板 精华推荐
- 《看一个TI老工程师如何驯服精密放大器》点评有礼!
- 【EEWORLD第八届社区明星人物】10月明星人物
- 【答题有奖】赛灵思工业与医疗专题有奖问答
- 畅游安富利人工智能云会展,挑战60天打卡学习养成记!冲击华为Mate40 Pro、Apple iPad Air等豪礼啦!
- 有奖直播|贝能国际推出基于英飞凌技术的毫米波雷达模组,完美解决PIR市场痛点
- 下载有礼!是德科技高速数字精选解决方案,专为您的数字设计而挑战!
- 【EEWORLD第九届社区明星人物】11月明星人物
- 【0元得开发板,还能赢T12焊台,报名倒计时】Follow me,与得捷一起解锁开发板超能力!
- 2024安路科技FPGA技术研讨会-杭州站 火热报名中
- 有奖直播预报名|与英飞凌一同探寻网络摄像机的黑科技