#![no_std] use bit_field::BitField; use embedded_hal::{ digital::v2::OutputPin, blocking::delay::DelayMs, }; /// A device driver for the AD9959 direct digital synthesis (DDS) chip. /// /// This chip provides four independently controllable digital-to-analog output sinusoids with /// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they /// are derived off a common system clock. /// /// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz. /// /// The chip supports a number of serial interfaces to improve data throughput, including normal, /// dual, and quad SPI configurations. pub struct Ad9959 { interface: INTERFACE, delay: DELAY, reference_clock_frequency: u32, system_clock_multiplier: u8, io_update: UPDATE, } pub trait Interface { type Error; fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>; fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>; fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; } #[derive(Copy, Clone, PartialEq)] pub enum Mode { SingleBitTwoWire = 0b00, SingleBitThreeWire = 0b01, TwoBitSerial = 0b10, FourBitSerial = 0b11, } /// The configuration registers within the AD9959 DDS device. The values of each register are /// equivalent to the address. pub enum Register { CSR = 0x00, FR1 = 0x01, FR2 = 0x02, CFR = 0x03, CFTW0 = 0x04, CPOW0 = 0x05, ACR = 0x06, LSRR = 0x07, RDW = 0x08, FDW = 0x09, CW1 = 0x0a, CW2 = 0x0b, CW3 = 0x0c, CW4 = 0x0d, CW5 = 0x0e, CW6 = 0x0f, CW7 = 0x10, CW8 = 0x11, CW9 = 0x12, CW10 = 0x13, CW11 = 0x14, CW12 = 0x15, CW13 = 0x16, CW14 = 0x17, CW15 = 0x18, } /// Specifies an output channel of the AD9959 DDS chip. pub enum Channel { One = 0, Two = 1, Three = 2, Four = 3, } /// Possible errors generated by the AD9959 driver. #[derive(Debug)] pub enum Error { Interface(InterfaceE), Bounds, Pin, Frequency, } impl From for Error { fn from(interface_error: InterfaceE) -> Self { Error::Interface(interface_error) } } impl Ad9959 where INTERFACE: Interface, DELAY: DelayMs, UPDATE: OutputPin, { pub fn new(interface: INTERFACE, reset_pin: &mut RST, io_update: UPDATE, delay: DELAY, desired_mode: Mode, clock_frequency: u32, multiplier: u8) -> Result> where RST: OutputPin, { let mut ad9959 = Ad9959 { interface: interface, io_update: io_update, delay: delay, reference_clock_frequency: clock_frequency, system_clock_multiplier: 1, }; ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; // Reset the AD9959 reset_pin.set_high().or_else(|_| Err(Error::Pin))?; // Delay for a clock cycle to allow the device to reset. ad9959.delay.delay_ms((1000.0 / clock_frequency as f32) as u8); reset_pin.set_low().or_else(|_| Err(Error::Pin))?; ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; // Program the interface configuration in the AD9959. Default to all channels enabled. let mut csr: [u8; 1] = [0xF0]; csr[0].set_bits(1..3, desired_mode as u8); ad9959.interface.write(0, &csr)?; // Configure the interface to the desired mode. ad9959.interface.configure_mode(Mode::FourBitSerial)?; // Latch the configuration registers to make them active. ad9959.latch_configuration()?; ad9959.interface.configure_mode(desired_mode)?; // Set the clock frequency to configure the device as necessary. ad9959.configure_system_clock(clock_frequency, multiplier)?; Ok(ad9959) } fn latch_configuration(&mut self) -> Result<(), Error> { self.io_update.set_high().or_else(|_| Err(Error::Pin))?; // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one // full SYNC_CLK pulse to register. For safety, we latch for 5 here. self.delay.delay_ms((5000.0 / self.system_clock_frequency()) as u8); self.io_update.set_low().or_else(|_| Err(Error::Pin))?; Ok(()) } /// Configure the internal system clock of the chip. /// /// Arguments: /// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core. /// * `prescaler` - The frequency prescaler of the system clock. Must be 1 or 4-20. /// /// Returns: /// The actual frequency configured for the internal system clock. pub fn configure_system_clock(&mut self, reference_clock_frequency: u32, prescaler: u8) -> Result> { self.reference_clock_frequency = reference_clock_frequency; if prescaler != 1 && (prescaler > 20 || prescaler < 4) { return Err(Error::Bounds); } let frequency = prescaler as f64 * self.reference_clock_frequency as f64; if frequency > 500_000_000.0f64 { return Err(Error::Frequency); } // TODO: Update / disable any enabled channels? let mut fr1: [u8; 3] = [0, 0, 0]; self.interface.read(Register::FR1 as u8, &mut fr1)?; fr1[0].set_bits(2..=6, prescaler); let vco_range = frequency > 255e6; fr1[0].set_bit(7, vco_range); self.interface.write(Register::FR1 as u8, &fr1)?; self.system_clock_multiplier = prescaler; Ok(self.system_clock_frequency()) } /// Perform a self-test of the communication interface. /// /// Note: /// This modifies the existing channel enables. They are restored upon exit. /// /// Returns: /// True if the self test succeeded. False otherwise. pub fn self_test(&mut self) -> Result> { let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; let old_csr = csr[0]; // Enable all channels. csr[0].set_bits(4..8, 0xF); self.interface.write(Register::CSR as u8, &csr)?; // Read back the enable. csr[0] = 0; self.interface.read(Register::CSR as u8, &mut csr)?; if csr[0].get_bits(4..8) != 0xF { return Ok(false); } // Clear all channel enables. csr[0].set_bits(4..8, 0x0); self.interface.write(Register::CSR as u8, &csr)?; // Read back the enable. csr[0] = 0xFF; self.interface.read(Register::CSR as u8, &mut csr)?; if csr[0].get_bits(4..8) != 0 { return Ok(false); } // Restore the CSR. csr[0] = old_csr; self.interface.write(Register::CSR as u8, &csr)?; Ok(true) } fn system_clock_frequency(&self) -> f64 { self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 } /// Enable an output channel. pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; csr[0].set_bit(channel as usize + 4, true); self.interface.write(Register::CSR as u8, &csr)?; Ok(()) } /// Disable an output channel. pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; csr[0].set_bit(channel as usize + 4, false); self.interface.write(Register::CSR as u8, &csr)?; Ok(()) } fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; let mut new_csr = csr; new_csr[0].set_bits(4..8, 0); new_csr[0].set_bit(4 + channel as usize, true); self.interface.write(Register::CSR as u8, &new_csr)?; self.interface.write(register as u8, &data)?; // Latch the configuration and restore the previous CSR. Note that the re-enable of the // channel happens immediately, so the CSR update does not need to be latched. self.latch_configuration()?; self.interface.write(Register::CSR as u8, &csr)?; Ok(()) } /// Configure the phase of a specified channel. /// /// Arguments: /// * `channel` - The channel to configure the frequency of. /// * `phase_turns` - The desired phase offset in normalized turns. /// /// Returns: /// The actual programmed phase offset of the channel in degrees. pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result> { if phase_turns > 1.0 || phase_turns < 0.0 { return Err(Error::Bounds); } let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16; self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0) } /// Configure the amplitude of a specified channel. /// /// Arguments: /// * `channel` - The channel to configure the frequency of. /// * `amplitude` - A normalized amplitude setting [0, 1]. /// /// Returns: /// The actual normalized amplitude of the channel relative to full-scale range. pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result> { if amplitude < 0.0 || amplitude > 1.0 { return Err(Error::Bounds); } let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16; let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]]; // Enable the amplitude multiplier for the channel if required. The amplitude control has // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever // full-scale is used. acr[1].set_bit(4, amplitude_control >= 1u16.wrapping_shl(10)); self.modify_channel(channel, Register::ACR, &acr)?; Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32) } /// Configure the frequency of a specified channel. /// /// Arguments: /// * `channel` - The channel to configure the frequency of. /// * `frequency` - The desired output frequency in Hz. /// /// Returns: /// The actual programmed frequency of the channel. pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result> { if frequency < 0.0 || frequency > self.system_clock_frequency() { return Err(Error::Bounds); } // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the // frequency tuning word and f_s is the system clock rate. let tuning_word: u32 = ((frequency as f64 / self.system_clock_frequency()) * 1u64.wrapping_shl(32) as f64) as u32; self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?; Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) } }