首页 > 其他分享 >STM32H563 HAL库 LWIP裸机移植

STM32H563 HAL库 LWIP裸机移植

时间:2025-01-16 21:57:41浏览次数:3  
标签:LWIP netif STM32H563 裸机 Init GPIO ETH heth RCC

一、STM32H563 与 LWIP 简介

STM32H563 是 STMicroelectronics 推出的一款高性能 32 位微控制器,具有丰富的外设和强大的处理能力,适用于各种复杂的嵌入式系统应用。LWIP(Lightweight IP)是一个小型开源的 TCP/IP 协议栈,旨在为嵌入式系统提供轻量级的网络功能,它能够在资源有限的系统中实现网络通信,如实现以太网通信、HTTP 服务器、UDP 和 TCP 连接等功能。

二、LWIP 裸机移植的准备工作

  1. 获取 LWIP 源代码

    • 首先,从 LWIP 的官方网站下载最新的 LWIP 源代码。你可以选择合适的版本,通常需要根据 STM32H563 的资源和项目需求来选择。
    • 将 LWIP 源代码添加到你的 STM32 工程目录中,一般可以将其放在一个单独的文件夹中,例如 LWIP
  2. 配置 LWIP

    • LWIP 提供了一个 lwipopts.h 文件,用于配置协议栈的各种参数,如内存池大小、TCP 窗口大小、是否支持 IPv6 等。根据 STM32H563 的可用资源和应用需求,对这些参数进行调整。
    • 例如,如果 STM32H563 内存有限,可以适当减小内存池的大小,但要确保不会导致内存不足而影响网络性能。
  3. STM32H563 硬件配置

    • 确保 STM32H563 的以太网外设已正确配置。使用 HAL 库的以太网相关函数,配置以太网的 PHY 和 MAC 层。
    • 配置相应的 GPIO 引脚,将以太网的 RMII 或 MII 接口引脚配置为正确的复用功能。

三、LWIP 移植步骤

  1. 添加必要的文件

    • 将 LWIP 的核心文件(如 core 文件夹下的源文件)添加到工程中,包括 ipv4ipv6tcpudp 等模块的源文件。
    • 同时,添加 netif 文件夹下的文件,用于网络接口的实现。
    • 对于 STM32H563,还需要添加 arch 文件夹下的文件,该文件夹包含了与 STM32 架构相关的适配文件。
  2. 修改 sys_arch.c 文件

    • sys_arch.c 文件中,需要实现与操作系统相关的函数。对于裸机移植,需要实现一些简单的任务管理函数,如 sys_mbox_newsys_mbox_free 等,这些函数在 LWIP 中用于消息传递和任务同步。
    • 可以使用简单的邮箱或队列机制,使用全局变量和标志位来实现这些功能,而不是依赖于操作系统的任务调度。
  3. 实现网络接口函数

    • ethernetif.c 文件中,实现 low_level_initlow_level_outputlow_level_input 等函数。
    • low_level_init 函数用于初始化以太网硬件,包括 MAC 地址的设置、PHY 的初始化和复位。
    • low_level_output 函数负责将数据从 LWIP 协议栈发送到以太网硬件,将数据封装成以太网帧并通过以太网外设发送出去。
    • low_level_input 函数从以太网外设接收数据,并将其传递给 LWIP 协议栈。

四、代码示例

#include "stm32h563xx_hal.h"
#include "lwip/opt.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/etharp.h"
#include "lwip/timeouts.h"
#include "ethernetif.h"

// 以太网句柄
ETH_HandleTypeDef heth;

// 网络接口
struct netif gnetif;

// 系统时钟配置
void SystemClock_Config(void);
// GPIO 初始化
static void MX_GPIO_Init(void);
// ETH 初始化
static void MX_ETH_Init(void);


// 主函数
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_ETH_Init();

    // 初始化 LWIP
    lwip_init();

    // 添加网络接口
    ip4_addr_t ipaddr, netmask, gw;
    IP4_ADDR(&ipaddr, 192, 168, 1, 100);
    IP4_ADDR(&netmask, 255, 255, 255, 0);
    IP4_ADDR(&gw, 192, 168, 1, 1);
    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernetif_input);
    netif_set_default(&gnetif);
    netif_set_up(&gnetif);

    while (1)
    {
        // 处理 LWIP 协议栈任务
        sys_check_timeouts();
        // 可以添加更多的应用程序代码,如处理 TCP 连接、接收数据等
    }
}


// 系统时钟配置
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLM = 16;
    RCC_OscInitStruct.PLL.PLLN = 336;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
    {
        while(1);
    }
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APCLK1Divider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APCLK2Divider = RCC_HCLK_DIV2;
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5)!= HAL_OK)
    {
        while(1);
    }
}


// GPIO 初始化
static void MX_GPIO_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 配置以太网引脚
    GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
}


// ETH 初始化
static void MX_ETH_Init(void)
{
    heth.Instance = ETH;
    heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
    heth.Init.Speed = ETH_SPEED_100M;
    heth.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
    heth.Init.PhyAddress = 0;
    heth.Init.MACAddr[0] = 0x00;
    heth.Init.MACAddr[1] = 0x80;
    heth.Init.MACAddr[2] = 0xE1;
    heth.Init.MACAddr[3] = 0x00;
    heth.Init.MACAddr[4] = 0x00;
    heth.Init.MACAddr[5] = 0x00;
    heth.Init.RxMode = ETH_RXPOLLING_MODE;
    heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
    heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
    if (HAL_ETH_Init(&heth)!= HAL_OK)
    {
        while(1);
    }
}


// 以太网中断服务程序
void ETH_IRQHandler(void)
{
    HAL_ETH_IRQHandler(&heth);
}


// 以太网接收中断回调函数
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
    ethernetif_input(&gnetif);
    // 重新启动接收中断
    HAL_ETH_Receive_IT(heth);
}


// 以太网接收函数
void ethernetif_input(struct netif *netif)
{
    struct pbuf *p;
    uint8_t *buffer;
    int len;

    // 从以太网外设接收数据
    len = HAL_ETH_GetReceivedFrame(&heth, &buffer);
    if (len > 0) {
        p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
        if (p!= NULL) {
            pbuf_take(p, buffer, len);
            if (etharp_input(netif, p) == ERR_OK) {
                // 数据传递给 LWIP 协议栈
                netif->input(p, netif);
            } else {
                pbuf_free(p);
            }
        }
        // 释放接收缓冲区
        HAL_ETH_ReleaseRxBuffer(&heth);
    }
}


// 以太网初始化函数
err_t ethernetif_init(struct netif *netif)
{
    netif->name[0] = 'e';
    netif->name[1] = '0';
    netif->output = etharp_output;
    netif->linkoutput = low_level_output;
    netif->mtu = 1500;
    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

    // 初始化以太网硬件
    if (low_level_init(netif) == ERR_OK) {
        // 启动接收中断
        HAL_ETH_Receive_IT(&heth);
        return ERR_OK;
    } else {
        return ERR_MEM;
    }
}


// 低级别发送函数
err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    // 将 pbuf 中的数据发送到以太网硬件
    HAL_ETH_Transmit(&heth, (uint8_t *)p->payload, p->len);
    return ERR_OK;
}


// 低级别初始化函数
err_t low_level_init(struct netif *netif)
{
    // 复位 PHY
    HAL_ETH_DeInit(&heth);
    // 重新初始化 ETH
    if (HAL_ETH_Init(&heth) == HAL_OK) {
        // 设置 MAC 地址
        netif->hwaddr[0] = heth.Init.MACAddr[0];
        netif->hwaddr[1] = heth.Init.MACAddr[1];
        netif->hwaddr[2] = heth.Init.MACAddr[2];
        netif->hwaddr[3] = heth.Init.MACAddr[3];
        netif->hwaddr[4] = heth.Init.MACAddr[4];
        netif->hwaddr[5] = heth.Init.MACAddr[5];
        return ERR_OK;
    } else {
        return ERR_MEM;
    }
}

代码解释

  • #include "stm32h563xx_hal.h":包含 STM32H563 的 HAL 库头文件。
  • #include "lwip/opt.h", #include "lwip/init.h" 等:包含 LWIP 的相关头文件。
  • ETH_HandleTypeDef heth;:定义以太网句柄,用于存储以太网的配置信息。
  • struct netif gnetif;:定义网络接口结构体。
  • SystemClock_Config():配置 STM32H563 的系统时钟。
  • MX_GPIO_Init():配置以太网所需的 GPIO 引脚,将它们设置为相应的复用功能。
  • MX_ETH_Init():初始化以太网外设,设置速度、双工模式、MAC 地址等。
  • ETH_IRQHandler():以太网中断服务程序,调用 HAL_ETH_IRQHandler 处理中断。
  • HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth):接收完成回调函数,调用 ethernetif_input 将数据传递给 LWIP 协议栈并重新启动接收中断。
  • ethernetif_input(struct netif *netif):从以太网外设接收数据并传递给 LWIP 协议栈。
  • ethernetif_init(struct netif *netif):初始化网络接口,设置网络接口的属性,并启动接收中断。
  • low_level_output(struct netif *netif, struct pbuf *p):将数据从 LWIP 协议栈发送到以太网硬件。
  • low_level_init(struct netif *netif):初始化以太网硬件,包括 PHY 的复位和 MAC 地址的设置。

五、总结

STM32H563 与 LWIP 的裸机移植需要仔细的配置和文件组织。通过上述步骤,我们可以将 LWIP 协议栈成功地移植到 STM32H563 上,实现基本的网络通信功能。在移植过程中,要注意 LWIP 参数的配置,确保其适应 STM32H563 的资源。同时,对于以太网硬件的配置和中断处理要准确无误,以保证数据的正常接收和发送。在实际应用中,可以进一步扩展该移植代码,实现更复杂的网络功能,如实现一个简单的 Web 服务器或一个 TCP 客户端,为 STM32H563 开发更强大的网络应用提供基础。

请记住,在调试过程中,需要使用网络调试工具,如 Wireshark 来检查网络数据包的发送和接收,确保网络通信正常。同时,根据实际情况调整 LWIP 的配置和 STM32H563 的硬件配置,以优化网络性能和系统资源利用。通过不断地优化和调试,你可以充分利用 STM32H563 的性能,实现高效可靠的网络通信。

以上内容提供了 STM32H563 HAL 库下 LWIP 裸机移植的基本信息和代码示例,希望对你的开发工作有所帮助,让你可以顺利实现 STM32H563 的网络通信功能。

✅作者简介:热爱科研的嵌入式开发者,修心和技术同步精进

❤欢迎关注我的知乎:对error视而不见

代码获取、问题探讨及文章转载可私信。

☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。

标签:LWIP,netif,STM32H563,裸机,Init,GPIO,ETH,heth,RCC
From: https://blog.csdn.net/duierrorshuobu/article/details/145169203

相关文章

  • STM32之LWIP网络通讯设计-下(十五)
    STM32F407系列文章-ETH-LWIP(十五)目录前言一、软件设计二、CubeMX实现1.配置前准备2.CubeMX配置1.ETH模块配置2.时钟模块配置3.中断模块配置4.RCC及SYS配置5.LWIP模块配置3.生成代码1.main文件2.用户层源文件3.用户层头文件4.效果演示三、移植实现总结......
  • STM32之LWIP网络通讯设计-上(十四)
    STM32F407系列文章-LWIP-Network(十四)目录前言一、以太网简介二、网络协议简介1.OSI模型2.TCP/IP协议3.协议层报文间的封装与拆封4.lwIP1.lwIP特性2.lwIP开源网址3.lwIP参考书籍三、通讯连接示意四、STM32内部ETH 1.内部MAC2.内部DMA五、PHY驱动芯片1.功......
  • stm32f407 cubemx lwip的简易服务器客户端收发项目
    元器件:野火stm32f407开发板技术:lwiptcp/ip内容:搭建的stm32服务器,可以接受和发送数据到客户端项目实现图片stm32通过串口发送数据客户端接受到数据客户端发送数据stm32收到数据**代码可以在我的主业进行下载**......
  • VMware ESXi 8.0U3c 发布 - 领先的裸机 Hypervisor
    VMwareESXi8.0U3c发布-领先的裸机Hypervisor同步发布Dell(戴尔)、HPE(慧与)、Lenovo(联想)、IEITSYSTEMS(浪潮信息)、Cisco(思科)、Fujitsu(富士通)、Hitachi(日立)、NEC(日电)、Huawei(华为)、xFusion(超聚变)OEM定制版请访问原文链接:https://sysin.org/bl......
  • VMware ESXi 7.0 U3r 发布 - 领先的裸机 Hypervisor
    VMwareESXi7.0U3r发布-领先的裸机HypervisorVMwareESXi7.0Update3Standard&AllCustomImageforESXi7.0U3InstallCD请访问原文链接:https://sysin.org/blog/vmware-esxi-7-u3/查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgVMwareESXi:专门构建......
  • lwIP 内存管理
    文章目录一、内存管理概述二、lwIP内存管理策略内存池(POOL)内存堆(HEAP)三、lwIP内存管理应用数据接收:发送数据:用户调用:接口控制块:四、lwIP内存管理原理内存池原理内存堆原理五、结论lwIP内存管理一、内存管理概述内存管理是指软件运行时对计算机内存资源的分配......
  • 单片机裸机常用的时间片轮训系统
            我学单片机初期的时候是没有接触过什么操作系统的,后续接触过FreeRTOS,了解过一下时间片轮训和抢占式。但是工作发现许多工控行业的产品用不到移植操作系统。        那我学习初期遇到一种问题就很棘手,比如我想在ADC采集温度到88度时,打开一个继电器,延时......
  • VMware ESXi 8.0d 发布下载 - 领先的裸机 Hypervisor
    VMwareESXi8.0d发布下载-领先的裸机HypervisorESXi8.0标准版,Dell(戴尔)、HPE(慧与)、Lenovo(联想)、Inspur(浪潮)OEM定制版请访问原文链接:https://sysin.org/blog/vmware-esxi-8/查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgVMwareESXi:专门构建......
  • KubeCon China 回顾|快手的 100% 资源利用率提升:从裸机迁移大规模 Redis 到 Kubernetes
    大家下午好,我是来自ApeCloud的吴学强,非常高兴能够在KubeCon做分享。今天的分享由我和来自快手的刘裕惺同学共同完成,我们分享的主题是将大规模的Redis实例从裸机迁移到Kubernetes上来提高资源的利用率。我们今天的议题包括几个方面,首先我会来简单介绍一下KubeBlock......
  • 【ZYNQ MPSoC开发】lwIP TCP发送用于数据缓存的软件FIFO设计
    设计背景    任务是在ZYNQ的PS上使用裸机运行lwIP协议栈使用TCP把PL端通过AXIDMA传来的将近100K采样率的ADC数据发送出去,但由于数据带宽很大,有853.3mbps,所以在每一次AXIDMA简单传输结束后,lwIP未必有足够的发送buffer立即把数据发送走,如果是发送完再进行下一次简单......