1. 程式人生 > >STM32F103系列實戰之DMA控制器

STM32F103系列實戰之DMA控制器

DMA簡介
直接儲存器存取(DMA)用來提供在外設和儲存器之間或者儲存器和儲存器之間的高速資料傳輸。無須CPU干預,資料可以通過DMA快速地移動,這就節省了CPU的資源來做其他操作。靈活的12路通用DMA(DMA1上有7個通道, DMA2上有5個通道)可以管理儲存器到儲存器、裝置到儲存器和儲存器到裝置的資料傳輸;還有一個仲裁器來協調各個DMA請求的優先權。2個DMA控制器支援環形緩衝區的管理,避免了控制器傳輸到達緩衝區結尾時所產生的中斷。
每個通道都有專門的硬體DMA請求邏輯,同時可以由軟體觸發每個通道;傳輸的長度、傳輸的源地址和目標地址都可以通過軟體單獨設定。DMA可以用於主要的外設: SPI、I2C、 USART,通用、基本和高階控制定時器TIMx, DAC、 I2S、SDIO和ADC。

DMA主要特性
1.12
個獨立的可配置的通道(請求): DMA1有7個通道, DMA2有5個通道;
2.每個通道都直接連線專用的硬體DMA請求,每個通道都同樣支援軟體觸發。這些功能通過軟體來配置;
3.在同一個DMA模組上,多個請求間的優先權可以通過軟體程式設計設定(共有四級:很高、高、中等和低),優先權設定相等時由硬體決定(請求0優先於請求1,依此類推) ;
4.獨立資料來源和目標資料區的傳輸寬度(位元組、半字、全字),模擬打包和拆包的過程。源和目標地址必須按資料傳輸寬度對齊;
5.支援迴圈的緩衝器管理;
6.每個通道都有3個事件標誌(DMA半傳輸、 DMA傳輸完成和DMA傳輸出錯),這3個事件標誌邏輯或成為一個單獨的中斷請求;
7.儲存器和儲存器間的傳輸;
8.外設和儲存器、儲存器和外設之間的傳輸;
9.快閃記憶體、 SRAM、外設的SRAM、 APB1、 APB2和AHB外設均可作為訪問的源和目標;
10.可程式設計的資料傳輸數目:最大為65535。

DMA框圖:

注:1.DMA2僅存在於大容量產品和互聯型產品。
       2.SPI/I2S3、UART4、TIM5、TIM6、TIM7和DAC的DMA請求僅存在於大容量產品和互聯型產品。

       3.ADC3、SDIO和TIM8的DMA請求僅存在於大容量產品。

每次DMA傳送由3個操作組成:
1.從外設資料暫存器或者從當前外設/儲存器地址暫存器指示的儲存器地址取資料,第一次傳輸時的開始地址是DMA_CPARx或DMA_CMARx暫存器指定的外設基地址或儲存器單元。
2.存資料到外設資料暫存器或者當前外設/儲存器地址暫存器指示的儲存器地址,第一次傳輸時的開始地址是DMA_CPARx或DMA_CMARx暫存器指定的外設基地址或儲存器單元。
3.執行一次DMA_CNDTRx暫存器的遞減操作,該暫存器包含未完成的運算元目。

通道配置過程
下面是配置DMA通道x的過程(x代表通道號):
1. 在DMA_CPARx暫存器中設定外設暫存器的地址。發生外設資料傳輸請求時,這個地址將是資料傳輸的源或目標。
2. 在DMA_CMARx暫存器中設定資料儲存器的地址。發生外設資料傳輸請求時,傳輸的資料將從這個地址讀出或寫入這個地址。
3. 在DMA_CNDTRx暫存器中設定要傳輸的資料量。在每個資料傳輸後,這個數值遞減。
4. 在DMA_CCRx暫存器的PL[1:0]位中設定通道的優先順序。
5. 在DMA_CCRx暫存器中設定資料傳輸的方向、迴圈模式、外設和儲存器的增量模式、外設和儲存器的資料寬度、傳輸一半產生中斷或傳輸完成產生中斷。
6. 設定DMA_CCRx暫存器的ENABLE位,啟動該通道。

一旦啟動了DMA通道,它既可響應連到該通道上的外設的DMA請求。當傳輸一半的資料後,半傳輸標誌(HTIF)被置1,當設定了允許半傳輸中斷位(HTIE)時,將產生一箇中斷請求。在資料傳輸結束後,傳輸完成標誌(TCIF)被置1,當設定了允許傳輸完成中斷位(TCIE)時,將產生一箇中斷請求。

中斷
每個DMA通道都可以在DMA傳輸過半、傳輸完成和傳輸錯誤時產生中斷。為應用的靈活性考慮,通過設定暫存器的不同位來開啟這些中斷。

DMA中斷請求

注意:在大容量產品中,DMA2通道4和DMA2通道5的中斷被對映在同一個中斷向量上。在互聯型產品中,DMA2通道4和DMA2通道5的中斷分別有獨立的中斷向量。所有其他的DMA通道都有自己的中斷向量。

DMA請求映像
DMA1
控制器
從外設(TIMx[x=1、2、 3、 4]、ADC1、 SPI1、 SPI/I2S2、I2Cx[x=1、 2]和USARTx[x=1、2、 3])產生的7個請求,通過邏輯或輸入到DMA1控制器,這意味著同時只能有一個請求有效。參見下圖的DMA1請求映像。外設的DMA請求,可以通過設定相應外設暫存器中的控制位,被獨立地開啟或關閉。

DMA1請求映像

 

各個通道的DMA1請求一覽

DMA2控制器
從外設(TIMx[5、 6、 7、 8]、 ADC3、 SPI/I2S3、 UART4、DAC通道1、 2和SDIO)產生的5個請求,經邏輯或輸入到DMA2控制器,這意味著同時只能有一個請求有效。參見下圖的DMA2請求映像。外設的DMA請求,可以通過設定相應外設暫存器中的DMA控制位,被獨立地開啟或關閉。
注意: DMA2控制器及相關請求僅存在於大容量產品和互聯型產品中。

DMA2請求映像

各個通道的DMA2請求一覽

 

下面我們以DMA的ADC1通道傳送功能為例介紹一下其簡單用法:

//ADC1取樣地址
#define ADC1_DR_Address ((uint32_t)0x4001244C)	
//ADC1 DMA配置
static void BSP_DMAAdc1_Init(void)
{
    DMA_InitTypeDef   DMA_AdcInit;	
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                          //啟動DMA1時鐘,ADC1對應DMA1通道1
    DMA_AdcInit.DMA_PeripheralBaseAddr = ADC1_DR_Address;                       //外設地址,
    DMA_AdcInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;       //外設資料字長,半字、16位雙位元組長度,根據ADC暫存器ADC_DR說明確定
    DMA_AdcInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                  //設定DMA的外設遞增模式,禁止遞增,只讀取一個外設資料
    DMA_AdcInit.DMA_MemoryBaseAddr = (u32)ADCSampleValue;                       //記憶體地址,存放讀取ADC資料的快取,根據實際情況確認快取大小
    DMA_AdcInit.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;               //記憶體資料字長,同外設長度,必須相同、防止傳輸錯亂
    DMA_AdcInit.DMA_MemoryInc = DMA_MemoryInc_Enable;	                        //設定DMA的記憶體遞增模式,允許遞增,讀取ADC1多通道資料,不同的通道資料存放在不同的快取中
    DMA_AdcInit.DMA_BufferSize = SAMPLETIMES*SAMPLEPORT;                        //DMA通道的DMA快取的大小,根據實際情況設定,即記憶體地址快取的size
    DMA_AdcInit.DMA_DIR = DMA_DIR_PeripheralSRC;                                //dma傳輸方向單向,由於讀取的是外設ADC上的資料,左移設定從外設到記憶體
    DMA_AdcInit.DMA_M2M = DMA_M2M_Disable;                                      //設定DMA的2個memory中的變數互相訪問,關閉記憶體間傳輸功能
    DMA_AdcInit.DMA_Mode = DMA_Mode_Circular;                                   //設定DMA的傳輸模式,迴圈傳輸模式,保證一直讀取外設資料
    DMA_AdcInit.DMA_Priority = DMA_Priority_VeryHigh;	                        //設定DMA的優先級別,級別最高
    DMA_Init(DMA1_Channel1, &DMA_AdcInit);	                                //初始化DMA
    DMA_Cmd(DMA1_Channel1,ENABLE);                                              //使能DMA1
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);                             //使能DMA傳輸完成中斷,傳輸完成中斷,及一次迴圈結束
    BSP_NVIC_Init(DMA1_Channel1_IRQn, 1, 4);                                    //初始化中斷,具體參看中斷相關內容
}

程式碼分析參見程式碼註釋。簡單點說使用DMA就得確認外設對應的DMA通道,確認傳輸方向,外設到記憶體、記憶體到外設等等。根據傳輸方向設定源地址和目的地址,上面的ADC1外設就是源地址、沒存記憶體就是目的地址。然後再根據實際需要傳輸的方式設定相關引數,初始化DMA並使能DMA。中斷根據實際情況來決定是否需要。