Example: A simple menu system with OBLCD
The purpose of a menu system is to allow a user to move through a range of alternatives and select one, in order to either change an operating parameter or initiate an action. Most people nowadays are familiar with the menu systems in cellular telephones. A menu system you might design for a SPLat with LCD is similar.
The following example will assume that there are just 4 push buttons available. From top to bottom these are labeled:
| Label | Name/function |
|---|---|
| <- | iLeftArrow |
| -> | iRightArrow |
| Y | iAccept |
| N | iReject |
Essentially the arrow keys will be used to select and change choices, while accept and reject keys will help finalise decisions.
The menu system will affect three memory variables:
WashTime. Duration of wash cycle, in minutes
PreWash. True or False, depending if a pre wash cycle is required
HotWash. If true, do a hot wash cycle, otherwise do a cold cycle.
It is absolutely essential that you plan your menu system on paper before you start writing any program code. The following table is a plan for the menu for a washing machine. It gives a choice of hot or cold wash and allows setting of the wash time between 5 and 25 minutes. Each row of the table corresponds to one display on the LCD. We will start by analyzing this tabulated plan, then we will see how it translates into SPLat code.
| Display | <- | -> | Y | N | |
|---|---|---|---|---|---|
| 0 | [1] | ||||
| 1 | 0123456789012345Wash type: XXXXChange? | [3] | [2] | Hot:[5] Cold:[4] | [0] |
| 2 | 0123456789012345Wash time: xxChange? | [1] | [3] | [6] | [0] |
| 3 | 0123456789012345Pre-wash: XxxY or N to change | [2] | [1] | Xxx=Yes Save | Xxx=No Save |
| 4 | [5] | [5] | Save [2] | [2] | |
| 5 | [4] | [4] | Save [2] | [2] | |
| 6 | 0123456789012345Change wash time xx | Dec xx min 5 | Inc xx max 25 | Save [3] | [3] |
Each row of the table is numbered. We call the numbers “State Numbers” and the whole table we call a “State Transition Table” or just “State Table”. If you are familiar with SPLatMaps or bubble diagrams you may recognize this as an alternative way of representing a Finite State Machine.
The State table has 6 columns. One is just for the state number. The second column is for the LCD display in that state. The final 4 columns specify what happens if the user presses each of the 4 available buttons. These are the “action columns”.
In an action column a number in [square brackets] means go to that new state (row) and change the display accordingly. The action columns can contain other actions as well, such as “Save” or change a variable (I’ve used X‘s as placeholders for variables both in the display column and in the action columns). I’ve also included a ruler line in the displays.
Let’s go through the 6 states, one at a time:
State 0
This allows for the possibility of the LCD not always being “owned” by the menu system. Perhaps in this state the bottom line shows wash temperature. Pressing Y will take us into the menu system proper at state 1.
State 1
Offers us the option to change the wash type. Pressing Y to accept the offer takes us to state 4 or 5, depending on the current wash type selection (hot or cold). Pressing an arrow key takes us to 2 or 3. If you study the arrow key actions for state 1, 2 and 3 you will discover that they form a loop, allowing you to scroll through these 3 “top level” menu choices. Pressing the N button takes us back to state 0, or notionally out of menu mode.
State 2
Displays the current wash time and asks if we want to change it. If we accept (Y) we go to state 6, which allows the wash time to be changed.
State 3
Lets us change the selection of the pre-wash.
States 4 and 5
These work together as a pair to allow toggling between hot wash and cold wash. The arrow keys toggle between them, Y accepts the current selection by saving it and N exits back to the top level of the menu without changing the setting.
State 6
Displays the wash time and allows us to change it using the arrow keys. Y saves the changed value while N rejects it. Notice that the table specifies that the value can only be incremented and decremented between the limits 5 to 25.
A few notes on all this:
- When we exit states 4, 5 or 6, we don’t return to the state we came from but to the next state in the top level menu sequence. This is a small refinement that will assist the user by automatically moving on to the next decision that must be made.
- State 3 updates its parameter immediately, whereas states 4/5 and 6 require the user to consciously accept or reject the change with Y or N. I’ve done this simply to illustrate for you the two different ways of operating. In a real world situation you should probably design for consistent behaviour.
- State 0 is rather poorly defined. In practice it would determine how this menu system meshes with other parts of the program that may also have claim to the buttons and the LCD.
- I called the arrow keys left and right rather than up and down, because the LCD can display left and right arrows. As it happens, I didn’t actually use them in the display!
The menu system will affect three memory variables:
HotWash: True if the wash is to be hot, false if the wash is to be cold.
WashTime: A simple integer variable giving wash time in minutes ( range 5 to 25)
PreWash: True if a pre-wash cycle is required.
The code for each state follows a consistent pattern, with the following elements:
- On entering the state for the first time, creation of the display for that state.
- A loop to look for one of the allowable keys, with a jump to an action segment if a key is pressed
- One action segment for each key. This may be an update of a variable and/or the display, or a jump to another state.
Here we go with the program:
;Sample menu system for washing machine
;Define keys
iLeftArrow iEQU 12 ;top button on MMi99
iRightArrow iEQU 11
iAccept iEQU 10
iReject iEQU 9
;Define memory locations.
;These are in the PermStore area
HotWash mEQU 6
WashTime mEQU 7
PreWash mEQU 8
;These are anywhere:
ScratchMem mEQU 9 ;Temporary storage
;initialize: Check if Permanent memory is valid,
; force defaults if not
PermRecall
GoIfZ DataOK
SetMem WashTime,10 ;Default
SetMem PreWash,T ;Default
SetMem HotWash,T ;Default
PermStore
PermStore
DataOK
OBLCD_Type 2
******** Menu system ***************************
;State 0 is easy: one message then wait for Y
SetMenu0
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Y for menu"
Menu0
GoIfInK iAccept,SetMenu1
GoTo Menu0
;==============State 1========================
SetMenu1
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Wash type: "
GoSub QryChange ;display "Change?"
Menu1ShowType
OBLCD_SetCur 0,11
Recall HotWash
GoIfT Menu1Hot
Menu1Cold
; 0123456789012345
OBLCD_Text "Cold "
GoTo Menu1KeyIn
Menu1Hot
; 0123456789012345
OBLCD_Text "Hot "
;Wait for a key
Menu1KeyIn
GoIfInK iLeftArrow,Menu1Left
GoIfInK iRightArrow,Menu1Right
GoIfInK iAccept,Menu1Accept
GoIfInK iReject,Menu1Reject
GoTo Menu1KeyIn ;Keep trying
Menu1Left
GoTo SetMenu3
Menu1Right
GoTo SetMenu2
Menu1Accept
Recall HotWash
GoIfT SetMenu5
GoTo SetMenu4
Menu1Reject
GoTo SetMenu0
;===========State 2========================
SetMenu2
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Wash time: "
GoSub QryChange ;display "Change?"
Menu2ShowTime
OBLCD_SetCur 0,11
Recall WashTime
OBLCD_UDecDispXVW
OBLCD_Text "m "
Menu2KeyIn
GoIfInK iLeftArrow,Menu2Left
GoIfInK iRightArrow,Menu2Right
GoIfInK iAccept,Menu2Accept
GoIfInK iReject,Menu2Reject
GoTo Menu2KeyIn
Menu2Left
GoTo SetMenu1
Menu2Right
GoTo SetMenu3
Menu2Accept
GoTo SetMenu6
Menu2Reject
GoTo SetMenu0
;===========State 3========================
SetMenu3
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Pre-wash: "
OBLCD_SetCur 1,0
OBLCD_Text "Y or N to change"
Menu3Show
OBLCD_SetCur 0,11
Recall PreWash
GoIfT Menu3PWYes
Menu3PWNo
OBLCD_Text "No "
GoTo Menu3KeyIn
Menu3PWYes
OBLCD_Text "Yes"
Menu3KeyIn
GoIfInK iLeftArrow,Menu3Left
GoIfInK iRightArrow,Menu3Right
GoIfInK iAccept,Menu3Accept
GoIfInK iReject,Menu3Reject
GoTo Menu3KeyIn
Menu3Left
GoTo SetMenu2
Menu3Right
GoTo SetMenu1
Menu3Accept
SetMem PreWash,T
PermStore
GoTo Menu3Show
Menu3Reject
SetMem PreWash,F
PermStore
GoTo Menu3Show
;======= State 4 =======================
SetMenu4
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Hot wash"
Menu4KeyIn
GoIfInK iLeftArrow,Menu4Left
GoIfInK iRightArrow,Menu4Right
GoIfInK iAccept,Menu4Accept
GoIfInK iReject,Menu4Reject
GoTo Menu4KeyIn
Menu4Left
GoTo SetMenu5
Menu4Right
GoTo SetMenu5
Menu4Accept
SetMem HotWash,T
PermStore
Menu4Reject
GoTo SetMenu2
;======= State 5 =======================
SetMenu5
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Cold wash"
Menu5KeyIn
GoIfInK iLeftArrow,Menu5Left
GoIfInK iRightArrow,Menu5Right
GoIfInK iAccept,Menu5Accept
GoIfInK iReject,Menu5Reject
GoTo Menu5KeyIn
Menu5Left
GoTo SetMenu4
Menu5Right
GoTo SetMenu4
Menu5Accept
SetMem HotWash,F
PermStore
Menu5Reject
GoTo SetMenu2
;===========State 6========================
SetMenu6
OBLCD_Cls
; 0123456789012345
OBLCD_Text "Change wash time"
Recall WashTime
Menu6Show
Store ScratchMem
OBLCD_SetCur 1,6
Recall ScratchMem
OBLCD_UDecDispXVW
OBLCD_Text "m "
Menu6KeyIn
GoIfInK iLeftArrow,Menu6Left
GoIfInK iRightArrow,Menu6Right
GoIfInK iAccept,Menu6Accept
GoIfInK iReject,Menu6Reject
GoTo Menu6KeyIn
Menu6Left
Recall ScratchMem
Push
GoIfXGE 25,Menu6KeyIn
IncX
GoTo Menu6Show
Menu6Right
Recall ScratchMem
Push
GoIfXLE 5,Menu6KeyIn
DecX
GoTo Menu6Show
Menu6Accept
Recall ScratchMem
Store WashTime
PermStore
Menu6Reject
GoTo SetMenu3
;===============================================
;Subroutine to display "Change?" (clears to EOL)
QryChange
OBLCD_SetCur 1,0
; 0123456789012345
OBLCD_Text "Change? "
Return
;============ End of program ================
Some notes about this code:
- Notice the very tidy factoring into one block of code per state. Notice also that all sections are essentially identical in layout. This uniformity makes the code easier to read. It is also a result of using a lot of copy/paste operations, saving a lot of typing.
- In each section there is the same sequence of testing each key and jumping if it has been pressed. Following that are the 4 action addresses and the code for each. In many cases the code at each action is just another jump to a new state. That means each state change uses two jumps to get there. I could have saved code and memory by jumping to the new state straight after the key test, but that would have made the program a little less regular and therefore more error prone. The only deviation from this schema is state 0, which is a little different in intent and function.
- I used a subroutine to display “Change?”, because that operation occurs twice. A very modest saving in memory, perhaps, but it usually pays to place repeated operations into subroutines.
- I have used very few comments. This is in part to leave to you to discover how the code works. Also, it is an unfortunate fact that the code for a state machine tends to obscure the logic, so making intelligible comments can be difficult. You really need access to the underlying plan, in this case the state transition table. Of course, the table could be included as a huge comment!