波形发生AD9833模块配置—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】

采集通道会读了。今天接波形芯片,出一组已知频率的正弦、三角、方波,用来标定刚才那条采集链。

芯片印的是 AD9833,Linux 主线驱动文件却是 drivers/staging/iio/frequency/ad9834.ccompatible = "adi,ad9833"。这不是配错。ad9834 驱动同时认 9833/9834。Kconfig 是 CONFIG_AD9834=y

产品 rk3568_evb

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

probe 必须有 mclk。缺 clocks,驱动直接失败,sysfs 里不会出现 ad9833。板上晶振 25 MHz,设备树用 fixed-clock 喂进去:

bash 复制代码
ad9833_mclk: ad9833-mclk {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <25000000>;
};

SPI 是 mode 2:CPOL=1,CPHA=0 。dts 只写 spi-cpol;。有的笔记写成「mode2 且 CPHA=1」,那其实是 mode 3,芯片会配不进去。资料上「SCLK 空闲为高、下降沿移入数据」对应的就是 mode 2。

三根线都在 GPIO0:CLK=B5、MOSI=B6、FSYNC/CS=C6。CS 不是硬件 SPI0 的 CS 脚,硬件控制器配不齐,走 spi-gpio 位拍 。硬件 &spi0 保持 disabled。只写 sck/mosi/cs-gpios、不挂 pinctrl 时,脚可能仍停在 SPI0 复用,寄存器「写成功」、管脚不动,模拟输出≈0。pinctrl 强制 GPIO。

频率:

bash 复制代码
Fout = (Fclk / 2^28) × FREQREG
FREQREG = Fout × 2^28 / Fclk

Fclk=25e6,1 kHz → FREQREG ≈ 10737。驱动把这个换算藏在 IIO 的 out_altvoltage0_frequency0 里,你写 1000 即可。

没有示波器,用 SARADC 回环。IIO 的 freq/wavetype/enable 是 只写,回读空是正常,不是失败。


1. 完整设备树

bash 复制代码
ad9833_mclk: ad9833-mclk {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <25000000>;
};

spi_ad9833: spi-ad9833 {
    compatible = "spi-gpio";
    #address-cells = <1>;
    #size-cells = <0>;
    pinctrl-names = "default";
    pinctrl-0 = <&GPIO0_C6_pin &GPIO0_B5_pin &GPIO0_B6_pin>;
    sck-gpios  = <&gpio0 RK_PB5 GPIO_ACTIVE_HIGH>; /* 13 */
    mosi-gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>; /* 14 */
    cs-gpios   = <&gpio0 RK_PC6 GPIO_ACTIVE_LOW>;  /* 22 FSYNC */
    num-chipselects = <1>;

    ad9833: dds@0 {
        compatible = "adi,ad9833";
        reg = <0>;
        spi-max-frequency = <1000000>;
        spi-cpol;                 /* mode 2: CPOL=1 CPHA=0 */
        clocks = <&ad9833_mclk>;
        clock-names = "mclk";
    };
};

&spi0 { status = "disabled"; };
bash 复制代码
ad9833_gpio {
    GPIO0_C6_pin: GPIO0-C6-pin {
        rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
    };
    GPIO0_B5_pin: GPIO0-B5-pin {
        rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
    };
    GPIO0_B6_pin: GPIO0-B6-pin {
        rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
    };
};

CONFIG_SPI_GPIO=yCONFIG_AD9834=y。CFI 严格的内核上,staging 驱动函数指针可能 panic,开发期 CONFIG_CFI_PERMISSIVE=y


2. 控制字(驱动已经替你写,但要能看懂示波器对不上的时候)

AD9833 每次 SPI 16 bit。B28=1 时频率 28 bit 分两次写。常用:

含义
0x2100 RESET + B28,进复位
0x4000 \ FREQ0_LSB
0x4000 \ FREQ0_MSB
0x2000 正弦,出复位
0x2002 三角(MODE=1)
0x2028 方波(OPBITEN + DIV2)
0x2040 SLEEP 一类,视位定义

IIO 属性:

bash 复制代码
out_altvoltage0_frequency0          写 Hz,只写
out_altvoltage0_out0_wavetype       sine | triangle | square,只写
out_altvoltage0_out_enable          1/0,只写;0 会置 RESET
out_altvoltage0_out0_wavetype_available  可读,列出支持的字符串
bash 复制代码
BASE=$(for d in /sys/bus/iio/devices/iio:device*; do
  grep -q ad9833 $d/name && echo $d
done)
echo 1000 > $BASE/out_altvoltage0_frequency0
echo sine > $BASE/out_altvoltage0_out0_wavetype
echo 1 > $BASE/out_altvoltage0_out_enable

write 返回成功即可。cat frequency0 空或 Device or resource busy 都可能,属性权限 0200。

iio:device0 常常就是它。不要因此去读 in_voltage0_raw------那是 SARADC 的。


3. 完整 C

bash 复制代码
/* ad9833_set.c  ./ad9833_set sine 1000
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

static int wr(const char *path, const char *s)
{
    int fd = open(path, O_WRONLY);
    if (fd < 0) {
        fprintf(stderr, "open %s: %s\n", path, strerror(errno));
        return -1;
    }
    int n = (int)write(fd, s, strlen(s));
    close(fd);
    return n < 0 ? -1 : 0;
}

static int find_dev(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, "ad9833") || strstr(name, "ad9834")) {
            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];
    const char *wave = (argc > 1) ? argv[1] : "sine";
    const char *freq = (argc > 2) ? argv[2] : "1000";

    if (find_dev(base, sizeof(base)) < 0) {
        fprintf(stderr, "ad9833 not found. 查 25MHz clock / spi-cpol / pinctrl\n");
        return 1;
    }
    printf("dev %s\n", base);

    if (strcmp(wave, "off") == 0) {
        snprintf(p, sizeof(p), "%s/out_altvoltage0_out_enable", base);
        return wr(p, "0");
    }
    snprintf(p, sizeof(p), "%s/out_altvoltage0_frequency0", base);
    if (wr(p, freq) < 0) {
        return 2;
    }
    snprintf(p, sizeof(p), "%s/out_altvoltage0_out0_wavetype", base);
    if (wr(p, wave) < 0) {
        return 3;
    }
    snprintf(p, sizeof(p), "%s/out_altvoltage0_out_enable", base);
    if (wr(p, "1") < 0) {
        return 4;
    }
    printf("set %s %s Hz\n", wave, freq);
    return 0;
}

4. ArkTS

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

@Entry
@Component
struct WaveGen {
  @State freq: string = '1000';
  @State msg: string = '写 IIO,属性只写,空回读是正常的';

  private base(): string {
    for (let i = 0; i < 8; i++) {
      let p: string = '/sys/bus/iio/devices/iio:device' + i.toString();
      let n: string = evbio.readText(p + '/name');
      if (n.indexOf('ad9833') >= 0 || n.indexOf('ad9834') >= 0) {
        return p;
      }
    }
    return '';
  }

  setWave(type: string) {
    let b: string = this.base();
    if (b.length === 0) {
      this.msg = '没有 ad9833。dmesg 查 probe / 25MHz clock';
      return;
    }
    let r1: string = evbio.writeText(b + '/out_altvoltage0_frequency0', this.freq);
    let r2: string = evbio.writeText(b + '/out_altvoltage0_out0_wavetype', type);
    let r3: string = evbio.writeText(b + '/out_altvoltage0_out_enable', '1');
    this.msg = 'freq=' + r1 + ' type=' + r2 + ' en=' + r3
      + '\n量独立的波形输出座,不要量 ADC 端子。'
      + '\n无示波器:接到 SARADC 外部通道画回环。';
  }

  build() {
    Column() {
      Text('AD9833').fontSize(22).fontWeight(FontWeight.Bold)
      TextInput({ text: this.freq }).width(160).onChange((v: string) => { this.freq = v; })
      Row() {
        Button('正弦').onClick(() => { this.setWave('sine'); }).margin(6)
        Button('三角').onClick(() => { this.setWave('triangle'); }).margin(6)
        Button('方波').onClick(() => { this.setWave('square'); }).margin(6)
        Button('停').onClick(() => {
          let b: string = this.base();
          evbio.writeText(b + '/out_altvoltage0_out_enable', '0');
          this.msg = 'enable=0 → RESET';
        }).margin(6)
      }
      Text(this.msg).margin({ top: 12 }).fontSize(14)
    }.padding(16)
  }
}

chmod 0666 要覆盖 out_altvoltage0_*。只给 in_* 放权,波形写仍是 13。


5. 物理输出

板上波形走运放之后的独立座,不是 ADC5。黑表笔 AGND,红表笔座芯。交流大约零点几伏,视运放和负载。双 0:量错座、SPI 没进芯片、脚还在复用。探针晃一下偶发数字是接触噪声。

enable=0 之后 ADC 上回环仍可能看到 ~0.8 V 直流偏置,那是运放,不是 DDS 还在震。看交流是否下来。


6. 现象对照

现象 原因 处理
无 ad9833 IIO 缺 25MHz clock fixed-clock + clocks=
probe 成功无波形 没 pinctrl GPIO,脚停在 SPI0 三脚 pinctrl
写 wavetype 回读空 属性只写 看 write 返回值
mode 不对 加了 spi-cpha 变成 mode3 只留 spi-cpol
当 ADC 读 device0 编号碰巧是 0 波形用 out_altvoltage,电压用 saradc
bash 复制代码
flowchart TD
  A[无波形] --> B{IIO name=ad9833?}
  B -->|无| C[clock 25MHz / AD9834=y / spi-gpio]
  B -->|有| D{write enable=1 OK?}
  D -->|权限 13| E[chmod out_altvoltage*]
  D -->|OK 仍无交流| F[pinctrl GPIO / 量对座 / mode2]

驱动文件名带 9834,芯片是 9833。25 MHz 必须有。SPI mode 2 只有 spi-cpol。位拍加 pinctrl。频率公式在驱动里,用户写 Hz。无示波器就回环。


7. 位拍:CLK / MOSI 不在同一套硬件 SPI 组里

三根线:

信号 linux
SCLK GPIO0_B5 13
MOSI GPIO0_B6 14
FSYNC/CS GPIO0_C6 22

B 组和 C 组拆开。硬件 &spi0 的一组 pinctrl 按 SPI0_M0 咬 CLK/MOSI/CS,板级把 &spi0 disabled ,改走 spi-gpio 位拍。只写 sck-gpios 不挂 pinctrl 时,脚可能仍停在 SPI0 复用:驱动 spi_sync 成功,管脚不动,模拟输出≈0。pinctrl 三脚强制 RK_FUNC_GPIO

SPI mode 2 :CPOL=1、CPHA=0。dts 只写 spi-cpol; 。再加 spi-cpha 就变成 mode 3,芯片对不齐沿,频率寄存器写进去也不出波。

bash 复制代码
SCLK 空闲为高
     ──┐  ┌──┐  ┌──
       └──┘  └──┘
MOSI 在下降沿附近稳定,芯片在沿上移入
FSYNC 整次 16 bit 期间拉低

每次 16 bit。频率 28 bit 要两次,中间 CS 要变(驱动 cs_change=1)。位拍时钟不要超过 1 MHz,dts spi-max-frequency = <1000000>


8. 回环和幅度

没有示波器:AD9833 输出座 → 外部 ADC 某一通道(VIN2~7,不要 VIN0/1)。使能 1 kHz 正弦,SARADC raw 在偏置附近晃;enable=0 交流下去,剩下运放直流。

幅度:改频率 不改峰峰值。芯片满幅大约零点几伏,运放之后常见 ~0.6 Vpp。万用表 DC 档几乎不随频率变;AC 档大约 0.15~0.2 V RMS 说明在震。DC 为 0 且 AC 为 0 才是没输出。

【截图 30-ad9833-scope】 示波器或 ADC 回环画出的正弦。 文件:assets/shots/30-ad9833-scope.jpg

FREQREG 换算在驱动里:

bash 复制代码
Fout = (Fclk / 2^28) × FREQREG

Fclk=25e6,写 sysfs 1000 即可。fout 不能超过 Fclk/2。缺 25 MHz fixed-clockdevm_clk_get 失败,IIO 里没有 ad9833。

bash 复制代码
hdc shell "dmesg | grep -i ad983"

失败常见:Failed to enable master clock、regulator AVDD。dummy regulator 有时能糊弄过 probe,晶振频率仍必须是 25e6,写错 10e6 频率会整体偏。


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

相关推荐
计算机毕设定制辅导-无忧学长1 小时前
《基于SpringBoot的未成年人健康知识科普平台的设计与实现》
java·开发语言·vue.js·spring boot·未成年人健康知识科普平台
Wang's Blog1 小时前
Java 项目实战: 外卖平台-数据库环境搭建与十一张表结构解析
java·服务器·redis
白远山2 小时前
上海24小时自助健身房系统软件开发实战指南
java·架构·uni-app·需求分析
泡海椒2 小时前
jquick-pdf 表格实战:动态数据 PDF 报表生成
java·开发语言·pdf
OxYGC2 小时前
[AI工程] Spring AI 第十四篇:Agent 五种模式在 2.0 里怎么写
java·spring·ai·ai编程
code2cat2 小时前
【随笔】从聊天到调用工具:理解MCP在AI应用中的位置
java·人工智能
2501_933923252 小时前
Spring Bean作用域揭秘:单例、原型、请求与会话的区别
java·后端·spring·java-ee
学编程就要猛3 小时前
基于Spring AI 的智能聊天机器人
java·spring ai·chat robot
修炼室3 小时前
Java开发工程师笔试经验贴【高频知识】
java