Did you know it is possible to toggle the state of a Arduino OUTPUT pin using a single line of code? It’s true! It’s also possible to use digitalRead() on an OUTPUT pin. Assuming pin 13 was set to output, this single line of code will cause the LED to change state (or flash) each time it is called:
digitalWrite(13, !digitalRead(13));
Keep reading to understand how these two tricks work.
Reading an OUTPUT
The first thing you might notice about that code snippet is that digitalRead() is used on a pin that is assumed to be an OUTPUT. So does that mean you can use the pin as an INPUT and OUTPUT at the same time? No. It actually turns out, when digitalRead() is called on an OUTPUT, you are reading the status of the a register called “PORTx” inside the ATmega microcontroller.
PORTx?
There are several registers that control the I/O pins of the AVR. One of them is called “PORTx”, which contains the current state of the pins for a certain “port.” The “x” represents B, C, D. In other words: PORTB, PORTC, PORTD. The pins on the Arduino board map to one of the bits in those registers.
In the case of PIN 13, that is found on PORTB, bit 1. That is also called PB1. PORTB has Arduino pins 8 through 13 on it. Hint, if you want to know what PORTx register is associated with a Pin, look at the UNO Schematic…
When the pin is configured for OUTPUT, digitalWrite() figures out with Port and Bit to change, and writes to it. That is what enables a HIGH or LOW on the OUTPUT.
So when you call digitalRead(), it looks at the PORTx register and returns the current value of the Port and Bit. If you used digitalWrite() to turn it HIGH, then digitalRead() will also return HIGH. Obviously digitalRead() will return a LOW if the pin is currently set as a LOW.
That takes care of the digitalRead() part, but what is the “!” [exclamation point] business all about?
Bang! NOT! Excitement!
[That was a pun, by the way.] The “!” operator in C is a boolean operator that means: “NOT”. Old school programmers will often refer to it as a “bang”. This is a quick way to invert a value. When you are dealing HIGH and LOW, you are actually dealing with “1” and “0”, respectively. In C, the opposite of 1 is 0. While the opposite of 0 is 1.
So the “!” bang/NOT operator inverts whatever the call to digitalRead() returns.
Put it All Together
Putting the mysterious digitalRead() and the dirty-sounding-but-totally clean bang-NOT operator together, what is going to happen? The digitalRead() will find the current value of the OUTPUT pin and the “!” operator will invert the value. This means, digitalWrite() writes the opposite of the pin’s current state.
Here’s an implementation of the Blink example, using this trick:
void setup() {
pinMode(13, OUTPUT); // use on-board LED
}
void loop() {
digitalWrite(13, !digitalRead(13)); // toggle state
delay(1000); // wait around for 1 sec (1000 ms)
}
Easy, eh?
[Cute flashing GIF made with Picasion]
7 Comments
Brilliant !! thank you saved me hours, worked perfectly 🙂
Tried the digitaltoggle from the arduino library, waste of time as its out of date and requires far too much set up to get it to work. Simplicity is elegance and this is such an elegant solution.
There is no “digitalToggle” in the Arduino library.
Thank you *so* much.
Looking this up is a recipe for a pissing contest, of “what’s the most acceptable way of using a for loop to standard”.
This is the best solution there is, and should rank even higher than the Arduino Forum results!
Thanks, BaldEngineer! I was *SURE* this would be possible somehow, just didn’t know where to put the bang 🙂
i tried using this to toggle the state of a relay so i can have it turn on and off with a single button. I don’t know if i somehow messed something up but now it seems like my relay will no longer switch (which i confirmed using the continuity tester on my multimeter). I’m wondering what the heck i did wrong, any pointers would be appreciated!
Pingback: baldengineer | When to use Arduino’s pinMode() (and why)