Move rotary encoder handling to a separate module
parent
a498332a7c
commit
2a1a5d66df
|
@ -30,6 +30,10 @@ features = ["stm32f103", "rt"]
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
features = ["rt", "stm32f103", "medium"]
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[dependencies.either]
|
||||||
|
default-features = false
|
||||||
|
version = "1.5.3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "blue_pill_more"
|
name = "blue_pill_more"
|
||||||
test = false
|
test = false
|
||||||
|
|
86
src/main.rs
86
src/main.rs
|
@ -35,8 +35,6 @@ use stm32f1xx_hal::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use embedded_hal::digital::v2::InputPin;
|
|
||||||
|
|
||||||
use switch_hal::{IntoSwitch, InputSwitch, OutputSwitch, ToggleableOutputSwitch};
|
use switch_hal::{IntoSwitch, InputSwitch, OutputSwitch, ToggleableOutputSwitch};
|
||||||
|
|
||||||
use ssd1306::{
|
use ssd1306::{
|
||||||
|
@ -54,6 +52,9 @@ use embedded_graphics::{
|
||||||
|
|
||||||
use arrayvec::ArrayString;
|
use arrayvec::ArrayString;
|
||||||
|
|
||||||
|
mod rotary;
|
||||||
|
use rotary::{Direction, Rotary};
|
||||||
|
|
||||||
type CLKPIN = PA8<Input<Floating>>;
|
type CLKPIN = PA8<Input<Floating>>;
|
||||||
type DTPIN = PA9<Input<Floating>>;
|
type DTPIN = PA9<Input<Floating>>;
|
||||||
|
|
||||||
|
@ -104,6 +105,8 @@ fn main() -> ! {
|
||||||
let sw = gpioa.pa10
|
let sw = gpioa.pa10
|
||||||
.into_floating_input(&mut gpioa.crh)
|
.into_floating_input(&mut gpioa.crh)
|
||||||
.into_active_low_switch();
|
.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);
|
init_encoder_pins(clk, dt, &mut afio, &dev_periph.EXTI);
|
||||||
|
|
||||||
// Configure pin C13 to drive the "PC13" LED as an active-low switch
|
// 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_push_pull_output(&mut gpioc.crh)
|
||||||
.into_active_low_switch();
|
.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 scl = gpiob.pb10.into_alternate_open_drain(&mut gpiob.crh);
|
||||||
let sda = gpiob.pb11.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,
|
1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Turn the LED on via the OutputPin trait
|
|
||||||
led.on().unwrap();
|
|
||||||
|
|
||||||
// Initialize the display
|
// Initialize the display
|
||||||
let mut display: GraphicsMode<_> = Builder::new()
|
let mut display: GraphicsMode<_> = Builder::new()
|
||||||
.with_i2c_addr(0x3c)
|
.with_i2c_addr(0x3c)
|
||||||
|
@ -144,6 +144,13 @@ fn main() -> ! {
|
||||||
// Every set of commands to the display is buffered until we flush it
|
// Every set of commands to the display is buffered until we flush it
|
||||||
display.flush().unwrap();
|
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
|
// We will draw a fixed message on the screen and also a moving circle
|
||||||
|
|
||||||
const C_RADIUS: i32 = 8;
|
const C_RADIUS: i32 = 8;
|
||||||
|
@ -156,17 +163,12 @@ fn main() -> ! {
|
||||||
let t = Text::new("Hello Rust!", Point::new(20, 16))
|
let t = Text::new("Hello Rust!", Point::new(20, 16))
|
||||||
.into_styled(TextStyle::new(Font6x8, BinaryColor::On));
|
.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
|
// Very brief delay before the loop, just to show how
|
||||||
delay.delay_us(10_u8);
|
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;
|
let mut button_last = false;
|
||||||
// Microcontroller programs never exit main, so we must loop!
|
// Microcontroller programs never exit main, so we must loop!
|
||||||
loop {
|
loop {
|
||||||
|
@ -194,7 +196,7 @@ fn main() -> ! {
|
||||||
display.flush().unwrap();
|
display.flush().unwrap();
|
||||||
|
|
||||||
// Control the horizontal position with the knob
|
// 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
|
// Wrap the ball back to the top when it falls off the bottom
|
||||||
cy += 1;
|
cy += 1;
|
||||||
if cy > (DISPLAY_H + C_RADIUS) { cy = -C_RADIUS };
|
if cy > (DISPLAY_H + C_RADIUS) { cy = -C_RADIUS };
|
||||||
|
@ -220,46 +222,30 @@ fn init_encoder_pins(
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn EXTI9_5() {
|
fn EXTI9_5() {
|
||||||
static mut CLK: Option<CLKPIN> = None;
|
static mut ENC: Option<Rotary<CLKPIN, DTPIN>> = None;
|
||||||
static mut DT: Option<DTPIN> = None;
|
|
||||||
static mut PREV_A: bool = false;
|
|
||||||
static mut PREV_C: bool = false;
|
|
||||||
|
|
||||||
let clk_pin = CLK.get_or_insert_with(|| {
|
let enc = ENC.get_or_insert_with(|| {
|
||||||
cortex_m::interrupt::free(|cs| {
|
cortex_m::interrupt::free(|cs| {
|
||||||
CLK_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)
|
||||||
let dt_pin = DT.get_or_insert_with(|| {
|
|
||||||
cortex_m::interrupt::free(|cs| {
|
|
||||||
DT_PIN.borrow(cs).replace(None).unwrap()
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if clk_pin.check_interrupt() {
|
if enc.pin_a.check_interrupt() {
|
||||||
let a = clk_pin.is_high().unwrap();
|
match enc.update().unwrap() {
|
||||||
let b = dt_pin.is_high().unwrap();
|
Direction::Clockwise => cortex_m::interrupt::free(|cs| {
|
||||||
// When A is changing, B should be stable
|
COUNT
|
||||||
if a != *PREV_A {
|
.borrow(cs)
|
||||||
*PREV_A = a;
|
.replace_with(|count| count.wrapping_add(1));
|
||||||
// Capture B as C whenever it changes from C's last value
|
}),
|
||||||
if b != *PREV_C {
|
Direction::CounterClockwise => cortex_m::interrupt::free(|cs| {
|
||||||
*PREV_C = b;
|
COUNT
|
||||||
if a == b { // Up
|
.borrow(cs)
|
||||||
cortex_m::interrupt::free(|cs| {
|
.replace_with(|count| count.wrapping_add(-1));
|
||||||
COUNT
|
}),
|
||||||
.borrow(cs)
|
Direction::None => {}
|
||||||
.replace_with(|count| count.wrapping_add(1))
|
};
|
||||||
});
|
enc.pin_a.clear_interrupt_pending_bit();
|
||||||
} else { // Down
|
|
||||||
cortex_m::interrupt::free(|cs| {
|
|
||||||
COUNT
|
|
||||||
.borrow(cs)
|
|
||||||
.replace_with(|count| count.wrapping_add(-1))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clk_pin.clear_interrupt_pending_bit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue