From e267dc1bc7074daf3689a283a7198e011e7e8945 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 7 Dec 2020 16:32:05 +0100 Subject: Rework UI --- sw/picardy/src/main.rs | 196 +++++------------------------------------------- sw/picardy/src/state.rs | 126 +++++++++++++++++++++++++++++++ sw/picardy/src/ui.rs | 166 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 309 insertions(+), 179 deletions(-) create mode 100644 sw/picardy/src/state.rs diff --git a/sw/picardy/src/main.rs b/sw/picardy/src/main.rs index dc8d0ef..ed9d799 100644 --- a/sw/picardy/src/main.rs +++ b/sw/picardy/src/main.rs @@ -26,7 +26,6 @@ #![no_std] use core::mem::MaybeUninit; -use core::fmt::Write; use cortex_m_rt::ExceptionFrame; use cortex_m_semihosting::hprintln; use panic_semihosting as _; @@ -42,147 +41,22 @@ use stm32f1xx_hal::{ qei::QeiOptions, }; -use embedded_hal::digital::v2::{OutputPin}; +use embedded_hal::digital::v2::OutputPin; use hd44780_driver::{Cursor, CursorBlink, Display, DisplayMode, HD44780}; pub mod ui; +pub mod state; pub mod si_clock; +use state::*; + static mut TIMER1: MaybeUninit> = MaybeUninit::uninit(); static mut DECISECONDS_COUNTER: MaybeUninit = MaybeUninit::uninit(); -enum Mode { - VFO, - BFO, -} - -enum TuneSpeed { - Slow, - Mid, - Fast -} - -enum FilterShift { - LSB, - USB, - Custom(u32), -} - -const VHF_BAND_EDGE : u32 = 144_000_000; -const VHF_LO : u32 = 114_286_000; -const BFO_LSB : u32 = 4_915_940; -const BFO_USB : u32 = 4_914_910; - -#[derive(PartialEq, Eq)] -enum SequenceState { - Rx, - ToTxStep1, - ToTxStep2, - Tx, - ToRxStep1, - ToRxStep2, -} - -struct State { - mode : Mode, - filter_shift : FilterShift, - bfo_tune_fail : bool, - vhf_qrg : u32, - tune_speed : TuneSpeed, - - sequence_state : SequenceState, - last_sequence_state_change : usize, -} - -impl State { - fn bfo(&self) -> u32 { - match self.filter_shift { - FilterShift::LSB => BFO_LSB, - FilterShift::USB => BFO_USB, - FilterShift::Custom(fs) => fs, - } - } - - fn if_qrg(&self) -> u32 { - self.vhf_qrg - VHF_LO - } - - fn vfo(&self) -> u32 { - self.if_qrg() - self.bfo() - } - - fn vfo_incr(&self) -> i32 { - match self.tune_speed { - TuneSpeed::Slow => 10, - TuneSpeed::Mid => 200, - TuneSpeed::Fast => 1000, - } - } - - fn bfo_incr(&self) -> i32 { - match self.tune_speed { - TuneSpeed::Slow => 10, - TuneSpeed::Mid => 50, - TuneSpeed::Fast => 100, - } - } -} - -fn update_disp(lcd: &mut HD44780, state: &State, delay: &mut Delay) -{ - let mut string = arrayvec::ArrayString::<[_; 16]>::new(); - - match (state.bfo_tune_fail, &state.mode) { - (true, _) => write!(string, "!").unwrap(), - (false, Mode::BFO) => write!(string, ">").unwrap(), - (false, Mode::VFO) => write!(string, " ").unwrap(), - } - write!(string, "{:<10}", state.bfo()).unwrap(); - - match state.filter_shift { - FilterShift::USB => write!(string, "U").unwrap(), - FilterShift::LSB => write!(string, "L").unwrap(), - FilterShift::Custom(_) => write!(string, " ").unwrap(), - } - - match state.sequence_state { - SequenceState::Rx => write!(string, " R").unwrap(), - SequenceState::Tx => write!(string, " T").unwrap(), - _ => write!(string, " X").unwrap(), - } - - lcd.set_cursor_pos(0, delay).unwrap(); - lcd.write_str(&string, delay).unwrap(); - - string.clear(); - /* Shorten the QRG to avoid using three digits for nothing */ - let disp_freq = (state.vhf_qrg as i32) - (VHF_BAND_EDGE as i32); - match state.mode { - Mode::BFO => write!(string, " .{:<06} ", disp_freq).unwrap(), - Mode::VFO => write!(string, ">.{:<06} ", disp_freq).unwrap(), - } - - match state.tune_speed { - TuneSpeed::Slow => write!(string, "S").unwrap(), - TuneSpeed::Mid => write!(string, "M").unwrap(), - TuneSpeed::Fast => write!(string, "F").unwrap(), - } - - lcd.set_cursor_pos(40, delay).unwrap(); - lcd.write_str(&string, delay).unwrap(); -} #[cortex_m_rt::entry] fn main() -> ! { - let mut state = State { - mode : Mode::VFO, - filter_shift : FilterShift::USB, - bfo_tune_fail : false, - vhf_qrg : VHF_BAND_EDGE, - tune_speed : TuneSpeed::Mid, - sequence_state : SequenceState::Rx, - last_sequence_state_change : 0, - }; + let mut state = State::new(); let cp = cortex_m::Peripherals::take().unwrap(); let dp = pac::Peripherals::take().unwrap(); @@ -267,8 +141,8 @@ fn main() -> ! { }, &mut delay).unwrap(); lcd.set_cursor_pos(0, &mut delay).unwrap(); - lcd.write_str("Hello, world!", &mut delay).unwrap(); - + lcd.write_str("HB9EGM", &mut delay).unwrap(); + delay.delay_ms(200u8); // Configure I2C1 to be used for Si5351 let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl); @@ -292,7 +166,7 @@ fn main() -> ! { let mut siclock = si_clock::SiClock::new(i2c_busmanager.acquire_i2c(), state.bfo(), state.vfo()); - update_disp(&mut lcd, &state, &mut delay); + ui::update_disp(&mut lcd, &state, &mut delay); let mut last_encoder_count = qei.count(); @@ -309,62 +183,30 @@ fn main() -> ! { loop { let mut update_disp_required = false; - let encoder_count = qei.count(); + let encoder_count : u16 = qei.count(); 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 }; - - match state.mode { - Mode::VFO => { - state.vhf_qrg = (state.vhf_qrg as i32 + delta * state.vfo_incr()) as u32; - }, - Mode::BFO => { - let new_bfo = (state.bfo() as i32 + delta * state.bfo_incr()) as u32; - state.filter_shift = FilterShift::Custom(new_bfo); - state.bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); - }, + let require_bfo_update = ui.update_encoder(&mut state, delta); + if require_bfo_update { + state.bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); } - siclock.set_vfo(state.vfo()); - update_disp_required = true; } - let button_state = ui.read_buttons(); - - if button_state.a { - state.mode = Mode::BFO; - update_disp_required = true; - } - else if button_state.b { - state.mode = Mode::VFO; - update_disp_required = true; - } + let button_result = ui.handle_buttons(&mut state); - if button_state.f { - state.filter_shift = match state.filter_shift { - FilterShift::USB => FilterShift::LSB, - FilterShift::LSB => FilterShift::USB, - FilterShift::Custom(_) => FilterShift::USB, - }; + if button_result.bfo_update { state.bfo_tune_fail = !siclock.set_bfo(state.bfo()).is_ok(); - update_disp_required = true; } - - if button_state.g { - state.tune_speed = match state.tune_speed { - TuneSpeed::Slow => TuneSpeed::Mid, - TuneSpeed::Mid => TuneSpeed::Fast, - TuneSpeed::Fast => TuneSpeed::Slow, - }; - update_disp_required = true; - } + update_disp_required |= button_result.display_update; let next_state = match state.sequence_state { SequenceState::Rx => { led.set_high().unwrap(); - if button_state.ptt { + if button_result.ptt { mute_spkr.set_high().unwrap(); mute_micn.set_high().unwrap(); seq2_switch.set_high().unwrap(); @@ -388,7 +230,7 @@ fn main() -> ! { }, SequenceState::Tx => { led.set_low().unwrap(); - if button_state.ptt { + if button_result.ptt { SequenceState::Tx } else { @@ -417,8 +259,8 @@ fn main() -> ! { } if update_disp_required { - update_disp(&mut lcd, &state, &mut delay) - }; + ui::update_disp(&mut lcd, &state, &mut delay); + } last_encoder_count = encoder_count; diff --git a/sw/picardy/src/state.rs b/sw/picardy/src/state.rs new file mode 100644 index 0000000..69fda9e --- /dev/null +++ b/sw/picardy/src/state.rs @@ -0,0 +1,126 @@ +pub const VHF_BAND_EDGE : u32 = 144_000_000; +pub const VHF_INITIAL_VFO : u32 = 144_300_000; +pub const VHF_LO : u32 = 114_286_000; +pub const BFO_LSB : u32 = 4_915_940; +pub const BFO_USB : u32 = 4_914_910; + +// Defines which parameter is changed by the encoder +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum UISelection { + VFO, + RIT, + IFShift, +} + +pub enum VFOSelection { + A, + B, +} + +pub enum TuneSpeed { + Slow, + Mid, + Fast +} + +#[derive(Clone, Copy)] +pub enum FilterShift { + LSB, + USB, + Custom(u32), +} + +#[derive(PartialEq, Eq)] +pub enum SequenceState { + Rx, + ToTxStep1, + ToTxStep2, + Tx, + ToRxStep1, + ToRxStep2, +} + +impl SequenceState { + fn apply_rit(&self) -> bool { + *self == SequenceState::Rx || + *self == SequenceState::ToRxStep1 || + *self == SequenceState::ToRxStep2 + } +} + +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 tune_speed : TuneSpeed, + pub sequence_state : SequenceState, + pub last_sequence_state_change : usize, +} + +impl State { + pub fn new() -> Self { + State { + rit : 0, + ui_sel : UISelection::VFO, + filter_shift : FilterShift::USB, + bfo_tune_fail : false, + vfo_sel : VFOSelection::A, + vfo_a : VHF_INITIAL_VFO, + vfo_b : VHF_INITIAL_VFO, + tune_speed : TuneSpeed::Mid, + sequence_state : SequenceState::Rx, + last_sequence_state_change : 0, + } + } + + pub fn bfo(&self) -> u32 { + match self.filter_shift { + FilterShift::LSB => BFO_LSB, + FilterShift::USB => BFO_USB, + FilterShift::Custom(fs) => fs, + } + } + + pub fn vhf_qrg(&self) -> u32 { + match self.vfo_sel { + VFOSelection::A => self.vfo_a, + VFOSelection::B => self.vfo_b, + } + } + + pub fn if_qrg(&self) -> u32 { + self.vhf_qrg() - VHF_LO + } + + pub fn vfo(&self) -> u32 { + let vfo = (self.if_qrg() - self.bfo()) as i32 + + if self.sequence_state.apply_rit() { self.rit } else { 0 }; + vfo as u32 + } + + pub fn vfo_incr(&self) -> i32 { + match self.tune_speed { + TuneSpeed::Slow => 10, + TuneSpeed::Mid => 200, + TuneSpeed::Fast => 1000, + } + } + + pub fn rit_incr(&self) -> i32 { + // RIT is always slow because it's a fine setting + 2 + } + + pub fn bfo_incr(&self) -> i32 { + match self.tune_speed { + TuneSpeed::Slow => 10, + TuneSpeed::Mid => 50, + TuneSpeed::Fast => 100, + } + } + +} diff --git a/sw/picardy/src/ui.rs b/sw/picardy/src/ui.rs index 2e93459..915adec 100644 --- a/sw/picardy/src/ui.rs +++ b/sw/picardy/src/ui.rs @@ -22,13 +22,17 @@ SOFTWARE. */ +use crate::state::*; + use core::fmt; +use core::fmt::Write; use stm32f1xx_hal::{ prelude::*, adc, rcc::{APB2, Clocks}, stm32::ADC1, + delay::Delay, gpio::gpioa::*, gpio::gpiob::*, gpio::gpioc::*, @@ -36,6 +40,7 @@ use stm32f1xx_hal::{ }; use embedded_hal::digital::v2::InputPin; +use hd44780_driver::HD44780; #[derive(PartialEq, Eq, Clone, Copy)] enum Btn0Buttons { @@ -51,7 +56,7 @@ enum Btn1Buttons { } #[derive(PartialEq, Eq, Clone, Copy)] -pub struct ButtonState { +struct ButtonState { pub a : bool, pub b : bool, pub c : bool, @@ -63,6 +68,22 @@ pub struct ButtonState { pub ptt : bool, } +impl ButtonState { + fn edge_detection(&self, old_state : &ButtonState) -> ButtonState { + ButtonState { + a : !old_state.a && self.a, + b : !old_state.b && self.b, + c : !old_state.c && self.c, + d : !old_state.d && self.d, + e : !old_state.e && self.e, + f : !old_state.f && self.f, + g : !old_state.g && self.g, + enc : !old_state.enc && self.enc, + ptt : !old_state.ptt && self.ptt, + } + } +} + impl Default for ButtonState { fn default() -> Self { ButtonState { @@ -94,6 +115,13 @@ impl fmt::Display for ButtonState { } } +#[derive(Default)] +pub struct ButtonResult { + pub bfo_update : bool, + pub display_update : bool, + pub ptt : bool, +} + pub struct UI { btn0_hist : [u16; 3], btn1_hist : [u16; 3], @@ -110,6 +138,8 @@ pub struct UI { btn_enc : PC15>, adc : adc::Adc, + + previous_button_state : ButtonState, } impl UI { @@ -128,10 +158,11 @@ impl UI { adc : adc1, mic_sw1, _mic_sw2 : mic_sw2, + previous_button_state : ButtonState::default(), } } - pub fn read_buttons(&mut self) -> ButtonState { + fn read_buttons(&mut self) -> ButtonState { let mut buttons = ButtonState::default(); // Debounce BTN0 and BTN1 @@ -211,4 +242,135 @@ impl UI { buttons } + + pub fn handle_buttons(&mut self, state: &mut State) -> ButtonResult { + let mut result = ButtonResult::default(); + let button_state = self.read_buttons(); + let button_updates = button_state.edge_detection(&self.previous_button_state); + self.previous_button_state = button_state; + + if button_updates.a { + state.vfo_sel = match state.vfo_sel { + VFOSelection::A => VFOSelection::B, + VFOSelection::B => VFOSelection::A, + }; + state.ui_sel = UISelection::VFO; + result.display_update = true; + } + + if button_updates.b { + state.ui_sel = UISelection::RIT; + result.display_update = true; + } + + 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), + (_, f) => (UISelection::IFShift, f), + }; + + state.ui_sel = new_ui_sel; + state.filter_shift = new_filter_shift; + + result.bfo_update = true; + result.display_update = true; + } + + if button_updates.d { + state.tune_speed = match state.tune_speed { + TuneSpeed::Slow => TuneSpeed::Mid, + TuneSpeed::Mid => TuneSpeed::Fast, + TuneSpeed::Fast => TuneSpeed::Slow, + }; + result.display_update = true; + } + + if button_updates.enc { + match state.ui_sel { + UISelection::VFO => {}, + UISelection::RIT => { + state.rit = 0; + }, + UISelection::IFShift => { + state.filter_shift = FilterShift::USB; + result.bfo_update = true; + }, + } + + result.display_update = true; + } + + result + } + + // Returns true if bfo must be reprogrammed + pub fn update_encoder(&mut self, state: &mut State, delta : i32) -> bool { + match state.ui_sel { + UISelection::VFO => { + match state.vfo_sel { + VFOSelection::A => { + state.vfo_a = (state.vfo_a as i32 + delta * state.vfo_incr()) as u32; + }, + VFOSelection::B => { + state.vfo_b = (state.vfo_b as i32 + delta * state.vfo_incr()) as u32; + }, + } + false + }, + UISelection::RIT => { + state.rit = state.rit + delta * state.rit_incr(); + false + }, + UISelection::IFShift => { + let new_bfo = (state.bfo() as i32 + delta * state.bfo_incr()) as u32; + state.filter_shift = FilterShift::Custom(new_bfo); + true + }, + } + } +} + +pub fn update_disp(lcd: &mut HD44780, state: &State, delay: &mut Delay) +{ + let mut string = arrayvec::ArrayString::<[_; 16]>::new(); + + /* Shorten the QRG to avoid using three digits for nothing */ + let disp_freq = (state.vhf_qrg() as i32) - (VHF_BAND_EDGE as i32); + write!(string, "{:<03}.{:<03} ", disp_freq / 1000, disp_freq % 1000).unwrap(); + + write!(string, "{}{:<04} ", if state.rit >= 0 { "+" } else { "-" }, state.rit.abs()).unwrap(); + + lcd.set_cursor_pos(0, delay).unwrap(); + lcd.write_str(&string, delay).unwrap(); + + string.clear(); + + match (state.bfo_tune_fail, &state.vfo_sel) { + (true, _) => write!(string, "VFO!").unwrap(), + (false, VFOSelection::A) => write!(string, "ERRa").unwrap(), + (false, VFOSelection::B) => write!(string, "ERRb").unwrap(), + } + + write!(string, "{}", if state.ui_sel == UISelection::RIT { ">RIT" } else { " RIT" }).unwrap(); + + 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 speed = match state.tune_speed { + TuneSpeed::Slow => "SLO", + TuneSpeed::Mid => "MID", + TuneSpeed::Fast => "FST", + }; + + write!(string, "{} {}", mode, speed).unwrap(); + + lcd.set_cursor_pos(40, delay).unwrap(); + lcd.write_str(&string, delay).unwrap(); } -- cgit v1.2.3