From c726ab4aa73c1102b7b881f504d354a23f14a68e Mon Sep 17 00:00:00 2001 From: Gabe Venberg Date: Wed, 10 Jan 2024 15:38:10 -0600 Subject: [PATCH] added pio-based rotary encoder reading. --- Cargo.toml | 2 ++ src/encoder.pio | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 41 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/encoder.pio diff --git a/Cargo.toml b/Cargo.toml index a8c5137..6675395 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } rp-pico = "0.8" max31855 = "0.1.0" hd44780-driver = "0.4.0" +pio-proc = "0.2" +pio = "0.2" # cargo build/run [profile.dev] diff --git a/src/encoder.pio b/src/encoder.pio new file mode 100644 index 0000000..2378209 --- /dev/null +++ b/src/encoder.pio @@ -0,0 +1,51 @@ +; from https://github.com/GitJer/Rotary_encoder/blob/master/pio_rotary_encoder.pio +.program pio_rotary_encoder +.wrap_target +.origin 0 ; The jump table has to start at 0 + ; it contains the correct jumps for each of the 16 + ; combination of 4 bits formed by A'B'AB + ; A = current reading of pin_A of the rotary encoder + ; A' = previous reading of pin_A of the rotary encoder + ; B = current reading of pin_B of the rotary encoder + ; B' = previous reading of pin_B of the rotary encoder + jmp read ; 0000 = from 00 to 00 = no change in reading + jmp CW ; 0001 = from 00 to 01 = clockwise rotation + jmp CCW ; 0010 = from 00 to 10 = counter clockwise rotation + jmp read ; 0011 = from 00 to 11 = error + + jmp CCW ; 0100 = from 01 to 00 = counter clockwise rotation + jmp read ; 0101 = from 01 to 01 = no change in reading + jmp read ; 0110 = from 01 to 10 = error + jmp CW ; 0111 = from 01 to 11 = clockwise rotation + + jmp CW ; 1000 = from 10 to 00 = clockwise rotation + jmp read ; 1001 = from 10 to 01 = error + jmp read ; 1010 = from 10 to 10 = no change in reading + jmp CCW ; 1011 = from 10 to 11 = counter clockwise rotation + + jmp read ; 1100 = from 11 to 00 = error + jmp CCW ; 1101 = from 11 to 01 = counter clockwise rotation + jmp CW ; 1110 = from 11 to 10 = clockwise rotation + jmp read ; 1111 = from 11 to 11 = no change in reading + +pc_start: ; this is the entry point for the program + in pins 2 ; read the current values of A and B and use + ; them to initialize the previous values (A'B') +read: + mov osr isr ; the OSR is (after the next instruction) used to shift + ; the two bits with the previous values into the ISR + out isr 2 ; shift the previous value into the ISR. This also sets + ; all other bits in the ISR to 0 + in pins 2 ; shift the current value into the ISR + ; the 16 LSB of the ISR now contain 000000000000A'B'AB + ; this represents a jmp instruction to the address A'B'AB + mov exec isr ; do the jmp encoded in the ISR +CW: ; a clockwise rotation was detected + irq 0 ; signal a clockwise rotation via an IRQ + jmp read ; jump to reading the current values of A and B +CCW: ; a counter clockwise rotation was detected + irq 1 ; signal a counter clockwise rotation via an IRQ +; jmp read ; jump to reading the current values of A and B. + ; the jmp isn't needed because of the .wrap, and the first + ; statement of the program happens to be a jmp read +.wrap diff --git a/src/main.rs b/src/main.rs index 6460b9b..69e39ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,14 +28,15 @@ mod app { Gpio0, Gpio1, Gpio14, Gpio16, Gpio17, Gpio18, Gpio19, Gpio2, Gpio20, Gpio21, Gpio25, Gpio3, Gpio4, Gpio5, }, - FunctionSio, FunctionSpi, FunctionUart, Pin, PullDown, SioOutput, + FunctionPio0, FunctionSio, FunctionSpi, FunctionUart, Pin, PullDown, SioOutput, }, + pio::PIOExt, sio::Sio, spi::{self, Spi}, uart::{self, UartConfig, UartPeripheral}, watchdog::Watchdog, }, - pac::{SPI0, UART0}, + pac::{self, SPI0, UART0}, Pins, }; @@ -119,6 +120,7 @@ mod app { #[init] fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) { let mut watchdog = Watchdog::new(ctx.device.WATCHDOG); + let mut pac = pac::Peripherals::take().unwrap(); let sio = Sio::new(ctx.device.SIO); // External high-speed crystal on the pico board is 12Mhz @@ -212,6 +214,25 @@ mod app { ) .unwrap(); + let rotary_clk: Pin<_, FunctionPio0, _> = pins.gpio11.into_function(); + let rotary_clk_id = rotary_clk.id().num; + let rotary_dt: Pin<_, FunctionPio0, _> = pins.gpio12.into_function(); + let rotary_dt_id = rotary_dt.id().num; + let program = pio_proc::pio_file!("src/encoder.pio"); + + let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); + pio.irq0().enable_sm_interrupt(0); + pio.irq1().enable_sm_interrupt(1); + let installed = pio.install(&program.program).unwrap(); + let (mut sm, _, _) = rp_pico::hal::pio::PIOBuilder::from_program(installed) + .set_pins(rotary_clk_id, 2) + .build(sm0); + sm.set_pindirs([ + (rotary_clk_id, rp_pico::hal::pio::PinDir::Output), + (rotary_dt_id, rp_pico::hal::pio::PinDir::Output), + ]); + sm.start(); + let mono = Rp2040Mono::new(ctx.device.TIMER); heartbeat::spawn().unwrap(); @@ -234,6 +255,22 @@ mod app { ) } + #[task(binds = PIO0_IRQ_0, shared = [target_temp])] + fn inc_target(mut ctx: inc_target::Context) { + ctx.shared.target_temp.lock(|t| { + *t += 1.0; + }); + update_display::spawn().unwrap(); + } + + #[task(binds = PIO0_IRQ_1, shared = [target_temp])] + fn dec_target(mut ctx: dec_target::Context) { + ctx.shared.target_temp.lock(|t| { + *t -= 1.0; + }); + update_display::spawn().unwrap(); + } + #[task(local = [thermocouple], shared = [recent_temp, uart])] fn read_temp(mut ctx: read_temp::Context) { match ctx.local.thermocouple.read_temp() {