lwIP——以太网DMA描述符

目录

1.什么是以太网DMA描述符?

2.以太网DMA描述符结构 

3.如何追踪描述符

4.总结归纳 

相关代码

老版本

①内存申请:

②描述符初始化:

代码详解(以发送描述符为例):

新版本(区别就是直接操作pbuf,pbuf贯穿于lwIP各层之间)

①为发送 / 接收描述符申请内存

②追踪 RX/TX 描述符 

③初始化 RX/TX 描述符 

代码详解(以发送描述符为例)

④建立描述符与pbuf缓冲区的联系


1.什么是以太网DMA描述符?

发送:不需要CPU的参与下,把描述符指向的缓冲区数据传输到Tx FIFO当中
接收:不需要CPU的参与下,将Rx FIFO中的数据传输到描述符指向的缓冲区当中

目前使用常规描述符

常规描述符结构(精简):

typedef struct 
{ 
	__IO uint32_t Status; 		/* 状态 */ 
	uint32_t ControlBufferSize; 	/* 缓冲区1和2的大小 */ 
	uint32_t Buffer1Addr; 		/* 缓冲区1的地址 */ 
	uint32_t Buffer2NextDescAddr; /* 缓冲区2的地址/指向下一个描述符 */ 
	/* 以下成员变量为增强描述符的 */ 
    	/* ……………………*/
} 
ETH_DMADescTypeDef;


TX DMA描述符成员变量:

 ● TDES0[31]置0:CPU可将数据拷贝到描述符中,拷贝完成之后把该位置1,告诉DMA可以发送数据

● TDES0[20]置1:描述符中的第二个地址是下一个描述符地址

● TDES1[28:16]:如果TDES0[20] 位置1,则该字段无效

● TDES3[31:0]:取决于TDES0[20]的值,为1,则指向下一个描述符地址


RX DMA描述符成员变量 :

● RDES0[31]置1:MAC将数据从RX FIFO传输到RX描述符中,拷贝完成之后该位置0,告诉CPU可以接收数据

● RDES0[14]置1:描述符中的第二个地址是下一个描述符地址

● RDES1[28:16]:如果RDES0[14] 位置1,则该字段无效

● RDES3[31:0]:取决于RDES0[14]的值,为1,则指向下一个描述符地址


 


2.以太网DMA描述符结构 

环形单向链表 :将一系列节点链接成链,并且单链表的最后一个节点指向链表的第一个节点,构成环状的链表

注意:
旧版本HAL库:申请一个新的缓冲区,DMA描述符指向它,然后将网络层下达的数据(pbuf缓冲区的内容)拷贝其中

新版本HAL库:直接管理pbuf缓冲区,不需要另外申请新的缓冲区



以太网DMA描述符注意细节 :

 
 


3.如何追踪描述符

ETH_HandleTypeDef中定义了RxDesc和TxDesc指针,它们是用来追踪Rx/Tx的DMA描述符(旧版本)

ETH_InitTypeDef中定义了RxDesc和TxDesc指针,它们是用来追踪Rx/Tx的DMA描述符(新版本) 

新旧版本原理是一样的 

TxDesc和RxDesc分别指向下 一个要发送或者接收的描述符


 


4.总结归纳 

 还是要注意:

老版本是申请一个缓存区,将pbuf缓存区的数据拷贝进去,再进行传输

新版本不用申请缓存区,直接操作pbuf


相关代码

老版本

①内存申请:

uint8_t ethernet_mem_malloc(void)
{
    // 为接收描述符表申请内存,内存区域为SRAMIN,大小为接收缓冲区数量乘以接收描述符类型的大小
    g_eth_dma_rx_dscr_tab = mymalloc(SRAMIN, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef)); 
    // 为发送描述符表申请内存,内存区域为SRAMIN,大小为发送缓冲区数量乘以发送描述符类型的大小
    g_eth_dma_tx_dscr_tab = mymalloc(SRAMIN, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef)); 

    // 为接收缓冲区申请内存,内存区域为SRAMIN,大小为接收缓冲区大小乘以接收缓冲区数量
    g_eth_rx_buf = mymalloc(SRAMIN, ETH_RX_BUF_SIZE * ETH_RXBUFNB); 
    // 为发送缓冲区申请内存,内存区域为SRAMIN,大小为发送缓冲区大小乘以发送缓冲区数量
    g_eth_tx_buf = mymalloc(SRAMAM, ETH_TX_BUF_SIZE * ETH_TXBUFNB); 

    // 如果接收描述符表、发送描述符表、接收缓冲区或发送缓冲区的指针为NULL(即内存分配失败)
    if (!( (uint32_t)&g_eth_dma_rx_dscr_tab ||!(uint32_t)&g_eth_dma_tx_dscr_tab ||!(uint32_t)&g_eth_rx_buf ||!(uint32_t)&g_eth_tx_buf) )
    {
        // 释放已分配的内存
        ethernet_mem_free(); 
        // 返回失败标志
        return 1; 
    }

    // 如果所有内存都分配成功,返回成功标志
    return 0; 
}


②描述符初始化:

HAL_ETH_DMATxDescListInit(&g_eth_handler,g_eth_dma_tx_dscr_tab,g_eth_tx_buf,ETH_TXBUFNB); /* 初始化发送描述符 */
HAL_ETH_DMARxDescListInit(&g_eth_handler,g_eth_dma_rx_dscr_tab,g_eth_rx_buf,ETH_RXBUFNB); /* 初始化接收描述符 */
代码详解(以发送描述符为例):
HAL_StatusTypeDef HAL_ETH_DMATxDescListInit(ETH_HandleTypeDef    *heth,
                                            ETH_DMADescTypeDef   *DMATxDescTab,
                                                       uint8_t   *TxBuff,
                                                      uint32_t   TxBuffCount)
{
    uint32_t i = 0U;
    ETH_DMADescTypeDef *dmatxdesc;

    // 1. 设置状态等于BUSY
    heth->State = HAL_ETH_STATE_BUSY;

    // 2. 指向第一个描述符
    heth->TxDesc = DMATxDescTab;

    // 3. 通过for循环来添加每一个描述符
    for(i=0U; i < TxBuffCount; i++)
    {
        // 获取Tx Desc列表的第i个成员的指针
        dmatxdesc = DMATxDescTab + i;

        // TDES0[20]置1:设置第二个地址链接位
        dmatxdesc->Status = ETH_DMATXDESC_TCH;

        // 给描述符的buffer赋值地址
        dmatxdesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);

        // 如果小于最大值
        if(i < (TxBuffCount-1U))
        {
            // 描述符的next就指向下一个,否则就指向第一个描述符
            dmatxdesc->Buffer2NextDescAddr = (uint32_t)(DMATxDescTab+i+1U);
        }
        else
        {
            // 最后一个描述符,设置下一个描述符地址寄存器等于第一个描述符基地址
            dmatxdesc->Buffer2NextDescAddr = (uint32_t)DMATxDescTab;
        }
    }

    // 4. 把描述符的地址赋值给DMA的寄存器
    (heth->Instance)->DMATDLAR = (uint32_t)DMATxDescTab;

    // 5. 设置状态等于Ready
    heth->State = HAL_ETH_STATE_READY;

    return HAL_OK;
}

新版本(区别就是直接操作pbuf,pbuf贯穿于lwIP各层之间

①为发送 / 接收描述符申请内存

ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];    /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];    /* Ethernet Tx DMA Descriptors */
ETH_RX/TX_DESC_CNT = 4

②追踪 RX/TX 描述符 

g_eth_handler.Init.RxDesc = DMARxDscrTab;
g_eth_handler.Init.TxDesc = DMATxDscrTab;

③初始化 RX/TX 描述符 

/*------------------- DMA Tx Descriptors Configuration -------------------*/
ETH_DMATxDescListInit(heth);

/*------------------- DMA Rx Descriptors Configuration -------------------*/
ETH_DMARxDescListInit(heth);
代码详解(以发送描述符为例)
static void ETH_DMATxDescListInit(ETH_HandleTypeDef *heth)
{
    ETH_DMADescTypeDef *dmatxdesc;
    uint32_t i;

    // 通过for循环来添加每一个描述符
    for (i = 0; i < (uint32_t)ETH_TX_DESC_CNT; i++)
    {
        // 指向第一个描述符
        dmatxdesc = heth->Init.TxDesc + i;
        // 对描述符中的成员变量清零
        WRITE_REG(dmatxdesc->DESC0, 0x0);
        WRITE_REG(dmatxdesc->DESC1, 0x0);
        WRITE_REG(dmatxdesc->DESC2, 0x0);
        WRITE_REG(dmatxdesc->DESC3, 0x0);
        // 描述符链表指针指向当前描述符
        WRITE_REG(heth->TxDescList.TxDesc[i], (uint32_t)dmatxdesc);
        // TDES0[20]置1:设置第二个地址链接位
        SET_BIT(dmatxdesc->DESC0, ETH_DMATXDESC_TCH);

        if (i < ((uint32_t)ETH_TX_DESC_CNT - 1U))
        {
            // 描述符的next就指向下一个,否则就指向第一个描述符
            WRITE_REG(dmatxskip->DESC3, (uint32_t)(heth->Init.TxDesc + i + 1U));
        }
        else
        {
            // 最后一个描述符,设置下一个描述符地址寄存器等于第一个描述符基地址
            WRITE_REG(dmatxdesc->DESC3, (uint32_t)(heth->Init.TxDesc));
        }

        // 设置DMA Tx描述符校验和插入
        SET_BIT(dmatxdesc->DESC0, ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL);
    }

    // 设置TX描述符链表当前索引为0
    heth->TxDescList.CurTxDesc = 0;

    // 设置传输描述符列表地址
    WRITE_REG(heth->Instance->DMATDLAR, (uint32_t)heth->Init.TxDesc);
}

④建立描述符与pbuf缓冲区的联系

static ETH_StatusTypeDef ETH_Prepare_Tx_Descriptors(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig, uint32_t ItMode)
{
    ETH_TxDescListTypeDef *dmatxdesclist = &heth->TxDescList;
    uint32_t descidx = dmatxdesclist->CurTxDesc;
    uint32_t firstdescdma = descidx;
    uint32_t idx;
    uint32_t descnbr = 0U;
    ETH_DMADescTypeDef *dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];
    ETH_BufferTypeDef *txbuffer = pTxConfig->TxBuffer;
    uint32_t bd_count = 0U;

    descnbr += 1U;

    // 如果descnbr为1,它就是指向第一个描述符,并且指向缓冲区1
    if (descnbr == 1U)
    {
        WRITE_REG(dmatxdesc->DESC2, (uint32_t)txbuffer->buffer);
        // 设置描述符指向的缓冲区大小
        MODIFY_REG(dmatxdesc->DESC1, ETH_DMATXDESC_TBS1, txbuffer->len);
        // 设置第一个描述符的OWN位
        SET_BIT(dmatxdesc->DESC0, ETH_DMATXDESC_OWN);
    }

    // 只有当报文被分成多个描述符(>1时)
    while (txbuffer->next!= NULL)
    {
        // descidx索引加一
        INCR_TX_DESC_INDEX(descidx, 1U);
        // 指向第二个描述符
        dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];

        descnbr += 1U;

        // 在列表中获得下一个tx缓冲区
        txbuffer = txbuffer->next;

        // 设置该描述符指向的tx缓冲区
        WRITE_REG(dmatxdesc->DESC2, (uint32_t)txbuffer->buffer);
        // 设置描述符指向的缓冲区大小
        MODIFY_REG(dmatxdesc->DESC1, ETH_DMATXDESC_TBS1, txbuffer->len);

        bd_count += 1U;

        // 设置描述符的OWN位
        SET_BIT(dmatxdesc->DESC0, ETH_DMATXDESC_OWN);
    }

    // 以下忽略多行代码
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值