前言:現代 MCU 經常需要處理大量外設數據(ADC、UART、SPI、I²C 等),如果把這些數據搬運完全交給 CPU,會佔用大量 CPU 週期,降低系統響應能力。**DMA(Direct Memory Access,直接內存訪問)**就是解決這個問題的利器:它可以在不佔用 CPU 的情況下,把外設數據自動搬運到內存(或把內存數據搬到外設),顯著降低 CPU 負擔、提升實時性與吞吐量。

目錄

一、DMA 是什麼?為什麼要用?

二、DMA 的總體架構

三、DMA 的典型傳輸方向與使用場景

四、DMA 的關鍵配置項與概念

五、DMA 的工作時序與中斷

六、常見誤區與調試要點

七、性能注意事項與優化建議

八、ADC+DMA


一、DMA 是什麼?為什麼要用?

DMA 的本質:一個硬件控制器,負責在外設寄存器、內存和外設之間直接搬運數據,搬運過程中 CPU 不參與數據逐字節拷貝(但仍需做配置和處理完成後的邏輯)。

主要優點

  • 降低 CPU 佔用:尤其適合大量或高速數據採集(如音頻、傳感器陣列、圖像流等)。
  • 更穩定的實時性:減少中斷頻率或避免中斷上下文開銷。
  • 更高吞吐量:硬件搬運比軟件循環拷貝更快、更節省總線帶寬。

適用場景舉例:ADC 多通道高速採樣、UART 大量數據接收、SPI 快速數據傳輸、內存到內存的批量拷貝、PWM 波形輸出緩衝等。

二、DMA 的總體架構

  • STM32F1 系列通常有 DMA1(個別器件還有 DMA2)。
  • DMA 被劃分為 通道(Channel),每個通道映射到一個或多個外設事件(例如 ADC、USART、SPI 等)。通道數量有限(例如 DMA1 通常有 7 個通道)。
  • 每個通道有自己的配置寄存器,控制源/目的地址、方向、數據寬度、優先級、傳輸長度、模式(循環/普通)等。
  • DMA 與外設通過**請求映射(request mapping)**關聯:某個外設觸發數據準備(如 ADC EOC),會產生 DMA 請求到對應通道。

STM32中DMA的使用入門_51CTO博客_#單片機

三、DMA 的典型傳輸方向與使用場景

  • 外圍設備 → 內存(Peripheral-to-Memory)
    場景:ADC 將轉換結果推送到內存、USART RX 接收數據寫內存。是最常見的用於數據採集的方向。
  • 內存 → 外圍設備(Memory-to-Peripheral)
    場景:從一個內存緩衝區把數據寫到 DAC、UART TX、SPI TX(比如發送音頻或圖像數據)。
  • 內存 → 內存(Memory-to-Memory)
    場景:大塊內存數據搬運(在 F1 上支持有限,注意映射與配置)。用於緩衝區複製、DMA 驅動的塊傳輸等。
  • 雙向 / 循環模式(實際是模式選擇)
    循環(Circular)適合連續流數據採集;普通(Normal)適合一次性傳輸。

四、DMA 的關鍵配置項與概念

  • 源地址(Peripheral address)與目的地址(Memory address)
    DMA 在搬運時會從源地址讀取數據並寫到目的地址;比如 ADC->內存: 源=ADC_data_register,目的=緩衝區首地址。
  • 傳輸方向(Direction):見上一節。
  • 傳輸長度(Number of Data):要搬運的數據項個數(不是字節數,依賴數據寬度)。
  • 數據寬度(Peripheral & Memory Data Size):常見為 8-bit、16-bit、32-bit。ADC(12-bit)一般使用 16-bit 寬度(half-word),UART 通常用 8-bit。數據寬度必須匹配硬件與緩衝格式。
  • 地址遞增(Peripheral increment / Memory increment):是否在每次傳輸後自動增加地址。外設寄存器通常禁用地址遞增(固定地址),內存緩衝通常啓用遞增。
  • 傳輸模式(Normal / Circular)
  • Normal:完成指定數量傳輸後停止並觸發完成中斷。
  • Circular:完成後自動重新開始(圍繞緩衝區),適合連續採樣。
  • 優先級(Priority):在多通道競爭 DMA 總線時決定搶佔順序(高優先級通道更早獲得總線)。
  • 中斷/標誌:半傳中斷(Half Transfer)、傳輸完成中斷(Transfer Complete)、傳輸錯誤中斷。半傳在循環模式下非常有用——可以實現“前半緩衝處理同時後半緩衝接收”的方案。

五、DMA 的工作時序與中斷

  • 啓動步驟(概念層面):
  1. 配置 DMA(源/目地址、長度、寬度、遞增、模式、優先級)。
  2. 配置外設(使能外設 DMA 請求,比如 ADC_DMACmd)。
  3. 使能 DMA 通道與相關中斷(如果需要)。
  4. 啓動外設觸發(ADC 軟件觸發或定時器觸發),數據開始流入緩衝區。
  • 半傳中斷(HT)與傳輸完成中斷(TC)
    在循環模式下,半傳中斷可以觸發處理已填滿的緩衝區一半數據,而 DMA 繼續寫入另一半;傳輸完成中斷則表示一輪緩衝寫滿。利用這兩個事件可以實現無縫“生產者-消費者”模式。
  • 錯誤處理:DMA 也可能產生傳輸錯誤(比如總線錯誤),要有相應的檢測與恢復策略(重啓 DMA、報警等)。

六、常見誤區與調試要點

  • 誤區:DMA 一開就萬事大吉
    DMA 確實減輕 CPU,但錯誤配置(地址錯誤、長度錯誤、寬度不匹配)會導致數據錯位、溢出或硬件掛起。調試時重點檢查外設數據寄存器地址、數據寬度和內存緩衝區大小。
  • 誤區:半傳中斷和全傳中斷二者可互相替代
    半傳中斷用於“流式處理”更低延遲;若只用全傳中斷,會在緩衝寫滿後才處理,可能加大延遲。
  • 調試技巧
  • 先用短緩衝進行功能驗證(例如 4 個採樣點);
  • 打開半傳/全傳中斷並在 ISR 中簡單翻轉 IO(示波器觀察)確認觸發節奏;
  • 檢查 DMA 優先級與競爭關係;
  • 確認內存對齊(16-bit/32-bit)與 CPU 訪問不會引起未對齊故障。
  • 跨域緩存問題(高級):F1 的 Cortex-M3 沒有數據緩存問題,然而在更高端系列(如 Cortex-M7)需要考慮 D-cache 與 DMA 的一致性:使用緩存刷新/失效或使用不可緩存內存區。

STM32中DMA的使用入門_51CTO博客_數據_02

七、性能注意事項與優化建議

  • 選擇合適的數據寬度:儘量讓外設與內存寬度匹配,減少額外的對齊/移動操作(例如 ADC 用 16-bit 存儲)。
  • 優先級與通道分配:把關鍵通道設高優先級;避免多個高頻外設共享同一 DMA 通道。
  • 中斷開銷最小化:把處理儘量放到任務或主循環,不在 ISR 中做大量計算;使用半傳中斷只做標記並在主循環處理。
  • 使用循環模式處理持續數據流:比如 ADC 連續採樣或串口持續接收,循環模式可以避免頻繁重啓 DMA。
  • 考慮緩衝大小權衡:小緩衝降低延遲但增加中斷頻率;大緩衝降低中斷負擔但增加響應延遲。

八、ADC+DMA

  • 當需要實時採集多通道數據(如傳感器陣列、音頻採樣、振動分析等)時,CPU 去輪詢或處理中斷會消耗大量計算資源,甚至跟不上數據速率。
  • ADC + DMA 可以實現:ADC 將轉換結果自動寫入內存緩衝區,CPU 僅在緩衝半滿或滿時處理數據,極大提高效率與實時性。

配置流程:

  • 設計緩衝區:為採樣結果準備好連續的內存數組(例如 uint16_t buffer[NUM_SAMPLES];)。通常選擇 16-bit 單元來保持 ADC 的 12-bit 數據。
  • 配置 ADC
  • 打開 ADC 時鐘,設置採樣時間、分辨率(12-bit)、觸發模式(軟件或定時器觸發)、掃描模式(若為多通道自動掃描)。
  • 對於多通道自動採樣,啓用掃描模式並在序列寄存器中設置通道順序。
  • 配置 DMA(外設 → 內存):
  • 源地址:ADC 數據寄存器地址(固定);目的地址:緩衝區首地址;
  • 數據寬度:一般選擇 16-bit(half-word);
  • 內存地址遞增:啓用;外設地址遞增:禁用;
  • 模式:如果是持續採樣(例如定時器觸發連續採樣),選擇 Circular(循環) 模式;若是一次性採集選擇 Normal
  • 啓用半傳/全傳中斷(根據需要)。
  • 把 ADC 的 DMA 請求打開(告訴 ADC 在 EOC 時發出 DMA 請求,讓 DMA 自動搬運數據)。
  • 啓動 ADC(與觸發器)與 DMA 通道:在正確的時序下啓動 DMA,然後啓動 ADC & 觸發(如果用定時器觸發,啓動定時器)。
  • 在中斷/任務中處理數據
  • 如果使用循環模式並啓用了半傳/全傳中斷:在半傳中斷中處理緩衝區前半,在全傳中斷中處理後半;處理完後繼續讓 DMA 循環寫入。
  • 如果使用普通模式:在傳輸完成中斷中處理整個緩衝區並可重新配置/重啓 DMA。

下一章節將詳細講解代碼部分。

STM32中DMA的使用入門_51CTO博客_數據_03