aboutsummaryrefslogtreecommitdiffstats
path: root/sw
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2020-12-07 16:32:05 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2020-12-07 16:32:05 +0100
commite267dc1bc7074daf3689a283a7198e011e7e8945 (patch)
tree17ec775d565a1f1d345d9871ab3e4b15030a836b /sw
parenta4f9f3400a7de9a389f65c52182676254c1faee2 (diff)
downloadpicardy-e267dc1bc7074daf3689a283a7198e011e7e8945.tar.gz
picardy-e267dc1bc7074daf3689a283a7198e011e7e8945.tar.bz2
picardy-e267dc1bc7074daf3689a283a7198e011e7e8945.zip
Rework UI
Diffstat (limited to 'sw')
-rw-r--r--sw/picardy/src/main.rs196
-rw-r--r--sw/picardy/src/state.rs126
-rw-r--r--sw/picardy/src/ui.rs166
3 files changed, 309 insertions, 179 deletions
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<CountDownTimer<pac::TIM1>> = MaybeUninit::uninit();
static mut DECISECONDS_COUNTER: MaybeUninit<usize> = 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<T: hd44780_driver::bus::DataBus>(lcd: &mut HD44780<T>, 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<Input<PullUp>>,
adc : adc::Adc<ADC1>,
+
+ 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<T: hd44780_driver::bus::DataBus>(lcd: &mut HD44780<T>, 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();
}