文章目录
- 一.概要
- 二.GD32F103C8T6单片机DMA外设特点
- 三.GD32单片机DMA内部结构图
- 四.DMA各通道请求
- 五.GD32F103C8T6单片机ADC-DMA采集例程
- 六.工程源代码下载
- 七.小结
一.概要
基本概念:
DMA是Direct Memory Access的首字母缩写,是一种完全由硬件执行数据交换的工作方式。DMA控制器从CPU接管对总线的控制,不经过CPU直接在内存和外设之间进行批量数据交换。DMA控制器向内存发出地址和控制信号,修改地址,对传送的字的个数计数,并且以中断方式向CPU报告传送操作的结束。 DMA方式一般用于高速传送成组数据。
DMA传输的三大要素:
传输源:DMA控制器从传输源读出数据;
传输目标:DMA控制器将数据传输的目标;
触发信号:用于触发一次数据传输的动作,执行一个单位的传输源至传输目标的数据传输;可以用来控制传输的时机。
DMA的主要优点:
由于CPU根本不参加传送操作,因此就省去了CPU取指令、取数、送数等操作。在数据传送过程中,没有保存现场、恢复现场之类的工作。内存地址修改、传送字个数的计数等等,也不是由软件实现,而是用硬件线路直接实现的。所以DMA方式能满足高速I/O设备的要求,也有利于CPU效率的发挥。
二.GD32F103C8T6单片机DMA外设特点
传输数据长度可编程配置,最大到65536;
7个通道,并且每个通道都可配置(DMA0有7个通道);
AHB和APB外设,片上闪存和SRAM都可以作为访问的源端和目的端;
每个通道连接固定的硬件DMA请求;
支持软件优先级(低、中、高、极高)和硬件优先级(通道号越低,优先级越高);
存储器和外设的数据传输宽度可配置:字节,半字,字;
存储器和外设的数据传输支持固定寻址和增量式寻址;
支持循环传输模式;
支持外设到存储器,存储器到外设,存储器到存储器的数据传输;
每个通道有3种类型的事件标志和独立的中断;
支持中断的使能和清除。
三.GD32单片机DMA内部结构图
DMA控制器由4部分组成:
AHB 从接口配置DMA
AHB主接口进行数据传输
仲裁器进行DMA请求的优先级管理
数据处理和计数
四.DMA各通道请求
多个外设请求被映射到同一个DMA通道。这些请求信号在经过逻辑或后进入DMA。通过配置对应外设的寄存器,每个外设的请求均可以独立的开启或关闭。用户必须确保同一时间,在同一个通道上仅有一个外设的请求被开启。
以ADC0为例,可以映射到DMA的Channel0
DMA的传输模式:
循环模式:用于处理一个环形的缓冲区,每轮传输结束时数据传输的配置会自动地更新为初始状态,DMA传输会连续不断地进行。 一般采用循环模式。
普通模式:在DMA传输结束时,DMA通道被自动关闭,进一步的DMA请求将不被满足。
五.GD32F103C8T6单片机ADC-DMA采集例程
STLINK接GD32F103C8T6开发板,STLINK接电脑USB口。
GD32F103C8T6开发板的PA4 引脚上的进行 ADC 电压采集,杜邦线连接 PA4 引脚与 VDD(3.3V),应该能读到单片机供电的电压值。
主要代码:
uint16_t adc_value;//ADC采样值
uint16_t Vol_Value;//电压值
/*!
\brief configure the DMA peripheral
\param[in] none
\param[out] none
\retval none
*/
void dma_config(void)
{
/* ADC_DMA_channel configuration */
dma_parameter_struct dma_data_parameter;
/* ADC DMA_channel configuration */
dma_deinit(DMA0, DMA_CH0);//ADC0需要映射到通道0
/* initialize DMA single data mode */
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_data_parameter.memory_addr = (uint32_t)(&adc_value);//采样值存储到变量
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_DISABLE;
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_data_parameter.number = 1;
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
dma_circulation_enable(DMA0, DMA_CH0);//循环模式使能
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);//使能DMA0通道0
}
void adc_config(void)
{
/* reset ADC */
adc_deinit(ADC0);
/* ADC mode config */
adc_mode_config(ADC_MODE_FREE);
/* ADC contineous function enable */
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
/* ADC scan mode disable */
adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE);
/* ADC data alignment config */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
/* ADC channel length config */
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);
/* ADC regular channel config */
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
/* ADC trigger config */
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
/* enable ADC interface */
adc_enable(ADC0);
delay_1ms(1);
/* ADC calibration and reset calibration */
adc_calibration_enable(ADC0);
/* ADC DMA function enable */
adc_dma_mode_enable(ADC0);//DMA使能
/* ADC software trigger enable */
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
}
int main(void)
{
rcu_apb2_clock_config(RCU_APB2_CKAHB_DIV1);//设置主频108M(#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000)),8M外部晶振 (#define HXTAL_VALUE ((uint32_t)8000000))
systick_config();//配置1ms SysTick
rcu_periph_clock_enable(RCU_AF);//AF时钟使能
delay_1ms(1000);
//配置PA4 ADC引脚
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
/* enable ADC1 clock */
rcu_periph_clock_enable(RCU_ADC0);
/* enable DMA0 clock */
rcu_periph_clock_enable(RCU_DMA0);
/* config ADC clock */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
//配置ADC
dma_config();//DMA配置
adc_config();//ADC配置
while(1)
{
Vol_Value=adc_value*3300/4095;//读取ADC值,并转换成电压值
delay_1ms(500);//等待500ms
}
}
实验结果:
用Keil运行程序,全速运行,查看Keil调试Watch界面,如下图,PA4引脚的采样值adc_value
为0xfff,电压值为3300mV。
六.工程源代码下载
通过网盘分享的文件:18.ADC_DMA实验.zip
链接: https://pan.baidu.com/s/1gXpLC8ddlfVSGXG4pkT8dA 提取码: ny7i
如果链接失效,可以联系博主给最新链接
程序下载下来之后解压就行
CSDN代码
七.小结
使用DMA进行数据收发能够提高数据传输的效率和可靠性。其次,使用DMA进行串口数据收发可以减轻CPU的负担。