forked from M-Labs/humpback-dds
dds: add clock control
This commit is contained in:
parent
38b1c7528c
commit
1d3ced0d16
|
@ -1,5 +1,4 @@
|
||||||
use embedded_hal::blocking::spi::Transfer;
|
use embedded_hal::blocking::spi::Transfer;
|
||||||
use cortex_m::asm::nop;
|
|
||||||
use cortex_m_semihosting::hprintln;
|
use cortex_m_semihosting::hprintln;
|
||||||
use core::assert;
|
use core::assert;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use core::mem::size_of;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Macro builder for bit masks
|
* Macro builder for bit masks
|
||||||
* $collection: Name for the bit mask collection
|
* $collection: Name for the bit mask collection
|
||||||
|
|
|
@ -78,7 +78,7 @@ where
|
||||||
* Return selected configuration field
|
* Return selected configuration field
|
||||||
*/
|
*/
|
||||||
pub fn get_configuration(&mut self, config_type: CFGMask) -> u8 {
|
pub fn get_configuration(&mut self, config_type: CFGMask) -> u8 {
|
||||||
(config_type.get_filtered_content(self.data) as u8)
|
config_type.get_filtered_content(self.data) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
105
src/dds.rs
105
src/dds.rs
|
@ -65,15 +65,17 @@ const READ_MASK :u8 = 0x80;
|
||||||
|
|
||||||
pub struct DDS<SPI> {
|
pub struct DDS<SPI> {
|
||||||
spi: SPI,
|
spi: SPI,
|
||||||
|
f_ref_clk: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI, E> DDS<SPI>
|
impl<SPI, E> DDS<SPI>
|
||||||
where
|
where
|
||||||
SPI: Transfer<u8, Error = E>
|
SPI: Transfer<u8, Error = E>
|
||||||
{
|
{
|
||||||
pub fn new(spi: SPI) -> Self {
|
pub fn new(spi: SPI, f_ref_clk: u64) -> Self {
|
||||||
DDS {
|
DDS {
|
||||||
spi
|
spi,
|
||||||
|
f_ref_clk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,13 +91,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Implement init
|
impl<SPI, E> DDS<SPI>
|
||||||
*/
|
where
|
||||||
impl<SPI, E> DDS<SPI>
|
|
||||||
where
|
|
||||||
SPI: Transfer<u8, Error = E>
|
SPI: Transfer<u8, Error = E>
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Implement init: Set SDIO to be input only, using LSB first
|
||||||
|
*/
|
||||||
pub fn init(&mut self) -> Result<(), Error<E>> {
|
pub fn init(&mut self) -> Result<(), Error<E>> {
|
||||||
match self.write_register(0x00, &mut [
|
match self.write_register(0x00, &mut [
|
||||||
0x00, 0x00, 0x00, 0x02
|
0x00, 0x00, 0x00, 0x02
|
||||||
|
@ -104,17 +107,81 @@ where
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Impleement configurations registers I/O through bitmasks
|
|
||||||
*/
|
|
||||||
impl<SPI, E> DDS<SPI>
|
|
||||||
where
|
|
||||||
SPI: Transfer<u8, Error = E>
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* Return (cfr1, cfr2, cfr3) contents
|
* 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),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_pll(&mut self, f_sys_clk: u64) -> Result<(), Error<E>> {
|
||||||
|
// Get a divider
|
||||||
|
let divider = f_sys_clk / self.f_ref_clk;
|
||||||
|
// Reject extreme divider values. However, accept no frequency division
|
||||||
|
if ((divider > 127 || divider < 12) && divider != 1) {
|
||||||
|
panic!("Invalid divider value for PLL!");
|
||||||
|
}
|
||||||
|
// Select a VCO
|
||||||
|
let vco = if divider == 1 {
|
||||||
|
6 // Bypass PLL if no frequency division needed
|
||||||
|
} else if f_sys_clk > 1_150_000_000 {
|
||||||
|
panic!("Invalid divider value for PLL!")
|
||||||
|
} else if f_sys_clk > 820_000_000 {
|
||||||
|
5
|
||||||
|
} else if f_sys_clk > 700_000_000 {
|
||||||
|
4
|
||||||
|
} else if f_sys_clk > 600_000_000 {
|
||||||
|
3
|
||||||
|
} else if f_sys_clk > 500_000_000 {
|
||||||
|
2
|
||||||
|
} else if f_sys_clk > 420_000_000 {
|
||||||
|
1
|
||||||
|
} else if f_sys_clk > 370_000_000 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
7 // Bypass PLL if f_sys_clk is too low
|
||||||
|
};
|
||||||
|
self.set_configurations(&mut [
|
||||||
|
// Enable PLL, set divider (valid or not) and VCO
|
||||||
|
(DDSCFRMask::PLL_ENABLE, 1),
|
||||||
|
(DDSCFRMask::N, divider as u32),
|
||||||
|
(DDSCFRMask::VCO_SEL, vco),
|
||||||
|
// Reset PLL lock before re-enabling it
|
||||||
|
(DDSCFRMask::PFD_RESET, 1),
|
||||||
|
])?;
|
||||||
|
self.set_configurations(&mut [
|
||||||
|
(DDSCFRMask::PFD_RESET, 0),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change external clock source (ref_clk)
|
||||||
|
pub fn set_ref_clk_frequency(&mut self, f_ref_clk: u64) {
|
||||||
|
self.f_ref_clk = f_ref_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Impleement configurations registers I/O through bitmasks
|
||||||
|
*
|
||||||
|
* Get all (cfr1, cfr2, cfr3) contents
|
||||||
*/
|
*/
|
||||||
fn get_all_configurations(&mut self) -> Result<[u32; 3], Error<E>> {
|
fn get_all_configurations(&mut self) -> Result<[u32; 3], Error<E>> {
|
||||||
let mut cfr_reg = [0; 12];
|
let mut cfr_reg = [0; 12];
|
||||||
|
@ -164,9 +231,8 @@ where
|
||||||
*/
|
*/
|
||||||
pub fn set_configurations(&mut self, mask_pairs: &mut[(DDSCFRMask, u32)]) -> Result<(), Error<E>> {
|
pub fn set_configurations(&mut self, mask_pairs: &mut[(DDSCFRMask, u32)]) -> Result<(), Error<E>> {
|
||||||
let mut data_array = self.get_all_configurations()?;
|
let mut data_array = self.get_all_configurations()?;
|
||||||
hprintln!("Initial array {:#X?}", data_array).unwrap();
|
|
||||||
for index in 0..mask_pairs.len() {
|
for index in 0..mask_pairs.len() {
|
||||||
// Reject any attempt to write LSB_FIRST and SBIO_INPUT_ONLY
|
// Reject any attempt to rewrite LSB_FIRST and SBIO_INPUT_ONLY
|
||||||
if mask_pairs[index].0 == DDSCFRMask::LSB_FIRST || mask_pairs[index].0 == DDSCFRMask::SDIO_IN_ONLY {
|
if mask_pairs[index].0 == DDSCFRMask::LSB_FIRST || mask_pairs[index].0 == DDSCFRMask::SDIO_IN_ONLY {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -177,7 +243,6 @@ where
|
||||||
_ => panic!("Invalid DDSCFRMask!"),
|
_ => panic!("Invalid DDSCFRMask!"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
hprintln!("Modified array {:#X?}", data_array).unwrap();
|
|
||||||
self.set_all_configurations(data_array.clone())
|
self.set_all_configurations(data_array.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,8 @@ use embedded_hal::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use core::cell;
|
use core::cell;
|
||||||
use core::mem::size_of;
|
|
||||||
|
|
||||||
use cortex_m;
|
use cortex_m;
|
||||||
use cortex_m::asm::nop;
|
|
||||||
use cortex_m_semihosting::hprintln;
|
use cortex_m_semihosting::hprintln;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
40
src/main.rs
40
src/main.rs
|
@ -10,13 +10,9 @@ use stm32h7xx_hal::hal::digital::v2::{
|
||||||
use stm32h7xx_hal::{pac, prelude::*, spi};
|
use stm32h7xx_hal::{pac, prelude::*, spi};
|
||||||
|
|
||||||
use cortex_m;
|
use cortex_m;
|
||||||
use cortex_m::asm::nop;
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use cortex_m_semihosting::hprintln;
|
use cortex_m_semihosting::hprintln;
|
||||||
|
|
||||||
use core::ptr;
|
|
||||||
use nb::block;
|
|
||||||
|
|
||||||
use firmware;
|
use firmware;
|
||||||
use firmware::{
|
use firmware::{
|
||||||
CPLD,
|
CPLD,
|
||||||
|
@ -102,7 +98,7 @@ fn main() -> ! {
|
||||||
|
|
||||||
let mut config = ConfigRegister::new(parts.spi1);
|
let mut config = ConfigRegister::new(parts.spi1);
|
||||||
let mut att = Attenuator::new(parts.spi2);
|
let mut att = Attenuator::new(parts.spi2);
|
||||||
let mut dds0 = DDS::new(parts.spi4);
|
let mut dds0 = DDS::new(parts.spi4, 25_000_000);
|
||||||
|
|
||||||
// Reset all DDS, set CLK_SEL to 0
|
// Reset all DDS, set CLK_SEL to 0
|
||||||
config.set_configurations(&mut [
|
config.set_configurations(&mut [
|
||||||
|
@ -115,21 +111,9 @@ fn main() -> ! {
|
||||||
(CFGMask::IO_RST, 0),
|
(CFGMask::IO_RST, 0),
|
||||||
(CFGMask::RST, 0),
|
(CFGMask::RST, 0),
|
||||||
(CFGMask::RF_SW, 13),
|
(CFGMask::RF_SW, 13),
|
||||||
(CFGMask::DIV, 2)
|
(CFGMask::DIV, 3)
|
||||||
]).unwrap();
|
]).unwrap();
|
||||||
|
|
||||||
// dds0.write_register(0x00, &mut[
|
|
||||||
// 0x00, 0x00, 0x00, 0x02
|
|
||||||
// ]).unwrap();
|
|
||||||
|
|
||||||
// dds0.write_register(0x01, &mut[
|
|
||||||
// 0x01, 0x01, 0x00, 0x20
|
|
||||||
// ]).unwrap();
|
|
||||||
|
|
||||||
// dds0.write_register(0x02, &mut[
|
|
||||||
// 0x05, 0x38, 0xC5, 0x28
|
|
||||||
// ]).unwrap();
|
|
||||||
|
|
||||||
dds0.init().unwrap();
|
dds0.init().unwrap();
|
||||||
|
|
||||||
dds0.set_configurations(&mut [
|
dds0.set_configurations(&mut [
|
||||||
|
@ -137,30 +121,17 @@ fn main() -> ! {
|
||||||
(DDSCFRMask::READ_EFFECTIVE_FTW, 1),
|
(DDSCFRMask::READ_EFFECTIVE_FTW, 1),
|
||||||
(DDSCFRMask::DIGITAL_RAMP_ENABLE, 0),
|
(DDSCFRMask::DIGITAL_RAMP_ENABLE, 0),
|
||||||
(DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1),
|
(DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1),
|
||||||
(DDSCFRMask::N, 0x14),
|
|
||||||
(DDSCFRMask::PLL_ENABLE, 1),
|
|
||||||
(DDSCFRMask::PFD_RESET, 1),
|
|
||||||
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 1),
|
|
||||||
(DDSCFRMask::I_CP, 7),
|
|
||||||
(DDSCFRMask::VCO_SEL, 5),
|
|
||||||
(DDSCFRMask::DRV0, 0),
|
|
||||||
]).unwrap();
|
]).unwrap();
|
||||||
|
|
||||||
hprintln!("{:#X?}", dds0.read_register(0x02, &mut[
|
dds0.enable_pll(1_150_000_000).unwrap();
|
||||||
0x00, 0x00, 0x00, 0x00
|
|
||||||
]).unwrap()).unwrap();
|
|
||||||
|
|
||||||
dds0.set_configurations(&mut [
|
|
||||||
(DDSCFRMask::PFD_RESET, 0),
|
|
||||||
]).unwrap();
|
|
||||||
|
|
||||||
hprintln!("{:#X?}", dds0.read_register(0x02, &mut[
|
hprintln!("{:#X?}", dds0.read_register(0x02, &mut[
|
||||||
0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00
|
||||||
]).unwrap()).unwrap();
|
]).unwrap()).unwrap();
|
||||||
|
|
||||||
// Calculate FTW
|
// Calculate FTW
|
||||||
let f_out = 8_008_135;
|
let f_out = 10_000_000;
|
||||||
let f_sclk = 100_000_000 / 2 * 20;
|
let f_sclk = 1_150_000_000;
|
||||||
let resolution :u64 = 1 << 32;
|
let resolution :u64 = 1 << 32;
|
||||||
let ftw = (resolution * f_out / f_sclk) as u32;
|
let ftw = (resolution * f_out / f_sclk) as u32;
|
||||||
|
|
||||||
|
@ -177,6 +148,7 @@ fn main() -> ! {
|
||||||
profile[7] = ((ftw >> 0 ) & 0xFF) as u8;
|
profile[7] = ((ftw >> 0 ) & 0xFF) as u8;
|
||||||
|
|
||||||
dds0.write_register(0x0E, &mut profile).unwrap();
|
dds0.write_register(0x0E, &mut profile).unwrap();
|
||||||
|
hprintln!("{:#X?}", dds0.read_register(0x0E, &mut profile).unwrap()).unwrap();
|
||||||
|
|
||||||
// Attenuator
|
// Attenuator
|
||||||
att.set_attenuation([
|
att.set_attenuation([
|
||||||
|
|
Loading…
Reference in New Issue