Conway s Game of Life Implemented on an LED Array

Nick Rhinehart
Seth Foster
E15: Fundamentals of Digital Systems
Professor: Matt Zucker
Lab Instructor: Erik Cheever
Submission Date: December 9th, 2010.
Conway s Game of Life Implemented on an LED Array
Table of Contents:
I. Abstract
II. Introduction
III. Background and Theory
IV. Project Design
V. Results
VI. Discussion
VII. Appendices
A. Circuit Design Layout and Diagrams
B. Complete Control Code
I. Abstract
We implemented Conway’s Game of Life on a 16x16 array of Light Emitting Diodes
(LEDs). The game is an algorithm that updates the state of cells, in our case LEDs, based upon
the previous state. The initial state of the game is configurable by a touch plate screen overlaid
above the LED array. We control the lighting of the LEDs individually by addressing two 4x16
decoders that output 16 rows and 16 columns of LEDs. To light an LED, we address and
provide high voltage to a column with the output of one decoder inverted, and address and
provide ground to a row with the output of another decoder. The addressing is performed by a
control algorithm run on an Arduino Uno configuration, an open­source platform incorporating an
ATMega 308 microcontroller and other hardware. The microcontroller also controls input from
the touch screen.
II. Introduction
The goal of our project was to simulate Conway’s Game of Life, with an initial state
configurable by a user through a touch interface. We found this to be an interesting and
appropriate project with its combination of hardware design and software design. The hardware
design utilizes a digital systems technique known as decoding, and the software is written in an
Arduino extension of C++. Both hardware and software were implemented with the usage of
algorithmic and control knowledge obtained from our coursework in Digital Systems. We were
motivated to create such a project because of our interest in the interaction between hardware
and software, as well as our interest in creating a tangible and interactive system that made
efficient and entertaining use of our acquired knowledge.
III. Background and Theory
The two main hardware elements in the project are the LED array and the touchscreen
which overlays it. To light an LED, a sufficiently high voltage difference must be applied from
cathode to anode. Because diodes are nonlinear devices, there is a voltage cutoff point (about
2V in the case of our LEDs) below which the LED will not light. This is unlike a lightbulb, the
brightness of which is relative to power in more of a linear way. Also, a diode is only conductive
in one direction at the voltages we use, thus we must apply high voltage to the correct ends of
the LEDs.
These facts allow us to index a single LED easily. However, controlling an array of 256
(16 columns x 16 rows) LEDs proves itself to be more difficult than controlling a single LED, as it
is infeasible to connect 512 (one for cathodes and one for anodes) individually controlled lines to
every LED. We are limited by the number of output pins by our microcontroller, which is only 20.
Instead, we connected all of the high lines of each LED in columns, and all of the low ends of the
LEDs in rows (see Fig. 1 in Appendix A), and used decoders to address each LED individually.
Using digital logic, we apply a negative voltage across all rows and columns, meaning that no
LEDs light up. Now, we select one row and one column to set to the correct voltages ­ high
voltage at the cathode and 0 volts at the anode. There is only one LED whose cathode is
connected to high voltage and whose anode is connected to 0 volts. All other LEDs are in one of
three other states, all three of which do not light up (See Fig. 2 in Appendix A):
Not in the same row or column as the indexed LED: Cathode at 0V, anode at high V.
Reverse­biased, LED does not light.
In the same column as the indexed LED: Cathode at high voltage, anode at high voltage.
0V difference, LED does not light.
In the same row as the indexed LED: Cathode at 0V, anode at 0V. 0V difference, LED
does not light.
Our decoders allow us to address each column and each row individually, as each is
controlled by 4 input lines. Each input line can be either high or low, and thus we have 2 4 16
possible output states for each decoder. The decoders are “active low,” meaning that the
selected output is given a low voltage, and 15 other lines are given a high voltage. The decoder
that controls the low ends of the LEDs thus has its outputs connected directly to the rows of the
LED array, and the decoder that controls the high voltage ends of the LEDs has its outputs
inverted. We used three inverters that allowed us to invert 6 lines each.
The touch plate interface is the other major hardware issue. The touchplates we used,
provided by Professor Cheever, are resistive touchscreens. They are constructed of two
parallel planes of glass, held apart by non­conductive spacers. When the touchscreen is
touched, the panes of glass will compress towards each other and make contact at the point
where they were touched.
Each pane of glass has a resistance. To read the touchscreen, we apply a voltage
across one of the panes of glass. When the screen is touched, that pane will touch the other
pane, creating a conductive path to the other two electrodes, where we examine the voltage. At
this point, the voltage at those electrodes is given by the voltage divider equation:
V read
V in
(
Rlo
Rhigh+Rlo
) We may assume (from experimental results) that the relationship between distance between the
electrode and touch point and resistance between the electrode and touch point is direct and
linear, and so we find
V read kd
where k is some linear coefficient and d is the distance between the electrode and the touch
point. By establishing a maximum and minimum at which both the voltage read and distance are
known, we can calculate position based on voltage read. Therefore, there are several steps
involved in sensing a given touch event:
Apply a voltage difference across the axis we are interested in
Read the voltage on the electrodes of the other axis
Apply a voltage to the second axis
Read the voltage on the electrodes of the first
There are, however, several issues with this type of resistive touchscreen. One is that
although the touchscreen has two sensing axes, only one may be read at any given time. We
solve this by switching between sensing axes rapidly enough that any given touch event is
guaranteed to be captured on both axes.
Another issue is the formation of a capacitor by the touchscreen. Any two points which
separate charge are known to have some capacitance. Voltage is defined as a separation of
charge, and so the touchscreen ­ which consists of two plates, not connected, at different
voltages ­ forms a capacitor. This means that the touchplate behaves like an RC circuit,
discharging voltage over time and being slow to rise to the voltage to which it is driven.
This becomes a serious problem when the frequency at which we switch sensing axes
gets close to or smaller than the time constant of the system. To avoid this behavior, therefore,
we attached each electrode to ground through a large resistor. Also, before sensing each axis,
we ground it through the microcontroller to discharge any stored energy in the touchplates.
Therefore, our process for sensing a touch event becomes more complex:
○ Apply a voltage across the axis we wish to read
○ Connect the electrodes of the other axis to ground, through the microcontroller
(this requires changing the sensing pins from input/high impedance mode to
output/0 volts mode)
○ Sense the voltage on the other axis (this requires changing the sensing pins back
to input/high impedance)
○ Apply a voltage across the second axis
○ Connect the electrodes of the first axis to ground
○ Sense the voltage on the first axis
The sensing routine is therefore somewhat time­consuming, which also causes flickering
if we are displaying LEDs at the same time as we are sensing touch events (which is usually the
case).
IV. Project Design
The above flowchart illustrates the flow of control in our project. The Arduino recieves
touch event data from the touchscreen at the initialization of the program, and uses this
information to calculate the initial state of the Game of Life. The microcontroller never
communicates directly with the LEDs, instead sending the decoders the address of the LED
which should be displayed. The decoders are what drive the LED array directly.
The touchscreen is connected to the Arduino on two general purpose IO pins and two
analog input pins, which are attached internally to an analog­to­digital converter. As discussed
above, the microcontroller senses touch events on the touchscreen. Because the analog input
pins are really general purpose IO pins, and as such can be set to output mode, we can easily
follow the steps outlined in the theory section.
The decoders are connected to the general purpose IO pins (or digital output) pins of the
Arduino. Since they are 4­to­16 decoders, each requires four pins. Though each IC does have
enable lines, we decided that we did not want to waste the pin real estate on the Arduino and
hardwired them to ground, keeping the decoders enabled at all times.
The outputs of one decoder (designated internally as “low”) are connected directly to the
columns of the LED array, which connect all the anodes in that column. The outputs of the other
IC, designated internally as “drive” , are connected to invertors, so that they become active high.
The outputs of the invertors are connected to the rows of the LED array, which connect the
cathodes of all the LEDs in that row.
In the hardware, so that the wiring can be understood at a glance, connections from “drive” to
invertors are yellow and orange; from invertors to cathodes are white; and from “low” to anodes
are black.
The code for the microcontroller is somewhat more complex than most of the
programming we did in class. A chart which illustrates the flow of control in the code is useful.
Once the program finishes determining the initial state, it enters a loop in which it
repeatedly applies the Conway algorithm to advance the game to the next step. At this time, this
loop continues indefinitely, even if the game state results in an empty set or steady­state
condition.
To follow is a bit of sample code detailing the determination of the next state of an
individual LED. The variable is 1 if the current LED is on, 0 if it is off. S is the
sum of all the horizontally, vertically, and diagonally tangent LEDs. This sum, in addition to the
state of the current LED are what determine if an LED is turned on, kept on, or turned off (birth,
keeping alive, and underpopulation or overpopulation, respectively).
/* S . T S = [5]+
LED [0]+
[1]+
[6]+
[7];
/*I LED , . */
(
&& (
S < 2)) S
A
[ ][ ]=0; //
/* K
(
/* T
LED (
&& (
S <= 3))
S
A
[ ][ ]=1;
LED LED 16 16 [2]+
2 3, LED . */
[3]+
LED */
[4]+
LED , ' )*/
(
&& (
S
A
S > 3))
[ ][ ]=0; //
/* O
, LED LED */
(!
&& (
S ==3))
S
A
[ ][ ]=1; //
3 LED , We embed this control inside loops that iterate over every LED in the LED array to determine the
next state of every LED. We display the state of the array for a brief period of time, and then
calculate and display the next state.
The initial state determination includes both reading touch events to turn on LEDs under
user control and initial calibration of the touchscreen. Before taking any data, we require the user
to touch (prompted by visual cues) the LEDs at positions (0,0) and (15,15) to establish
maximum and minimum values of x and y which contain the LED array.
Once these are established, we may compute which LED a user is touching from the
input from the touchscreen.
It is known that the distance between each LED is constant. Because the resistance
between two points on the touchscreen is some linear function of the distance between them, we
may establish that the difference between LEDs in terms of the output from the ADC is also
constant. We may write
Touch event position minimum bound
ma imum bound minimum bound °
Selected LED ma imum LEDs Selected LED
ma imum LEDs
Touch even position minimum bound
ma imum bound minimum bound
After the touchscreen has been calibrated , the initial state is determined from user input.
Using the above equation, on each touch event the state of the selected LED is toggled. This
process continues until the user touches an out­of­bounds location, at which time the program
begins to apply the Conway algorithm.
V. Results
We successfully implemented every element of our project, albeit with some suboptimal
outcomes. For instance, because we only address one LED at a time, the LEDs can be seen to
flicker under circumstances where the micoprocessor is doing a lot of calculation at the same
time. For instance, during the initial configuration state, because the processor is splitting its time
between reading touch events and displaying LEDs, it cannot update the LEDs fast enough to
avoid flicker. Likewise, in the case of a game state with many activated LEDs, the processor
cannot iterate through the active LEDs fast enough to avoid flicker.
From the beginning, the touchscreen interface has been troublesome to work with, and it
is no less so in the final version. We note that the calibration function often reads garbage values
from the user. Minimum values greater than maximum values are an example of such garbage
values. We took the naive solution of simply forcing the user to redo the calibration function until
we got values that were likely to be correct.no issues with the LED control logic gates. Though
we initially ran into several errors in addressing the LEDs, we were able to successfully debug
these errors, which included: using the wrong IC inverters (7406s instead of 7404s; luckily we
made the decision not to hardwire any ICs to the boards and instead put them inside soldered
sockets, thus the ICs were easily interchanged), wrong pin cable connections from the decoder
outputs, and two unconnected anode solder joints that were easily remedied.
VI. Discussion
A major obstacle to implementation of the project was overcoming the inherent
inaccuracy of the touchplate. Currently, calibration can still be somewhat inexact, so the LED lit
is often one or two LEDs away from the “touched” LED. We designate this to be an artifact of
the touchplate itself, and are working on obtaining better calibration values. Two potential
methods for obtaining better calibration values we are considering include obtaining several
calibration values and averaging them or obtaining calibration values for the other corners and
averaging them. We are considering several extensions, including a potentiometer that controls
the speed of the game propagation, a push button to reset either the game or the processor, and
the replacing of the touchplate with one that is more accurate. In the future, a dedicated
controller, such as an MSP430, would be desirable, as one is much more compact than our
Arduino board (and is more feasible to use as a long­term embedded controller). Also, we could
display written instructions as letters composed of LEDs at specific points in the program that
instruct the user what to do. All of these potential additions must take into consideration the
space and memory limitations of the microcontroller.
VII. Appendices
Appendix A ­ Circuit Design Layout and Diagrams
Figure 1: A concept n n array of LEDs
Figure 2: Indexing (1,1) in the n n array.
Figure 3: Completed LED array
Figure 4: Decoders
Figure 5: Inverters
Figure 6: Columns of LEDs
Schematic 1: LED array
Schematic 2: Decoders and Invertors
Schematic 3: Arduino and Touchscreen
Appendix B ­ Complete Control Code
/****************************************************************************************
E15 F
P
: C
' G
L
W
S
F
N
R
T
A
A
, C++ . T A
­
. M
.
. .
W C
' G
L
, 16 16 LED . C
'
G
L
, . T
(
W
) :
1. A ­
2. A 3. A 4. A C
.
.
' G
L
. T . W LED .
T LED . T C
, .
, .
, , , , ­
P
C
A
, 16 16 , G
L
T
.
P
E
C
, , P
M
Z
, .
****************************************************************************************/
//U
#
<
() .>
, //W 16 16 LED #
L
16
#
L
16
//T
//GPIO #
A 2
#
B 3
#
C 4
#
D 5
//T
#
#
#
#
//T //
//
A 8
B 9
C 10
D 11
, . F GPIO ­
.
, . T
.
.
­
.
.
#
#
20
20
(
)
//N ();
*
, ... )
//F
.
[128];
// _
;
_
(
, );
(
, 128, , );
_
(
);
S
.
(
);
() */
A
[
L
][
A
(
A
);
T
=50;
.
[4];
.
X,
Y;
C
(
.
­ // (
/*T S
C
.
);
.
()
A
, . I //
, //
//M
(
A
S
.
);
(
A
,
T
//
); //D
0;
A
(
A
[
L
][
/*I
A
( , ,;
( =0; <
L
; ++) //F ( =0; <
L
; ++) A
[ ][ ]=0;
S
.
(1) ,
.
//C
, ]; //
1 0 . 1=
, 0=
//
//H );
A
128 //T
(
, ' . ­ . I L
//T
L
A
]) , ) . S
LED .
...
//S 0.
.
, 0. */
(
/*T
L
][
L
]) C
' G
L
. I A
, , . T
, A
, A
, ,
A
. */
S
A
[
L
][
L
]; //T .
,;
[8]=
=0;
A
(
A
[
S
/* T 0 ; //
//
A
);
: //M
, , , , .
0 .
() G
L
. F ,
. W 0. */
( =0; <
L
; ++) ( =0; <
L
; ++) = A
[ ][ ];
[0]=
A
[ ][ ­1];
//C
A
.
[1]=
A
[ ][ +1];
[2]=
A
[ ­1][ ];
[3]=
A
[ +1][ ];
[4]=
A
[ ­1][ ­1];
[5]=
A
[ ­1][ +1];
[6]=
A
[ +1][ +1];
[7]=
A
[ +1][ ­1];
, (( ­1) <= 0) (( +1) >= //C
[0]=0;
//
[4]=0;
[7]=0;
//
L
) [1]=0;
//F , // 16, , 0.
(1­
0.
[5]=0;
[6]=0;
(( ­1) <= 0) [2]=0;
[4]=0;
[5]=0;
(( +1) >= L
) [3]=0;
[6]=0;
[7]=0;
//N , /* S LED . T LED 16 16 S = [0]+
[1]+
[2]+
[3]+
[5]+
[6]+
[7];
.
LED */
[4]+
)
/*I LED , . */
(
&& (
S < 2)) S
A
[ ][ ]=0; //
/* K
(
LED (
&& (
S <= 3))
S
A
[ ][ ]=1;
/* T
(
LED && (
S > 3))
S
A
[ ][ ]=0; //
LED , 2 3, ' )*/
LED . */
/* O
, LED LED */
(!
&& (
S ==3))
S
A
[ ][ ]=1; //
( =0; <
L
; ++) ( =0; <
L
; ++) A
[ ][ ]=
S
A
//C
3 S
LED , A
A
[ ][ ];
S
(
A
[
L
][
L
],
T
) /*
H
. T
' , LED , . I 16 16 , , LED .
T , . W , T
A
' , , ,,
=0;
[256][2];
A
;
=0;
( =0; <
L
; ++) ( =0; <
L
; ++) (
A
[ ][ ]==1) [
][0]= ;
[
][1]= ;
A
,
A
. N , ,
256 LED //F //I //S
LED . */
...
...
.
.
++;
//M
[
[
][0]=NULL;
][1]=NULL;
A
= (
)(
=0;
T
=1;
//N
­
.
.
­1);
//K
// . M
// ' .
(
<(
T
*
A
)) /* T
' , . E
LED (
[
][0]==NULL) //I ' ...
=0;
// !
(
[
][0],
[
][1]); //D
++;
++;
(
T
);
//D
(
X, Y)
/* D
LED , W , . T
, X Y . T
, AND 0000000001 , GPIO . */
W
(
A, (
X&1));
W
(
B, ((
X>>1)&1)); //S
.
W
(
C, ((
X>>2)&1));
W
(
D, ((
X>>3)&1));
W
W
W
W
(
(
(
(
A, (
B, ((
C, ((
D, ((
.
Y&1));
Y>>1)&1));
Y>>2)&1));
Y>>3)&1));
()
/* I , A
++ () () (). S
, .. . S
, , . */
M
(
A, OUTPUT); //S GPIO M
(
B, OUTPUT); //
M
(
C, OUTPUT);
M
(
D, OUTPUT);
M
(
A, OUTPUT);
M
(
B, OUTPUT);
M
(
C, OUTPUT);
M
(
D, OUTPUT);
S
.
(9600);
//B
.
. */
//I
,
S
.
("I ()");
()
/* B
' () () ' , () ;
S
.
("I () ()\ ");
= ();
S
.
("I () (), \ ");
. */
/**************************************************************
**************************************************************
T
.
**************************************************************
**************************************************************/
//P L = A0; //A
/
L = A1; //A
/
D
= 7;
D
= 6;
B
= 0;
R
= 1;
R
(
* V
, * V
)
/* T
, . I ://
.
.
/N S /
1/C
/ 91/L 4/T
S
*/
/********************************
* R
********************************/
(10);
M
(
D
, OUTPUT); //S M
(
L , OUTPUT);
W
(
L , LOW);
W
(
D
, HIGH);
*
M
M
(
(
W
W
M
(
M
(
(1);
(
(
V
D
, OUTPUT); //D
L , OUTPUT); //
D
, LOW);
L , LOW);
D
, INPUT); //S L , INPUT);
//
= R
(
+5V
.
L );
/*********************************
* R
/
********************************/
(10);
M
(
D
, OUTPUT); //S M
(
L , OUTPUT);
W
(
L , LOW);
+5V
/T
S
.
.
M
M
M
M
W
(
(
W
W
(
D
, HIGH);
D
, OUTPUT); //D
L , OUTPUT); //
D
, LOW);
L , LOW);
(
(
(
(
(1);
*
D
, INPUT); //S L , INPUT);
//
V
= R
(
L );
S
C
(
A
[
L
][
L
], [4]) /* T
. I
A
, , . */
= [2]; //B
= [1];
= [0];
= [3];
[2] = //M
(
­
), (
­
) ;
A
(
A
);
//I
T
=40; //A .
X, Y;
X=
­1;
Y=
­1;
,
,
=0;
((
X<
R
((
X<
=0;
(
//V
//I
//
; //V
,
)) //W
Y); //G ))
//I ' //
.
= (
= (
);
);
X­
Y­
/*T //I ' //M
LED R
)&&(
Y<
(&
X, &
) (
Y<
=1;
) ...
...
...
LED
­­­­­­­­­­­­­­­­ = ­­­­­­­­­­­­­
LED
(
= (
= (
A
, /
)*
L
LED. */
)(((
/
[0])*
L
));
)(15­((
/
[1])*
L
));
[
][
(140); //D
S
(
]=!
A
,
T
A
' [
][
/3); //S
]; //
' LED
.
/* T I ,;
( =0; <
C
L
(
[4])
; ++)
( =0; <
L
( , );
(2);
(0,0);
X=0, ((
X<
//D
; ++) //
LED (0,0) (15,15).
. */
LED //H
(0,0) Y=0;
R
(&
X, &
)&&(
Y);
Y<
)) //W
//
//S
( =0; <
L
; ++)
( , );
(10);
++;
[0] = [1] = X; //S
Y;
LED X=0;
Y=0;
//
(15,15);
((
X<
R
(&
X, &
)&&(
Y);
Y<
)) //W
//S
(=
L
­1, =
L
( , );
(10);
[2] = [3] = /*T X; //S
Y;
LED ­1; >=0; ­­, ­­)
.
, 0 . S , , ' .
(
[2]<=(
[0]+100) [1]<=(
[3]+100)) C
(
);
. W ,
This report takes more of a traditional lab report format than have other reports for this course. It should be written as if the
reader was another member of the class. S/he has some familiarity with the Verilog/C/etc.. but perhaps not with the nitty­gritty
details of your chosen application/project. With your report in hand s/he should be able to quickly reproduce what you did and
understand it well enough to add to it.
Title page/Table of contents (5 pts) ­ project title, names of participants, and a table of contents.
Abstract (10 pts) ­ 1 paragraph describing what you did.
Introduction (15 pts) ­ What is the goal of your project? What motivated you? Why is it interesting?
Background/Theory (15 pts) ­ What technical details does your reader need to understand?
Completed Project Design (30 pts) ­ Start with a very high level description of your project. Get increasingly detailed in
your description until you get to the level of short sections of code/circuitry that are critical to the proper operation of
your project. Any comments in your code should be good enough that somebody in the class could read and
understand it, without having to refer to any external references.
Results (15 pts) ­ How well did you achieve your goals? This should include quantitative results (and perhaps
qualitative).
Discussion/Conclusion (10 pts) ­ What worked well? What didn't? What are obvious extensions?
Appendices as needed. The body of the report should only
contain short bits of code necessary to explain
the project operation. The appendix should contain complete code listings.
The report will be in a single file that is uploaded to a SwatFiles dropbox. The file must be either word doc or pdf.
Details about dropbox to follow.