STM32F107单片机驱动Dp83848以太网芯片程序 项目开发用到了Dp83848这一个以太网芯片,本人发现其配置起来比较麻烦,所以整理了一份STM32F107单片机驱动Dp83848的程序代码例程,方便大家学习相关代码的配置

最近在项目里折腾STM32F107和DP83848这对搭档,这PHY芯片配置起来真是让人头大。特别是当硬件同事把原理图甩过来的时候,看到那一堆MDIO/MDC信号线就预感到要掉头发。好在最后摸清了套路,这里把关键代码和踩坑经验分享给大家。

先说硬件接法,DP83848的PHYAD0引脚决定了PHY地址。咱们板子上这个引脚接地,所以PHY地址是0x00。这个地址后面配置MAC的时候要特别注意,很多兄弟在这里栽跟头。

上电第一步得先伺候好时钟。STM32的AHB总线时钟要开启,别忘了MAC用的DMA时钟:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC | RCC_AHBPeriph_ETH_MAC_Tx | 
                     RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);

接下来是GPIO配置。以RMII接口为例,注意CR寄存器要配置成50MHz模式:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12; // RMII_TXD0/TXD1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);

PHY初始化这块最磨人。建议先用软件复位打个招呼:

uint16_t phy_status;
ETH_WritePHYRegister(DP83848_PHY_ADDRESS, PHY_REG_BMCR, PHY_Reset);
do {
    phy_status = ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_REG_BMCR);
} while (phy_status & PHY_Reset); // 等复位完成

然后开启自动协商,这里有个坑——DP83848的自动协商完成标志位有点迟钝,实测要加个延时:

ETH_WritePHYRegister(DP83848_PHY_ADDRESS, PHY_REG_BMCR, PHY_AutoNegotiation);
HAL_Delay(1500); // 不睡够时间直接读状态会扑街

do {
    phy_status = ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_REG_BMSR);
} while (!(phy_status & PHY_AutoNego_Complete));

链路状态检测建议用中断方式。配置PHY的中断引脚接到STM32的某个EXTI:

// 开启链接状态变化中断
ETH_WritePHYRegister(DP83848_PHY_ADDRESS, PHY_REG_MISR, PHY_Link_Status_Interrupt);

// 配置EXTI中断回调
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if(GPIO_Pin == PHY_INT_PIN) {
        uint16_t status = ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_REG_MISR);
        if(status & PHY_Link_Status_Interrupt) {
            // 处理链接状态变化
            handle_network_event();
        }
    }
}

MAC层的DMA描述符配置是个精细活,这里给个发送描述符的初始化示例:

ETH_DMADESCTypeDef *dma_tx_desc;
dma_tx_desc = ETH_GetDMATxDesc();
for(int i=0; i<TX_DESC_COUNT; i++) {
    dma_tx_desc->Status = ETH_DMATXDESC_OWN; // 告诉DMA可以接管
    dma_tx_desc->Buffer1Addr = (uint32_t)&tx_buffer[i][0];
    if(i == TX_DESC_COUNT-1) {
        dma_tx_desc->Status |= ETH_DMATXDESC_RER; // 环回配置
    }
    dma_tx_desc = (ETH_DMADESCTypeDef*)(dma_tx_desc->Buffer2NextDescAddr);
}

最后上主程序骨架。建议先做个链路检测再启动协议栈:

int main(void) {
    hardware_init();
    phy_init();
    
    while(1) {
        if(ETH_CheckLink() == ETH_LINK_UP) {
            // 亮个绿灯表示联网成功
            LED_Set(GREEN_LED, ON);
            
            // 这里跑LwIP的定时检查
            ethernetif_input(&gnetif);
        } else {
            LED_Toggle(RED_LED);
            HAL_Delay(500);
        }
    }
}

调试时最实用的技巧:用示波器抓MDIO波形。曾经遇到读取PHYID始终为0xFFFF的问题,最后发现是MAC的MDC时钟分频系数不对。STM32的ETHMACMIIAR寄存器中,CR位需要根据系统时钟频率配置,72MHz主频时建议设为0x1C(即28分频)。

完整工程里还有个关键点——中断优先级配置。接收中断要设为最高优先级,不然在高流量时容易丢包。这里给出NVIC配置参考:

HAL_NVIC_SetPriority(ETH_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);

最后提醒各位,DP83848的硬件复位信号至少要保持10ms低电平。曾经有兄弟偷懒用RC电路做复位,结果每次上电初始化都失败,改成MCU控制复位脚后才正常。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐