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 4 | |||||
Author | Message | ||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4223 |
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 KingdomPosts: 857 |
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 KingdomPosts: 857 |
P.S. I have actual motor/encoders and h-bridge drivers, ready to go |
||||
JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 3800 |
Wow! John |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4223 |
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 KingdomPosts: 857 |
@Volhout Hey Harm, Not having much luck, here. With no connections at all, shouldn't I be getting a reading of zero? I get: 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 KingdomPosts: 857 |
Just tested with a Pico clone @126MHz and this spits out a continuous: So I have tried with both an original RPi Pico and the "black", 16MB clone. |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4223 |
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 KingdomPosts: 9110 |
The numbers in the post suggest you are reading integer data into a floating point variable |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4223 |
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 KingdomPosts: 857 |
@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 |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 857 |
Zipped code attached. PIO Quad Decode.zip |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4223 |
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 KingdomPosts: 857 |
@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: NetherlandsPosts: 4223 |
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 KingdomPosts: 857 |
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 KingdomPosts: 857 |
f= 189e6 '189 MHz (50% of 378MHz clock of PicoMiteVGA) Beautiful |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4223 |
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 KingdomPosts: 857 |
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 KingdomPosts: 857 |
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 4 |
Print this page |