GD32F407VE天空星开发板ADC配置详解

一、ADC概述

1.1 ADC基本概念

ADC(Analog-to-Digital Converter)即模数转换器,是将模拟信号转换为数字信号的关键电子元器件。在嵌入式系统中,ADC使我们能够采集和处理来自现实世界的各种模拟信号。

模拟信号 vs 数字信号对比:

区别 模拟信号 数字信号
取值方式 连续,能取任意值 离散,只能取有限值
时间表现 连续变化 离散变化
抗干扰能力 易受噪声干扰 抗干扰能力强
存储与处理 难以直接处理 易于存储和处理

1.2 ADC应用领域

ADC广泛应用于各种电子设备中:

  • 音频设备:麦克风、录音设备
  • 传感器应用:温度、光、压力传感器
  • 医疗设备:心电图、血压计
  • 工业自动化:过程监控、PLC控制
  • 电池管理:智能手机、电动车

1.3 ADC工作原理

ADC转换过程包含三个关键步骤:

1. 采样(Sampling)

将连续的模拟信号在时间上离散化,按固定时间间隔采集信号值。

2. 量化(Quantization)

将采样得到的连续数值映射到有限的离散等级中。

3. 编码(Encoding)

将量化后的等级转换为二进制数字。

示例:温度监控系统
假设使用3位ADC监控0-50°C温度,对应0-5V电压:

采样电压(V) 量化等级 二进制编码
0.50 0 000
1.10 1 001
1.80 2 010
2.70 4 100

1.4 ADC关键参数

  1. 分辨率:输出数字信号的位数,决定精度

    • 8位:256个等级
    • 12位:4096个等级(GD32F407采用)
    • 16位:65536个等级
  2. 采样率:每秒采样次数(SPS),决定转换速度

  3. 通道数:可采集的模拟信号输入通路数量

  4. 时钟频率:决定ADC转换速率上限

二、GD32F407 ADC开发

2.1 芯片温度采样

硬件配置

GD32F407内置温度传感器,连接到ADC通道16。

配置代码:

void ADC_config() {
	// 使能时钟 rcu_periph_clock_enable
	rcu_periph_clock_enable(RCU_ADC0);
	// 重置 adc_deinit
	adc_deinit();
    
    // ===================基本配置========================
	// 设置分频系数(84M/4=21M)  adc_clock_config
	adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    // 设置分辨率 adc_resolution_config
	adc_resolution_config(ADC0,ADC_RESOLUTION_12B);
	// 设置数据对齐(右对齐) adc_data_alignment_config
	adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    
   	// ===================模式配置========================
	// 设置同步模式(独立模式) adc_sync_mode_config
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
	// 设置单次模式(禁用连续转换) adc_special_function_config
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);
	// 设置非扫描模式 adc_special_function_config
	adc_special_function_config(ADC0,ADC_SCAN_MODE,DISABLE);
    
    // ===================通道配置========================
	// 设置是否打开插入通道(不打开) adc_special_function_config
	adc_special_function_config(ADC0,ADC_INSERTED_CHANNEL_AUTO,DISABLE);
	// 设置转换通道个数(包括常规通道组和插入通道组) adc_channel_length_config
	adc_channel_length_config(ADC0,ADC_ROUTINE_CHANNEL,1);
	// 设置转换哪一个通道以及所处序列位置 adc_routine_channel_config
    // 温度传感器需要更长的采样时间,因为内部信号源阻抗较高,需要更多时间充电采样电容
    // 确保信号在采样期间能稳定到足够精度 >= ADC_SAMPLETIME_84
	adc_routine_channel_config(ADC0,0,ADC_CHANNEL_16,ADC_SAMPLETIME_84);
	// 使能16到18内部通道(温度和参考电压通道) adc_channel_16_to_18
	adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH,ENABLE);

	// 使能ADC adc_enable
	adc_enable(ADC0);
	// 内部校准(需要delay等待) 
	delay_1ms(1);
	// 校准 adc_calibration_enable
	adc_calibration_enable(ADC0);
}
温度读取与转换
void ADC_temp() {
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
	// 等待数据转换完成  没有转换完RESET,一直循环
	while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC));
	adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除标志位
	// 读取ADC
	uint16_t adc = adc_routine_data_read(ADC0);
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	// 电压转温度  ((1.45 - 电压) * 1000 / 4.1) + 25
	float temp = ((1.45 - vol) * 1000 / 4.1) + 25;
	
	printf("[inner] adc = %d, vol = %.2f, temp = %.2f\n", adc, vol, temp);
}
转换时间计算

ADC总转换时间 = 采样时间 + 固定12个时钟周期(量化编码)

例如:21MHz时钟,84周期采样时间:

  • 采样时间:84 × (1/21MHz) = 4μs
  • 量化编码:12 × (1/21MHz) ≈ 0.57μs
  • 总时间:约4.57μs

2.2 电位器采样

硬件连接

电位器连接到PC4引脚(ADC通道14):

VCC → 电位器 → PC4(ADC14) → GND
配置代码
/**********************************************************
 * @brief   模拟输入配置 
 * @param 	rcu  			时钟端口  	RCU_GPIOx(x = A,B,C,D,E,F,G,H,I)
 * @param 	port  			引脚端口  	GPIOx(x = A,B,C,D,E,F,G,H,I)
 * @param 	pin  			引脚  	  	GPIO_PIN_x(x=0..15)
 * @return  
 **********************************************************/
static inline void GPIO_analog(rcu_periph_enum rcu, uint32_t port, uint32_t pin) {
	// 时钟初始化
	rcu_periph_clock_enable(rcu);
	// GPIO模式:输入
	gpio_mode_set(port, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, pin);
}

// PC4 引脚模拟输入
GPIO_analog(RCU_GPIOC, GPIOC, GPIO_PIN_4);
// 底板  电位器  IN14
adc_routine_channel_config(ADC0,0,ADC_CHANNEL_14,ADC_SAMPLETIME_15);  
读取电位器值
void ADC_vol() {
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
	// 等待数据转换完成  没有转换完RESET,一直循环
	while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC));
	adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除标志位
	// 读取ADC
	uint16_t adc = adc_routine_data_read(ADC0);
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	printf("[vol] adc = %d, vol = %.2f\n", adc, vol);
}

2.3 多通道采样

单通道轮询模式

依次配置不同通道进行采样:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "USART0.h"
#include "gpio_cfg.h"

void USART0_on_recv(uint8_t* data, uint32_t len) {
    printf("recv[%d]:%s\n", len, data);
}

void ADC_config() {
	// 使能时钟 rcu_periph_clock_enable
	rcu_periph_clock_enable(RCU_ADC0);
	// 重置 adc_deinit
	adc_deinit();
    
    // ===================基本配置========================
	// 设置分频系数(84M/4=21M)  adc_clock_config
	adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    // 设置分辨率 adc_resolution_config
	adc_resolution_config(ADC0,ADC_RESOLUTION_12B);
	// 设置数据对齐(右对齐) adc_data_alignment_config
	adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    
   	// ===================模式配置========================
	// 设置同步模式(独立模式) adc_sync_mode_config
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
	// 设置单次模式(禁用连续转换) adc_special_function_config
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);
	// 设置非扫描模式 adc_special_function_config
	adc_special_function_config(ADC0,ADC_SCAN_MODE,DISABLE);
    
    // ===================通道配置========================
	// 设置是否打开插入通道(不打开) adc_special_function_config
	adc_special_function_config(ADC0,ADC_INSERTED_CHANNEL_AUTO,DISABLE);
	// 设置转换通道个数(包括常规通道组和插入通道组) adc_channel_length_config
	adc_channel_length_config(ADC0,ADC_ROUTINE_CHANNEL,1);

	// 使能ADC adc_enable
	adc_enable(ADC0);
	// 内部校准(需要delay等待) 
	delay_1ms(1);
	// 校准 adc_calibration_enable
	adc_calibration_enable(ADC0);
}
void ADC_temp() {
	// ====================== 内部温度
	// 设置转换哪一个通道以及所处序列位置 adc_routine_channel_config
    // 温度传感器需要更长的采样时间,因为内部信号源阻抗较高,需要更多时间充电采样电容
    // 确保信号在采样期间能稳定到足够精度 >= ADC_SAMPLETIME_84
	adc_routine_channel_config(ADC0,0,ADC_CHANNEL_16, ADC_SAMPLETIME_84);
	// 使能16到18内部通道(温度和参考电压通道) adc_channel_16_to_18
	adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH,ENABLE);
	
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
	// 等待数据转换完成  没有转换完RESET,一直循环
	while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC));
	adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除标志位
	// 读取ADC
	uint16_t adc = adc_routine_data_read(ADC0);
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	// 电压转温度  ((1.45 - 电压) * 1000 / 4.1) + 25
	float temp = ((1.45 - vol) * 1000 / 4.1) + 25;
	
	printf("[inner] adc = %d, vol = %.2f, temp = %.2f\n", adc, vol, temp);
}

void ADC_vol() {
	// 底板  电位器  IN14
	adc_routine_channel_config(ADC0,0,ADC_CHANNEL_14,ADC_SAMPLETIME_15);
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
	// 等待数据转换完成  没有转换完RESET,一直循环
	while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC));
	adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除标志位
	// 读取ADC
	uint16_t adc = adc_routine_data_read(ADC0);
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	printf("[vol] adc = %d, vol = %.2f\n", adc, vol);
}

//主函数
int main(void) {
	// 配置全局优先级分组规则
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);

    // 初始化系统嘀嗒定时器
    systick_config();
    // 初始化USART
    USART0_init();
	ADC_config(); // ADC配置
	
	// PC4 引脚模拟输入
	GPIO_analog(RCU_GPIOC, GPIOC, GPIO_PIN_4);

    while(1) {
    //轮询获取adc的数据
		ADC_temp(); // ADC读取内部温度
		ADC_vol(); // 底板 电位器
		delay_1ms(1000);
    }
	
	return 0;
}

多通道扫描模式

同时配置多个通道,自动顺序采样:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "USART0.h"
#include "gpio_cfg.h"

void USART0_on_recv(uint8_t* data, uint32_t len) {
    printf("recv[%d]:%s\n", len, data);
}

void ADC_config() {
	// 使能时钟 rcu_periph_clock_enable
	rcu_periph_clock_enable(RCU_ADC0);
	// 重置 adc_deinit
	adc_deinit();
    
    // ===================基本配置========================
	// 设置分频系数(84M/4=21M)  adc_clock_config
	adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    // 设置分辨率 adc_resolution_config
	adc_resolution_config(ADC0,ADC_RESOLUTION_12B);
	// 设置数据对齐(右对齐) adc_data_alignment_config
	adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    
   	// ===================模式配置========================
	// 设置同步模式(独立模式) adc_sync_mode_config
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
	// 设置单次模式(禁用连续转换) adc_special_function_config
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);
	// 设置非扫描模式 adc_special_function_config
	adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE); // ==========ENABLE 扫描模式
    
    // ===================通道配置========================
	// 设置是否打开插入通道(不打开) adc_special_function_config
	adc_special_function_config(ADC0,ADC_INSERTED_CHANNEL_AUTO,DISABLE);
	// 设置转换通道个数(包括常规通道组和插入通道组) adc_channel_length_config
	adc_channel_length_config(ADC0,ADC_ROUTINE_CHANNEL,2); // ====== 通道个数 2个
	// ====================== 内部温度  第 0 个
	// 设置转换哪一个通道以及所处序列位置 adc_routine_channel_config
    // 温度传感器需要更长的采样时间,因为内部信号源阻抗较高,需要更多时间充电采样电容
    // 确保信号在采样期间能稳定到足够精度 >= ADC_SAMPLETIME_84
	adc_routine_channel_config(ADC0,0,ADC_CHANNEL_16, ADC_SAMPLETIME_84);
	// 使能16到18内部通道(温度和参考电压通道) adc_channel_16_to_18
	adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH,ENABLE);
	// ====================== 底板  电位器  IN14   第1个
	adc_routine_channel_config(ADC0,1,ADC_CHANNEL_14,ADC_SAMPLETIME_15);

	// 使能ADC adc_enable
	adc_enable(ADC0);
	// 内部校准(需要delay等待) 
	delay_1ms(1);
	// 校准 adc_calibration_enable
	adc_calibration_enable(ADC0);
}

void ADC_get() {
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
	// 等待数据转换完成  没有转换完RESET,一直循环
	while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC));
	adc_flag_clear(ADC0, ADC_FLAG_EOC); // 清除标志位
	
	// ==============================  内部温度
	// 读取ADC
	uint16_t adc = adc_routine_data_read(ADC0);
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	// 电压转温度  ((1.45 - 电压) * 1000 / 4.1) + 25
	float temp = ((1.45 - vol) * 1000 / 4.1) + 25;
	
	printf("[inner] adc = %d, vol = %.2f, temp = %.2f\n", adc, vol, temp);

	// ==============================  电位器
	// 读取ADC
	adc = adc_routine_data_read(ADC0);
	// ADC转换为电压
	vol = adc * 3.3 / 4095;
	printf("[vol] adc = %d, vol = %.2f\n", adc, vol);
}

int main(void) {
	// 配置全局优先级分组规则
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);

    // 初始化系统嘀嗒定时器
    systick_config();
    // 初始化USART
    USART0_init();
	ADC_config(); // ADC配置
	
	// PC4 引脚模拟输入
	GPIO_analog(RCU_GPIOC, GPIOC, GPIO_PIN_4);

    while(1) {
		ADC_get(); // ADC读取
		delay_1ms(1000);
    }
	
	return 0;
}

2.4 DMA多通道采样

使用DMA实现高效的多通道数据采集:

DMA配置
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "USART0.h"
#include "gpio_cfg.h"

void USART0_on_recv(uint8_t* data, uint32_t len) {
    printf("recv[%d]:%s\n", len, data);
}

#define ADC_RECV_LEN  2
// 用于接收两个通道的数值
static uint16_t g_recv_buff[ADC_RECV_LEN];

#define DMA_CH  DMA1, DMA_CH0
#define DMA_PERIPH_ADDR   (uint32_t)(&ADC_RDATA(ADC0))
#define DMA_SUB_PERIPH    DMA_SUBPERI0

// DMA配置函数
//  DMA  外设到内存  函数定义
static void DMA_config() {
	// 时钟使能
	rcu_periph_clock_enable(RCU_DMA1);
	// 重置
	dma_deinit(DMA_CH);
	dma_single_data_parameter_struct init_struct;
	// 结构体参数初始化
	dma_single_data_para_struct_init(&init_struct);
	// ============ 外设到内存拷贝
	// 方向: 外设到内存  
	init_struct.direction = DMA_PERIPH_TO_MEMORY;
	// 源头:外设
	init_struct.periph_addr = DMA_PERIPH_ADDR;
	init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 不增长
	// 目的:内存
	init_struct.memory0_addr = (uint32_t)g_recv_buff;  // 指定的数组
	init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 增长

	// 搬运一个数据的大小
	init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT; // 16位
	// 搬运数据个数
	init_struct.number = ADC_RECV_LEN; // 最大的搬运个数,实际有可能少于它
	// 优先级
	init_struct.priority = DMA_PRIORITY_HIGH;
	// ======================= 循环模式,启用
	init_struct.circular_mode       = DMA_CIRCULAR_MODE_ENABLE;
	
	// DMA初始化, 设置搬运规则
	dma_single_data_mode_init(DMA_CH, &init_struct);
	// 设置子外设
	dma_channel_subperipheral_select(DMA_CH, DMA_SUB_PERIPH);
	
	// 通知DMA干活,它开始搬运,可以用DMA接收数据
	// 这里不要等待搬运完成,因为不知道啥时候有数据来
	dma_channel_enable(DMA_CH);
}

void ADC_config() {
	// 使能时钟 rcu_periph_clock_enable
	rcu_periph_clock_enable(RCU_ADC0);
	// 重置 adc_deinit
	adc_deinit();
    
    // ===================基本配置========================
	// 设置分频系数(84M/4=21M)  adc_clock_config
	adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    // 设置分辨率 adc_resolution_config
	adc_resolution_config(ADC0,ADC_RESOLUTION_12B);
	// 设置数据对齐(右对齐) adc_data_alignment_config
	adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    
   	// ===================模式配置========================
	// 设置同步模式(独立模式) adc_sync_mode_config
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
	// 设置单次模式(禁用连续转换) adc_special_function_config
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);
	// 设置非扫描模式 adc_special_function_config
	adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE); // ==========ENABLE 扫描模式
    
    // ===================通道配置========================
	// 设置是否打开插入通道(不打开) adc_special_function_config
	adc_special_function_config(ADC0,ADC_INSERTED_CHANNEL_AUTO,DISABLE);
	// 设置转换通道个数(包括常规通道组和插入通道组) adc_channel_length_config
	adc_channel_length_config(ADC0,ADC_ROUTINE_CHANNEL,2); // ====== 通道个数 2个
	// ====================== 内部温度  第 0 个
	// 设置转换哪一个通道以及所处序列位置 adc_routine_channel_config
    // 温度传感器需要更长的采样时间,因为内部信号源阻抗较高,需要更多时间充电采样电容
    // 确保信号在采样期间能稳定到足够精度 >= ADC_SAMPLETIME_84
	adc_routine_channel_config(ADC0,0,ADC_CHANNEL_16, ADC_SAMPLETIME_84);
	// 使能16到18内部通道(温度和参考电压通道) adc_channel_16_to_18
	adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH,ENABLE);
	// ====================== 底板  电位器  IN14   第1个
	adc_routine_channel_config(ADC0,1,ADC_CHANNEL_14,ADC_SAMPLETIME_15);

	// 使能ADC adc_enable
	adc_enable(ADC0);
	// 内部校准(需要delay等待) 
	delay_1ms(1);
	// 校准 adc_calibration_enable
	adc_calibration_enable(ADC0);
	
	// ======================== DMA 代码
	// DMA请求发送
	adc_dma_request_after_last_enable(ADC0);
	// ADC允许DMA模式
	adc_dma_mode_enable(ADC0);
	
	// DMA配置
	DMA_config();
}

void ADC_get() {
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
	// 等待搬运完成   只有没有搬完RESET,进入循环
	while(RESET == dma_flag_get(DMA_CH, DMA_FLAG_FTF));
	// 循环的外面,清除标志位, 不清除不能重复搬运
	dma_flag_clear(DMA_CH, DMA_FLAG_FTF);
	
	// ==============================  内部温度  第0个
	// 读取ADC
	uint16_t adc = g_recv_buff[0];
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	// 电压转温度  ((1.45 - 电压) * 1000 / 4.1) + 25
	float temp = ((1.45 - vol) * 1000 / 4.1) + 25;
	
	printf("[inner] adc = %d, vol = %.2f, temp = %.2f\n", adc, vol, temp);

	// ==============================  电位器  第1个
	// 读取ADC
	adc = g_recv_buff[1];
	// ADC转换为电压
	vol = adc * 3.3 / 4095;
	printf("[vol] adc = %d, vol = %.2f\n", adc, vol);
}

int main(void) {
	// 配置全局优先级分组规则
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);

    // 初始化系统嘀嗒定时器
    systick_config();
    // 初始化USART
    USART0_init();
	ADC_config(); // ADC配置
	// PC4 引脚模拟输入
	GPIO_analog(RCU_GPIOC, GPIOC, GPIO_PIN_4);
    while(1) {
		ADC_get(); // ADC读取
		delay_1ms(1000);
    }
	return 0;
}
连续采样模式
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "USART0.h"
#include "gpio_cfg.h"

void USART0_on_recv(uint8_t* data, uint32_t len) {
    printf("recv[%d]:%s\n", len, data);
}

#define ADC_RECV_LEN  2
// 用于接收两个通道的数值
static uint16_t g_recv_buff[ADC_RECV_LEN];

#define DMA_CH  DMA1, DMA_CH0
#define DMA_PERIPH_ADDR   (uint32_t)(&ADC_RDATA(ADC0))
#define DMA_SUB_PERIPH    DMA_SUBPERI0

// DMA配置函数
//  DMA  外设到内存  函数定义
static void DMA_config() {
	// 时钟使能
	rcu_periph_clock_enable(RCU_DMA1);
	// 重置
	dma_deinit(DMA_CH);
	dma_single_data_parameter_struct init_struct;
	// 结构体参数初始化
	dma_single_data_para_struct_init(&init_struct);
	// ============ 外设到内存拷贝
	// 方向: 外设到内存  
	init_struct.direction = DMA_PERIPH_TO_MEMORY;
	// 源头:外设
	init_struct.periph_addr = DMA_PERIPH_ADDR;
	init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 不增长
	// 目的:内存
	init_struct.memory0_addr = (uint32_t)g_recv_buff;  // 指定的数组
	init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 增长

	// 搬运一个数据的大小
	init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT; // 16位
	// 搬运数据个数
	init_struct.number = ADC_RECV_LEN; // 最大的搬运个数,实际有可能少于它
	// 优先级
	init_struct.priority = DMA_PRIORITY_HIGH;
	// ======================= 循环模式,启用
	init_struct.circular_mode       = DMA_CIRCULAR_MODE_ENABLE;
	
	// DMA初始化, 设置搬运规则
	dma_single_data_mode_init(DMA_CH, &init_struct);
	// 设置子外设
	dma_channel_subperipheral_select(DMA_CH, DMA_SUB_PERIPH);
	
	// 通知DMA干活,它开始搬运,可以用DMA接收数据
	// 这里不要等待搬运完成,因为不知道啥时候有数据来
	dma_channel_enable(DMA_CH);
}

void ADC_config() {
	// 使能时钟 rcu_periph_clock_enable
	rcu_periph_clock_enable(RCU_ADC0);
	// 重置 adc_deinit
	adc_deinit();
    
    // ===================基本配置========================
	// 设置分频系数(84M/4=21M)  adc_clock_config
	adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    // 设置分辨率 adc_resolution_config
	adc_resolution_config(ADC0,ADC_RESOLUTION_12B);
	// 设置数据对齐(右对齐) adc_data_alignment_config
	adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    
   	// ===================模式配置========================
	// 设置同步模式(独立模式) adc_sync_mode_config
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
	// 设置(连续转换) adc_special_function_config
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,ENABLE); // ENABLE 连续模式
																//和上面的DMA的有区别的是这	
	// 设置扫描模式 adc_special_function_config
	adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE); // ==========ENABLE 扫描模式
    
    // ===================通道配置========================
	// 设置是否打开插入通道(不打开) adc_special_function_config
	adc_special_function_config(ADC0,ADC_INSERTED_CHANNEL_AUTO,DISABLE);
	// 设置转换通道个数(包括常规通道组和插入通道组) adc_channel_length_config
	adc_channel_length_config(ADC0,ADC_ROUTINE_CHANNEL,2); // ====== 通道个数 2个
	// ====================== 内部温度  第 0 个
	// 设置转换哪一个通道以及所处序列位置 adc_routine_channel_config
    // 温度传感器需要更长的采样时间,因为内部信号源阻抗较高,需要更多时间充电采样电容
    // 确保信号在采样期间能稳定到足够精度 >= ADC_SAMPLETIME_84
	adc_routine_channel_config(ADC0,0,ADC_CHANNEL_16, ADC_SAMPLETIME_84);
	// 使能16到18内部通道(温度和参考电压通道) adc_channel_16_to_18
	adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH,ENABLE);
	// ====================== 底板  电位器  IN14   第1个
	adc_routine_channel_config(ADC0,1,ADC_CHANNEL_14,ADC_SAMPLETIME_15);

	// 使能ADC adc_enable
	adc_enable(ADC0);
	// 内部校准(需要delay等待) 
	delay_1ms(1);
	// 校准 adc_calibration_enable
	adc_calibration_enable(ADC0);
	
	// ======================== DMA 代码
	// DMA请求发送
	adc_dma_request_after_last_enable(ADC0);
	// ADC允许DMA模式
	adc_dma_mode_enable(ADC0);
	
	// DMA配置
	DMA_config();
	
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);
}

void ADC_get() {
	// 等待搬运完成   只有没有搬完RESET,进入循环
	while(RESET == dma_flag_get(DMA_CH, DMA_FLAG_FTF));
	// 循环的外面,清除标志位, 不清除不能重复搬运
	dma_flag_clear(DMA_CH, DMA_FLAG_FTF);
	
	// ==============================  内部温度  第0个
	// 读取ADC
	uint16_t adc = g_recv_buff[0];
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	// 电压转温度  ((1.45 - 电压) * 1000 / 4.1) + 25
	float temp = ((1.45 - vol) * 1000 / 4.1) + 25;
	
	printf("[inner] adc = %d, vol = %.2f, temp = %.2f\n", adc, vol, temp);

	// ==============================  电位器  第1个
	// 读取ADC
	adc = g_recv_buff[1];
	// ADC转换为电压
	vol = adc * 3.3 / 4095;
	printf("[vol] adc = %d, vol = %.2f\n", adc, vol);
}

int main(void) {
	// 配置全局优先级分组规则
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);

    // 初始化系统嘀嗒定时器
    systick_config();
    // 初始化USART
    USART0_init();
	ADC_config(); // ADC配置
	
	// PC4 引脚模拟输入
	GPIO_analog(RCU_GPIOC, GPIOC, GPIO_PIN_4);

    while(1) {
		ADC_get(); // ADC读取
		delay_1ms(1000);
    }
	
	return 0;
}

2.5 插入通道采样

插入通道用于高优先级的数据采集,可中断常规通道:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "USART0.h"
#include "gpio_cfg.h"

void USART0_on_recv(uint8_t* data, uint32_t len) {
    printf("recv[%d]:%s\n", len, data);
}

void ADC_config() {
	// 使能时钟 rcu_periph_clock_enable
	rcu_periph_clock_enable(RCU_ADC0);
	// 重置 adc_deinit
	adc_deinit();
    
    // ===================基本配置========================
	// 设置分频系数(84M/4=21M)  adc_clock_config
	adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    // 设置分辨率 adc_resolution_config
	adc_resolution_config(ADC0,ADC_RESOLUTION_12B);
	// 设置数据对齐(右对齐) adc_data_alignment_config
	adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);
    
   	// ===================模式配置========================
	// 设置同步模式(独立模式) adc_sync_mode_config
	adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
	// 设置单次模式(禁用连续转换) adc_special_function_config
	adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,DISABLE);
	// 设置非扫描模式 adc_special_function_config
	adc_special_function_config(ADC0,ADC_SCAN_MODE,ENABLE); // ==========ENABLE 扫描模式
    
    // ===================通道配置========================
	// 设置打开插入通道(打开) adc_special_function_config
	adc_special_function_config(ADC0,ADC_INSERTED_CHANNEL_AUTO,ENABLE); // 1. ------------- 注入通道
	// 设置转换通道个数(包括常规通道组和插入通道组) adc_channel_length_config
	// 2. ------------- 注入通道
	adc_channel_length_config(ADC0,ADC_INSERTED_CHANNEL,2); // ====== 注入通道个数 2个
	// ====================== 内部温度  第 0 个
	// 设置转换哪一个通道以及所处序列位置 
    // 温度传感器需要更长的采样时间,因为内部信号源阻抗较高,需要更多时间充电采样电容
    // 确保信号在采样期间能稳定到足够精度 >= ADC_SAMPLETIME_84
	adc_inserted_channel_config(ADC0,0,ADC_CHANNEL_16, ADC_SAMPLETIME_84); // 3. ------------- 注入通道
	// 使能16到18内部通道(温度和参考电压通道) adc_channel_16_to_18
	adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH,ENABLE);
	// ====================== 底板  电位器  IN14   第1个
	adc_inserted_channel_config(ADC0,1,ADC_CHANNEL_14,ADC_SAMPLETIME_15); // 4. ------------- 注入通道

	// 使能ADC adc_enable
	adc_enable(ADC0);
	// 内部校准(需要delay等待) 
	delay_1ms(1);
	// 校准 adc_calibration_enable
	adc_calibration_enable(ADC0);
}

void ADC_get() {
	// 告诉ADC需要转换数据(使能触发)
	adc_software_trigger_enable(ADC0, ADC_INSERTED_CHANNEL); // 5. ------------- 注入通道
	// 等待数据转换完成  没有转换完RESET,一直循环
	while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOIC));  // 6. ------------- 注入通道
	adc_flag_clear(ADC0, ADC_FLAG_EOIC); // 清除标志位   // 7. ------------- 注入通道
	
	// ==============================  内部温度
	// 读取ADC
	uint16_t adc = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0); // 8. ------------- 注入通道
	// ADC转换为电压
	float vol = adc * 3.3 / 4095;
	// 电压转温度  ((1.45 - 电压) * 1000 / 4.1) + 25
	float temp = ((1.45 - vol) * 1000 / 4.1) + 25;
	
	printf("[inner] adc = %d, vol = %.2f, temp = %.2f\n", adc, vol, temp);

	// ==============================  电位器
	// 读取ADC
	adc = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1);  // 9. ------------- 注入通道
	// ADC转换为电压 
	vol = adc * 3.3 / 4095;
	printf("[vol] adc = %d, vol = %.2f\n", adc, vol);
}

int main(void) {
	// 配置全局优先级分组规则
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);

    // 初始化系统嘀嗒定时器
    systick_config();
    // 初始化USART
    USART0_init();
	ADC_config(); // ADC配置
	
	// PC4 引脚模拟输入
	GPIO_analog(RCU_GPIOC, GPIOC, GPIO_PIN_4);

    while(1) {
		ADC_get(); // ADC读取
		delay_1ms(1000);
    }
	
	return 0;
}

三、下载与调试

3.1 调试接口

GD32F407支持多种调试接口:

SWD接口(推荐)
  • SWDIO:数据线
  • SWCLK:时钟线
  • GND:地线
  • VCC:电源(可选)
JTAG接口
  • TMS:模式选择
  • TCK:时钟
  • TDI:数据输入
  • TDO:数据输出
  • nTRST:复位

3.2 调试配置

Keil MDK调试设置
  1. 选择调试器

    • 在Options for Target → Debug中选择对应的调试器
    • 常用:ST-Link、DAP-Link、J-Link
  2. 接口配置

    • 选择SWD接口
    • 设置正确的时钟频率
  3. 优化设置

    • 开发阶段设置为-O0(不优化)
    • 确保调试时变量可见
天空星开发板接线
DAP-Link → 天空星
SWDIO   → PA13
SWCLK   → PA14  
GND     → GND
3.3V    → 3.3V(可选)

3.3 调试技巧

  1. 实时变量监控

    • 使用Watch窗口监控关键变量
    • 设置数据断点
  2. 外设寄存器查看

    • 使用Peripherals菜单查看外设状态
    • 实时监控ADC转换结果
  3. 串口调试

    • 结合串口输出调试信息
    • 实时显示采样数据

四、总结

GD32F407的ADC模块功能强大,支持多种工作模式:

  1. 单次转换:适合不频繁的采样
  2. 扫描模式:适合多通道顺序采样
  3. DMA模式:高效的多通道连续采样
  4. 插入通道:高优先级紧急采样

五、函数解析

函数注释翻译

/* 函数声明 */
/* 初始化配置 */
/* 复位ADC(恢复默认值) */
void adc_deinit(void);
/* 配置所有ADC的时钟分频 */
void adc_clock_config(uint32_t prescaler);
/* 使能或禁用ADC特殊功能 */
void adc_special_function_config(uint32_t adc_periph , uint32_t function , ControlStatus newvalue);
/* 配置ADC数据对齐方式(左对齐/右对齐) */
void adc_data_alignment_config(uint32_t adc_periph , uint32_t data_alignment);
/* 使能ADC接口 */
void adc_enable(uint32_t adc_periph);
/* 禁用ADC接口 */
void adc_disable(uint32_t adc_periph);
/* ADC校准与复位校准(提高转换精度) */
void adc_calibration_enable(uint32_t adc_periph);
/* 配置温度传感器、内部参考电压通道或VBAT通道功能 */
void adc_channel_16_to_18(uint32_t function, ControlStatus newvalue);
/* 配置ADC分辨率(如12位、10位、8位、6位) */
void adc_resolution_config(uint32_t adc_periph, uint32_t resolution);
/* 配置ADC过采样模式(多次采样平均,提高精度) */
void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint8_t ratio);
/* 使能ADC过采样模式 */
void adc_oversample_mode_enable(uint32_t adc_periph);
/* 禁用ADC过采样模式 */
void adc_oversample_mode_disable(uint32_t adc_periph);

/* DMA配置(用于高速数据传输) */
/* 使能DMA请求(ADC转换完成后触发DMA传输) */
void adc_dma_mode_enable(uint32_t adc_periph);
/* 禁用DMA请求 */
void adc_dma_mode_disable(uint32_t adc_periph);
/* 当DMA使能时,在每次常规序列转换结束后触发DMA请求 */
void adc_dma_request_after_last_enable(uint32_t adc_periph);
/* 当检测到DMA控制器的传输结束信号后,禁用DMA引擎 */
void adc_dma_request_after_last_disable(uint32_t adc_periph);

/* 常规序列和注入序列配置(转换通道管理) */
/* 配置ADC不连续模式(每次转换固定数量的通道后停止) */
void adc_discontinuous_mode_config(uint32_t adc_periph , uint8_t adc_sequence , uint8_t length);
/* 配置常规序列或注入序列的通道长度(转换的通道数量) */
void adc_channel_length_config(uint32_t adc_periph , uint8_t adc_sequence , uint32_t length);
/* 配置ADC常规通道(优先级、通道号、采样时间) */
void adc_routine_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc_channel , uint32_t sample_time);
/* 配置ADC注入通道(可打断常规序列的紧急通道) */
void adc_inserted_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc_channel , uint32_t sample_time);
/* 配置ADC注入通道的偏移值(用于与常规通道数据做差) */
void adc_inserted_channel_offset_config(uint32_t adc_periph , uint8_t inserted_channel , uint16_t offset);
/* 配置ADC外部触发源(如定时器、EXTI信号触发转换) */
void adc_external_trigger_source_config(uint32_t adc_periph , uint8_t adc_sequence , uint32_t external_trigger_source);
/* 使能ADC外部触发(配置触发模式:上升沿/下降沿/双边沿) */
void adc_external_trigger_config(uint32_t adc_periph , uint8_t adc_sequence , uint32_t trigger_mode);
/* 使能ADC软件触发(通过软件启动转换) */
void adc_software_trigger_enable(uint32_t adc_periph , uint8_t adc_sequence);
/* 配置转换结束模式(单次转换/连续转换) */
void adc_end_of_conversion_config(uint32_t adc_periph , uint8_t end_selection);

/* 通道数据读取 */
/* 读取ADC常规序列数据寄存器 */
uint16_t adc_routine_data_read(uint32_t adc_periph);
/* 读取ADC注入序列数据寄存器(指定注入通道) */
uint16_t adc_inserted_data_read(uint32_t adc_periph , uint8_t inserted_channel);

/* 看门狗配置(监控转换值范围) */
/* 禁用ADC模拟看门狗单通道监控 */
void adc_watchdog_single_channel_disable(uint32_t adc_periph );
/* 使能ADC模拟看门狗单通道监控(指定通道) */
void adc_watchdog_single_channel_enable(uint32_t adc_periph , uint8_t adc_channel);
/* 配置ADC模拟看门狗序列监控(监控常规/注入序列的所有通道) */
void adc_watchdog_sequence_channel_enable(uint32_t adc_periph , uint8_t adc_sequence);
/* 禁用ADC模拟看门狗(指定序列) */
void adc_watchdog_disable(uint32_t adc_periph , uint8_t adc_sequence);
/* 配置ADC模拟看门狗阈值(下限和上限,超出范围触发中断) */
void adc_watchdog_threshold_config(uint32_t adc_periph , uint16_t low_threshold , uint16_t high_threshold);

/* 中断与标志函数 */
/* 获取ADC标志位状态(如转换完成、溢出等) */
FlagStatus adc_flag_get(uint32_t adc_periph , uint32_t adc_flag);
/* 清除ADC标志位 */
void adc_flag_clear(uint32_t adc_periph , uint32_t adc_flag);
/* 获取ADC常规序列软件启动转换的标志位状态 */
FlagStatus adc_routine_software_startconv_flag_get(uint32_t adc_periph);
/* 获取ADC注入序列软件启动转换的标志位状态 */
FlagStatus adc_inserted_software_startconv_flag_get(uint32_t adc_periph);
/* 获取ADC中断标志位状态 */
FlagStatus adc_interrupt_flag_get(uint32_t adc_periph , uint32_t adc_interrupt);
/* 清除ADC中断标志位 */
void adc_interrupt_flag_clear(uint32_t adc_periph , uint32_t adc_interrupt);
/* 使能ADC中断 */
void adc_interrupt_enable(uint32_t adc_periph , uint32_t adc_interrupt);
/* 禁用ADC中断 */
void adc_interrupt_disable(uint32_t adc_periph , uint32_t adc_interrupt);

/* ADC同步模式(多ADC协同工作) */
/* 配置ADC同步模式(如同步采样、交替采样等) */
void adc_sync_mode_config(uint32_t sync_mode);
/* 配置ADC同步模式下两个采样阶段的延迟 */
void adc_sync_delay_config(uint32_t sample_delay);
/* 配置ADC同步模式的DMA模式选择 */
void adc_sync_dma_config(uint32_t dma_mode );
/* 配置ADC同步模式下,DMA引擎在检测到传输结束信号后禁用 */
void adc_sync_dma_request_after_last_enable(void);
/* 配置ADC同步模式下,DMA引擎根据SYNCDMA位发出请求 */
void adc_sync_dma_request_after_last_disable(void);
/* 读取ADC同步模式下的常规序列数据寄存器 */
uint32_t adc_sync_routine_data_read(void);

#endif /* GD32F4XX_ADC_H */

ADC(Analog-to-Digital Converter,模数转换器)用于将模拟信号(如电压、温度)转换为数字量,是嵌入式系统中采集传感器数据的核心外设。GD32F4xx的ADC功能丰富,支持多通道、高分辨率、DMA传输、同步采样等,以下按功能模块拆解核心函数:

一、初始化与基础配置(ADC工作前提)

这组函数用于ADC的底层配置(时钟、分辨率、校准等),是所有ADC功能的基础。

  1. 核心初始化函数

    • adc_deinit:复位ADC所有寄存器(清除之前的配置),确保初始化从默认状态开始。
    • adc_clock_config:配置ADC时钟分频(ADC时钟源自APB2,需分频后使用,如ADC_CK_AHB_DIV2),时钟频率影响转换速度(频率越高,转换越快,但功耗和噪声可能增加)。
    • adc_enable/adc_disable:使能/禁用ADC模块(ADC上电后需等待稳定时间才能开始转换)。
  2. 精度与数据格式配置

    • adc_resolution_config:设置ADC分辨率(12位/10位/8位/6位),分辨率越高,转换精度越高(12位对应04095,6位对应063),但转换时间更长。
    • adc_data_alignment_config:配置数据对齐方式(左对齐/右对齐),右对齐更便于直接读取数值(如12位数据右对齐时,直接读取低12位即可)。
    • adc_calibration_enable:ADC校准(关键步骤!),通过校准消除硬件偏移误差,提高转换精度,每次上电后需执行一次。
  3. 特殊功能配置

    • adc_channel_16_to_18:使能内部通道(通道16:温度传感器,通道17:内部参考电压VREFINT,通道18:VBAT电池电压),用于采集芯片内部物理量。
    • adc_oversample_mode_config:过采样模式(多次采样取平均),通过牺牲速度换取更高精度(如16次采样平均可将12位精度提升至16位),ratio为采样倍数,shift为结果右移位数(用于平均计算)。
二、通道转换配置(核心功能:指定“采集哪些通道”)

ADC通过“序列”管理多个通道的转换,分为常规序列(默认优先级)和注入序列(可打断常规序列的紧急通道),适合不同场景。

  1. 序列基础配置

    • adc_channel_length_config:设置序列长度(常规/注入序列包含的通道数量,如3个通道则长度为3)。
    • adc_end_of_conversion_config:配置转换模式(单次转换/连续转换):
      • 单次转换:序列转换完成后停止,需重新触发;
      • 连续转换:序列转换完成后自动重启,适合连续采集。
  2. 常规序列配置(普通通道)

    • adc_routine_channel_config:配置常规通道的转换参数:
      • rank:通道优先级(1为最高,按优先级顺序转换);
      • adc_channel:通道号(如ADC_CHANNEL_0对应PA0);
      • sample_time:采样时间(如ADC_SAMPLETIME_15表示15个ADC时钟周期,采样时间越长,抗噪声能力越强,但转换越慢)。
    • 示例:配置常规序列为“通道0(PA0)→ 通道1(PA1)”,优先级1和2,采样时间15周期。
  3. 注入序列配置(紧急通道)

    • adc_inserted_channel_config:配置注入通道(如外部触发的紧急信号),可打断常规序列优先转换,参数与常规通道类似。
    • adc_inserted_channel_offset_config:设置注入通道的偏移值(转换结果 = 实际值 - 偏移值),用于与常规通道数据做差分析(如差分测量)。
  4. 转换触发方式

    • 软件触发:adc_software_trigger_enable(通过代码手动启动转换,适合按需采集);
    • 外部触发:adc_external_trigger_source_config + adc_external_trigger_config(如定时器触发、EXTI中断触发,适合周期性或事件驱动采集)。
三、DMA配置(高速数据传输,解放CPU)

当需要连续采集大量数据(如音频信号、高频传感器)时,通过DMA将ADC转换结果直接传输到内存,无需CPU干预,提高效率。

  1. DMA基础使能

    • adc_dma_mode_enable:使能ADC的DMA请求(转换完成后自动触发DMA传输)。
    • adc_dma_request_after_last_enable:配置“每次常规序列最后一个通道转换完成后触发DMA请求”(确保一次序列的所有数据批量传输)。
  2. DMA传输控制

    • adc_dma_request_after_last_disable:禁用“最后一个通道触发”,改为“每个通道转换完成后立即触发DMA”(适合单通道连续采样,实时性更高)。
四、数据读取(获取转换结果)

转换完成后,通过以下函数读取数字量:

  • adc_routine_data_read:读取常规序列的转换结果(所有常规通道共享一个数据寄存器,按优先级顺序覆盖);
  • adc_inserted_data_read:读取指定注入通道的转换结果(每个注入通道有独立寄存器,不会覆盖)。
五、模拟看门狗(监控信号范围,异常报警)

模拟看门狗用于实时监控ADC转换值是否在设定的阈值范围内,超出范围时触发中断,适合保护场景(如电压过高报警)。

  1. 监控范围配置

    • adc_watchdog_threshold_config:设置阈值范围(low_threshold下限,high_threshold上限),超出范围则触发标志位/中断。
  2. 监控对象配置

    • adc_watchdog_single_channel_enable:监控单个通道(如只监控通道0的电压);
    • adc_watchdog_sequence_channel_enable:监控整个常规/注入序列(所有通道都需在阈值内)。
六、中断与标志(转换状态反馈)

通过中断或标志位判断转换是否完成、是否触发看门狗等,常用标志/中断包括:

  • ADC_FLAG_EOC:常规序列转换完成;
  • ADC_FLAG_EOIC:注入序列转换完成;
  • ADC_INT_WDE:看门狗事件(超出阈值)。

函数作用:

  • adc_flag_get/adc_flag_clear:查询/清除标志位(查询方式);
  • adc_interrupt_enable/adc_interrupt_flag_clear:使能/清除中断(中断方式,效率更高)。
七、同步模式(多ADC协同,提升采样能力)

GD32F4xx可能包含多个ADC(如ADC0、ADC1),同步模式可让多ADC协同工作,适合更高速度或多通道同步采样场景:

  • adc_sync_mode_config:配置同步模式(如同步采样同一信号提高精度,或交替采样不同通道提高总采样率);
  • adc_sync_dma_config:配置同步模式下的DMA传输(多ADC数据通过DMA集中传输)。

典型使用流程

场景1:单通道单次采样(如温度传感器)
  1. 配置ADC时钟:adc_clock_config(ADC_CK_AHB_DIV2)
  2. 使能ADC:adc_enable(ADC0)
  3. 校准ADC:adc_calibration_enable(ADC0)
  4. 配置通道:adc_channel_length_config(ADC0, ADC_ROUTINE_CHANNEL, 1)(长度1),adc_routine_channel_config(ADC0, 1, ADC_CHANNEL_16, ADC_SAMPLETIME_480)(温度传感器通道16);
  5. 软件触发转换:adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL)
  6. 等待转换完成:while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC))
  7. 读取数据:uint16_t temp_data = adc_routine_data_read(ADC0)
场景2:多通道连续采样(带DMA,如3个外部传感器)
  1. 初始化DMA:配置DMA通道,内存地址递增,外设地址固定为ADC数据寄存器;
  2. ADC配置:
    • 使能DMA:adc_dma_mode_enable(ADC0)
    • 配置连续转换:adc_end_of_conversion_config(ADC0, ADC_END_OF_CONVERSION_CONTINUOUS)
    • 配置3个常规通道:adc_channel_length_config(ADC0, ADC_ROUTINE_CHANNEL, 3),依次配置通道0、1、2的优先级和采样时间;
  3. 启动DMA和ADC:dma_channel_enable(DMA0, DMA_CH0)adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL)
  4. DMA自动将转换结果存入内存数组,CPU可周期性读取数组数据。

关键注意事项

  1. 采样时间与转换速度:采样时间越长,抗噪声能力越强,但转换速率越低(需根据信号带宽权衡,如低频信号用长采样时间)。
  2. 校准:每次上电后必须执行adc_calibration_enable,否则转换精度会大幅下降。
  3. 通道与引脚映射:ADC通道对应固定GPIO(如ADC0_CH0对应PA0),需参考数据手册的引脚定义表。
  4. DMA与连续模式:连续转换+DMA是高速采集的最佳组合,可实现无CPU干预的持续数据传输。

掌握这些函数可实现从简单传感器采集到高速信号分析的各种ADC应用,是嵌入式数据采集的核心技能。需要具体场景的代码模板(如温度采集、多通道DMA采样)可以告诉我。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐