Move rotary encoder handling to a separate module
parent
a498332a7c
commit
2a1a5d66df
|
@ -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
|
||||
|
|
78
src/main.rs
78
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<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| {
|
||||
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))
|
||||
});
|
||||
} else { // Down
|
||||
cortex_m::interrupt::free(|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))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
clk_pin.clear_interrupt_pending_bit();
|
||||
.replace_with(|count| count.wrapping_add(-1));
|
||||
}),
|
||||
Direction::None => {}
|
||||
};
|
||||
enc.pin_a.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