PDA

View Full Version : New approach to Rotary Encoder



Ioannis
- 21st March 2010, 23:12
Here is an experiment on Rotary Encoders and a different approach than the standard XOR way.



loop1:
if portb.1=0 then 'here is switch 1 of the rotary encoder
counter=counter+1
goto lcd
endif

if portb.0=0 then 'here is switch 2 of the rotary encoder
counter=counter-1
goto lcd
endif

goto loop1

lcd:
lcdout $fe,$c0,dec5 counter
while (portb.0=0 or portb.1=0):pause 10:wend
goto loop1


The key is the last While/Wend command where it checks for the next pulse comming after the first edge of either switch of the encoder.

Common of the encoder is Low and Pull Up is supposed to be active.

Ioannis

Mike, K8LH
- 22nd March 2010, 00:20
Can you refresh our memory and show us an example of "the standard XOR way"?

Ioannis
- 22nd March 2010, 10:28
Two examples in the attachments. They use Interrupts though. When I find some spare time I wll try to make the routines work with IOC and DT-INTS too.

Ioannis

Marin
- 15th February 2011, 19:21
Did you try this code with mechanical encoder, or normal optical encoder?

Ioannis
- 16th February 2011, 11:21
That was with mechanical one.

Ioannis

jefrem
- 15th August 2011, 17:00
With use 4 mecanic rotary encoder with this code (pic18f4550) ?

Ioannis
- 15th August 2011, 21:40
I don't see why not. Its fast enough.

Ioannis

Mike, K8LH
- 16th August 2011, 02:51
A question, please? With the following "while" statement, does the program wait there until both encoder pins are high? If so, then you're only detecting one of every four AB transitions when moving continually in one direction, correct?



while (portb.0=0 or portb.1=0):pause 10:wend
5872

cncmachineguy
- 16th August 2011, 03:47
Yes I think you are correct, this will only detect from 3 to 0 or 3 to 2. So it does not count in quadrature mode. It could be modified with a few more while loops to detect when it leaves the current state.

Ioannis
- 16th August 2011, 22:17
I think not since there is OR in the WHILE statement.

Ioannis

Mike, K8LH
- 24th October 2011, 03:50
I think not since there is OR in the WHILE statement.

But doesn't the OR mean that you're stuck in this loop while either A OR B is low? In other words, you can only get out of the loop when both A and B are high?

Demon
- 24th October 2011, 05:51
Yup, as long as one is LOW, it remains in the loop.

Robert

Ioannis
- 24th October 2011, 10:11
Yes it will for the time that either is in LOW state. If there is a possibility that this condition is likely to happn pemanently, then you have other options to choose.

As stated on he first post, that is an interesting experiment of another way (more simple) to do the same task. No-one is forced to accept it. Or use it. Just forget it and you will be just fine. :)

Ioannis

Mike, K8LH
- 25th October 2011, 01:28
Please forgive me if it seemed that I was complaining. I was just trying to understand how your method worked.

If you're using a rotary encoder with detents as a control, detecting a single AB transition (out of four) between detents is probably preferable. That's how I do it but I use a switch state latch and simple parallel switch state logic to filter out the unwanted switch states.

Your method is simple and elegant. Thank you for sharing with us...

Cheerful regards, Mike

Ioannis
- 25th October 2011, 08:19
No problem Mike. I have to admit that I was a little touchy.

Yes, the idea was to read a mechanical rotary encoder with detents. Sure is better to buffer the port states and then do the processing.

Even better, use interrups to grab the port change and then do whatever you want. More, make a debounce based on interrupts since mechanical switch of the encoder do bounce a little. But this is for more advanced programmers. Maybe later when my related project allows.

Ioannis

podgycode
- 4th November 2011, 08:22
Hello world. I have learnt a lot from lurking these threads so i thought i would try and help someone else out a bit.




All your encoder worries can be over for about 50 pence and if i can get a picture in here i will show you how...........

6111

the thing to realise is that moving along waveform A from left to right the negative edge of A is always when B is low. moving back the other way the negative edge is always when B is high.

the jk flip flop clock input is negative edge triggered and when triggered it feeds the input on J through to Q -- but only if j and k inputs are opposite hence section 1 of the nor gate to invert the signal. j is always opposite to k now. and the clock input always gets triggered right in the middle of the high or low so it never misses.

so, when moving clockwise Q will be high and anticlockwise it will go low.

two other parts of the quad nor gate (2 and 3 above) to produce a hardware XOR on output C and producing the waveform at bottom of screen.

Now all you need to do is feed C into an input that generates interrupts on a negative edge trigger. when the interrupt fires all it has to do is look at the level of D and either add 1 to clockwise total or subtract 1. Thats it. Your programming days are over.

And, all the mushy signal edges and bounce are tidied up too. Lovely solid latched outputs.
And you are going to have to go some to bother them with speed or with slowness.

Not bad for 50p I hope you will agree.

Heckler
- 4th November 2011, 15:39
Hey podgycode...
I have not tried your circuit. I would guess it works as you describe.

What I can't understand is why is this so difficult to do using two PIC pins and some code. Are these rotary encoders so difficult to read using code??

I am struggling with this very thing myself. I have two rotary encoders... one that I have wired up and done some basic testing... one that I have not tried yet.

I have also done quite a bit of searching over on the Parallax Basic Stamp forum, since the basic stamp uses VERY similar language to our PICbasic.

I really would like to use interrupts (DT instant INT's, if possible)

My real problem is TIME. If I only had more of it, I'm sure you all can relate.

Any way, if anyone has some good sample code (that used interrupts) for one to try... that they might be willing to share :)

aratti
- 4th November 2011, 22:03
Here the code for a rotary optical encoder, using quadrature.
Just connect the Channel A to portB.6 and channel B to portB.7, ground portB.5 and portB.4

Connection for LCD can be found to page 96 of PBP user manual.





include "ALLDIGITAL.pbp"
INCLUDE "DT_INTS-14.bas" ; Base Interrupt System
'INCLUDE "ReEnterPBP.bas" ; Include if using PBP interrupts

DEFINE OSC 20

' PIC 16F628
' PicBasic program to demonstrate operation of an LCD in 4-bit mode
' and reading an optical encoder in quadrature
'
' LCD should be connected as follows:
' LCD PIC
' DB4 PortA.0
' DB5 PortA.1
' DB6 PortA.2
' DB7 PortA.3
' RS PortA.4 (add 4.7K pullup resistor to 5 volts)
' E PortB.0
' RW Ground
' Vdd 5 volts
' Vss Ground
' Vo 20K potentiometer (or ground)
' DB0-3 No connect


TrisA = 000000
TrisB = 110000
PortB=0

Define LCD_DREG PORTA
Define LCD_DBIT 0
Define LCD_RSREG PORTA
define LCD_RSBIT 4
define LCD_EREG PORTB
define LCD_EBIT 0
define LCD_BITS 4
define LCD_LINES 2
define LCD_COMMANDUS 2000
define LCD_DATAUS 50

Flag var bit
wsave VAR BYTE $70 SYSTEM ' alternate save location for W
Q_New var Byte
Q_Old var byte
Q_Count var word


' Set variable value @ startup
Flag = 0
Q_New = 0
Q_Old = 0
Q_Count = 0

ASM
INT_LIST macro ; IntSource, Label, Type, Resetflag?
INT_Handler RBC_INT, _Encoder, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE RBC_INT ; enable external (INT) interrupts


Lcdout $fe, 1 ' Clear LCD screen
Lcdout "System Ready" ' Display message
Pause 500 ' Wait .5 second

Goto Main_Loop

Encoder:
Q_New = PortB.7 + PortB.7 + PortB.6

if Q_Old = 0 then
if Q_New = 2 then Minus_Count
if Q_New = 1 then Plus_Count
endif

if Q_Old = 1 then
if Q_New = 0 then Minus_Count
if Q_New = 3 then Plus_Count
endif

if Q_Old = 3 then
if Q_New = 1 then Minus_Count
if Q_New = 2 then Plus_Count
endif

if Q_Old = 2 then
if Q_New = 3 then Minus_Count
if Q_New = 0 then Plus_Count
endif

goto Q_Skip

Minus_Count:
Q_Count = Q_Count - 1
Flag = 1
goto Q_Skip

Plus_Count:
Q_Count = Q_Count + 1
Flag = 1
Q_Skip:
Q_Old = Q_New


@ INT_RETURN

Main_Loop:

if Flag = 1 then
Lcdout $fe, 1
Lcdout Dec Q_Count
Flag = 0
endif

goto Main_Loop

end


Enjoy

Cheers

Al.

podgycode
- 5th November 2011, 01:46
heckler,
have a look at this:-

' Target PIC : 12F, 16F, 18F
' Hardware : Non specific
' Oscillator : internal or external
' Keywords : ON INTERRUPT
' Description : PICBASIC PRO program to demonstrate use of On Interrupt
' Interrupts in BASIC. Turn LED on. Interrupt on PORTB.0 (INTE) turns LED off.
' Program waits .5 seconds and turns LED back on.

led Var PORTB.7

OPTION_REG = $7f ' Enable PORTB pullups

On Interrupt Goto myint ' Define interrupt handler
INTCON = $90 ' Enable INTE interrupt

mainloop:
High led ' Turn LED on
Goto mainloop ' Do it forever

' Interrupt handler
Disable ' No interrupts past this point
myint:
Low led ' If we get here, turn LED off
Pause 500 ' Wait .5 seconds
INTCON.1 = 0 ' Clear interrupt flag
Resume ' Return to main program
Enable

End

------------------------
the main loop runs and when an interrupt happens the program jumps to the handler called myint. does what is in myint and then returns to resume where it left off in the main loop.

just set the config so your interrupt is enabled that you want to use and put the code to read it in the myint routine. the main loop can do whatever it likes.

what do you think?

Mike, K8LH
- 5th November 2011, 02:21
Connection for LCD can be found to page 96 of PBP user manual.

Do you have a link the that manual, please? I've looked at two different PBP Compiler Manuals so far and neither has a schematic for an LCD interface on page 96...

Heckler
- 5th November 2011, 06:07
@ podgycode... Thanks for your example, but what I meant was I would like to implement a rotary encoder via DT's Instant Interupts. I have used "on interrupt" successfully before to detect push buttons, etc. I have yet to use InstantINT's. I am going to try aratti's example in post #18 above.

@ aratti... could you please clarify a couple of questions regarding your example...

ground portB.5 and portB.4Why is this necessary?



Q_New = PortB.7 + PortB.7 + PortB.6 What does this line of code do??



TrisA = 000000
TrisB = 110000 I assume these need % in front of the binary number??

I hope this is not considered "hijacking" of this thread... as it still regards implementing Rotary Encoders.
Thanks for your help




example

Heckler
- 5th November 2011, 06:25
@ aratti... one more question


wsave VAR BYTE $70 SYSTEM ' alternate save location for W
could you help me with this line? Is it correct or did the forum software mess it up??

thanks

P.S. Oh yea... disregard this question... I think I see how it works..

Q_New = PortB.7 + PortB.7 + PortB.6 What does this line of code do??

aratti
- 5th November 2011, 09:08
Heckler, yes trisA and trisB need "%" (don't know Why have been removed)

PortB.4 and portB.5 are set as input and never leave an input pin floating.

Wsave is a variable used by DT INT-14.


@Mike K8LH

The PBP manual they give me with PBP ver 2.47. But you can Find it also at page 168 of PBP3 manual.

Cheers

Al.

Mike, K8LH
- 6th November 2011, 04:51
@Mike K8LH

The PBP manual they give me with PBP ver 2.47. But you can Find it also at page 168 of PBP3 manual.


I wonder if there may be a problem with the PBP3 manual at http://www.pbp3.com/downloads/PBP_Reference_Manual.pdf? It seems to stop downloading at 731 kB (consistently)... Argh!!!

mackrackit
- 6th November 2011, 05:42
Works fine here. I just opened it in my browser.

Mike, K8LH
- 7th November 2011, 01:52
Works fine here. I just opened it in my browser.

Ok, it worked today... I've got it...

aratti
- 9th November 2011, 14:50
Here I post an update of the code posted in #18. This new code has a more efficient ISR so it can read at higher frequency. I don't know the maximum frequency that the system can acheive, so if someone with some spare time will give a try and post the result, could be useful.

As the previuos code, the mainloop doesn't do much, it simply update the display.

Here the code revised:



' PicBasic program to demonstrate how to read an optical encoder
' in quadrature. The code uses a 2 lines LCD in 4-bits mode
' **** MCU USED: PIC 16F628 ***
'
' ------------------ LCD should be connected as follows -------------------
' LCD PIC Port
' --- --------
' DB4 PortA.0
' DB5 PortA.1
' DB6 PortA.2
' DB7 PortA.3
' RS PortA.4 (add 4.7K pullup resistor to 5 volts)
' E PortB.0
' RW Ground
' Vdd 5 volts
' Vss Ground
' Vo 20K potentiometer (or ground)
' DB0-3 No connected

'-------------------------------------------------------------------------
' Encoder channel A PortB.6 (use pullups resistor if necessary)
' Encoder channel B PortB.7 (use pullups resistor if necessary)
' N/U PortB.0 (output)
' N/U PortB.1 (output)
' N/U PortB.2 (output)
' N/U PortB.3 (output)
' N/U PortB.4 (input - to be grounded)
' N/U PortB.5 (input - to be grounded)
'
'-------------------------------------------------------------------------

INCLUDE "DT_INTS-14.bas" ' Base Interrupt System (by Darrel Taylor)

DEFINE OSC 20

eeprom 0,[2,0,3,1,1,3,0,2]

Define LCD_DREG PORTA
Define LCD_DBIT 0
Define LCD_RSREG PORTA
define LCD_RSBIT 4
define LCD_EREG PORTB
define LCD_EBIT 0
define LCD_BITS 4
define LCD_LINES 2
define LCD_COMMANDUS 2000
define LCD_DATAUS 50

CMCON = %00000111
TrisA = %00000000
TrisB = %11110000
PortB = 0

Flag var bit
wsave VAR BYTE $70 SYSTEM ' alternate save location for W
Q_New var Byte
Q_Old var byte
M_Count var byte [4]
P_Count var byte [4]
Q_Count var word

' ---------------------- Set variable value @ startup ----------------------

For Q_Old = 0 to 3
Read Q_Old,M_Count[Q_Old]
Read Q_Old + 4,P_Count[Q_Old]
Next Q_Old

Q_Count = 0
Q_New = 0
Q_Old = 0
Flag = 0

ASM
INT_LIST macro ; IntSource, Label, Type, Resetflag?
INT_Handler RBC_INT, _Encoder, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE RBC_INT ; enable external (INT) interrupts

'---------------------------- Initialize LCD ----------------------------

Lcdout $fe, 1 ' Clear LCD screen
Lcdout "System Ready" ' Display message
Pause 500 ' Wait .5 second

Goto Main_Loop

Encoder: ' ISR

Q_New = PortB.7 + PortB.7 + PortB.6 ' get port status

If M_Count[Q_Old] = Q_New then ' if M_Count code satisfied then minus
Q_Count = Q_Count - 1
Flag = 1 ' set flag to true to update display
goto Q_Skip
endif

If P_Count[Q_Old] = Q_New then ' if M_Count code satisfied then plus
Q_Count = Q_Count + 1
Flag = 1 ' set flag to true to update display
endif

Q_Skip:

Q_Old = Q_New ' update Q_Old Byte

@ INT_RETURN

Main_Loop:

if Flag = 1 then
Lcdout $fe, 1 ' Clear LCD screen
lcdout dec Q_Count ' Display encoder position (counts)
Flag = 0 ' reset flag to false, display updated
endif

goto Main_Loop

end


Cheers

Al.

Heckler
- 9th November 2011, 15:11
Thanks! Aratti...

I had just finished adapting your earlier example for a 16F690 pic. It took me a while to figure out why my LCD was not working... Dang MCLR pin tripped me up untill I rememberd that it could not act as an output... then I had to move my LCD to PortC... then I had to figure out that my OLD 4 line LCD (bought it about 20 Years ago from AllElectronics) needed more time on the LCD_COMMANDUS.

Anyway it is working GREAT...
I will give your latest code a spin this weekend.

As far as update time to the LCD... it seems to be limited to any Pause or other code that is in the Main Loop. I had been blinking an LED in the main loop using a Pause statement and therefore this limited the LCD update frequency. I think I'll try and impliment another DT-Int using a timer to run the blinking LED in the main loop. This should remove any excessive delay to the LCD update.

PS. I can post Aratti's modified working code for the 16F690 if anybody is interested...

Mike, K8LH
- 12th November 2011, 06:13
.... As far as update time to the LCD... it seems to be limited to any Pause or other code that is in the Main Loop. I had been blinking an LED in the main loop using a Pause statement and therefore this limited the LCD update frequency. I think I'll try and impliment another DT-Int using a timer to run the blinking LED in the main loop. This should remove any excessive delay to the LCD update.

May I offer insight into another way around this problem? I've always been concerned about the overhead of printing to an HD44780 compatible character display. For example, if you tie up the processor for 400 usecs while printing eight (8) characters to the LCD, you might have a problem with another process that needs to run at 100 usec intervals. Instead of updating the display the usual way, with a 40 usec delay between each write, why not refresh the LCD from a 32 character buffer as a background task using interrupts. I did this recently on a TI MSP430 Launchpad project and it worked very well.

Here's the basic idea (C code). Your main program simply writes ASCII data into a 32 character array to update the display. Array elements 0-15 and 16-31 are mapped to LCD lines 1 and 2, respectively. The driver in the ISR only writes one character at a time from the array to the LCD during each interrupt, but I used a 500 usec interrupt interval so the entire display is actually updated around 59 times each second (updates are perceived as instantaneous). Also note that the PutLCD routine (not shown) does not need to include the normal 40 usec LCD inter-character delay.

Food for thought... Regards, Mike




unsigned char lcd[32] = { " " };



void interrupt()
{ // 500 us TMR2 interrupts
static char bndx = 0x00; // lcd display buffer index, 0..31
static char line = 0xC0; // lcd display DDRAM address

pir1.TMR2IF = 0; // clear TMR2 interrupt flag
/* *
* refresh one character on the display each interrupt. entire *
* display refreshed every 17 msecs (58.8 Hz refresh rate). *
* */
if(line.7) // if "new line"
PutLCD(line ^= 64,0); // toggle LCD DDRAM "line" address
else // not "new line" so
PutLCD(lcd[bndx++],1); // refresh display, bump index
bndx &= 31; // pseudo mod 31, 0..31 inclusive
if((bndx & 15) == 0) // if new line (0 or 16)
line.7 ^= 1; // toggle b7 pseudo "new line" flag
}

Ioannis
- 8th December 2011, 22:12
For some peculiar reason that I cannot understand, Al's code is counting +/- 4 steps instead of one for each click.

Anyone can speculate why?

With a logic analyzer I have confirmed that the encoder is working fine. Here is a snapshot for 4 clicks of the encoder.

6164

Ioannis

HenrikOlsson
- 9th December 2011, 07:36
Hi Ioannis,
I haven't looked at the particular code in question but generally, you decode all 4 edges, hence the term quadrature. Look at the capture for one click, first Ch.0 goes low (1) then Ch.1 goes low (2), then Ch.0 goes high (3) and finally Ch.1 goes high (4). So, 4 counts per click.

/Henrik.

Ioannis
- 9th December 2011, 14:31
Hmm, thanks Henrik, it looks logic now... well...

Ioannis

Demon
- 27th February 2012, 23:57
I need a rotary encoder for manual input in flight simulator; changing radio frequency, autopilot altitude, etc. I like the "slower" detection in post #1 for this particular application or else I'd issue 4 keyboard commands in a single detent. 100 feet would change to 500 feet with a single click with no way to set to the in-between values.

But Podgy brings up a very important point; all rotary encoder datasheets I've seen mention debounce. I know most PIC inputs have Schmitt triggers, but just how effective are they in these cases? I can't test until my batch arrives.

Robert
:)

Ioannis
- 28th February 2012, 08:56
a couple of 100nF were needed for mine. After placing the caps to ground from each encoder output pin, false readings were a past.

Ioannis

fratello
- 12th April 2012, 09:32
Need help with one enconder ... a little different.
How can I read the "up/+" and "down/-" action of this particular encoder ?
Any help will be appreciated ! Thanks in advance !

fratello
- 12th April 2012, 10:39
It's ok something like this ?!


TrisA = %00000000
TrisB = %11110000
PortB = 0

Flag var bit
Q_New var Byte
Q_Old var byte
M_Count var byte [4]
P_Count var byte [4]
Q_Count var word

' ---------------------- Set variable value @ startup ----------------------

For Q_Old = 0 to 3
Read Q_Old,M_Count[Q_Old]
Read Q_Old + 4,P_Count[Q_Old]
Next Q_Old

Q_Count = 0
Q_New = 0
Q_Old = 0
Flag = 0


Goto Main_Loop

Encoder: '
Q_New = PortB.7 + PortB.6 + PortB.5 ' get port status

If M_Count[Q_Old] = Q_New then ' if M_Count code satisfied then minus
Q_Count = Q_Count - 1
Flag = 0 ' set flag for DOWN
goto Q_Skip
endif

If P_Count[Q_Old] = Q_New then ' if M_Count code satisfied then plus
Q_Count = Q_Count + 1
Flag = 1 ' set flag for UP
endif


Q_Skip:
Q_Old = Q_New ' update Q_Old Byte


Main_Loop:
if Flag = 1 then do command1
if Flag = 0 then do command2
goto Main_Loop

fratello
- 13th April 2012, 08:00
So far...no good :( ...
Complete "hardware" it's like in picture . I intend to use an remote with some "rotary encoder" ...
The remote controller has six wires . They are connected to three rows and three columns. This allows maximum of 3*3=9 functions. Roller takes three functions because it must be possible to know which way it is rotated (how doing this in PBP ?!?). Rest of the functions are assigned to six buttons.
I use great pice of code (keypad matrix routine) of Mr. Mister-e and the "regular" buttons works verry fine...but I want to use the function of roller too.
I can't figure out how doing this, so any "clue" will be appreciated !

HenrikOlsson
- 13th April 2012, 09:52
Hi,
Without analyzing your code in detail one possible problem might be this line

Q_New = PortB.7 + PortB.6 + PortB.5 ' get port status
In this case you'll get Q_New = 1 no matter WHICH of the three pins are high. I'm not sure but I guess that's not what you want.

You could try something like Q_New = PortB & %11100000 >> 5 instead.

Here's another approach (untested) which is smaller in terms of program space and RAM but like I said it's untested so it might not even work.

oldState VAR BYTE
newState VAR BYTE
Q_Count VAR WORD
DIR VAR BIT

UP CON 1
DN CON 0

newState = (PortB & %11100000) ' Isolate top three bits, newState will be 32, 64 or 128

If newState <> 0 THEN
If newState <> oldState THEN ' Changed from last time?

Select Case oldState
Case 32
If NewState = 128 THEN ' Was 1 now 4 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 1 now 2 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF


Case 64
If NewState = 32 THEN ' Was 2 now 1 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 2 now 4 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF


Case 128
If NewState = 64 THEN ' Was 4 now 2 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 4 now 1 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF

END SELECT

oldState = NewState

ENDIF
ENDIF

fratello
- 13th April 2012, 10:38
It a real pleasure to see how generous some people can be ... Thank You, AGAIN, Mr. Henrik !
...I think I missing something ... If UP do something, If Down do something else...dont ?!
But nothing happens.

'************************************************* ***************
@ DEVICE pic16F628A, XT_OSC, WDT_OFF, PWRT_ON, BOD_OFF, MCLR_ON, LVP_OFF

Define OSC 4 ' 4MHz
CMCON = 7 ' Disable on-chip comparator, PORTA in digital mode

include "alldigital.pbp"

TrisA = 000000
PortB = 010000
TrisB = 000000

oldState VAR BYTE
newState VAR BYTE
Q_Count VAR WORD
DIR VAR BIT

UP CON 1
DN CON 0


Main_Loop:
newState = (PortB & 100000)
goto encoder
if dir = up then portb.0 = 1
if dir = dn then portb.1 = 1
goto Main_Loop
goto encoder
if dir = up then
portb.4 = 1
portb.0 = 1
endif
if dir = dn then
portb.4 = 0
portb.1 = 1
endif
goto Main_Loop


encoder:

If newState <> 0 THEN
If newState <> oldState THEN ' Changed from last time?

Select Case oldState
Case 32
If NewState = 128 THEN ' Was 1 now 4 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 1 now 2 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF


Case 64
If NewState = 32 THEN ' Was 2 now 1 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 2 now 4 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF


Case 128
If NewState = 64 THEN ' Was 4 now 2 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 4 now 1 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF

END SELECT

oldState = NewState

ENDIF
ENDIF


Or I wired the roller wrong ?
Here ( http://www.angelfire.com/nd/maza/kenwood.html ) it's a working variant, but in other "way" , using Atmega ...

mackrackit
- 13th April 2012, 11:00
http://www.picbasic.co.uk/forum/showthread.php?t=778&p=33089#post33089

HenrikOlsson
- 13th April 2012, 12:01
Try GOSUB Encoder and put a RETURN at the end of the subroutine. Currently you GOTO Encoder and then you run off into never never land. I'm not saying that will make it work but that's one possible problem.

Also, make sure that have the "wiper" of the roller connected to 5V, verify that you get signals on the actuall pins of the PIC when you rotate the encoder/roller.

fratello
- 13th April 2012, 12:21
I think the hardware must be build in this way (since the rest of buttons are allready connected as matrix). I wonder if "KeyPad.bas" of Mr.Mister-e can be addapted to read the increment/decrement of roller, by "including" somehow the code for encoder ....
Brrrrrr...it's beyond of my intellectual capacities :( .

HenrikOlsson
- 13th April 2012, 14:23
Hi,
I don't have any experience with that routine but provided everything else works the way you want why don't you just set the pin which is common to the encoder (RB5 ?) high, read the port, and return the pin low.

GOSUB ScanMatrix 'Or whatever
PortB.5 = 1
GOSUB Encoder
PortB.5 = 0

Finally, in the first code you posted you had the encoder to RB7-5 so I based my code on that. Now, in your latest schematic, you have it on RB2-0 which means that my code (if that's what you're trying) definitely won't work without modifying it since it expects the three "switches" in the encoder to be on RB7-5.

fratello
- 13th April 2012, 18:22
What I do wrong ?!
I adapted "keypad.bas" to my hw :

KB_ROW = 3 ; 3 ROW keypad
KB_ROW_PORT = PORTB ; Keypad ROW on PORTB
KB_ROW_BIT = 3 ; ROW0 = PORTB.3
KB_COL = 3 ; 3 COL keypad
KB_COL_PORT = PORTB ; Keypad Col on PORTB
KB_COL_BIT = 0 ; COL0 = PORTB.0
DebounceDelay = 0x80 ; debounce delay 41mSec
SINGLE_SCAN = 0 ; Scan ;till a key is pressed
KEYPAD_AUTOREPEAT = 1

and I wrote this pice of code :

@ DEVICE pic16F628A, XT_OSC, WDT_OFF, PWRT_ON, BOD_OFF, MCLR_ON, LVP_OFF

Define OSC 4 ' 4MHz
CMCON = 7 ' Disable on-chip comparator, PORTA in digital mode

include "alldigital.pbp"
include "C:\PBP\enc_KEY.BAS"

TrisA = %00000000
PortA = %00000000
PortB = %00111000
TrisB = %00011000

oldState VAR BYTE
newState VAR BYTE
Q_Count VAR WORD
DIR VAR BIT

UP CON 1
DN CON 0


Main_Loop:
newState = (PortB & %00111000)

portb.5 = 1
gosub encoder
portb.5 = 0

if dir = up then
porta.0 = 0
porta.1 = 1
endif
if dir = dn then
porta.0 = 1
porta.1 = 0
endif
goto Main_Loop


encoder:
If newState <> 0 THEN
If newState <> oldState THEN ' Changed from last time?
Select Case oldState
Case 32
If NewState = 128 THEN ' Was 1 now 4 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 1 now 2 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF

Case 64
If NewState = 32 THEN ' Was 2 now 1 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 2 now 4 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF

Case 128
If NewState = 64 THEN ' Was 4 now 2 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 4 now 1 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF
END SELECT

oldState = NewState

ENDIF
ENDIF
Return
..but no change in porta.0 or porta.1 state ...

fratello
- 13th April 2012, 19:20
Update code :

Main_Loop:
gosub keypadscan
newState = (PortB & %00000111)
gosub encoder
gosub check
goto main_loop

Check :
if dir = up then
porta.0 = 0
porta.1 = 1
endif
if dir = dn then
porta.0 = 1
porta.1 = 0
endif
return


encoder:
If newState <> 0 THEN
If newState <> oldState THEN ' Changed from last time?
Select Case oldState
Case 32
If NewState = 128 THEN ' Was 1 now 4 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 1 now 2 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF

Case 64
If NewState = 32 THEN ' Was 2 now 1 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 2 now 4 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF

Case 128
If NewState = 64 THEN ' Was 4 now 2 = Up
Q_Count = Q_Count + 1
DIR = UP
ELSE ' Was 4 now 1 = Down
Q_Count = Q_Count - 1
DIR = DN
ENDIF
END SELECT

oldState = NewState

ENDIF
ENDIF
Return
but no matter what M (1,2 or 3) I push, porta.0 goes high and stay so.

HenrikOlsson
- 13th April 2012, 20:01
Hi,
In the first code you posted you read the status of PortB before driving the the common pin for the encoder so there probably won't be any signals comming in.
In the second, updated example, you never drive the common pin for the encoder at all.
You need to set the "common pin" high (or low depending on hardware) and THEN read the port otherwise you won't get anything back.


You really need to sit back and think the problem thru, not just cut and paste pieces of code together without understanding what they do and how the hardware they're intended to interface to is supposed to work.

Think it thru.... The "encoder" has a "wiper" which "cylces" thru the different contacts. Depending on which contact the wipers is positioned at you'll get a signal out on a certain pin. In order to get that signal out the "wiper" must be "powered" in one way or another. If the inputs from the "contacts" are pulled up then wiper must be grounded so that it pulls the contact (and signal LOW). Likewise if the inputs from the contacts are pulled down then the "wiper" must be high in order to pull the inputs high.

If the inputs are pulled up with resistors then you'll get a "walking zero" across the three pins, if the inputs are pulled down you'll get a "walking one" across the inputs. You must adapt the code to match.

My suggestion, for now, is that you ditch the matrix routine untill you get the encoder working - and understand how it works.

/Henrik.

fratello
- 13th April 2012, 20:12
Thanks for support !
"In the second, updated example, you never drive the common pin for the encoder at all."
But ..."keypadscan" don't do this ? Proteus show how "0" walk through portb 3,4,5.

HenrikOlsson
- 13th April 2012, 20:23
Hi,
Like I said earlier, I don't have any experience with that piece fo code but if I'd venture I guess (and I'm pretty certain I'm right) it scans the pins once each time you GOSUB the routine. This means it does NOT scan the pins during the time you GOSUB the encoder routine.

Does Proteus show that walking zero across the pins at the same time as you do newState = PortB & %00000111 - I'd say no.

If your inputs are pulled up with resistors then the common/wiper of the encoder must be LOW at the time you read the status of the port. You must understand and remember that the PIC executes the code sequentially, one instruction after another.

fratello
- 14th April 2012, 12:25
Step-by-step I think I begin to understand ...
The correct hardware it's this.
The software can be this :

@ DEVICE pic16F628A, XT_OSC, WDT_OFF, PWRT_ON, BOD_OFF, MCLR_ON, LVP_OFF

Define OSC 4 ' 4MHz
CMCON = 7 ' Disable on-chip comparator, PORTA in digital mode

include "alldigital.pbp"
include "C:\PBP\enc_KEY.BAS"

TRISB = 000000
PORTB = 000000

oldState VAR BYTE
newState VAR BYTE
DIR VAR BIT

UP CON 1
DN CON 0

Main_Loop:
portb.5 = 1
gosub keypadscan
gosub check
goto main_loop

Check :
if dir = up then
portb.0 = 1
pause 1000
portb.0 = 0
endif
if dir = dn then
portb.1 = 1
pause 1000
portb.1 = 0
endif
return

encoder:
portb.5 = 0
newState = (porta & 000111)
If newState <> oldState THEN ' Changed from last time?
Select Case oldState
Case 2
If NewState = 4 THEN ' Was 1 now 4 = Up
DIR = up
ELSE ' Was 1 now 2 = Down
DIR = dn
ENDIF

Case 4
If NewState = 1 THEN ' Was 2 now 1 = Up
DIR = up
ELSE ' Was 2 now 4 = Down
DIR = dn
ENDIF

Case 1
If NewState = 2 THEN ' Was 4 now 2 = Up
DIR = up
ELSE ' Was 4 now 1 = Down
DIR = dn
ENDIF
END SELECT

oldState = NewState
endif
portb.5 = 1
Return
The question is : where to put "gosub encoder" ? Inside the "enc_key.bas" (it's keypad.bas addapted to 2 row/3 cols) ???

HenrikOlsson
- 14th April 2012, 13:54
Hi,
Now we're getting somewhere - that looks like it might work.
The keypad routine has nothing to do with reading the encoder. Put the GOSUB Encoder in the mainloop, before GOSUB check obviously....

And again, if it doesn't work then remove the keypad routine untill you get the encoder routine working - THEN try to make the two coexist. There's no guarantee that the encoder code works, I just posted it as one possible idea of how to read that kind of "encoder".

fratello
- 14th April 2012, 14:20
No matter what I do, portb.1 goes high and low :( .... portb.0 don't change :( ....

TRISB = 000000
PORTB = 000000

oldState VAR BYTE
newState VAR BYTE
DIR VAR BIT

UP CON 1
DN CON 0

oldstate = (porta & 000111)

Main_Loop:

gosub Encoder
gosub check
goto main_loop

Check :
if dir = up then
portb.0 = 1
pause 1000
portb.0 = 0
endif
if dir = dn then
portb.1 = 1
pause 1000
portb.1 = 0
endif
return

encoder:
portb.5 = 1
pause 100
newState = (porta & 000111)
portb.5 = 0
If newState <> oldState THEN ' Changed from last time?
Select Case oldState
Case 1
If NewState = 4 THEN dir=up
If NewState = 2 THEN dir=dn

Case 2
If NewState = 4 THEN dir=up
If NewState = 1 THEN dir=dn

Case 4
If NewState = 1 THEN dir=up
If NewState = 2 THEN dir=dn

END SELECT

oldstate=newstate
endif
Return

HenrikOlsson
- 14th April 2012, 14:45
Hi,
Since you have the inputs pulled high and the wiper "grounding" each input as it cycles thru them you can't use 1, 2 and 4 as the states since that "looks for" a '1' on the individual pins. You need to use 3, 5 and 6 instead.

fratello
- 14th April 2012, 15:38
Twillight zone ....

Select Case oldState
Case 3
If NewState = 6 THEN dir=up
If NewState = 5 THEN dir=dn

Case 5
If NewState = 3 THEN dir=up
If NewState = 6 THEN dir=dn

Case 6
If NewState = 5 THEN dir=up
If NewState = 3 THEN dir=dn
END SELECT
No matter what I do, portb.1 goes high and low :( .... portb.0 don't change :( ....

HenrikOlsson
- 14th April 2012, 16:13
Don't know what to tell you but the code works just fine here.

HSEROUT ["Program start",13]

Main:
PortB.5 = 0
newState = PortA & %00000111
PortB.5 = 1

If newState <> 7 THEN
If newState <> oldState THEN

Select Case oldState
Case 3
If NewState = 6 THEN dir=up
If NewState = 5 THEN dir=dn

Case 5
If NewState = 3 THEN dir=up
If NewState = 6 THEN dir=dn

Case 6
If NewState = 5 THEN dir=up
If NewState = 3 THEN dir=dn
END SELECT

HSEROUT["oldState: ", DEC oldState, " newState: ", DEC newState, " DIR: ", DEC DIR, 13]

oldState = newState
Pause 1000
ENDIF
ENDIF

Goto Main
This was executed on an AMICUS18 board with a PIC18F25K20. A wire from RB5 to the pulled up inputs was used to simulate the "encoder", the results are here:

6404

As you can see, it works.

Have you verified that PortB.5 does go LOW?
Have you verified that you DO get a LOW on RA0,1 and 2 as you rotate the encoder?
Have you checked the datasheet to see if any other peripherals are multiplexed onto the pins you're trying to use? ADC? Comparator?

mackrackit
- 14th April 2012, 16:29
Been said before and I will say it again.

NEVER TRUST A SIM!

fratello
- 14th April 2012, 17:16
... ok, so work, despite the "non-working" of simulator !
Next step is to try to make the two coexist (keypad.bas & encoder.bas) .
First I will build the schematic and making some tests. Thanks again for the huge support !

fratello
- 14th April 2012, 19:16
It's this a viable options of reading encoder AND the others buttons ?

Main:
;==============
;reading buttons on line 0 (pseudo-encoder)
PortB.5 = 0
newState = PortA & %00000111
PortB.5 = 1
If newState <> 7 THEN
If newState <> oldState THEN

Select Case oldState
Case 3
If NewState = 6 THEN
dir=up
portb.0 = 1
endif
If NewState = 5 THEN
dir=dn
portb.1 = 1
endif

Case 5
If NewState = 3 THEN
dir=up
portb.0 = 1
endif
If NewState = 6 THEN
dir=dn
portb.1 = 1
endif

Case 6
If NewState = 5 THEN
dir=up
portb.0 = 1
endif
If NewState = 3 THEN
dir=dn
portb.1 = 1
endif
END SELECT
oldState = newState
Pause 100
ENDIF
ENDIF
;==============
;reading buttons on line 1
Porta.4 = 0
status_2 = PortA & %00000111
Porta.4 = 1
select case status_2
case 3
gosub trackDn
case 5
gosub mute
case 6
gosub trackUp
end select
;==============
;reading buttons on line 2
Porta.3 = 0
status_3 = PortA & %00000111
Porta.3 = 1
select case status_3
case 3
gosub volDn
case 5
gosub volUp
case 6
gosub Sursa
end select
Goto Main

fratello
- 14th April 2012, 22:24
So far, so god ... Now another problem : how can I separates signals?
If rotary switch is Up, can't read if TK- or V- are pressed ..
If rotary switch is in middle position, can't read if MUTE or V+ are pressed ...
If rotary switch is down, can't read if TK+ or S are pressed...


LE - I think this work :

;reading buttons on line 1
Porta.4 = 0
trisb.5 = 1
pause 50
status_2 = PortA & %00000111
Porta.4 = 1
trisb.5 = 0

fratello
- 16th April 2012, 18:01
New code works pretty well ... But, how can I avoid false roller codes ? If I turn up, not always the the code "see" up ; if I turn down, not always the code "see" down ...Sometimes, the commands (Up/Down) are inversed !
I try to add some "pause" between different commands, but the problem remains.

HenrikOlsson
- 16th April 2012, 18:26
No, don't try to slow it down...speed it up. If you have any pauses in there try removing those. If your program is going to do 'other' things as well (which I'm sure it is) then you may need to sample the encoder several times thru out the main loop.

The reason is because if you are at one state and then turn forward two states but you do it fast enough so that the code doesn't detect the "middle" state, then it looks like you turned one state backwards instead of two forward.

So, you want to sample the encoder as fast as you can possibly turn it - and then some.

/Henrik.

fratello
- 16th April 2012, 18:53
I try both variants, presented in code, but without major improvements.

;==============
;reading buttons on row 0 (pseudo-encoder)
PortA.1 = 0
; pause 10
@ nop
newState = PortA & %00011100 ; this is for my hw : 11100 = 28
PortA.1 = 1
If newState <> 28 THEN
If newState <> oldState THEN

Select Case oldState
Case 12
If NewState = 24 THEN
dir=up
endif
If NewState = 20 THEN
dir=dn
endif

Case 20
If NewState = 12 THEN
dir=up
endif
If NewState = 24 THEN
dir=dn
endif

Case 24
If NewState = 20 THEN
dir=up
endif
If NewState = 12 THEN
dir=dn
endif
END SELECT
oldState = newState
GOSUB UpOrDn
pause 100
endif
ENDIF

HenrikOlsson
- 16th April 2012, 19:41
Which two variants? I only see one in your previous message.
In that one you have a PAUSE 10 at the beginning, which is commented out, and also a @NOP. Then you have a PAUSE 100 near the end which is NOT commented out.

/Henrik.

fratello
- 16th April 2012, 20:29
Sorry...
"@ nop" ... not good
and then
"pause 10" ....idem.

Ioannis
- 16th April 2012, 22:05
If you place two 100nF to ground for debouncing false results will be limited.
Ioannis

fratello
- 16th April 2012, 22:28
I already tried that...without results ! %$@#^*#@^ roller !

mister_e
- 17th April 2012, 01:07
not sure to understand your thing, but give yourself a chance, plug the encoder on a set of "interruptable" I/O, the wiper to GND and use interrupt to triger the Encoder reading. end of the story.

HenrikOlsson
- 17th April 2012, 07:28
END SELECT
oldState = newState
GOSUB UpOrDn
pause 100 ' <--- Have you removed this?
endif
ENDIF

fratello
- 17th April 2012, 08:17
Mr.Mister_E : Can not put the wiper to GND because of hardware ( http://www.picbasic.co.uk/forum/showthread.php?t=12911&p=113126#post113126 ).
Mr.Henrik : I remove already this line ; is better but not best.

fratello
- 17th April 2012, 15:58
I tried to ad a second check of direction :

loop:
PortA.1 = 0
newState = PortA & 011100 ; this is for my hw : 11100 = 28
pause 50 ; no matter what I put here : none, @ nop , pause 100 !
newa = PortA & 011100
PortA.1 = 1
If newState <> 28 THEN
If newState <> oldState THEN

Select Case oldState
Case 12
If NewState = 20 THEN
if newa = 24 then dir=up
endif

case 12
If NewState = 24 THEN
if newa = 20 then dir=dn
endif


Case 20
If NewState = 24 THEN
if newa = 12 then dir=up
endif

Case 20
If NewState = 12 THEN
if newa = 24 then dir=dn
endif

Case 24
If NewState = 12 THEN
if newa = 20 then dir=up
endif

Case 24
If NewState = 20 THEN
if newa = 12 then dir=dn
endif

END SELECT
oldState = newstate
GOSUB UpOrDn
endif
ENDIF


But, in this way, any direction I rotate, the command is always "Dir Up" ?! Never goes "down" ...

HenrikOlsson
- 17th April 2012, 17:21
And what does the UpOrDn routine look like?

fratello
- 17th April 2012, 18:05
This routine should do "Track+" /"Track-" :

UpOrDn:
if dir=1 then
PortB.4 = 0
pauseus 3850
PortB.4 = 1
pauseus 3850
PortB.4 = 0
pauseus 660
PortB.4 = 1
endif

if dir=0 then
PortB.4 = 0
pauseus 4450
PortB.4 = 1
pauseus 4400
PortB.4 = 0
pauseus 660
PortB.4 = 1
endif
return

HenrikOlsson
- 17th April 2012, 18:15
And what do we have in there, if not delays... They are quite short (8-10ms in total) but it's still possible that that is what's messing with you.
I don't know what you intended to do with that latest version though, why do you have two cases with the same value, just put the code for both in one.

fratello
- 17th April 2012, 18:56
This is schematic and code...Maybe I miss something important ...

@ DEVICE pic16F628A, XT_OSC, WDT_OFF, PWRT_ON, BOD_OFF, MCLR_ON, LVP_OFF, PROTECT_ON

include "alldigital.pbp"
Define OSC 4 ' 4MHz
CMCON = 7 ' Disable on-chip comparator, PORTA in digital mode


TrisA = %00011100 ; coloane la RA2, 3, 4 ; randuri la RA1, 0 ,7
porta = %00011100
TRISB = %00000000
PORTB = %00010000

oldState VAR BYTE
newState VAR BYTE

UP CON 1
DN CON 0


loop:
PortA.1 = 0
newState = PortA & %00011100 ; this is for my hw : 11100 = 28
PortA.1 = 1

If newState <> 28 THEN
If newState <> oldState THEN

Select Case oldState
Case 12
If NewState = 20 THEN dir=up
If NewState = 24 THEn dir=dn

Case 20
If NewState = 24 THEN dir=up
If NewState = 12 THEN dir=dn

Case 24
If NewState = 12 THEN dir=up
If NewState = 20 THEN dir=dn
END SELECT
oldState = newstate
GOSUB UpOrDn
pause 15 ; time for UpOrDn
endif
ENDIF
;============
UpOrDn:
if dir=DN then
PortB.4 = 0
pauseus 3850
PortB.4 = 1
pauseus 3850
PortB.4 = 0
pauseus 660
PortB.4 = 1
endif

if dir=UP then
PortB.4 = 0
pauseus 4450
PortB.4 = 1
pauseus 4400
PortB.4 = 0
pauseus 660
PortB.4 = 1
endif
return

end

HenrikOlsson
- 17th April 2012, 19:07
1) I don't understand why you keep putting those PAUSE statements in there, now you have a PAUSE 15 in there....
2) There's no GOTO LOOP anywhere so the program will "fall thru" into the UpOrDn routine then hit the RETURN and bad things will happpen. (BTW, Loop is a reserved word in newer versions of PBP, best to get used to not using it.
3) Try resampling the pins and set oldState when you return from the subroutine, ie (changes in red):

Main:
PortA.1 = 0
newState = PortA & %00011100 ; this is for my hw : 11100 = 28
PortA.1 = 1
If newState <> 28 THEN
If newState <> oldState THEN

Select Case oldState
Case 12
If NewState = 20 THEN dir=up
If NewState = 24 THEn dir=dn

Case 20
If NewState = 24 THEN dir=up
If NewState = 12 THEN dir=dn

Case 24
If NewState = 12 THEN dir=up
If NewState = 20 THEN dir=dn
END SELECT
GOSUB UpOrDn

PAUSE 15 ; time for UpOrDn

' Now resample the encoder and keep the state as oldState
PortA.1 = 0
oldState = PortA & %00011100 ; this is for my hw : 11100 = 28
PortA.1 = 1

ENDIF
ENDIF
GOTO MAIN
;============
UpOrDn:
if dir=DN then
PortB.4 = 0
pauseus 3850
PortB.4 = 1
pauseus 3850
PortB.4 = 0
pauseus 660
PortB.4 = 1
endif
if dir=UP then
PortB.4 = 0
pauseus 4450
PortB.4 = 1
pauseus 4400
PortB.4 = 0
pauseus 660
PortB.4 = 1
endif
RETURN
END

fratello
- 17th April 2012, 20:28
Mr. Henrik I must get my hat in front of you ! (Hope G.translate work well :) !).
NOW, the code work verry good !!! Only sometimes the command are inversed , but in 9 case of 10 it's ok, so I can live with this...
Thanks for lost so much time with me ! I am verry grateful !

HenrikOlsson
- 17th April 2012, 21:05
Great, finally... Now try removing the PAUSE 15 and see if that improves it further.

fratello
- 19th April 2012, 16:23
Thanks to Mr.Henrik Olsson this code work PERFECT !!! I'm sure I would not have done it without his help !

Main:
PortA.1 = 0
newState = PortA & 011100
PortA.1 = 1

If newState <> 28 THEN
If newState <> oldState THEN

Select Case oldState
Case 12
If NewState = 20 THEN dir=up
If NewState = 24 THEn dir=dn

Case 20
If NewState = 24 THEN dir=up
If NewState = 12 THEN dir=dn

Case 24
If NewState = 12 THEN dir=up
If NewState = 20 THEN dir=dn
END SELECT

GOSUB UpOrDn

PortA.1 = 0
OldState = PortA & 011100
PortA.1 = 1

endif
ENDIF
Goto Main

kzeoprf
- 21st August 2012, 10:44
Hi to all. I want to use a rotary encoder with a 18f252, and i have a lot of troubles. I want to use interupts, so my only solution PORTB or I can use Portc also?

I made a pcb and I made the connections to PORTC, and I also use a debounce circuit, which I think causes problems. Anyone has a snipset code cause I do not have any expieriance with encoders?
5v
__|__
| |
/ /
\ \
/10k / 10K
\ \
| A | B ENC
- -
| |-------------------------------
\ |
/10K \
\ /10K
/ \
|--------------------- |--------------------
PORTC.2 | PORTC.3 |
---- ----- 100nF
---- -----
| |
--------- -----------
----- -----
- -

HenrikOlsson
- 21st August 2012, 18:05
Hello,
On the 18F252 there's no interrupt capabillity on PortC.

Your schematic really doesn't show up that good here but it LOOKS like you have a 10k from 5V to the "top" of each of the switches in the encoders, then the other side of each switch thru another 10k to the input with a 100nF cap to GND, is that correct?

If the above is not correct please post a proper schematic for us to see.
If the above IS correct then it's not a very good hardware design IMHO. When the switch in the ecoder is closed you have 2*10k in series to the input, decoupled by a 100nF cap, this may be fine. But when the switch is open there's no path for the capacitor to discharge except thru the fairly high input impedance of the pin. In effect the pin floats which is not a good thing for inputs.

/Henrik.

Demon
- 7th September 2012, 02:47
I finally got around to trying my rotary encoders out on an 18F24K22 using DT interrupts.

I kept things simple; checking for only 1 movement per click (detent). This encoder also has a momentary ON switch.



'************************************************* **************************
'* Name : 18F24K22 encoder.pbp *
'* Author : Demon *
'* Date : Sep 5 2012 *
'* Version : 1.0 *
'* Hardware : PIC 18F24K22, internal oscillator, 8 MHz *
'* : ICSP *
'* : MeLabs U2 Programmer v4.32 *
'* Software : PIC Basic Pro v2.60C *
'* : MicroCode Studio Plus v2.2.1.1 *
'* : MPASM v5.46 *
'* CONFIG : - if you use these, you must comment the ones in the *
' .INC file in PBP folder *
' - available options at the bottom of P*.INC file in *
' MPLAB TOOLS/MPASM SUITE folder *
'************************************************* **************************
asm
__CONFIG _CONFIG1H, _FOSC_INTIO67_1H & _PLLCFG_OFF_1H & _PRICLKEN_ON_1H & _FCMEN_OFF_1H & _IESO_OFF_1H
__CONFIG _CONFIG2L, _PWRTEN_ON_2L & _BOREN_SBORDIS_2L & _BORV_285_2L
__CONFIG _CONFIG2H, _WDTEN_OFF_2H
__CONFIG _CONFIG3H, _PBADEN_ON_3H & _HFOFST_OFF_3H & _MCLRE_EXTMCLR_3H
__CONFIG _CONFIG4L, _STVREN_ON_4L & _LVP_OFF_4L & _XINST_OFF_4L & _DEBUG_OFF_4L
endasm

;--- Oscillator speed ------------------------------------------------------

DEFINE OSC 8
CLEAR

;--- Setup Interrupts ------------------------------------------------------
INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; PBP Re-entry for external interrupt

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler INT_INT, _ExternalInterrupt0, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE INT_INT ; enable external (INT) interrupts

;--- Setup Registers -------------------------------------------------------

OSCCON = %01100110 ' OSCILLATOR CONTROL REGISTER
OSCTUNE = %00000000 ' OSCILLATOR TUNING REGISTER

PMD0 = %11111111 ' PERIPHERAL MODULE DISABLE REGISTER 0
PMD1 = %11111111 ' PERIPHERAL MODULE DISABLE REGISTER 1
PMD2 = %00001111 ' PERIPHERAL MODULE DISABLE REGISTER 2

INTCON2=%10000100 ' INTERRUPT CONTROL 2 REGISTER

ANSELA = 0 ' Digital I/O
ANSELB = 0 ' Digital I/O
ANSELC = 0 ' Digital I/O

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

TRISA = %00000000 ' Set port A pins to output
TRISB = %00000001 ' Set port B pin 0 to input, others output
TRISC = %00000110 ' Set port C pins 1,2 to input, others output

;--- Pins ------------------------------------------------------------------

WiperA VAR PortB.0 ' Interrupt 0
WiperB VAR PortC.2

Switch VAR PortC.1

LedPower VAR PortB.5

;--- Variables -------------------------------------------------------------

LEDS VAR BYTE ' 8 Leds connected on Port A

;--- Program Start ---------------------------------------------------------

start:

Pause 200 ' Let PIC stabilize

Ledpower = 0

Leds = %00000001
PortA = leds

;--- The Main Loop ---------------------------------------------------------

mainloop:

if Switch = 1 then
Ledpower = 1 ' Turn ON Led
pause 200 ' Debounce
Ledpower = 0 ' Turn it back OFF
endif

Goto mainloop

end

'---[INT - interrupt handler]---------------------------------------------------
ExternalInterrupt0:
if wiperb = 1 then
if leds.7 = 0 then
LEDS = LEDs << 1 ' Leds blink counter-clockwise
endif
else
if leds.0 = 0 then
leds = leds >> 1 ' Leds blink clockwise
endif
endif

PortA = leds
pause 100
@ INT_RETURN



I used both 0.01uF and 0.1uF caps to improve debounce and wired the C pin straight to Vss (only check wiper A and B). This is the recommended circuit by the manufacturer:

6657

Robert

Ioannis
- 7th September 2012, 07:54
Nice try Robert.

I only object to the use of Pause 100 inside the ISR.

Ioannis

Demon
- 7th September 2012, 22:55
Nice try Robert.

I only object to the use of Pause 100 inside the ISR.

Ioannis


I had first added PAUSE to reduce jitter, the only debounce I could think of on short notice. But adding 0.1uF caps as suggested in this thread was the key. I left the 0.01uF caps in, they can't hurt.

I now removed the PAUSE and it works even better (faster response time).

Thanks!

Robert

Demon
- 8th September 2012, 00:47
Changes:

- used a low priority interrupt for the switch to free up Main Routine for other logic.
- added comments for the registers.
- used constants (I remember something about improved efficiency, this is just to get in the habit).
- PAUSE in low priority interrupt does not cause problems.

Robert


'************************************************* **************************
'* Name : 18F24K22 encoder.pbp *
'* Author : Demon *
'* Date : Sep 5 2012 *
'* Version : 1.0 *
'* Notes : Test program *
'* Hardware : PIC 18F24K22, internal oscillator, 8 MHz *
'* : ICSP *
'* : MeLabs U2 Programmer v4.32 *
'* Software : PIC Basic Pro v2.60C *
'* : MicroCode Studio Plus v2.2.1.1 *
'* : MPASM v5.46 *
'* CONFIG : - if you use these, you must comment the ones in the *
' .INC file in PBP folder *
' - available options at the bottom of P*.INC file in *
' MPLAB TOOLS/MPASM SUITE folder *
'************************************************* **************************
asm
__CONFIG _CONFIG1H, _FOSC_INTIO67_1H & _PLLCFG_OFF_1H & _PRICLKEN_ON_1H & _FCMEN_OFF_1H & _IESO_OFF_1H
__CONFIG _CONFIG2L, _PWRTEN_ON_2L & _BOREN_SBORDIS_2L & _BORV_285_2L
__CONFIG _CONFIG2H, _WDTEN_OFF_2H
__CONFIG _CONFIG3H, _PBADEN_ON_3H & _HFOFST_OFF_3H & _MCLRE_EXTMCLR_3H
__CONFIG _CONFIG4L, _STVREN_ON_4L & _LVP_OFF_4L & _XINST_OFF_4L & _DEBUG_OFF_4L
endasm

;--- Oscillator speed ------------------------------------------------------

DEFINE OSC 8
CLEAR

;--- Setup Interrupts ------------------------------------------------------
DEFINE USE_LOWPRIORITY 1

INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; PBP Re-entry for external interrupt
INCLUDE "ReEnterPBP-18LP.bas" ; PBP Re-entry for low priority external interrupt

ASM
;----[High Priority Interrupts] --------------------------------------------
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler INT0_INT, _ExternalInterrupt0, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor

;----[Low Priority Interrupts] ---------------------------------------------
INT_LIST_L macro ; IntSource, Label, Type, ResetFlag?
INT_Handler INT1_INT, _ExternalInterrupt1, PBP, yes
endm
INT_CREATE_L ; Creates the Low Priority interrupt processor

;----[Enable Interrupts] ---------------------------------------------------
INT_ENABLE INT0_INT ; Enable (INT0) external interrupt
INT_ENABLE INT1_INT ; Enable (INT1) external interrupt
ENDASM

INTCON2.7=1
' bit 7 RBPU: PORTB Pull-up Enable bit
' 1 = All PORTB pull-ups are disabled
' 0 = PORTB pull-ups are enabled provided that the pin is an input and the
' corresponding WPUB bit is set.

INTCON2.6=0
' bit 6 INTEDG0: External Interrupt 0 Edge Select bit
' 1 = Interrupt on rising edge
' 0 = Interrupt on falling edge

INTCON2.5=1
' bit 5 INTEDG1: External Interrupt 1 Edge Select bit
' 1 = Interrupt on rising edge
' 0 = Interrupt on falling edge

INTCON2.4=0
' bit 4 INTEDG2: External Interrupt 2 Edge Select bit
' 1 = Interrupt on rising edge
' 0 = Interrupt on falling edge

;--- Setup Registers -------------------------------------------------------

OSCCON = %01100110 ' OSCILLATOR CONTROL REGISTER
' bit 7 IDLEN: Idle Enable bit
' 1 = Device enters Idle mode on SLEEP instruction
' 0 = Device enters Sleep mode on SLEEP instruction
' bit 6-4 IRCF<2:0>: Internal RC Oscillator Frequency Select bits(2)
' 111 = HFINTOSC – (16 MHz)
' 110 = HFINTOSC/2 – (8 MHz)
' 101 = HFINTOSC/4 – (4 MHz)
' 100 = HFINTOSC/8 – (2 MHz)
' 011 = HFINTOSC/16 – (1 MHz)(3)
' If INTSRC = 0 and MFIOSEL = 0:
' 010 = HFINTOSC/32 – (500 kHz)
' 001 = HFINTOSC/64 – (250 kHz)
' 000 = LFINTOSC – (31.25 kHz)
' If INTSRC = 1 and MFIOSEL = 0:
' 010 = HFINTOSC/32 – (500 kHz)
' 001 = HFINTOSC/64 – (250 kHz)
' 000 = HFINTOSC/512 – (31.25 kHz)
' If INTSRC = 0 and MFIOSEL = 1:
' 010 = MFINTOSC – (500 kHz)
' 001 = MFINTOSC/2 – (250 kHz)
' 000 = LFINTOSC – (31.25 kHz)
' If INTSRC = 1 and MFIOSEL = 1:
' 010 = MFINTOSC – (500 kHz)
' 001 = MFINTOSC/2 – (250 kHz)
' 000 = MFINTOSC/16 – (31.25 kHz)
' bit 3 OSTS: Oscillator Start-up Time-out Status bit
' 1 = Device is running from the clock defined by FOSC<3:0> of the CONFIG1H register
' 0 = Device is running from the internal oscillator (HFINTOSC, MFINTOSC or LFINTOSC)
' bit 2 HFIOFS: HFINTOSC Frequency Stable bit
' 1 = HFINTOSC frequency is stable
' 0 = HFINTOSC frequency is not stable
' bit 1-0 SCS<1:0>: System Clock Select bit
' 1x = Internal oscillator block
' 01 = Secondary (SOSC) oscillator
' 00 = Primary clock (determined by FOSC<3:0> in CONFIG1H).

OSCCON2 = %00000100 ' OSCILLATOR CONTROL REGISTER 2
' bit 7 PLLRDY: PLL Run Status bit
' 1 = System clock comes from 4xPLL
' 0 = System clock comes from an oscillator, other than 4xPLL
' bit 6 SOSCRUN: SOSC Run Status bit
' 1 = System clock comes from secondary SOSC
' 0 = System clock comes from an oscillator, other than SOSC
' bit 5 Unimplemented: Read as ‘0’.
' bit 4 MFIOSEL: MFINTOSC Select bit
' 1 = MFINTOSC is used in place of HFINTOSC frequencies of 500 kHz, 250 kHz and 31.25 kHz
' 0 = MFINTOSC is not used
' bit 3 SOSCGO(1): Secondary Oscillator Start Control bit
' 1 = Secondary oscillator is enabled.
' 0 = Secondary oscillator is shut off if no other sources are requesting it.
' bit 2 PRISD: Primary Oscillator Drive Circuit Shutdown bit
' 1 = Oscillator drive circuit on
' 0 = Oscillator drive circuit off (zero power)
' bit 1 MFIOFS: MFINTOSC Frequency Stable bit
' 1 = MFINTOSC is stable
' 0 = MFINTOSC is not stable
' bit 0 LFIOFS: LFINTOSC Frequency Stable bit
' 1 = LFINTOSC is stable
' 0 = LFINTOSC is not stable

OSCTUNE = %00000000 ' OSCILLATOR TUNING REGISTER
' bit 7 INTSRC: Internal Oscillator Low-Frequency Source Select bit
' 1 = 31.25 kHz device clock derived from the MFINTOSC or HFINTOSC source
' 0 = 31.25 kHz device clock derived directly from LFINTOSC internal oscillator
' bit 6 PLLEN: Frequency Multiplier 4xPLL for HFINTOSC Enable bit(1)
' 1 = PLL enabled
' 0 = PLL disabled
' bit 5-0 TUN<5:0>: Frequency Tuning bits – use to adjust MFINTOSC and HFINTOSC frequencies
' 011111 = Maximum frequency
' 011110 =
' • • •
' 000001 =
' 000000 = Oscillator module (HFINTOSC and MFINTOSC) are running at the factory
' calibrated frequency.
' 111111 =
' • • •
' 100000 = Minimum frequency

PMD0 = %11111111 ' PERIPHERAL MODULE DISABLE REGISTER 0
' bit 7 UART2MD: UART2 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 6 UART1MD: UART1 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 5 TMR6MD: Timer6 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 4 TMR5MD: Timer5 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 3 TMR4MD: Timer4 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 2 TMR3MD: Timer3 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 1 TMR2MD: Timer2 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 0 TMR1MD: Timer1 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power

PMD1 = %11111111 ' PERIPHERAL MODULE DISABLE REGISTER 1
' bit 7 MSSP2MD: MSSP2 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 6 MSSP1MD: MSSP1 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 5 Unimplemented: Read as ‘0’
' bit 4 CCP5MD: CCP5 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 3 CCP4MD: CCP4 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 2 CCP3MD: CCP3 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 1 CCP2MD: CCP2 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 0 CCP1MD: CCP1 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power

PMD2 = %00001111 ' PERIPHERAL MODULE DISABLE REGISTER 2
' bit 7-4 Unimplemented: Read as ‘0’
' bit 3 CTMUMD: CTMU Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 2 CMP2MD: Comparator C2 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 1 CMP1MD: Comparator C1 Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power
' bit 0 ADCMD: ADC Peripheral Module Disable Control bit
' 1 = Module is disabled, Clock Source is disconnected, module does not draw digital power
' 0 = Module is enabled, Clock Source is connected, module draws digital power

ANSELA = 0 ' Digital I/O
ANSELB = 0 ' Digital I/O
ANSELC = 0 ' Digital I/O

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

TRISA = %00000000 ' Set port A pins to output
TRISB = %00000011 ' Set port B pins 0,1 to input, others output
TRISC = %00000100 ' Set port C pin 2 to input, others output

;--- Pins ------------------------------------------------------------------

WiperA VAR PortB.0 ' Input, Interrupt 0
WiperB VAR PortC.2 ' Input

Switch VAR PortB.1 ' Input, Interrupt 1 low priority

LedPower VAR PortB.5 ' Output

Leds VAR PortA ' Output, 8 Leds connected on Port A

;--- Variables -------------------------------------------------------------

;--- Constants -------------------------------------------------------------

StateOff con 0 ' Off
StateOn con 1 ' On

ShiftOne con 1 ' One

DebounceTime con 250 ' Debounce time
StabilizeTime con 200 ' Stabilize time

;--- Initialize pins -------------------------------------------------------

Ledpower = StateOff ' Turn OFF switch Led
Leds = %00000001 ' Turn ON first Led

;--- Initialize variables --------------------------------------------------

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

goto Start ' Jump over sub-routines

ExternalInterrupt0: ' [INT0 - interrupt handler]
if wiperb = StateOn then ' Check if turning counter-clockwise
if leds.7 = StateOff then ' Check if reached last LED
LEDS = LEDs << ShiftOne ' Blink Led
endif
else ' Check if turning clockwise
if leds.0 = StateOff then ' Check if reached first LED
leds = leds >> ShiftOne ' Blink Led
endif
endif
@ INT_RETURN

ExternalInterrupt1: ' [INT1 - interrupt handler]
Ledpower = StateOn ' Turn ON switch Led
pause DebounceTime ' Basic debounce
@ INT_RETURN

;--- Program Start ---------------------------------------------------------

Start:

Pause StabilizeTime ' Let PIC stabilize

;--- The Main Loop ---------------------------------------------------------

mainloop:

Ledpower = StateOff ' Turn switch Led back OFF
Goto mainloop

end

dovegroup
- 17th February 2013, 13:06
My own code for reading Rotary Encoder





'************************************************* ***************
'* Name : UNTITLED.BAS *
'* Author : [select VIEW...EDITOR OPTIONS] *
'* Notice : Copyright (c) 2013 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 16/2/2013 *
'* Version : 1.0 *
'* Notes : *
'* : *
'************************************************* ***************
TRISA = %11111 '
TRISB = %00000000 '8 LEDs.

PORTB=0 '

x VAR Byte '
Aux VAR Byte '
Enc VAR Byte '



x=0 '
Aux=0
Enc=0


aaa:
Aux = Enc '
Enc = (PORTA & 3) ' RA0 RA1 2 bits variable 'ENC'.
If Aux = 2 And Enc = 3 Then x=x+1 '
If Aux = 3 And Enc = 2 Then x=x-1 '
PORTB = x ' 8 LED

goto aaa

End

Ioannis
- 17th February 2013, 21:57
How does this work? Since Enc=Aux=0, How can the If clause be true? There must be some pull-ups in PORTA pins.

Ioannis

CuriousOne
- 6th June 2013, 09:04
Here's the initial approach, I've modified for my needs. It does not works, what may be the reason?



LEFT var GPIO.0
RIGHT var GPIO.1
COUNTER VAR word
counter=128

cycl:
if LEFT=0 then 'here is switch 1 of the rotary encoder
counter=counter+1
goto lcd
endif

if RIGHT=0 then 'here is switch 2 of the rotary encoder
counter=counter-1
goto lcd
endif

goto cycl

lcd:
hpwm 1,counter,5000
while (LEFT=0 or RIGHT=0):pause 10:wend
goto cycl



MCU is PIC12F683

CuriousOne
- 6th June 2013, 09:22
well, I think I found the problem - it never treats inputs as inputs, despite a high level on LEFT or RIGHT, it still =0

Seems like I need some command to configure pins as input?

CuriousOne
- 6th June 2013, 09:28
TRISIO=%11111111

does not helps

CuriousOne
- 6th June 2013, 09:30
ANSEL = %00000000
CMCON0 = 7
this helped :)

dovegroup
- 11th July 2013, 18:47
How does this work? Since Enc=Aux=0, How can the If clause be true? There must be some pull-ups in PORTA pins.

Ioannis


Sorry for the late reply
See here and we will resolve the questions

https://sites.google.com/site/proyectosroboticos/encoder/encoder-por-software/encoder-simple-con-16f628a

Ioannis
- 11th July 2013, 22:05
It seems very strange that I got an e-mail notification only today about this thread by dovegroup's reply.

I am not very good at C but from your example, dovegroup, I think the variable x might change more than one at every step in rotating the encoder because of the bouncing effects.

Ioannis