aboutsummaryrefslogtreecommitdiffstats
path: root/sw/eval-clock-cw-tx/src/ui.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sw/eval-clock-cw-tx/src/ui.rs')
-rw-r--r--sw/eval-clock-cw-tx/src/ui.rs274
1 files changed, 274 insertions, 0 deletions
diff --git a/sw/eval-clock-cw-tx/src/ui.rs b/sw/eval-clock-cw-tx/src/ui.rs
new file mode 100644
index 0000000..564b7d1
--- /dev/null
+++ b/sw/eval-clock-cw-tx/src/ui.rs
@@ -0,0 +1,274 @@
+/*
+ The MIT License (MIT)
+
+ Copyright (c) 2020 Matthias P. Braendli
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+use crate::state::*;
+
+use core::fmt;
+use core::fmt::Write;
+
+use stm32f1xx_hal::{
+ delay::Delay,
+ gpio::gpiob::*,
+ gpio::gpioc::*,
+ gpio::{Input, PullUp, Floating},
+};
+
+use embedded_hal::digital::v2::InputPin;
+use hd44780_driver::HD44780;
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+struct ButtonState {
+ pub a : bool,
+ pub b : bool,
+ pub c : bool,
+ pub d : bool,
+ pub e : bool,
+ pub enc : 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,
+ enc : !old_state.enc && self.enc,
+ }
+ }
+}
+
+impl Default for ButtonState {
+ fn default() -> Self {
+ ButtonState {
+ a : false,
+ b : false,
+ c : false,
+ d : false,
+ e : false,
+ enc : false,
+ }
+ }
+}
+
+impl fmt::Display for ButtonState {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}{}{}{}{}{}",
+ if self.a { "A" } else { "a" },
+ if self.b { "B" } else { "b" },
+ if self.c { "C" } else { "c" },
+ if self.d { "D" } else { "d" },
+ if self.e { "E" } else { "e" },
+ if self.enc { "X" } else { "x" })
+ }
+}
+
+#[derive(Default)]
+pub struct ButtonResult {
+ pub display_update : bool,
+}
+
+pub struct UI {
+ btn0 : PB1<Input<Floating>>,
+ btn1 : PB0<Input<Floating>>,
+
+ btn2 : PB12<Input<PullUp>>,
+ btn3 : PB13<Input<PullUp>>,
+
+ btn_enc : PC15<Input<PullUp>>,
+
+ previous_button_state : ButtonState,
+}
+
+impl UI {
+ pub fn new(
+ pb0: PB0<Input<Floating>>,
+ pb1: PB1<Input<Floating>>,
+ pb12: PB12<Input<PullUp>>,
+ pb13: PB13<Input<PullUp>>,
+ pc15 : PC15<Input<PullUp>>) -> UI {
+
+ UI {
+ btn0 : pb1,
+ btn1 : pb0,
+ btn2 : pb12,
+ btn3 : pb13,
+ btn_enc : pc15,
+ previous_button_state : ButtonState::default(),
+ }
+ }
+
+ fn read_buttons(&mut self) -> ButtonState {
+ let mut buttons = ButtonState::default();
+
+ let b0_low = self.btn0.is_low().unwrap();
+ let b1_low = self.btn1.is_low().unwrap();
+
+ if b0_low && b1_low {
+ buttons.e = true;
+ }
+ else if b0_low {
+ buttons.a = true;
+ }
+ else if b1_low {
+ buttons.b = true;
+ }
+
+ if self.btn2.is_low().unwrap() {
+ buttons.c = true;
+ }
+
+ if self.btn3.is_low().unwrap() {
+ buttons.d = true;
+ }
+
+ if self.btn_enc.is_low().unwrap() {
+ buttons.enc = true;
+ }
+
+ 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.ui_sel, state.vfo_sel) {
+ (UISelection::VFO, VFOSelection::A) => VFOSelection::B,
+ (UISelection::VFO, VFOSelection::B) => VFOSelection::A,
+ _ => state.vfo_sel.clone(),
+ };
+ state.ui_sel = UISelection::VFO;
+ result.display_update = true;
+ }
+
+ if button_updates.c {
+ let (new_ui_sel, new_filter_shift) = match (state.ui_sel, state.mode) {
+ (UISelection::Mode, Mode::CW(CWMode::StraightKey)) => (UISelection::Mode, Mode::CW(CWMode::Iambic)),
+ (UISelection::Mode, Mode::CW(CWMode::Iambic)) => (UISelection::Mode, Mode::CW(CWMode::StraightKey)),
+ (_, f) => (UISelection::Mode, f),
+ };
+
+ state.ui_sel = new_ui_sel;
+ state.mode = new_filter_shift;
+
+ 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::Mode => {
+ state.mode = Mode::CW(CWMode::StraightKey);
+ },
+ }
+
+ result.display_update = true;
+ }
+
+ result
+ }
+
+ // Returns true if bfo must be reprogrammed
+ pub fn update_encoder(&mut self, state: &mut State, delta : i32) {
+ 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;
+ },
+ }
+ },
+ UISelection::Mode => {
+ match state.mode {
+ Mode::CW(CWMode::Iambic) => {
+ let mut new_wpm = state.cw_wpm as i32 + delta / 4;
+ if new_wpm < 1 {
+ new_wpm = 1;
+ }
+
+ let wpm = new_wpm as u32;
+ state.cw_wpm = wpm;
+ state.mode = Mode::CW(CWMode::Iambic);
+ },
+ _ => { },
+ }
+ },
+ }
+ }
+}
+
+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();
+
+ let disp_freq = state.vfo() as i32;
+ write!(string, "{:<05}.{:<03} ", disp_freq / 1000, disp_freq % 1000).unwrap();
+
+ write!(string, " CW{:<02}", state.cw_wpm).unwrap();
+
+ lcd.set_cursor_pos(0, delay).unwrap();
+ lcd.write_str(&string, delay).unwrap();
+
+ string.clear();
+
+ match &state.vfo_sel {
+ VFOSelection::A => write!(string, "VFOa").unwrap(),
+ VFOSelection::B => write!(string, "VFOb").unwrap(),
+ }
+
+ write!(string, " {}", if state.ui_sel == UISelection::Mode { ">" } else { " " }).unwrap();
+
+ let mode = match state.mode {
+ Mode::CW(CWMode::StraightKey) => "CWs",
+ Mode::CW(CWMode::Iambic) => "CWp",
+ };
+
+ 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();
+}