SARADC模数转换—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】

PWM 会出波形了。今天把电压变成数字。板上这组转换器也被用户键用了两路,读数前先认准设备名字。

往外部端子灌 2 V,页面上的毫伏数不动。第一反应是「ADC 坏了」。打开 IIO,读的却是 iio:device0/in_voltage0_raw。device0 的名字是 ad9833,那是波形芯片抢走的编号,根本没有 in_voltage*。SARADC 的名字是 fe720000.saradc ,运行时多半是 iio:device1 。通道 0/1 还被板载 KEY3/KEY4 占用。外部端子是 VIN2~VIN7 ,对应 in_voltage2_raw ... in_voltage7_raw

前端不是把 ±5 V 直灌进 SoC。端子先经过运放和分压,缩到 SARADC 的 0~1.8 V。你在页面上读到的是 脚上的 raw,不是端子上的伏特。2 V 灌进去不会显示 2000。要两点标定。

产品 rk3568_evb

bash 复制代码
device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts

官方连线图:信号、地、参考。这块板参考是 vcca_1v8,不是 3.3 V。流程图:打开设备 → 选通道 → 读 raw → 换算。开源鸿蒙用户态走 IIO sysfs,不走 HDF ADC 那套,除非你另外配了 HCS。两边不要同时绑同一颗 SARADC。


1. 通道怎么分

RK3568 SARADC:10 bit(有的镜像报 12 bit,以 in_voltage_scale 为准),8 通道,控制器 saradc@fe720000

通道 sysfs 接到哪
0 in_voltage0_raw KEY3,VIN0
1 in_voltage1_raw KEY4,VIN1
2~7 in_voltage2_raw ... 7 外部端子 ADCINB0~5,经运放

按 KEY3,通道 0 的 raw 掉到接近 0------说明你找对了 SARADC 这颗 IIO。不要用通道 0/1 当外部电压输入,按键会乱报,外部端子也不会变。

bash 复制代码
hdc shell "cat /sys/bus/iio/devices/iio:device*/name"

典型:

bash 复制代码
ad9833
fe720000.saradc
max30100
hdc1080
veml6040
mlx90614
adc_linux_virtual_iio_driver

按名字找:

bash 复制代码
for d in /sys/bus/iio/devices/iio:device*; do
    echo -n "$d "
    cat $d/name
done

namesaradc 或等于 fe720000.saradc 的那颗,才读 in_voltageN_raw

bash 复制代码
hdc shell "cat /sys/bus/iio/devices/iio:device1/in_voltage_scale"
hdc shell "cat /sys/bus/iio/devices/iio:device1/in_voltage2_raw"

脚电压 mV ≈ raw × scale。1.8 V / 1024 ≈ 1.76 mV/LSB。满量程 raw≈1023 对应约 1800 mV。超了削顶,一直 1023 不是「2 V 读成了 1023 毫伏」,是已经顶格。


2. 前端运放:±5 V 进端子,1.8 V 进芯片

外部六路经过两块运放(常见 TLC2274 一类,0.8 V 偏置 + 电阻分压,独立 3.3 V 给模拟前端)。端子允许大约 ±5 V(以原理图耐压为准),缩到 SARADC 脚的 0~1.8 V。

bash 复制代码
端子 VIN  (±5 V)
    │  分压 + 0.8 V 偏置
    ▼
运放
    │  0~1.8 V
    ▼
SARADC VIN2~VIN7
    │  raw 0~1023
    ▼
sysfs in_voltageN_raw

负电压也能采:偏置让 0 V 端子落在 raw 中间附近,负端往下、正端往上。不要用「raw=0 就是 0 V」去套端子。

两点标定(推荐):

  1. 端子接地,记 raw0、脚电压 V0 = raw0 × scale
  2. 端子接已知 Vref(例如 2.500 V 基准),记 raw1、V1
  3. 增益 g = (2.500 - 0) / (V1 - V0),偏置 b = 0 - V0 × g
  4. 之后端子电压 ≈ (raw × scale) × g + b

应用里把 g、b 做成可填系数,不要写死在固件里。换模块、换电阻,系数就变。

前端没工作的症状:通道 2~7 全是同一个值,扫 0~5 V 都不动。查运放供电(丝印附近 3.3 V)、0.8 V 基准、端子有没有接到你以为的那一路。通道 0/1 会跟着 KEY3/KEY4 动,那只说明 SARADC 本身活着。

设备树:

bash 复制代码
&saradc {
    status = "okay";
    vref-supply = <&vcca_1v8>;
};

内核 CONFIG_ROCKCHIP_SARADC=yCONFIG_IIO=y。公版 adc_keys 挂通道 0,板载按键那篇已经把它 disabled。这里不要再打开。


3. 和波形芯片回环

没有示波器时,把 AD9833 的模拟输出接到某一路外部 ADC(不要接到通道 0/1)。使能正弦 1 kHz,raw 会在偏置附近晃。enable=0 之后交流分量下去,留下运放的直流偏置(常见大约 0.8 V 对应的 raw)。

采样率:sysfs 一次 cat 只有一个点,不是示波器。1 kHz 正弦用 shell 循环读,只能看见乱跳的数,不能还原波形。验收标准是「enable 前后 raw 的峰峰值明显不同」,不是画出漂亮的正弦。要画图,用下面的 C 尽量快地采几百个点,再在主机上画。

量错座:波形座和 ADC 端子是两排。黑表笔 AGND。交流大约零点几伏峰峰值,万用表 AC 档大约 0.15~0.2 V RMS 是有输出;DC 档看到偏置是正常的。


4. 完整 C:按名字找 SARADC,打 raw

bash 复制代码
/* saradc_dump.c
 * armv7-unknown-linux-ohos-clang --target=arm-linux-ohos \
 *   --sysroot=${OHOS_NATIVE}/sysroot \
 *   -o saradc_dump saradc_dump.c \
 *   -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1
 *
 * ./saradc_dump           # 打 ch0..7 一次
 * ./saradc_dump 4 200     # 通道 4,采 200 点
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>

static int read_int(const char *path, int *out)
{
    char buf[64];
    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        return -1;
    }
    int n = (int)read(fd, buf, sizeof(buf) - 1);
    close(fd);
    if (n <= 0) {
        return -1;
    }
    buf[n] = 0;
    *out = atoi(buf);
    return 0;
}

static int find_saradc(char *out, int len)
{
    DIR *d = opendir("/sys/bus/iio/devices");
    struct dirent *e;
    if (!d) {
        return -1;
    }
    while ((e = readdir(d)) != NULL) {
        char p[256], name[64];
        int fd, n;
        if (strncmp(e->d_name, "iio:device", 10) != 0) {
            continue;
        }
        snprintf(p, sizeof(p), "/sys/bus/iio/devices/%s/name", e->d_name);
        fd = open(p, O_RDONLY);
        if (fd < 0) {
            continue;
        }
        n = (int)read(fd, name, sizeof(name) - 1);
        close(fd);
        if (n <= 0) {
            continue;
        }
        name[n] = 0;
        if (strstr(name, "saradc")) {
            snprintf(out, len, "/sys/bus/iio/devices/%s", e->d_name);
            closedir(d);
            return 0;
        }
    }
    closedir(d);
    return -1;
}

int main(int argc, char **argv)
{
    char base[256], p[300];
    int ch, n, i, raw;
    if (find_saradc(base, sizeof(base)) < 0) {
        fprintf(stderr, "没有 saradc。查 &saradc 和名字 fe720000.saradc\n");
        return 1;
    }
    printf("IIO %s\n", base);

    if (argc < 2) {
        for (ch = 0; ch < 8; ch++) {
            snprintf(p, sizeof(p), "%s/in_voltage%d_raw", base, ch);
            if (read_int(p, &raw) == 0) {
                printf("ch%d raw=%d%s\n", ch, raw,
                       (ch <= 1) ? "  (KEY)" : "  (外部)");
            } else {
                printf("ch%d 读失败\n", ch);
            }
        }
        printf("外部看 ch2..7。灌电压时 ch0/1 会跟着按键跳,不是端子。\n");
        return 0;
    }
    ch = atoi(argv[1]);
    n = (argc > 2) ? atoi(argv[2]) : 100;
    snprintf(p, sizeof(p), "%s/in_voltage%d_raw", base, ch);
    for (i = 0; i < n; i++) {
        if (read_int(p, &raw) < 0) {
            perror(p);
            return 2;
        }
        printf("%d\n", raw);
    }
    return 0;
}

把 200 个点拷回主机画散点,比在板上用 shell 循环 cat 干净。


5. ArkTS:标定系数放页面上

bash 复制代码
import evbio from 'libevbio.so';

@Entry
@Component
struct SarAdc {
  @State ch: number = 2;
  @State raw: number = -1;
  @State mv: string = '';
  @State vin: string = '';
  @State log: string = '按名字找 saradc,不要写死 device0';
  @State gain: string = '1';
  @State offset: string = '0';
  private base: string = '';
  private timer: number = -1;
  private scale: number = 1.76;

  build() {
    Column() {
      Text('SARADC VIN2~7').fontSize(22).fontWeight(FontWeight.Bold)
      Row() {
        ForEach([2, 3, 4, 5, 6, 7], (c: number) => {
          Button('ch' + c.toString())
            .onClick(() => { this.ch = c; })
            .margin(4)
        })
      }
      Text('raw ' + this.raw.toString() + '  脚 ' + this.mv + ' mV').margin({ top: 8 })
      Text('端子(标定后) ' + this.vin + ' V').fontSize(20)
      Row() {
        Text('gain').margin(4)
        TextInput({ text: this.gain }).width(80).onChange((v: string) => { this.gain = v; })
        Text('offset').margin(4)
        TextInput({ text: this.offset }).width(80).onChange((v: string) => { this.offset = v; })
      }.margin({ top: 8 })
      Text(this.log).fontSize(14).margin({ top: 8 })
    }.padding(16)
  }

  aboutToAppear() {
    this.base = evbio.findIio('saradc');
    if (this.base.length === 0) {
      this.log = '没有 fe720000.saradc。device0 若是 ad9833,那是波形芯片';
      return;
    }
    let s: string = evbio.readText(this.base + '/in_voltage_scale');
    let sc: number = parseFloat(s);
    if (sc > 0) {
      this.scale = sc;
    }
    this.log = this.base + ' scale=' + this.scale.toString();
    this.timer = setInterval(() => { this.tick(); }, 200);
  }

  tick() {
    let t: string = evbio.readText(this.base + '/in_voltage' + this.ch.toString() + '_raw');
    this.raw = parseInt(t, 10);
    if (isNaN(this.raw)) {
      return;
    }
    let pin: number = this.raw * this.scale;
    this.mv = pin.toFixed(1);
    let g: number = parseFloat(this.gain);
    let o: number = parseFloat(this.offset);
    if (isNaN(g)) { g = 1; }
    if (isNaN(o)) { o = 0; }
    this.vin = (pin / 1000.0 * g + o).toFixed(3);
  }

  aboutToDisappear() {
    if (this.timer >= 0) {
      clearInterval(this.timer);
    }
  }
}

HAP 读 sysfs 要 chmod in_voltage*_rawnamein_voltage_scale

通道 0/1 可以在诊断页只读显示「KEY3/KEY4」,不要标成「外部 1、外部 2」。


6. 现象对照

现象 原因 处理
device0 没有 in_voltage device0 是 ad9833 按 name=saradc 找,多半 device1
灌 2 V raw 不变 读的 ch0/1,或前端没电 改 ch2~7;量运放 3.3 V
raw 一直满量程 端子超过 1.8 V 映射后削顶 降电压或看分压
显示 2000 mV 把 raw 当毫伏,或没标定 raw×scale 是脚电压;端子要增益
负电压 raw 不翻 没把偏置算进去 两点标定
回环不晃 接到 KEY 通道或波形没 enable 外部通道 + AD9833 enable=1
KEY3 乱报 外部信号灌进 VIN0 外部只用 2~7
bash 复制代码
flowchart TD
  A[灌电压 raw 不动] --> B{name 是 saradc?}
  B -->|否, ad9833| C[换设备, 按名字找]
  B -->|是| D{通道是 2..7?}
  D -->|0/1| E[那是 KEY3/KEY4]
  D -->|是| F{ch2-7 全不变?}
  F -->|是| G[运放供电 / 0.8V 基准 / 端子]
  F -->|只有满量程| H[削顶, 降电压]

SARADC 叫 fe720000.saradc,编号常是 iio:device1。外部走通道 2~7,前端运放把 ±5 V 缩到 0~1.8 V。C 和 ArkTS 都读 raw,端子伏特靠两点标定。波形芯片可以回环,当没有示波器时的活证据。

读 raw 是第一件事。换算、标定、回环都排在名字找对之后。


系列第 29 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10

相关推荐
花间相见1 小时前
【后端开发|网络编程】—— 五种实时通信方案全解析:短轮询、长轮询、SSE、MQTT、WebSocket
后端·网络协议
坤岭1 小时前
LLM应用安全护栏实战
后端
程序猿阿越1 小时前
containerd如何创建Pod
后端·kubernetes·源码阅读
名字还没想好☜1 小时前
Python sqlite3 实战:事务提交、参数化防注入、WAL 并发与 row_factory 取字典
后端·python·编程语言
志尊宝1 小时前
Vue3 零基础每日笔记(037):动手封装 useMouse 与 useWindowSize——事件监听的标准模板
前端·javascript·vue.js·笔记·html5
花间相见1 小时前
【AI应用开发|Agent记忆】—— Codex 与 Claude Code 持久化记忆对比:两条不用向量库的路线
后端·agent
葡萄城技术团队1 小时前
GcExcel V9.2 新特性揭秘:公式对象的无损往返
后端
蜗牛互联网1 小时前
Claude放宽生命科学限制:代价是验证、分级和30天留存
java·人工智能·后端
上进小菜猪2 小时前
被 32 位整数卡了 30 年脖子:PostgreSQL 事务号困局与 V9 的 64 位解法
后端