This is only a preview of the December 2016 issue of Silicon Chip. You can view 45 of the 104 pages in the full issue, including the advertisments. For full access, purchase the issue for $10.00 or subscribe for access to the latest issues. Items relevant to "Automotive Sensor Modifier":
Items relevant to "Arduino-Based Digital Theremin":
Items relevant to "Voltage/Current Reference With Touchscreen, Pt.2":
Articles in this series:
Articles in this series:
Purchase a printed copy of this issue for $10.00. |
Micromite
Plus Advanced
Programming, Pt.2
Last month, we went over some of the new features of the Micromite
Plus, including reading and writing files on an SD card and defining
GUI (graphical user interface) controls. Now we’re going to take a
look at some extra features which allow even more advanced GUI
controls to be built very easily.
By Geoff Graham
A
S EXPLAINED in Pt.1 last month,
it’s trivial to create a GUI control
using the Micromite Plus. In most cases, a single line of BASIC will create a
check box, text input control or one of
nine other different types of GUI elements. The Micromite Plus firmware
manages these controls for you, taking
care of display and user interaction via
the touch interface. The BASIC program can query the state of the controls
at a later time, to see what changes the
user has made.
Sometimes when a control is touch
ed, you need your program to respond
immediately. One way to do this
would be with a simple IF statement in
the main program loop. For example:
IF CTRLVAL(PwrSwitch) = 1 THEN . . .
However, with a complex program,
it is more efficient to use an interrupt
to detect when a control has been
touched. This is especially true if the
program is performing background
processing while the user is interacting
with the GUI. To use a touch interrupt,
you must first set it up. For example:
GUI INTERRUPT IntTouchDown
74 Silicon Chip
After this command, touching the
screen will cause MMBasic to interrupt whatever the main program is
doing and execute the code in the
subroutine IntTouchDown. When this
subroutine exits, the main program
will continue as if nothing happened
(apart from any state changes which
occur in that subroutine).
Within the interrupt subroutine,
you can discover what control was
touched by using the TOUCH(REF) function which will return the reference
number of the control currently being
touched. Note that REF is a keyword
and should not be replaced with a reference number or variable in this case.
In a large program with many controls,
it is best to use the SELECT CASE statement to select the appropriate code for
each control. For example:
With this sort of structure, you can
process almost any touch completely
within the interrupt. As a result, the
main program could consist of just the
commands to set up the controls and
then continue with its main job.
Sub IntTouchDown
SELECT CASE TOUCH(REF)
CASE PwrSwitch
' do some action
CASE OTHERCTRL
' do some other action
END SELECT
END SUB
CONST PwrSwitch = 41
CONST RedLED = 42
Interrupt example code
As an example of how an interrupt
could be useful, consider the situation where you would like to run a
motor whenever an on-screen switch
is touched. This requires the BASIC
program to activate the motor’s power relay and illuminate a virtual LED
on the screen.
First, you need to define two constants. The first is the reference number for the switch control and the second is the reference for the on-screen
LED control:
Then you would create the controls
as follows:
GUI SWITCH PwrSwitch, c$, x, y, etc
GUI LED RedLED, c$, x, y, etc
Next, the main program should set
siliconchip.com.au
up the GUI interrupt and initiate a
never-ending loop:
GUI INTERRUPT IntTouchDown
DO : LOOP
The interrupt subroutine would
look something like this:
Sub IntTouchDown
SELECT CASE TOUCH(REF)
CASE PwrSwitch
PIN(1) = CTRLVAL(PwrSwitch)
CTRLVAL(RedLED) = CTRLVAL(PwrSwitch)
END SELECT
END SUB
In the above code fragment, we assume that the motor’s relay is connected to pin 1. The CTRLVAL() function will get the state of the switch (0
for off and 1 for on) and copy that to
pin 1 which will control the relay (ie,
“1” means close the relay). We also get
that value a second time and apply it
to the on-screen LED so that it will reflect the state of the motor.
GUI programming
This concept of handling on-screen
activity within an interrupt is common
in GUI (graphical user interface) programming but it may be unfamiliar to
newcomers.
Conventional programs start by setting everything up, then doing something and then ending. GUI programming is different and this is because it
is the user who is in control of the program flow, not the program. The user
might touch this control or that; there
is no predicting which control will be
siliconchip.com.au
touched next and a linear program is
not the ideal way to handle this.
In a GUI environment, the program
should set everything up and wait to
see which control the user touches.
When the user does touch a control,
the appropriate action can be taken
and when that action is complete, the
program should resume waiting. This
can be referred to as an “event-driven
program”.
The exception is when there is some
lengthy processing that must be done
as soon as a control is touched. When
an interrupt occurs, MMBasic will
only run the program in the interrupt
subroutine and that means that other
interrupts and the main program are
blocked. This is fine when the interrupt action is quick (say, less than a
millisecond) but if it is lengthy (say,
over 100 milliseconds) the effect could
be disastrous, as the main loop will
freeze while the interrupt is processed.
For example, when a button is
touched, you might want your program
to send a message to some other item
of equipment. Sending a message over
a communications link can take some
time (eg, half a second) and if that was
done within the interrupt routine, it
would appear to the user that the program has frozen for this time. Also, if
the main loop is performing any realtime tasks, such as monitoring a motor and controlling its speed, the fact
that this is not occurring for a significant amount of time could cause real
problems.
To avoid this, the interrupt subroutine can set a flag which will let the
main program loop know that an action is required. Setting a flag means
that the program will write a value
into a variable which can then be recognised in another part of the program
as a signal to do something. The main
loop can then handle that flag in any
way it requires. For example, if the
flag indicates that a message is to be
sent, that message could be sent one
character at a time while the main loop
continues to run, so that it is not interrupted for a long period.
Here is an example of how you could
tackle the above requirement. You first
define the flag, then set up the button
and the touch-down interrupt:
DIM CommFlag = 0
CONST ButtonRef = 42
GUI BUTTON ButtonRef, c$, x, y, etc
GUI INTERRUPT IntTouchDown
The interrupt would simply set the
flag whenever the button is touched:
Sub IntTouchDown
SELECT CASE TOUCH(REF)
CASE ButtonRef
CommFlag = 1
END SELECT
END SUB
The main program loop would then
monitor this flag:
DO
IF CommFlag = 1 THEN
. . . code to send the message . . .
CommFlag = 0
ENDIF
LOOP
Note that it is the responsibility of
the main program to reset the flag so
that it then can detect when another
message must be sent. If you want the
code to avoid blocking the main loop
while sending the message, you could
increment CommFlag as each byte is
sent and only reset it to zero at the end.
If doing this, the interrupt subroutine could disable the button (using the
GUI DISABLE command) until the message has been sent. It could then be reenabled when the flag is reset, to avoid
interrupting a message when transmission has already begun.
Touch-up interrupt
In most cases, you can process all
user input in the touch-down interrupt. But there are always exceptions
and a typical example is when you
need to change the characteristics of
the control that is being touched. For
example, you might want to change
the foreground colour of a button from
white to red when it is “down”. When
it is returned to the “up” state, the colour should revert to white.
Setting the button colour when it
is pressed is easy. Just define a touchdown interrupt and change the colour
in the interrupt handler routine when
that control is touched. However, to
return the colour to white when it is
released, you need to detect when the
touch has been removed from the control (ie, touch-up). This can be done
with a touch-up interrupt.
To specify a touch-up interrupt, you
add the name of the subroutine for
this interrupt to the end of the GUI INTERRUPT command. For example:
GUI INTERRUPT IntTouchDown, IntTouchUp
Within the touch-up subroutine,
December 2016 75
ated with a specific page, you use the
following command immediately before the controls are created:
GUI SETUP nn
where “nn” is the page number that is
being set up. In the following example, page 1 has two controls and page
3 has two different controls:
GUI SETUP 1
GUI SWITCH 41, c$, x, y, etc
GUI LED 42, c$, x, y, etc
GUI SETUP 3
GUI CHECKBOX 43, c$, x, y, etc
GUI CHECKBOX 43, c$, x, y, etc
will hide all the controls used for page
1 and reveal (un-hide) all the controls
associated with page 2.
You can have up to 32 pages, ranging from page 1 to page 32 and you can
display two or more pages at the same
time. For example,
When a program starts up, the set-up
page will default to page 1. This means
that if you do not use the GUI SETUP
command, all GUI elements will be
associated with page 1. Also, the page
to be displayed will default to page 1
so your program will run perfectly as
a single-page application.
Note that the control reference numbers must be unique across all controls,
regardless of what page they are on.
It is also perfectly legal for the program to change the characteristics of
a control on a page which is not displayed. When that page is eventually
displayed, the control will be drawn
with its new characteristics.
For example, a hidden control might
be on a page that's not active. If that
page was selected for display, the control will still be hidden when the page
is shown. However, the BASIC program can un-hide that control even
if the page is not displayed and then,
when the page is subsequently selected for display, the control will be visible and active.
PAGE 1, 5
Message boxes
Fig.6: the Micromite Plus can drive an LCD panel with up to 800x480 pixels in
true (24-bit) colour as demonstrated by this image. It was loaded from the SD
card using the LOAD IMAGE command. The speed of loading is not super fast
so you would not use this as a photo frame but it is useful for loading logos and
background images.
you can use the same structure as in
the touch-down subroutine but you
need to find the reference number of
the last control that was touched. This
is because no control is currently being touched. To get the number of the
last control touched you need to use
the TOUCH(LASTREF) function.
The following example shows how
you could meet the above requirement
and implement both a touch-down and
a touch-up interrupt:
SUB IntTouchDown
SELECT CASE TOUCH(REF)
CASE ButtonRef
GUI FCOLOUR RGB(RED), ButtonRef
END SELECT
END SUB
SUB IntTouchUp
SELECT CASE TOUCH(LASTREF)
CASE ButtonRef
GUI FCOLOUR RGB(WHITE), ButtonRef
END SELECT
END SUB
Switching screen pages
Most GUI interfaces will have a
number of screens or pages that will
be displayed for the user.
For example, there may be a main
screen which is first displayed but
when the user touches a button labelled “Options”, the screen switches
to another display which allows the
user to set various options.
76 Silicon Chip
With the Micromite Plus, this can be
easily achieved using the PAGE command. For example,
PAGE 1
will hide all controls currently on the
screen and show all the controls associated with page 1. Similarly,
PAGE 2
will display both pages 1 and 5. This is
useful if you have a set of controls that
is common on a number of screens;
this set can be defined on one page
and that page’s number then used in
the list for each page switch.
To define which controls are associ-
YouTube Video
The author has produced a video
which describes and demonstrates
the capabilities of the Micromite
Plus. You’ll find it at:
https://youtu.be/j12LidkzG2A
One very useful GUI function is
MSGBOX(). This will display a dialog
box in the centre of the screen, with
a message and up to four customisable buttons.
When it is invoked, the message box
will wait for the user to touch one of
the buttons, then return with the number of the button touched. At the same
time, MMBasic will redraw any controls that were obscured by the box.
The syntax of the function is as follows:
MSGBOX(caption$, b1$, b2$, b3$, b4$)
All the arguments are strings and
caption$ is the message to be displayed
siliconchip.com.au
Fig.7: one of the more powerful controls is the NUMBERBOX which is an onscreen box that can hold a number. When it is touched, a number pad will
appear, allowing the user to enter any number, including floating point numbers,
using scientific notation. Displaying the number pad and entering the number are
both done without involving the main BASIC program which can continue with
other duties, such as monitoring sensors and other inputs.
in the centre of the box. Multiple lines
can be displayed by inserting the “|”
(pipe) character into the caption where
the new line is to start.
b1$ and b2$ etc are the captions for
the various buttons. The number of
buttons displayed is determined by the
number of captions specified.
The following is an example of how
this function could be used:
IF MSGBOX("Start Failed","Cancel","Retry") = 0 THEN
GOTO EXIT
ELSE
GOTO RETRY
ENDIF
When run, the MSGBOX function
would display a box containing the
words “Start Failed” and two buttons
labelled “Cancel” and “Retry”. The
user will be forced to select either button and when this is done, MMBasic
will restore the display to normal and
return the number of the button to the
BASIC program.
Mixing GUI controls
with general graphics
You may be tempted to mix the general graphics commands (CIRCLE, BOX,
etc) with GUI controls but the best advice is simply don’t do it.
When you use the GUI controls,
MMBasic keeps track of where they are
on the screen and their state (ie, visible, hidden, etc) and it will use this
information while it is managing the
screen. For example, when the user
touches a text box, MMBasic will disable all GUI controls on the screen and
display them in dull colours. MMBa-
sic will then draw the QWERTY keyboard (to enable user input) over these
controls.
The reason that MMBasic disables
these controls, by the way, is to indicate to the user that the on-screen
keyboard is the only active part of
the screen.
On the other hand, MMBasic does
not track the location and state of any
general graphics commands and they
will not be dimmed. Even worse, they
may be partially overwritten by the onscreen keyboard and when the user has
finished with the keyboard, MMBasic
will not redraw them.
You should therefore use either
the general graphics commands (CIRCLE, BOX, etc) or the GUI controls but
not both on the same screen display.
The one exception is the clear screen
command (CLS) which will first run
through the GUI table and set any visible GUI controls to hidden before it
then clears the screen.
If you do want to mix the two types
of graphics commands, you should intercept the touch-down and touch-up
interrupts for any text box and number
box controls. This will indicate that a
virtual keyboard has been displayed or
removed. During these interrupts, you
could then redraw any general graphics that you may have used.
Similarly, you should redraw these
graphics immediately after the MSGBOX() function has been used, as it
will have also overwritten parts of
the screen.
Finally, MMBasic for the Micromite
Plus will be improved and updated
into the future, with new features already planned. To access these updates and other information relating
to the Micromite Plus, please check
the author’s website at geoffg.net/
SC
micromite.html
The Australian Arduino experts!
Tronixlabs is owned and operated by Arduino experts including "Arduino Workshop" author John Boxall
Check out our wide range of quality Arduino and compatible boards, modules, and so much more!
Order online • Visit tronixlabs.com.au/arduino
support<at>tronixlabs.com • $5 flat-rate delivery Australia wide! • Latest updates on twitter - follow <at>tronixlabs
siliconchip.com.au
December 2016 77
|