parent
507bd0dfef
commit
0c5614e38e
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["LongHairedHacker <sebastian@sebastians-site.de>"]
|
authors = ["LongHairedHacker <sebastian@sebastians-site.de>"]
|
||||||
name = "wspr-beacon"
|
name = "stm32-pll-ssb"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
@ -11,11 +11,12 @@ cortex-m-rtic = "1.1.3"
|
||||||
defmt = "0.3.2"
|
defmt = "0.3.2"
|
||||||
defmt-rtt = "0.3.2"
|
defmt-rtt = "0.3.2"
|
||||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||||
stm32f1xx-hal = { version = "0.9.0", features = ["stm32f103", "rt"] }
|
stm32f1xx-hal = { version = "0.9.0", features = ["stm32f103", "rt", "medium"] }
|
||||||
embedded-hal = {version = "0.2.3"}
|
embedded-hal = {version = "0.2.3"}
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
arrayvec = {version = "0.7.0", default-features = false}
|
arrayvec = {version = "0.7.0", default-features = false}
|
||||||
systick-monotonic = "1.0.0"
|
systick-monotonic = "1.0.0"
|
||||||
|
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
95
src/main.rs
95
src/main.rs
|
@ -16,29 +16,28 @@ use rtic::app;
|
||||||
|
|
||||||
mod si5153;
|
mod si5153;
|
||||||
|
|
||||||
#[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [TIM2])]
|
#[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [SPI3])]
|
||||||
mod app {
|
mod app {
|
||||||
|
|
||||||
use core::num::dec2flt::float;
|
|
||||||
|
|
||||||
use stm32f1xx_hal::{
|
use stm32f1xx_hal::{
|
||||||
adc,
|
adc,
|
||||||
gpio::{
|
gpio::{
|
||||||
self, gpioa, gpiob, gpioc, Alternate, Analog, Floating, Input, OpenDrain, Output,
|
self, gpioa, gpiob, gpioc, Alternate, Analog, Floating, Input, OpenDrain, Output,
|
||||||
PushPull, CRL,
|
PushPull, CRH, CRL,
|
||||||
},
|
},
|
||||||
i2c,
|
i2c,
|
||||||
i2c::BlockingI2c,
|
i2c::BlockingI2c,
|
||||||
pac::{ADC1, I2C1},
|
pac::{ADC1, I2C1, TIM4},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
serial::{self, Config, Serial},
|
serial::{self, Config, Serial},
|
||||||
stm32,
|
stm32,
|
||||||
timer::{self, Event},
|
timer::{self, Channel, Event, Tim3NoRemap, Tim4NoRemap},
|
||||||
};
|
};
|
||||||
|
|
||||||
use systick_monotonic::Systick;
|
use systick_monotonic::Systick;
|
||||||
|
|
||||||
use arrayvec::ArrayString;
|
use arrayvec::ArrayString;
|
||||||
|
use num_traits::float::Float;
|
||||||
|
|
||||||
use crate::si5153;
|
use crate::si5153;
|
||||||
|
|
||||||
|
@ -50,8 +49,15 @@ mod app {
|
||||||
),
|
),
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
type AudioPwm = timer::PwmHz<
|
||||||
|
TIM4,
|
||||||
|
Tim4NoRemap,
|
||||||
|
timer::Ch<2>,
|
||||||
|
gpio::Pin<Alternate<gpio::PushPull>, CRH, 'B', 8>,
|
||||||
|
>;
|
||||||
|
|
||||||
#[monotonic(binds = SysTick, default = true)]
|
#[monotonic(binds = SysTick, default = true)]
|
||||||
type MonoTimer = Systick<1_000_000>;
|
type MonoTimer = Systick<1_000>;
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {}
|
struct Shared {}
|
||||||
|
@ -61,8 +67,13 @@ mod app {
|
||||||
pll: si5153::Si5153<AppI2C1>,
|
pll: si5153::Si5153<AppI2C1>,
|
||||||
i2c: AppI2C1,
|
i2c: AppI2C1,
|
||||||
board_led: gpioc::PC13<Output<PushPull>>,
|
board_led: gpioc::PC13<Output<PushPull>>,
|
||||||
|
rx_en: gpioa::PA7<Output<PushPull>>,
|
||||||
adc1: adc::Adc<ADC1>,
|
adc1: adc::Adc<ADC1>,
|
||||||
mic_in: gpio::Pin<Analog, CRL, 'A', 4>,
|
mic_in: gpio::Pin<Analog, CRL, 'A', 4>,
|
||||||
|
i_in: gpio::Pin<Analog, CRL, 'A', 1>,
|
||||||
|
q_in: gpio::Pin<Analog, CRL, 'A', 0>,
|
||||||
|
phase: f32,
|
||||||
|
audio_pwm: AudioPwm,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
|
@ -111,16 +122,50 @@ mod app {
|
||||||
let mut pll = si5153::Si5153::new(&i2c);
|
let mut pll = si5153::Si5153::new(&i2c);
|
||||||
pll.init(&mut i2c, 25000000, 800000000, 800000000);
|
pll.init(&mut i2c, 25000000, 800000000, 800000000);
|
||||||
pll.set_ms_source(&mut i2c, si5153::Multisynth::MS0, si5153::PLL::A);
|
pll.set_ms_source(&mut i2c, si5153::Multisynth::MS0, si5153::PLL::A);
|
||||||
|
pll.set_ms_source(&mut i2c, si5153::Multisynth::MS1, si5153::PLL::A);
|
||||||
|
pll.set_ms_source(&mut i2c, si5153::Multisynth::MS2, si5153::PLL::B);
|
||||||
|
|
||||||
|
pll.set_ms_freq(&mut i2c, si5153::Multisynth::MS0, 8_000_000);
|
||||||
|
pll.set_ms_phase(&mut i2c, si5153::Multisynth::MS0, 100);
|
||||||
|
pll.enable_ms_output(&mut i2c, si5153::Multisynth::MS0);
|
||||||
|
|
||||||
|
pll.set_ms_freq(&mut i2c, si5153::Multisynth::MS1, 8_000_000);
|
||||||
|
pll.set_ms_phase(&mut i2c, si5153::Multisynth::MS1, 0);
|
||||||
|
pll.enable_ms_output(&mut i2c, si5153::Multisynth::MS1);
|
||||||
|
|
||||||
|
pll.pll_reset(&mut i2c);
|
||||||
|
|
||||||
let adc1 = adc::Adc::adc1(cx.device.ADC1, clocks);
|
let adc1 = adc::Adc::adc1(cx.device.ADC1, clocks);
|
||||||
let mic_in = gpioa.pa4.into_analog(&mut gpioa.crl);
|
let mic_in = gpioa.pa4.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
let mut pwm =
|
let i_in = gpioa.pa1.into_analog(&mut gpioa.crl);
|
||||||
cx.device
|
let q_in = gpioa.pa0.into_analog(&mut gpioa.crl);
|
||||||
.TIM2
|
|
||||||
.pwm_hz::<Tim3NoRemap, _, _>(pins, &mut afio.mapr, 4800.Hz(), &clocks);
|
|
||||||
|
|
||||||
transmit::spawn().unwrap();
|
let audio_out = gpiob.pb8.into_alternate_push_pull(&mut gpiob.crh);
|
||||||
|
let mut audio_pwm = cx.device.TIM4.pwm_hz::<Tim4NoRemap, _, _>(
|
||||||
|
audio_out,
|
||||||
|
&mut afio.mapr,
|
||||||
|
4800.Hz(),
|
||||||
|
&clocks,
|
||||||
|
);
|
||||||
|
audio_pwm.enable(Channel::C3);
|
||||||
|
audio_pwm.set_duty(Channel::C3, 0u16);
|
||||||
|
|
||||||
|
let mut rx_en = gpioa.pa7.into_push_pull_output(&mut gpioa.crl);
|
||||||
|
rx_en.set_high();
|
||||||
|
|
||||||
|
let mut bias_pin = gpioa.pa6.into_alternate_push_pull(&mut gpioa.crl);
|
||||||
|
let mut bias_pwm = cx.device.TIM3.pwm_hz::<Tim3NoRemap, _, _>(
|
||||||
|
bias_pin,
|
||||||
|
&mut afio.mapr,
|
||||||
|
4800.Hz(),
|
||||||
|
&clocks,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut timer = timer::Timer2::new(cx.device.TIM2, &clocks).counter_hz();
|
||||||
|
timer.start(4800.Hz()).unwrap();
|
||||||
|
// Generate an interrupt when the timer expires
|
||||||
|
timer.listen(Event::Update);
|
||||||
|
|
||||||
(
|
(
|
||||||
Shared {},
|
Shared {},
|
||||||
|
@ -128,21 +173,35 @@ mod app {
|
||||||
i2c,
|
i2c,
|
||||||
pll,
|
pll,
|
||||||
board_led,
|
board_led,
|
||||||
|
rx_en,
|
||||||
adc1,
|
adc1,
|
||||||
mic_in,
|
mic_in,
|
||||||
|
i_in,
|
||||||
|
q_in,
|
||||||
|
phase: 0.0,
|
||||||
|
audio_pwm,
|
||||||
},
|
},
|
||||||
init::Monotonics(mono),
|
init::Monotonics(mono),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(local=[pll, i2c, adc1, mic_in])]
|
#[task(binds=TIM2, local=[pll, i2c, adc1, mic_in, i_in, q_in, audio_pwm, phase, board_led])]
|
||||||
fn transmit(mut ctx: transmit::Context) {
|
fn transmit(mut ctx: transmit::Context) {
|
||||||
let mut adc = ctx.local.adc1;
|
*ctx.local.phase += core::f32::consts::PI * 2.0 * 1000.0 / 4800.0;
|
||||||
let mut mic_in = ctx.local.mic_in;
|
if *ctx.local.phase > 2.0 * core::f32::consts::PI {
|
||||||
|
*ctx.local.phase -= core::f32::consts::PI;
|
||||||
|
ctx.local.board_led.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
let data: u16 = adc.read(&mut *mic_in).unwrap();
|
//defmt::debug!("Phase: {}", ctx.local.phase);
|
||||||
let sample = (data as f32 / u16::MAX as f32);
|
|
||||||
|
|
||||||
transmit::spawn_after(208.micros().into()).unwrap();
|
let max_duty = if ctx.local.audio_pwm.get_max_duty() != 0 {
|
||||||
|
ctx.local.audio_pwm.get_max_duty() as f32
|
||||||
|
} else {
|
||||||
|
2.0.powi(16)
|
||||||
|
};
|
||||||
|
|
||||||
|
let sample = (ctx.local.phase.sin() + 0.5) * max_duty;
|
||||||
|
ctx.local.audio_pwm.set_duty(Channel::C3, sample as u16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub enum Multisynth {
|
||||||
|
|
||||||
const MS_BASE_ADDR: [u8; 3] = [42, 50, 58];
|
const MS_BASE_ADDR: [u8; 3] = [42, 50, 58];
|
||||||
const MS_CTRL_ADDR: [u8; 3] = [16, 17, 18];
|
const MS_CTRL_ADDR: [u8; 3] = [16, 17, 18];
|
||||||
|
const CLK_PHOFF_ADDR: [u8; 3] = [165, 166, 167];
|
||||||
|
|
||||||
impl Multisynth {
|
impl Multisynth {
|
||||||
fn base_address(&self) -> u8 {
|
fn base_address(&self) -> u8 {
|
||||||
|
@ -40,6 +41,11 @@ impl Multisynth {
|
||||||
fn ctrl_address(&self) -> u8 {
|
fn ctrl_address(&self) -> u8 {
|
||||||
return MS_CTRL_ADDR[*self as usize];
|
return MS_CTRL_ADDR[*self as usize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn phoff_address(&self) -> u8 {
|
||||||
|
defmt::debug!("Adress: {}", CLK_PHOFF_ADDR[*self as usize]);
|
||||||
|
return CLK_PHOFF_ADDR[*self as usize];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PllParams {
|
pub struct PllParams {
|
||||||
|
@ -77,11 +83,10 @@ where
|
||||||
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs); // Disable all outputs
|
self.write_byte_reg(i2c, CLK_ENABLE_CONTROL, self.outputs); // Disable all outputs
|
||||||
self.write_byte_reg(i2c, XTAL_LOAD_CAP, 0xD2); //crystal load capacitor = 10pF
|
self.write_byte_reg(i2c, XTAL_LOAD_CAP, 0xD2); //crystal load capacitor = 10pF
|
||||||
|
|
||||||
self.write_byte_reg(i2c, PLL_RESET, 0xA0); // Reset both PLLs
|
|
||||||
|
|
||||||
for ms in [Multisynth::MS0, Multisynth::MS1, Multisynth::MS2].iter() {
|
for ms in [Multisynth::MS0, Multisynth::MS1, Multisynth::MS2].iter() {
|
||||||
self.ms_srcs[*ms as usize] = PLL::A;
|
self.ms_srcs[*ms as usize] = PLL::A;
|
||||||
self.write_byte_reg(i2c, ms.ctrl_address(), 0x0F); // MSi as Source, PLLA to MSi, 8 mA output
|
self.write_byte_reg(i2c, ms.ctrl_address(), 0x0F); // MSi as Source, PLLA to MSi, 8 mA output
|
||||||
|
self.write_byte_reg(i2c, ms.phoff_address(), 0); // Phase offset to 0.
|
||||||
}
|
}
|
||||||
|
|
||||||
for pll in [PLL::A, PLL::B].iter() {
|
for pll in [PLL::A, PLL::B].iter() {
|
||||||
|
@ -101,6 +106,8 @@ where
|
||||||
|
|
||||||
self.write_params(i2c, pll.base_address(), ¶ms)
|
self.write_params(i2c, pll.base_address(), ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.write_byte_reg(i2c, PLL_RESET, 0xA0); // Reset both PLLs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_ms_output(&mut self, i2c: &mut I2C, synth: Multisynth) {
|
pub fn enable_ms_output(&mut self, i2c: &mut I2C, synth: Multisynth) {
|
||||||
|
@ -143,6 +150,14 @@ where
|
||||||
self.write_params(i2c, synth.base_address(), ¶ms)
|
self.write_params(i2c, synth.base_address(), ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_ms_phase(&mut self, i2c: &mut I2C, synth: Multisynth, phase: u8) {
|
||||||
|
self.write_byte_reg(i2c, synth.phoff_address(), phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pll_reset(&mut self, i2c: &mut I2C) {
|
||||||
|
self.write_byte_reg(i2c, PLL_RESET, 0xA0);
|
||||||
|
}
|
||||||
|
|
||||||
fn write_byte_reg(&self, i2c: &mut I2C, reg_addr: u8, data: u8) {
|
fn write_byte_reg(&self, i2c: &mut I2C, reg_addr: u8, data: u8) {
|
||||||
let res = i2c.write(I2C_ADDR, &[reg_addr, data]);
|
let res = i2c.write(I2C_ADDR, &[reg_addr, data]);
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
|
|
Loading…
Reference in New Issue