STM32-笔记35-DMA(直接存储器访问)

一、什么叫DMA?

        DMA(Direct Memory Access,直接存储器访问)提供在外设与内存、存储器和存储器之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,CPU对于内存的工作来说就无法使用。

简单来说,就是一个数据的搬运工

而搬运的路径有三种方式:如下所示

  • 存储器→存储器(例如:复制某特别大的数据buf)
  • 存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
  • 外设→存储器 (例如:将串口RDR寄存器写入某数据buf)
  •         存储器我们知道,就是内存,存储器到存储器之间的搬运,也就是从内存中一地方搬运到内存中另一个地方。

            那么这里的外设是指什么呢?

            外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设,而这里的存储器包括 自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的

    具体在参考手册中DMA框图中可以看见外设有哪些

     二、DMA存在的意义?

            代替 CPU 搬运数据,为 CPU 减负。

  • 1. 数据搬运的工作比较耗时间;
  • 2. 数据搬运工作时效要求高(有数据来就要搬走);
  • 3. 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。
  • 三、搬运的流程

    存储器→存储器

    存储器→外设

    外设→存储器

    四、DMA控制器-通道

    STM32F103 有 2 个 DMA 控制器,DMA1 有 7 个通道,DMA 2 有 5 个通道。
    一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。
    STM32F103C8T6 只有 DMA1 !
    DMA1有7个通道:

    每个通道传输特定外设的数据

    五、DMA优先级管理

    优先级管理采用软件+硬件:
            软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级
    最高级>高级>中级>低级
            硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。
            比如:如果软件优先级相同,通道2优先于通道4

    六、DMA传输方式

     

    七、DMA寄存器及库函数介绍
     

    八、小实验1:DMA内存到内存数据搬运

    实验目的

    使用DMA将一个大数组的数据搬运到另一个位置。

    复制项目文件19-串口打印功能

    重命名为40-DMA实验(内存到内存)

    新建文件夹dma dma.c  dma.h

    打开项目文件

    加载文件

    main.c

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "uart1.h"
    #include "dma.h"
    
    int main(void)
    {
        HAL_Init();                         /* 初始化HAL库 */
        stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
        led_init();//初始化led灯
        uart1_init(115200);
        dma_init();
        printf("hello word!\r\n");
        dma_transmit();
        
    //    led1_ON();
    //    led1_OFF();
        while(1)
        { 
            led1_ON();
            led2_OFF();
            delay_ms(500);
            led1_OFF();
            led2_ON();
            delay_ms(500);
        }
    }
    

    dma.c

    #include "dma.h"
    #include "stdio.h"
    #define BUF_SIZE 16
    
    uint32_t src_buf[BUF_SIZE] = {
        0x00000000,0x11111111,0x22222222,0x33333333,
        0x44444444,0x55555555,0x66666666,0x77777777,
        0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
        0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
    };
    uint32_t dst_buf[BUF_SIZE] = {0};
    
    DMA_HandleTypeDef dma_handle = {0};
    //初始化dma函数
    void dma_init(void)
    {
        __HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1
        dma_handle.Instance = DMA1_Channel1;//选择DMA1通道1(由于是内存传递内存所以不管哪一个通道都可以)
        dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;//方向:内存到内存
        //内存的设置
        dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐
        dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动
        //外设的设置
        dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式
        dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;//外设增量模式启动
        
        dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式
        dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)
        
        HAL_DMA_Init(&dma_handle);
    }
    //准备数据传输
    void dma_transmit(void)
    {
        //启动DMA传输
        HAL_DMA_Start(&dma_handle,(uint32_t)src_buf,(uint32_t)dst_buf,sizeof(uint32_t) * BUF_SIZE);
        //查询DMA传输状态
        while(__HAL_DMA_GET_FLAG(&dma_handle,DMA_FLAG_TC1) == RESET);
        //打印数据
        int i;
        for(i=0;i<BUF_SIZE;i++)
        {
            printf("buf[%d]:%X\r\n",i,dst_buf[i]);
        }
        
    }
    

    dma.h

    #ifndef __DMA_H__
    #define __DMA_H__
    
    #include "sys.h"
    
    void dma_init(void);
    void dma_transmit(void);
        
    #endif
    

    九、小实验2:DMA内存到外设数据搬运

    实验目的

    使用DMA将一个大数组的数据通过串口1发送 。

    内存通过DMA将数据搬运到外设中

    USART1-TX负责将内存中的数据按照指定的格式和波特率发送到外部设备‌

    复制项目文件40-DMA实验(内存到内存)

    重命名项目文件41-DMA实验(内存到外设)

    打开项目文件

    main.c

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "uart1.h"
    #include "dma.h"
    
    uint8_t send_buf[1000] = {0};
    extern UART_HandleTypeDef uart1_handle;
    
    int main(void)
    {
        HAL_Init();                         /* 初始化HAL库 */
        stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
        led_init();//初始化led灯
        uart1_init(115200);
        dma_init();
        //printf("hello word!\r\n");
        
        int i = 0;
        for(i=0;i<1000;i++)
        {
            send_buf[i] = 'A';
        }
        //串口句柄,发送的数据,发送的个数
        HAL_UART_Transmit_DMA(&uart1_handle,send_buf,1000);//以DMA的方式发送数据
        while(1)
        { 
            led1_ON();
            led2_OFF();
            delay_ms(500);
            led1_OFF();
            led2_ON();
            delay_ms(500);
        }
    }
    

    dma.c

    #include "dma.h"
    #include "stdio.h"
    
    extern UART_HandleTypeDef uart1_handle;
    DMA_HandleTypeDef dma_handle = {0};
    //初始化dma函数
    void dma_init(void)
    {
        __HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1
        dma_handle.Instance = DMA1_Channel4;//选择DMA1通道4,内存到外设通道4-以DMA的方式发送(tx)给串口1数据
        dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;//方向:内存到外设
        //内存的设置
        dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐
        dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动:地址递增
        //外设的设置
        dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式
        dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设不递增
        
        dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式
        dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)
        
        HAL_DMA_Init(&dma_handle);
        //链接:内存链接到串口1:外设的句柄,成员变量,dma的句柄
        __HAL_LINKDMA(&uart1_handle,hdmatx,dma_handle);
    }
    

    dma.h

    #ifndef __DMA_H__
    #define __DMA_H__
    
    #include "sys.h"
    
    void dma_init(void);
        
    #endif
    

    结果:

    十、小实验3:DMA外设到内存数据搬运

    实验目的

    使用DMA接收串口1发的数据。

    串口通过DMA将数据搬运到内存中

    USART1-RX负责将外部设备‌中的数据按照指定的格式和波特率发送到内存,

    也就是内存接收外部设备中的数据

    所以这里的USART1-RX和USART1-TX是针对内存来讲(暂时先这么记)

    复制项目文件41-DMA实验(内存到外设)

    重命名项目文件42-DMA实验(外设到内存)

    main.c

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "uart1.h"
    #include "dma.h"
    
    int main(void)
    {
        HAL_Init();                         /* 初始化HAL库 */
        stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
        led_init();//初始化led灯
        uart1_init(115200);
        dma_init();
        printf("hello word!\r\n");
        
        while(1)
        { 
            led1_ON();
            led2_OFF();
            delay_ms(500);
            led1_OFF();
            led2_ON();
            delay_ms(500);
        }
    }
    

    dma.c

    #include "dma.h"
    #include "stdio.h"
    #include "uart1.h"
    
    extern UART_HandleTypeDef uart1_handle;
    DMA_HandleTypeDef dma_handle = {0};
    extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收缓冲区 */
                                                   
    
    //初始化dma函数
    void dma_init(void)
    {
        __HAL_RCC_DMA1_CLK_ENABLE();//使能DMA1
        dma_handle.Instance = DMA1_Channel5;//选择DMA1通道5,外设到内存通道5-以DMA的方式发送(rx)给串口1数据
        dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//方向:外设到内存
        //内存的设置
        dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//内存数据对其方式:字节方式对齐
        dma_handle.Init.MemInc = DMA_MINC_ENABLE;//内存增量启动:地址递增
        //外设的设置
        dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//外设数据对其方式
        dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;//外设不递增
        
        dma_handle.Init.Mode = DMA_NORMAL;//DMA的模式:正常模式和循环模式
        dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;//DMA优先级:中级(只有一个DMA设置成什么无所谓)
        
        HAL_DMA_Init(&dma_handle);
        //链接:内存链接到串口1:外设的句柄,成员变量,dma的句柄
        __HAL_LINKDMA(&uart1_handle,hdmarx,dma_handle);
        //串口通过DMA接收
        HAL_UART_Receive_DMA(&uart1_handle,uart1_rx_buf,UART1_RX_BUF_SIZE);//将数据接收到uart1_rx_buf缓冲区里面长度是UART1_RX_BUF_SIZE这么长
    }

    dma.h

    #ifndef __DMA_H__
    #define __DMA_H__
    
    #include "sys.h"
    
    void dma_init(void);
        
    #endif
    

    结果:

    作者:1101 1101

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32-笔记35-DMA(直接存储器访问)

    发表回复