On the STM32 MCU's DMA technology

On the MCU's DMA technology

DMA Technical Overview

DMA peripherals and memory (or memory and memory) technology to exchange data directly without passing through the CPU bus. In the MCU, the DMA is a very important technique which can reduce the processing pressure of the CPU, improving the processing efficiency of the peripheral data.

concept:

  • Channel: DMA channel indicates a request for a set of peripheral memory,
  • Data alignment: the source and destination address of the data source to be aligned alignment transfer width
  • Arbiter : can set the priority, the priority decision by the same hardware software coordinated priority, a plurality of peripheral access the same memory

DMA definition can be seen, this is a use of bus technology, reducing the pressure and the CPU reads the data stored in the above, can perform other operations. When the CPU initializes the transfer operation, the transfer operation itself is a DMA controller to implement and complete.

DMA controller and Cortex-M3 cores share a memory system data bus to perform direct data transfer. When simultaneous access to the same target CPU and DMA (RAM or peripheral), DMA may request access to system bus of the CPU is stopped several cycles, the bus arbiter performs scheduling cycle, in order to ensure that the CPU can be at least half of the system bus (memory or peripherals) bandwidth.

stm32F4 in DMA

DMA Key Features

Direct memory access (DMA) for providing high speed data transfer between the memory and the memory, and in between peripherals and memories. DMA data can be moved quickly by the CPU without any operation. This saves CPU resources for other operational use.

DMA controller architecture based complex bus matrix, the powerful double AHB master bus architecture binding together separate FIFO to optimize the system bandwidth.

There are two DMA controller 16 data streams (each controller 8), each have a DMA controller for managing one or more peripheral memory access request. Each data stream may have a total of up to eight channels (or request). Each channel has an arbiter for processing priority between the DMA request.

  • Each data stream has a separate four 32-bit FIFO buffer memory (FIFO)

  • For each stream request up to eight channels. This selection can be software configured to allow several (several, while attention is not enabled). In DMA_SxCRthe data flow configuration register, CHSEL[2:0]uniquely determine a channel) request to start DMA Peripheral

The manual is on the block diagram:

According to the table can be seen, a DMA controller manages eight data streams, each data stream having eight channels, each data stream between peripherals and memory for data exist in a FIFO buffer.

Each data stream is associated with a DMA request, but can not each channel is associated with a DMA request, for example, DMA1:

I.e., each stream within a DMA channel request to take effect, and the target transmission source transmitting the entire 4 GB area (between the address 0x0000 0000 and 0xFFFF FFFF) can be addressed peripherals and memory.

Three transmission modes

  • Peripheral to memory
    • This mode is enabled (the DMA_SxCRtime register bit EN is set 1), each time a request peripherals, data source stream starts to transmit FIFO.
    • When reaching the threshold level of the FIFO, the FIFO contents are removed and stored in the destination, after every time the data transfer from the peripheral to the FIFO, the corresponding data will be immediately removed and stored in the direct mode destination.
    • Only after winning an arbitration, FIFO data will be removed
  • Memory-to-peripheral
    • This mode is enabled (the DMA_SxCRtime position register EN 1), the data stream transfer starts immediately, from the source FIFO is completely filled.
    • Each peripheral request occurs, FIFO contents are removed and stored in the destination.
    • Only after winning the arbitration of data streams, the corresponding data stream should have access to AHB source or destination port.
  • Memory-to-memory
    • DMA channel can also work in the case where no peripheral request trigger.
    • By DMA_SxCRthe time the data stream to enable register enable bit (EN) is set, the data stream immediately begins to fill the FIFO, until it reaches the threshold level. After reaching a threshold level, the contents of the FIFO will be removed and stored into the destination.
    • Only after winning the arbitration of data streams, the corresponding data stream should have access to AHB source or destination port.

Said above these three modes, the simple fact to understand is that the data can be first into the FIFO, waiting for the next trigger is removed or write, or direct FIFO mode, do not wait to reach the threshold to operate.

DMA configuration and workflow

DMA is analogous to move cargo warehouse, so you need to configure the following basic requirements: a place in the repository, the unit size of the warehouse warehouse transfer manner. HAL way to configure the library as an example:

  • Configure the destination and source, here is the location of the warehouse it can be considered
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR; //外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr; //DMA 存储器0地址
  • Configure the DMA buffer size, it can be considered as warehouse capacity
DMA_InitStructure.DMA_BufferSize = DMA_BufferSize; //数据传输量
  • Peripheral configuration and whether the incremented memory address registers, the data here means is incremented in the same place or put down, the source and destination can be equipped with a separate
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc; //存储器增量模式
  • Set peripheral and memory width, warehouse units, which will be placed is inconsistent problems.
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //外设数据长度:32位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize; //存储器数据长度
  • Set DMA mode (or normal cycle (single))
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 使用循环模式
  • Setting priorities
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //高优先级
  • Setting mode (peripheral to memory, memory to peripheral, memory-to-memory)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //外设到存储器模式
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //使用全FIFO
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //外设突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //存储器突发单次传输

In the configuration of DMA Example camera DCMI

First, look at the code:

//DCMI DMA配置
//DMA_Memory0BaseAddr:存储器地址    将要存储摄像头数据的内存地址(也可以是外设地址)
//DMA_BufferSize:存储器长度    0~65535
//DMA_MemoryDataSize:存储器位宽
//DMA_MemoryDataSize:存储器位宽
//DMA_MemoryInc:存储器增长方式
void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr, u16 DMA_BufferSize, u32 DMA_MemoryDataSize, u32 DMA_MemoryInc)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    //RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);//DCMI 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能 
    
    DMA_DeInit(DMA2_Stream1);
    while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE) //等待DMA2_Stream1可配置
    {
    }

    /* 配置 DMA Stream */
    DMA_InitStructure.DMA_Channel = DMA_Channel_1;  //通道1 DCMI通道
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR; //外设地址为:DCMI->DR
    DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr; //DMA 存储器0地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //外设到存储器模式
    DMA_InitStructure.DMA_BufferSize = DMA_BufferSize; //数据传输量
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc; //存储器增量模式
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //外设数据长度:32位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize; //存储器数据长度
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 使用循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //高优先级
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO模式
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //使用全FIFO
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //外设突发单次传输
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //存储器突发单次传输
    DMA_Init(DMA2_Stream1, &DMA_InitStructure); //初始化DMA Stream

    DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);
    NVIC_InitStructure.NVIC_IRQChannel= DMA2_Stream1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}

void DMA2_Stream1_IRQHandler(void)
{        
    if(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1)==SET)//DMA2_Steam1,传输完成标志
    {  
         DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1);//清除传输完成中断
            datanum++;
    }                                                
}

The first is manual, the DMA may be used DCMI data stream is a 1, the configuration of the flow passage DMA2 streams 1 and 7:

  • DMA2 wait while loop configuration DMA2 complete after a transfer, where a Stream 1 of Channel.
  • After configuring peripheral address and a memory address, transfer mode, a data transfer amount, i.e., 1 byte per transfer.
  • DCMI address is fixed, the peripheral non-incremental, memory an LCD, a non-fixed address increment, length is 16 bits, the peripheral length is selected 16-bit RGB565.
  • The transmission cycle is displayed, so to cycle mode, and the transmission FIFO configuration, initialization and interrupt DMA2

DMA read and write data using the comparison operation of the CPU

We do a little experiment, may not necessarily be accurate, i.e., the timer counter to the transmission data through the DMA USART, and compare the data call by the CPU USART transmits the time code based on the atom are modified from the code.

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "sram.h"
#include "malloc.h"
#include "ILI93xx.h"
#include "led.h"
#include "timer.h"
#include "touch.h"
#include "GUI.h"
#include "GUIDemo.h"
#include "includes.h"
#include "../RESOURCES/logo/logo/Logo.h"
#include "usmart.h"
#include "spi.h"
#include "w25qxx.h"
#include "24cxx.h"
#include "main_tasks.h"
#include "ff.h"
#include "exfuns.h"
#include "fattester.h"
#include "adc.h"
#include "dma.h"

volatile uint32_t gcounter = 0;
const u8 TEXT_TO_SEND[]={"STM32F4 DMA TEST. "};
void extra_while_task(void);

//通用定时器5中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!
void TIM5_Int_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);  ///使能TIM4时钟

    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;  //定时器分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseInitStructure.TIM_Period = arr;   //自动重装载值
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure);

    TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE); //允许定时器3更新中断
    TIM_Cmd(TIM5, ENABLE); //使能定时器3

    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //定时器4中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

}
//定时器5中断服务函数
void TIM5_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM5, TIM_IT_Update) == SET) //溢出中断
    {
        gcounter++;
    }
    TIM_ClearITPendingBit(TIM5, TIM_IT_Update);  //清除中断标志位
}

//DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量  
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{ 
 
    DMA_InitTypeDef  DMA_InitStructure;
    
    if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
    {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能 
        
    }else 
    {
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能 
    }
  DMA_DeInit(DMA_Streamx);
    
    while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置 
    
  /* 配置 DMA Stream */
  DMA_InitStructure.DMA_Channel = chx;  //通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址
  DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器0地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
  DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量 
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式 
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
  DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream
    

} 
//开启一次DMA传输
//DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 
//ndtr:数据传输量  
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
 
    DMA_Cmd(DMA_Streamx, DISABLE);                      //关闭DMA传输 
    
    while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}  //确保DMA可以被设置  
        
    DMA_SetCurrDataCounter(DMA_Streamx,ndtr);          //数据传输量  
 
    DMA_Cmd(DMA_Streamx, ENABLE);                      //开启DMA传输 
}

int main(void)
{
    u8 count = 0;
    uint8_t buffer[256];
    u8 res = 0;
    POINT_COLOR = DARKBLUE;
    delay_init(168);        //延时初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);     //中断分组配置
    uart_init(115200);      //串口波特率设置
    TFTLCD_Init();          //初始化LCD
    KEY_Init();
    LED_Init();             //LED初始化
    TIM3_Int_Init(10000 - 1, 16800 - 1); //10Khz计数,1秒钟中断一次
    //不需要经过OS的任务
    extra_while_task();
    
}

#define SEND_BUF_SIZE 8000
void extra_while_task(void)
{
    u32 i = 0;
    u8 t=0,mask=0,j=sizeof(TEXT_TO_SEND);
    u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
    if(1)
    {
        for(i=0;i<SEND_BUF_SIZE;i++)//填充ASCII字符集数据
        {
            if(t>=j)//加入换行符
            {
                if(mask)
                {
                    SendBuff[i]=0x0a;
                    t=0;
                }else 
                {
                    SendBuff[i]=0x0d;
                    mask++;
                }   
            }else//复制TEXT_TO_SEND语句
            {
                mask=0;
                SendBuff[i]=TEXT_TO_SEND[t];
                t++;
            }          
        }        
        Adc_Init();         //初始化ADC
        TIM5_Int_Init(10 - 1, 16800 - 1); //10Khz计数,10个us中断一次
        while(1)
        {
            //Get_Adc_Average(ADC_Channel_5,20);//获取通道5的转换值,20次取平均
            //按DMA发送
            MYDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA2,STEAM7,CH4,外设为串口1,存储器为SendBuff,长度为:SEND_BUF_SIZE.
            USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  //使能串口1的DMA发送     
            gcounter = 0;
            MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE);     //开始一次DMA传输!
            while(1)
            {
                if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
                { 
                    DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清除DMA2_Steam7传输完成标志
                    printf("dma over: %d\r\n",gcounter);
                    break; 
                }
            }
            //按CPU发送
            printf("cpu start\r\n");
            gcounter = 0;
            for(i = 0;i<SEND_BUF_SIZE;i++)
            {
                while ((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
                USART1->DR = (u8)SendBuff[i];
            }
            printf("cpu over: %d\r\n",gcounter);
            while(1);//halt
        }
    }
}

The experimental results, the DMA count is 355:

USART 392 is sent directly to:

The amount of data sent is 8000 bytes, corresponds to 8K, when a larger amount of data the DMA advantages are obvious.

Guess you like

Origin www.cnblogs.com/RegressionWorldLine/p/11788248.html