449 lines
14 KiB
C
449 lines
14 KiB
C
#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, ®value);
|
||
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, ®value);
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|