News for hackers. Stuff that matters.
Re:A closer look at Raspberry Pi RP2040 Programmable IOs by MatthiasWandel on Tuesday February 16, 2021 @01:52PM (#26157349)
The question was not how quadrature encoding works, but how to implement it with the limited PIO instructions
Re:A closer look at Raspberry Pi RP2040 Programmable IOs by dragontamer on Tuesday February 16, 2021 @02:59PM (#26158072)
Hmmm.

Any I/O is basically compression. So lets think about what we're actually outputting. We're converting a raw bitstream from two GPIO pins paired (Ex: 00, 01, 01, 01, 11, 11, 10, 00, 01, 11), into a singular message: such as +6.

There are two applications of a quadrature encoder that I can think of.

1. Virtual Pot -- Converting the messages into a kind of "-100 to +100 slider", such as a volume control knob. In this case, you want a regular update schedule at human-interface speeds (~1000 updates / second or slower).

2. Rotational Velocity sensor -- Converting the messages into a speed. You're willing to batch the bitstream up as slowly as possible, maybe waiting for a rollover event (+128 or -128). And you just interrupt on those overflows.

-------

Since the input format is already set, we now think about the output format: how to represent +6 and or -12?

Based on the PIO instruction set, it seems like +6 and -12 probably would be easiest as two separate registers in two different state-machines.

Every time an "increment" is detected, the FIFO-associated with +1 gets a +1 bit added to it. Every time a "decrement" is detected, the 2nd FIFO associated with -1 gets a +1 bit added to it.

----

In pseudocode:

    IncrementSide(){
        currentState = 00; // default

        while(1){
            nextState = in(GPIO0) | in(GPIO1);
            if(currentState == 00 and nextState == 01){
                push 1;
            }
            if(currentState == 01 and nextState == 11){
                push 1;
            }
            if(currentState == 11 and nextState == 10){
                push 1;
            }
            if(currentState == 10 and nextState == 00){
                push 1;
            }
            currentState = nextState;
        }
    }
"nextState" and "currentState" probably is just the X and Y registers of the PIO state machines. The above is "very pseudocode" as I don't really understand PIO yet.

I'm saying "push 1", but "push 1 into the OSR". It looks like the OSR has some kind of auto-push mechanism, but if that doesn't work then a manual-push might be needed in the code proper. Hard to say from the docs alone.

DecrementSide would be just the inverted if-statements:

            if(currentState == 00 and nextState == 11){
                push 1; // Decrement side checking for a decrement
            }
--------

This above methodology would only work for #2 ("velocity"), and fails for #1 ("positional"), because the increment-side and decrement-side would be updating at varying rates.

I probably can make a positional-decoder instead of a velocity one using similar principles. Or maybe by somehow ensuring that the +1 and -1 messages go into the same FIFO to be picked up by the host CPU.

The idea is route "0" messages to /dev/null, compressing the input stream. The host still sees the important +1 and -1 messages. The exact mechanism for that is up for debate, but the simple if-else state machine above seems to accomplish that to a limited extent.

------

EDIT: Hmmm, maybe a singular FIFO can be done if we have push1 and push0 for the two kinds of messages: 1 representing +1 and 0 representing -1.

Anyway, my point is that there's lots of solutions here. It doesn't seem very difficult to me conceptually. Just a lot of experimentation needed to know exactly how those 9 instructions work and the exact mechanisms of the ISR / OSR / X reg / Y reg.