在 STM32 上使用 Modbus 协议进行通信需要结合硬件配置和软件实现。下面我将从硬件连接、软件配置到代码实现,为你提供一个完整的指南。
硬件连接
Modbus RTU 通常使用 RS-485 接口进行通信,因此需要一个 RS-485 转换器。典型的连接方式如下:
-
STM32 与 RS-485 转换器连接:
- STM32 的 UART TX 引脚 → RS-485 转换器的 DI(数据输入)
- STM32 的 UART RX 引脚 → RS-485 转换器的 RO(数据输出)
- STM32 的一个 GPIO 引脚 → RS-485 转换器的 RE/DE(接收 / 发送使能)
-
RS-485 网络连接:
- RS-485 转换器的 A 线 → 网络中的 A 线
- RS-485 转换器的 B 线 → 网络中的 B 线
- 两端设备需连接 120Ω 终端电阻
STM32CubeMX 配置
使用 STM32CubeMX 配置基本外设:
-
配置 UART:
- 选择一个 UART(如 USART1)
- 设置波特率(如 115200)、数据位(8)、停止位(1)、无校验
- 启用接收和发送
-
配置 GPIO:
- 选择一个 GPIO 引脚作为 RS-485 的发送使能控制(如 PA8)
- 设置为输出模式
-
配置 NVIC:
- 启用 UART 接收中断
-
生成代码:
- 选择 IDE(如 MDK-ARM、System Workbench 等)
- 生成初始化代码
软件实现
结合前面提供的 Modbus 库和 STM32 HAL 库,实现 Modbus RTU 通信:
#include "stm32f4xx_hal.h"
#include "modbus.h"
/* 定义硬件资源 */
extern UART_HandleTypeDef huart1;
#define RS485_DIR_PIN GPIO_PIN_8
#define RS485_DIR_PORT GPIOA
/* 定义Modbus上下文和缓冲区 */
ModbusContext modbus_ctx;
uint8_t modbus_rx_buffer[256];
/* 硬件接口函数:发送数据 */
static int modbus_hw_send(const uint8_t* data, uint32_t length, void* context) {
(void)context;
/* 切换到发送模式 */
HAL_GPIO_WritePin(RS485_DIR_PORT, RS485_DIR_PIN, GPIO_PIN_SET);
/* 发送数据 */
HAL_StatusTypeDef status = HAL_UART_Transmit(&huart1, (uint8_t*)data, length, 1000);
/* 等待发送完成 */
while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET);
/* 切换到接收模式 */
HAL_GPIO_WritePin(RS485_DIR_PORT, RS485_DIR_PIN, GPIO_PIN_RESET);
return (status == HAL_OK) ? (int)length : -1;
}
/* 硬件接口函数:接收数据 */
static int modbus_hw_receive(uint8_t* data, uint32_t max_length, uint32_t timeout_ms, void* context) {
(void)context;
/* 使用中断方式接收数据 */
HAL_StatusTypeDef status = HAL_UART_Receive(&huart1, data, max_length, timeout_ms);
return (status == HAL_OK) ? (int)max_length : -1;
}
/* UART接收中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart) {
if (huart == &huart1) {
/* 数据接收完成,可添加处理逻辑 */
}
}
/* 初始化Modbus */
void Modbus_Init(void) {
/* 初始化Modbus上下文 */
ModbusError error = Modbus_Init(&modbus_ctx, MB_RTU, 0x01,
modbus_hw_send, modbus_hw_receive,
NULL, modbus_rx_buffer, sizeof(modbus_rx_buffer));
if (error != MODBUS_OK) {
/* 处理初始化错误 */
}
/* 设置超时时间 */
Modbus_SetTimeout(&modbus_ctx, 1000);
/* 连接Modbus */
Modbus_Connect(&modbus_ctx);
}
/* 主函数中的Modbus操作示例 */
void Modbus_Task(void) {
uint16_t holding_regs[10];
ModbusError error;
/* 读取保持寄存器 */
error = Modbus_ReadHoldingRegisters(&modbus_ctx, 0x02, 0x0000, 5, holding_regs);
if (error == MODBUS_OK) {
/* 处理读取的数据 */
} else {
/* 处理错误 */
}
/* 写入单个寄存器 */
error = Modbus_WriteSingleRegister(&modbus_ctx, 0x02, 0x0001, 0x1234);
if (error != MODBUS_OK) {
/* 处理错误 */
}
/* 延时 */
HAL_Delay(100);
}
主函数实现
int main(void) {
/* MCU Configuration--------------------------------------------------------*/
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/* 初始化Modbus */
Modbus_Init();
/* 启动UART接收中断 */
HAL_UART_Receive_IT(&huart1, modbus_rx_buffer, 1);
/* 主循环 */
while (1) {
/* 处理Modbus任务 */
Modbus_Task();
/* 其他任务 */
HAL_Delay(10);
}
}
注意事项
-
RS-485 方向控制:
- 在发送数据前将 RS-485 设置为发送模式
- 发送完成后将 RS-485 设置为接收模式
- 确保有足够的延时,避免数据冲突
-
中断处理:
- 实现 UART 接收中断处理函数
- 在中断处理函数中处理接收到的数据
-
错误处理:
- 检查 Modbus 操作的返回值
- 实现错误恢复机制
-
性能优化:
- 使用 DMA 进行数据传输,减少 CPU 占用
- 优化接收缓冲区管理
-
电源和接地:
- 确保所有设备共地
- 考虑使用隔离型 RS-485 转换器
通过以上步骤,你可以在 STM32 上成功实现 Modbus RTU 通信。根据实际需求,你可以进一步扩展功能,如添加更多的 Modbus 功能码支持、实现 Modbus 服务器模式等。
4万+

被折叠的 条评论
为什么被折叠?



