10.24.2011

Arduino Development Challenges


Here is a snippet of code from a sample Arduino sketch I've been toying with:

TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));


What... the... fuck? The only thing I remotely recognize are the operators - and even those are all scary and bit-wise. TCCR2A,  _BV(), COM2A1... what are these things? They are certainly not defined anywhere in this sketch. Nor are they it defined in any of the manually linked files or libraries. To me, this exemplifies that the hardest part about learning an entirely new vertical system at once is not knowing at what level constructs live.

Now, I do know what this code is supposed to be doing. It's commented rather well, actually. And looking at it alongside the Arduino datasheet I can tell the what. But I need to know the how. It's like being able to understand pre-made sentences in a foreign language, but not being able to create your own. In order to form my own, I need to know what each part does.


TCCR2A

Let's start with what it is supposed to be doing. It's supposed to be setting the Compare Output Mode (COM) on Timer 2, by setting the appropriate COM bits in the TCCR2A register. The TCCR2A register, like many on the Arduino, is an 8 bit bitmask. It look like this:

TCCR2A
 
bit| name   | explanation
---+--------+-------------------------
 7 | COM2A1 | Compare Output Mode A
 6 | COM2A0 | Compare Output Mode A
 5 | COM2B1 | Compare Output Mode B
 4 | COM2B0 | Compare Output Mode B
 3 | -      | reserved
 2 | -      | reserved
 1 | WGM21  | Waveform Generation Mode
 0 | WGM20  | Waveform Generation Mode


The first thing to understand is that the names of all the Arduino registers (TCCR2A, TCCR2B, and about 90 others) are global, read-write variables containing the bitmask value in the register. On bootup, when all the bits in TCCR2A are set to 0, the value of TCCR2A is B00000000 (that's Arduino shorthand for binary 0). And at any time in your sketch you can set TCCR2A = B01010101 or whatever you want. TCCR2A is defined as:

#define TCCR2A _SFR_MEM8(0xB0)



 _BV()

_BV() is defined in sfr_defs.h:

#define _BV(bit) (1 << (bit))

OK, so bitwise-shift a 1 a certain number of positions to the left. I get it on a technical level, but it doesn't make sense to me yet on a conceptual level. Especially not knowing exactly what types of values are typically passed it (the name of the argument - bit - implies some meaning there...)

By the way, the accepted mnemonic is "Bit Value".


COM2A0 and the rest
 
The answer that took me the longest to grok was this: the names of the bits (COM2A1, COM2A0, etc.) are NOT defined variables for the values of those bits. They are instead constants which contain the bit's location relative to the byte in which they reside. In other words:
#define COM2B0 4
#define COM2B1 5
#define COM2A0 6
#define COM2A1 7



Putting it all together

Now back to the sample code I'm trying to understand:

TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));


AHA! To set the COM2A1 bit to ON (what it actually does is unimportant to this post), you shift an 'ON' bit (a one) left by the correct number of positions (7) and then bitwise-or it onto the byte!

TCCR2A |= _BV(COM2A1) // turn ON the COM2A1 bit

And to set the COM2A0 bit to OFF, you start the same way (in this case shifting a one 6 bits to the left) but negate it and bitwise-and it onto the byte!

TCCR2A &= ~_BV(COM2A0) // turn OFF the COM2A0 bit


What a trip. I'm still trying to think of whether I like this notation or not. Most of the alternatives I can come up with each have their own faults. This method is at least fairly self-documenting once you know it.


References:
grep define /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/options/gcc-version/include/avr/iom328p.h | less
http://www.arcfn.com/2009/07/secrets-of-arduino-pwm.html
http://smacula.blogspot.com/2011/04/creating-variable-frequency-pwm-output.html

3 comments:

Unknown said...

Aah interesting to see somebody using my articles as a reference :)

Unknown said...

Thanks, dude. There is no way I would have figured that all out without your putting into english. Gracias.

Unknown said...
This comment has been removed by the author.