This is only a preview of the December 2024 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 "Secure Remote Switch, Part 1":
Items relevant to "Multi-Channel Volume Control, Part 2":
|
Max’s Cool Beans
By Max the Magnificent
Arduino Bootcamp – Part 24 (it’s time to get serial)
Y
ou know how people sometimes tell you, “I’ve got good
Before we dive headfirst into the fray
with gusto and abandon, following
our previous column in the November
issue, several readers emailed me,
moaning and groaning that the puzzle
I posed is unsolvable. “Oh, ye of little
faith” is all I can say. Well, not quite;
I follow this with, “Watch and learn,
Grasshopper”.
As you may recall, and as illustrated
in Fig.1(a), our problem involves a
black box with three inputs (A, B, C)
and three outputs (NotA, NotB, NotC).
Each output is to be the logical negation
of its corresponding input, so a 0 on A
results in a 1 on NotA, and vice versa;
the situation for the other input-output
pairs is similar.
We are permitted by those who don
the undergarments of authority to
use only two NOT gates along with
as many 2- and 3-input AND and OR
gates as we wish.
There are various ways in which we
can tackle this problem. The solution
shown in Fig.1(b) was generated by
my friend George Harper. Personally, I
think George’s resolution to this conundrum is awe-inspiring in its elegance.
I should note that George is using
a pseudo-language of his own devising to represent this solution (most
computer languages won’t allow us to
start a variable name with a numerical
character, for example). Also, George
employs ‘&’, ‘|’, and ‘!’ characters in his lution is layered. Equation #3 means,
Boolean equations to represent AND, “the internal node 1one will be 1
OR, and NOT functions, respectively. if both 0or1ones is 1 and any of
This really is cool. Once we’ve speci- the ABC inputs are 1”. The physical
fied the names of our input and output implementation of this expression will
signals, we move onto the Boolean require only a 2-input AND gate and a
3-input OR gate.
equations describing the internal nodes.
Similarly, Equation #4 tells us, “the
These equations can be transferred onefor-one into corresponding logic gates. internal node 1or3ones will be 1 if
In the following discussions, “#n” 1ones is 1 or all of the ABC inputs are
refers to the line number comments 1”. The physical implementation of this
associated with the internal nodes. All expression requires only a 2-input OR
gate and a 3-input AND gate.
of this is pretty much self-explanatory.
Equation #5 is similar in concept
Remember that 0 and 1 equate to the
Boolean concepts of false and true, to equation #2. Since the only posrespectively.
sibilities are for 0, 1, 2 or all 3 of the
This means we can read equation #1 inputs to be 1, then if 1or3ones is 1
as saying, “internal node 2or3ones (true), 0or2ones must be 0 (false),
will be 1 (true) if any two or all three and vice versa. We can achieve this
of inputs ABC are 1; otherwise, it will be by inverting the output of 1or3ones
0 (false).” The physical implementa- with our second and final NOT gate.
tion of this expression requires three
Using equations #6 and #7 to gen2-input AND gates and one 3-input
erate the remaining internal nodes,
0ones and 2ones, is easy peasy lemon
OR gate.
Similarly, equation #2 tells us, “the squeezy. The physical implementation
value of internal node 0or1ones of each requires just a single 2-input
is NOT the value of internal node AND gate.
2or3ones”. Another way saying that
The key takeaway from all this is that
is, “internal node 0or1ones will be 0 if we now have three extremely useful
2or3ones is 1; otherwise, it will be 1”. signals: 0ones , 1one , and 2ones ,
An alternative approach to wrap
which tell us if none, only one, or
our brains around this
part of the problem is
A
NotA
that there are only four
Black
Box
B
NotB
broad possibilities: all
C
NotC
the inputs are 0, one of
the inputs is 1, two of
(a) The problem
the inputs are 1, or all
three of the inputs are 1.
Inputs:
Since we know that
A, B, C;
2or3ones will be 1 if
Outputs:
two or three of the inputs
NotA, NotB, NotC;
are 1, it will be 0 only if
Internal Nodes:
none or only one of the
2or3ones = ( (A & B) | (A & C) | (B & C) ); // #1
inputs are 1.
0or1ones = !(2or3ones);
// #2
1one
= 0or1ones & (A | B | C);
// #3
Since we generate
1or3ones = 1one | (A & B & C);
// #4
our 0or1ones value
0or2ones = !(1or3ones);
// #5
simply by inverting
0ones
= 0or2ones & 0or1ones;
// #6
2ones
= 0or2ones & 2or3ones;
// #7
whatever value is present on 2or3ones, the
Equations for Outputs:
implementation of this
NotA = 0ones | (1one & (B | C)) | (2ones & (B & C));
NotB = 0ones | (1one & (A | C)) | (2ones & (A & C));
expression will only reNotC = 0ones | (1one & (A | B)) | (2ones & (A & B));
quire one of our two NOT
(b) The solution
gates (“our preciouses”).
Fig.1:
proof
that
there
is
a solution to the problem!
As we can see, this so-
Practical Electronics | December | 2024
17
news and bad news, which do
you want first?” Don’t worry. I’m not
going to say that. What I will say is,
“I have happy news and sad news”.
Also, I’m not going to give you a
choice because we’ll start with the opposite of happy news, which is that this
is to be the last in our current collection
of Arduino Bootcamp columns ( ).
Meanwhile, the opposite of sad news
is that we are going to start a new
series of Weird and Wonderful Arduino
Projects, which will use many of the
techniques we’ve explored together,
including some we are about to discover
in this column ( ️). But first…
😞
☺
B or NotB, that is the question
only two of our three ABC inputs are
1, respectively. Having access to these
signals means we now have sufficient
information at our disposal to generate
our three output values.
This is where things get so clever I’m
tempted to squeak with excitement. We
want an output to be 1 only if its corresponding input is 0. Pick any output.
This output will be 1 if 0ones is 1
(meaning all the inputs are 0), or if 1one
is 1 and either of the other inputs is 1
(meaning this input is 0), or if 2ones
is 1 and both the other inputs are 1
(again meaning this input is 0). This is
reflected in our three output equations.
Every time I look at this solution, a
smile comes to my face, and I think,
“Wow! That. Is. So. Ingenious!”
Pondering our persisting poser
Do you remember the other puzzle
I posed in last month’s column? I said
to assume our four 7-segment lightemitting diode (LED) displays are only
ever going to be used to implement a
clock. Also, to assume that this clock
can support both 12- and 24-hour
modes and that we are suppressing any
leading zeros in the most-significant
hours digit (we introduced this concept
in PE, August 2024).
Although we didn’t explicitly state
it, let’s also assume that we are going
to exclude the displays’ decimal point
segments from these discussions.
Based on all these assumptions, what
would be the maximum number of segments we might see active at the same
time? And what would be the ideal
value for our current-limiting resistors
to obtain the maximum possible segment current values, assuming a total
allocation of 350mA to power all four
of our displays?
I bet this is not as easy as you think.
If you haven’t already done so, why
don’t you take a stab at this now, and
then compare it to my answer, which
I’ll present at the end of this column.
Lest we forget
As per our previous column, the
current implementation of our clock
employs four latching CD4511 BCD
(binary-coded decimal) to 7-segment
decoders. This means each of our 7-segment displays has its own BCD decoder,
each of which latches (stores) the BCD
value we wish it to display.
As usual, you can download a PDF
of our existing ensemble (named CBDec24-brd-01.pdf). And, as usual, all
the files mentioned in this column
are available from the December 2024
download page on the PE website:
https://pemag.au/link/ac1m
Since we are no longer multiplexing
the displays, they maintain a constant
18
brightness, irrespective of the number
we decide to use.
However, there are two disadvantages
to this scenario. The first is that, in
addition to the 4 ABCD signals driving
the inputs to the BCD decoders, each
decoder also requires its own LE (latch
enable) signal.
Since we are currently using four
displays, this means we are consuming 4 + 4 = 8 of our Arduino Uno’s 12
precious digital input/output (I/O)
pins. Every time we add a digit, we
will lose another pin ( ).
The second disadvantage is that BCD
decoders don’t control the dp (decimal
point) segments on 7-segment displays.
As we will come to see, these segments
can be employed to convey all sorts of
useful information.
So, if we decide we want to use our
dp segments, we will have to control
them individually, which means losing
another pin for each display. In the
case of our current 4-digit clock, this
means we will end up consuming
4 + 4 + 4 = 12 digital pins, leaving us
with no pins available to implement
other functions, like driving a buzzer,
for example.
😞
Shift over
Happily, there is another solution
open to us. One that will potentially
allow us to drive as many 7-segment
displays as we wish—including their
dp segments—using only 3, 4, or 5 of
the Arduino’s digital I/Os, depending
on our implementation.
In an earlier column (PE, September
2024), we introduced the concept of
memory elements (or ‘registers’) called
D-type flip-flops. We also described how
these little scamps could be connected
to form a serial in, parallel out (SIPO)
shift register.
Suppose we form an 8-bit ‘bare
bones’ SIPO shift register as illustrated
in Fig.2(a). All the registers share a
common clock signal, which we’ve
called SRCLK (shift register clock).
The data input to the first register in
the chain is called SER (serial data in).
The output from each register drives
the next register in the chain.
When a rising edge (a 0 to 1 transition)
is applied to SRCLK, the value on SER
will be loaded into the first register. At
the same time, the existing contents
of the first register will be loaded into
the second, the contents of the second
register will be loaded into the third,
and so on down the chain.
One way to visualize this is as a line
of eight people passing objects to each
other when someone shouts “Now!”.
So, to load an 8-bit value into our
shift register, we first place the first a
0 or 1 on the SER input, then apply
a positive-going pulse to the SRCLK
input. We then place the next 0 or 1 on
the SER input, apply another positivegoing pulse to the SRCLK input, and
so forth until all eight bits have been
loaded.
This means we need to load the last
bit first and the first bit last, if you see
what I mean.
The outputs from all eight registers,
named QA through QH in our illustration,
are presented to the outside world,
where they can be used to drive all
sorts of things like… well, like a 7-segment display.
In our case, we are going to use the
Q A through Q G outputs to drive the
corresponding a through g segments
on our display, while the QH output
can be used to control our dp segment.
Now, this is the clever bit. Assume
our 8-bit shift register is presented and
packaged as an integrated circuit (IC).
We can daisy chain multiple devices
together to form a longer shift register.
In this case, the SRCLK signal would be
used to drive all the chips. Meanwhile,
the QH output from the first chip could
be used to drive the SER input to the
second device, and so on down the line.
Returning to Fig.2(a), observe that
we’ve taken a copy of the QH output that
we’ve called QH’. It’s this QH’ output that
we will use to drive the next device in
the chain. The reason for our doing this
will become apparent shortly.
Just to keep things simple, we will
refer to these outputs more concisely
as abcdefgh in the discussions below.
The main point to note at this stage
is that, by daisy-chaining a bunch of
these devices together, we can now
control as many displays as we wish
using only two of our Arduino’s digital
I/O pins. Pretty cool, eh?
All clear!
Suppose we decide to daisy chain
four 8-bit SIPO shift registers and use
them to drive our four 7-segment displays. Further suppose we wish to clear
the registers, loading each element
with a logic 0 value. In the case of the
bare-bones implementation shown
in Fig.2(a), we could achieve this by
presenting a 0 to the SER input of the
first register, then applying 32 clock
pulses to the SRCLK input.
Our Arduino Uno’s system clock is
running at 16MHz (16 million cycles
a second), so loading our shift register
with 0s will take only an imperceivable
fraction of a second.
However, since we often wish to clear
the contents of our shift registers, and
some applications will involve very
long shift register chains, it’s common
to provide a SRCLR (shift register clear)
input, as illustrated in Fig.2(b). It’s also
Practical Electronics | December | 2024
SER
SER
SER
SER
OE
RCLK
QA
SRCLK
QA
SRCLK
SRCLR
RCLK
QA
SRCLK
QA
SRCLK
SRCLR
SRCLR
QB
QB
QB
QB
QC
QC
QC
QC
QD
QD
QD
QD
QE
QE
QE
QE
QF
QF
QF
QF
QG
QG
QG
QH
QH
QH
QH’
QH’
(a) Bare bones
(b) With clear
(c) Registered outputs
(d) Output enable
QH’
QH’
Q
G
QH
Fig.2: four different flavours of eight-bit SIPO (serial in, parallel out) shift register.
common for this signal to be active-low,
as shown by the bar over its name.
Once again, the SRCLR signal from
our microcontroller will be connected
to all the shift registers in the chain.
our shift register to drive the abcdefg
and dp segments on our display, respectively. So, to display ‘4’, we need a
value of 01100110 in our shift register.
The problem is that we need to shift
this value in through the SER input
A fly in the soup
using eight clocks (Fig.4).
Unfortunately, there’s a fly in the
Just for giggles, let’s assume we power
soup and an elephant in the room (I up with our shift register containnever metaphor I didn’t like). Suppose ing random/unknown 0 and 1 values
we connect the outputs from our 8-bit (shown as red question marks in Fig.4).
shift register to a 7-segment display (via While we are in the process of clocking
current-limiting resistors, of course).
our new values in, our display will
Now suppose we wish to display respond as shown in Fig.5.
the number ‘4’. This requires segments
All this will take place in a fraction
ade to be turned off, while segments of a second. If you blink, you’ll miss it,
bcfg will need to be turned on as il- which means this may not be particulustrated in Fig.3.
larly important if we wish to update
Remember that we are planning on our display relatively infrequently.
using the abcdefg and h outputs from
However, if we are constantly loading new values into our
shift register, even if the
Clock
a
b
c
d
e
f
g
h
a
values are the same (like
Start ? ? ? ? ? ? ? ?
repeatedly displaying ‘4’,
#1 0 ? ? ? ? ? ? ?
f
b
‘4’, ‘4’…), we could end
g
c
e
d
dp
Fig.3: displaying a ‘4’.
#2
#3
#4
#5
#6
#7
#8
1 0 ? ? ? ? ? ?
1 1 0 ? ? ? ? ?
0 1 1 0 ? ? ? ?
0 0 1 1 0 ? ? ?
1 0 0 1 1 0 ? ?
1 1 0 0 1 1 0 ?
0 1 1 0 0 1 1 0
Fig.4: loading our 8-bit value.
Practical Electronics | December | 2024
Unknown
a
f
g
e
up with blurred, ‘ghosting’ effects on
our display (the concept of ghosting
was introduced in PE, July 2024).
The solution is to add an additional
set of D-type flip-flops as illustrated
in Fig.2(c). These output registers are
controlled by their own clock, which
we’ve called RCLK (register clock). We
are showing RCLK as being positiveedge triggered, so these registers will
be loaded when a rising edge (0 to 1
transition) is applied to RCLK.
The idea here is that the output registers will maintain whatever 0 and 1
values we last loaded into them, and
it’s these values that are presented
to the outside world. We can load a
new series of 0 and 1 values into our
shift register using 8 pulses on SRCLK
without affecting the contents of the
output registers.
Once we are ready to rock and roll, a
single pulse on RCLK will copy these
new values into the output registers
As usual, the RCLK signal from the
Off
On
b
c
d
Start
#1
#2
#3
#4
#5
#6
#7
#8
Fig.5: how the display changes while loading the new data.
19
QB
1
16
VCC
QC
2
15
QA
QD
3
14
SER
QE
4
13
OE
QF
5
12
RCLK
QG
6
11
SRCLK
QH
7
10
SRCLR
GND
8
9
QH’
Fig.6: top view of the 74xx595 shift register.
microcontroller will be connected to
all the shift registers in the chain.
Chaining all the devices using their
QH’ outputs means we can update all
their contents and copy their new
values into the output registers simulatneously.
Feeling enabled
One more feature that is commonly
found in devices like shift registers
is the ability to disable their outputs.
Consider the addition of the active-low
OE (output enable) input as illustrated
in Fig.2(d).
When this signal is in its active (0)
state, whatever values are stored in
3
We aren’t obliged to use all the features offered by this device. A minimal
implementation would involve us
using only three microcontroller pins
to drive the SER, SRCLK, and RCLK
pins on the shift register(s).
In this case, we would connect a
pull-up resistor to the SRCLR pin to
place it in its inactive state (we can
clear the register by loading it with a
string of 0s), and a pull-down resistor to
the OE pin to hold it in its active state.
Alternatively, we might opt for a
maximal implementation in which
we also use the SRCLR and OE pins,
thereby requiring us to dedicate five
pins from our microcontroller.
For the purposes of this column, I
chose to straddle the fence by using
four pins to control my shift register
chain (I decided to reserve the OE
feature for another day).
the output registers will appear on the
device’s abcdefgh outputs. In contrast,
when this signal is in its inactive (1)
state, the abcdefgh outputs will be
placed in a tristate condition. That
effectively disconnects what they are
connected to (our 7-segment displays).
The OE signal from the microcontroller will be connected to all the
shift registers in the chain. In our clock
project, we could employ this signal to
control the brightness of the displays by
using pulse-width modulation (PWM)
to turn all the outputs from the shift
registers on and off very quickly. We
introduced the concept of PWM in PE,
November 2023).
There’s a chip for that
I bet you’re wondering if there’s a
device that fulfills all the requirements
exhibited by Fig.2(d). Well, wonder no
more, because the 74HC595 ticks all
the checkboxes. I had a quick Google
while no one was looking and found
a five-pack on Amazon for £6, plus £9
shipping (https://pemag.au/link/ac1n),
but there are lots of other sources.
When we look at the data sheet for
this little rascal (https://pemag.au/link/
ac1o), we discover that they’ve used
the same pin names as us (Fig.6). What
are the odds? It’s spooky, isn’t it!
3
D3
3
D2
Clearing the decks for action
Our new circuit diagram using four
74xx595 shift registers is shown in
Fig.7. Pin 2 from the Arduino drives
the SER input to the first shift register;
the h’ output from this chip drives the
SER input to the next chip and so-on
down the line.
The h’ output from the last chip in
the chain is left dangling (unconnected);
3
Connection
D0
D1
No connection
Ground (0V)
5 10 9 1 2 4 6 7
5 10 9 1 2 4 6 7
5 10 9 1 2 4 6 7
5 10 9 1 2 4 6 7
dp g f e d c b a
dp g f e d c b a
dp g f e d c b a
dp g f e d c b a
Display segments
and pin numbers
Resistors or
Resistor Packs
h g f e d c b a
h’
74xx595 SER
h g f e d c b a
h’
74xx595 SER
h g f e d c b a
h’
74xx595 SER
h g f e d c b a
h’
74xx595 SER
*1
*1
*1
*1
*2
*3
*4
*2
*3
*4
*2
*3
*4
*1 OE
= Connected to GND (active state) via 10kΩ pull-down resistor
*2 SRCLR = Shift register clear (active low)
*3 SRCLK = Shift register clock (positive-edge triggered)
*4 RCLK = Storage register clock (positive-edge triggered)
*2
*3
*4
5
4
3 2
Arduino pin numbers
Fig.7: our new display driver circuit uses four 74xx595 shift registers.
20
Practical Electronics | December | 2024
Adding the shift registers
Once we’ve added our new
current-limiting resistors, the
next step is to add the four shift
registers as illustrated in Fig.8.
Practical Electronics | December | 2024
Fig.9: wiring the
inputs to the shift
Arduino pin
registers.
numbers
2 SER
3 RCLK
4 SRCLK
5 SRCLR
5 4 3 2
Arduino pin numbers
21
Lower Breadboard
Lower Breadboard
we can use it to add more chips
to the chain later if we wish.
The first thing we’ll do is
clear the decks for action. Make
Note: Notches on
sure your clock is powered
Pin 1
Pin 1
Pin 1
down, then remove the wires
the right-hand side
connecting Arduino pins 2
through 9 to our breadboards.
74xx595
74xx595
74xx595
74xx595
We’ll also remove any wires
connected to the inputs of the
CD4511 chips, plus any wires
connecting the CD4511s to the
four 270Ω resistor packs.
As part of this, we will
remove any components (pullup resistors and decoupling
capacitors) associated with the
CD4511s. Also, any power and Fig.8: adding the shift registers.
ground wires associated with
the CD4511s and the wires connecting It’s very important to note that we are ter to verify that you are seeing +5V
pin 1 on each of the resistor packs to rotating these chips 180° to the way we (give or take) across all the power and
ground (associated with the dp seg- previously inserted our BCD decoders. ground rail pairs on the breadboards.
ments on the displays).
This is a tricky topic. The commonly Also check that you are seeing +5V
Finally, we will remove the CD4511 accepted practice is to orientate our (give or take) between pins 8 and 16
chips and the resistor packs.
chips horizontally such that the notch/ on each chip (apply the black probe
dimple is on the left and pin 1 is located to pin 8 and the red probe to pin 16).
Resistance is futile
on the lower left-hand corner. However,
Keeping the red probe on pin 16, also
It would be wonderful if we could we are positioning our shift register check that you are seeing +5V between
keep on using our existing 270Ω resistor chips the other way round, with the pin 16 and pin 13 on each chip. Once
packs, but it was not to be. When we notch/dimple on the right and pin 1 you are satisfied that everything is
look at the 74xx595 data sheet, we see on the upper right-hand corner.
tickety-boo, power the system down
that each output can supply only 6mA.
We are doing this to make our wiring again before proceeding to the next step.
We know that the supply voltage easier (but I still feel naughty).
from the Arduino Uno is 5V and that
Add the black wires connecting pin Adding the wires
The next step is to add the wires
our red segment LEDs have a voltage 8 on these chips to the 0V (ground) rail
drop of 2V. Rearranging Ohm’s law, V and the red wires connecting pin 16 driving the inputs to the shift registers
= IR, giving R = V/I, tells us that our to the +5V (power) rail. Also add the as illustrated in Fig.9. Although these
ideal resistors would be (5V – 2V) ÷ 100nF decoupling capacitors between wires are all shown as being orange in
0.006A = 500Ω.
the power and ground rails as close to these diagrams, I personally prefer to
use different colors in my own impleThe closest standard resistor value is the red wires as possible.
510Ω (with green-brown-brown color
Add the 10kΩ pull-down resistor mentation.
For example, I started by using a long
bands), which would be great, although and the purple wires as shown. This
560Ω (with green-blue-brown color ensures that pin 13 (the OE input) to yellow wire to connect pin 2 on the
each device is in its active low state, Arduino to pin 14 (the SER data input)
bands) would also do nicely.
on the right-hand shift register. I then
Now we have a choice. We can use thereby enabling the outputs.
You can see the current state of play used a short yellow wire to connect pin
32 discrete (individual) 510Ω or 560Ω
9 (the QH’ output) on the right-hand
resistors if we have them, either plugged in the file CB-Dec24-brd-02.pdf
shift register to pin 14 (the SER input)
This would be a great time to power
directly into our breadboards, or on
four 8-resistor breakout boards (BOBs) everything up and use your multime- on the next chip in the chain.
as discussed in PE, July 2024.
Another option would be to
use 560Ω resistor packs, which is
what I’ve shown in my diagrams.
As we’ve discussed before,
these components are created
by a variety of manufacturers,
like the Bourns 4100R Series
(https://pemag.au/link/ac13),
74xx595
74xx595
74xx595
74xx595
and they are available from most
major vendors, such as DigiKey
(https://pemag.au/link/ac1p).
A quick test
Before we ‘go for broke’, let’s perform
a quick test to ensure we can talk to
our shift register chain in a language
it understands. I whipped up a little
test program, which you can find in
the file CB-dec24-code-01.txt
As you’ll see when you cast your
orbs over this code, we start with some
definitions that will ease our lives by
making our program more understandable and easier to modify in the future.
In addition to defining NUM_SEGS_
ALL to be 32 (the total number of segments in our four displays), we also
define the active and inactive values for
our RCLK, SRCLK, and SRCLR signals.
Next, we declare the names and
numbers of the Arduino pins we wish
to use to drive our shift register chain,
assigning pins 2, 3, 4, and 5 to variable
names PinSer, PinRclk, PinSrclk
and PinSrclr, respectively.
To resistors
for display D2
To resistors
for display D1
To resistors
for display D0
74xx595
74xx595
74xx595
74xx595
Fig.10: wiring the outputs
from the shift registers.
In our setup() function, we first set
all the shift register signals to their inactive values, then we pulse the PinSrclr
signal to clear the shift register chips,
after which we pulse the PinRclk
signal to copy the contents of the shift
registers into their output registers.
The portion of the code of most interest to us here is the loop() function,
shown in Listing 1. We start on line 66
when we present a logic 1 to the serial
input to the first shift register in the
chain. Then we use the for() loop
on lines 67 through 74 to repeatedly
load this value into the shift register.
Inside this loop, we first pulse the
PinSrclk signal to copy the data value
into the shift register, then we pulse
the PinRclk pin to copy the contents
of the shift registers into their output
registers. We wait for a second to give
ourselves time to see what’s happening
on our displays, then we do it all again.
We expect to see the segments on
the displays light up one after the
other, starting with a , then b , then
c… ending with dp on the first
display, followed by the other
displays in their turn.
On line 76, we present a logic
0 to the serial input to the first
shift register in the chain, then
we use the for() loop on lines
77 through 84 to turn all the segments off again, one after the other.
From Arduino
Once you’ve verified that everything
is working as expected, why don’t you
experiment with this program a little.
For example, if you delete lines 71
through 73 from inside the first loop
and reinsert them outside the loop
starting at line 75, all the segments will
light up simultaneously, stay lit for one
second, then start to be extinguished
one after the other as before.
Cunning code
Now take a deep breath because
the time has come to pull everything
together. We’re going to take our old
clock program, which was based on
our BCD decoder implementation, and
modify it to use our new shift register
realisation. You can feast your orbs
on my version of this code in the file
CB-dec24-code-02.txt
After all we’ve been through together,
you should be able to understand the
bulk of this program without any problem. The interesting part is found in
the loop() function.
We start by declaring a four-element
array of 8-bit unsigned integers called
tmpData[] (temporary data), along
with a 32-bit unsigned integer called
dispData (display data). Observe
that we are using the uint8_t and
uint32_t data types for these values
(we introduced these data types in
detail in PE, September 2020).
Listing 2: building the 32-bit value
to be shifted.
Listing 1: some simple test code.
22
Practical Electronics | December | 2024
Lower Breadboard
Similarly, I used two more
short yellow wires to connect the QH’ output from the
second chip to the SER input
on the third chip, and the QH’
output from the third chip to
the SER input on the fourth.
Next, I connected pin 3
from the Arduino to pin 12
(the RCLK input) on the righthand chip, then added short
wires to connect this pin to
the corresponding pins on
the other chips. Similarly,
I connected the SRCLK and
SRCLR signals as shown.
We’re so close I can taste
it. The final step is to connect the outputs from the
shift registers to the inputs to
the current-limiting resistors
driving the displays (Fig.10).
The full-up implementation is shown
in file CB-Dec24-brd-03.pdf
To resistors
for display D3
As usual, we use our real-time clock
(RTC) to determine the hour (in 24-hour
format) and minute, both of which are
represented as integers. As usual, we
convert it from 24-hour format to 12hour format and we load the most- and
least-significant digits of our hour and
minute values into our temporary data
array as illustrated in lines 108 through
111 of Listing 2.
Now what we need to do is build
the 32-bit value to be loaded into our
shift register. Remember earlier when
I wrote that we need to load the last
bit first and the first bit last? That explains why we load the most-significant
hour digit into tmpData[0] and the
least-significant minute digit into
tmpData[3], opposite to the way we
did things before.
Our next task is to take these four
8-bit tmpData[] values and use them
to construct a single 32-bit dispData
value, as illustrated in Fig.11.
We start on line 114 by clearing our
dispData value to all 0s. The next bit
is quite clever, because we use a for()
loop whose index follows the sequence
0, 1, 2, 3. Each time round the loop, we
use this index to shift the contents of
the corresponding tmpData[] value
left by the index multiple of 8 bits using
the << shift left operator.
This means tmpData[0] will be
shifted left by 8*0 = 0 bits, tmpData[1]
will be shifted left by 8*1 = 8 bits,
tmpData[2] will be shifted left by 8*2
= 16 bits and so forth. As part of this, 0s
will be shifted into the newly-vacated,
least-significant (right-hand) bits.
After performing each shift, we
logically OR the existing contents of
dispData with the contents of the
newly shifted value using the compound |= assignment operator. If you
wish to delve deeper into any of this,
you might take a look at my Masking and the C/C++ Bitwise Operators
column (https://pemag.au/link/ac1q).
One part of this deserves special attention. Observe the use of (uint32_t)
on line 117. Let’s consider what would
happen if we omitted this from our
statement.
“Type casting” refers to the process
of converting one data type to another.
There are two forms of type casting:
implicit and explicit. One example
of an implicit type cast occurs when
we assign a smaller value to a larger
one, like assigning an 8-bit value to a
32-bit value, for example. In this case,
the 8-bit value will be padded with 24
leading zeros (assuming it’s an unsigned
type, anyway).
In line 117 of Listing 2, this implicit cast is the last thing that occurs
prior to the new value being assigned
to dispData. Before this, our 8-bit
Practical Electronics | December | 2024
Components from Part 1
LEDs (assorted colours)
Resistors (assorted values)
Solderless breadboard
Multicore jumper wires (male-to-male)
https://amzn.to/3E7VAQE
https://amzn.to/3O4RvBt
https://amzn.to/3O2L3e8
https://amzn.to/3O4hnxk
Components from Part 2
7-segment display(s)
https://amzn.to/3Afm8yu
Components from Part 5
Momentary pushbutton switches
https://amzn.to/3Tk7Q87
Components from Part 6
Passive piezoelectric buzzer
https://amzn.to/3KmxjcX
Components for Part 9
SW-18010P vibration switch
https://bit.ly/46SfDA4
Components for Part 10
Breadboard mounting trimpots
https://bit.ly/3QAuz04
Components for Part 12
Light-Dependent Resistor
https://bit.ly/3S2430m
Components for Part 13
BC337 NPN Transistor
https://bit.ly/40xAgyS
Components for Part 14
HC-SR04 Ultrasonic Sensor
https://bit.ly/49AMBq4
Components for Part 15
Real-Time Clock (RTC)
https://bit.ly/3S9OjHl
Components for Part 18
Long tailed (0.1-inch/2.54mm pitch) header pins
https://bit.ly/3U1Vp2z
Components for Part 19
Prototyping boards
Kit of popular SN74LS00 chips
https://bit.ly/3UMkcZ1
https://bit.ly/3wqgzyv
Components for Part 20
16V 100µF electrolytic capacitors
Ceramic capacitors (assorted values)
https://bit.ly/44LzpNa
https://bit.ly/4bEAUiv
Components for Part 22
SN74LS48N BCD Decoders
16-pin resistor pack (8 × 390Ω)
https://bit.ly/3zT18jx
https://bit.ly/4d0ISDz
Components for Part 23
4 × CD4511 BCD Decoders
16-pin resistor pack (8 × 390Ω)
https://bit.ly/3yzWl6k
https://bit.ly/46HZ2PQ
Components for Part 24
74HC595 8-bit shift registers
4 × 16-pin resistor packs (8 × 560Ω)
tmpData[] values will be shifted
left by multiples of 8 bits as we just
discussed.
Since the << shift left operator has a
higher precedence than the |= assignment operator, the shifted bits will ‘fall
off the end’ of our 8-bit value.
This is why we are using (uint32_t)
https://bit.ly/3yzWl6k
https://bit.ly/46HZ2PQ
to perform an explicit cast. It has a
higher precedence than a shift operator,
so our 8-bit tmpData[] values will be
cast into 32-bit values (padded with
0s in the most-significant bits) before
the shift takes place. It’s the resulting
shifted 32-bit value that will be assigned
to our 32-bit dispData variable.
tmpData[3]
tmpData[2]
tmpData[1]
tmpData[0]
(LS Minutes)
8
0
(MS Minutes)
8
0
(LS Hours)
8
0
(MS Hours)
8
0
a b c d e f g h a b c d e f g h a b c d e f g h a b c d e f g h
31
dispData
To Shift
Register
0
Fig.11: Building our 32-bit dispData value.
23
Digits
Segments
0
6
1
2
2
5
3
5
4
4
5
5
6
5
7
3
8
7
9
5
Fig.12: the number of segments used by each digit in our current display scheme.
The last function in my program is
we can remove the remaining combinations (10 through 59) from further
consideration.
As we see, our worst-case scenario
occurs at eight minutes past eight
o’clock in the evening, which is 20:08
when our clock is running in its 24-hour
mode. This will cause us to have 11 +
13 = 24 segments lit at the same time.
I could have solved this in my head,
but would I have been sure I was right?
By jotting things down in this way, we
can see how we arrived at our answer
and easily check things later.
Our second task was to determine the
value of the current-limiting resistors
that will provide us with the maximum possible segment current value
assuming a total allocation of 350mA
(the origin of this value was discussed
in our previous column).
We want to drive our segments as
bright as possible without exceeding
their 20mA maximum as defined in
their data sheet. I’m afraid to say that
this is a trick question because the
answer depends on the type of chips
we are using to drive our displays.
Let’s start by calculating 350mA ÷
24 segments = 14.58mA per segment.
If we were still using the CD4511 BCD
to 7-segment decoder chips from our
previous column, each output can
handle up to 25mA, which means our
14.58mA will be a breeze.
Since we are using the Arduino Uno’s
5V power supply, and we know the
forward voltage drop of our red LED
DisplayTime() , which takes our
32-bit dispData value and loads it
into the shift register, starting with bit
0 and ending with bit 31. That means
bit 0 in dispData ends up driving
the dp segment in display D3, while
bit 31 in dispData ends up driving
the a segment in display D0.
I just powered up my clock, and it
works like a charm, as is apparent from
the great big beaming smile on my face
(I look like this now: ).
☺
All is revealed!
Now we consider our concluding
conundrum. Our first task was to determine the maximum number of segments
we might see active at the same time.
Before we start, write your solution
down on a piece of paper (we don’t
want any cheating).
Some people’s minds work in such
a way that the answer will simply
pop out to them. Others, like your
humble narrator, are obliged to take a
more circuitous approach. There is no
right or wrong path to success here. I
started by noting the number of segments associated with each displayed
digit (Fig.12).
Based on this, I created the tables
shown in Fig.13. The reason we don’t
need to document all the possibilities
in the minutes table is that the mostsignificant digit cycles through 0, 1, 2,
3, 4, 5. Since we know 0 consumes the
most segments out of these options,
24-Hour
12-Hour
Minutes
H
#Segs
H
#Segs
H
#Segs
M
#Segs
1
2
3
4
5
6
7
8
9
10
11
12
2
5
5
4
5
5
3
7
5
2+6 = 8
2+2 = 4
2+5 = 7
0
6
2
1
5
2
3
5
4
4
5
5
6
5
3
7
8
7
9
5
10 2+6 = 8
11 2+2 = 4
12
13
14
15
16
17
18
19
20
21
22
23
2+5 = 7
2+5 = 7
2+4 = 6
2+5 = 7
2+5 = 7
2+3 = 5
2+7 = 9
2+5 = 7
5+6 = 11
5+2 = 7
5+5 = 10
5+5 = 10
00
01
02
03
04
05
06
07
08
09
10
:
59
6+6 = 12
6+2 = 8
6+5 = 11
6+5 = 11
6+4 = 10
6+5 = 11
6+5 = 11
6+3 = 9
6+7 = 13
6+5 = 11
2+6 = 8
:
:
5+5 = 10
segments is 2V, we can calculate the
desired value of our current-limiting
resistors as (5V – 2V) ÷ 0.01458A =
206Ω. The nearest regular resistor
value is 200Ω (with red-black-brown
color bands), which is “close enough
for government work”, as they say, so
we would go with that.
However, since we are now using
74HC595 shift registers to drive our
displays, we are bound to obey the
constraints of their data sheet, which
states that the maximum current per
output is only 6mA.
That still results in relatively bright
displays, and it reduces our worst-case
display current to 6mA × 24 segments =
144mA. This is well below our 350mA
maximum, leaving 350mA – 144mA =
206mA on the table for us to use for
other purposes.
However, it also means that the values
of our current-limiting resistors are
now calculated as (5V – 2V) ÷ 0.006A
= 500Ω. In this case, the nearest regular
value is 510Ω (with green-brown-brown
color bands).
What? That’s it?
I’m afraid we’ve come to the end of
our Arduino Bootcamp series. Still,
turn that frown upside-down into a
smile because all is not doom and
gloom.
In this series, we’ve covered a lot
of ground and learned all sorts of interesting things, many of which will
prove useful in our new Weird and
Wonderful Arduino Projects series,
starting next month.
Until then, let’s all think about things
we could do to extend the capabilities
of the clock project we just created. I’ll
share my thoughts in our next column
(albeit under a different name).
As always, I welcome your insightful
comments, penetrating questions, and
PE
sagacious suggestions.
Online resources
For the purposes of this series, I’m going to assume
that you are already familiar with fundamental
concepts like voltage, current and resistance.
If not, you might want to start by perusing and
pondering a short series of articles I penned on
these very topics – see: https://pemag.au/link/ac16
Similarly, I’ll assume you are no stranger to
solderless breadboards. Having said this, even if
you’ve used these little scamps before, there are
some aspects to them that can trap the unwary, so
may I suggest you feast your orbs on a column I
wrote just for you – see: https://pemag.au/link/ac17
Last, but not least, you will find a treasure
trove of resources at the Arduino.cc website,
including example programs and reference
documentation.
Fig.13: my approach to solving the problem.
24
Practical Electronics | December | 2024
|