Log in

View Full Version : Reading incremental encoder



robbe55
- 21st April 2016, 13:32
Hi all,
For a university project I have written a PICBASIC code to read digital signals from the encoder on my MCU and send them to both another MCU (to graphically plot the result on PC via Python) and to an LCD screen (for debugging). Now, since i'm completely new to picbasic i have no idea whether my code will work. Since we have limited time to actually work with the hardware, we have to make sure the software is carefully prepared beforehand. That's why i would kindly ask some of you guys, with a lot more experience, to look through my code and make me aware of certain mistakes.
The MCU i'm using is PIC12F683 (datasheet: http://akizukidenshi.com/download/ds/microchip/pic12f683.pdf). It's an 8 pin MCU.
The encoder output (which is the MCU input) exists out of 2 square waves, which are the same but shifted 90° in phase (to check which way the encoder is turning).
What i want to get as output is a 3 digit number, because that is what to next MCU requires as input. So i wanted to take number 0 to 499 for clockwise turning and 501 to 999 for counterclockwise turning.
I'm pretty convinced the idea behind my method is correct, but i have no idea whether i have written it all in a correct way, without too many syntax errors.
Here's what i've written so far, i hope i did a decent job of explaining what i want to achieve, if not you can always ask me.
It would be great if some people with actual PICBASIC knowledge could help me :)
Thanks!

'************************************************* ***************
'* Name : RotaryEncoder.BAS *
'* Author : Robbe Degezelle *
'* Notice : Copyright (c) 2016 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 12/04/2016 *
'* Version : 1.0 *
'* Notes : *
'* : *
'************************************************* ***************


'initialisation------
NewVal = (GPIO & 000) >> 3
OldVal = NewVal
ANSEL = 00000 ' all pins digital
TRISIO = 1111 'set all ports to inputs
'Enc_1 VAR GPIO.5 ' first encoder input
'Enc_2 var GPIO.4 ' second encoder input
LCD_pin var GPIO.1




'variables-----------
NewVal VAR BYTE
OldVal VAR BYTE
Counter VAR BYTE
CounterCC var byte
Counter = 000
CounterCC = 500
word1 var word 'C
word1=000
word2 var word 'CC
word2=000
P4800 CON 188 '4800 baud true
N9600 con $4054 '9600 baud rate inverted




'Program----------------
Main
NewVal = (GPIO & 000) >> 3 ; zou 0,1,2 of 3 moeten geven
If NewVal <> OldVal Then
Branch CurVal, [S0,S1,S2,S3] ; indien waarde veranderd, vergelijken met oud
EndIF
AfterBranch
Goto Main


S0
If OldVal = 2 Then
GoSub Clockwise
ELSE
GoSub CounterClockwise
EndIF
Goto AfterBranch

S1
if OldVal = 0 Then
GoSub Clockwise
ELSE
GoSub CounterClockwise
EndIF
Goto AfterBranch

S2
if OldVal = 3 Then
GoSub Clockwise
Else
GoSub CounterClockwise
EndIf
Goto AfterBranch

S3
if OldVal = 1 Then
GoSub Clockwise
Else
GoSUb CounterClockwise
Endif
Goto AfterBranch



Clockwise
OldVal = CurVal
Counter = Counter + 1
word1 = Counter
serout2 GPIO.0,p4800, ["B1", DEC3 word1]
gosub disp_lcd
Return

CounterClockwise
OldVal = CurVal
CounterCC = CounterCC + 1
word2 = CounterCC
serout2 GPIO.0,p4800, ["B1", DEC3 word2]
gosub disp_lcd
return


'LCD
disp_lcd
serout2 LCD_pin, N9600, [$FE, 1$] 'clear display
pause 1
serout2 LCD_pin, N9600, [$FE, 2$] 'cursor returns home
pause 1
serout LCD_pin, N9600, ["TellerC= ", DEC Counter]
pause 1
SEROUT2 LCD_pin,N9600, [$FE, $C0] 'cursor to 2nd line
pause 1
SEROUT2 LCD,N9600, ["TellerCC= ", dec CounterCC]
return


end

HenrikOlsson
- 21st April 2016, 18:24
You have limited time to work with the hardware and you don't have time to even compile the code to see if there are any syntax errors and you want us to check for it you. Is that it? OK, here are a couple of things I noticed:

CounterCC is declared as a BYTE but you're trying to squeeze a value of 500 into it.
There are no colons after LABELS.
There are no % signs where your expressing your numbers in binary.
You're reading the state of the IO pins into NewVAL but you're using CurVal in the BRANCH statement.
Provided the decoding logic is correct (which I haven't checked) it's not going to keep up with a turning encoder since all the SEROUT statements will take tens or even hundreds of ms each time - that may not matter, I don't know - just pointing it out.

When posting code in the forum, please use code-tags.

/Henrik.

Scampy
- 21st April 2016, 18:30
Well it fails to compile, and I presume that you are using PBP2.6x rather than 3.x

http://micro-heli.co.uk/codetest.png

To be honest, you will learn a lot more, and gain a better understanding by actually downloading the relevant software such as micro code studio, mplab etc and debug the code until it compiles without errors. You don't need the physical hardware as at this stage all you want to do is to know if the code has no errors. But, having said that, the only real way you will know if the project functions correctly is to either use an expensive simulator that runs the hex code, or get a solderless breadboard and build the circuit and debug both hardware and software at the same time

Scampy
- 21st April 2016, 18:33
You have limited time to work with the hardware and you don't have time to even compile the code to see if there are any syntax errors

/Henrik.

You beat me to it...

Scampy
- 21st April 2016, 18:45
'LCD
disp_lcd
serout2 LCD_pin, N9600, [$FE, 1$] 'clear display
pause 1
serout2 LCD_pin, N9600, [$FE, 2$] 'cursor returns home
pause 1
serout LCD_pin, N9600, ["TellerC= ", DEC Counter]
pause 1
SEROUT2 LCD_pin,N9600, [$FE, $C0] 'cursor to 2nd line
pause 1
SEROUT2 LCD,N9600, ["TellerCC= ", dec CounterCC]
return


OK you may of got some syntax wrong, but at least check for obvious omissions first !

Scampy
- 21st April 2016, 19:33
You also need to set the PIC registers and to get reliable 9600 baud rate you need to ensure the PIC is running at 4Mhz as serial out assumes a 4mhz timing base

From the BPB manual



SEROUT2 assumes a 4MHz oscillator when generating its bit timing. To maintain the proper baud rate timing rate with other oscillator values, be sure to DEFINE the OSC setting to the new Oscillator value. An oscillator speed faster than 4MHz may be required for reliable operation at 9600 baud and above


You need to read section 3.5.2 of the datasheet on how to configure the internal oscillator HFINTOSC which is factory calibrated to 8Mhz, The frequency of the HFINTOSC can be altered via software using the OSCTUNE register (see the Register section 3-2).

The point of mentioning this is that you really need to spend time developing the hardware as well as the software... the two go hand in hand.

It may seem that we've come down hard on you, but then you must appreciate that the guys here see 100's of posts from new members who ask for help, but don't want to put the effort in from the start. The guys here (Henrik, Tabsoft and Richard to name a few) are very helpful and have lots of coding experience, and it's only fair to have done the ground work first, and then they are only too pleased to help (and I'm talking from experience here)

Scampy
- 21st April 2016, 20:19
Ok this compiles... but it won't work as there are lots missing from the code, such as the "afterbranch" subroutine hasn't been included (I just gave it a label and return) I've also added some config settings, and a few missing variables...



'************************************************* ***************
'* Name : RotaryEncoder.BAS *
'* Author : Robbe Degezelle *
'* Notice : Copyright (c) 2016 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 12/04/2016 *
'* Version : 1.0 *
'* Notes : *
'* : *
'************************************************* ***************


'initialisation------


@ __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOD_ON
' Internal Oscillator
' Enable watch dog timer
' Enable power up timer
' Disable MCLR pin
' Enable brown out detect


ANSEL = 0 ' Set all digital
CMCON0 = 7 ' Analog comparators off

LCD_pin var GPIO.1

'variables-----------
NewVal VAR word
OldVal VAR word
Counter VAR word
CounterCC var word
word1 var word 'C
word2 var word 'CC

CurVal var word ' This seemed to be missing from your original code... again I presume it should be a word variable as you have statements like OldVal = CurVal

P4800 CON 188 '4800 baud true
N9600 con $4054 '9600 baud rate inverted

word2=000
NewVal = (GPIO & 000) >> 3
OldVal = NewVal
Counter = 000
CounterCC = 500
word1=000

'Program----------------
Main:
NewVal = (GPIO & 000) >> 3 ; zou 0,1,2 of 3 moeten geven
If NewVal <> OldVal Then
Branch CurVal, [S0,S1,S2,S3] ; indien waarde veranderd, vergelijken met oud
EndIF
goto AfterBranch
Goto Main


S0:
If OldVal = 2 Then
GoSub Clockwise
ELSE
GoSub CounterClockwise
EndIF
Goto AfterBranch

S1:
if OldVal = 0 Then
GoSub Clockwise
ELSE
GoSub CounterClockwise
EndIF
Goto AfterBranch

S2:
if OldVal = 3 Then
GoSub Clockwise
Else
GoSub CounterClockwise
EndIf
Goto AfterBranch

S3:
if OldVal = 1 Then
GoSub Clockwise
Else
GoSUb CounterClockwise
Endif
Goto AfterBranch



Clockwise:
OldVal = CurVal
Counter = Counter + 1
word1 = Counter
serout2 GPIO.0,p4800, ["B1", DEC3 word1]
gosub disp_lcd
Return

CounterClockwise:
OldVal = CurVal
CounterCC = CounterCC + 1
word2 = Countercc
serout2 GPIO.0,p4800, ["B1", DEC3 word2]
gosub disp_lcd
return


'LCD
disp_lcd:
serout2 LCD_pin, N9600, [$FE, $1] 'clear display
pause 1
serout2 LCD_pin, N9600, [$FE, $2] 'cursor returns home
pause 1
serout2 LCD_pin, N9600, ["TellerC= ", DEC Counter]
pause 1
SEROUT2 LCD_pin,N9600, [$FE, $C0] 'cursor to 2nd line
pause 1
SEROUT2 LCD_pin,N9600, ["TellerCC= ", dec CounterCC] ' corrected this line
return


AfterBranch: ' no idea what afterbranch is meant to do, as you have several goto's afterbranch but there is no such subroutine
Return


There may be other coding errors, but the syntax seems OK and like I said this now compiles, but until you add the missing subroutines, and take on board some of the comments Henrik has mentioned it won't work. Also, your post seemed to have good command of the English language, but some of the comments (hi-lighted in green) are in a different language, which makes me wonder if the code is your own work ?

picster
- 4th May 2016, 14:49
The most efficient method for an encoder that I've encountered is a "current state/next state" combination of 4 bits that you can use for a lookup function. The result of the lookup defines the action, where a result of 1 could be "increment", 2 could be "decrement", 0 could be "do nothing/not valid". Super slick, minimal code space. Don't forget to allow for debouncing.