New Library for Arduino

Hey guys,

I have never really liked the Arduino libraries so I have written a new one to my liking. I understand that my library is as "nice looking" or as newbie friendly but I think it is far better suited for larger projects and libraries.

I called it OOArduino and it is written in C++. I am calling the current version 0.0.0 but I am already using it in a project and finding it easy to use.

My main gripes with the Arduino libraries are global variables and singletons. My new library requires passing in all of the registers you wish to use. The only thing that it grabs itself is it puts in are the interrupts for which it installs handlers which you can hook into yourself. This also means you have complete control over what is running on your processor at any time.

I can say a lot more but I think the code speaks for itself. I can't build the library on Windows right now but if anyone wants to write build-scripts that would be awesome.

I'm sure there are bugs and there are defiantly still things that need to be implemented so please throw all of your ideas up on the issue tracker.

The project is hosted at bitbucket at https://bitbucket.org/kevincox/ooarduino and I have some docs up at http://kevincox.bitbucket.org/ooarduino/doxygen/.

Of course, if you have anything to say right here is perfect.

Enjoy, Kevin

Hi kevincox.

Could you post some example to see how it works?

My new library requires passing in all of the registers you wish to use

Do you have any plans to do it easier to use? , i mean all the device specific stuff.

Regards.

arcachofo:
Hi kevincox.

Could you post some example to see how it works?

My new library requires passing in all of the registers you wish to use

Do you have any plans to do it easier to use? , i mean all the device specific stuff.

Regards.

Sorry, I meant to mention that I have generator functions. They are located in factory.h and remove much of the tediousness. For example:

// note: not tested.

#include <ooarduino/pin.h>
#include <ooarduino/usart.h>
#include <ooarduino/factory.h>
using namespace OOArduino;

int main ( void )
{
    Pin p13(CREATE_PIN(B, 5, Pin::out));
    Usart usart(CREATE_USART(0));

    // use usart...

    for (;;)
    {
        p13.toggle();
    }

    return 0;
}

There are also Arduino specific functions. (currently only set up for the Deulimilinove becuase thats the board I have.)

// note: not tested.

#include <ooarduino/arduino/pins.h>
using namespace OOArduino;

int main ( void )
{
    Pin p13(Arduino::getPin(13, Pin::out));

    for (;;)
    {
        p13.toggle();
    }

    return 0;
}

I just want to mention one more thing. The best way to get a feel is to look at the docs (http://kevincox.bitbucket.org/ooarduino/doxygen/) and look around. The most interesting classes are Pin (for digital IO), PwmController and PwmPin (for PWM), BufferedSerial (for easy serial comunications) and Clock/LongClock (for timing).

I plan on adding examples of how to do simple things in the wiki but I don't have time right now. Hopefully in a couple of days I'll have blinking lights and serial communication.

When performing a "read-modify-write" operation, you really must be cautious to make it an atomic operation if the same register might ever be manipulated within an interrupt context. Failing to do so leads to infrequent but extremely difficult to diagnose problems. For example:

http://code.google.com/p/arduino/issues/detail?id=146 http://forums.adafruit.com/viewtopic.php?f=31&t=7594

I looked briefly at your pin.cpp. My first impression is it also has issue 146.

On the library design as a whole, I have 3 general comments. This may sound a bit harsh, but hopefully it helps...

First, passing register addresses as parameters to the constructors is going to lead to inefficient code. It might even end up even slower than Arduino's functions? You should really consider using templates, especially if your API is already exposing C++ syntax. Oleg's USB Host Shield library has a hardware abstraction layer that would be one good example. It's possible, with proper use of templates, to get the compiler to optimize the pin usage instances to single instructions (which are automatically atomic operations), and similarly impressive gains could be made with the timers and other peripherals.

Second, passing numerous related registers to a constructor seems unnecessarily complex. You may feel it's hardware agnostic to do so, but it's not. Even the register set is absolutely AVR-8 specific, not to mention the implementation within the class. Even 8-bit AVR XMEGA has a different (larger) set of registers, as does 32 bit AVR. By publishing an API consisting of hardware specific registers tied to exactly one platform, you'll never be (easily) portable to any other hardware platform. It's also error-prone to require more parameters than necessary, especially multiple consecutive parameters of the same type (at least if the types differ, getting them mixed up results in a compile error instead of wrong runtime behavior).

Third, why go to so much trouble to build classes around hardware access, but tie the API so closely to exactly one architecture? Especially if your implementation doesn't strive for good performance, why would anyone want to use it? What's the advantage?

If your goal is a clean API, I think you'd do much better to get a ChipKit or Maple board and think carefully about how your API can work across dramatically different hardware.

If your goal is performance, at the very least look at Oleg's code and as you develop yours, use avr-objdump to view a disassembly of the generated assembly to make sure the compiler really is optimizing at compile time.

If you're really good, you might try for both some degree of cross platform support (or at least an API capable of it) and good performance. But that's very hard.....

[quote author=Paul Stoffregen link=topic=104755.msg785965#msg785965 date=1336392745] When performing a "read-modify-write" operation, you really must be cautious to make it an atomic operation if the same register might ever be manipulated within an interrupt context. Failing to do so leads to infrequent but extremely difficult to diagnose problems. For example:

http://code.google.com/p/arduino/issues/detail?id=146 http://forums.adafruit.com/viewtopic.php?f=31&t=7594

I looked briefly at your pin.cpp. My first impression is it also has issue 146. [/quote] Yes, you are right. For most classes they are intended to be retreent but as Pin only owns part of the register I do need to do it atomically.

[quote author=Paul Stoffregen link=topic=104755.msg785965#msg785965 date=1336392745] First, passing register addresses as parameters to the constructors is going to lead to inefficient code. It might even end up even slower than Arduino's functions? You should really consider using templates, especially if your API is already exposing C++ syntax. Oleg's USB Host Shield library has a hardware abstraction layer that would be one good example. It's possible, with proper use of templates, to get the compiler to optimize the pin usage instances to single instructions (which are automatically atomic operations), and similarly impressive gains could be made with the timers and other peripherals. [/quote] Yes, I though about this and there are defiantly benefits but there are considerable drawbacks. I may consider changing it but I see a couple of problems.

  • Ugly/Hacky - Templates are for genericism and partly code generation (this fits a bit here) but it would also require that the CREATE_* macros not return proper objects. (They would have to generate a parameter list. It isn;t terrible but as my library is intended to be "nice" it doesn't really fit in.
  • Code size, Every function/class would have to be a template and we would have to have a different function/class compiled for each pin/timer/usart... It isn't the biggest deal but it does make a difference.

With templates I would get a lower memory usage as well. But, for now at least (please send other benefits/drawbacks my way), the overhead isn't that big (I haven't measured but I would speculate less than the Arduino functions (they have a number of function calls)). But it is there. I hope to do more optimization (won't help for Pin but it will for higher level classes) as well as seeing how I can get inlining to help. Of course if your code does have parts that need to be fast you could use the basic AVR functionality for those and you can't beat one clock cycle.

[quote author=Paul Stoffregen link=topic=104755.msg785965#msg785965 date=1336392745] Second, passing numerous related registers to a constructor seems unnecessarily complex. You may feel it's hardware agnostic to do so, but it's not. Even the register set is absolutely AVR-8 specific, not to mention the implementation within the class. Even 8-bit AVR XMEGA has a different (larger) set of registers, as does 32 bit AVR. By publishing an API consisting of hardware specific registers tied to exactly one platform, you'll never be (easily) portable to any other hardware platform. It's also error-prone to require more parameters than necessary, especially multiple consecutive parameters of the same type (at least if the types differ, getting them mixed up results in a compile error instead of wrong runtime behavior).[/quote]

The intent is to later add #ifdefs to change things required for other 8-bit avr's. Right now it is completely obvious it would only work on my chip and that is because that is what I have made it for. Even if the constructors do have to change by changing the constructors and the CREATE_* functions in sync that can be managed.

[quote author=Paul Stoffregen link=topic=104755.msg785965#msg785965 date=1336392745] Third, why go to so much trouble to build classes around hardware access, but tie the API so closely to exactly one architecture? Especially if your implementation doesn't strive for good performance, why would anyone want to use it? What's the advantage?

If your goal is a clean API, I think you'd do much better to get a ChipKit or Maple board and think carefully about how your API can work across dramatically different hardware. [/quote] I could write a set of interfaces (Pin, Usart, PwmPin, Clock, etc...) and then change my classes to implement them. This is probably a step in the right direction to support more devices/architectures. It would add worse performance (although I am not too concerned about that.) Right now the intent is to "simulate" the interfaces by providing alternate implementations for the functions via #ifdefs. [quote author=Paul Stoffregen link=topic=104755.msg785965#msg785965 date=1336392745] If your goal is performance, at the very least look at Oleg's code and as you develop yours, use avr-objdump to view a disassembly of the generated assembly to make sure the compiler really is optimizing at compile time. [/quote] I haven't even got here yet but the could of times I did look at the disassembly I did see more instructions than I would like. One day I will get here.

[quote author=Paul Stoffregen link=topic=104755.msg785965#msg785965 date=1336392745] If you're really good, you might try for both some degree of cross platform support (or at least an API capable of it) and good performance. But that's very hard..... [/quote] This would be awesome but this project did stem from a need. I did create it to solve my own problem and it did that pretty well, I did put more effort into it than a throw-away and hope that it shows.

A lot of what you said about cross-platformness is related to the idea that this library not necessarily designed to create "portable" code but rather to simplify development on one chip. More for a write anywhere, compile there, run there. That being said it would be nice if most (if not all) code to worked across all 8-bit avrs.

My target of performance on this library was "acceptable". It shouldn't be ridiculous but when making opportunities for nice code they should be taken. For example, all of the interrupts have an indirection. This makes it so that the library does not "steal" you interrupts but does cause a slight performance degradation. There are libraries out there that focus on having certain things compile down to one instruction but not this one.

Thanks for your thoughts and time looking at the code. Kevin