观察stm32f7xx_hal_eth.c,源文件的开头部分描述了这个库文件的使用说明。

目录

1. (#)Declare a ETH_HandleTypeDef handle structure

2. (#)Fill parameters of Init structure in heth handle

3.(#)Call HAL_ETH_Init() API to initialize the Ethernet peripheral (MAC, DMA, ...)

4. (#)Initialize the ETH low level resources through the HAL_ETH_MspInit() API:

5. (#)Initialize Ethernet DMA Descriptors in chain mode and point to allocated buffers:

6. (#)Enable MAC and DMA transmission and reception:

7. (#)Prepare ETH DMA TX Descriptors and give the hand to ETH DMA to transfer the frame to MAC TX FIFO:

8. (#)Poll for a received frame in ETH RX DMA Descriptors and get received frame parameters

9. (#) Get a received frame when an ETH RX interrupt occurs:

10.  (#) Communicate with external PHY device:

11. (#) Configure the Ethernet MAC after ETH peripheral initialization

12. (#) Configure the Ethernet DMA after ETH peripheral initialization


1. (#)Declare a ETH_HandleTypeDef handle structure

for example:

ETH_HandleTypeDef  heth;

参见CubeMX自动添加的外设源文件eth.c的最开头部分

2. (#)Fill parameters of Init structure in heth handle

参见MX_ETH_Init()最后一句之前的部分

/* ETH init function */
void MX_ETH_Init(void)
{uint8_t MACAddr[6] ;heth.Instance = ETH;heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;heth.Init.PhyAddress = 1;MACAddr[0] = 0x00;MACAddr[1] = 0x80;MACAddr[2] = 0xE1;MACAddr[3] = 0x00;MACAddr[4] = 0x00;MACAddr[5] = 0x00;heth.Init.MACAddr = &MACAddr[0];heth.Init.RxMode = ETH_RXPOLLING_MODE;heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;HAL_ETH_Init(&heth);}

第一个ETH,深究起来,可以发现

#define ETH                 ((ETH_TypeDef *) ETH_BASE)  

其中 ETH_TypeDef类型定义了以太网MAC层所需要的信息

/** * @brief Ethernet MAC*/typedef struct
{__IO uint32_t MACCR;__IO uint32_t MACFFR;__IO uint32_t MACHTHR;__IO uint32_t MACHTLR;__IO uint32_t MACMIIAR;__IO uint32_t MACMIIDR;__IO uint32_t MACFCR;__IO uint32_t MACVLANTR;             /*    8 */uint32_t      RESERVED0[2];__IO uint32_t MACRWUFFR;             /*   11 */__IO uint32_t MACPMTCSR;uint32_t      RESERVED1[2];__IO uint32_t MACSR;                 /*   15 */__IO uint32_t MACIMR;__IO uint32_t MACA0HR;__IO uint32_t MACA0LR;__IO uint32_t MACA1HR;__IO uint32_t MACA1LR;__IO uint32_t MACA2HR;__IO uint32_t MACA2LR;__IO uint32_t MACA3HR;__IO uint32_t MACA3LR;               /*   24 */uint32_t      RESERVED2[40];__IO uint32_t MMCCR;                 /*   65 */__IO uint32_t MMCRIR;__IO uint32_t MMCTIR;__IO uint32_t MMCRIMR;__IO uint32_t MMCTIMR;               /*   69 */uint32_t      RESERVED3[14];__IO uint32_t MMCTGFSCCR;            /*   84 */__IO uint32_t MMCTGFMSCCR;uint32_t      RESERVED4[5];__IO uint32_t MMCTGFCR;uint32_t      RESERVED5[10];__IO uint32_t MMCRFCECR;__IO uint32_t MMCRFAECR;uint32_t      RESERVED6[10];__IO uint32_t MMCRGUFCR;uint32_t      RESERVED7[334];__IO uint32_t PTPTSCR;__IO uint32_t PTPSSIR;__IO uint32_t PTPTSHR;__IO uint32_t PTPTSLR;__IO uint32_t PTPTSHUR;__IO uint32_t PTPTSLUR;__IO uint32_t PTPTSAR;__IO uint32_t PTPTTHR;__IO uint32_t PTPTTLR;__IO uint32_t RESERVED8;__IO uint32_t PTPTSSR;uint32_t      RESERVED9[565];__IO uint32_t DMABMR;__IO uint32_t DMATPDR;__IO uint32_t DMARPDR;__IO uint32_t DMARDLAR;__IO uint32_t DMATDLAR;__IO uint32_t DMASR;__IO uint32_t DMAOMR;__IO uint32_t DMAIER;__IO uint32_t DMAMFBOCR;__IO uint32_t DMARSWTR;uint32_t      RESERVED10[8];__IO uint32_t DMACHTDR;__IO uint32_t DMACHRDR;__IO uint32_t DMACHTBAR;__IO uint32_t DMACHRBAR;
} ETH_TypeDef;

而ETH_BASE地址显然是以太网外设的基址,再深入看一下,

#define ETH_BASE              (AHB1PERIPH_BASE + 0x8000)
#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000)
#define PERIPH_BASE   ((uint32_t)0x40000000) /*!< Base address of : AHB/ABP Peripherals */

查看STM32F7参考手册2.2.2 存储器映射和寄存器边界地址,可以发现正好可以对应上

0x93FF-0x8000+1==0x1400==5120Byte,然后再数一数ETH_TypeDef结构体的大小1046*4=4184个Byte,似乎有差距,再翻手册的1450页

...

数一数,这个表跟以太网有关的寄存器共有56个(除去RESERVED),56个32位寄存器。

而代码里也是56个寄存器(除去RESERVED)。

而且0x1054-0x00+1==4184Byte,跟代码恰好对应上。所以可以看出来, 存储器映射和寄存器边界地址这个表,留了蛮多的冗余位置,或者叫保护位置,不让这些寄存器塞得太满。

之后再看

heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;

这个参数描述了是否是自适应数据速率(10/100Mbps)以及数据传输模式(半双工/ 全双工)

  uint32_t             AutoNegotiation;           /*!< Selects or not the AutoNegotiation mode for the external PHYThe AutoNegotiation allows an automatic setting of the Speed (10/100Mbps)and the mode (half/full-duplex).This parameter can be a value of @ref ETH_AutoNegotiation */

之后再看

heth.Init.PhyAddress = 1;

这个参数描述了物理地址,为什么是1呢,其实是有说法的,DP83848以太网PHY芯片上共有5个引脚描述物理地址,总共是2^5=32个,所以说STM32芯片最多可以识别32个以太网PHY芯片

再看下边,这个参数描述了MAC地址

  MACAddr[0] = 0x00;MACAddr[1] = 0x80;MACAddr[2] = 0xE1;MACAddr[3] = 0x00;MACAddr[4] = 0x00;MACAddr[5] = 0x00;heth.Init.MACAddr = &MACAddr[0];

再看下边,这个参数描述了接收模式,可以选择Polling mode以及Interrupt mode

heth.Init.RxMode = ETH_RXPOLLING_MODE;
  uint32_t             RxMode;                    /*!< Selects the Ethernet Rx mode: Polling mode, Interrupt mode.This parameter can be a value of @ref ETH_Rx_Mode */

再看下边,这个参数描述了checksum模式,可以选择软件或者硬件

heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
  uint32_t             ChecksumMode;              /*!< Selects if the checksum is check by hardware or by software. This parameter can be a value of @ref ETH_Checksum_Mode */

再看下边,这个参数描述了PHY接口模式,可以选择MII或RMII

  heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
  uint32_t             MediaInterface    ;               /*!< Selects the media-independent interface or the reduced media-independent interface. This parameter can be a value of @ref ETH_Media_Interface */

上边看了这几个参数,其实我想说,利用CubeMX来配置的话,其实只要在GUI界面上点选好了,生成代码时就会自动生成,比如

3.(#)Call HAL_ETH_Init() API to initialize the Ethernet peripheral (MAC, DMA, ...)

这个函数有点长,还是粘贴上来吧

/*** @brief  Initializes the Ethernet MAC and DMA according to default*         parameters.* @param  heth: pointer to a ETH_HandleTypeDef structure that contains*         the configuration information for ETHERNET module* @retval HAL status*/
HAL_StatusTypeDef HAL_ETH_Init(ETH_HandleTypeDef *heth)
{uint32_t tempreg = 0, phyreg = 0;uint32_t hclk = 60000000;uint32_t tickstart = 0;uint32_t err = ETH_SUCCESS;/* Check the ETH peripheral state */if(heth == NULL){return HAL_ERROR;}/* Check parameters */assert_param(IS_ETH_AUTONEGOTIATION(heth->Init.AutoNegotiation));assert_param(IS_ETH_RX_MODE(heth->Init.RxMode));assert_param(IS_ETH_CHECKSUM_MODE(heth->Init.ChecksumMode));assert_param(IS_ETH_MEDIA_INTERFACE(heth->Init.MediaInterface));  if(heth->State == HAL_ETH_STATE_RESET){/* Allocate lock resource and initialize it */heth->Lock = HAL_UNLOCKED;/* Init the low level hardware : GPIO, CLOCK, NVIC. */HAL_ETH_MspInit(heth);}/* Enable SYSCFG Clock */__HAL_RCC_SYSCFG_CLK_ENABLE();/* Select MII or RMII Mode*/SYSCFG->PMC &= ~(SYSCFG_PMC_MII_RMII_SEL);SYSCFG->PMC |= (uint32_t)heth->Init.MediaInterface;/* Ethernet Software reset *//* Set the SWR bit: resets all MAC subsystem internal registers and logic *//* After reset all the registers holds their respective reset values */(heth->Instance)->DMABMR |= ETH_DMABMR_SR;/* Wait for software reset */while (((heth->Instance)->DMABMR & ETH_DMABMR_SR) != (uint32_t)RESET){}/*-------------------------------- MAC Initialization ----------------------*//* Get the ETHERNET MACMIIAR value */tempreg = (heth->Instance)->MACMIIAR;/* Clear CSR Clock Range CR[2:0] bits */tempreg &= ETH_MACMIIAR_CR_MASK;/* Get hclk frequency value */hclk = HAL_RCC_GetHCLKFreq();/* Set CR bits depending on hclk value */if((hclk >= 20000000)&&(hclk < 35000000)){/* CSR Clock Range between 20-35 MHz */tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div16;}else if((hclk >= 35000000)&&(hclk < 60000000)){/* CSR Clock Range between 35-60 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div26;}  else if((hclk >= 60000000)&&(hclk < 100000000)){/* CSR Clock Range between 60-100 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div42;}  else if((hclk >= 100000000)&&(hclk < 150000000)){/* CSR Clock Range between 100-150 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div62;}else /* ((hclk >= 150000000)&&(hclk <= 200000000)) */{/* CSR Clock Range between 150-216 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div102;    }/* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */(heth->Instance)->MACMIIAR = (uint32_t)tempreg;/*-------------------- PHY initialization and configuration ----------------*//* Put the PHY in reset mode */if((HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_RESET)) != HAL_OK){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set the ETH peripheral state to READY */heth->State = HAL_ETH_STATE_READY;/* Return HAL_ERROR */return HAL_ERROR;}/* Delay to assure PHY reset */HAL_Delay(PHY_RESET_DELAY);if((heth->Init).AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE){/* Get tick */tickstart = HAL_GetTick();/* We wait for linked status */do{HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);/* Check for the Timeout */if((HAL_GetTick() - tickstart ) > LINKED_STATE_TIMEOUT_VALUE){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);return HAL_TIMEOUT;}} while (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS));/* Enable Auto-Negotiation */if((HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_AUTONEGOTIATION)) != HAL_OK){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set the ETH peripheral state to READY */heth->State = HAL_ETH_STATE_READY;/* Return HAL_ERROR */return HAL_ERROR;   }/* Get tick */tickstart = HAL_GetTick();/* Wait until the auto-negotiation will be completed */do{HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);/* Check for the Timeout */if((HAL_GetTick() - tickstart ) > AUTONEGO_COMPLETED_TIMEOUT_VALUE){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);return HAL_TIMEOUT;}} while (((phyreg & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE));/* Read the result of the auto-negotiation */if((HAL_ETH_ReadPHYRegister(heth, PHY_SR, &phyreg)) != HAL_OK){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set the ETH peripheral state to READY */heth->State = HAL_ETH_STATE_READY;/* Return HAL_ERROR */return HAL_ERROR;   }/* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */if((phyreg & PHY_DUPLEX_STATUS) != (uint32_t)RESET){/* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */(heth->Init).DuplexMode = ETH_MODE_FULLDUPLEX;  }else{/* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */(heth->Init).DuplexMode = ETH_MODE_HALFDUPLEX;           }/* Configure the MAC with the speed fixed by the auto-negotiation process */if((phyreg & PHY_SPEED_STATUS) == PHY_SPEED_STATUS){  /* Set Ethernet speed to 10M following the auto-negotiation */(heth->Init).Speed = ETH_SPEED_10M; }else{   /* Set Ethernet speed to 100M following the auto-negotiation */ (heth->Init).Speed = ETH_SPEED_100M;}}else /* AutoNegotiation Disable */{/* Check parameters */assert_param(IS_ETH_SPEED(heth->Init.Speed));assert_param(IS_ETH_DUPLEX_MODE(heth->Init.DuplexMode));/* Set MAC Speed and Duplex Mode */if(HAL_ETH_WritePHYRegister(heth, PHY_BCR, ((uint16_t)((heth->Init).DuplexMode >> 3) |(uint16_t)((heth->Init).Speed >> 1))) != HAL_OK){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set the ETH peripheral state to READY */heth->State = HAL_ETH_STATE_READY;/* Return HAL_ERROR */return HAL_ERROR;}  /* Delay to assure PHY configuration */HAL_Delay(PHY_CONFIG_DELAY);}/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set ETH HAL State to Ready */heth->State= HAL_ETH_STATE_READY;/* Return function status */return HAL_OK;
}

该怎么看呢,挑主要的内容,有些语句只是用于代码检查,就先略去不看。

刚才的参数里并没有这个State,因此State刚运行到这里应该就是HAL_ETH_STATE_RESET,进入这个if语句

  if(heth->State == HAL_ETH_STATE_RESET){/* Allocate lock resource and initialize it */heth->Lock = HAL_UNLOCKED;/* Init the low level hardware : GPIO, CLOCK, NVIC. */HAL_ETH_MspInit(heth);}

这个HAL_ETH_MspInit()函数用于初始化所谓的“低级硬件”,即GPIO,CLOCK,NVIC,这个函数在eth.c中,如下

void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
{GPIO_InitTypeDef GPIO_InitStruct;if(heth->Instance==ETH){/* USER CODE BEGIN ETH_MspInit 0 *//* USER CODE END ETH_MspInit 0 *//* Peripheral clock enable */__ETH_CLK_ENABLE();/**ETH GPIO Configuration    PC1     ------> ETH_MDCPA1     ------> ETH_REF_CLKPA2     ------> ETH_MDIOPA7     ------> ETH_CRS_DVPC4     ------> ETH_RXD0PC5     ------> ETH_RXD1PB11     ------> ETH_TX_ENPG13     ------> ETH_TXD0PG14     ------> ETH_TXD1 */GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;GPIO_InitStruct.Alternate = GPIO_AF11_ETH;HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;GPIO_InitStruct.Alternate = GPIO_AF11_ETH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;GPIO_InitStruct.Alternate = GPIO_AF11_ETH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;GPIO_InitStruct.Alternate = GPIO_AF11_ETH;HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);/* Peripheral interrupt init*/HAL_NVIC_SetPriority(ETH_IRQn, 0, 0);HAL_NVIC_EnableIRQ(ETH_IRQn);/* USER CODE BEGIN ETH_MspInit 1 *//* USER CODE END ETH_MspInit 1 */}
}

确实也是配置了GPIO,CLOCK,NVIC

其中GPIO参考Open7XXI-C开发板的原理图

通过CubeMX来配置

其中的时钟也是通过CubeMX配置,中断也是通过CubeMX配置

再往下看,使能系统时钟

  /* Enable SYSCFG Clock */__HAL_RCC_SYSCFG_CLK_ENABLE();

使能RMII接口

  /* Select MII or RMII Mode*/SYSCFG->PMC &= ~(SYSCFG_PMC_MII_RMII_SEL);SYSCFG->PMC |= (uint32_t)heth->Init.MediaInterface;

使能以太网软件复位

  /* Ethernet Software reset *//* Set the SWR bit: resets all MAC subsystem internal registers and logic *//* After reset all the registers holds their respective reset values */(heth->Instance)->DMABMR |= ETH_DMABMR_SR;

等待以太网软件复位

  /* Wait for software reset */while (((heth->Instance)->DMABMR & ETH_DMABMR_SR) != (uint32_t)RESET){}

之后进入MAC初始化

主要就是配时钟频率,通过写MACMIIAR寄存器的CR字段来配置。

  /*-------------------------------- MAC Initialization ----------------------*//* Get the ETHERNET MACMIIAR value */tempreg = (heth->Instance)->MACMIIAR;/* Clear CSR Clock Range CR[2:0] bits */tempreg &= ETH_MACMIIAR_CR_MASK;/* Get hclk frequency value */hclk = HAL_RCC_GetHCLKFreq();/* Set CR bits depending on hclk value */if((hclk >= 20000000)&&(hclk < 35000000)){/* CSR Clock Range between 20-35 MHz */tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div16;}else if((hclk >= 35000000)&&(hclk < 60000000)){/* CSR Clock Range between 35-60 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div26;}  else if((hclk >= 60000000)&&(hclk < 100000000)){/* CSR Clock Range between 60-100 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div42;}  else if((hclk >= 100000000)&&(hclk < 150000000)){/* CSR Clock Range between 100-150 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div62;}else /* ((hclk >= 150000000)&&(hclk <= 200000000)) */{/* CSR Clock Range between 150-216 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div102;    }/* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */(heth->Instance)->MACMIIAR = (uint32_t)tempreg;

从中貌似发现了中文参考手册的一个笔误,150~168MHz或许应该改为150~ 216MHz,因为STM32F7最大频率可以配到216MHz。而代码这块貌似也有笔误?150MHz~200MHz?

  else /* ((hclk >= 150000000)&&(hclk <= 200000000)) */{/* CSR Clock Range between 150-216 MHz */ tempreg |= (uint32_t)ETH_MACMIIAR_CR_Div102;    }

之后进入PHY初始化,先写PHY_BCR寄存器(DP83848手册上是叫做BMCR)的第15位,设置PHY为RESET模式。

之后延时255ms,确保配置PHY RESET模式成功

  /* Delay to assure PHY reset */HAL_Delay(PHY_RESET_DELAY);
/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
#define PHY_RESET_DELAY                 ((uint32_t)0x000000FF)

之后进入if else语句,由于之前在CubeMX里配置的是自适应,所以进入if

之后等待建立链接状态LINKED STATE,实际上是通过PHY_BSR寄存器(DP83848手册里叫BMSR寄存器)来配置的。

/* We wait for linked status */do{HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);/* Check for the Timeout */if((HAL_GetTick() - tickstart ) > LINKED_STATE_TIMEOUT_VALUE){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);return HAL_TIMEOUT;}} while (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS));

其中需要检测寄存器第2位,若第2位==1,则说明已经建立链接。

#define PHY_LINKED_STATUS               ((uint16_t)0x0004)  /*!< Valid link established               */

之后是确认一下是否配置成自适应模式

/* Enable Auto-Negotiation */if((HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_AUTONEGOTIATION)) != HAL_OK){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set the ETH peripheral state to READY */heth->State = HAL_ETH_STATE_READY;/* Return HAL_ERROR */return HAL_ERROR;   }/* Get tick */tickstart = HAL_GetTick();/* Wait until the auto-negotiation will be completed */do{HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);/* Check for the Timeout */if((HAL_GetTick() - tickstart ) > AUTONEGO_COMPLETED_TIMEOUT_VALUE){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);return HAL_TIMEOUT;}} while (((phyreg & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE));

之后就是跟自适应有关的双工模式和数据速率的配置。

    /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */if((phyreg & PHY_DUPLEX_STATUS) != (uint32_t)RESET){/* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */(heth->Init).DuplexMode = ETH_MODE_FULLDUPLEX;  }else{/* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */(heth->Init).DuplexMode = ETH_MODE_HALFDUPLEX;           }/* Configure the MAC with the speed fixed by the auto-negotiation process */if((phyreg & PHY_SPEED_STATUS) == PHY_SPEED_STATUS){  /* Set Ethernet speed to 10M following the auto-negotiation */(heth->Init).Speed = ETH_SPEED_10M; }else{   /* Set Ethernet speed to 100M following the auto-negotiation */ (heth->Init).Speed = ETH_SPEED_100M;}

再之后跳过else,看到HAL_ETH_Init函数的末尾,即为

/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);/* Set ETH HAL State to Ready */heth->State= HAL_ETH_STATE_READY;/* Return function status */return HAL_OK;

HAL_ETH_Init函数的末尾调用了ETH_MACDMAConfig函数,我就不粘贴了。注意里边有两个结构体,分别描述MAC和DMA参数的。

ETH_MACInitTypeDef结构体里的成员变量,以及STM32F7手册38.8.1 MAC寄存器说明,发现都可以对应上。ETH_MACInitTypeDef结构体成员变量对应于ETH_MACCR、ETH_MACFFR、ETH_MACHTHR、ETH_MACHTLR、ETH_MACFCR、ETH_MACVLANTR寄存器里的某些位,具体不再详说。

ETH_DMAInitTypeDef结构体里的成员变量同样可以与手册对应上,参考STM32F7手册38.8.4 DMA寄存器说明,可以发现对应于ETH_DMABMR、ETH_DMAOMR寄存器。

之后再次确认自适应状态,如果自适应未使能,则强制配置双工模式及100M数据速率。

之后将上边配置好的ETH_MACCR、ETH_MACFFR、ETH_MACHTHR、ETH_MACHTLR、ETH_MACFCR、ETH_MACVLANTR寄存器值写入到寄存器里,值得注意的是每写完一个寄存器的各个位之后,需要延时1ms时间,以确保写入操作写入成功

  HAL_Delay(ETH_REG_WRITE_DELAY);
/* Delay to wait when writing to some Ethernet registers */
#define ETH_REG_WRITE_DELAY ((uint32_t)0x00000001)

同样,DMA相关的寄存器值也是同样写入到ETH_DMABMR、ETH_DMAOMR寄存器。

之后RxMode,我们在CubeMX里选择的是polling mode,因此跳过这个if语句。

之后调用ETH_MACAddressConfig函数配置MAC地址,我们的MAC地址在CubeMX里配置的,也可以在eth.c中看到

至此ETH_MACDMAConfig函数分析完毕,HAL_ETH_Init函数也分析完毕。

4. (#)Initialize the ETH low level resources through the HAL_ETH_MspInit() API:

(##) Enable the Ethernet interface clock using
               (+++) __HAL_RCC_ETHMAC_CLK_ENABLE();
               (+++) __HAL_RCC_ETHMACTX_CLK_ENABLE();
               (+++) __HAL_RCC_ETHMACRX_CLK_ENABLE();
           
          (##) Initialize the related GPIO clocks
          (##) Configure Ethernet pin-out
          (##) Configure Ethernet NVIC interrupt (IT mode)

这个HAL_ETH_MspInit()函数用于初始化所谓的“低级硬件”,即GPIO,CLOCK,NVIC,这个函数在eth.c中。第3小节已经分析过,因此略去。

5. (#)Initialize Ethernet DMA Descriptors in chain mode and point to allocated buffers:

(##) HAL_ETH_DMATxDescListInit(); for Transmission process
          (##) HAL_ETH_DMARxDescListInit(); for Reception process

初始化DMA链状描述符,TX和RX的。链状描述符是什么呢?

千兆以太网卡的数据传输任务由DMA完成,DMA传输操作通过预先在内存中建立描述符的方式完成。描述符的作用是指定MAC帧数据所在的缓存地址,每个描述符可以最多指定两个缓存地址,缓存大小有严格控制,一个描述符不能指定全部一个帧的缓存数据,需要多个描述符构成描述符链来完成。

有两种描述符链结构:环状描述符和链状描述符。链状描述符中的第二个buffer指定了下一个描述符所在的物理地址,而第一个buffer指定帧数据缓存的位置,环状结构描述符的位置是有序排放的,两个buffer都指向帧数据的缓存地址,最后一个描述符指向第一个描述符所在物理地址形成桶状描述符链。环状和链状结构如图所示,一个描述符链只能用来存储一个MAC帧的数据,DMA每个通道一次最多完成两个MAC帧的传输,多MAC帧的传输需要重新使能DMA通道。 描述符的具体结构如图所示

代码里看到的数据结构是增强型的描述符

/** * @brief  ETH DMA Descriptors data structure definition*/ typedef struct
{__IO uint32_t   Status;           /*!< Status */uint32_t   ControlBufferSize;     /*!< Control and Buffer1, Buffer2 lengths */uint32_t   Buffer1Addr;           /*!< Buffer1 address pointer */uint32_t   Buffer2NextDescAddr;   /*!< Buffer2 or next descriptor address pointer *//*!< Enhanced ETHERNET DMA PTP Descriptors */uint32_t   ExtendedStatus;        /*!< Extended status for PTP receive descriptor */uint32_t   Reserved1;             /*!< Reserved */uint32_t   TimeStampLow;          /*!< Time Stamp Low value for transmit and receive */uint32_t   TimeStampHigh;         /*!< Time Stamp High value for transmit and receive */} ETH_DMADescTypeDef;

同理可知RX跟TX类似。这里先不展开来看。

6. (#)Enable MAC and DMA transmission and reception:

(##) HAL_ETH_Start();

/*** @brief  Enables Ethernet MAC and DMA reception/transmission * @param  heth: pointer to a ETH_HandleTypeDef structure that contains*         the configuration information for ETHERNET module* @retval HAL status*/
HAL_StatusTypeDef HAL_ETH_Start(ETH_HandleTypeDef *heth)
{  /* Process Locked */__HAL_LOCK(heth);/* Set the ETH peripheral state to BUSY */heth->State = HAL_ETH_STATE_BUSY;/* Enable transmit state machine of the MAC for transmission on the MII */ETH_MACTransmissionEnable(heth);/* Enable receive state machine of the MAC for reception from the MII */ETH_MACReceptionEnable(heth);/* Flush Transmit FIFO */ETH_FlushTransmitFIFO(heth);/* Start DMA transmission */ETH_DMATransmissionEnable(heth);/* Start DMA reception */ETH_DMAReceptionEnable(heth);/* Set the ETH state to READY*/heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);/* Return function status */return HAL_OK;
}

先是使能MAC传输,包括TX和RX,主要就是更改寄存器ETH_MACCR某些位的值,详情见STM32F7参考手册

  /* Enable transmit state machine of the MAC for transmission on the MII */ETH_MACTransmissionEnable(heth);/* Enable receive state machine of the MAC for reception from the MII */ETH_MACReceptionEnable(heth);

然后刷新发送FIFO,也是更改寄存器ETH_DMAOMR某位的值,详情见STM32F7参考手册

  /* Flush Transmit FIFO */ETH_FlushTransmitFIFO(heth);

然后使能DMA传输,包括TX和RX,主要就是更改寄存器ETH_DMAOMR某些位的值,详情见STM32F7参考手册

  /* Start DMA transmission */ETH_DMATransmissionEnable(heth);/* Start DMA reception */ETH_DMAReceptionEnable(heth);

7. (#)Prepare ETH DMA TX Descriptors and give the hand to ETH DMA to transfer the frame to MAC TX FIFO:

(##) HAL_ETH_TransmitFrame();

主要功能是根据FrameLength来计算需要的TX BUFFER的数量,因为最大发送包的大小为1524bit

  /* Get the number of needed Tx buffers for the current frame */if (FrameLength > ETH_TX_BUF_SIZE){bufcount = FrameLength/ETH_TX_BUF_SIZE;if (FrameLength % ETH_TX_BUF_SIZE) {bufcount++;}}else {  bufcount = 1;}
#define ETH_TX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for transmit              */
#define ETH_MAX_PACKET_SIZE    ((uint32_t)1524)    /*!< ETH_HEADER + ETH_EXTRA + ETH_VLAN_TAG + ETH_MAX_ETH_PAYLOAD + ETH_CRC */

之后就是配置DMA TX描述符。先略一下

8. (#)Poll for a received frame in ETH RX DMA Descriptors and get received frame parameters

(##) HAL_ETH_GetReceivedFrame(); (should be called into an infinite loop)

有时间再展开写,简单来说就是从链式描述符中读出来每一帧

9. (#) Get a received frame when an ETH RX interrupt occurs:

(##) HAL_ETH_GetReceivedFrame_IT(); (called in IT mode only)
跟上一个函数的区别是从中断函数中得到数据帧。

10.  (#) Communicate with external PHY device:

(##) Read a specific register from the PHY  
              HAL_ETH_ReadPHYRegister();
         (##) Write data to a specific RHY register:
              HAL_ETH_WritePHYRegister();

简单说来就是如何读取 / 写入外部PHY芯片的寄存器。

11. (#) Configure the Ethernet MAC after ETH peripheral initialization

HAL_ETH_ConfigMAC();  all MAC parameters should be filled.

貌似是单独配置MAC,之前的ETH_MACDMAConfig既可以配MAC又可以配DMA,跟这个的功能可能有重叠。

12. (#) Configure the Ethernet DMA after ETH peripheral initialization

HAL_ETH_ConfigDMA(); all DMA parameters should be filled.

貌似是单独配置DMA,之前的ETH_MACDMAConfig既可以配MAC又可以配DMA,跟这个的功能可能有重叠。

STM32F7以太网HAL库源文件(stm32f7xx_hal_eth.c)笔记相关推荐

  1. [野火]STM32 F103 HAL库开发实战指南笔记之简单外设总结

    1.GPIO编程总结 使能 GPIO 端口时钟: 初始化 GPIO 目标引脚为推挽输出模式: 编写简单测试程序,控制 GPIO 引脚输出高.低电平. 这部分宏控制 LED 亮灭的操作是直接向 BSRR ...

  2. [野火]STM32 F103 HAL库开发实战指南笔记之基础总结

    1.本书配套的仿真器为 Fire-Debugger,遵循 ARM 公司的 CMSIS-DAP 标准,支持所有基于 Cortex-M 内核的单片机,常见的 M3.M4 和 M7 都可以完美支持.Fire ...

  3. STM32 HAL库开发学习笔记: USART1串口通讯(中断方式) IDE-STM32CubeIDE

    STM32串口通讯有三种方式,分别为阻塞(轮询).中断.DMA.这里将用中断的方式开发. 笔者也是刚入门STM32 HAL库开发,该笔记致希望于能帮到初学者,文中配置步骤.代码.实验现象均是笔者实践可 ...

  4. STM32 HAL库学习笔记1-HAL库简介

    STM32 HAL库学习笔记1-HAL库简介 HAL库 SPL 库 和 HAL 库两者相互独立,互不兼容.几种库的比较如下 目前几种库对不同芯片的支持情况如下 ST 中文官网上有一篇<关于ST库 ...

  5. STM32 HAL库学习笔记2 HAL库介绍

    STM32 HAL库学习笔记2 HAL库介绍 CMSIS标准 一.再次认识HAL库 HAL库设计思想 HAL库实现方式 以GPIO模块为例 GPIO外设数据类型 GPIO外设接口函数 二.使用HAL库 ...

  6. 【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用、接收数据不全的问题)

    [STM32笔记]HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒.串口唤醒和回调无法一起使用.接收数据不全的问题) [STM32笔记]低功耗模式配置及避坑汇总 前文: blog.csd ...

  7. 【STM32笔记】HAL库低功耗模式配置(ADC唤醒无法使用、低功耗模式无法烧录解决方案)

    [STM32笔记]HAL库低功耗模式配置(ADC唤醒无法使用.低功耗模式无法烧录解决方案) [STM32笔记]低功耗模式配置及避坑汇总 一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需 ...

  8. STM32 HAL库学习笔记4-SPI

    STM32 HAL库学习笔记4-SPI 前言 一.SPI协议简介 SPI物理层 SPI协议层 1.基本通讯过程 2. 通讯的起始和停止信号 3. 数据有效性 4. CPOL/CPHA 及通讯模式 二. ...

  9. STM32CUBEMX入门学习笔记3:HAL库以及STM32CUBE相关资料

    STM32CUBEMX入门学习笔记3:HAL库以及STM32CUBE相关资料 微雪课堂:http://www.waveshare.net/study/article-629-1.html 之前的正点原 ...

  10. STM32F407霸天虎HAL库学习笔记——串口发送

    STM32F407霸天虎HAL库学习笔记--串口收发 一.软件准备 二.硬件准备 三.CubeMX配置 四.Keil printf重写 main函数 五.实验效果 一.软件准备 软件准备 二.硬件准备 ...

最新文章

  1. 基2频率抽取实现FFT的Verilog程序
  2. StringBuider 在什么条件下使用效率更高?
  3. 设计模式--门面(Facade)模式
  4. boost::hana::empty用法的测试程序
  5. java.lang.VerifyError解决方案
  6. find命令---Linux学习笔记
  7. html多出的空白页怎么删除,word多出一页空白页怎么删除,这4个方法总有一个能解决,真实挂机网赚项目...
  8. Nginx安装使用及与tomcat实现负载均衡
  9. 由Java编写的在线教育系统源码有何优势?
  10. 设计模式之简单工厂、工厂方法、抽象工厂
  11. Kafka-安装和使用
  12. Linux 安装nginx
  13. 水仙花数素数质因数分解的C语言实现
  14. Linux从零学习记录(四)
  15. 【TS】<T> 泛型
  16. java咖啡机故障5_咖啡机不得不知道的常见故障及解决方法
  17. 在Kmplayer中添加sub字幕过大而挡住视频的解决方法
  18. shell脚本计时方法
  19. 优信电子所有博客汇总(导航搜索)
  20. java类的封装_Java类中的封装

热门文章

  1. 利用Python实现自动投票以及自动爬虫IP代理
  2. harmonyos系统官网,harmonyos系统官网2.0版本
  3. 超速判断java_汽车超速到底是如何判定的?
  4. 新旧版MATLAB中的希尔伯特-黄变换(HHT)及其边际谱的求取问题
  5. 七夕 - 程序员表白代码
  6. 服务器无法远程的原因
  7. 073_SFDC Limit
  8. esp32测试wifi速率
  9. 2020年3月20日阿里内推笔试题
  10. 87键键盘insert键使用方法