Part 2 of 4. Page 1 covered the DDS word, power rules, wiring, and the perfboard baseboard. This page covers the two user interfaces: the command-line tool for scripts and the terminal UI for live scope work.
Trying the tool from the command line
The command-line tool is the fastest way to prove that the chain works. With a serial-wired AD9850 module and the default 125 MHz oscillator, ad985x freq 20M loads a 20 MHz tuning word.
The response line shows both the user-level state and the exact bytes sent to the chip:
|
1 2 3 4 |
OK FREQ=20000000 PHASE=0 PWR=0 FTW=0x28F5C28F W=00 28 F5 C2 8F |
That byte string is useful during bench work. If the scope shows nothing, I do not have to wonder what the code intended to load. The tool has already printed the control byte, the tuning word, the phase, and the power state.
The same command grammar works for reset, raw bytes, phase, power, status, capability checks, and stream playback. Examples include ad985x phase 90deg, ad985x pwr down, ad985x reset, ad985x raw 00 28 F5 C2 8F, and ad985x stream examples/sweep.txt --interval-ms 500.
Every explicit command reloads the DDS even when the state has not changed. That is intentional. With a write-only chip, repeating a command is a valid recovery action after a loose jumper, missed power rail, or reset event. The software should not optimize away a reload it cannot verify.
Using the TUI at the bench
The terminal UI became more useful than I expected because it matches how the scope session actually goes. A frequency slider, phase control, power toggle, reset button, raw-byte panel, stream playback, and response log are all visible at once. The TUI is not a separate implementation; it sends the same commands through the same engine as the CLI.
Running ad985x-tui --simulate opens the same interface against the software model, so the TUI can be used on a development machine without a Pi or DDS module attached:
|
1 2 3 4 5 6 7 8 |
> CAPS < OK CAPS V=0.5.0 MODE=SERIAL CHIP=AD9850 > FREQ 20000000 < OK FREQ=20000000 PHASE=0 PWR=0 FTW=0x28F5C28F W=00 28 F5 C2 8F SIM: latched W=00 28 F5 C2 8F FTW=0x28F5C28F PHASE=0 PWR=0 MODE=SERIAL |
That last line is the important part. The simulated chip is not just echoing the encoder. It watches the virtual pin transitions, samples the data bus or serial input, and decodes the word that would have been latched. When the command layer says it sent 00 28 F5 C2 8F and the simulated chip independently reports the same latched word, the byte order and serial bit order have been checked before the module is even connected.
Failure modes I wanted the software to catch
The bench failures are simple, but they are exactly the kind that waste time.
An unpowered DDS module accepts every GPIO sequence from the Pi and produces no output. The chip has no readback, so software cannot tell the difference between a successful load into a powered device and forty bits clocked into a dead rail. The practical rule is boring and useful: when all outputs are flat, check 3.3 V and common ground first.
Reset is another trap. After reset, the AD9850 returns to parallel mode. If the driver assumes serial mode survived, it will shift forty good serial bits into a chip that is interpreting the bus as parallel data. The tool tracks that reset happened and re-enters serial mode before the next serial load.
The analog outputs can mislead too. The comparator square outputs are better for frequency counting because they give the scope clean edges. Counting the sine output directly can wobble from measurement gating and make a correct tuning word look suspicious. The small potentiometer on the blue AD9850 module controls the comparator threshold, which shows up as square-wave duty cycle; for these tests I adjusted it close to 50 percent. If the square outputs are missing or ragged, that trimmer is the first thing to check. If one sine pin looks weak while the square outputs are fine, probe the other sine output before blaming the chip.
Those observations shaped the tool more than the happy path did. The driver keeps its own state, rejects dangerous W0 bits, reloads on explicit commands, warns on profile mismatches, re-enters serial mode after reset, and gives simulation output that can be compared against the command response.
The next page gets into how that structure is built: the C++ core library, the Linux GPIO backend, and why the CLI and TUI are thin frontends over the same engine.
