11. ADC 操作指南

本文档说明在 CV184x 平台上通过 IIO 接口使用 SARADC、通道与 sysfs 节点对应关系、电压换算及用户态/内核态操作示例。

11.1. 操作准备

1. 内核要求

  • 使用 SDK 提供的内核镜像。

2. ADC 与通道说明

  • 用户层通过 IIO 接口访问 15 路 12-bit ADC 通道,参考电压 1.5V。

  • 芯片内共有 5 组 SARADC,每组 3 路 12-bit 通道:

    • SARADC0~SARADC2:常规 ADC(共 9 路)

    • RTC_SARADC0~RTC_SARADC1:RTC 域 ADC(共 6 路)

3. IIO 设备路径与通道对应关系

IIO 设备路径为 /sys/bus/iio/devices/iio:device0/,各通道对应的原始值节点如下:

in_voltage1_raw ~ in_voltage3_raw   # SARADC0
in_voltage4_raw ~ in_voltage6_raw   # SARADC1
in_voltage7_raw ~ in_voltage9_raw   # SARADC2
in_voltage10_raw ~ in_voltage12_raw # RTC_SARADC0
in_voltage13_raw ~ in_voltage15_raw # RTC_SARADC1

4. 电压计算公式

电压(mV)= 原始值 × 1500 / 4095

5. 引脚与功能切换

  • SARADC0 通道 3 对应的引脚名称为 ADC3;可采用默认 GPIO 功能,无需切换:

    cvi_pinmux -r ADC3
    
  • RTC_SARADC0 通道 10 对应引脚 PWR_SEQ3;需切换为 GPIO 功能,例如:

    cvi_pinmux -r PWR_SEQ3
    cvi_pinmux -w PWR_SEQ3/PWR_GPIO_5
    

11.2. 操作流程

步骤 1:硬件连接

  • 确认所用 ADC 通道对应的物理引脚(可参考原理图)。

  • 将待测电压源接到对应引脚,注意输入电压不得超过参考电压 1.5V。

  • 保证参考电压稳定。

步骤 2:引脚配置

  • 常规 ADC 通道(SARADC0~2)一般无需额外 pinmux。

  • RTC 域 ADC(RTC_SARADC0/1)或需将对应引脚配置为 ADC 功能,或需切回 GPIO 时,使用 cvi_pinmux 进行切换。

步骤 3:数据读取

  • 用户态:通过 /sys/bus/iio/devices/iio:device0/ 下对应的 in_voltageN_raw 节点读取原始值。

步骤 4:数据处理

  • 读取原始值后,用公式「电压(mV)= 原始值 × 1500 / 4095」换算为电压。

  • 按需做滤波、校准等后处理。

11.3. 操作示例

11.3.1. 读取 SARADC0 通道 1 并换算电压

# 读取原始值
cat /sys/bus/iio/devices/iio:device0/in_voltage1_raw
# 示例输出:2048
# 电压(mV)= 2048 × 1500 / 4095 ≈ 750

说明:开发板原理图中 ADC 常按通道标号(如 SARADC1~9 对应上述 SARADC0~2 的 9 路,PWR_SARADC1~6 对应 RTC_SARADC0/1 的 6 路),具体以原理图为准。

11.3.2. SARADC0 通道 3 与 RTC_SARADC0 通道 10 读取示例

可用万用表对比:将通道接 1.8V 或接地(注意原始值最大为 4095,换算电压最大为 1.5V)。

cat /sys/bus/iio/devices/iio:device0/in_voltage3_raw
cat /sys/bus/iio/devices/iio:device0/in_voltage10_raw
# 接地时读数应接近 0
_images/ADC1.jpg

11.3.3. 内核态 ADC 读写示例

步骤 1:在设备树中为需要配置ADC通道的节点添加ADC通道属性

my-adc-consumer@0 {
    compatible = "myco,my-adc-consumer";
    io-channels = <&saradc0 0>;   /* 0 表示通道 0,范围 0~14 */
    status = "okay";
};

步骤 2:在驱动中申请ADC通道并读取ADC值

struct my_adc_consumer {
    ...
    struct iio_channel *chan;
    ...
};

static int my_adc_consumer_probe(struct platform_device *pdev)
{
    struct my_adc_consumer *priv;
    int ret, val;

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /* 获取 ADC 通道 */
    priv->chan = devm_iio_channel_get(&pdev->dev, NULL);
    if (IS_ERR(priv->chan)) {
        dev_err(&pdev->dev, "iio_channel_get failed: %ld\n", PTR_ERR(priv->chan));
        return PTR_ERR(priv->chan);
    }
    /* 读取原始值 */
    ret = iio_read_channel_raw(priv->chan, &val);
    if (ret < 0) {
        dev_err(&pdev->dev, "read raw failed: %d\n", ret);
        return ret;
    }
    dev_info(&pdev->dev, "ADC raw value: %d\n", val);
    ...
}

11.3.4. 用户态 ADC 读写示例

以下示例通过 IIO sysfs 读取指定通道的原始值并换算为电压(mV)。通道 1~15 对应 in_voltage1_raw``~``in_voltage15_raw,电压公式:电压(mV)= 原始值 × 1500 / 4095。用法:./程序名 <通道号>,通道号 1~15。

 1#include <stdio.h>
 2#include <stdlib.h>
 3#include <fcntl.h>
 4#include <unistd.h>
 5#include <string.h>
 6#include <errno.h>
 7
 8#define ADC_PATH "/sys/bus/iio/devices/iio:device0/"
 9#define MAX_CHANNELS 15
10
11float read_adc_voltage(int channel)
12{
13    char path[256];
14    char buf[32];
15    int fd, raw;
16    float voltage;
17
18    if (channel < 1 || channel > MAX_CHANNELS) {
19        fprintf(stderr, "Invalid channel: %d (1-%d)\n", channel, MAX_CHANNELS);
20        return -1.0;
21    }
22
23    snprintf(path, sizeof(path), "%sin_voltage%d_raw", ADC_PATH, channel);
24
25    fd = open(path, O_RDONLY);
26    if (fd < 0) {
27        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
28        return -1.0;
29    }
30
31    memset(buf, 0, sizeof(buf));
32    if (read(fd, buf, sizeof(buf) - 1) < 0) {
33        fprintf(stderr, "Failed to read %s: %s\n", path, strerror(errno));
34        close(fd);
35        return -1.0;
36    }
37
38    close(fd);
39
40    raw = atoi(buf);
41    voltage = (raw * 1500.0) / 4095.0;
42
43    return voltage;
44}
45
46int main(int argc, char *argv[])
47{
48    int channel;
49    float voltage;
50
51    if (argc != 2) {
52        printf("Usage: %s <channel>\n", argv[0]);
53        printf("Channels: 1-15\n");
54        printf("  1-3: SARADC0\n");
55        printf("  4-6: SARADC1\n");
56        printf("  7-9: SARADC2\n");
57        printf("  10-12: RTC_SARADC0\n");
58        printf("  13-15: RTC_SARADC1\n");
59        return 1;
60    }
61
62    channel = atoi(argv[1]);
63    voltage = read_adc_voltage(channel);
64
65    if (voltage >= 0) {
66        printf("Channel %d: %.2f mV (%.3f V)\n",
67               channel, voltage, voltage / 1000.0);
68    }
69
70    return 0;
71}

运行方式说明

  1. 保存源码:将上述代码保存为文件,例如 adc_read.c

  2. 编译:在宿主机或目标板上使用对应工具链编译。示例(目标板为 ARM 时请替换为实际交叉编译器):

    # 交叉编译示例(以实际工具链为准)
    arm-none-linux-uclibcgnueabihf-gcc -o adc_read adc_read.c -static
    
  3. 运行:在目标板上执行,传入通道号 1~15。无参数或参数错误时会打印用法与通道对应关系。

    ./adc_read <通道号>
    

    示例:

    ./adc_read 1      # 读取 SARADC0 通道 1
    ./adc_read 10     # 读取 RTC_SARADC0 通道 10
    ./adc_read        # 打印用法:Channels 1-15,及 1-3/4-6/7-9/10-12/13-15 对应关系
    
  4. 输出:成功时打印该通道电压,如 Channel 1: 750.00 mV (0.750 V);失败时在 stderr 输出错误信息。