# STM32 DMA
###### tags: `STM32`
* 本篇只解說原理,不解說DMA程式上使用方式
* DMA就是不佔用CPU運算資源,將『資料傳輸』的控制權交給 DMA 控制器,讓資料在週邊 (Peripherals) 與記憶體 (SRAM) 之間直接流動。
* DMA 是一種讓週邊設備與記憶體(或記憶體與記憶體)之間直接進行資料傳輸的技術。透過將傳輸控制權從 CPU 轉交給 DMA 控制器,實現資料流動與指令執行的並行,有效減輕 CPU 負擔並提升系統整體的吞吐量。
* 傳輸方向: 除了提到的「週邊 ↔ SRAM」,DMA 也能處理「SRAM ↔ SRAM」(例如大型陣列的搬移)或「Flash ↔ SRAM」。
* 本篇解說
* STM32L0 and STM32L4方塊圖,STM32F系列也可以參考如下方塊圖
* STM32G4系列就沒有分 DMA1 和 DMA2,G4是規劃一個DMA區域讓User自行分配
## DMA 原理
* Cortex-M 內核和 DMA1 控制器都與其他 MCU 周邊設備交互通過一系列Bus。如果仍然不清楚,重要的是要注意flash和SRAM暫儲器是 MCU 內核之外的元件,因此它們需要相互交互其他通過總線互連。
* Cortex-M 內核和 DMA1 控制器都是主機。這意味著他們是唯一的可以在總線上啟動事務的單元。但是,必須規範對總線的訪問這樣他們就不能同時訪問同一個從外圍設備。
* BusMatrix 管理 Cortex-M 內核與 DMA1 之間的訪問仲裁控制器。仲裁使用循環算法來控制對總線的訪問。這BusMatrix 由兩個主機(CPU、DMA)和四個從機(flash、SRAM、AHB1 帶有 AHB 到高級外部總線 (APB) 橋接器和 AHB2)。 BusMatrix 也允許在它們之間自動互連多個外部設備。
* 系統總線將 Cortex-M 內核連接到 BusMatrix。
* DMA 總線將 DMA 的高級高性能總線 (AHB) 主接口連接到 BusMatrix。
* AHB 到 APB 橋接器提供了 AHB 和 APB 總線之間的完全同步連接,其中連接了大多數周邊設備
## 為什麼說 DMA 「不佔用 CPU 運算週期 (Clock Cycles)或減少 CPU 中斷負擔」
* CPU 從週邊(例如 I2C 或 UART)讀取一個 Byte。
* CPU 將這個 Byte 寫入 SRAM 的某個位置。
* 這段時間 CPU 沒辦法處理其他邏輯運算。
### 有了 DMA (Direct Memory Access) 之後
* CPU 只需要花幾微秒告訴 DMA:「幫我把 UART 收到的 100 個 Byte 搬到 SRAM 的 0x20000000 位置,搬完再叫我。」
* 指令下達後,CPU 立即去做其他數學運算或邏輯判斷。
* DMA 與 CPU 會分時或同時共享系統總線 (Bus) 來存取 SRAM。
### DMA應用場景
1. 指令型傳輸 (CMD: UART, I2C, SPI, CAN)
* 這類通訊非常適合 DMA,主要原因不在於「資料量」,而在於**「非同步等待」**。
* 優勢:當你發送一個長字串(如 AT Command)或讀取感測器時,如果不使用 DMA,CPU 必須在每個 Byte 傳輸完成後被中斷一次。
* 實務感觸:使用 DMA 後,你可以實現「背景傳輸」。
* 範例:發送 100 Bytes 的 UART Log,CPU 只需要花 1 條指令啟動 DMA,然後就可以去跑更重要的 PID 演算法,完全不需要理會這 100 次的發送中斷。
2. ADC 傳輸的場景分析
1. 多通道 (6~12組以上):非常有感
* 當通道數變多時,若使用傳統的「中斷觸發(Interrupt)」或「輪詢(Polling)」:
* 中斷地獄:每轉換完一個通道就進一次中斷,CPU 會花大量的時間在進出中斷(Context Switch),導致主程式效率低落。
* DMA 的做法:設定 ADC 進入 掃描模式 (Scan Mode),結合 DMA。ADC 會自動轉換完通道 1 到通道 12,並由 DMA 依序填入 SRAM 中的陣列。
* 結果:CPU 只需要在「整組 12 個通道都轉完」時看一眼結果就好。
2. 少量通道 (1~2組):感觸不明顯
* 原因:如果只有一兩個數值,且取樣頻率不高,CPU 順手讀一下暫存器(Register)的時間極短,配置 DMA 的程式碼複雜度反而更高。
* 修正觀點:即使通道少,如果「取樣頻率極高」(例如音訊處理,每秒取樣 48,000 次),DMA 依然是必須的。因為高頻率的中斷會讓 CPU 沒時間處理其他邏輯。
## STM32 L0 and STM32 L4
### M0

### M4

* I-bus
* 該bus將 Cortex®-M4 內核的指令總線連接到 BusMatrix。
* I-bus使用該總線來獲取指令。
* 該總線的目標是包含代碼的存儲器(Flash memory、內部 SRAM 或通過 FMC 或 OCTOSPI 的外部存儲器)。
* D-bus
* 該bus將 Cortex®-M4 內核的數據總線連接到 BusMatrix。
* 這D-bus由內核用於文字加載和調試訪問。
* 該總線的目標是包含代碼的存儲器(Flash memory、內部 SRAM 或通過 FMC 或 OCTOSPI 的外部存儲器)。
* S-bus
* 該bus將 Cortex®-M4 內核的系統總線連接到 BusMatrix。
* 這s-bus是內核用於訪問位於外部設備或 SRAM 區域中的數據。
* 本次的目標總線是內部 SRAM,AHB1 外設包括 APB1 和 APB2外設、AHB2 外設和外部存儲器通過 OCTOSPI 或 FMC。
* SRAM2 也可通過該總線訪問以允許與 SRAM1 和 SRAM3 的連續映射。
* DMA總線
* 該總線將 DMA 的 AHB 主接口連接到 BusMatrix。
* 總線是 SRAM1、SRAM2 和 SRAM3,AHB1 外設包括 APB1 和APB2 外設、AHB2 外設和通過 OCTOSPI 的外部存儲器或FMC。
* DMA2D-bus 總線
* 該總線將 DMA2D 的 AHB 主接口連接到 BusMatrix。
* 的目標該總線是通過 OCTOSPI 的 SRAM1、SRAM2 和 SRAM3 以及外部存儲器或FMC。
## STM32 G4 DMA搭配方式
```=
void Init_DMA1_CH1(void)
{
// ADC12 -> DMA1_CH1
_DMA1_CCR1.bit.DIR = 0; // Read data from peripheral
_DMA1_CCR1.bit.CIRC = 1; // circular mode enabled
_DMA1_CCR1.bit.MINC = 1; // memory increment mode enabled
_DMA1_CCR1.bit.PINC = 0; // peripheral increment mode disabled
_DMA1_CCR1.bit.PSIZE = 2; // Set peripheral size to 32-bits
_DMA1_CCR1.bit.MSIZE = 2; // Set memory size to 32-bits
_DMA1_CCR1.bit.PL = 3; //priority = very high
_DMA1_CCR1.bit.TCIE = 1; // Enable DMA TC ISR
_DMA1_CNDTR1.bit.NDT = 7; // Number of data need to be transfer
_DMA1_CPAR1.bit.PA = (unsigned long)&_ADC12_CDR.all; // address of _ADC12_CDR.all or (0x5000030C)
_DMA1_CMAR1.bit.MA = (unsigned long)&adc12RawData;
_DMAMUX1_C0CR.bit.DMAREQ_ID = 5; // Set ADC12 as the DMA request source
_DMA1_CCR1.bit.EN = 1;
}
```
```=
void Init_DMA1_CH2(void)
{
// ADC345 -> DMA1_CH2
_DMA1_CCR2.bit.DIR = 0; // Read data from peripheral
_DMA1_CCR2.bit.CIRC = 1; // circular mode enabled
_DMA1_CCR2.bit.MINC = 1; // memory increment mode enabled
_DMA1_CCR2.bit.PINC = 0; // peripheral increment mode disabled
_DMA1_CCR2.bit.PSIZE = 2; // Set peripheral size to 32-bits
_DMA1_CCR2.bit.MSIZE = 2; // Set memory size to 32-bits
_DMA1_CCR2.bit.PL = 1; // priority = Med
_DMA1_CCR2.bit.TCIE = 0; // Disable DMA TC ISR
_DMA1_CNDTR2.bit.NDT = 8; // Number of data need to be transfer
_DMA1_CPAR2.bit.PA = (unsigned long)&_ADC345_CDR.all; // address of _ADC345_CDR.all or (0x5000070C)
_DMA1_CMAR2.bit.MA = (unsigned long)&adc345RawData;
_DMAMUX1_C1CR.bit.DMAREQ_ID = 37; // Set ADC345 as the DMA request source
_DMA1_CCR2.bit.EN = 1;
}
```
* M4

## STM32 DMA Datasheet
### STM32 L0 DMA
* [Using the STM32F0/F1/F3/Gx/Lx Series DMA controller](https://www.st.com/resource/en/application_note/cd00160362-using-the-stm32f0f1f3gxlx-series-dma-controller-stmicroelectronics.pdf)
### STM32 F2 F4 F7 DMA
* [Using the STM32F2, STM32F4 and STM32F7 Series
DMA controller](https://www.st.com/resource/en/application_note/dm00046011-using-the-stm32f2-stm32f4-and-stm32f7-series-dma-controller-stmicroelectronics.pdf)
## Reference
* [ARM的BUS Matrix的作用](https://blog.csdn.net/m0_49540263/article/details/111675882)
* [CortexM3与CortexM0的bus matrix](https://blog.csdn.net/qq_34686440/article/details/116561517?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163600662716780264061646%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163600662716780264061646&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-116561517.first_rank_v2_pc_rank_v29&utm_term=BUS+Matrix+M0)
* [【STM32】 DMA原理,步骤超细详解,一文看懂DMA](https://blog.csdn.net/as480133937/article/details/104927922)