This is only a preview of the August 2025 issue of Practical Electronics. You can view 0 of the 80 pages in the full issue. Articles in this series:
Articles in this series:
Articles in this series:
Articles in this series:
Articles in this series:
Items relevant to "180-230V DC Motor Speed Controller, part two":
Articles in this series:
|
Max’s Cool Beans
By Max the Magnificent
Weird & Wonderful
Arduino Projects
Part 8: in which we handle switches like heroes
A
s usual, I have a cornucopia of
cool concepts to convey, and
I’m struggling to decide where
to start. Now that I think about it, I recently came across some interesting
nuggets of knowledge regarding the
expression of integer values in different bases, so let’s kick off there.
2B or not 2B
Currently, in the standard C programming language, integer values, such as
literals and constants, can be written
using either decimal or hexadecimal
notation; other bases, such as binary,
are not supported by default. Decimal values are expressed as standard
numbers written without any prefix.
For example:
int x = 42;
By comparison, hexadecimal values
are prefixed with 0x or 0X (I prefer the
former). For example, the hexadecimal
equivalent of 42 in decimal could be
expressed as follows:
int y = 0x2A;
In the case of the C++ programming
language, the 2014 release (known as
C++14) introduced support for binary
int z = 0b101010;
As fate would have it, the Arduino’s
integrated development environment
(IDE) uses a combination of C and C++.
As a result, the current version of the
IDE supports decimal, hexadecimal
and binary notations.
Don’t get bitten by a byte
The original C language featured
four integer data types: char, short,
int and long. We’ve discussed these
before. They can all be modified with
the signed or unsigned keywords. The
short, int and long types are signed by
default; that is, signed is implied if this
keyword is omitted.
In contrast, the char type is special.
It’s always eight bits (one byte) in size,
but it may be signed or unsigned by
default, depending on the compiler
being used. As you can imagine, this
has resulted in countless problems
when programs are ported from one
machine (and its compiler) to another.
When the Arduino was launched
in 2005, its creators aimed to make
SRCLR
SRCLK
b
1
16
VCC
c
2
15
a
d
3
14
SER
SER
e
4
13
OE
RCLK
f
5
12
RCLK
g
6
11
SRCLK
h
7
10
SRCLR
GND
8
9
h’
(a) Package
values that are prefixed with 0b or 0B
(I prefer the former). This means that
the binary equivalent of 42 in decimal
can be expressed as follows:
0 1 2 3 4 5 6 7
h’
0 1 2 3 4 5 6 7
OE
Connection
No connection
a
b
c
d
e
f
(b) Block diagram
Fig.1: the 8-bit 74HC595 SIPO shift register IC configuration.
54
Shift
register
g
h
Output
register
things easy for beginners to grasp. As
part of this, they added a new integer data type called byte, which is an
unsigned 8-bit integer that can hold
values in the range 0 to 255 in decimal. In reality, ‘under the hood’, byte
is an alias for unsigned char (or, more
technically, uint8_t).
The Arduino’s documentation also
states that we can use binary values
in the form Bxxxxxxxx (only uppercase ‘B’ is allowed). I must admit that
I always wondered why we are obliged
to specify exactly eight bits.
For example, if we wish to specify
a 6-bit binary value of 101010, then
using an 8-bit B00101010 (with two
leading zeros) will work, while using
a 6-bit B101010 (without the leading
zeros) will cause the compiler to ‘throw
a wobbly’ (flag an error).
It turns out that, to support this capability (‘under the hood’ once again),
the Arduino’s cunning creators include
a suite of definitions like the following:
#define B00000000 0
#define B00000001 1
#define B00000010 2
#define B00000011 3
…
#define B11111110 254
#define B11111111 255
So, when working with the Arduino,
you can use the byte data type along
with Bxxxxxxxx values if you wish.
However, if you want to make your
programs portable or if you plan on
using other microcontrollers (and their
compilers) in the future, stick to the
fixed-width data types (int8_t, uint8_t,
int16t, uint16_t etc) and the 0b prefix
for binary values.
Setting the scene
As you may recall, as part of our general experiments in the January 2025
Practical Electronics | August | 2025
Practical Electronics | August | 2025
Bouncy, bouncy
In our June 2025
SRCLR
SRCLR
column, we added To shift
From
SRCLK
three momentary register SRCLK
Switches
pushbutton switches
RCLK
RCLK
to our breadboard and
used them to directly
drive the 74HC595’s
SRCLR, SRCLK, and
RCLK inputs. Almost
immediately, we ran
DIGITAL I/O (PWM ~)
into switch bounce
problems.
Arduino
As we discussed at Fig.2: debouncing switches with the Arduino.
that time, when we
activate or deactivate an electrome- I’ve stated 6ms is that several years ago,
chanical switch, it rarely executes a one of my friends, an acknowledged
single, clean transition. Instead, it can expert in embedded system design,
bounce up to 100+ times over a period performed extensive tests on a wide
variety of switches, finding an averas long as six milliseconds (6ms).
Switch bounce isn’t a concern if age bounce time of around 2ms, and a
we’re doing something simple, such maximum bounce time of around 6ms.
As we discussed in last month’s
as using a switch to control LED(s), because the bounces are faster than we can column how, in less critical deployperceive. However, it can cause prob- ments, such as those in consumer eleclems when using the switch to drive tronics, designers often assume that
high-speed digital logic, such as our worst-case bounce durations are as
shift register or the input to a micro- low as 1ms, depending on the switch
type and the application.
controller, because each bounce can
Does pressing a button on your
be perceived as a legitimate switchremote control sometimes cause your
ing event.
In our case, pressing the SRCLK TV to jump multiple channels? Now
switch caused the shift register to be you know why!
In the case of embedded systems
clocked multiple times.
There are various methods for de- and microcontroller applications, debouncing signals from switches, with signers typically assume a worst-case
both hardware and software techniques. bounce durations of 10ms. Meanwhile,
The solution we adopted was to feed designers of mission-critical and safethe outputs from the switches into our ty-critical systems tend to be conservaArduino, debounce the switches in tive, so they assume worst-case bounce
durations of 20ms.
software, and then feed the debounced
In my own debounce solutions, I
versions to the shift register (Fig.2).
We began by treating each switch in- assume the actual real-world bounce
dependently. Every time we pressed the duration to be no more than 6ms, but I
SRCLR or SRCLK switches to modify usually design to accommodate longer
the contents of the shift register, we
bounce durations ‘just in case’.
had to press the RCLK switch to copy
these contents into the output register. But what about…
There are many shortfalls in our curTo prevent our switch-pressing finger
from becoming fatigued, we modi- rent ‘cheap and cheerful’ switch hanfied our code to automatically add an dling solution. Let’s start with the lowRCLK pulse following an SRCLR or hanging fruit. As we’ve discussed in
SRCLK event.
previous columns, the 74HC595 doesn’t
You can download a copy of the contain any special initialisation logic.
latest and greatest version of our cur- This means it can power up containrent switch debounce code and see ing a random collection of 0s and 1s.
the current state of our breadboard in
Thus far, we’ve addressed this conthe files named CB-aug25-code-01.txt cern by commencing every experiment
and CB-aug25-brd-01.pdf, respectively. by hand-clearing the device. The great
As usual, all files mentioned in this thing about having the Arduino in the
column are available as part of the
circuit is that we can tweak our setup()
August 2025 download package from function to do this for us.
the PE website at https://pemag.au/
Another problem with our current
link/ac6f
code is that it assumes that all the
pushbutton switches will be in their
They bounce how long?!
inactive states when power is first apSwitch bounce is a contentious topic. plied to the system. In the real world,
Many references state that the bouncing it’s not beyond the bounds of imaginalasts no longer than 1ms. The reason tion that the user may press and hold
~10
~9
8
7
~6
~5
4
~3
2
TX-1
RX-0
column, we populated a breadboard
with an eight-segment light-emitting
diode (LED) bar graph display, along
with current-limiting resistors. At that
time, we drove all the display segments directly using eight of our Arduino Uno R3 microcontroller’s digital
input/output (I/O) pins.
Later, in the June 2025 issue, we
added an eight-bit serial-in, parallelout (SIPO) shift register in the form of
a 74HC595 integrated circuit (IC). We
are using a breadboard-friendly dualin-line package (DIP) version, as illustrated in Fig.1(a).
As per its data sheet (https://pemag.
au/link/ac1o), the eight primary outputs from this device are named a to
h (or QA to QH). However, we can also
think of them as being numbered 0 to
7. We moved to using these signals
to drive the segments on our display.
In last month’s column, we utilised
our 74HC595 with a 2-input XNOR
function (which we created using a
2-input XOR and a NOT gate) to implement a linear feedback shift register (LFSR), which generates a pseudorandom sequence of values. We will
return to this in next month’s column;
for now, we have other fish to fry.
As illustrated in Fig.1(b), the
74HC595 actually contains two 8-bit
registers: the shift register itself and
an output register.
In addition to the SER (serial data)
input, this device has four control signals. However, we are currently using
only three of these because we are connecting the OE (output enable) input
to ground, permanently enabling the
outputs.
Regarding the remaining control
signals, the active-low SRCLR (shift
register clear) input, which is level-
sensitive, clears all the shift register
bits to 0. By comparison, a rising edge
(a 0-to-1 transition) on the positiveedge-triggered SRCLK (shift register
clock) input performs a 1-bit shift on
the current contents of the shift register.
As part of this, the value being presented to the SER input is copied into
bit 0; the original contents of bit 0 are
copied into bit 1; the original contents
of bit 1 are copied into bit 2, and so
on. We can visualise the original contents of bit 7 as conceptually ‘falling
off the end’.
Once we’ve changed the contents
of the shift register via its SRCLR or
SRCLK inputs, we need to apply a
rising edge (a 0-to-1 transition) to the
positive-edge-triggered RCLK (output
register clock) input. This causes the
current contents of the shift register
to be copied into the output register,
thereby making them visible to the
outside world.
55
one or more switches prior to powering up the system, so it behooves us
to take that into account.
Mission-critical and safety-critical
systems must be as foolproof as possible. Of course, we should never forget
what the late, great Douglas Adams
wrote about this: “A common mistake
that people make when trying to design
something completely foolproof is to
underestimate the ingenuity of complete fools.”
As a counterpoint to this, when I
give a lecture on embedded systems,
one of the points I usually make is,
“Half of the safety systems we build
into a system are there to tell us when
the user has turned the other half off.”
We have multiple options here. For
example, on power-up, in our setup()
function, we could have the Arduino
to read the current states of the switches and write these values to our shift
register.
Another possibility – one we might
consider using in the retro game console we are building – would be to
enter a special configuration mode if
we detect that one of our switches is
active when the system is powered on.
And another alternative would be to
simply wait for all the switches to be
in (or return to) their inactive states
before proceeding to the main body
of the program.
Sad to relate, though, that we still
haven’t addressed the main problem,
which is that…
We live in a beautiful noisy world
When we introduced our initial
switch debounce solution in the June
2025 issue, I wrote, “In an ideal world,
starting from the switch being in its inactive state, the Arduino would detect
the leading edge of the activation and
cause its output to the shift register (SR)
to respond immediately.” This is essentially how we implemented things.
However, I also coyly noted that,
“This approach may not be quite as
ideal as we might suppose.”
Consider our current approach, as
illustrated in Fig.3(a). As soon as we
see the first edge of what we assume
to be the start of a switch transition on
the SRCLK signal from the switch, we
generate a pulse on the SRCLK signal
to the shift register (we follow this with
a pulse on the RCLK signal to the shift
register, but we aren’t showing that
here to keep things simple).
If you examine our code, you’ll see
that we then use a delay(10) statement
to wait 10 milliseconds, thereby allowing any bouncing to occur before we
return to checking the switch. 10ms
is a nice round number, and it’s greater than the 6ms worst-case bouncing
56
we might expect
to see.
Bounce
The delay()
SRCLK
function is blockfrom SW
ing, which leaves
10ms
SRCLK
the Arduino twidto SR
dling its metaphorical thumbs; that
(a) Trigger on first edge in a noise-free world
is, it prevents us
from doing anyNoise
Bounce
thing useful, such
SRCLK
as reading new
from SW
values from the
Arduino’s inputs,
SRCLK
performing comto SR
putations, and
(b) Trigger on first edge in a noisy world
driving new values
to its outputs.
The Arduino
Noise
Bounce
Uno R3 we are
SRCLK
using in these exfrom SW
> 6ms
periments runs at
16MHz (16 milSRCLK
lion clock cycles
to SR
per second). This
(c) Trigger > 6ms after last bounce
means that doing
nothing for 10ms
Fig.3: strategies for addressing noisy digital signals.
equates to 160,000
clock cycles that could be employed days) before someone presses the
switch.
to do something useful.
So, what we’re going to do, as ilHowever, the real problem with our
existing solution is that it doesn’t ac- lustrated in Fig.3(c), is wait until the
count for noise in the form of electro- signals from our switches have been
magnetic interference (EMI). This can steady for more than 6ms following
originate from natural sources, such the last bounce (thereby ensuring that
as lightning, or human-made sources, we’ve really and truly stopped bouncing) before taking any action. This also
including radio transmitters or motors
means that brief pulses from EMI or
switching on and off.
With our program responding to the other sources of electrical noise will
first active edge on the SRCLK signal, be ignored.
consider what would happen if noise
were present on this signal, as depict- Tortuous terminology
Before we proceed, there’s someed in Fig.3(b). That’s right, our current
code would ‘jump the gun’, as it were. thing else that deserves mention. The
This would be, ahem, unfortunate if SRCLK input to our shift register is desomeone were using this code as part scribed as “edge-triggered” because all
of something like a missile launch the action occurs at an edge or transition. In this case, we’re talking about
control system.
To be honest, EMI is probably not positive edges in the form of 0-to-1
something we need to worry about transitions.
By comparison, the SRCLR input to
in the case of our home-based experiments, but it is a significant concern our shift register is said to be “levelin mission-critical and safety-critical triggered” (or “level-sensitive”) because
applications such as industrial automa- it’s the active level of this signal that’s
tion, medical devices, and aerospace. important. In the case of our shift regAnd, of course, one of our goals in ister, if the SRCLR input is low state,
these columns is to learn and apply the SRCLK input will have no effect
‘best practices’, enabling the knowl- on the register’s contents.
A ‘synchronous system’ is a digital
edge gained to be deployed in realsystem in which a shared clock signal
world applications.
It may be worth reminding our- coordinates all operations. This signal,
selves that the timing relationships typically in the form of a square(ish)
in the waveforms depicted in our il- wave, provides a regular timing referlustrations, such as Fig.3, are not to ence, and all changes in the system’s
scale. For example, we might take a state occur in sync with the clock’s
coffee break or go for a long walk be- edges.
In our case, even though we’re using
tween adjacent presses of our SRCLK
switch, which means the noise could a hand-pressed switch to instigate our
occur seconds, minutes, hours (even clock signal, this input still defines
Practical Electronics | August | 2025
Inactive
Noise
Active
Bounce
Inactive
Noise
Bounce
SRCLK
from SW
Edge-triggered
If we decide that we want to do
something on the inactive edge
SRCLK to SR
> 6ms
> 6ms
RCLK to SR
SRCLR
from SW
Level-sensitive
If we decide that we want to do
something when it goes inactive
SRCLR to SR
> 6ms
> 6ms
RCLK to SR
Fig.4: our newer and more flexible method for handling signals from bouncing switch contacts.
the synchronous domain of the shift
register. That is, whatever happens
only on a clock edge is synchronous
to that edge, even if the clock is erratic
or driven by a human.
Meanwhile, the SRCLR input, being
level-sensitive and immediate, is said to
be ‘asynchronous’ to that clock domain.
That is, it takes effect independently
of the clock.
Switch handling revisited
Our original switch-handling implementation primarily focused on
what would happen when our switches were pressed, but we didn’t spend
much time worrying about what to do
when they were released. This wasn’t
a problem in this case, but some systems may require us to support different actions when our switches are
pressed and released.
Bearing all this in mind, let’s create
a solution capable of handling all the
cases illustrated in Fig.4.
For this portion of our discussions,
let’s assume our control signals power
up in their inactive states. Let’s start
with the SRCLK signal coming from
the switch. Our routine will ignore any
noise on this signal, responding only
to an active edge when we know the
signal has stopped bouncing (ie, it is
stable for more than 6ms).
At this point, it will first apply a
positive-going pulse to the SRCLK
signal feeding the shift register, followed by a positive-going pulse on the
RCLK signal feeding the shift register.
Additionally, we will design our routine to allow us to perform actions on
this signal’s inactive edge if needed
in the future.
Practical Electronics | August | 2025
Next, consider the SRCLR signal
coming from the switch. In our original code, we used this to first apply
a negative-going pulse to the SRCLR
signal feeding the shift register, followed by a positive-going pulse on the
RCLK signal feeding the shift register.
We could follow the same approach
in our new routine. However, since I
want to demonstrate alternative ways
of doing things, I’ve decided that we
will treat the SRCLR signal feeding
the shift register as a debounced version of the SRCLR signal coming from
the switch.
Counters vs shift registers
Let’s take a step back and contemplate things at a high level of abstraction. What we really want is a single solution that can be used to debounce any
of our switches, regardless of whether we wish their signals to operate in
an edge-triggered or level-sensitive
manner. Our solution should be capable of handling both positive and
negative edges, as well as both activelow and active-high signals.
As part of this, we will monitor
the signals from our switches, wait
for them to transition from one state
to another, then wait for 16ms after
the final bounce before performing
any actions.
Why 16ms? Well, it’s significantly longer than the worst-case 6ms of
expected switch bounce; it sits comfortably between the 10ms commonly used in embedded systems and the
20ms typical for mission- and safetycritical applications; and it integrates
well with our implementation strategy,
as discussed below.
A very common software technique
is to use a loop that executes periodically (say, once every millisecond) in
conjunction with a counter.
Suppose a signal from a switch starts
off low. In this case, all we need to do
is wait for it to be continuously high
for 16ms. One way to achieve this is
to initialise our counter with a value
of 16. Every time our loop executes,
we will check the state of the switch.
If it’s high, we decrement the contents
of the counter; if it’s low, we reload the
counter with a value of 16.
When the counter reaches 0, we
know that the signal has been steadily high for 16ms. Once we know the
signal has transitioned to a stable high
level, we can perform any required actions, then start waiting for it to transition low again.
Once again, we will initialise our
counter with a value of 16. Once again,
every time our loop executes, we will
check the state of the switch. In this
case, if it’s low, we decrement the contents of the counter; if it’s high, we
reload the counter with a value of 16.
When the counter reaches 0, we know
that the signal has remained steadily
low for 16ms.
Another approach involves utilising
a software shift register; this is what
we will do. We will use a 16-bit (twobyte) variable to store the recent ‘history’ of our shift register, where these
16 bits correspond to 16 readings over
16ms (ie, one per millisecond).
Suppose the signal from our switch
starts off low. We can initialise our shift
register with 0x0000 in hexadecimal
(which is equivalent to sixteen 0s in
binary). Every time our loop executes,
57
If all 1s
WFO
sary actions and then transitions the
switch to its WFO state.
JR
If not
all 1s
Names of states
If not
all 0s
WFZ
WFO = WAITING_FOR_ONES
JR = JUST_RISEN
WFZ = WAITING_FOR_ZEROS
JF = JUST_FALLEN
Names of functions
JF
UpdateSwitches()
ProcessSwitches()
If all 0s
Fig.5: a state machine view of our switch handling solution.
we shift the register one bit to the left
and then insert the current value (low
= 0, high = 1) from the switch into the
least significant bit.
When the shift register contains
0xFFFF (which is equivalent to sixteen
1s in binary), we know that the signal
has been steadily high for 16ms. Once
this happens, we can perform any required actions, then start waiting for
it to transition back to all zeroes. In
this case, we know the shift register
already contains all 1s, so no initialisation is required.
As before, every time our loop executes, we shift the register one bit to
the left and then insert the current 0 or
1 value from the switch into the least
significant bit. When the shift register
contains 0x0000, we know that the
signal has been steadily low for 16ms.
Did you see that?
You may be thinking that 22ms –
the sum of our 6ms worst-case bounce
scenario and our 16ms delay after
the final bounce – is a long time to
wait for something to occur after we
press a switch.
It’s true that the human perception
of delay in response to pressing a
button can be surprisingly sensitive.
However, there’s a practical threshold below which most people won’t
consciously notice a delay.
A delay under 10ms is typically
imperceptible to humans. A delay
of 10-30ms may be slightly noticeable to very sensitive users, especially in contexts that require fast feedback (eg, gamers or musicians), but
it generally still feels instantaneous.
A delay of 30-100ms becomes noticeable to the average person, who may
describe the system as feeling “laggy”
or “slow to respond”.
Most users will perceive any delay
greater than 100ms, feeling the system
is “sluggish” or “unresponsive”. So,
as a rule of thumb, we should aim
to keep any button-to-action delays
under 50ms. However, don’t take my
word for any of this, as this is a test
58
you can easily perform for yourself
using your Arduino, pushbuttons
and LEDs.
Getting into a state
As previously discussed, we will
employ a software-based switch debounce approach, using a shift register as part of our overall switch-
handling solution.
The easiest way to implement our
switch handling scheme is as a state
machine (Fig.5). Each of our switches
will have a corresponding state machine, and each state machine will
have four states: WFO (waiting for
ones), JR (just risen), WFZ (waiting
for zeros), and JF (just fallen).
If the signal from a switch starts off
at 0 when the system is powered up,
we will load its corresponding shift
register with 0x0000 and initialise its
state machine to the WFO state. Contrariwise, if the signal from a switch
starts off at 1, we will load its shift
register with 0xFFFF and initialise
its state machine to the WFZ state.
Every time we cycle around our
1ms loop thereafter, we will call
two functions. First, we will call the
UpdateSwitches() function, followed
by the ProcessSwitches() function.
For each switch, the Update
Switches() function shifts that switch’s
register one bit to the left and adds
the current state of the switch to the
least-significant bit of the register.
Next, it performs a test. If the switch
is currently in its WFO state and the
shift register contains all 1s, it will
transition to the JR state. Alternatively, if the switch is currently in
its WFZ state and the shift register
contains all 0s, it will transition to
the JF state.
Now, this is the clever bit (well,
I think so). For each switch, the
ProcessSwitches() function checks
to see if it’s in its JR state, in which
case it performs any necessary actions
and then transitions the switch to its
WFZ state, or if it’s in its JF state, in
which case it performs any neces-
Consider the code
Remembering that I’m a rough-andtough hardware design engineer (not
a weedy software weenie), I must
admit that I’m rather proud of this
solution, which you can peruse and
ponder in the file named CB-aug25code-02.txt. I won’t wear out my welcome by walking through this code
line by line. Instead, we’ll just hit
some of the highlights.
First, we define a constant called
TICK and assign it a value of 1; this
will control our 1ms loop. We also
create a bunch of definitions for the
names of our control signals, the
names of our state machine’s states,
and the values we will use to preload
and compare our switch-related shift
registers (ALL_ZEROS is defined as
0x0000, while ALL_ONES is defined
as 0xFFFF).
The first part that’s a little different
from what we’ve seen before occurs
on line 30, as illustrated in Listing
2(a). Remember that we are using the
convention that the listing number
(2 in this case) corresponds to the
numerical part of the matching code
file name (“02” here).
This code snippet defines a new
type that combines two related pieces
of data: swData (switch data), which
will serve as our 16-bit shift register,
and swState (switch state), where
we will store the current state of the
switch. The name of this structure
is SwStuff.
We will use this new type to store
the data associated with our state
machines. As a reminder, we introduced the concepts of struct (short
for “structure”) and typedef (short
for “type definition”) in our Coding
Tips and Tricks discussions in the
December 2020 issue.
Later, on line 47, we declare an array
of type SwStuff called Switches[], as
illustrated in Listing 2(b). In this case,
NUM_SIGNALS (the number of control signals, which corresponds to the
number of switches) has been defined
as 3, but this can be easily changed to
accommodate more or fewer switches.
Now let’s consider the setup() function. After configuring input and
output pins and setting the signals
to the shift register to their inactive
states, we clear the shift register by
pulsing its SRCLR signal, followed
by its RCLK signal.
Next, we initialise the state machines associated with the switches by loading the shift register and
setting the state for each switch. We
then wait until all the switches are
Practical Electronics | August | 2025
Listing 2(a): defining a new type.
Listing 2(b): using our new type.
Mad max, ready to power up the Arduino!
I know we’ve wandered a little off
the beaten path, but we are still working toward building our retro games
console, as seen in Photo 1.
Of particular interest here are the
eight pushbutton switches mounted
on the front panel: four white direction control switches and four coloured action control switches. Each
of our games will use these switches
in different ways, and our latest-andgreatest switch-handling solution can
be easily enhanced to address this.
Listing 2(c): the main loop() function.
Next time
in their inactive states. If any of the
switches are in their active states,
we flash the Arduino’s onboard LED
(the one attached to pin 13) until they
are returned to their inactive states.
You can test that this works by
pressing one or more of the switches
while the Arduino is powered up.
The loop() function is easy peasy
lemon squeezy, as illustrated in
Listing 2(c).
The Arduino’s millis() function
returns the number of milliseconds
since our program started running. In
this case, TimeThen is a global variable that we use to remember the last
time an action was performed, while
timeNow is a local variable that stores
the current time.
In a nutshell, once every millisecond, we first call the
UpdateS witches() function and then call the
ProcessSwitches()
function. The great
thing about this is how
things are separated out.
The UpdateSwitches()
function treats each
switch identically.
It’s only the Process
Switches() function that
is required to handle any
switch-specific actions.
We’ve already discussed what these
functions do, so I’ll leave it to you
to review the code to see how everything plays together. I suggest that
you peruse and ponder the program
while comparing it to the actions depicted in Figs. 4 and 5. Please feel
Practical Electronics | August | 2025
free to email me if you’ve got any
questions (my address is at the end
of this column).
Lastly, load this code into your
Arduino and ensure that your LFSR
continues to function as before, secure
in the knowledge that everything will
continue to work in the noisiest of
noisy environments.
Consider the console
You may be thinking that we’ve
put a lot of effort into our new switch
handling solution, not least that the
old one was ‘good enough for government work’, as they say. Still, there’s
a r e a s o n behind our madness.
Give me strength! Once again, we’ve
run out of time. There’s so much to
do and so little time. I’m too young
for all this excitement!
In our next column, we will begin
by addressing the homework problems posed in last month’s column.
After that, we will wire up our game
console switches and apply our new
switch handling code to these little
scamps.
Meanwhile, if you have any
thoughts that you’d care to share on
anything you’ve read here, please
feel free to drop me an email at max<at>
clivemaxfield.com. And, as always,
PE
have a good one!
Photo 1: an early version of our
retro games console
(Photograph by
Joe Farr).
59
|