Files
ETH_TCP_Demo/drivers/hal/stm32f4_hal_eth.c
2026-03-09 15:34:18 +08:00

444 lines
13 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.

/*
* 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 <string.h>
/* 以太网句柄 */
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, &regvalue);
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;
/* 初始化ETHMAC、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, &regvalue);
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);
}