GD32F407VE天空星开发板ADC配置详解
GD32F407 ADC开发摘要 GD32F407VE天空星开发板内置12位ADC模块,支持多通道模拟信号采集。ADC配置要点包括: 时钟设置(默认84MHz分频至21MHz) 选择12位分辨率(4096级) 配置单次/连续转换模式 设置采样时间(温度传感器需≥84周期) 校准ADC提高精度 典型应用: 内部温度传感器(通道16):需特殊使能,转换公式为((1.45V-Vadc)*1000/4.1
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关键参数
-
分辨率:输出数字信号的位数,决定精度
- 8位:256个等级
- 12位:4096个等级(GD32F407采用)
- 16位:65536个等级
-
采样率:每秒采样次数(SPS),决定转换速度
-
通道数:可采集的模拟信号输入通路数量
-
时钟频率:决定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调试设置
-
选择调试器
- 在Options for Target → Debug中选择对应的调试器
- 常用:ST-Link、DAP-Link、J-Link
-
接口配置
- 选择SWD接口
- 设置正确的时钟频率
-
优化设置
- 开发阶段设置为-O0(不优化)
- 确保调试时变量可见
天空星开发板接线
DAP-Link → 天空星
SWDIO → PA13
SWCLK → PA14
GND → GND
3.3V → 3.3V(可选)
3.3 调试技巧
-
实时变量监控
- 使用Watch窗口监控关键变量
- 设置数据断点
-
外设寄存器查看
- 使用Peripherals菜单查看外设状态
- 实时监控ADC转换结果
-
串口调试
- 结合串口输出调试信息
- 实时显示采样数据
四、总结
GD32F407的ADC模块功能强大,支持多种工作模式:
- 单次转换:适合不频繁的采样
- 扫描模式:适合多通道顺序采样
- DMA模式:高效的多通道连续采样
- 插入通道:高优先级紧急采样
五、函数解析
函数注释翻译
/* 函数声明 */
/* 初始化配置 */
/* 复位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功能的基础。
-
核心初始化函数
adc_deinit:复位ADC所有寄存器(清除之前的配置),确保初始化从默认状态开始。adc_clock_config:配置ADC时钟分频(ADC时钟源自APB2,需分频后使用,如ADC_CK_AHB_DIV2),时钟频率影响转换速度(频率越高,转换越快,但功耗和噪声可能增加)。adc_enable/adc_disable:使能/禁用ADC模块(ADC上电后需等待稳定时间才能开始转换)。
-
精度与数据格式配置
adc_resolution_config:设置ADC分辨率(12位/10位/8位/6位),分辨率越高,转换精度越高(12位对应04095,6位对应063),但转换时间更长。adc_data_alignment_config:配置数据对齐方式(左对齐/右对齐),右对齐更便于直接读取数值(如12位数据右对齐时,直接读取低12位即可)。adc_calibration_enable:ADC校准(关键步骤!),通过校准消除硬件偏移误差,提高转换精度,每次上电后需执行一次。
-
特殊功能配置
adc_channel_16_to_18:使能内部通道(通道16:温度传感器,通道17:内部参考电压VREFINT,通道18:VBAT电池电压),用于采集芯片内部物理量。adc_oversample_mode_config:过采样模式(多次采样取平均),通过牺牲速度换取更高精度(如16次采样平均可将12位精度提升至16位),ratio为采样倍数,shift为结果右移位数(用于平均计算)。
二、通道转换配置(核心功能:指定“采集哪些通道”)
ADC通过“序列”管理多个通道的转换,分为常规序列(默认优先级)和注入序列(可打断常规序列的紧急通道),适合不同场景。
-
序列基础配置
adc_channel_length_config:设置序列长度(常规/注入序列包含的通道数量,如3个通道则长度为3)。adc_end_of_conversion_config:配置转换模式(单次转换/连续转换):- 单次转换:序列转换完成后停止,需重新触发;
- 连续转换:序列转换完成后自动重启,适合连续采集。
-
常规序列配置(普通通道)
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周期。
-
注入序列配置(紧急通道)
adc_inserted_channel_config:配置注入通道(如外部触发的紧急信号),可打断常规序列优先转换,参数与常规通道类似。adc_inserted_channel_offset_config:设置注入通道的偏移值(转换结果 = 实际值 - 偏移值),用于与常规通道数据做差分析(如差分测量)。
-
转换触发方式
- 软件触发:
adc_software_trigger_enable(通过代码手动启动转换,适合按需采集); - 外部触发:
adc_external_trigger_source_config+adc_external_trigger_config(如定时器触发、EXTI中断触发,适合周期性或事件驱动采集)。
- 软件触发:
三、DMA配置(高速数据传输,解放CPU)
当需要连续采集大量数据(如音频信号、高频传感器)时,通过DMA将ADC转换结果直接传输到内存,无需CPU干预,提高效率。
-
DMA基础使能
adc_dma_mode_enable:使能ADC的DMA请求(转换完成后自动触发DMA传输)。adc_dma_request_after_last_enable:配置“每次常规序列最后一个通道转换完成后触发DMA请求”(确保一次序列的所有数据批量传输)。
-
DMA传输控制
adc_dma_request_after_last_disable:禁用“最后一个通道触发”,改为“每个通道转换完成后立即触发DMA”(适合单通道连续采样,实时性更高)。
四、数据读取(获取转换结果)
转换完成后,通过以下函数读取数字量:
adc_routine_data_read:读取常规序列的转换结果(所有常规通道共享一个数据寄存器,按优先级顺序覆盖);adc_inserted_data_read:读取指定注入通道的转换结果(每个注入通道有独立寄存器,不会覆盖)。
五、模拟看门狗(监控信号范围,异常报警)
模拟看门狗用于实时监控ADC转换值是否在设定的阈值范围内,超出范围时触发中断,适合保护场景(如电压过高报警)。
-
监控范围配置
adc_watchdog_threshold_config:设置阈值范围(low_threshold下限,high_threshold上限),超出范围则触发标志位/中断。
-
监控对象配置
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:单通道单次采样(如温度传感器)
- 配置ADC时钟:
adc_clock_config(ADC_CK_AHB_DIV2); - 使能ADC:
adc_enable(ADC0); - 校准ADC:
adc_calibration_enable(ADC0); - 配置通道:
adc_channel_length_config(ADC0, ADC_ROUTINE_CHANNEL, 1)(长度1),adc_routine_channel_config(ADC0, 1, ADC_CHANNEL_16, ADC_SAMPLETIME_480)(温度传感器通道16); - 软件触发转换:
adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL); - 等待转换完成:
while(RESET == adc_flag_get(ADC0, ADC_FLAG_EOC)); - 读取数据:
uint16_t temp_data = adc_routine_data_read(ADC0)。
场景2:多通道连续采样(带DMA,如3个外部传感器)
- 初始化DMA:配置DMA通道,内存地址递增,外设地址固定为ADC数据寄存器;
- 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的优先级和采样时间;
- 使能DMA:
- 启动DMA和ADC:
dma_channel_enable(DMA0, DMA_CH0),adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL); - DMA自动将转换结果存入内存数组,CPU可周期性读取数组数据。
关键注意事项
- 采样时间与转换速度:采样时间越长,抗噪声能力越强,但转换速率越低(需根据信号带宽权衡,如低频信号用长采样时间)。
- 校准:每次上电后必须执行
adc_calibration_enable,否则转换精度会大幅下降。 - 通道与引脚映射:ADC通道对应固定GPIO(如ADC0_CH0对应PA0),需参考数据手册的引脚定义表。
- DMA与连续模式:连续转换+DMA是高速采集的最佳组合,可实现无CPU干预的持续数据传输。
掌握这些函数可实现从简单传感器采集到高速信号分析的各种ADC应用,是嵌入式数据采集的核心技能。需要具体场景的代码模板(如温度采集、多通道DMA采样)可以告诉我。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)