返回首页

ADC 学习笔记

概述

ADC (Analog-to-Digital Converter) 用于将模拟信号转换为数字值。本示例展示如何使用 Zephyr ADC API 进行多通道采样。


1. ADC 基础概念

1.1 关键参数

参数说明
分辨率 (Resolution)ADC 位数,如 10bit、12bit
增益 (Gain)输入信号放大倍数
参考电压 (Reference)ADC 满量程参考电压
采样时间 (Acquisition Time)采样电容充电时间
输入通道正输入/负输入(差分模式)

1.2 电压计算

ADC值 = (Vin / Vref) * (2^resolution - 1)
Vin = ADC值 * Vref / (2^resolution - 1)

2. 设备树配置

2.1 ADC 节点定义

&adc {
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";

    channel@0 {
        reg = <0>;                              // 通道号
        zephyr,gain = "ADC_GAIN_1";             // 增益 1x
        zephyr,reference = "ADC_REF_INTERNAL";  // 内部参考电压
        zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
        zephyr,input-positive = <NRF_SAADC_AIN0>;  // 正输入引脚
        zephyr,resolution = <10>;               // 10位分辨率
    };
};

2.2 用户通道映射

/ {
    zephyr,user {
        io-channels = <&adc 0>, <&adc 1>, <&adc 3>;
    };
};

2.3 差分模式配置

channel@3 {
    reg = <3>;
    zephyr,gain = "ADC_GAIN_1";
    zephyr,reference = "ADC_REF_INTERNAL";
    zephyr,input-positive = <NRF_SAADC_AIN5>;  // 正输入
    zephyr,input-negative = <NRF_SAADC_AIN6>;  // 负输入
    zephyr,resolution = <12>;
};

3. ADC 核心 API

3.1 获取 ADC 通道

// 从设备树获取 ADC 通道规范
static const struct adc_dt_spec adc_channels[] = {
    DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, DT_SPEC_AND_COMMA)
};

// 单个通道
static const struct adc_dt_spec adc_chan = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);

3.2 检查设备就绪

if (!adc_is_ready_dt(&adc_channels[i])) {
    printk("ADC device not ready\n");
}

3.3 配置通道

err = adc_channel_setup_dt(&adc_channels[i]);
if (err < 0) {
    printk("Could not setup channel #%d (%d)\n", i, err);
}

3.4 读取 ADC

uint16_t buf;
struct adc_sequence sequence = {
    .buffer = &buf,
    .buffer_size = sizeof(buf),
};

// 初始化序列
adc_sequence_init_dt(&adc_channels[i], &sequence);

// 读取
err = adc_read_dt(&adc_channels[i], &sequence);
if (err < 0) {
    printk("Could not read (%d)\n", err);
}

3.5 转换为毫伏

int32_t val_mv = (int32_t)buf;
err = adc_raw_to_millivolts_dt(&adc_channels[i], &val_mv);
if (err == 0) {
    printk(" = %d mV\n", val_mv);
}

4. 增益与参考电压

4.1 增益选项

ADC_GAIN_1_6   // 1/6 增益
ADC_GAIN_1_5   // 1/5 增益
ADC_GAIN_1_4   // 1/4 增益
ADC_GAIN_1_3   // 1/3 增益
ADC_GAIN_1_2   // 1/2 增益
ADC_GAIN_1     // 1x 增益(默认)
ADC_GAIN_2     // 2x 增益
ADC_GAIN_3     // 3x 增益
ADC_GAIN_4     // 4x 增益

4.2 参考电压选项

ADC_REF_VDD_1      // VDD/1
ADC_REF_VDD_1_2    // VDD/2
ADC_REF_VDD_1_3    // VDD/3
ADC_REF_VDD_1_4    // VDD/4
ADC_REF_INTERNAL   // 内部参考(通常 0.6V)
ADC_REF_EXTERNAL0  // 外部参考 0
ADC_REF_EXTERNAL1  // 外部参考 1

4.3 nRF SAADC 参考


5. 分辨率与精度

5.1 分辨率选择

zephyr,resolution = <8>;   // 8-bit: 0-255
zephyr,resolution = <10>;  // 10-bit: 0-1023
zephyr,resolution = <12>;  // 12-bit: 0-4095
zephyr,resolution = <14>;  // 14-bit: 0-16383

5.2 过采样提高精度

zephyr,oversampling = <2>;   // 2x 过采样
zephyr,oversampling = <4>;   // 4x 过采样
zephyr,oversampling = <8>;   // 8x 过采样
zephyr,oversampling = <16>;  // 16x 过采样
zephyr,oversampling = <32>;  // 32x 过采样

效果: 过采样可以降低噪声,提高有效位数。


6. 差分模式

6.1 差分输入

zephyr,input-positive = <NRF_SAADC_AIN5>;
zephyr,input-negative = <NRF_SAADC_AIN6>;

6.2 差分值处理

if (adc_channels[i].channel_cfg.differential) {
    val_mv = (int32_t)((int16_t)buf);  // 有符号值
} else {
    val_mv = (int32_t)buf;  // 无符号值
}

差分模式特点:


7. 采样序列

7.1 单次采样

struct adc_sequence sequence = {
    .buffer = &buf,
    .buffer_size = sizeof(buf),
};
adc_read_dt(&adc_chan, &sequence);

7.2 多次采样(缓冲)

uint16_t buf[16];
struct adc_sequence sequence = {
    .buffer = buf,
    .buffer_size = sizeof(buf),
    .options = NULL,  // 可选配置
};

7.3 多通道采样

for (size_t i = 0; i < ARRAY_SIZE(adc_channels); i++) {
    adc_sequence_init_dt(&adc_channels[i], &sequence);
    adc_read_dt(&adc_channels[i], &sequence);
    // 处理 buf...
}

8. 实际应用

8.1 测量电池电压

// 设备树配置
channel@0 {
    reg = <0>;
    zephyr,gain = "ADC_GAIN_1_4";      // VDD/4 增益
    zephyr,reference = "ADC_REF_INTERNAL";  // 0.6V 参考
    zephyr,input-positive = <NRF_SAADC_VDD>;  // VDD 通道
    zephyr,resolution = <12>;
};

// 代码
int32_t vdd_mv = buf;
adc_raw_to_millivolts_dt(&adc_chan, &vdd_mv);
vdd_mv *= 4;  // 补偿 1/4 增益
printk("Battery: %d mV\n", vdd_mv);

8.2 测量温度传感器

// NTC 热敏电阻分压测量
// VDD --- [NTC] --- AIN0 --- [10k] --- GND

int32_t adc_mv = buf;
adc_raw_to_millivolts_dt(&adc_chan, &adc_mv);

// 计算 NTC 电阻
float r_ntc = 10000.0 * adc_mv / (3300 - adc_mv);

// 根据 NTC 参数计算温度(简化)
float temp = 1.0 / (1.0/298.15 + log(r_ntc/10000)/3950) - 273.15;

9. 配置选项 (prj.conf)

CONFIG_ADC=y              # 启用 ADC 驱动
CONFIG_ADC_ASYNC=y        # 异步读取(可选)
CONFIG_ADC_NRFX_SAADC=y   # Nordic SAADC 驱动

10. 关键问题总结

问题答案
ADC 分辨率在哪设?设备树 zephyr,resolution
参考电压是多少?内部参考 0.6V,VDD 参考为 VDD/4
如何测量电池电压?使用 VDD 通道 + 1/4 增益
差分模式怎么配?设置 input-positiveinput-negative
如何提高精度?过采样 zephyr,oversampling

11. 学习心得

  1. 设备树配置:ADC 通道参数在设备树中声明,代码通过 adc_dt_spec 获取
  2. 增益选择:根据输入电压范围选择合适的增益
  3. 参考电压:内部参考稳定,VDD 参考适合测量电池
  4. 差分模式:适合精密测量,消除共模噪声

小白 🤖 2026-03-14