12_DMA直接存储器存取

DMA直接存储器存取

DMA简介

image-20240215134541761

存储器映像

image-20240215142759007

DMA框图

image-20240215180519508

DMA基本结构

image-20240215182943598

DMA请求

image-20240215184040361

数据宽度与对齐

image-20240216002914975

数据转运+DMA

image-20240216003144849

ADC扫描模式+DMA

image-20240216121432744

DMA数据转运

接线图

image-20240216132139304

代码

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多通道

接线图

image-20240218142353090

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);
	}
}
posted @ 2024-02-26 00:33  爱吃冰激凌的黄某某  阅读(7)  评论(0编辑  收藏  举报