diff options
| -rw-r--r-- | sw/picardy/src/main.rs | 196 | ||||
| -rw-r--r-- | sw/picardy/src/state.rs | 126 | ||||
| -rw-r--r-- | sw/picardy/src/ui.rs | 166 | 
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();  }  | 
