STM32学习笔记(十七)- DMA直接存储器存取 库函数配置DMA数据转运&DMA+AD多通道

目录

一,DMA简介

二,DMA框图

三, DMA处理

四, DMA请求

五,存储器影像

六,DMA数据转运 - keil工程

1.相关函数

2.代码部分

3.效果图

七,DMA+AD多通道

1.代码部分


一,DMA简介

1.DMA(Direct Memory Access)直接存储器存取

2.DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

3.两个DMA控制器有12个通道(DMA17个通道,DMA25个通道),每个通道专门用来管理来自 于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

4.每个通道都支持软件触发和特定的硬件触发

5.DMA主要特性

二,DMA框图

1. DMA总线将DMA的AHB主控接口与总线矩阵相联,总线矩阵协调着CPU的DCode和DMA到
SRAM、闪存和外设的访问。
2.如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优
先权。

三, DMA处理

在发生一个事件后,外设向 DMA 控制器发送一个请求信号。 DMA 控制器根据通道的优先权处理
请求。当 DMA 控制器开始访问发出请求的外设时, DMA 控制器立即发送给它一个应答信号。当
DMA 控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求, DMA 控制
器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。
总之,每次
DMA 传送由 3 个操作组成:
● 从外设数据寄存器或者从当前外设 / 存储器地址寄存器指示的存储器地址取数据,第一次传
输时的开始地址是 DMA_CPARx DMA_CMARx 寄存器指定的外设基地址或存储器单元。
● 存数据到外设数据寄存器或者当前外设
/ 存储器地址寄存器指示的存储器地址,第一次传输
时的开始地址是 DMA_CPARx DMA_CMARx 寄存器指定的外设基地址或存储器单元。
● 执行一次
DMA_CNDTRx 寄存器的递减操作,该寄存器包含未完成的操作数目。

四, DMA请求

五,存储器影像

如果想要知道某个寄存器的地址,打开用户手册,在第二章的存储器影像,可以找到该外设的起始地址; 然后看一下该外设寄存器所在的寄存器总表,可以找到它的偏移地址;实际地址就是起始地址加偏移地址

六,DMA数据转运 - keil工程

1.相关函数

//回复缺省配置
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);

//初始化
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

//结构体初始化
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);

//使能DMA
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

//中断输出使能
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);

//设置当前数据寄存器(给传输计数器写数据)
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); 

//获取当前数据寄存器(返回传输计数器的值)
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

//获取标志位,清除标志位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
void DMA_ClearFlag(uint32_t DMAy_FLAG);

//获取中断状态,清除中断状态
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);

2.代码部分

1. 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;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;	//外设站点的起始地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//数据宽度
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//是否自增	
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;		//存储器的起始地址
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;		//数据宽度
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;				//是否自增
	DMA_InitStructure.DMA_BufferSize = Size;			//缓存区大小(传输计数器)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;					//传输方向
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;					//选择硬件触发还是软件触发
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;					//传输模式(是否使用自动重装)
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;				//优先级
	
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1, DISABLE);
}

void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

2. 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
3. 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, 8, (uint32_t)DataA, 8);
	OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
	
	while(1)
	{
		DataA[0]++;
		DataA[1]++;
		DataA[2]++;
		DataA[3]++;
		
		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);
	}
}

3.效果图

DMA数据转运

七,DMA+AD多通道

使用ADC连续扫描模式+DMA循环转运

1.代码部分

1. AD.c

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

//对AD初始化
void AD_Init(void)
{
	//开启时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//ADC分频
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);	//这样ADCCLK=72MHz/6
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	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); 
	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_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_NbrOfChannel = 4;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;		//扫描模式
	ADC_Init(ADC1,&ADC_InitStructure);
	
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;	//外设站点的起始地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//数据宽度
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			//是否自增	
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;		//存储器的起始地址
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;		//数据宽度
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;				//是否自增
	DMA_InitStructure.DMA_BufferSize = 4;			//缓存区大小(传输计数器)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;					//传输方向
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;					//选择硬件触发还是软件触发
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;					//传输模式(是否使用自动重装)
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;				//优先级
	
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	ADC_DMACmd(ADC1, ENABLE);
	
	//开启ADC电源
	ADC_Cmd(ADC1, ENABLE);
	
	//校准,当复位校准状态为SET的时候完成复位校准
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//当ADC触发之后,ADC 连续转换,DMA循环转运
	
}

2. AD.h

#ifndef _AD_H
#define _AD_H

#include "stdint.h"
extern uint16_t AD_Value[4];
void AD_Init(void);

#endif

3. 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);
		
	}
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值