发新帖本帖赏金 80.00元(功能说明)我要提问
返回列表
打印
[开发工具]

揭秘ETH模块中的DMA:让数据飞起来的“搬运工”全解析

[复制链接]
826|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
DKENNY|  楼主 | 2025-5-26 16:10 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 DKENNY 于 2025-5-26 16:15 编辑

#申请原创# #技术资源#  @21小跑堂

前言
      今天我们继续讲讲ETH这玩意儿,我们知道ETH在使用的时候,会有个专门的DMA使用,文档里说以太网通信需要DMA,代码里要配置DMA,但DMA到底是干啥的?为啥以太网非得用它?它跟其他外设(比如串口USART)的DMA有啥不一样?不用DMA行不行?

1、引言:DMA是个啥?为啥以太网离不开它?
      如果我们在用APM32F407开发一个联网设备,比如一个能跟远程服务器聊天的智能传感器,我们的程序要发送一大堆数据(比如传感器读数)到服务器,或者从服务器接收一堆数据,这时候,APM32F407的以太网模块就像一个“快递公司”,负责把数据打包、发送,或者接收、拆包。但问题来了:这些数据量可能很大,频繁地搬来搬去会让CPU忙得喘不过气。怎么办?这就是 DMA 登场的时候!
      DMA(Direct Memory Access,直接内存访问)就像快递公司里的“自动搬运机器人”。它可以在CPU“偷懒”的时候,自动把数据从一个地方(比如内存)搬到另一个地方(比如以太网模块),或者反过来搬回来。有了DMA,CPU就不用亲自操心数据的搬运,可以专心干别的活儿(比如处理传感器数据)。
      在以太网通信中,DMA的角色尤其重要,因为以太网数据量大、速度快,手动搬运根本忙不过来。这篇文章将带你搞清楚:
        1. DMA在以太网模块中具体干了啥?
        2. 它传输的是什么数据?
        3. 跟普通外设(像USART)的DMA有啥区别?
        4. 它怎么帮单片机跟服务器通信?
        5. 不用DMA会发生什么?

2、DMA的基础知识:从“搬运工”到“超级助手”

2.1 DMA是什么?
      DMA是“直接内存访问”的缩写,顾名思义,它能直接访问内存,把数据从一个地方搬到另一个地方,而不需要CPU亲自参与。正常情况下,CPU就像个“勤劳的搬运工”,每次都要亲自把数据从内存读出来,写到外设(比如以太网模块)里,或者反过来。但如果数据量很大,CPU就会累得不行,效率也低。
      DMA就像一个聪明的“机器人”,可以接管搬运任务。它的工作方式是:
        - CPU通知DMA:“你把这块内存的数据搬到以太网模块去!”
        - DMA就自动开始搬运,搬完后通知CPU:“活儿干完了!”
        - CPU在这期间可以干别的,比如计算、控制LED,或者啥也不干(省电!)。

2.2 为什么以太网需要DMA?
      以太网通信跟其他外设(比如串口)比起来,有几个“硬核”特点:
        - 数据量大:一个以太网数据包(帧)可能有几十到上千字节,远超串口的几十字节。
        - 速度快:以太网支持10Mbps或100Mbps,数据来得又快又猛,CPU手动搬运根本跟不上。
        - 实时性强:网络通信讲究“低延迟”,如果CPU忙着搬数据,可能会错过关键时机,导致丢包。

      DMA的出现完美解决了这些问题。这玩意儿就是一个“超级搬运工”,能快速、批量地把数据搬来搬去,让以太网通信又快又稳。

      图1:DMA在以太网通信中的角色


3、DMA在APM32F407以太网模块中的作用:它具体干了啥?
      APM32F407的以太网模块内置了MAC(Media Access Control,介质访问控制),负责处理以太网帧的逻辑(打包、拆包、校验等)。但MAC只是个“打包员”,它需要有人把数据送到它手里,或者把收到的数据搬走。这就是DMA的舞台!

3.1 DMA传输的内容:搬运的是啥?
      在以太网通信中,DMA主要搬运的是以太网数据帧(Ethernet Frame)。一个以太网帧包含:
        - 前导码:用来同步信号。
        - 目标地址和源地址:标明数据发给谁、从哪儿来。
        - 类型字段:说明数据是什么类型(例如IP包)。
        - 数据载荷:真正的“货物”,例如你的传感器数据或服务器的响应。
        - 校验码(CRC):用来检查数据有没有出错。

      DMA的任务是:
        - 发送时:把内存里准备好的以太网帧(包括头、数据和校验码)搬到MAC的发送缓冲区(TX Buffer)。
        - 接收时:把MAC接收缓冲区(RX Buffer)里的以太网帧搬到内存,供程序处理。

3.2 以太网DMA的工作流程
      APM32F407的以太网模块有专门的DMA控制器,分为 发送DMA(TX DMA)接收DMA(RX DMA)。以下是它们的工作流程:

      发送流程
      1. 程序准备数据
        - 你的程序(比如用LwIP协议栈)生成一个数据包,比如一个UDP包(“Hello, Server!”)。
        - 数据包被封装成以太网帧,存储在内存的某个区域(比如一个数组)。
      2. 配置DMA:
        - CPU告诉TX DMA:“把内存地址0x20001000开始的1500字节数据搬到MAC的TX Buffer!”
        - 配置包括源地址(内存)、目标地址(TX Buffer)、数据长度等。
      3. DMA搬运:
        - TX DMA自动从内存读取数据,搬到MAC的发送缓冲区。
        - 搬运过程中,CPU可以干别的(比如处理传感器数据)。
      4. MAC发送:
        - MAC把缓冲区的数据通过PHY(物理层芯片)发送到网线。
        - 发送完成后,DMA通知CPU:“数据发完了!”

      接收流程
      1. MAC接收数据:
        - PHY从网线接收到数据帧,交给MAC。
        - MAC把数据帧存到接收缓冲区(RX Buffer)。
      2. 配置DMA:
        - CPU告诉RX DMA:“把MAC的RX Buffer数据搬到内存地址0x20002000!”
        - 配置包括源地址(RX Buffer)、目标地址(内存)、最大长度等。
      3. DMA搬运:
        - RX DMA自动从RX Buffer读取数据,搬到内存。
        - 搬完后,DMA通知CPU:“新数据到啦!”
      4. 程序处理:
        - 程序从内存读取数据,交给协议栈处理(比如解析服务器的响应)。

      图2:以太网DMA工作流程


4、与普通外设的DMA对比:以太网DMA有啥特别?
      你可能听说过其他外设(比如USART、SPI)也用DMA,比如串口用DMA传输一串字符。乍一看,以太网的DMA跟这些好像差不多,都是“搬数据”。但实际上,以太网DMA有几个独特之处,让它跟普通外设的DMA大不相同。

4.1 数据量和速度
      普通外设(USART)
        - 数据量小:串口一次传输几十字节,比如一个字符串“Hello”。
        - 速度慢:USART的波特率通常是115200bps(约11.5KB/s),远低于以太网的100Mbps(12.5MB/s)。
        - DMA任务简单:一次搬运几十字节,CPU手动搬运也能勉强应付。

      以太网:
        - 数据量大:一个以太网帧可能有1500字节,连续传输可能有成千上万个帧。
        - 速度快:100Mbps的速率意味着每秒要处理12.5MB数据,CPU手动搬运根本忙不过来。
        - DMA任务复杂:需要高效、批量地搬运大数据块,还要保证实时性。

      串口DMA就像搬运一篮子苹果,慢悠悠搬就行;而以太网DMA像搬运一卡车货物,必须用高速传送带。

4.2 数据结构
      普通外设(USART)
        - 数据是简单的字节流,比如一串字符,没有复杂的结构。
        - DMA直接把字节从内存搬到串口寄存器,或者反过来。

      以太网
        - 数据是以太网帧,有复杂的结构(前导码、地址、数据、校验码)。
        - DMA需要按照帧的格式搬运,还要配合MAC的缓冲区管理(TX/RX Descriptor)。

      串口DMA就像搬运一堆散装零件,简单直接;而以太网DMA像搬运一箱箱精心打包的快递,得多留心格式。

4.3 缓冲区管理
     普通外设(USART):
        - 通常只有一个简单的缓冲区(比如串口发送寄存器)。
        - DMA直接把数据搬进/搬出这个寄存器,管理简单。

      以太网:
        - 以太网模块有专门的发送和接收缓冲区(TX Buffer和RX Buffer),通过描述符(Descriptor)管理。
        - 描述符是一个数据结构,告诉DMA每个数据帧的地址、长度、状态等信息。
        - DMA需要根据描述符一个接一个地搬运帧,管理复杂得多。

      串口DMA就像把东西扔进一个小篮子;而以太网DMA像管理一个大仓库,货物按清单(描述符)整齐摆放。

4.4 中断和实时性
      普通外设(USART):
        - 传输完成后,DMA触发一次中断,通知CPU“搬完了”。
        - 实时性要求低,晚点处理也没啥大不了。

      以太网:
        - 数据帧来得快,DMA可能频繁触发中断(每次帧搬完都可能通知)。
        - 实时性要求高,稍有延迟就可能丢包,影响网络通信。

      串口DMA就像慢悠悠送封信,晚点没事;而以太网DMA像实时送快递,晚了客户就得投诉。

      图3:以太网DMA与USART DMA对比


5、DMA如何辅助与远程服务器通信?
      以太网通信的终极目标是让APM32F407跟远程服务器“聊天”,比如发送传感器数据或接收控制命令。DMA在其中扮演了“高速搬运工”的角色,确保数据顺畅流动。让我们以一个实际场景为例,来看看DMA是怎么干活的。

5.1 场景:发送传感器数据到服务器
      假设你用APM32F407开发一个温湿度传感器,通过以太网把数据发送到远程服务器。流程如下:
      1. 程序生成数据
        - 传感器每秒采集一次温湿度,生成一个JSON格式的数据包,比如`{"temp":25.5, "humidity":60}`。
        - LwIP协议栈把数据封装成UDP包,加上IP头、UDP头,变成以太网帧,存储在内存(比如地址0x20001000)。
      2. DMA搬运数据
        - CPU配置TX DMA,告诉它:“把0x20001000的1500字节数据搬到MAC的TX Buffer!”
        - TX DMA自动从内存读取数据,搬到发送缓冲区。
        - 搬运过程中,CPU可以继续采集下一次传感器数据。
      3. MAC和PHY发送
        - MAC把缓冲区的数据通过PHY(比如LAN8720)发送到网线。
        - 数据通过路由器、交换机,最终到达服务器。
      4. DMA通知完成
        - TX DMA搬完数据,触发中断,通知CPU:“发送缓冲区已准备好,可以发下一个帧了!”

      DMA就像个快递员,把你打包好的传感器数据(快递包裹)从仓库(内存)送到卡车(MAC),然后卡车开往服务器。

5.2 场景:接收服务器的响应
      现在服务器回复了一条命令,比如`{"command":"turn_on_led"}`。接收流程如下:
      1. PHY和MAC接收
        - PHY从网线接收到数据帧,交给MAC。
        - MAC把数据存到接收缓冲区(RX Buffer)。
      2. DMA搬运数据
        - CPU配置RX DMA,告诉它:“把RX Buffer的数据搬到内存0x20002000!”
        - RX DMA自动从缓冲区读取数据,搬到内存。
      3. 程序处理
        - RX DMA搬完数据,触发中断,通知CPU:“新数据到啦!”
        - 程序从内存读取数据,解析命令,点亮LED。

      这时,DMA像个收货员,把服务器送来的包裹(数据帧)从卡车(MAC)搬到仓库(内存),让你轻松处理。

5.3 DMA的贡献
      DMA在与服务器通信中的作用可以总结为:
      - 高效搬运:快速搬运大数据帧,适应以太网的高速需求。
      - 解放CPU:让CPU专注逻辑处理(比如解析JSON、控制硬件),而不是忙于搬数据。
      - 保证实时性:及时搬运数据,减少丢包,确保通信顺畅。

      图4:DMA在服务器通信中的作用


6、如果不用DMA会怎么样?
      为了突出DMA的重要性,我们来设想一下:如果以太网通信不用DMA,全靠CPU手动搬运,会发生什么?

6.1 CPU忙到“崩溃”
        - 问题:以太网帧动辄上千字节,100Mbps速率下每秒有12.5MB数据。CPU需要不停地从内存读取数据,写入MAC的寄存器,或者反过来。
        - 后果:CPU几乎全部时间都花在搬运数据上,没空干别的(比如处理传感器、更新显示屏)。系统会变得超级卡顿。
      贴切的说,没有DMA的时候,CPU就像一个快递员,既要打包、搬货,还要开车送货,累得晕头转向。

6.2 数据丢失
        - 问题:以太网数据来得快,CPU手动搬运可能跟不上节奏。如果MAC的接收缓冲区满了,新数据就会被丢弃。
        - 后果:丢包严重,通信不稳定,可能连不上服务器。
      贴切的说,没有DMA的时候,快递卡车送货太快,仓库来不及收,包裹就被扔了。

6.3 实时性差
        - 问题:网络通信讲究低延迟,CPU忙于搬运会导致响应变慢。
        - 后果:服务器发来的命令可能延迟处理,用户体验变差(比如LED晚点亮)。
      贴切的说,没有DMA的时候,客户等着收快递,但快递员忙着搬货,送货慢得让人抓狂。

6.4 总结:不用DMA几乎不可能
      以太网通信的高速和大流量特性决定了DMA的不可或缺。没有DMA,CPU会被数据搬运任务拖垮,通信效率和稳定性都会大幅下降。在实际开发中,APM32F407的以太网模块几乎总是搭配DMA使用。

7、DMA的实现:代码和配置
      为了更直观地理解DMA,我们来看看在APM32F407上如何配置以太网DMA,以及实际的一些代码实现。

7.1 DMA配置的关键点
      APM32F407的以太网DMA通过以下寄存器配置:
        - ETH_DMATxDTR:发送描述符表寄存器,指向TX描述符列表。
        - ETH_DMARxDTR:接收描述符表寄存器,指向RX描述符列表。
        - ETH_DMACTLR:控制DMA的启动、停止和模式。
        - ETH_DMAISR:中断状态寄存器,检查DMA传输状态。

      描述符(Descriptor)是DMA的“任务清单”,每个描述符包含:
        - 数据缓冲区的地址。
        - 数据长度。
        - 状态标志(比如传输完成、错误)。

7.2 代码示例:以太网DMA初始化
以下是初始化以太网DMA的代码,基于LAN8720(RMII接口):
#include "apm32f4xx_eth.h"
#include "apm32f4xx_gpio.h"
#include "apm32f4xx_rcm.h"

#define TX_DESC_NUM 4
#define RX_DESC_NUM 4
ETH_DMADesc_T txDescTable[TX_DESC_NUM];
ETH_DMADesc_T rxDescTable[RX_DESC_NUM];
uint8_t txBuffer[TX_DESC_NUM][1536]; // 每个缓冲区1536字节
uint8_t rxBuffer[RX_DESC_NUM][1536];

void ETH_DMA_Init(void)
{
    // 使能以太网时钟
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_ETHMAC | RCM_AHB1_PERIPH_ETHMACTX | RCM_AHB1_PERIPH_ETHMACRX);

    // 初始化描述符表
    ETH_DMADescConfig_T descConfig;
    descConfig.buffer1Addr = (uint32_t)txBuffer;
    descConfig.buffer2Addr = 0;
    descConfig.length = 1536;
    descConfig.descNum = TX_DESC_NUM;
    ETH_DMATxDescConfig(txDescTable, &descConfig);

    descConfig.buffer1Addr = (uint32_t)rxBuffer;
    descConfig.descNum = RX_DESC_NUM;
    ETH_DMARxDescConfig(rxDescTable, &descConfig);

    // 配置DMA
    ETH_DMAConfig_T dmaConfig;
    dmaConfig.txDescTable = txDescTable;
    dmaConfig.rxDescTable = rxDescTable;
    dmaConfig.dropFrame = ENABLE;
    ETH_DMAConfig(&dmaConfig);

    // 启动DMA
    ETH_DMAStart();
}

7.3 数据发送和接收
以下是使用DMA发送和接收数据的代码:
void ETH_SendPacket(uint8_t* data, uint32_t len)
{
    // 填充发送缓冲区
    memcpy(txBuffer[0], data, len);

    // 配置描述符
    txDescTable[0].length = len;
    txDescTable[0].status = ETH_DMATXDESC_OWN;

    // 触发发送
    ETH_DMATxStart();
}

void ETH_ReceivePacket(uint8_t* buffer, uint32_t* len)
{
    if (!(rxDescTable[0].status & ETH_DMARXDESC_OWN))
    {
        *len = rxDescTable[0].length;
        memcpy(buffer, rxBuffer[0], *len);
        rxDescTable[0].status = ETH_DMARXDESC_OWN; // 归还描述符
    }
}
     说明:
        - 发送时,程序把数据拷贝到txBuffer,DMA根据描述符搬运到MAC。
        - 接收时,DMA把RX Buffer的数据搬到rxBuffer,程序从中读取。

8、常见问题
      在开发以太网应用时,DMA配置可能会遇到问题。以下是常见的坑和解决办法:
      1. 问题:DMA不工作
        - 检查DMA时钟是否使能(RCM_AHB1_PERIPH_ETHMAC)。
        - 确认描述符表的地址和长度是否正确。
      2. 问题:丢包严重
        - 检查描述符数量是否足够(TX_DESC_NUM和RX_DESC_NUM太少会导致溢出)。
        - 确保内存分配正确,避免缓冲区覆盖。
      3. 问题:中断频繁
        - 检查DMA中断配置,禁用不必要的中断(比如每次帧都触发)。
        - 优化协议栈处理速度,减少DMA等待时间。

9、总结:DMA是不可或缺的“搬运英雄”
      本文中,我们从DMA的定义、作用,到在APM32F407以太网模块中的具体实现,彻底揭开了它的神秘面纱。DMA是高速以太网通信的“搬运工”,负责把以太网帧在内存和MAC之间快速搬运,解放CPU,保证通信的效率和稳定性。
      与普通外设(像USART)的DMA相比,以太网DMA处理的数据量更大、结构更复杂、实时性要求更高。没有DMA,CPU会被搬运任务拖垮,通信几乎无法进行。在与远程服务器的通信中,DMA确保数据顺畅流动,让你的传感器数据或控制命令快速到达目的地。



使用特权

评论回复

打赏榜单

21小跑堂 打赏了 80.00 元 2025-05-29
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论
21小跑堂 2025-5-29 15:59 回复TA
以太网通信中DMA的使用方法,作者从DMA的介绍到以太网中DMA起到的作用入手,以实际操作演示DMA如何提升以太网的通讯效率。整体流程完整,源码和解读较佳。 
沙发
霜咬回响| | 2025-5-27 11:16 | 只看该作者
Ethernet的DMA独立出来,我估计是因为网络通讯比较繁重的情况,别耽误其它外设的触发吧?

使用特权

评论回复
板凳
DKENNY|  楼主 | 2025-5-27 15:26 | 只看该作者
霜咬回响 发表于 2025-5-27 11:16
Ethernet的DMA独立出来,我估计是因为网络通讯比较繁重的情况,别耽误其它外设的触发吧? ...

是的,以太网 DMA 独立出来是因为网络通信数据量大、速度快(100Mbps 每秒 12.5MB),CPU 亲自搬运会忙不过来。独立 DMA 解放 CPU,确保不耽误其他外设(如串口、ADC)的中断触发。相比普通 DMA,以太网 DMA 专为高吞吐设计,带专用描述符和缓冲区管理。

使用特权

评论回复
地板
霜咬回响| | 2025-5-27 16:25 | 只看该作者
楼主,您有测试过如何MCU使用查询模式直接回读下,两者的读取速度吗?

使用特权

评论回复
发新帖 本帖赏金 80.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

52

主题

94

帖子

10

粉丝