cortex_m3_stm32嵌入式學習筆記(十九):DMA實驗(高速傳輸)

ARM 400瀏覽
DMA,全稱為: Direct Memory Access,即直接存儲器訪問。 DMA 傳輸方式無需 CPU 直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場的過程,通過硬件為 RAM 與 I/O 設備開辟一條直接傳送數據的通路, 能使 CPU 的效率大為提高。

即DMA傳輸前,CPU要把總線控制權交給DMA控制器,而在結束DMA傳輸后,DMA控制器應立即把總線控制權再交回給CPU。 一個完整的DMA傳輸過程必須經過下面的4個步驟。 1.DMA請求 CPU對DMA控制器初始化,并向I/O接口發出操作命令,I/O接口提出DMA請求。 2.DMA響應 DMA控制器對DMA請求判別優選級及屏蔽,向總線裁決邏輯提出總線請求。當CPU執行完當前總線周期即可釋放總線控制權。此時,總線裁決邏輯輸出總線應答,表示DMA已經響應,通過DMA控制器通知I/O接口開始DMA傳輸。 3.DMA傳輸 DMA控制器獲得總線控制權后,CPU即刻掛起或只執行內部操作,由DMA控制器輸出讀寫命令,直接控制RAM與I/O接口進行DMA傳輸。 4.DMA結束 當完成規定的成批數據傳送后,DMA控制器即釋放總線控制權,并向I/O接口發出結束信號。當I/O接口收到結束信號后,一方面停 止I/O設備的工作,另一方面向CPU提出中斷請求,使CPU從不介入的狀態解脫,并執行一段檢查本次DMA傳輸操作正確性的代碼。最后,帶著本次操作結果及狀態繼續執行原來的程序。

簡單的說就是一種高速傳輸數據的方式啦?

從外設( TIMx、 ADC、 SPIx、 I2Cx 和 USARTx)產生的 DMA 請求,通過邏輯或輸入到DMA 控制器,這就意味著同時只能有一個請求有效。外設的 DMA 請求,可以通過設置相應的外設寄存器中的控制位,被獨立地開啟或關閉。
下面是是DMA1各通道一覽表:



? ? ? ? ??

本次實驗實現串口1的DMA傳送,即用到通道4

配置DMA大體步驟:

1)使能 DMA 時鐘
2) 初始化 DMA 通道 4 參數
3)使能串口 DMA 發送
4)使能 DMA1 通道 4,啟動傳輸。

5)查詢 DMA 傳輸狀態


配置DMA的dma.c

#include "dma.h" DMA_InitTypeDef DMA_InitStructure; u16 DMA1_MEM_LEN;//保存DMA每次數據傳送的長度  //DMA1的各通道配置 //這里的傳輸形式是固定的,這點要根據不同的情況來修改 //從存儲器->外設模式/8 位數據寬度/存儲器增量模式 //DMA_CHx:DMA通道CHx //cpar:外設地址 //cmar:存儲器地址 //cndtr:數據傳輸量 void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr) { 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能 DMA 時鐘 	DMA_DeInit(DMA_CHx);//將DMA的通道1寄存器重設為缺省值 	DMA1_MEM_LEN=cndtr; 	DMA_InitStructure.DMA_PeripheralBaseAddr=cpar;//DMA外設串口基地址 	DMA_InitStructure.DMA_MemoryBaseAddr=cmar;//DMA內存基地址 	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//數據傳輸方向內存到外設 	DMA_InitStructure.DMA_BufferSize=cndtr;//DMA通道的DMA緩存的大小 	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外設地址不變 	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//內存地址寄存器遞增 	//數據寬度為8位 	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; 	//數據寬度為8位 	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;  	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//工作在正常緩存模式 	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//DM 通道擁有中優先級 	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//非內存到內存傳輸 	DMA_Init(DMA_CHx,&DMA_InitStructure);//初始化 DMA 的通道 } //開啟一次DMA傳輸 void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx) { 	DMA_Cmd(DMA_CHx,DISABLE );//關閉 USART1 TX DMA1所指示的通道 	DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//設置 DMA 緩存的大小 	DMA_Cmd(DMA_CHx, ENABLE); //使能 USART1 TX DMA1 所指示的通道 }

看這一句

void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)

這是初始化DMA的語句,第一個參數填DMA的通道CHx ,(本次實驗用到的是通道4),第二個參數是外設地址,我們是要向串口發送數據,所以要填串口接受發送數據存儲器 USART1->DR 的地址,第三個參數填存儲器地址,(可以理解為待發送數據的地址),第四個參數填數據傳輸量(即待發送數據的大小)

dma.h

#ifndef _DMA_H #define _DMA_H #include "sys.h" void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr); void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx); #endif

主函數

#include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "lcd.h" #include "key.h" #include "dma.h" const u8 TEXT_TO_SEND[]={"DMA test~~~~~~"}; #define TEXT_LENTH sizeof(TEXT_TO_SEND)-1 u8 SendBuff[(TEXT_LENTH+2)*100]; u16 i;u8 t;float pro=0; void init(void) { 	delay_init(); 	uart_init(9600); 	LED_Init(); 	KEY_Init(); 	LCD_Init(); 	//DMA1通道4,外設為串口1,存儲器為SendBuff,長(TEXT_LENTH+2)*100. 	MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,(TEXT_LENTH+2)*100); 	POINT_COLOR=RED; 	LCD_ShowString(60,40,200,24,24," DMA Test~^~"); 	LCD_ShowString(60,70,200,16,16," M Difficult"); 	LCD_ShowString(60,90,200,16,16," By--yh"); 	LCD_ShowString(60,110,200,16,16," 2015/1/25"); 	LCD_ShowString(60,130,200,16,16,"KEY0:Start"); 	t=0; 	for(i=0;i<(TEXT_LENTH+2)*100;i++)//填充緩存區(生成待發送數據) 	{ 		if(t>=TEXT_LENTH)//添加換行 		{ 			SendBuff[i++]=0x0d; 			SendBuff[i]=0x0a; 			t=0; 		} 		else 			SendBuff[i++]=TEXT_TO_SEND[t++]; 	} }  int main(void) { 	init();i=0; 	POINT_COLOR=BLUE;//設置字體為藍色 	while(1) 	{ 		t=KEY_Scan(0); 		if(t==KEY0_PRES) 		{ 			LCD_ShowString(60,150,200,16,16,"Start Transimit...."); 			LCD_ShowString(60,170,200,16,16,"   %");//顯示百分號 			printf("rnDMA DATA: rn "); 			USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); 			MYDMA_Enable(DMA1_Channel4);//開始一次DMA傳輸! 			//等待 DMA 傳輸完成,此時我們來做另外一些事,點燈 			//實際應用中,傳輸數據期間,可以執行另外的任務 			while(1) 			{ 				if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)//等待通道 4 傳輸完成 				{ 					DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道 4 傳輸完成標志 					break; 				} 				pro=DMA_GetCurrDataCounter(DMA1_Channel4);//當前還剩余數據量 				pro=1-pro/((TEXT_LENTH+2)*100);//得到百分比  				pro*=100; //擴大 100 倍 				LCD_ShowNum(60,170,pro,3,16); 				//LED1=!LED1; 			} 			LCD_ShowNum(60,170,100,3,16);//顯示100%  			LCD_ShowString(60,150,200,16,16,"Transimit Finished!");//傳送完成 		} 		i++; 		delay_ms(10); 		if(i==20) 		{ 			LED0=!LED0; 			i=0; 		} 	} } 

有一個函數

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx) 

獲得當前數據量剩余大小(還有多少沒發送完)

其實最重要的當傳輸數據時MCU的CPU是可以干別的事情的,這才我們利用DMA傳輸的優勢(雖然本實驗沒有體現出來)


我們可以在串口助手里看到如下內容

? ?

說明串口接收到了DMA傳輸的數據了。。

此實驗可以對比之前的串口通信的實驗,那次是普通的發送

七星彩走势图2元网官网