stm32 DMA数据搬运 操作寄存器+库函数

发布者:一条属马的龙最新更新时间:2016-06-15 来源: eefocus关键字:stm32  DMA  数据搬运  操作寄存器  库函数 手机看文章 扫描二维码
随时随地手机看文章
       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通道一览表
stm32 DMA数据搬运 操作寄存器+库函数(转)
 
本例实现使用CPU和DMA搬运同一组数据,通过计时,比较两者的搬运效率。
 
直接操作寄存器
 
DMA的中断状态寄存器(DMA_ISR):
stm32 DMA数据搬运 操作寄存器+库函数(转)
 
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):
 
stm32 DMA数据搬运 操作寄存器+库函数(转)
 
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->DR

    Nvic_Init(1,0,DMA1_Channel1_IRQChannel,4);      //设置抢占优先级为0,响应优先级为0,中断分组为4

    Gpio_Init();

    StartTime = TIM2->CNT;

    while(i<32)                             //CPU搬运
    {
        DST_Buffer[i]=SRC_Const_Buffer[i];
        i++;
    }

    CPUSpendTime = TIM2->CNT - StartTime;

    printf("\r\n the CPU spend : %dus! \r\n",CPUSpendTime);

    if(strncmp((const char *)SRC_Const_Buffer,(const char *)DST_Buffer,32) ==0)      //验证传输效果,判断两数组是否相同
    {
        printf("\r\n CPU Transmit Success! \r\n");
    }else{
        printf("\r\n CPU Transmit Fail! \r\n");
    }

    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("\r\n the DMA spend : %dus! \r\n",DMASpendTime);
    
    if(strncmp((const char *)SRC_Const_Buffer,(const char *)DST_Buffer,32) ==0)      //验证传输效果,判断两数组是否相同
    {
        printf("\r\n DMA Transmit Success! \r\n");
    }else{
        printf("\r\n DMA Transmit Fail! \r\n");
    }     
    
    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

#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"


//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:执行存储器地址增量操作        

    //设定外设数据宽度    S
    DMA_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  32

vu16 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! 

关键字:stm32  DMA  数据搬运  操作寄存器  库函数 引用地址:stm32 DMA数据搬运 操作寄存器+库函数

上一篇:stm32 BKP寄存器操作操作寄存器+库函数
下一篇:STM32学习笔记之SPI_DMA寄存器级操作

推荐阅读最新更新时间:2024-03-16 14:57

基于PSoC3的多通讯接口时的DMA设计
     随着上层应用软件的日趋多样化,现在的便携式电子产品对嵌入式芯片的功能需求越来越高,单一或仅可以局部定制的传统芯片已经不能满足需要。因此数字系统和模拟系统都可以根据需要灵活定制成为芯片设计、开发的发展方向。Cypress为满足业界需要继PSoC1之后开发了PSoC3和PSoC5全新可编程模拟和数字嵌入式芯片。其中PSoC3使用基于单循环流水线的高性能8051内核(67MHz/33MIPS),PSoC5则是基于32位ARM Cortex-M3的内核(80MHz/100MIPS);两者都内置闪存、SRAM,支持片外存储器访问,在8、16和32bit应用中同时实现了高集成度和高灵活性。本文重点讲述了PSoC3在多通讯接口设计中的
[嵌入式]
STM32】一种基于Cortex-M内核的精确延时方法
Cortex-M内核是一种广泛应用于嵌入式系统中的32位微处理器内核,常常用于实现低功耗、高性能和高可靠性的应用程序。在某些应用场景中,需要对程序进行精确的延时控制,例如控制电机的启动时间、PWM波形的频率和占空比等。本文将介绍一种基于Cortex-M内核的精确延时方法,可以实现纳秒级别的精度。 1. 原理 Cortex-M内核中的SysTick定时器是一个24位的倒计时器,可以实现从1ms到2^24-1即16,777,215个时钟周期的定时。使用SysTick定时器可以实现微秒级别的延时控制,但是要实现纳秒级别的延时控制,则需要进行更高精度的计时方案。 通过测量实际的时钟周期数来精确计算时间,可以获得更高精度的计时结果。
[单片机]
STM32串口终端
[单片机]
<font color='red'>STM32</font>串口终端
STM32学习004_调试工具J_LINK
jlink是segger公司的产品,jlink的官方提供调试软件和驱动,调试软件有jlink命令(j-tag调试),j-mem(内存查看),j-flash(flash烧写)等我主要用到的是jlink commander,以下是它的命令集: SEGGER J-Link指挥官V4.20h('?'寻求帮助) 编译2010年10月5日19:11:57 DLL版本V4.20h,编译于2010年10月5日19:11:41 固件:J-Link ARM V8编译2010年5月5日08:59:59 硬件:V8.00 S / N:20100214 功能:RDI,FlashDL,FlashBP,JFlash,GDBFull VTarg
[单片机]
STM32_USART 串口通讯详解
对51单片机有了解的都知道51单片机的串口通讯工作原理,我们单片机使用的电平TTL电平,为了使我们的的单片机与PC进行通信,就需要一个电平转换芯片,把TTL电平转换为USB电平(使用的USB接口,如果使用的DB9接口,电平转换芯片则为TTL转RS232电平芯片),然后通过对SBUF寄存器的读写操作来实现PC与MCU的通信。STM32的串口通讯原理与51相同。下面就对USART尽行具体的介绍。 USART(Universal synchronous asynchronous receivertransmitter )通用同步异步收发器,是STM32上基于串口通讯协议来实现与外部通信的一个外设,因为串口通讯协议的简单,便捷,所以在
[单片机]
STM32_USART 串口通讯详解
STM32之SPI_CR1寄存器的SSM, SSI位理解
近日调试STM32的SPI程序,现在记录下自己的一点小理解。 STM32之SPI_CR1寄存器的SSM, SSI位理解 SSM位,启用或禁止软件从设备选择。SSM置位时,NSS输入引脚的电平将被SSI的值代替。 SSI位,在SSM=1时有意义,决定NSS引脚上的电平,NSS引脚上的IO值将忽略。 NSS输入分为硬件输入和软件控制输入两种模式。NSS有内部和外部引脚。当NSS是软件控制输入模式时,NSS的内部引脚和外部引脚断开。内部引脚通过SPI_CR1寄存器的SSI位来驱动,外部引脚留作他用(可以作为GPIO驱动从设备的片选信号)。 当SSM位置位使能时,启用软件从设备选择,也就是软件控制输入模式。外
[单片机]
STM32 定时器2+串口
定时器2+串口发送 USART.c #include STM32Lib\\stm32f10x.h /********************************************** **串口配置函数,这里使能了两个串口,其中串口2使用了中断接收模式 ** **********************************************/ u8 Uart1_Get_Flag; //串口1接收到数据标志 u8 Uart1_Get_Data; //串口1接收的数据 void USART_Configuration(void) { GPIO_InitTypeDef GPIO_
[单片机]
<font color='red'>STM32</font> 定时器2+串口
STM32电源管理系统浅谈
电源对电子设备的重要性不言而喻,它是保证系统稳定运行的基础,而保证系统能稳定运行后,又有低功耗的要求。在很多应用场合中都对电子设备的功耗要求非常苛刻,如某些传感器信息采集设备,仅靠小型的电池提供电源,要求工作长达数年之久,且期间不需要任何维护;由于智慧穿戴设备的小型化要求,电池体积不能太大导致容量也比较小,所以也很有必要从控制功耗入手,提高设备的续行时间。 STM32的电源管理系统主要分为: 1、备份域 2、调压器供电电路 3、ADC电源电路 备份域电路 STM32的备份域包括LSE振荡器、RTC、备份寄存器及备份SRAM这些器件,这部分的电路可以通过STM32的VBAT引脚获取供电电源,在实际应用中一般会使用3V的钮扣电
[单片机]
<font color='red'>STM32</font>电源管理系统浅谈
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved