## DMA 数据传输 ## 判别规则(核心,唯一要记的) 1. **该不该用 DMA**:大量/连续数据 **且** CPU 要同时干别的 → DMA;数据少/低频 → 中断/轮询(配置开销不划算) 2. **普通 vs 循环**:传完一次就结束(一次性任务/固定采样)→ **普通(NORMAL)**;需要"永不停"的流(音频/连续采样/不定长串口接收)→ **循环(CIRCULAR)**,CNDTR 到 0 自动重装 3. **方向看"谁流向谁"**:内存→外设 = **M2P**(发送);外设→内存 = **P2M**(接收/采样);内存→内存 = **M2M**(搬运,**免外设请求,配置完置 EN 立即启动**) 一句话:**"量大且 CPU 要干别的才用 DMA;传完就停用普通、永不停用循环;方向看数据从哪流向哪,M2M 免请求。"** ## 抽象描述(一句话本质) 把 DMA 想成**专职工人搬货**:CPU 只"下单"(配置)不"搬货",DMA 把外设和内存之间的数据直接搬好,搬完发 1 次完成中断喊一声。搬数据有三种干法:**轮询 = CPU 站在那儿干等**;**中断 = CPU 每搬一字节被打断一次**;**DMA = CPU 全程不用管**。 ## 三种搬运方式对比 | 方式 | CPU 参与 | 100 字节代价 | 适用 | | ---- | -------------- | -------------------------------- | ------------------------ | | 轮询 | 死等 | CPU 全程被绑住,啥也干不了 | 极简单/调试 | | 中断 | 每字节打断一次 | 进 100 次中断,每次现场保存/恢复 | 数据少/低频/需即时处理 | | DMA | 零参与 | 配置一次,搬完只进 1 次完成中断 | 大量/连续 + CPU 要干别的 | ## 传输方向三选一 | 方向 | 谁流向谁 | 典型场景 | 谁启动 | | ---- | ---------------------------------- | ----------------------- | ---------------------------------------- | | M2P | 内存 → 外设寄存器(如 USART1->DR) | 串口发一批数据 | 等外设请求(如开 `USART1->CR3 | = USART_CR3_DMAT`,TXE) | | P2M | 外设寄存器 → 内存 | 串口接收 / ADC 连续采样 | 等外设请求(RXNE) | | M2M | 内存 → 内存 | Flash→SRAM、拷数组 | **免请求**,置 EN 立即搬完(MEM2MEM 位) | ## 普通 vs 循环(核心) | | 普通 NORMAL | 循环 CIRCULAR | | ---------- | -------------------------------------------- | ---------------------------------------- | | CNDTR 到 0 | 停止 + 完成中断 | **自动重装回初始值,继续搬** | | 适用 | 一次性任务(发一批字符串、固定 1000 次采样) | 持续流(音频、连续采样、不定长串口接收) | | 生活类比 | 送完这趟就下班 | 送完一趟接下一单,永远不停 | | 配错的后果 | 该循环配普通 → 传完一批就断流 | 该普通配循环 → 数据反复重发/覆盖 | ## 配置要点与坑 - **顺序**:参数全配好(CPAR/CMAR/CNDTR/CCR)→ **最后才置 EN** - **CNDTR**:16 位,上限 65535;超过分段或循环 - **地址递增**:外设寄存器地址固定 → PINC 关;内存缓冲区连续 → MINC 开 - **通道冲突**:同一通道同一时刻只能服务一个外设 - **外设请求**:M2P/P2M 必须开外设的 DMA 请求位,否则通道配了也不动 - **Cache 一致性**:Cortex-M7 需注意;F103 无 Cache 不用担心 - **M2M 方向反直觉**:MEM2MEM 模式下 CPAR 放目的、CMAR 放源 ## 正反例(建立直觉) - **正例1**:串口一次发 1KB,CPU 同时跑业务 → 用 DMA(大量 + CPU 要干别的) - **正例2**:ADC 连续采样 1000 次存内存 → 用 DMA(P2M,量大持续) - **反例1**:每秒收 1~2 个命令字节 → 中断就够(数据太少,DMA 配置开销 > 收益) - **反例2**:每次搬完都要立刻处理该字节 → 用中断(DMA 攒一批才通知,实时响应反而慢) ## 易混对比 ### DMA vs 中断 | | DMA | 中断 | | -------- | ------------------------ | -------------------- | | 搬数据 | DMA 控制器搬,CPU 零参与 | CPU 每字节进 ISR 搬 | | 中断次数 | 搬完只 1 次 | 每字节 1 次 | | 适用 | 量大/连续 | 量少/低频/需即时处理 | ### 外设请求 vs 启动(M2P/P2M vs M2M) | | M2P/P2M | M2M | | -------------- | ----------------------------- | -------------------- | | 要不要外设请求 | 要(TXE/RXNE 决定每步何时搬) | 不要,置 EN 立即搬完 | | 生活类比 | 货现产现送,货到了才搬 | 货全在仓库,直接搬 | ## 变体验证(3 题,全过=学会) 1. 串口每秒只收 1~2 字节命令,主循环跑 UI → **用中断**(数据少,DMA 配置开销远大于收益) 2. 陷阱题:ADC 连续采样**固定 1000 次**采完一次性处理 → **普通模式**(固定次数、采完就停;循环模式用于永不停流,会不断覆盖缓冲) 3. 陷阱题:"内存→内存 DMA 和内存→外设一样,都要等外设请求才启动" → **错**,M2M 免请求立即启动,只有 M2P/P2M 等外设请求 ## 口述要点(面试怎么讲) - **结论先行**:量大且 CPU 要干别的 → DMA;传完就停用普通、永不停用循环;方向看谁流向谁 - **DMA 省在哪**:中断搬 100 字节 = 100 次现场保存/恢复、CPU 每字节被打断;DMA = 配置一次零参与,只进 1 次完成中断 - **为什么不能全用 DMA**:启动有配置开销,数据少/低频收益低;"攒一批才通知"在需即时处理场景反而实时性差 - **为什么方向/模式要在启动前想清楚**:方向决定"要不要外设请求(TXE/RXNE)、谁的地址递增(外设固定/内存递增)";普通/循环决定"CNDTR 到 0 停不停(自动重装)"。三者构成传输闭环。类比:方向 = 取货送货路线,普通/循环 = 送完下班还是等下一单,外设请求 = 货现产现送,M2M = 货全在仓库不用等 - **易错点**:忘开外设 DMA 请求;方向配错;该循环配普通断流;忘配 NVIC(完成中断不来);CNDTR 超 65535 ## 关系网络 | 相邻概念 | 关系 | 孤立理解会犯的错 | | --------------------- | --------------------------------------------------------------- | --------------------------------- | | 中断 | DMA 每字节不打断 CPU,搬完才发 1 次完成中断 | 以为 DMA 不产生中断 | | NVIC | DMA 完成中断同样要配优先级/使能 | 只配 DMA 不配 NVIC,中断永远不来 | | 串口/ADC | 典型应用 `HAL_UART_Transmit_DMA` / `Receive_DMA`;开 `CR3.DMAT` | 忘开外设 DMA 请求,通道配了也不动 | | 内存(Cache) | M7 需 Cache 一致性;F103 无 Cache | 移植 M7 忽略 Cache,搬完读到旧值 | | 中断优先级(主题 32) | DMA 完成中断走 NVIC 优先级体系 | 优先级方向与 FreeRTOS 混淆 | ## 学习日期 2026-08-18 --- _每主题一页,复习时只翻本目录。_