Friday, February 10, 2012

AVRez: A Simple Arduino API for other chips

The Arduino IS its software library.   As I'm sure you've seen on the web, its pretty easy to breadboard an Arduino -- and to do it for about 10 bucks -- so Arduino is not really about the hardware.  And the IDE is no Eclipse or Emacs.  While this may actually be an advantage for complete beginners :-) it cannot be the IDE that sustains the Arduino's popularity.

The true secret to the Arduino is in its software library -- which AFAIK actually came from a project called Wiring.  Here's a quick quiz to prove my point. What does this do:


Huh??? 99.9% of you will say (for .1% who get it right and don't get my drift, your next challenge is to write the equivalent of analogRead(11) without looking at the datasheet).

Well if you happen to have read the Atmel 328 processor spec, and pored through the avr-gcc io328xxx.h header file you'd know that this sets the bit corresponding to PB2 in the data direction register to high.

Make sense yet?  No?  Let me make it clearer.

pinMode(10, OUTPUT);

That's the Arduino/Wiring library version of the same code above.  And pretty much anyone who understands that pins must be designated input or output can understand that line.  You don't even have to be familiar with the Arduino/Wiring library!  Its the magic of self-documenting code. 

So when you go digging around in "AppNotes" and other example code, don't be fooled by the garbage you find.  Its not "real" embedded programmers that directly use unreadable archana like "ADMUX |= 1; ASCSRA |= _BV(ADSC); while (ADSCRA&_BV(ADSC); result=ADCL | (((uint16_t)(ADCH))*256);" (that's how you read an analog pin PB2 by the way).  Its actually just the "real bad" ones :-).  Or institutions who have a vested interested in locking you into their platform.

In fact the only real confusion with "pinMode(10,OUTPUT)" is identifying what "10" means.
 
This is why I've decided to make a super simple library that I call "AVRez" that provides a similar API for other chips.  You can look at it at here.  This library is intended for use with chips that are not "Arduino" compatible so a few changes had to be made made, most especially in the pin out.  However the basic API is instantly understandable to anyone whether they know the Arduino APIs or not.

With this library, for example, you can call "pinMode(pinB2, OUTPUT);".  The only visual difference between this and the Arduino library is the constant "pinB2".  Instead of making up separate names and numbers for each pin, I've chosen to use the pin names exactly as described in the datasheet.  This will make code running on "barebones" processors more understandable.

Often performance is cited as the reason registers are directly bit-banged in "main" code.  But this argument is better understood as simply a criticism of existing abstraction layers -- just because one abstraction layer is inefficient does not mean all must be!
 
In AVRez, for performance reasons a pin name actually expands to a complete list of all registers that are needed to manipulate the pin.  Then the macros "pinMode", "digitialWrite", etc just use the registers that they needs and the rest get dropped by the C preprocessor.  This allows the AVRez functions to be quite efficient; for example "pinMode" ultimately becomes just "DDRB |= _BV(PB2);" which is about the most efficient "C" implementation possible (but actually, technically not quite correct.  It should be "cli(); DDRB |= _BV(PB2); sei();". Can you figure out why?  In fact this issue is actually why the AVR processor has 2 special commands just for the job that are named "sbr" and "cbr".  Compare this to the implementations you see in the Arduino core library...

But one issue with this approach is that the name "pinB2" does not have a type.  So you can't store it into a variable like this:

int pin = pinB2;

But it would be quite possible to add a pin enumeration to the library and therefore fall back to something a bit less efficient.  Then you could use something like:

int pin = pinIndex(pinB2);  // Or maybe "= pinB2index;" is clearer...
pinMode(pin, OUTPUT);

One question for thought before I sign off: what is the MOST efficient way to translate a pin enumeration into code?  What we see in the Arduino library ain't it! :-)