STM32F407ZET7+ETH+LWIP移植freemodbus_TCP

目录

前言

一、STM32Cubemx配置

1、选择STM32F407ZET7芯片创建工程,首先配置RCC、SYS和时钟,配置界面如下(根据自己情况配置就好)

2、配置ETH,参数默认即可,配置界面如下(我的的引脚有调整,是根据实际电路调整的,大家根据自己情况调整即可)

3、配LWIP,我使用的静态IP,配置界面如下

4、配置串口,有需要的话方便看数据,配置界面如下

5、到此CubeMX就配置完成可以生成代码了(根据自己用的软件改一下设置即可)

二、修改程序、移植FreeModbus源码

1、先复位一下芯片(不写也没啥问题)

到此之前的例程:有需要的自己下载幺,不需要积分的!!!

2.FreeModbus源码下载

3.移植FreeModbus源码

①在工程文件目录下新建一个 FreeModbus_TCP 文件夹,将需要的文件都移植到该文件夹下

②打开 freemodbus-v1.6 文件夹,点击 modbus 文件夹,将modbus 文件夹中的全部文件移植到新建的 FreeModbus_TCP 文件夹中

③将freemodbus-v1.6\demo\STR71XTCP中的 port 文件,移植到新建的 FreeModbus_TCP 文件夹中

④新建的 FreeModbus_TCP 文件夹内容移植最终结果如下:下面的两个文件目前还没有!!

⑤导入到工程中

⑥点击魔法棒,选择 C/C++,添加文件路径

三、编辑程序

四、测试

①打开modbus poll 在connection选connect连接或按F3

②点击"Setup"→“Read/Write Definition…”,或者按快捷键F8设置从机地址,功能码,起始地址,寄存器数量等信息

③测试结果

五、工程文件


前言

        本次实验以 STM32F407ZET7 芯片为MCU,使用 8MHz 外部时钟源。以太网PHY层芯片为 LAN8720A,移植FreeModbus实现ModbusTCP网口通信,做客户端(从机)实现网口TCP-Modbus通信。

另:本人首次使用,也是结合网上多个大佬的经验做的,有不足的地方请大家指正,内容比较详细,对像我一样的新手比较友好,如果觉得过细可跳着看!!!

一、STM32Cubemx配置

1、选择STM32F407ZET7芯片创建工程,首先配置RCC、SYS和时钟,配置界面如下(根据自己情况配置就好)

2、配置ETH,参数默认即可,配置界面如下(我的的引脚有调整,是根据实际电路调整的,大家根据自己情况调整即可)

我配置了一个GPIO_Output作为复位引脚,(低电平复位、默认为高电平)

3、配LWIP,我使用的静态IP,配置界面如下

选LAN8742就可以

4、配置串口,有需要的话方便看数据,配置界面如下

5、到此CubeMX就配置完成可以生成代码了(根据自己用的软件改一下设置即可)

二、修改程序、移植FreeModbus源码

1、先复位一下芯片(不写也没啥问题)

到目前为止,已经初始化完成了,在cmd中可以ping通!!!!!!

到此之前的例程:有需要的自己下载幺,不需要积分的!!!

STM32Cubemx+F407ZET7-ETH+LWIP基础工程(能ping通)icon-default.png?t=N7T8https://download.youkuaiyun.com/download/qq_33288274/88743720

(设备IP为:192.168.1.10 电脑IP为:192.168.1.200 子网掩码:255.255.255.0 默认网关:192.168.1.1 注意:复位引脚我默认放在了低电平(一直复位状态),根据自己需要将复位引脚电平拉高或者配置一下Cubemx默认放在高电平即可)

2.FreeModbus源码下载

下载链接放在下面了,自己下载就可以了!
FreeModbus源码下载链接icon-default.png?t=N7T8https://www.embedded-experts.at/en/freemodbus-downloads/

3.移植FreeModbus源码

①在工程文件目录下新建一个 FreeModbus_TCP 文件夹,将需要的文件都移植到该文件夹下

②打开 freemodbus-v1.6 文件夹,点击 modbus 文件夹,将modbus 文件夹中的全部文件移植到新建的 FreeModbus_TCP 文件夹中

③将freemodbus-v1.6\demo\STR71XTCP中的 port 文件,移植到新建的 FreeModbus_TCP 文件夹中

④新建的 FreeModbus_TCP 文件夹内容移植最终结果如下:下面的两个文件目前还没有!!

⑤导入到工程中

FreeModbus_TCP 文件夹中的文件导入

选择FreeModbus_TCP 文件夹中的mb.c文件

选择FreeModbus_TCP 文件夹中的 functions 文件夹、 port 文件夹、tcp 文件夹的全部.c文件

结果如下图所示(目前最后一个文件还没有!)

⑥点击魔法棒,选择 C/C++,添加文件路径

三、编辑程序

③找不到这个文件在哪的可以在mb.c的头文件中跳转打开!!后面找不到的也一样!

④修改 portevent.c将以下程序,替换原来的程序

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eQueuedEvent;
static BOOL     xEventInQueue;

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
    xEventInQueue = FALSE;
    return TRUE;
}

BOOL
xMBPortEventPost( eMBEventType eEvent )
{
    xEventInQueue = TRUE;
    eQueuedEvent = eEvent;
    return TRUE;
}

BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    BOOL            xEventHappened = FALSE;

    if( xEventInQueue )
    {
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
        xEventHappened = TRUE;
    }
    return xEventHappened;
}

⑤添加 #include “string.h”  和  #define NETCONN_COPY 0x01

⑥新建User_modbus_TCP.c文件

#include <stdio.h>
#include <string.h>
#include "User_modbus_TCP.h"
#include "mb.h"
#include "mbutils.h"

void ModbusTCPInit(void)
{
    eMBTCPInit(MODBUS_TCP_PORT);
    eMBEnable();
}

void ModbusTCPDeInit(void)
{
    eMBDisable();
    eMBClose();
}

void ModbusTCPMain(void)
{
    if (MB_ENOERR != eMBPoll())
    {
        ModbusTCPDeInit();
        ModbusTCPInit();
    }
}

//线圈
#define REG_Coils_START   1
#define REG_Coils_SIZE    10

uint8_t  Coils_Data[REG_Coils_SIZE] = {1,1,0,1,0,0,1,1,1,0};

/**
 * @brief: 读线圈---01,写线圈---05
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNCoils      线圈数量
 * @param eMode         读写模式
 * @return eMBErrorCode 错误码
 */
eMBErrorCode eMBRegCoilsCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{
    uint16_t i = 0,byteOffset=0,bitOffset=0,RegIndex = usAddress - REG_Coils_START-1;
    if ((usAddress >= REG_Coils_START)&&(usAddress + usNCoils <= REG_Coils_START + REG_Coils_SIZE+1))
    {
        if (MB_REG_READ == eMode)
        {
          for(i=0;i<usNCoils;i++)
          {
              byteOffset = i / 8;
              bitOffset = i % 8;
              xMBUtilSetBits(&pucRegBuffer[byteOffset], bitOffset, 1, Coils_Data[RegIndex+i]);
          }
        }
        else
        {
          for(i=0;i<usNCoils;i++)
          {
              byteOffset = i / 8;
              bitOffset = i % 8;
              Coils_Data[RegIndex+i]=xMBUtilGetBits(&pucRegBuffer[byteOffset], bitOffset, 1);
          }
        }
    }
    else
    {
        return MB_ENOREG;
    }
    
    return MB_ENOERR;
}

 //离散寄存器
#define REG_DISCRETE_START   10
#define REG_DISCRETE_SIZE    20

uint8_t  Discrete_Data[REG_DISCRETE_SIZE] = {1,1,0,1,0,0,1,1,1,0,1,0,0,1};

/**
 * @brief:读离散寄存器---02
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNDiscrete   寄存器个数
 * @return eMBErrorCode 返回错误码
 */
eMBErrorCode eMBRegDiscreteCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
    uint16_t i = 0,byteOffset=0,bitOffset=0,RegIndex = usAddress - REG_DISCRETE_START-1;
  
    if ((usAddress >= REG_DISCRETE_START)&&(usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE+1))
    {
      for(i=0;i<usNDiscrete;i++)
      {
          byteOffset = i / 8;
          bitOffset = i % 8;
          xMBUtilSetBits(&pucRegBuffer[byteOffset], bitOffset, 1, Discrete_Data[RegIndex+i]);
      }
    }
    else
    {
        return MB_ENOREG;
    }

    return MB_ENOERR;
}

//保持寄存器
#define REG_HOLDING_REGISTER_START   10
#define REG_HOLDING_REGISTER_SIZE    30

uint16_t  Holding_Data[REG_HOLDING_REGISTER_SIZE] = 
{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12};

/**
 * @brief: 读保持寄存器---03,写保持寄存器---06
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNRegs       寄存器个数
 * @param eMode         读写模式
 * @return eMBErrorCode 返回错误码
 */

eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
    uint16_t i = 0,RegIndex = usAddress - REG_HOLDING_REGISTER_START-1;
  
    if ((usAddress >= REG_HOLDING_REGISTER_START )&&(usAddress + usNRegs <= REG_HOLDING_REGISTER_START  + REG_HOLDING_REGISTER_SIZE+1))
    {
        if (MB_REG_READ == eMode)//读
        {
          for(i=0;i<usNRegs;i++)
          {
            pucRegBuffer[i*2] = (UCHAR)(Holding_Data[RegIndex+i]>>8);
            pucRegBuffer[i*2+1] = (UCHAR)Holding_Data[RegIndex+i];
          }
        }
        else//写
        {
          for(i=0;i<usNRegs;i++)
          {
            Holding_Data[RegIndex+i]=(pucRegBuffer[i*2]<<8)|(pucRegBuffer[i*2+1]);
          }
        }
    }
    else
    {
        return MB_ENOREG;
    }

    return MB_ENOERR;
}

//输入寄存器
#define REG_INPUT_REGISTER_START    1
#define REG_INPUT_REGISTER_SIZE    20

uint16_t  Input_Data[REG_DISCRETE_SIZE] = 
{100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119};
/**
 * @brief: 读输入寄存器---04
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNRegs       寄存器个数
 * @return eMBErrorCode 返回错误码
 */
eMBErrorCode eMBRegInputCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
    uint16_t i = 0,RegIndex = usAddress - REG_INPUT_REGISTER_START-1;
    if ((usAddress >= REG_INPUT_REGISTER_START)&&(usAddress + usNRegs <= REG_INPUT_REGISTER_START + REG_INPUT_REGISTER_SIZE+1))
    {
        for(i=0;i<usNRegs;i++)
        {
          pucRegBuffer[i*2] = (UCHAR)(Input_Data[RegIndex+i]>>8);
          pucRegBuffer[i*2+1] = (UCHAR)Input_Data[RegIndex+i];
        }
    }
    else
    {
        return MB_ENOREG;
    }
    
    return MB_ENOERR;
}

⑦新建User_modbus_TCP.h文件

#ifndef __User_modbbus_TCP_H__
#define	__User_modbbus_TCP_H__

#include "main.h"

#define MODBUS_TCP_PORT 0         //0代表默认即为502,也可以自己设

extern void ModbusTCPInit(void);
extern void ModbusTCPMain(void);

#endif

⑧主程序中

⑨printf重定向(必须要有,不然会卡主,目前不知道原因,有知道的可以在评论区解释一下!)

#include <stdio.h>
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}

到这里程序就移植完成了!!!

四、测试

①打开modbus poll 在connection选connect连接或按F3

端口号在User_modbus_TCP.h定义,0为默认即为502,也可以按照自己需求修改

②点击"Setup"→“Read/Write Definition…”,或者按快捷键F8设置从机地址,功能码,起始地址,寄存器数量等信息

③测试结果

其他同理!!

功能代码表放在下面了!

五、工程文件

按需自取幺!

STM32F407ZET7+ETH+LWIP移植modbusTCP测试通过(带软件和freemodbusv1.6包)icon-default.png?t=N7T8https://download.youkuaiyun.com/download/qq_33288274/88747061F407ZET7-ETH+LWIP+freemodbus+FreeRTOS+SPI+DMAicon-default.png?t=N7T8https://download.youkuaiyun.com/download/qq_33288274/88791756

F407ZET7-ETH+LWIP+freemodbus+FreeRTOS+SPI+DMA+IWDG测试通过(带软件)icon-default.png?t=N7T8https://download.youkuaiyun.com/download/qq_33288274/88849886

ps:积分不够的可以留下邮箱,我看到的了可以私发

### STM32F407微控制器中ETH外设的DMA支持情况 STM32F407是一款基于ARM Cortex-M4内核的高性能微控制器,其以太网(ETH)外设确实支持直接存储器访问(DMA)。这种支持允许数据在内存和以太网硬件之间高效传输而无需CPU频繁干预[^2]。 #### DMA描述符的角色 以太网DMA的核心概念之一是使用描述符来管理数据缓冲区。这些描述符本质上是由四个32位寄存器组成的结构,用于保存状态信息、控制参数以及指向实际数据缓冲区的指针。描述符的状态由`TDES0`寄存器中的`OWN`位决定,该位指示当前描述符的所有权属于DMA还是CPU。具体来说: - 当`OWN=0`时,表明CPU拥有描述符的所有权,可以向其中写入待发送的数据。 - CPU完成数据填充后会将`OWN`置为1,通知DMA接管并执行数据发送任务。 - 数据发送完成后,DMA会重新将`OWN`清零,告知CPU可再次更新此描述符的内容[^1]。 #### 发送与接收过程中的数据位置 尽管描述符本身包含了必要的元数据和配置项,但真正的应用层数据并不存储于描述符内部而是位于独立分配的缓冲区内。例如,在链接脚本文件如`STM32H743ZITX_FLASH.ld`中可以看到特定地址被预留给了接收描述符(`RxDecripSection`)、发送描述符(`TxDecripSection`)及其关联的接收池(`Rx_PoolSection`)等区域[^3]。 对于具体的实现细节而言,官方文档《STM32F4系列参考手册》提供了详尽指导,涵盖了如何初始化DMA通道、配置描述符链表以及处理中断等方面的知识点。开发者需依据手册建议合理规划RAM空间布局,并正确设置各部分基址以便顺利开展通信活动。 ```c // 初始化DMA描述符的一个简单例子 void Ethernet_DMA_Init(void){ // 假定已定义好相应的描述符数组及缓冲区 DmaDesc_Tx[0].status = TDES_OWN | TDES_INT_ON_COMPLETION; DmaDesc_Tx[0].buffer1_addr = (uint32_t)&tx_buffer[0]; } ``` 上述代码片段展示了如何初步设定一个发送描述符实例的部分属性值,其中包括指定目标缓冲区首地址并将初始所有权赋予DMA模块等内容。 ---
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值