GD32F330 | ADC实例 基于DMA方式
GD32F330 | ADC实例 基于DMA方式
简单记录一下 ADC多通道转换 DMA搬运 的使用,以 GD32F330G8U6 为例:
一、基础知识
12位 ADC 是一种采用逐次逼近方式的模拟数字转换器。
转换模式:
– 转换单个通道,或者扫描一序列的通道;
– 单次模式,每次触发转换一次选择的输入通道;
– 连续模式,连续转换所选择的输入通道;
– 间断模式;
– 同步模式(适用于具有两个或多个ADC的设备)。
DMA(Direct Memory Access)— 直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。
二、软件配置过程
常规通道转换 DMA 搬运:通过ADC转换,在ADC 转换完成后 触发DMA 进行数据搬运。
1、开启工作时钟
// 1. 开启时钟 rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_ADC); rcu_periph_clock_enable(RCU_DMA); rcu_adc_clock_config(RCU_ADCCK_APB2_DIV6);
2、GPIO 初始化
GPIO 用于 AD 转换功能必须配置为模拟输入模式
// 2. GPIO配置 gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3 | GPIO_PIN_4);
注:ADC 的通道与引脚的对应关系可查阅具体芯片的数据手册 (Pin definitions 章节):
3、ADC 初始化
配置 ADC 模式、对齐方式、转换长度、采样周期以及触发方式等等
// 3. ADC配置 adc_deinit(); adc_special_function_config(ADC_SCAN_MODE, ENABLE); // 扫描模式 adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE); // 连续转换 adc_resolution_config(ADC_RESOLUTION_12B); adc_data_alignment_config(ADC_DATAALIGN_RIGHT); adc_channel_length_config(ADC_REGULAR_CHANNEL, 2); adc_regular_channel_config(0, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(1, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5); adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE); // 软件触发 adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE); // 触发使能 adc_dma_mode_enable(); // 使能DMA接收 adc_enable(); // 使能ADC模块 delay_1ms(0x03); // ADC在使能之后需要进行短暂的延时,时间周期约为2毫秒左右,以保证ADC能正常启动。 adc_calibration_enable();
4、DMA 初始化
使用 DMA,最核心就是配置要传输的数据,包括数据从哪里来,要到哪里去,传输的数据的单位是什么,要传多少数据,是一次传输还是循环传输等等。
- 从哪里来到哪里去 -- DMA 传输数据的方向有三个:(1)从外设到存储器 (2)从存储器到外设 (3)从存储器到存储器
- 要传多少,单位是什么 -- 要想数据传输正确,源和目标地址存储的数据宽度一般会设置成一致
- 什么时候传输完成 -- 传输完成分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。
// 4. DMA配置 dma_parameter_struct dma_data_parameter; dma_deinit(DMA_CH0); dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA); dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; dma_data_parameter.memory_addr = DstAddress; dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT; dma_data_parameter.number = DataLength; dma_data_parameter.priority = DMA_PRIORITY_ULTRA_HIGH; dma_init(DMA_CH0, &dma_data_parameter); dma_circulation_enable(DMA_CH0); dma_memory_to_memory_disable(DMA_CH0); dma_channel_enable(DMA_CH0);
注: DstAddress -- 存储器地址,一般设置为我们自定义存储区的首地址。
5、触发 ADC 转换
开启 ADC 转换,在ADC 转换完成后 ADC 转换结果 就会保存在 DMA配置的 DstAddress 地址里,在我们需要 最新的 ADC 转换结果 时直接读取即可。
adc_software_trigger_enable(ADC_REGULAR_CHANNEL); // 开启ADC转换
三、ADC 使用注意事项
- ADC通道的采集引脚未配置为模拟输入,GD32要求通道IO口必须配置为模拟输入;
- ADC时钟过高,ADC采样时钟高于ADC最高采样时钟获取到的数据不具有参考意义,需要手动分频到合适的ADC 要求的频率;
- ADC采样值偏小或不稳定,应该适当的降低ADC时钟,加大采样周期的值;
- ADC的查询法在获取ADC数值的时候,不允许重入,即不允许在同一时间调用ADC的数据读取函数
以上总结仅供参考,若有不对之处,敬请谅解。
未经本人同意,不得转载,谢谢合作!