119 lines
4.0 KiB
Plaintext
119 lines
4.0 KiB
Plaintext
; --------------------------------------------------
|
|
; Quadrature Encoder reader using PIO
|
|
; by Christopher (@ZodiusInfuser) Parrott
|
|
; --------------------------------------------------
|
|
;
|
|
; Watches any two pins (i.e. do not need to be consecutive) for
|
|
; when their state changes, and pushes that new state along with
|
|
; the old state, and time since the last change.
|
|
;
|
|
; - X is used for storing the last state
|
|
; - Y is used as a general scratch register and for storing the current state
|
|
; - OSR is used for storing the state-change timer
|
|
;
|
|
; After data is pushed into the system, a long delay takes place
|
|
; as a form of switch debounce to deal with rotary encoder dials.
|
|
; This is currently set to 500 cycles, but can be changed using the
|
|
; debounce constants below, as well as adjusting the frequency the PIO
|
|
; state machine runs at. E.g. a freq_divider of 250 gives a 1ms debounce.
|
|
|
|
|
|
; Debounce Constants
|
|
; --------------------------------------------------
|
|
.define SET_CYCLES 10
|
|
.define ITERATIONS 30
|
|
.define JMP_CYCLES 16
|
|
.define public ENC_DEBOUNCE_CYCLES (SET_CYCLES + (JMP_CYCLES * ITERATIONS))
|
|
|
|
; Ensure that ENC_DEBOUNCE_CYCLES is a multiple of the number of cycles the
|
|
; wrap takes, which is currently 10 cycles, otherwise timing may be inaccurate
|
|
|
|
|
|
; Encoder Program
|
|
; --------------------------------------------------
|
|
.program encoder
|
|
|
|
.wrap_target
|
|
loop:
|
|
; Copy the state-change timer from OSR, decrement it, and save it back
|
|
mov y, osr
|
|
jmp y-- osr_dec
|
|
osr_dec:
|
|
mov osr, y
|
|
; takes 3 cycles
|
|
|
|
; Read the state of both encoder pins and check if they are different from the last state
|
|
jmp pin encA_was_high
|
|
mov isr, null
|
|
jmp read_encB
|
|
encA_was_high:
|
|
set y, 1
|
|
mov isr, y
|
|
read_encB:
|
|
in pins, 1
|
|
mov y, isr
|
|
jmp x!=y state_changed [1]
|
|
; takes 7 cycles on both paths
|
|
.wrap
|
|
|
|
state_changed:
|
|
; Put the last state and the timer value into ISR alongside the current state,
|
|
; and push that state to the system. Then override the last state with the current state
|
|
in x, 2
|
|
mov x, ~osr ; invert the timer value to give a sensible value to the system
|
|
in x, 28
|
|
push noblock ; this also clears isr
|
|
mov x, y
|
|
|
|
; Perform a delay to debounce switch inputs
|
|
set y, (ITERATIONS - 1) [SET_CYCLES - 1]
|
|
debounce_loop:
|
|
jmp y-- debounce_loop [JMP_CYCLES - 1]
|
|
|
|
; Initialise the timer, as an inverse, and decrement it to account for the time this setup takes
|
|
mov y, ~null
|
|
jmp y-- y_dec
|
|
y_dec:
|
|
mov osr, y
|
|
jmp loop [1]
|
|
;takes 10 cycles, not counting whatever the debounce adds
|
|
|
|
|
|
; Initialisation Code
|
|
; --------------------------------------------------
|
|
% c-sdk {
|
|
#include "hardware/clocks.h"
|
|
|
|
static const uint8_t ENC_LOOP_CYCLES = encoder_wrap - encoder_wrap_target;
|
|
|
|
//The time that the debounce takes, as the number of wrap loops that the debounce is equivalent to
|
|
static const uint8_t ENC_DEBOUNCE_TIME = ENC_DEBOUNCE_CYCLES / ENC_LOOP_CYCLES;
|
|
|
|
|
|
static inline void encoder_program_init(PIO pio, uint sm, uint offset, uint pinA, uint pinB, uint16_t divider) {
|
|
pio_sm_config c = encoder_program_get_default_config(offset);
|
|
|
|
sm_config_set_jmp_pin(&c, pinA);
|
|
sm_config_set_in_pins(&c, pinB);
|
|
sm_config_set_in_shift(&c, false, false, 1);
|
|
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
|
pio_gpio_init(pio, pinA);
|
|
pio_gpio_init(pio, pinB);
|
|
gpio_pull_up(pinA);
|
|
gpio_pull_up(pinB);
|
|
pio_sm_set_consecutive_pindirs(pio, sm, pinA, 1, 0);
|
|
pio_sm_set_consecutive_pindirs(pio, sm, pinB, 1, 0);
|
|
sm_config_set_clkdiv_int_frac(&c, divider, 0);
|
|
pio_sm_init(pio, sm, offset, &c);
|
|
}
|
|
|
|
static inline void encoder_program_start(PIO pio, uint sm, bool stateA, bool stateB) {
|
|
pio_sm_exec(pio, sm, pio_encode_set(pio_x, (uint)stateA << 1 | (uint)stateB));
|
|
pio_sm_set_enabled(pio, sm, true);
|
|
}
|
|
|
|
static inline void encoder_program_release(PIO pio, uint sm) {
|
|
pio_sm_set_enabled(pio, sm, false);
|
|
pio_sm_unclaim(pio, sm);
|
|
}
|
|
%} |