aboutsummaryrefslogtreecommitdiffstats
path: root/sw/eval-clock-cw-tx/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sw/eval-clock-cw-tx/src/main.rs')
-rw-r--r--sw/eval-clock-cw-tx/src/main.rs156
1 files changed, 119 insertions, 37 deletions
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<gpio::Output<gpio::PushPull>>,
ui: ui::UI,
@@ -66,7 +79,10 @@ struct SharedWithISR {
cw_paddle_ring: gpio::gpiob::PB9<gpio::Input<gpio::PullUp>>,
ptt_out: gpio::gpiob::PB3<gpio::Output<gpio::PushPull>>,
seq_switch: gpio::gpiob::PB5<gpio::Output<gpio::PushPull>>,
- led : gpio::gpiob::PB14<gpio::Output<gpio::PushPull>>,
+ led: gpio::gpiob::PB14<gpio::Output<gpio::PushPull>>,
+
+ wspr_symbol_ix: usize,
+ wspr_symbol_duration_ticks: u32,
}
static mut SHARED: MaybeUninit<SharedWithISR> = 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::<MESSAGE_LEN> = 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();