Delicious Bookmark this on Delicious      HOME > > ARDUINO LANGUAGE COURSE  t.o.c.

An "up/ down" counter, binary, and switch bounce

This is one of a collection of pages which, together, attempt to show you "everything" about the Arduino's programming language.

There is a page for you with more information about the project in general, and the way these pages are organized, if you want that.

Please visit my page about power browsing notes sometime.

This page, and the software it references, ©TK Boyd, 1/2010.

Where we're going

While this topic introduces few concepts that are new, it does take a new look at some things we've seen already, and touches on some details I spared you earlier. Read it carefully to consolidate things you should know.

We'll be using an Arduino equipped with three LEDs and two buttons. The buttons, as before, will be identified as "green" and "yellow", and will again be momentary. you may even see the famous "bounce" in the course of this tutorial.

The program will cause the LEDs to display a number, in binary. Pressing the green button will cause the count to go up by one, pressing the yellow will cause the count to go down by one.

We will implement a "roll over" mechanism. With three LEDs, the biggest number we can show is seven (all three LEDs on). If we are at seven, and press the green button to go "up" again, we will find ourselves back at zero, no LEDs lit. In a similar vein, if we are at zero, and press the yellow button to go "down", we will find ourselves at seven.

First, word about "binary"....

If I have a box with a dozen donuts in it, does anything change if I say I have a box with 12 donuts in it? What's in the box doesn't change just because we WRITE DOWN the quantity differently.

"Seven", "7" and "B111" all mean the same thing, if you know how to read the different systems of showing how much of something there is.

The last representation, "B111" would be understood by the Arduino to mean "the binary number 111".

The expression "binary number" can mislead people who are new to the idea of representing numbers in non-traditional ways, e.g. showing what we usually call "5", or "five" as 101, which is that number shown in a simple binary representations. "Binary" doesn't even really mean very much... although it is commonly used. In such usage, it usually means that the user will be showing unsigned (negative numbers not possible), whole (no fractions) numbers using 1s and 0s.

I'm not going to inflict a long discussion of the pros and cons and arithmetic of binary on you here... but you need to know how to count to seven to follow this tutorial. Here you go...

Decimal*   Binary**

0           000
1           001
2           010
3           011
4           100
5           101
6           110
7           111

(*"Decimal" is the "normal" system of expressing quantities which we and everyone else use every day.)

(**"Binary": Unsigned, whole numbers only system.)

Our first version of this program will not use the switches at all. It will just, repeatedly, count from 0 to 7. We've done this before, but not quite the same way. This version lets you see a "for" loop in action again, and gives you an example of "switch... case... " control structure. See rems in program.

/*FEAa1UpDownCountStart
ver 30 Dec 09

Requires 3 LEDs, two momentary switches
*/

const byte LED0=11;//LED for LS bit connected here
const byte LED1=12;//LED connected here
const byte LED2=13;//LED for MS bit connected here

const byte bGrnSwitch=4;//pin green switch is on
const byte bYelSwitch=5;//pin yellow switch is on

void setup()//"setup" always present
{
   pinMode(LED0,OUTPUT);
   pinMode(LED1,OUTPUT);
   pinMode(LED2,OUTPUT);

   pinMode(bGrnSwitch,INPUT);
   pinMode(bYelSwitch,INPUT);
}

void loop()//This function always present
{ for (int intLoopCount=0;//Start off with intLoop equal to 0
       intLoopCount<8;//Repeat the loop while intLoopCount<8
       intLoopCount++)//Each time, increase by 1.
    //Do all of the statements between the curly braces
    {showOnLEDs(intLoopCount);
     delay(200);
    }//end of "for"
}

void setLEDs(byte bL2, byte bL1, byte bL0)
{
   if (bL0==0) digitalWrite(LED0,LOW);
               else  digitalWrite(LED0,HIGH);
   if (bL1==0) digitalWrite(LED1,LOW);
               else  digitalWrite(LED1,HIGH);
   if (bL2==0) digitalWrite(LED2,LOW);
               else  digitalWrite(LED2,HIGH);
 }

void showOnLEDs(byte bToShow)
/*Here's an example of the "switch.. case..."
  control structure for you. The same thing
  can be done with a bunch of "if (bToShow==..."
  statements, but this is more elegant, and
  provides the option of a default action
  more simply*/
  {
  switch (bToShow) {
    case 0:setLEDs(0,0,0);
      break;
    case 1:setLEDs(0,0,1);
      break;
    case 2:setLEDs(0,1,0);
      break;
    case 3:setLEDs(0,1,1);
      break;
    case 4:setLEDs(1,0,0);
      break;
    case 5:setLEDs(1,0,1);
      break;
    case 6:setLEDs(1,1,0);
      break;
    case 7:setLEDs(1,1,1);
      break;
    default:setLEDs(1,1,1);
      // if nothing else matches.. should not
      // arise... but if it does, do this.

  }//End of switch... case...
  }//End of function "showOnLEDs"

Once you are clear about what is in that, you can move it towards the final requirement for a switch-controlled up- down counter SIMPLY by modifying what is in the "loop" function, and adding a variable and initializing it, and initializing the LED display. The second stage version also has some debugging lines....

/*FEAa1UpDownCount2nd.. i.e. second stage of development
ver 27 Dec 09

Requires 3 LEDs, two momentary switches
*/

const byte LED0=11;//LED for LS bit connected here
const byte LED1=12;//LED connected here
const byte LED2=13;//LED for MS bit connected here

const byte bGrnSwitch=4;//pin green switch is on
const byte bYelSwitch=5;//pin yellow switch is on

byte bToShow=4;//This variable will hold the number
     //the LEDs should be showing at any given moment.
     //Here we are establishing it, and giving it an
     //initial value....
     //This has been made a global variable so that
     //  it can be accessed both in "setup" and "loop"

void setup()//"setup" always present
{
   pinMode(LED0,OUTPUT);
   pinMode(LED1,OUTPUT);
   pinMode(LED2,OUTPUT);

   pinMode(bGrnSwitch,INPUT);
   pinMode(bYelSwitch,INPUT);

   showOnLEDs(bToShow);//... and this displays on the
     //LEDs the number in bToShow.

   Serial.begin(9600);//for debugging work
}

void loop()//This function always present
{
  if (digitalRead(bGrnSwitch)==LOW)
     {bToShow++;
      if (bToShow>7) bToShow=0;
      showOnLEDs(bToShow);
      }//end of "if...")
  Serial.println(int(bToShow));//for debugging
  delay(150);//Include if Serial.print in use.
}//end of function "loop"

void setLEDs(byte bL2, byte bL1, byte bL0)
{
   if (bL0==0) digitalWrite(LED0,LOW);
               else  digitalWrite(LED0,HIGH);
   if (bL1==0) digitalWrite(LED1,LOW);
               else  digitalWrite(LED1,HIGH);
   if (bL2==0) digitalWrite(LED2,LOW);
               else  digitalWrite(LED2,HIGH);
 }

void showOnLEDs(byte bToShow)
/*Here's an example of the "switch.. case..."
  control structure for you. The same thing
  can be done with a bunch of "if (bToShow==..."
  statements, but this is more elegant, and
  provides the option of a default action
  more simply*/
  {
  switch (bToShow) {
    case 0:setLEDs(0,0,0);
      break;
    case 1:setLEDs(0,0,1);
      break;
    case 2:setLEDs(0,1,0);
      break;
    case 3:setLEDs(0,1,1);
      break;
    case 4:setLEDs(1,0,0);
      break;
    case 5:setLEDs(1,0,1);
      break;
    case 6:setLEDs(1,1,0);
      break;
    case 7:setLEDs(1,1,1);
      break;
    default:setLEDs(1,1,1);
      // if nothing else matches.. should not
      // arise... but if it does, do this.
}//End of "switch... case..."
}//End of showOnLEDs

Comments on "stuff" in the above:

Note there is an if with just one thing to do when bToShow>7 inside the "if (digitalRead...", which has multiple statements to be executed. This is called "nesting" statements.

Once that is working... you'll need to press the green button only briefly... rem out the debugging lines... everything to do with "Serial...", and the "delay(..." line.

NOW try to operate the program. You will find it "broken". Almost every time you press the green button, however briefly, the number displayed on the LEDs changes... but not merely to the next number. It seems to go to a randomly chosen next number.

Not so! What is happening is this:

You cannot press the button briefly enough to capture just one trip through "loop". When you press the button, it is down long enough to cause bToShow to go up one MANY TIMES.

Okay...

First attempt to solve: We're going to move...

digitalRead(bGrnSwitch)==LOW

... out into a boolean function, similar to one we did another time. When the function is called, it will look at the green switch. If it is closed, the function will return "true", and if not it will return false. Furthermore, if when we enter the function, the switch is closed, we will not return from the function until the switch reopens. (That still won't fully work, by the way... we'll come back to why later... but it will be progress.

Our third stage is as follows....

/*FEAa1UpDownCount3rd.. third stage of development
ver 27 Dec 09

Requires 3 LEDs, two momentary switches
*/

const byte LED0=11;//LED for LS bit connected here
const byte LED1=12;//LED connected here
const byte LED2=13;//LED for MS bit connected here

const byte bGrnSwitch=4;//pin green switch is on
const byte bYelSwitch=5;//pin yellow switch is on

byte bToShow=4;//This variable will hold the number
     //the LEDs should be showing at any given moment.
     //Here we are establishing it, and giving it an
     //initial value....
     //This has been made a global variable so that
     //  it can be accessed both in "setup" and "loop"

void setup()//"setup" always present
{
   pinMode(LED0,OUTPUT);
   pinMode(LED1,OUTPUT);
   pinMode(LED2,OUTPUT);

   pinMode(bGrnSwitch,INPUT);
   pinMode(bYelSwitch,INPUT);

   showOnLEDs(bToShow);//... and this displays on the
     //LEDs the number in bToShow.

//   Serial.begin(9600);//for debugging work
}

void loop()//This function always present
{
  if (boSwitchPressed(bGrnSwitch))
     {bToShow++;
      if (bToShow>7) bToShow=0;
      showOnLEDs(bToShow);
      }//end of "if...")
//  Serial.println(int(bToShow));//for debugging
//  delay(150);//Include if Serial.print in use.
}//end of function "loop"

void setLEDs(byte bL2, byte bL1, byte bL0)
{
   if (bL0==0) digitalWrite(LED0,LOW);
               else  digitalWrite(LED0,HIGH);
   if (bL1==0) digitalWrite(LED1,LOW);
               else  digitalWrite(LED1,HIGH);
   if (bL2==0) digitalWrite(LED2,LOW);
               else  digitalWrite(LED2,HIGH);
 }

void showOnLEDs(byte bToShow)
/*Here's an example of the "switch.. case..."
  control structure for you. The same thing
  can be done with a bunch of "if (bToShow==..."
  statements, but this is more elegant, and
  provides the option of a default action
  more simply*/
  {
  switch (bToShow) {
    case 0:setLEDs(0,0,0);
      break;
    case 1:setLEDs(0,0,1);
      break;
    case 2:setLEDs(0,1,0);
      break;
    case 3:setLEDs(0,1,1);
      break;
    case 4:setLEDs(1,0,0);
      break;
    case 5:setLEDs(1,0,1);
      break;
    case 6:setLEDs(1,1,0);
      break;
    case 7:setLEDs(1,1,1);
      break;
    default:setLEDs(1,1,1);
      // if nothing else matches.. should not
      // arise... but if it does, do this.
}//End of "switch... case..."
}//End of showOnLEDs

boolean boSwitchPressed(byte bWhichSwitch)
  {
   boolean boAns=false;//What is in this will eventually be passed
     //back to the calling code. Assume switch NOT pressed,
     //for the moment.
   if (digitalRead(bWhichSwitch)==LOW)
     {
        boAns=true;
        while (digitalRead(bWhichSwitch)==LOW) {;}//Does "nothing", but goes round and round
         //this "while" loop until switch released.
     }//
     return boAns;
  }//end of boSwitchPressed

This will probably MOSTLY work for you. But "give it some exercise"... Press the green button maybe twenty times. Does the number displayed on the LEDs ALWAYS go up just once for each press of the switch? You will probably see cases where the number jumps two or even more, for just one press of the button. This will be due to switch "bounce" discussed earlier.

In the 4th development stage of the program, we introduce two delays in the boSwitchPressed function. This overcomes the bounce problem.

We also bring the yellow switch into play.

Now you would think that implementing the yellow switch would be easy. Trouble is, I put the number to be displayed in a byte type variable. Byte type variables hold 0-255 (inclusive). So what happens when I go down one from zero?

I'm taking a little bit of a chance, as I can find nothing in the documentation of the Arduino to confirm that it does what I expect, but in many systems, if you go down from 0 with a byte type datum you get 255. That is what my code depends on.

Note that I didn't do anything about the yellow switch until I had the green switch working. And that I'm sharing code between the two switches. This approach means you get to your goal more quickly if you try to do too many things at once. You must always know where you are going, but try to pick a "one-thing-at-a-time" path to your goal, and you will do well.

/*FEAa1UpDownCount4th.. fourth stage of development
ver 27 Dec 09

Requires 3 LEDs, two momentary switches
*/

const byte LED0=11;//LED for LS bit connected here
const byte LED1=12;//LED connected here
const byte LED2=13;//LED for MS bit connected here

const byte bGrnSwitch=4;//pin green switch is on
const byte bYelSwitch=5;//pin yellow switch is on

byte bToShow=4;//This variable will hold the number
     //the LEDs should be showing at any given moment.
     //Here we are establishing it, and giving it an
     //initial value....
     //This has been made a global variable so that
     //  it can be accessed both in "setup" and "loop"

void setup()//"setup" always present
{
   pinMode(LED0,OUTPUT);
   pinMode(LED1,OUTPUT);
   pinMode(LED2,OUTPUT);

   pinMode(bGrnSwitch,INPUT);
   pinMode(bYelSwitch,INPUT);

   showOnLEDs(bToShow);//... and this displays on the
     //LEDs the number in bToShow.

//   Serial.begin(9600);//for debugging work
}

void loop()//This function always present
{
  if (boSwitchPressed(bGrnSwitch))
     {bToShow++;
      if (bToShow>7) bToShow=0;
      showOnLEDs(bToShow);
      }//end of "if...")

  if (boSwitchPressed(bYelSwitch))
     {bToShow=bToShow-1;//Yes, you can do this by "bToShow--;", if you wish.
      if (bToShow==255) bToShow=7;
      showOnLEDs(bToShow);
      }//end of "if...")

//  Serial.println(int(bToShow));//for debugging
//  delay(150);//Include if Serial.print in use.
}//end of function "loop"

void setLEDs(byte bL2, byte bL1, byte bL0)
{
   if (bL0==0) digitalWrite(LED0,LOW);
               else  digitalWrite(LED0,HIGH);
   if (bL1==0) digitalWrite(LED1,LOW);
               else  digitalWrite(LED1,HIGH);
   if (bL2==0) digitalWrite(LED2,LOW);
               else  digitalWrite(LED2,HIGH);
 }

void showOnLEDs(byte bToShow)
/*Here's an example of the "switch.. case..."
  control structure for you. The same thing
  can be done with a bunch of "if (bToShow==..."
  statements, but this is more elegant, and
  provides the option of a default action
  more simply*/
  {
  switch (bToShow) {
    case 0:setLEDs(0,0,0);
      break;
    case 1:setLEDs(0,0,1);
      break;
    case 2:setLEDs(0,1,0);
      break;
    case 3:setLEDs(0,1,1);
      break;
    case 4:setLEDs(1,0,0);
      break;
    case 5:setLEDs(1,0,1);
      break;
    case 6:setLEDs(1,1,0);
      break;
    case 7:setLEDs(1,1,1);
      break;
    default:setLEDs(1,1,1);
      // if nothing else matches.. should not
      // arise... but if it does, do this.
}//End of "switch... case..."
}//End of showOnLEDs

boolean boSwitchPressed(byte bWhichSwitch)
  {
   boolean boAns=false;//What is in this will eventually be passed
     //back to the calling code. Assume switch NOT pressed,
     //for the moment.
   if (digitalRead(bWhichSwitch)==LOW)
     {
        delay(20);
        boAns=true;
        while (digitalRead(bWhichSwitch)==LOW) {;}//Does "nothing", but goes round and round
         //this "while" loop until switch released.
        delay(20);
     }//
     return boAns;
  }//end of boSwitchPressed

Odds and ends....

At the start of this tutorial, we made reference to the fact that you could write binary numbers with 1s an 0s. All you need to do is prefix them with a "B" and the Arduino understand that you mean a binary number. So, B111 is binary for seven, while 111 is one more than one hundred and ten.

You can also write hexadecimal numbers. Just preface them with 0x (zero-x). So 0x12 is hexadecimal for what we usually call eighteen. Yes, Virginia, you will want to write numbers in hexadecimal notation one day.

You can, although I don't remember the last time I wanted to since I was using a PDP-8 in the 1970s, write numbers in octal. You just prefix the number with a zero.... 010 thus stands for what we usually call eight. AND HEREIN LIES A "GOTCHA": Do not prefix any decimal number you are writing with a leading zero. Whereas in most of the world, 10 and 010 are the same thing, they are not within an Arduino.




   Search this site or the web      powered by FreeFind

Site search Web search
Site Map    What's New    Search

The search engine is not intelligent. It merely seeks the words you specify. It will not do anything sensible with "What does the 'could not compile' error mean?" It will just return references to pages with "what", "does", "could", "not".... etc.

SPELL your search term properly. When I review search logs, it is amazing how many people ask the engine to search for something meaningless.


Why does this site cause a script to run? I have my web-traffic monitored for me by eXTReMe tracker. They offer a free tracker. If you want to try it, check out their site. And if there are Google ads on the page, they are run with scripts, too.


Click here to return to Arduino COURSE table of contents.
Click here to go to the author's home page.

Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.


Here is how you can contact this page's editor. This page, and the software it references, ©TK Boyd, 1/2010.

Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org