PDA

View Full Version : Rotary Encoder on 20x4 LCD with pushbutton digit select and other features



csantex
- 7th February 2018, 00:18
'************************************************* ******************************
;* Name : PIC16F887.BAS
;* Author : CSANTEX
;* Date :
;* Version : DEFAULT
;* Notes :
'************************************************* ******************************
;Max Word count available is 8192. Max EEPROM available is 256 bytes
;************************************************* ******************************
;This code displays a number that is incremented for clockwise rotation or decremented
;for counter-clockwise rotation on a 20x4 LCD. The display has two digits to the
;left of the decimal and three digits to the right of the decimal. Each time the
;shaft is pushed in, a digit is selected starting from left to right. This code
;could be used to display voltages that are preset by a user on a diy power supply
;or for Ampere presets or any of the like.
;The code is a bit long but it works well.
;Does not use interrupts.
;Written for use on PIC16F887 but can be modified to fit any PIC microcontroller.
;COULD USE SOME OPTIMIZATION!
;************************************************* ******************************
;************************************************* ******************************
' include "PIC16F887.PBPINC" ; processor specific variable definitions
INCLUDE "Modedefs.Bas"


@ ERRORLEVEL -306 ; Suppress messages related to Page Boundries.
;************************************************* ******************************
#CONFIG
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_ON & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
#ENDCONFIG

DEFINE OSC 8

DEFINE LCD_DREG PORTD ' PORTA is LCD data port
DEFINE LCD_DBIT 4 ' PORTA.4 is the data LSB ( 4 BIT MODE )
DEFINE LCD_RSREG PORTB ' RS is connected to PORTA.1
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTB ' E is connected to PORTA.3
DEFINE LCD_EBIT 1
DEFINE LCD_BITS 4 ' 4 lines of data are used (4 BITS)
DEFINE LCD_LINES 4 ' It is a 4-line display
DEFINE LCD_COMMANDUS 1500 ' Use 1500uS command delay
DEFINE LCD_DATAUS 44 ' Use 44uS data delay
;************************************************* ******************************
OSCCON = 10000 ; Set PIC16F88 to 4MHz = 0100000, or 8MHz = 0110000
TRISA = 11111 ; Port A set to inputs
TRISB = 01100 ; Port B set to OUTPUTS
TRISC = 11111 ; Port C set to inputs
TRISD = 000000 ; Port D set to Ouputs
PORTD.5 = 0 ; Set output low
ANSEL = 0000 ; Set all analog pins to digital
ANSELH = 0000
' LED var PortD.7 ; LED for flashing
A_IN VAR PORTC.6
B_IN VAR PORTC.5
SW_IN VAR PORTC.4
;************************************************* ******************************
ONES VAR BYTE ; VARIABLE
TENS VAR BYTE ; VARIABLE
HUND VAR BYTE ; VARIABLE
THOU VAR BYTE ; VARIABLE
TNTH VAR BYTE ; VARIABLE
SUBTTL VAR WORD ; VARIABLE
TOTAL VAR WORD ; TOTAL VALUE
PLACE VAR BYTE ; VARIABLE


MYFLAGS VAR BYTE
;************************************************* ******************************

INIT:
PAUSE 1000
CLEAR
PLACE = 6

' LCDOUT $FE, $C0, DEC PLACE ;FOR DEBUG ONLY
' LCDOUT $FE, $94, DEC5 (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES ;FOR DEBUG ONLY

LCDOUT $FE, $D4, DEC1 TNTH, DEC1 THOU, ".", DEC1 HUND, DEC1 TENS, DEC1 ONES

;************************************************* ******************************
MAIN:
IF A_IN < B_IN THEN
IF PLACE = 6 AND TOTAL < 10000 THEN
IF MYFLAGS.0 = 0 THEN
ONES = ONES + 1
IF ONES > 9 THEN
ONES = 0
TENS = TENS + 1
ENDIF
IF TENS > 9 THEN
TENS = 0
HUND = HUND + 1
ENDIF
IF HUND > 9 THEN
HUND = 0
THOU = THOU + 1
ENDIF
IF THOU > 9 THEN
THOU = 0
TNTH = TNTH + 1
ENDIF
IF TNTH > 1 THEN THOU = 0
GOSUB SHOW_TOTAL
ENDIF
ENDIF
IF MYFLAGS.0 = 1 THEN
IF TNTH = 0 THEN
IF PLACE = 5 THEN
ONES = ONES + 1
IF ONES > 9 THEN ONES = 0
GOSUB SHOW_ONES
ENDIF
IF PLACE = 4 THEN
TENS = TENS + 1
IF TENS > 9 THEN TENS = 0
GOSUB SHOW_TENS
ENDIF
IF PLACE = 3 THEN
HUND = HUND + 1
IF HUND > 9 THEN HUND = 0
GOSUB SHOW_HUND
ENDIF
IF PLACE = 1 THEN
THOU = THOU + 1
IF THOU > 9 THEN THOU = 0
GOSUB SHOW_THOU
ENDIF
ENDIF
IF SUBTTL = 0 THEN
IF PLACE = 0 THEN
TNTH = TNTH + 1
IF TNTH > 1 THEN TNTH = 0
GOSUB SHOW_TNTH
GOTO PUSHBUTTON
ENDIF
ENDIF
ENDIF
GOSUB SUM_TOTALS
IF TOTAL = 10000 THEN
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $80, "MAXIMUM LIMIT"
PAUSE 1000
LCDOUT $FE, $80, " "
ENDIF
ENDIF

PUSHBUTTON:
IF SW_IN = 0 THEN
PLACE = PLACE + 1
IF PLACE = 2 THEN PLACE = 3
IF PLACE > 6 THEN PLACE = 0
' LCDOUT $FE, $C0, DEC PLACE ;FOR DEBUG ONLY
LCDOUT $FE, $D4+PLACE

IF PLACE = 6 THEN
LCDOUT $FE, $0C ;BLINK OFF
MYFLAGS.0 = 0
ELSE
LCDOUT $FE, $0F
LCDOUT $FE, $D4+PLACE
MYFLAGS.0 = 1
ENDIF

GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
ENDIF
GOSUB SUM_TOTALS
IF PLACE != 6 THEN
LCDOUT $FE, $0F
LCDOUT $FE, $D4+PLACE
' MYFLAGS.0 = 1
ENDIF


DECREMENT:
IF B_IN < A_IN THEN
IF PLACE = 6 AND TOTAL > 0 THEN
IF MYFLAGS.0 = 0 THEN
ONES = ONES - 1
IF ONES > 9 THEN
ONES = 9
TENS = TENS - 1
ENDIF
IF TENS > 9 THEN
TENS = 9
HUND = HUND - 1
ENDIF
IF HUND > 9 THEN
HUND = 9
THOU = THOU - 1
ENDIF
IF THOU > 9 THEN
THOU = 9
TNTH = TNTH - 1
ENDIF
IF TNTH > 1 THEN THOU = 0
GOSUB SHOW_TOTAL
ENDIF
ENDIF
IF MYFLAGS.0 = 1 THEN
IF TNTH = 0 THEN
IF PLACE = 5 THEN
ONES = ONES - 1
IF ONES > 9 THEN ONES = 9
GOSUB SHOW_ONES
GOTO MAIN
ENDIF
IF PLACE = 4 THEN
TENS = TENS - 1
IF TENS > 9 THEN TENS = 9
GOSUB SHOW_TENS
GOTO MAIN
ENDIF
IF PLACE = 3 THEN
HUND = HUND - 1
IF HUND > 9 THEN HUND = 9
GOSUB SHOW_HUND
GOTO MAIN
ENDIF
IF PLACE = 1 THEN
THOU = THOU - 1
IF THOU > 9 THEN THOU = 9
GOSUB SHOW_THOU
GOTO MAIN
ENDIF
ENDIF
IF SUBTTL = 0 THEN
IF PLACE = 0 THEN
TNTH = TNTH - 1
IF TNTH > 1 THEN TNTH = 1
GOSUB SHOW_TNTH
GOTO MAIN
ENDIF
ENDIF
ENDIF
GOSUB SUM_TOTALS
IF TOTAL = 0 THEN
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $80, "MINIMUM LIMIT"
PAUSE 1000
LCDOUT $FE, $80, " "
ENDIF
ENDIF
GOTO MAIN

SHOW_TOTAL:
' LCDOUT $FE, $94, DEC5 (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES ;FOR DEBUG ONLY
LCDOUT $FE, $D4, DEC1 TNTH, DEC1 THOU, ".", DEC1 HUND, DEC1 TENS, DEC1 ONES


DEBOUNCE:
WHILE A_IN=0 OR B_IN=0 OR SW_IN=0 ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
WEND
RETURN


;************************************************* ******************************
SHOW_TNTH:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D4, DEC1 TNTH
LCDOUT $FE, $D4
LCDOUT $FE, $0F ;BLINK ON
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_THOU:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D5, DEC1 THOU
LCDOUT $FE, $D5
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_HUND:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D7, DEC1 HUND
LCDOUT $FE, $D7
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_TENS:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D8, DEC1 TENS
LCDOUT $FE, $D8
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_ONES:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D9, DEC1 ONES
LCDOUT $FE, $D9
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SUM_TOTALS:
TOTAL = (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES
SUBTTL = (THOU*1000)+(HUND*100)+(TENS*10)+ONES
RETURN
;************************************************* ******************************

;************************************************* ******************************
END

csantex
- 7th February 2018, 01:49
First post is obviously mislabeled since it didn't come out right but here it is the way it is supposed to be. Hopefully...



'************************************************* ******************************
;* Name : PIC16F887.BAS
;* Author : CSANTEX
;* Date :
;* Version : DEFAULT
;* Notes :
'************************************************* ******************************
;Max Word count available is 8192. Max EEPROM available is 256 bytes
;************************************************* ******************************
;This code displays a number that is incremented for clockwise rotation or decrements
;for counter-clockwise rotation on a 20x4 LCD. The display has two digits to the
;left of the decimal and three digits to the right of the decimal. Each time the
;shaft is pushed in, a digit is selected starting from left to right. This code
;could be used to display voltages that are preset by a user on a diy power supply
;or for Ampere presets or any of the like.
;The code is a bit long but it works well.
;***This work was derrived from the thread "New Approach to Rotary Encoder (http://www.picbasic.co.uk/forum/showthread.php?t=12911&page=3)", based on sample code
;submitted by Ioannis
;COULD USE SOME OPTIMIZATION!
;************************************************* ******************************
;************************************************* ******************************
' include "PIC16F887.PBPINC" ; processor specific variable definitions
INCLUDE "Modedefs.Bas"

@ ERRORLEVEL -306 ; Suppress messages related to Page Boundries.
;************************************************* ******************************
#CONFIG
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_ON & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
#ENDCONFIG

DEFINE OSC 8

DEFINE LCD_DREG PORTD ' PORTA is LCD data port
DEFINE LCD_DBIT 4 ' PORTA.4 is the data LSB ( 4 BIT MODE )
DEFINE LCD_RSREG PORTB ' RS is connected to PORTA.1
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTB ' E is connected to PORTA.3
DEFINE LCD_EBIT 1
DEFINE LCD_BITS 4 ' 4 lines of data are used (4 BITS)
DEFINE LCD_LINES 4 ' It is a 4-line display
DEFINE LCD_COMMANDUS 1500 ' Use 1500uS command delay
DEFINE LCD_DATAUS 44 ' Use 44uS data delay
;************************************************* ******************************
OSCCON = %01110000 ; Set PIC16F88 to 4MHz = %01100000, or
; 8MHz = %01110000
TRISA = %11111111 ; Port A set to inputs
TRISB = %11101100 ; Port B set to OUTPUTS
TRISC = %01111111 ; Port C set to inputs
TRISD = %00000000 ; Port D set to Ouputs
PORTD.5 = 0 ; Set output low
ANSEL = %00000000 ; Set all analog pins to digital
ANSELH = %00000000
' LED var PortD.7 ; LED for flashing
A_IN VAR PORTC.6
B_IN VAR PORTC.5
SW_IN VAR PORTC.4
;************************************************* ******************************
ONES VAR BYTE ; VARIABLE
TENS VAR BYTE ; VARIABLE
HUND VAR BYTE ; VARIABLE
THOU VAR BYTE ; VARIABLE
TNTH VAR BYTE ; VARIABLE
SUBTTL VAR WORD ; VARIABLE
TOTAL VAR WORD ; TOTAL VALUE
PLACE VAR BYTE ; VARIABLE

MYFLAGS VAR BYTE
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
INIT:
PAUSE 1000
CLEAR
PLACE = 6
' LCDOUT $FE, $C0, DEC PLACE ;FOR DEBUG ONLY
' LCDOUT $FE, $94, DEC5 (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES ;FOR DEBUG ONLY
LCDOUT $FE, $D4, DEC1 TNTH, DEC1 THOU, ".", DEC1 HUND, DEC1 TENS, DEC1 ONES


MAIN:
IF A_IN < B_IN THEN
IF PLACE = 6 AND TOTAL < 10000 THEN
IF MYFLAGS.0 = 0 THEN
ONES = ONES + 1
IF ONES > 9 THEN
ONES = 0
TENS = TENS + 1
ENDIF
IF TENS > 9 THEN
TENS = 0
HUND = HUND + 1
ENDIF
IF HUND > 9 THEN
HUND = 0
THOU = THOU + 1
ENDIF
IF THOU > 9 THEN
THOU = 0
TNTH = TNTH + 1
ENDIF
IF TNTH > 1 THEN THOU = 0
GOSUB SHOW_TOTAL
ENDIF
ENDIF
IF MYFLAGS.0 = 1 THEN
IF TNTH = 0 THEN
IF PLACE = 5 THEN
ONES = ONES + 1
IF ONES > 9 THEN ONES = 0
GOSUB SHOW_ONES
ENDIF
IF PLACE = 4 THEN
TENS = TENS + 1
IF TENS > 9 THEN TENS = 0
GOSUB SHOW_TENS
ENDIF
IF PLACE = 3 THEN
HUND = HUND + 1
IF HUND > 9 THEN HUND = 0
GOSUB SHOW_HUND
ENDIF
IF PLACE = 1 THEN
THOU = THOU + 1
IF THOU > 9 THEN THOU = 0
GOSUB SHOW_THOU
ENDIF
ENDIF
IF SUBTTL = 0 THEN
IF PLACE = 0 THEN
TNTH = TNTH + 1
IF TNTH > 1 THEN TNTH = 0
GOSUB SHOW_TNTH
GOTO PUSHBUTTON
ENDIF
ENDIF
ENDIF
GOSUB SUM_TOTALS
IF TOTAL = 10000 THEN
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $80, "MAXIMUM LIMIT"
PAUSE 1000
LCDOUT $FE, $80, " "
ENDIF
ENDIF

PUSHBUTTON:
IF SW_IN = 0 THEN
PLACE = PLACE + 1
IF PLACE = 2 THEN PLACE = 3
IF PLACE > 6 THEN PLACE = 0
' LCDOUT $FE, $C0, DEC PLACE ;FOR DEBUG ONLY
LCDOUT $FE, $D4+PLACE

IF PLACE = 6 THEN
LCDOUT $FE, $0C ;BLINK OFF
MYFLAGS.0 = 0
ELSE
LCDOUT $FE, $0F
LCDOUT $FE, $D4+PLACE
MYFLAGS.0 = 1
ENDIF

GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
ENDIF
GOSUB SUM_TOTALS
IF PLACE != 6 THEN
LCDOUT $FE, $0F
LCDOUT $FE, $D4+PLACE
' MYFLAGS.0 = 1
ENDIF

DECREMENT:
IF B_IN < A_IN THEN
IF PLACE = 6 AND TOTAL > 0 THEN
IF MYFLAGS.0 = 0 THEN
ONES = ONES - 1
IF ONES > 9 THEN
ONES = 9
TENS = TENS - 1
ENDIF
IF TENS > 9 THEN
TENS = 9
HUND = HUND - 1
ENDIF
IF HUND > 9 THEN
HUND = 9
THOU = THOU - 1
ENDIF
IF THOU > 9 THEN
THOU = 9
TNTH = TNTH - 1
ENDIF
IF TNTH > 1 THEN THOU = 0
GOSUB SHOW_TOTAL
ENDIF
ENDIF
IF MYFLAGS.0 = 1 THEN
IF TNTH = 0 THEN
IF PLACE = 5 THEN
ONES = ONES - 1
IF ONES > 9 THEN ONES = 9
GOSUB SHOW_ONES
GOTO MAIN
ENDIF
IF PLACE = 4 THEN
TENS = TENS - 1
IF TENS > 9 THEN TENS = 9
GOSUB SHOW_TENS
GOTO MAIN
ENDIF
IF PLACE = 3 THEN
HUND = HUND - 1
IF HUND > 9 THEN HUND = 9
GOSUB SHOW_HUND
GOTO MAIN
ENDIF
IF PLACE = 1 THEN
THOU = THOU - 1
IF THOU > 9 THEN THOU = 9
GOSUB SHOW_THOU
GOTO MAIN
ENDIF
ENDIF
IF SUBTTL = 0 THEN
IF PLACE = 0 THEN
TNTH = TNTH - 1
IF TNTH > 1 THEN TNTH = 1
GOSUB SHOW_TNTH
GOTO MAIN
ENDIF
ENDIF
ENDIF
GOSUB SUM_TOTALS
IF TOTAL = 0 THEN
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $80, "MINIMUM LIMIT"
PAUSE 1000
LCDOUT $FE, $80, " "
ENDIF
ENDIF
GOTO MAIN

SHOW_TOTAL:
' LCDOUT $FE, $94, DEC5 (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES ;FOR DEBUG ONLY
LCDOUT $FE, $D4, DEC1 TNTH, DEC1 THOU, ".", DEC1 HUND, DEC1 TENS, DEC1 ONES

DEBOUNCE:
WHILE A_IN=0 OR B_IN=0 OR SW_IN=0 ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
WEND
RETURN

;************************************************* ******************************
SHOW_TNTH:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D4, DEC1 TNTH
LCDOUT $FE, $D4
LCDOUT $FE, $0F ;BLINK ON
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_THOU:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D5, DEC1 THOU
LCDOUT $FE, $D5
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_HUND:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D7, DEC1 HUND
LCDOUT $FE, $D7
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_TENS:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D8, DEC1 TENS
LCDOUT $FE, $D8
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_ONES:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D9, DEC1 ONES
LCDOUT $FE, $D9
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SUM_TOTALS:
TOTAL = (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES
SUBTTL = (THOU*1000)+(HUND*100)+(TENS*10)+ONES
RETURN
;************************************************* ******************************

;************************************************* ******************************
END

csantex
- 8th February 2018, 05:00
The previous post was an attempt to write code for a rotary encoder that would allow the user to increment or decrement the value displayed by a factor of 1, on a 20x4 LCD. The range was set for 00.000 to 10.000. With the push of the shaft button on the rotary encoder, the user could select any of the 4 least significant digits to change their value from 0 to 9 individually. The most significant digit could only be set to 1 or 0, depending on the value of the least 4. If the least 4 were set to all zeros, then the 5th digit could be set to 1.

This next version accomplishes the same results with what I thought would be more efficient code. As it turns out, the original code uses 1549 words if you delete the command to clear PortD.5. It is not used nor necessary. The 2nd version does use less written code from the user but in turn, uses more words in memory than the original. 1572 words total to be exact. I have noticed and increase in word count at the time of compiling in other projects as well. I have come to the conclusion that using only "IF" statements uses less program memory.

I believe this code to be reliable and beneficial to anyone needing the features it provides to the user. I know there is more efficient code out there. I'm not proficient in programming like others who have been doing this for a long time so I leave it to this forum to evaluate and critique it for everyone's gain. If anyone can make use of this code or learn from it, then my task is complete for I have learned from it as well.

Please enjoy the code.
New code below,

csantex



'************************************************* ******************************
;* Name : PIC16F887.BAS
;* Author : CSANTEX
;* Notice : Copyright (c) 2018
;* : All Rights Reserved
;* Date :
;* Version : Version 2
;* Notes :
'************************************************* ******************************
;Max Word count available is 8192. Max EEPROM available is 256 bytes
;************************************************* ******************************
;This code displays a number that is incremented for clockwise rotation or decrements
;for counter-clockwise rotation on a 20x4 LCD. The display has two digits to the
;left of the decimal and three digits to the right of the decimal. Each time the
;shaft is pushed in, a digit is selected starting from left to right. This code
;could be used to display voltages that are preset by a user on a diy power supply
;or for Ampere presets or any of the like.
;The code is a bit long but it works well.

;COULD USE MORE OPTIMIZATION!

;Original workd count=1549
;V2 word count =1572
;************************************************* ******************************
;************************************************* ******************************
' include "PIC16F887.PBPINC" ; processor specific variable definitions
INCLUDE "Modedefs.Bas"

@ ERRORLEVEL -306 ; Suppress messages related to Page Boundries.
;************************************************* ******************************
#CONFIG
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_ON & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
#ENDCONFIG

DEFINE OSC 8

DEFINE LCD_DREG PORTD ' PORTA is LCD data port
DEFINE LCD_DBIT 4 ' PORTA.4 is the data LSB ( 4 BIT MODE )
DEFINE LCD_RSREG PORTB ' RS is connected to PORTA.1
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTB ' E is connected to PORTA.3
DEFINE LCD_EBIT 1
DEFINE LCD_BITS 4 ' 4 lines of data are used (4 BITS)
DEFINE LCD_LINES 4 ' It is a 4-line display
DEFINE LCD_COMMANDUS 1500 ' Use 1500uS command delay
DEFINE LCD_DATAUS 44 ' Use 44uS data delay
;************************************************* ******************************
OSCCON = %01110000 ; Set PIC16F88 to 4MHz = %01100000, or
; 8MHz = %01110000
TRISA = %11111111 ; Port A set to inputs
TRISB = %11101100 ; Port B set to OUTPUTS
TRISC = %01111111 ; Port C set to inputs
TRISD = %00000000 ; Port D set to Ouputs
ANSEL = %00000000 ; Set all analog pins to digital
ANSELH = %00000000
' LED var PortD.7 ; LED for flashing
A_IN VAR PORTC.6
B_IN VAR PORTC.5
SW_IN VAR PORTC.4
;************************************************* ******************************
ONES VAR BYTE ; VARIABLE
TENS VAR BYTE ; VARIABLE
HUND VAR BYTE ; VARIABLE
THOU VAR BYTE ; VARIABLE
TNTH VAR BYTE ; VARIABLE
SUBTTL VAR WORD ; VARIABLE
TOTAL VAR WORD ; TOTAL VALUE
PLACE VAR BYTE ; VARIABLE

MYFLAGS VAR BYTE
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
;************************************************* ******************************
INIT:
PAUSE 1000
CLEAR
PLACE = 6

' LCDOUT $FE, $C0, DEC PLACE ;FOR DEBUG ONLY
' LCDOUT $FE, $94, DEC5 (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES ;FOR DEBUG ONLY

LCDOUT $FE, $D4, DEC1 TNTH, DEC1 THOU, ".", DEC1 HUND, DEC1 TENS, DEC1 ONES


MAIN:
IF A_IN < B_IN THEN
IF PLACE = 6 AND TOTAL < 10000 THEN
IF MYFLAGS.0 = 0 THEN
TOTAL = TOTAL + 1
ONES = TOTAL DIG 0
TENS = TOTAL DIG 1
HUND = TOTAL DIG 2
THOU = TOTAL DIG 3
TNTH = TOTAL DIG 4
GOSUB SHOW_TOTAL
ENDIF
ENDIF
IF MYFLAGS.0 = 1 THEN
IF TNTH = 0 THEN
SELECT CASE PLACE
CASE 5
ONES = ONES + 1
IF ONES > 9 THEN ONES = 0
GOSUB SHOW_ONES
CASE 4
TENS = TENS + 1
IF TENS > 9 THEN TENS = 0
GOSUB SHOW_TENS
CASE 3
HUND = HUND + 1
IF HUND > 9 THEN HUND = 0
GOSUB SHOW_HUND
CASE 1
THOU = THOU + 1
IF THOU > 9 THEN THOU = 0
GOSUB SHOW_THOU
END SELECT
ENDIF
IF SUBTTL = 0 THEN
IF PLACE = 0 THEN
TNTH = TNTH + 1
IF TNTH > 1 THEN TNTH = 0
GOSUB SHOW_TNTH
GOTO PUSHBUTTON
ENDIF
ENDIF
ENDIF
GOSUB SUM_TOTALS
IF TOTAL = 10000 THEN
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $80, "MAXIMUM LIMIT"
PAUSE 1000
LCDOUT $FE, $80, " "
ENDIF
ENDIF

PUSHBUTTON:
IF SW_IN = 0 THEN
PLACE = PLACE + 1
IF PLACE = 2 THEN PLACE = 3
IF PLACE > 6 THEN PLACE = 0
LCDOUT $FE, $D4+PLACE

IF PLACE = 6 THEN
LCDOUT $FE, $0C ;BLINK OFF
MYFLAGS.0 = 0
ELSE
LCDOUT $FE, $0F
LCDOUT $FE, $D4+PLACE
MYFLAGS.0 = 1
ENDIF

GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
ENDIF
GOSUB SUM_TOTALS
IF PLACE != 6 THEN
LCDOUT $FE, $0F
LCDOUT $FE, $D4+PLACE
ENDIF

DECREMENT:
IF B_IN < A_IN THEN
IF PLACE = 6 AND TOTAL > 0 THEN
IF MYFLAGS.0 = 0 THEN
TOTAL = TOTAL - 1
ONES = TOTAL DIG 0
TENS = TOTAL DIG 1
HUND = TOTAL DIG 2
THOU = TOTAL DIG 3
TNTH = TOTAL DIG 4
GOSUB SHOW_TOTAL
ENDIF
ENDIF
IF MYFLAGS.0 = 1 THEN
IF TNTH = 0 THEN
SELECT CASE PLACE
CASE 5
ONES = ONES - 1
IF ONES > 9 THEN ONES = 9
GOSUB SHOW_ONES
GOTO MAIN
CASE 4
TENS = TENS - 1
IF TENS > 9 THEN TENS = 9
GOSUB SHOW_TENS
GOTO MAIN
CASE 3
HUND = HUND - 1
IF HUND > 9 THEN HUND = 9
GOSUB SHOW_HUND
GOTO MAIN
CASE 1
THOU = THOU - 1
IF THOU > 9 THEN THOU = 9
GOSUB SHOW_THOU
GOTO MAIN
END SELECT
ENDIF
IF SUBTTL = 0 THEN
IF PLACE = 0 THEN
TNTH = TNTH - 1
IF TNTH > 1 THEN TNTH = 1
GOSUB SHOW_TNTH
GOTO MAIN
ENDIF
ENDIF
ENDIF
GOSUB SUM_TOTALS
IF TOTAL = 0 THEN
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $80, "MINIMUM LIMIT"
PAUSE 1000
LCDOUT $FE, $80, " "
ENDIF
ENDIF
GOTO MAIN

SHOW_TOTAL:
' LCDOUT $FE, $94, DEC5 (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES ;FOR DEBUG ONLY

LCDOUT $FE, $D4, DEC1 TNTH, DEC1 THOU, ".", DEC1 HUND, DEC1 TENS, DEC1 ONES

DEBOUNCE:
WHILE A_IN=0 OR B_IN=0 OR SW_IN=0 ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
WEND
RETURN

;************************************************* ******************************
SHOW_TNTH:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D4, DEC1 TNTH
LCDOUT $FE, $D4
LCDOUT $FE, $0F ;BLINK ON
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_THOU:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D5, DEC1 THOU
LCDOUT $FE, $D5
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_HUND:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D7, DEC1 HUND
LCDOUT $FE, $D7
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_TENS:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D8, DEC1 TENS
LCDOUT $FE, $D8
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SHOW_ONES:
LCDOUT $FE, $0C ;BLINK OFF
LCDOUT $FE, $D9, DEC1 ONES
LCDOUT $FE, $D9
LCDOUT $FE, $0F
GOSUB DEBOUNCE ;DO NOTHING WHILE WE WAIT TO RELEASE THE BUTTON.
RETURN
SUM_TOTALS:
TOTAL = (TNTH*10000)+(THOU*1000)+(HUND*100)+(TENS*10)+ONES
SUBTTL = (THOU*1000)+(HUND*100)+(TENS*10)+ONES
RETURN
;************************************************* ******************************

;************************************************* ******************************
END

Ioannis
- 10th February 2018, 20:08
Good example csantex. Thanks for posting.

Ioannis

csantex
- 10th February 2018, 20:37
Thanks Ioannis, after all, it was your idea that started this little venture.

richard
- 13th February 2018, 08:33
if you do it more algorithmically the size can be reduced dramatically ,it becomes scalable digit wise

more functions can easily be added and the mcu can be employed to perform a real task like pid control of a power supply and not just become a display controller


ps note my lcd defines suite my setup and are different


'************************************************* ***************
'* Name : LCD_TEST.BAS *
'* Author : RICHARD *
'
'* Date : 4/8/2017 *
'* Version : 1.0 *
'* Notes : *
'* : 16f887 *
'************************************************* ***************


#CONFIG
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_ON & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
#ENDCONFIG

DEFINE OSC 8


'LCD defines begin here
DEFINE LCD_BITS 4 'defines the number of data interface lines (4 or 8)
DEFINE LCD_DREG PORTB 'defines the port where data lines are connected to
DEFINE LCD_DBIT 0 'defines the position of data lines for 4-bit interface (0 or 4)
DEFINE LCD_RSREG PORTB 'defines the port where RS line is connected to
DEFINE LCD_RSBIT 4 'defines the pin where RS line is connected to
DEFINE LCD_EREG PORTB 'defines the port where E line is connected to
DEFINE LCD_EBIT 5 'defines the pin where E line is connected
DEFINE LCD_COMMANDUS 1500 'defines the delay after LCDOUT statement
DEFINE LCD_DATAUS 50 'delay in micro seconds
'END of LCD DEFINES


Pause 500 ' LCD initialize time
lcdout $FE,1

num_digits con 5 ; range 4 t0 9 tested ok note sum_total will not work for more than 5 digits
sw_threshhold con 10
lp_threshhold con 100 ;long press
sw_inhibit_time con 100
msd_limit con 5 ;limit value for msd note sum_total will not work for more than 5 digits and limit > 5 ;optional
digit var byte[num_digits-1]
d_index var byte
overflow var bit
underflow var bit
noroll_flg var bit ;optional
d_inx var byte
sw_cnt var byte
sw_flag var bit
re var byte
last_re var byte
ev var byte
tmp1 var byte
tmp2 var byte ;optional
tmp3 var byte ;optional
sw_inhibit var byte
total var word
enc_sw var PORTC.4
enc_a var portc.5
enc_b var portc.6
clear
d_index = num_digits
re = porta&48
last_re = re
gosub dup


main:
if !sw_inhibit then gosub chk_sw
if sw_flag then ;service the sw if active
if sw_cnt > lp_threshhold then ;long press clears settings
for d_index = 0 to (num_digits-1)
digit[d_index] = 0
next
gosub dup
else
d_index = d_index - 1 ;increment set digit {index posn 6 will stop editing of settinge}
if d_index > num_digits then d_index = num_digits
gosub set_cursor
sw_inhibit = sw_inhibit_time ; don't reactivate sw immediately [more user friendly]
endif
sw_cnt = 0
sw_flag = 0
endif
;poll encoder a poll rate of 5-10ms is adequate
re = portc&96
if re != last_re then ;encoder has moved
ev=ev<<2 ;shift old reading into position for compare
ev=ev|((portc&96)>>5) ;read enc pins 4,5 mask off others then combine this reading with last reading
;lookup table is a form of digital filter to debounce encoder [no caps used on encoder at all]
LOOKUP (ev&$f),[0,255, 1, 0, 1, 0, 0, 255, 255, 0, 0, 1, 0, 1, 255, 0],tmp1 ;lookup the decision matrix to determine rotation
endif
;service encoder result if there is one
if tmp1 && re == 0 then ; tmp1 255 is anticlockwise or 1 for clockwise [swap a/b pins if direction incorrect]
if d_index != num_digits then
if tmp1 = 255 then
gosub dec_digit
else
gosub inc_digit
endif
endif
gosub dup
tmp1 = 0
endif
;service sw inhibit if necessary
if sw_inhibit then sw_inhibit = sw_inhibit - 1 ; [make sw action more user friendly]

pause 5 ;adjust to suite desired responsiveness or more functions in loop

goto main

dup: ;update lcd
LCDOUT $FE, $94
tmp1=num_digits
while tmp1
tmp1=tmp1-1
if tmp1 = 1 then
LCDOUT ".", digit[tmp1]+$30
else
LCDOUT digit[tmp1]+$30
endif
wend
gosub SUM_TOTALS
LCDOUT $FE, $80, dec5 total
set_cursor: ;update cursor
if d_index == num_digits then
LCDOUT $FE,$0C
elseif d_index > 1 then
LCDOUT $FE,$0f,$fe,$94 + (num_digits - 1) - d_index
else
LCDOUT $FE,$0f,$fe,$94 + num_digits - d_index
endif
return

inc_digit: ;inc current digit
d_inx = d_index
gosub norollu ;optional
if noroll_flg then ;optional
digit[d_index] = digit[d_index] + 1
gosub chk_ovf
while overflow
digit[d_inx] = 0
d_inx = d_inx + 1
if d_inx >= num_digits then return
gosub norollu ;optional
if noroll_flg then ;optional
digit[d_inx] = digit[d_inx] + 1
gosub chk_ovf
endif ;optional
wend
endif ;optional
return
chk_ovf: ;check if overflow occured
overflow = 0
if digit[d_inx] > 9 then overflow = 1
return

chk_unf: ;check if underflow occured
underflow = 0
if digit[d_inx] > 127 then underflow = 1
return
dec_digit: ;decrement current digit
d_inx = d_index
gosub norolld ;optional
if noroll_flg then ;optional
digit[d_index] = digit[d_index] - 1
gosub chk_unf
while underflow
digit[d_inx] = 9
d_inx= d_inx + 1
if d_inx >= num_digits then return
gosub norolld ;optional
if noroll_flg then ;optional
digit[d_inx] = digit[d_inx] - 1
gosub chk_unf
endif ;optional
wend
endif ;optional
return
chk_sw: ;see if switch is active
if sw_flag then return
if !enc_sw then
if sw_cnt < 255 then sw_cnt = sw_cnt + 1
else
if sw_cnt > sw_threshhold then sw_flag = 1
endif
return

norolld: ; stop rollunder from 0000.00 to 9999.99 ;optional
tmp3=digit[d_inx] ;optional
noroll_flg=0 ;optional
for tmp2=(d_inx+1) to (num_digits -1) ;optional
tmp3=tmp3+digit[tmp2] ;optional
next ;optional
if tmp3 then noroll_flg = 1 ;optional
return ;optional
norollu: ; stop rollover from 9999.99 to 0000.00 and limit msd
noroll_flg=0 ;optional
if d_inx < (num_digits -1) then ;optional
tmp3=9-digit[d_inx] ;optional
for tmp2=(d_inx+1) to (num_digits -2) ;optional
tmp3=tmp3+(9-digit[tmp2] ) ;optional
next ;optional
tmp3=tmp3+(msd_limit-digit[tmp2] ) ;optional
else ;optional
tmp3=msd_limit-digit[d_inx] ;optional
endif ;optional
if tmp3 then noroll_flg = 1 ;optional
return ;optional
SUM_TOTALS: ;max digit is 5 and max_msd = 5 or else result will overflow
tmp1=num_digits
total=0
while tmp1
tmp1=tmp1-1
total=total*10
total=total+digit[tmp1]
wend
return

csantex
- 13th February 2018, 17:23
Thanks Richard for commenting and posting code. Your code takes up much less space. I made the hardware changes to my setup and included the necessary configuration registers for programming. I don't understand why you "and" PortA with decimal 48. Please explain. See example below.
As per your code, it does not function properly on my setup even though I made the LCD changes. What am I not seeing or missing? See video of power up and what happens when I turn the knob of the encoder. See what happens when I select a digit and then turn the knob. I can only get it to respond to the knob turn when I do it fast, not when it is done slowly.


Encoder type used: I had used 3 10nF caps on the 3 lines to ground for debounce in my original setup. Failed to mention that, but I used a new one with no caps and still didn't work.
8594


Video:

https://youtu.be/kfHVTcCwP7g

Thanks, csantex




if you do it more algorithmically the size can be reduced dramatically ,it becomes scalable digit wise

more functions can easily be added and the mcu can be employed to perform a real task like pid control of a power supply and not just become a display controller


ps note my lcd defines suite my setup and are different


'************************************************* ***************
'* Name : LCD_TEST.BAS *
'* Author : RICHARD *
'
'* Date : 4/8/2017 *
'* Version : 1.0 *
'* Notes : *
'* : 16f887 *
'************************************************* ***************


#CONFIG
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_ON & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
#ENDCONFIG

DEFINE OSC 8


'LCD defines begin here
DEFINE LCD_BITS 4 'defines the number of data interface lines (4 or 8)
DEFINE LCD_DREG PORTB 'defines the port where data lines are connected to
DEFINE LCD_DBIT 0 'defines the position of data lines for 4-bit interface (0 or 4)
DEFINE LCD_RSREG PORTB 'defines the port where RS line is connected to
DEFINE LCD_RSBIT 4 'defines the pin where RS line is connected to
DEFINE LCD_EREG PORTB 'defines the port where E line is connected to
DEFINE LCD_EBIT 5 'defines the pin where E line is connected
DEFINE LCD_COMMANDUS 1500 'defines the delay after LCDOUT statement
DEFINE LCD_DATAUS 50 'delay in micro seconds
'END of LCD DEFINES

;TRIS REGISTERS FROM MY CODE WITH COMMENTS****************************

OSCCON = %01110000 ; Set PIC16F88 to 4MHz = %01100000, or
; 8MHz = %01110000
' TRISA = %11111111 ; Port A set to inputs 'COMMENTED OUT SINCE IT BOOTS UP AS INPUTS ANYWAYS. <<<<<<<<<<<<<<<
' TRISB = %11101100 ; Port B set to OUTPUTS 'COMMENTED OUT SINCE LCD DEFINES SET DATA DIRECTIONS. <<<<<<<<<<<<<<<<
TRISC = %01111111 ; Port C set to inputs
' TRISD = %00000000 ; Port D set to Ouputs 'NOT USED IN YOUR CODE <<<<<<<<<<<<<<<<<<<<<<<
ANSEL = %00000000 ; Set all analog pins to digital
ANSELH = %00000000

;************************************************* *********************

; YOUR CODE==============================================
Pause 500 ' LCD initialize time
lcdout $FE,1

num_digits con 5 ; range 4 t0 9 tested ok note sum_total will not work for more than 5 digits
sw_threshhold con 10
lp_threshhold con 100 ;long press
sw_inhibit_time con 100
msd_limit con 5 ;limit value for msd note sum_total will not work for more than 5 digits and limit > 5 ;optional
digit var byte[num_digits-1]
d_index var byte
overflow var bit
underflow var bit
noroll_flg var bit ;optional
d_inx var byte
sw_cnt var byte
sw_flag var bit
re var byte
last_re var byte
ev var byte
tmp1 var byte
tmp2 var byte ;optional
tmp3 var byte ;optional
sw_inhibit var byte
total var word
enc_sw var PORTC.4
enc_a var portc.5
enc_b var portc.6
clear
d_index = num_digits
re = porta&48 '<<<<<<<<<<<<<<< Why do you "&" dec48 with Port A?
last_re = re
gosub dup

The rest of the code is as you wrote it....

csantex
- 13th February 2018, 18:21
For anyone interested, here is a video of my code, even though its long, but working with the hardware setup as in Richard's comment. Changed my code to reflect that. I think it would help me if we could see a video of Richards code in action. I'm very interested.

csantex


https://youtu.be/gccL6IHuldg

richard
- 13th February 2018, 20:39
I don't understand why you "and" PortA with decimal 48. Please explain

I originally had encoder on pins 4,5 . and missed fixing that line when I moved re pins to 5,6
it only sets re initial state so after first move its not important , the first move could be in error though
I should "and" PortA with decimal 96. for those pins

richard
- 13th February 2018, 22:42
oops
and moved it to portc too to match your setup

richard
- 13th February 2018, 23:23
As per your code, it does not function properly on my setup even though I made the LCD changes. What am I not seeing or missing? See video of power up and what happens when I turn the knob of the encoder. See what happens when I select a digit and then turn the knob. I can only get it to respond to the knob turn when I do it fast, not when it is done slowly.

I have the identical rotary encoder module and use no caps at all on it,
I have used a pic18f458 @8mhz , a pic18f25k22 @64mhz they both work perfectly.
I don't have a pic16 chip with the correct pinout to suit the lcd wiring in my easypic7 board so I can't test for those chips
but can see no reason for it not to work.

csantex
- 13th February 2018, 23:33
oops
and moved it to portc too to match your setup

I caught that earlier and made the changes then but it still didn't work. Here is my schematic:
Hope its not too big. If too small, I provided an image attachment.


8595

csantex
- 13th February 2018, 23:46
I have the identical rotary encoder module and use no caps at all on it,
I have used a pic18f458 @8mhz , a pic18f25k22 @64mhz they both work perfectly.
I don't have a pic16 chip with the correct pinout to suit the lcd wiring in my easypic7 board so I can't test for those chips
but can see no reason for it not to work.

If your using 18F chips then that may be the reason why. Perhaps the commands are getting compiled differently for 18F's than for 16F's??
No matter, I have an 18F4423 I can use to test that theory...
Thanks.

richard
- 14th February 2018, 02:38
I don't use lcd's or pic16's much but here is a trap



LCDOUT ".", digit[tmp1]+$30

inline math for lcdout on a pic16 turns to crap,you need to do the math first then print result


tmp2 =digit[tmp1]+$30

LCDOUT ".", tmp2

the code still fails on pic16, may be too many nested gosubs

richard
- 14th February 2018, 03:35
moved enc to porta 234 on 16f648a, reduced nesting by two levels

now works


'************************************************* ***************
'* Name : LCD_TEST.BAS *
'* Author : RICHARD *
'
'* Date : 4/8/2017 *
'* Version : 1.0 *
'* Notes : *
'* : 16f648a *
'************************************************* ***************
#CONFIG
__config _HS_OSC & _WDT_ON & _PWRTE_OFF & _MCLRE_ON & _BODEN_ON & _LVP_OFF & _CP_OFF
#ENDCONFIG

DEFINE OSC 8


'LCD defines begin here
DEFINE LCD_BITS 4 'defines the number of data interface lines (4 or 8)
DEFINE LCD_DREG PORTB 'defines the port where data lines are connected to
DEFINE LCD_DBIT 0 'defines the position of data lines for 4-bit interface (0 or 4)
DEFINE LCD_RSREG PORTB 'defines the port where RS line is connected to
DEFINE LCD_RSBIT 4 'defines the pin where RS line is connected to
DEFINE LCD_EREG PORTB 'defines the port where E line is connected to
DEFINE LCD_EBIT 5 'defines the pin where E line is connected
DEFINE LCD_COMMANDUS 1500 'defines the delay after LCDOUT statement
DEFINE LCD_DATAUS 50 'delay in micro seconds
'END of LCD DEFINES
DEFINE LCD_LINES 4
cmcon=7
Pause 500 ' LCD initialize time
lcdout $FE,1

num_digits con 5 ; range 4 t0 9 tested ok note sum_total will not work for more than 5 digits
sw_threshhold con 10
lp_threshhold con 100 ;long press
sw_inhibit_time con 100
msd_limit con 5 ;limit value for msd note sum_total will not work for more than 5 digits and limit > 5 ;optional
digit var byte[num_digits]
d_index var byte
overflow var bit
underflow var bit
noroll_flg var bit ;optional
d_inx var byte
sw_cnt var byte
sw_flag var bit
re var byte
last_re var byte
ev var byte
tmp1 var byte
tmp2 var byte ;optional
tmp3 var byte ;optional
sw_inhibit var byte
total var word
enc_act var byte
enc_sw var PORTa.4
enc_a var porta.2
enc_b var porta.3

clear
d_index = num_digits
re = porta&12
last_re = re
gosub dup


main:
if !sw_inhibit then gosub chk_sw
if sw_flag then ;service the sw if active
if sw_cnt > lp_threshhold then ;long press clears settings
for d_index = 0 to (num_digits-1)
digit[d_index] = 0
next
gosub dup
else
d_index = d_index - 1 ;increment set digit {index posn 6 will stop editing of settinge}
if d_index > num_digits then d_index = num_digits
gosub set_cursor
sw_inhibit = sw_inhibit_time ; don't reactivate sw immediately [more user friendly]
endif
sw_cnt = 0
sw_flag = 0
endif
;poll encoder a poll rate of 5-10ms is adequate
re = porta&12
if re != last_re then ;encoder has moved
last_re = re ; this line fell off pic18 version
ev=ev<<2 ;shift old reading into position for compare
ev=ev|((porta&12)>>2) ;read enc pins 4,5 mask off others then combine this reading with last reading
;lookup table is a form of digital filter to debounce encoder [no caps used on encoder at all]
LOOKUP (ev&$f),[0,255, 1, 0, 1, 0, 0, 255, 255, 0, 0, 1, 0, 1, 255, 0],enc_act ;lookup the decision matrix to determine rotation
endif
;service encoder result if there is one
if enc_act && (re == 0) then ; tmp1 255 is anticlockwise or 1 for clockwise [swap a/b pins if direction incorrect]
if d_index != num_digits then
if enc_act = 255 then
d_inx = d_index
gosub norolld ;optional
if noroll_flg then ;optional
digit[d_index] = digit[d_index] - 1
underflow = 0
if digit[d_inx] > 127 then underflow = 1
while underflow
underflow = 0
digit[d_inx] = 9
d_inx= d_inx + 1
if d_inx < num_digits then
gosub norolld ;optional
if noroll_flg then ;optional
digit[d_inx] = digit[d_inx] - 1
if digit[d_inx] > 127 then underflow = 1
endif ;optional
endif
wend
endif ;optional
else
d_inx = d_index
gosub norollu ;optional
if noroll_flg then ;optional
digit[d_index] = digit[d_index] + 1
overflow = 0
if digit[d_inx] > 9 then overflow = 1
while overflow
overflow = 0
digit[d_inx] = 0
d_inx = d_inx + 1
if d_inx < num_digits then
gosub norollu ;optional
if noroll_flg then ;optional
digit[d_inx] = digit[d_inx] + 1
if digit[d_inx] > 9 then overflow = 1
endif ;optional
endif
wend
endif ;optional
endif
gosub dup
endif

enc_act = 0
endif
;service sw inhibit if necessary
if sw_inhibit then sw_inhibit = sw_inhibit - 1 ; [make sw action more user friendly]

pause 5 ;adjust to suite desired responsiveness or more functions in loop

goto main

dup: ;update lcd
LCDOUT $FE, $94
tmp1=num_digits
while tmp1
tmp1=tmp1-1
tmp2 = digit[tmp1]+$30
if tmp1 = 1 then
LCDOUT ".", tmp2
else
LCDOUT tmp2
endif
wend
gosub SUM_TOTALS
LCDOUT $FE, $80, dec5 total
set_cursor: ;update cursor
if d_index == num_digits then
LCDOUT $FE,$0C
else
tmp2 = $94 + num_digits - d_index
if d_index > 1 then tmp2=tmp2-1
LCDOUT $FE,$0f,$fe,tmp2
endif
return

chk_sw: ;see if switch is active
if sw_flag then return
if !enc_sw then
if sw_cnt < 255 then sw_cnt = sw_cnt + 1
else
if sw_cnt > sw_threshhold then
sw_flag = 1
else
sw_cnt=0
endif
endif
return

norolld: ; stop rollunder from 0000.00 to 9999.99 ;optional
tmp3=digit[d_inx] ;optional
noroll_flg=0 ;optional
for tmp2=(d_inx+1) to (num_digits -1) ;optional
tmp3=tmp3+digit[tmp2] ;optional
next ;optional
if tmp3 then noroll_flg = 1 ;optional
return ;optional
norollu: ; stop rollover from 9999.99 to 0000.00 and limit msd
noroll_flg=0 ;optional
if d_inx < (num_digits -1) then ;optional
tmp3=9-digit[d_inx] ;optional
for tmp2=(d_inx+1) to (num_digits -2) ;optional
tmp3=tmp3+(9-digit[tmp2] ) ;optional
next ;optional
tmp3=tmp3+(msd_limit-digit[tmp2] ) ;optional
else ;optional
tmp3=msd_limit-digit[d_inx] ;optional
endif ;optional
if tmp3 then noroll_flg = 1 ;optional
return ;optional
SUM_TOTALS: ;max digit is 5 and max_msd = 5 or else result will overflow
tmp1=num_digits
total=0
while tmp1
tmp1=tmp1-1
total=total*10
total=total+digit[tmp1]
wend
return

csantex
- 14th February 2018, 04:29
moved enc to porta 234 on 16f648a, reduced nesting by two levels

now works



Yes now it does and I now see what you meant by scalable. Range is 000.00 to 599.99
So at startup with no button push, the knob does nothing until the user is ready to use it.
Push Button, user selects the hundreds digit from 000.00 to 500.00.
Next push, user selects the tens digit for a range of 000.00 to 590.00.
Next push, user selects the ones digit for a range of 000.00 to 599.00
Next push is for tenths. 599.90
Nest push is for hundredths. 599.99
Push button for a second or so and it clears it all to zero.
Last push, the knob is disabled again.

Very interesting way of doing it. I must say that this method is way more functional that what I was doing.

Thanks for a great lesson in programming.

On a side note, this pic has an 8 level stack. I only counted 2 nested GOSUBS max. I guess 2 was 2 much...

richard
- 14th February 2018, 04:37
On a side note, this pic has an 8 level stack. I only counted 2 nested GOSUBS max. I guess 2 was 2 much...
pbp uses 4 of them before your code even starts

now see what happens when
num_digits con 5 is changed

try 4 up to 9 or beyond

also try changing
msd_limit con 5

to other values


also hold switch down for a couple of seconds

Ioannis
- 14th February 2018, 09:19
On the http://www.best-microcontroller-projects.com/rotary-encoder.html site there was an article that uses a different type of encoder polling.

It is in C but if I can read it then everyone can!

I have not yet understood completely how it decodes the encoder data but seems very interesting and as the author says it decodes also the speed?

No hardware set up to test it but if anyone has the time I'd like to know the results.

Ioannis

richard
- 14th February 2018, 11:10
I have not yet understood completely how it decodes the encoder
it looks like a cruel hoax to me, maybe you need to sign up to get the working version.
if the encoder is static [on detent] after 12 polls state=0xffff
you then need 12 polls in a row where the clk pin is 0 to reach the
0xf000 state , since the polling is asynchronous I cannot see how that can happen
reliably ,so I tried it in my example


' enstate = (enstate<<1 )| enc_a | $e000
' if enstate == $f000 then
' enstate=0


' if enc_b then

and it fails as I expected when polled @5mS
the example polls @100uS , if you need that much horse power I can't see the point

Ioannis
- 14th February 2018, 15:39
Thanks for testing it Richard. It was very strange to me.

Anyway, no other version exists. I did sign in to the site but no other version exists.

Ioannis

csantex
- 15th February 2018, 01:42
For anyone interested in Richard's code example and how it works, watch the video below.
The video shows most of what the code does. Thanks for sharing Richard.

csantex


https://youtu.be/b0b8exnzD8M

richard
- 15th February 2018, 03:07
Thanks for testing it Richard. It was very strange to me.

Anyway, no other version exists. I did sign in to the site but no other version exists.

Ioannis


tried it on Arduino mega
@100uS it worked
@200uS its patchy
@400uS or longer its crap

conclusion , not much use but quaint

CuriousOne
- 4th June 2020, 12:16
I tried to re-use Richards code, but I have encoder on PORTB.3 and PORTB.4 and not using button. I've changed enc_a to portb.4
and enc_b to portb.3, but I guess I also have to modify re = porta&12 ? to what it set?

richard
- 5th June 2020, 06:23
I've changed enc_a to portb.4 and enc_b to portb.3, but I guess I also have to modify re = porta&12 ? to what it set?


its a simple binary mask used to select only the port pin data from the two pins of interest
the mask value is 2 to power of enc_a.bit + 2 to power of enc_b.bit.


you also need to shift the data gathered into bits 0 and 1 for proper evaluation