I apologise again for not posting more recently. I have been suffering from a really virulent chest infection which has held me back a bit!
I was in the middle of constructing one of the display sections of the Chess clock on matrix board. It is nearly complete but I have had terrible trouble making it. Matrix board is ok providing you have a simple layout. The circuit that we are trying to construct with separate seven segment displays and four separate shift registers is not a simple layout. Upon reflection I should have had PCBS professionally made and my problems would have been solved. I didn't and I have a three quarters complete mess of wires and chip holders on some matrix board! I will complete and get it working but I don't think I'll be making another in that fashion. It was a nightmare!!
So what do we do now? The faint hearted quit and go and do something else...hopefully something more profitable! The stubborn and courageous (thats me!!) find another solution.....Browsing the various websites available and looking at some of the other technical blogs online; various other people have done things slightly differently. Most people have used a combined 4x Seven Segment display which gets rid of a lot of the circuit layout issues.
Check out the following sites for inspiration:
http://proto-pic.co.uk/categories/LCD-and-LED-Displays/ - Where displays can be obtained for a very reasonable price....
http://tronixstuff.wordpress.com/2010/07/07/getting-started-with-arduino-chapter-twelve/ - Jon Boxall's blog which is excellent by the way...better than mine at the moment!
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265669651 - The arduino forum on common anode seven segment LED displays
So I bought two of these 4x Seven Segment displays and began working out how they tick. They come from China as do most electronic components these days and there is a datasheet available. As these parts are stocked by Sparkfun I will link to their page from here:
Helpfully sparkfun already also have an eagle footprint created so we can draw a schematic and once the circuit is complete and working layout another PCB! This time things should be much more simple to achieve....less wires equals less of a layout and construction headache...(I say this now...later I may change my mind!!)
So it was while I was looking at the device on sparkfun's website and reading some of the tutorial pages available when I noticed an issue....well not really an issue but a difference from the other displays we have been using. For starters these are common anode displays, meaning that the positive input to the diodes are connected together; our previous displays were common cathode. The other issue is that these displays have to have each segment driven separately. For those who are interested these displays are quite easy to get up and running....here is an excellent tutorial which I used at first myself just to ensure the displays were working:
http://allaboutee.com/2011/07/09/arduino-4-digit-7-segment-display-tutorial/
The article goes through all of the important bits of information....and gets the general ideas and concepts across in order to get this display up and running. The issue is that in order to drive this display fully 14 control pins will be needed from our microcontroller of choice. I'm using an arduino development system but any micro will need 14 pins....This is a lot of pins to sacrifice just to drive one display and as I need two of these for my chess clocks this isn't an option. Ok...as in previous posts....I'll just use shift registers. I got the parts needed and set up the circuit as shown on Jon Boxall's blog.
Parts needed:
1x Microcontroller and development area - An arduino and breadboard
1x Serial cable for programming and powering the arduino
2x 74HC595 shift registers
4x BC548 transistors
4x 1k resistors
9x 220R resistors
Lots of connector wires
Lots of patience and a sense of humour!
Here is the applicable chapter from Jon's blog
http://tronixstuff.wordpress.com/2010/07/07/getting-started-with-arduino-chapter-twelve/
As can be seen things are a little different. This is because in we are going to have to multiplex the display in order to get all four displays on at the same time. This uses a persistence of vision technique that I didn't use in the previous circuits. The seven segments are turned on as before by the appropriate binary code number being sent serially to the first shift register. The second shift register controls which segment is on and we will write the control software to multiplex the segments (flash the segments at a high refresh rate to make it look like they are all on at the same time.....in reality they aren't but this isn't visible to the naked eye so it isn't an issue.
Here is the circuit diagram. I am using a common anode display so my transistors are controlling the anodes of the display with the emitter pins unlike Jon's circuit where he had a common cathode display and was controlling the collectors. My transistors are controlling when the pins go HIGH and Jon's were controlling when the pins go LOW....
So....I connected up the circuit and commenced with the writing of the control software....I had already looked at Jon's code...assumed I understood it and figured it wouldn't be too difficult to make a fork of it to control mine. For some reason life is never simple and it took me ages to get this working....I must be losing my edge!! I found it very difficult to get each display on correctly and update properly. My biggest mistake was that I was trying to turn the display completely on and off when I updated the display with a new character. I now know and understand that it isn't possible to do this when using a persistence of vision multiplexed display!
Here is the code....As it is quite a big program I'm giving a link.
By the way Git hub - great idea but a massive pain to use for the neophyte.
I accept this code is horrible to look at for someone else....so I will break the program down into what it does in each section and that should make it easier to understand. Please feel free to comment or email me if there is something that is not clear.
I decided just for fun to make the circuit into a clock getting the current time from my computer via the serial programming cable.
The first section tells the compiler to include two libraries and set up some variables ready to store information. The comments provided give most of the detail of what is going on. Like in the previous posts I am using a couple of arrays to store the information which turns on the required LEDS to create the characters and I also am using a small array to store the information for which cell is currently active. The other variables are for counting and to tell the compiler which microprocessor pins are being used in this circuit.
//External libraries for getting the current PC Time
//over the serial connection
#include <Wire.h>
#include "RTClib.h"
//Function for getting the current system time
RTC_Millis RTC;
//Count variables
int i=0;
int j=0;
// Pin connected to ST_CP (pin 12) of 74HC595
int latchPin = 8;
// Pin connected to SH_CP (pin 11 of 74HC595
int clockPin = 12;
// Pin connected to DS (pin 14) of 74HC595
int dataPin = 11;
/* initialise a four element array which turns the transistors on
to control which segment is active */
int segmentSelect[4]= { 1,2,4,8 };
/* Initialise a One Dimensional integer array with
the values for 0 - 9 on the Seven Segment LED Display */
int seven_seg_digits[10]={ 192,249,164,176,153,146,130,248,128,152 };
/*
without decimal point(s)
{ dp,g,f,e,d,c,b,a },
{ 1,1,0,0,0,0,0,0 }, // = 192 in decimal - common anode
{ 1,1,1,1,1,0,0,1 }, // = 249 in decimal - common anode
{ 1,1,0,0,0,0,0,1 }, // = 164 in decimal - common anode
{ 1,0,1,1,0,0,0,0 }, // = 176 in decimal - common anode
{ 1,0,0,1,1,0,0,1 }, // = 153 in decimal - common anode
{ 1,0,0,1,0,0,1,0 }, // = 146 in decimal - common anode
{ 1,0,0,0,0,0,1,0 }, // = 130 in decimal - common anode
{ 1,1,1,1,1,0,0,0 }, // = 248 in decimal - common anode
{ 1,0,0,0,0,0,0,0 }, // = 128 in decimal - common anode
{ 0,1,1,0,0,0,0,1 }, // = 152 in decimal - common anode
// Just for further reference here are the
// separate segment connections
segment a = 14
segment b = 16
segment c = 13
segment d = 3
segment e = 5
segment f = 11
segment g = 15
d.p. = 7
*/
// constants won't change. Used here to
// set pin numbers:
// Arduino Pin 9 is connected to the colon LED pin on the Seven Segment
const int colonPin = 9;
// Variables will change:
int colonLedState = LOW; // colonLedState used to set the LED
long previousMillis = 0; // will store last time the Colon LED was updated
/*
The following variables is a long because the time, measured in miliseconds,
will quickly become a bigger number than can be stored in an int.
*/
long interval = 1000;
//Integer variables to store the value(s) of the character that we wish to display on the Seven Segment
int firstDigit=0;
int secondDigit=0;
int thirdDigit=0;
int fourthDigit=0;
****************************************************************
The next section of code is a small function which clears the display completely by turning off the transistors which are controlling the cell anodes. It does this by loading the binary number 0000 0000 0000 into the shift registers which causes the anode pins to be low and therefore not powered.
void clearDisplay() {
/*
Take the latch pin of the shift register(s) low. shift zero serially into the shift register(s) which turns the transistors off
and then take the latch pin of the shift register high to re-latch with the new 'zero' data
*/
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, 0);
shiftOut(dataPin, clockPin, MSBFIRST, 0);
digitalWrite(latchPin, HIGH);
}
***************************************************************
The next section is the setup function. This part of the program tells the compiler which pins are being used as outputs and to start the serial listener. It also grabs the current PC system time over the serial link. We will use this as the display data for the seven segment so that we have something meaningful to display. I'm going to change this for the chess clocks as we want a count down timer in that situation...not a real time clock! The setup function is only called once, so whatever needs setting up in order for our device to correctly function needs to be done here...
void setup() {
// set the arduino pins to output so you
// can control the shift register(s)
// and the colon LED segment
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(colonPin, OUTPUT);
//Clear all the Display
clearDisplay;
//Turn on the serial Monitor - useful for debugging!
Serial.begin(57600);
Serial.println("Ready");
//Get the current system time via the serial port
RTC.begin(DateTime(__DATE__, __TIME__));
}
****************************************************************
The next section is the main loop function. This is the part of the program that is constantly run. It calls the other functions which get the circuit to function as we designed...In this case it calls the next function which is the display function:
void loop() {
// Call the display function
displayNumber();
}
****************************************************************
The code below is the main heart of the program and hopefully is explained by the text following it!
/*
Using an interrupt constantly display characters on the display which are taken from the system clock
Display these characters by rapidly sweeping from right to left to make it seem as though the characters are constantly on....Persistence of vision technique
*/
void displayNumber() {
#define DISPLAY_BRIGHTNESS 1500
long beginTime = millis();
for(int digit = 4 ; digit > 0 ; digit--) {
//Turn on a digit for a short amount of time
switch(digit) {
case 1:
displayDigitOne();
break;
case 2:
displayDigitTwo();
break;
case 3:
displayDigitThree();
break;
case 4:
displayDigitFour();
break;
}
//Display this digit for a fraction of a second (between 1us and 5ms)
delayMicroseconds(DISPLAY_BRIGHTNESS);
//Turn on all segments
updateDisplay();
}
//Wait for 20ms to pass before we paint the display again
while( (millis() - beginTime) < 10) ;
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the Colon LED
previousMillis = currentMillis;
if (colonLedState == LOW)
colonLedState = HIGH;
else
colonLedState = LOW;
// set the colon LED with the State of the variable:
digitalWrite(colonPin, colonLedState);
}
}
****************************************************************
This is the section of code that I found the hardest to write and after looking at other people's code and explanations at first I still didn't get it! So what is going on here:
the first line of code is a statement of definition - where the compiler sees DISPLAY_BRIGHTNESS written it will use the value 1500....what the program does here is set the refresh rate for the 4x Seven Segment Display. Just like your computer monitor or television's refresh rate our display will flicker on between each segment every 1500 micro-seconds.
The next line is a variable declaration that stores the amount of time the program has been running. We will use this information as a starting point to get the microcontroller to perform certain functions such as updating each display in turn....
After that we have a For to loop combined with a switch statement. What this section of code does is turn on each segment in turn from right to left from four to one calling a separate function which contains the information we wish to display. This information is written to the display for 20ms before it is then updated with a new a possibly different character to display.
The final section is a fork of the blink without delay example from the arduino forum. We are using it in this case to flash the colon on the display every second so that it looks like a clock! Again we are marking the point at which this section of code has been run and then after a second has passed we toggle the state of the colon led which is connected to pin 9 of the arduino.
The above code section is an example of using an interrupt. The code above constantly runs no matter what else is going on in the program. The reason we need to do this is because if we interrupt the display it will look flickery and dull...and as I found all the time while I was trying to get this working the last segment is wonderfully bright and all the others are barely on.....
*********************************************************************************
The next function is quite a simple one...all it does is grab the current system time from the serial connection and then by using some simple maths tricks send the required number to each of the displays in turn. The time stamp is obtained in sections. The hour will be a two digit number from zero to 23. If we divide this number by ten we get a number and a remainder. If we drop the remainder (3) we have the first number we wish to display on the first segment. For segment two we display the remainder 3. The minute functions are obtained in the same way
Get the current time from the serial connection - DateTime now = RTC.now();
divide the hours value - for example 16 by ten and we have 1 remainder 6
store the value 1 for the first segment - firstDigit=now.hour()/10;
store the remainder value 6 to the second segment - secondDigit=now.hour()%10;
divide the minutes value for example 35 by ten and we have 3 remainder 5
store the value 3 for the third segment - thirdDigit=now.minute()/10;
store the remainder value 5 to the fourth segment - fourthDigit=(now.minute()%10);
/*
Using the current PC time as a data source, set the segments to display the time by passing the current time as variables to the segments
*/
void updateDisplay() {
DateTime now = RTC.now();
firstDigit=now.hour()/10;
secondDigit=now.hour()%10;
thirdDigit=now.minute()/10;
fourthDigit=(now.minute()%10);
}
****************************************************************
This next section of code deals with sending the required character to the required segment via the shift register serially. The latch pin of the shift register is taken low. The required character is shifted into the register serially from the position highlighted in the character array. The required segment is then selected and finally the data is then latched into the shift register by taking the latch pin high. Each of the segments are treated in the same way but they all have their own function.
void displayDigitOne() {
//take the latch pin of the shift register(s) low and shift in
//the data for the required character, then turn the correct //transistor
//for the first segment on which turns the segment on
//then latch the shift register by taking the latch pin HIGH
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, seven_seg_digits[firstDigit]);
shiftOut(dataPin, clockPin, MSBFIRST, segmentSelect[0]);
digitalWrite(latchPin, HIGH);
}
********************************************************************************
And now finally for the ubiquitous video showing that this all works and it isn't some great pretence! Enjoy people and happy holidays. Next up will be setting the time using a rotary enoder and then finally we will turn the clocks into fischer count down chess clocks. Then hopefully I will get all of it permanently constructed and into some sensible and reasonable looking housings and give them away! I might make two just in case....i'm growing quite attached to the idea!
Hi mate, tried your code by I get an "Error compiling"
ReplyDeleteMore detail:
RTClib\RTClib.cpp.o: In function `date2days':
C:\Users\Riley\Documents\Arduino\libraries\RTClib/RTClib.cpp:26: multiple definition of `i'
_7_Segment.cpp.o:(.bss.i+0x0): first defined here
Any chance you could help me out?
Riley,
DeleteI'm not sure what is going on here. It compiles perfectly on my own setup. I suspect the RTC library is now out of date when used with the new versions of the arduino ide. I tried compiling the code on the latest version of the arduino ide 1.01 and it's throwing lots of errors up. I don't currently have access to the chess clocks but I will in a couple of hours. I think I may need to re-write the code using an updated real time clock library. I will get something working and get back to you.
It would be helpful if you could let me know which version of the arduino ide you are using and also if possible send me your code so that I can look through it.
Cheers
Alex
Hi Riley,
DeleteFixed the problem. I reproduced your error on the latest version of the arduino ide and found the issue. The problem is with using the letter i as a variable name. It's something that most coders are taught to do but it is a bad habit to get into! I used the letter i and the letter j as a variable name declared at the top of my code. I didn't use these variables in most of my program. I use them in the last function called updateSegmentValueSerial(). As this function isn't being called in the program loop (because I'm not using it) it can be removed. Delete the entire function and then remove the variable declarations for 'i' and 'j' and the code will compile.
So to be clear:
delete or comment out the following lines from your code:
int i=0;
int j=0;
Next delete or comment out the updateSegmentValueSerial function as you don't need it.
/* - add this to the start of the function
void updateSegmentValueSerial(){
if (Serial.available() > 0) {
// read the incoming byte:
innerloop = Serial.read();
// say what you got:
Serial.print("Serial Data received: ");
Serial.println(innerloop);
if (innerloop == '0' || innerloop == '0')
{
outerloop=0;
}
else if (innerloop == '1' || innerloop == '!')
{
outerloop=1;
}
else if (innerloop == '2' || innerloop == '@')
{
outerloop=2;
}
else if (innerloop == '3' || innerloop == '#')
{
outerloop=3;
}
else if (innerloop == '4' || innerloop == '$')
{
outerloop=4;
}
else if (innerloop == '5' || innerloop == '%')
{
outerloop=5;
}
else if (innerloop == '6' || innerloop == '^')
{
outerloop=6;
}
else if (innerloop == '7' || innerloop == '&')
{
outerloop=7;
}
else if (innerloop == '8' || innerloop == '*')
{
outerloop=8;
}
else if (innerloop == '9' || innerloop == '(')
{
outerloop=9;
}
}
}
*/ add this to the end of the function
the code will now compile!!!
This all assumes you don't want to use the updateSegmentValueSerial function to manually set the number displayed on a segment. If you do we need to re-write the code with different variable names. In that case rename i and j to something else and change all the references to i and j in the updateSegmentValueSerial function. You will then need to add a call to the function in the loop function.
Hope this helps and you understand what has happened.
Cheers
Alex
Made the changes and it compiles ok, however the behavior of the 7 segments display is not what I would expect.
DeleteI made a video of it as it's a bit hard to explain: http://youtu.be/lfpmyNWyxbw
I've speed it up by 4x, and the current time on my computer as about 1:47pm I believe.
Riley,
DeleteI've commented on your video as well. I'm really pleased to see someone is reading my blog and trying my circuits! I think there may be an issue with your circuit. Have you built this circuit on a breadboard like I did? If so you need to check your wiring. I would use the previous posts to check that all of the displays are working correctly - make each segment count from 0-9 and ensure that all of them are at the same brightness. If that doesn't work please send me your code and I will test it on my setup. If you can email me a link to your code and schematic and I will take a look...
Cheers
Alex
Riley!
DeleteI have found what the problem is with your circuit. It was my fault and I'm sorry. The schematic diagram I posted was incorrect. I must have drawn it from memory when I was writing the post and that was a mistake. The shift register (labelled IC3 in the diagram above) needs it's pin 9 connecting to pin 14 of the second shift register (labelled IC4 in the diagram above). Then pin 9 of IC4 needs to be disconnected from anything (left floating). All other connections remain the same. My sincerest apologies for posting an incorrect diagram - I have updated the diagram and tested the circuit and it does work. Please check out my video -
http://youtu.be/onuOeDP1lVU
I would be very interested in seeing a video of your circuit working correctly! I would also like to know if there is any particular topics you would like me to post about. I am currently finishing off a section on linear power supplies and should really finish these chess clocks off. Next I was thinking of talking about switch-mode power supplies. What do you think?
Cheers
Alex
Just re-wired and it now displays a read-able time.
ReplyDeleteIt says 7:11pm on my computer, and 13:17 on the arduino display.
It seem to reset back to 13:16 every time I restart the arduino, and then count up from there.
As for topics I would like to see, maybe using the seven segment display for other things, like a count down timer, or stop watch?
DeleteI'm looking mostly at robotic stuff at the moment so maybe how to control, or send information to a robot via RF or Xbee, or even from a RC controller?
Also I've seen quite a few tutorials on how to get metric numbers from sensors such as range finders or temp sensors, but I haven't seen many that show you how to use this data in real life projects other then just getting it to right to console or a display.
This current guide has been very usefull to me as just about every guide I could find on powering a 4x7 segment display with shift registers used common cathode displays.
The circuit takes the start time from the time the arduino was flashed. You flashed your arduino at 13:17 NZ time. As soon as you remove the USB lead and power to the arduino it will start counting again at 13:17. I only really did the clock as an example of something that could be displayed. In order to this circuit keep good time you will always have to keep it connected to your computer or....power the arduino via an external PSU and then program the arduino and remove the USB programming cable (keep the power connected) It will keep excellent time....until the power is turned off!
DeleteTo make a proper clock we would need to add a real time clock module with a battery backup (There are circuits for doing this all over the net although I can do a post on one if you like - DS1307 is one of the most popular real time clock devices.)
Just for you I will do a post on how to interface the seven segments to circuits to provide output. The lists are endless - Stopwatch, egg timer, volt meter / current meter - any 4 digit display, thermometer etc....I will do a post on how to use the seven segments to display a temperature next...
Cheers for reading the blog and for the feedback
Alex