【STM32】以太网DMA描述符

武汉加油!中国加油!


1、什么是以太网DMA?

学过STM32的同学都应该知道DMA就是不需要CPU的参与就能够实现内存和外设之间的数据交换,同样的,对于STM32互联型单片机的以太网DMA的作用也是如此,它的作用就是在不需要CPU的参与下,实现内存和以太网外设的数据交换。

用通俗一点的话来表述,就是我们将要发送的数据放到一片内存去,告诉以太网DMA,我已经将数据放过去了,你去取出来发送到网络中去吧。当网络数据来了的时候,以太网DMA自动将数据拷贝到一片内存中,产生中断告诉CPU,数据来了,你去取出来吧。

2、DMA描述符的本质是什么?

在STM32的参考手册中,我们可以找到发送描述符的定义,如下:
在这里插入图片描述
一眼看上去好像很复杂,这些是寄存器吗?但是找了一大堆文档,也没有找到它的寄存器地址,因为它根本就不是什么寄存器,而是4个32Bit的内存。对的,你去找发送描述符的硬件结构,是肯定找不到的,因为它完全是纯软件的概念,它的本质就是我们自己用结构体来实现这个描述符,然后将描述符的首地址写入到【ETH_DMATDLAR】寄存器中,STM32就知道这片内存是用来作为发送描述符了。

发送描述符的主要作用就是用来记录发送缓冲区的大小,缓冲区的地址,还有这个缓冲区的状态等等,里面有很多信息,这些信息是用来协同CPU和DMA二者之间工作的,我把他们的功能简要的写了出来,如下所示:

TDES0主要用来表示描述符的状态和控制信息
TDES1表示该描述符缓冲区数据的有效长度
TDES2表示描述符缓冲区的地址,我们要发送的数据,就是放在这个地址所指向的内存中
TDES3表示下一个描述符的地址

我们通过定义一个结构体来实现这个发送描述符,如下所示

typedef struct  {
  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 */
} ETH_DMADESCTypeDef;

这个结构体的本质就是4个32Bit的变量,和STM32参考手册中定义的一样。
Status用来表示该描述符的状态
ControlBufferSize表示该描述符缓冲区数据的长度
Buffer1Addr用来存放该描述符缓冲区的地址,我们要发送的数据,就是放在这个地址开始的内存中
Buffer2NextDescAddr表示下一个描述符的地址

当我们需要发送数据的时候,我们把数据拷贝到发送描述符的缓冲区中(Buffer1Addr),告诉DMA我们拷贝完成了,DMA就会从发送描述符的缓冲区中取数据,将数据通过以太网外设发送到网络中去。

同样地,以太网外设接收到了网络中的数据时,DMA自动拷贝数据到接收描述符的缓冲区中(Buffer1Addr),产生中断告诉CPU,有数据来了,我们就可以取出描述符的数据,从而知道接收到了什么。

3、DMA和CPU如何有序地访问描述符?

上面我们说了,描述符是的本质就是4个32位的内存,描述符的Status表示该描述符的状态和控制信息。因为描述符是DMA和CPU二者之间的共享内存,既然是共享资源,就需要进行保护,当DMA在使用的时候,CPU就不能使用,当CPU在使用的时候,DMA就不能使用。这个使用权的控制,就通过TDES0寄存器中的OWN位体现出来,对应到发送描述符结构体就是Status变量的最高位。
在这里插入图片描述
我们看上图可知,当OWN位为0的时候,表示CPU可以将要发送的数据拷贝到描述符中,拷贝完成以后,我们手动将描述符的OWN位设置为1,以此来告诉DMA控制器,我已经拷贝完数据了,你可以从描述符中取出数据进行发送了。这时候DMA就会取出描述符中的数据,将数据发送出去,DMA在操作完描述符以后,自动将OWN位设置为0,告诉CPU,我DMA已经发送完数据啦,你可以拷贝下一帧数据到描述符上了。整个发送的过程就是这样配合的。

4、发送的数据实际上放在哪里?

发送DMA描述符只有4个32Bit的内存空间,这点内存肯定放不下我们要发送的以太网数据帧,那么我们要发送的数据实际上是存在描述符的哪里呢?
在这里插入图片描述
TDES2就是用来设置存放要发送数据缓冲区的首地址的,TDES1是用来告诉DMA这个缓冲区有效数据长度的。我们可以开辟一个数组,将数组的首地址写入Buffer1Addr中,这样就设置好了发送描述符实际存放数据的内存地址,我们往这个数组中写入要发送的数据,再把数据长度写入ControlBufferSize中,DMA就可以从这片内存中取ControlBufferSize长度的数据出来进行发送了。

例如,我们要发送一帧512字节的数据,那么我们就需要先建立一个至少大于512字节的数组,将要发送的数据拷贝到这个数组里面,然后设置这个描述符的数据长度是512即可,如下图所示。
建立发送数据和描述符的关系

5、要发送的数据长度超出一个描述符能够存放的最大长度怎么办?

如果我们发送的一帧数据很大,一个描述符没有办法放下那么多数据,应该怎么办呢?这时候就需要用到链表,将这帧以太网数据分割位若干部分,分别存放在多个描述符里面,描述符之间用链表的形式建立连接。说起来有点抽象,我们举个例子。

例如,有4K字节的一帧数据要发送出去,但是每一个描述符的缓冲区大小只有1K,这时候就需要用4个描述符来存储要发送的这一帧数据,请看下图。我们把第一个1K的数据放入描述符中,并且设置它的TEDS0寄存器的FS位为1,表示这个描述符存储了数据帧的第一个分块,把最后1K的数据放入描述符中,设置它的TDES0寄存器的LS位为1,表示这是该帧数据的最后一个分块。这样DMA就能够根据这些信息组合出一条完整的数据帧,进行发送。
在这里插入图片描述


本文写于2020年2月12日。

  • 10
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值