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

观察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 PHY
                                                           The 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_MDC
    PA1     ------> ETH_REF_CLK
    PA2     ------> ETH_MDIO
    PA7     ------> ETH_CRS_DV
    PC4     ------> ETH_RXD0
    PC5     ------> ETH_RXD1
    PB11     ------> ETH_TX_EN
    PG13     ------> ETH_TXD0
    PG14     ------> 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,跟这个的功能可能有重叠。

猜你喜欢

转载自blog.csdn.net/wofreeo/article/details/89217601