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 : My bounce-busting bullet proof Rotary Encoder code
Author | Message | ||||
apalert Newbie Joined: 06/07/2023 Location: AustraliaPosts: 15 |
No pullups and no capacitors! Peter Mather's RE code is fantastic, it just works. But I'm a newbie and can't understand it. Mulling it over coffee I wondered if it could be simpler. Maybe only read the first pulse of switch noise, call it the data and ignore the rest using a flag, preset true but made false as soon as the first pulse of noise is received. Maybe both inputs need a flag system. Maybe the interrupt on one resets the flag for the other. Put down my coffee and coded it up in minutes. And it worked...mostly. I did spend rather a lot of time tweaking polishing embellishing and commenting :) One thing I learned while testing is that it's very easy to exceed the speed these rotary encoders can cope with which causes missed pulses. The data sheet for my encoder gives maximum rotary speed of 100 rpm. It has 20 indents per full turn so that calculates down to 33 clicks per second, a bit over 1 1/2 turns, 30mS per click. You can easily exceed that in short bursts. I setup a timing loop and with a finger twirl on the knob for 1/4 turn some pulses were as low as 6mS apart, 5 times the maximum speed recommended. Little wonder it missed a few pulses! One tweak was to grab the direction data on the first clock pin interrupt but not finalize it until the data pin interrupt. This means the user can overshoot 49% of the way to the next indent and back off without a miscount. Anyway, in case there is interest I'll attach the code: 'Code for rotary encoder interpretation by Malcolm Young 'Rotating the encoder prints incremental numbers as appropriate ' eg 0 1 2 3 4 3 2 1 0 -1 -2 etc as well as a direction flag 'in the form of 1 and -1. Developed on Picomite OPTION ExPLICIT OPTION DEFAULT NONE dim float pre_action = 0 'temporarily holds data from new encoder movement dim float dir_action = 0 'makes "public" the new directional data dim float inc_action = 0 'makes "public" the new incremental data dim float old_inc_action = 1 'used to decide if data has changed dim float clock_enable = 1 'flag to control clock pin interrupt process dim float reset_enable = 0 'flag to control data pin interrupt process dim float clk = 20 'use to name clock pin dim float dat = 19 'use to name data pin setpin clk, intl , clock_interrupt,pullup 'clock pin (encoder pin a) setpin dat, intb, reset_clock_enable,pullup 'data pin (encoder pin b) print "inc_action", "dir_action" do If inc_action <> old_inc_action then print inc_action,, dir_action 'cls: text 96,60, str$(inc_action),RB,6 'LCD panel output old_inc_action = inc_action end if loop sub clock_interrupt if clock_enable = 1 then clock_enable=0 'prevents any further "noise" pulses being processed if pin(dat) = 1 then 'direction was clockwise pre_action = 1 else 'direction was counter-clockwise pre_action = -1 endif reset_enable=1 'makes reset_clock_enable ready to accept data endif end sub sub reset_clock_enable if reset_enable = 1 then reset_enable = 0 'prevents any further "noise" pulses being processed if pin(clk) = 0 then 'prevents processing if RE position reverted to origin dir_action = pre_action 'directional output inc_action=inc_action + pre_action 'incremental output endif clock_enable = 1 'enables data pin again, even if reverted to origin endif end sub |
||||
Geoffg Guru Joined: 06/06/2011 Location: AustraliaPosts: 3194 |
Well done! That should go in the New Fruit of the Shed. Geoff Geoff Graham - http://geoffg.net |
||||
apalert Newbie Joined: 06/07/2023 Location: AustraliaPosts: 15 |
Thanks Geoff, happy for that to happen. Maybe edit out the jokey introduction and I'd be happy add a bit more about rotary encoder theory, a few diagrams perhaps. Mal |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 858 |
Would be cool if one of our PIO wizards could figure out quad decode where MMBasic could read/set/reset the high-speed counter. The Python guys appear to have it. |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6786 |
Do you mean the count inputs defined with OPTION COUNTER? Their operation is configured by SETPIN CIN? They are inputs that count into variables, aren't they? Just zero the variable. IIRC the RP2040 doesn't have any on-chip counters. It has timers, which can have various input sources. EDIT: Ah.... do you mean TIMER? You can just use TIMER=0 to reset that. . Edited 2024-02-05 02:53 by Mixtel90 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 858 |
Ah, maybe I shouldn't have used the word "counter". I was meaning a var. Don't really understand how this stuff works. Back in my DOS days, I was using Quickbasic (well, PDS 7.1). I needed millisecond determinism that couldn't be interrupted. I hijacked the clock interrupt and reprogrammed it to ~1ms (from 55ms). I included code to compensate so that the clock maintained correct time. Anyway, I was able to share variables with QB and as things changed in the (MASM) background task, the vars in Basic were updated. So I am imagining a PIO quad decoder/counter that would simply update a variable that MMBasic has access to (?) |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 858 |
Duplicate Edited 2024-02-05 06:12 by PhenixRising |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6786 |
When you use a counter on the PicoMite: OPTION COUNT countpin1 [,countpin2] [,countpin3] [,countpin4] SETPIN GPx, CNT 'etc. for various options to read it: a = pin(GPx) To zero it pin(GPx) = 0 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 858 |
Quad decode doesn't work with regular counters. How would you differentiate between up/down and how would you achieve X4 resolution. |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6786 |
The obvious way is to use interrupts. You can trigger on just a rising edge, just a falling edge or both. If you are after *very* fast response from a rotary encoder then you need to reduce the resolution. For most purposes (turning an encoder by hand) interrupts are plenty fast enough. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
PhenixRising Guru Joined: 07/11/2023 Location: United KingdomPosts: 858 |
MIKROE 1917 Sub zci 'zero count on index Pin(85)=0 spi write 1,&h88 spi write 1,&h63 Pin(85)=1 end Sub Sub nc 'normal quadrature count Pin(85)=0 spi write 1,&h88 spi write 1,&h43 Pin(85)=1 end Sub Sub lci 'load count on index Pin(85)=0 spi write 1,&h88 spi write 1,&h53 Pin(85)=1 end Sub Sub cc 'clear count Pin(85)=0 spi write 1,&h20 Pin(85)=1 end Sub Sub es 'encoder status pin(85)=0 spi write 1,&h70 spi read 1,status() pin(85)=1 print bin$(status(0)) end Sub Sub clearnc_status pin(85)=0 spi write 1,&h30 pin(85)=1 end Sub Sub read_OTR pin(85)=0 spi write 1,&h68 spi read 4,Indx_latched() pin(85)=1 end Sub Sub tp() 'tell position pin(85)=0 spi write 1,&h60 spi read 4,psns() pin(85)=1 apos=(psns(1)<<24) or (psns(2)<<16) or (psns(3)<<8) or psns(4) if apos and &h80000000 then inc apos, - &hffffffff print apos print hex$(apos) end Sub Sub tp64() 'tell position 64 pin(85)=0 spi write 1,&h60 spi read 4,psns() pin(85)=1 apos=(psns(1)<<24) or (psns(2)<<16) or (psns(3)<<8) or psns(4) print apos print hex$(apos) end Sub Sub dp(cntv) 'define position pin(85)=0 spi write 1,&h98 spi write 1,(&hff and cntv >> 24) spi write 1,(&hff and cntv >> 16) spi write 1,(&hff and cntv >> 8) spi write 1,(&hff and cntv) pin(85)=1 pin(85)=0 spi write 1,&he0 pin(85)=1 end Sub Sub lc(cntv)'load counter pin(85)=0 spi write 1,&h98 spi write 1,(&hff and cntv >> 24) spi write 1,(&hff and cntv >> 16) spi write 1,(&hff and cntv >> 8) spi write 1,(&hff and cntv) pin(85)=1 end Sub |
||||
Print this page |