/* * STM32F4以太网硬件抽象层实现 * 功能:实现STM32F4平台的以太网硬件抽象层 * 依赖硬件:STM32F407VE * 跨平台适配点:通过硬件抽象层接口适配不同平台 */ #include "hal.h" #include "stm32f4xx_hal.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 /* 以太网句柄 */ static ETH_HandleTypeDef heth; static uint32_t g_phy_address = 0; /* 存储PHY地址 */ 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描述符和缓冲区 */ /* 优化缓冲区对齐方式,提高内存访问效率 */ __attribute__((section(".RxDecripSection"))) __attribute__((aligned(32))) static ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB]; __attribute__((section(".TxDecripSection"))) __attribute__((aligned(32))) static ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB]; __attribute__((section(".RxArraySection"))) __attribute__((aligned(32))) static uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; __attribute__((section(".TxArraySection"))) __attribute__((aligned(32))) static uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* 信号量 */ static osal_sem_t s_xSemaphore = NULL; /** * @brief 检测并配置PHY * @return HAL_StatusTypeDef: 操作结果 * @note 负责检测PHY地址并进行配置 */ static hal_status_t detect_and_configure_phy(void) { uint32_t phy_id1 = 0, phy_id2 = 0; uint8_t detected_phy_addr = 0xFF; /* 无效的初始值 */ uint32_t regvalue; /* 优先检查常见的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]; /* 读取PHY ID寄存器(通常是Reg 2和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; /* 读取PHY ID寄存器(通常是Reg 2和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; /* PHY软复位 */ osal_log_i("Resetting PHY at address %d...", g_phy_address); /* 写入复位位 */ HAL_ETH_WritePHYRegister(&heth, g_phy_address, PHY_BCR, PHY_RESET); /* 等待复位完成 */ 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超时 /* 添加延迟以确保PHY稳定 */ osal_thread_mdelay(50); // 优化为50ms延迟 return HAL_STATUS_OK; } else { osal_log_e("No PHY found!"); return HAL_STATUS_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; /* 启用硬件校验和 */ HAL_ETH_SetMACConfig(&heth, macConf); } /** * @brief 以太网初始化 * @return hal_status_t: 操作结果 * @note 初始化以太网硬件 */ static hal_status_t stm32f4_eth_init(void) { /* 使用固定MAC地址避免冲突/过滤 */ uint8_t macaddress[6] = { 0x00, 0x80, 0xE1, 0x00, 0x00, 0x55 }; ETH_MACConfigTypeDef macConf; HAL_StatusTypeDef hal_eth_init_status; 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; /* 初始化ETH(MAC、DMA、GPIO通过MSP)以启用MDC/MDIO */ hal_eth_init_status = HAL_ETH_Init(&heth); if (hal_eth_init_status == HAL_OK) { /* 检测并配置PHY */ if (detect_and_configure_phy() == HAL_STATUS_OK) { /* 配置MAC */ configure_mac(&macConf); /* 手动启用混杂模式,因为它不在结构体中 */ heth.Instance->MACFFR |= ETH_MACFFR_PM; /* 启动ETH中断 */ HAL_ETH_Start_IT(&heth); /* 创建信号量 */ s_xSemaphore = osal_sem_create("eth_sem", 0); return HAL_STATUS_OK; } } else { osal_log_e("HAL_ETH_Init failed"); } return HAL_STATUS_ERROR; } /** * @brief 以太网反初始化 * @return hal_status_t: 操作结果 * @note 反初始化以太网硬件 */ static hal_status_t stm32f4_eth_deinit(void) { /* 停止ETH */ HAL_ETH_Stop(&heth); /* 反初始化ETH */ if (HAL_ETH_DeInit(&heth) != HAL_OK) { return HAL_STATUS_ERROR; } /* 删除信号量 */ if (s_xSemaphore != NULL) { osal_sem_delete(s_xSemaphore); s_xSemaphore = NULL; } return HAL_STATUS_OK; } /** * @brief 以太网发送数据 * @param data: 数据指针 * @param len: 数据长度 * @return hal_status_t: 操作结果 * @note 发送数据 */ static hal_status_t stm32f4_eth_send(const uint8_t *data, uint32_t len) { ETH_TxPacketConfigTypeDef txConfig; ETH_BufferTypeDef txBuffer; memset(&txConfig, 0, sizeof(ETH_TxPacketConfigTypeDef)); /* 启用硬件校验和插入 */ 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; txBuffer.buffer = (uint8_t*)data; txBuffer.len = len; txBuffer.next = NULL; txConfig.Length = len; txConfig.TxBuffer = &txBuffer; if (HAL_ETH_Transmit(&heth, &txConfig, 10) == HAL_OK) { return HAL_STATUS_OK; } else { return HAL_STATUS_ERROR; } } /** * @brief 以太网接收数据 * @param data: 数据指针 * @param len: 数据长度指针 * @param timeout: 超时时间 * @return hal_status_t: 操作结果 * @note 接收数据 */ static hal_status_t stm32f4_eth_recv(uint8_t *data, uint32_t *len, uint32_t timeout) { uint8_t *buffer = NULL; uint32_t rxLength = 0; if (s_xSemaphore == NULL) { return HAL_STATUS_ERROR; } if (osal_sem_take(s_xSemaphore, timeout) != OSAL_OK) { return HAL_STATUS_TIMEOUT; } if (HAL_ETH_ReadData(&heth, (void**)&buffer) == HAL_OK) { /* Get the received frame length from Rx descriptor list */ rxLength = heth.RxDescList.RxDataLength; if (rxLength > 0 && buffer != NULL && data != NULL && len != NULL) { memcpy(data, buffer, rxLength); *len = rxLength; return HAL_STATUS_OK; } } return HAL_STATUS_ERROR; } /** * @brief 获取以太网链接状态 * @return hal_status_t: 操作结果 * @note 获取以太网链接状态 */ static hal_status_t stm32f4_eth_get_link_status(void) { 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_link_state_stable ? HAL_STATUS_OK : HAL_STATUS_ERROR; } g_last_link_check_time = current_time; /* 检查PHY地址是否有效 */ if (g_phy_address == 0 || g_phy_address > 31) { osal_log_e("Invalid PHY address: %d", g_phy_address); g_link_state_stable = 0; g_link_state_counter = 0; return HAL_STATUS_ERROR; } /* 使用配置的PHY地址读取状态寄存器 */ HAL_StatusTypeDef status = HAL_ETH_ReadPHYRegister(&heth, g_phy_address, PHY_BSR, ®value); if (status != HAL_OK) { osal_log_e("Failed to read PHY register, status=%d", status); g_link_state_counter--; if (g_link_state_counter <= 0) { g_link_state_counter = 0; g_link_state_stable = 0; } return HAL_STATUS_ERROR; } osal_log_i("PHY BSR=0x%04x, LinkStatus=%d", regvalue, (regvalue & PHY_LINKED_STATUS) != 0); if ((regvalue & PHY_LINKED_STATUS) != 0) { /* 链接状态防抖 */ g_link_state_counter++; if (g_link_state_counter >= 3) /* 连续3次检测到链接状态为up */ { g_link_state_stable = 1; } return HAL_STATUS_OK; } else { /* 链接状态防抖 */ g_link_state_counter--; if (g_link_state_counter <= 0) /* 连续3次检测到链接状态为down */ { g_link_state_counter = 0; g_link_state_stable = 0; } return HAL_STATUS_ERROR; } } /* 以太网硬件抽象层操作结构体 */ const hal_eth_ops_t stm32f4_eth_ops = { .init = stm32f4_eth_init, .deinit = stm32f4_eth_deinit, .send = stm32f4_eth_send, .recv = stm32f4_eth_recv, .get_link_status = stm32f4_eth_get_link_status }; /** * @brief 以太网Rx完成回调 * @param heth: 以太网句柄 * @return 无 * @note 以太网接收完成回调函数 */ void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) { (void)heth; if (s_xSemaphore != NULL) { osal_sem_release(s_xSemaphore); } } /** * @brief 以太网Rx分配回调 * @param buff: 缓冲区指针 * @return 无 * @note 以太网接收缓冲区分配回调函数 */ void HAL_ETH_RxAllocateCallback(uint8_t **buff) { static int rx_idx = 0; *buff = Rx_Buff[rx_idx]; rx_idx = (rx_idx + 1) % ETH_RXBUFNB; } /** * @brief 以太网Rx链接回调 * @param pStart: 开始指针 * @param pEnd: 结束指针 * @param buff: 缓冲区指针 * @param Length: 长度 * @return 无 * @note 以太网接收链接回调函数 */ void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff, uint16_t Length) { (void)Length; *pStart = (void *)buff; *pEnd = (void *)buff; } /** * @brief 以太网中断处理函数 * @return 无 * @note 以太网中断处理函数 */ void ETH_IRQHandler(void) { /* 直接调用HAL中断处理函数,不使用全局临界区 * HAL_ETH_IRQHandler内部已经有适当的中断保护机制 */ HAL_ETH_IRQHandler(&heth); } /** * @brief 以太网MSP初始化 * @param heth: 以太网句柄 * @return 无 * @note 初始化以太网的MSP */ 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); }