STM32_8(DMA)

一、DMA

  • DMA(Direct Memory Access)直接存储器存取
  • DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
  • 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
  • 每个通道都支持软件触发和特定的硬件触发
  • 存储器和存储器之间的数据转运用软件触发,外设到存储器的数据转运用硬件触发
  • STM32F103C8T6 DMA资源:DMA1(7个通道)

1. 存储器映像

2. DMA框图

可以把这张图看为CPU(Cortex-M3核心)和存储器两个部分,Flash是主闪存,SRAM是运行内存。

寄存器:CPU可以读写寄存器,并且寄存器的每一位后面都连接着一根导线,这些导线可以用于控制外设电路的状态,所以寄存器就是连接软件和硬件的桥梁。

总线矩阵的左端是主动单元,拥有存储器的访问权,右边是被动单元,它们的存储器只能被左边的主动单元读写。

3. DMA基本结构

如果是硬件触发,需要在对应的外设调用XXX_DMACmd,开启触发信号的输出。

如果需要DMA中断,需要调用DMA_ITConfig来开启中断输出,再在NVIC配置相应的中断通道,然后再写中断函数即可。

在运行过程中,如果转运完成,传输计数器清0,如果再给传输器赋值,需要先让DMA失能,然后写传输计数器的值,再让DMA使能即可。

4. DMA请求

5. DMA 举例

1. 数据转运+DMA

任务:将左边的SRAM数组转换到右边的SRAM数组。

2. ADC扫描模式+DMA

左边触发一次DMA,7个通道一次进行AD转换,每一次的转换结果都会放到ADC_DR数据寄存器中,我们需要做的就是在每个单独的通道转换完成后,进行一次DMA数据转运,并且目的地址进行自增。在ADC启动下一轮转换后,DMA同样也启动下一轮转运,ADC和DMA同步工作。

二、代码部分

1. DMA数据转运

cpp 复制代码
#include "MyDMA.h"

uint16_t DMA_Buffer;

/* DMA实现的3个条件:1.传输计数器大于0;2.触发源有触发信号;3.DMA使能。 */
void MyDMA_Init(uint32_t Addr1, uint32_t Addr2, uint16_t Buffer)
{
    DMA_Buffer = Buffer;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                                      // DMA1时钟使能

    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = Addr1;                                       // 外设起始地址
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;                 // 数据宽度
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;                         // 指定外围地址寄存器递增
    DMA_InitStructure.DMA_MemoryBaseAddr = Addr2;                                           // 存储器起始地址                
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                         
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                           // 传输一次停止(指定操作模式)
    DMA_InitStructure.DMA_BufferSize = Buffer;                                              // 传输内容具体大小
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                      // 传输方向:外设站点到存储器站点
    DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;                                             // M2M: 0-硬件触发, 1-软件触发
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
    DMA_Cmd(DMA1_Channel1, DISABLE);                                                        // DMA1使能

}

/* DMA传输封装 */
void MyDMA_Transfer(void)
{
    DMA_Cmd(DMA1_Channel1, DISABLE);
    DMA_SetCurrDataCounter(DMA1_Channel1, DMA_Buffer);                                       // 设置传输中的数据单元数
    DMA_Cmd(DMA1_Channel1, ENABLE);                                                          // DMA必须先断开,然后设置传输数据,最后开启
    
    while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);                                       // 等待DMA1信道1传输标志
}

2. DMA+ADC多通道

cpp 复制代码
#include "Bsp_ADC.h"

uint16_t ADC_Value[4] = {0, 0, 0, 0};

void Bsp_ADC_Init(void)
{

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);                // 1.时钟配置
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                                   // 2.ADC分频,ADC最大14M,接近来的时候是72M,所以要分频到14M以下

    GPIO_InitTypeDef GPIO_InitStructure;                                // 3.GPIO配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);  // 4.配置规则组
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);  // 相当于填充菜单列表方法(开启扫描模式)
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); 

    /* 加入DMA和ADC融合 */
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;                         // 选择ADC1中的DR数据寄存器
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Value;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                         // 传输一次停止
    DMA_InitStructure.DMA_BufferSize = 4;                                                   // 传输内容具体大小
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                      // 传输方向:外设站点到存储器站点
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                            // M2M: 0-硬件触发, 1-软件触发
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    ADC_InitTypeDef ADC_InitStructure;                                                      // 5.配置ADC
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                                  // 数据对齐
    ADC_InitStructure.ADC_NbrOfChannel = 4;                                                 // 只看前x个位置       
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                            // 扫描模式是否开启
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;                     // 外部触发转换选择
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                      // 连续转换模式
    // ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                   
    ADC_Init(ADC1, &ADC_InitStructure);

    DMA_Cmd(DMA1_Channel1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
    ADC_Cmd(ADC1, ENABLE);                                                                  // 6.开启ADC

    ADC_ResetCalibration(ADC1);                                                             // 7.校准ADC
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) == SET);

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                                                 // ADC软件启动转换,因为开启了连续转换模式,所以只开启一次即可

    // 如果需要看门狗和中断,则需要额外配置(开启看门狗和中断配置)
}

///* 单次触发ADC然后DMA转运到SRAM中 */
//void ADC_GetValue(void)
//{
//    DMA_Cmd(DMA1_Channel1, DISABLE);
//    DMA_SetCurrDataCounter(DMA1_Channel1, 4);
//    DMA_Cmd(DMA1_Channel1, ENABLE);

//    // ADC_SoftwareStartConvCmd(ADC1, ENABLE);                                         // ADC软件启动转换

//    while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
//    DMA_ClearFlag(DMA1_FLAG_TC1);
//}
相关推荐
Miuney_MAX1 天前
【单片机】之HC32F460中断向量选择
单片机·嵌入式硬件
集大周杰伦1 天前
RV1126开发板烧录与SSH登录实践
linux·ssh·嵌入式·rv1126·瑞芯微开发工具·ssh 远程登录
猫猫的小茶馆1 天前
【ARM】ARM的介绍
c语言·开发语言·arm开发·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆1 天前
【PCB工艺】数模电及射频电路基础
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·pcb工艺
点灯小铭1 天前
基于单片机的智能药物盒设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
梓德原1 天前
【基础】详细分析带隙型稳压电路的工作原理
单片机·嵌入式硬件·物联网
国科安芯1 天前
航天医疗领域AS32S601芯片的性能分析与适配性探讨
大数据·网络·人工智能·单片机·嵌入式硬件·fpga开发·性能优化
小李做物联网1 天前
【物联网毕业设计】60.1基于单片机物联网嵌入式项目程序开发之图像厨房监测系统
stm32·单片机·嵌入式硬件·物联网
贝塔实验室1 天前
新手如何使用Altium Designer创建第一张原理图(三)
arm开发·单片机·嵌入式硬件·fpga开发·射频工程·基带工程·嵌入式实时数据库
@good_good_study1 天前
STM32 ADC多通道采样实验
stm32·单片机·嵌入式硬件