GD32F407单片机开发入门(十九)DMA详解及ADC-DMA方式采集含源码

一.概要

基本概念:
DMA是Direct Memory Access的首字母缩写,是一种完全由硬件执行数据交换的工作方式。DMA控制器从CPU接管对总线的控制,不经过CPU直接在内存和外设之间进行批量数据交换。DMA控制器向内存发出地址和控制信号,修改地址,对传送的字的个数计数,并且以中断方式向CPU报告传送操作的结束。 DMA方式一般用于高速传送成组数据。

DMA传输的三大要素:
传输源:DMA控制器从传输源读出数据;
传输目标:DMA控制器将数据传输的目标;
触发信号:用于触发一次数据传输的动作,执行一个单位的传输源至传输目标的数据传输;可以用来控制传输的时机。

DMA的主要优点:
由于CPU根本不参加传送操作,因此就省去了CPU取指令、取数、送数等操作。在数据传送过程中,没有保存现场、恢复现场之类的工作。内存地址修改、传送字个数的计数等等,也不是由软件实现,而是用硬件线路直接实现的。所以DMA方式能满足高速I/O设备的要求,也有利于CPU效率的发挥。

二.GD32F407VET6单片机DMA外设特点

两个AHB主机接口传输数据,一个AHB从机接口配置DMA。
16个通道(每个DMA控制器有8个通道),每个通道连接8个特定的外设请求。
存储器和外设支持单一传输,4拍、8拍和16拍增量突发传输。
当外设和存储器传输数据时,支持存储器切换。
支持软件优先级(低、中、高、超高)和硬件优先级(通道号越低,优先级越高)。
存储器和外设的数据传输宽度可配置:字节,半字,字。
存储器和外设的数据传输支持固定寻址和增量式寻址。
支持循环传输模式。
支持三种传输方式:
– 存储器到外设;
– 外设到存储器;
– 存储器到存储器(仅DMA1支持);
DMA和外设均可配置为传输控制器: – DMA作为传输控制器:可配置数据传输长度,最大为65535。 – 外设作为传输控制器:数据传输的完成取决于外设的最后一个传输请求。
支持单数据传输和多数据传输模式:
– 多数据传输模式:在存储器数据宽度和外设数据宽度不同的时候,自动打包/解包数
据;
– 单数据传输模式:当且仅当FIFO空的时候从源地址读取数据,存进FIFO,然后把
FIFO 的数据写到目标地址。
每个通道有5种类型的事件标志和独立的中断,支持中断的使能和清除。

三.GD32单片机DMA内部结构图

在这里插入图片描述

DMA控制器由4部分组成:

AHB 从接口配置DMA
AHB主接口进行数据传输
仲裁器进行DMA请求的优先级管理
数据处理和计数

寄存器DMA_CHxCTL的PWIDTH和MWIDTH位域决定了外设和存储器的数据传输宽度。DMA控制器支持8位,16位和32位的数据宽度。

四.DMA各通道请求

DMA从外设读数据存储到存储器,大致流程如下图。
在这里插入图片描述
在这里插入图片描述

如下图所示,每个DMA控制器有8个通道,每个通道有多个外设请求。以ADC0为例,可以映射到DMA的Channel0。
在这里插入图片描述

DMA的传输模式:

循环模式:通过寄存器DMA_CHxCTL的CMEN位置1使能,用于处理一个环形的缓冲区,每轮传输结束时数据传输的配置会自动地更新为初始状态,DMA传输会连续不断地进行,一般采用循环模式。

普通模式:在DMA传输结束时,DMA通道被自动关闭,进一步的DMA请求将不被满足。

五.GD32F407VET6单片机ADC-DMA采集例程

STLINK接GD32F407VET6开发板,STLINK接电脑USB口。

在这里插入图片描述

GD32F407VET6开发板的PA4,PA45引脚上的进行 ADC 电压采集,杜邦线连接 PA4,PA5 引脚与 VDD(3.3V)或者GND(0V),就能读到单片机供电的电压值。

主要代码:

#include "gd32f4xx.h"
#include "gd32f4xx_libopt.h"
#include "systick.h"

uint16_t adc_value[2];//ADC采样值


//ADC配置,DMA配置
void ADC_config(void)
{
		dma_single_data_parameter_struct  dma_single_data_parameter;
		rcu_periph_clock_enable(RCU_GPIOA);
		gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4);//PA4配置成输入
		gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_5);//PA5配置成输入
		//ADC相关时钟配置
		rcu_periph_clock_enable(RCU_GPIOA);
		rcu_periph_clock_enable(RCU_DMA1);
		rcu_periph_clock_enable(RCU_ADC0);
		adc_clock_config(ADC_ADCCK_PCLK2_DIV6);

    /* DMA相关配置 DMA1 CH0*/
	dma_deinit(DMA1, DMA_CH0);
	dma_single_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
	dma_single_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	dma_single_data_parameter.memory0_addr = (uint32_t)(&adc_value);
	dma_single_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;                         // 存储器地址是否为增量模式
	dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
	dma_single_data_parameter.circular_mode = DMA_CIRCULAR_MODE_ENABLE;                        // 循环模式
	dma_single_data_parameter.direction = DMA_PERIPH_TO_MEMORY;                                // 外设到存储器
	dma_single_data_parameter.number = 2;                                                      // DMA缓存大小
	dma_single_data_parameter.priority = DMA_PRIORITY_HIGH;
	dma_single_data_mode_init(DMA1,DMA_CH0,&dma_single_data_parameter);
	dma_channel_enable(DMA1, DMA_CH0);
	dma_circulation_enable(DMA1, DMA_CH0);        
	
	//ADC相关配置
	adc_deinit();      // 复位ADC
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);                         //独立模式
	
	adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);               // 扫描模式
	adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);         // 连续模式
	adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);                  // 右对齐
	adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 2);              //读取2通道
	
	/* ADC regular channel config */
	adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_144);//ADC0的通道4
	adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_5, ADC_SAMPLETIME_144);//ADC0的通道5
	adc_external_trigger_config(ADC0,ADC_REGULAR_CHANNEL,EXTERNAL_TRIGGER_DISABLE);
	adc_dma_request_after_last_enable(ADC0);
	adc_dma_mode_enable(ADC0);
	adc_enable(ADC0);
	adc_calibration_enable(ADC0);
	adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
}


uint16_t Vol_Value1,Vol_Value2;//采集到的电压值
int main(void)
{

  systick_config();//配置系统主频168M,外部8M晶振,配置在#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL        (uint32_t)(168000000)
	ADC_config();	//配置ADC
	while(1)
	{
		 
	Vol_Value1=adc_value[0]*3300/4095;//转换成电压值
	Vol_Value2=adc_value[1]*3300/4095;//转换成电压值

		
	}
}

实验结果:
PA4接GND,PA5接3.3V,用Keil运行程序,全速运行,查看Keil调试Watch界面,如下图,PA4,PA5引脚的采样值adc_value[0]
为0,adc_value[1]为0xfff,对应的电压值为0mV与3300mV,所以ADC DMA采样成功。
在这里插入图片描述

六.工程源代码下载

源码下载链接如下:
CSDN

七.小结

使用DMA进行数据收发能够提高数据传输的效率和可靠性。其次,使用DMA进行串口数据收发可以减轻CPU的负担。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值