2020-06-09 00:20:10 +08:00
|
|
|
#![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, DELAY, UPDATE> {
|
|
|
|
interface: INTERFACE,
|
|
|
|
delay: DELAY,
|
2020-06-10 18:40:44 +08:00
|
|
|
reference_clock_frequency: f32,
|
2020-06-09 00:20:10 +08:00
|
|
|
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<InterfaceE> {
|
|
|
|
Interface(InterfaceE),
|
2020-06-10 18:40:44 +08:00
|
|
|
Check,
|
2020-06-09 00:20:10 +08:00
|
|
|
Bounds,
|
|
|
|
Pin,
|
|
|
|
Frequency,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <InterfaceE> From<InterfaceE> for Error<InterfaceE> {
|
|
|
|
fn from(interface_error: InterfaceE) -> Self {
|
|
|
|
Error::Interface(interface_error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <PinE, InterfaceE, INTERFACE, DELAY, UPDATE> Ad9959<INTERFACE, DELAY, UPDATE>
|
|
|
|
where
|
|
|
|
INTERFACE: Interface<Error = InterfaceE>,
|
|
|
|
DELAY: DelayMs<u8>,
|
|
|
|
UPDATE: OutputPin<Error = PinE>,
|
|
|
|
|
|
|
|
{
|
|
|
|
pub fn new<RST>(interface: INTERFACE,
|
|
|
|
reset_pin: &mut RST,
|
|
|
|
io_update: UPDATE,
|
|
|
|
delay: DELAY,
|
|
|
|
desired_mode: Mode,
|
2020-06-10 18:40:44 +08:00
|
|
|
clock_frequency: f32,
|
2020-06-09 00:20:10 +08:00
|
|
|
multiplier: u8) -> Result<Self, Error<InterfaceE>>
|
|
|
|
where
|
|
|
|
RST: OutputPin,
|
|
|
|
{
|
|
|
|
let mut ad9959 = Ad9959 {
|
|
|
|
interface: interface,
|
|
|
|
io_update: io_update,
|
|
|
|
delay: delay,
|
|
|
|
reference_clock_frequency: clock_frequency,
|
|
|
|
system_clock_multiplier: 1,
|
|
|
|
};
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// 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))?;
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// Program the interface configuration in the AD9959. Default to all channels enabled.
|
|
|
|
let mut csr: [u8; 1] = [0xF0];
|
2020-06-10 18:40:44 +08:00
|
|
|
csr[0].set_bits(1..3, desired_mode as u8);
|
|
|
|
ad9959.interface.write(Register::CSR as u8, &csr)?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// 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)?;
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
// Read back the CSR to ensure it specifies the mode correctly.
|
|
|
|
let mut updated_csr: [u8; 1] = [0];
|
|
|
|
ad9959.interface.read(Register::CSR as u8, &mut updated_csr)?;
|
|
|
|
if updated_csr[0] != csr[0] {
|
|
|
|
return Err(Error::Check);
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:20:10 +08:00
|
|
|
// 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<InterfaceE>> {
|
|
|
|
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.
|
2020-06-10 18:40:44 +08:00
|
|
|
/// * `multiplier` - The frequency multiplier of the system clock. Must be 1 or 4-20.
|
2020-06-09 00:20:10 +08:00
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// The actual frequency configured for the internal system clock.
|
|
|
|
pub fn configure_system_clock(&mut self,
|
2020-06-10 18:40:44 +08:00
|
|
|
reference_clock_frequency: f32,
|
|
|
|
multiplier: u8) -> Result<f64, Error<InterfaceE>>
|
2020-06-09 00:20:10 +08:00
|
|
|
{
|
|
|
|
self.reference_clock_frequency = reference_clock_frequency;
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
if multiplier != 1 && (multiplier > 20 || multiplier < 4) {
|
2020-06-09 00:20:10 +08:00
|
|
|
return Err(Error::Bounds);
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
let frequency = multiplier as f64 * self.reference_clock_frequency as f64;
|
2020-06-09 00:20:10 +08:00
|
|
|
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)?;
|
2020-06-10 18:40:44 +08:00
|
|
|
fr1[0].set_bits(2..=6, multiplier);
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
let vco_range = frequency > 255e6;
|
|
|
|
fr1[0].set_bit(7, vco_range);
|
|
|
|
|
|
|
|
self.interface.write(Register::FR1 as u8, &fr1)?;
|
2020-06-10 18:40:44 +08:00
|
|
|
self.system_clock_multiplier = multiplier;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
Ok(self.system_clock_frequency())
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
pub fn get_reference_clock_frequency(&self) -> f32 {
|
|
|
|
self.reference_clock_frequency
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_reference_clock_multiplier(&mut self) -> Result<u8, Error<InterfaceE>> {
|
|
|
|
let mut fr1: [u8; 3] = [0, 0, 0];
|
|
|
|
self.interface.read(Register::FR1 as u8, &mut fr1)?;
|
|
|
|
|
|
|
|
Ok(fr1[0].get_bits(2..=6) as u8)
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:20:10 +08:00
|
|
|
/// 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<bool, Error<InterfaceE>> {
|
|
|
|
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<InterfaceE>> {
|
|
|
|
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<InterfaceE>> {
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
pub fn is_enabled(&mut self, channel: Channel) -> Result<bool, Error<InterfaceE>> {
|
|
|
|
let mut csr: [u8; 1] = [0; 1];
|
|
|
|
self.interface.read(Register::CSR as u8, &mut csr)?;
|
|
|
|
|
|
|
|
Ok(csr[0].get_bit(channel as usize + 4))
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:20:10 +08:00
|
|
|
fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error<InterfaceE>> {
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error<InterfaceE>> {
|
|
|
|
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.read(register as u8, &mut data)?;
|
|
|
|
|
|
|
|
// 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.interface.write(Register::CSR as u8, &csr)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:20:10 +08:00
|
|
|
/// Configure the phase of a specified channel.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// * `channel` - The channel to configure the frequency of.
|
2020-06-10 18:40:44 +08:00
|
|
|
/// * `phase_turns` - The desired phase offset in turns.
|
2020-06-09 00:20:10 +08:00
|
|
|
///
|
|
|
|
/// Returns:
|
2020-06-10 18:40:44 +08:00
|
|
|
/// The actual programmed phase offset of the channel in turns.
|
2020-06-09 00:20:10 +08:00
|
|
|
pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result<f32, Error<InterfaceE>> {
|
2020-06-10 18:40:44 +08:00
|
|
|
let phase_offset: u16 = (phase_turns * (1 << 14) as f32) as u16 & 0x3FFFu16;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?;
|
2020-06-10 18:40:44 +08:00
|
|
|
|
|
|
|
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_phase(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
|
|
|
let mut phase_offset: [u8; 2] = [0; 2];
|
|
|
|
self.read_channel(channel, Register::CPOW0, &mut phase_offset)?;
|
|
|
|
|
|
|
|
let phase_offset = u16::from_be_bytes(phase_offset) & 0x3FFFu16;
|
|
|
|
|
|
|
|
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
2020-06-09 00:20:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<f32, Error<InterfaceE>> {
|
|
|
|
if amplitude < 0.0 || amplitude > 1.0 {
|
|
|
|
return Err(Error::Bounds);
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16;
|
|
|
|
|
|
|
|
let mut acr: [u8; 3] = [0; 3];
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// 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.
|
2020-06-10 18:40:44 +08:00
|
|
|
if amplitude_control < (1 << 10) {
|
|
|
|
|
|
|
|
let masked_control = amplitude_control & 0x3FF;
|
|
|
|
acr[1] = masked_control.to_be_bytes()[0];
|
|
|
|
acr[2] = masked_control.to_be_bytes()[1];
|
|
|
|
|
|
|
|
// Enable the amplitude multiplier
|
|
|
|
acr[1].set_bit(4, true);
|
|
|
|
}
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
self.modify_channel(channel, Register::ACR, &acr)?;
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_amplitude(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
|
|
|
let mut acr: [u8; 3] = [0; 3];
|
|
|
|
self.read_channel(channel, Register::ACR, &mut acr)?;
|
|
|
|
|
|
|
|
if acr[1].get_bit(4) {
|
|
|
|
let amplitude_control: u16 = (((acr[1] as u16) << 8) | (acr[2] as u16)) & 0x3FF;
|
|
|
|
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
|
|
|
} else {
|
|
|
|
Ok(1.0)
|
|
|
|
}
|
2020-06-09 00:20:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<f64, Error<InterfaceE>> {
|
|
|
|
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())
|
|
|
|
}
|
2020-06-10 18:40:44 +08:00
|
|
|
|
|
|
|
pub fn get_frequency(&mut self, channel: Channel) -> Result<f64, Error<InterfaceE>> {
|
|
|
|
// Read the frequency tuning word for the channel.
|
|
|
|
let mut tuning_word: [u8; 4] = [0; 4];
|
|
|
|
self.read_channel(channel, Register::CFTW0, &mut tuning_word)?;
|
|
|
|
let tuning_word = u32::from_be_bytes(tuning_word);
|
|
|
|
|
|
|
|
// Convert the tuning word into a frequency.
|
|
|
|
Ok(tuning_word as f64 * self.system_clock_frequency() / ((1u64 << 32)) as f64)
|
|
|
|
}
|
2020-06-09 00:20:10 +08:00
|
|
|
}
|