1

we are asked to create an Arduino code that would run a 4 Bit Binary UP counter once a push button switch is pressed and then 4 Bit Binary Down counter on the second click. Then vice versa. This should be done using only one switch. I figured out the Count up portion but I don't know how to add the count down code for the same switch. Can someone help me with this. Thank you. please see below for my program enter image description here

int buttonState = 0;
int timer = 300;
int pin8 = 8;
int pin9 = 9;
int pin10 = 10;
int pin11 = 11;

void setup() 
{ 
pinMode(7,INPUT); 
pinMode(8,OUTPUT); 
pinMode(9,OUTPUT); 
pinMode(10,OUTPUT); 
pinMode(11,OUTPUT); 

}

void loop() 
{ 
digitalWrite(pin8, LOW); 
digitalWrite(pin9, LOW); 
digitalWrite(pin10, LOW); 
digitalWrite(pin11, LOW);
buttonState = digitalRead(7);
  
if(buttonState == HIGH) 
{
digitalWrite(pin8, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
delay(timer); //bit 1
digitalWrite(pin9, HIGH);
delay(timer);
digitalWrite(pin9, LOW);
delay(timer); //bit 2
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
digitalWrite(pin9, LOW);
delay(timer); //bit 3
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin10, LOW);
delay(timer); //bit 4
digitalWrite(pin8, HIGH);
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
digitalWrite(pin10, LOW);
delay(timer); //bit 5
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
delay(timer); //bit 6
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
delay(timer); //bit 7
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin11, LOW);
delay(timer); //bit 8
digitalWrite(pin8, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin8, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 9
digitalWrite(pin9, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin9, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 10
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin8, LOW);
digitalWrite(pin9, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 11
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 12
digitalWrite(pin8, HIGH);
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin8, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 13
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 14
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin9, LOW);
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 15
}
}
2
  • why don't you just count a variable from 0 to 0x0F and decide the four bits into the LED outputs? ... the code would be simple and not repetitive – jsotola Feb 11 at 7:45
  • if(buttonState == HIGH) means "as long as the button is pressed". You probably want to use a flag that changes, when the button is clicked. – PMF Feb 11 at 10:35
3

Currently you are just setting the LEDs manually one after another in your code. That is not actually a binary counter (as it is most likely wanted by the exercise), but only looks like one.

First you need to understand, what a counter really is: Basically it is just a variable, that holds a number. Where exactly this variable is saved, is not important. For your case you would use a variable in your code. The Counter/Timer hardware in the Arduino uses a fixed SFR (Special Function Register) for it.

That the counter is 4bit, means, that its value is hold by only 4 bits. That gives you a value range from 0 to 15 (2^4 values). So a 4bit counter variable would count from 0 to 15. You could just have a counter variable, that you could increment or decrement (depending on the direction, that you want to count).

// Declare counter variable and initialize with zero
int counter_variable = 0;
// Increment
counter_variable++;
// or Decrement
counter_variable--;

For the "binary" part: Here it is important to understand, that the decimal number from 0 to 15 is already saved in the variable as binary (since microcontrollers/computers only know binary, nothing else). So you can write out the state of the 4 least significant bytes of the counter variable:

digitalWrite(pin8  ,counter_variable & 0b0001);
digitalWrite(pin9  ,counter_variable & 0b0010);
digitalWrite(pin10 ,counter_variable & 0b0100);
digitalWrite(pin11 ,counter_variable & 0b1000);

The & is a bitwise AND. The result of the operation will only leave bits set, when they are set on both sides. So here we are isolating the corresponding bits. digitalWrite() will interpret anything but zero as HIGH. Thus the pin is set HIGH, if the corresponding bit is set in counter_variable. But you see, that we are repeating us here. We could use a for loop and define our pins in an array:

// at global scope
int led_pins[4] = {8, 9, 10, 11};

// in the button if statement
for(int i=0; i<4; i++){
    digitalWrite(led_pins[i], counter_variable & (1 << i));
}

We have organized the pins of the LEDs in an array, so they can be easily indexed. Then we are looping through the pins array and setting the pins similar to the last version. But now we are using (1 << i). The << operator shifts the bits of the number before it by the number of digits, that comes after it. Decimal 1 is binary 0b0001. So, when i is 2 (for example), this would shift the bits 2 digits to the left, giving you 0b0100. We are just generating the same bit patterns as in the last version.

But we can even optimize further via direct port manipulation. Inside the microcontroller the output pins are controlled via simple Special Function Registers. They work somewhat like a variable. All output pins are organized into socalled "Ports" (groups of up to 8 pins). Each port has one PORTx registers (where x stands for the character associated with that port), which sets the state of the pins, if they are in output mode. Setting the corresponding bit in the correct PORTx register will set the pin to HIGH. Clearing the bit (setting to zero) will set the pin to LOW.

You are using the Arduino pins 8 to 11. If you look at a pinout diagram of the Arduino Uno, you will see, that these pins are also marked as PB0 to PB3, which stands for "Port B, pins 0 to 3 (inside that port)". The corresponding PORTx register is PORTB. So we can set all pins of the port at once by assigning a new value to that register. The following code would set an alternating on/off pattern for all 8 pins of PORTB:

PORTB = 0b10101010;

Now we only want to use the lowest 4 bits and set them to the values, that our counter variable has. So we are masking away the 4 higher bits with the bitwise AND operator and then assign that value to the register:

PORTB = counter_variable & 0b00001111;

So now the if statement in void loop() might look like this (using a for loop):

if(buttonState == HIGH){
    // count up counter variable
    for(int counter_variable=0; counter_variable < 16; counter_variable++){
        // write out to the pins
        PORTB = counter_variable & 0b00001111;
        // delay a bit to show the value
        delay(timer);
    }
}

And now we want that each consecutive button to count in the other direction than before. So we can declare another variable as direction and then use another if statement, to switch between two for loops:

// On global scope
int direction = 1;

// in void loop()

if(buttonState == HIGH){
    if(direction == 1){
        // count up counter variable
        for(int counter_variable=0; counter_variable < 16; counter_variable++){
            // write out to the pins
            PORTB = counter_variable & 0b00001111;
            // delay a bit to show the value
            delay(timer);
        }
        // change direction
        direction = -1;
    } else {
        // count up counter variable
        for(int counter_variable=15; counter_variable >= 0; counter_variable--){
            // write out to the pins
            PORTB = counter_variable & 0b00001111;
            // delay a bit to show the value
            delay(timer);
        }
        // change direction
        direction = 1;
    }
}

Note:

  • This is probably not the most efficient way, but I think it is quite understandable (which is also important).
  • This still uses much delay() calls, just as your code. That will make your button unresponsive. The counter must first finish, before the button will react again. That might be ok for you. If not, you need to restructure the program for using a non-blocking coding style like in the BlinkWithoutDelay example from the Arduino IDE (using millis()). There are lots of tutorials about that on the web.
  • When using a non-blocking coding style, you also need to check the button for the transition to HIGH, instead for the state high. Otherwise the code would run only, while the button is pressed.
  • Buttons bounce, when being pressed. So there are many transitions between HIGH and LOW for a few ms, when the button is pressed. In the code above this is not important, because the started counter needs so long, that every bounce is finished after the counter finished. With code, that needs less than a few ms, you might get into problems there. I would suggest the Bounce2 library for that. It handles the bounce detection for you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.