Files
ETH_TCP_Demo/lwip/port/drv_eth.c

449 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "drv_eth.h"
#include "lwip/opt.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/timeouts.h"
#include "netif/etharp.h"
#include "lwip/ethip6.h"
#include "osal.h"
#include <string.h>
/* Ethernet Handle */
ETH_HandleTypeDef heth;
static uint32_t g_phy_address = 0; /* Stored PHY Address */
static uint32_t g_last_link_check_time = 0; /* 上次链接状态检测时间 */
static uint32_t g_link_check_interval = 1000; /* 链接状态检测间隔 (ms) */
static uint8_t g_link_state_stable = 0; /* 链接状态稳定性标志 */
static uint8_t g_link_state_counter = 0; /* 链接状态计数器,用于防抖 */
/* DMA Descriptors and Buffers */
/* 优化缓冲区对齐方式,提高内存访问效率 */
__attribute__((section(".RxDecripSection"))) __attribute__((aligned(32))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB];
__attribute__((section(".TxDecripSection"))) __attribute__((aligned(32))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB];
__attribute__((section(".RxArraySection"))) __attribute__((aligned(32))) uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
__attribute__((section(".TxArraySection"))) __attribute__((aligned(32))) uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
/* 缓冲区管理优化:可以根据实际网络流量调整缓冲区大小和数量 */
/* 建议值:
* - 高流量场景ETH_RXBUFNB = 8, ETH_TXBUFNB = 8
* - 低内存场景ETH_RXBUFNB = 2, ETH_TXBUFNB = 2
*/
/* Semaphore for Ethernet */
static osal_sem_t s_xSemaphore = NULL;
/* 函数声明 */
static HAL_StatusTypeDef detect_and_configure_phy(void);
static void configure_mac(ETH_MACConfigTypeDef *macConf);
/* MSP Init - Implemented in board.c */
// void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
// {
// (void)heth;
// GPIO_InitTypeDef GPIO_InitStructure;
// __HAL_RCC_GPIOA_CLK_ENABLE();
// __HAL_RCC_GPIOB_CLK_ENABLE();
// __HAL_RCC_GPIOC_CLK_ENABLE();
// __HAL_RCC_SYSCFG_CLK_ENABLE();
// __HAL_RCC_ETH_CLK_ENABLE();
// /* PA1, PA2, PA7 */
// GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
// GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
// GPIO_InitStructure.Pull = GPIO_NOPULL;
// GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
// GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
// HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
// /* PC1, PC4, PC5 */
// GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
// HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
// /* PB11, PB12, PB13 */
// GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
// HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
//
// HAL_NVIC_SetPriority(ETH_IRQn, 0x07, 0);
// HAL_NVIC_EnableIRQ(ETH_IRQn);
// }
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
(void)heth;
if (s_xSemaphore != NULL)
{
osal_sem_release(s_xSemaphore);
}
}
void HAL_ETH_RxAllocateCallback(uint8_t **buff)
{
static int rx_idx = 0;
*buff = Rx_Buff[rx_idx];
rx_idx = (rx_idx + 1) % ETH_RXBUFNB;
}
void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff, uint16_t Length)
{
(void)Length;
*pStart = (void *)buff;
*pEnd = (void *)buff;
}
void ETH_IRQHandler(void)
{
/* 直接调用 HAL 中断处理函数,不使用全局临界区
* HAL_ETH_IRQHandler 内部已经有适当的中断保护机制
*/
HAL_ETH_IRQHandler(&heth);
}
/**
* @brief 以太网硬件初始化函数
* @param netif: 网络接口结构体指针
* @return 无
* @note 负责初始化以太网硬件包括MAC、DMA、PHY等
*/
static void low_level_init(struct netif *netif)
{
/* Use a fixed MAC address to avoid conflicts/filtering */
uint8_t macaddress[6] = { 0x00, 0x80, 0xE1, 0x00, 0x00, 0x55 };
ETH_MACConfigTypeDef macConf;
HAL_StatusTypeDef hal_eth_init_status;
/* Generate MAC address from UID */
/*
uint32_t uid0 = HAL_GetUIDw0();
uint32_t uid1 = HAL_GetUIDw1();
uint32_t uid2 = HAL_GetUIDw2();
macaddress[0] = 0x02;
macaddress[1] = 0x80;
macaddress[2] = 0xE1;
macaddress[3] = (uid0 >> 16) & 0xFF;
macaddress[4] = (uid1 >> 8) & 0xFF;
macaddress[5] = uid2 & 0xFF;
*/
osal_log_i("MAC: %02x:%02x:%02x:%02x:%02x:%02x",
macaddress[0], macaddress[1], macaddress[2],
macaddress[3], macaddress[4], macaddress[5]);
/* 初始化 ETH 句柄 */
heth.Instance = ETH;
heth.Init.MACAddr = macaddress;
heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
heth.Init.TxDesc = DMATxDscrTab;
heth.Init.RxDesc = DMARxDscrTab;
heth.Init.RxBuffLen = ETH_RX_BUF_SIZE;
/* Initialize ETH (MAC, DMA, GPIOs via MSP) first to enable MDC/MDIO */
hal_eth_init_status = HAL_ETH_Init(&heth);
if (hal_eth_init_status == HAL_OK)
{
/* 检测并配置 PHY */
if (detect_and_configure_phy() == HAL_OK)
{
netif->flags |= NETIF_FLAG_LINK_UP;
}
}
else
{
osal_log_e("HAL_ETH_Init failed");
}
/* 配置 MAC */
configure_mac(&macConf);
/* Enable Promiscuous Mode manually as it's not in the struct */
heth.Instance->MACFFR |= ETH_MACFFR_PM;
/* 启动 ETH 中断 */
HAL_ETH_Start_IT(&heth);
/* 创建信号量 */
s_xSemaphore = osal_sem_create("eth_sem", 0);
/* 配置网络接口 */
netif->hwaddr_len = 6;
memcpy(netif->hwaddr, macaddress, 6);
netif->mtu = 1500;
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#if LWIP_IPV6
netif->flags |= NETIF_FLAG_IGMP;
#endif
}
/**
* @brief 检测并配置 PHY
* @return HAL_StatusTypeDef: 操作结果
* @note 负责检测 PHY 地址并进行配置
*/
static HAL_StatusTypeDef detect_and_configure_phy(void)
{
uint32_t phy_id1 = 0, phy_id2 = 0;
uint8_t detected_phy_addr = 0xFF; /* Invalid initial value */
uint32_t regvalue;
/* Step 1: PHY address detection */
osal_log_i("Detecting PHY Address...");
/* 优先检查常见的 PHY 地址,减少遍历次数 */
uint8_t common_phy_addresses[] = {0, 1, 2, 3, 16, 17, 18, 19, 20};
uint8_t common_phy_count = sizeof(common_phy_addresses) / sizeof(common_phy_addresses[0]);
/* 先检查常见地址 */
for(uint8_t i = 0; i < common_phy_count; i++) {
uint8_t addr = common_phy_addresses[i];
/* Read PHY ID registers (typically Reg 2 and 3) */
if(HAL_ETH_ReadPHYRegister(&heth, addr, 2, &phy_id1) == HAL_OK &&
HAL_ETH_ReadPHYRegister(&heth, addr, 3, &phy_id2) == HAL_OK) {
if((phy_id1 != 0xFFFF) && (phy_id1 != 0x0000) && (phy_id1 != 0)) {
detected_phy_addr = addr;
osal_log_i("Found PHY at Address %d (ID: %04x %04x)", addr, phy_id1, phy_id2);
goto phy_found;
}
}
}
/* 如果常见地址没找到,再遍历所有可能的地址 */
for(uint8_t addr = 0; addr <= 31; addr++) {
/* 跳过已经检查过的常见地址 */
uint8_t skip = 0;
for(uint8_t i = 0; i < common_phy_count; i++) {
if(addr == common_phy_addresses[i]) {
skip = 1;
break;
}
}
if(skip) continue;
/* Read PHY ID registers (typically Reg 2 and 3) */
if(HAL_ETH_ReadPHYRegister(&heth, addr, 2, &phy_id1) == HAL_OK &&
HAL_ETH_ReadPHYRegister(&heth, addr, 3, &phy_id2) == HAL_OK) {
if((phy_id1 != 0xFFFF) && (phy_id1 != 0x0000) && (phy_id1 != 0)) {
detected_phy_addr = addr;
osal_log_i("Found PHY at Address %d (ID: %04x %04x)", addr, phy_id1, phy_id2);
goto phy_found;
}
}
}
phy_found:
if (detected_phy_addr != 0xFF)
{
g_phy_address = detected_phy_addr;
/* Step 2: PHY Soft Reset */
osal_log_i("Resetting PHY...");
/* Write Reset Bit */
HAL_ETH_WritePHYRegister(&heth, g_phy_address, PHY_BCR, PHY_RESET);
/* Wait for Reset to clear */
uint32_t tickstart = osal_tick_get();
uint32_t reset_timeout = 200; // 优化为 200ms 超时
do {
HAL_ETH_ReadPHYRegister(&heth, g_phy_address, PHY_BCR, &regvalue);
if((regvalue & PHY_RESET) == 0) break;
} while ((osal_tick_get() - tickstart) < reset_timeout); // 200ms timeout
/* Add a delay to ensure PHY is stable */
osal_thread_mdelay(50); // 优化为 50ms 延迟
return HAL_OK;
}
else
{
osal_log_e("No PHY found!");
return HAL_ERROR;
}
}
/**
* @brief 配置 MAC
* @param macConf: MAC 配置结构体指针
* @return 无
* @note 负责配置 MAC 的双工模式、速度和校验和等
*/
static void configure_mac(ETH_MACConfigTypeDef *macConf)
{
HAL_ETH_GetMACConfig(&heth, macConf);
macConf->DuplexMode = ETH_FULLDUPLEX_MODE;
macConf->Speed = ETH_SPEED_100M;
macConf->ChecksumOffload = ENABLE; /* Enable HW Checksum */
HAL_ETH_SetMACConfig(&heth, macConf);
}
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
(void)netif;
err_t errval;
struct pbuf *q;
ETH_TxPacketConfigTypeDef txConfig;
ETH_BufferTypeDef txBuffers[16];
memset(&txConfig, 0, sizeof(ETH_TxPacketConfigTypeDef));
/* Enable Hardware Checksum Insertion */
txConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
txConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
txConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;
int i = 0;
for(q = p; q != NULL; q = q->next)
{
if (i >= 16) break;
txBuffers[i].buffer = (uint8_t*)q->payload;
txBuffers[i].len = q->len;
if (q->next != NULL) {
txBuffers[i].next = &txBuffers[i+1];
} else {
txBuffers[i].next = NULL;
}
i++;
}
txConfig.Length = p->tot_len;
txConfig.TxBuffer = &txBuffers[0];
if (HAL_ETH_Transmit(&heth, &txConfig, 10) == HAL_OK) {
errval = ERR_OK;
} else {
errval = ERR_IF;
}
return errval;
}
static struct pbuf * low_level_input(struct netif *netif)
{
(void)netif;
struct pbuf *p = NULL;
uint8_t *buffer = NULL;
uint32_t rxLength = 0;
if (HAL_ETH_ReadData(&heth, (void**)&buffer) == HAL_OK)
{
rxLength = heth.RxDescList.RxDataLength;
// osal_log_i("Rx: rxLength=%d", rxLength);
if (rxLength > 0 && buffer != NULL) {
/* 尝试使用 PBUF_REF 模式创建 pbuf实现零拷贝
* 注意:需要确保缓冲区在 pbuf 使用期间有效
*/
p = pbuf_alloc(PBUF_RAW, rxLength, PBUF_REF);
if (p != NULL) {
/* 直接设置 pbuf 的 payload 指针,避免数据拷贝 */
p->payload = buffer;
p->len = rxLength;
p->tot_len = rxLength;
// osal_log_i("Rx: p->tot_len=%d", p->tot_len);
} else {
/* 如果 PBUF_REF 失败,回退到传统方式 */
p = pbuf_alloc(PBUF_RAW, rxLength, PBUF_POOL);
if (p != NULL) {
pbuf_take(p, buffer, rxLength);
} else {
osal_log_e("pbuf_alloc failed");
}
}
/* HAL_ETH_ReadData internally calls ETH_UpdateDescriptor, so descriptors are rebuilt automatically */
}
} else {
/* ReadData failed, maybe no data available */
}
return p;
}
void ethernetif_input(struct netif *netif)
{
struct pbuf *p;
if (s_xSemaphore == NULL) {
osal_thread_mdelay(100);
return;
}
if (osal_sem_take(s_xSemaphore, 100) == OSAL_OK)
{
do {
p = low_level_input(netif);
if (p != NULL)
{
// osal_log_i("Rx: len=%d", p->tot_len); // Debug print
if (netif->input(p, netif) != ERR_OK)
{
pbuf_free(p);
}
}
} while(p != NULL);
}
}
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
netif->hostname = "lwip";
#endif
netif->name[0] = 'e';
netif->name[1] = 't';
netif->output = etharp_output;
netif->linkoutput = low_level_output;
low_level_init(netif);
return ERR_OK;
}
void ethernet_link_check_state(struct netif *netif)
{
uint32_t regvalue = 0;
uint32_t current_time = osal_tick_get();
/* 实现时间间隔控制,减少 PHY 寄存器读取 */
if ((current_time - g_last_link_check_time) < g_link_check_interval)
{
return;
}
g_last_link_check_time = current_time;
/* Use configured PHY Address */
HAL_ETH_ReadPHYRegister(&heth, g_phy_address, PHY_BSR, &regvalue);
if ((regvalue & PHY_LINKED_STATUS) != (uint16_t)RESET)
{
/* 链接状态防抖 */
g_link_state_counter++;
if (g_link_state_counter >= 3) /* 连续 3 次检测到链接状态为 up */
{
if (!netif_is_link_up(netif))
{
netif_set_link_up(netif);
osal_log_i("Ethernet Link Up");
}
g_link_state_stable = 1;
}
}
else
{
/* 链接状态防抖 */
g_link_state_counter--;
if (g_link_state_counter <= 0) /* 连续 3 次检测到链接状态为 down */
{
g_link_state_counter = 0;
if (netif_is_link_up(netif))
{
netif_set_link_down(netif);
osal_log_i("Ethernet Link Down");
}
g_link_state_stable = 0;
}
}
}