Move rotary encoder handling to a separate module

master
Levi Pearson 2020-04-15 22:57:31 -06:00
parent a498332a7c
commit 2a1a5d66df
3 changed files with 99 additions and 50 deletions

View File

@ -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

View File

@ -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<Input<Floating>>;
type DTPIN = PA9<Input<Floating>>;
@ -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<CLKPIN> = None;
static mut DT: Option<DTPIN> = None;
static mut PREV_A: bool = false;
static mut PREV_C: bool = false;
static mut ENC: Option<Rotary<CLKPIN, DTPIN>> = 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();
}
}

59
src/rotary.rs Normal file
View File

@ -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<A, B> {
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<A, B> Rotary<A, B>
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<Direction, Either<A::Error, B::Error>> {
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)
}
}