天天看點

STM32-DMA

簡介

DMA(Direct Memory Access)—直接存儲器存取, 是單片機的一個外設,它的主要功能

是用來搬資料,但是不需要占用 CPU, 即在傳輸資料的時候, CPU 可以幹其他的事情,好

像是多線程一樣。 資料傳輸支援從外設到存儲器或者存儲器到存儲器,這裡的存儲器可以

是 SRAM 或者是 FLASH。 DMA 控制器包含了 DMA1 和 DMA2, 其中 DMA1 有 7 個通道,

DMA2 有 5 個通道, 這裡的通道可以了解為傳輸資料的一種管道。要注意的是 DMA2 隻存

在于大容量的單片機中

DMA 有 DMA1 和 DMA2 兩個控制器, DMA1 有 7 個通道, DMA2 有 5 個通道,不同

的 DMA 控制器的通道對應着不同的外設請求,這決定了我們在軟體程式設計上該怎麼設定,

具體見 DMA 請求映像表

STM32-DMA
STM32-DMA

        每個通道可以接收多個外設的請求,但是同一時間隻能接收一個,不能同時接收多個。

DMA 資料配置

 使用 DMA,最核心就是配置要傳輸的資料,包括

資料從哪裡來?要到哪裡去?

傳輸的資料的機關是什麼?

要傳多少資料?

是一次傳輸還是循環傳輸?

等等。

1. 從哪裡來到哪裡去

我們知道 DMA 傳輸資料的方向有三個:從外設到存儲器,從存儲器到外設,從存儲

器到存儲器。 具體的方向 DMA_CCR 位 4 DIR 配置: 0 表示從外設到存儲器, 1 表示從存儲

器到外設。 這裡面涉及到的外設位址由 DMA_CPAR 配置,存儲器位址由 DMA_CMAR 配

置。

外設到存儲器

當我們使用從外設到存儲器傳輸時,以 ADC 采集為例。 DMA 外設寄存器的位址對應

的就是 ADC 資料寄存器的位址, DMA 存儲器的位址就是我們自定義的變量(用來接收存

儲 AD 采集的資料) 的位址。 方向我們設定外設為源位址。

存儲器到外設

當我們使用從存儲器到外設傳輸時,以序列槽向電腦端發送資料為例。 DMA 外設寄存器

的位址對應的就是序列槽資料寄存器的位址, DMA 存儲器的位址就是我們自定義的變量(相

當于一個緩沖區,用來存儲通過序列槽發送到電腦的資料) 的位址。方向我們設定外設為目

标位址

存儲器到存儲器

當我們使用從存儲器到存儲器傳輸時,以内部 FLASH 向内部 SRAM 複制資料為例。

DMA 外設寄存器的位址對應的就是内部 FLASH(我們這裡把内部 FALSH 當作一個外設來

看) 的位址, DMA 存儲器的位址就是我們自定義的變量(相當于一個緩沖區,用來存儲來

自内部 FLASH 的資料)的位址。方向我們設定外設(即内部 FLASH) 為源位址。 跟上面

兩個不一樣的是,這裡需要把 DMA_CCR 位 14: MEM2MEM:存儲器到存儲器模式配置

為 1,啟動 M2M 模式。

配置如下:

1 void USARTx_DMA_Config(void)
2 {
3 DMA_InitTypeDef DMA_InitStructure;
4
5 // 開啟 DMA 時鐘
6 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

7 // 外設位址 設定 DMA 源位址:序列槽資料寄存器位址*/
8 DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
9 // 記憶體位址 (要傳輸的變量的指針)
10 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
11 // 方向:從記憶體到外設
12 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

13 // 傳輸大小
14 DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
15 // 外設位址不增
16 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
17 // 記憶體位址自增
18 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
19 // 外設資料機關
20 DMA_InitStructure.DMA_PeripheralDataSize =
21 DMA_PeripheralDataSize_Byte;
22 // 記憶體資料機關
23 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
24 // DMA 模式,一次或者循環模式
25 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
26 //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
27 // 優先級:中
28 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
29 // 禁止記憶體到記憶體的傳輸
30 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
31 // 配置 DMA 通道
32 DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);
33 // 使能 DMA
34 DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
35 }

1 int main(void)
2 {
3     uint16_t i;

4     /* 初始化 USART */
5     USART_Config();
6
7     /* 配置使用 DMA 模式 */
8     USARTx_DMA_Config();
9
15     /*填充将要發送的資料*/
16     for (i=0; i<SENDBUFF_SIZE; i++)
17     {
18         SendBuff[i] = 'P';
20     }
21
22 /*為示範 DMA 持續運作而 CPU 還能處理其它事情,持續使用 DMA 發送資料,量非常大,
23 *長時間運作可能會導緻電腦端序列槽調試助手會卡死,滑鼠亂飛的情況,
24 *或把 DMA 配置中的循環模式改為單次模式*/
25
26 /* USART1 向 DMA 發出 TX 請求 */
27 USART_DMACmd(USARTx, USART_DMAReq_Tx, ENABLE);
28
29 /* 此時 CPU 是空閑的,可以幹其他的事情 */
30 //例如同時控制 LED
31 while (1)
32 {
33     LED1_TOGGLE
34     Delay(0xFFFFF);
35 }
36 }
USART_DMACmd 函數用于控制 USART 的 DMA 請求的啟動和關閉。它接收三個參
數,第一個參數用于設定序列槽外設, 可以是 USART1/2/3 和 UART4/5 這 5 個參數可選, 第
二個參數設定序列槽的具體 DMA 請求,有序列槽發送請求 USART_DMAReq_Tx 和接收請求
USART_DMAReq_Rx 可選,第三個參數用于設定啟動請求 ENABLE 或者關閉請求
DISABLE。運作該函數後 USART的 DMA發送傳輸就開始了,根據配置存儲器的資料會發
送到序列槽。
DMA 傳輸過程是不占用 CPU 資源的,可以一邊傳輸一次運作其他任務。






           

繼續閱讀