Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 12:43 28 Nov 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 : Playing with a rotary encoder

Author Message
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 6798
Posted: 10:31am 17 Jun 2023
Copy link to clipboard 
Print this post

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 States
Posts: 3150
Posted: 07:28pm 21 Jun 2023
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 6798
Posted: 08:00pm 21 Jun 2023
Copy link to clipboard 
Print this post

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: Australia
Posts: 6100
Posted: 09:07pm 21 Jun 2023
Copy link to clipboard 
Print this post

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 States
Posts: 3150
Posted: 11:16pm 21 Jun 2023
Copy link to clipboard 
Print this post

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: Netherlands
Posts: 4247
Posted: 06:52am 22 Jun 2023
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 6798
Posted: 06:55am 22 Jun 2023
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 6798
Posted: 08:28am 22 Jun 2023
Copy link to clipboard 
Print this post

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 States
Posts: 3150
Posted: 12:13pm 22 Jun 2023
Copy link to clipboard 
Print this post

  Volhout said  You can also read the rising edge on the clock signal by making the interrupt trigger at both edges.

This should make the rotary work on both indents...


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 States
Posts: 3150
Posted: 01:20pm 22 Jun 2023
Copy link to clipboard 
Print this post

  Mixtel90 said  This is a test routine I did for testing some ideas on the joystick


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


To reply to this topic, you need to log in.

© JAQ Software 2024