- LwIP应用开发实战指南:基于STM32
- 刘火良 杨森
- 2856字
- 2023-07-10 16:09:24
3.6 软件设计
3.6.1 获取STM32的裸机工程模板
关于STM32的裸机工程模板,我们直接使用野火STM32开发板配套的HAL库例程即可。这里我们选取比较简单的例程—“GPIO输出—使用固件库点亮LED”作为裸机工程模板。该裸机工程模板均可以在对应板子的A盘/程序源码目录下获取,下面以野火STM32F429-挑战者开发板的光盘目录为例,获取一个简单的裸机例程,具体如图3-11所示。
图3-11 获取STM32的裸机工程模板
3.6.2 添加bsp_eth.c与bsp_eth.h
打开裸机工程模板之后,创建一个文件夹,命名为eth,并且在该文件夹下创建两个文件,分别为bsp_eth.c与bsp_eth.h文件,具体如图3-12所示。
图3-12 创建bsp_eth.c与bsp_eth.h文件
然后将bsp_eth.c文件添加到工程分组中,如图3-13所示。
图3-13 将bsp_eth.c添加到工程中
然后我们就可以在bsp_eth.c文件中初始化eth驱动了,暂时加入以下代码,具体见代码清单3-1。
代码清单3-1 bsp_eth.c内容
1 /** 2 ****************************************************************************** 3 * @file main.c 4 * @author fire 5 * @version V1.0 6 * @date 2019-xx-xx 7 * @brief eth 8 ********************************************************************* 9 * @attention 10 * 11 * 实验平台:野火STM32 F429开发板 12 * 论坛:http://www.firebbs.cn 13 * 淘宝:http://firestm32.taobao.com 14 * 15 *********************************************************************** 16 */ 17 #include "./eth/bsp_eth.h" 18 #include "main.h" 19 20 21 #ifndef PRINT_DEBUG 22 #define PRINT_DEBUG 23 #endif 24 25 #ifndef PRINT_ERR 26 #define PRINT_ERR 27 #endif 28 29 /* 定义全局以太网句柄 */ 30 ETH_HandleTypeDef heth; 31 32 #if defined ( __ICCARM__ ) /*! < 编辑器宏定义 */ 33 #pragma data_alignment=4 34 #endif 35 __ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END; 36 /* 以太网DMA接收描述符 */ 37 38 #if defined ( __ICCARM__ ) /*! < 编辑器宏定义 */ 39 #pragma data_alignment=4 40 #endif 41 __ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END; 42 /* 以太网DMA发送描述符 */ 43 44 #if defined ( __ICCARM__ ) /*! < IAR Compiler */ 45 #pragma data_alignment=4 46 #endif 47 __ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; 48 /* 以太网接收缓冲区 */ 49 50 #if defined ( __ICCARM__ ) /*! < 编辑器宏定义 */ 51 #pragma data_alignment=4 52 #endif 53 __ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; 54 /* 以太网发送缓冲区 */ 55 56 57 void HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle) 58 { 59 GPIO_InitTypeDef GPIO_InitStruct; 60 if (ethHandle->Instance==ETH) 61 { 62 /* USER CODE BEGIN ETH_MspInit 0 */ 63 64 /* USER CODE END ETH_MspInit 0 */ 65 // /* 启用外设时钟 */ 66 // __HAL_RCC_ETH_CLK_ENABLE(); 67 68 /**ETH GPIO配置 69 PC1 ------> ETH_MDC 70 PA1 ------> ETH_REF_CLK 71 PA2 ------> ETH_MDIO 72 PA7 ------> ETH_CRS_DV 73 PC4 ------> ETH_RXD0 74 PC5 ------> ETH_RXD1 75 PB11 ------> ETH_TX_EN 76 PG13 ------> ETH_TXD0 77 PG14 ------> ETH_TXD1 78 */ 79 GPIO_InitStruct.Pin = ETH_MDC_Pin|ETH_RXD0_Pin|ETH_RXD1_Pin; 80 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 81 GPIO_InitStruct.Pull = GPIO_NOPULL; 82 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 83 GPIO_InitStruct.Alternate = GPIO_AF11_ETH; 84 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); 85 86 GPIO_InitStruct.Pin = ETH_REF_CLK_Pin|ETH_MDIO_Pin|ETH_CRS_DV_Pin; 87 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 88 GPIO_InitStruct.Pull = GPIO_NOPULL; 89 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 90 GPIO_InitStruct.Alternate = GPIO_AF11_ETH; 91 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 92 93 GPIO_InitStruct.Pin = ETH_TX_EN_Pin; 94 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 95 GPIO_InitStruct.Pull = GPIO_NOPULL; 96 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 97 GPIO_InitStruct.Alternate = GPIO_AF11_ETH; 98 HAL_GPIO_Init(ETH_TX_EN_GPIO_Port, &GPIO_InitStruct); 99 100 GPIO_InitStruct.Pin = ETH_TXD0_Pin|ETH_TXD1_Pin; 101 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; 102 GPIO_InitStruct.Pull = GPIO_NOPULL; 103 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 104 GPIO_InitStruct.Alternate = GPIO_AF11_ETH; 105 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); 106 107 /* USER CODE BEGIN ETH_MspInit 1 */ 108 /* 启用以太网全局中断 */ 109 HAL_NVIC_SetPriority(ETH_IRQn, 6, 0); 110 HAL_NVIC_EnableIRQ(ETH_IRQn); 111 112 /* 启用以太网时钟 */ 113 __HAL_RCC_ETH_CLK_ENABLE(); 114 /* USER CODE END ETH_MspInit 1 */ 115 } 116 } 117 118 static void Eth_Reset(void) 119 { 120 /* PHY外设复位:PI1 */ 121 GPIO_InitTypeDef GPIO_InitStructure; 122 __HAL_RCC_GPIOI_CLK_ENABLE(); 123 124 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 125 GPIO_InitStructure.Pull = GPIO_PULLUP; 126 GPIO_InitStructure.Speed = GPIO_SPEED_FAST; 127 GPIO_InitStructure.Pin = GPIO_PIN_1; 128 HAL_GPIO_Init(GPIOI, &GPIO_InitStructure); 129 HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_RESET); 130 HAL_Delay(5); 131 HAL_GPIO_WritePin(GPIOI, GPIO_PIN_1, GPIO_PIN_SET); 132 HAL_Delay(5); 133 } 134 135 void HAL_ETH_MspDeInit(ETH_HandleTypeDef* ethHandle) 136 { 137 if (ethHandle->Instance==ETH) 138 { 139 /* USER CODE BEGIN ETH_MspDeInit 0 */ 140 141 /* USER CODE END ETH_MspDeInit 0 */ 142 /* 禁用外设时钟 */ 143 __HAL_RCC_ETH_CLK_DISABLE(); 144 145 /*配置以太网GPIO: 146 *PC1 ------> ETH_MDC 147 *PA1 ------> ETH_REF_CLK 148 *PA2 ------> ETH_MDIO 149 *PA7 ------> ETH_CRS_DV 150 *PC4 ------> ETH_RXD0 151 *PC5 ------> ETH_RXD1 152 *PB11 ------> ETH_TX_EN 153 *PG13 ------> ETH_TXD0 154 *PG14 ------> ETH_TXD1 155 */ 156 HAL_GPIO_DeInit(GPIOC, ETH_MDC_Pin|ETH_RXD0_Pin|ETH_RXD1_Pin); 157 158 HAL_GPIO_DeInit(GPIOA, ETH_REF_CLK_Pin|ETH_MDIO_Pin|ETH_CRS_DV_Pin); 159 160 HAL_GPIO_DeInit(ETH_TX_EN_GPIO_Port, ETH_TX_EN_Pin); 161 162 HAL_GPIO_DeInit(GPIOG, ETH_TXD0_Pin|ETH_TXD1_Pin); 163 164 /* USER CODE BEGIN ETH_MspDeInit 1 */ 165 166 /* USER CODE END ETH_MspDeInit 1 */ 167 } 168 } 169 170 HAL_StatusTypeDef Bsp_Eth_Init(void) 171 { 172 HAL_StatusTypeDef ret; 173 174 uint8_t MACAddr[6] ; 175 176 HAL_ETH_DeInit(&heth); 177 178 Eth_Reset(); 179 180 ETH->DMABMR |= ETH_DMABMR_SR; 181 182 /* 初始化以太网 */ 183 MACAddr[0] = 0x02; 184 MACAddr[1] = 0x00; 185 MACAddr[2] = 0x00; 186 MACAddr[3] = 0x00; 187 MACAddr[4] = 0x00; 188 MACAddr[5] = 0x00; 189 heth.Instance = ETH; 190 heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; 191 heth.Init.PhyAddress = LAN8720_PHY_ADDRESS; 192 heth.Init.MACAddr = &MACAddr[0]; 193 heth.Init.RxMode = ETH_RXPOLLING_MODE; //接收模式(轮询) 194 heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; 195 heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; 196 heth.Init.Speed = ETH_SPEED_100M; //速度 197 heth.Init.DuplexMode = ETH_MODE_FULLDUPLEX; 198 199 /* 配置以太网外设(GPIO、时钟、MAC、DMA)*/ 200 ret = HAL_ETH_Init(&heth); 201 if (ret == HAL_OK) 202 PRINT_DEBUG("eth hardware init success...\n"); 203 else 204 PRINT_DEBUG("eth hardware init failed...\n"); 205 206 /* 初始化Tx发送描述符列表 */ 207 HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); 208 209 /* 初始化Rx接收描述符列表 */ 210 HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); 211 /* 启用MAC和DMA传输和接收 */ 212 return ret; 213 } 214 215 216 void ETH_IRQHandler(void) 217 { 218 HAL_ETH_IRQHandler(&heth); 219 220 } 221 222 /** 223 * @brief 以太网接收完成回调处理 224 * @param heth: 以太网句柄 225 * @retval None 226 */ 227 228 void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) 229 { 230 231 } 232 233 void HAL_ETH_TxCpltCallback(ETH_HandleTypeDef *heth) 234 { 235 ; 236 } 237 238 void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *heth) 239 { 240 PRINT_ERR("eth err\n"); 241 } 242
STM32的HAL库使用一个数据结构对以太网进行描述,我们可以认为那是一个以太网的句柄,记录着以太网的注册基地址、连接状态、发送描述、接收描述等,该数据结构是ETH_HandleTypeDef,具体见代码清单3-2。在bsp_eth.c中我们需要定义一个用于描述以太网的数据结构heth,这样就能通过heth对以太网进行初始化、收发数据等操作。
代码清单3-2 ETH_HandleTypeDef结构
1 typedef struct 2 { 3 ETH_TypeDef *Instance; /*!< 注册基地址 */ 4 5 ETH_InitTypeDef Init; /*!< 以太网初始化配置 */ 6 7 uint32_t LinkStatus; /*!< 以太网链路状态 */ 8 9 ETH_DMADescTypeDef *RxDesc; /*!< 获取Rx描述符 */ 10 11 ETH_DMADescTypeDef *TxDesc; /*!< 要设置的Tx描述符 */ 12 13 ETH_DMARxFrameInfos RxFrameInfos; /*!< 最后的Rx帧信息 */ 14 15 __IO HAL_ETH_StateTypeDef State; /*!< ETH通信状态 */ 16 17 HAL_LockTypeDef Lock; /*!< ETH锁定 */ 18 19 } ETH_HandleTypeDef;
我们先看一下Bsp_Eth_Init()函数,调用HAL库的HAL_ETH_DeInit(&heth)进行复位ETH配置,在该复位函数内部会调用bsp_eth.c文件中的HAL_ETH_MspDeInit()函数,然后再对heth的参数进行初始化,如开启网络自适应功能、设置PHY的地址、设置MAC地址、设置接收网络数据的方式为中断方式、设置检查校验为硬件校验、设置以太网速度为100Mbit/s等(速度和工作模式无须配置),然后调用HAL库的HAL_ETH_Init()函数将以太网进行初始化,此时会调用HAL_ETH_MspInit()对以太网的接口进行初始化,所以,bsp_eth.c文件需要对HAL_ETH_MspInit()进行封装,根据我们的硬件接口(I/O接口)进行初始化操作,再初始化以太网的接收、发送描述符列表,这些无须我们理会,HAL库已经帮我们处理好了,这样,一个以太网接口就基本初始化完成了,但是,收发数据的操作还需要我们自己写驱动,所以我们暂时还不可以使用它进行网络数据的收发操作,因为数据的收发需要配合LwIP,这些会在后面的章节中介绍。
3.6.3 修改stm32f4xx_hal_conf.h文件
有人可能会问,PHY的初始化在哪呢?其实,调用HAL_ETH_Init()函数的时候,HAL库就已经对PHY进行初始化了,当然,每个不一样的PHY肯定有不一样的配置,所以这就需要我们自己对PHY参数进行配置,我们的开发板使用的是LAN8720A芯片,LAN8720A复位时需要一段延时时间,这里需要定义延时时间的长度,大约为5~50ms即可,驱动代码中需要获取PHY的速度和工作模式,LAN8720A的R31是特殊控制/状态寄存器,包括指示以太网速度和工作模式的状态位,所以,我们需要在stm32f4xx_hal_conf.h中添加自己的PHY配置,具体见代码清单3-3。
代码清单3-3 PHY配置
1 /* ############ 以太网外设配置 ################# */ 2 3 /* Section 1 : 以太网外设配置部分 */ 4 5 /* MAC地址(宏定义)*/ 6 #define MAC_ADDR0 2U 7 #define MAC_ADDR1 0U 8 #define MAC_ADDR2 0U 9 #define MAC_ADDR3 0U 10 #define MAC_ADDR4 0U 11 #define MAC_ADDR5 0U 12 13 /* 定义以太网驱动器缓冲区的大小和数量 */ 14 #define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE 15 /* 接收的缓冲区大小 */ 16 #define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE 17 /* 发送的缓冲区大小 */ 18 #define ETH_RXBUFNB ((uint32_t)8U) 19 /* 8个接收缓冲区,大小为ETH_RX_BUF_SIZE */ 20 #define ETH_TXBUFNB ((uint32_t)8U) 21 /* 8个发送缓冲区,大小为ETH_TX_BUF_SIZE */ 22 23 /* Section 2: PHY配置部分 */ 24 25 /* 定义LAN8720地址 */ 26 #define LAN8720_PHY_ADDRESS 0U 27 /* PHY复位延迟,这些值基于1ms systick中断 */ 28 #define PHY_RESET_DELAY ((uint32_t)0x00000005U) 29 /* PHY配置延迟 */ 30 #define PHY_CONFIG_DELAY ((uint32_t)0x00000005U) 31 32 #define PHY_READ_TO ((uint32_t)0x0000FFFFU) 33 #define PHY_WRITE_TO ((uint32_t)0x0000FFFFU) 34 35 /* section 3通用PHY寄存器 */ 36 37 #define PHY_BCR ((uint16_t)0x00U) 38 /*! < 收发器基本控制寄存器 */ 39 #define PHY_BSR ((uint16_t)0x01U) 40 /*! < 收发器基本状态寄存器 */ 41 42 #define PHY_RESET ((uint16_t)0x8000U) /*! < PHY复位 */ 43 #define PHY_LOOPBACK ((uint16_t)0x4000U) 44 /*! < 选择环回模式 */ 45 #define PHY_FULLDUPLEX_100M ((uint16_t)0x2100U) 46 /*! < 将全双工模式设置为100 Mbit/s */ 47 #define PHY_HALFDUPLEX_100M ((uint16_t)0x2000U) 48 /*! < 将半双工模式设置为100 Mbit/s */ 49 #define PHY_FULLDUPLEX_10M ((uint16_t)0x0100U) 50 /*! < 将全双工模式设置为10 Mbit/s */ 51 #define PHY_HALFDUPLEX_10M ((uint16_t)0x0000U) 52 /*! < 将半双工模式设置为10 Mbit/s */ 53 #define PHY_AUTONEGOTIATION ((uint16_t)0x1000U) 54 /*! < 启用自动协商功能 */ 55 #define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200U) 56 /*! < 重启自动协商功能 */ 57 #define PHY_POWERDOWN ((uint16_t)0x0800U) 58 /*! < 选择省电模式 */ 59 #define PHY_ISOLATE ((uint16_t)0x0400U) 60 /*! < 从MII隔离PHY */ 61 62 #define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020U) 63 /*! < 自动协商过程已完成 */ 64 #define PHY_LINKED_STATUS ((uint16_t)0x0004U) 65 /*! < 建立了有效链接 */ 66 #define PHY_JABBER_DETECTION ((uint16_t)0x0002U) 67 /*! < 检测到Jabber状况 */ 68 69 /* Section 4: 扩展PHY寄存器 */ 70 #define PHY_SR ((uint16_t)0x1FU) 71 /*! < PHY状态寄存器偏移 */ 72 73 #define PHY_SPEED_STATUS ((uint16_t)0x0004U) 74 /*! < PHY速度掩码 */ 75 #define PHY_DUPLEX_STATUS ((uint16_t)0x0010U) 76 /*! < PHY状态掩码 */