From 2a1a5d66df3d1adbe84cd3b1ece8a6a961e66c57 Mon Sep 17 00:00:00 2001 From: Levi Pearson Date: Wed, 15 Apr 2020 22:57:31 -0600 Subject: [PATCH] Move rotary encoder handling to a separate module --- Cargo.toml | 4 +++ src/main.rs | 86 +++++++++++++++++++++------------------------------ src/rotary.rs | 59 +++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 50 deletions(-) create mode 100644 src/rotary.rs diff --git a/Cargo.toml b/Cargo.toml index 4df1e21..14f00b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,10 @@ features = ["stm32f103", "rt"] version = "0.5.3" features = ["rt", "stm32f103", "medium"] +[dependencies.either] +default-features = false +version = "1.5.3" + [[bin]] name = "blue_pill_more" test = false diff --git a/src/main.rs b/src/main.rs index 2e8a66b..01c6ecd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,8 +35,6 @@ use stm32f1xx_hal::{ prelude::*, }; -use embedded_hal::digital::v2::InputPin; - use switch_hal::{IntoSwitch, InputSwitch, OutputSwitch, ToggleableOutputSwitch}; use ssd1306::{ @@ -54,6 +52,9 @@ use embedded_graphics::{ use arrayvec::ArrayString; +mod rotary; +use rotary::{Direction, Rotary}; + type CLKPIN = PA8>; type DTPIN = PA9>; @@ -104,6 +105,8 @@ fn main() -> ! { let sw = gpioa.pa10 .into_floating_input(&mut gpioa.crh) .into_active_low_switch(); + + // Set up the rotary encoder pins for use with the interrupt init_encoder_pins(clk, dt, &mut afio, &dev_periph.EXTI); // Configure pin C13 to drive the "PC13" LED as an active-low switch @@ -111,7 +114,7 @@ fn main() -> ! { .into_push_pull_output(&mut gpioc.crh) .into_active_low_switch(); - // Configure the I2C pins we are using to the correct mode + // Configure the I2C pins we are using for the display to the correct mode let scl = gpiob.pb10.into_alternate_open_drain(&mut gpiob.crh); let sda = gpiob.pb11.into_alternate_open_drain(&mut gpiob.crh); @@ -131,9 +134,6 @@ fn main() -> ! { 1000, ); - // Turn the LED on via the OutputPin trait - led.on().unwrap(); - // Initialize the display let mut display: GraphicsMode<_> = Builder::new() .with_i2c_addr(0x3c) @@ -144,6 +144,13 @@ fn main() -> ! { // Every set of commands to the display is buffered until we flush it display.flush().unwrap(); + // Enable interrupts + unsafe { + core_periph.NVIC.set_priority(Interrupt::EXTI9_5, 1); + NVIC::unmask(Interrupt::EXTI9_5); + } + NVIC::unpend(Interrupt::EXTI9_5); + // We will draw a fixed message on the screen and also a moving circle const C_RADIUS: i32 = 8; @@ -156,17 +163,12 @@ fn main() -> ! { let t = Text::new("Hello Rust!", Point::new(20, 16)) .into_styled(TextStyle::new(Font6x8, BinaryColor::On)); + // Turn the LED on via the OutputPin trait + led.on().unwrap(); // Very brief delay before the loop, just to show how delay.delay_us(10_u8); - // Enable interrupts - unsafe { - core_periph.NVIC.set_priority(Interrupt::EXTI9_5, 1); - NVIC::unmask(Interrupt::EXTI9_5); - } - NVIC::unpend(Interrupt::EXTI9_5); - let mut button_last = false; // Microcontroller programs never exit main, so we must loop! loop { @@ -194,7 +196,7 @@ fn main() -> ! { display.flush().unwrap(); // Control the horizontal position with the knob - cx = counter.max(C_RADIUS/2).min(DISPLAY_W-C_RADIUS/2); + cx = counter.max(C_RADIUS/2).min(DISPLAY_W - C_RADIUS/2); // Wrap the ball back to the top when it falls off the bottom cy += 1; if cy > (DISPLAY_H + C_RADIUS) { cy = -C_RADIUS }; @@ -220,46 +222,30 @@ fn init_encoder_pins( #[interrupt] fn EXTI9_5() { - static mut CLK: Option = None; - static mut DT: Option = None; - static mut PREV_A: bool = false; - static mut PREV_C: bool = false; + static mut ENC: Option> = None; - let clk_pin = CLK.get_or_insert_with(|| { + let enc = ENC.get_or_insert_with(|| { cortex_m::interrupt::free(|cs| { - CLK_PIN.borrow(cs).replace(None).unwrap() - }) - }); - let dt_pin = DT.get_or_insert_with(|| { - cortex_m::interrupt::free(|cs| { - DT_PIN.borrow(cs).replace(None).unwrap() + let clk = CLK_PIN.borrow(cs).replace(None).unwrap(); + let dt = DT_PIN.borrow(cs).replace(None).unwrap(); + Rotary::new(clk, dt) }) }); - if clk_pin.check_interrupt() { - let a = clk_pin.is_high().unwrap(); - let b = dt_pin.is_high().unwrap(); - // When A is changing, B should be stable - if a != *PREV_A { - *PREV_A = a; - // Capture B as C whenever it changes from C's last value - if b != *PREV_C { - *PREV_C = b; - if a == b { // Up - cortex_m::interrupt::free(|cs| { - COUNT - .borrow(cs) - .replace_with(|count| count.wrapping_add(1)) - }); - } else { // Down - cortex_m::interrupt::free(|cs| { - COUNT - .borrow(cs) - .replace_with(|count| count.wrapping_add(-1)) - }); - } - } - } - clk_pin.clear_interrupt_pending_bit(); + if enc.pin_a.check_interrupt() { + match enc.update().unwrap() { + Direction::Clockwise => cortex_m::interrupt::free(|cs| { + COUNT + .borrow(cs) + .replace_with(|count| count.wrapping_add(1)); + }), + Direction::CounterClockwise => cortex_m::interrupt::free(|cs| { + COUNT + .borrow(cs) + .replace_with(|count| count.wrapping_add(-1)); + }), + Direction::None => {} + }; + enc.pin_a.clear_interrupt_pending_bit(); } } diff --git a/src/rotary.rs b/src/rotary.rs new file mode 100644 index 0000000..17e5114 --- /dev/null +++ b/src/rotary.rs @@ -0,0 +1,59 @@ +use embedded_hal as hal; +use either::Either; +use hal::digital::v2::InputPin; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Rotary { + pub pin_a: A, + pub pin_b: B, + prev_a: bool, + prev_c: bool, +} + +/// The encoder direction is either `Clockwise`, `CounterClockwise`, or `None` +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Direction { + /// A clockwise turn + Clockwise, + /// A counterclockwise turn + CounterClockwise, + /// No change + None, +} + +impl Rotary +where + A: InputPin, + B: InputPin, +{ + pub fn new(pin_a: A, pin_b: B) -> Self { + Self { + pin_a, + pin_b, + prev_a: false, + prev_c: false, + } + } + + pub fn update(&mut self) -> Result> { + let a = self.pin_a.is_high().map_err(Either::Left)?; + let dir = if a != self.prev_a { + self.prev_a = a; + let b = self.pin_b.is_high().map_err(Either::Right)?; + if b != self.prev_c { + self.prev_c = b; + if a == b { + Direction::CounterClockwise + } else { + Direction::Clockwise + } + } else { + Direction::None + } + } else { + Direction::None + }; + Ok(dir) + } +} +