GD32_ADC多通道扫描+DMA高效数据传输实战解析

张开发
2026/4/9 4:46:11 15 分钟阅读

分享文章

GD32_ADC多通道扫描+DMA高效数据传输实战解析
1. GD32 ADC多通道扫描DMA传输的核心价值第一次接触GD32的ADC多通道扫描功能时我被它的设计哲学惊艳到了。想象一下你正在做一个智能家居环境监测项目需要同时采集室内温度、湿度、光照强度和空气质量数据。如果每个传感器都单独采集不仅代码臃肿还会浪费大量CPU资源。而ADC多通道扫描DMA的组合就像请了个专业的管家团队能自动帮你完成所有数据采集和搬运工作。GD32的ADC模块支持最多18个通道16个外部2个内部采用12位逐次逼近式架构。在实际项目中我常用它来采集外部传感器信号通过分压电路测量0-3.3V电压内部温度传感器用于监测芯片工作温度内部参考电压用于校准测量精度扫描模式的精髓在于它能像流水线工人一样按照预设顺序自动遍历所有启用通道。配合DMA传输采集到的数据会自动存入指定内存区域完全不需要CPU干预。实测在GD32F103系列上使用72MHz主频时四通道扫描采样率能稳定达到1MHz以上。2. 硬件设计的关键细节很多新手容易在硬件设计环节踩坑。去年我做工业传感器项目时就遇到过ADC读数漂移的问题后来发现是PCB布局不当导致的。这里分享几个血泪教训分压电路设计测量12V电压时推荐使用10kΩ2kΩ电阻组合精度1%计算公式Vout Vin × (R2/(R1R2))实际项目中最好加入0.1uF滤波电容我的经验值是放在分压电阻与ADC输入引脚之间PCB布局要点ADC走线要远离数字信号线特别是PWM输出模拟地(AGND)和数字地(DGND)单点连接在ADC电源引脚放置10uF0.1uF去耦电容组合内部通道使用技巧温度传感器需要至少5us采样时间对应ADC_SAMPLETIME_239POINT5VREFINT通道的典型值为1.2V但实际值需要在代码中校准启动内部通道前必须调用adc_tempsensor_vrefint_enable()3. 软件配置全流程解析下面以GD32F103CBT6为例展示四通道采集的完整配置流程。这个配置模板我在三个量产项目中验证过稳定性很有保障。3.1 ADC初始化void ADC_Config(void) { rcu_periph_clock_enable(RCU_ADC0); rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6); //12MHz ADC时钟 adc_mode_config(ADC_MODE_FREE); 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_tempsensor_vrefint_enable(); // 配置通道序列注意顺序就是扫描顺序 adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_8, ADC_SAMPLETIME_55POINT5); // 12V输入 adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5); // 3.3V输入 adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_16, ADC_SAMPLETIME_239POINT5); // 温度 adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_17, ADC_SAMPLETIME_239POINT5); // VREF // 触发配置 adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); // 校准流程 adc_enable(ADC0); delay_ms(1); adc_calibration_enable(ADC0); // 启用DMA adc_dma_mode_enable(ADC0); adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); }3.2 DMA配置技巧DMA是高效传输的关键配置不当会导致数据错位。我推荐使用二维数组存储采样数据方便后期处理#define CHANNEL_COUNT 4 #define SAMPLE_COUNT 32 uint16_t adc_values[SAMPLE_COUNT][CHANNEL_COUNT] {0}; void DMA_Config(void) { dma_parameter_struct dma_init_struct; rcu_periph_clock_enable(RCU_DMA0); dma_deinit(DMA0, DMA_CH0); dma_init_struct.periph_addr (uint32_t)ADC_RDATA(ADC0); dma_init_struct.memory_addr (uint32_t)adc_values; dma_init_struct.direction DMA_PERIPHERAL_TO_MEMORY; dma_init_struct.memory_width DMA_MEMORY_WIDTH_16BIT; dma_init_struct.periph_width DMA_PERIPHERAL_WIDTH_16BIT; dma_init_struct.priority DMA_PRIORITY_ULTRA_HIGH; dma_init_struct.number SAMPLE_COUNT * CHANNEL_COUNT; dma_init_struct.periph_inc DMA_PERIPH_INCREASE_DISABLE; dma_init_struct.memory_inc DMA_MEMORY_INCREASE_ENABLE; dma_init(DMA0, DMA_CH0, dma_init_struct); dma_circulation_enable(DMA0, DMA_CH0); dma_channel_enable(DMA0, DMA_CH0); }关键参数说明memory_inc必须启用否则所有数据会写入同一地址circulation_enable实现循环缓冲新数据自动覆盖旧数据优先级设为ULTRA_HIGH可避免数据丢失4. 数据处理与性能优化采集到原始数据只是第一步正确处理数据才能获得准确测量值。根据我的项目经验分享几个实用技巧4.1 电压值转换float Get_Voltage(uint16_t raw, float vref) { return (raw * vref) / 4095.0f; // 12bit分辨率 }4.2 温度计算优化内部温度传感器的计算公式需要特别注意float Get_Temperature(uint16_t temp_raw, uint16_t vrefint_raw) { float vref 1.2f * 4095.0f / vrefint_raw; float vsense temp_raw * vref / 4095.0f; return (1.45f - vsense) / 0.0041f 25.0f; }4.3 数字滤波方案对于噪声较大的环境推荐采用滑动平均滤波#define FILTER_SIZE 8 typedef struct { uint16_t buffer[FILTER_SIZE]; uint8_t index; uint32_t sum; } Filter_TypeDef; uint16_t Filter_AddValue(Filter_TypeDef *filter, uint16_t new_value) { filter-sum - filter-buffer[filter-index]; filter-sum new_value; filter-buffer[filter-index] new_value; filter-index (filter-index 1) % FILTER_SIZE; return (uint16_t)(filter-sum / FILTER_SIZE); }4.4 性能优化技巧时钟配置APB2时钟设为72MHz时ADC时钟建议配置为12MHz6分频过高的ADC时钟会导致精度下降采样时间选择常规信号55.5周期约4.6us高阻抗源239.5周期约20us温度传感器必须≥239.5周期DMA优化使用双缓冲技术可避免数据处理时的数据冲突定期检查DMA_CNDTR寄存器确认传输进度在最近的一个电机控制项目中通过优化上述参数我们将ADC采样到数据处理的延迟从原来的35us降低到了12us效果非常显著。

更多文章