观察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
for example:
ETH_HandleTypeDef heth;
参见CubeMX自动添加的外设源文件eth.c的最开头部分
参见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界面上点选好了,生成代码时就会自动生成,比如
这个函数有点长,还是粘贴上来吧
/**
* @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函数也分析完毕。
(##) 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小节已经分析过,因此略去。
(##) 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类似。这里先不展开来看。
(##) 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);
(##) 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描述符。先略一下
(##) HAL_ETH_GetReceivedFrame(); (should be called into an infinite loop)
有时间再展开写,简单来说就是从链式描述符中读出来每一帧
(##) HAL_ETH_GetReceivedFrame_IT(); (called in IT mode only)
跟上一个函数的区别是从中断函数中得到数据帧。
(##) Read a specific register from the PHY
HAL_ETH_ReadPHYRegister();
(##) Write data to a specific RHY register:
HAL_ETH_WritePHYRegister();
简单说来就是如何读取 / 写入外部PHY芯片的寄存器。
HAL_ETH_ConfigMAC(); all MAC parameters should be filled.
貌似是单独配置MAC,之前的ETH_MACDMAConfig既可以配MAC又可以配DMA,跟这个的功能可能有重叠。
HAL_ETH_ConfigDMA(); all DMA parameters should be filled.
貌似是单独配置DMA,之前的ETH_MACDMAConfig既可以配MAC又可以配DMA,跟这个的功能可能有重叠。