最近在调试GD32F303的ADC功能时,遇到了一个令人费解的现象:当输入电压在0.415V至0.455V这个狭窄区间时,ADC采样值会"卡住"不变,而其他电压区间却能正常工作。经过一番排查,发现问题竟出在ADC时钟分频配置上。本文将详细记录整个排查过程,分享如何通过调整时钟分频解决这个棘手问题。
项目中使用GD32F303CCT6的ADC模块采集多路模拟信号,大部分情况下工作正常。但在进行精细电压调节测试时,发现当输入电压进入0.415V-0.455V这个特定区间时,ADC采样值会停止变化,仿佛被"锁定"在一个固定值上。
典型问题表现:
首先怀疑是硬件问题,进行了以下排查:
硬件排查无果后,转向软件配置检查。ADC初始化代码如下:
c复制void HAL::adc_init() {
/* ADC channel length config */
adc_channel_length_config(ADC0, ADC_INSERTED_CHANNEL, 4);
/* ADC regular channel config */
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_17, ADC_SAMPLETIME_71POINT5); //VREFINT
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_3, ADC_SAMPLETIME_71POINT5); //ISENS
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_2, ADC_SAMPLETIME_71POINT5); //VISENS
adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_1, ADC_SAMPLETIME_71POINT5); //VOSENS
/* ADC external trigger enable */
adc_external_trigger_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE);
/* ADC external trigger source config */
adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL, ADC0_1_2_EXTTRIG_INSERTED_NONE);
/* ADC data alignment config */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
/* ADC SCAN function enable */
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
/* ADC Vbat and temperature channel enable */
adc_tempsensor_vrefint_enable();
/* ADC resolusion 12B */
adc_resolution_config(ADC0, ADC_RESOLUTION_12B);
/* enable ADC interface */
adc_enable(ADC0);
delay_ms(1);
/* ADC calibration and reset calibration */
adc_calibration_enable(ADC0);
}
查阅GD32官方手册,ADC时钟最高支持30MHz。在我们的系统中,APB2时钟为120MHz,初始配置使用4分频:
c复制rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4); // ADC时钟=120MHz/4=30MHz
理论上这符合手册规格,但实际表现却不稳定。参考网上类似案例,尝试调整时钟分频:
首先尝试将分频改为6(ADC时钟=20MHz):
c复制rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);
问题有所改善,"卡住"的电压区间变窄,但未完全解决
进一步降低时钟至8分频(ADC时钟=15MHz):
c复制rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
问题完全消失,所有电压区间采样正常
注意:时钟分频调整需要在ADC校准前完成,建议放在ADC初始化代码的开头部分。
降低ADC时钟分频虽然解决了采样异常问题,但也会带来一些性能影响:
| 分频系数 | ADC时钟频率 | 采样率 | 稳定性 | 功耗 |
|---|---|---|---|---|
| DIV4 | 30MHz | 最高 | 较差 | 较高 |
| DIV6 | 20MHz | 中 | 一般 | 中 |
| DIV8 | 15MHz | 较低 | 最佳 | 较低 |
实际应用建议:
除了时钟分频外,以下因素也可能导致ADC采样异常:
采样时间配置:
参考电压稳定性:
PCB布局与接地:
软件滤波处理:
c复制#define SAMPLE_COUNT 5
uint16_t adc_filter(uint32_t channel) {
uint16_t samples[SAMPLE_COUNT];
for(int i=0; i<SAMPLE_COUNT; i++) {
samples[i] = adc_read(channel);
}
// 简单排序实现中值滤波
for(int i=0; i<SAMPLE_COUNT-1; i++) {
for(int j=i+1; j<SAMPLE_COUNT; j++) {
if(samples[j] < samples[i]) {
uint16_t temp = samples[i];
samples[i] = samples[j];
samples[j] = temp;
}
}
}
return samples[SAMPLE_COUNT/2];
}
在调试ADC问题时,以下工具和技巧非常有用:
信号发生器:
逻辑分析仪:
变量实时监控:
c复制void debug_adc_values() {
while(1) {
uint16_t val1 = adc_read(ADC_CHANNEL_1);
uint16_t val2 = adc_read(ADC_CHANNEL_2);
printf("CH1: %4d, CH2: %4d\r\n", val1, val2);
delay_ms(100);
}
}
温度监测:
经过这次调试经历,最大的收获是:即使官方手册标明了参数上限,实际应用中也需要留有一定余量。特别是在模拟电路设计中,理论值往往需要在实际环境中验证调整。