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: