12_DMA直接存储器存取
DMA直接存储器存取
DMA简介
存储器映像
DMA框图
DMA基本结构
DMA请求
数据宽度与对齐
数据转运+DMA
ADC扫描模式+DMA
DMA数据转运
接线图
代码
MyDMA.c
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
MyDMA_Size=Size;
//使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//初始化DMA
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=AddrA; //外设站点基地址
DMA_InitStruct.DMA_PeripheralDataSize=DMA_MemoryDataSize_Byte; //以字节方式传输
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable; //地址自增
DMA_InitStruct.DMA_MemoryBaseAddr=AddrB; //存储器站点基地址
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; //以字节方式传输
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable; //地址自增
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC; //外设站点作为数据源
DMA_InitStruct.DMA_BufferSize=Size; //传输计数器
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal; //单次重装模式
DMA_InitStruct.DMA_M2M=DMA_M2M_Enable; //软件触发
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; //通道优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
}
void MyDMA_Transfer(void)
{
//DMA失能
DMA_Cmd(DMA1_Channel1,DISABLE);
//设置传输计数器
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
//DMA使能
DMA_Cmd(DMA1_Channel1,ENABLE);
//等待DMA转运完成
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
//清除标志位
DMA_ClearFlag(DMA1_FLAG_TC1);
}
MyDMA.h
#ifndef __MYDMA_H__
#define __MYDMA_H__
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
void MyDMA_Transfer(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
int main(void)
{
OLED_Init();
MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
OLED_ShowString(1,1,"DataA:");
OLED_ShowString(3,1,"DataB:");
OLED_ShowHexNum(1,7,(uint32_t)DataA,8);
OLED_ShowHexNum(3,7,(uint32_t)DataB,8);
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
while(1)
{
for(int i=0;i<4;i++)
{
DataA[i]++;
}
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
MyDMA_Transfer();
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
}
}
DMA+AD多通道
接线图
ADC单次扫描+DMA单次转运模式
AD.c
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
//使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADCCLK=72MHz/6=12MHz
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN; //模拟输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
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);
//初始化ADC
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_ContinuousConvMode=DISABLE; //单次转换
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐:右对齐
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //触发源:软件触发
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent; //独立模式
ADC_InitStruct.ADC_NbrOfChannel=4; //通道数量
ADC_InitStruct.ADC_ScanConvMode=ENABLE; //扫描模式
ADC_Init(ADC1,&ADC_InitStruct);
//初始化DMA
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设站点基地址
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //以半字16位传输
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //地址不自增
DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value; //存储器站点基地址
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //以半字16位传输
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable; //地址自增
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC; //外设站点作为数据源
DMA_InitStruct.DMA_BufferSize=4; //传输计数器
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal; //单次重装模式
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable; //硬件触发
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; //通道优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
//DMA使能
DMA_Cmd(DMA1_Channel1,ENABLE);
//开启DMA触发信号
ADC_DMACmd(ADC1,ENABLE);
//使能ADC
ADC_Cmd(ADC1,ENABLE);
//复位校准
ADC_ResetCalibration(ADC1);
//等待复位校准完毕
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
//开始校准
ADC_StartCalibration(ADC1);
//等待校准完成
while(ADC_GetCalibrationStatus(ADC1)==SET);
}
void AD_GetValue(void)
{
//DMA失能
DMA_Cmd(DMA1_Channel1,DISABLE);
//设置传输计数器
DMA_SetCurrDataCounter(DMA1_Channel1,4);
//DMA使能
DMA_Cmd(DMA1_Channel1,ENABLE);
//软件触发转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
//等待DMA转换完成
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
//清除DMA标志位
DMA_ClearFlag(DMA1_FLAG_TC1);
}
AD.h
#ifndef __AD_H__
#define __AD_H__
extern uint16_t AD_Value[4];
void AD_Init(void);
void AD_GetValue(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD0,AD1,AD2,AD3;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
OLED_ShowString(3,1,"AD2:");
OLED_ShowString(4,1,"AD3:");
while(1)
{
AD_GetValue();
OLED_ShowNum(1,5,AD_Value[0],4);
OLED_ShowNum(2,5,AD_Value[1],4);
OLED_ShowNum(3,5,AD_Value[2],4);
OLED_ShowNum(4,5,AD_Value[3],4);
Delay_ms(100);
}
}
ADC连续扫描+DMA循环转运模式
AD.c
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
//使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//配置ADCCLK
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADCCLK=72MHz/6=12MHz
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN; //模拟输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
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);
//初始化ADC
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //连续转换
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐:右对齐
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //触发源:软件触发
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent; //独立模式
ADC_InitStruct.ADC_NbrOfChannel=4; //通道数量
ADC_InitStruct.ADC_ScanConvMode=ENABLE; //扫描模式
ADC_Init(ADC1,&ADC_InitStruct);
//初始化DMA
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设站点基地址
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //以半字16位传输
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //地址不自增
DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value; //存储器站点基地址
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //以半字16位传输
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable; //地址自增
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC; //外设站点作为数据源
DMA_InitStruct.DMA_BufferSize=4; //传输计数器
DMA_InitStruct.DMA_Mode=DMA_Mode_Circular; //循环重装模式
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable; //硬件触发
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium; //通道优先级
DMA_Init(DMA1_Channel1,&DMA_InitStruct);
//DMA使能
DMA_Cmd(DMA1_Channel1,ENABLE);
//开启DMA触发信号
ADC_DMACmd(ADC1,ENABLE);
//使能ADC
ADC_Cmd(ADC1,ENABLE);
//复位校准
ADC_ResetCalibration(ADC1);
//等待复位校准完毕
while(ADC_GetResetCalibrationStatus(ADC1)==SET);
//开始校准
ADC_StartCalibration(ADC1);
//等待校准完成
while(ADC_GetCalibrationStatus(ADC1)==SET);
//软件触发转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
AD.h
#ifndef __AD_H__
#define __AD_H__
extern uint16_t AD_Value[4];
void AD_Init(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD0,AD1,AD2,AD3;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
OLED_ShowString(3,1,"AD2:");
OLED_ShowString(4,1,"AD3:");
while(1)
{
OLED_ShowNum(1,5,AD_Value[0],4);
OLED_ShowNum(2,5,AD_Value[1],4);
OLED_ShowNum(3,5,AD_Value[2],4);
OLED_ShowNum(4,5,AD_Value[3],4);
Delay_ms(100);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!