单片机作为从机如何移植FreeModbus

将 FreeModbus 移植到从机设备上需要按照以下步骤进行,下面以 STM32 为例详细说明:

一、准备工作

  1. 获取 FreeModbus 源码:从官方 GitHub 下载最新版本(https://github.com/ARMmbed/freemodbus)。
  2. 开发环境:安装 STM32CubeMX、Keil MDK 或 GCC 工具链。
  3. 硬件资源:UART 接口(用于 RTU 模式)或以太网控制器(用于 TCP 模式)。定时器(用于超时检测)。

二、文件结构分析

FreeModbus 源码主要包含以下核心文件:

plaintext

├── demo/               # 示例代码
├── include/            # 头文件
│   ├── mb.h            # Modbus主头文件
│   ├── mbconfig.h      # 配置文件(需用户自定义)
│   ├── mbframe.h       # 帧结构定义
│   └── ...
├── src/                # 源文件
│   ├── mbcrc.c         # CRC校验
│   ├── mbfunccoils.c   # 线圈操作功能码处理
│   ├── mbfuncdiag.c    # 诊断功能码处理
│   ├── mbfuncinput.c   # 输入寄存器功能码处理
│   ├── mbfuncholding.c # 保持寄存器功能码处理
│   ├── mbinit.c        # 初始化函数
│   ├── mbrtu.c         # RTU模式实现
│   ├── mbtcp.c         # TCP模式实现
│   └── ...
└── port/               # 移植层(需用户实现)
    ├── port.h          # 平台相关定义
    ├── portevent.c     # 事件处理
    ├── porttimer.c     # 定时器实现
    └── portserial.c    # 串口通信实现

三、创建自定义配置文件

在工程中创建mbconfig.h文件,根据需求配置 Modbus 参数:

c

运行

/* mbconfig.h */
#ifndef __MB_CONFIG_H
#define __MB_CONFIG_H

/* ----------------------- 配置选项 ----------------------- */
#define MB_RTU_ENABLED          1       // 启用RTU模式
#define MB_TCP_ENABLED          0       // 禁用TCP模式(根据需求修改)
#define MB_ASCII_ENABLED        0       // 禁用ASCII模式

/* ----------------------- 从机地址 ----------------------- */
#define MB_SLAVE_ADDRESS_MIN    1
#define MB_SLAVE_ADDRESS_MAX    247

/* ----------------------- 寄存器配置 ----------------------- */
#define MB_FUNC_HOLDING_ENABLED     1   // 启用保持寄存器
#define MB_FUNC_INPUT_ENABLED       1   // 启用输入寄存器
#define MB_FUNC_COIL_ENABLED        1   // 启用线圈
#define MB_FUNC_DISCRETE_INPUT_ENABLED 1 // 启用离散输入

/* ----------------------- 缓冲区大小 ----------------------- */
#define MB_SER_PDU_SIZE_MAX     256     // 最大PDU长度
#define MB_TCP_PDU_SIZE_MAX     253     // TCP模式最大PDU长度

#endif

四、实现移植层接口

port/目录下实现与硬件相关的接口:

1. portserial.c - 串口通信实现

c

运行

/* portserial.c */
#include "port.h"
#include "stm32f1xx_hal.h"

extern UART_HandleTypeDef huart1; // 根据实际使用的UART修改

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    // 配置UART参数
    // 示例:使用HAL库初始化UART
    return TRUE;
}

void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    // 启用/禁用接收和发送中断
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    // 发送单个字节
    HAL_UART_Transmit(&huart1, (uint8_t*)&ucByte, 1, 100);
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    // 接收单个字节
    HAL_UART_Receive(&huart1, (uint8_t*)pucByte, 1, 100);
    return TRUE;
}

/* 串口接收中断回调函数 */
void vMBPortSerialIRQHandler( void )
{
    // 处理串口中断
    // 调用eMBRTUReceiveFSM()或eMBTCPProcess()
}

2. porttimer.c - 定时器实现

c

运行

/* porttimer.c */
#include "port.h"
#include "stm32f1xx_hal.h"

extern TIM_HandleTypeDef htim2; // 根据实际使用的定时器修改

BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
    // 配置定时器,设置超时时间
    // 示例:使用HAL库初始化TIM2
    return TRUE;
}

void
vMBPortTimersEnable(  )
{
    // 启用定时器
    HAL_TIM_Base_Start_IT(&htim2);
}

void
vMBPortTimersDisable(  )
{
    // 禁用定时器
    HAL_TIM_Base_Stop_IT(&htim2);
}

/* 定时器中断回调函数 */
void vMBPortTimersIRQHandler( void )
{
    // 处理定时器中断
    // 调用eMBRTUTimerT35Expired()
}

3. portevent.c - 事件处理

c

运行

/* portevent.c */
#include "port.h"

static eMBEventType eQueuedEvent;
static BOOL         xEventInQueue;

BOOL
xMBPortEventInit( void )
{
    xEventInQueue = FALSE;
    return TRUE;
}

BOOL
xMBPortEventPost( eMBEventType eEvent )
{
    // 事件入队
    eQueuedEvent = eEvent;
    xEventInQueue = TRUE;
    return TRUE;
}

BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    // 事件出队
    BOOL xEventHappened = xEventInQueue;
    if( xEventHappened )
    {
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
    }
    return xEventHappened;
}

五、实现寄存器操作回调函数

根据应用需求,实现寄存器读写回调函数:

c

运行

/* user_mb_app.c */
#include "mb.h"
#include "mbport.h"

/* 定义寄存器存储空间 */
static USHORT usRegHoldingBuf[100]; // 保持寄存器
static USHORT usRegInputBuf[100];   // 输入寄存器
static UCHAR  ucRegCoilsBuf[100];   // 线圈
static UCHAR  ucRegDiscreteBuf[100]; // 离散输入

/* 保持寄存器读写回调 */
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
    // 实现保持寄存器的读写逻辑
    // 检查地址范围,读写usRegHoldingBuf数组
    return MB_ENOERR;
}

/* 输入寄存器读回调 */
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    // 实现输入寄存器的读逻辑
    // 通常从硬件读取数据存入pucRegBuffer
    return MB_ENOERR;
}

/* 线圈读写回调 */
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
    // 实现线圈的读写逻辑
    // 读写ucRegCoilsBuf数组
    return MB_ENOERR;
}

/* 离散输入读回调 */
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    // 实现离散输入的读逻辑
    return MB_ENOERR;
}

六、主程序初始化与调用

在主程序中初始化 Modbus 从机并循环处理:

c

运行

/* main.c */
#include "main.h"
#include "stm32f1xx_hal.h"
#include "mb.h"
#include "mbport.h"

/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM2_Init(void);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init(); // 初始化UART
    MX_TIM2_Init();        // 初始化定时器
    
    /* 初始化Modbus RTU从机,地址1,波特率9600,无校验 */
    eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );
    
    /* 注册回调函数 */
    eMBSetRegHoldingCB( eMBRegHoldingCB );
    eMBSetRegInputCB( eMBRegInputCB );
    eMBSetRegCoilsCB( eMBRegCoilsCB );
    eMBSetRegDiscreteCB( eMBRegDiscreteCB );
    
    /* 启用Modbus */
    eMBEnable();
    
    while (1)
    {
        /* 轮询处理Modbus请求 */
        (void)eMBPoll();
        
        /* 其他应用逻辑 */
        // ...
    }
}

七、常见问题与调试技巧

  1. 通信失败:检查硬件连接(RX/TX 是否接反,是否共地)。确认波特率、数据位、校验位是否一致。
  2. 寄存器访问异常:检查寄存器地址范围是否越界。调试时可在回调函数中添加日志输出。
  3. 性能优化:合理设置定时器超时值(T35 时间)。使用中断方式处理串口收发以提高响应速度。

八、移植到其他平台的注意事项

  1. 不同 MCU 的差异:替换相应的 HAL 库函数(如 STM32、ESP32 的串口和定时器 API 不同)。
  2. 内存管理:注意堆栈大小,避免溢出。
  3. 中断优先级:确保 Modbus 相关中断优先级足够高,避免被其他中断干扰。

通过以上步骤,即可完成 FreeModbus 在从机设备上的移植。实际应用中,还需根据具体需求调整配置参数和寄存器操作逻辑。

更多内容全在下方专栏

全网最受欢迎的嵌入式笔试专栏

笔试专栏包含全部最新的笔试必考考点,非常适合在找工作面经薄弱的同学

3000+订阅还会涨价,提前订阅提前享受,持续更新中。

专栏链接:https://www.nowcoder.com/creation/manager/columnDetail/mPZ4kk

#嵌入式秋招#
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务