Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 09:02 05 May 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : PIO for quadrature encoders

     Page 1 of 3    
Author Message
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 02:21pm 24 Apr 2024
Copy link to clipboard 
Print this post

I seem to remember that this wasn't possible with the PicoMite but I don't remember what the limitation was(?):


# SPDX-FileCopyrightText: 2022 Jamon Terrell <github@jamonterrell.com>
# SPDX-License-Identifier: MIT

from rp2 import PIO, StateMachine, asm_pio
from machine import Pin
import utime
@asm_pio(autopush=True, push_thresh=32)
def encoder():
   label("start")
   wait(0, pin, 0)         # Wait for CLK to go low
   jmp(pin, "WAIT_HIGH")   # if Data is low
   mov(x, invert(x))           # Increment X
   jmp(x_dec, "nop1")
   label("nop1")
   mov(x, invert(x))
   label("WAIT_HIGH")      # else
   jmp(x_dec, "nop2")          # Decrement X
   label("nop2")
   
   wait(1, pin, 0)         # Wait for CLK to go high
   jmp(pin, "WAIT_LOW")    # if Data is low
   jmp(x_dec, "nop3")          # Decrement X
   label("nop3")
   
   label("WAIT_LOW")       # else
   mov(x, invert(x))           # Increment X
   jmp(x_dec, "nop4")
   label("nop4")
   mov(x, invert(x))
   wrap()

   
sm1 = StateMachine(1, encoder, freq=125_000_000, in_base=Pin(3), jmp_pin=Pin(2))
sm1.active(1)

while(True):
   utime.sleep(1)
   sm1.exec("in_(x, 32)")
   x = sm1.get()
   print(x)


So I would need to switch to Python for this?

Github Link
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 02:51pm 24 Apr 2024
Copy link to clipboard 
Print this post

Phenix,

Yes, you can switch to python. This is RP2040 PIO/python code.
That makes life easier.

But..

This code is directly portable to MMBasic on the picomite.
1/ There is a built in PIO assembler. Instructions may not be exactly identical, but it works fine.
2/ There (also) is a "direct ecexute PIO instruction from MMBasic"
3/ And you can read the PIO FIFO to get results.

But AFAIK, you are programming on CMM2, and CMM2 does not have a PIO.

Regards,

Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 03:09pm 24 Apr 2024
Copy link to clipboard 
Print this post

  Volhout said  Phenix,

Yes, you can switch to python. This is RP2040 PIO/python code.
That makes life easier.

But..

This code is directly portable to MMBasic on the picomite.
1/ There is a built in PIO assembler. Instructions may not be exactly identical, but it works fine.
2/ There (also) is a "direct ecexute PIO instruction from MMBasic"
3/ And you can read the PIO FIFO to get results.

But AFAIK, you are programming on CMM2, and CMM2 does not have a PIO.

Regards,

Volhout


Actually no. Just the PicoMite and the ARMmite. I'd like this PIO routine to work with the PicoMite  
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3663
Posted: 05:54pm 24 Apr 2024
Copy link to clipboard 
Print this post

It will already work with Picomite. (Some syntax tweaks, sure.)

John
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 06:56pm 24 Apr 2024
Copy link to clipboard 
Print this post

  JohnS said  It will already work with Picomite. (Some syntax tweaks, sure.)

John


Thanks John, exciting stuff  

Will try to lock-out the weekend to study and experiment.

Any clue regarding maximum pulse frequency to expect? I have seen some impressive numbers from Harm in the past but these Octoquad guys are talking a measly 250KHz, albeit with eight encoders (16 pulse trains).

They do state, however, that they haven't tested the limit.
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3663
Posted: 07:07pm 24 Apr 2024
Copy link to clipboard 
Print this post

I've no idea about max freq, but does the code actually already do everything you want because if it doesn't you may be speeding it up or slowing it down.  Beware of needing too many (PIO) program steps - each PIO is limited.

John
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 07:33pm 24 Apr 2024
Copy link to clipboard 
Print this post

Yeah, I only need to read one encoder but it would be nice to have a second one for an optional feature.

In actual fact, 250KHz=1M quadrature cts/sec which is plenty fast. A linear axis can achieve a velocity of 1000mm/sec with a position resolution of 1µm.

It was just that; I seem to remember Harm achieving much higher pulse rates.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 05:11am 25 Apr 2024
Copy link to clipboard 
Print this post

Phenix,

The PIO has 4 statemachines. When clocked at 133 Mhz, they easily decode a 10Mhz quadrature encoder..
Each. So you can run up to 8 on a single pico.
Question is what you do with it.
A pico may run a 1ms PID, but maybe not for 8 channels.

Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 05:36am 25 Apr 2024
Copy link to clipboard 
Print this post

I've been a proponent of Dual-Loop Feedback for several decades, now.

Not always practical but if there is a need for true precision and stability, I highly recommend it.

The industry has kinda gone backwards, over the years. Modern BLMs have an encoder fixed to the motor shaft and so this is utilized for position feedback.
The problem is that; only the motor shaft-angle is controlled. It's assumed that the mechanical drive-train is backlash-free (never the case).
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 05:50am 25 Apr 2024
Copy link to clipboard 
Print this post

@Volhout

10MHz makes much more sense, considering sysclock and asm code.

Very cool indeed  
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 07:17am 25 Apr 2024
Copy link to clipboard 
Print this post

Phenix,

When you are looking into the code, let me try to explain how I read their code, and think it works.

A PIO can run a small program but has a limitted instruction set. A PIO has 2 register that can be used for counting: X and Y. Which ever one you choose does not matter. But it can ONLY DECREMENT, not increment.

For increment they INVERT the register, decrement, then invert back. This is a 1's complement instruction. Meaning that this way of calculating has typically 2 zero values (0, and &hFFFFFFFF) . All registers are 32 bit.

The ARM can talk to the PIO using FIFO's. MMBasic can write values in the FIFO's, and the PIO can take the values, and vice versa. Every PIO has 4 state machines, and each state machine has 2 FIFO's, 4 deep. Default one for READ and one fro WRITE (but you can chain them to create one that is 8 deep).

The program in the state machine does following:

start
1/check quadrature inputs
2/increment -or- decrement X register
3/copy X to output-register
4/push output-register in out-FIFO
5/read in-FIFO to input register (* see note)
go to start

The loop time determines the the maximum quadrature frequency.
1/ and 2/ are required to run the quadrature decoder
3/ and 4/ is needed because X cannot be copied to the FIFO directly
This way MMBasic can at any time pull data from the FIFO and get the quadrature count.
5/ is needed to the ZERO-ing.

MMBasic can instruct PIO to execute direct commands. But since MMBasic is "slow" and PIO is "fast" it is not possible to let PIO execute 2 successive commands. Only 1 command can be executed. The command is "copy input-register to X". In case you write 0 to the in-FIFO, the program will set the value 0 ready in the input-register. So when you execute the direct command, you will effectively zero the X register.

But of coarse you can also write a different value to the FIFO (in example &h80000000) and use that as ZERO reference. Then quadrature decoding will work from there.

Because the direct command is executed immediately, this can also happen in the increment instruction (INVERT-DECREMENT-INVERT) when X is inverted. Confusing the algorithm. That is why it is essential to only ZERO the system when the associated axis is halted.

1/ can be simple decoding a single edge, or dual edge, or dual edge on both I and Q signals. The latter gives better resolution, but you need to run the loop 4x more often, so the quadrature speed goes down.

Once you have this program in PIO memory, each of the 4 state machines can use the exact same code. Each state machine has it's own IO pin definition, it's own FIFO's, and it's own clock speed. So you can run one decoder at 100MHz, and onother one ia 100kHz (to use for a rotary encoder on the user interface, not on the machine at all).

Note: FIFO is 4 deep. So the most recent quadrature count is at position 4 in FIFO, You have to read all 4, and skip the first 3, only use the 4'th value as it is most recent.

Regards,

Volhout
Edited 2024-04-25 17:23 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 08:34am 25 Apr 2024
Copy link to clipboard 
Print this post

@Volhout

Very much appreciated, thank you.

Your timing was perfect because I was looking at This Issue


  Quote  
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.


  Quote  
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


  Quote  
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.


Not sure that this is going to be robust enough. Don't want to be destroying mega-buck machinery.  
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 09:29am 25 Apr 2024
Copy link to clipboard 
Print this post

Phenix,

I have an idea what caused their problem. You can also use a direct command to get the X value. And when you apply that at the wrong moment you see exactly what they identify. And yes, using Y solves that issue.

I am sure this code can be made robust. Essential is that you supply the PIO pins with good logic level signals. I have great confidence in PIO, once it runs, it simply works. I think of it more as "hardware". Like an FPGA. And it is pretty much that.

Volhout
Edited 2024-04-25 19:30 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 10:05am 25 Apr 2024
Copy link to clipboard 
Print this post

  Volhout said  Phenix,

I have an idea what caused their problem. You can also use a direct command to get the X value. And when you apply that at the wrong moment you see exactly what they identify. And yes, using Y solves that issue.

I am sure this code can be made robust. Essential is that you supply the PIO pins with good logic level signals. I have great confidence in PIO, once it runs, it simply works. I think of it more as "hardware". Like an FPGA. And it is pretty much that.

Volhout


Thanks for the reassurance  

Signal interfacing is via my long-time-standard MC3486 Differential Receiver because my encoders feature differential drivers.


mc3486.pdf
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 10:47am 25 Apr 2024
Copy link to clipboard 
Print this post

Phenix,

Just a suggestion:

For the quadrature decoder, the code they show is decoding 1 phase out of 4. Decoding 2 phases out of 4 is also simple (you use 1 signal as a clock, the other as data). Decoding 4 phases is more tricky.

So I would start the effort with 1/4 or 2/4 decoding. And once you have that running (including the PID on Pico), you can start thinking about 4/4 decoding. 4/4 is complexer PIO code, and with same mechanics, lays higher burden on the whole system.

Regards,

Volhout
Edited 2024-04-25 20:48 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 10:57am 25 Apr 2024
Copy link to clipboard 
Print this post

@Volhout

Just throwing thoughts at you because I realise that you understand this stuff.

After good results with the H7, I decided to try the Pico because I'm shooting for a design that can use the most readily available components and it doesn't get more ubiquitus than the Pico.

A PID loop-rate of 1KHz is the generally accepted rate for 99.9% of applications because mechanical time-constants are comparatively low:

Mechanical time constant is defined as the minimum time that an axis takes to reach 63% of target velocity. The loop rate that is recommended to be 5-10 times faster (way more than Nyquist).

The sizes of machinery that I work with, these numbers are crazy. When coupled to a real load and with inertia, friction, stiction to contend with, a much lower sampling rate can still be a waste of processor time. The PicoMite can handle 1KHz but I have settled for 500HZ (the default for BeckHoff's TwinCAT and other big players).

So, If I can eliminate my current LS7366 (quad counter) which I source from the US, all the better.

Even using separate PicoMite processors, one-per-axis, coordinated motion over a serial-link can be extremely accurate. Only 8-bit addressing though so limited to 255 axes      
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3663
Posted: 11:02am 25 Apr 2024
Copy link to clipboard 
Print this post

  PhenixRising said  Not sure that this is going to be robust enough. Don't want to be destroying mega-buck machinery.  

Wrong code is wrong code.

John
Edited 2024-04-25 21:04 by JohnS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 11:06am 25 Apr 2024
Copy link to clipboard 
Print this post

  Volhout said  Phenix,

Just a suggestion:

For the quadrature decoder, the code they show is decoding 1 phase out of 4. Decoding 2 phases out of 4 is also simple (you use 1 signal as a clock, the other as data). Decoding 4 phases is more tricky.

So I would start the effort with 1/4 or 2/4 decoding. And once you have that running (including the PID on Pico), you can start thinking about 4/4 decoding. 4/4 is complexer PIO code, and with same mechanics, lays higher burden on the whole system.

Regards,

Volhout


Hmmm, some of the older machines that I retrofit, only have 10 pulses/mm but thanks to quadrature, I have X4 resolution.

OTOH, the newer motors that I am looking at have 17-bits/rev   so I would be looking at scaling, anyway.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3558
Posted: 11:13am 25 Apr 2024
Copy link to clipboard 
Print this post

This code basically is a 2/4 decoder (signal B is clock, A is data).

.program quadrature
start:
   wait 0 pin 0            ; wait for B == 0
   jmp PIN, wait_high      ; if A == 0
   mov y, !x                   ; x++ {
   jmp y--, nop1               ;
nop1:                           ;
   mov x, !y                   ; }
   jmp nop2
wait_high:
   jmp x--, nop2           ; x-- {
nop2:                       ; }

   wait 1 PIN 0            ; wait for B == 1
   jmp PIN, wait_low       ; if A == 0
   jmp x--, start               ; x-- {
                              ; }
wait_low:                   ; else
   mov y, !x                   ; x++ {
   jmp y--, nop4               ;
nop4:                           ;
   mov x, !y                   ;
   jmp start                   ; }


It uses X to count, and Y in the invert-dec-invert.

I have also found a 4/4 piece of code, on Github. This may work, although it uses the Y register and X register. So you will have to stop the axis for zero-ing.

; Must start at 0 so that the following jump table can be jumped into with a
; 'mov pc, isr' instruction.
.origin 0
   ; 16 element jump table based on 4-bit encoder last state and current state.
   jmp delta0      ; 00-00
   jmp minus1      ; 00-01
   jmp plus1       ; 00-10
   jmp delta0      ; 00-11
   jmp plus1       ; 01-00
   jmp delta0      ; 01-01
   jmp delta0      ; 01-10
   jmp minus1      ; 01-11
   jmp minus1      ; 10-00
   jmp delta0      ; 10-01
   jmp delta0      ; 10-10
   jmp plus1       ; 10-11
   jmp delta0      ; 11-00
   jmp plus1       ; 11-01
   jmp minus1      ; 11-10
   jmp delta0      ; 11-11

; Program actually starts here.
.wrap_target
delta0:
public start:
   mov isr, null       ; Make sure that the input shift register is cleared when table jumps to delta0.
   in y, 2             ; Upper 2-bits of address are formed from previous encoder pin readings
   mov y, pins         ; Lower 2-bits of address are formed from current encoder pin readings. Save in Y as well.
   in y, 2
   mov pc, isr         ; Jump into jump table which will then jump to delta0, minus1, or plus1 labels.
minus1:
   jmp x-- output      ; Decrement x
   jmp output
plus1:
   mov x, ~x           ; Increment x by calculating x=~(~x - 1)
   jmp x-- next2
next2:
   mov x, ~x
output:
   mov isr, x          ; Push out updated counter.
   push noblock
.wrap


Actually quite smart use of the PIO capabilities. Uses most of the PIO memory though (28/32) becuase the large jump table.

Success with programming...

Volhout
Edited 2024-04-25 21:34 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 309
Posted: 11:52am 25 Apr 2024
Copy link to clipboard 
Print this post

Oooo, I like the jump-table. I do the same thing, elsewhere.

Very cool and very much appreciated. Gonna clear my desk and start playing.
 
     Page 1 of 3    
Print this page
© JAQ Software 2024