一、STM32H563 与 LWIP 简介
STM32H563 是 STMicroelectronics 推出的一款高性能 32 位微控制器,具有丰富的外设和强大的处理能力,适用于各种复杂的嵌入式系统应用。LWIP(Lightweight IP)是一个小型开源的 TCP/IP 协议栈,旨在为嵌入式系统提供轻量级的网络功能,它能够在资源有限的系统中实现网络通信,如实现以太网通信、HTTP 服务器、UDP 和 TCP 连接等功能。
二、LWIP 裸机移植的准备工作
-
获取 LWIP 源代码:
- 首先,从 LWIP 的官方网站下载最新的 LWIP 源代码。你可以选择合适的版本,通常需要根据 STM32H563 的资源和项目需求来选择。
- 将 LWIP 源代码添加到你的 STM32 工程目录中,一般可以将其放在一个单独的文件夹中,例如
LWIP
。
-
配置 LWIP:
- LWIP 提供了一个
lwipopts.h
文件,用于配置协议栈的各种参数,如内存池大小、TCP 窗口大小、是否支持 IPv6 等。根据 STM32H563 的可用资源和应用需求,对这些参数进行调整。 - 例如,如果 STM32H563 内存有限,可以适当减小内存池的大小,但要确保不会导致内存不足而影响网络性能。
- LWIP 提供了一个
-
STM32H563 硬件配置:
- 确保 STM32H563 的以太网外设已正确配置。使用 HAL 库的以太网相关函数,配置以太网的 PHY 和 MAC 层。
- 配置相应的 GPIO 引脚,将以太网的 RMII 或 MII 接口引脚配置为正确的复用功能。
三、LWIP 移植步骤
-
添加必要的文件:
- 将 LWIP 的核心文件(如
core
文件夹下的源文件)添加到工程中,包括ipv4
、ipv6
、tcp
、udp
等模块的源文件。 - 同时,添加
netif
文件夹下的文件,用于网络接口的实现。 - 对于 STM32H563,还需要添加
arch
文件夹下的文件,该文件夹包含了与 STM32 架构相关的适配文件。
- 将 LWIP 的核心文件(如
-
修改
sys_arch.c
文件:- 在
sys_arch.c
文件中,需要实现与操作系统相关的函数。对于裸机移植,需要实现一些简单的任务管理函数,如sys_mbox_new
、sys_mbox_free
等,这些函数在 LWIP 中用于消息传递和任务同步。 - 可以使用简单的邮箱或队列机制,使用全局变量和标志位来实现这些功能,而不是依赖于操作系统的任务调度。
- 在
-
实现网络接口函数:
- 在
ethernetif.c
文件中,实现low_level_init
、low_level_output
、low_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, ðernetif_init, ðernetif_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