C++17 component to use SSD1306 displays using ATtiny85

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

Hi,

 

I would like to share with you my work to develop a generic component written in C++17 to use SSD1306 displays controlled by ATtiny85.

The key points about this work are:

1. High level of abstraction;

2. Code reusability;

3. High space efficiency.

 

My initial motivation was to explore how a modern C++ approach can be used to develop good programs to ATtiny85. It seems to me that the majority of the community that have the space performance as a goal is ignoring C++ because believes that the language can't perform so well as the C programming language. This is true when we are talking about an "archaic C++" that uses dynamic polymorphism and doesn't abuse of the compile-time phase that can bring run-time bugs to compile-time and can do other things like generate optimized code under the hood. 

 

Let me know what you think.

 

https://github.com/ricardocosme/...

 

--

Cosme

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

Last Edited: Mon. Jan 20, 2020 - 12:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Helpful, thanks for showing this.

 

Can you explain what sed is doing, I have not used it in years and years and years.

 

https://github.com/ricardocosme/att85/blob/develop/demo/ssd1306/Makefile#L19

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

Certainly, it's true that C++17 offers a lot of fancy new features that might allow better code generation. How does that work out? Do you have any benchmarks or comparisons for space used by roughly comparable programs and libraries?

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

ron_sutherland wrote:

Can you explain what sed is doing, I have not used it in years and years and years.

 

https://github.com/ricardocosme/att85/blob/develop/demo/ssd1306/Makefile#L19

Yes, we're using the 's' command of the sed to insert the makefile that lists the dependencies of a source as a target of itself. For example, let's suppose that we have a source 'main.cpp' with the following content:

//main.cpp
#include "foo.hpp"
int main(){}

Let's suppose that 'foo.hpp' doesn't have any includes at all. So, the object file 'main.o' have two dependencies: 'main.cpp' and 'foo.hpp'. We can have a makefile(main.d) to represent this scenario:

main.o : main.cpp foo.hpp

This is important because we would like to automatically recompile 'main.o' when make is executed and one of the dependencies like 'foo.hpp' has changed. But, if 'main.d' isn't a target of itself, one natural change in the list of the dependencies, like adding a new include in 'foo.hpp', for example, the 'main.d' will not be regenerated automatically to consider the new header that was included by 'foo.hpp'. The sed here is doing this task of put the makefile with suffix .d as a target of itself. Which means that the above example will be replaced by sed to the following:

main.o main.d : main.cpp foo.hpp

The sed command can be read as:

sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' < main.d

The regexp here is:

\(main\)\.o[ :]*

We capture the string "main" that is immediately followed by the sequence ".o : ".

The replacement here is: 

\1.o main.d :

We recover the captured string, which is the sequence "main", and the matched sequence is replaced by: 

main.d main.d : 

BTW, this solution to handle dependencies is provided by the utility make. Please, take a look at: https://www.gnu.org/software/mak...
 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

Thank you for that, I need to get C++ working with the 5.4.0 version that I am using first, I can then compare listings with a newer version (e.g., https://www.avrfreaks.net/forum/...). I don't trust this stuff, so I like to have a reference that I have done some testing on.

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

the_real_seebs wrote:

Certainly, it's true that C++17 offers a lot of fancy new features that might allow better code generation.

IMO, this isn't about C++17. The main point here is about how you use the language. We can achieve a result as good as what we have with C++17 using the C++11/14, or why not, C++98/03. The modern C++ "began" around 2001, actually, in that year at least we had a public publication about modern practices which are very important to achieve the goals mentioned in this post. 

 

the_real_seebs wrote:

How does that work out?

You can use a lot of the effort that the compiler can offer. Through the usage of meta functions and constexpr functions the component can take important decisions at compile-time. Some examples based on this work:

1. The att85::display component uses Policies(types in the end) to decide what it need to do at the initialization of the display without taking branches in run-time. We can see this at display.hpp:157 and detail/reset.hpp:92;

2. The processing of a string in compile-time permits an assembly without a run-time loop to walk in a charset table and we put in the storage(flash memory) only the glyphs(chars) that will be used by the program, instead of the whole table;

3. It's possible to edit a char image using the bitmap in the orientation of the screen, take a look at att85/ssd1306/font/16x32/chars.hpp:19, please. There is a constrexpr function toGDDRAM to translate in compile-time this human representation to the required model.

 

the_real_seebs wrote:

Do you have any benchmarks or comparisons for space used by roughly comparable programs and libraries?

Yes. I pushed one benchmark related to the library(tinusaur-ssd1306xled) that I used before write this.

https://github.com/ricardocosme/...

 

I'm also developing the component with benchmarks between att85 and adhoc versions that only uses free functions of the detail namespace. I will push those as soon as possible.

 

Thank you for your interest.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

We can achieve a result as good as what we have with C++17 using the C++11/14,

So why didn’t you?

in general, it would be more educational if you explicitly explained what feature “new in c++17” you are using, and why.

more convincing, too.  Right now it seems like you’re implying “older c++ may have been slow and bloated, but now we can do better as long as your willing to use a bleeding edge compiler version that isn’t supported by the manufacturer (and might be the last version ever for avr.)”

 

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

The benchmark would be the equivalent Arduino libraries. How does this compare?

 

For C++, Arduino would be the audience methinks.

 

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

rcosme wrote:

 

BTW, this solution to handle dependencies is provided by the utility make. Please, take a look at: https://www.gnu.org/software/mak...
 

 

What's the point?  You're not writing a project that has thousands of files and takes several minutes to build from clean.

Years ago I was using DMBS https://github.com/abcminiuser/dmbs, and after dealing with bugs like a t85 project linking a library that was built for a t84 project, I realized the best solution for small embedded projects is to rebuild everything.

I just add a .PHONY line for the target in my make template.

https://github.com/nerdralph/ner...

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

The GPIO abstraction templates make for an interesting read.

https://github.com/ricardocosme/...

 

I wonder if there's not a cleaner way to code it; writing a class for every GPIO is a bit tedious, particularly when you go beyond the 8-pin parts.

 

The bit-bang code you used is very inefficient.

https://github.com/ricardocosme/...

 

Several years ago I wrote a blog post about optimizing bit-banged SPI, which is also applicable to bit-banged I2C.

http://nerdralph.blogspot.com/20...

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

what feature “new in c++17” you are using, and why.

(BTW I revived this thread because of your NEW thread implementing a library that requires C++20.  The desire for "why" is only increased)

 

writing a class for every GPIO is a bit tedious, particularly when you go beyond the 8-pin parts.

Well, maybe.  Presumably you can get it done FOR you on a per-chip basis, assuming that you don't mind using standard names.

Arduino uses a set of parallel tables to describe each "Arduino Pin" (and the SAMD code uses an array of structs), which isn't so much different.

 

 

only the glyphs(chars) that will be used by the program, instead of the whole table

That's really neat. but it's such a dramatic change in fundamental operation that it pretty much obscures any code size comparisons.

 

 

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

And now I remember one of the reasons I don't like complex templates.

../../include/att85/ssd1306/detail/send_str.hpp:17:52: error: incomplete type 'att85::ssd1306::send_char<att85::ssd1306::display<128, 32, att85::pb2, att85::pb0, 120, 2, att85::ssd1306::ClearOnInit, att85::ssd1306::commands::ChargePump, att85::ssd1306::DefDisplayClock>, att85::ssd1306::font::_8x8, 'A'>' used in nested name specifier

While I personally understand the error means your send_char template doesn't support uppercase (or at least 'A'), I was never able to find a way to get the compiler to generate user-friendly error messages.  The other problem that has only gotten worse as new versions of C++ are used is that the subset of developers that can use and modify a sophisticated template library becomes smaller and smaller.  Other than auto variables, I've rarely used any of the new C++ features in the past 20 years.

 

And since gcc and Clang/llvm have improved their implementation of LTO, you can now get lots of sophisticated compile-time optimizations with plain old C99.

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

westfw wrote:

 

writing a class for every GPIO is a bit tedious, particularly when you go beyond the 8-pin parts.

Well, maybe.  Presumably you can get it done FOR you on a per-chip basis, assuming that you don't mind using standard names.

Arduino uses a set of parallel tables to describe each "Arduino Pin" (and the SAMD code uses an array of structs), which isn't so much different.

 

I'm not sure what you mean.  Are you suggesting some sort of code generator based on the Atmel XML part description files?

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

westfw wrote:

We can achieve a result as good as what we have with C++17 using the C++11/14,

So why didn’t you?

I didn't write this work at c++11/14 because c++17 was the last standard that I had available in hands in January of 2020. My projects to AVR are an opportunity to me to learn and apply the last features of C++.

 

westfw wrote:

in general, it would be more educational if you explicitly explained what feature “new in c++17” you are using, and why.

For sure. If we use c++14 as an alternative, I would say that there are two C++17 features used in this work: inline variables and compile-time if.

This is a header only project that can take advantage of inline variables. This feature allows something like  'constexpr inline Inverse inverse;' in a header file. (Take a look at 'att85/ssd1306/commands/commands.hpp'). Using a previous standard, in this case, we can constructs a new object through 'Inverse{}' instead of using the global object 'inverse'. At last but yet related to inline variables, we need to write some alternative to the initialization of some static data members.

We can use compile-time if to write a more cleaner code than solutions like function overloads using tag dispatching. For example, we have:

template<typename Sda, typename Scl>
constexpr inline void send_byte(uint8_t byte) {
  if constexpr(USI<Sda, Scl>()) {
    //do_something
  } else {
    //do_something
  }
}

That is more expressive than something like
 

template<typename Sda, typename Scl>
constexpr inline void send_byte(uint8_t byte, true_type) {
 //do_something_when_true
}

template<typename Sda, typename Scl>
constexpr inline void send_byte(uint8_t byte, false_type) {
 //do_something_when_false
}

template<typename Sda, typename Scl>
constexpr inline void send_byte(uint8_t byte) {
  send_byte<Sda, Scl>(byte, integral_constant<bool, USI<Sda, Scl>()>{});
}

 

westfw wrote:

more convincing, too.  Right now it seems like you’re implying “older c++ may have been slow and bloated, but now we can do better as long as your willing to use a bleeding edge compiler version that isn’t supported by the manufacturer (and might be the last version ever for avr.)”

Nope, I wrote this:

rcosme wrote:
IMO, this isn't about C++17. The main point here is about how you use the language.

IMHO, the usage of compile-time operations with a generic programming (GP) as one of the main paradigms to the solution, are good key points to a good solution using C++. New standards can make your life more easier.

 

Thank you for your interest in this work.

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

Last Edited: Tue. Oct 20, 2020 - 12:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

The benchmark would be the equivalent Arduino libraries. How does this compare?

I don't use arduino but I will try to find some time to benchmark with something popular in the arduino's world. Do you have some arduino-library to suggest?

 

Kartman wrote:

For C++, Arduino would be the audience methinks.

Yeah, I agree, I have this feeling too.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

Are you suggesting some sort of code generator based on the Atmel XML part description files?

That would work.  that is how the current .h files are generated.  There should surely be something that generates C++ something from them by now!  The current state of "we encourage the use of C++ but only provide C infrastucture" is a a little sad.
(You have the same (but backwards?) problem if you want to program and ARM in assembly language.  The .h files are full of C structures, casts, "UL" constants, and enums, and the ASM support is very weak.  ( https://github.com/WestfW/Minima... )

I confess that I had more in mind that someone would do it manually (perhaps with editor macro assistance, or other forms of lesser automatic), and publish the results.  PORTA1.set(); and so on.

 

 

For C++, Arduino would be the audience methinks.

Yes, except:

  1. various "More C++y" abstractions have been published in the Arduino community for pins and such, that have not caught on.  (eg https://github.com/greiman/Digit... )
  2. The Arduino team seems pretty apathetic about performance improvements.
  3. Ditto for "More C++iness."
  4. They're back at C++11

 

The most popular OLED library is probably https://github.com/adafruit/Adaf...

Bill Greiman has one as well:  https://github.com/greiman/SSD13...

(I don't know offhand whether either works on a tiny85.)

 

 

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

(alas, I think most of us are probably stuck on understanding the basics of C++, and chasing the latest feature in the latest standard is ... terrifying.  Especially since the C++ standards people don't seem to be particularly supportive of embedded programming. (such as their non-support for named memory spaces.))

 

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

westfw wrote:
named memory spaces

 

That sounds interesting, but I have no idea what it would be. What I would like is to have a tool (e.g., in VSCode) that would hint at what things in C++ will use the heap. There is no chance I will recall without the paperclip reminding me. The other day I ran across a little blog on Rust where it talked a little about a mechanism for programs to opt-out of higher libraries:

 

Quote:
By marking themselves as no_std, programs confine themselves to the functionality found in libcore. This functionality, in turn, makes no system assumptions — and in particular, performs no heap allocations.

 

http://dtrace.org/blogs/bmc/2020/10/11/rust-after-the-honeymoon/

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

>I would say that there are two C++17 features used in this work: inline variables and compile-time if

 

inline variables are quite nice and is worth the upgrade, the 'if constexpr' seems to get less use because the compiler already eliminates the dead code from a 'normal' constexpr decision. If C++20 has features that are useful, AND the compiler was commonly available, I wouldn't hesitate to use it. For an Arm mcu I would stick to whatever is available from Arm (gcc), for avr I would use whatever Arduino uses (I have 7.3, not sure if they have anything more current).

 

Not a normal use for inline vars, but you can get yourself a reference to a register struct in the following examples without having to init the reference outside the class (and create another template just to init the reference var). Keeping vars completely inside a class template for example means you can init them where they are created without having to otherwise create a template outside the class for each var you created.

avr 328-

https://godbolt.org/z/hvrncK

stm32g0-

https://godbolt.org/z/PW1MPY

 

I also use the same style for nrF52, and avr0/1.

 

For the avr0/1, the Port 'class' looks basically the same as these (looks more like the stm32 version). I have a script that generates the avr headers from the atdf file, but you still have to tweak the results as you still end up with odd problems and have to manually tweak the files where needed. The avr0/1 tiny/mega are similar enough that you can make everything in one code base, but there are differences that force you into doing things that make the code start to creep into the #ifdef world, or try to use other methods available in the language to do the same, but starts to look a little cluttered when doing so (at least in the files where differences start to show up).

 

What I do now, is separate the mega and tiny. The avr0 mega's are all the same, and just differ by pin count, sizes, peripherals available. So I start with one 'master header' from the mega4809 (script generated, problems corrected), then split out the pin count specific things (pins, peripherals, interrupt vectors, etc.) into 'pin count' named headers, and the sizes (not pin count specific) into mcu named headers. So I end up with pin count headers, and size headers, and the main cpu define will get me the right headers to use, with me just adding which package to use for the mcu (28 or 32 pin, 40 or 48 pin). In the end, I get a mcu specific combo to use and am prevented from using pins/peripherals that are not available.

 

For the mega- from the two 'master headers', copy, paste, modify (sizes) or trim out the missing from the lower pin count versions, and you end with mcu headers for each mcu that just has the sizes (quite small header), and the four pin count headers with all the other info (28,32,40,48 pin headers in this case). Since there are not that many mega's, this method is just as easy as trying to get a script to generate perfect headers for each mcu. It only needs to be done once, so manually editing some files is a one time job and never have to touch them again.

 

 

> opt-out of higher libraries

 

You have no higher C++ libraries for an avr, so have nothing to fear from C++. Even when using something like an Arm mcu where you have the C++ Standard Library available, there is no law stating you have to use it. I usually don't. At some point your resources may allow you to start using the library if there is something in there that can be useful to you, but its not a requirement. You still have all the basic building blocks a library writer has, and the tools you have available in C++ are quite nice.

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

curtvm wrote:
You have no higher C++ libraries for an avr, so have nothing to fear from C++.

 

So my memory corruption problems were caused by the libraries I was using but could not decipher, fair enough, but also a reason to stay with C. I am attached to avr-libc, and have been looking at picolibc for when I try ARM or others, it is saying the tinystdio functions of picolibc no longer call malloc.

 

https://github.com/picolibc/picolibc

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

I assumed you were talking about something like the C++ Standard Library, but I guess you meant libraries in general.

 

 

>but also a reason to stay with C

 

You are in the same boat in any case- if some library decides to use malloc, not much you can do about it except to not use the chosen library, or change it. Same in C++.

 

You do have control, though. If you do not want to use the heap at all, get rid of the heap related symbols so any use of things like malloc will fail at compile time.

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

I move this to https://www.avrfreaks.net/commen...
 

Sorry about the mess. I was trying to move the posts that I had replied with *Quote* to a new ones using *Reply*.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

Last Edited: Tue. Oct 20, 2020 - 10:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ralphd wrote:

What's the point?  You're not writing a project that has thousands of files and takes several minutes to build from clean.

At first, I agree with you. I don't have extensive experience in embedded projects to conclude anything about this. What I would like to point out is that modern C++ is not like C or C-with-classes, there may be a huge load under the compiler to handle compile-time operations. We can take minutes to unroll expression templates in generic components that are implemented in C++98/03, for example. One example of this is Boost.Spirit v2, which is by the way a terrific project. We can took 3minutes or more with a single TU(translation unit). I'm using an example from a desktop perspective in terms of usability, but, Is it clear what we can do or not when programming to AVR? I don't think so. We can use heavy generic and metaprogramming approaches to build solutions to AVR with C++.

ralphd wrote:

Years ago I was using DMBS https://github.com/abcminiuser/dmbs, and after dealing with bugs like a t85 project linking a library that was built for a t84 project,

Right, I know well this type of problem. This can be in part due to the build system, btw.

ralphd wrote:

I realized the best solution for small embedded projects is to rebuild everything.

At first, it's more than accceptable to me.

ralphd wrote:

I just add a .PHONY line for the target in my make template.
https://github.com/nerdralph/ner...

Yep, I also use .PHONY to declare a *flash* rule to upload my program.

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

How did you write the font code?  Did you write a code generator?

https://github.com/ricardocosme/...

 

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

Kartman wrote:

 

The benchmark would be the equivalent Arduino libraries. How does this compare?

 

I took some measures using Arduino 1.8.13 with std=c++17 and the Tiny4kOLED library that is offered through the Library Manager:

Benchmark: https://github.com/ricardocosme/...

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

westfw wrote:

 

  1. The Arduino team seems pretty apathetic about performance improvements

 

I began in the embedded world "yesterday", but... Well, I can't understand this. We're talking about tiny devices! Performance doesn't matter!? Really?

 

westfw wrote:

 

The most popular OLED library is probably https://github.com/adafruit/Adaf...

Bill Greiman has one as well:  https://github.com/greiman/SSD13...

(I don't know offhand whether either works on a tiny85.)

 

I took a look at but it seems to me that they don't have support to Attiny85 or Attiny13(which are the mcus that I'm supporting right now).

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

rcosme wrote:

ralphd wrote:

What's the point?  You're not writing a project that has thousands of files and takes several minutes to build from clean.

At first, I agree with you. I don't have extensive experience in embedded projects to conclude anything about this. What I would like to point out is that modern C++ is not like C or C-with-classes, there may be a huge load under the compiler to handle compile-time operations. We can take minutes to unroll expression templates in generic components that are implemented in C++98/03, for example. One example of this is Boost.Spirit v2, which is by the way a terrific project. We can took 3minutes or more with a single TU(translation unit). I'm using an example from a desktop perspective in terms of usability, but, Is it clear what we can do or not when programming to AVR? I don't think so. We can use heavy generic and metaprogramming approaches to build solutions to AVR with C++.

 

Using a google cloud shell, in the demo/ssd1306 directory, I ran make -Bj:

real    0m0.994s
user    0m3.047s
sys     0m0.574s

 

There's not much point generating the .lss and .s files every time you rebuild.  Excluding them cuts the time almost in half:

real    0m0.552s
user    0m1.580s
sys     0m0.358s

 

If I only generate the .elf files, the time is under 500ms:

real    0m0.463s
user    0m1.436s
sys     0m0.271s

 

Looking at your Makefile, I have a few suggestions:

1) use -C to when generating a disassembly from compiled C++.

2) you can skip the hex files and flash elf directly with -U flash:w:$<:e

3) use MCU_TARGET?=attiny85 so it can easily be changed from the commandline

 

real    0m0.994s
user    0m3.047s
sys     0m0.574s

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

curtvm wrote:
You are in the same boat in any case- if some library decides to use malloc, not much you can do about it except to not use the chosen library, or change it. Same in C++.

 

Maybe C libraries are done for a different reason than C++. I want libraries that can do the job, and I can maintain (also a license that does not put me on the hook for things I can't efficiently deliver with the embedded hardware). I am not trying to market my programming skills; in my world, the simpler, the better.

 

http://svn.savannah.gnu.org/viewvc/avr-libc/trunk/avr-libc/libc/stdlib/malloc.c?revision=2149

 

curtvm wrote:
Heap related symbols

 

? I am not sure what those might be. It would be interesting if there were a switch that would cause the compile to fail if the heap was used.

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

Different mcu's, different variations on how the heap is setup, but for a mega328 you can comment out the line in the linker script avr5.xn-

/* PROVIDE (__heap_start = .) ; */

 

With no __heap_start symbol, you cannot link your project without error if malloc is brought in.

 

 

 

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

Hmm.   So is there now a way to prevent gratuitous inclusion of virtual functions in the newer C++s?

For example, Arduino has:
 

class Print
{
        :
    virtual size_t write(uint8_t) = 0;
    virtual size_t write(const uint8_t *buffer, size_t size);
    virtual int availableForWrite() { return 0; }
    virtual void flush() { /* Empty implementation for backward compatibility */ }
        :
};

class Stream : public Print
{
    :
  public:
    virtual int available() = 0;
    virtual int read() = 0;
    virtual int peek() = 0;
    :
};

class HardwareSerial : public Stream
{
  public:
    virtual void begin(unsigned long);
    virtual void begin(unsigned long baudrate, uint16_t config);
    virtual void end();
    virtual int available(void) = 0;
    virtual int peek(void) = 0;
    virtual int read(void) = 0;
    virtual void flush(void) = 0;
    virtual size_t write(uint8_t) = 0;
    using Print::write; // pull in write(str) and write(buf, size) from Print
    virtual operator bool() = 0;
};

 

Which results in any sketch that includes HardwareSerial capability ends up NOT garbage-collecting the available, peek, availableForWrite, and etc functions whether or not they are actually used...

I've always understood that this is just "a limitation of the way that virtual functions work", but maybe things have changed?

 

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

I wonder if there's not a cleaner way to code it; writing a class for every GPIO is a bit tedious, particularly when you go beyond the 8-pin parts.

I haven't thought about it. When I wrote this work I only had in hands one ATtiny85 and there wasn't any motivation to handle other devices or architectures.

The bit-bang code you used is very inefficient.

I see. Thank you for pointing this out to me. I've read your blog post.

Improvement: https://github.com/ricardocosme/...

demo/alarm.cpp has 320bytes using USI and it had 324bytes using the old bit-banging. Now, this demo has 312bytes using the bit-banging.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

(BTW I revived this thread because of your NEW thread implementing a library that requires C++20.  The desire for "why" is only increased)

Right. I want to try this work with C++20 with hopes of removing the macro ATT85_SSD1306_STR to use compile-time strings. I always forget the name of this macro when writing a project. I would like to write something like this: disp.out<font::_8x8, "string">(0, 0);

That's really neat. but it's such a dramatic change in fundamental operation that it pretty much obscures any code size comparisons.

Yes. I have a support to runtime strings too, although in this case it's expected a charset to avoid wasting memory.

Maybe I will look to this point with more caution to do benchmarks, but, in the end, what is important is to reach the best outcome.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

I was never able to find a way to get the compiler to generate user-friendly error messages. 

C++20 has a language support to write and use Concepts. Instead of a huge stack of error messages that are generated by the compiler because it tries to reach the longest possible path, we can make the compiler stop at the point that makes sense to us. It's possible writing or using a Concept or at least a set of loose constraints(requirements) under the template parameter.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.