STM32_8(DMA)

DMA

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

 

存储器映像

 

DMA框图

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

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

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

 

DMA基本结构

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

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

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

 

DMA请求

 

举例

数据转运+DMA

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

 

ADC扫描模式+DMA

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

 

代码部分

DMA数据转运

#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传输标志
}

 

DMA+ADC多通道

#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);
//}

本文作者:每天学习之路

本文链接:https://www.cnblogs.com/toutiegongzhu/p/17390768.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   烟儿公主  阅读(114)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 夏日大冒险 暴躁的兔子
夏日大冒险 - 暴躁的兔子
00:00 / 00:00
An audio error has occurred.

作词 : 暴躁的兔子

作曲 : 暴躁的兔子

编曲 : IOF

混音:Gfanfan

出品:网易飓风

夏天 不要再浪费时间

实现 你承诺过的改变

别再 找一堆借口拖延

现在就和我一起飞向海边

人生苦短 你应该学会如何作乐

低着头还怎么应对挫折

人应该为自己活着

不用去迎合

要去寻欢作乐

撮合我的浪漫和悲欢

把这荒诞人生都塞满

生活难免磕磕绊绊

对抗生活的平庸就是浪漫

学会取悦自己逆风翻盘

去反抗变态的三观

把条条框框都砸烂

建立新的规则推翻谈判

无可救药的人呐

和我一起去海边

看那日出和晚霞 海天一线

看阳光穿越地平线

现实交织的明天

就在这个夏天

为自己改变

别怕山高路远

去冒险

我真的不care你是否会喜欢我

不跟风被定义的美 全都是灾祸

我才不讨好大多数绝不与示弱

过好你的生活

你管我应该怎么快活

没有人能有资格审判

别人的生活和牵绊

快闭上你的高谈阔论

乘风破浪吧 理想的风帆

我就是肆意张扬又如何

我就是锋芒毕露又如何

我就是离经叛道又如何

我就是要出格 你管我要如何

我就是与众不同又如何

我就是特立独行又如何

我就是不知好歹又如何

你管我怎样出格 你管我如何

无可救药的人呐

和我一起去海边

看那日出和晚霞 海天一线

看阳光穿越地平线

现实交织的明天

就在这个夏天

为自己改变

别怕山高路远

不知进退的人呐

和我一起去海边

聊聊曾经的理想 一起想当年

那曾想改变世界的人

是否还满腔热忱

不羁的我们放肆着

反抗那命运的指针

解放灵魂

推广:网易飓风

企划:贾焱祺

监制:徐思灵

出品人:谢奇笛