ad983x/lib.rs
1//! This is a platform agnostic Rust driver for the AD9833, AD9834, AD9837
2//! and AD9838 low-power programmable waveform generators / direct digital
3//! synthesizers (DDS) using the [`embedded-hal`] traits.
4//!
5//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
6//!
7//! This driver allows you to:
8//! - Enable/disable/reset the device. See [`enable()`].
9//! - Set the frequency registers. See: [`set_frequency()`].
10//! - Select the output frequency register. See: [`select_frequency()`].
11//! - Set the phase registers. See: [`set_phase()`].
12//! - Select the output phase register. See: [`select_phase()`].
13//! - Set the frequency registers MSBs/LSBs separately. See: [`set_frequency_msb()`].
14//! - Set the output waveform. See: [`set_output_waveform()`].
15//! - Power down/up device parts. See: [`set_powered_down()`].
16//! - Select control source on AD9834/AD9838. See: [`set_control_source()`].
17//!
18//! [`enable()`]: struct.Ad983x.html#method.enable
19//! [`set_frequency()`]: struct.Ad983x.html#method.set_frequency
20//! [`select_frequency()`]: struct.Ad983x.html#method.select_frequency
21//! [`set_phase()`]: struct.Ad983x.html#method.set_phase
22//! [`select_phase()`]: struct.Ad983x.html#method.select_phase
23//! [`set_frequency_msb()`]: struct.Ad983x.html#method.set_frequency_msb
24//! [`set_output_waveform()`]: struct.Ad983x.html#method.set_output_waveform
25//! [`set_powered_down()`]: struct.Ad983x.html#method.set_powered_down
26//! [`set_control_source()`]: struct.Ad983x.html#method.set_control_source
27//!
28//! [Introductory blog post](https://blog.eldruin.com/ad983x-waveform-generator-dds-driver-in-rust/)
29//!
30//! ## The devices
31//!
32//! The AD9833, AD9834, AD9837 and AD9838 are low power, programmable waveform
33//! generators capable of producing sine, triangular, and square wave outputs.
34//! Waveform generation is required in various types of sensing, actuation,
35//! and time domain reflectometry (TDR) applications. The output frequency and
36//! phase are software programmable, allowing easy tuning. No external
37//! components are needed. The frequency registers are 28 bits wide: with a
38//! 25 MHz clock rate, resolution of 0.1 Hz can be achieved; with a 1 MHz
39//! clock rate, the AD9833 can be tuned to 0.004 Hz resolution.
40//!
41//! The devices are written to via a 3-wire serial interface (SPI).
42//! This serial interface operates at clock rates up to 40 MHz and is
43//! compatible with DSP and microcontroller standards. The devices operate
44//! with a power supply from 2.3 V to 5.5 V.
45//!
46//! Datasheets:
47//! - [AD9833](https://www.analog.com/media/en/technical-documentation/data-sheets/ad9833.PDF)
48//! - [AD9834](https://www.analog.com/media/en/technical-documentation/data-sheets/AD9834.PDF)
49//! - [AD9837](https://www.analog.com/media/en/technical-documentation/data-sheets/AD9837.PDF)
50//! - [AD9838](https://www.analog.com/media/en/technical-documentation/data-sheets/AD9838.PDF)
51//!
52//! Application Note:
53//! - [Programming the AD9833/AD9834](https://www.analog.com/media/en/technical-documentation/application-notes/AN-1070.pdf)
54//!
55//! Article explaining DDS using an AD9833:
56//! - [All about direct digital synthesis](https://www.analog.com/en/analog-dialogue/articles/all-about-direct-digital-synthesis.html)
57//!
58//! ## SysfsPin / Software control source on AD9834/AD9838
59//!
60//! AD9834/AD9838 devices offer the possibility to control several functions
61//! either through hardware pins or software settings. While hardware pin
62//! control is selected, these software operations will be ignored by the hardware.
63//! This driver allows this as well as it would be a valid use case to
64//! configure the status of these functions while on hardware pin control mode
65//! in preparation for a smooth switch to software control.
66//!
67//! ## Usage examples (see also examples folder)
68//!
69//! To use this driver, import this crate and an `embedded_hal` implementation,
70//! then instantiate the appropriate device.
71//! In the following examples an instance of the device AD9833 will be created
72//! as an example. Other devices can be created with similar methods like:
73//! `Ad983x::new_ad9837(...)`.
74//!
75//! Please find additional examples using hardware in this repository: [driver-examples].
76//!
77//! This includes an example MIDI player that plays Beethoven's ninth symphony.
78//!
79//! [driver-examples]: https://github.com/eldruin/driver-examples
80//!
81//! ### Set the frequency register 0 and enable
82//!
83//! ```no_run
84//! use ad983x::{Ad983x, FrequencyRegister};
85//! use embedded_hal_bus::spi::ExclusiveDevice;
86//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
87//!
88//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
89//! let chip_select = SysfsPin::new(25);
90//! let dev = ExclusiveDevice::new(spi, chip_select, Delay);
91//! let mut dds = Ad983x::new_ad9833(dev);
92//! dds.reset().unwrap(); // reset is necessary before operation
93//! dds.set_frequency(FrequencyRegister::F0, 4724).unwrap();
94//! dds.enable().unwrap();
95//! // Given a 25 MHz clock, this now outputs a sine wave
96//! // with a frequency of 440 Hz, which is a standard
97//! // A4 tone.
98//!
99//! // Get device back
100//! let _dev = dds.destroy();
101//! ```
102//!
103//! ### Set frequency registers 0 and 1 and alternate between them
104//!
105//! With a 25 MHz clock this alternates between A4 and D5 tones.
106//!
107//! ```no_run
108//! use ad983x::{Ad983x, FrequencyRegister};
109//! use embedded_hal_bus::spi::ExclusiveDevice;
110//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
111//!
112//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
113//! let chip_select = SysfsPin::new(25);
114//! let dev = ExclusiveDevice::new(spi, chip_select, Delay);
115//! let mut dds = Ad983x::new_ad9833(dev);
116//! dds.reset().unwrap(); // reset is necessary before operation
117//! // A4 tone for a 25 MHz clock
118//! dds.set_frequency(FrequencyRegister::F0, 4724).unwrap();
119//! // D5 tone for a 25 MHz clock
120//! dds.set_frequency(FrequencyRegister::F1, 6306).unwrap();
121//! dds.enable().unwrap();
122//! loop {
123//! // some delay
124//! dds.select_frequency(FrequencyRegister::F1).unwrap();
125//! // some delay
126//! dds.select_frequency(FrequencyRegister::F0).unwrap();
127//! }
128//! ```
129//!
130//! ### Set the phase register 1 and select it
131//!
132//! ```no_run
133//! use ad983x::{Ad983x, PhaseRegister};
134//! use embedded_hal_bus::spi::ExclusiveDevice;
135//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
136//!
137//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
138//! let chip_select = SysfsPin::new(25);
139//! let dev = ExclusiveDevice::new(spi, chip_select, Delay);
140//! let mut dds = Ad983x::new_ad9833(dev);
141//! dds.reset().unwrap(); // reset is necessary before operation
142//! dds.set_phase(PhaseRegister::P1, 4724).unwrap();
143//! dds.select_phase(PhaseRegister::P1).unwrap();
144//! ```
145//!
146//! ### Set output waveform to be triangular
147//!
148//! ```no_run
149//! use ad983x::{Ad983x, OutputWaveform};
150//! use embedded_hal_bus::spi::ExclusiveDevice;
151//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
152//!
153//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
154//! let chip_select = SysfsPin::new(25);
155//! let dev = ExclusiveDevice::new(spi, chip_select, Delay);
156//! let mut dds = Ad983x::new_ad9833(dev);
157//! dds.reset().unwrap(); // reset is necessary before operation
158//! dds.set_output_waveform(OutputWaveform::Triangle).unwrap();
159//! ```
160//!
161//! ### Power down the DAC
162//!
163//! ```no_run
164//! use ad983x::{Ad983x, PoweredDown};
165//! use embedded_hal_bus::spi::ExclusiveDevice;
166//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
167//!
168//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
169//! let chip_select = SysfsPin::new(25);
170//! let dev = ExclusiveDevice::new(spi, chip_select, Delay);
171//! let mut dds = Ad983x::new_ad9833(dev);
172//! dds.reset().unwrap(); // reset is necessary before operation
173//! dds.set_powered_down(PoweredDown::Dac).unwrap();
174//! ```
175//!
176//! ### Use hardware pins as control source
177//!
178//! ```no_run
179//! use ad983x::{Ad983x, ControlSource};
180//! use embedded_hal_bus::spi::ExclusiveDevice;
181//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
182//!
183//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
184//! let chip_select = SysfsPin::new(25);
185//! let dev = ExclusiveDevice::new(spi, chip_select, Delay);
186//! let mut dds = Ad983x::new_ad9838(dev);
187//! dds.reset().unwrap(); // reset is necessary before operation
188//! dds.set_control_source(ControlSource::HardwarePins).unwrap();
189//! // Hardware pins can now be used to control the device.
190//! // The corresponding software settings will be ignored.
191//! ```
192
193#![deny(unsafe_code, missing_docs)]
194#![no_std]
195
196use core::marker::PhantomData;
197use embedded_hal::spi::{Mode, MODE_2};
198
199/// All possible errors in this crate
200#[derive(Debug)]
201pub enum Error<E> {
202 /// SPI communication error
203 Spi(E),
204 /// Invalid argument provided
205 InvalidArgument,
206}
207
208/// Frequency registers
209#[derive(Debug, Clone, Copy, PartialEq)]
210pub enum FrequencyRegister {
211 /// Frequency register 0
212 F0,
213 /// Frequency register 1
214 F1,
215}
216
217/// Phase registers
218#[derive(Debug, Clone, Copy, PartialEq)]
219pub enum PhaseRegister {
220 /// Phase register 0
221 P0,
222 /// Phase register 1
223 P1,
224}
225
226/// Output waveform
227#[derive(Debug, Clone, Copy, PartialEq)]
228pub enum OutputWaveform {
229 /// Sinusoidal wave (default)
230 Sinusoidal,
231 /// Triangle wave
232 Triangle,
233 /// Square wave with its value matching the MSB of DAC data
234 /// (not available on AD9834/AD9838, use `SignBitOutput`)
235 SquareMsbOfDac,
236 /// Square wave with its value matching the MSB of DAC data divided by 2
237 /// (not available on AD9834/AD9838, use `SignBitOutput`)
238 SquareMsbOfDacDiv2,
239}
240
241/// Sign bit output on AD9834/AD9838 devices
242#[derive(Debug, Clone, Copy, PartialEq)]
243pub enum SignBitOutput {
244 /// Disabled (high impedance) (default)
245 Disabled,
246 /// Comparator output
247 Comparator,
248 /// Square wave with its value matching the MSB of DAC data
249 SquareMsbOfDac,
250 /// Square wave with its value matching the MSB of DAC data divided by 2
251 SquareMsbOfDacDiv2,
252}
253
254/// Powered-down device configuration
255#[derive(Debug, Clone, Copy, PartialEq)]
256pub enum PoweredDown {
257 /// All chip parts are enabled (default)
258 Nothing,
259 /// Power down only the DAC
260 Dac,
261 /// Disable only the internal clock
262 InternalClock,
263 /// Power down the DAC and disable the internal clock
264 DacAndInternalClock,
265}
266
267/// Hardware pin / software control source for the functions:
268/// frequency register selection, phase register selection,
269/// reset of internal registers, and DAC power-down.
270/// (Only available on AD9834 and AD9838 devices)
271#[derive(Debug, Clone, Copy, PartialEq)]
272pub enum ControlSource {
273 /// Functions are controlled only through software (default)
274 Software,
275 /// Functions are controlled only through hardware pins
276 HardwarePins,
277}
278
279/// SPI mode (CPOL = 1, CPHA = 0)
280pub const MODE: Mode = MODE_2;
281
282/// Markers
283#[doc(hidden)]
284pub mod marker {
285 /// AD9833/AD9837 device
286 pub struct Ad9833Ad9837(());
287 /// AD9834/AD9838 device
288 pub struct Ad9834Ad9838(());
289}
290
291struct BitFlags;
292
293#[derive(Debug, Default, Clone, Copy, PartialEq)]
294struct Config {
295 bits: u16,
296}
297
298/// AD983x direct digital synthesizer
299#[derive(Debug)]
300pub struct Ad983x<DEV, IC> {
301 spi: DEV,
302 control: Config,
303 _ic: PhantomData<IC>,
304}
305
306mod ad9833_ad9837;
307mod ad9834_ad9838;
308mod common;