基于正点原子阿波罗F429开发板的LWIP应用(1)——网络ping通
说在开头
正点原子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添加头文件和宏定义;
#include "stdio.h" #include "string.h"#define LAN8720A 0 #define YT8512C 1extern 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添加串口重定向;
int fputc(int ch, FILE *f) {//具体哪个串口可以更改huart1为其它串口HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0x0f);return ch; }
- stm32f4xx_it.c添加程序运行指示灯LED;
//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驱动代码;
//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芯片适配代码;
//开头增加头文件: #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 HALFDUPLEXYT8512C:第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 ==0x0004101 = 10BASE-T full-duplex ==0x0014010 = 100BASE-TX half-duplex ==0x0008110 = 100BASE-TX full-duplex ==0x0018YT8512C(SR寄存器地址:0x11):--一定要在自协商结束后才有效第11位:判断自协商状态,1 已完成自协商;0 未进行或未开启自协商第15到13位:000 = 10BASE-T half-duplex ==0x0000001 = 10BASE-T full-duplex ==0x2000010 = 100BASE-TX half-duplex ==0x4000011 = 100BASE-TX full-duplex ==0x6000100 = 1000BASE-TX half-duplex ==0x8000101 = 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增加热插拔代码;
//修改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); #endifprintf("网线插入\n"); /* USER CODE END 5 */}else /* netif is down */{ /* USER CODE BEGIN 6 */netif_set_down(netif); #if(LWIP_DHCP == 1) dhcp_stop(netif); #endifprintf("网线拔出\n"); /* USER CODE END 6 */} }
- ethernetif.c增加开启自协商和PHY复位代码
//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增加变量和业务代码;
//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工程如下: