stm32 DMA數(shù)據(jù)搬運(yùn) 操作寄存器+庫函數(shù)
一個(gè)完整的微控制器通常由CPU、存儲(chǔ)器和外設(shè)等組件構(gòu)成。這些組件一般在結(jié)構(gòu)和功能上都是獨(dú)立的,而各個(gè)組件的協(xié)調(diào)和交互就由CPU完成。如此一來,CPU作為整個(gè)芯片的核心,其處理的工作量是很大的。如果CPU先從A外設(shè)拿到一個(gè)數(shù)據(jù)送給B外設(shè)使用,同時(shí)C外設(shè)又需要D外設(shè)提供一個(gè)數(shù)據(jù)。。。這樣的數(shù)據(jù)搬運(yùn)工作將使CPU的負(fù)荷顯得相當(dāng)繁重。
嚴(yán)格的說,搬運(yùn)數(shù)據(jù)只是CPU的比較不重要的一種工作。CPU最重要的工作室進(jìn)行數(shù)據(jù)運(yùn)算,從加減乘除到一些高級(jí)的運(yùn)算,包括浮點(diǎn)、積分、微分、FFT等。CPU還需要負(fù)責(zé)復(fù)雜的中斷申請(qǐng)和響應(yīng),以保證芯片的實(shí)時(shí)性能。
理論上常見的控制外設(shè),比如Usart、I2C、SPI甚至是USB等通信接口,單純的利用CPU進(jìn)行協(xié)議模擬也是可以實(shí)現(xiàn)的,比如51單片機(jī)經(jīng)常使用I/O口模擬I2C協(xié)議通信。但這樣既浪費(fèi)了CPU的資源,同時(shí)實(shí)現(xiàn)后的性能表現(xiàn)往往和使用專門的硬件模塊實(shí)現(xiàn)的效果相差甚遠(yuǎn)。從這個(gè)角度來看,各個(gè)外設(shè)控制器的存在,無疑降低了CPU的負(fù)擔(dān),解放了CPU的資源。
數(shù)據(jù)搬運(yùn)這一工作占用了大部分的CPU資源,成為了降低CPU的工作效率的主要原因之一。于是需要一種硬件結(jié)構(gòu)分擔(dān)CPU這一職能 —— DMA。
從數(shù)據(jù)搬運(yùn)的角度看,如果要把存儲(chǔ)地址A的數(shù)值賦給另外一個(gè)地址上B的變量,CPU實(shí)現(xiàn)過程為首先讀出A地址上的數(shù)據(jù)存儲(chǔ)在一個(gè)中間變量,然后再轉(zhuǎn)送到B地址的變量上。使用DMA則不需要中間變量,直接將A地址的數(shù)值傳送到B地址的變量里。無疑減輕了CPU的負(fù)擔(dān),也提高了數(shù)據(jù)搬運(yùn)的效率。
stm32中 DMA1有7個(gè)通道,DMA2有5個(gè)通道。DMA掛載的時(shí)鐘為AHB總線,其時(shí)鐘為72Mhz,所以可以實(shí)現(xiàn)高速數(shù)據(jù)搬運(yùn)。
stm32的DMA1通道一覽表
本例實(shí)現(xiàn)使用CPU和DMA搬運(yùn)同一組數(shù)據(jù),通過計(jì)時(shí),比較兩者的搬運(yùn)效率。
直接操作寄存器
DMA的中斷狀態(tài)寄存器(DMA_ISR):
TEIFx:通道x的傳輸錯(cuò)誤標(biāo)志(x = 1 … 7) (Channel x transfer error flag) 硬件設(shè)置這些位。在DMA_IFCR寄存器的相應(yīng)位寫入’1’可以清除這里對(duì)應(yīng)的標(biāo)志位。
0:在通道x沒有傳輸錯(cuò)誤(TE); 1:在通道x發(fā)生了傳輸錯(cuò)誤(TE)。
HTIFx:通道x的半傳輸標(biāo)志(x = 1 … 7) (Channel x half transfer flag) 硬件設(shè)置這些位。在DMA_IFCR寄存器的相應(yīng)位寫入’1’可以清除這里對(duì)應(yīng)的標(biāo)志位。
0:在通道x沒有半傳輸事件(HT); 1:在通道x產(chǎn)生了半傳輸事件(HT)。
TCIFx:通道x的傳輸完成標(biāo)志(x = 1 … 7) (Channel x transfer complete flag) 硬件設(shè)置這些位。在DMA_IFCR寄存器的相應(yīng)位寫入’1’可以清除這里對(duì)應(yīng)的標(biāo)志位。
0:在通道x沒有傳輸完成事件(TC); 1:在通道x產(chǎn)生了傳輸完成事件(TC)。
DMA_IFCR中斷標(biāo)志清除寄存器:
結(jié)構(gòu)類似DMA_ISR。
CTEIFx:清除通道x的傳輸錯(cuò)誤標(biāo)志(x = 1 … 7) (Channel x transfer error clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對(duì)應(yīng)TEIF標(biāo)志。
CHTIFx:清除通道x的半傳輸標(biāo)志(x = 1 … 7) (Channel x half transfer clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對(duì)應(yīng)HTIF標(biāo)志。
CTCIFx:清除通道x的傳輸完成標(biāo)志(x = 1 … 7) (Channel x transfer complete clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對(duì)應(yīng)TCIF標(biāo)志。
CGIFx:清除通道x的全局中斷標(biāo)志(x = 1 … 7) (Channel x global interrupt clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對(duì)應(yīng)的GIF、TEIF、HTIF和TCIF標(biāo)志。
DMA通道配置寄存器(DMA_CCRx):
MEM2MEM:存儲(chǔ)器到存儲(chǔ)器模式 (Memory to memory mode) 該位由軟件設(shè)置和清除。 0:非存儲(chǔ)器到存儲(chǔ)器模式; 1:啟動(dòng)存儲(chǔ)器到存儲(chǔ)器模式。
PL:通道優(yōu)先級(jí) (Channel priority level) 這些位由軟件設(shè)置和清除。00:低 01:中 10:高 11:最高
MSIZE:存儲(chǔ)器數(shù)據(jù)寬度 (Memory size) 這些位由軟件設(shè)置和清除。 00:8位 01:16位 10:32位 11:保留
PSIZE:外設(shè)數(shù)據(jù)寬度 (Peripheral size) 這些位由軟件設(shè)置和清除。 00:8位 01:16位 10:32位 11:保留
MINC:存儲(chǔ)器地址增量模式 (Memory increment mode) 該位由軟件設(shè)置和清除。 0:不執(zhí)行存儲(chǔ)器地址增量操作 1:執(zhí)行存儲(chǔ)器地址增量操作
PINC:外設(shè)地址增量模式 (Peripheral increment mode) 該位由軟件設(shè)置和清除。 0:不執(zhí)行外設(shè)地址增量操作 1:執(zhí)行外設(shè)地址增量操作
CIRC:循環(huán)模式 (Circular mode) 該位由軟件設(shè)置和清除。 0:不執(zhí)行循環(huán)操作 1:執(zhí)行循環(huán)操作
DIR:數(shù)據(jù)傳輸方向 (Data transfer direction) 該位由軟件設(shè)置和清除。 0:從外設(shè)讀 1:從存儲(chǔ)器讀
TEIE:允許傳輸錯(cuò)誤中斷 (Transfer error interrupt enable) 該位由軟件設(shè)置和清除。 0:禁止TE中斷 0:允許TE中斷
HTIE:允許半傳輸中斷 (Half transfer interrupt enable) 該位由軟件設(shè)置和清除。 0:禁止HT中斷 0:允許HT中斷
TCIE:允許傳輸完成中斷 (Transfer complete interrupt enable) 該位由軟件設(shè)置和清除。 0:禁止TC中斷 0:允許TC中斷
EN:通道開啟 (Channel enable) 該位由軟件設(shè)置和清除。 0:通道不工作 1:通道開啟
DMA通道x傳輸數(shù)量寄存器(DMA_CNDTRx)(x = 1…7)
低16位有效。這個(gè)寄存器控制通道每次傳輸?shù)臄?shù)據(jù)量,數(shù)據(jù)傳輸數(shù)量為0至65535。該寄存器會(huì)隨著傳輸?shù)倪M(jìn)行而遞減,為0表示已經(jīng)發(fā)送完成。
DMA外設(shè)地址寄存器(DMA_CPARx)
32位寄存器。外設(shè)數(shù)據(jù)寄存器的基地址,作為數(shù)據(jù)傳輸?shù)脑椿蚰繕?biāo)。
DMA存儲(chǔ)地址寄存器(DMA_CMARx)
存儲(chǔ)器地址[31:0],存儲(chǔ)器地址作為數(shù)據(jù)傳輸?shù)脑椿蚰繕?biāo)。
代碼如下: (system.h 和stm32f10x_it.h等相關(guān)代碼參照stm32 直接操作寄存器開發(fā)環(huán)境配置)
User/main.c
#include#include "system.h"#include "usart.h" #include "dma.h"#include "tim.h" #include "string.h"#define LED1 PAout(4)#define LED2 PAout(5)#define LED3 PAout(6)void Gpio_Init(void);//數(shù)據(jù)源uc32 SRC_Const_Buffer[32] ={ 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x21314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31324,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x65758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75768,0x797A7B7C,0x7D7E7F80};//目標(biāo)位置u32 DST_Buffer[32];int main(void){ u8 i=0;u16 StartTime=0,CPUSpendTime=0,DMASpendTime=0;;Rcc_Init(9); //系統(tǒng)時(shí)鐘設(shè)置Usart1_Init(72,9600);Tim_Init(TIM_2,65535,71); //初始化TIM2定時(shí)器,設(shè)定重裝值和分頻值,計(jì)時(shí)時(shí)間為1us/次Dma_Init(DMA1_Channel1,(u32)SRC_Const_Buffer,(u32)DST_Buffer); //初始化DMA,外設(shè)地址示例 &USART1->DRNvic_Init(1,0,DMA1_Channel1_IRQChannel,4); //設(shè)置搶占優(yōu)先級(jí)為0,響應(yīng)優(yōu)先級(jí)為0,中斷分組為4Gpio_Init();StartTime = TIM2->CNT;while(i<32) //CPU搬運(yùn){DST_Buffer[i]=SRC_Const_Buffer[i];i++;}CPUSpendTime = TIM2->CNT - StartTime;printf("rn the CPU spend : %dus! rn",CPUSpendTime);if(strncmp((const char *)SRC_Const_Buffer,(const char *)DST_Buffer,32) ==0) //驗(yàn)證傳輸效果,判斷兩數(shù)組是否相同{printf("rn CPU Transmit Success! rn");}else{printf("rn CPU Transmit Fail! rn");}i=0;while(i<32) //清空目標(biāo)數(shù)組,準(zhǔn)備DMA搬運(yùn){DST_Buffer[i]=0;i++;} StartTime = TIM2->CNT;Dma_Enable(DMA1_Channel1,32);//DMA搬運(yùn)while( DMA1_Channel1 -> CNDTR != 0); //等待傳輸完成DMASpendTime= TIM2->CNT - StartTime;printf("rn the DMA spend : %dus! rn",DMASpendTime);if(strncmp((const char *)SRC_Const_Buffer,(const char *)DST_Buffer,32) ==0) //驗(yàn)證傳輸效果,判斷兩數(shù)組是否相同{printf("rn DMA Transmit Success! rn");}else{printf("rn DMA Transmit Fail! rn");} while(1); }void Gpio_Init(void){RCC->APB2ENR=1<<2; //使能PORTA時(shí)鐘 GPIOA->CRL&=0x0FFFF; // PA0~3設(shè)置為浮空輸入,PA4~7設(shè)置為推挽輸出GPIOA->CRL=0x34; //USART1 串口I/O設(shè)置GPIOA -> CRH&=0xFFFFF00F; //設(shè)置USART1 的Tx(PA.9)為第二功能推挽,50MHz;Rx(PA.10)為浮空輸入GPIOA -> CRH=0x008B0; }
User/stm32f10x_it.c
本文引用地址:http://m.butianyuan.cn/article/201611/318034.htm#include "stm32f10x_it.h"#include "system.h"#include "stdio.h"#define LED1 PAout(4)#define LED2 PAout(5)#define LED3 PAout(6)#define LED4 PAout(7)void DMAChannel1_IRQHandler(void) //和啟動(dòng)文件有關(guān),STM32F10x.s中 和 STM32F10x_md.s DMA中斷接口函數(shù)不同{if( DMA1 ->ISR & (1<<1)) //傳輸完成中斷{LED1 = 1;DMA1->IFCR = 1<<1; //清除傳輸完成中斷}if( DMA1 ->ISR & (1<<2)) //半傳輸完成中斷{DMA1 ->IFCR = 1<<2; //清除半傳輸完成中斷}if( DMA1 ->ISR & (1<<3)) //傳輸錯(cuò)誤中斷{LED4 =1 ;DMA1 ->IFCR = 1<<3; //清除傳輸錯(cuò)誤中斷}DMA1 ->IFCR = 1<<0; //清除此通道的中斷}
Library/src/dma.c
#include#include "system.h"#include "dma.h"http://DMA通道初始化函數(shù)//傳輸方向:存儲(chǔ)器 -> 存儲(chǔ)器模式 ,32位數(shù)據(jù)模式,存儲(chǔ)器增量模式//參數(shù)說明:// DMA_CHx :選擇DMA控制器通道,DMA1有1-7,DMA2有1-4// P_Adress :外設(shè)地址// M_Adress :存儲(chǔ)器地址void Dma_Init(DMA_Channel_TypeDef * DMA_CHx,u32 P_Address ,u32 M_Address){RCC->AHBENR = 1<<0;DMA_CHx -> CCR &= 0xFFFF0; //復(fù)位 DMA_CHx -> CCR = 1<<1; //允許傳輸完成中斷//DMA_CHx -> CCR = 1<<2; //允許半傳輸中斷DMA_CHx -> CCR = 1<<3; //允許傳輸錯(cuò)誤中斷 讀寫一個(gè)保留的地址區(qū)域,將會(huì)產(chǎn)生DMA傳輸錯(cuò)誤 //設(shè)定數(shù)據(jù)傳輸方向DMA_CHx -> CCR = 0<<4; //設(shè)定數(shù)據(jù)傳輸方向 0:從外設(shè)讀 1:從存儲(chǔ)器讀DMA_CHx -> CCR = 0<<5; //0:不執(zhí)行循環(huán)操作 1:執(zhí)行循環(huán)操作 //設(shè)定地址增量DMA_CHx -> CCR = 1<<6; //0:不執(zhí)行外設(shè)地址增量操作 1:執(zhí)行外設(shè)地址增量操作DMA_CHx -> CCR = 1<<7; //0:不執(zhí)行存儲(chǔ)器地址增量操作 1:執(zhí)行存儲(chǔ)器地址增量操作 //設(shè)定外設(shè)數(shù)據(jù)寬度 SDMA_CHx -> CCR = 0<<8; //外設(shè)數(shù)據(jù)寬度,由[9:8]兩位控制DMA_CHx -> CCR = 1<<9; //00:8位 01:16位 10:32位 11:保留 //設(shè)定存儲(chǔ)數(shù)據(jù)寬度DMA_CHx -> CCR = 0<<10; //存儲(chǔ)器數(shù)據(jù)寬度,由[11:10]兩位控制DMA_CHx -> CCR = 1<<11; //00:8位 01:16位 10:32位 11:保留 //設(shè)定為中等優(yōu)先級(jí)DMA_CHx -> CCR = 1<<12; //通道優(yōu)先級(jí),由[13:12]兩位控制DMA_CHx -> CCR = 1<<13; //00:低 01:中 10:高 11:最高 DMA_CHx -> CCR = 1<<14; //0:非存儲(chǔ)器到存儲(chǔ)器模式; 1:啟動(dòng)存儲(chǔ)器到存儲(chǔ)器模式。 //必須配置好通道后配置地址DMA_CHx -> CPAR = (u32)P_Address; //設(shè)定外設(shè)寄存器地址DMA_CHx -> CMAR = (u32)M_Address; //設(shè)定數(shù)據(jù)存儲(chǔ)器地址}//DMA通道使能//參數(shù)說明:// DMA_CHx :選擇DMA控制器通道,DMA1有1-7,DMA2有1-4// Number :數(shù)據(jù)傳輸量void Dma_Enable(DMA_Channel_TypeDef * DMA_CHx,u16 Number){DMA_CHx -> CCR &= ~(1<<0); //關(guān)閉上一次DMA傳輸DMA_CHx -> CNDTR = Number; //數(shù)據(jù)傳輸量DMA_CHx -> CCR = 1<<0; //開始DMA傳輸 }
Library/inc/dma.h
#includevoid Dma_Init(DMA_Channel_TypeDef * DMA_CHx,u32 P_Adress ,u32 M_Address);void Dma_Enable(DMA_Channel_TypeDef * DMA_CHx,u16 Number);
直接操作寄存器輸出:
the CPU spend : 972us!
CPU Transmit Success!
the DMA spend : 5us!
DMA Transmit Success!
庫函數(shù)操作
mian.c
#include "stm32f10x.h"#include "stdio.h"#include "string.h"#define PRINTF_ON 1#define BufferSize 32vu16 LeftDataCounter;vu32 Tick;uc32 SRC_Const_Buffer[BufferSize] = { 0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x21314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31324,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x65758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75768,0x797A7B7C,0x7D7E7F80};u32 DST_Buffer[BufferSize];u8 i=0,DMASpendTime=0,CPUSpendTime=0;void RCC_Configuration(void);void GPIO_Configuration(void);void NVIC_Configuration(void);void USART_Configuration(void);void DMA_Configuration(void);int main(void){RCC_Configuration();GPIO_Configuration();NVIC_Configuration();USART_Configuration();DMA_Configuration();SysTick_Config(72);Tick = 0;while(i
stm32f10x_it.c
#include "stm32f10x_it.h"#include "stdio.h"extern vu32 Tick;extern vu16 LeftDataCounter;void SysTick_Handler(void){Tick++;}void DMA1_Channel6_IRQHandler(void){LeftDataCounter = DMA_GetCurrDataCounter(DMA1_Channel6); //獲取剩余待傳輸數(shù)據(jù)DMA_ClearITPendingBit(DMA1_IT_GL6);}
庫函數(shù)輸出:
Transmit Success!
the CPU spend : 68us!
the DMA spend : 7us!
評(píng)論