GD32 ADC采样问题

一、问题描述

最近用GD32F303CCT6的ADC采集电压,测试过程中发现ADC存在诡异的问题。调节测量电压,绝大部分情况下均能正确采样电压值,证明软硬件没有问题。但在做精细调节测试时,发现当采样信号大致在0.415V~0.455V之间时,ADC采样值不变。一开始以为硬件问题,经过反复排查,确认引脚上的电压和设定值一致,并且一旦超过该范围,又能采样到正确的值。而且在其他某些电压区间也存在该问题。

原理图如下

配置程序如下

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设计上有缺陷,但并未具体说明问题是什么。最终参考这篇博客“GD32F450单片机ADC采集值不变问题 - - 21ic电子技术开发论坛”提到了同样的问题,博客中提到的解决方法如下图

但经过测试,将rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4)放在adc_calibration_enable(ADC0)后面,问题并未解决。最后怀疑是不是ADC时钟太快,将时钟降低,改位rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6),问题有所好转,出问题的区间电压范围似乎变小了,但并未完全解决。如此来说应该就是ADC的时钟问题,最后把时钟再降低,改为rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8),问题解决!

通过GD官方手册的描述,ADC时钟是可以达到30M的,我的板子APB2时钟为120M,rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4)将ADC时钟分频为30M,理论上是没问题的。通过这次掉坑的经历来看,GD32的ADC时钟还是不能太快。

三、总结

有些坑是自己挖的,还有一些坑是别人为你准备的,但不管什么坑,掉过一次之后都应该长教训。如果还能为别人把坑填平,也算功德一件,共勉!

### GD32 ADC 采样配置教程 #### 配置概述 为了在GD32微控制器上实现ADC采样,需要完成以下几个方面的配置:硬件资源的选择、软件库的初始化以及中断/DMA机制的应用。具体来说,通过选择合适的引脚并将其配置为ADC模拟输入模式,可以利用GD官方固件库中的函数来简化初始化流程。 以下是详细的配置步骤和示例代码: --- #### 硬件资源配置 根据引用内容[^2],如果仅需使用AO引脚作为ADC接口,则可以选择具有ADC功能的引脚(如`PC1`),并将该引脚分配给ADC0的第11通道。此外,还需要确认所选芯片型号的数据手册中关于引脚功能定义的部分[^3][^4]。 对于火焰传感器或其他类似的模数转换需求,通常会涉及以下操作: - 将AO引脚配置为ADC模拟输入模式。 - 如果存在DO引脚,则可将其配置为普通的GPIO输入模式用于数字信号判断。 --- #### 初始化代码示例 下面是基于GD32F303RCT6单片机及其RT-Thread系统的ADC初始化代码示例[^1]: ```c #include "rtthread.h" #include "gd32f30x_adc.h" // 定义全局变量 uint16_t adc_value; void adc_init(void) { // 启用时钟 rcu_periph_clock_enable(RCU_ADC0); rcu_periph_clock_enable(RCU_GPIOC); // 配置PC1为模拟输入模式 gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_NOPULL, GPIO_PIN_1); // ADC配置结构体初始化 struct adc_config config; adc_struct_para_init(&config); // 设置ADC参数 config.continuous_conversion = DISABLE; // 单次转换模式 config.external_trigger_convert_source = ADC_REGULAR_CHANNEL_EXTTRIG_NONE; // 不使用外部触发源 config.inserted_channel_priority = ENABLE; // 插入通道优先级使能 config.scan_mode = DISABLE; // 关闭扫描模式 // 应用配置 adc_init(ADC0, &config); // 配置规则组序列 adc_regular_sequence_set(ADC0, 1, 0, ADC_CHANNEL_11); // 使用ADC0的第11通道 // 配置采样时间 adc_sample_time_config(ADC0, ADC_REGULAR_CHANNEL, ADC_SAMPLETIME_55POINT5CYCLES); // 开启ADC校准 adc_calibration_enable(ADC0); // 开启ADC adc_enable(ADC0); // 延迟等待稳定 delay_ms(1); // 开始一次转换 adc_software_start_conv(ADC0); } int main(void) { rt_thread_delay(rt_tick_from_millisecond(100)); // 系统启动延迟 adc_init(); // 初始化ADC while (1) { if (adc_flag_get(ADC0, ADC_FLAG_EOC)) { // 判断是否完成转换 adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除标志位 adc_value = adc_regular_data_read(ADC0); // 获取转换后的数值 rt_kprintf("ADC Value: %d\n", adc_value); // 打印结果 } rt_thread_delay(rt_tick_from_millisecond(500)); } return RT_EOK; } ``` 上述代码实现了如下功能: 1. **启用时钟**:开启ADC外设及时钟树的相关部分。 2. **引脚配置**:将目标引脚设置为模拟输入模式。 3. **ADC初始化**:调用库函数完成基本参数设定,包括工作模式、触发方式等。 4. **规则组序列设置**:指定参与采样的具体通道号。 5. **采样时间和校准处理**:调整最佳采样周期,并执行必要的校正动作。 6. **循环读取数据**:持续监测EOC状态以获取最新的AD转换值。 --- #### DMA支持扩展 若希望进一步提升效率,还可以引入DMA技术自动传输数据至内存缓冲区。例如,在原有基础上增加以下片段即可实现连续采集: ```c dma_parameter_struct dma_init_struct; rcu_periph_clock_enable(RCU_DMA0); // 启动DMA时钟 dma_deinit(DMA0, DMA_CH2); // 复位DMA通道 dma_struct_para_init(&dma_init_struct); dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL; dma_init_struct.memory_addr = (uint32_t)&buffer[0]; dma_init_struct.memory_inc_length = DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.number = BUFFER_SIZE; dma_init_struct.peripheral_base_addr= (uint32_t)&ADC_RDATA(ADC0); dma_init_struct.per_memory_width = DMA_MEMORY_WIDTH_16BIT; dma_init_struct.priority = DMA_PRIORITY_HIGH; dma_init_struct.transfer_complete_ie = ENABLE; dma_init(DMA0, DMA_CH2, &dma_init_struct); dma_circulation_disable(DMA0,DMA_CH2); dma_channel_enable(DMA0,DMA_CH2); adc_dma_mode_enable(ADC0); ``` 此段程序展示了如何绑定DMA与ADC之间的协作关系,从而减少CPU干预频率。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值