MODBUS三种通讯模式RTU,ASCII,TCP,功能码,RCR校验

本文详细介绍了Modbus通信的三种模式:RTU、ASCII和TCP。RTU模式使用CRC校验,ASCII模式使用LRC校验,TCP模式基于以太网和TCP/IP。RTU模式数据帧包括地址码、功能码、数据和校验码,ASCII模式增加了字符起始和结束标志,TCP模式则包含MBAP和PDU两部分。同时,文中列举了各种模式下的数据帧格式和功能码示例,以及错误应答的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MODBUS通信模式最主要有三种:

RTU模式,ASCII模式和TCP模式。Modbus TCP基于以太网和TCP/IP协议,Modbus RTU和Modbus ASCII则是使用异步串行传输(通常是RS-232/422/485)。

RTU模式:

地址码功能码数据校验码
一字节一字节n字节两字节(CRC)

从机都有相应的地址码,便于主机识别,从机地址为0到255,0为广播地址,248-255保留。总线上只能有一个主设备,但可以有一个或者多个(最多247个 ip地址1-247)从设备。

广播模式:主设备向所有的从设备发送请求指令,从设备收到指令后,各自处理,不要求返回应答;这种模式下,请求指令必须是Modbus标准功能中的写指令;比如 0x06,0x10 功能码 

其中数据已帧为单位进行数据传输,每帧最长为252字节,最短为0。如果一byte数据的传输时间为T,那么每两帧之间的间隔最小应该要大于3.5T,否则从机不能分辨这是两帧。第二,同一帧连续的两个数据之间的间隔时间不能超过1.5T,否则节点会认为这一帧数据不完整,这说明我们在modbus传输的时候要使能一个定时器的工作。

例子:

发送:09 03 00 04 00 03 XX

主站告诉从站09,我要读取的地址偏移为4、5、6的Holding Register的数值。其中"03"是读Holding Register的功能码,"00 04 00 03"是数据区,"00 04"是寄存器的地址,"00 03"说明要连续读三个寄存器的值。"XX"代表最后的校验位。

接收:09 03 06 02 2B 00 01 00 64 XX

从站回应该地址偏移为4的寄存器值为02 2B,地址偏移为5的寄存器值为00 01,地址偏移为6的寄存器值为00 64。其中"09 03"是复制了主站发来的地址和功能码,"06"代表接下来的数据共有6个字节。

ASCII模式:

起始地址码功能码数据校验回车换行
字符  ':'(冒号)两字节两字节0到2 * 252字节两字节(LRC校验)两字节(CR,LF)

帧的起始一字符 ' : '冒号开始,结束为回车换行,其对应的16进制可以到ASCII表中进行查询。字节间传输的间隔时间不能大于1s,大于1s认为这一帧数据丢失.同样我们可以计算出来ASCII帧的最大长度是513字节。

RTU使用CRC校验ASCII使用LRC校验。

TCP模式:

ModbusTCP的数据帧可分为两部分:MBAP+PDU。

主站为client端,主动建立连接;从站为server端,等待连接。

1.报文头MBAP

MBAP为报文头,长度为7字节,组成如下:

事务处理标识协议标识长度单元标识符
2字节2字节2字节1字节

事务处理标识 :可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文。
协议标识符 :00 00表示ModbusTCP协议。
长度 :表示接下来的数据长度,单位为字节。
单元标识符 :可以理解为设备地址。

2.帧结构PDU

PDU由功能码+数据组成。功能码为1字节,数据长度不定,由具体功能决定。

例子:

发送:01 c8 00 00 00 06 01 03 00 14 00 0a

序列号:01 c8 ,协议标识符:00 00 ,长度:00 06 ,单元标识符/服务器地址:01,功能码:03,寄存器地址:00 14 ,读取几位数据:00 0a。

接收:01 c8 00 00 00 17 01 03 14 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 03 00 00

 序列号:01 c8 ,协议标识符:00 00 ,长度:00 17 ,单元标识符/服务器地址:01 ,功能码:03,数据长度:14 ,数据:就是后面的在第6位和第9位有数据。

下一条数据序,列号就会加一,变为01 c9。

读:

MSB表示高字节;LSB表示低字节。

寄存器数据低地址数据放在前面,高地址数据放在后面

主节点发送帧格式:

序号

0

1

2

3

4

5

6

7

字段定义

ADDR

CMD

MSB

LSB

MSB

LSB

LSB

MSB

解释

从机地址

命令类型

寄存器起始地址

寄存器个数n

CRC校验

从节点正常应答帧格式:

序号

0

1

2

3

4

5

6

L+1

L+2

L+3

L+4

字段定义

ADDR

CMD

Length

MSB

LSB

MSB

LSB

MSB

LSB

LSB

MSB

解释

从机地址

命令类型

发送字节数L=n*2

第一个寄存器的值

第二个寄存器的值

最后一个寄存器的值

CRC校验

写单个寄存器:

主节点发送帧格式:

序号

0

1

2

3

4

5

6

7

字段定义

ADDR

CMD

MSB

LSB

MSB

LSB

LSB

MSB

解释

 从机地址

命令类型

寄存器地址

数据

CRC校验

从节点正常应答帧格式:

序号

0

1

2

3

4

5

6

7

字段定义

ADDR

CMD

MSB

LSB

MSB

LSB

LSB

MSB

解释

从机地址

命令类型

寄存器地址

数据

CRC校验

正确的应答格式:

地址码功能码数据码校验
从机自己的与发送的保持一致(范围:0x00-0x7f)根据功能需求校验码

错误应答:

功能码加0x80,原本功能码最高位不会为1,加0x80最高位为1,判断出错,数据位的异常码可以知道大概是什么错误。

地址码功能码数据码校验
从机自己的与发送的保持一致(范围:0x00-0x7f) + 0x80异常码校验码

异常码:

功能码:

0x01: 读线圈寄存器(可读可写线圈)
0x02: 读离散输入寄存器(可读不可写线圈)
0x03: 读保持寄存器(可读可写寄存器)
0x04: 读输入寄存器(可读不可写寄存器)
0x05: 写单个线圈寄存器(可写单个线圈不可读)
0x06: 写单个保持寄存器(可写单个寄存器不可读)
0x0f: 写多个线圈寄存器(可写多个线圈不可读)
0x10: 写多个保持寄存器(可写多个寄存器不可读)

01H-->读线圈寄存器

地址码功能码数据校验
发送010100 20 00 05FD C3
响应010101 1590 47

发送数据:读取地址:00 20,从0x20处开始读取,00 05 读取5个寄存器。

响应数据:01,只有5个寄存器,小于8,5/8=0,加1,所以为1,假设有9个,则为02,以此类推。15,二进制:00010101,在5个寄存器中有3个处于开的状态。

02H--->读离散输入寄存器

地址码功能码数据校验
发送010200 20 00 05B9 C3
响应010201 0220 49

发送数据:读取地址:00 20,从0x20处开始读取,00 05 读取5个寄存器。

响应数据:01,只有5个寄存器,小于8,5/8=0,加1,所以为1,假设有9个,则为02,以此类推。02,二进制:00000010,在5个寄存器中有1个处于开的状态,在此功能下只能读取从机状态。

03-->读保持寄存器

地址码功能码数据校验
发送010300 14 00 0345 CF
响应010306 00 14 00 1E 00 0071 70

发送数据:读取地址:00 14,从0x14处开始读取3个寄存器。

响应数据:06,后面有6位,00 14,发送的数据0x14,00 1E发送的数据0x1E,00 00发送的数据0X00。

04--> 读输入寄存器

地址码功能码数据校验
发送010400 14 00 03F0 0F
响应010406 00 14 00 1E 00 0030 96

发送数据:读取地址:00 14,从0x14处开始读取,00 03 读取3个寄存器。

响应数据:06,后面有6位,00 14,发送的数据0x14,00 1E发送的数据0x1E,00 00发送的数据0X00,在此功能下只能读取从机状态。

05-->写单个线圈寄存器

地址码功能码数据校验
发送010500 20 00 00CC 00
响应010500 20 00 00CC 00

发送数据:读取地址:00 20,从0x20处开始写入,00 00 写入值。

响应数据:跟发送的一样。

06-->写单个保持寄存器

地址码功能码数据校验
发送010600 01 00 03XX XX
响应010600 01 00 03XX XX

发送数据:读取地址:00 01,从0x01处开始写入,00 03 写入值。

响应数据:跟发送的一样。

0F-->写多个线圈寄存器

地址码功能码数据校验
发送010F00 14 00 03 01 00CC 00
响应010F00 14 00 03CC 00

发送数据:读取地址:00 14,从0x14处开始写入,00 03 写入3个值,   3小于8,所以为01,     00,写入的3个数据都为0。

响应数据:00 14,写入地址,00 03,3个数据。

10-->写多个保持寄存器

地址码功能码数据校验
发送011000 34 00 02 04 0C 02 12 45XX XX
响应011000 34 00 02XX XX

发送数据:读取地址:00 34,从0x34处开始写入,00 02 写入2个值,04后面有4位,0C 02和12 45都为数据。

响应数据:00 14,写入地址,00 02,2个数据。

CRC校验:

校验码是4位是因为多项式为5位,校验码少一位。

点击下方公众号,扫描二维码关注,更多科技AI等你来发现哦 

#include "stm32f10x.h" // Device header #include "Serial.h" #include "modbus.h" #include "stm32f10x_usart.h" #include "stm32f10x_tim.h" #include "stm32f10x_pwr.h" #include "stm32f10x_bkp.h" LED_Typedef LED_Struct[3]; uint16_t Reg[4] = {0x0001, 0x0000, 0x0200, 0x0400}; // 初始值 uint8_t Modbus_RCR[2]; MODBUS modbus; /*********************************************** * 函数名:Modbus_Init * 功能 :Modbus协议初始化 * 参数 :无 * 返回值:无 ***********************************************/ void Modbus_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); // 从备份寄存器读取设备地址(断电不丢失) if (BKP_ReadBackupRegister(BKP_DR1) != 0) Reg[REG_DEV_ADDR] = BKP_ReadBackupRegister(BKP_DR1); modbus.myID = Reg[REG_DEV_ADDR]; // 设置本机地址 modbus.timrun = 0; // 关闭定时器 } /*********************************************** * 函数名:Modbus_CRC16 * 功能 :计算Modbus CRC16校验码 * 参数 :data-数据指针,len-数据长度 * 返回值:CRC16校验值 ***********************************************/ uint16_t Modbus_CRC16(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } void Modbus_Func3(void) { uint16_t Regadd,Reglen; uint8_t i,j; //得到要读取寄存器的首地址 Regadd = modbus.rcbuf[2]*256+modbus.rcbuf[3];//读取的首地址 //得到要读取寄存器的数据长度 Reglen = modbus.rcbuf[4]*256+modbus.rcbuf[5];//读取的寄存器个数 //发送回应数据包 i = 0; modbus.sendbuf[i++] = modbus.myID; //ID号:发送本机设备地址 modbus.sendbuf[i++] = 0x03; //发送功能码 modbus.sendbuf[i++] = ((Reglen*2)%256); //返回字节个数 for(j=0;j<Reglen;j++) //返回数据 { //reg是提前定义好的16位数组(模仿寄存器) modbus.sendbuf[i++] = Reg[Regadd+j]/256;//高位数据 modbus.sendbuf[i++] = Reg[Regadd+j]%256;
最新发布
03-19
<think>好的,用户想了解在STM32F10x平台上实现Modbus协议以及CRC16校验函数的示例。首先,我需要整理Modbus协议的基本知识,特别是RTU模式下的帧结构,包括地址码、功能码、数据域CRC校验部分。接下来,STM32的具体实现方面,可能需要涉及串口配置、定时器用于超时管理,以及CRC模块的使用。 用户提到CRC16校验函数的正确性,所以需要解释STM32内置的CRC模块,以及如何配置它来计算Modbus RTU的CRC16。需要注意的是,STM32的CRC默认多项式可能与Modbus的不同,因此需要调整多项式为0x8005,初始值为0xFFFF,并且输入数据需要按字节反转最终结果取反。这部分需要详细说明,避免用户配置错误。 然后,给出示例代码结构,包括初始化USARTCRC外设,发送接收Modbus帧的函数。在接收处理中,需要检查CRC校验是否正确,这里需要强调CRC校验的正确步骤,比如计算接收数据的CRC并与帧中的CRC进行比较。 可能的问题点包括CRC配置参数是否正确,数据字节的处理顺序,以及超时管理的实现。用户可能在移植过程中遇到CRC校验不通过的情况,需要确保所有参数设置正确,特别是多项式输入反转的设置。 此外,用户可能对Modbus协议的具体实现细节感兴趣,比如如何处理不同的功能码,或者如何优化代码效率。因此,在相关问题中可以提出这些方面,帮助用户进一步深入理解。 最后,确保回答结构清晰,分步骤说明,代码示例正确,并且引用相关的资料,比如提到的FreeMODBUS移植中的指针指向具体处理函数的部分,增强回答的可信度。</think>### STM32F10x Modbus协议实现与CRC16校验详解 #### 一、Modbus RTU帧结构 Modbus RTU帧格式为: $$ \text{地址码}(1B) + \text{功能码}(1B) + \text{数据域}(nB) + \text{CRC16}(2B) $$ 传输采用3.5字符静默时间作为帧间隔[^1]。 #### 二、STM32硬件配置 1. **USART配置**(以115200bps为例) ```c USART_InitTypeDef USART_InitStruct; USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStruct); ``` 2. **CRC硬件模块配置** ```c RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE); // Modbus CRC16参数设置 CRC_ReverseInputDataConfig(CRC_ReverseInputData_8bits); // 字节反转 CRC_SetPolynomial(0x8005); // Modbus多项式 CRC_SetInitRegister(0xFFFF); // 初始值 ``` #### 三、CRC16校验实现 Modbus使用CRC-16-IBM多项式: $$ x^{16} + x^{15} + x^2 + 1 \quad (0x8005) $$ 计算步骤: 1. 初始化CRC寄存器为0xFFFF 2. 逐字节异或数据 3. 执行8次位移异或操作 硬件加速实现: ```c uint16_t Modbus_CRC16(uint8_t *data, uint16_t length) { CRC_ResetDR(); while(length--) { CRC_CalcCRC(*data++); } return (CRC_GetCRC() ^ 0xFFFF); // 结果取反 } ``` #### 四、Modbus帧处理示例 接收处理流程: ```c #define MAX_FRAME_LEN 256 uint8_t rx_buffer[MAX_FRAME_LEN]; void USART1_IRQHandler() { static uint16_t rx_index = 0; if(USART_GetITStatus(USART1, USART_IT_RXNE)) { rx_buffer[rx_index++] = USART_ReceiveData(USART1); // 超时定时器重置(3.5字符时间) TIM_SetCounter(TIM2, 0); TIM_Cmd(TIM2, ENABLE); } } void TIM2_IRQHandler() // 超时中断 { if(TIM_GetITStatus(TIM2, TIM_IT_Update)) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); uint16_t crc_recv = (rx_buffer[rx_index-1] << 8) | rx_buffer[rx_index-2]; uint16_t crc_calc = Modbus_CRC16(rx_buffer, rx_index-2); if(crc_calc == crc_recv) { Process_Modbus_Frame(rx_buffer); } rx_index = 0; } } ``` #### 五、关键验证点 1. 多项式必须配置为0x8005 2. 输入数据需按字节反转(LSB优先) 3. 最终结果需要取反输出 4. 帧间隔超时时间计算: $$ t_{timeout} = \frac{3.5 \times 11 \times 1000}{波特率} (ms) $$ #### 六、移植注意事项 FreeMODBUS实现中通过`mb.c`动态绑定协议处理函数,如: ```c // mb.c中的协议选择逻辑 #if MB_RTU_ENABLED xMBPortSerialInit(38400, 'N', 8, 1); xMBRTUInit(&xFrameHandlers); #endif ``` 这种架构允许灵活切换RTU/ASCII/TCP协议[^1]。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮若于心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值