3. 使用ADC采集电位器电压并串口输出

任务目标

本案例基于GD32微控制器,完成以下闭环:配置ADC规则组通道采集电位器分压电压,将采样值转换为实际电压值,并通过串口(USART)以可读格式输出。最终通过串口助手观察电压数值随电位器旋钮变化,验证ADC与串口通信链路正确。

硬件连接与前置条件

  • 开发板:GD32F103系列(以GD32F103C8T6为例,其他型号需核对引脚与时钟配置)
  • 电位器:10kΩ,两端接3.3V与GND,中间抽头接PA1(ADC通道1)
  • 串口:USART0(PA9-TX, PA10-RX),通过USB-TTL模块连接PC,波特率115200
  • 软件:Keil MDK或IAR,GD32标准外设库(V3.x)

核心实现代码

以下代码完成ADC初始化(规则组、软件触发、单次转换)、USART0初始化及电压数据轮询发送。代码基于GD32标准外设库,使用gd32f10x.h头文件。

/* main.c - GD32F103 ADC电位器电压采集与串口输出 */
#include "gd32f10x.h"
#include <stdio.h>

/* 重定向fputc到USART0,用于printf */
int fputc(int ch, FILE *f) {
    usart_data_transmit(USART0, (uint8_t)ch);
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    return ch;
}

static void gpio_config(void) {
    /* PA1: ADC输入模拟浮空 */
    gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
    /* PA9: TX复用推挽; PA10: RX浮空输入 */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}

static void usart0_config(void) {
    usart_deinit(USART0);
    usart_baudrate_set(USART0, 115200U);
    usart_word_length_set(USART0, USART_WL_8BIT);
    usart_stop_bit_set(USART0, USART_STB_1BIT);
    usart_parity_config(USART0, USART_PM_NONE);
    usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
    usart_enable(USART0);
}

static void adc_config(void) {
    /* 使能ADC时钟并配置分频 */
    rcu_periph_clock_enable(RCU_ADC0);
    adc_clock_config(ADC_ADCCK_PCLK2_DIV6);   /* 约12MHz,ADC时钟≤14MHz */

    /* ADC复位与规则组配置 */
    adc_deinit(ADC0);
    adc_mode_config(ADC_MODE_FREE);           /* 独立模式 */
    adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE);
    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE); /* 单次转换 */
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
    adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1U);

    /* 通道1,采样时间55.5周期,软件触发 */
    adc_regular_channel_config(ADC0, 0U, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE); /* 软件触发 */
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);

    /* 使能ADC并校准 */
    adc_enable(ADC0);
    delay_1ms(1U);
    adc_calibration_enable(ADC0);
}

int main(void) {
    /* 系统时钟默认108MHz(需在system_gd32f10x.c中配置) */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_USART0);

    gpio_config();
    usart0_config();
    adc_config();

    while(1) {
        /* 软件触发一次转换并等待完成 */
        adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
        while(RESET == adc_flag_get(ADC0, ADC_FLAG_ENDC));

        uint16_t adc_value = adc_regular_data_read(ADC0);
        float voltage = (float)adc_value * 3.3f / 4095.0f;

        /* 串口输出:原始值 + 电压值 */
        printf("ADC=%4d, Voltage=%.2fV\r\n", adc_value, voltage);

        delay_1ms(500U);   /* 500ms输出一次 */
    }
}

编译与运行

  1. 创建GD32标准外设库工程,将上述代码替换main.c内容。
  2. 在system_gd32f10x.c中确认系统时钟为108MHz(__SYSTEM_CLOCK_108M_PLL_HXTAL宏定义)。
  3. 编译并下载至开发板。
  4. 连接串口至PC,打开串口助手(波特率115200,8N1)。

验证方法与通过标准

  • 现象:串口助手每500ms收到一行数据,格式如ADC=2048, Voltage=1.65V。
  • 判定通过:旋转电位器旋钮,ADC值与电压值随之线性变化;电压范围在0V~3.3V之间;电压计算值与万用表实测值误差小于±0.05V(忽略电源纹波与ADC量化误差)。
  • 预期输出示例(电位器居中时):ADC=2048, Voltage=1.65V。该值由12位ADC满量程4095与3.3V基准直接计算得出,非实测结果。

常见问题排查

  • 串口无输出:检查PA9/PA10接线是否交叉连接;确认usart_baudrate_set与串口助手波特率一致;检查fputc重定向是否生效(需包含stdio.h并勾选微库)。
  • 电压值固定不变:确认电位器抽头确实连接PA1;检查ADC通道号是否与引脚匹配(PA1对应ADC_CHANNEL_1)。
  • 电压值跳变过大:在电位器抽头与GND之间并联0.1μF电容滤波;检查电源是否稳定。