新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > stm32 DMA數據搬運 操作寄存器+庫函數

        stm32 DMA數據搬運 操作寄存器+庫函數

        作者: 時間:2016-11-09 來源:網絡 收藏
        DMA(Direct Memory Access)常譯為“存儲器直接存取”。早在Intel的8086平臺上就有了DMA應用了。
        一個完整的微控制器通常由CPU、存儲器和外設等組件構成。這些組件一般在結構和功能上都是獨立的,而各個組件的協調和交互就由CPU完成。如此一來,CPU作為整個芯片的核心,其處理的工作量是很大的。如果CPU先從A外設拿到一個數據送給B外設使用,同時C外設又需要D外設提供一個數據。。。這樣的數據搬運工作將使CPU的負荷顯得相當繁重。
        嚴格的說,搬運數據只是CPU的比較不重要的一種工作。CPU最重要的工作室進行數據運算,從加減乘除到一些高級的運算,包括浮點、積分、微分、FFT等。CPU還需要負責復雜的中斷申請和響應,以保證芯片的實時性能。
        理論上常見的控制外設,比如Usart、I2C、SPI甚至是USB等通信接口,單純的利用CPU進行協議模擬也是可以實現的,比如51單片機經常使用I/O口模擬I2C協議通信。但這樣既浪費了CPU的資源,同時實現后的性能表現往往和使用專門的硬件模塊實現的效果相差甚遠。從這個角度來看,各個外設控制器的存在,無疑降低了CPU的負擔,解放了CPU的資源。
        數據搬運這一工作占用了大部分的CPU資源,成為了降低CPU的工作效率的主要原因之一。于是需要一種硬件結構分擔CPU這一職能 —— DMA。
        從數據搬運的角度看,如果要把存儲地址A的數值賦給另外一個地址上B的變量,CPU實現過程為首先讀出A地址上的數據存儲在一個中間變量,然后再轉送到B地址的變量上。使用DMA則不需要中間變量,直接將A地址的數值傳送到B地址的變量里。無疑減輕了CPU的負擔,也提高了數據搬運的效率。
        stm32中 DMA1有7個通道,DMA2有5個通道。DMA掛載的時鐘為AHB總線,其時鐘為72Mhz,所以可以實現高速數據搬運。
        stm32的DMA1通道一覽表
        本例實現使用CPU和DMA搬運同一組數據,通過計時,比較兩者的搬運效率。
        直接操作寄存器
        DMA的中斷狀態寄存器(DMA_ISR):
        TEIFx:通道x的傳輸錯誤標志(x = 1 … 7) (Channel x transfer error flag) 硬件設置這些位。在DMA_IFCR寄存器的相應位寫入’1’可以清除這里對應的標志位。
        0:在通道x沒有傳輸錯誤(TE); 1:在通道x發生了傳輸錯誤(TE)。
        HTIFx:通道x的半傳輸標志(x = 1 … 7) (Channel x half transfer flag) 硬件設置這些位。在DMA_IFCR寄存器的相應位寫入’1’可以清除這里對應的標志位。
        0:在通道x沒有半傳輸事件(HT); 1:在通道x產生了半傳輸事件(HT)。
        TCIFx:通道x的傳輸完成標志(x = 1 … 7) (Channel x transfer complete flag) 硬件設置這些位。在DMA_IFCR寄存器的相應位寫入’1’可以清除這里對應的標志位。
        0:在通道x沒有傳輸完成事件(TC); 1:在通道x產生了傳輸完成事件(TC)。
        DMA_IFCR中斷標志清除寄存器:
        結構類似DMA_ISR。
        CTEIFx:清除通道x的傳輸錯誤標志(x = 1 … 7) (Channel x transfer error clear) 這些位由軟件設置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應TEIF標志。
        CHTIFx:清除通道x的半傳輸標志(x = 1 … 7) (Channel x half transfer clear) 這些位由軟件設置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應HTIF標志。
        CTCIFx:清除通道x的傳輸完成標志(x = 1 … 7) (Channel x transfer complete clear) 這些位由軟件設置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應TCIF標志。
        CGIFx:清除通道x的全局中斷標志(x = 1 … 7) (Channel x global interrupt clear) 這些位由軟件設置和清除。 0:不起作用 1:清除DMA_ISR寄存器中的對應的GIF、TEIF、HTIF和TCIF標志。
        DMA通道配置寄存器(DMA_CCRx):
        MEM2MEM:存儲器到存儲器模式 (Memory to memory mode) 該位由軟件設置和清除。 0:非存儲器到存儲器模式; 1:啟動存儲器到存儲器模式。
        PL:通道優先級 (Channel priority level) 這些位由軟件設置和清除。00:低 01:中 10:高 11:最高
        MSIZE:存儲器數據寬度 (Memory size) 這些位由軟件設置和清除。 00:8位 01:16位 10:32位 11:保留
        PSIZE:外設數據寬度 (Peripheral size) 這些位由軟件設置和清除。 00:8位 01:16位 10:32位 11:保留
        MINC:存儲器地址增量模式 (Memory increment mode) 該位由軟件設置和清除。 0:不執行存儲器地址增量操作 1:執行存儲器地址增量操作
        PINC:外設地址增量模式 (Peripheral increment mode) 該位由軟件設置和清除。 0:不執行外設地址增量操作 1:執行外設地址增量操作
        CIRC:循環模式 (Circular mode) 該位由軟件設置和清除。 0:不執行循環操作 1:執行循環操作
        DIR:數據傳輸方向 (Data transfer direction) 該位由軟件設置和清除。 0:從外設讀 1:從存儲器讀
        TEIE:允許傳輸錯誤中斷 (Transfer error interrupt enable) 該位由軟件設置和清除。 0:禁止TE中斷 0:允許TE中斷
        HTIE:允許半傳輸中斷 (Half transfer interrupt enable) 該位由軟件設置和清除。 0:禁止HT中斷 0:允許HT中斷
        TCIE:允許傳輸完成中斷 (Transfer complete interrupt enable) 該位由軟件設置和清除。 0:禁止TC中斷 0:允許TC中斷
        EN:通道開啟 (Channel enable) 該位由軟件設置和清除。 0:通道不工作 1:通道開啟
        DMA通道x傳輸數量寄存器(DMA_CNDTRx)(x = 1…7)
        低16位有效。這個寄存器控制通道每次傳輸的數據量,數據傳輸數量為0至65535。該寄存器會隨著傳輸的進行而遞減,為0表示已經發送完成。
        DMA外設地址寄存器(DMA_CPARx)
        32位寄存器。外設數據寄存器的基地址,作為數據傳輸的源或目標。
        DMA存儲地址寄存器(DMA_CMARx)
        存儲器地址[31:0],存儲器地址作為數據傳輸的源或目標。
        代碼如下: (system.h 和stm32f10x_it.h等相關代碼參照stm32 直接操作寄存器開發環境配置)
        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);//數據源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);                            //系統時鐘設置Usart1_Init(72,9600);Tim_Init(TIM_2,65535,71);            //初始化TIM2定時器,設定重裝值和分頻值,計時時間為1us/次Dma_Init(DMA1_Channel1,(u32)SRC_Const_Buffer,(u32)DST_Buffer);    //初始化DMA,外設地址示例 &USART1->DRNvic_Init(1,0,DMA1_Channel1_IRQChannel,4);      //設置搶占優先級為0,響應優先級為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)      //驗證傳輸效果,判斷兩數組是否相同{printf("rn CPU Transmit Success! rn");}else{printf("rn CPU Transmit Fail! rn");}i=0;while(i<32)                            //清空目標數組,準備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)      //驗證傳輸效果,判斷兩數組是否相同{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設置為浮空輸入,PA4~7設置為推挽輸出GPIOA->CRL=0x34; //USART1 串口I/O設置GPIOA -> CRH&=0xFFFFF00F;   //設置USART1 的Tx(PA.9)為第二功能推挽,50MHz;Rx(PA.10)為浮空輸入GPIOA -> CRH=0x008B0;      }

        User/stm32f10x_it.c

        本文引用地址:http://www.104case.com/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)  //和啟動文件有關,STM32F10x.s中 和  STM32F10x_md.s DMA中斷接口函數不同{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通道初始化函數//傳輸方向:存儲器 -> 存儲器模式 ,32位數據模式,存儲器增量模式//參數說明://            DMA_CHx         :選擇DMA控制器通道,DMA1有1-7,DMA2有1-4//            P_Adress     :外設地址//            M_Adress     :存儲器地址void Dma_Init(DMA_Channel_TypeDef * DMA_CHx,u32 P_Address ,u32 M_Address){RCC->AHBENR = 1<<0;DMA_CHx -> CCR  &= 0xFFFF0;        //復位      DMA_CHx -> CCR  = 1<<1;            //允許傳輸完成中斷//DMA_CHx -> CCR  = 1<<2;            //允許半傳輸中斷DMA_CHx -> CCR  = 1<<3;            //允許傳輸錯誤中斷 讀寫一個保留的地址區域,將會產生DMA傳輸錯誤  //設定數據傳輸方向DMA_CHx -> CCR  = 0<<4;            //設定數據傳輸方向   0:從外設讀 1:從存儲器讀DMA_CHx -> CCR  = 0<<5;            //0:不執行循環操作 1:執行循環操作            //設定地址增量DMA_CHx -> CCR  = 1<<6;            //0:不執行外設地址增量操作 1:執行外設地址增量操作DMA_CHx -> CCR  = 1<<7;            //0:不執行存儲器地址增量操作 1:執行存儲器地址增量操作        //設定外設數據寬度    SDMA_CHx -> CCR  = 0<<8;            //外設數據寬度,由[9:8]兩位控制DMA_CHx -> CCR  = 1<<9;            //00:8位 01:16位 10:32位 11:保留   //設定存儲數據寬度DMA_CHx -> CCR  = 0<<10;            //存儲器數據寬度,由[11:10]兩位控制DMA_CHx -> CCR  = 1<<11;            //00:8位 01:16位 10:32位 11:保留   //設定為中等優先級DMA_CHx -> CCR  = 1<<12;            //通道優先級,由[13:12]兩位控制DMA_CHx -> CCR  = 1<<13;            //00:低 01:中 10:高 11:最高       DMA_CHx -> CCR  = 1<<14;            //0:非存儲器到存儲器模式; 1:啟動存儲器到存儲器模式。        //必須配置好通道后配置地址DMA_CHx -> CPAR = (u32)P_Address;    //設定外設寄存器地址DMA_CHx -> CMAR = (u32)M_Address;    //設定數據存儲器地址}//DMA通道使能//參數說明://            DMA_CHx         :選擇DMA控制器通道,DMA1有1-7,DMA2有1-4//            Number       :數據傳輸量void Dma_Enable(DMA_Channel_TypeDef * DMA_CHx,u16 Number){DMA_CHx -> CCR &= ~(1<<0);        //關閉上一次DMA傳輸DMA_CHx    -> CNDTR = Number;        //數據傳輸量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!
        庫函數操作
        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);   //獲取剩余待傳輸數據DMA_ClearITPendingBit(DMA1_IT_GL6);}
        庫函數輸出:
        Transmit Success!
        the CPU spend : 68us!
        the DMA spend : 7us!



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 高清| 关岭| 浦北县| 道孚县| 黄大仙区| 岚皋县| 五台县| 孝昌县| 花莲市| 贺州市| 绥宁县| 武功县| 萨嘎县| 金平| 且末县| 出国| 六枝特区| 民乐县| 旬邑县| 荥阳市| 临沭县| 卓资县| 麻江县| 公主岭市| 白银市| 修文县| 高安市| 称多县| 砀山县| 泸定县| 黔西| 济源市| 延庆县| 辽源市| 宁都县| 宝兴县| 米泉市| 平塘县| 轮台县| 贺兰县| 桐梓县|