PDA

View Full Version : My rotary encoder adventures



Demon
- 19th August 2024, 19:42
This is what I have to far:


'------------------------------------------------------------------------------
' SPEED 26-36us to execute IF logic '
'------------------------------------------------------------------------------
'
' Change log
' 2024-08-18 Convert from 16F1937

@ ERRORLEVEL -301 ; turn off ADC clock ignored message
@ ERRORLEVEL -306 ; turn off crossing page boundary message

'************************************************* **********************
' Default in file: PBP3_1\DEVICES\PIC16F1936.PBPINC *
' List in file: PBP3_1\DEVICE_REFERENCE\PIC16F1936.INFO *
' Note: PIC18 devices, the __CONFIG directive has *
' been superceded by the CONFIG directive *
'************************************************* **********************
#CONFIG
__CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
__CONFIG _CONFIG2, _WRT_OFF & _VCAPEN_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_LO & _LVP_OFF
#ENDCONFIG

;--- Defines -------------------------------------------------------------------

DEFINE OSC 32

DEFINE LCD_DREG PORTB ' Set LCD data port
DEFINE LCD_DBIT 0 ' Set starting data bit
DEFINE LCD_RSREG PORTC ' Set LCD register select port
DEFINE LCD_RSBIT 5 ' Set LCD register select bit
DEFINE LCD_EREG PORTC ' Set LCD enable port
DEFINE LCD_EBIT 4 ' Set LCD enable bit
DEFINE LCD_BITS 4 ' Set LCD bus size
DEFINE LCD_LINES 4 ' Set number of lines on LCD
DEFINE LCD_COMMANDUS 1000 ' Set command delay time in microseconds
DEFINE LCD_DATAUS 50 ' Set data delay time in microseconds

define CCP4_REG PORTC ' PWM Pulse out to LCD contrast
DEFINE CCP4_BIT 1 ' 2N2907 PNP with 1K on base
define CCP5_REG PORTC ' PWM Pulse out to LCD backlight
DEFINE CCP5_BIT 2 ' 2N2222A NPN with 1K on base

DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
Define HSER_BAUD 115200
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRGH 0
DEFINE HSER_SPBRG 68

;--- Setup registers -----------------------------------------------------------

SPLLEN CON %1 ' PLL enable
IRCF CON %1110 ' to enable 8 MHz
SCS CON %00 ' system clock determined by FOSC
OSCCON = (SPLLEN << 7) | (IRCF << 3) | SCS

BAUDCON.3 = 1 ' Enable 16 bit baudrate generator

;--- Setup analog pins to digital ----------------------------------------------

ANSELA = %00000000
ANSELB = %00000000
'ANSELC = %00000000 ' No analog on port C
'ANSELD = %00000000 ' No port D
'ANSELE = %00000000 ' No analog on port E

;--- Setup Port directions -----------------------------------------------------

TRISA = %00000111 ' Pin A7 = ...available
' Pin A6 = ...available
' Pin A5 = ...available
' Pin A4 = ...available
' Pin A3 = ...available
' Pin A2 = SPST
' Pin A1 = WiperB
' Pin A0 = WiperA

TRISB = %00000000 ' Pin B7 = iCSP clock
' Pin B6 = ICSP data
' Pin B5 = Blink LED1
' Pin B4 = Blink LED2
' Pin B3 = LCD data D7
' Pin B2 = LCD data D6
' Pin B1 = LCD data D5
' Pin B0 = LCD data D4

TRISC = %10000000 ' Pin C7 = RX
' Pin C6 = TX
' Pin C5 = LCD RS
' Pin C4 = LCD EN
' Pin C3 = ...available
' Pin C2 = PWM LCD backlight
' Pin C1 = PWM LCD contrast
' Pin C0 = ...available

'TRISD = %00000000 ' No port D
'
TRISE = %00000000 ' Pin E3 = MCLR

;--- Setup constants -----------------------------------------------------------


;--- Setup pins ----------------------------------------------------------------

Enc1_WiperA var PortA.0
Enc1_WiperB var PortA.1
Enc1_SPST VAR PortA.2

BlinkLED1 VAR LatB.5 ' Switch these ON/OFF to determine time
BlinkLED2 VAR LatB.4 ' required to execute a section of code

;--- Setup variables -----------------------------------------------------------

Enc1_previous var BYTE
Enc1_rotation var BYTE
Enc1_direction VAr BYTE

USARTcounter VAr WORd ' Used to show program is looping
Enc1_counter VAr WORd ' Used to display encoder movement

;--- Program initialization ----------------------------------------------------

BlinkLED1 = 0
BlinkLED2 = 0

HPWM 2,100,1953 ' LCD contrast (V0 pin)
HPWM 1,180,1953 ' LCD backlight (LED anode)

USARTcounter = 0

Enc1_previous = 0
Enc1_rotation = 0
Enc1_direction = 0
Enc1_counter = 0

goto Start

;--- Subroutines ---------------------------------------------------------------

Start:
Pause 500 ' Let PIC and LCD stabilize

LCDOUT $FE, 1 : Pauseus 1
LCDOUT $FE, $80, "ROTARY ENCODER TEST" : Pauseus 1

Mainloop:

BlinkLED1 = 1 ' Top of LOOP on Logic 2 probe
BlinkLED2 = 0

if USARTcounter < 65025 then ' To show program is working
USARTcounter = USARTcounter + 1
else
USARTcounter = 0
endif

' Wiper Chart:
' ============
' A B
' --- ---
' 0 0
' 1 0 /\ CCW
' 1 1
' 0 1 \/ CW
' 0 0
'
' Careful, EC11 30 detents 15 pulses will move from 00 to 11
' EC11 20 detents can move from 00 back to 00 in one click

if Enc1_WiperA = 0 and Enc1_WiperB = 0 then ' See wiper chart above
if Enc1_previous = 01 then ' 2 digits to follow chart better
Enc1_direction = 1 ' 1=CW, 0=CCW
Enc1_rotation = 1 ' Motion occurred
else
if Enc1_previous = 10 then
Enc1_direction = 0 ' 0=CCW
Enc1_rotation = 1 ' Motion occurred
else
Enc1_direction = 0 ' Not relevant without motion
Enc1_rotation = 0 ' No motion occurred
endif
endif
Enc1_previous = 00 ' Save wiper position
endif

if Enc1_WiperA = 1 and Enc1_WiperB = 0 then
if Enc1_previous = 00 then
Enc1_direction = 1
Enc1_rotation = 1
else
if Enc1_previous = 11 then
Enc1_direction = 0
Enc1_rotation = 1
else
Enc1_direction = 0
Enc1_rotation = 0
endif
endif
Enc1_previous = 10
endif

if Enc1_WiperA = 1 and Enc1_WiperB = 1 then
if Enc1_previous = 10 then
Enc1_direction = 1
Enc1_rotation = 1
else
if Enc1_previous = 01 then
Enc1_direction = 0
Enc1_rotation = 1
else
Enc1_direction = 0
Enc1_rotation = 0
endif
endif
Enc1_previous = 11
endif

if Enc1_WiperA = 0 and Enc1_WiperB = 1 then
if Enc1_previous = 11 then
Enc1_direction = 1
Enc1_rotation = 1
else
if Enc1_previous = 00 then
Enc1_direction = 0
Enc1_rotation = 1
else
Enc1_direction = 0
Enc1_rotation = 0
endif
endif
Enc1_previous = 01
endif

if Enc1_rotation = 1 then
if (Enc1_WiperA = 0 and Enc1_WiperB = 0) or _
(Enc1_WiperA = 1 and Enc1_WiperB = 1) then
if Enc1_direction = 1 then
Enc1_counter = Enc1_counter + 1 ' Turned 1 position CW
else
if Enc1_counter > 0 then
Enc1_counter = Enc1_counter - 1 ' Turned 1 position CCW
endif
endif
endif
endif

BlinkLED1 = 0 ' Bottom of IFs on Logic 2 probe
BlinkLED2 = 1

hserout ["#:", DEC5 USARTcounter,_
" C:", dec3 Enc1_counter,_
" A:", DEC1 Enc1_WiperA,_
" B:", DEC1 Enc1_WiperB,_
" SW:", DEC1 Enc1_SPST, 10]

BlinkLED1 = 0 ' After HSEROUT on Logic 2 probe
BlinkLED2 = 0

LCDOUT $FE, $D4, "C:", dec3 Enc1_counter,_
" A:", DEC1 Enc1_WiperA,_
" B:", DEC1 Enc1_WiperB,_
" SW:", DEC1 Enc1_SPST : Pauseus 1

BlinkLED1 = 0 ' After LCDOUT on Logic 2 probe
BlinkLED2 = 0

goto mainloop
end



To put things into perspective, these are the times I've observed so far:

9720


Those encoder readings on channels 0 and 1 were done while turning the encoder as fast as I could. I wanted to see if I could over-run the time it takes the PIC to execute all the encoder code at 32 MHz.

The PIC is not even close to being overwhelmed, so now I know I should be able to process at least 2 encoders at the same time (at least that's what should happen in my mind :D ).

Demon
- 19th August 2024, 19:58
Here's an example of a chinesium EC11 clone:

9722


Every time I press on the switch built into the shaft, wiper A bounces. Also, note how wiper A never came down at the top left.

So now I have some good quality control tests to do on EC11s that I'll be having made in china.
:D


(I would never have thought of checking for this defect. I'm fiddling with my Saleae Logic 2 probe on a rotary encoder. I just wanted to see how a manual press would compare with the encoder pulses.)


EDIT: These are from AliExpress; you get what you pay for. But when you order from Alibaba, you can get reimbursed if you can demonstrate that a product is not performing accord to specifications.

I don't mind the aliexpress lower quality, I use them mainly for testing.

HenrikOlsson
- 19th August 2024, 20:17
You have turned off the WDT (for speed I suppose) but forgot to tell PBP about it so no speed gained.

Anyway, try this as your encoder logic and see if it beats 26us.

New_Encoder VAR BYTE
Old_Encoder VAR BYTE
Test VAR BYTE
Enc1_counter VAR WORD

BlinkLED1 VAR LatB.5 ' Switch these ON/OFF to determine time
BlinkLED2 VAR LatB.4

MainLoop:
BlinkLED1 = 1 ' Top of LOOP on Logic 2 probe
BlinkLED2 = 0

New_Encoder = PortA & %00000011 ' Read encoder signals on RA0 and RA1
Test = New_Encoder ^ Old_Encoder ' Bitwise XOR current state with previous state to see if any pins changed.

IF Test.0 = 1 THEN ' Edge detected on channel A?
IF Old_Encoder.0 ^ Old_Encoder.1 THEN ' If Old_Encoder is 0 or 3 we count up, otherwise we count down.
Enc1_counter = Enc1_counter + 1
ELSE
Enc1_counter = Enc1_counter - 1
ENDIF
GOTO MainLoop
ENDIF

IF Test.1 = 1 THEN ' Edge detected on channel B?
IF Old_Encoder.0 ^/ Old_Encoder.1 THEN ' If Old_Encoder is 1 or 2 we count up, otherwise we count down.
Enc1_counter = Enc1_counter + 1
ELSE
Enc1_counter = Enc1_counter - 1
ENDIF
Goto MainLoop
ENDIF

BlinkLED1 = 0 ' Bottom of IFs on Logic 2 probe
BlinkLED2 = 1

' Other stuff here...

Goto MainLoop

EDIT: Oh, the timing measurment will not work properly since we're jumping back to mainloop before flipping the LEDs but you can fix that.

Demon
- 19th August 2024, 20:45
Holy poopenstuff!

My logic took about 20us, yours took about 2.4us!

:O

I knew there was room for improvement with bit manipulation, but I didn't expect nearly a ten-fold acceleration.


EDIT: Ooops, just noticed you're jumping out with turning off LED, adjusting now.


3.9 to 4.9us, depending on where it jumps out of logic.

Still, that's 4-5 times faster than my IFs.


EDIT SOME MORE: I put my LCDOUTs back in to check on Enc1_Counter, and something funky is going on. It's looping like crazy, gonna check to see if I can figure it out.

Demon
- 19th August 2024, 21:13
Henrik, my encoder goes from 00 to 11 in one click, then back to 00 in the next click.

Does your encoder do that also?

EDIT: I put in an old encoder that goes from 00 to 00 and that one works "better" from click to click, but as I move that one slowly, I can see the counter moving super fast, then stop on the click.

Demon
- 19th August 2024, 21:42
[QUOTE=HenrikOlsson;155986]You have turned off the WDT (for speed I suppose) but forgot to tell PBP about it so no speed gained.

You're assuming I know what I'm doing. :D

I didn't think I'd need WDT, until someone tells me I should use it. :)

Demon
- 19th August 2024, 23:57
Henrik, maybe a video would be helpful. 1st half is with a encoder with 30detents/15pulses, 2nd half is 20detents (I don't have datasheet for that one, no idea how many pulses).

https://youtu.be/kQLFlk5DtlQ?si=aoTj5MFWcqUiVqj3


That rocker switch selects logic; mine is first in video, yours is 2nd when switch is rocked to the right.

My code with combined logic. I added a switch and moved LCDOUT into a subroutine to keep the code clean:


goto Start

;--- Subroutines ---------------------------------------------------------------
LCDoutput:

LCDOUT $FE, $D4, "C:", dec5 Enc1_counter,_
" A:", DEC1 Enc1_WiperA,_
" B:", DEC1 Enc1_WiperB,_
" SW:", DEC1 Enc1_SPST : Pauseus 1
Return

Start:
Pause 500 ' Let PIC and LCD stabilize

LCDOUT $FE, 1 : Pauseus 1
LCDOUT $FE, $80, "ROTARY ENCODER TEST" : Pauseus 1

Mainloop:

BlinkLED1 = 1 ' Top of LOOP on Logic 2 probe
if logicselector = 1 then goto MyLogic

' Wiper Chart:
' ============
' A B
' --- ---
' 0 0
' 1 0 /\ CCW
' 1 1
' 0 1 \/ CW
' 0 0
'
' Careful, EC11 30 detents 15 pulses will move from 00 to 11
' EC11 20 detents can move from 00 back to 00 in one click

New_Encoder = PortA & %00000011 ' Read encoder signals on RA0 and RA1
Test = New_Encoder ^ Old_Encoder ' Bitwise XOR current state with previous state to see if any pins changed.

IF Test.0 = 1 THEN ' Edge detected on channel A?
IF Old_Encoder.0 ^ Old_Encoder.1 THEN ' If Old_Encoder is 0 or 3 we count up, otherwise we count down.
Enc1_counter = Enc1_counter + 1
ELSE
Enc1_counter = Enc1_counter - 1
ENDIF
BlinkLED1 = 0 ' Bottom of IFs on Logic 2 probe
GOSUB LCDoutput
GOTO MainLoop
ENDIF

IF Test.1 = 1 THEN ' Edge detected on channel B?
IF Old_Encoder.0 ^/ Old_Encoder.1 THEN ' If Old_Encoder is 1 or 2 we count up, otherwise we count down.
Enc1_counter = Enc1_counter + 1
ELSE
Enc1_counter = Enc1_counter - 1
ENDIF
BlinkLED1 = 0 ' Bottom of IFs on Logic 2 probe
GOSUB LCDoutput
Goto MainLoop
ENDIF

BlinkLED1 = 0 ' Bottom of IFs on Logic 2 probe

GOSUB LCDoutput

goto mainloop

MyLogic:

if Enc1_WiperA = 0 and Enc1_WiperB = 0 then ' See wiper chart above
if Enc1_previous = 01 then ' 2 digits to follow chart better
Enc1_direction = 1 ' 1=CW, 0=CCW
Enc1_rotation = 1 ' Motion occurred
else
if Enc1_previous = 10 then
Enc1_direction = 0 ' 0=CCW
Enc1_rotation = 1 ' Motion occurred
else
Enc1_direction = 0 ' Not relevant without motion
Enc1_rotation = 0 ' No motion occurred
endif
endif
Enc1_previous = 00 ' Save wiper position
endif

if Enc1_WiperA = 1 and Enc1_WiperB = 0 then
if Enc1_previous = 00 then
Enc1_direction = 1
Enc1_rotation = 1
else
if Enc1_previous = 11 then
Enc1_direction = 0
Enc1_rotation = 1
else
Enc1_direction = 0
Enc1_rotation = 0
endif
endif
Enc1_previous = 10
endif

if Enc1_WiperA = 1 and Enc1_WiperB = 1 then
if Enc1_previous = 10 then
Enc1_direction = 1
Enc1_rotation = 1
else
if Enc1_previous = 01 then
Enc1_direction = 0
Enc1_rotation = 1
else
Enc1_direction = 0
Enc1_rotation = 0
endif
endif
Enc1_previous = 11
endif

if Enc1_WiperA = 0 and Enc1_WiperB = 1 then
if Enc1_previous = 11 then
Enc1_direction = 1
Enc1_rotation = 1
else
if Enc1_previous = 00 then
Enc1_direction = 0
Enc1_rotation = 1
else
Enc1_direction = 0
Enc1_rotation = 0
endif
endif
Enc1_previous = 01
endif

if Enc1_rotation = 1 then
if (Enc1_WiperA = 0 and Enc1_WiperB = 0) or _
(Enc1_WiperA = 1 and Enc1_WiperB = 1) then
if Enc1_direction = 1 then
Enc1_counter = Enc1_counter + 1 ' Turned 1 position CW
else
if Enc1_counter > 0 then
Enc1_counter = Enc1_counter - 1 ' Turned 1 position CCW
endif
endif
endif
endif

GOSUB LCDoutput

goto mainloop

Demon
- 20th August 2024, 01:18
Henrik, inspired by your code. I got down to 6.4 - 8.6us:

Goes in initialization section:




Enc1_previous = %11111111
Enc1_Current = %00000000
Enc1_counter = 0


MainLoop:



MyLogic:

Enc1_Current = (Enc1_WiperA << 1) + Enc1_WiperB

IF Enc1_Current != Enc1_Previous THEN
IF Enc1_Current = %00000011 THEN
IF Enc1_Previous = %00000010 THEN
Enc1_counter = Enc1_counter + 1
ELSE
IF Enc1_Previous = %00000001 THEN
Enc1_counter = Enc1_counter - 1
ENDIF
ENDIF
ELSE
IF Enc1_Current = %00000000 THEN
IF Enc1_Previous = %00000001 THEN
Enc1_counter = Enc1_counter + 1
ELSE
IF Enc1_Previous = %00000010 THEN
Enc1_counter = Enc1_counter - 1
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF

Enc1_Previous = Enc1_Current

BlinkLED1 = 0 ' Bottom of IFs on Logic 2 probe

GOSUB LCDoutput

goto mainloop



Since my encoder doesn't stop on 01 or 10, I only need to check 11 and 00. Already 3 times faster than my original.


EDIT: Updated times

9723

HenrikOlsson
- 20th August 2024, 06:01
I use that code with an optical encoder to convert from quartature to up/down pulses and it tracks the encoder perfectly to >200k edges per second. I modified it to incorporate your counter but I might have messed something up.


Since my encoder doesn't stop on 01 or 10, I only need to check 11 and 00. Already 3 times faster than my original.
It might not have a detent at every quadrature state but it sure must "go thru" them.
If you are at 00 and go to 11, which direction did it turn? There's no way to know unless also decoding the 10/01 states in between the detents - and you are doing that.

Also, don't forget that your LCDoutput subroutine takes considerable amount of time, you can not allow the encoder to pass thru two (or more) states without reading it or it WILL miss pulses or even count backwards.

Take your logic analyzer capture and measure the time between the rising edges of channel A and B respectively.
The pulsewidth of channel A is 11ms but the time between the edges on A and B is much shorter than 5.5ms due to the fact that the phase shift is far from 90° on your encoder. Lets say it's 2ms... How long does your LCDoutput subroutine take?

Ioannis
- 20th August 2024, 13:18
Every time I press on the switch built into the shaft, wiper A bounces. Also, note how wiper A never came down at the top left.

Who could imagine that....! Wow, chinese keep surprising me!

Ioannis

Demon
- 20th August 2024, 17:07
I use that code with an optical encoder to convert from quartature to up/down pulses and it tracks the encoder perfectly to >200k edges per second.

Yeah, that's what I thought. We're not using the exact same device. I noticed such irregularities when I was switching between my own encoders (2 different batches).



It might not have a detent at every quadrature state but it sure must "go thru" them.

Oh definitely. I even see them display on the LCD when I turn my encoder REAL slow.



If you are at 00 and go to 11, which direction did it turn? There's no way to know unless also decoding the 10/01 states in between the detents - and you are doing that.

Yup, that's why I check the Current state against the Previous state. I only increase/decrease my counter on 00 and 11, cause that's where the encoder has a physical detent. A user turning the knob doesn't care about intermediate steps; only that the device actuates by one position when the knob goes click.



Take your logic analyzer capture and measure the time between the rising edges of channel A and B respectively.
The pulsewidth of channel A is 11ms but the time between the edges on A and B is much shorter than 5.5ms due to the fact that the phase shift is far from 90° on your encoder. Lets say it's 2ms...

The fastest interval between wiper A and B is on a drop, and it's 3.88ms



How long does your LCDoutput subroutine take?

~1.92ms


Note that the final product will use a dedicated PIC to display data on LCDs, and a separate PIC to process incoming encoder signals.

I'm only using the LCD alongside the encoder to help me understand how the encoder is behaving.

Demon
- 20th August 2024, 17:30
It might not have a detent at every quadrature state but it sure must "go thru" them.


Oh definitely. I even see them display on the LCD when I turn my encoder REAL slow.


That's why I had put this comment in my code, to show the CW/CCW progression through the wiper states:


' Wiper Chart:
' ============
' A B
' --- ---
' 0 0
' 1 0 /\ CCW
' 1 1
' 0 1 \/ CW
' 0 0
'
' Careful, EC11 30 detents 15 pulses will move from 00 to 11
' EC11 20 detents can move from 00 back to 00 in one click


No wonder my older EC11 were causing me so much grief in my initial testing. :D

Demon
- 20th August 2024, 17:46
Here's one of my faster ones:

A is 15.22ms duration
Difference between rise of A and B is 3.50ms
I nearly had time to send LCDOUT twice.

9724


I'm more than satisfied with what I have so far as encoder logic. When I remove LCDOUT, this is how many times the encoder logic can cycle during a fast pulse; a gazillion.

9725


I'm thrilled actually; with a big smile on my face as I type this. :D

HenrikOlsson
- 20th August 2024, 18:44
Very good, then there's plenty of margin.
Next challenge: Velocity sensing, ie a way to detect that the user wants the value to change by a lot and not have to turn the knob 600 turns. Getting that just right seems to be tricky, even the big boys don't always... (I've never tried).

How will you transfer the count from one PIC to the other? Keep track of your timing, you might be able to do it all with a single PIC. If needed, divide the LCDOutput routine in two (or more) parts if needed.

Ioannis
- 20th August 2024, 18:58
You can experiment with

DEFINE LCD_COMMANDUS 2500

And

DEFINE LCD_DATAUS 50

parameters in order to match your LCD module. Maybe you are lucky and have a faster one. But be carefull as these might not work with all modules.

The Velocity sensing is a really good one. Indeed, if the user has to to change values in a broad range it is very handy. Maybe if Timer is involved can make things simpler.

Ioannis

Demon
- 20th August 2024, 21:39
Very good, then there's plenty of margin.
Next challenge: Velocity sensing, ie a way to detect that the user wants the value to change by a lot and not have to turn the knob 600 turns. Getting that just right seems to be tricky, even the big boys don't always... (I've never tried). ...

That's exactly why I started looking up Elapsed Timer last night. I already have an idea in mind; rotate the encoder at 3 speeds, record the time elapsed between pulses, and then use that to determine if I increment/decrement by ones, fives or tens. I figure with some tweaking I can generate a decent compromise.

I've already adjusted my logic this morning to fall down to 359 when it goes below 0; to better simulate a heading gauge in flight sim, and inverse, flip up to 0 after moving up past 359.



... How will you transfer the count from one PIC to the other? ...

HSEROUT, that's why I had it in my early tests. It compares somewhat with LCDOUT timewise, maybe a tad slower.

I have a lot of R&D to go on that subject (like protocol and such).



... If needed, divide the LCDOutput routine in two (or more) parts if needed.

Nah, I have a LOT of controls to manage. I'm putting encoders as priority on a PIC, then switches and pots separately (ADC probably takes forever in comparison), and 4 LCDs wherever I have room and time available.

This is a preliminary layout; 3 enclosures: left, center and right . The right side has the most encoders with:

- 4 dual encoders
- 7 single encoders
- 1 mouse wheel (essentially a single encoder)

Those Custom Keys in the middle are just a reminder for a 4th optional enclosure at far left.

9726



The thin red stripes are LED stripes; shielded towards the controls to simulate a red glow, like this:

9727

rocket_troy
- 4th October 2024, 07:09
Very good, then there's plenty of margin.
Next challenge: Velocity sensing, ie a way to detect that the user wants the value to change by a lot and not have to turn the knob 600 turns. Getting that just right seems to be tricky, even the big boys don't always... (I've never tried).

I'm in the middle of a project requiring a rotary encoder to set a weight target for a loadcell. I plan on using Richard's graphical LCD driver for a dot matrix display (not superfast update refreshing), so I actually needed something like what you've described there Henrik. Using Demon's code, I twiddled it a bit to utilise (elapsed) Timer0 on an 18F26K22 to provide the screen refreshing. This is what I basically came up with (which I'm happy with):




'REMOVED ALL THE INIALIZATION STUFF FOR THIS PASTING

Enc1_counter = 1000

SETFONT FONT5x7
'SETFONT bignum5
LATC.4 = 0 'using a pic pin output as a temporary ground rail for the rotary encoder
gosub grf_clr

T0CON.7 = 1 'enable timer 0
T0CON.6 = 0 'timer 0 in 16 bit mode
T0CON.5 = 0 'timer 0 clock source = internal instruction clock
'T0CON.4
T0CON.3 = 0 'timer prescaler assigned
T0CON.2 = 0 ' prescaler assigned to 1:4
T0CON.1 = 0 ' "
T0CON.0 = 1 ' "

Timer_Counter = 0
Change = 0

MainLoop:
Timer_Counter.lowbyte = TMR0L
Timer_Counter.highbyte = TMR0H
New_Encoder = (PortB.4 << 1) + PortB.5

IF New_Encoder != Old_Encoder THEN
IF New_Encoder = %00000011 THEN
IF Old_Encoder = %00000010 THEN
Change = Change+1
Enc1_counter = Enc1_counter + Change
ELSE
IF Old_Encoder = %00000001 THEN
Change = Change+1
Enc1_counter = Enc1_counter - Change
ENDIF
ENDIF
ELSE
IF New_Encoder = %00000000 THEN
IF Old_Encoder = %00000001 THEN
Change = Change+1
Enc1_counter = Enc1_counter + Change
ELSE
IF Old_Encoder = %00000010 THEN
Change = Change+1
Enc1_counter = Enc1_counter - Change
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF

if change >= 1 and Timer_Counter>60000 then
TMR0H=0
TMR0L=0
ARRAYWRITE BUFF,[sdec Enc1_counter,0]
gosub grf_clr
'DMDSTR 10,10, BUFFp,1
DMDSTR 10,10, BUFF,1
gosub show
Change = 0
else
pause 5
endif

Old_Encoder = New_Encoder

goto MainLoop:


Troy