说在开头
正点原子F429开发板主芯片采用的是STM32F429IGT6,网络PHY芯片采用的是LAN8720A(V1)和YT8512C(V2),采用的是RMII连接,PHY_ADDR为0;在代码中将会对不同的芯片做出适配。
CubeMX版本:6.6.1;
F4芯片组Pack包版本:STM32Cube FW_F4 V1.27.0;
1、CubeMX配置过程
- 打开System Core点击RCC使能外部高速时钟(25M晶振)和低速设置时钟(32.768K晶振);
- 打开RTC:点击Timers------>RTC,勾选"Active ClockSource",其余参数保持默认
- 打开Clock Configuration设置系统时钟树:系统时钟拉满,设为180M(记得时钟树需要手动设置PLL来源是HSE、sSystem Clock来源是HSE经过PLL分频后的PLLCLK);将RTC的时钟源设为LSE(外部32.768K晶振);
- 设置LED引脚,PB0和PB1都为GPIO_Output模式,默认输出低电平;
- 使能串口1:点击Connectivity------>USART1,模式设置为Asynchronous,其余参数默认;
- 使能I2C2:点击Connectivity------>I2C2,模式设置为I2C2,在下面的"Parameter Settings"将Primary slave address的值设为64(设置FC8574的地址是64),之后将I2C2的GPIO设置为PH4和PH5;
- 使能ETH:点击Connectivity------>ETH,设置为RMII模式,在下面的"Parameter Settings"将Primary slave address的值设为1536;在旁边的"NVIC Settings"勾选"ETH global interrupt";将ETH_TXD0和ETH_TXD1的引脚设为PG13和PG14;
- 使能LWIP:点击Middleware------>LWIP,勾选Enable,在下面的"Platform Settings"将Driver PHY设为LAN8742,将BSP_COMPONENT_DRIVER也设为LAN8742;在下面的"Key Options"中找到"Network Interfaces Options"类,展开它的选项,将"LWIP_NETIF_HOSTNAME"设为Enable;
- 进入Project Manager页面设置工程设置:工程名和保存地址自定义,设置IDE为MDK;然后进入Code Generator选项,先勾选只添加必要的库文件到工程、然后勾选"Generate peripheral initialization as a pair of ".c/h' fles per periphera",其含义是生成的外设初始化代码是否要拆分成.c和.h文件;
- 最后点击右上角的"GENENATE CODE"即可生成代码;
2、MDK代码修改
-
修改MDK设置;
-
-
main.h添加头文件和宏定义;
cpp#include "stdio.h" #include "string.h" #define LAN8720A 0 #define YT8512C 1 extern uint8_t PHY_TYPE; #define YT8512C_PHYSCSR ((uint16_t)0x0011U) #define YT8512C_PHYSCSR_AUTONEGO_DONE ((uint16_t)0x0800U) #define YT8512C_PHYSCSR_HCDSPEEDMASK ((uint16_t)0xE000U) #define YT8512C_PHYSCSR_10BT_HD ((uint16_t)0x0000U) #define YT8512C_PHYSCSR_10BT_FD ((uint16_t)0x2000U) #define YT8512C_PHYSCSR_100BTX_HD ((uint16_t)0x4000U) #define YT8512C_PHYSCSR_100BTX_FD ((uint16_t)0x6000U) #define YT8512C_PHYSCSR_1000BTX_HD ((uint16_t)0x8000U) #define YT8512C_PHYSCSR_1000BTX_FD ((uint16_t)0xA000U)
-
usart.c添加串口重定向;
cppint fputc(int ch, FILE *f) { //具体哪个串口可以更改huart1为其它串口 HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f); return ch; }
-
stm32f4xx_it.c添加程序运行指示灯LED;
cpp//stm32f4xx_it.c开头添加:static uint16_t led_count = 0; //SysTick_Handler函数中" HAL_IncTick();"后添加以下代码: led_count++; if(led_count >= 250) { led_count = 0; HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);//LED0翻转 }
-
i2c.c增加PCF8574驱动代码;
cpp//i2c.c添加: void PCF8574_WriteBit(uint8_t IO_Num,uint8_t IO_Sta) { uint8_t IO_Sta_Get[2]={0}; HAL_I2C_Master_Receive(&hi2c2, 0x40, IO_Sta_Get, 1, 1000); if(IO_Sta==0)//清零 { IO_Sta_Get[0]=IO_Sta_Get[0]&(~(1<<IO_Num)); }else{ IO_Sta_Get[0]=IO_Sta_Get[0]|(1<<IO_Num); } HAL_I2C_Master_Transmit(&hi2c2, 0x40, IO_Sta_Get, 1, 1000); } uint8_t PCF8574_ReadBit(uint8_t IO_Num) { uint8_t IO_Sta_Get[2]={0}; HAL_I2C_Master_Receive(&hi2c2, 0x40, IO_Sta_Get, 1, 1000); return IO_Sta_Get[0]; } //i2c.h添加: void PCF8574_WriteBit(uint8_t IO_Num,uint8_t IO_Sta); uint8_t PCF8574_ReadBit(uint8_t IO_Num);
-
lan8742.c增加PHY芯片适配代码;
cpp//开头增加头文件: #include "main.h" //修改LAN8742_GetLinkState函数为如下内容: int32_t LAN8742_GetLinkState(lan8742_Object_t *pObj) { uint32_t readval = 0; /* Read Status register */ if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0) { return LAN8742_STATUS_READ_ERROR; } /* Read Status register again */ if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0) { return LAN8742_STATUS_READ_ERROR; } if((readval & LAN8742_BSR_LINK_STATUS) == 0) { /* Return Link Down status */ return LAN8742_STATUS_LINK_DOWN; } /* Check Auto negotiaition */ if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) < 0) { return LAN8742_STATUS_READ_ERROR; } /*判断BCR寄存器的第12位是否为1,1 开启自协商;0 关闭自协商*/ if((readval & LAN8742_BCR_AUTONEGO_EN) != LAN8742_BCR_AUTONEGO_EN)//关闭 { /* 未开启自协商就读取BCR寄存器判断速率和双工状态: LAN8720A 第13位,1:100M, 0 10M 第8位,1:FULLDUPLEX, 0 HALFDUPLEX YT8512C: 第6位+第13位:(0,1) 100M,(0,0)10M 第8位,1:FULLDUPLEX, 0 HALFDUPLEX */ if(PHY_TYPE == YT8512C) { if((readval & 0x0040) == 0x0040) //只有在第6位为0时才可以通过判断第13位来判断状态 { return LAN8742_STATUS_READ_ERROR; } } if(((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT) && ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE)) { return LAN8742_STATUS_100MBITS_FULLDUPLEX; } else if ((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT) { return LAN8742_STATUS_100MBITS_HALFDUPLEX; } else if ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE) { return LAN8742_STATUS_10MBITS_FULLDUPLEX; } else { return LAN8742_STATUS_10MBITS_HALFDUPLEX; } } else /* Auto Nego enabled 开启自协商*/ { /* 开启自协商后读取SR寄存器判断速率和双工状态: LAN8720A(SR寄存器地址:0x1f): 第12位:判断自协商状态,1 已完成自协商;0 未进行或未开启自协商 第4到2位: 001 = 10BASE-T half-duplex ==0x0004 101 = 10BASE-T full-duplex ==0x0014 010 = 100BASE-TX half-duplex ==0x0008 110 = 100BASE-TX full-duplex ==0x0018 YT8512C(SR寄存器地址:0x11):--一定要在自协商结束后才有效 第11位:判断自协商状态,1 已完成自协商;0 未进行或未开启自协商 第15到13位: 000 = 10BASE-T half-duplex ==0x0000 001 = 10BASE-T full-duplex ==0x2000 010 = 100BASE-TX half-duplex ==0x4000 011 = 100BASE-TX full-duplex ==0x6000 100 = 1000BASE-TX half-duplex ==0x8000 101 = 1000BASE-TX full-duplex ==0xA000 */ if(PHY_TYPE == LAN8720A) { if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_PHYSCSR, &readval) < 0) { return LAN8742_STATUS_READ_ERROR; } /* Check if auto nego not done */ if((readval & LAN8742_PHYSCSR_AUTONEGO_DONE) == 0) { return LAN8742_STATUS_AUTONEGO_NOTDONE; } if((readval & LAN8742_PHYSCSR_HCDSPEEDMASK) == LAN8742_PHYSCSR_100BTX_FD) { return LAN8742_STATUS_100MBITS_FULLDUPLEX; } else if ((readval & LAN8742_PHYSCSR_HCDSPEEDMASK) == LAN8742_PHYSCSR_100BTX_HD) { return LAN8742_STATUS_100MBITS_HALFDUPLEX; } else if ((readval & LAN8742_PHYSCSR_HCDSPEEDMASK) == LAN8742_PHYSCSR_10BT_FD) { return LAN8742_STATUS_10MBITS_FULLDUPLEX; } else { return LAN8742_STATUS_10MBITS_HALFDUPLEX; } } else if(PHY_TYPE == YT8512C) { if(pObj->IO.ReadReg(pObj->DevAddr, YT8512C_PHYSCSR, &readval) < 0) { return LAN8742_STATUS_READ_ERROR; } /* Check if auto nego not done */ if((readval & YT8512C_PHYSCSR_AUTONEGO_DONE) == 0) { return LAN8742_STATUS_AUTONEGO_NOTDONE; } if((readval & YT8512C_PHYSCSR_HCDSPEEDMASK) == YT8512C_PHYSCSR_100BTX_FD) { return LAN8742_STATUS_100MBITS_FULLDUPLEX; } else if ((readval & YT8512C_PHYSCSR_HCDSPEEDMASK) == YT8512C_PHYSCSR_100BTX_HD) { return LAN8742_STATUS_100MBITS_HALFDUPLEX; } else if ((readval & YT8512C_PHYSCSR_HCDSPEEDMASK) == YT8512C_PHYSCSR_10BT_FD) { return LAN8742_STATUS_10MBITS_FULLDUPLEX; } else if ((readval & YT8512C_PHYSCSR_HCDSPEEDMASK) == YT8512C_PHYSCSR_10BT_HD) { return LAN8742_STATUS_10MBITS_HALFDUPLEX; } } } return LAN8742_STATUS_READ_ERROR; }
-
lwip.c增加热插拔代码;
cpp//修改ethernet_link_status_updated函数为如下内容: static void ethernet_link_status_updated(struct netif *netif) { if (netif_is_up(netif)) { /* USER CODE BEGIN 5 */ netif_set_up(netif); #if(LWIP_DHCP == 1) dhcp_start(netif); #endif printf("网线插入\n"); /* USER CODE END 5 */ } else /* netif is down */ { /* USER CODE BEGIN 6 */ netif_set_down(netif); #if(LWIP_DHCP == 1) dhcp_stop(netif); #endif printf("网线拔出\n"); /* USER CODE END 6 */ } }
-
ethernetif.c增加开启自协商和PHY复位代码
cpp//low_level_init函数最后LAN8742_Init(&LAN8742);语句后添加开启自协商代码: LAN8742_StartAutoNego(&LAN8742); HAL_Delay(2000); //HAL_ETH_MspInit函数最后添加PHY识别和复位代码: #include "i2c.h" uint32_t regval; HAL_ETH_ReadPHYRegister(&heth, 0, 2, ®val); if (regval && 0xFFF == 0xFFF) /* 旧板卡(LAN8720A)*/ { PHY_TYPE = LAN8720A; PCF8574_WriteBit(7,1); /* 硬件复位 */ HAL_Delay(100); PCF8574_WriteBit(7,0); /* 复位结束 */ HAL_Delay(100); } else /* 新板卡(YT8512C) */ { PHY_TYPE = YT8512C; PCF8574_WriteBit(7,0); /* 硬件复位 */ HAL_Delay(100); PCF8574_WriteBit(7,1); /* 复位结束 */ HAL_Delay(100); }
-
main.c增加变量和业务代码;
cpp//main.c开头添加: extern struct netif gnetif; uint8_t PHY_TYPE = LAN8720A; //main函数while(1)前增加以下代码: #if(LWIP_DHCP == 1) while(gnetif.ip_addr.addr == 0) { MX_LWIP_Process(); } printf("DHCP succeed:%d.%d.%d.%d\n\n", ((gnetif.ip_addr.addr)&0x000000ff), (((gnetif.ip_addr.addr)&0x0000ff00)>>8), (((gnetif.ip_addr.addr)&0x00ff0000)>>16), ((gnetif.ip_addr.addr)&0xff000000)>>24); #endif //main函数while(1)中增加以下代码: MX_LWIP_Process();
修改完成后编译:0警告和0错误;
烧录后插入路由器中串口打印板子的IP地址,我这打印的是:DHCP succeed:192.168.1.251。(一定要插入路由器中,因为当前代码采用的是DHCP模式,下一篇将教大家如何在此代码的基础上加入静态IP代码)
本文对应的MDK工程和CubeMX工程如下:https://download.csdn.net/download/qq_44712722/90868312