為什麼用 DMA?
- 解放 CPU:資料搬移由硬體自動完成,CPU 可執行其他任務。
- 高速:記憶體間直接傳輸,不經 CPU,頻率可達系統時脈。
- 低延遲:無中斷開銷,適合大塊資料、週期性搬移。
DMA 傳輸模式
- 記憶體到記憶體 (Memory-to-Memory):SRAM ↔ SRAM、SRAM ↔ Flash。
- 外設到記憶體:如 UART 接收 → SRAM。
- 記憶體到外設:SRAM → UART 發送暫存器。
關鍵參數配置 (STM32F103VE)
| 參數 | 本範例設定 | 說明 |
| 通道 | DMA1 Channel 1 | 支援 M2M 的通道 |
| 方向 | DMA_DIR_PeripheralSRC | Flash (視為外設) → SRAM |
| 資料寬度 | DMA_PeripheralDataSize_Word / DMA_MemoryDataSize_Word | 32-bit (4 bytes) 傳輸 |
| 位址增量 | 啟用 DMA_PeripheralInc_Enable, DMA_MemoryInc_Enable | 源/目標位址自增 |
| 模式 | DMA_Mode_Normal | 單次傳輸,非循環 |
| 優先級 | DMA_Priority_High | 高優先級 |
| M2M | DMA_M2M_Enable | 啟用記憶體到記憶體 |
| 傳輸量 | BufferSize | 以「資料寬度單位」計數 (非位元組) |
完整初始化程式碼
#define SRC_ADDR ((uint32_t)SRC_Const_Buffer) // Flash 位址 (const 資料)
#define DST_ADDR ((uint32_t)DST_Buffer) // SRAM 位址
#define BUF_WORDS 256 // 256 個 32-bit 字 = 1 KB
void DMA_Init(void) {
DMA_InitTypeDef dma;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
dma.DMA_PeripheralBaseAddr = SRC_ADDR;
dma.DMA_MemoryBaseAddr = DST_ADDR;
dma.DMA_DIR = DMA_DIR_PeripheralSRC;
dma.DMA_BufferSize = BUF_WORDS;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
dma.DMA_Mode = DMA_Mode_Normal;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_M2M = DMA_M2M_Enable;
DMA_Init(DMA1_Channel1, &dma);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); // 傳輸完成中斷
DMA_Cmd(DMA1_Channel1, ENABLE); // 啟動
}
中斷服務常式 (ISR)
void DMA1_Channel1_IRQHandler(void) {
if (DMA_GetITStatus(DMA1_IT_TC1)) {
// 可選:檢查剩餘計數器 DMA_GetCurrDataCounter()
DMA_ClearITPendingBit(DMA1_IT_GL1);
// 設旗標通知主程式傳輸完成
dma_done = 1;
}
}
NVIC 優先級設定
void NVIC_Config(void) {
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 0 搶佔, 4 子優先級
NVIC_InitTypeDef nvic = {
.NVIC_IRQChannel = DMA1_Channel1_IRQn,
.NVIC_IRQChannelPreemptionPriority = 0,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE
};
NVIC_Init(&nvic);
}
主程式輪詢等待
int main(void) {
SystemInit();
NVIC_Config();
DMA_Init();
while (!dma_done); // 簡單輪詢,實際專案建議用信號量/事件
// 驗證資料...
while (1);
}
關鍵觀察:變數儲存區
SRC_Const_Buffer 定義為 const uint32_t → 編譯器置於 Flash (0x0800xxxx)。
DST_Buffer 定義為 uint32_t (無 const) → 編譯器置於 SRAM (0x2000xxxx)。
- DMA 實際搬移路徑:**Flash → SRAM**,驗證 M2M 模式下「外設」端可為 Flash。
常見坑點
- BufferSize 單位:是「資料寬度單位」,32-bit 模式下 256 代表 1024 位元組,非 256 位元組。
- Flash 讀取速度:若系統時脈 > 24 MHz 需開啟 Flash 等待週期 (
FLASH_SetLatency),否則 DMA 讀取可能錯誤。
- 快取一致性:Cortex-M3 無資料快取,無需清理;M4/M7 有快取時需
SCB_CleanDCache/SCB_InvalidateDCache。