ad9959/
lib.rs

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