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状态掩码 */