From 4c506aadc3f709d5bcc424630fa46ea50a840dfd Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 30 Jan 2021 10:54:00 +0100 Subject: Implement CW with tone injection --- sw/picardy/Cargo.lock | 41 ++++++--- sw/picardy/Cargo.toml | 6 -- sw/picardy/src/main.rs | 238 ++++++++++++++++++++++++++++++++---------------- sw/picardy/src/state.rs | 30 +++--- sw/picardy/src/ui.rs | 28 +++--- 5 files changed, 220 insertions(+), 123 deletions(-) (limited to 'sw') diff --git a/sw/picardy/Cargo.lock b/sw/picardy/Cargo.lock index bc9fcba..485ecc2 100644 --- a/sw/picardy/Cargo.lock +++ b/sw/picardy/Cargo.lock @@ -59,13 +59,26 @@ dependencies = [ [[package]] name = "cortex-m" -version = "0.6.4" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cdafeafba636c00c467ded7f1587210725a1adfab0c24028a7844b87738263" +checksum = "9075300b07c6a56263b9b582c214d0ff037b00d45ec9fde1cc711490c56f1bb9" dependencies = [ "aligned", "bare-metal", "bitfield", + "cortex-m 0.7.1", + "volatile-register", +] + +[[package]] +name = "cortex-m" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b756a8bffc56025de45218a48ff9b801180440c0ee49a722b32d49dcebc771" +dependencies = [ + "bare-metal", + "bitfield", + "embedded-hal", "volatile-register", ] @@ -96,7 +109,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bffa6c1454368a6aa4811ae60964c38e6996d397ff8095a8b9211b1c1f749bc" dependencies = [ - "cortex-m", + "cortex-m 0.7.1", ] [[package]] @@ -174,7 +187,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d55dedd501dfd02514646e0af4d7016ce36bc12ae177ef52056989966a1eec" dependencies = [ - "cortex-m", + "cortex-m 0.7.1", "cortex-m-semihosting", ] @@ -183,7 +196,7 @@ name = "picardy" version = "0.1.0" dependencies = [ "arrayvec", - "cortex-m", + "cortex-m 0.6.7", "cortex-m-rt", "cortex-m-semihosting", "embedded-hal", @@ -206,9 +219,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2", ] @@ -249,7 +262,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f42e140835229dea9c0b2498d2c88afe52ed2bd42a7cc6068c95adb5134b541" dependencies = [ - "cortex-m", + "cortex-m 0.6.7", "embedded-hal", ] @@ -274,7 +287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "849b1e8d9bcfd792c9d9178cf86165d299a661c26e35d9322ae9382d3f3fe460" dependencies = [ "bare-metal", - "cortex-m", + "cortex-m 0.6.7", "cortex-m-rt", "vcell", ] @@ -286,7 +299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf679de34580d2f8806d9a6384c110b6df002404e1ff024cf8d567c91df4d4b2" dependencies = [ "cast", - "cortex-m", + "cortex-m 0.6.7", "cortex-m-rt", "embedded-dma", "embedded-hal", @@ -297,9 +310,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.54" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" dependencies = [ "proc-macro2", "quote", @@ -320,9 +333,9 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vcell" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" [[package]] name = "version_check" diff --git a/sw/picardy/Cargo.toml b/sw/picardy/Cargo.toml index e2192f6..e773d57 100644 --- a/sw/picardy/Cargo.toml +++ b/sw/picardy/Cargo.toml @@ -5,12 +5,6 @@ authors = ["Matthias P. Braendli "] edition = "2018" license = "MIT" -[features] -default = [] - -# Enable CW transmit -cw = [] - [dependencies] arrayvec = { version = "0.5", default-features = false, features = [] } # alloc-cortex-m = "0.3" # requires nightly diff --git a/sw/picardy/src/main.rs b/sw/picardy/src/main.rs index 6d1d803..ffe5a47 100644 --- a/sw/picardy/src/main.rs +++ b/sw/picardy/src/main.rs @@ -43,7 +43,6 @@ use stm32f1xx_hal::{ }; use embedded_hal::digital::v2::OutputPin; -#[cfg(feature = "cw")] use embedded_hal::digital::v2::InputPin; use hd44780_driver::{Cursor, CursorBlink, Display, DisplayMode, HD44780}; @@ -55,8 +54,23 @@ pub mod log10f; use state::*; -const TICKS_PER_SECOND : u32 = 50; +const TICKS_PER_SECOND : u32 = 100; + +struct SharedWithISR { + state : State, + last_sequence_state_change : u32, + cw_ptt_timestamp : u32, + ui : ui::UI, + cw_pwm: cw::CWPWM, + cw_paddle_tip: gpio::gpiob::PB8>, + seq0n: gpio::gpiob::PB3>, + seq1_pa: gpio::gpiob::PB4>, + seq2_switch: gpio::gpiob::PB5>, + mute_spkr : gpio::gpioa::PA2>, + mute_micn : gpio::gpioa::PA1>, +} +static mut SHARED: MaybeUninit = MaybeUninit::uninit(); static mut CLOCK_TIMER: MaybeUninit> = MaybeUninit::uninit(); static mut TICK_COUNTER: MaybeUninit = MaybeUninit::uninit(); @@ -64,10 +78,14 @@ fn ticks_now() -> u32 { cortex_m::interrupt::free(|_cs| unsafe { *TICK_COUNTER.as_ptr() }) } +fn get_state_copy() -> State { + cortex_m::interrupt::free(|_cs| unsafe { + (*SHARED.as_ptr()).state.clone() + }) +} + #[cortex_m_rt::entry] fn main() -> ! { - let mut state = State::new(); - let cp = cortex_m::Peripherals::take().unwrap(); let dp = pac::Peripherals::take().unwrap(); @@ -94,22 +112,19 @@ fn main() -> ! { let pb13 = gpiob.pb13.into_pull_up_input(&mut gpiob.crh); // BTN3 Button G let pc15 = gpioc.pc15.into_pull_up_input(&mut gpioc.crh); let adc1 = dp.ADC1; - let mut ui = ui::UI::new(mic_sw1, mic_sw2, pb0, pb1, adc1, &mut rcc.apb2, &clocks, pb12, pb13, pc15); + let ui = ui::UI::new(mic_sw1, mic_sw2, pb0, pb1, adc1, &mut rcc.apb2, &clocks, pb12, pb13, pc15); - -#[cfg(feature = "cw")] - let (mut cwpwm, cw_paddle_tip) = { + let (cw_pwm, cw_paddle_tip) = { let pa8 = gpioa.pa8.into_alternate_push_pull(&mut gpioa.crh); // CW PWM output using TIM1 Ch1 let tim1 = Timer::tim1(dp.TIM1, &clocks, &mut rcc.apb2); - let cwpwm = cw::CWPWM::new(pa8, tim1, &mut afio.mapr); + let cw_pwm = cw::CWPWM::new(pa8, tim1, &mut afio.mapr); let cw_paddle_tip = gpiob.pb8.into_pull_up_input(&mut gpiob.crh); // CW paddle tip - (cwpwm, cw_paddle_tip) + (cw_pwm, cw_paddle_tip) }; let mut s_meter = gpioa.pa5.into_analog(&mut gpioa.crl); let mut adc2 = adc::Adc::adc2(dp.ADC2, &mut rcc.apb2, clocks); let mut last_s_meter_update_time = 0; - let mut last_sequence_state_change = 0; // Configure PB14 as output. (LED) let mut led = gpiob.pb14.into_push_pull_output(&mut gpiob.crh); @@ -117,12 +132,13 @@ fn main() -> ! { let (pa15, pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); let _cw_key_n = pa15.into_push_pull_output_with_state(&mut gpioa.crh, gpio::State::High); // TODO output - let mut seq0n = pb3.into_push_pull_output_with_state(&mut gpiob.crl, gpio::State::High); - let mut seq1_pa = pb4.into_push_pull_output_with_state(&mut gpiob.crl, gpio::State::Low); - let mut seq2_switch = gpiob.pb5.into_push_pull_output_with_state(&mut gpiob.crl, gpio::State::Low); - let mut mute_spkr = gpioa.pa2.into_push_pull_output_with_state(&mut gpioa.crl, gpio::State::Low); - let mut mute_micn = gpioa.pa1.into_push_pull_output_with_state(&mut gpioa.crl, gpio::State::Low); + let seq0n = pb3.into_push_pull_output_with_state(&mut gpiob.crl, gpio::State::High); + let seq1_pa = pb4.into_push_pull_output_with_state(&mut gpiob.crl, gpio::State::Low); + let seq2_switch = gpiob.pb5.into_push_pull_output_with_state(&mut gpiob.crl, gpio::State::Low); + + let mute_spkr = gpioa.pa2.into_push_pull_output_with_state(&mut gpioa.crl, gpio::State::Low); + let mute_micn = gpioa.pa1.into_push_pull_output_with_state(&mut gpioa.crl, gpio::State::Low); let c1 = gpioa.pa6; let c2 = gpioa.pa7; @@ -192,10 +208,23 @@ fn main() -> ! { ); let i2c_busmanager = shared_bus::BusManagerSimple::new(i2c); - let mut siclock = si_clock::SiClock::new(i2c_busmanager.acquire_i2c(), state.bfo(), state.vfo()); + + let mut siclock = { + let shared = unsafe { &mut *SHARED.as_mut_ptr() }; + *shared = SharedWithISR { + state : State::new(), + last_sequence_state_change : 0, + cw_ptt_timestamp : 0, + ui, + cw_pwm, + cw_paddle_tip, seq0n, seq1_pa, seq2_switch, mute_spkr, mute_micn + }; + + si_clock::SiClock::new(i2c_busmanager.acquire_i2c(), shared.state.bfo(), shared.state.vfo()) + }; let mut last_s_meter_value = 0; - ui::update_disp(&mut lcd, &state, &mut delay, last_s_meter_value); + ui::update_disp(&mut lcd, &get_state_copy(), &mut delay, last_s_meter_value, false); let mut last_encoder_count = qei.count(); @@ -213,8 +242,12 @@ fn main() -> ! { unsafe { pac::NVIC::unmask(pac::Interrupt::TIM2); } + let mut last_disp_update_counter = 1; loop { let mut update_disp_required = false; + let mut bfo_tune_fail = false; + + let state = get_state_copy(); let previous_vfo = state.vfo(); let previous_bfo = state.bfo(); @@ -223,75 +256,35 @@ fn main() -> ! { if encoder_count != last_encoder_count { let delta = encoder_count.wrapping_sub(last_encoder_count); let delta = if delta > 0x7FFF { delta as i32 - 0x10000 } else { delta as i32 }; - let require_bfo_update = ui.update_encoder(&mut state, delta); + + let require_bfo_update = cortex_m::interrupt::free(|_cs| { + let shared = unsafe { &mut *SHARED.as_mut_ptr() }; + shared.ui.update_encoder(&mut shared.state, delta) + }); + if require_bfo_update { - state.bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); + bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); } siclock.set_vfo(state.vfo()); update_disp_required = true; } - let button_result = ui.handle_buttons(&mut state); - if previous_bfo != state.bfo() { - state.bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); + bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); } if previous_vfo != state.vfo() { siclock.set_vfo(state.vfo()); } - update_disp_required |= button_result.display_update; - - let next_state = match state.sequence_state { + match state.sequence_state { SequenceState::Rx => { led.set_high().unwrap(); - - mute_spkr.set_low().unwrap(); - mute_micn.set_low().unwrap(); - seq2_switch.set_low().unwrap(); - seq0n.set_high().unwrap(); - - if button_result.ptt { - SequenceState::Switching - } - else { - SequenceState::Rx - } - }, - SequenceState::Switching => { - mute_spkr.set_high().unwrap(); - mute_micn.set_high().unwrap(); - seq2_switch.set_high().unwrap(); - seq0n.set_low().unwrap(); - seq1_pa.set_low().unwrap(); - - if button_result.ptt { - SequenceState::Tx - } - else { - SequenceState::Rx - } }, + SequenceState::Switching => {} SequenceState::Tx => { led.set_low().unwrap(); - - seq1_pa.set_high().unwrap(); - if button_result.ptt { - SequenceState::Tx - } - else { - SequenceState::Switching - } - }, - }; - - let t_now = ticks_now(); - const SWITCHING_DELAY : u32 = TICKS_PER_SECOND * 200 / 1000; - if state.sequence_state != next_state && - last_sequence_state_change + SWITCHING_DELAY <= t_now { - state.sequence_state = next_state; - last_sequence_state_change = t_now; + } } let s_meter_value: u16 = adc2.read(&mut s_meter).unwrap(); @@ -302,26 +295,24 @@ fn main() -> ! { // ADC is 12-bit, convert to dB full-scale let s_meter_value = 10f32 * log10f::log10f(f32::from(s_meter_value) / 4092f32); let s_meter_value = s_meter_value as i32; + let t_now = ticks_now(); if last_s_meter_update_time + 10 < t_now { update_disp_required = s_meter_value != last_s_meter_value; last_s_meter_value = s_meter_value; last_s_meter_update_time = t_now; } + if last_disp_update_counter != state.update_disp_counter { + update_disp_required = true; + last_disp_update_counter = state.update_disp_counter; + } + if update_disp_required { - ui::update_disp(&mut lcd, &state, &mut delay, s_meter_value); + ui::update_disp(&mut lcd, &state, &mut delay, s_meter_value, bfo_tune_fail); } last_encoder_count = encoder_count; -#[cfg(feature = "cw")] - if cw_paddle_tip.is_low().unwrap() { - cwpwm.on(); - } - else { - cwpwm.off(); - } - cortex_m::asm::wfi(); } } @@ -333,6 +324,97 @@ fn TIM2() { let ticks = unsafe { &mut *TICK_COUNTER.as_mut_ptr() }; *ticks += 1; + + let mut shared = unsafe { &mut *SHARED.as_mut_ptr() }; + let button_result = shared.ui.handle_buttons(&mut shared.state); + + if button_result.display_update { + shared.state.update_disp_counter += 1; + } + + let cw_paddle_tip_low = shared.cw_paddle_tip.is_low().unwrap(); + + const CW_PTT_DELAY : u32 = TICKS_PER_SECOND * 800 / 1000; + let cw_ptt = if shared.state.mode == Mode::CW { + if cw_paddle_tip_low { + shared.cw_ptt_timestamp = *ticks; + true + } + else { + shared.cw_ptt_timestamp + CW_PTT_DELAY > *ticks + } + } + else { + false + }; + + let cw_beep = shared.state.mode == Mode::CW && cw_paddle_tip_low; + + let next_state = match shared.state.sequence_state { + SequenceState::Rx => { + shared.mute_spkr.set_low().unwrap(); + shared.mute_micn.set_low().unwrap(); + shared.seq2_switch.set_low().unwrap(); + shared.seq0n.set_high().unwrap(); + shared.cw_pwm.off(); + + if button_result.ptt || cw_ptt { + SequenceState::Switching + } + else { + SequenceState::Rx + } + }, + SequenceState::Switching => { + shared.mute_spkr.set_high().unwrap(); + shared.seq2_switch.set_high().unwrap(); + shared.seq0n.set_low().unwrap(); + shared.seq1_pa.set_low().unwrap(); + + if cw_beep { + shared.cw_pwm.on(); + } + else { + shared.cw_pwm.off(); + } + + if button_result.ptt { + shared.mute_micn.set_high().unwrap(); + SequenceState::Tx + } + else if cw_ptt { + shared.mute_micn.set_low().unwrap(); + SequenceState::Tx + } + else { + SequenceState::Rx + } + }, + SequenceState::Tx => { + shared.seq1_pa.set_high().unwrap(); + + if cw_beep { + shared.cw_pwm.on(); + } + else { + shared.cw_pwm.off(); + } + + if button_result.ptt || cw_ptt { + SequenceState::Tx + } + else { + SequenceState::Switching + } + }, + }; + + const SWITCHING_DELAY : u32 = TICKS_PER_SECOND * 20 / 1000; + if shared.state.sequence_state != next_state && + shared.last_sequence_state_change + SWITCHING_DELAY <= *ticks { + shared.state.sequence_state = next_state; + shared.last_sequence_state_change = *ticks; + } } #[cortex_m_rt::exception] diff --git a/sw/picardy/src/state.rs b/sw/picardy/src/state.rs index ed99087..239cf18 100644 --- a/sw/picardy/src/state.rs +++ b/sw/picardy/src/state.rs @@ -3,6 +3,8 @@ pub const VHF_INITIAL_VFO : u32 = 144_300_000; pub const VHF_LO : u32 = 114_286_400; pub const BFO_LSB : u32 = 6_000_700 + 1_100; pub const BFO_USB : u32 = 6_000_700 - 1_100; +pub const BFO_CW : u32 = 6_000_700 - 1_100; +pub const QRG_CORRECTION : i32 = -1_600; // Defines which parameter is changed by the encoder #[derive(Clone, Copy, PartialEq, Eq)] @@ -18,20 +20,22 @@ pub enum VFOSelection { B, } +#[derive(Clone)] pub enum TuneSpeed { Slow, Mid, Fast } -#[derive(Clone, Copy)] -pub enum FilterShift { +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum Mode { LSB, USB, - Custom(u32), + CustomShift(u32), + CW, } -#[derive(PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub enum SequenceState { Rx, Switching, @@ -44,16 +48,17 @@ impl SequenceState { } } +#[derive(Clone)] pub struct State { pub ui_sel : UISelection, pub vfo_a : u32, pub vfo_b : u32, pub vfo_sel : VFOSelection, pub rit : i32, - pub filter_shift : FilterShift, - pub bfo_tune_fail : bool, + pub mode : Mode, pub tune_speed : TuneSpeed, pub sequence_state : SequenceState, + pub update_disp_counter : u8, } impl State { @@ -61,21 +66,22 @@ impl State { State { rit : 0, ui_sel : UISelection::VFO, - filter_shift : FilterShift::USB, - bfo_tune_fail : false, + mode : Mode::USB, vfo_sel : VFOSelection::A, vfo_a : VHF_INITIAL_VFO, vfo_b : VHF_INITIAL_VFO, tune_speed : TuneSpeed::Mid, sequence_state : SequenceState::Rx, + update_disp_counter : 0, } } pub fn bfo(&self) -> u32 { - match self.filter_shift { - FilterShift::LSB => BFO_LSB, - FilterShift::USB => BFO_USB, - FilterShift::Custom(fs) => fs, + match self.mode { + Mode::LSB => BFO_LSB, + Mode::USB => BFO_USB, + Mode::CustomShift(fs) => fs, + Mode::CW => BFO_CW, } } diff --git a/sw/picardy/src/ui.rs b/sw/picardy/src/ui.rs index 1133582..b15a289 100644 --- a/sw/picardy/src/ui.rs +++ b/sw/picardy/src/ui.rs @@ -195,15 +195,16 @@ impl UI { } if button_updates.c { - let (new_ui_sel, new_filter_shift) = match (state.ui_sel, state.filter_shift) { - (UISelection::IFShift, FilterShift::USB) => (UISelection::IFShift, FilterShift::LSB), - (UISelection::IFShift, FilterShift::LSB) => (UISelection::IFShift, FilterShift::USB), - (UISelection::IFShift, FilterShift::Custom(_)) => (UISelection::IFShift, FilterShift::USB), + let (new_ui_sel, new_filter_shift) = match (state.ui_sel, state.mode) { + (UISelection::IFShift, Mode::USB) => (UISelection::IFShift, Mode::LSB), + (UISelection::IFShift, Mode::LSB) => (UISelection::IFShift, Mode::CW), + (UISelection::IFShift, Mode::CW) => (UISelection::IFShift, Mode::USB), + (UISelection::IFShift, Mode::CustomShift(_)) => (UISelection::IFShift, Mode::USB), (_, f) => (UISelection::IFShift, f), }; state.ui_sel = new_ui_sel; - state.filter_shift = new_filter_shift; + state.mode = new_filter_shift; result.display_update = true; } @@ -224,7 +225,7 @@ impl UI { state.rit = 0; }, UISelection::IFShift => { - state.filter_shift = FilterShift::USB; + state.mode = Mode::USB; }, } @@ -254,14 +255,14 @@ impl UI { }, UISelection::IFShift => { let new_bfo = (state.bfo() as i32 + delta * state.bfo_incr()) as u32; - state.filter_shift = FilterShift::Custom(new_bfo); + state.mode = Mode::CustomShift(new_bfo); true }, } } } -pub fn update_disp(lcd: &mut HD44780, state: &State, delay: &mut Delay, s_meter_value : i32) +pub fn update_disp(lcd: &mut HD44780, state: &State, delay: &mut Delay, s_meter_value: i32, bfo_tune_fail: bool) { let mut string = arrayvec::ArrayString::<[_; 16]>::new(); @@ -280,7 +281,7 @@ pub fn update_disp(lcd: &mut HD44780, state: string.clear(); - match (state.bfo_tune_fail, &state.vfo_sel) { + match (bfo_tune_fail, &state.vfo_sel) { (true, _) => write!(string, "VFO!").unwrap(), (false, VFOSelection::A) => write!(string, "VFOa").unwrap(), (false, VFOSelection::B) => write!(string, "VFOb").unwrap(), @@ -290,10 +291,11 @@ pub fn update_disp(lcd: &mut HD44780, state: write!(string, "{}", if state.ui_sel == UISelection::IFShift { ">" } else { " " }).unwrap(); - let mode = match state.filter_shift { - FilterShift::USB => "USB", - FilterShift::LSB => "LSB", - FilterShift::Custom(_) => "IFs", + let mode = match state.mode { + Mode::USB => "USB", + Mode::LSB => "LSB", + Mode::CustomShift(_) => "IFs", + Mode::CW => "CW ", }; let speed = match state.tune_speed { -- cgit v1.2.3