Did you know that most AVR chips have a type of hardware exclusive OR (XOR) option when it comes to the logic levels of the output pins? If you look in the datasheet (the image above is a screenshot from an ATtiny13 datasheet) you’ll find a section on Toggling the Pin. It turns out that if you set a PORT as an output, writing logic one to the corresponding PIN register will toggle the logic levels of that out. This is really easy to overlook if you’re writing in C, but I’ve been working on learning a bit of assembler language and found this to be very useful. Keep reading after the break and I’ll tell you how I happened upon this info and what it’s good for.
So first off, let’s talk about why this doesn’t matter very much if you’re writing in C code. Normally if you want to toggle some output pins you’ll just write a one-liner that XOR’s with a bitmask:
PORTB ^= 0xFF;
This is a bit of C shorthand (learn more about that from my tutorial series) that performs the XOR on the current output levels and writes the result back to the port. But the same thing can be done in hardware by writing the bitmask to the PINB register:
PINB = 0xFF;
You don’t really care, because it’s just one line of code. In fact it’s probably easier to XOR the PORTB because it makes more sense conceptually. But the compiler might end up using more cycles than if you had written to the PIN register.
I happened across this feature because I was blinking some LEDs as a way to learn assembler. I had this jumble of code in an Interrupt Service Routine:
ldi myReg, 0xFF in intReg, PORTB eor intReg, myReg out PORTB, intReg
It loads a bitmask into one register, loads in the current logic from PORTB to another register, performs an XOR of the two, and writes the result back to PORTB. This takes four cycles and requires two registers. Toggling bits is such a rudimentary operation I was surprised there wasn’t a command to XOR bits directly so I started searching around. I came across this article over at AVR Freaks which clued me into the bit toggle feature. Now I was able to reduce my assembler code as follows:
ldi intReg2, 0xFF ;temporarity use intReg2 as a bit mask out PINB, intReg2 ;writing to PINB effectivley does an Exclusive OR on PORTB
This results in the same toggling effect, but takes just two cycles and requires the use of only one register.
What I found most interesting is that no matter how much I use AVR chips, there ‘s never a shortage of surprises waiting to be found in the datasheet.