DMA +串口通信

DMA,全稱爲: Direct Memory Access,即直接存儲器訪問, DMA 傳輸將數據從一個地址空間複製到另外一個地址空間。 當 CPU 初始化這個傳輸動作,傳輸動作本身是由DMA 控制器 來實行和完成。典型的例子就是移動一個外部內存的區塊到芯片內部更快的
內存區。像是這樣的操作並沒有讓處理器工作拖延,反而可以被重新排程去處理其他的工作。 DMA 傳輸對於高效能嵌入式系統算法和網絡是很重要的。 DMA 傳輸方式無需 CPU 直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場的過程,通過硬件爲 RAM 與 I/O 設備開闢一條直接傳送數據的通路, 能使 CPU 的效率大爲提高。

STM32 的 DMA 有以下一些特性:
●每個通道都直接連接專用的硬件 DMA 請求,每個通道都同樣支持軟件觸發。這些功能通過軟件來配置。
●在七個請求間的優先權可以通過軟件編程設置(共有四級:很高、高、中等和低),假如在相等優先權時由硬件決定(請求 0 優先於請求 1,依此類推) 。
●獨立的源和目標數據區的傳輸寬度(字節、半字、全字),模擬打包和拆包的過程。源和目標地址必須按數據傳輸寬度對齊。
●支持循環的緩衝器管理
●每個通道都有 3 個事件標誌(DMA 半傳輸, DMA 傳輸完成和 DMA 傳輸出錯),這 3 個事件標誌邏輯或成爲一個單獨的中斷請求。
●存儲器和存儲器間的傳輸
●外設和存儲器,存儲器和外設的傳輸
●閃存、 SRAM、外設的 SRAM、 APB1 APB2 和 AHB 外設均可作爲訪問的源和目標。
●可編程的數據傳輸數目:最大爲 65536

本實驗是基於戰艦開發板 單片機爲STM32F103ZET6.
該單片機有兩個DAM控制器 ,DMA1和DMA2,本次實驗是基於DMA1實驗。

DMA初始化步驟:
1、 使能DMA時鐘
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
2、
初始化DMA通道參數。DMA通道的選擇需要根據自己的需要 ,查詢數據手冊DMA請求映像,選擇所需要初始化的DMA通道。
DMA1請求映像。如下圖:
這裏寫圖片描述
利用ST官方庫初始化DMA通道:
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,DMA_InitTypeDef* DMA_InitStruct)
其中,DMA_InitTypeDef結構體定義如下:
typedef struct
{
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;
第一個參數 是用來設置DMA傳輸的外設地址(比如要進行串口傳輸,那麼外設地址應該爲USART->DR ,表示方法爲 &USART->DR,如果是AD轉換的話,那麼外設的地址就是ADC->DR,表示方法爲&ADC->DR)。
第二個參數 爲內存地址,也就是我們存放DMA傳輸數據的內存地址。
第三個參數是設置數據傳輸的方向,決定是從外設讀取數據到內存還是送數據第四個參數是設置一次傳輸數據量的大小
第五個參數是設置傳輸數據的時候外設地址是不變還是遞增。如果設置爲遞增,那麼下一次傳輸的時候地址加 1,這裏因爲我們是一直往固定外設地址&USART1->DR發送數據,所以地址不遞增。
第六個參數是設置傳輸數據時 候內存地址 是否遞增。 這個參數和DMA_PeripheralInc 意思接近,只不過針對的是內存。 這裏我們的場景是將內存中連續存儲單元的數據發送到串口,毫無疑問內存地址是需要遞增的,所以值爲 DMA_MemoryInc_Enable。
第七個參數是用來設置外設的數據長度是爲字節傳輸( 8bits),半字 傳 輸 (16bits) 還 是 字 傳 輸 (32bits) , 這 裏 我 們 是 8 位 字 節 傳 輸 , 所 以 值 設 置 爲DMA_PeripheralDataSize_Byte。 u8—–Byte(字節) u16 —-halfword (半字) u32——word(字)。
第八個參數是用來設置內存的數據長度,和第七個參數意思接近,這
裏我們同樣設置爲字節傳輸 DMA_MemoryDataSize_Byte。
第九個參數用來設置DMA是否循環採集。如果只採集一次,設置爲 DMA_Mode_Normal,如果是循環採集則設置爲DMA_Mode_Circular(循環模式通常用於ADC連續轉換)
第十個參數用來設置DMA通道的優先級。
第十一個參數設置是否是存儲器到存儲器模式傳輸。
接下來爲初始化DMA+串口的代碼:
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)PerBase_Addr; // 外設地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)MemBase_Addr; //內存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //數據傳輸方向爲內存到外設
DMA_InitStructure.DMA_BufferSize = Send_Num; //DMA通道緩存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址不變
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內存地址自增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //可以設置成循環模式,正常模式下數據傳輸完成後停止傳輸
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //通道優先級配置 在多通道DMA傳輸的時候 這個參數的配置是有意義的
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止內存到內存的傳輸
DMA_Init(DMA_CHx, &DMA_InitStructure);
使能串口DMA發送:USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
使能DMA通道,啓動傳輸:
DMA_Cmd(DMA_CHx, ENABLE);

主函數測試代碼:
int main(void)
{
NVIC_Config();
SysTick_init();
LED_Config();
USART_Config(115200);
printf(“uart is read!!\r\n”);
DMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)tx_buf,txbuf_size);
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //串口向DMA發出請求
LED_Config();
LED_On();
while(1);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章