AI:嵌入式码农晋升之路其三:freemobus从机移植

摘要:

本文长度有点长,宗旨是对比现在比较热门的AI应用对嵌入式开发的辅助作用,尚且处于摸索之中。从下文的对比中来看,各个应用各有优劣,豆包和DeepSeek对freemodbus的应用似乎推理更深一筹,能够直接给出数据更新的回调函数,但是豆包忽略了参数需要映射到结构体中去。同时三个应用都没有直接给出移植的具体方案,把重点都放在了应用上。所以,笔者再次提出了优化方向,优化移植细节。从结果上看豆包遗忘了定时器中断的处理,DeepSeek更是没有涉及到modbus收发处理的串口中断以及定时器中断处,而KIMI基本给处理这些处理函数。
总的来说,这些应用对我们开发有一定的辅助作用,但是并不是一蹴而就的,对我们这些熟悉开发过程的人来说,省略的时手动编码的时间,但是对于初学者会有许多漏洞,增加调试难度。就编码效率上来说,先生成一版代码在上面手动修改,比一行一行编码还是要效率高很多。
由于篇幅有限,也是由于AI辅助代码编写正在摸索中,就没有继续再记录提出优化要求继续优化代码。后面如果摸索出一套更有效的提示语策略再和大家分享。

提出问题

stm32f103移植freemodbus从机,保持寄存器地址0x1000,寄存器数量10,通信参数为结构及,分别为开关机状态、电压、电流、功率、设置电压、设置电流、设置功率、控制命令、温度、告警字。

AI回答问题

KIMI回答

根据您的需求,我将为您详细说明如何在STM32F103上移植FreeModbus从机,并设置寄存器地址为0x1000,寄存器数量为10。这些寄存器将分别用于存储开关机状态、电压、电流、功率、设置电压、设置电流、设置功率、控制命令、温度和告警字。

1.准备工作

首先,确保您已经安装了STM32开发环境(如STM32CubeIDE)和FreeModbus协议栈。可以从FreeModbus官网下载协议栈源码。

2.工程配置

• 创建STM32工程:在STM32CubeIDE中创建一个新的工程,选择您的STM32F103芯片。

• 添加FreeModbus源码:将FreeModbus协议栈的源码文件(如mb.hmbproto.cmbfunc.c等)添加到您的工程中。

• 配置串口:配置USART用于Modbus RTU通信。例如,使用USART1,设置波特率为9600,8位数据位,1位停止位,无校验位。

3.配置寄存器

根据您的需求,我们需要定义10个寄存器,起始地址为0x1000。在FreeModbus中,寄存器地址从0开始,因此实际的数组索引将从0开始。

定义寄存器结构

typedef struct {
    uint16_t status;       // 开关机状态
    uint16_t voltage;      // 电压
    uint16_t current;      // 电流
    uint16_t power;        // 功率
    uint16_t set_voltage;  // 设置电压
    uint16_t set_current;  // 设置电流
    uint16_t set_power;    // 设置功率
    uint16_t control_cmd;  // 控制命令
    uint16_t temperature;  // 温度
    uint16_t alarm;        // 告警字
} ModbusRegisters;

初始化寄存器
在FreeModbus中,寄存器通常存储在一个数组中。您需要在代码中初始化这个数组,并将其与FreeModbus协议栈关联。

#define MB_REG_START_ADDR 0x1000
#define MB_REG_NUM 10

ModbusRegisters mb_regs;
uint16_t mb_reg_array[MB_REG_NUM];

void MB_InitRegisters() {
    mb_reg_array[0] = &mb_regs.status;
    mb_reg_array[1] = &mb_regs.voltage;
    mb_reg_array[2] = &mb_regs.current;
    mb_reg_array[3] = &mb_regs.power;
    mb_reg_array[4] = &mb_regs.set_voltage;
    mb_reg_array[5] = &mb_regs.set_current;
    mb_reg_array[6] = &mb_regs.set_power;
    mb_reg_array[7] = &mb_regs.control_cmd;
    mb_reg_array[8] = &mb_regs.temperature;
    mb_reg_array[9] = &mb_regs.alarm;
}

4.配置FreeModbus协议栈

在FreeModbus协议栈中,您需要配置从机的地址、串口通信参数以及寄存器的起始地址和数量。

配置从机地址

#define MB_SLAVE_ID 1

配置串口通信参数

void USART1_Config(uint16_t baudrate) {
    // 配置USART1的波特率、数据位、停止位等参数
    // 示例代码省略,具体配置请参考STM32CubeIDE文档
}

配置寄存器地址和数量
在FreeModbus协议栈的初始化函数中,设置寄存器的起始地址和数量。

eMBErrorCode eMBInit(uint8_t ucSlaveAddress, uint32_t ulBaudRate, eMBParity eParity, uint16_t usRegAddressStart, uint16_t usRegAddressEnd) {
    // 初始化FreeModbus协议栈,设置从机地址、波特率、校验位、寄存器起始地址和结束地址
    // 示例代码省略,具体实现请参考FreeModbus文档
}

调用初始化函数:

eMBInit(MB_SLAVE_ID, 9600, MB_PARITY_NONE, MB_REG_START_ADDR, MB_REG_START_ADDR + MB_REG_NUM - 1);

5.主循环

在主循环中,调用FreeModbus协议栈的轮询函数,以处理Modbus通信。

int main(void) {
    // 初始化硬件和FreeModbus协议栈
    SystemInit();
    USART1_Config(9600);
    MB_InitRegisters();
    eMBInit(MB_SLAVE_ID, 9600, MB_PARITY_NONE, MB_REG_START_ADDR, MB_REG_START_ADDR + MB_REG_NUM - 1);

    while (1) {
        // 轮询FreeModbus协议栈
        eMBPoll();
    }
}

6.测试与调试

使用Modbus Poll工具(如Modbus Poll)进行测试。配置工具以连接到您的STM32从机,读取和写入寄存器值,验证通信是否正常。

7.注意事项

• 寄存器地址映射:FreeModbus协议栈中寄存器地址从0开始,但Modbus协议中地址从1开始。因此,您需要在代码中进行适当的映射。

• 数据类型对齐:确保寄存器数组中的数据类型与FreeModbus协议栈的要求一致。

• 线程安全:如果您的系统使用了RTOS,确保FreeModbus协议栈的轮询函数在合适的任务中运行。

通过以上步骤,您可以在STM32F103上成功移植FreeModbus从机,并配置所需的寄存器。希望这些信息对您有所帮助!如果您在实现过程中遇到任何问题,欢迎随时提问。

豆包回答

下面为你详细介绍如何在 STM32F103 上移植 FreeModbus 作为从机,保持寄存器地址 0x1000,寄存器数量 10,同时涵盖通信参数(开关机状态、电压、电流等)的设置:

1. 准备工作

  • 硬件平台:STM32F103 开发板,确保具备串口通信功能,用于 Modbus 通信。
  • 软件环境:安装 Keil MDK 开发环境,准备 FreeModbus 开源库代码。

2. 移植 FreeModbus 库

2.1 下载 FreeModbus 库

从 FreeModbus 官方 GitHub 仓库(https://github.com/ARMmbed/freemodbus )下载最新版本的库代码。

2.2 配置工程

将 FreeModbus 库中的源文件(如 mb.cmbfunc.cmbport.c 等)添加到 Keil MDK 工程中,并包含相应的头文件路径。

3. 配置寄存器

3.1 定义寄存器数组

在代码中定义一个数组来存储寄存器的值,寄存器地址从 0x1000 开始,数量为 10。

#define REGISTER_START_ADDRESS 0x1000
#define REGISTER_NUMBER 10

// 定义寄存器数组
uint16_t usRegHoldingBuf[REGISTER_NUMBER];

// 初始化寄存器
void Registers_Init(void) {
    // 开关机状态
    usRegHoldingBuf[0] = 0; 
    // 电压
    usRegHoldingBuf[1] = 0; 
    // 电流
    usRegHoldingBuf[2] = 0; 
    // 功率
    usRegHoldingBuf[3] = 0; 
    // 设置电压
    usRegHoldingBuf[4] = 0; 
    // 设置电流
    usRegHoldingBuf[5] = 0; 
    // 设置功率
    usRegHoldingBuf[6] = 0; 
    // 控制命令
    usRegHoldingBuf[7] = 0; 
    // 温度
    usRegHoldingBuf[8] = 0; 
    // 告警字
    usRegHoldingBuf[9] = 0; 
}
3.2 实现寄存器读写回调函数

FreeModbus 库通过回调函数来处理寄存器的读写操作,需要实现相应的回调函数。

eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) {
    USHORT usRegIndex;

    // 检查寄存器地址是否合法
    if ((usAddress < REGISTER_START_ADDRESS) || (usAddress + usNRegs > REGISTER_START_ADDRESS + REGISTER_NUMBER)) {
        return MB_ENOREG;
    }

    // 计算寄存器在数组中的索引
    usRegIndex = usAddress - REGISTER_START_ADDRESS;

    switch (eMode) {
        case MB_REG_READ:
            // 读取寄存器值
            for (USHORT i = 0; i < usNRegs; i++) {
                pucRegBuffer[2 * i] = (uint8_t)(usRegHoldingBuf[usRegIndex + i] >> 8);
                pucRegBuffer[2 * i + 1] = (uint8_t)(usRegHoldingBuf[usRegIndex + i]);
            }
            break;

        case MB_REG_WRITE:
            // 写入寄存器值
            for (USHORT i = 0; i < usNRegs; i++) {
                usRegHoldingBuf[usRegIndex + i] = (pucRegBuffer[2 * i] << 8) | pucRegBuffer[2 * i + 1];
            }
            break;
    }

    return MB_ENOERR;
}

4. 初始化 FreeModbus 从机

#include "mb.h"
#include "mbport.h"

void Modbus_Slave_Init(void) {
    // 初始化 Modbus 从机
    eMBErrorCode eStatus;

    // 初始化寄存器
    Registers_Init();

    // 配置 Modbus 从机参数
    eStatus = eMBInit(MB_RTU, 1, 0, 9600, MB_PAR_NONE);

    if (eStatus == MB_ENOERR) {
        // 启动 Modbus 从机
        eStatus = eMBEnable();
    }
}

5. 主循环中处理 Modbus 通信

int main(void) {
    // 初始化 Modbus 从机
    Modbus_Slave_Init();

    while (1) {
        // 处理 Modbus 通信
        (void)eMBPoll();

        // 可以在这里更新寄存器的值,例如读取传感器数据
        // usRegHoldingBuf[1] = Read_Voltage();
        // usRegHoldingBuf[2] = Read_Current();
    }
}

6. 注意事项

  • 串口配置:确保 STM32F103 的串口配置与 Modbus 通信参数一致,包括波特率、数据位、停止位和校验位。
  • 中断处理:FreeModbus 库依赖串口中断来处理通信数据,需要正确配置串口中断服务函数。
  • 错误处理:在实际应用中,需要对 FreeModbus 库的返回错误码进行处理,以确保通信的稳定性。

通过以上步骤,你可以在 STM32F103 上成功移植 FreeModbus 作为从机,并实现寄存器的读写操作。

DeepSeek回答

在STM32F103上移植FreeMODBUS从机,并配置保持寄存器地址0x1000,寄存器数量为10,涉及以下几个步骤:

1. 硬件准备

  • STM32F103:确保硬件连接正确,特别是UART引脚(用于Modbus通信)。
  • FreeMODBUS库:下载并准备好FreeMODBUS库。

2. 配置FreeMODBUS

  • 修改mbconfig.h:根据需求配置FreeMODBUS的参数,如Modbus从机地址、波特率、数据位、停止位等。
  • 修改mbrtu.cmbtcp.c:根据使用的Modbus协议(RTU或TCP)进行相应的配置。

3. 定义保持寄存器

在FreeMODBUS中,保持寄存器通常通过eMBRegHoldingCB回调函数来访问。你需要在mbfuncholding.c中实现这个回调函数。

#include "mb.h"
#include "mbframe.h"

#define REG_HOLDING_START 0x1000
#define REG_HOLDING_NREGS 10

// 保持寄存器数组
static USHORT usRegHoldingBuf[REG_HOLDING_NREGS] = {0};

eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
    eMBErrorCode eStatus = MB_ENOERR;
    USHORT iRegIndex;

    if ((usAddress >= REG_HOLDING_START) && (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS))
    {
        iRegIndex = (USHORT)(usAddress - REG_HOLDING_START);

        switch (eMode)
        {
            // 读保持寄存器
            case MB_REG_READ:
                while (usNRegs > 0)
                {
                    *pucRegBuffer++ = (UCHAR)(usRegHoldingBuf[iRegIndex] >> 8);
                    *pucRegBuffer++ = (UCHAR)(usRegHoldingBuf[iRegIndex] & 0xFF);
                    iRegIndex++;
                    usNRegs--;
                }
                break;

            // 写保持寄存器
            case MB_REG_WRITE:
                while (usNRegs > 0)
                {
                    usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                    usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                    iRegIndex++;
                    usNRegs--;
                }
                break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

4. 初始化Modbus从机

在主程序中初始化Modbus从机,并启动Modbus任务。

#include "mb.h"
#include "mbutils.h"

int main(void)
{
    // 初始化硬件(如UART、GPIO等)
    HardwareInit();

    // 初始化Modbus从机
    eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_NONE);

    // 启动Modbus从机
    eMBEnable();

    while (1)
    {
        // 处理Modbus通信
        eMBPoll();

        // 其他任务
    }
}

5. 映射寄存器到实际变量

将保持寄存器映射到实际的变量,如开关机状态、电压、电流等。

// 假设这些变量已经在其他地方定义
extern uint16_t g_switch_state;
extern uint16_t g_voltage;
extern uint16_t g_current;
extern uint16_t g_power;
extern uint16_t g_set_voltage;
extern uint16_t g_set_current;
extern uint16_t g_set_power;
extern uint16_t g_control_cmd;
extern uint16_t g_temperature;
extern uint16_t g_alarm_word;

void UpdateRegisters()
{
    usRegHoldingBuf[0] = g_switch_state;
    usRegHoldingBuf[1] = g_voltage;
    usRegHoldingBuf[2] = g_current;
    usRegHoldingBuf[3] = g_power;
    usRegHoldingBuf[4] = g_set_voltage;
    usRegHoldingBuf[5] = g_set_current;
    usRegHoldingBuf[6] = g_set_power;
    usRegHoldingBuf[7] = g_control_cmd;
    usRegHoldingBuf[8] = g_temperature;
    usRegHoldingBuf[9] = g_alarm_word;
}

6. 主循环中更新寄存器

在主循环中定期更新保持寄存器的值。

while (1)
{
    // 处理Modbus通信
    eMBPoll();

    // 更新保持寄存器
    UpdateRegisters();

    // 其他任务
}

7. 调试与测试

  • 使用Modbus调试工具(如Modbus Poll)测试从机功能。
  • 确保寄存器读写操作正常,数据正确。

8. 进一步优化

  • 根据实际需求优化代码,如增加错误处理、日志记录等。
  • 考虑使用DMA或中断来提高通信效率。

总结

通过以上步骤,你可以在STM32F103上成功移植FreeMODBUS从机,并配置保持寄存器地址0x1000,寄存器数量为10。确保硬件连接正确,并根据实际需求调整代码。

优化问题

由于三个比较热门的应用似乎都只把重心放在了参数结构体的创建、然后降寄存器映射到结构体参数上,调用freemodbus使用。并没有涉及到移植的工作。所以继续调整提问完善移植工作。

完善freemodbus移植的修改

KIMI完善

根据您的需求,我将详细说明如何在STM32F103上移植FreeModbus从机,并配置寄存器地址为0x1000,寄存器数量为10。这些寄存器将分别用于存储开关机状态、电压、电流、功率、设置电压、设置电流、设置功率、控制命令、温度和告警字。

1.准备工作

• 开发环境:确保您已经安装了STM32开发环境(如STM32CubeIDE)。

• FreeModbus源码:从FreeModbus官网下载协议栈源码。

• 硬件配置:确保您的STM32F103开发板已经连接好串口通信模块(如RS485模块)。

2.工程配置

• 创建STM32工程:在STM32CubeIDE中创建一个新的工程,选择您的STM32F103芯片。

• 添加FreeModbus源码:将FreeModbus协议栈的源码文件(如mb.hmbproto.cmbfunc.c等)添加到您的工程中。

• 配置串口:配置USART用于Modbus RTU通信。例如,使用USART1,设置波特率为9600,8位数据位,1位停止位,无校验位。

3.配置寄存器

根据您的需求,我们需要定义10个寄存器,起始地址为0x1000。在FreeModbus中,寄存器地址从0开始,因此实际的数组索引将从0开始。

定义寄存器结构

typedef struct {
    uint16_t status;       // 开关机状态
    uint16_t voltage;      // 电压
    uint16_t current;      // 电流
    uint16_t power;        // 功率
    uint16_t set_voltage;  // 设置电压
    uint16_t set_current;  // 设置电流
    uint16_t set_power;    // 设置功率
    uint16_t control_cmd;  // 控制命令
    uint16_t temperature;  // 温度
    uint16_t alarm;        // 告警字
} ModbusRegisters;

初始化寄存器
在FreeModbus中,寄存器通常存储在一个数组中。您需要在代码中初始化这个数组,并将其与FreeModbus协议栈关联。

#define MB_REG_START_ADDR 0x1000
#define MB_REG_NUM 10

ModbusRegisters mb_regs;
uint16_t mb_reg_array[MB_REG_NUM];

void MB_InitRegisters() {
    mb_reg_array[0] = &mb_regs.status;
    mb_reg_array[1] = &mb_regs.voltage;
    mb_reg_array[2] = &mb_regs.current;
    mb_reg_array[3] = &mb_regs.power;
    mb_reg_array[4] = &mb_regs.set_voltage;
    mb_reg_array[5] = &mb_regs.set_current;
    mb_reg_array[6] = &mb_regs.set_power;
    mb_reg_array[7] = &mb_regs.control_cmd;
    mb_reg_array[8] = &mb_regs.temperature;
    mb_reg_array[9] = &mb_regs.alarm;
}

4.配置FreeModbus协议栈

在FreeModbus协议栈中,您需要配置从机的地址、串口通信参数以及寄存器的起始地址和数量。

配置从机地址

#define MB_SLAVE_ID 1

配置串口通信参数

void USART1_Config(uint16_t baudrate) {
    // 配置USART1的波特率、数据位、停止位等参数
    // 示例代码省略,具体配置请参考STM32CubeIDE文档
}

配置寄存器地址和数量
在FreeModbus协议栈的初始化函数中,设置寄存器的起始地址和数量。

eMBErrorCode eMBInit(uint8_t ucSlaveAddress, uint32_t ulBaudRate, eMBParity eParity, uint16_t usRegAddressStart, uint16_t usRegAddressEnd) {
    // 初始化FreeModbus协议栈,设置从机地址、波特率、校验位、寄存器起始地址和结束地址
    // 示例代码省略,具体实现请参考FreeModbus文档
}

调用初始化函数:

eMBInit(MB_SLAVE_ID, 9600, MB_PARITY_NONE, MB_REG_START_ADDR, MB_REG_START_ADDR + MB_REG_NUM - 1);

5.实现适配层函数

根据STM32F103的硬件特性,实现适配层函数。这包括串口的初始化和读写函数,以及定时器的启动和停止函数。

串口适配层

// portserial.c
#include "mb.h"
#include "mbport.h"

// 串口初始化
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity) {
    // 使用STM32CubeMX配置好的USART1初始化函数
    MX_USART1_UART_Init();
    return TRUE;
}

// 串口发送一个字节
BOOL xMBPortSerialPutByte(CHAR ucByte) {
    // 直接操作USART寄存器发送字节
    USART1->DR = ucByte;
    return TRUE;
}

// 串口接收一个字节
BOOL xMBPortSerialGetByte(CHAR *pucByte) {
    // 直接操作USART寄存器接收字节
    *pucByte = (USART1->DR & (uint16_t)0x00FF);
    return TRUE;
}

// 串口中断处理
void USART1_IRQHandler(void) {
    if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) {
        __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
        prvvUARTRxISR();
    }

    if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE)) {
        __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);
        prvvUARTTxReadyISR();
    }
}

定时器适配层

// porttimer.c
#include "mb.h"
#include "mbport.h"

// 定时器初始化
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) {
    // 使用STM32CubeMX配置好的定时器初始化函数
    MX_TIM4_Init();
    __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);
    __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);
    return TRUE;
}

// 使能定时器
void vMBPortTimersEnable(void) {
    __HAL_TIM_SET_COUNTER(&htim4, 0);
    __HAL_TIM_ENABLE(&htim4);
}

// 禁用定时器
void vMBPortTimersDisable(void) {
    __HAL_TIM_DISABLE(&htim4);
}

// 定时器中断处理
void TIM4_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);
        prvvTIMERExpiredISR();
    }
}

6.主循环

在主循环中,调用FreeModbus协议栈的轮询函数,以处理Modbus通信。

int main(void) {
    // 初始化硬件和FreeModbus协议栈
    SystemInit();
    USART1_Config(9600);
    MB_InitRegisters();
    eMBInit(MB_SLAVE_ID, 9600, MB_PARITY_NONE, MB_REG_START_ADDR, MB_REG_START_ADDR + MB_REG_NUM - 1);

    while (1) {
        // 轮询FreeModbus协议栈
        eMBPoll();
    }
}

豆包完善

要进一步完善 FreeModbus 在 STM32F103 上的移植,可从多方面进行优化,包括错误处理、串口配置、代码结构优化、数据更新逻辑等,以下是详细内容:

1. 增强错误处理

在 FreeModbus 初始化和运行过程中,可能会出现各种错误,需要对这些错误进行详细的处理和记录,方便后续调试和维护。

#include <stdio.h>
#include "mb.h"
#include "mbport.h"

// 定义寄存器相关宏
#define REGISTER_START_ADDRESS 0x1000
#define REGISTER_NUMBER 10

// 定义寄存器数组
uint16_t usRegHoldingBuf[REGISTER_NUMBER];

// 初始化寄存器
void Registers_Init(void) {
    for (int i = 0; i < REGISTER_NUMBER; i++) {
        usRegHoldingBuf[i] = 0;
    }
}

// 实现寄存器读写回调函数
eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode) {
    USHORT usRegIndex;

    // 检查寄存器地址是否合法
    if ((usAddress < REGISTER_START_ADDRESS) || (usAddress + usNRegs > REGISTER_START_ADDRESS + REGISTER_NUMBER)) {
        return MB_ENOREG;
    }

    // 计算寄存器在数组中的索引
    usRegIndex = usAddress - REGISTER_START_ADDRESS;

    switch (eMode) {
        case MB_REG_READ:
            // 读取寄存器值
            for (USHORT i = 0; i < usNRegs; i++) {
                pucRegBuffer[2 * i] = (uint8_t)(usRegHoldingBuf[usRegIndex + i] >> 8);
                pucRegBuffer[2 * i + 1] = (uint8_t)(usRegHoldingBuf[usRegIndex + i]);
            }
            break;

        case MB_REG_WRITE:
            // 写入寄存器值
            for (USHORT i = 0; i < usNRegs; i++) {
                usRegHoldingBuf[usRegIndex + i] = (pucRegBuffer[2 * i] << 8) | pucRegBuffer[2 * i + 1];
            }
            break;
    }

    return MB_ENOERR;
}

// 初始化 FreeModbus 从机
void Modbus_Slave_Init(void) {
    eMBErrorCode eStatus;

    // 初始化寄存器
    Registers_Init();

    // 配置 Modbus 从机参数
    eStatus = eMBInit(MB_RTU, 1, 0, 9600, MB_PAR_NONE);
    if (eStatus != MB_ENOERR) {
        // 记录初始化错误信息
        printf("Modbus initialization failed with error code: %d\n", eStatus);
        return;
    }

    // 启动 Modbus 从机
    eStatus = eMBEnable();
    if (eStatus != MB_ENOERR) {
        // 记录启动错误信息
        printf("Modbus enable failed with error code: %d\n", eStatus);
    }
}

2. 优化串口配置

确保串口配置与 Modbus 通信参数严格一致,同时要正确处理串口中断,保证数据的可靠传输。

// 在 mbport.c 中完善串口初始化函数
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity) {
    // 配置串口 GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能串口和 GPIO 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // 配置 USART1 Tx (PA.9) 为复用推挽输出
    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 (PA.10) 为浮空输入
    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 = ulBaudRate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    switch (eParity) {
        case MB_PAR_NONE:
            USART_InitStructure.USART_Parity = USART_Parity_No;
            break;
        case MB_PAR_ODD:
            USART_InitStructure.USART_Parity = USART_Parity_Odd;
            break;
        case MB_PAR_EVEN:
            USART_InitStructure.USART_Parity = USART_Parity_Even;
            break;
    }
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    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);

    // 配置 NVIC
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // 使能 USART1
    USART_Cmd(USART1, ENABLE);

    return TRUE;
}

// 在 mbport.c 中实现串口发送和接收函数
BOOL xMBPortSerialPutByte(CHAR ucByte) {
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, (uint8_t)ucByte);
    return TRUE;
}

BOOL xMBPortSerialGetByte(CHAR *pucByte) {
    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) {
        *pucByte = (CHAR)USART_ReceiveData(USART1);
        return TRUE;
    }
    return FALSE;
}

// 串口中断服务函数
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        // 调用 FreeModbus 接收处理函数
        prvvUARTRxISR();
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

3. 代码结构优化

将不同功能的代码模块化,提高代码的可读性和可维护性。例如,将寄存器初始化、Modbus 初始化、数据更新等功能分别封装成独立的函数。

4. 数据更新逻辑完善

在主循环中添加数据更新逻辑,根据实际应用场景更新寄存器的值,例如读取传感器数据并更新到相应的寄存器中。

// 模拟读取传感器数据函数
uint16_t Read_Voltage(void) {
    // 这里可以添加实际的电压读取代码
    return 220;
}

uint16_t Read_Current(void) {
    // 这里可以添加实际的电流读取代码
    return 1;
}

int main(void) {
    // 初始化 Modbus 从机
    Modbus_Slave_Init();

    while (1) {
        // 处理 Modbus 通信
        (void)eMBPoll();

        // 更新寄存器的值,例如读取传感器数据
        usRegHoldingBuf[1] = Read_Voltage();
        usRegHoldingBuf[2] = Read_Current();

        // 可以添加适当的延时,避免过于频繁的更新
        for (volatile int i = 0; i < 100000; i++);
    }
}

DeepSeek完善

在STM32F103上移植FreeMODBUS从机并完善其功能,除了基本的配置和寄存器映射外,还需要考虑以下几个方面:


1. FreeMODBUS 配置文件修改 (mbconfig.h)

mbconfig.h中,根据需求配置FreeMODBUS的参数。以下是一些关键配置项:

// 使能RTU模式
#define MB_RTU_ENABLED                   (1)

// 使能保持寄存器功能
#define MB_FUNC_HOLDING_ENABLED           (1)

// 从机地址
#define MB_ADDRESS_DEFAULT                (1)

// 串口波特率
#define MB_BAUDRATE_DEFAULT               (9600)

// 数据位、停止位、校验位
#define MB_PARITY_DEFAULT                 (MB_PAR_NONE)

// 使能调试信息
#define MB_DEBUG_ENABLED                  (1)

// 设置堆栈大小(根据FreeRTOS或裸机环境调整)
#define MB_PORT_TASK_STACKSIZE            (256)

// 设置任务优先级
#define MB_PORT_TASK_PRIO                 (5)

2. 硬件初始化

main.c中初始化硬件(如UART、GPIO等),确保Modbus通信的硬件环境正确。

#include "stm32f10x.h"
#include "mb.h"

void HardwareInit(void)
{
    // 初始化时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // 配置USART1的TX (PA9) 和 RX (PA10)
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 配置USART1
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 9600;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStruct);

    USART_Cmd(USART1, ENABLE);
}

3. 实现保持寄存器回调函数

mbfuncholding.c中实现保持寄存器的读写回调函数。

#include "mb.h"
#include "mbframe.h"

#define REG_HOLDING_START 0x1000
#define REG_HOLDING_NREGS 10

// 保持寄存器数组
static USHORT usRegHoldingBuf[REG_HOLDING_NREGS] = {0};

eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
    eMBErrorCode eStatus = MB_ENOERR;
    USHORT iRegIndex;

    if ((usAddress >= REG_HOLDING_START) && (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS))
    {
        iRegIndex = (USHORT)(usAddress - REG_HOLDING_START);

        switch (eMode)
        {
            // 读保持寄存器
            case MB_REG_READ:
                while (usNRegs > 0)
                {
                    *pucRegBuffer++ = (UCHAR)(usRegHoldingBuf[iRegIndex] >> 8);
                    *pucRegBuffer++ = (UCHAR)(usRegHoldingBuf[iRegIndex] & 0xFF);
                    iRegIndex++;
                    usNRegs--;
                }
                break;

            // 写保持寄存器
            case MB_REG_WRITE:
                while (usNRegs > 0)
                {
                    usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                    usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                    iRegIndex++;
                    usNRegs--;
                }
                break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

4. 映射寄存器到实际变量

将保持寄存器映射到实际的变量(如开关机状态、电压、电流等)。

// 假设这些变量已经在其他地方定义
extern uint16_t g_switch_state;
extern uint16_t g_voltage;
extern uint16_t g_current;
extern uint16_t g_power;
extern uint16_t g_set_voltage;
extern uint16_t g_set_current;
extern uint16_t g_set_power;
extern uint16_t g_control_cmd;
extern uint16_t g_temperature;
extern uint16_t g_alarm_word;

void UpdateRegisters()
{
    usRegHoldingBuf[0] = g_switch_state;
    usRegHoldingBuf[1] = g_voltage;
    usRegHoldingBuf[2] = g_current;
    usRegHoldingBuf[3] = g_power;
    usRegHoldingBuf[4] = g_set_voltage;
    usRegHoldingBuf[5] = g_set_current;
    usRegHoldingBuf[6] = g_set_power;
    usRegHoldingBuf[7] = g_control_cmd;
    usRegHoldingBuf[8] = g_temperature;
    usRegHoldingBuf[9] = g_alarm_word;
}

5. 主程序初始化与任务调度

在主程序中初始化Modbus从机,并启动Modbus任务。

#include "mb.h"
#include "mbutils.h"

int main(void)
{
    // 初始化硬件
    HardwareInit();

    // 初始化Modbus从机
    eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_NONE);

    // 启动Modbus从机
    eMBEnable();

    while (1)
    {
        // 处理Modbus通信
        eMBPoll();

        // 更新保持寄存器
        UpdateRegisters();

        // 其他任务
    }
}

6. 调试与测试

  • 使用Modbus调试工具(如Modbus Poll)测试从机功能。
  • 确保寄存器读写操作正常,数据正确。

7. 进一步优化

  • 增加错误处理:在回调函数中增加错误处理逻辑,确保数据读写安全。
  • 使用DMA或中断:提高通信效率,减少CPU占用。
  • 日志记录:增加调试日志,便于排查问题。
  • 多任务支持:如果使用RTOS,确保Modbus任务与其他任务协同工作。

8. 完整代码结构

Project/
├── Inc/
│   ├── mbconfig.h
│   ├── mbframe.h
│   ├── mb.h
│   ├── stm32f10x_conf.h
├── Src/
│   ├── main.c
│   ├── mbfuncholding.c
│   ├── mbrtu.c
│   ├── stm32f10x_it.c
│   ├── hardware.c

通过以上步骤,你可以完善FreeMODBUS的移植,并在STM32F103上实现一个功能完整的Modbus从机。

总结与展望

输入的需求越清晰,提示语越明确,生成的方案越符合我们的需求。期待能够提炼一套更符合嵌入式软件开发的提示语策略。希望后面各个应用在训练更多数据后能够更加智能的为我们的工作带来便利。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Geek__1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值