Thursday, August 22, 2013

29. Programming an 8 x 8 LED Array

Taking a bit of a break from the Raspberry Pi (although it's still working away upstairs taking motion-initiated pictures through my field scope), I thought I would have to have a go at some more flashing lights.  

I found a kit available from Ciseco which cost me £7 including postage HERE.  It was very easy to build, and it is in fact a shield for the Arduino, which just plugs in to the Arduino's header pins.

So I also bought yet another Arduino Uno R3, this time from Banggood - HERE and this was fantastic value - £5.70 with free postage (from China - I think).  That's about a third of the normal price.  Although it was advertised as the 16 kB ATMega 16U2 (the surface mount type chip that you can't remove) as opposed to the 32 kB my other Arduinos are, in fact when it arrived, it was an ATMega 328P-PU - ie the dual in-line (DIL) type - same as my others!  I was tickled pink about that.  I have since ordered 3 x Arduino UNO ATmega328P-PU at £5.46 each (again with free postage).  Maybe these will turn out to be the 16U2 type!  Anyway, the delivery of the first one was reasonably quick and the value is terrific.


Here is the 8 x 8 LED array installed in its shield which is mounted on an Arduino.

The assembly of the shield kit involved some fairly straightforward soldering and this didn't take too long.

Now the task was to load some code onto the Arduino.  First you plug the USB cable into the Arduino at one end, and my PC at the other end.  My PC already has the Arduino IDE installed, so all I had to do was to load a sketch with code.

I borrowed the code from Tom Igoe's RowColumnScanning sketch HERE, and although that sketch is for controlling the x-y position of a lit-up LED by adjusting 2 variable resistors, I modified it to do some crazy row and column scanning, and the code is here:

1:  /*  
2:   Row-Column Scanning an 8x8 LED matrix with X-Y input  
3:   This example controls an 8x8 LED matrix using two analog inputs  
4:   created 27 May 2009  
5:   modified 30 Aug 2011  
6:   by Tom Igoe  
7:   This example works for the Lumex LDM-24488NI Matrix. See   
8:   http://sigma.octopart.com/140413/datasheet/Lumex-LDM-24488NI.pdf  
9:   for the pin connections  
10:   For other LED cathode column matrixes, you should only need to change   
11:   the pin numbers in the row[] and column[] arrays  
12:   rows are the anodes  
13:   cols are the cathodes  
14:   ---------  
15:   Pin numbers:  
16:   Matrix:  
17:   * Digital pins 2 through 13,  
18:   * analog pins 2 through 5 used as digital 16 through 19  
19:   Potentiometers:  
20:   * center pins are attached to analog pins 0 and 1, respectively  
21:   * side pins attached to +5V and ground, respectively.  
22:   This example code is in the public domain.  
23:   http://www.arduino.cc/en/Tutorial/RowColumnScanning  
24:   see also http://www.tigoe.net/pcomp/code/category/arduinowiring/514 for more  
25:   */  
26:  // 2-dimensional array of row pin numbers:  
27:  const int row[8] = {  
28:   2,7,19,5,13,18,12,16 };  
29:  // 2-dimensional array of column pin numbers:  
30:  const int col[8] = {  
31:   6,11,10,3,17,4,8,9 };  
32:  // 2-dimensional array of pixels:  
33:  int pixels[8][8];        
34:  // cursor position:  
35:  int x = 5;  
36:  int y = 5;  
37:  void setup() {  
38:   // initialize the I/O pins as outputs  
39:   // iterate over the pins:  
40:   for (int thisPin = 0; thisPin < 8; thisPin++) {  
41:    // initialize the output pins:  
42:    pinMode(col[thisPin], OUTPUT);   
43:    pinMode(row[thisPin], OUTPUT);   
44:    // take the col pins (i.e. the cathodes) high to ensure that  
45:    // the LEDS are off:   
46:    digitalWrite(col[thisPin], HIGH);    
47:   }  
48:   // initialize the pixel matrix:  
49:   for (int x = 0; x < 8; x++) {  
50:    for (int y = 0; y < 8; y++) {  
51:     pixels[x][y] = HIGH;  
52:    }  
53:   }  
54:  }  
55:  void loop() {  
56:   // read input:  
57:   //scanColsThenRows();  
58:   // refreshScreen();  
59:  for (int t =50; t >=1; t--) {  
60:   scanColsThenRows(t);  
61:    refreshScreen();  
62:  }  
63:  for (int t =1; t < 50; t++) {  
64:   scanColsThenRows2(t);  
65:    refreshScreen();  
66:  }  
67:  }  
68:  void scanColsThenRows(int t) {  
69:  for (int thisCol = 0; thisCol < 8; thisCol++) {  
70:   // take the row pin (anode) low:  
71:   digitalWrite(col[thisCol], LOW);  
72:   // iterate over the rows (cathodes):  
73:   for (int thisRow = 0; thisRow < 8; thisRow++) {  
74:    // when the column is LOW and the row is HIGH,  
75:    // the LED where they meet turns on:  
76:    digitalWrite(row[thisRow], HIGH);  
77:    // delay long enough to see the LED on:  
78:    delay(t);  
79:    // turn the pixel on:  
80:    digitalWrite(row[thisRow], LOW);  
81:   }  
82:   // take the column pin low to turn off the whole column:  
83:   digitalWrite(col[thisCol], HIGH);  
84:  }  
85:  for (int thisRow = 0; thisRow < 8; thisRow++) {  
86:   // take the col pin (cathode) high:  
87:   digitalWrite(row[thisRow], HIGH);  
88:   // iterate over the cols (anodes):  
89:   for (int thisCol = 0; thisCol < 8; thisCol++) {  
90:    // when the row is HIGH and the row is LOW,  
91:    // the LED where they meet turns on:  
92:    digitalWrite(col[thisCol], LOW);  
93:    // delay long enough to see the LED on:  
94:    delay(t);  
95:    // turn the pixel on:  
96:    digitalWrite(col[thisCol], HIGH);  
97:   }  
98:   // take the column pin low to turn off the whole column:  
99:   digitalWrite(row[thisRow], LOW);  
100:  }  
101:  }  
102:  void scanColsThenRows2(int t) {  
103:  for (int thisCol = 7; thisCol >= 0; thisCol--) {  
104:   // take the row pin (anode) low:  
105:   digitalWrite(col[thisCol], LOW);  
106:   // iterate over the rows (cathodes):  
107:   for (int thisRow = 7; thisRow >= 0; thisRow--) {  
108:    // when the column is LOW and the row is HIGH,  
109:    // the LED where they meet turns on:  
110:    digitalWrite(row[thisRow], HIGH);  
111:    // delay long enough to see the LED on:  
112:    delay(t);  
113:    // turn the pixel on:  
114:    digitalWrite(row[thisRow], LOW);  
115:   }  
116:   // take the column pin low to turn off the whole column:  
117:   digitalWrite(col[thisCol], HIGH);  
118:  }  
119:  for (int thisRow = 7; thisRow >= 0; thisRow--) {  
120:   // take the col pin (cathode) high:  
121:   digitalWrite(row[thisRow], HIGH);  
122:   // iterate over the cols (anodes):  
123:   for (int thisCol = 7; thisCol >= 0; thisCol--) {  
124:    // when the row is HIGH and the row is LOW,  
125:    // the LED where they meet turns on:  
126:    digitalWrite(col[thisCol], LOW);  
127:    // delay long enough to see the LED on:  
128:    delay(t);  
129:    // turn the pixel on:  
130:    digitalWrite(col[thisCol], HIGH);  
131:   }  
132:   // take the column pin low to turn off the whole column:  
133:   digitalWrite(row[thisRow], LOW);  
134:  }  
135:  }  
136:  void refreshScreen() {  
137:   // iterate over the rows (anodes):  
138:   for (int thisRow = 0; thisRow < 8; thisRow++) {  
139:    // take the row pin (anode) high:  
140:    digitalWrite(row[thisRow], HIGH);  
141:    // iterate over the cols (cathodes):  
142:    for (int thisCol = 0; thisCol < 8; thisCol++) {  
143:     // get the state of the current pixel;  
144:     int thisPixel = pixels[thisRow][thisCol];  
145:     // when the row is HIGH and the col is LOW,  
146:     // the LED where they meet turns on:  
147:     digitalWrite(col[thisCol], thisPixel);  
148:     // turn the pixel off:  
149:     if (thisPixel == LOW) {  
150:      digitalWrite(col[thisCol], HIGH);  
151:     }  
152:    }  
153:    // take the row pin low to turn off the whole row:  
154:    digitalWrite(row[thisRow], LOW);  
155:   }  
156:  }  

The refreshScreen(int t) method is possibly superfluous in this sketch, but I kept it in in case I made more modifications later.

You will have noticed that the sketch above simply makes a single light scan columns using the scanColsThenRows() method and then rows, and then it reverses the direction of apparent movement in scanColsThenRows2(int t).  The integer t inside the bracket sets the value in milliseconds for the delay(t) command.  The loop for (int t =1; t < 50; t++) { ..} makes the value of t change between 1 and 50 ms between each LED lighting.  So the activity speeds up, then reverses direction, and then slows down.  It goes a bit frantic during the fast bit, but it's interesting to see what the eye-brain system observes when the activity is very fast. (Persistence of vision (POV) again makes you think the whole lot is lit up when in fact there is only ever one LED lit at any time - they're just flashing faster than you can make out).

Here is a video of part of the cycle:



The way the LEDs are connected in the array is as follows:
This is the anode row, cathode column arrangement.  An alternative is to have cathode rows and anode columns, where all the light-emitting diodes above would be reversed.  The resistors are to limit the voltage across the LED (columns) so that excess current is not drawn.

The diagram relates the array's pin numbers 1 to 16 to the row and column numbers.  You can see that each light emitting diode (LED) is represented by an arrow showing the direction of current flow as shown below: 

In order for current to flow, and light up the LED, the voltage on the positive ( + ve) (anode) side must be HIGH and the voltage on the negative ( - ve) (cathode) must be LOW.  If both anode and cathode are the same, whether they are both HIGH or LOW, the LED will not switch on. Additionally, if the anode is LOW, the LED will not glow, whether the cathode is HIGH or LOW. Here is a truth table:

Anode
Cathode
LED State
HIGH HIGH OFF
HIGH LOW ON
LOW HIGH OFF
LOW LOW OFF


By connecting all the anodes in each row together, and all the cathodes in each column together, the state of any LED can be controlled by manipulating the rows and columns to be either HIGH or LOW.   

The actual connections within the LED matrix are such that some of the matrix pins are connected to rows and some are connected to columns.  These and the connections of the matrix pin numbers to the Arduino are summarised in the following table:

Matrix pin no.RowColumnArduino pin number
15-13
27-12
3-211
4-310
58-16 (analog pin 2)
6-517 (analog pin 3)
76-18 (analog pin 4)
83-19 (analog pin 5)
91-2
10-43
11-64
124-5
13-16
142-7
15-78
16-89


This allows mapping of the row and column numbers to the Arduino pin numbers, as is done in the sketch above with the lines of code:



const int row[8] = { 2,7,19,5,13,18,12,16 };

and

const int col[8] = { 6,11,10,3,17,4,8,9 };

That is -  row 1 is Arduino pin number 2, row 2 is Arduino pin number 7, column 2 is Arduino pin number 11 and so on...

Oh ... I love lights....

No comments:

Post a Comment