当前位置: 首页 > news >正文

探索STM32 Modbus RTU 主从机源码及其实践

stm32modbus RTU包主从机源码,支持单个多个寄存器的写入和读取,有相应的上位机软件,代码注释详细可读性强

一、引言

在工业控制领域,Modbus RTU 协议是一种广泛应用的通信协议。今天咱们就来聊聊基于 STM32 的 Modbus RTU 包主从机源码,它不仅支持单个或多个寄存器的读写操作,还配备了相应的上位机软件,而且代码注释详细,可读性超强,对学习和实际项目开发都非常有帮助。

二、STM32 Modbus RTU 主从机源码剖析

(一)初始化部分

以 STM32F4 为例,首先要对串口进行初始化,因为 Modbus RTU 是基于串口通信的。

// 串口初始化函数 void USART_Init(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIO时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 使能串口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // PA9 (TX) 复用功能推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); // PA10 (RX) 浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); // 连接PA9到USART1_TX GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); // 连接PA10到USART1_RX GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // 串口基本参数配置 USART_InitStructure.USART_BaudRate = 9600; 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); // 使能串口 USART_Cmd(USART1, ENABLE); }

这段代码很清晰,先开启 GPIO 和串口的时钟,然后分别对串口的 TX 和 RX 引脚进行配置,TX 设为复用推挽输出,RX 设为浮空输入,并将引脚与 USART1 关联。最后配置串口的波特率、数据位、停止位、奇偶校验等参数并使能串口。

(二)Modbus RTU 协议处理部分

  1. CRC 校验计算

CRC 校验在 Modbus RTU 里很关键,用来保证数据传输的准确性。

// CRC16 计算函数 unsigned short CRC16(unsigned char *buf, unsigned short len) { unsigned short crc = 0xFFFF; unsigned char i; while (len--) { crc ^= *buf++; for (i = 0; i < 8; i++) { if (crc & 0x0001) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; }

这里通过对传入的数据缓冲区和长度进行循环计算,每次将当前字节与 CRC 寄存器异或,然后根据最低位是否为 1 进行相应移位和异或操作,最终得到 CRC16 校验值。

  1. 读取寄存器操作
// 读取寄存器函数,支持单个或多个寄存器读取 void Modbus_ReadRegisters(unsigned char slaveAddr, unsigned short startReg, unsigned short numRegs, unsigned short *regs) { unsigned char txBuf[256]; unsigned char rxBuf[256]; unsigned short crc; // 构建读取寄存器请求帧 txBuf[0] = slaveAddr; txBuf[1] = 0x03; txBuf[2] = (unsigned char)(startReg >> 8); txBuf[3] = (unsigned char)(startReg & 0xFF); txBuf[4] = (unsigned char)(numRegs >> 8); txBuf[5] = (unsigned char)(numRegs & 0xFF); crc = CRC16(txBuf, 6); txBuf[6] = (unsigned char)(crc & 0xFF); txBuf[7] = (unsigned char)(crc >> 8); // 发送请求帧 for (int i = 0; i < 8; i++) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, txBuf[i]); } // 等待接收响应帧 while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); for (int i = 0; i < (2 + 2 * numRegs + 2); i++) { while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); rxBuf[i] = USART_ReceiveData(USART1); } // 校验CRC unsigned short receivedCRC = (rxBuf[2 + 2 * numRegs] << 8) | rxBuf[2 + 2 * numRegs + 1]; unsigned short calculatedCRC = CRC16(rxBuf, 2 + 2 * numRegs); if (receivedCRC == calculatedCRC) { // 解析响应数据 for (int i = 0; i < numRegs; i++) { regs[i] = (rxBuf[3 + 2 * i] << 8) | rxBuf[4 + 2 * i]; } } }

这个函数首先构建读取寄存器的请求帧,包含从机地址、功能码(0x03 表示读取保持寄存器)、起始寄存器地址和寄存器数量,然后计算 CRC 并添加到帧尾。接着通过串口发送请求帧,等待接收响应帧,接收到后再次校验 CRC,若校验通过则解析出寄存器数据。

  1. 写入寄存器操作
// 写入单个寄存器函数 void Modbus_WriteSingleRegister(unsigned char slaveAddr, unsigned short regAddr, unsigned short regValue) { unsigned char txBuf[256]; unsigned short crc; // 构建写入单个寄存器请求帧 txBuf[0] = slaveAddr; txBuf[1] = 0x06; txBuf[2] = (unsigned char)(regAddr >> 8); txBuf[3] = (unsigned char)(regAddr & 0xFF); txBuf[4] = (unsigned char)(regValue >> 8); txBuf[5] = (unsigned char)(regValue & 0xFF); crc = CRC16(txBuf, 6); txBuf[6] = (unsigned char)(crc & 0xFF); txBuf[7] = (unsigned char)(crc >> 8); // 发送请求帧 for (int i = 0; i < 8; i++) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, txBuf[i]); } }

对于写入单个寄存器,同样先构建请求帧,功能码为 0x06,包含从机地址、寄存器地址和要写入的值,计算 CRC 后通过串口发送出去。写入多个寄存器操作类似,只是功能码和数据帧格式稍有不同。

三、上位机软件

有了主从机源码,配套的上位机软件也很重要。常见的上位机软件可以用 Qt 或者 LabVIEW 来开发。以 Qt 为例,通过串口通信模块可以方便地与 STM32 进行数据交互。

// Qt 串口通信简单示例代码 #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo> #include <QDebug> int main() { QSerialPort serial; serial.setPortName("COM1"); // 根据实际端口修改 serial.setBaudRate(QSerialPort::Baud9600); serial.setDataBits(QSerialPort::Data8); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); if (serial.open(QIODevice::ReadWrite)) { qDebug() << "串口打开成功"; QByteArray writeData = "Hello, STM32"; serial.write(writeData); QByteArray readData = serial.readAll(); qDebug() << "读取到的数据: " << readData; serial.close(); } else { qDebug() << "串口打开失败"; } return 0; }

这段 Qt 代码简单演示了如何打开串口、发送数据和读取数据。在实际的上位机软件中,会根据 Modbus RTU 协议构建更复杂的读写请求,并对返回的数据进行处理和显示,方便用户监控和控制从机设备。

四、总结

STM32 Modbus RTU 主从机源码结合配套的上位机软件,为工业控制等领域的通信应用提供了很好的基础。通过详细的代码注释和清晰的代码结构,无论是初学者学习 Modbus 协议,还是工程师进行项目开发,都能从中受益。希望大家在实际应用中能充分利用这些资源,开发出更出色的项目。

stm32modbus RTU包主从机源码,支持单个多个寄存器的写入和读取,有相应的上位机软件,代码注释详细可读性强

http://www.jsqmd.com/news/459318/

相关文章:

  • 探索雷塞HBS86H 86闭环电机驱动器方案宝藏
  • 数据库系统工程师-操作系统 I/O 管理:数据库性能优化的底层核心
  • 基于YOLOv8的人脸表情识别系统【附源码】
  • 探索Potrace算法:位图矢量化的奇妙之旅
  • 一个创业老兵关于四个终极问题的二十年纪实
  • HTML_段落与换行
  • 微网综合能源优化调度代码合集:涵盖多种智能算法与实战应用场景
  • 负荷预测:布谷鸟优化的LSTM模型及对比分析
  • LazyCut
  • 在工控项目里最头疼的就是IO状态监控页面制作,每个按钮指示灯都得手动关联变量。上周调试KTP700触摸屏时突然开窍——做个万能IO显示模板不香吗
  • MATLAB P文件转码工具:将P文件转换为M文件
  • 发电机定子回路故障Simulink单相电流纵联差动保护仿真模型及动作电流波形分析
  • 基于FPGA的FIR滤波器设计:从MATLAB参数设计到FPGA实现及验证
  • 鸿蒙中 系统语言和区域的获取与监听
  • 计算机毕业设计springboot单亲家庭帮扶管理系统 基于SpringBoot的单身父母家庭综合支持与服务系统 特殊结构家庭社会救助与资源对接数字化平台
  • Pscad仿真-三机九节点系统,储能替换一台同步机,对比是否加入调频策略 三机系统改成50hz
  • Adobe Photoshop
  • SpringBoot3快速集成SMS4J,10分钟搞定短信+OA双渠道消息发送
  • 02计算机组成原理-流水线冒险(上)
  • 06.Python 中数字:整数、浮点数完全指南
  • 新手避坑指南:惯性器件参数表里的‘零偏稳定性‘可能骗了你
  • 电力负荷聚类分析:从数据到典型场景
  • 基于PFC6.0的单轴拉伸实验:二维与三维探索及声发射振铃计数解析
  • 锂电池SOC估算:EKF估计SOC仿真与扩展卡尔曼滤波
  • 基于YOLOv8的钢材表面缺陷检测系统【附源码+可远程安装部署】
  • VC维与PAC学习:如何量化你的模型复杂度?
  • 用广义神经网络GRNN实现多特征输入单因变量输出的拟合预测
  • 【小龙虾】OpenClaw 3.8继续炸场!龙虾不睡觉,全球程序员连夜赶工
  • 基于MotorCAD的永磁电机退磁仿真及电流波形自定义探索
  • 车载以太网TC8测试实战:SOME/IP协议在SOA架构下的完整测试流程(含Vector工具链配置)