新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > stm32 DMA數(shù)據(jù)搬運 操作寄存器+庫函數(shù)

stm32 DMA數(shù)據(jù)搬運 操作寄存器+庫函數(shù)

作者: 時間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
DMA(Direct Memory Access)常譯為“存儲器直接存取”。早在Intel的8086平臺上就有了DMA應(yīng)用了。
一個完整的微控制器通常由CPU、存儲器和外設(shè)等組件構(gòu)成。這些組件一般在結(jié)構(gòu)和功能上都是獨立的,而各個組件的協(xié)調(diào)和交互就由CPU完成。如此一來,CPU作為整個芯片的核心,其處理的工作量是很大的。如果CPU先從A外設(shè)拿到一個數(shù)據(jù)送給B外設(shè)使用,同時C外設(shè)又需要D外設(shè)提供一個數(shù)據(jù)。。。這樣的數(shù)據(jù)搬運工作將使CPU的負荷顯得相當繁重。
嚴格的說,搬運數(shù)據(jù)只是CPU的比較不重要的一種工作。CPU最重要的工作室進行數(shù)據(jù)運算,從加減乘除到一些高級的運算,包括浮點、積分、微分、FFT等。CPU還需要負責(zé)復(fù)雜的中斷申請和響應(yīng),以保證芯片的實時性能。
理論上常見的控制外設(shè),比如Usart、I2C、SPI甚至是USB等通信接口,單純的利用CPU進行協(xié)議模擬也是可以實現(xiàn)的,比如51單片機經(jīng)常使用I/O口模擬I2C協(xié)議通信。但這樣既浪費了CPU的資源,同時實現(xiàn)后的性能表現(xiàn)往往和使用專門的硬件模塊實現(xiàn)的效果相差甚遠。從這個角度來看,各個外設(shè)控制器的存在,無疑降低了CPU的負擔,解放了CPU的資源。
數(shù)據(jù)搬運這一工作占用了大部分的CPU資源,成為了降低CPU的工作效率的主要原因之一。于是需要一種硬件結(jié)構(gòu)分擔CPU這一職能 —— DMA。
從數(shù)據(jù)搬運的角度看,如果要把存儲地址A的數(shù)值賦給另外一個地址上B的變量,CPU實現(xiàn)過程為首先讀出A地址上的數(shù)據(jù)存儲在一個中間變量,然后再轉(zhuǎn)送到B地址的變量上。使用DMA則不需要中間變量,直接將A地址的數(shù)值傳送到B地址的變量里。無疑減輕了CPU的負擔,也提高了數(shù)據(jù)搬運的效率。
stm32中 DMA1有7個通道,DMA2有5個通道。DMA掛載的時鐘為AHB總線,其時鐘為72Mhz,所以可以實現(xiàn)高速數(shù)據(jù)搬運。
stm32的DMA1通道一覽表
本例實現(xiàn)使用CPU和DMA搬運同一組數(shù)據(jù),通過計時,比較兩者的搬運效率。
直接操作寄存器
DMA的中斷狀態(tài)寄存器(DMA_ISR):
TEIFx:通道x的傳輸錯誤標志(x = 1 … 7) (Channel x transfer error flag) 硬件設(shè)置這些位。在DMA_IFCR寄存器的相應(yīng)位寫入’1’可以清除這里對應(yīng)的標志位。
0:在通道x沒有傳輸錯誤(TE); 1:在通道x發(fā)生了傳輸錯誤(TE)。
HTIFx:通道x的半傳輸標志(x = 1 … 7) (Channel x half transfer flag) 硬件設(shè)置這些位。在DMA_IFCR寄存器的相應(yīng)位寫入’1’可以清除這里對應(yīng)的標志位。
0:在通道x沒有半傳輸事件(HT); 1:在通道x產(chǎn)生了半傳輸事件(HT)。
TCIFx:通道x的傳輸完成標志(x = 1 … 7) (Channel x transfer complete flag) 硬件設(shè)置這些位。在DMA_IFCR寄存器的相應(yīng)位寫入’1’可以清除這里對應(yīng)的標志位。
0:在通道x沒有傳輸完成事件(TC); 1:在通道x產(chǎn)生了傳輸完成事件(TC)。
DMA_IFCR中斷標志清除寄存器:
結(jié)構(gòu)類似DMA_ISR。
CTEIFx:清除通道x的傳輸錯誤標志(x = 1 … 7) (Channel x transfer error clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應(yīng)TEIF標志。
CHTIFx:清除通道x的半傳輸標志(x = 1 … 7) (Channel x half transfer clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應(yīng)HTIF標志。
CTCIFx:清除通道x的傳輸完成標志(x = 1 … 7) (Channel x transfer complete clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應(yīng)TCIF標志。
CGIFx:清除通道x的全局中斷標志(x = 1 … 7) (Channel x global interrupt clear) 這些位由軟件設(shè)置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應(yīng)的GIF、TEIF、HTIF和TCIF標志。
DMA通道配置寄存器(DMA_CCRx):
MEM2MEM:存儲器到存儲器模式 (Memory to memory mode) 該位由軟件設(shè)置和清除。 0:非存儲器到存儲器模式; 1:啟動存儲器到存儲器模式。
PL:通道優(yōu)先級 (Channel priority level) 這些位由軟件設(shè)置和清除。00:低 01:中 10:高 11:最高
MSIZE:存儲器數(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:存儲器地址增量模式 (Memory increment mode) 該位由軟件設(shè)置和清除。 0:不執(zhí)行存儲器地址增量操作 1:執(zhí)行存儲器地址增量操作
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:從存儲器讀
TEIE:允許傳輸錯誤中斷 (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位有效。這個寄存器控制通道每次傳輸?shù)臄?shù)據(jù)量,數(shù)據(jù)傳輸數(shù)量為0至65535。該寄存器會隨著傳輸?shù)倪M行而遞減,為0表示已經(jīng)發(fā)送完成。
DMA外設(shè)地址寄存器(DMA_CPARx)
32位寄存器。外設(shè)數(shù)據(jù)寄存器的基地址,作為數(shù)據(jù)傳輸?shù)脑椿蚰繕恕?/div>
DMA存儲地址寄存器(DMA_CMARx)
存儲器地址[31:0],存儲器地址作為數(shù)據(jù)傳輸?shù)脑椿蚰繕恕?/div>
代碼如下: (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};//目標位置u32 DST_Buffer[32];int main(void){                  u8 i=0;u16 StartTime=0,CPUSpendTime=0,DMASpendTime=0;;Rcc_Init(9);                            //系統(tǒng)時鐘設(shè)置Usart1_Init(72,9600);Tim_Init(TIM_2,65535,71);            //初始化TIM2定時器,設(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)先級為0,響應(yīng)優(yōu)先級為0,中斷分組為4Gpio_Init();StartTime = TIM2->CNT;while(i<32)                             //CPU搬運{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)      //驗證傳輸效果,判斷兩數(shù)組是否相同{printf("rn CPU Transmit Success! rn");}else{printf("rn CPU Transmit Fail! rn");}i=0;while(i<32)                            //清空目標數(shù)組,準備DMA搬運{DST_Buffer[i]=0;i++;}  StartTime = TIM2->CNT;Dma_Enable(DMA1_Channel1,32);//DMA搬運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)      //驗證傳輸效果,判斷兩數(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時鐘     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://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)  //和啟動文件有關(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))        //傳輸錯誤中斷{LED4 =1 ;DMA1 ->IFCR = 1<<3;    //清除傳輸錯誤中斷}DMA1 ->IFCR = 1<<0;        //清除此通道的中斷}

Library/src/dma.c

#include #include "system.h"#include "dma.h"http://DMA通道初始化函數(shù)//傳輸方向:存儲器 -> 存儲器模式 ,32位數(shù)據(jù)模式,存儲器增量模式//參數(shù)說明://            DMA_CHx         :選擇DMA控制器通道,DMA1有1-7,DMA2有1-4//            P_Adress     :外設(shè)地址//            M_Adress     :存儲器地址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;            //允許傳輸錯誤中斷 讀寫一個保留的地址區(qū)域,將會產(chǎn)生DMA傳輸錯誤  //設(shè)定數(shù)據(jù)傳輸方向DMA_CHx -> CCR  = 0<<4;            //設(shè)定數(shù)據(jù)傳輸方向   0:從外設(shè)讀 1:從存儲器讀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í)行存儲器地址增量操作 1:執(zhí)行存儲器地址增量操作        //設(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è)定存儲數(shù)據(jù)寬度DMA_CHx -> CCR  = 0<<10;            //存儲器數(shù)據(jù)寬度,由[11:10]兩位控制DMA_CHx -> CCR  = 1<<11;            //00:8位 01:16位 10:32位 11:保留   //設(shè)定為中等優(yōu)先級DMA_CHx -> CCR  = 1<<12;            //通道優(yōu)先級,由[13:12]兩位控制DMA_CHx -> CCR  = 1<<13;            //00:低 01:中 10:高 11:最高       DMA_CHx -> CCR  = 1<<14;            //0:非存儲器到存儲器模式; 1:啟動存儲器到存儲器模式。        //必須配置好通道后配置地址DMA_CHx -> CPAR = (u32)P_Address;    //設(shè)定外設(shè)寄存器地址DMA_CHx -> CMAR = (u32)M_Address;    //設(shè)定數(shù)據(jù)存儲器地址}//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

#include     void 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!



評論


技術(shù)專區(qū)

關(guān)閉