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 : Playing with a rotary encoder
Author | Message | ||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6798 |
I'm messing about with designing a little test oscillator, nothing fancy - just using PLAY TONE for sine and PLAY SOUND for square, triangle etc. I want it to fit into a 80mm x 40mm DIN module case that I've rescued though. That gives me one rotary encoder and one button to play with. At the moment I'm using matherp's lovely bomb-proof rotary encoder routine to set the frequency. However, I'm now getting to the bit where I'm creating a new screen for mode selection. Rather than attempt to re-use Peter's code for this I thought I might simply disable its interrupts and set my own for this. After some messing about I came up with this snippet of inline code instead of using interrupts: i is just the index to rc() 'read the rotary encoder b=a 'remember last encoder pins a=Port(in0,1,in1,1) 'read encoder pins simultaneously If a=3 Then b=3:i=0 'reset things if in neutral If a<>b Then rc(i)=a:Inc i 'if they have changed then store and bump pointer If i=3 Then 'when we have 3 values sort out the store 'you can do anything here but you must reset i to zero at the end If rc(0)=1 Then Inc count Else Inc count,-1 Print count i=0 EndIf Basically it's a state machine. Three readings are taken of the pins on successive scans through the program. If both pins are high the encoder is in "neutral" and everything is reset - we need three different values. After the third scan rc() will hold either 1 0 2 or 2 0 1 depending on the direction of rotation. I'm not saying this is the ultimate in encoder reading, but it seems to work well enough when using a small knob to change a value. There is no apparent contact bounce and you get a single count change even if you turn the encoder slowly. It may be missing high speed pulses but that's not important for highlighting items in a menu. I hope this is of some use to someone. ----------------------------------- EDIT: When I first posted this a couple of days ago it appeared to be working very well, however I've changed things elsewhere in the program and now I can't get it to work! There may be a fundamental problem that I've missed so please don't assume that this is working code! Even stranger, having just done another test run (cut & paste from the above) it *is* working! It may be an idea to make b and i STATIC so that they can't get corrupted. Edited 2023-06-20 07:44 by Mixtel90 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3150 |
This is working for me with CLK on pin 1 and DT on pin 2, except that I have to turn through 2 detents to get it to increment or decrement. Is this expected? Here is the entire program: 'rotary_encoder_mixtel.bas dim integer in0=2,in1=1,rc(3) setpin in0,din:setpin in1,din do 'read the rotary encoder b=a 'remember last encoder pins a=Port(in0,1,in1,1) 'read encoder pins simultaneously If a=3 Then b=3:i=0 'reset things if in neutral If a<>b Then rc(i)=a:Inc i 'if they have changed then store and bump pointer If i=3 Then 'when we have 3 values sort out the store 'you can do anything here but you must reset i to zero at the end If rc(0)=1 Then Inc count Else Inc count,-1 Print count i=0 EndIf loop PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6798 |
Might depend on your encoder. I've been getting one increment/decrement per detent. There are problems. You need three program passes for each complete read. That can cause problems sometimes. I only came up with it because I wanted to use the same encoder for different purposes with different minimum and maximum count values and different increments. I figured that an inline solution might be easier than an interrupt type. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 6100 |
This is the code I was using last year. ' simple encoder reading TassyJim Nov 2022 SETPIN GP19, DIN, PULLUP' setup RB as an input SETPIN GP20, INTL, RInt, PULLUP' setup an interrupt when RA goes high DO ' < main body of the program > LOOP SUB RInt ' Interrupt to decode the encoder output IF PIN(GP19) = 1 THEN Value = Value + 1 ' clockwise rotation ELSE Value = Value - 1 ' anti clockwise rotation ENDIF PRINT Value END SUB Jim VK7JH MMedit MMBasic Help |
||||
lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3150 |
Thanks, Jim. That code also took two detents to increment or decrement. This is with a rotary encoder from a 45-in-1 sensor kit. I have some different ones from Aliexpress which have the pins in a different order. They increment/decrement as expected. The sensorkit one has just two resistors; the other one has 5 resistors and 3 capacitors. There's no sign of any active components. For my intended use, it doesn't really matter whether it takes one tick or two. There may be some Arduino code I can look at to see if I can get the sensorkit one to work as expected. (Off topic--Mick--you've posted that you've been playing with a joystick--can you post your code?) PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4247 |
Jim's code only checks for the falling edge on the clock signal. You can also read the rising edge on the clock signal by making the interrupt trigger at both edges. SETPIN xxx, INTB, Rint, PULLUP But in that case you have to compare both pin states (since you don't know what triggered it). IF PIN(xxx)=PIN(yyy) THEN INC Value,1 ELSE INC Value,-1 END IF This should make the rotary work on both indents... Edited 2023-06-22 16:55 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6798 |
Thanks Jim. I did try something very similar to that at first. IIRC I was getting some strange triggering. Then I discovered an article about reading them as a state machine, which eliminates a lot of the problems as a read of the pins *has* to be an expected value. I tried to get that into a few lines of MMBasic to make an inline block. I may have oversimplified the idea. lol -------------------- The encoder I've been using is one I got from "a proper shop" some time ago. Probably RS. Obviously there are no resistors or anything, it's a bare encoder. I've got it set up straight on the GP pins with the common connected to GND and I'm using the internal pullups, perhaps unwisely but they seem to work. :) --------------- How much of the code, lizby? The joystick itself is just two analogue pots into ADC inputs. Then I scale the inputs to get 0-100 and do some setpoint comparisons to set flags. The scaled analogue values, flags and inputs from the joystick switch and four buttons (in the same flag word) can then be read over I2C as required. The game box end (I2C master) can change things on the controller: Flip it over to make it left-handed Allocate and read a 20-character ID string Change the I2C address Change the joystick sensitivity setpoint for the flags It can read: The ID string Just the flags (for speed) The analogue registers as well as the flags I can't remember now just where I left the development - I got a bit diverted. I can soon put it back on the bench and grab what you'd like from it though. It's not final, but most of it is working. I was playing with changing settings on the controller IIRC and something wasn't working... Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6798 |
This is a test routine I did for testing some ideas on the joystick. It's a lot smaller than the controller code. :) It's intended to use one of the cheap "thumb sticks" like those in the multiple sensor sets. I *think* I had X on GP26, but you'll figure it out. I was arranging the stick with connections at the bottom, so ignore X and Y markings on the board. I think they are reversed here. The variable sens is the "sensitivity", adjusting how far from neutral the stick moves before registering the direction as a flag. Change the Goto to test different ideas: bits / switches / values / values2 / object / object2 'joystick test SetPin gp26,ain SetPin gp27,ain 'SetPin gp0,off SetPin gp6,din,pullup sens=1.5 GoTo bits bits: Do y=Pin(gp26) x=Pin(gp27) b=0 b=b+Not(Pin(gp6)) 'bit 7 - joystick button b=b<<1 b=b+0 'bit 6 - spare input b=b<<1 b=b+0 'bit 5 - button B b=b<<1 b=b+0 'bit 4 - button A b=b<<1 b=b+(x>3.3-sens) 'bit 3 - joystick right b=b<<1 b=b+(x<sens) 'bit 2 - joystick left b=b<<1 b=b+(y<sens) 'bit 1 - joystick down b=b<<1 b=b+(y>3.3-sens) 'bit 0 - joystick up Print Bin$(b,8) Pause 100 Loop switches: Do y=Pin(gp26) x=Pin(gp27) up = (y>(3.3-sens)) down=(y<sens) left=(x<sens) right=(x>(3.3-sens)) Print @(200,100)"> "; If up=1 Then Print "UP "; Else Print " "; If down=1 Then Print "DOWN "; Else Print " "; If left=1 Then Print "LEFT "; Else Print " "; If right=1 Then Print "RIGHT "; Else Print " "; If Pin(gp6)=0 Then Print "BUTTON <" Else Print " <" 'Print up,down,left,right,Pin(gp6) Pause 100 Loop values: Do Print Pin(gp26),Pin(gp27),Pin(gp6) Pause 100 Loop values2: Do y=Int(Pin(gp26)*1000/33) x=Int(Pin(gp27)*1000/33) Print x,y Pause 100 Loop object: x=400 y=150 Do Print @(x,y)" " Select Case Pin(gp26) Case >(3.3-sens) y=y-10*(y>0) Case <sens y=y+10*(y<460) End Select Select Case Pin(gp27) Case >(3.3-sens) x=x+10*(x<730) Case <sens x=x-10*(x>0) End Select Print @(x,y)"@" Pause 10 Loop object2: x=50 y=50 Do Print @(x,460-y)" " x=Int(Pin(gp27)*730/3.3) y=Int(Pin(gp26)*460/3.3) Print @(x,460-y)"@" Pause 30 Loop Edited 2023-06-22 18:34 by Mixtel90 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3150 |
Thanks--that makes it work for the rotary encoder from the 45-in-1 sensor kit, but it increments/decrements twice for the one which worked with Jim's code. ' simple encoder reading TassyJim Nov 2022, updated Volhout SetPin 2, DIN, PULLUP' setup RB as an input SetPin 1, INTB, RInt, PULLUP' setup an interrupt when RA goes high Do ' < main body of the program > Loop Sub RInt ' Interrupt to decode the encoder output ' If Pin(2) = 1 Then If Pin(2) = Pin(1) Then Value = Value + 1 ' clockwise rotation Else Value = Value - 1 ' anti clockwise rotation EndIf Print Value End Sub PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3150 |
Ok, I've got the hang of it now--thanks. Here's code to move a dot on a screen with the joystick. This LCD is a 1.3" 128x64 SSD1306I2C, but I scaled with mm.hres and mm.vres, so it should work on any MMBasic screen, I think. Here's a Youtube video: Moving Dot Const jsRx=34,jsRy=32,jsSW=29 ' GP27,GP28,GP22 SetPin jsRy,ain SetPin jsRx,ain SetPin jsSW,din,pullup CLS Do ' Print @(x,460-y)" " x=Int(Pin(jsRx)*MM.HRes/3.3) y=MM.VRes-Int(Pin(jsRy)*MM.VRes/3.3) ' Print @(x,460-y)"@" diffx=x-x2: diffy=y-y2 If (Abs(diffx)>1) Or (Abs(diffy)>1) Then ' If (x<>x2) Or (y<>y2) Then Pixel x2,y2,0 x2=x:y2=y Print x,y Pixel x,y,1 EndIf Pause 30 Loop ("Rx" and "Ry" are as labeled on my joystick.) (Sorry to go OT.) ~ Edited 2023-06-22 23:20 by lizby PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
Print this page |