humpback-dds/src/dds.rs

654 lines
19 KiB
Rust
Raw Normal View History

2020-08-12 15:31:06 +08:00
use embedded_hal::blocking::spi::Transfer;
use crate::Error;
2020-08-26 11:04:08 +08:00
use core::mem::size_of;
2020-09-18 12:25:39 +08:00
use core::convert::TryInto;
use arrayvec::ArrayVec;
2020-08-12 15:31:06 +08:00
2020-08-26 13:18:50 +08:00
/*
* Bitmask for all configurations (Order: CFR3, CFR2, CFR1)
*/
2020-08-26 11:04:08 +08:00
construct_bitmask!(DDSCFRMask; u32;
// CFR1 bitmasks
2020-08-13 17:17:21 +08:00
LSB_FIRST, 0, 1,
SDIO_IN_ONLY, 1, 1,
EXT_POWER_DOWN_CTRL, 3, 1,
AUX_DAC_POWER_DOWN, 4, 1,
REFCLK_IN_POWER_DOWN, 5, 1,
DAC_POWER_DOWN, 6, 1,
DIGITAL_POWER_DOWN, 7, 1,
SEL_AUTO_OSK, 8, 1,
OSK_ENABLE, 9, 1,
LOAD_ARR_IO_UPDATE, 10, 1,
CLEAR_PHASE_ACU, 11, 1,
CLEAR_DIGITAL_RAMP_ACU, 12, 1,
AUTOCLEAR_PHASE_ACU, 13, 1,
AUTOCLEAR_DIGITAL_RAMP_ACU, 14, 1,
LOAD_LRR_IO_UPDATE, 15, 1,
SEL_DDS_SIN_OUT, 16, 1,
PROFILE_CTRL, 17, 4,
INV_SINC_FILTER_ENABLE, 22, 1,
MANUAL_OSK_EXT_CTRL, 23, 1,
RAM_PLAYBACK_DST, 29, 2,
2020-08-26 11:04:08 +08:00
RAM_ENABLE, 31, 1,
2020-08-13 17:17:21 +08:00
2020-08-26 11:04:08 +08:00
// CFR2 bitmasks
FM_GAIN, 0 +32, 4,
PARALLEL_DATA_PORT_ENABLE, 4 +32, 1,
SYNC_TIM_VALIDATION_DISABLE, 5 +32, 1,
DATA_ASSEM_HOLD_LAST_VALUE, 6 +32, 1,
MATCHED_LATENCY_ENABLE, 7 +32, 1,
TXENABLE_INV, 9 +32, 1,
PDCLK_INV, 10 +32, 1,
PDCLK_ENABLE, 11 +32, 1,
IO_UPDATE_RATE_CTRL, 14 +32, 2,
READ_EFFECTIVE_FTW, 16 +32, 1,
DIGITAL_RAMP_NO_DWELL_LOW, 17 +32, 1,
DIGITAL_RAMP_NO_DWELL_HIGH, 18 +32, 1,
DIGITAL_RAMP_ENABLE, 19 +32, 1,
DIGITAL_RAMP_DEST, 20 +32, 2,
SYNC_CLK_ENABLE, 22 +32, 1,
INT_IO_UPDATE_ACTIVE, 23 +32, 1,
EN_AMP_SCALE_SINGLE_TONE_PRO, 24 +32, 1,
2020-08-14 14:14:14 +08:00
2020-08-26 11:04:08 +08:00
// CFR3 bitmasks
N, 1 +64, 7,
PLL_ENABLE, 8 +64, 1,
PFD_RESET, 10 +64, 1,
REFCLK_IN_DIV_RESETB, 14 +64, 1,
REFCLK_IN_DIV_BYPASS, 15 +64, 1,
I_CP, 19 +64, 3,
VCO_SEL, 24 +64, 3,
DRV0, 28 +64, 2
2020-08-17 12:15:11 +08:00
);
2020-08-14 14:14:14 +08:00
2020-08-17 11:45:42 +08:00
const WRITE_MASK :u8 = 0x00;
const READ_MASK :u8 = 0x80;
2020-09-18 12:25:39 +08:00
#[derive(Clone)]
pub enum RAMDestination {
Frequency = 0,
Phase = 1,
Amplitude = 2,
Polar = 3,
}
#[derive(Clone)]
pub enum RAMOperationMode {
DirectSwitch = 0,
RampUp = 1,
BidirectionalRamp = 2,
ContinuousBidirectionalRamp = 3,
ContinuousRecirculate = 4,
}
2020-08-12 15:31:06 +08:00
pub struct DDS<SPI> {
spi: SPI,
2020-09-15 14:03:59 +08:00
f_ref_clk: f64,
f_sys_clk: f64,
2020-08-12 15:31:06 +08:00
}
impl<SPI, E> DDS<SPI>
where
2020-08-17 11:45:42 +08:00
SPI: Transfer<u8, Error = E>
2020-08-12 15:31:06 +08:00
{
2020-09-15 14:03:59 +08:00
pub fn new(spi: SPI, f_ref_clk: f64) -> Self {
2020-08-12 15:31:06 +08:00
DDS {
2020-08-26 16:49:37 +08:00
spi,
f_ref_clk,
2020-08-26 17:39:33 +08:00
f_sys_clk: f_ref_clk,
2020-08-12 15:31:06 +08:00
}
}
}
2020-08-17 12:15:11 +08:00
impl<SPI, E> Transfer<u8> for DDS<SPI>
2020-08-12 15:31:06 +08:00
where
SPI: Transfer<u8, Error = E>
{
type Error = Error<E>;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
self.spi.transfer(words).map_err(Error::SPI)
}
}
2020-08-17 11:45:42 +08:00
2020-08-26 16:49:37 +08:00
impl<SPI, E> DDS<SPI>
where
2020-08-26 13:18:50 +08:00
SPI: Transfer<u8, Error = E>
2020-08-26 16:49:37 +08:00
{
/*
* Implement init: Set SDIO to be input only, using LSB first
*/
2020-08-26 13:18:50 +08:00
pub fn init(&mut self) -> Result<(), Error<E>> {
match self.write_register(0x00, &mut [
0x00, 0x00, 0x00, 0x02
]) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
/*
2020-08-26 16:49:37 +08:00
* Implement clock control
*/
pub fn enable_divided_ref_clk(&mut self) -> Result<(), Error<E>> {
self.set_configurations(&mut [
// Disable PLL
(DDSCFRMask::PLL_ENABLE, 0),
// Take ref_clk source from divider
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0),
// Ensure divider is not reset
(DDSCFRMask::REFCLK_IN_DIV_RESETB, 1),
2020-08-26 17:39:33 +08:00
])?;
2020-09-15 14:03:59 +08:00
self.f_sys_clk = self.f_ref_clk / 2.0;
2020-08-26 17:39:33 +08:00
Ok(())
2020-08-26 16:49:37 +08:00
}
pub fn enable_normal_ref_clk(&mut self) -> Result<(), Error<E>> {
self.set_configurations(&mut [
// Disable PLL
(DDSCFRMask::PLL_ENABLE, 0),
// Take ref_clk source from divider bypass
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 1),
// Reset does not matter
(DDSCFRMask::REFCLK_IN_DIV_RESETB, 1),
2020-08-26 17:39:33 +08:00
])?;
self.f_sys_clk = self.f_ref_clk;
Ok(())
2020-08-26 16:49:37 +08:00
}
2020-09-15 14:03:59 +08:00
pub fn enable_pll(&mut self, f_sys_clk: f64) -> Result<(), Error<E>> {
2020-08-26 16:49:37 +08:00
// Get a divider
2020-09-15 14:03:59 +08:00
let divider = (f_sys_clk / self.f_ref_clk) as u64;
2020-08-26 16:49:37 +08:00
// Reject extreme divider values. However, accept no frequency division
2020-09-18 12:25:39 +08:00
if (divider > 127 || divider < 12) && divider != 1 {
2020-08-31 16:48:21 +08:00
// panic!("Invalid divider value for PLL!");
return Err(Error::DDSCLKError);
2020-08-26 16:49:37 +08:00
}
2020-08-31 16:48:21 +08:00
let vco = self.get_VCO_no(f_sys_clk, divider as u8)?;
2020-08-26 16:49:37 +08:00
self.set_configurations(&mut [
// Enable PLL, set divider (valid or not) and VCO
(DDSCFRMask::PLL_ENABLE, 1),
(DDSCFRMask::N, divider as u32),
2020-08-31 16:48:21 +08:00
(DDSCFRMask::VCO_SEL, vco.into()),
2020-08-26 16:49:37 +08:00
// Reset PLL lock before re-enabling it
(DDSCFRMask::PFD_RESET, 1),
])?;
self.set_configurations(&mut [
(DDSCFRMask::PFD_RESET, 0),
2020-08-26 17:39:33 +08:00
])?;
2020-09-15 14:03:59 +08:00
self.f_sys_clk = self.f_ref_clk * (divider as f64);
2020-08-26 17:39:33 +08:00
Ok(())
2020-08-26 16:49:37 +08:00
}
// Change external clock source (ref_clk)
2020-09-15 14:03:59 +08:00
pub fn set_ref_clk_frequency(&mut self, f_ref_clk: f64) -> Result<(), Error<E>> {
// Override old reference clock frequency (ref_clk)
2020-08-26 16:49:37 +08:00
self.f_ref_clk = f_ref_clk;
2020-09-15 14:03:59 +08:00
// Calculate the new system clock frequency, examine the clock tree
2020-08-31 16:48:21 +08:00
let mut configuration_queries = [
// Acquire PLL status
(DDSCFRMask::PLL_ENABLE, 0),
// Acquire N-divider, to adjust VCO if necessary
(DDSCFRMask::N, 0),
// Acquire REF_CLK divider bypass
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0)
];
self.get_configurations(&mut configuration_queries)?;
if configuration_queries[0].1 == 1 {
// Recalculate sys_clk
2020-09-15 14:03:59 +08:00
let divider :f64 = configuration_queries[1].1.into();
2020-08-31 16:48:21 +08:00
let f_sys_clk = self.f_ref_clk * divider;
// Adjust VCO
match self.get_VCO_no(f_sys_clk, divider as u8) {
Ok(vco_no) => {
self.set_configurations(&mut [
// Update VCO selection
(DDSCFRMask::VCO_SEL, vco_no.into()),
// Reset PLL lock before re-enabling it
(DDSCFRMask::PFD_RESET, 1),
])?;
self.set_configurations(&mut [
(DDSCFRMask::PFD_RESET, 0),
])?;
// Update f_sys_clk from recalculation
self.f_sys_clk = f_sys_clk;
Ok(())
},
Err(_) => {
// Forcibly turn off PLL, enable default clk tree (divide by 2)
self.enable_divided_ref_clk()
}
}
}
else if configuration_queries[2].1 == 0 {
2020-09-15 14:03:59 +08:00
self.f_sys_clk = self.f_ref_clk / 2.0;
2020-08-31 16:48:21 +08:00
Ok(())
}
else {
self.f_sys_clk = self.f_ref_clk;
Ok(())
}
}
2020-09-16 12:05:45 +08:00
// Change sys_clk frequency, method to be determined
pub fn set_sys_clk_frequency(&mut self, f_sys_clk: f64) -> Result<(), Error<E>> {
// If f_sys_clk is exactly the same as f_ref_clk, then invoke enable_normal_ref_clk
if f_sys_clk == self.f_ref_clk {
self.enable_normal_ref_clk()
}
// Otherwise, if the requested sys_clk is half of ref_clk, invoke enable_divided_ref_clk
else if f_sys_clk == (self.f_ref_clk / 2.0) {
self.enable_divided_ref_clk()
}
// Finally, try enabling PLL
else {
self.enable_pll(f_sys_clk)
}
}
2020-09-14 17:33:50 +08:00
#[allow(non_snake_case)]
2020-09-15 14:03:59 +08:00
fn get_VCO_no(&mut self, f_sys_clk: f64, divider: u8) -> Result<u8, Error<E>> {
2020-08-31 16:48:21 +08:00
// Select a VCO
if divider == 1 {
Ok(6) // Bypass PLL if no frequency division needed
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 1_150_000_000.0 {
2020-08-31 16:48:21 +08:00
Err(Error::DDSCLKError)
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 820_000_000.0 {
2020-08-31 16:48:21 +08:00
Ok(5)
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 700_000_000.0 {
2020-08-31 16:48:21 +08:00
Ok(4)
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 600_000_000.0 {
2020-08-31 16:48:21 +08:00
Ok(3)
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 500_000_000.0 {
2020-08-31 16:48:21 +08:00
Ok(2)
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 420_000_000.0 {
2020-08-31 16:48:21 +08:00
Ok(1)
2020-09-15 14:03:59 +08:00
} else if f_sys_clk > 370_000_000.0 {
2020-08-31 16:48:21 +08:00
Ok(0)
} else {
Ok(7) // Bypass PLL if f_sys_clk is too low
}
2020-08-26 16:49:37 +08:00
}
/*
2020-08-26 17:39:33 +08:00
* Implement configurations registers I/O through bitmasks
2020-08-26 16:49:37 +08:00
*
* Get all (cfr1, cfr2, cfr3) contents
2020-08-26 13:18:50 +08:00
*/
fn get_all_configurations(&mut self) -> Result<[u32; 3], Error<E>> {
let mut cfr_reg = [0; 12];
self.read_register(0x00, &mut cfr_reg[0..4])?;
self.read_register(0x01, &mut cfr_reg[4..8])?;
self.read_register(0x02, &mut cfr_reg[8..12])?;
Ok([
(cfr_reg[0] as u32) << 24 | (cfr_reg[1] as u32) << 16 | (cfr_reg[2] as u32) << 8 | (cfr_reg[3] as u32),
(cfr_reg[4] as u32) << 24 | (cfr_reg[5] as u32) << 16 | (cfr_reg[6] as u32) << 8 | (cfr_reg[7] as u32),
(cfr_reg[8] as u32) << 24 | (cfr_reg[9] as u32) << 16 | (cfr_reg[10] as u32) << 8 | (cfr_reg[11] as u32)
])
}
/*
* Get a set of configurations using DDSCFRMask
*/
pub fn get_configurations<'w>(&mut self, mask_pairs: &'w mut[(DDSCFRMask, u32)]) -> Result<&'w [(DDSCFRMask, u32)], Error<E>> {
let data_array = self.get_all_configurations()?;
for index in 0..mask_pairs.len() {
mask_pairs[index].1 = match mask_pairs[index].0.get_shift() {
0..=31 => mask_pairs[index].0.get_filtered_content(data_array[0]),
32..=63 => mask_pairs[index].0.get_filtered_content(data_array[1]),
64..=95 => mask_pairs[index].0.get_filtered_content(data_array[2]),
_ => panic!("Invalid DDSCFRMask!"),
}
}
Ok(mask_pairs)
}
/*
* Write (cfr1, cfr2, cfr3) contents
*/
fn set_all_configurations(&mut self, data_array: [u32; 3]) -> Result<(), Error<E>> {
for register in 0x00..=0x02 {
self.write_register(register, &mut [
((data_array[register as usize] >> 24) & 0xFF) as u8,
((data_array[register as usize] >> 16) & 0xFF) as u8,
((data_array[register as usize] >> 8 ) & 0xFF) as u8,
((data_array[register as usize] >> 0 ) & 0xFF) as u8
])?;
}
Ok(())
}
/*
* Set a set of configurations using DDSCFRMask
*/
pub fn set_configurations(&mut self, mask_pairs: &mut[(DDSCFRMask, u32)]) -> Result<(), Error<E>> {
let mut data_array = self.get_all_configurations()?;
for index in 0..mask_pairs.len() {
2020-08-26 16:49:37 +08:00
// Reject any attempt to rewrite LSB_FIRST and SBIO_INPUT_ONLY
2020-08-26 13:18:50 +08:00
if mask_pairs[index].0 == DDSCFRMask::LSB_FIRST || mask_pairs[index].0 == DDSCFRMask::SDIO_IN_ONLY {
continue;
}
match mask_pairs[index].0.get_shift() {
0..=31 => mask_pairs[index].0.set_data_by_arg(&mut data_array[0], mask_pairs[index].1),
32..=63 => mask_pairs[index].0.set_data_by_arg(&mut data_array[1], mask_pairs[index].1),
64..=95 => mask_pairs[index].0.set_data_by_arg(&mut data_array[2], mask_pairs[index].1),
_ => panic!("Invalid DDSCFRMask!"),
};
}
self.set_all_configurations(data_array.clone())
}
2020-08-26 17:39:33 +08:00
/*
2020-09-16 14:23:01 +08:00
* Setup a complete single tone profile
2020-09-16 12:05:45 +08:00
* Phase: Expressed in positive degree, i.e. [0.0, 360.0)
* Frequency: Must be non-negative
2020-08-26 17:39:33 +08:00
* Amplitude: In a scale from 0 to 1, taking float
*/
2020-09-15 14:03:59 +08:00
pub fn set_single_tone_profile(&mut self, profile: u8, f_out: f64, phase_offset: f64, amp_scale_factor: f64) -> Result<(), Error<E>> {
2020-08-26 17:39:33 +08:00
assert!(profile < 8);
2020-09-16 14:23:01 +08:00
assert!(f_out >= 0.0);
2020-08-26 17:39:33 +08:00
assert!(phase_offset >= 0.0 && phase_offset < 360.0);
assert!(amp_scale_factor >=0.0 && amp_scale_factor <= 1.0);
let resolutions :[u64; 3] = [1 << 32, 1 << 16, 1 << 14];
2020-09-15 14:03:59 +08:00
let ftw = ((resolutions[0] as f64) * f_out / self.f_sys_clk) as u32;
2020-08-26 17:39:33 +08:00
let pow = ((resolutions[1] as f64) * phase_offset / 360.0) as u16;
2020-08-27 11:15:42 +08:00
let asf :u16 = if amp_scale_factor == 1.0 {
0x3FFF
} else {
((resolutions[2] as f64) * amp_scale_factor) as u16
};
2020-09-18 12:25:39 +08:00
2020-08-26 17:39:33 +08:00
// Setup configuration registers before writing single tone register
2020-09-18 12:25:39 +08:00
self.enable_single_tone_configuration()?;
2020-08-26 17:39:33 +08:00
// Transfer single tone profile data
self.write_register(0x0E + profile, &mut [
((asf >> 8 ) & 0xFF) as u8,
((asf >> 0 ) & 0xFF) as u8,
((pow >> 8 ) & 0xFF) as u8,
((pow >> 0 ) & 0xFF) as u8,
((ftw >> 24) & 0xFF) as u8,
((ftw >> 16) & 0xFF) as u8,
((ftw >> 8 ) & 0xFF) as u8,
((ftw >> 0 ) & 0xFF) as u8,
])
2020-08-31 17:43:15 +08:00
}
2020-09-16 14:23:01 +08:00
/*
* Set frequency of a single tone profile
* Frequency: Must be non-negative
* Keep other field unchanged in the register
*/
pub fn set_single_tone_profile_frequency(&mut self, profile: u8, f_out: f64) -> Result<(), Error<E>> {
// Setup configuration registers before writing single tone register
2020-09-18 12:25:39 +08:00
self.enable_single_tone_configuration()?;
2020-09-16 14:23:01 +08:00
// Calculate frequency tuning work (FTW)
let f_res: u64 = 1 << 32;
let ftw = ((f_res as f64) * f_out / self.f_sys_clk) as u32;
// Read existing amplitude/phase data
let mut register: [u8; 8] = [0; 8];
self.read_register(0x0E + profile, &mut register)?;
// Overwrite FTW
register[4] = ((ftw >> 24) & 0xFF) as u8;
register[5] = ((ftw >> 16) & 0xFF) as u8;
register[6] = ((ftw >> 8) & 0xFF) as u8;
register[7] = ((ftw >> 0) & 0xFF) as u8;
// Update FTW by writing back the register
self.write_register(0x0E + profile, &mut register)
}
/*
* Set phase offset of a single tone profile
* Phase: Expressed in positive degree, i.e. [0.0, 360.0)
* Keep other field unchanged in the register
*/
pub fn set_single_tone_profile_phase(&mut self, profile: u8, phase_offset: f64) -> Result<(), Error<E>> {
// Setup configuration registers before writing single tone register
2020-09-18 12:25:39 +08:00
self.enable_single_tone_configuration()?;
2020-09-16 14:23:01 +08:00
// Calculate phase offset work (POW)
let phase_res: u64 = 1 << 16;
let pow = ((phase_res as f64) * phase_offset / 360.0) as u16;
// Read existing amplitude/frequency data
let mut register: [u8; 8] = [0; 8];
self.read_register(0x0E + profile, &mut register)?;
// Overwrite POW
register[2] = ((pow >> 8) & 0xFF) as u8;
register[3] = ((pow >> 0) & 0xFF) as u8;
// Update POW by writing back the register
self.write_register(0x0E + profile, &mut register)
}
/*
* Set amplitude offset of a single tone profile
* Amplitude: In a scale from 0 to 1, taking float
* Keep other field unchanged in the register
*/
pub fn set_single_tone_profile_amplitude(&mut self, profile: u8, amp_scale_factor: f64) -> Result<(), Error<E>> {
// Setup configuration registers before writing single tone register
2020-09-18 12:25:39 +08:00
self.enable_single_tone_configuration()?;
2020-09-16 14:23:01 +08:00
// Calculate amplitude_scale_factor (ASF)
let amp_res: u64 = 1 << 14;
let asf :u16 = if amp_scale_factor == 1.0 {
0x3FFF
} else {
((amp_res as f64) * amp_scale_factor) as u16
};
// Read existing frequency/phase data
let mut register: [u8; 8] = [0; 8];
self.read_register(0x0E + profile, &mut register)?;
// Overwrite POW
register[0] = ((asf >> 8) & 0xFF) as u8;
register[1] = ((asf >> 0) & 0xFF) as u8;
// Update POW by writing back the register
self.write_register(0x0E + profile, &mut register)
}
2020-09-18 12:25:39 +08:00
// Helper function to switch into single tone mode
// Need to setup configuration registers before writing single tone register
fn enable_single_tone_configuration(&mut self) -> Result<(), Error<E>> {
self.set_configurations(&mut [
(DDSCFRMask::RAM_ENABLE, 0),
(DDSCFRMask::DIGITAL_RAMP_ENABLE, 0),
(DDSCFRMask::OSK_ENABLE, 0),
(DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0),
])?;
self.set_configurations(&mut [
(DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1),
])
}
// Helper function to switch into RAM mode
// Need to setup configuration registers before writing into RAM profile register
fn enable_ram_configuration(&mut self, ram_dst: RAMDestination) -> Result<(), Error<E>> {
self.set_configurations(&mut [
(DDSCFRMask::RAM_ENABLE, 1),
(DDSCFRMask::RAM_PLAYBACK_DST, ram_dst as u32),
])
}
// Helper function to switch out of RAM mode
// Need to setup configuration registers before writing into RAM profile register
fn disable_ram_configuration(&mut self) -> Result<(), Error<E>> {
self.set_configurations(&mut [
(DDSCFRMask::RAM_ENABLE, 0),
])
}
/*
* Configure a RAM mode profile
*
*/
pub fn set_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16,
ram_dst: RAMDestination, no_dwell_high: bool, zero_crossing: bool,
op_mode: RAMOperationMode, playback_rate: f64, data: &[u8]
) -> Result<(), Error<E>> {
// Check the legality of this setup
assert!(profile < 7);
assert!(end_addr >= start_addr);
assert!(end_addr < 1024);
assert_eq!(data.len() as u16, (end_addr - start_addr + 1) * 4);
// Calculate address step rate, and check legality
let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64;
if (step_rate == 0 || step_rate > 0xFFFF) {
return Err(Error::ParameterError);
}
// Before setting up RAM, disable RAM_ENABLE
self.enable_ram_configuration(ram_dst.clone())?;
// Write a RAM profile, but include all data in RAM
self.write_register(0x0E + profile, &mut [
0x00,
((step_rate >> 8) & 0xFF).try_into().unwrap(),
((step_rate >> 0) & 0xFF).try_into().unwrap(),
((end_addr >> 2) & 0xFF).try_into().unwrap(),
((end_addr & 0x3) << 6).try_into().unwrap(),
((start_addr >> 2) & 0xFF).try_into().unwrap(),
((start_addr & 0x3) << 6).try_into().unwrap(),
((no_dwell_high as u8) << 5) | ((zero_crossing as u8) << 3) | (op_mode as u8)
])?;
// Temporarily disable RAM mode while accessing into RAM
self.disable_ram_configuration();
self.write_ram(data)?;
// Properly configure start_addr and end_addr
self.enable_ram_configuration(ram_dst)
}
// Helper function to write data in RAM
// Need address range for data size check
fn write_ram(&mut self, data: &[u8]) -> Result<(), Error<E>> {
let mut vec: ArrayVec<[u8; 8192]> = ArrayVec::new();
vec.try_push(0x16)
.map_err(|_| Error::DDSRAMError)?;
vec.try_extend_from_slice(data)
.map_err(|_| Error::DDSRAMError)?;
let mut data_slice = vec.as_mut_slice();
self.spi.transfer(&mut data_slice)
.map(|_| ())
.map_err(Error::SPI)
}
2020-08-31 17:43:15 +08:00
/*
* Test method for DDS.
* Return the number of test failed.
*/
pub fn test(&mut self) -> Result<u32, Error<E>> {
// Test configuration register by getting SDIO_IN_ONLY and LSB_FIRST.
let mut error_count = 0;
let mut config_checks = [
2020-09-01 10:21:55 +08:00
(DDSCFRMask::SDIO_IN_ONLY, 1),
2020-08-31 17:43:15 +08:00
(DDSCFRMask::LSB_FIRST, 0)
];
self.get_configurations(&mut config_checks)?;
if config_checks[0].1 == 0 {
error_count += 1;
}
if config_checks[1].1 == 1 {
error_count += 1;
}
Ok(error_count)
}
2020-08-26 13:18:50 +08:00
}
2020-08-26 11:04:08 +08:00
2020-09-18 12:25:39 +08:00
// Strong check for bytes passed to a register
2020-08-17 11:45:42 +08:00
macro_rules! impl_register_io {
($($reg_addr: expr, $reg_byte_size: expr),+) => {
impl<SPI, E> DDS<SPI>
where
SPI: Transfer<u8, Error = E>
{
pub fn write_register(&mut self, addr: u8, bytes: &mut[u8]) -> Result<(), Error<E>> {
match addr {
$(
$reg_addr => {
assert_eq!(bytes.len(), $reg_byte_size);
2020-08-17 12:15:11 +08:00
let mut arr: [u8; $reg_byte_size + 1] = [0; ($reg_byte_size + 1)];
arr[0] = addr | WRITE_MASK;
2020-08-17 11:45:42 +08:00
for i in 0..$reg_byte_size {
arr[i+1] = bytes[i];
}
2020-09-14 17:33:50 +08:00
self.spi.transfer(&mut arr)
.map(|_| ())
.map_err(Error::SPI)
2020-08-17 12:15:11 +08:00
},
)*
_ => panic!("Bad address for DDS writing.")
}
}
pub fn read_register<'w>(&mut self, addr: u8, bytes: &'w mut[u8]) -> Result<&'w [u8], Error<E>> {
match addr {
$(
$reg_addr => {
assert_eq!(bytes.len(), $reg_byte_size);
let mut arr: [u8; $reg_byte_size + 1] = [0; ($reg_byte_size + 1)];
arr[0] = addr | READ_MASK;
match self.spi.transfer(&mut arr).map_err(Error::SPI) {
Ok(ret) => {
assert_eq!(ret.len(), $reg_byte_size + 1);
for i in 0..$reg_byte_size {
bytes[i] = ret[i+1];
}
Ok(bytes)
},
Err(e) => Err(e),
}
2020-08-17 11:45:42 +08:00
},
)*
2020-08-17 12:15:11 +08:00
_ => panic!("Bad address for DDS reading.")
2020-08-17 11:45:42 +08:00
}
}
}
}
}
impl_register_io!(
0x00, 4,
0x01, 4,
0x02, 4,
0x03, 4,
0x04, 4,
0x07, 4,
0x08, 2,
0x09, 4,
0x0A, 4,
0x0B, 8,
0x0C, 8,
0x0D, 4,
0x0E, 8,
0x0F, 8,
0x10, 8,
0x11, 8,
0x12, 8,
0x13, 8,
0x14, 8,
0x15, 8
2020-08-17 11:45:42 +08:00
);