【GD32】GD32F303CCT6的AD采样实战:单通道+DMA+软件触发配置详解

张开发
2026/4/20 5:44:08 15 分钟阅读

分享文章

【GD32】GD32F303CCT6的AD采样实战:单通道+DMA+软件触发配置详解
1. GD32F303CCT6的AD采样基础认知第一次接触GD32的AD采样功能时我和很多初学者一样有点懵。这款国产MCU的ADC模数转换器性能其实相当不错12位分辨率下最高采样率能达到2.4Msps。在实际项目中我经常用它来做传感器数据采集比如温度、光照强度这些模拟量检测。GD32F303CCT6内置3个ADC模块支持多达16个外部通道。这里有个小技巧虽然官方手册没明确标注通道对应引脚但实际测试发现和STM32F103的引脚映射完全一致。比如ADC0的通道7对应PA7引脚这个发现让我少走了不少弯路。不过要注意供电电压范围当VDDA3.3V时输入电压绝对不能超过3.3V否则可能损坏芯片。ADC的工作模式主要有单次、连续、扫描和间断模式。我们这次要实现的单通道DMA软件触发方案特别适合需要定时采集数据的场景。比如我之前做的智能花盆项目就是用这种方案每隔5秒采集一次土壤湿度传感器的数据。2. 硬件环境搭建先说说我的硬件配置经验。我用的是GD32F303CCT6最小系统板核心板自带8MHz晶振通过内部PLL倍频到120MHz主频。ADC参考电压我直接接了3.3V如果对精度要求高建议单独用REF3033这类基准电压芯片。接线时特别注意模拟输入引脚PA7要远离数字信号线我在第一个版本就吃过亏把PA7和PWM输出线并排走线结果ADC读数总是跳变。后来改成星型接地模拟部分单独走线噪声明显降低。如果条件允许最好在PA7对地加个100nF的滤波电容。时钟配置是关键ADC时钟不能超过14MHz。我的常用配置是rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4); // APB260MHz, ADC时钟15MHz虽然略超规格但实测稳定性没问题。保守点可以选DIV6得到10MHz时钟。3. GPIO和时钟初始化实战初始化代码我优化过好几个版本下面是最终稳定运行的配置void ADC_GPIOAndRCU_Config(void) { // GPIOA时钟使能一定要放在最前面 rcu_periph_clock_enable(RCU_GPIOA); // 模拟输入配置注意不用设置上下拉 gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_7); // 外设时钟使能顺序很重要 rcu_periph_clock_enable(RCU_ADC0); rcu_periph_clock_enable(RCU_DMA0); // ADC时钟分频 rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4); }这里我踩过两个坑一是忘记使能GPIOA时钟导致配置不生效二是ADC时钟使能顺序不对导致采样异常。建议严格按照先GPIO后外设的顺序初始化。4. DMA配置详解DMA配置是性能关键我的经验是void ADC_DMA_Config(void) { dma_parameter_struct dma_cfg; // 每次上电先复位DMA通道 dma_deinit(DMA0, DMA_CH0); // 核心参数配置 dma_cfg.periph_addr (uint32_t)(ADC_RDATA(ADC0)); dma_cfg.memory_addr (uint32_t)adc_value; dma_cfg.periph_inc DMA_PERIPH_INCREASE_DISABLE; dma_cfg.memory_inc DMA_MEMORY_INCREASE_ENABLE; dma_cfg.periph_width DMA_PERIPHERAL_WIDTH_16BIT; dma_cfg.memory_width DMA_MEMORY_WIDTH_16BIT; dma_cfg.direction DMA_PERIPHERAL_TO_MEMORY; dma_cfg.number 1; // 单次传输数据量 dma_cfg.priority DMA_PRIORITY_HIGH; dma_init(DMA0, DMA_CH0, dma_cfg); // 循环模式必须开启 dma_circulation_enable(DMA0, DMA_CH0); // 提前使能DMA通道 dma_channel_enable(DMA0, DMA_CH0); }特别注意memory_inc要设为ENABLE虽然我们只传一个数据但有些编译器优化会导致问题。priority优先级根据系统负载调整如果还有其他DMA传输建议ADC用最高优先级。5. ADC模块深度配置ADC配置最复杂我拆解成几个关键步骤void ADC_Config(void) { // 1. 模式设置 adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE); adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); adc_mode_config(ADC_MODE_FREE); // 2. 触发配置 adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); // 3. 数据格式 adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); // 4. 通道配置 adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1); adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_7, ADC_SAMPLETIME_55POINT5); // 5. DMA功能 adc_dma_mode_enable(ADC0); // 6. 使能ADC adc_enable(ADC0); delay_1ms(1); // 必须的稳定等待 // 7. 校准 adc_calibration_enable(ADC0); // 8. 启动转换 adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); }采样时间ADC_SAMPLETIME_55POINT5对应55.5个时钟周期适合信号源阻抗较大的情况。如果信号源驱动能力强可以缩短到1.5周期提升采样率。6. 常见问题排查指南零点漂移问题我遇到过0.2V的固定偏移后来发现是PCB布局问题。解决方法确保AGND和DGND单点连接在VDDA和GND之间加10uF100nF去耦电容软件校准采集已知0V输入时的读数作为偏移量数据跳动大检查输入信号是否稳定尝试增加采样周期在代码中增加软件滤波比如滑动平均#define FILTER_LEN 8 uint16_t filter_buf[FILTER_LEN]; uint16_t adc_filter(uint16_t new_val) { static uint8_t idx 0; filter_buf[idx] new_val; if(idx FILTER_LEN) idx 0; uint32_t sum 0; for(int i0; iFILTER_LEN; i) { sum filter_buf[i]; } return sum / FILTER_LEN; }DMA不触发检查DMA通道是否使能确认ADC的DMA模式已开启查看NVIC优先级配置避免被其他中断阻塞7. 性能优化技巧经过多个项目验证这些技巧能显著提升ADC性能时钟优化适当提高APB2时钟频率ADC时钟设置在12-14MHz之间使用PLL锁相环提供稳定时钟源PCB布局要点模拟信号走线尽量短避免平行走数字信号线铺铜时模拟区域单独划分软件技巧// 在每次采集前重置DMA dma_channel_disable(DMA0, DMA_CH0); dma_channel_enable(DMA0, DMA_CH0); // 校准周期建议每24小时执行一次 if(need_calibrate) { adc_disable(ADC0); adc_enable(ADC0); delay_1ms(1); adc_calibration_enable(ADC0); }采样时序优化对于周期性信号使用定时器触发采样在信号稳定阶段进行采样避免在MCU高负载时采样这套方案在我最近的水质监测项目中表现很稳定连续运行30天ADC读数漂移小于1LSB。关键是要理解每个配置参数背后的硬件原理根据实际应用场景灵活调整。比如采样时间的选择需要权衡速度和精度DMA缓冲区的设计要考虑数据吞吐量。

更多文章