ad9959/
lib.rs

1#![no_std]
2
3use core::num::Wrapping;
4
5use arbitrary_int::{Number, u2, u3, u4, u5, u10, u14, u24};
6use bitbybit::{bitenum, bitfield};
7use embedded_hal::{blocking::delay::DelayUs, digital::v2::OutputPin};
8
9/// A trait that allows a HAL to provide a means of communicating with the AD9959.
10pub trait Interface {
11    type Error;
12    fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>;
13    fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Self::Error>;
14    fn read(
15        &mut self,
16        addr: Address,
17        data: &mut [u8],
18    ) -> Result<(), Self::Error>;
19}
20
21/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to
22/// the configuration bits of the DDS CSR register.
23#[derive(PartialEq)]
24#[bitenum(u2, exhaustive = true)]
25pub enum Mode {
26    SingleBitTwoWire = 0b00,
27    SingleBitThreeWire = 0b01,
28    TwoBitSerial = 0b10,
29    FourBitSerial = 0b11,
30}
31
32pub type Channel = u4;
33
34#[bitfield(u8, default = 0xf0)]
35#[derive(Debug, PartialEq)]
36pub struct Csr {
37    #[bit(0, rw)]
38    lsb_first: bool,
39    #[bits(1..=2, rw)]
40    mode: Mode,
41    #[bits(4..=7, rw)]
42    channel: u4,
43}
44
45#[bitfield(u24, default = 0)]
46#[derive(Debug, PartialEq)]
47pub struct Fr1 {
48    #[bit(0, rw)]
49    sw_sync: bool,
50    #[bit(1, rw)]
51    hw_sync: bool,
52    #[bit(4, rw)]
53    dac_ref_pd: bool,
54    #[bit(5, rw)]
55    sync_clk_pd: bool,
56    #[bit(6, rw)]
57    ext_pd: bool,
58    #[bit(7, rw)]
59    ext_clk_pd: bool,
60    #[bits(8..=9, rw)]
61    modulation: u2,
62    #[bits(10..=11, rw)]
63    ramp_up_down: u2,
64    #[bits(12..=14, rw)]
65    profile_pin: u3,
66    #[bits(16..=17, rw)]
67    charge_pump: u2,
68    #[bits(18..=22, rw)]
69    pll_divier: u5,
70    #[bit(23, rw)]
71    vco_high: bool,
72}
73
74#[bitfield(u24, default = 0)]
75#[derive(Debug, PartialEq)]
76pub struct Acr {
77    #[bits(0..=9, rw)]
78    asf: u10,
79    #[bit(10, rw)]
80    load_arr: bool,
81    #[bit(11, rw)]
82    ramp: bool,
83    #[bit(12, rw)]
84    multiplier: bool,
85    #[bits(14..=15, rw)]
86    step: u2,
87    #[bits(16..=23, rw)]
88    arr: u8,
89}
90
91#[allow(clippy::upper_case_acronyms)]
92#[bitenum(u7)]
93pub enum Address {
94    CSR = 0x00,
95    FR1 = 0x01,
96    FR2 = 0x02,
97    CFR = 0x03,
98    CFTW0 = 0x04,
99    CPOW0 = 0x05,
100    ACR = 0x06,
101    LSRR = 0x07,
102    RDW = 0x08,
103    FDW = 0x09,
104    CW1 = 0x0a,
105    CW2 = 0x0b,
106    CW3 = 0x0c,
107    CW4 = 0x0d,
108    CW5 = 0x0e,
109    CW6 = 0x0f,
110    CW7 = 0x10,
111    CW8 = 0x11,
112    CW9 = 0x12,
113    CW10 = 0x13,
114    CW11 = 0x14,
115    CW12 = 0x15,
116    CW13 = 0x16,
117    CW14 = 0x17,
118    CW15 = 0x18,
119}
120
121/// Possible errors generated by the AD9959 driver.
122#[derive(Debug)]
123pub enum Error {
124    Interface,
125    Check,
126    Bounds,
127    Pin,
128    Frequency,
129}
130
131/// A device driver for the AD9959 direct digital synthesis (DDS) chip.
132///
133/// This chip provides four independently controllable digital-to-analog output sinusoids with
134/// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they
135/// are derived off a common system clock.
136///
137/// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz.
138///
139/// The chip supports a number of serial interfaces to improve data throughput, including normal,
140/// dual, and quad SPI configurations.
141pub struct Ad9959<I> {
142    interface: I,
143    ftw_per_hz: f32,
144    mode: Mode,
145}
146
147impl<I: Interface> Ad9959<I> {
148    /// Construct and initialize the DDS.
149    ///
150    /// Args:
151    /// * `interface` - An interface to the DDS.
152    /// * `reset_pin` - A pin connected to the DDS reset input.
153    /// * `io_update` - A pin connected to the DDS io_update input.
154    /// * `delay` - A delay implementation for blocking operation for specific amounts of time.
155    /// * `desired_mode` - The desired communication mode of the interface to the DDS.
156    /// * `clock_frequency` - The clock frequency of the reference clock input.
157    /// * `multiplier` - The desired clock multiplier for the system clock. This multiplies
158    ///   `clock_frequency` to generate the system clock.
159    pub fn new(
160        interface: I,
161        reset: &mut impl OutputPin,
162        io_update: &mut impl OutputPin,
163        delay: &mut impl DelayUs<u8>,
164        mode: Mode,
165        reference_clock_frequency: f32,
166        multiplier: u5,
167    ) -> Result<Self, Error> {
168        let mut ad9959 = Ad9959 {
169            interface,
170            ftw_per_hz: 0.0,
171            mode,
172        };
173        io_update.set_low().or(Err(Error::Pin))?;
174
175        // Reset the AD9959 (Pounder v1.1 and earlier)
176        // On Pounder v1.2 and later the reset has been done through the GPIO extender in
177        // PounderDevices before.
178        reset.set_high().or(Err(Error::Pin))?;
179        // Delays here are at least 1 SYNC_CLK period. The SYNC_CLK is guaranteed
180        // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). We use 5uS instead of 4uS to
181        // guarantee conformance with datasheet requirements.
182        delay.delay_us(5);
183        reset.set_low().or(Err(Error::Pin))?;
184
185        ad9959
186            .interface
187            .configure_mode(Mode::SingleBitTwoWire)
188            .or(Err(Error::Interface))?;
189
190        let csr = Csr::default().with_channel(u4::new(0b1111)).with_mode(mode);
191        ad9959.write(Address::CSR, &csr.raw_value().to_be_bytes())?;
192
193        io_update.set_high().or(Err(Error::Pin))?;
194        delay.delay_us(5);
195        io_update.set_low().or(Err(Error::Pin))?;
196
197        ad9959
198            .interface
199            .configure_mode(mode)
200            .or(Err(Error::Interface))?;
201
202        // Empirical evidence indicates a delay is necessary here for the IO update to become
203        // active. This is likely due to needing to wait at least 1 clock cycle of the DDS for the
204        // interface update to occur.
205        delay.delay_us(5);
206
207        // Read back the CSR to ensure it specifies the mode correctly.
208        let mut updated_csr = 0u8.to_be_bytes();
209        ad9959.read(Address::CSR, &mut updated_csr)?;
210        if updated_csr != csr.raw_value().to_be_bytes() {
211            return Err(Error::Check);
212        }
213
214        // Set the clock frequency to configure the device as necessary.
215        ad9959.set_system_clock(reference_clock_frequency, multiplier)?;
216        io_update.set_high().or(Err(Error::Pin))?;
217        delay.delay_us(5);
218        io_update.set_low().or(Err(Error::Pin))?;
219
220        Ok(ad9959)
221    }
222
223    fn read(&mut self, reg: Address, data: &mut [u8]) -> Result<(), Error> {
224        self.interface.read(reg, data).or(Err(Error::Interface))
225    }
226
227    fn write(&mut self, reg: Address, data: &[u8]) -> Result<(), Error> {
228        self.interface.write(reg, data).or(Err(Error::Interface))
229    }
230
231    /// Configure the internal system clock of the chip.
232    ///
233    /// Arguments:
234    /// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core.
235    /// * `multiplier` - The frequency multiplier of the system clock. Must be 1 or 4-20.
236    ///
237    /// Returns:
238    /// The actual frequency configured for the internal system clock.
239    fn set_system_clock(
240        &mut self,
241        reference_clock_frequency: f32,
242        multiplier: u5,
243    ) -> Result<f32, Error> {
244        let sysclk = multiplier.value() as f32 * reference_clock_frequency;
245        if match multiplier.value() {
246            1 => !(1e6..=500e6).contains(&reference_clock_frequency),
247            4..=20 => {
248                !(10e6..=125e6).contains(&reference_clock_frequency)
249                    || !(100e6..=500e6).contains(&sysclk)
250            }
251            _ => true,
252        } {
253            return Err(Error::Bounds);
254        }
255        let mut fr1 = u24::new(0).to_be_bytes();
256        self.read(Address::FR1, &mut fr1)?;
257        let fr1 = Fr1::new_with_raw_value(u24::from_be_bytes(fr1))
258            .with_pll_divier(multiplier)
259            .with_vco_high(sysclk >= 200e6);
260        self.write(Address::FR1, &fr1.raw_value().to_be_bytes())?;
261        self.ftw_per_hz = (1u64 << 32) as f32 / sysclk;
262        Ok(sysclk)
263    }
264
265    /// Get the current CSR register.
266    pub fn csr(&mut self) -> Result<Csr, Error> {
267        let mut data = u8::new(0).to_be_bytes();
268        self.read(Address::CSR, &mut data)?;
269        Ok(Csr::new_with_raw_value(u8::from_be_bytes(data)))
270    }
271
272    /// Get the current FR1 register.
273    pub fn fr1(&mut self) -> Result<Fr1, Error> {
274        let mut data = u24::new(0).to_be_bytes();
275        self.read(Address::FR1, &mut data)?;
276        Ok(Fr1::new_with_raw_value(u24::from_be_bytes(data)))
277    }
278
279    /// Perform a self-test of the communication interface.
280    ///
281    /// Note:
282    /// This modifies the existing channel enables. They are restored upon exit.
283    ///
284    /// Returns:
285    /// True if the self test succeeded. False otherwise.
286    pub fn self_test(&mut self) -> Result<bool, Error> {
287        let mut data = [0];
288
289        // Get current CSR.
290        self.read(Address::CSR, &mut data)?;
291        let old_csr = data;
292
293        let mut csr = Csr::new_with_raw_value(data[0]);
294
295        // Enable all channels.
296        csr.set_channel(u4::new(0b1111));
297        self.write(Address::CSR, &[csr.raw_value()])?;
298        self.read(Address::CSR, &mut data)?;
299        if Csr::new_with_raw_value(data[0]).channel() != csr.channel() {
300            return Ok(false);
301        }
302
303        // Clear all channel enables.
304        csr.set_channel(u4::new(0b0000));
305        self.write(Address::CSR, &[csr.raw_value()])?;
306        self.read(Address::CSR, &mut data)?;
307        if Csr::new_with_raw_value(data[0]).channel() != csr.channel() {
308            return Ok(false);
309        }
310
311        // Restore the CSR.
312        self.write(Address::CSR, &old_csr)?;
313
314        Ok(true)
315    }
316
317    /// Get the current system clock frequency in Hz.
318    fn system_clock_frequency(&self) -> f32 {
319        (1u64 << 32) as f32 / self.ftw_per_hz
320    }
321
322    /// Update an output channel configuration register.
323    ///
324    /// Args:
325    /// * `channel` - The channel to configure.
326    /// * `register` - The register to update.
327    /// * `data` - The contents to write to the provided register.
328    fn write_channel(
329        &mut self,
330        channel: Channel,
331        register: Address,
332        data: &[u8],
333    ) -> Result<(), Error> {
334        // Disable all other outputs so that we can update the configuration register of only the
335        // specified channel.
336        let csr = Csr::default().with_channel(channel).with_mode(self.mode);
337        self.write(Address::CSR, &csr.raw_value().to_be_bytes())?;
338        self.write(register, data)?;
339        Ok(())
340    }
341
342    /// Read a configuration register of a specific channel.
343    ///
344    /// Args:
345    /// * `channel` - The channel to read.
346    /// * `register` - The register to read.
347    /// * `data` - A location to store the read register contents.
348    fn read_channel(
349        &mut self,
350        channel: Channel,
351        register: Address,
352        data: &mut [u8],
353    ) -> Result<(), Error> {
354        let csr = Csr::default().with_channel(channel).with_mode(self.mode);
355        self.write(Address::CSR, &csr.raw_value().to_be_bytes())?;
356        self.read(register, data)?;
357        Ok(())
358    }
359
360    /// Configure the phase of a specified channel.
361    ///
362    /// Arguments:
363    /// * `channel` - The channel to configure the frequency of.
364    /// * `phase_turns` - The desired phase offset in turns.
365    ///
366    /// Returns:
367    /// The actual programmed phase offset of the channel in turns.
368    pub fn set_phase(
369        &mut self,
370        channel: Channel,
371        phase: f32,
372    ) -> Result<f32, Error> {
373        let pow = u14::new((phase * (1 << 14) as f32) as u16 & 0x3FFF);
374        self.write_channel(
375            channel,
376            Address::CPOW0,
377            &pow.value().to_be_bytes(),
378        )?;
379        Ok(pow.value() as f32 / (1 << 14) as f32)
380    }
381
382    /// Get the current phase of a specified channel.
383    ///
384    /// Args:
385    /// * `channel` - The channel to get the phase of.
386    ///
387    /// Returns:
388    /// The phase of the channel in turns.
389    pub fn get_phase(&mut self, channel: Channel) -> Result<f32, Error> {
390        let mut pow = 0u16.to_be_bytes();
391        self.read_channel(channel, Address::CPOW0, &mut pow)?;
392        let pow = u16::from_be_bytes(pow) & 0x3FFF;
393        Ok(pow as f32 / (1 << 14) as f32)
394    }
395
396    /// Configure the amplitude of a specified channel.
397    ///
398    /// Arguments:
399    /// * `channel` - The channel to configure the frequency of.
400    /// * `amplitude` - A normalized amplitude setting [0, 1].
401    ///
402    /// Returns:
403    /// The actual normalized amplitude of the channel relative to full-scale range.
404    pub fn set_amplitude(
405        &mut self,
406        channel: Channel,
407        amplitude: f32,
408    ) -> Result<f32, Error> {
409        if !(0.0..=1.0).contains(&amplitude) {
410            return Err(Error::Bounds);
411        }
412        let asf = (amplitude * (1 << 10) as f32) as u16;
413        let acr = match u10::try_new(asf) {
414            Ok(asf) => Acr::default().with_multiplier(true).with_asf(asf),
415            Err(_) => Acr::default().with_multiplier(false),
416        };
417        self.write_channel(
418            channel,
419            Address::ACR,
420            &acr.raw_value().to_be_bytes(),
421        )?;
422        Ok(asf as f32 / (1 << 10) as f32)
423    }
424
425    /// Get the configured amplitude of a channel.
426    ///
427    /// Args:
428    /// * `channel` - The channel to get the amplitude of.
429    ///
430    /// Returns:
431    /// The normalized amplitude of the channel.
432    pub fn get_amplitude(&mut self, channel: Channel) -> Result<f32, Error> {
433        let mut acr = u24::new(0).to_be_bytes();
434        self.read_channel(channel, Address::ACR, &mut acr)?;
435        let acr = Acr::new_with_raw_value(u24::from_be_bytes(acr));
436        Ok(if acr.multiplier() {
437            1.0
438        } else {
439            acr.asf().value() as f32 / (1 << 10) as f32
440        })
441    }
442
443    /// Configure the frequency of a specified channel.
444    ///
445    /// Arguments:
446    /// * `channel` - The channel to configure the frequency of.
447    /// * `frequency` - The desired output frequency in Hz.
448    ///
449    /// Returns:
450    /// The actual programmed frequency of the channel.
451    pub fn set_frequency(
452        &mut self,
453        channel: Channel,
454        frequency: f32,
455    ) -> Result<f32, Error> {
456        if frequency < 0.0 || frequency > self.system_clock_frequency() {
457            return Err(Error::Bounds);
458        }
459        let ftw = (frequency * self.ftw_per_hz) as u32;
460        self.write_channel(channel, Address::CFTW0, &ftw.to_be_bytes())?;
461        Ok(ftw as f32 / self.ftw_per_hz)
462    }
463
464    /// Get the frequency of a channel.
465    ///
466    /// Arguments:
467    /// * `channel` - The channel to get the frequency of.
468    ///
469    /// Returns:
470    /// The frequency of the channel in Hz.
471    pub fn get_frequency(&mut self, channel: Channel) -> Result<f32, Error> {
472        let mut ftw = 0u32.to_be_bytes();
473        self.read_channel(channel, Address::CFTW0, &mut ftw)?;
474        let ftw = u32::from_be_bytes(ftw);
475        Ok(ftw as f32 / self.ftw_per_hz)
476    }
477
478    /// Finalize DDS configuration
479    ///
480    /// # Note
481    /// This is intended for when the DDS profiles will be written as a stream of data to the DDS.
482    ///
483    /// # Returns
484    /// (i, mode) where `i` is the interface to the DDS and `mode` is the frozen `Mode`.
485    pub fn freeze(self) -> (I, Mode) {
486        (self.interface, self.mode)
487    }
488}
489
490/// Represents a means of serializing a DDS profile for writing to a stream.
491pub struct ProfileSerializer {
492    mode: Mode,
493    // reorder or pad to work around https://github.com/japaric/heapless/issues/305
494    // TODO: check
495    // heapless::Vec<u8, 32>, especially its extend_from_slice() is slow
496    index: usize,
497    data: [u8; 32],
498}
499
500impl ProfileSerializer {
501    /// Construct a new serializer.
502    ///
503    /// # Args
504    /// * `mode` - The communication mode of the DDS.
505    pub fn new(mode: Mode) -> Self {
506        Self {
507            mode,
508            index: 0,
509            data: [0; 32],
510        }
511    }
512
513    /// Update a number of channels with the requested profile.
514    ///
515    /// # Args
516    /// * `channels` - A set of channels to apply the configuration to.
517    /// * `ftw` - If provided, indicates a frequency tuning word for the channels.
518    /// * `pow` - If provided, indicates a phase offset word for the channels.
519    /// * `acr` - If provided, indicates the amplitude control register for the channels. The ACR
520    ///   should be stored in the 3 LSB of the word. Note that if amplitude scaling is to be used,
521    ///   the "Amplitude multiplier enable" bit must be set.
522    #[inline]
523    pub fn push(
524        &mut self,
525        channels: Channel,
526        ftw: Option<Wrapping<i32>>,
527        pow: Option<Wrapping<u14>>, // a-i v2: i14
528        acr: Option<Acr>,
529    ) {
530        self.push_write(
531            Address::CSR,
532            &Csr::default()
533                .with_mode(self.mode)
534                .with_channel(channels)
535                .raw_value()
536                .to_be_bytes(),
537        );
538        if let Some(ftw) = ftw {
539            self.push_write(Address::CFTW0, &ftw.0.to_be_bytes());
540        }
541        if let Some(pow) = pow {
542            self.push_write(Address::CPOW0, &pow.0.value().to_be_bytes());
543        }
544        if let Some(acr) = acr {
545            self.push_write(Address::ACR, &acr.raw_value().to_be_bytes());
546        }
547    }
548
549    /// Add a register write to the serialization data.
550    #[inline]
551    fn push_write(&mut self, register: Address, value: &[u8]) {
552        let data = &mut self.data[self.index..];
553        data[0] = register as u8;
554        data[1..1 + value.len()].copy_from_slice(value);
555        self.index += 1 + value.len();
556    }
557
558    /// Get the serialized profile as a slice of 32-bit words.
559    ///
560    /// # Note
561    /// The serialized profile will be padded to the next 32-bit word boundary by adding dummy
562    /// writes to the CSR or LSRR registers.
563    ///
564    /// # Returns
565    /// A slice of `u32` words representing the serialized profile.
566    #[inline]
567    pub fn finalize(&mut self) -> &[u32] {
568        // Pad the buffer to 32-bit (4 byte) alignment by adding dummy writes to CSR and LSRR.
569        // In the case of 1 byte padding, this instead pads with 5 bytes as there is no
570        // valid single-byte write that could be used.
571        if self.index & 1 != 0 {
572            // Pad with 3 bytes
573            self.push_write(Address::LSRR, &0u16.to_be_bytes());
574        }
575        if self.index & 2 != 0 {
576            // Pad with 2 bytes
577            self.push_write(
578                Address::CSR,
579                &Csr::default()
580                    .with_mode(self.mode)
581                    .raw_value()
582                    .to_be_bytes(),
583            );
584        }
585        bytemuck::cast_slice(&self.data[..self.index])
586    }
587}