LwIP——以太网描述符

news2024/9/21 16:38:43

目录

什么是以太网DMA描述符

TX DMA描述符成员变量简介

RX DMA描述符成员变量简介

以太网DMA描述符结构

 如何追踪描述符

 如何创建Tx/Rx描述符

以太网发送和接收数据流程

总结


在移植LwIP之前有必要了解一下以太网DMA描述符的相关知识,ST以太网模块中的接收/发送FIFO和内存之间的以太网数据包传输是以太网DMA描述符完成的。它们一共有两个描述符列表:一个用于接收,一个用于发送,这两个列表的基地址分别写入DMARDLAR寄存器和DMATDLAR寄存器。关于DMA的相关详细介绍可以参考之前写的一篇博客。

【STM32】DMA原理,配置步骤超详细,一文搞懂DMA_~Old的博客-CSDN博客_dma配置

什么是以太网DMA描述符

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

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

其中,ST把描述符分为两种:

 对应于在HAL库中的头文件stm32f4xx_hal_eth.h中的结构体ETH_DMAInitTypeDef

typedef struct  
{
  __IO uint32_t   Status;           /*!<状态 */
  
  uint32_t   ControlBufferSize;     /*!< 缓冲区1和2的大小 */
  
  uint32_t   Buffer1Addr;           /*!< 缓冲区1的地址 */
  
  uint32_t   Buffer2NextDescAddr;   /*!< 缓冲区2的地址/保存下一个描述符的地址 */
  
  /*!< Enhanced ETHERNET DMA PTP Descriptors 增强描述符相关的内容,比如时间戳和IPV4校验和 */
  uint32_t   ExtendedStatus;        /*!<增强描述符状态 */
  
  uint32_t   Reserved1;             /*!<保留*/
  
  uint32_t   TimeStampLow;          /*!< 时间戳低位 */
  
  uint32_t   TimeStampHigh;         /*!< 时间戳高位 */

} ETH_DMADescTypeDef;

关于TX DMA描述符成员变量的介绍可以参考ST官方的中文参考手册以太网章节:

TX DMA描述符成员变量简介

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

 TDES0[20]:置1,描述符中的第二个地址是下一个描述符地址,ST默认是把该位置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]:描述符中的第二个地址是下一个描述符地址,ST以太网驱动库是默认把该位置1

RSES1[28;16] ;如果RDES0[14]位置1,则该字段无效

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

以太网DMA描述符结构

有两种描述符结构,一种是环形结构,一种是链接结构。在ST提供的以太网驱动库stm32f4xx_hal_eth.c中初始化发送和接收描述符列表就使用的是链接结构,DMA描述符连接结构的具体描述如下图。

  上面第一幅图是对于HAL版本V1.26而言的,下面的是HAL版本V1.27而言。

上面的DMA描述符链接结构可以由一下的函数来实现,如下表所示:

HAL版本        函数
F4_V1.26.0HAL_ETH_DMATxDescListInit()
HAL_ETH_DMARxDescListInit()
F4_V1.27.0/F7_V1.17/H7_V1.10ETH_DMATxDescListInit()
ETH_DMARxDescListInit()

 这些函数应该注意以下几点:

  • 跨度性:一个以太网数据包可以跨越一个或者多个DMA描述符
  • 唯一性:一个DMA描述符只能用于一个以太网数据包
  • 循环性:DMA描述符列表中的最后一个描述符指向第一个,形成循环链式结构。

什么是环形单向链表

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

 如何追踪描述符

为了追踪Tx/Rx的DMA描述符,在下图结构体中定义了两个非常重要的指针变量,如下代码所示:

#if (USE_HAL_ETH_REGISTER_CALLBACKS == 1)
typedef struct __ETH_HandleTypeDef
#else
typedef struct
#endif /* USE_HAL_ETH_REGISTER_CALLBACKS */
{
  ETH_TypeDef                *Instance;     /*!< Register base address       */
  
  ETH_InitTypeDef            Init;          /*!< Ethernet Init Configuration */
  
  uint32_t                   LinkStatus;    /*!< Ethernet link status        */
  
  ETH_DMADescTypeDef         *RxDesc;       /*!< Rx descriptor to Get        */
  
  ETH_DMADescTypeDef         *TxDesc;       /*!< Tx descriptor to Set        */
  
  ETH_DMARxFrameInfos        RxFrameInfos;  /*!< last Rx frame infos         */
  
  __IO HAL_ETH_StateTypeDef  State;         /*!< ETH communication state     */
  
  HAL_LockTypeDef            Lock;          /*!< ETH Lock                    */

#if (USE_HAL_ETH_REGISTER_CALLBACKS == 1)

  void    (* TxCpltCallback)     ( struct __ETH_HandleTypeDef * heth);  /*!< ETH Tx Complete Callback   */
  void    (* RxCpltCallback)     ( struct __ETH_HandleTypeDef * heth);  /*!< ETH Rx  Complete Callback   */
  void    (* DMAErrorCallback)   ( struct __ETH_HandleTypeDef * heth);  /*!< DMA Error Callback      */
  void    (* MspInitCallback)    ( struct __ETH_HandleTypeDef * heth);  /*!< ETH Msp Init callback       */
  void    (* MspDeInitCallback)  ( struct __ETH_HandleTypeDef * heth);  /*!< ETH Msp DeInit callback     */

#endif  /* USE_HAL_ETH_REGISTER_CALLBACKS */

} ETH_HandleTypeDef;

其中的RxDesc和TxDesc两个指针变量指向ETH_DMADescTypeDef结构体,在使用过程中它们两个分别指向下一个要发送或者接收的描述符,如下图所示那样:

 如何创建Tx/Rx描述符

为发送/接收描述符和缓冲区申请内存

这里观看正点原子视频,其实发送/接收描述符本质上也是一个内存块,和缓冲区本质上都是内存。

方法1:使用算法实现的内存申请和释放函数来申请内存

/**
 * @breif       为ETH底层驱动申请内存
 * @param       无
 * @retval      0,正常
 *              1,失败
 */
uint8_t ethernet_mem_malloc(void)
{
    g_eth_dma_rx_dscr_tab = mymalloc(SRAMIN, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef)); /* 申请内存 */
    g_eth_dma_tx_dscr_tab = mymalloc(SRAMIN, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef)); /* 申请内存 */
    g_eth_rx_buf = mymalloc(SRAMIN, ETH_RX_BUF_SIZE * ETH_RXBUFNB); /* 申请内存 */
    g_eth_tx_buf = mymalloc(SRAMIN, ETH_TX_BUF_SIZE * ETH_TXBUFNB); /* 申请内存 */

    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;       /* 申请成功 */
}

方法2:ST官方给出的例程使用数组来实现内存申请

/* Private variables ---------------------------------------------------------*/
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
  #pragma data_alignment=4
#endif
__ALIGN_BEGIN ETH_DMADescTypeDef  DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */

#if defined ( __ICCARM__ ) /*!< IAR Compiler */
  #pragma data_alignment=4
#endif
__ALIGN_BEGIN ETH_DMADescTypeDef  DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */

#if defined ( __ICCARM__ ) /*!< IAR Compiler */
  #pragma data_alignment=4
#endif
__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */

#if defined ( __ICCARM__ ) /*!< IAR Compiler */
  #pragma data_alignment=4
#endif
__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */

其中

/* 初始化发送描述符 */

  HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);


/* 初始化接收描述符  */

  HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], 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;
  
  /* Process Locked */
  __HAL_LOCK(heth);
  
  /*设置状态等于BUSY */
  heth->State = HAL_ETH_STATE_BUSY;
  
  /* 指向第一个描述符 */
  heth->TxDesc = DMATxDescTab;
  
  /* 通过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 ((heth->Init).ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)
    {
      /* Set the DMA Tx descriptors checksum insertion */
      dmatxdesc->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL;
    }
    
    /* 如果小于最大值 */
    if(i < (TxBuffCount-1U))
    {
      /* 描述符的next就指向下一个,否则就指向第一个描述符 */
      dmatxdesc->Buffer2NextDescAddr = (uint32_t)(DMATxDescTab+i+1U);
    }
    else
    {
      /* 最后一个描述符,设置下一个描述符地址寄存器就等于第一个描述符基地址 */ 
      dmatxdesc->Buffer2NextDescAddr = (uint32_t) DMATxDescTab;  
    }
  }
  
  /* 把描述符地址赋值给DMA的寄存器 */
  (heth->Instance)->DMATDLAR = (uint32_t) DMATxDescTab;
  
  /* 设置状态等于Ready */
  heth->State= HAL_ETH_STATE_READY;
  
  /* Process Unlocked */
  __HAL_UNLOCK(heth);
  
  /* Return function status */
  return HAL_OK;
}

以太网发送和接收数据流程

发送:通过网络层下发数据包之后,通过内存拷贝函数,把pbuf数据包拷贝到Tx描述符管理的缓冲区中,缓冲区就保存了要发送的数据包,之后通过以太网的DMA实现存储器到存储器的传输,转移到TX FIFO中,转发到MAC内核,这时候通过介质独立接口发往到PHY设备中,转成电信号/光信号发送出去。

接收:PHY接收到电信号/光信号经过内部的解调和AD的转换,利用介质独立接口发往到MAC内核,再转发到RX FIFO中,通过DMA的存储器到存储器传输到Rx描述符管理的缓冲区中,再拷贝到Rx对应的pbuf中,再往上层网络层发送数据包。

总结

1、TxDesc用来追踪DMA描述符

2、DMA描述符是用来管理缓冲区的

3、缓存区是用来保存数据包的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/80032.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【华为OD机试真题 python】箱子之字形摆放【2022 Q4 | 100分】

■ 题目描述 【箱子之字形摆放】 有一批箱子(形式为字符串,设为str), 要求将这批箱子按从上到下以之字形的顺序摆放在宽度为 n 的空地,请输出箱子的摆放位置。 例如:箱子ABCDEFG,空地宽度为3,摆放结果如图: 则输出结果为:AFGBECD 输入描述 输入一行字符串,通过空…

19. 丢弃法(Dropout)

1. 动机 一个好的模型需要对输入数据的扰动鲁棒&#xff08;不管一张图片加入多少噪音&#xff0c;也能看清这张图片&#xff09; 使用有噪音的数据等价于Tikhonov正则丢弃法&#xff1a;在层之间加入噪音 输入数据加入随机扰动可以防止过拟合&#xff0c;泛化性更好&#xf…

Python学习基础笔记四十四——模块

1、看一个例子&#xff1a; 创建一个demo.py文件&#xff1a; print(in demo.py)def hello():print(in hello function) 然后我们在另外一个文件中import这个demo文件&#xff1a; import demo# 调用demo.py文件中的hello()函数 demo.hello() 注意&#xff0c;demo后面没有…

Linux系统网络编程——第二十节 多路复用之epoll 模型

目录 epoll相关系统调用 1、epoll_create&#xff1a; 2、epoll_ctl() 3、epoll_wait epoll模型原理 epoll的使用场景 各位好&#xff0c;博主新建了个公众号《自学编程村》&#xff0c;拉到底部即可看到&#xff0c;有情趣可以关注看看哈哈&#xff0c;关注后还可以加博主…

Redis原理篇

目录Redis数据结构动态字符串SDS整数集合Intset键值型Dict压缩链表 ZipList快速链表QuickList跳表SkipList对象RedisObjectRedis网络模型Redis通信协议-RESP协议Redis内存回收过期key处理内存淘汰策略Redis数据结构 动态字符串SDS Redis构建了一种新的字符串结构&#xff0c;…

从C#5.0说起:再次总结C#异步调用方法发展史

本篇继续介绍WaitHandler类及其子类 Mutex&#xff0c;ManualResetEvent&#xff0c;AutoResetEvent的用法。 .NET中线程同步的方式多的让人看了眼花缭乱&#xff0c;究竟该怎么去理解呢&#xff1f; 其实&#xff0c;我们抛开.NET环境看线程同步&#xff0c;无非是执行两种操…

软件测试基础知识总览【纯知识,建议收藏慢慢学】

1. 软件测试定义 首先要明确测试的定义,所谓测试,就是以检验产品是否满足需求为目标。 而软件测试,自然是为了发现软件(产品)的缺陷而运行软件(产品) 比较标准的软件测试的定义是:在规定的条件下对程序进行操作&#xff0c;以发现错误&#xff0c;对软件质量进行评估。 IEE…

算法总结,不断更新

文章目录摩尔投票法DFS算法BFS算法题源来自于力扣网 摩尔投票法 适用场景 如何在选票无序的情况下&#xff0c;选出获胜者。 例题&#xff1a; 找出数组中&#xff0c;出现次数超过总数一半的数字&#xff08;出现次数 > n/2&#xff09;。 输入&#xff1a;[1,1,3,2,4,6,…

10000字吐血总结+24张图带你彻底弄懂线程池

大家好。今天跟大家聊一聊无论是在工作中常用还是在面试中常问的线程池&#xff0c;通过画图的方式来彻底弄懂线程池的工作原理&#xff0c;以及在实际项目中该如何自定义适合业务的线程池。 一、什么是线程池 线程池其实是一种池化的技术的实现&#xff0c;池化技术的核心思…

MVC|JAVA|SSM框架计算机硬件评测交流平台的开发和实现

收藏点赞不迷路 关注作者有好处 文末获取源码 项目编号&#xff1a;BS-PT-070 一&#xff0c;项目简介 计算机硬件在社会上有很多广泛的发烧友&#xff0c;他们急需一个发布专业硬件测评数据的平台并进行交流互动的社区。本次开发实现的计算机硬件交流平台就是作为一个专业的…

Android序列化之Parcel源码分析(2)

文章目录1.Parcel.java2.Parcelable和Parcel的关系3.Parcel写入数据源码分析3.1.java层Parcel创建3.2.native层Parcel创建3.3写入IBinder接口标识符3.4写入String数据4.Parcel读取数据源码分析4.1获取IBinder接口标识符4.2读取String数据1.Parcel.java Android可以通过Parcel进…

【OpenCV学习】第15课:处理卷积边缘问题

仅自学做笔记用,后续有错误会更改 &#xff08;卷积的概念可以看看第14课&#xff09; 理论 卷积边缘问题&#xff1a;从下图最右方的结果可以看出&#xff0c;卷积操作之后&#xff0c; 剩余的绿色像素部分&#xff0c; 我们是没有处理到的 那么如何处理这个问题呢&#xf…

论文3:查找文献在指定期刊的引用格式

文章目录说明&#xff1a;1.谷歌学术搜索&#xff08;可以用一些国内的镜像&#xff09;&#xff0c;并点击被引用次数2.勾选在引用文章中搜索&#xff0c;并在搜索框搜索指定期刊的关键词3.这里指定期刊是RAL即IEEE Robotics and Automation Letters4.任意点开上图中的一篇文章…

支付宝当面付网站对接支付教程

有很多人会开支付宝当面付但是不会配置它老会出现一下情况 第二种情况如下&#xff1a; 如果遇到以上情况可以按照我的步骤就可以解决 详细步骤&#xff1a; 一、应用APPID获取方法 1.打开网站&#xff1a;https://openhome.alipay.com/platform/developerIndex.htm&#x…

Canal配置多个实例以及将Mysql指定表的binlog导入指定的Kafka的Topic

Canal配置多个实例以及将Mysql指定表的binlog导入指定的Kafka的Topic 进入Canal的conf目录 复制模板配置文件 cp -r example/ Ordercp -r example/ Orderdetail修改canal.propertieswenjain vim canal.properties修改内容如下&#xff0c;指定输出模式为kafka canal.serverM…

【元胞自动机】心房颤动/扑动模型研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

(附源码)ssm学校疫情服务平台 毕业设计 291202

ssm学校疫情服务平台 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对学校疫情服务平台等问…

【数电实验】触发器及其应用

实验三 触发器及其应用 一 实验目的 1 了解触发器的触发方式&#xff08;上升沿触发、下降沿出发&#xff09;及其触发特点&#xff1b; 2 测试常用触发器的逻辑功能&#xff1b; 3 掌握用触发器设计同步时序逻辑电路的方法。 二 实验内容 1 测试双D触发器74HC74的逻辑功能…

手工编译konsole备忘

背景 系统自带的终端弱爆了&#xff0c;本来想编译深度终端的&#xff0c;但DTK风格的程序在非DDE桌面&#xff08;应该是dde_kwin这个窗管的问题&#xff09;巨难看&#xff0c;无意中添加了Konsole&#xff0c;发现已经有我需要使用的右键打开当前目录文件管理器的功能。 …

Go context.Context的学习

一、前言 Golang context是Golang应用开发常用的并发控制技术&#xff0c;它与WaitGroup最大的不同点是context对于派生goroutine有更强的控制力&#xff0c;它可以控制多级的goroutine。 context翻译成中文是”上下文”&#xff0c;即它可以控制一组呈树状结构的goroutine&a…