DMA傳輸將數據從一個地址空間複製到另一個地址空間。當CPU初始化這個傳輸動作,傳輸動作本身是由DMA控制器來實現和完成的。
DMA傳輸方式無需CPU直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場過程,通過硬件爲RAM和IO設備開闢一條直接傳輸數據的通道,使得CPU的效率大大提高。
DMA作用:爲CPU減負。
STM32最多有2個DMA控制器(DMA2僅存在大容量產品中),DMA1有7個通道。DMA2有5個通道。每個通道專門用來管理來自於一個或多個外設對存儲器訪問的請求。還有一個仲裁起來協調各個DMA請求的優先權。
STM32的DMA有以下一些特性:
①每個通道都直接連接專用的硬件DMA請求,都支持軟件觸發,這些通過軟件來配置。
②在七個請求間的優先權可以通過軟件編程設置(共有四級:很高、高、中等和低),假如在相等優先權時由硬件決定(請求0優先於請求1,依此類推) 。
③ 獨立的源和目標數據區的傳輸寬度(字節、半字、全字),模擬打包和拆包的過程。源 和目標地址必須按數據傳輸寬度對齊。
④ 支持循環的緩衝器管理
⑤ 每個通道都有3個事件標誌(DMA 半傳輸,DMA傳輸完成和DMA傳輸出錯),這3個事件標誌邏輯或成爲一個單獨的中斷請求。
⑥ 外設和存儲器,存儲器和外設的傳輸 ,存儲器和存儲器間的傳輸
⑦ 閃存、SRAM、外設的SRAM、APB1 APB2和AHB外設均可作爲訪問的源和目標。
⑧ 可編程的數據傳輸數目:最大爲65536
從外設(TIMx、ADC、SPIx、I2Cx 和 USARTx)產生的 DMA 請求,通過邏輯或輸入到DMA 控制器,這就意味着同時只能有一個請求有效。外設的 DMA 請求,可以通過設置相應的外設寄存器中的控制位,被獨立地開啓或關閉。
下面是配置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使能庫函數
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq,FunctionalState NewState);
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void DAC_DMACmd(uint32_t DAC_Channel, FunctionalState NewState);
void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
void SDIO_DMACmd(FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq,FunctionalState NewState);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase,uint16_t TIM_DMABurstLength)
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource,FunctionalState NewState);
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef* DMA_InitStruct)
{
uint32_t DMA_PeripheralBaseAddr; //外設基地址
uint32_t DMA_MemoryBaseAddr; //存儲器基地址
uint32_t DMA_DIR; //數據傳輸方向
uint32_t DMA_BufferSize; //通道傳輸數據量
uint32_t DMA_PeripheralInc;//外設增量模式
uint32_t DMA_MemoryInc; //存儲器增量模式
uint32_t DMA_PeripheralDataSize; //外設數據寬度
uint32_t DMA_MemoryDataSize; //存儲器數據寬度
uint32_t DMA_Mode; //模式:是否循環
uint32_t DMA_Priority; //優先級
uint32_t DMA_M2M; //是否存儲器到存儲器方式
}DMA_InitTypeDef;
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx,FunctionalState NewState);
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx,uint32_t DMA_IT, FunctionalState NewState);
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx,uint16_t DataNumber);uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
void DMA_ClearFlag(uint32_t DMAy_FLAG);
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
DMA配置程序過程
① 使能DMA時鐘
RCC_AHBPeriphClockCmd();
② 初始化DMA通道參數
DMA_Init();
③使能串口DMA發送,串口DMA使能函數:
USART_DMACmd();
④使能DMA1通道,啓動傳輸。
DMA_Cmd();
⑤查詢DMA傳輸狀態
DMA_GetFlagStatus();
⑥獲取/設置通道當前剩餘數據量:
DMA_GetCurrDataCounter();
DMA_SetCurrDataCounter();