欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > Modbus RTU协议详解:基于 STM32 与脉冲电源的通信项目实例

Modbus RTU协议详解:基于 STM32 与脉冲电源的通信项目实例

2025/3/14 17:29:38 来源:https://blog.csdn.net/qq_45315034/article/details/145993146  浏览:    关键词:Modbus RTU协议详解:基于 STM32 与脉冲电源的通信项目实例

基于 STM32 与脉冲电源的 Modbus RTU 通信项目详解

本文内容是工作中实际遇到的一个项目

一、引言

在工业控制和自动化领域,设备之间的可靠通信至关重要。Modbus RTU 协议作为一种广泛应用的串行通信协议,因其简单、高效且开放的特性,成为连接各种工业设备的理想选择。本文将围绕使用 STM32 单片机与脉冲电源通过 Modbus RTU 协议进行通信的项目展开,详细介绍 Modbus RTU 协议的原理、通信设置、消息帧格式、功能代码以及寄存器地址表等内容,并结合实际项目进行说明。

二、Modbus RTU 协议概述

2.1 基本概念

Modbus 是一种主 - 从式协议,在本项目中,上位机作为主站发起通信请求,整流机(脉冲电源)作为从站进行应答。采用 RTU(十六进制数)传输模式,数据以二进制代码形式传输,通过 CRC16 循环冗余校验确保数据传输的准确性。

2.2 通信口设置

2.2.1 通讯方式

采用异步串行通讯接口 RS - 485,这种接口具有抗干扰能力强、传输距离远等优点,适合工业现场环境。

2.2.2 波特率

默认值为 19200bps,且可在 4800 - 115200 的范围内进行设置。波特率决定了数据传输的速率,需要根据实际通信距离和设备性能进行选择。

2.2.3 字节数据格式
  • 起始位:1 位,用于标识数据帧的开始。
  • 数据位:可设置为 7 位或 8 位,本项目默认采用 8 位数据位,能传输更多的数据信息。
  • 停止位:可设置为 1 位或 2 位,本项目默认采用 1 位停止位,用于标识数据帧的结束。
  • 校验位:可设置为偶校验、无校验或奇校验,本项目默认采用偶校验,用于检测数据传输过程中是否发生错误。

字节数据格式如下:

1	*	*	*	*	*	*	*	*	1	*
起始位               数据位(从低到高)                    校验位   停止位

三、消息帧格式

3.1 读寄存器帧

主站向从站发送读寄存器请求时,消息帧格式如下:

从站地址功能代码首寄存器地址寄存器数 NCRC16
1 字节1 字节2 字节2 字节2 字节

例如,要读取从站地址为 01H 的设备中,从地址 0000H 开始的 5 个寄存器的值,读寄存器帧如下:

01H    03H    0000H    0005H    CrcL, CrcH

3.2 读寄存器返回帧

从站接收到读寄存器请求并处理后,向主站返回的消息帧格式如下:

从站地址功能代码字节数寄存器数据CRC16
1 字节1 字节1 字节N * 2 字节2 字节

假设成功读取 5 个寄存器的值,读寄存器返回帧如下:

01H    03H    0AH    [寄存器数据]    CrcL, CrcH

其中,字节数为 0AH(即 10 字节,因为每个寄存器占 2 字节,5 个寄存器共 10 字节)。

3.3 写寄存器帧

主站向从站发送写寄存器请求时,消息帧格式如下:

从站地址功能代码首寄存器地址寄存器数 N字节数寄存器数据CRC16
1 字节1 字节2 字节2 字节1 字节N * 2 字节2 字节

例如,要向从站地址为 01H 的设备中,从地址 0005H 开始的 5 个寄存器写入数据,写寄存器帧如下:

01H    10H    0005H    0005H    0AH    [寄存器数据]    CrcL, CrcH

3.4 写寄存器返回帧

从站接收到写寄存器请求并处理成功后,向主站返回的消息帧格式如下:

从站地址功能代码首寄存器地址寄存器数 NCRC16
1 字节1 字节2 字节2 字节2 字节

写寄存器返回帧示例:

01H    10H    0005H    0005H    CrcL, CrcH

四、功能代码

4.1 功能代码列表

功能代码ModBus 名功能名广播数量
03HRead Holding Registers读 N 个寄存器值No5
10HWrite Multiple Registers写 N 个寄存器值No5

4.2 功能代码说明

  • 03H:用于主站向从站读取多个保持寄存器的值。主站指定起始寄存器地址和要读取的寄存器数量,从站将相应寄存器的值返回给主站。
  • 10H:用于主站向从站写入多个保持寄存器的值。主站指定起始寄存器地址、要写入的寄存器数量和具体的寄存器数据,从站将数据写入相应的寄存器。

五、寄存器地址表

5.1 寄存器地址表内容

编号参数符号参数名地址类型数据类型数值范围备注
1V电压显示值016 位无符号(World)0 - 1200 (10 进制)电压有一位小数点,读到 800 代表 80.0V
2I电流显示值116 位无符号(World)0 - 2000 (10 进制)电流有 2 个小数点,读 1500 代表 15.00A
3状态显示216 位无符号(World)0 - 4 (10 进制)0——正常 1——过热
2——过流 3——其它
4F频率显示316 位无符号(World)额定值范围内(10 进制)读到 6000 代表 6000HZ
5D占空比显示416 位无符号(World)0 - 100 (10 进制)读到 100 代表 100%
6FV电压设置值516 位无符号(World)0 - 1200 (10 进制)电压有一位小数点,写入 1000 代表 100.0V
7FI电流设置值616 位无符号(World)0 - 2000 (10 进制)电流有 2 个小数点,写入 1000 代 10.00A
8FF频率设置716 位无符号(World)写入 3000 代表 3000HZ
9FD占空比设置816 位无符号(World)0 - 100写入 50 代表 50%
10RUN启停命令916 位无符号(World)0 - 10 - 关机 1 - 开机
11备用1016 位无符号(World)
12VC/CC稳压稳流1116 位无符号(World)0 - 10 - 稳流 1 - 稳压
13脉冲个数设置1216 位无符号(World)1 - 60000写入 200 代表 200 个
14间隔时间1316 位无符号(World)1 - 99999写入 100 代表 100 秒
15循环次数1416 位无符号(World)1 - 99999写入 100 代表 100 次
16脉冲启动1516 位无符号(World)0 - 10 - 关闭脉冲 1 - 启动脉冲

5.2 寄存器地址表说明

  • 数据类型:所有寄存器的数据类型均为 16 位无符号整型(两字节)。
  • 小数点处理:通信传输中带小数点的数据全部用整数代替,例如 27.9 代替为 279。
  • 数据传输顺序:全部寄存器数据在传输过程中用十六进制数表示,先传高字节,再传低字节。例如,传送 279 时,先传 01H,再传 17H。

六、CRC16 校验

6.1 校验原理

CRC16 是一种循环冗余校验方法,用于检测数据在传输过程中是否发生错误。在本项目中,CRC16 校验从从站地址到数据区最后一个字节,计算多项式码为 A001(hex)。

6.2 校验计算示例

在 STM32 中实现 CRC16 校验计算可以通过以下代码示例:

#include <stdint.h>// CRC16 计算函数
uint16_t crc16(uint8_t *data, uint16_t length) {uint16_t crc = 0xFFFF;uint16_t i, j;for (i = 0; i < length; i++) {crc ^= (uint16_t)data[i];for (j = 0; j < 8; j++) {if (crc & 0x0001) {crc >>= 1;crc ^= 0xA001;} else {crc >>= 1;}}}return crc;
}

使用示例:

uint8_t message[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x05};
uint16_t crc = crc16(message, sizeof(message));
uint8_t crcL = (uint8_t)(crc & 0xFF);
uint8_t crcH = (uint8_t)(crc >> 8);

七、STM32 + KEIL5 + 固件库举例说明 ModbusRTU 协议

以下是使用STM32标准固件库实现Modbus RTU协议通信的详细步骤和代码示例。

1. 硬件连接

与使用HAL库时的硬件连接方式相同,将STM32的串口(如USART1)与RS - 485转换器连接,RS - 485转换器的A、B线连接到Modbus RTU从站设备的对应接口,同时确保所有设备共地。

2. 工程准备

在Keil等开发环境中创建一个基于STM32标准固件库的工程,将标准固件库文件添加到工程中。

3. 代码实现

3.1 串口初始化

使用标准固件库函数对串口进行初始化,设置通信参数。

#include "stm32f10x.h"// 串口初始化函数
void USART1_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;// 使能GPIOA和USART1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);// 配置USART1 Tx (PA9)为复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 配置USART1 Rx (PA10)为浮空输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);// USART1配置USART_InitStructure.USART_BaudRate = 19200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_Init(USART1, &USART_InitStructure);// 使能USART1接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);// 使能USART1USART_Cmd(USART1, ENABLE);// 配置NVICNVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}
3.2 CRC16校验函数

实现CRC16校验函数,用于计算和验证消息帧的CRC校验值。

#include <stdint.h>// CRC16计算函数
uint16_t crc16(uint8_t *data, uint16_t length) {uint16_t crc = 0xFFFF;uint16_t i, j;for (i = 0; i < length; i++) {crc ^= (uint16_t)data[i];for (j = 0; j < 8; j++) {if (crc & 0x0001) {crc >>= 1;crc ^= 0xA001;} else {crc >>= 1;}}}return crc;
}
3.3 发送读寄存器请求

编写函数来发送读寄存器请求消息帧,并计算CRC校验值。

// 发送读寄存器请求
void sendReadRequest(uint8_t slaveAddress, uint16_t startAddress, uint16_t registerCount) {uint8_t request[8];request[0] = slaveAddress;request[1] = 0x03;request[2] = (uint8_t)(startAddress >> 8);request[3] = (uint8_t)(startAddress & 0xFF);request[4] = (uint8_t)(registerCount >> 8);request[5] = (uint8_t)(registerCount & 0xFF);uint16_t crc = crc16(request, 6);request[6] = (uint8_t)(crc & 0xFF);request[7] = (uint8_t)(crc >> 8);for (int i = 0; i < 8; i++) {while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);USART_SendData(USART1, request[i]);}
}
3.4 接收并处理响应

在串口接收中断服务函数中接收从站的响应消息帧,并验证CRC校验值。

#define BUFFER_SIZE 256
uint8_t receiveBuffer[BUFFER_SIZE];
uint8_t receiveIndex = 0;// 串口1中断服务函数
void USART1_IRQHandler(void) {if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {receiveBuffer[receiveIndex++] = USART_ReceiveData(USART1);// 这里可以根据实际情况添加接收完成判断条件// 例如,根据消息帧长度判断USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}// 处理接收到的响应
void processResponse() {// 验证CRC校验uint16_t receivedCrc = (uint16_t)(receiveBuffer[receiveIndex - 2]) | ((uint16_t)(receiveBuffer[receiveIndex - 1]) << 8);uint16_t calculatedCrc = crc16(receiveBuffer, receiveIndex - 2);if (receivedCrc == calculatedCrc) {// 处理响应数据// 这里可以根据响应消息帧的格式解析数据// 例如,获取寄存器数据等} else {// CRC校验失败}receiveIndex = 0; // 清空接收缓冲区
}

4. 主函数调用示例

int main(void) {USART1_Init();// 发送读寄存器请求sendReadRequest(0x01, 0x0000, 0x0005);while (1) {// 可以在合适的时机调用processResponse函数处理接收到的响应if (receiveIndex > 0) {processResponse();}}
}

5. 关键部分解释

  • 串口初始化USART1_Init 函数使用标准固件库的GPIO和USART初始化函数对串口进行配置,设置波特率、数据位、停止位、校验位等参数,并使能接收中断。
  • CRC16校验crc16 函数根据Modbus RTU协议的规则计算CRC校验值,用于保证消息帧的完整性。
  • 消息帧发送sendReadRequest 函数构建读寄存器请求消息帧,计算CRC校验值,并通过串口发送出去。
  • 消息帧接收:在 USART1_IRQHandler 中断服务函数中接收从站的响应消息帧,将数据存储在 receiveBuffer 中。processResponse 函数用于处理接收到的响应,验证CRC校验值并解析数据。

通过以上步骤,可以使用STM32标准固件库实现基于Modbus RTU协议的通信。需要注意的是,实际应用中可能需要根据具体情况对代码进行调整和优化,例如添加超时处理、错误处理等功能。

八、总结

通过本项目,深入了解 Modbus RTU 协议的原理、通信设置、消息帧格式、功能代码和寄存器地址表等内容,并使用 STM32 单片机实现与脉冲电源的通信。Modbus RTU 协议以其简单、可靠的特点,为工业设备之间的通信提供了有效的解决方案。在实际应用中,需要根据具体需求进行通信参数的设置和错误处理,以确保通信的稳定性和准确性。同时,通过合理的硬件设计和软件编程,可以实现更复杂的工业控制和自动化应用。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词