From e04fec33f079db4c5d42aa412f7b670784ec1b68 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 13 Mar 2023 23:14:32 +0100 Subject: Add WSPR TX to eval-clock-cw-tx --- sw/eval-clock-cw-tx/src/main.rs | 156 ++++++++++++++++++++++++++++++---------- 1 file changed, 119 insertions(+), 37 deletions(-) (limited to 'sw/eval-clock-cw-tx/src/main.rs') diff --git a/sw/eval-clock-cw-tx/src/main.rs b/sw/eval-clock-cw-tx/src/main.rs index b256926..52193b4 100644 --- a/sw/eval-clock-cw-tx/src/main.rs +++ b/sw/eval-clock-cw-tx/src/main.rs @@ -25,6 +25,8 @@ #![no_main] #![no_std] use core::mem::MaybeUninit; +use core::fmt::Write; +use arrayvec::ArrayString; use cortex_m_rt::ExceptionFrame; use cortex_m_semihosting::hprintln; use panic_semihosting as _; @@ -51,12 +53,23 @@ pub mod si_clock; use state::*; -const TICKS_PER_SECOND : u32 = 100; +// One WSPR symbol is 256/375s +const WSPR_SYMBOL_TICKS : u32 = 256; +// TIM2 runs at that rate, and increments TICK_COUNTER +const TICKS_PER_SECOND : u32 = 375; + +// See README.md +const WSPR_SYMBOLS : [u8; 162] = [ + 1, 1, 2, 0, 0, 2, 2, 2, 1, 2, 0, 0, 3, 3, 1, 0, 2, 2, 3, 2, 0, 1, 0, 1, 3, 3, 1, 0, 2, 2, 0, 0, 2, 0, 3, 2, 0, 3, 0, + 1, 2, 0, 2, 0, 2, 0, 3, 2, 1, 1, 0, 2, 3, 3, 0, 3, 0, 2, 2, 3, 1, 0, 3, 0, 2, 2, 0, 1, 3, 2, 3, 0, 3, 2, 3, 0, 1, 0, + 0, 3, 0, 0, 1, 2, 1, 3, 0, 2, 2, 1, 3, 2, 3, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 2, 3, 2, 0, 1, 2, 0, 1, 1, 1, 0, 3, 1, 2, + 2, 1, 1, 0, 3, 2, 2, 2, 1, 1, 1, 0, 0, 2, 2, 2, 3, 0, 3, 2, 2, 3, 3, 0, 0, 2, 2, 2, 2, 0, 3, 3, 0, 1, 2, 1, 3, 0, 0, + 0, 1, 1, 2, 2, 0]; struct SharedWithISR { state: State, last_sequence_state_change: u32, - feldhell_ptt: bool, + usb_data_transmit: usb::Transmit, cw_ptt_timestamp: u32, cw_key_out_n: gpio::gpioa::PA15>, ui: ui::UI, @@ -66,7 +79,10 @@ struct SharedWithISR { cw_paddle_ring: gpio::gpiob::PB9>, ptt_out: gpio::gpiob::PB3>, seq_switch: gpio::gpiob::PB5>, - led : gpio::gpiob::PB14>, + led: gpio::gpiob::PB14>, + + wspr_symbol_ix: usize, + wspr_symbol_duration_ticks: u32, } static mut SHARED: MaybeUninit = MaybeUninit::uninit(); @@ -188,13 +204,15 @@ fn main() -> ! { *shared = SharedWithISR { state: State::new(), last_sequence_state_change: 0, - feldhell_ptt: false, + usb_data_transmit: usb::Transmit::None, cw_ptt_timestamp: 0, cw_key_out_n, ui, cw_pwm, cw_keyer: cw::Keyer::new(12, TICKS_PER_SECOND), cw_paddle_tip, cw_paddle_ring, ptt_out, seq_switch, led, + wspr_symbol_ix: 0, + wspr_symbol_duration_ticks: 0, }; si_clock::SiClock::new(i2c_busmanager.acquire_i2c(), 0, shared.state.vfo_display()) @@ -219,32 +237,70 @@ fn main() -> ! { unsafe { pac::NVIC::unmask(pac::Interrupt::TIM2); } let mut last_disp_update_counter = 1; - let mut previous_usb_freq = usb.frequency; let mut previous_vfo = 0; let mut previous_state = SequenceState::Rx; - loop { - let mut update_disp_required = false; + const MESSAGE_LEN: usize = 32; + let mut usb_message: ArrayString:: = ArrayString::new(); + loop { usb.handle(); - if previous_usb_freq != usb.frequency { - previous_usb_freq = usb.frequency; + let (state_copy, update_siclock_required) = cortex_m::interrupt::free(|_cs| { + let shared = unsafe { &mut *SHARED.as_mut_ptr() }; - cortex_m::interrupt::free(|_cs| { - let shared = unsafe { &mut *SHARED.as_mut_ptr() }; - shared.state.set_vfo(usb.frequency); - }); + if shared.wspr_symbol_ix == WSPR_SYMBOLS.len() && shared.wspr_symbol_duration_ticks == WSPR_SYMBOL_TICKS { + usb.clear_transmit(); + shared.wspr_symbol_ix = 0; + shared.wspr_symbol_duration_ticks = 0; + } - update_disp_required = true; - } + let mut update_siclock_required = false; - let state = cortex_m::interrupt::free(|_cs| unsafe { - let shared = SHARED.as_mut_ptr(); - (*shared).feldhell_ptt = usb.is_transmit(); - (*shared).state.clone() + match shared.state.mode { + Mode::CW(_) => { + let vfo = shared.state.vfo_display(); + if previous_vfo != vfo || previous_state != shared.state.sequence_state { + shared.state.set_vfo(vfo); + update_siclock_required = true; + } + previous_vfo = vfo; + } + Mode::FeldHell => { + if let Some(f) = usb.take_frequency() { + shared.state.set_vfo(f); + update_siclock_required = true; + } + }, + Mode::WSPR => { + if shared.state.wspr_freq_offset_updated { + update_siclock_required = true; + } + shared.state.wspr_freq_offset_updated = false; + } + }; + shared.usb_data_transmit = usb.transmit(); + + (shared.state.clone(), update_siclock_required) }); + if update_siclock_required { + let f = state_copy.vfo_siclock() * 100 + + match state_copy.sequence_state { + SequenceState::Tx(SequenceMode::WSPR) => state_copy.wspr_freq_offset_centihz, + _ => 0, + }; + + usb_message.clear(); + if write!(&mut usb_message, "F{}\n", f).is_ok() { + usb.send_message(usb_message.as_bytes()); + } + siclock.set_vfo_centihertz(f); + } + + previous_state = state_copy.sequence_state.clone(); + + let mut update_disp_required = update_siclock_required; let encoder_count : u16 = qei.count(); if encoder_count != last_encoder_count { @@ -262,26 +318,19 @@ fn main() -> ! { update_disp_required = true; } - match (previous_state.clone(), state.sequence_state.clone()) { + match (previous_state.clone(), state_copy.sequence_state.clone()) { (SequenceState::Rx, SequenceState::Switching(_)) => usb.send_transmit(), (SequenceState::Switching(_), SequenceState::Rx) => usb.send_receive(), _ => (), } - let vfo = state.vfo_display(); - if previous_vfo != vfo || previous_state != state.sequence_state { - siclock.set_vfo(state.vfo_siclock()); - } - previous_vfo = vfo; - previous_state = state.sequence_state.clone(); - - if last_disp_update_counter != state.update_disp_counter { + if last_disp_update_counter != state_copy.update_disp_counter { update_disp_required = true; - last_disp_update_counter = state.update_disp_counter; + last_disp_update_counter = state_copy.update_disp_counter; } if update_disp_required { - ui::update_disp(&mut lcd, &state, &mut delay); + ui::update_disp(&mut lcd, &state_copy, &mut delay); } last_encoder_count = encoder_count; @@ -321,14 +370,24 @@ fn TIM2() { } }, Mode::FeldHell => { - shared.feldhell_ptt + shared.usb_data_transmit == usb::Transmit::FELDHELL + } + Mode::WSPR => { + if shared.usb_data_transmit == usb::Transmit::WSPR && + shared.wspr_symbol_ix != WSPR_SYMBOLS.len() && + shared.wspr_symbol_duration_ticks != WSPR_SYMBOL_TICKS { + true + } + else { + false + } } }; let cw_beep = match shared.state.mode { Mode::CW(CWMode::StraightKey) => cw_paddle_tip_low, Mode::CW(CWMode::Iambic) => shared.cw_keyer.tick(*ticks, cw_paddle_tip_low, cw_paddle_ring_low), - Mode::FeldHell => false, // Done in usb.c + _ => false, }; let next_state = match shared.state.sequence_state { @@ -336,11 +395,10 @@ fn TIM2() { shared.ptt_out.set_low(); shared.seq_switch.set_low(); if ptt { - if shared.state.mode == Mode::FeldHell { - SequenceState::Switching(SequenceMode::FeldHell) - } - else { - SequenceState::Switching(SequenceMode::CW) + match shared.state.mode { + state::Mode::WSPR => SequenceState::Switching(SequenceMode::WSPR), + state::Mode::FeldHell => SequenceState::Switching(SequenceMode::FeldHell), + state::Mode::CW(_) => SequenceState::Switching(SequenceMode::CW), } } else { @@ -385,6 +443,30 @@ fn TIM2() { SequenceState::Tx(SequenceMode::FeldHell) => { shared.led.set_low(); }, + SequenceState::Tx(SequenceMode::WSPR) => { + shared.cw_key_out_n.set_low(); + shared.cw_pwm.off(); + shared.led.set_low(); + + if shared.wspr_symbol_duration_ticks < WSPR_SYMBOL_TICKS { + shared.wspr_symbol_duration_ticks += 1; + } + + if shared.wspr_symbol_duration_ticks == WSPR_SYMBOL_TICKS { + if shared.wspr_symbol_ix < WSPR_SYMBOLS.len() { + shared.state.wspr_freq_offset_centihz = WSPR_SYMBOLS[shared.wspr_symbol_ix] as u32 * 100 * 375 / 256; + shared.state.wspr_freq_offset_updated = true; + shared.wspr_symbol_ix += 1; + } + else { + shared.state.wspr_freq_offset_centihz = 0; + } + + if shared.wspr_symbol_ix != WSPR_SYMBOLS.len() { + shared.wspr_symbol_duration_ticks = 0; + } + } + }, _ => { shared.led.set_high(); shared.cw_pwm.off(); -- cgit v1.2.3