added pio-based rotary encoder reading.

This commit is contained in:
Gabe Venberg 2024-01-10 15:38:10 -06:00
parent 6f6ecc833e
commit c726ab4aa7
3 changed files with 92 additions and 2 deletions

View file

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

51
src/encoder.pio Normal file
View file

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

View file

@ -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() {