Thursday, October 10, 2013

36 The 4-digit 7-segment LED

This is something I wanted to do for a long time, and as I am waiting for some parts (from China, again) and as I have a spare Arduino, I thought I would knock this up.  The project comes from Nathan Siedle, at http://www.hobbytronics.co.uk/arduino-countdown-timer.  Nathan's permission to use this work, is covered by what he describes as "beerware" - if you use it and you bump into him some time, you buy him a beer!  What a great license arrangement - this should be made into formal legislation!

Nathan warns that this method does not use current limiting resistors, so be careful you don't blow your display!  The code makes use of Pulse Width Modulation (PWM) to limit the current to the segments, and Persistence of Vision allows you to see the segments illuminated without being aware of the pulsing.  PWM is possible using certain Arduino pins, and in this case, Arduino Pins 6, 9, 10 and 11 are utilised.  These would normally be used with analogWrite() to get PWM, but Nathan uses digitalWrite() with a delay:

 149: delayMicroseconds(DISPLAY_BRIGHTNESS);

I think this is called Bit-banging, using a software technique rather than the available hardware capability, and this can be done on any of the Arduino's data pins - it doesn't have to be on the PWM pins 3, 5, 6, 9, 10 and 11 (which have the symbol ~ in front of them on the Arduino board).


The details of the display are as follows:
This model is a white light display, while mine is green.  These can be bought for less than £2 from most suppliers.  There are 8 pins (number 1 is the first one on the left that you can see above, and as with most chips, the numbers are sequential in an anti-clockwise direction). 

There are actually 35 LEDs in all, including 4x7 digits, a colon, an upper point  - apostrophe for use as an am/pm indicator if used in a clock) and a lower point for each digit.  These are annotated here:

The 35 LEDs are connected to the 16 pins as follows:



The common anodes of the digits DIG1, DIG2, DIG3 and DIG4 (minus the decimal points - DP) are connected to the PWM inputs of the Arduino.  The points L1 & L2,(colon) L3 (apostrophe) and decimal points DP1DP2DP3 and DP4 can be, but in this case, are not, connected.  So the display's pins 4 & 12 (colon common anodes and common cathodes respectively),  7 (decimal point common cathodes), 9 & 10 (apostrophe cathode and anode, respectively) are not used in this project.  Be careful here if you're tempted to connect them up, as the PWM requirements are unknown (to me).

Because these other features are not being used, Nathan's code has only programmed the digits with the characters 0 to 9, without decimal points, apostrophe and colon.  Note that there are some redundant lines of code which have been commented out.  The code, which also describes the wiring, is here:


1:    
2:  /*  
3:   6-13-2011  
4:   Spark Fun Electronics 2011  
5:   Nathan Seidle  
6:     
7:   This version modified by www.hobbytronics.co.uk as a countdown timer  
8:   Ideal as a simple egg timer or other timer. Easily add a buzzer  
9:     
10:   This code is public domain but you buy me a beer if you use this   
11:   and we meet someday (Beerware license).  
12:     
13:   4 digit 7 segment display:  
14:   http://www.sparkfun.com/products/9483  
15:   Datasheet:   
16:   http://www.sparkfun.com/datasheets/Components/LED/7-Segment/YSD-439AR6B-35.pdf  
17:    
18:   This is an example of how to drive a 7 segment LED display from an ATmega   
19:   without the use of current limiting resistors.  
20:   This technique is very common but requires some knowledge of electronics   
21:   - you do run the risk of dumping too much current through the segments   
22:   and burning out parts of the display. If you use the stock code you should   
23:   be ok, but be careful editing the brightness values.  
24:     
25:   This code should work with all colors (red, blue, yellow, green) but the   
26:   brightness will vary from one color to the next because the forward voltage   
27:   drop of each color is different. This code was written and calibrated for the  
28:   red color.  
29:    
30:   This code will work with most Arduino's but you may want to re-route some of  
31:   the pins.  
32:    
33:   7 segments  
34:   4 digits  
35:   1 colon  
36:   =  
37:   12 pins required for full control   
38:     
39:   */  
40:    
41:  int digit1 = 11; //PWM Display pin 1  
42:  int digit2 = 10; //PWM Display pin 2  
43:  int digit3 = 9; //PWM Display pin 6  
44:  int digit4 = 6; //PWM Display pin 8  
45:    
46:  //Pin mapping from Arduino to the ATmega DIP28 if you need it  
47:  //http://www.arduino.cc/en/Hacking/PinMapping  
48:  int segA = A1; //Display pin 14  
49:  int segB = 3; //Display pin 16  
50:  int segC = 4; //Display pin 13  
51:  int segD = 5; //Display pin 3  
52:  int segE = A0; //Display pin 5  
53:  int segF = 7; //Display pin 11  
54:  int segG = 8; //Display pin 15  
55:    
56:  int start_num=9999; // Number to countdown from  
57:  unsigned long time;  
58:    
59:  void setup() {          
60:   pinMode(segA, OUTPUT);  
61:   pinMode(segB, OUTPUT);  
62:   pinMode(segC, OUTPUT);  
63:   pinMode(segD, OUTPUT);  
64:   pinMode(segE, OUTPUT);  
65:   pinMode(segF, OUTPUT);  
66:   pinMode(segG, OUTPUT);  
67:    
68:   pinMode(digit1, OUTPUT);  
69:   pinMode(digit2, OUTPUT);  
70:   pinMode(digit3, OUTPUT);  
71:   pinMode(digit4, OUTPUT);  
72:     
73:   pinMode(13, OUTPUT);  
74:  }  
75:    
76:  void loop() {  
77:     
78:   //long startTime = millis();  
79:   if((millis()/1000) < start_num){  
80:    displayNumber(start_num -(millis()/1000));  
81:   }  
82:   else {  
83:    // reached zero, flash the display  
84:    time=millis();  
85:    while(millis() < time+200) {  
86:     displayNumber(0); // display 0 for 0.2 second  
87:    }  
88:    time=millis();    
89:    while(millis() < time+200) {  
90:     lightNumber(10); // Turn display off for 0.2 second  
91:    }  
92:   }   
93:   //while( (millis() - startTime) < 2000) {  
94:   //displayNumber(1217);  
95:   //}  
96:   //delay(1000);   
97:  }  
98:    
99:  //Given a number, we display 10:22  
100:  //After running through the 4 numbers, the display is left turned off  
101:    
102:  //Display brightness  
103:  //Each digit is on for a certain amount of microseconds  
104:  //Then it is off until we have reached a total of 20ms for the function call  
105:  //Let's assume each digit is on for 1000us  
106:  //If each digit is on for 1ms, there are 4 digits, so the display  
107:  //is off for 16ms.  
108:  //That's a ratio of 1ms to 16ms or 6.25% on time (PWM).  
109:  //Let's define a variable called brightness that varies from:  
110:  //5000 blindingly bright (15.7mA current draw per digit)  
111:  //2000 shockingly bright (11.4mA current draw per digit)  
112:  //1000 pretty bright (5.9mA)  
113:  //500 normal (3mA)  
114:  //200 dim but readable (1.4mA)  
115:  //50 dim but readable (0.56mA)  
116:  //5 dim but readable (0.31mA)  
117:  //1 dim but readable in dark (0.28mA)  
118:    
119:  void displayNumber(int toDisplay) {  
120:  #define DISPLAY_BRIGHTNESS 500  
121:    
122:  #define DIGIT_ON HIGH  
123:  #define DIGIT_OFF LOW  
124:    
125:   long beginTime = millis();  
126:    
127:   for(int digit = 4 ; digit > 0 ; digit--) {  
128:    
129:    //Turn on a digit for a short amount of time  
130:    switch(digit) {  
131:    case 1:  
132:     digitalWrite(digit1, DIGIT_ON);  
133:     break;  
134:    case 2:  
135:     digitalWrite(digit2, DIGIT_ON);  
136:     break;  
137:    case 3:  
138:     digitalWrite(digit3, DIGIT_ON);  
139:     break;  
140:    case 4:  
141:     digitalWrite(digit4, DIGIT_ON);  
142:     break;  
143:    }  
144:    
145:    //Turn on the right segments for this digit  
146:    lightNumber(toDisplay % 10);  
147:    toDisplay /= 10;  
148:    
149:    delayMicroseconds(DISPLAY_BRIGHTNESS);   
150:    //Display digit for fraction of a second (1us to 5000us, 500 is pretty good)  
151:    
152:    //Turn off all segments  
153:    lightNumber(10);   
154:    
155:    //Turn off all digits  
156:    digitalWrite(digit1, DIGIT_OFF);  
157:    digitalWrite(digit2, DIGIT_OFF);  
158:    digitalWrite(digit3, DIGIT_OFF);  
159:    digitalWrite(digit4, DIGIT_OFF);  
160:   }  
161:    
162:   while( (millis() - beginTime) < 10) ;   
163:   //Wait for 20ms to pass before we paint the display again  
164:  }  
165:    
166:  //Given a number, turns on those segments  
167:  //If number == 10, then turn off number  
168:  void lightNumber(int numberToDisplay) {  
169:    
170:  #define SEGMENT_ON LOW  
171:  #define SEGMENT_OFF HIGH  
172:    
173:   switch (numberToDisplay){  
174:    
175:   case 0:  
176:    digitalWrite(segA, SEGMENT_ON);  
177:    digitalWrite(segB, SEGMENT_ON);  
178:    digitalWrite(segC, SEGMENT_ON);  
179:    digitalWrite(segD, SEGMENT_ON);  
180:    digitalWrite(segE, SEGMENT_ON);  
181:    digitalWrite(segF, SEGMENT_ON);  
182:    digitalWrite(segG, SEGMENT_OFF);  
183:    break;  
184:    
185:   case 1:  
186:    digitalWrite(segA, SEGMENT_OFF);  
187:    digitalWrite(segB, SEGMENT_ON);  
188:    digitalWrite(segC, SEGMENT_ON);  
189:    digitalWrite(segD, SEGMENT_OFF);  
190:    digitalWrite(segE, SEGMENT_OFF);  
191:    digitalWrite(segF, SEGMENT_OFF);  
192:    digitalWrite(segG, SEGMENT_OFF);  
193:    break;  
194:    
195:   case 2:  
196:    digitalWrite(segA, SEGMENT_ON);  
197:    digitalWrite(segB, SEGMENT_ON);  
198:    digitalWrite(segC, SEGMENT_OFF);  
199:    digitalWrite(segD, SEGMENT_ON);  
200:    digitalWrite(segE, SEGMENT_ON);  
201:    digitalWrite(segF, SEGMENT_OFF);  
202:    digitalWrite(segG, SEGMENT_ON);  
203:    break;  
204:    
205:   case 3:  
206:    digitalWrite(segA, SEGMENT_ON);  
207:    digitalWrite(segB, SEGMENT_ON);  
208:    digitalWrite(segC, SEGMENT_ON);  
209:    digitalWrite(segD, SEGMENT_ON);  
210:    digitalWrite(segE, SEGMENT_OFF);  
211:    digitalWrite(segF, SEGMENT_OFF);  
212:    digitalWrite(segG, SEGMENT_ON);  
213:    break;  
214:    
215:   case 4:  
216:    digitalWrite(segA, SEGMENT_OFF);  
217:    digitalWrite(segB, SEGMENT_ON);  
218:    digitalWrite(segC, SEGMENT_ON);  
219:    digitalWrite(segD, SEGMENT_OFF);  
220:    digitalWrite(segE, SEGMENT_OFF);  
221:    digitalWrite(segF, SEGMENT_ON);  
222:    digitalWrite(segG, SEGMENT_ON);  
223:    break;  
224:    
225:   case 5:  
226:    digitalWrite(segA, SEGMENT_ON);  
227:    digitalWrite(segB, SEGMENT_OFF);  
228:    digitalWrite(segC, SEGMENT_ON);  
229:    digitalWrite(segD, SEGMENT_ON);  
230:    digitalWrite(segE, SEGMENT_OFF);  
231:    digitalWrite(segF, SEGMENT_ON);  
232:    digitalWrite(segG, SEGMENT_ON);  
233:    break;  
234:    
235:   case 6:  
236:    digitalWrite(segA, SEGMENT_ON);  
237:    digitalWrite(segB, SEGMENT_OFF);  
238:    digitalWrite(segC, SEGMENT_ON);  
239:    digitalWrite(segD, SEGMENT_ON);  
240:    digitalWrite(segE, SEGMENT_ON);  
241:    digitalWrite(segF, SEGMENT_ON);  
242:    digitalWrite(segG, SEGMENT_ON);  
243:    break;  
244:    
245:   case 7:  
246:    digitalWrite(segA, SEGMENT_ON);  
247:    digitalWrite(segB, SEGMENT_ON);  
248:    digitalWrite(segC, SEGMENT_ON);  
249:    digitalWrite(segD, SEGMENT_OFF);  
250:    digitalWrite(segE, SEGMENT_OFF);  
251:    digitalWrite(segF, SEGMENT_OFF);  
252:    digitalWrite(segG, SEGMENT_OFF);  
253:    break;  
254:    
255:   case 8:  
256:    digitalWrite(segA, SEGMENT_ON);  
257:    digitalWrite(segB, SEGMENT_ON);  
258:    digitalWrite(segC, SEGMENT_ON);  
259:    digitalWrite(segD, SEGMENT_ON);  
260:    digitalWrite(segE, SEGMENT_ON);  
261:    digitalWrite(segF, SEGMENT_ON);  
262:    digitalWrite(segG, SEGMENT_ON);  
263:    break;  
264:    
265:   case 9:  
266:    digitalWrite(segA, SEGMENT_ON);  
267:    digitalWrite(segB, SEGMENT_ON);  
268:    digitalWrite(segC, SEGMENT_ON);  
269:    digitalWrite(segD, SEGMENT_ON);  
270:    digitalWrite(segE, SEGMENT_OFF);  
271:    digitalWrite(segF, SEGMENT_ON);  
272:    digitalWrite(segG, SEGMENT_ON);  
273:    break;  
274:    
275:   case 10:  
276:    digitalWrite(segA, SEGMENT_OFF);  
277:    digitalWrite(segB, SEGMENT_OFF);  
278:    digitalWrite(segC, SEGMENT_OFF);  
279:    digitalWrite(segD, SEGMENT_OFF);  
280:    digitalWrite(segE, SEGMENT_OFF);  
281:    digitalWrite(segF, SEGMENT_OFF);  
282:    digitalWrite(segG, SEGMENT_OFF);  
283:    break;  
284:   }  
285:  }  


And here's the result of my version which counts down from 9999 seconds and then flashes the display:


Thanks Nathan - clever stuff - there's a pint of Guinness for you when we meet at my local pub!  (This is now known locally as a GuinnessWare License).

No comments:

Post a Comment