采集通道会读了。今天接波形芯片,出一组已知频率的正弦、三角、方波,用来标定刚才那条采集链。
芯片印的是 AD9833,Linux 主线驱动文件却是 drivers/staging/iio/frequency/ad9834.c,compatible = "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=y、CONFIG_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-clock,devm_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