Arduino-Lite, Lightweight AVR library developed by RoboPeak(1)

中文版请看这里

Arduino-Lite is a lightweight high-performance firmware library for AVR MCUs, which is developed based on the original Arduino project. We make it easy to use, just like the Arduino. Compared to the original library, Arduino-Lite generates smaller binaries and perform faster.

And now, we are honored to introduce the Arduino-Lite to the public, and to make it open source.

The open source version of Arduino-Lite can be downloaded at Google Code: http://code.google.com/p/arduino-lite/

This is the first one of the series articles. Detailed usage of Arduino-Lite will be posted soon.

0. Supported Devices

Besides the standard Arduino controller boards based on Atmega328/168, all third-party boards using the following AVR chips are supported. We also published a driver-less AVR/51 programmer called RP USB Connector which is used internally by RoboPeak Team. The firmware of RP USB Connector is also developed with the Arduino-Lite library.

1. Why yet another library, and why to use it

Arduino and Arduino-Lite are both written with C++/C, and based on avr-gcc, but the Arduino-Lite has some pretty advantages:

  • Very very lightweight

Most binaries based on Arduino-Lite are 50% smaller than which based on Arduino.

  • High Efficiency

Many functions provided by Arduino-Lite, such as DIGITAL_WRITE, which is equivalent to the digitalWrite in the Arduino, implemented by only one AVR instruction.

  • More AVR chips and frequencies are supported

Besides Atmega8(A), Atmega168(PA), Atmega328(PA), Atmega1280, Arduino-Lite also supports Attiny2313, Attiny26, Atmega48(PA), Atmega88(PA)

Working frequencies supported by Arduino-Lite are ranging from 1Mhz to 20Mhz.

However, Arduino-Lite advances in following features as well:

  • Self-contained, no third-party tools, compilers or libraries dependencies.

With Arduino-Lite, the only tool you need to develop, compile or burn to devices is the common text editor, which is delivered by most modern operating system.
Avr-gcc(WINAVR) and relative libraries comes with the Arduino-Lite package.

  • Flexible and easy-to-integrate Makefile-based compiling system, but no more Makefile composing or generating operations are required.

The easiest way to create a new Arduino-Lite project is decompress the template and rename it. You can place your code in any place in the project directory. And Arduino-Lite will compile your project properly. You don’t need to modify/compose/generate Makefile anymore.

1.0 Who and when to use Arduino-Lite

We think Arduino-Lite will be perfect choice in following situations:

  • Binary size or device cost sensitive, such as situations have to use Attiny or Atmega48 which contain small ROMs.
  • Performance sensitive, such as some real-time applications like industry controlling and robot controlling
  • Make gurus or IDE fans
  • Interested in Arduino/AVR, expecting a better IDE than the Arduino IDE and more effective firmware library
  • Developing firmware for those devices and frequencies not supported by Arduino in Arduino-way.

Arduino-Lite might not be suitable for:

  • Who don’t like command line or Makefiles (We are planing to make Arduino-Lite to support the Arduino IDE)
  • Whose projects are based on third-party Arduino libraries

1.1 Advantages shared by Arduino and Arduino-Lite

It is easy to do some common IO operations with Arduino, such as digitalWrite() digitalRead() and analogWrite(). This makes e-artists easier to develop their artworks, not requiring them to be familiarly with registers like DDR and PIN. Hobbyists without any background in MCU development can access the PWM features without knowing the working modes of the timers.

For instance, developing with the standard avr-gcc (WINAVR), developers need to write the following code to make the LED fading:

//Set PWM mode: fast mode for timer 0
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);
//set timer 0 prescale factor to 64
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);
sbi(DDRB, PB1);
sbi(TCCR, COM11);
unsigned char led_brightness = 0;
for (led_brightness=0; led_brightness = 255; led_brightness++)
{
   OCR01 = led_brightness;
}

Above code require the developer understand the name and usage of registers, what harden the development. Developers, even if experienced, might forget the detailed usage of some register.What’s more, registers of different AVR chips are differing from each other.

Based on Arduino, code can be simple like this:

pinMode(9, OUTPUT);
unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)
{
   analogWrite(9, led_brightness);
}

Arduino makes code simple and expressive, and makes it compilable across different chips as well.
We think the following features make the Arduino excellent:

  • Wrap common operation into functions, to hide the difference of devices
  • Renaming AVR IO pins to a serial number; compared to names like PB1 and PC3, it is quite easier to use. This feature hides the different pin binding of chips, and makes programs cross-platform.

The above advantages make the Arduino widely used. While developing the Arduino-Lite, we inherits these features. The code base on Arduino-Lite with the same functionality looks like this:

PIN_MODE(9, OUTPUT);
unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)
{
   ANALOG_WRITE(9, led_brightness);
}

Almost the same as the Arduino version, only letter cases are changed. In order to easily migrate Arduino code to Arduino-Lite, most Arduino-Lite version of the functions in the Arduino come with the all capitalized letters.

1.2 The defects to the Arduino

Slow

The implementation of digitalRead() digitalWrite() and analogWrite() functions in Arduino are very heavy, and less effective.

In order to make the numbering pin feature work properly, while invoking IO operating functions, pin numbers need to be converted to the controller register. Let us see into the analogWrite() function:

void analogWrite(uint8_t pin, uint8_t val)
{
 pinMode(pin, OUTPUT);
 if (digitalPinToTimer(pin) == TIMER1A) {
  // connect pwm to pin on timer 1, channel A
  sbi(TCCR1A, COM1A1);
  // set pwm duty
  OCR1A = val;
 } else if (digitalPinToTimer(pin) == TIMER1B) {
  // connect pwm to pin on timer 1, channel B
  sbi(TCCR1A, COM1B1);
  // set pwm duty
  OCR1B = val;

 } else if (digitalPinToTimer(pin) == TIMER0A) {
  if (val == 0) {
   digitalWrite(pin, LOW);
  } else {
   // connect pwm to pin on timer 0, channel A
   sbi(TCCR0A, COM0A1);
   // set pwm duty
   OCR0A = val;
  }
 } else if (digitalPinToTimer(pin) == TIMER0B) {
  if (val == 0) {
   digitalWrite(pin, LOW);
  } else {
   // connect pwm to pin on timer 0, channel B
   sbi(TCCR0A, COM0B1);
   // set pwm duty
   OCR0B = val;
  }
 } else if (digitalPinToTimer(pin) == TIMER2A) {
  // connect pwm to pin on timer 2, channel A
  sbi(TCCR2A, COM2A1);
  // set pwm duty
  OCR2A = val;
 } else if (digitalPinToTimer(pin) == TIMER2B) {
  // connect pwm to pin on timer 2, channel B
  sbi(TCCR2A, COM2B1);
  // set pwm duty
  OCR2B = val;
 } else if (val < 128)
  digitalWrite(pin, LOW);
 else
  digitalWrite(pin, HIGH);
}

The code is long. If we operate the registers directly, it will be:

OCR01 = led_brightness;

The difference between codes makes 10x difference in performance. This makes Arduino not be suitable for efficiency-sensitive products or fields.

The size of the binary

For the same reason, the size of the binary of Arduino are really big. The fading led example is about 2kb compiled with the Arduino library, what is about half of the available rom size of Atmega48/attiny chips.

Binaries like this size will not be acceptable in cost-sensitive situations, and even to the hobbyists, large binaries limit the feature of the program.

Only limited AVR models are support, and have some restrictions on external hardwares

Arduino only support Atmega8/Atmega168/Atmega328/Atmega1280 working on 8MHz or 16MHz with external crystal. Some products doesn’t need such high performance chips, might choose Attiny series, atmega48 or atmega88, which is cheaper. In some application, the two frequencies cannot cover the requirement of performance or power consumption. Arduino cannot help in these situations.

1.3 Modifications made by Arduino-Lite

Thanks to the marcos in Arduino-Lite, we keep it simple but providing higher performance and smaller binary size. Almost all core functions in Arduino have their Arduino-lite version, most of which are implemented by marcos. In Arduino-Lite, the conversion from numbering pin to AVR registers are finished in the compiling phase, which is in the runtime in Arduino.

Let take the PWM example once more: The equivalent of analogWrite(pin, value) in Arduino-Lite is ANALOG_WRITE(pin, value).

Outputing PWM signals to the Pin 9 should be like this:

ANALOG_WRITE(9, pwm_value);

This is almost the same as the arduino version, but after macro expension:

#define ANALOG_WRITE( pin, val )  \
    do{                          \
         PWM_ENABLE(pin);        \
         PWM_SET(pin,val);       \
    }                            \
    while(0)

And expend the PWM_ENALBE and PWM_SET:

sbi(TCCR1A, COM1A1);
OCR1A = pwm_value;

Compared to the heavy lone Arduino version,Arduino-Lite generates only 2 AVR instructions to fulfill the same requirement.

Arduino-Lite also provide reduced interfaces, such as PWM_SET(pin, value) generates only 1 instruction.

Let’s compare the binary size of the LED fading example

LED Fading(PWM)

  • Arduino version: 2048 byte
  • Arduino-Lite version: 100byte

In Arduino, ADC pins (PC port) can only operate in analog mode (using analogRead), and cannot be used as digital IO port. In Arduino-Lite, we extended the pin numbers of Arduino, and make adc pins to be operated as digital IOs. The pin definition of AtMegaX8(m48,m88,m168,m328):

// ATMEL ATMEGA8 & 168
//
//                  +-\/-+
//            PC6  1|    |28  PC5 (AI 5/*D19)
//      (D 0) PD0  2|    |27  PC4 (AI 4/*D18)
//      (D 1) PD1  3|    |26  PC3 (AI 3/*D17)
//      (D 2) PD2  4|    |25  PC2 (AI 2/*D16)
// PWM+ (D 3) PD3  5|    |24  PC1 (AI 1/*D15)
//      (D 4) PD4  6|    |23  PC0 (AI 0/*D14)
//            VCC  7|    |22  GND
//            GND  8|    |21  AREF
//     *(D20) PB6  9|    |20  AVCC
//     *(D21) PB7 10|    |19  PB5 (D 13)
// PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
// PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
//      (D 7) PD7 13|    |16  PB2 (D 10) PWM
//      (D 8 )PB0 14|    |15  PB1 (D 9) PWM
//                  +----+

We will introduct the detailed guide to install and use Arduino-Lite in following articles.

Leave a comment ?

30 Comments.

  1. Arduino-Lite! Lightweight AVR Library | ITead Studio - pingback on 2011/04/01 at 01:32
  2. Aah, come on. Why you can not just use the same function names? They do the same thing right? You are not using the Arduino at the same time right? So why change?

    Having the same function name would me code cross compatible between Arduino and Arduino-Lite.

    • Good suggestion:-). Actually there exist some difference between Arduino-lite and Arduino versions. The Arduino-Lite’s functions(Macros) cannot use variable as its pin parameter.

      If you want to completely replace the Arduino functions, you may try writing code like this:

      #define digitalWrite DIGITAL_WRITE
      #define analogWrite ANALOG_WRITE

      And put the code into a common head file. Then those existing Arduino-based libraries may be used directly without changes.

  3. Great project. Can we use Arduino IDE to compile the sketch?

    • The arduino-lite source can be compiled inside Arduino IDE. We haven’t verified it yet, but it should work. Simply replace the arduino core source code with the Arduino-Lite source. You may have a try:-)

  4. How come when I click build.cmd or enter make, make is looking for the winavr installation at:

    make: stat: d:/csksoft/svn_pool/arduino-lite/bin/win32/winavr-20090313/lib/gcc/../../avr/include/stdio.h:

    I have change the WinAVR home in the makefile to
    c:/Electronics/arduino-lite/bin/win32/WinAVR-20090313

    • The example path in the makefile is misleading, you may try to use the following path:

      /c/Electronics/arduino-lite/bin/win32/WinAVR-20090313

      BTW. you do not need to modify the makefile if you just want to use the self-contained WinAVR inside Arduino-Lite.

  5. HI again,

    I trying to port simple arduino sketch below that compiles to 3566 bytes.
    **************************************************
    int sensorPin1 = 0;
    int sensorPin2 = 1;

    void setup()
    {
    Serial.begin(9600);
    }

    void loop()
    {

    int reading1 = analogRead(sensorPin1);
    int reading2 = analogRead(sensorPin2);

    float voltage1 = reading1 * 4.97;
    float voltage2 = reading2 * 4.97;
    voltage1 /= 1024.0;
    voltage2 /= 1024.0;

    float temperature1C = voltage1 * 100 ;
    float temperature2C = voltage2 * 100 ;

    Serial.print(“Temp1 = “);Serial.print(temperature1C); Serial.print(” C”);
    Serial.print(” Temp2 = “);Serial.print(temperature2C); Serial.println(” C”);
    delay(1000);
    }
    **************************************************

    The corresponding arduino-lite port I did is below:
    **************************************************
    #include “arduino_lit.h”
    #define sensorPin1 0
    #define sensorPin2 1

    void setup()
    {
    serial_begin(9600);
    PIN_MODE(sensorPin1, INPUT );
    PIN_MODE(sensorPin2, INPUT );
    enable_adc();
    }

    void loop()
    {
    unsigned int reading1 = analogRead(sensorPin1);
    unsigned int reading2 = analogRead(sensorPin2);

    float voltage1 = reading1 * 4.97;
    float voltage2 = reading2 * 4.97;

    voltage1 /= 1024.0;
    voltage2 /= 1024.0;

    float temperature1C = voltage1 * 100 ;
    float temperature2C = voltage2 * 100 ;

    print(“Temp1 = “, serial_puts);
    print(temperature1C, serial_puts);
    print(” C”, serial_puts);

    print(” Temp2 = “, serial_puts);
    print(temperature2C, serial_puts);
    print(” C\n”, serial_puts);

    delay(1000);
    }

    int main()
    {
    init();
    setup();
    while(1)
    loop();
    }
    **************************************************

    The arduino-lite codes compile to 3270 bytes.
    Is this correct? I thought the code should be smaller by about 1/2 of the original? Am I doing something wrong?

    • The code should be OK. Arduino-lite doesn’t guarantee the generated binary is always 50% smaller.

      For your case, the use of float point will introduce about 2Kb’s code size. (AVR use software emulation to calculate float point operations). Though the Arduino’s Serial class has been replaced with the serial_xxx functions, the code size reduction introduced by these functions is relatively small compared to the big float point emulation code.

    • All depends in what you want…

      For example for me this sketch compiles to 5416 bytes for mega1280:
      ***********************************************

      int sensorPin1 = 0;
      int sensorPin2 = 1;

      void setup()
      {
      pinMode(sensorPin1, INPUT );
      pinMode(sensorPin2, INPUT );

      digitalWrite(sensorPin1, LOW); // Ensure pullups disabled
      digitalWrite(sensorPin2, LOW); // Ensure pullups disabled

      Serial.begin(9600);
      }

      void loop()
      {

      int reading1 = analogRead(sensorPin1);
      int reading2 = analogRead(sensorPin2);

      float voltage1 = reading1 * 4.97;
      float voltage2 = reading2 * 4.97;

      voltage1 /= 1024.0;
      voltage2 /= 1024.0;

      float temperature1C = voltage1 * 100 ;
      float temperature2C = voltage2 * 100 ;

      Serial.print(“Temp1 = “);
      Serial.print(temperature1C);
      Serial.print(” C”);

      Serial.print(” Temp2 = “);
      Serial.print(temperature2C);
      Serial.println(” C”);

      delay(1000);
      }

      ***********************************************

      The corresponding arduino-lite port I did ,using arduino IDE with arduino-lite core:
      ***********************************************

      #define sensorPin1 0
      #define sensorPin2 1

      void setup() {
      serial_begin(9600);
      enable_adc();
      }
      void loop(){
      int reading1 = analogRead(sensorPin1);
      int reading2 = analogRead(sensorPin2);

      float voltage1 = reading1 * 4.97; voltage1 /= 1024.0;
      float voltage2 = reading2 * 4.97; voltage2 /= 1024.0;

      float temperature1C = voltage1 * 100;
      float temperature2C = voltage2 * 100;

      PRINT(“Temp1 = “); PRINT(temperature1C); PRINT(” C”);
      PRINT(” Temp2 = “); PRINT(temperature2C); PRINT(” C\n”);
      delay(1000);
      }

      ***********************************************
      The arduino-lite codes compile to 3728 bytes for mega1280.

      All the sketches is this thread do the same.

      Regards.

  6. JohanEkdahl

    1. I just skimmed the first article. I do think I understand what you think is the advantage, but if I got it right there are also disadvantages. The Arduino framework is bloated and slow because you can have a run-time-variable argument to e.g. DIGITAL_WRITE. I seem to read into your lean-and-mean solution that Arduino-light does not support this feature? Am I corect? If so you ought to mention that too..

    2. Unless you have a blessing from the Arduino folks, you probably need to change the name. From the official Arduino site:

    • You are correct. Actually we had documented this issue in each function descriptions in the “Arduino-Lite Development Reference Manual”.

      And thanks for mention us the naming policy, we use this name just for a better understanding of the lib and let more people to use it. If the name matters, we will change it.

  7. Why on earth would a “function” name be all in upper case? I assume it’s a macro?

    EDIT: Oh dear God, I went to download the code to investigate this – it’s 21.3MB – exactly what is “lite” about 21MB?

  8. Interesting project.

    I didn’t like arduino due to the bloated code, but i like arduino hardware. I decided to order an 1280 board and use other compiler, but.. let’s try this…

    I tried Arduino Lite whits little modifications in Linux and compiles ok, but i just have atmega128 and atmega32 at hand, so i have to wait for my 1280 borad to try it.

    What about supporting atmega128??

    Best regards.

    • Hi arcachofo,
      Currently, Arduino-Lite doesn’t support mega128 or mega32, as we don’t have such chips on hand. But such chips can be enabled with a little porting efforts, as they are all AVRs.

  9. Hi csk, thanks for the reply.

    If you point me to the chamges needed i’ll be happy to do the effort.

    I also could add an option in makefile to enable Linux compiling.

    • Hi arcachofo,

      Sorry for the delay. Basically, you need to implement those AVR chip specific code for the new AVR chip. These code are guarded by the Macro like __AVR_ATmega8__.

      e.g. in the file wiring.c

      The following code contains the timer config implementations for Atmega8 and AtmegaX8(48 88 168)

      #if !defined(__AVR_ATmega8__)
      sbi(TCCR0A, WGM01);
      sbi(TCCR0A, WGM00);
      #endif
      // set timer 0 prescale factor to 64
      #if defined(__AVR_ATmega8__)
      sbi(TCCR0, CS01);
      sbi(TCCR0, CS00);
      #else
      sbi(TCCR0B, CS01);
      sbi(TCCR0B, CS00);
      #endif
      // enable timer 0 overflow interrupt
      #if defined(__AVR_ATmega8__)
      sbi(TIMSK, TOIE0);
      #else
      sbi(TIMSK0, TOIE0);
      #endif

      • Hi again.

        I found this work is already done for several mcus, have a look to arduino-extras.zip here:
        http://www.avr-developers.com/corefiles/index.html

        They use annother aproach, not by mcu, but by used registers when possible:

        #if defined(TCCR0A) && defined(WGM01)
        sbi(TCCR0A, WGM01);
        sbi(TCCR0A, WGM00);
        #endif

        //* set timer 0 prescale factor to 64
        #if defined(__AVR_ATmega128__)
        //* I dont like to have to CPU specific but its different values for the ATmega128
        sbi(TCCR0, CS02);
        #elif defined(TCCR0) && defined(CS01) && defined(CS00)
        //* this combination is for the standard atmega8
        sbi(TCCR0, CS01);
        sbi(TCCR0, CS00);
        #elif defined(TCCR0B) && defined(CS01) && defined(CS00)
        //* this combination is for the standard 168/328/1280/2560
        sbi(TCCR0B, CS01);
        sbi(TCCR0B, CS00);
        #elif defined(TCCR0A) && defined(CS01) && defined(CS00)
        //* this combination is for the __AVR_ATmega645__ series
        sbi(TCCR0A, CS01);
        sbi(TCCR0A, CS00);
        #else
        #error Timer 0 prescale factor 64 not set correctly
        #endif

        // enable timer 0 overflow interrupt
        //#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
        #if defined(TIMSK) && defined(TOIE0)
        sbi(TIMSK, TOIE0);
        #elif defined(TIMSK0) && defined(TOIE0)
        sbi(TIMSK0, TOIE0);
        #else
        #error Timer 0 overflow interrupt not set correctly
        #endif

        There are also pin definitions for each board and so.

  10. Ok, i had a look to sources and have an idea about the changes.

    I am thinking about arduino libraries.. perhaps should be posible have some option to map arduino functions to lite macros and use arduino libraries?? , for example lcd and so…

    • Yes, it is possible. One approach is simply define the standard Arduino functions as the lite macros

      i.e.

      #if defined(digitalWrite)
      #undef digitalWrite
      #endif

      #define digitalWrite DIGITAL_WRITE

      But as the lite’s macros don’t support variables as their input parameters, so some of the library cannot be enabled in such way.

  11. Thanks for the tip, very useful.

    Just letting my mind fly:

    I imagine some kind of pre-compile step where arduino functions are replaced by lite’s macros.. but only when function’s first argument is integer, for example this code:

    digitalWrite( 10, HIGH );

    digitalWrite( varname, HIGH);

    is converted to:

    DIGITAL_WRITE( 10, HIGH );

    digitalWrite( varname, HIGH );

    this is saved to an intermediate file wich then is compiled.

    this way any arduino code could be compiled using lite’s macros when possible and variables can be also used.

    This could be done in a not very complex java file for portability.

  12. Just a question, do you have any plan to support any other AVR chips, e.g. ATTiny84 or ATTiny85?

  13. One example of the diference that lite makes, the reduced size is good, but the main diference is in speed, mostly in aplications that use extensive use of pins read/write.

    For example: drawing a full-screen bitmap in a nokia 5110 lcd, with mega1280, arduino-lite integrated in arduino 1.0 IDE.

    arduino: 181 ms
    lite: 21 ms

    this is x8.6 FPS.

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>