ATTiny Getting Started and Code Benchmarking

attiny_bo_built-upOnce I had some hardware to test with, the next steps were to work out how to get a program onto the ATTiny and how to write efficient code. As 1kb of flash memory is not much to play with, space efficiency was a likely programming challenge!

The first step was to find a hardware core definition for the ATTiny13 that would work with the Arduino IDE. An internet search uncovered a few candidates, with the most recent evolutionary attempt (MicroCore) also looking like the most promising for my purposes as it supported the latest IDE versions. It was easy enough to load the board definition through the Arduino IDE by following the instructions at the release site.

MicroCore also supports directly programming the ATTiny13 from the IDE through the programmer. In my case I was using the ArduinoAsISP setting and the shield I described in an earlier post.

Programming the ATTiny13 is a two step process. For a new device the fuses on the hardware need to be set, an easy process described in the MicroCore documentation and driven by a few choices in the IDE menu. The fuses are then written to the hardware as if it was loading a bootloader into the MCU.

To subsequently load compiled code, a simple ‘upload’ from the IDE does all the work. It could not be simpler and hats off to the MicroCore developers.

Benchmarking

Whilst MicroCore supports a useful subset of the Arduino libraries, I wanted to test the overhead imposed by using the libraries.

I decided to use a test project of 2 LEDs connected to 2 separate outputs and have the ATTiny flash them alternately on/off (ie, only one on at any time). To create a more ‘real world’ scenario for the types of applications I envisioned  for these MCUs, I added an optional switch to control the free running flash cycle.

This basic application was then coded using library functions (eg, digitalWrite()) in one version and direct port manipulation in another. For the latter case, one two variants were created – using loop() and using main(). All versions of this code can be found at the end of this article.

attiny_benchmarkThe summarized results for code size (in bytes) are in the table on the left. As expected, the libraries do add some overhead. For this application this amounts to around 50 bytes of flash memory – not a huge amount in bytes but around 25% of the original code size and 5% of the available flash memory. I do expect, however, that the library overhead is a fixed amount and as a proportion of the total code it should reduce as the code size increases.

So what does this all mean? I probably can stick to the Arduino libraries and write portable code. If things get tight, however, I know I can gain at least 5% in program memory.

 

Code using libraries (Blink.ino)

/*
 Blink
 Alternately turns 2 LEDs on for one ON_TIME, then off for OFF_TIME.
 Uses Arduino library throughout.
*/

#define CONTROLLED 0

const uint8_t pinLED1 = 2;
const uint8_t pinLED2 = 1;
#if CONTROLLED
const uint8_t pinSw = 3;
#endif

const uint16_t ON_TIME = 500; // milliseconds
const uint16_t OFF_TIME = 500; // milliseconds

void setup() 
{
  // initialize digital pin as output
  pinMode(pinLED1, OUTPUT);
  pinMode(pinLED2, OUTPUT);

  // Set initial values for digital output (dfefault 0)
  digitalWrite(pinLED2, HIGH);
 
#if CONTROLLED
  // initialize switch input, pullup
  pinMode(pinSw, INPUT_PULLUP);
#endif
}

void loop() 
{
#if CONTROLLED
  if (digitalRead(pinSw) == LOW)
#endif
  {
    digitalWrite(pinLED1, HIGH);
    digitalWrite(pinLED2, LOW);
    delay(ON_TIME);
    digitalWrite(pinLED1, LOW);
    digitalWrite(pinLED2, HIGH);
    delay(OFF_TIME);
  }
}

Code using direct port writes (Blink_Direct.ino)

/*
 Blink_Direct
 Alternately turns 2 LEDs on for ON_TIME, then off for OFF_TIME.
 Uses direct port manipulation
*/

#define CONTROLLED 0

static const uint8_t pinLED1 = 2;
static const uint8_t pinLED2 = 1;
#if CONTROLLED
static const uint8_t pinSw = 3;
#endif

static const uint16_t ON_TIME = 500; // milliseconds
static const uint16_t OFF_TIME = 500; // milliseconds

void setup() 
{
  // initialize digital pin as output
  DDRB = _BV(pinLED1) | _BV(pinLED2);

  // Set initial values for digital output (dfefault 0)
  PORTB = _BV(pinLED2);

#if CONTROLLED
  // initialize switch input, pullup
  // input mode set by default
  PORTB |= _BV(pinSw);
#endif
}

void loop()
{
#if CONTROLLED
  if (!(PINB & _BV(pinSw)))
#endif
  {
    PINB = _BV(pinLED1) | _BV(pinLED2); // toggle the LEDs
    delay(ON_TIME); 
    PINB = _BV(pinLED1) | _BV(pinLED2); // toggle the LEDs
    delay(OFF_TIME);
  }
}

Code using direct port writes and main() (Blink_Direct_Main.ino)

/*
 Blink_Direct
 Alternately turns 2 LEDs on for ON_TIME, then off for OFF_TIME.
 Uses direct port manipulation and includes main()
*/

#define CONTROLLED 0

static const uint8_t pinLED1 = 2;
static const uint8_t pinLED2 = 1;
#if CONTROLLED
static const uint8_t pinSw = 3;
#endif

static const uint16_t ON_TIME = 500; // milliseconds
static const uint16_t OFF_TIME = 500; // milliseconds

void setup() 
{
  // initialize digital pin as output and initialise to zero
  DDRB = _BV(pinLED1) | _BV(pinLED2);
  PORTB = _BV(pinLED2);

#if CONTROLLED
  // initialize switch input, pullup
  // input mode set by default
  PORTB |= _BV(pinSw);
#endif
}

int main() 
{
 setup();

  while(1)
  {
#if CONTROLLED
    if (!(PINB & _BV(pinSw)))
#endif
    {
      PINB = _BV(pinLED1) | _BV(pinLED2); // toggle the LEDs
      delay(ON_TIME); // flash on time
      PINB = _BV(pinLED1) | _BV(pinLED2); // toggle the LEDs
      delay(OFF_TIME); // flash off time
    }
  }
}

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s