Monday, November 14, 2011

Sparkfun Inventor's Kit Circuit 05: More LEDs

 

Since I'm impatient to get my marine robot working and know this means mastering code for motors and servos, I was at first less than enthusiastic about going back to working with LEDs. But then I remembered that the clever structuring of the Sparkfun Inventor's Kit tutorials demonstrates that what you can do with LEDs you can then do with motors and servos, i.e., the code principles are remarkably similar, as is the code itself. And that insight alone is worth a lot.

So today we are looking at the use of an Integrated Circuit.  On first read the tutorial seems alien and intimidating "To use it you "clock in" the data and then lock it in (latch it). To do this you set the data pin to either HIGH or LOW, pulse the clock, then set the data pin again and pulse the clock repeating until you have shifted out 8 bits of data. then you pulse the latch and the 8 bits are transferred to the shift register pins."
Huh?  Say what?
Thank God they then say "It sounds complicated but is really simple once you get the hang of it." Newbies like me (and the target student group I represent) need reassurances like this to keep our confidence up as we plunge into these new and for us uncharted waters.

It is a cliche, of course, but it turns out that everything sounds complicated at first to those who didn't grow up swimming in those waters, and everything appears simple once you really get the hang of it.  In fact I would go so far as to say that one of the reasons STEM subjects (science, technology, engineering, and mathematics) seem so hard is because once people who have struggled through the learning curve do get the hang of it, the subjects seems so simple that we rarely back up and try to simplify the path it took us to get there so that others can make it through without stumbling or bumping into unnecessary roadblocks.

There is also a tendency I've observed in human nature to resist making our hard-won insights freely available to others.  As one teacher I knew put it, "I struggled  hard to make it, it was tough for me to make C's let alone A's in science.  If you think I'm going to suddenly make it easy on these kids, you're nuts. They gotta suffer and earn it like I did."

I liken this to the a treasure hunter who spends years without a map searching for a mysterious hidden treasure island.  When he finally gets there he discovers that the island's treasures are actually so abundant that they could be shared with everybody and that the island is also actually rather easy to get to, once you know where it is.  But he keeps the location a secret, refuses to share the map he made, and suggests that only those who spend the same number of years searching should get the rewards available in this "promised land".

Our intent in this program is to be mindful of the idea that "once you get the hang of robotics it is really rather simple" and make sure that we work on  providing a good, clear roadmap so that others don't have to struggle to get where we got.  This is the role of the explorer and the pathfinder in our opinion.

So this odyssey is about making things simple for those who come after. It is about translating the arcane into common language.

During the years I was with our Harvard Acapella group "The Harvard Squares"  and our West Coast musical group "The Quintessentials", my Wall Street financial investment specialist friend and fellow songster Paul Sagawa  used to jokingly  say to the crowds, "Ladies and Gentlemen, we are the group that sings so that YOU DON'T HAVE TO!".

We will apply the same logic to our PORPOISE program: We are the group that struggles through the arcane and obscure and difficult parts of the technical  manuals so that YOU DON'T have to!"   We will dedicate ourselves to figuring out where things seem complicated and do our best to making it user friendly and fun.

This personal blog is a mere log of the journey we are taking to get there!

So what about the IC chip journey?

Well, before we can decipher what it means to "clock in" data and "lock it in (latch it") we have to get our "feet wet" and our "hands dirty" and actually build the circuit and play around with it -- that is the best strategy in any event for anything!

The key here seems to be an understanding that the function of adding the 74HC595 Shift Register IC to the Arduino is that it "will give you an additional 8 outputs (to control LEDs and motors and Servos) using only three Arduino pins".  So it gives expanded capabilities.  And "They can also be linked together to give you a nearly unlimited number of outputs using the same four pins".

The 74HC595 Shift Register IC is also known as a "serial to parallel converter" which gives a good indication of its function, getting through the serial bottleneck and enabling us to control a much larger array of actuators and servos and motors without having to use additional microcontrollers.

That is great news, particularly considering that this chip costs a couple dozen cents, while the chip on the Arduino costs a few dollars.

The manual says, encouragingly, "once you're comfortable playing around with it and its datasheet (available online http://ardx.org/datasheet/IC-74HC595.pdf  )the world of chips will be your oyster."  An additional resource is the "in depth look at how a shift register works found at http://ardx.org/SHIF; this just takes you to a wikipedia entry which I didn't find that useful at this stage of knowledge. 

The datasheet may not be immediately helpful to the novice but one thing that can be appreciated is how the IC resembles the Arduino board itself in that some of the leads are datapins, and there is one labeled grnd for Ground (pin 8) and one labeled Vcc ( pin 16) for supply voltage.  This can also be seen on the supplied pattern for Circuit 05. Pin 14 is for Data, pin 13 for output, pin 12 for this mysterious thing called "Latch", pin 11 for Clk, which must be "Clock" and pin 10 for Reset. Pins 1 through 7 and Pin 9 and pin 15 are for Q parameters, whatever they are (Q1 is usually a designator for a transistor, so maybe that is what they refer to here?). 

 


In terms of building, this one should be simple but what is tricky is that the pattern layout doesn't fit all that well on the breadboard, making you have to guess a bit to make the squares on the layout sheet line up with the breadboard beneath.  It helps in any case to learn to read and understand the Schematics provided, even though they sometimes don't see to correspond well to the real world wiring. The Schematic tells you how things really need to connect and helps you understand why the breadboard is wired the way it is. Ultimately we will have to work off of schematics completely with no helpful pattern layouts as our crutches or training wheels and this is the time to start understanding how breadboard wiring and schematics correspond.

Note that Pin 4 on the Arduino goes to Latch on the IC, Pin 3 to Clock and Pin 2 to Data, and that the 0 - 7 leads go to the LED's and resistors, going through the positive side of the LED whose negative side goes through the resistor to ground. The trickiest part is noting that IC lead 15, which is Q0, just underneath lead 16 which is the Vcc lead going to the positive rail, goes to the top LED on the left and then you start using the leads from 1 through 7 which correspond to the rest of the LEDs, so you have some crossed wires. IT gets a bit messy in real life, despite how nice and neat it looks on the Schematic.

Reset goes to the positive rail and ground, of course, goes to ground.

Hooking this messy circuit up makes one appreciative of the clean-ness of a well made PCB board.



The code is downloaded from http://ardx.org/CODE05 but is reproduced here to save you a step:



/*     ---------------------------------------------------------
 *     |  Arduino Experimentation Kit Example Code             |
 *     |  CIRC-05 .: 8 More LEDs :. (74HC595 Shift Register)   |
 *     ---------------------------------------------------------
 *
 * We have already controlled 8 LEDs however this does it in a slightly
 * different manner. Rather than using 8 pins we will use just three
 * and an additional chip.
 *
 *
 */


//Pin Definitions
//Pin Definitions
//The 74HC595 uses a serial communication
//link which has three pins
int data = 2;
int clock = 3;
int latch = 4;

//Used for single LED manipulation
int ledState = 0;
const int ON = HIGH;
const int OFF = LOW;
                       

/*
 * setup() - this function runs once when you turn your Arduino on
 * We set the three control pins to outputs
 */
void setup()
{
  pinMode(data, OUTPUT);
  pinMode(clock, OUTPUT); 
  pinMode(latch, OUTPUT); 
}

/*
 * loop() - this function will start after setup finishes and then repeat
 * we set which LEDs we want on then call a routine which sends the states to the 74HC595
 */
void loop()                     // run over and over again
{
  int delayTime = 100; //the number of milliseconds to delay between LED updates
  for(int i = 0; i < 256; i++){
   updateLEDs(i);
   delay(delayTime);
  }
}



/*
 * updateLEDs() - sends the LED states set in ledStates to the 74HC595
 * sequence
 */
void updateLEDs(int value){
  digitalWrite(latch, LOW);     //Pulls the chips latch low
  shiftOut(data, clock, MSBFIRST, value); //Shifts out the 8 bits to the shift register
  digitalWrite(latch, HIGH);   //Pulls the latch high displaying the data
}

/*
 * updateLEDsLong() - sends the LED states set in ledStates to the 74HC595
 * sequence. Same as updateLEDs except the shifting out is done in software
 * so you can see what is happening.
 */
void updateLEDsLong(int value){
  digitalWrite(latch, LOW);    //Pulls the chips latch low
  for(int i = 0; i < 8; i++){  //Will repeat 8 times (once for each bit)
  int bit = value & B10000000; //We use a "bitmask" to select only the eighth
                               //bit in our number (the one we are addressing this time through
  value = value << 1;          //we move our number up one bit value so next time bit 7 will be
                               //bit 8 and we will do our math on it
  if(bit == 128){digitalWrite(data, HIGH);} //if bit 8 is set then set our data pin high
  else{digitalWrite(data, LOW);}            //if bit 8 is unset then set the data pin low
  digitalWrite(clock, HIGH);                //the next three lines pulse the clock pin
  delay(1);
  digitalWrite(clock, LOW);
  }
  digitalWrite(latch, HIGH);  //pulls the latch high shifting our data into being displayed
}


//These are used in the bitwise math that we use to change individual LEDs
//For more details http://en.wikipedia.org/wiki/Bitwise_operation
int bits[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
int masks[] = {B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111};
/*
 * changeLED(int led, int state) - changes an individual LED
 * LEDs are 0 to 7 and state is either 0 - OFF or 1 - ON
 */
 void changeLED(int led, int state){
   ledState = ledState & masks[led];  //clears ledState of the bit we are addressing
   if(state == ON){ledState = ledState | bits[led];} //if the bit is on we will add it to ledState
   updateLEDs(ledState);              //send the new LED state to the shift register
 }


When I upload the code... Al Hamdu'Lillah! It works, lighting up all the red LEDs in various sequences!
What we are told is that it is counting up in binary.  



 If you had trouble figuring this one out, because of the crossed wires and stuff, they have kindly posted a video at http://ardx.org/VIDE05



 Now to look at the code and figure out what it does!

The manual suggests we experiment with controlling individual LEDs saying that the eight LED states "are stored in on byte (an 8 bit value) and giving details on how this works (i.e. the history of bit-math)  at http://ardx.org/BINA and pointing us to http://ardx.org/BITW for more details on bitwise maths and the set of operators that help an Arduino manipulate bits (the key lesson here is the sites statement  "On the simple low cost digital processors used in many embedded systems, bitwise operations are typically several times as fast as multiplication, sometimes also significantly faster than addition, at least in certain technical circumstances. On the other hand, modern processors in high performance segments usually perform addition and multiplication as fast as bitwise operations[1] but the latter typically occupy less machine resources and may therefore be more optimal for overall performance and/or power consumption. Division is normally very slow compared to bitwise operations."    Since we are dealing with low cost boards without much memory, bitwise math helps us to do more with less.

The tutorial instructs us to change this:

void loop()                     // run over and over again
{
  int delayTime = 100; //the number of milliseconds to delay between LED updates
  for(int i = 0; i < 256; i++){
   updateLEDs(i);
   delay(delayTime);
  }
}

To this:

void loop()                     // run over and over again
{
  int delayTime = 100; //the number of milliseconds to delay between LED updates
  for(int i = 0; i < 8; i++){
   changeLED(i, ON);
   delay(delayTime);
  }
  for(int i = 0; i < 8; i++){
   changeLED(i, OFF);
   delay(delayTime);
}
}
(To do this, my technique, rather than writing over the original code, is to highlight the original and then comment it out using Edit>Comment and then paste the revision underneath. Then I can get back to the original by highlighting and going to Edit>Uncomment).

When this modification is uploaded the LEDs light up and go off in sequence.

The final thing the manual suggests we try is to go back to the code from CIRC02 where we changed the LEDs using digitalWrite(led,state) (meaning something like digitalWrite(1edPins[7], HIGH); ).  They suggest we can use the animations from CIRC02 by copying the code into this sketch and changing all the digitalWrite()'s to changeLED()'s.    This will become the essence of the way we use Arduino's as beginners. No students are expected to be able to write their own code before they can get into programming their robots.  Instead we copy code from other powerful applications and mod it to do what we want.  The trick is in knowing what and how to modify, not being a great code writer. Not at this stage of the game! And you can go far with this copy-paste-modify routine if you learn how to spot what to change.

In this case, the pieces I will copy in to this IC chip sketch are

/*
 * loop() - this function will start after setup finishes and then repeat
 * we call a function called oneAfterAnother(). if you would like a different behaviour
 * uncomment (delete the two slashes) one of the other lines
 */
void loop()                     // run over and over again
{
  oneAfterAnotherNoLoop();   //this will turn on each LED one by one then turn each off
  //oneAfterAnotherLoop();   //does the same as oneAfterAnotherNoLoop but with 
                             //much less typing
  //oneOnAtATime();          //this will turn one LED on then turn the next one
                             //on turning the 
                             //former off (one LED will look like it is scrolling 
                             //along the line
  //inAndOut();              //lights the two middle LEDs then moves them out then back 
                             //in again
}
 
And 
 
/*
 * oneAfterAnotherLoop() - Will light one LED then delay for delayTime then light
 * the next LED until all LEDs are on it will then turn them off one after another
 *
 * this does it using a loop which makes for a lot less typing. 
 * than oneOnAtATimeNoLoop() does exactly the same thing with less typing
 */
void oneAfterAnotherLoop(){
  int delayTime = 100; //the time (in milliseconds) to pause between LEDs
                       //make smaller for quicker switching and larger for slower
 
//Turn Each LED on one after another
  for(int i = 0; i <= 7; i++){
    digitalWrite(ledPins[i], HIGH);  //Turns on LED #i each time this runs i
    delay(delayTime);                //gets one added to it so this will repeat 
  }                                  //8 times the first time i will = 0 the final
                                     //time i will equal 7;
 
//Turn Each LED off one after another
  for(int i = 7; i >= 0; i--){  //same as above but rather than starting at 0 and counting up
                                //we start at seven and count down
    digitalWrite(ledPins[i], LOW);  //Turns off LED #i each time this runs i
    delay(delayTime);                //gets one subtracted from it so this will repeat 
  }                                  //8 times the first time i will = 7 the final
                                     //time it will equal 0
                                     
                                     
}
 
/*
 * oneOnAtATime() - Will light one LED then the next turning off all the others
 */
void oneOnAtATime(){
  int delayTime = 100; //the time (in milliseconds) to pause between LEDs
                       //make smaller for quicker switching and larger for slower
  
  for(int i = 0; i <= 7; i++){
    int offLED = i - 1;  //Calculate which LED was turned on last time through
    if(i == 0) {         //for i = 1 to 7 this is i minus 1 (i.e. if i = 2 we will
      offLED = 7;        //turn on LED 2 and off LED 1)
    }                    //however if i = 0 we don't want to turn of led -1 (doesn't exist)
                         //instead we turn off LED 7, (looping around)
    digitalWrite(ledPins[i], HIGH);     //turn on LED #i
    digitalWrite(ledPins[offLED], LOW); //turn off the LED we turned on last time
    delay(delayTime);
  }
}


/*
 * inAndOut() - This will turn on the two middle LEDs then the next two out
 * making an in and out look
 */
void inAndOut(){
  int delayTime = 100; //the time (in milliseconds) to pause between LEDs
                       //make smaller for quicker switching and larger for slower
  
  //runs the LEDs out from the middle
  for(int i = 0; i <= 3; i++){
    int offLED = i - 1;  //Calculate which LED was turned on last time through
    if(i == 0) {         //for i = 1 to 7 this is i minus 1 (i.e. if i = 2 we will
      offLED = 3;        //turn on LED 2 and off LED 1)
    }                    //however if i = 0 we don't want to turn of led -1 (doesn't exist)
                         //instead we turn off LED 7, (looping around)
    int onLED1 = 3 - i;       //this is the first LED to go on ie. LED #3 when i = 0 and LED 
                             //#0 when i = 3 
    int onLED2 = 4 + i;       //this is the first LED to go on ie. LED #4 when i = 0 and LED 
                             //#7 when i = 3 
    int offLED1 = 3 - offLED; //turns off the LED we turned on last time
    int offLED2 = 4 + offLED; //turns off the LED we turned on last time
    
    digitalWrite(ledPins[onLED1], HIGH);
    digitalWrite(ledPins[onLED2], HIGH);    
    digitalWrite(ledPins[offLED1], LOW);    
    digitalWrite(ledPins[offLED2], LOW);        
    delay(delayTime);
  }
 
  //runs the LEDs into the middle
  for(int i = 3; i >= 0; i--){
    int offLED = i + 1;  //Calculate which LED was turned on last time through
    if(i == 3) {         //for i = 1 to 7 this is i minus 1 (i.e. if i = 2 we will
      offLED = 0;        //turn on LED 2 and off LED 1)
    }                    //however if i = 0 we don't want to turn of led -1 (doesn't exist)
                         //instead we turn off LED 7, (looping around)
    int onLED1 = 3 - i;       //this is the first LED to go on ie. LED #3 when i = 0 and LED 
                             //#0 when i = 3 
    int onLED2 = 4 + i;       //this is the first LED to go on ie. LED #4 when i = 0 and LED 
                             //#7 when i = 3 
    int offLED1 = 3 - offLED; //turns off the LED we turned on last time
    int offLED2 = 4 + offLED; //turns off the LED we turned on last time
    
    digitalWrite(ledPins[onLED1], HIGH);
    digitalWrite(ledPins[onLED2], HIGH);    
    digitalWrite(ledPins[offLED1], LOW);    
    digitalWrite(ledPins[offLED2], LOW);        
    delay(delayTime);
  }
}


But where to place them? 


My instinct is to copy all of the above and place it where the void loop() code was.  When I do that however, I get an error message. 
The book predicted this would happen. It says that this is a powerful technique but that "you'll also need to change a few other things but follow the compile errors and it works itself out.". 
So I look at my compile errors one by one. 

The first error is "CODE05:71 error oneAfterAnotherNoLoop' was not declared in this scope".
The Arduino sketch highlights line 71 in yellow to draw my eye immediately to the line where the error occured (That is what CODE05:71 error meant; there is an error in line 71 of the sketch named CODE05.)

What does it mean, "not declared in this scope"?  According to one arduino tutorial on variables
"Now what, you might be wondering, did the word "scope" in that error message above mean? It refers to the part of your program in which the variable can be used. This is determined by where you declare it. For example, if you want to be able to use a variable anywhere in your program, you can declare at the top of your code. This is called a global variable"


As I hunt through the original code I realize that I didn't bother to copy the section for oneAfterAnotherNoLoop because in Sketch 2 of the tutorial we learned that that was an inefficient way of using the LEDs.  So I left it out.  All I have to do, then, to resolve this error, is to comment out the line
// oneAfterAnotherNoLoop(); 
Now I uncomment  the next line:
oneAfterAnotherLoop();   //does the same as oneAfterAnotherNoLoop but with
                             //much less typing

And I compile to see what happens.
It says now CODE05:96 error  "ledPins was not declared in this Scope"
But before I drive myself crazy trying to figure all this out I need to follow the advice of the tutorial and "change all the digitalWrite()'s to changeLED()'s and see what happens.


I use Arduino's Find and Replace function and change all of them with "Replace All"
When I try to compile I still get the "ledPins was not declared in this Scope" message, but at least I've followed the directions!

Now, taking my cue from the original sketch for CODE05, where the  loop() function contained code saying "changeLED(i, ON);" I reason that perhaps I should simply replace all of the "ledPins" declarations to simply "i". When I do that all of the "ledPins was not declared in this Scope" error disappear but I get a new error in the "inAndOut" section of the code wherever the replacement caused the code to look like this:

 changeLED(i[onLED1], HIGH);
    changeLED(i[onLED2], HIGH);   
    changeLED(i[offLED1], LOW);   
    changeLED(i[offLED2], LOW);

Instead of

 changeLED(ledPins[onLED1], HIGH);  

It says "invalid types 'int[int]' for array subscript at line  152.

My instinct now is simply to comment that whole section out and see what happens before trying to figure out how to fix this (remember, like most of you, I have no background in computer coding whatsoever!).

When I do that the error now appears in line 97 'changeLED' was not declared in this scope.

So it is back to the drawing board, looking for where changeLED was declared in the original sketch for this tutorial.

As I examine the code I realize now that when I commented out the inAndOut section I accidentally commented out the part of the code that defines "changeLED", so I have to go and uncomment that.


That code is the following:


/*
 * updateLEDs() - sends the LED states set in ledStates to the 74HC595
 * sequence
 */
void updateLEDs(int value){
  changeLED(latch, LOW);     //Pulls the chips latch low
  shiftOut(data, clock, MSBFIRST, value); //Shifts out the 8 bits to the shift register
  changeLED(latch, HIGH);   //Pulls the latch high displaying the data
}

/*
 * updateLEDsLong() - sends the LED states set in ledStates to the 74HC595
 * sequence. Same as updateLEDs except the shifting out is done in software
 * so you can see what is happening.
 */
void updateLEDsLong(int value){
  changeLED(latch, LOW);    //Pulls the chips latch low
  for(int i = 0; i < 8; i++){  //Will repeat 8 times (once for each bit)
  int bit = value & B10000000; //We use a "bitmask" to select only the eighth
                               //bit in our number (the one we are addressing this time through
  value = value << 1;          //we move our number up one bit value so next time bit 7 will be
                               //bit 8 and we will do our math on it
  if(bit == 128){changeLED(data, HIGH);} //if bit 8 is set then set our data pin high
  else{changeLED(data, LOW);}            //if bit 8 is unset then set the data pin low
  changeLED(clock, HIGH);                //the next three lines pulse the clock pin
  delay(1);
  changeLED(clock, LOW);
  }
  changeLED(latch, HIGH);  //pulls the latch high shifting our data into being displayed
}


//These are used in the bitwise math that we use to change individual LEDs
//For more details http://en.wikipedia.org/wiki/Bitwise_operation
int bits[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
int masks[] = {B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111};
/*
 * changeLED(int led, int state) - changes an individual LED
 * LEDs are 0 to 7 and state is either 0 - OFF or 1 - ON
 */
 void changeLED(int led, int state){
   ledState = ledState & masks[led];  //clears ledState of the bit we are addressing
   if(state == ON){ledState = ledState | bits[led];} //if the bit is on we will add it to ledState
   updateLEDs(ledState);              //send the new LED state to the shift register
 }



I run it now and it compiles fine, but the lights do nothing.  So now I have to figure out how to talk to the lights...


Finally I get it working. I realize that I should NEVER have done a "replace all" changing all the "digitalWrite"s to "changeLED"s because there are digitalWrites at the end of the original CODE05 sketch that are essential.  Also, I realize that the error when I replace ledPins with "i" comes from when the modified code says "i[offLED] because one can not concatenate these since i is not an array.  So all I have to do is elminate the i and the brackets and just use "offLED" or whatever because that is defined in terms of i.


Here then is the modified code that combines the animations from CODE02 into the sketch of CODE05:


/*     ---------------------------------------------------------
 *     |  Arduino Experimentation Kit Example Code             |
 *     |  CIRC-05 .: 8 More LEDs :. (74HC595 Shift Register)   |
 *     ---------------------------------------------------------
 *
 * We have already controlled 8 LEDs however this does it in a slightly
 * different manner. Rather than using 8 pins we will use just three
 * and an additional chip.
 *
 *
 */


//Pin Definitions
//Pin Definitions
//The 74HC595 uses a serial communication
//link which has three pins
int data = 2;
int clock = 3;
int latch = 4;

//Used for single LED manipulation
int ledState = 0;
const int ON = HIGH;
const int OFF = LOW;
                       

/*
 * setup() - this function runs once when you turn your Arduino on
 * We set the three control pins to outputs
 */
void setup()
{
  pinMode(data, OUTPUT);
  pinMode(clock, OUTPUT); 
  pinMode(latch, OUTPUT); 
}

/*
 * loop() - this function will start after setup finishes and then repeat
 * we set which LEDs we want on then call a routine which sends the states to the 74HC595
 */
//void loop()                     // run over and over again
//{
//  int delayTime = 100; //the number of milliseconds to delay between LED updates
//  for(int i = 0; i < 8; i++){
//   changeLED(i,ON);
//   delay(delayTime);
//  }
//  for(int i = 0; i < 8; i++){
//   changeLED(i,OFF);
//   delay(delayTime);
//}
//}

void loop()                     // run over and over again
{
  //oneAfterAnotherNoLoop();   //this will turn on each LED one by one then turn each off
oneAfterAnotherLoop();   //does the same as oneAfterAnotherNoLoop but with
                             //much less typing
  oneOnAtATime();          //this will turn one LED on then turn the next one
                             //on turning the
                             //former off (one LED will look like it is scrolling
                             //along the line
  inAndOut();              //lights the two middle LEDs then moves them out then back
                             //in again
}

/*
 * oneAfterAnotherLoop() - Will light one LED then delay for delayTime then light
 * the next LED until all LEDs are on it will then turn them off one after another
 *
 * this does it using a loop which makes for a lot less typing.
 * than oneOnAtATimeNoLoop() does exactly the same thing with less typing
 */
void oneAfterAnotherLoop(){
  int delayTime = 100; //the time (in milliseconds) to pause between LEDs
                       //make smaller for quicker switching and larger for slower

//Turn Each LED on one after another
  for(int i = 0; i <= 7; i++){
    changeLED(i, HIGH);  //Turns on LED #i each time this runs i
    delay(delayTime);                //gets one added to it so this will repeat
  }                                  //8 times the first time i will = 0 the final
                                     //time i will equal 7;

//Turn Each LED off one after another
  for(int i = 7; i >= 0; i--){  //same as above but rather than starting at 0 and counting up
                                //we start at seven and count down
    changeLED(i, LOW);  //Turns off LED #i each time this runs i
    delay(delayTime);                //gets one subtracted from it so this will repeat
  }                                  //8 times the first time i will = 7 the final                                    //time it will equal                                                                     
}

/*
 * oneOnAtATime() - Will light one LED then the next turning off all the others
 */
void oneOnAtATime(){
  int delayTime = 100; //the time (in milliseconds) to pause between LEDs
                       //make smaller for quicker switching and larger for slower
 
  for(int i = 0; i <= 7; i++){
    int offLED = i - 1;  //Calculate which LED was turned on last time through
    if(i == 0) {         //for i = 1 to 7 this is i minus 1 (i.e. if i = 2 we will
      offLED = 7;        //turn on LED 2 and off LED 1)
    }                    //however if i = 0 we don't want to turn of led -1 (doesn't exist)
                         //instead we turn off LED 7, (looping around)
    changeLED(i, HIGH);     //turn on LED #i
    changeLED(offLED, LOW); //turn off the LED we turned on last time
    delay(delayTime);
  }
}

/*
 * inAndOut() - This will turn on the two middle LEDs then the next two out
 * making an in and out look
 */
void inAndOut(){
  int delayTime = 100; //the time (in milliseconds) to pause between LEDs
                       //make smaller for quicker switching and larger for slower
 
  //runs the LEDs out from the middle
  for(int i = 0; i <= 3; i++){
    int offLED = i - 1;  //Calculate which LED was turned on last time through
    if(i == 0) {         //for i = 1 to 7 this is i minus 1 (i.e. if i = 2 we will
      offLED = 3;        //turn on LED 2 and off LED 1)
    }                    //however if i = 0 we don't want to turn of led -1 (doesn't exist)
                         //instead we turn off LED 7, (looping around)
    int onLED1 = 3 - i;       //this is the first LED to go on ie. LED #3 when i = 0 and LED
                             //#0 when i = 3
    int onLED2 = 4 + i;       //this is the first LED to go on ie. LED #4 when i = 0 and LED
                             //#7 when i = 3
    int offLED1 = 3 - offLED; //turns off the LED we turned on last time
    int offLED2 = 4 + offLED; //turns off the LED we turned on last time
   
    changeLED(onLED1, HIGH);
    changeLED(onLED2, HIGH);   
    changeLED(offLED1, LOW);   
    changeLED(offLED2, LOW);       
    delay(delayTime);
  }

  //runs the LEDs into the middle
  for(int i = 3; i >= 0; i--){
    int offLED = i + 1;  //Calculate which LED was turned on last time through
    if(i == 3) {         //for i = 1 to 7 this is i minus 1 (i.e. if i = 2 we will
      offLED = 0;        //turn on LED 2 and off LED 1)
    }                    //however if i = 0 we don't want to turn of led -1 (doesn't exist)
                         //instead we turn off LED 7, (looping around)
    int onLED1 = 3 - i;       //this is the first LED to go on ie. LED #3 when i = 0 and LED
                             //#0 when i = 3
    int onLED2 = 4 + i;       //this is the first LED to go on ie. LED #4 when i = 0 and LED
                             //#7 when i = 3
    int offLED1 = 3 - offLED; //turns off the LED we turned on last time
    int offLED2 = 4 + offLED; //turns off the LED we turned on last time
   
    changeLED(onLED1, HIGH);
    changeLED(onLED2, HIGH);   
    changeLED(offLED1, LOW);   
    changeLED(offLED2, LOW);       
    delay(delayTime);
  }
}



/*
 * updateLEDs() - sends the LED states set in ledStates to the 74HC595
 * sequence
 */
void updateLEDs(int value){
  digitalWrite(latch, LOW);     //Pulls the chips latch low
  shiftOut(data, clock, MSBFIRST, value); //Shifts out the 8 bits to the shift register
  digitalWrite(latch, HIGH);   //Pulls the latch high displaying the data
}

/*
 * updateLEDsLong() - sends the LED states set in ledStates to the 74HC595
 * sequence. Same as updateLEDs except the shifting out is done in software
 * so you can see what is happening.
 */
void updateLEDsLong(int value){
  digitalWrite(latch, LOW);    //Pulls the chips latch low
  for(int i = 0; i < 8; i++){  //Will repeat 8 times (once for each bit)
  int bit = value & B10000000; //We use a "bitmask" to select only the eighth
                               //bit in our number (the one we are addressing this time through
  value = value << 1;          //we move our number up one bit value so next time bit 7 will be
                               //bit 8 and we will do our math on it
  if(bit == 128){digitalWrite(data, HIGH);} //if bit 8 is set then set our data pin high
  else{digitalWrite(data, LOW);}            //if bit 8 is unset then set the data pin low
  digitalWrite(clock, HIGH);                //the next three lines pulse the clock pin
  delay(1);
  digitalWrite(clock, LOW);
  }
  digitalWrite(latch, HIGH);  //pulls the latch high shifting our data into being displayed
}


//These are used in the bitwise math that we use to change individual LEDs
//For more details http://en.wikipedia.org/wiki/Bitwise_operation
int bits[] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000};
int masks[] = {B11111110, B11111101, B11111011, B11110111, B11101111, B11011111, B10111111, B01111111};
/*
 * changeLED(int led, int state) - changes an individual LED
 * LEDs are 0 to 7 and state is either 0 - OFF or 1 - ON
 */
 void changeLED(int led, int state){
   ledState = ledState & masks[led];  //clears ledState of the bit we are addressing
   if(state == ON){ledState = ledState | bits[led];} //if the bit is on we will add it to ledState
   updateLEDs(ledState);              //send the new LED state to the shift register
 }



It works great, playing out each of the three animations in sequence and repeating. 


Try it out for yourself!














No comments:

Post a Comment