Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 20:41 18 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 2 of 3    
Author Message
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 08:23pm 25 Apr 2024
Copy link to clipboard 
Print this post

Hi Phenix,

I was so excited that I had to solve this issue. I hope you are not offended, but here is a working set of code you can use as a basis.

This is a Quadrature generator in MMBasic running at the pico (in my case a GEEK) that uses gp2 and gp3 as quadrature outputs.
It goes 1000 counts (each count has 4 phases) up, and 1000 counts down.

 'geek Q-generator

 del=0.1  'delay per state

 SetPin gp2,dout
 SetPin gp3,dout

 Do
   '1000 turns left
   For i=1 To 1000
     Port(4,2)=&b00
     Pause del
     Port(4,2)=&b10
     Pause del
     Port(4,2)=&b11
     Pause del
     Port(4,2)=&b01
     Pause del
   Next
   Print "left 1000"
   Pause 2000

   '1000 turns right
   For i=1 To 1000
     Port(4,2)=&b11
     Pause del
     Port(4,2)=&b10
     Pause del
     Port(4,2)=&b00
     Pause del
     Port(4,2)=&b01
     Pause del
   Next
   Print "right 1000"
   Pause 2000
 Loop


Connect the quadrature outputs to a second picomite on gp0 and gp1.
This is the program for the quadrature decoder using PIO. It has been running with the GEEK code above for hours, and did not loose a count. You can reset the position counter by pressing "r".

 'Q_decoder_pico_PIO
 
 'based on Github code
 
 'Generic defines
 f= 63e6   '63 MHz (50% of 126MHz clock of PicoMiteVGA)
 setpin gp0,pio1    'the I signal
 setpin gp1,pio1    'the Q signal
 
 
 ' PIO code -------------------------------------------------------------------
 
 'header and start of the PIO code
 pio assemble 1,".program test"
 pio assemble 1,".line 0"
 
 'jump table that forms the state machine
 pio assemble 1,"JMP push_out" '00-00
 pio assemble 1,"JMP minus1"   '00-01
 pio assemble 1,"JMP plus1"    '00-10
 pio assemble 1,"JMP push_out" '00-11
 pio assemble 1,"JMP plus1"    '01-00
 pio assemble 1,"JMP push_out" '01-01
 pio assemble 1,"JMP push_out" '01-10
 pio assemble 1,"JMP minus1"   '01-11
 pio assemble 1,"JMP minus1"   '10-00
 pio assemble 1,"JMP push_out" '10-01
 pio assemble 1,"JMP push_out" '10-10
 pio assemble 1,"JMP plus1"    '10-11
 pio assemble 1,"JMP push_out" '11-00
 pio assemble 1,"JMP plus1"    '11-01
 pio assemble 1,"JMP minus1"   '11-10
 pio assemble 1,"JMP push_out" '11-11
 
 'the actual PIO code, uses Y to store previous IO state
 pio assemble 1,".wrap target" 'end of program returns here
 pio assemble 1,"delta0:"      'this is also the no-change entry
 pio assemble 1,"mov ISR,null" 'clear jump address
 pio assemble 1,"in Y, 2"      'shift 2 bits from Y into ISR
 pio assemble 1,"mov Y, pins " 'Copy 2 IO bits into Y (note "space" after pins)
 pio assemble 1,"in Y, 2"      'shift these 2 bits from Y into ISR
 pio assemble 1,"mov PC, ISR"  'jump to the address in ISR (jump table))
 
 'The plus and minus routines, X is the actual 32 bit position counter
 pio assemble 1,"minus1:"      'jump here to decrement X
 pio assemble 1,"JMP X--, push_out"  'decrement X
 pio assemble 1,"JMP push_out" 'regardless X value go to push_out
 pio assemble 1,"plus1:"       'label to increment X
 pio assemble 1,"mov X, !X"    'invert X
 pio assemble 1,"JMP X--, next1" 'decrement x
 pio assemble 1,"next1:"        'regardless X value come here
 pio assemble 1,"mov X, !X"     'invert back
 
 'send the most recent position to FIFO
 pio assemble 1,"push_out:"      'label
 pio assemble 1,"mov ISR, X"     'move X to ISR
 pio assemble 1,"push"           'push ISR into FIFO, not blocked
 
 'outer loop and end of program
 pio assemble 1,".wrap"          'end of code, jump to wrap target
 pio assemble 1,".end program"' list"

 
 'configure PIO 1 statemachine 0 (free on PicoMiteVGA)
 p0=pio(pinctrl 0,0,0,gp0)                      'gp0 is lowest IN pin (gp0,gp1))
 e0=pio(execctrl gp0,pio(.wrap target),pio(.wrap))         'wrap and wrap target
 s0=pio(shiftctrl 0,0,0,0,0,0)        'shift IN direction is left (the 5'th '0')

 
 'initialize PIO 1 state machine 0
 pio init machine 1,0,f,p0,e0,s0,0      'init machine with start=0 (=jmp delta0)

 
 'start the quadrature decoder PIO 1 statemachine 0
 pio start 1,0
 

 
 'main MMBasic code -------------------------------------------------------------
 
 dim dat%(3)     'array to store FIFO data

 'main loop
 do
   a$=inkey$                                     'just for control
   
   'get the data from the fifo
   pio read 1,0,4,dat%()                         'read whole fifo
   posi=dat%(3)                                  'last data in fifo
   if posi>2147483647 then inc posi,-4294967296  '2'th complement
   print posi                                    'show position
   
   'just some pause
   pause 100                                     'any delay needed
   
   'reset position (PIO X register) under control of keyboard
   if a$="r" then                                'press r to zero position
     pio execute 1,0,&hA023    '= assembly "mov X, null" (zero the X register)
     a$=""
   end if
   
 loop while a$=""  'exit when any key not r
 
end


There are some changes versus the original code that I took from Github. The most essential was the change of the "delta0" in the jump table to "push_out". I did this so I would get the latest position, also when nothing moves (no encoder changes).

Also note the direct execution of a PIO command that does not assemble, so I need to manual assemble, and present the machine instruction in hex.

There are also some assemble quirks, that I need to discuss with Peter.

Good luck playing,

Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

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

Holy moly...are you kidding me!?!?!      

I just cleared my desk and surrounded myself with Pico and PIO-related stuff and...BAM..you get it done in a heartbeat?!?!

Absolutely amazing!!!

My head keeps falling on the desk (looong day) and so I need to hit the sack but I'll hit this in a few hours.

Excited is not the word.      
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 08:38pm 25 Apr 2024
Copy link to clipboard 
Print this post

P.S. I have actual motor/encoders and h-bridge drivers, ready to go  
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3678
Posted: 09:12pm 25 Apr 2024
Copy link to clipboard 
Print this post

Wow!

John
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 07:31am 26 Apr 2024
Copy link to clipboard 
Print this post

Phenix,

To close things off, here is a version of the quadrature generator including acceleration and deceleration curves.

 'geek Q-generator
 
 dim del(1000)  'delay per state
 
 'create acceleration an deceleration curve
 max_freq = 10e3           '(10kHz))
 init_q = 30               'ms start speed per phase
 min_q  = 1000/max_freq    'ms end speed per phase
 
 q=init_q    
 for i=1 to 500
   del(i) = q + min_q
   del(1001-i) = q + min_q
   q = q * 0.95
 next

 'set IO pins  
 SetPin gp2,dout
 SetPin gp3,dout
 
 Do
   '1000 turns left
   For i=1 To 1000
     Port(4,2)=&b00
     Pause del(i)
     Port(4,2)=&b10
     Pause del(i)
     Port(4,2)=&b11
     Pause del(i)
     Port(4,2)=&b01
     Pause del(i)
   Next
   Print "left 1000"
   Pause 2000
   
   '1000 turns right
   For i=1 To 1000
     Port(4,2)=&b11
     Pause del(i)
     Port(4,2)=&b10
     Pause del(i)
     Port(4,2)=&b00
     Pause del(i)
     Port(4,2)=&b01
     Pause del(i)
   Next
   Print "right 1000"
   Pause 2000
 Loop

Edited 2024-04-26 17:32 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 01:14pm 29 Apr 2024
Copy link to clipboard 
Print this post

@Volhout

Hey Harm,

Not having much luck, here.

With no connections at all, shouldn't I be getting a reading of zero?

I get:

  Quote  
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08
5.02199039e+08


And hitting the "r" key doesn't make a difference.

Used a 74HC4050 for level shifting and encoder signals are good:

setpin gp0,din
setpin gp1,din

do
print pin(gp0),pin(gp1)
loop

Edited 2024-04-30 00:14 by PhenixRising
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 05:41pm 29 Apr 2024
Copy link to clipboard 
Print this post

Just tested with a Pico clone @126MHz and this spits out a continuous:

  Quote  
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09
-1.450672939e+09


So I have tried with both an original RPi Pico and the "black", 16MB clone.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 06:55pm 29 Apr 2024
Copy link to clipboard 
Print this post

Hi Phenix,

It is a good sign that the count does not change when no signals are applied.
However, the rshould zero the count.

Please zip up your pico  pio program, the whole thing, including the mmbasic config, post it here. I’ll look at it.
The assembler can be a bit stubborn, and maybe can find this out.
What version are you running? V5.08.00 or V5.09.00rc3 or 4?
Try supplying some signals to gp0 and gp1 and check if something changes.

We will get this working.

Volhout
Edited 2024-04-30 05:14 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8605
Posted: 07:04pm 29 Apr 2024
Copy link to clipboard 
Print this post

The numbers in the post suggest you are reading integer data into a floating point variable
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 07:16pm 29 Apr 2024
Copy link to clipboard 
Print this post

Good catch.

The array must be an integer array. Hence  DIM dat%()

Volhout
Edited 2024-04-30 05:18 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 07:27pm 29 Apr 2024
Copy link to clipboard 
Print this post

@Volhout

Hey buddy  

Fresh installs, no config
V5.08 straight Pico and now the latest 5.09 PicoVGA that Pete last posted in the other thread Ver is only reporting "5.09", no rev number.

Code is a straight cut and paste from your post.

Have the encoder feeding GP0 and GP1 and the test code shows good signal transitions:


setpin gp0,din
setpin gp1,din

do
print pin(gp0),pin(gp1)
loop

Ones and zeroes flashing like crazy when I rotate the encoder but the numbers don't change in the quad-detect program.



@matherp
Heck I got excited about the integer thing and so I made "posi" an int and now I get

  Quote  
502199039
502199039
502199039
502199039
502199039
502199039
502199039
502199039
502199039



 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 07:41pm 29 Apr 2024
Copy link to clipboard 
Print this post

Zipped code attached.
PIO Quad Decode.zip
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 08:33pm 29 Apr 2024
Copy link to clipboard 
Print this post

Hi Phenix,

I confirm your findings. There is something strange here. I had few occasions where the code worked, and many where the same code did not work. Exactly as you described, the position does not advance, and pressing r does not reset the count.

As if the reading of the fifo was stuck. I will try to diagnose further tomorrow, put some debug in the PIO code to see if the PIO code runs it's program correctly.

Then it is up to the FIFO handling (which I expect it is). Maybe I forgot something. Some initialisation that accidentally sometimes is correct.

Regards,

Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 08:44pm 29 Apr 2024
Copy link to clipboard 
Print this post

@Volhout

Much appreciated but please don't give it priority. It's obvious that you're always crazy-busy  
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 07:14am 30 Apr 2024
Copy link to clipboard 
Print this post

Hi Phenix,

I found 2 problems.

1/ At start Y is not initialized, and thus can be any value. This allows a first initial jump to an address beyond 27, and these locations are filled with 0x0000 (JMP to 0). This could cause an infinte loop. I solved to by filling the rest of memory with JMP delta0, so you always end up at a valid location.
This is not the real cause, but makes the code mor robust.

2/ The real cause why the program does not work is one for Peter. I will address it in the thread for the 5.09.00 release candidates.
A program starts and ends with


PIO ASSEMBLE x,".program NAME"
-
-
-
PIO ASSEMBLE x,".end program"


The last line can also be
PIO ASSEMBLE x,".end program list"


In this case the console shows a listing of the hex codes the assembler produced.
In case you use the "list" option. the program works as designed. In case you omit the "list" option, the program does not work.

Please find the version 2 code (with minor changes from the first).

 'Q_decoder_pico_PIO
 
 'based on Github code
 
 'Generic defines
 f= 63e6            '63 MHz (50% of 126MHz clock of PicoMiteVGA)
 setpin gp0,pio1    'the I signal
 setpin gp1,pio1    'the Q signal
 
 pio clear 1
 dim dat%(3)     'array to store FIFO data
 
 ' PIO code -------------------------------------------------------------------
 
 'header and start of the PIO code
 pio assemble 1,".program test"
 pio assemble 1,".line 0"
 
 'jump table that forms the state machine
 pio assemble 1,"JMP push_out" '00-00
 pio assemble 1,"JMP minus1"   '00-01
 pio assemble 1,"JMP plus1"    '00-10
 pio assemble 1,"JMP push_out" '00-11
 pio assemble 1,"JMP plus1"    '01-00
 pio assemble 1,"JMP push_out" '01-01
 pio assemble 1,"JMP push_out" '01-10
 pio assemble 1,"JMP minus1"   '01-11
 pio assemble 1,"JMP minus1"   '10-00
 pio assemble 1,"JMP push_out" '10-01
 pio assemble 1,"JMP push_out" '10-10
 pio assemble 1,"JMP plus1"    '10-11
 pio assemble 1,"JMP push_out" '11-00
 pio assemble 1,"JMP plus1"    '11-01
 pio assemble 1,"JMP minus1"   '11-10
 pio assemble 1,"JMP push_out" '11-11
 
 'the actual PIO code, uses Y to store previous IO state
 pio assemble 1,".wrap target" 'end of program returns here
 PIO assemble 1,"delta0:"      'label to return to
 pio assemble 1,"mov ISR,null" 'clear jump address
 pio assemble 1,"in Y, 2"      'shift 2 bits from Y into ISR
 pio assemble 1,"mov Y, pins " 'Copy 2 IO bits into Y (note "space" after pins)
 pio assemble 1,"in Y, 2"      'shift these 2 bits from Y into ISR
 pio assemble 1,"mov PC, ISR"  'jump to the address in ISR (jump table))
 
 'The plus and minus routines, X is the actual 32 bit position counter
 pio assemble 1,"minus1:"      'jump here to decrement X
 pio assemble 1,"JMP X--, push_out"  'decrement X
 pio assemble 1,"JMP push_out" 'regardless X value go to push_out
 pio assemble 1,"plus1:"       'label to increment X
 pio assemble 1,"mov X, !X"    'invert X
 pio assemble 1,"JMP X--, next1" 'decrement x
 pio assemble 1,"next1:"        'regardless X value come here
 pio assemble 1,"mov X, !X"     'invert back
 
 'send the most recent position to FIFO
 pio assemble 1,"push_out:"      'label
 pio assemble 1,"mov ISR, X"     'move X to ISR
 pio assemble 1,"push"           'push ISR into FIFO, not blocked
 
 'outer loop and end of program
 pio assemble 1,".wrap"          'end of code, jump to wrap target
 pio assemble 1,"jmp delta0"     'dummy
 pio assemble 1,"jmp delta0"     'dummy
 pio assemble 1,"jmp delta0"     'dummy
 pio assemble 1,"jmp delta0"     'dummy
 'pio assemble 1,".end program"
 pio assemble 1,".end program list"

 
 'configure PIO 1 statemachine 0 (free on PicoMiteVGA)
 p0=pio(pinctrl 0,0,0,gp0)                     'gp0 is lowest IN pin (gp0,gp1))
 e0=pio(execctrl gp0,pio(.wrap target),pio(.wrap))        'wrap and wrap target
 s0=pio(shiftctrl 0,0,0,0,0,0)       'shift IN direction is left (the 5'th '0')

 
 'initialize PIO 1 state machine 0
 pio init machine 1,0,f,p0,e0,s0,pio(.wrap target) 'init machine @ start loop

 
 'start the quadrature decoder PIO 1 statemachine 0
 pio start 1,0
 

 
 'main MMBasic code -------------------------------------------------------------

 'main loop
 do
   a$=inkey$                                     'just for control
   
   'get the data from the fifo
   pio read 1,0,4,dat%()                         'read whole fifo
   posi%=dat%(3)                                  'last data in fifo
   if posi%>2147483647 then inc posi%,-4294967296 '2'th complement
   print posi%                                    'show position
   
   'just some pause
   pause 100                                     'any delay needed
   
   'reset position (PIO X register) under control of keyboard
   if a$="r" then                                'press r to zero position
     pio execute 1,0,&hA023    '= assembly "mov X, null" (zero the X register)
     a$=""
   end if
   
 loop while a$=""  'exit when any key not r
 
end

Edited 2024-04-30 17:15 by Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 07:23am 30 Apr 2024
Copy link to clipboard 
Print this post

Perfect!!!

My freaking HERO

Soooo many thanks  

This PIO stuff is really, really exciting.

Got a couple of client issues to deal with but then I'll be closing the loops.

"C'mon baby, do the Pico-Motion"  
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 08:05am 30 Apr 2024
Copy link to clipboard 
Print this post


f= 189e6            '189 MHz (50% of 378MHz clock of PicoMiteVGA)


Beautiful  
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3589
Posted: 09:12am 30 Apr 2024
Copy link to clipboard 
Print this post

Hi Phenix,

That is burning fast. But you may not actually need 189MHz on the PIO. Understand that when you want a second, or third, or fourth quadrature decoder, they can run on different statemachines in the same PIO. Running the exact same program in PIO memory. Each with their own speed.

You only need to

PIO INIT MACHINE with a PIO(PINCTRL) set for that specific state machine (i.e GP2)
PIO START


With f=189MHz you can technically decode 18MHz transitions, or 4.5MHz quadrature clock. I think you are safe to run it at 40MHz (or 63..).

But you may need the 378MHz on the pico to run the PID algorithms for the loop in MMBasic. Especially if you use this for multiple axis.

Was fun to help,

Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

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

Hi Harm,

Great news re: another encoder. I won't be adding axes (for now) because I prefer DCS (distributed control systems) but I like to use two encoders/axis when possible.

Worst-case execution is when I run the PID + Velocity profiler but even at 252MHz and using settick 1, I have plenty of time.

I'll probably switch to settick 2 though because for these big motors + drive-trains, even a 500Hz PID-update is overkill.

At some point, a counter of pulses + direction would be cool. With this, we could take the output from a stepper-motor controller, purely as a command and convert machines to closed-loop servo.

There are endless "CNC" controls for steppers but when those things come under-load, they stall or lose steps. Not the case with closed-loop servos.

This PicoMite, right now, outperforms the dedicated LM629 chip (£40 to £65) that is currently unobtainium.


 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 326
Posted: 11:27am 30 Apr 2024
Copy link to clipboard 
Print this post

Oh, too cool. I exit the MMBasic program and the counter keeps on updating and when I restart the MMBasic program, the initializing code preserves the count...wonderful  
 
     Page 2 of 3    
Print this page
© JAQ Software 2024