libmodbus 移植 STM32(基础篇)

目录

一、前言

在前几篇笔记中,我们完整解析了 libmodbus 的主从通信、报文收发与从机回应的核心源码,掌握了其上层 API 的封装逻辑与底层协议实现。但 libmodbus 原生仅适配 Windows、Linux 等操作系统,若要在 STM32 裸机或 FreeRTOS 环境下使用该库实现工业级 Modbus 通信,就需要完成源码的移植改造。本次笔记聚焦 libmodbus 移植到 STM32 的基础工作,讲解移植的核心思路、源码改造的准备步骤,以及待修改的核心硬件操作函数,为后续适配 STM32 串口驱动、完成完整移植打下基础。

二、libmodbus 移植核心思路

libmodbus 的移植核心原则是保留上层应用调用的各类 Modbus 函数(如 modbus_write_bits、modbus_reply 等),仅替换底层与硬件强相关的代码 ------ 具体来说,就是将_modbus_rtu_backend结构体中绑定的串口打开、数据收发、超时等待等硬件操作函数,从适配 Windows/Linux 的系统调用,替换为 STM32 的 HAL/LL 库串口操作函数,确保上层 API 的使用方式完全不变,降低应用层开发的适配成本。

补充:STM32 场景下无需保留跨平台兼容逻辑(如 Windows 的 strcpy_s、Linux 的 termios 串口配置),可直接删除无关代码,简化源码结构,适配嵌入式裸机 / FreeRTOS 的轻量需求。

三、源码改造准备工作

本次移植基于libmodbus-3.1.10.zip版本进行修改,核心准备步骤如下:

  1. 复制源码中的modbus-rtu.c文件,重命名为modbus-st-rtu.c(专属 STM32 的 RTU 模式实现文件);

  2. modbus-st-rtu.c添加到 Source Insight 4.0 中,方便源码编辑与阅读;

  3. 删除无关代码:

    • 适配 Win32/Linux 系统的条件编译分支代码;
    • _get_termios_speed函数(STM32 串口速率可通过库函数固定配置,无需动态获取 / 解析)。

删除无关代码后,_modbus_rtu_connect函数简化为如下形式:

c 复制代码
static int _modbus_rtu_connect(modbus_t *ctx)
{
    ctx->s = open(ctx_rtu->device, flags);
    return 0;
}

此外,以下与串口模式、流量控制相关的函数也可直接删除,因 STM32 嵌入式场景无需复杂的串口模式配置:

  • modbus_rtu_set_serial_mode:设置串口模式(STM32 串口模式初始化时固定配置);
  • modbus_rtu_get_serial_mode:获取串口模式(无需动态查询);
  • modbus_rtu_get_rts/modbus_rtu_set_rts:RTS 流量控制(工业现场极简场景下可省略);
  • modbus_rtu_set_custom_rts/modbus_rtu_get_rts_delay/modbus_rtu_set_rts_delay:自定义 RTS 控制与延时(无需适配)。

已完成的基础修改

针对 STM32 串口初始化后默认视为 "连接状态" 的特性,修改_modbus_rtu_is_connected函数,简化连接状态判断逻辑:

c 复制代码
static unsigned int _modbus_rtu_is_connected(modbus_t *ctx)
{
    return 1; // STM32串口初始化完成后,默认视为已连接
}

四、待修改的核心硬件操作函数

以下函数是移植的核心改造点,原代码依赖 Windows/Linux 系统调用,需替换为 STM32 HAL/LL 库的串口操作函数,标记的待修改代码如下:

1. 串口发送函数(替换 write 为 STM32 串口发送)

c 复制代码
static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
        return write(ctx->s, req, req_length); // 需替换为HAL_UART_Transmit等函数
}

2. 串口接收函数(替换 read 为 STM32 串口接收)

c 复制代码
static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
{
    return read(ctx->s, rsp, rsp_length); // 需替换为HAL_UART_Receive等函数
}

3. 串口连接函数(替换 open 为 STM32 串口初始化)

c 复制代码
static int _modbus_rtu_connect(modbus_t *ctx)
{
    ctx->s = open(ctx_rtu->device, flags); // 需替换为STM32串口初始化函数
    return 0;
}

4. 串口关闭函数(补充 STM32 串口反初始化逻辑)

c 复制代码
static void _modbus_rtu_close(modbus_t *ctx)
{
    // 需添加STM32串口反初始化/禁用代码
}

5. 串口缓冲区刷新函数(替换 tcflush 为 STM32 串口清空)

c 复制代码
static int _modbus_rtu_flush(modbus_t *ctx)
{
    return tcflush(ctx->s, TCIOFLUSH); // 需替换为HAL_UART_Flush等函数
}

6. 超时等待函数(替换 select 为 STM32 超时判断)

c 复制代码
static int
_modbus_rtu_select(modbus_t *ctx, fd_set *rset, struct timeval *tv, int length_to_read)
{
    int s_rc;
    while ((s_rc = select(ctx->s + 1, rset, NULL, NULL, tv)) == -1) {
        if (errno == EINTR) {
            if (ctx->debug) {
                fprintf(stderr, "A non blocked signal was caught\n");
            }
            /* Necessary after an error */
            FD_ZERO(rset);
            FD_SET(ctx->s, rset);
        } else {
            return -1;
        }
    }

    if (s_rc == 0) {
        /* Timeout */
        errno = ETIMEDOUT;
        return -1;
    }
	
    return s_rc; // 需替换为STM32基于定时器的超时等待逻辑
}

补充:select是 Linux/Windows 的 IO 多路复用函数,STM32 无此系统调用,需基于定时器(如 SysTick、通用定时器)实现串口接收的超时等待逻辑,适配嵌入式场景的超时控制需求。

五、定制化 RTU 上下文创建函数

为适配 STM32 串口参数配置需求,定制化修改modbus_new_rtu函数,命名为modbus_new_st_rtu,核心代码如下:

c 复制代码
// 定制化STM32 RTU上下文创建函数,添加串口参数配置
modbus_t *
modbus_new_st_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit)
{
    modbus_t *ctx;
    modbus_rtu_t *ctx_rtu;

    /* Check device argument */
    if (device == NULL || *device == 0) {
        fprintf(stderr, "The device string is empty\n");
        errno = EINVAL;
        return NULL;
    }

    ctx = (modbus_t *) malloc(sizeof(modbus_t));
    if (ctx == NULL) {
        return NULL;
    }

    _modbus_init_common(ctx);
    ctx->backend = &_modbus_rtu_backend;
    ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t));
    if (ctx->backend_data == NULL) {
        modbus_free(ctx);
        errno = ENOMEM;
        return NULL;
    }
    ctx_rtu = (modbus_rtu_t *) ctx->backend_data;

    /* Device name and \0 */
    ctx_rtu->device = (char *) malloc((strlen(device) + 1) * sizeof(char));
    if (ctx_rtu->device == NULL) {
        modbus_free(ctx);
        errno = ENOMEM;
        return NULL;
    }

    strcpy(ctx_rtu->device, device);

    // 显式配置STM32串口参数
    ctx_rtu->baud = baud;
    if (parity == 'N' || parity == 'E' || parity == 'O') {
        ctx_rtu->parity = parity;
    } else {
        modbus_free(ctx);
        errno = EINVAL;
        return NULL;
    }
    ctx_rtu->data_bit = data_bit;
    ctx_rtu->stop_bit = stop_bit;

    ctx_rtu->confirmation_to_ignore = FALSE;

    return ctx;
}

补充:该函数删除了原modbus_new_rtu中 Win32/Linux 的字符串拷贝分支(仅保留通用strcpy),新增波特率、奇偶校验、数据位、停止位的显式配置,后续可将这些参数与 STM32 串口初始化函数(如HAL_UART_Init)绑定,完成硬件参数的落地配置。

六、总结

  1. libmodbus 移植 STM32 的核心是保留上层 API,替换底层硬件操作函数,删除跨平台无关代码;
  2. 基础改造步骤:复制专属源码文件→删除系统适配代码→简化连接状态判断→定制上下文创建函数;
  3. 核心待修改函数为串口收发、连接、关闭、刷新、超时等待,需替换为 STM32 HAL/LL 库函数。

七、结尾

本次完成了 libmodbus 移植到 STM32 的基础准备工作,核心是梳理出需要改造的硬件操作函数,定制化适配 STM32 的串口参数配置逻辑。后续将进一步讲解如何将这些待修改函数替换为 STM32 HAL 库代码,并适配 FreeRTOS 的任务调度机制,实现真正可运行的嵌入式 Modbus 通信程序。感谢各位的阅读,持续关注本系列笔记,一起完成 libmodbus 从源码解析到 STM32 落地的全流程实践!

相关推荐
AomanHao16 分钟前
【阅读笔记】沙尘图像线性颜色校正A fusion-based enhancing approach for single sandstorm image
图像处理·笔记·isp·图像增强·沙尘图像·色偏·颜色校正
宇木灵2 小时前
C语言基础-十、文件操作
c语言·开发语言·学习
枷锁—sha2 小时前
【pwn系列】Pwndbg 汇编调试实操教程
网络·汇编·笔记·安全·网络安全
山岚的运维笔记3 小时前
SQL Server笔记 -- 第73章:排序/对行进行排序
数据库·笔记·后端·sql·microsoft·sqlserver
国科安芯4 小时前
基于RISC-V架构的抗辐照MCU在空间EDFA控制单元中的可靠性分析
单片机·嵌入式硬件·性能优化·架构·risc-v·安全性测试
一路往蓝-Anbo5 小时前
第 7 章:内存地图 (Memory Map) 深度设计——DDR 与 SRAM
linux·stm32·单片机·嵌入式硬件·网络协议
QQ24391975 小时前
语言在线考试与学习交流网页平台信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·spring boot·sql·学习·java-ee
一路往蓝-Anbo5 小时前
第 8 章:M33 领航——引导 A35 加载 U-Boot 与 Linux 内核
linux·运维·服务器·stm32·单片机·嵌入式硬件·网络协议
眼镜哥(with glasses)6 小时前
0215笔记-语言模型,提问范式与 Token
人工智能·笔记·语言模型
FakeOccupational6 小时前
【电路笔记 通信】香农公式(Shannon-Hartley Theorem/香农-哈特利定理)证明(暂记)
笔记