Altering a Large Integer Value on an LCD with only Three Buttons

In a menu system, it is not always easy to alter a large integer value with simple increments or decrements of the whole value because of the time it will take to alter 10s of thousands of value. Therefore, I created the simple procedure, listed below, that alters the value a digit at a time, so it is easier to manage, and looks good to the user of the menu system.

The routine is controlled by three buttons for
Up, Down and Select, where the Select button, when pressed, moves to the value's next digit to the right, and the Up and Down buttons increment or decrement the chosen digit. The digit itself flashes, telling the user where the value is being changed. When the last digit is flashing, a press of the Select button will complete the operation and the integer value will be created within a variable.

'

' /\\\\\\\\\

' /\\\///////\\\

' \/\\\ \/\\\ /\\\ /\\\

' \/\\\\\\\\\\\/ /\\\\\ /\\\\\\\\\\ /\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\

' \/\\\//////\\\ /\\\///\\\ \/\\\////// /\\\/////\\\ \////\\\//// \////\\\//// \////////\\\

' \/\\\ \//\\\ /\\\ \//\\\ \/\\\\\\\\\\ /\\\\\\\\\\\ \/\\\ \/\\\ /\\\\\\\\\\

' \/\\\ \//\\\ \//\\\ /\\\ \////////\\\ \//\\/////// \/\\\ /\\ \/\\\ /\\ /\\\/////\\\

' \/\\\ \//\\\ \///\\\\\/ /\\\\\\\\\\ \//\\\\\\\\\\ \//\\\\\ \//\\\\\ \//\\\\\\\\/\\

' \/// \/// \///// \////////// \////////// \///// \///// \////////\//

' Let's find out together what makes a PIC Tick!

'

' 24-bit Value increment/decrement with 3 buttons: Left, Right and Select

'

' The Select button moves along the value displayed on the LCD, digit by digit

' The Left button decrements the digit

' The Right button increments the digit

' When the Select button is pressed on the last digit, the value will be fully altered

'

' Allows values from 0 to 8388607 to be created

' Written by Les Johnson for the Positron8 compiler versions 4.0.1.9 and over.

'

Device = 18F25K20 ' Tell the compiler what device to compile for

Declare Xtal = 64 ' Tell the compiler what frequency the device is operating at (in MHz)

'

' Setup the Alphanumeric LCD

'

Declare LCD_DTPin = PORTA.0

Declare LCD_RSPin = PORTA.4

Declare LCD_ENPin = PORTA.5

Declare LCD_Interface = 4

Declare LCD_Lines = 2

Declare LCD_Type = Alphanumeric

Declare LCD_CommandUs = 2000

Declare LCD_DataUs = 50

'

' Define the button pins

'

$define Left_Button_Pin PORTB.0 ' The Port.Pin used for the Left button

$define Right_Button_Pin PORTB.1 ' The Port.Pin used for the Right button

$define Select_Button_Pin PORTB.2 ' The Port.Pin used for the Select button

'

' Values for button presses

'

Symbol cNoButton_Pressed = 0 ' No button is pressed

Symbol cLeftButton_Pressed = 1 ' The Left button is pressed

Symbol cRightButton_Pressed = 2 ' The Right button is pressed

Symbol cSelectButton_Pressed = 3 ' The Select button is pressed

'

' Create a variable for the demo

'

Dim Global_lValue As Long ' Holds the 24-bit value that has been altered


'--------------------------------------------------------------------

' The main program starts here

'

Main:

Cls ' Clear the LCD's display

Print At 1, 1, "ALTER:" ' Print the text "ALTER:" just before the value to alter

Global_lValue = Value24_Adjust(10, 1234567) ' Adjust the value sent, character by character from left to right

Print At 2, 1, "VALUE IS:", Dec Global_lValue ' Display the new value on the LCD


'--------------------------------------------------------------------

' Adjust a 24-bit value held in pValue, character by character from left to right

' Using left, right and select buttons

' Input : pValue holds the value to alter (0 to 8388607)

' Output : Returns the altered value (0 to 8388607)

' Notes : The digit that is being altered will flash and the select button will move to the next digit in the value

' : When the Select button is pressed on the last digit, the altered value will be returned

'

Proc Value24_Adjust(pXpos As Byte, pValue As Long), pValue

Dim tDigFlash As Bit ' Set to 1 if a space is to be placed as the digit's character, to flash it

Dim bFlashCount As Byte ' Holds a loop iterations counter to flash a digit's character

Dim bValDigPos As Byte ' Holds the X position of the digit that is being altered in the value

Dim bDigAltVal As Byte ' Holds the value of the character to be altered within the main value

Dim lValue As Long ' Holds the value to multiply with

Dim bElement As Byte ' Holds the array element count for loops

Dim bDigVals[7] As Byte Heap ' Holds the individual digits in the value to display

Dim bDig[7] As Byte Heap ' Holds the individual digits of the value to alter

Dim bSIndex As bFlashCount ' Holds the source index for the value digits conversion loop

Dim bDIndex As bValDigPos ' Holds the destination index for the value digits conversion loop

'

' Convert the seperate digits held in pValue into the elements of the array bDigVals

'

bSIndex = 0 ' Reset the source index to 0

For bDIndex = Bound(bDig) DownTo 0 ' Create a loop for the amount of digits in pValue

bDigVals[bDIndex] = Dig pValue, bSIndex ' Load the array bDigVals with the seperate digit values

Inc bSIndex ' Increment the source index

Next


bDigAltVal = bDigVals[0] ' Start at digit 0 for altering (Left Hand Digit)

bValDigPos = 0 ' Start at digit 0 of the value to alter (the 10000s)

bFlashCount = 0 ' Reset the flash iterations counter

tDigFlash = 0 ' Start with the digit displayed and not the flashing space character

GoSub hDisplayValue ' Display the value held in pValue on line 1 of the LCD

Do ' Create a loop to alter the value digit by digit

Select Buttons_Get() ' Compare the button pressed value

Case cLeftButton_Pressed ' Is the left button pressed?

tDigFlash = 0 ' Yes. So make it so the digit is displayed first, before the flash

bFlashCount = 0 ' Reset the character flash iterations counter

Dec bDigAltVal ' Decrement the chosen digit's value

If bDigAltVal.SByte < 0 Then bDigAltVal = 9 ' Make sure the value does not go below 0 or above 9

bDigVals[bValDigPos] = bDigAltVal ' Transfer the digit being altered into the digit of the value


Case cRightButton_Pressed ' Is the right button pressed?

tDigFlash = 0 ' Yes. So make it so the digit is displayed first, before the flash

bFlashCount = 0 ' Reset the character flash iterations counter

Inc bDigAltVal ' Increment the chosen digit's value

If bDigAltVal > 9 Then bDigAltVal = 0 ' Make sure the value does not go above 9 or below 0

bDigVals[bValDigPos] = bDigAltVal ' Transfer the digit being altered into the digit of the value


Case cSelectButton_Pressed ' Is the select button pressed?

tDigFlash = 0 ' Yes. So make it so the digit is displayed first, before the flash

bFlashCount = 0 ' Reset the character flash iterations counter

Inc bValDigPos ' Increment the digit to be altered position

If bValDigPos > Bound(bDigVals) Then ' Is the digit position beyond the amount of digits?

Break ' Yes. So exit the loop

EndIf

bDigAltVal = bDigVals[bValDigPos] ' Transfer the element of bDigVals that holds the digit being altered

EndSelect

'

' A loop iterations counter to flash the digit that is being altered

'

Inc bFlashCount ' Increment the loop iterations counter

If bFlashCount > 50 Then ' Has it gone over its limit?

bFlashCount = 0 ' Yes. So reset its value

Toggle tDigFlash ' Toggle the bit flag to flash the digit being altered

EndIf

GoSub hDisplayValue ' Display the value held in pValue on line 1 of the LCD

DelayMS 10 ' A delay to flash the digit being altered

Loop

'

' Helper subroutine to display the value on line 1 of the LCD

' Array; bDigVals holds the individual digits of the value being altered

'

hDisplayValue:

For bElement = Bound(bDig) DownTo 0 ' Create a loop for the amount of elements in bDigVals

bDig[bElement] = bDigVals[bElement] + 48 ' Create an ASCII digit from the value held in the array's element

Next

'

' Calculate the actual value of pValue based upon the digits within it

' By multiplying to a power of 10 for each digit

'

lValue = 10 ' Start off with a multiplication of 10

pValue = bDigVals[6] ' Place the value held within element 6 of bDigVals into pValue

For bElement = 5 DownTo 0 ' Create a loop for the amount of multiplications

pValue = pValue + (bDigVals[bElement] * lValue) ' Convert the element values back into an integer value

lValue = lValue * 10 ' Multiply with a power of 10 each loop iteration

Next


If tDigFlash = 1 Then ' Is the digit flash flag set?

bDig[bValDigPos] = " " ' Yes. So place a space in the element that represents the value's flashing digit

EndIf

Print At 1, pXpos, bDig#0, bDig#1, bDig#2, bDig#3, bDig#4, bDig#5, bDig#6 ' Display the value on line 1 of the LCD

EndProc


'--------------------------------------------------------------------

' Detect a button press and return what button was pressed

' Input : None

' Output : Returns the a value for a particular button:

' 0 = no button, 1 = Left button, 2 = Right button, 3 = Select button

' Notes : Uses the micrcontroller's internal pull-up resistors for the button pins

'

Proc Buttons_Get(), Byte

PinMode(Left_Button_Pin, Input_PullUp) ' Make sure the left button's pin is an input with an internal pull-up resistor

PinMode(Right_Button_Pin, Input_PullUp) ' Make sure the right button's pin is an input with an internal pull-up resistor

PinMode(Select_Button_Pin, Input_PullUp) ' Make sure the select button's pin is an input with an internal pull-up resistor


Result = cNoButton_Pressed ' Default to a return of no button pressed

If Button_SingleGet(Left_Button_Pin) = 1 Then ' Is the Left button pressed?

Result = cLeftButton_Pressed ' Yes. So return a value for the button pressed


ElseIf Button_SingleGet(Right_Button_Pin) = 1 Then ' Is the Right button pressed?

Result = cRightButton_Pressed ' Yes. So return a value for the button pressed


ElseIf Button_SingleGet(Select_Button_Pin) = 1 Then ' Is the Select button pressed?

Result = cSelectButton_Pressed ' Yes. So return a value for the button pressed

EndIf

EndProc


'--------------------------------------------------------------------

' Detect a single button press

' Input : pButton holds the pin number that the button is attached to (0 to 23)

' Output : Returns 1 if a button press is detected

' Notes : Once the button has been detected and a result returned,

' it will not detect the button again until it has been released

' : The bits within the variable "lStateBits" hold the previous states of the pins

' : For active low button presses with a pull-up resistor

'

Proc Button_SingleGet(pButton As Pin), Bit

Symbol cPressed = 0 ' The value for a button pressed

Symbol cNotPressed = 1 ' The value for a button not pressed

Static Dim lStateBits As Long = 0 ' Create, and reset, a static variable to hold the states of 24 pins


Result = 0 ' Default to a false result

If GetBit(lStateBits, pButton) = 0 Then ' Has the button already been pressed?

If GetPin(pButton) = cPressed Then ' No. So is the button pressed?

Result = 1 ' Yes. So return a true result

EndIf

LoadBit(lStateBits, pButton, Result) ' Store the button's previous state

Else ' Otherwise... The button has not been previously pressed

If GetPin(pButton) = cNotPressed Then ' So... Is the button pressed?

LoadBit(lStateBits, pButton, 0) ' No. So reset the previous button state

EndIf

EndIf

EndProc


'-------------------------------------------------------------

' Setup the fuses on a PIC18F25K20 device, for the 4xPLL with an external crystal

'

Config_Start

FOSC = HSPLL ' HS oscillator, PLL enabled and under software control

Debug = Off ' Background debugger disabled' RB6 and RB7 configured as general purpose I/O pins

XINST = Off ' Instruction set extension and Indexed Addressing mode disabled (Legacy mode)

STVREN = Off ' Reset on stack overflow/underflow disabled

WDTEN = Off ' WDT disabled (control is placed on SWDTEN bit)

FCMEN = Off ' Fail-Safe Clock Monitor disabled

IESO = Off ' Two-Speed Start-up disabled

WDTPS = 128 ' Watchdog is 1:128

BOREN = Off ' Brown-out Reset disabled in hardware and software

BORV = 18 ' VBOR set to 1.8 V nominal

MCLRE = On ' MCLR pin enabled, RE3 input pin disabled

HFOFST = Off ' The system clock is held Off until the HF-INTOSC is stable.

LPT1OSC = Off ' T1 operates in standard power mode
PBADEN = Off ' PORTB<4:0> pins are configured as digital I/O on Reset
CCP2MX = PORTC ' CCP2 input/output is multiplexed with RC1
LVP = Off ' Single-Supply ICSP disabled
CP0 = Off ' Block 0 (000800-001FFFh) not code-protected
CP1 = Off ' Block 1 (002000-003FFFh) not code-protected
CPB = Off ' Boot block (000000-0007FFh) not code-protected
CPD = Off ' Data eeprom not code-protected
WRT0 = Off ' Block 0 (000800-001FFFh) not write-protected
WRT1 = Off ' Block 1 (002000-003FFFh) not write-protected
WRTB = Off ' Boot block (000000-0007FFh) not write-protected
WRTC = Off ' Configuration registers (300000-3000FFh) not write-protected
WRTD = Off ' Data eeprom not write-protected
EBTR0 = Off ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
EBTR1 = Off ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
EBTRB = Off ' Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

Below is a circuit diagram for the program listing above.

The Positron8 BASIC Compiler Source Files for the above program listing, as well as a version that operates with 16-bit variables, instead of 24-bit variables, can be downloaded from here: Positron8 - Value Change With 3 Buttons The zip file also contains the Proteus Simulation file.

A video of the above circuit and program working within a simulator can be seen below: