上一章实现的MAC数据包的基础收发功能,但是只是简单的操作了ETH外设的收发包函数并没有深入了解其中的原理逻辑,本章结合STM32F40x文档与STM32F4x7_ETH_Driver驱动库了解MAC的收发包流程。
一、描述符列表
在创建描述符列表之前先了解描述符列表的定义,描述符就软件来说就是一个结构体,而描述符列表分为两种结构。
①环形结构(环形队列)
②链接结构(环形链表)
这两种数据结构可以参考网上其他的说明,了解数据结构的话对这两种结构应该很熟悉了。
驱动库中定义的描述符结构体如下:
/** * @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 */ #ifdef USE_ENHANCED_DMA_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 */ #endif /* USE_ENHANCED_DMA_DESCRIPTORS */ } ETH_DMADESCTypeDef;
结合代码与手册图,TDES0 为DMA描述符状态位,TDES1为控制位和对RDES2、RDES3的字节计数。使用环形结构时TDES3 作为缓冲区2的地址,使用链接结构时TDES3作为下一个描述符的地址。
Tx描述符与Rx描述符在TDES0 不同,RDES1、RDES2、RDES3都是相同的。
MAC的发送与接收主要就是对DMA描述符的读写操作。
USE_ENHANCED_DMA_DESCRIPTORS 为定义使用增强描述符,具体参考STM32F4xx手册。
接下来就是具体的MAC发送、接收以及描述符相关操作的代码分析了。
二、链接结构描述符创建
ETH驱动库创建的描述符列表是环形链接结构的。
/* Initialize Tx Descriptors list: Chain Mode */ ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); /* Initialize Rx Descriptors list: Chain Mode */ ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
ethernetif.c中,low_level_init()函数在配置完MAC地址之后,创建了Tx和Rx的描述符链表。使用到的参数如下:
/* Ethernet Rx & Tx DMA Descriptors */ extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; /* Ethernet Driver Receive buffers */ extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Driver Transmit buffers */ extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
//stm32f4x7_eth.c #define ETH_RXBUFNB 4 /* 4 Rx buffers of size ETH_RX_BUF_SIZE */ #define ETH_TXBUFNB 4 /* 4 Tx buffers of size ETH_TX_BUF_SIZE */
首先是创建Tx和Rx描述符的第一参数:DMARxDscrTab[ETH_RXBUFNB]和DMATxDscrTab[ETH_TXBUFNB]为两个结构体数组,数组的成员为描述符列表的成员,也就是说创建了有4个节点Rx描述符链表和4个节点Dx描述符链表。
//stm32f4x7_eth.c #ifdef CUSTOM_DRIVER_BUFFERS_CONFIG /* Redefinition of the Ethernet driver buffers size and count */ #define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */ #define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */ #define ETH_RXBUFNB 4 /* 4 Rx buffers of size ETH_RX_BUF_SIZE */ #define ETH_TXBUFNB 4 /* 4 Tx buffers of size ETH_TX_BUF_SIZE */ #endif
/** @defgroup ENET_Buffers_setting * @{ */ #define ETH_MAX_PACKET_SIZE 1524 /*!< ETH_HEADER + ETH_EXTRA + VLAN_TAG + MAX_ETH_PAYLOAD + ETH_CRC */ #define ETH_HEADER 14 /*!< 6 byte Dest addr, 6 byte Src addr, 2 byte length/type */ #define ETH_CRC 4 /*!< Ethernet CRC */ #define ETH_EXTRA 2 /*!< Extra bytes in some cases */ #define VLAN_TAG 4 /*!< optional 802.1q VLAN Tag */ #define MIN_ETH_PAYLOAD 46 /*!< Minimum Ethernet payload size */ #define MAX_ETH_PAYLOAD 1500 /*!< Maximum Ethernet payload size */ #define JUMBO_FRAME_PAYLOAD 9000 /*!< Jumbo frame payload size */
以上是定义的Rx缓冲区1以及Tx缓冲区1的大小(1524byte),4 Tx buffers 也就是对应了Rx和Tx描述符的环形链表结构中的4个节点。
第二个参数是链表第一个节点的数据(缓存)地址。
//stm32f4x7_eth.c __align(4) uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */ __align(4) uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */
为Rx和Tx缓冲区分别开辟了一段内存,创建了Rx_Buff和Tx_Buff两个4行1524列的二维数组,也就是第[0:3]行对应描述符[0:3]缓冲区1的地址。1524为MAC数据包的最大帧大小。
第三个参数是链表的节点计数,与Rx_Buff和Tx_Buff的行参数一致。
创建描述符链表的函数如下:
//stm32f4x7_eth.c /** * @brief Initializes the DMA Tx descriptors in chain mode. * @param DMATxDescTab: Pointer on the first Tx desc list * @param TxBuff: Pointer on the first TxBuffer list * @param TxBuffCount: Number of the used Tx desc in the list * @retval None */ void ETH_DMATxDescChainInit(ETH_DMADESCTypeDef *DMATxDescTab, uint8_t* TxBuff, uint32_t TxBuffCount) { uint32_t i = 0; ETH_DMADESCTypeDef *DMATxDesc; /* Set the DMATxDescToSet pointer with the first one of the DMATxDescTab list */ DMATxDescToSet = DMATxDescTab; /* Fill each DMATxDesc descriptor with the right values */ for(i=0; i < TxBuffCount; i++) { /* Get the pointer on the ith member of the Tx Desc list */ DMATxDesc = DMATxDescTab + i; /* Set Second Address Chained bit */ DMATxDesc->Status = ETH_DMATxDesc_TCH; /* Set Buffer1 address pointer */ DMATxDesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]); /* Initialize the next descriptor with the Next Descriptor Polling Enable */ if(i < (TxBuffCount-1)) { /* Set next descriptor address register with next descriptor base address */ DMATxDesc->Buffer2NextDescAddr = (uint32_t)(DMATxDescTab+i+1); } else { /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ DMATxDesc->Buffer2NextDescAddr = (uint32_t) DMATxDescTab; } } /* Set Transmit Desciptor List Address Register */ ETH->DMATDLAR = (uint32_t) DMATxDescTab; }
简单分析下代码
/* Set the DMATxDescToSet pointer with the first one of the DMATxDescTab list */ DMATxDescToSet = DMATxDescTab;
首先将DMATxDescToSet(DMA当前发送描述符)设置为DMATxDescTab的以一个元素,也就是等于描述符0(创建的描述符结构体数组的第一个元素)。
接下来的for循环就是创建环形链接描述符的具体实现了
/* Set Second Address Chained bit */ DMATxDesc->Status = ETH_DMATxDesc_TCH;
这一行初始化描述符的状态(TDES0),将TCH(位20)置1,其他值0,表示TDES3的数据为下一个描述符的地址。
/* Set Buffer1 address pointer */ DMATxDesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);
这一行为每一个链表中的描述符的缓冲区1设置了内存地址。
接下来的if语句用于判断当前节点是否为最后一个节点,中间节点时将当前描述符的TDES3(数据缓冲区2)指向下一个描述符的地址,也就是DMATxDescTab数组元素的下一个,当前节点为最后一个描述符时,将描述符的TDES2指向第一个节点(DMATxDescTab的地址)。
/* Set Transmit Desciptor List Address Register */ ETH->DMATDLAR = (uint32_t) DMATxDescTab;
在发送描述符配置完成后,就将配置完成的描述符链表设置到发送描述符地址寄存器中,完成发送描述符初始化。
标签:DMA,Tx,Rx,STM32F407MAC,uint32,描述符,ETH,以太网,SIZE From: https://www.cnblogs.com/fuyunxiansen/p/18034641