Testing AVR with C++ library

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone,
I recently started programming AVRs with C++. Everything was ok until I had to do some testing. It seems like most AVR platform specific testing libs don't support C++ features like templates, so I went with C++ header-only lib - Catch (https://github.com/catchorg/Catch2). Problem appeared after running it - tests with only C++ code compiled without problems, but when I tried to test AVR things it shows long list of errors. 

My plan to test AVR:
-isolate setting every register to one class named Avr and create methods like set_bits, clear_bits etc
-pass this class in template like so:

 template<class HW = Avr> 

and use those methods in normal programming
-in testing though use other class AvrTest with mocked methods (like internal array or variable pretending to be register)

So I created stub of test scenerio and included my file which also included <avr/io.h> and compiled it with avr-g++ with c++ support. Everything failed miserably, so I concluded that it doesn't look for C++ std libs used in Catch. I added it manually and also failed. Can somebody help me with that problem? I don't know if I have to isolate AVR code more to not include <avr/io.h> at any time or what.

Here's errors log:

https://pastebin.com/9GkdF5Hf

 

(command used to compile is contained in first line)

 

here's test suite code:

extern "C" {
#include <stdint.h>
}

#include "lib/catch.hpp"
#include "../src/adc.hpp"

SCENERIO("ADC is working properly", "[ADC]") {
  GIVEN("A new Adc object") {
    WHEN("State is checked after creation") {
      THEN("ADC is enabled") {}

      THEN("ADC interrupts are enabled") {}

      THEN("Prescaler is correctly set") {}

      THEN("ADC register is adjusted to simplify usage of 8 bit data") {}
    }
  }
}

Here's ADC class code:

#ifndef ADC_HPP
#define ADC_HPP

extern "C" {
#include <avr/io.h>
}
#include "util/avr.hpp"

template <class HW = Avr>
class Adc {
 public:
  inline Adc() {
    // Uref = AVCC with external capacitor at AREF pin,
    // set let adjust of result for convienient 8bit access
    HW::set(ADMUX, (1 << REFS0) | (1 << ADLAR));

    // ADC enable, free running mode, start first
    // conversion, interrupt on update, turn on
    // interrupts default prescaler (/2)
    HW::set(ADCSRA, (1 << ADEN) | (1 << ADFR) | (1 << ADSC) | (1 << ADIF) |
                        (1 << ADIE));
  }

  inline uint8_t get_8bit_conversion_result() const { return HW::get(ADCH); }

  inline void stop_ADC() const { HW::clear(ADCSRA, (1 << ADEN)); }
  inline void start_ADC() const { HW::update(ADCSRA, (1 << ADEN)); }
};

#endif  // ADC_HPP

aaand avr.hpp code:

#ifndef AVR_HPP
#define AVR_HPP

extern "C" {
#include <avr/io.h>
}

class Avr {
 public:
  static inline volatile uint8_t& get(volatile uint8_t& address) {
    return address;
  }

  static inline void set(volatile uint8_t& address, const uint8_t value) {
    address = value;
  }

  static inline void clear(volatile uint8_t& address, const uint8_t value) {
    address &= ~value;
  }

  static inline void update(volatile uint8_t& address, const uint8_t value) {
    address |= value;
  }

 private:
  Avr() {}
  ~Avr() {}
};

#endif  // AVR_HPP

 

Have a good day!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jump3r3 wrote:
so I concluded that it doesn't look for C++ std libs used in Catch.
Do you mean STL? The AVR version of C++ (avr-g++) does not come with STL though I think there have been something like 3 separate attempts to deliver the same. A google for "avr gcc C++ stl" will likely prove illuminating.

 

EDIT: BTW avr-g++ does have libstdc++ but I imagine you mean things like std::string, std::vector, etc from the Standard Template Library?

 

EDIT2: This is one of the STL implementations I was thinking of: http://andybrown.me.uk/2011/01/1... however I know there's a page somewhere that compares ALL the available implementations and I think it had arguments for why some of the others may be "better".

 

EDIT3: Ah I think this was the comparison I was thinking of: https://www.gammon.com.au/forum/...

Last Edited: Wed. Jul 11, 2018 - 09:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, but STL doesn't solve the problem. As you may see, in error log errors aren't related to absent STL libs, but something is wrong with the floating point types. That can be a avr-g++ / g++ clash because they're using different definitions of those? Or I dunno, maybe types defined in stdint (I don't really remember name of that header, it provided uint8_t and more) are overriding standard types? I'm looking for any clue which would help resolving this issue.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
avr-g++ -mmcu=atmega8 -I /usr/lib/gcc/x86_64-pc-linux-gnu/8.1.1/../../../../include/c++/8.1.1 ...

Who added the paths to the headers of the host compiler? You?

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Maybe this link will help: https://gcc.gnu.org/onlinedocs/g...

 

As an extension, GNU C and GNU C++ support additional floating types, which are not supported by all targets.

  • __float128  is available on i386, x86_64, IA-64, and hppa HP-UX, as well as on PowerPC GNU/Linux targets that enable the vector scalar (VSX) instruction set. __float128 supports the 128-bit floating type. On i386, x86_64, PowerPC, and IA-64 other than HP-UX, __float128 is an alias for _Float128. On hppa and IA-64 HP-UX, __float128 is an alias for long
    double
    .
Last Edited: Wed. Jul 11, 2018 - 10:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@sternst, yes I added them to make Catch happy
@El Tanya's, so I have to do it other way around, right? Compile my tests with g++ and add libs for avr. I tried it before, but even setting internal flag (which isnt recommended TBH) didn't help much. I think this don't substitute setting -mmcu flag, there's something more, maybe linker related. I'll post errors caused by g++ later.

Edit: now I realized that compiling by g++ makes more sense, as we're testing on PC.

Last Edited: Wed. Jul 11, 2018 - 10:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jump3r3 wrote:
@sternst, yes I added them to make Catch happy
But you simply can't use them for a completely different compiler/target.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, I think for your tests, you need to compile the AVR stuff for the PC and not the other way around. I can't even begin to imagine what kind of problems this will cause.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okey, compiled with G++ with flag for atmega8 set. Doesn't work either, I may be wrong but I think this is connected with incompatible exceptions provided from AVR headers. 

I'll probably try to create complete HAL to avoid this.

 

Here's log:

https://pastebin.com/X8HKQJpq

 

If somebody got any other idea, I'll appreciate it. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I think this is connected with incompatible exceptions provided from AVR headers. 

"Exceptions"???  I don't think avr-g++ supports c++ exceptions; it is one of the things that makes porting normal C++ code so frustrating.

But I'm not sure the errors you posted have anything to do with C++ exceptions, so maybe you meant something else...

 

 

/usr/include/c++/8.1.1/x86_64-pc-linux-gnu/bits/c++locale.h:52:23: error: ‘uselocale’ was not declared in this scope

I think the chances of avr-g++ being able to support something like the locale features desktop users have come to expect are ... small.  No OS, no locale support.

/usr/lib/gcc/avr/8.1.0/../../../../avr/include/time.h:116:22: error: conflicting declaration ‘typedef uint32_t time_t’
vs  typedef __time_t time_t;

And avr-libc has its own "time" library that is substantially different from "normal."

 

/usr/include/stdlib.h:62:5: error: conflicting declaration ‘typedef struct div_t div_t’
   } div_t;
/usr/include/stdlib.h:70:5: error: conflicting declaration ‘typedef struct ldiv_t ldiv_t’
   } ldiv_t;

These are a little puzzling, because the div_t and ldiv_t structures from avr-libc look just like the ones in /usr/include...

OTOH, you really need to be sure that things from /usr do NOT get included when compiling avr code.  You need the ones from ...avr/include

One of the major differences is going to be sizeof(int)

 

 

/usr/include/bits/types/FILE.h:7:25: note: previous declaration as ‘typedef struct _IO_FILE FILE’
 typedef struct _IO_FILE FILE;

/usr/include/c++/8.1.1/cstdio:179:11: error: ‘::vsscanf’ has not been declared

No actual "files" in avr-gcc.  Limited stdio library.

 

 

/usr/include/c++/8.1.1/memory:116:58: error: cast from ‘void*’ to ‘uintptr_t’ {aka ‘short unsigned int’} loses precision [-fpermissive]

sizeof(int) issue?

 

       bad_message =     EBADMSG,
                         ENOMSG
       identifier_removed =    EIDRM,
                               EEDR
       no_link =     ENOLINK,
       no_link =     ENOLINK,
        ... etc ...

No OS, no global error codes.

 

/usr/include/c++/8.1.1/x86_64-pc-linux-gnu/bits/ctype_base.h:49:35: error: ‘_ISupper’ was not declared in this scope
     static const mask upper     = _ISupper;

/usr/include/c++/8.1.1/x86_64-pc-linux-gnu/bits/ctype_base.h:50:32: error: ‘_ISlower’ was not declared in this scope
     static const mask lower  = _ISlower;
/usr/include/c++/8.1.1/x86_64-pc-linux-gnu/bits/ctype_base.h:51:32: error: ‘_ISalpha’ was not declared in this scope
     static const mask alpha  = _ISalpha;
/usr/include/c++/8.1.1/x86_64-pc-linux-gnu/bits/ctype_base.h:52:32: error: ‘_ISdigit’ was not declared in this scope
     static const mask digit  = _ISdigit;
/usr/include/c++/8.1.1/x86_64-pc-linux-gnu/bits/ctype_base.h:52:32: note: suggested alternative: ‘_ISwdigit’
     static const mask digit  = _ISdigit;

... etc

I'm a little surprised by these, since avr-libc does have a set of chracter classification functions https://www.microchip.com/webdoc...

Perhaps the C++ ones are supposed to be locale-aware (and ... no locales...)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

C and C++ standards are a moving target.

GCC has a flag to compile to a specific standard with the -std= flag.

Some of the supported -std flags are c99, c11 c++98, gnu++11, but there are more of them.

 

Your results may also vary with the version of the compiler you are using.

I simply use the version installed by apt on my linux box, but it is a few years old.

The most recent version vor avr-gcc seems to be the version maintained and downloadable from the Atmel / Microchip site.

 

I also know of a few attempts to use templates for I/O register definitions, but those are all for ARM Cortex processors.

But you might still be interested to get some Ideas.

http://xpcc.io/getting-started/
https://github.com/roboterclubaachen/xpcc
http://blog.salkinium.com/typesafe-register-access-in-c++/
http://www.eld.leidenuniv.nl/~moene/Home/books/

https://github.com/kvasir-io/Kvasir

https://www.voti.nl/bmptk/docs/index.html

https://www.youtube.com/watch?v=A_saS93Clgk

Edit:

"Microsoft" -> "Microchip". Oops...

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

Last Edited: Sun. Jul 15, 2018 - 05:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"definitions", my bad. What I tried to get is AVR definitions needed by avr/io.h and ensure everything else is using my native platform definitions (x86_64). Today came to me that int8_t for example isn't another type but typedef for char, so I can't use AVR definition for int (which is 8 or 16 bits wide) and my system's definition for int (which is likely wider). So thanks for enlightening that and TIL I guess.  

Last Edited: Thu. Jul 12, 2018 - 09:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Okey, I moved everything into abstraction using static polymorphism by templates, so far it's working, so if you or anybody is interested in this I'll post code on Github in a day or two. 

I'm happy that everything is done in compile time, so size remains the same as I would use registers directly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd like to take a look, for sure.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okey, so I completed project to point where it's in usable state. It isn't finished though, but I'm working on it.

So, most interesting things:
1. I used OOP C++, it's quite efficient
2. I've written HAL which abstracts most of AVR stuff (except ISR() calls, this is implemented via awful macro)
3. It has tests in C++ which uses HAL, effectively emulating AVR (it's in WIP state, it's necessary to implement some things done by hardware by yourself)

Here's some links:
1. HAL (incomplete):
https://github.com/jakub-gonet/AVR_O-scope/blob/master/src/util/avr.hpp

2. main file:

http://https://github.com/jakub-gonet/AVR_O-scope/blob/master/src/main.cpp

3. example class encapsulating AVR peripheral

http://https://github.com/jakub-gonet/AVR_O-scope/blob/master/src/adc.hpp

4. ...and test for it

http://https://github.com/jakub-gonet/AVR_O-scope/blob/master/test/adc_test.cpp

 

Anyway, I'm quite new here, are somewhere in forum hints for C++ on AVR except that pinned post from TFrancuz or not really?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Paulvdh wrote:
downloadable from the Atmel / Microsoft (sic) site.

 

You mean Microchip, of course .. ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...