pi-pico-pio-quadrature-encoder
pi-pico-pio-quadrature-encoder copied to clipboard
Reading the position sometimes returns values near 2^32
I'm trying to use this project to read an encoder with an Adafruit Feather RP2040. Here's my test program:
#include "hardware/pio.h"
#include "quadrature.pio.h"
PIO pio = pio0;
uint sm;
void setup() {
uint offset = pio_add_program(pio, &quadrature_program);
sm = pio_claim_unused_sm(pio, true);
quadrature_program_init(pio, sm, offset, D10, D9);
Serial.begin(115200);
while (!Serial);
Serial.println("Hello!");
}
void loop() {
static uint pos = 0;
pio_sm_exec_wait_blocking(pio, sm, pio_encode_in(pio_x, 32));
uint newPos = pio_sm_get_blocking(pio, sm);
if (pos != newPos) {
Serial.print("Pos = ");
Serial.print(newPos);
Serial.println();
pos = newPos;
}
}
It generally works but as I rotate the encoder I see weird values:
Pos = 157
Pos = 158
Pos = 159
Pos = 4294967136
Pos = 159
Pos = 160
Pos = 159
That value (4294967136) is 2^32 - 160, so I think there's some weird range or sign error or something.
it initializes to 0, so if you go counterclockwise, you'll get a negative number. Change your "uint" to "int" and I think that will make it show as the negative number if you want that.
The issue occurs when I’m rotating it clockwise. The number will suddenly jump then go back. Looking at the PIO code I’m pretty sure it’s because the fetch instruction gets executed in between the mov x, !x
operations
ah--that makes sense. Maybe I'll add a DMA enabled version where it pushes an update on every change and you just DMA it to a memory location that you can read from code whenever you like.
there's a much easier fix: don't invert the X register and use Y to do the increment:
mov Y, !X
jmp Y--, next
mov X, !Y
This way the value in X is always good to be read.
Also, the BUG comment in the PIO code really is a bug: the code likely only increments 1 per 4 steps instead of 1 per 2 steps, because in half the inc/dec operations the code runs through both and leaves X with the previous value.
I've switched to using the shift register:
.program quadrature0
.wrap_target
wait 0 PIN 0
in PINS, 2
wait 1 PIN 0
in PINS, 2
.wrap
.program quadrature1
.wrap_target
wait 0 PIN 1
in PINS, 2
wait 1 PIN 1
in PINS, 2
.wrap
Thus all my code needs to do is read the FIFOs. At one point I was using ISRs instead but Arduino has its own special interrupt code and I didn't care enough to figure out how to get that to play nice with the PIO interrupts.
@firelizzard18 --that's definitely a simpler solution, but for the sake of others reading the issue--the main difference in implementation is that the one in this repo does not require active work by the processor--it is fully asynchronous and happens in the background. The one mentioned in the comment above requires that the processor read it frequently to avoid skipping steps, and is time-sensitive (not a knock on the solution, just writing this so that less experienced readers understand the reasons for the complexity)
I'm just sharing my code in case it's useful to someone else. My use case is sending USB keyboard events when the encoder ticks so I have no need for a counter.