The Project - Part Two
The scope of this project was to create a simulation of a 7-segment display and Binary-Coded-Decimal (BCD) encoder in vanilla HTML, CSS and JavaScript (JS). In part one we used HTML to establish the screen, CSS to style it and we started using JS to model the behaviour.
The screen contained two sections, a dropdown selection from which the user could choose a decimal digits to be presented. The styling of the screen, including the positioning of HTML elements, was achieved through CSS. Finally, we used JS to enable simulation of the lines going into the BCD and mapping to the output lines that illuminate specific segments of the 7-segment display.
In this post we take the simulation of the BCD behaviour one stage further to emulate the circuitry it contains. Such as circuit comprises of wires, transistors and other electronic devices at a microscopic scale. The electronic components are organised to provide logic gates such as AND
, OR
and NOT
(and more). This post will document the process I when through to replicate the logic circuit from first principles.
The source code for this post can be found in this JSFiddle.
The Truth Table
During the project we have referenced the following Truth table several times. The first section (left) refers to the digit that user can select from a dropdown (select) for presentation as a 7-segment display (7SD.)
The middle section documents the binary encoded value of the decimal using first bits (binary digits) that will be used as input (lines/wires) into the BCD (Binary-Coded-Decimal) modules.
The right side of the table documents the seven output lines from the BCD module that align to each of the segments of the 7SD.
+--------------------------------------------+
| Truth Table |
+-----+--------------+-----------------------+
| | Input lines | Output lines |
| Dec | A B C D | a b c d e f g |
+-----+--------------+-----------------------+
| 0 | 0 0 0 0 | 1 1 1 1 1 1 0 |
| 1 | 0 0 0 1 | 1 1 0 0 0 0 0 |
| 2 | 0 0 1 0 | 1 0 1 1 0 1 0 |
| 3 | 0 0 1 1 | 1 1 1 0 0 1 1 |
| 4 | 0 1 0 0 | 1 1 0 0 1 0 1 |
| 5 | 0 1 0 1 | 0 1 1 0 1 1 1 |
| 6 | 0 1 1 0 | 0 1 1 1 1 1 1 |
| 7 | 0 1 1 1 | 1 1 0 0 0 1 0 |
| 8 | 1 0 0 0 | 1 1 1 1 1 1 1 |
| 9 | 1 0 0 1 | 1 1 1 0 1 1 1 |
+-----+--------------+-----------------------+
Stage Three - Simulate the BCD Boolean logic
In this last stage we have some complexity entering the simulation of the BCD. We capture the Boolean logic that mimics the circuitry and the logic gates that performs the mapping between the input lines to the output lines in the booleanLogic
array.
const decimalSelection = attachDecimalChangeEvent(
simulatedLogicalBCD, inputLines);
function simulatedLogicalBCD(inputWires) {
const [A,B,C,D] = [...inputWires].map(wire => wire === "1");
const booleanLogic = [
A || !B || ((C || !D) && (!C || D)),
A || B || !C || D,
A || ((B || C || !D) && (!B || ((C || D) && !(C && D)))),
!(((A || (B && !(B && C))) && (!A || B || C)) || D),
A || ((B || ((C || !D) && (!C || D) && !(C && D))) &&
!(B && C && D)),
A || C || ((B || !D) && (!B || D)),
A || ((B || C) && !(B && C && D))
];
const outputWires = booleanLogic.map(flag => `${+flag}`);
simulatedBCD_Presentation(outputWires);
}
Computer Science time
The above example is still a simulation because we are using Boolean logic &&
, ||
and !
operations to determine which segments to illuminate in order to represent the digit. An actual BCD module would perform bitwise operations through AND
, OR
and NOT
logic gates to manipulate voltages instead of True and False values.
The booleanLogic
array contains the Boolean expressions used to translate the input lines into output lines. As stated earlier, these simulate the circuitry within an actual BCD module but how do we formulate the expressions? Here I will capture the process I went through.
1) For each segment (output line) identify if the 1's and 0's occur the least for each of the ten input lines. These are the results.
+-----+-----------+--------+-------+---------------------+
| Seg | 1's | 0's | Least | Input lines |
+-----+-----------+--------+-------+---------------------+
| a | 01234789 | 56 | 0 | 0101 0110 |
| b | 013456789 | 2 | 0 | 0010 |
| c | 0235689 | 147 | 0 | 0001 0100 0111 |
| d | 0268 | 134579 | 1 | 0000 0010 0110 1000 |
| e | 045689 | 1237 | 0 | 0001 0010 0011 0111 |
| f | 02356789 | 14 | 0 | 0001 0100 |
| g | 2345689 | 017 | 0 | 0000 0001 0111 |
+-----+-----------+--------+-------+---------------------+
Notice:
a) Segment d is the only one with fewer 1's than 0's.
b) No segment requires more than four input patterns.
c) The number '2' is the only one that illuminates segment b.
2) We now convert each set of input patterns into initial Boolean expressions as follows. The -
represents logical not
and the 4-bit Input lines are labelled A-D.
a) Invert zeros by applying NOT
to the Input line.
b) AND
all four of the Input lines that make a pattern.
c) OR
the patterns together to form an expression.
----+---------------------+-------------------------------------------
Seg | Input lines | Boolean expressions
----+---------------------+-------------------------------------------
a | 0101 0110 | -AB-CD or -ABC-D
b | 0010 | -A-BC-D
c | 0001 0100 0111 | -A-B-CD or -AB-C-D or -ABCD
d | 0000 0010 0110 1000 | -A-B-C-D or -A-BC-D or -ABC-D or A-B-C-D
e | 0001 0010 0011 0111 | -A-B-CD or -A-BC-D or -A-BCD or -ABCD
f | 0001 0100 | -A-B-CD or -AB-C-D
g | 0000 0001 0111 | -A-B-C-D or -A-B-CD or -ABCD
----+---------------------+-------------------------------------------
In step 1 we found that only Output line d mapped fewer 1's than 0's. This means we now have to invert all expressions except the one for Output line d, which yields the following JavaScript.
const booleanLogic = [
!((!A && B && !C && D) || (!A && B && C && !D)),
!(!A && !B && C && !D),
!((!A && !B && !C && D) || (!A && B && !C && !D)
|| (!A && B && C && D)),
((!A && !B && !C && !D) || (!A && !B && C && !D)
|| (!A && B && C && !D) || (A && !B && !C && !D)),
!((!A && !B && !C && D) || (!A && !B && C && !D)
|| (!A && !B && C && D) || (!A && B && C && D)),
!((!A && !B && !C && D) || (!A && B && !C && !D)),
!((!A && !B && !C && !D) || (!A && !B && !C && D)
|| (!A && B && C && D))
];
As it stands this will provide the results we expect but is a bit verbose and can be refined.
3) We can reduce (but not exactly simplify) the expressions by isolating the common Output lines (bits.) Let's take segment 'a' as an example. In
!((!A && B && !C && D) || (!A && B && C && !D))
the following are repeated !A &&
and B &&
so can be extracted from each pattern as follows.
!(!A && B && ((!C && D) || (C && !D)))
This process can be applied to several of the expressions yielding the following table.
+-----+---------------------------------------------+
| Seg | Boolean expressions |
+-----+---------------------------------------------+
| a | -(-AB and (-CD or C-D)) |
| b | -(-A-BC-D) |
| c | -(-A and (-B-CD or B-C-D or BCD)) |
| d | (-A-B-C or -A-BC or -ABC or A-B-C) and -D |
| e | -(-A and (-B-CD or -BC-D or -BCD or BCD)) |
| f | -(-A-C and (-BD or B-D)) |
| g | -(-A and (-B-C-D or -B-CD or BCD)) |
+-----+---------------------------------------------+
This equates to the following JavaScript.
const booleanLogic = [
!(!A && B && ((!C && D) || (C && !D))),
!(!A && !B && C && !D),
!(!A && ((!B && !C && D) || (B && ((!C && !D) || (C && D))))),
((!A && ((!B && !C) || (!B && C) || (B && C))) ||
(A && !B && !C)) && !D,
!(!A && ((!B && ((!C && D) || (C && !D) || (C && D))) ||
(B && C && D))),
!(!A && !C && ((!B && D) || (B && !D))),
!(!A && ((!B && !C && !D) || (((!B && !C) || (B && C)) && D)))
];
We can reduce the occurrence of not
operations by applying De Morgan's Law. Please see my post on the topic for a description of this process.
In brief, if we have a patten such as (!B && !C)
we can convert this to !(B || C)
. Notice the three steps of this process are:
1) Negate the entire pattern (convert !(pattern)
to (pattern)
or the other way round.
2) Negate each of the terms inside the pattern (e.g. B
becomes !B
, !D
becomes D
).
3) Swap the operation inside the pattern (e.g. OR (||)
becomes AND (&&)
and vice-a-verse.
Using segment 'b' as an example, we convert,
!(!A && !B && C && !D)
// into
(A || B || !C || D)
which does not impact the logic but is obviously simpler.
Applied across all of the expressions, where it improves matters, we get.
const booleanLogic = [
A || !B || ((C || !D) && (!C || D)),
A || B || !C || D,
A || ((B || C || !D) && (!B || ((C || D) && !(C && D)))),
!(((A || ((B || C) && (B || !C) && !(B && C))) &&
(!A || B || C)) || D),
A || ((B || ((C || !D) && (!C || D) && !(C && D))) &&
!(B && C && D)),
A || C || ((B || !D) && (!B || D)),
A || ((B || C || D) && (B || C || !D) && !(B && C && D))
];
Finally, (A || B) && (A || !B)
is the same as A
so the expression for segment 'd' can be simplified from,
(B || C) && (B || !C)
// to just
B
Likewise (B || C || D) && (B || C || !D)
is equivalent to (B || C) && (B || C)
, so can be reduced it in segment 'g' to (B || C)
, giving us this final version.
const booleanLogic = [
A || !B || ((C || !D) && (!C || D)),
A || B || !C || D,
A || ((B || C || !D) && (!B || ((C || D) && !(C && D)))),
!(((A || (B && !(B && C))) && (!A || B || C)) || D),
A || ((B || ((C || !D) && (!C || D) && !(C && D))) &&
!(B && C && D)),
A || C || ((B || !D) && (!B || D)),
A || ((B || C) && !(B && C && D))
];
If you can spot any more ways to reduce the above expressions, and can document to the procedure and prove it works, please document them in the discussions section below.
Thank you for sticking with these posts to the end. I challenge you to try and enhance this code to include the missing hexadecimal digits 'AbCdEF', still using the 7-segment display.
I am currently investigating how the technique known a Karnaugh Maps (aka k-maps) can be used to further simplify the Boolean expressions above. If this is of interest to you please add a comment in the discussions section.
Top comments (0)