avrIO
avrIO copied to clipboard
C++11/17/20 library for manipulating I/O port pins and I/O registers of AVR8
** avrIO [[https://github.com/ricardocosme/avrIO/actions?query=workflow%3A%22tests%22][https://github.com/ricardocosme/avrIO/workflows/tests/badge.svg?branch=main]] [[https://github.com/ricardocosme/avrIO/actions?query=workflow%3A%22demos%22][https://github.com/ricardocosme/avrIO/workflows/demos/badge.svg?branch=main]] C++11/17/20 library for manipulating I/O port pins and registers of AVR8. The purpose of this library is to provide a higher level of abstraction for operating I/O ports and general I/O registers with zero-overhead in mind. It is a header-only library that does not require any external dependencies. The only requirement is to compile with ~avr-gcc~ using ~-Os~ optimization and have at least C++11 support. ~avr-libc~ is a reference for this work and avrIO can be seen as a C++ approach for ~avr/io.h~
It can be useful in the application layer, delivering an expressive code(see [[file:demo/c++11/led.cpp][demo/c++11/led.cpp]]) as also be used to develop flexible and concise APIs(see [[file:demo/c++20/api.cpp][demo/c++20/api.cpp]]).
**** Single I/O port pin
#+BEGIN_SRC C++ Pb0 led{output}; Pb3 sw{pullup};
while(true) led.high(sw.is_low()); #+END_SRC [[file:demo/c++11/led.cpp][demo/c++11/led.cpp]]
A LED connected to the pin ~PB0~ is turned on when the switch connected to ~PB3~ is on.
It illustrates the operation of on one pin at a time.
**** Multiples I/O port pins #+BEGIN_SRC C++ auto [swA, swB] = in(pullup, pb2, pb1); auto [ledA, ledB] = out(pb0, pb4);
while(true) set(ledA(lazy::is_low(swA)), ledB(lazy::is_low(swB))); #+END_SRC [[file:demo/c++17/leds.cpp][demo/c++17/leds.cpp]]
A LED connected to the pin ~PB0~ is turned on when the switch connected to ~PB2~ is on and a LED connected to ~PB4~ is turned on when the switch connected to ~PB1~ is on.
It illustrates the operation of more than one pin at once. There is a C++11 version of demo at [[file:demo/c++11/leds.cpp][demo/c++11/leds.cpp]].
**** Developing API
#+BEGIN_SRC C++ template<avr::io::Pin Pin> struct led_t { Pin pin; led_t(Pin ppin) : pin(ppin) { out(pin); }; void on(bool v = true) { high(pin, v); } };
int main() { led_t led{pb0}; Pb3 sw{pullup};
while(true) led.on(sw.is_low());
} #+END_SRC [[file:demo/c++20/api.cpp][demo/c++20/api.cpp]]
The version above has support to C++20 Concepts and the template parameter of the class template ~led~ is implicitly deducted. There is a version [[file:demo/c++11/api.cpp][demo/c++11/api.cpp]] with support to C++11.
This illustrates how to design an API that receives an I/O port pin.
This library can help the designer to offer an API that needs to handle pins. It can receive the information using a flexible and minimal representation. The argument ~Pin~ can be anything that models the concept ~avr::io::Pin~ and with only one object the the location of the pin can be represented. There is no need, for example, to ask for the registers that are related to the pin in question.
**** I/O registers
#+BEGIN_SRC C++ //enables the power-down sleep mcucr = (mcucr & ~sm0) | sm1 | se;
//or something more expressive and concise set(se, sm1, sm0(off));
//an integer can still be assigned portb = 0x07;
//or something more expressive that doesn't use bitwise operators portb = {pb3, pb2, pb1};
portb = pb0 | pc1; //compile error because it isn't allowed to mix //bits from different registers.
portb = pb0 | pc1.bv(); //Ok. The byte value can be obtained to bypass //the type system. #+END_SRC Note: Using the ATtiny13A here to illustrate some operations on register but the same thing can be done at any other MCU that is supported by the library.
*** Performance The goal here is to compare the code generated using [[https://github.com/ricardocosme/avrIO][avrIO]] with a hypothetical reference code that doesn't use any expressive abstration, like the one that uses ~sbi~ or ~cbi~ instructions directly in the code.
Builds using ~avr-gcc-10.2 -mmcu=attiny13a -Os~.
**** demo/c++11/led.cpp [-std=c++11]
56 bytes
#+BEGIN_SRC
00000022
**** demo/c++11/leds.cpp [-std=c++11]
70 bytes
#+BEGIN_SRC
00000022
**** demo/c++20/api.cpp [-std=c++20]
56 bytes
#+BEGIN_SRC
00000022
As we can see, there is no overhead due to the library in the above examples.
*** How to use it? This is a header only library that doesn't require any external dependency to work. It should be enough add the path to the ~include~ directory to your project:
- Check the requirements section.
- Add the ~include~ directory to your include path.
- Add ~#include <avr/io.hpp>~ to your source and enjoy it!
*** Supported microcontrollers
- ATtiny13A/13
- ATtiny25/45/85
- ATmega328P
*** Requirements
- ~avr-gcc~ with at least ~-std=c++11~ (Tests with ~avr-gcc 10.2~)
- Optimization level greater or equal to ~-O2~. This library is designed with the optimization ~-Os~ in mind.