Forum: Mikrocontroller und Digitale Elektronik [AVR] Unerwarteter Compilerfehler mit constexpr


von Einer K. (Gast)


Angehängte Dateien:

Lesenswert?

Gefunden wurde der Fehler von einem hiesigen Forenmitglied.
Und das in/mit meinem Code...
(Eine Blamage?)
Leider kann ich nicht sehen, was ich dort falsch mache.

Ich möchte euch bitten, das zu testen, und mir zu sagen, was ich falsch 
mache, oder ob das ein Compiler Bug ist.


Minimalisierter Testcode im Anhang.

Testbedingung:

Compiler Gnu avr-c++ unter win10
Getestete Versionen: 5.4 7.3 8.2 9.1
Einstellung gnu++11 oder höher
Sowohl mit Arduino IDE, als auch mit Atmel Studio.

Die Kompilation läuft mit allen Compilerversionen für den ATMega328P und 
ATMega32U4 sauber durch, und funktioniert auch alles.

Die Compiler Varianten 8.2 und 9.1 versagen an dem Array für den 
ATMega2560 mit folgender Meldung:
1
Fehler:10:82: error: reinterpret_cast from integer to pointer
2
   10 |  /**/ constexpr Register portList[] = {&PORTE,&PORTE,&PORTE,&PORTE,&PORTG,&PORTE,&PORTH,&PORTH,&PORTH,&PORTH,&PORTB,&PORTB,&PORTB,&PORTB,&PORTJ,&PORTJ,&PORTH,&PORTH,&PORTD,&PORTD,&PORTD,&PORTD,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTA,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTD,&PORTG,&PORTG,&PORTG,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTL,&PORTB,&PORTB,&PORTB,&PORTB,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTF,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,&PORTK,};
3
      |                                                                                  ^
4
exit status 1
5
reinterpret_cast from integer to pointer

Das entfernen des Wortes "constexpr" würde das Problem ausmerzen, ist 
dann aber mit ungewolltem Speicherverbrauch zur Laufzeit verbunden

Wer hat den Lattenschuss?
Ich oder der Compiler?
Was habe ich übersehen?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Die Compiler Varianten 8.2 und 9.1 versagen

... versagen nicht, sondern implementieren den Standard lediglich 
rigoroser. Die alten Versionen waren nachlässiger.

Die "PORTxx" sind Makros welche einen C-Style-Cast der Art "(volatile 
uint8_t*) 0x1234" enthalten. Dieser wird vom C++-Compiler als 
"reinterpret_cast<volatile uint8_t*> (0x1234)" verstanden (named cast), 
daher die Ausgabe. Ein solches reinterpret_cast ist aber in 
constexpr-Kontexten verboten.

Ein Workaround ist es, die Pointer-Adressen als uintptr_t ins Array zu 
speichern (ohne cast), und dann beim Zugriff erst das reinterpret_cast 
zu machen.

Arduino Fanboy D. schrieb:
> Ich oder der Compiler?

Der Code ist laut Standard fehlerhaft :-)

: Bearbeitet durch User
von Vincent H. (vinci)


Lesenswert?

In C++20 wird es dann möglich sein deine "portList" mit Hilfe von 
std::bit_cast (https://en.cppreference.com/w/cpp/numeric/bit_cast) zu 
erzeugen.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Vincent H. schrieb:
> In C++20 wird es dann möglich sein deine "portList" mit Hilfe von
> std::bit_cast (https://en.cppreference.com/w/cpp/numeric/bit_cast) zu
> erzeugen.

Da steht aber:

This function template is constexpr if and only if each of To, From and 
the types of all subobjects of To and From:
- is not a pointer type;

von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> Ein Workaround ist es, die Pointer-Adressen als uintptr_t ins Array zu
> speichern (ohne cast), und dann beim Zugriff erst das reinterpret_cast
> zu machen.
Werde ich untersuchen.

---------------------

Niklas G. schrieb:
> ... versagen nicht, sondern implementieren den Standard lediglich
> rigoroser. Die alten Versionen waren nachlässiger.

Wenn das der Punkt wäre, dann müssten 8.2 und 9.1 auch bei den 328P und 
32U4 Arrays erbost aufschreien. Denn es ist ja exakt das gleiche 
Prinzip.
*Tun sie aber nicht!*
Das geht ohne Murren durch und funktioniert auch.


--------------------------

Niklas G. schrieb:
> Ein solches reinterpret_cast ist aber in
> constexpr-Kontexten verboten.

Da die Arrays in constexpr Funktionen ausgewertet werden, wäre somit 
auch dort der reinterpret_cast, deiner Ansage nach, ebenfalls verboten.
Das wäre dann der Todesstoß für das ganze Vorhaben.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Das geht ohne Murren durch und funktioniert auch.

Dann funktioniert das bei denen irgendwie ohne Cast. Such doch mal die 
Definition von PORTxx bei den unterschiedlichen Controllern raus...

Arduino Fanboy D. schrieb:
> Da die Arrays in constexpr Funktionen ausgewertet werden, wäre somit
> auch dort der reinterpret_cast, deiner Ansage nach, ebenfalls verboten.

Richtig.

Arduino Fanboy D. schrieb:
> Das wäre dann der Todesstoß für das ganze Vorhaben.

Es sei denn du speicherst im Array die Adresse als Zahl statt des 
Pointers, und castest erst ganz zum Schluss, wenn tatsächlich etwas auf 
den Port geschrieben wird. Oder du speicherst im Array nur Port-Nummern 
(A->0, B->1, C->2), welche dann auch schön in 8 bit passen, und mappst 
das erst zum Schluss durch eine Look-Up-Table auf die 
Peripherie-Register-Adressen, welche dann erst auf den Pointer-Typ 
gecastet werden. Beim AVR sind die Register ja leider nicht regelmäßig 
angeordnet und aus der Nummer berechenbar...

von Vincent H. (vinci)


Lesenswert?

Niklas G. schrieb:
> Vincent H. schrieb:
>> In C++20 wird es dann möglich sein deine "portList" mit Hilfe von
>> std::bit_cast (https://en.cppreference.com/w/cpp/numeric/bit_cast) zu
>> erzeugen.
>
> Da steht aber:
>
> This function template is constexpr if and only if each of To, From and
> the types of all subobjects of To and From:
> - is not a pointer type;

F.uck -.-

Das mit den Pointern les ich zum ersten mal. Wahhh. Eigentlich wär 
bit_cast in meinen Augen DAS C++20 Feature für Embedded gwesen... Damit 
hätte man endlich die ganzen SVD generierten Header sinnvoll nutzen 
könne können. :(

von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> Dann funktioniert das bei denen irgendwie ohne Cast. Such doch mal die
> Definition von PORTxx bei den unterschiedlichen Controllern raus...

Das habe ich schon hinter mir....
Das sieht identisch aus.
(ja, die Zahlen/Adressen unterscheiden sich, aber sonst nix)

-> Ja, das untersuche ich nochmal!


Auch der Code nach dem Preprozessor sieht gut aus.
Nicht schön, aber bei allen versuchten Compiler und io*.h Versionen 
identisch
Der Preprozessor ist also auch unschuldig.

-> Auch das untersuche ich nochmal!



Niklas G. schrieb:
> Es sei denn du speicherst im Array die Adresse als Zahl statt des
> Pointers, und castest erst ganz zum Schluss, wenn tatsächlich etwas auf
> den Port geschrieben wird. Oder du speicherst im Array nur Port-Nummern
> (A->0, B->1, C->2), welche dann auch schön in 8 bit passen, und mappst
> das erst zum Schluss durch eine Look-Up-Table auf die
> Peripherie-Register-Adressen, welche dann erst auf den Pointer-Typ
> gecastet werden. Beim AVR sind die Register ja leider nicht regelmäßig
> angeordnet und aus der Nummer berechenbar...

Ja, vielleicht muss so ein Workaround her um das Problem zu umgehen.
Nur bleibe dann die Frage offen, ob das ein Compiler Bug ist...
(welcher dann irgendwann evtl. behoben wird)
Um sowas möchte ich nicht gerne drumrum arbeiten.
Es wäre dann schöner, wenn die Ursache behoben wird.

von Niklas Gürtler (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Nur bleibe dann die Frage offen, ob das ein Compiler Bug ist...

Es ist wenn schon ein Compiler/Library Bug wenn es kompiliert. Der 
Standard deklariert solchen Code als falsch. Vielleicht "funktioniert" 
es auf den kleinen Cobtrollern nur aufgrund irgendwelcher AVR 
Besonderheiten im Compiler - korrekt ist es nicht. Bei Cortex-M geht es 
jedenfalls mit aktuellem GCC auch nicht, mit alten Versionen schon 
(fälschlicherweise).

von Einer K. (Gast)


Lesenswert?

Einen Unterschied kann ich jetzt erkennen!

Hier ein Auszug aus der iomxx0_1.h, welche für den Mega2560 eingebunden 
wird.
#define PORTB   _SFR_IO8(0x05)
#define PORTH  _SFR_MEM8(0x102)

Einträge mit _SFR_IO8() gehen durch.
Einträge mit _SFR_MEM8() werden angemäckert.


...
In dem Punkt sind alle Versionen der iomxx0_1.h gleich, jeder Compiler 
bringt ja seine eigene mit.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Einträge mit _SFR_IO8() gehen durch.
> Einträge mit _SFR_MEM8() werden angemäckert.

Ah, wahrscheinlich werden "MEM" Adressen als "normale" Pointer über den 
16bit-Adressraum verwendet (LD, ST) und die "IO" Adressen über den 
IO-Adressraum (IN, OUT), und bei letzterem hat der GCC sowieso eine 
Sonderbehandlung.

von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> und bei letzterem hat der GCC sowieso eine
> Sonderbehandlung.
Das ist wahr.

Aber die nötige Information wird nicht aus den Macros bereit gestellt.
Zumindest nicht anhand des Datentypes, der ist bei beiden Varianten 
gleich, oder?


Auszug aus der sfr_defs.h
1
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
2
3
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)
4
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
Der einzige für mich sichtbare Unterschied ist die Addition zweier 
Literale

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Aber die nötige Information wird nicht aus den Macros bereit gestellt.
> Zumindest nicht anhand des Datentypes,

Kann sein, kenne mich mit AVR nicht so gut aus. Wie gesagt ist der Code 
so nicht korrekt, du kommst um eine Anpassung früher oder später nicht 
herum...

von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> du kommst um eine Anpassung früher oder später nicht
> herum...
Sei es drum....
Selbst die Anpassung will nicht gelingen!

Also lautet meine Frage jetzt "Wie tun?"


---------
Der vorherigen Idee gefolgt:
1
 /*constexpr*/ uintptr_t portList[] = {(uintptr_t)&PORTH};
Geht ohne Meldung durch!

--------
1
 constexpr uintptr_t portList[] = {(uintptr_t)&PORTH};
Versagt mit der bekannten Meldung.
> error: reinterpret_cast from integer to pointer
Auch so bekomme ich die Adressen/Pointer nicht ins Array.


----

Es ist auch nicht so, dass der explizite Cast selber ein Problem 
darstellt, hier ein extrem:
1
constexpr   int portList[] = {(int)&PORTB}; // geht durch
2
constexpr   int portList[] = {(int)&PORTH}; // Error
Auch wieder:
> error: reinterpret_cast from integer to pointer

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Auch so bekomme ich die Adressen/Pointer nicht ins Array.

Korrekt, du musst du Adressen leider von Hand eintippen, wenn in den 
AVR-Headern nicht irgendwo Makros mit den Adressen stehen. Zwei 
reinterpret_cast (bzw. (uintptr_t) - was das Gleiche ist) hintereinander 
sind auch nicht besser als eins.

So ala:
1
struct Register { std::uint16_t address; };
2
3
static constexpr Register R_PORTA { 0x0002 + __SFR_OFFSET };
4
static constexpr Register R_PORTB { 0x0005 + __SFR_OFFSET };
5
static constexpr Register R_PORTH { 0x0102 };
6
7
constexpr Register portList[] = { R_PORTA, R_PORTB };
8
9
void writePort (Register r, uint8_t value) {
10
  *reinterpret_cast<volatile uint8_t*> (r.address) = value;
11
}

Arduino Fanboy D. schrieb:
> constexpr   int portList[] = {(int)&PORTB}; // geht durch

Wie gesagt eigentlich auch falsch, die GCC-Sonderbehandlung von 
IO-Adressen kleiner 0x1F ist wohl fälschlicherweise nachlässig.

Da anscheinend zumindest beim ATmega64/128/256 die PIN-DDR-PORT Register 
doch einem gewissen Muster folgen, könnte man es vielleicht noch so 
schöner machen:
1
struct Register {
2
  std::uint16_t address;
3
  std::uint8_t read () const {
4
    return *reinterpret_cast<volatile std::uint16_t*> (address);
5
  }
6
  void write (std::uint8_t value) const {
7
    *reinterpret_cast<volatile std::uint16_t*> (address) = value;
8
  }
9
  bool readBit (std::uint8_t bit) const {
10
    return (read () >> bit) & 1;
11
  }
12
  void setBit (std::uint8_t bit, bool value) const {
13
    std::uint8_t x = read ();
14
    if (value) x |= (1 << bit); else x &= ~(1 << bit);
15
    write (x);
16
  }
17
};
18
19
struct Port {
20
  std::uint8_t iPort;
21
  constexpr std::uint16_t addrOffset () const {
22
    return (iPort >= 7) ? ((iPort-7)*3 + 0x100) : (iPort * 3);
23
  }
24
  constexpr Register pin () const { return { addrOffset () }; }
25
  constexpr Register ddr () const { return { addrOffset () + 1 }; }
26
  constexpr Register port () const { return { addrOffset () + 2 }; }
27
};
28
29
struct Pin {
30
  Port port;
31
  std::uint8_t iPin;
32
  void writeOut (bool value) const {
33
    port.port ().writeBit (iPin, value);
34
  }
35
  bool readIn () const {
36
    return port.readBit (iPin);
37
  }
38
  void dirOut () const {
39
    dir (true);
40
  }
41
  void dirIn () const {
42
    dir (false);
43
  }
44
  void dir (bool d) const {
45
    port.ddr ().writeBit (iPin, value);
46
  }
47
};
48
49
static constexpr PortA { 0 };
50
static constexpr PortB { 1 };
51
static constexpr PortC { 2 };
52
static constexpr PortD { 3 };
53
static constexpr PortE { 4 };
54
static constexpr PortF { 5 };
55
static constexpr PortG { 6 };
56
static constexpr PortH { 7 };
57
static constexpr PortI { 8 };
58
static constexpr PortJ { 9 };
59
static constexpr PortK { 10 };
60
static constexpr PortL { 11 };
61
62
constexpr Pin pinList [] = { { PortA, 3 }, { PortB, 2 }, { PortH, 7 } };
63
64
int main () {
65
  for (auto & p : pinList) {
66
    p.writeOut ();
67
    p.setOut (true);
68
  }
69
}

: Bearbeitet durch User
von Einer K. (Gast)


Angehängte Dateien:

Lesenswert?

Niklas G. schrieb:
> Korrekt, du musst du Adressen leider von Hand eintippen, wenn in den
> AVR-Headern nicht irgendwo Makros mit den Adressen stehen.
Das wäre sehr unschön!
Denn es zieht weitreichende Umbauten nach sich.
Es sind ja nicht nur die PORTX betroffen, sondern auch DDRX und PINX
Und das dann für eine ganze Latte an AVRs

Es wäre mir deutlich lieber, die schon in den Headern vorhandenen 
Adressen nutzen zu können.

--

Aber einen Vorteil hat es ja, es läuft mit allen Compiler Versionen, 
welche ich hier zum testen habe.


Im Anhang eine Testversion. Ist noch ein Provisorium
Die Template Funktion findet sich später in einer Klasse wieder

----------------

Niklas G. schrieb:
> Da anscheinend zumindest beim ATmega64/128/256 die PIN-DDR-PORT Register
> doch einem gewissen Muster folgen, könnte man es vielleicht noch so
> schöner machen:
Werde ich untersuchen!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Vielleicht so?
1
#include <avr/io.h>
2
3
struct P
4
{
5
    volatile uint8_t &p;
6
    uint8_t operator = (uint8_t val) const { return p = val; }
7
    operator uint8_t () const { return p; }
8
};
9
10
constexpr P ports[] = { PORTB, PORTD };
11
12
uint8_t func (bool z)
13
{
14
    ports[0] = ports[1] = 0;
15
    return ports[z];
16
}

Zugreifen kann man dann wie bei einem Array, allerdings hat man 
Referenzen und keine Zeiger.  Kommt drauf an, was du mit dem Array dann 
machen willst.

Nachteil bei solchen constexpr ist aber immer, dass sie Speicher 
(.rodata) schlucken, wenn nicht alles zur Compilezeit aufgelöst werden 
kann wie im Beispiel.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

...blöderweise wird ports[] auch in folgendem Beispiel angelegt, obwohl 
es wegoptimiert werden könnte:
1
void use_P (P);
2
3
void call ()
4
{
5
    use_P (ports[0]);
6
}
1
_Z6call_Pv:
2
  lds r24,_ZL5ports
3
  lds r25,_ZL5ports+1
4
  jmp _Z5use_P1P
5
6
  .section  .rodata
7
_ZL5ports:
8
  .word  56
9
  .word  50

: Bearbeitet durch User
von Einer K. (Gast)


Angehängte Dateien:

Lesenswert?

Johann L. schrieb:
> Nachteil bei solchen constexpr ist aber immer, dass sie Speicher
> (.rodata) schlucken, wenn nicht alles zur Compilezeit aufgelöst werden
> kann wie im Beispiel.

Ja, sowas ist fatal.



Im Grunde möchte ich folgendes realisieren....
1. kein Ram verbrauch, für diesen Kram
2. keine Arrays im Flash
3. Arduino Pin Nummerierung verwenden
4. ein schlichtes/naives OOP artiges Interface
5. möglichst hohe Geschwindigkeit, zur Laufzeit.

Zur Compilezeit darf der Compiler ruhig derbe ackern.


In den Anhang packe ich mal eine funktionierende Version.
Eine Arduino typische Library.
Ist noch nicht die Ausgeburt aller Schönheit, ich weiß....
Halt noch eine Baustelle.

von Wilhelm M. (wimalopaan)


Lesenswert?

Wahrscheinlich widerspricht es der Arduino-Philosophie, aber der Trick 
besteht darin, statt Werten (Pointer) Typen zu benutzen (wie 
std::integral_constant). Bei Typen ist man sich immer sicher, dass sie 
im Maschinencode ja nicht mehr existieren.

Also statt einer Liste von Zeigerwerten der Ports/Pins, eine Liste von 
Typen, die das repräsentieren. Die Auflösung zu einer Adresse wird dann 
genau dort gemacht, wo man sie braucht, also ggf. in einer 
Elementfunktion. Dann wird alles schön wegoptimiert. (Irgendwo hier im 
Forum stecken auch meine Beispiele dazu, ca. aus dem Jahr 2017 oder 
davor).

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Vielleicht so?

Sicher dass das funktioniert? Da sind doch wieder die PORTx-Makros inkl. 
Cast in einem contexpr-Kontext.

Johann L. schrieb:
> obwohl
> es wegoptimiert werden könnte:

Kann es ja nicht, weil man durch den nicht-inline Funktionsaufruf 
erzwingt, dass die Instanz angelegt wird...

Arduino Fanboy D. schrieb:
> 2. keine Arrays im Flash
> 5. möglichst hohe Geschwindigkeit, zur Laufzeit.

Das geht halt nur, wenn dem Compiler an jeder Stelle der Pin bekannt 
ist. So etwas wie mein Beispiel (Iteration)

Niklas G. schrieb:
> for (auto & p : pinList) {
>     p.writeOut ();
>     p.setOut (true);
>   }

passt damit nicht zusammen, weil hier die Pin-Nummern erst zur Laufzeit 
bekannt und variabel sind.

von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> Das geht halt nur, wenn dem Compiler an jeder Stelle der Pin bekannt
> ist.
Ja, so ist es jetzt bei mir realisiert. (siehe Zip Anhang)
(war es vorher auch schon, da haben mir nur die neueren Compiler in den 
Brei gespuckt)

Wilhelm M. schrieb:
> Wahrscheinlich widerspricht es der Arduino-Philosophie, aber der Trick
> besteht darin, statt Werten (Pointer) Typen zu benutzen (wie
> std::integral_constant). Bei Typen ist man sich immer sicher, dass sie
> im Maschinencode ja nicht mehr existieren.

Die "Arduino-Philosophie" kann mich mal....
Einzig die Pin Nummerierung muss ich übernehmen, weil sie auf den Boards 
aufgedruckt ist, und jeder Arduino Jünger sie so erwartet.
Da steckt der Kompromiss, welchen ich eingehen muss.
Daher auch der Zwang zu solchen Arrays.


> (wie std::integral_constant)
Leider keine STD Lib
Leider auch keine STL
Nur die C-Lib

Wilhelm M. schrieb:
> (Irgendwo hier im
> Forum stecken auch meine Beispiele dazu, ca. aus dem Jahr 2017 oder
> davor).

Ich meine mich schwach zu erinnern....
Was das nicht ein riesen Template Berg?
Finde es aber gerade nicht wieder.....

Wenn mich meine Erinnerung nicht trügt war die Benutzung in etwa so:
setPin(led); // um eine LED einzuschalten.

Das gefällt mir nicht so...
Ich hätte es gerne so:
led.setHigh();
oder
led = 1;
oder
led.set(HIGH)
oder
led(1)

Das ermöglicht eine recht simple Notation in der Anwendung.

setPin(led); Hier erscheint die led passiv, mit ihr wird etwas 
angestellt
led.set(HIGH) Hier erhält die LED einen Auftrag: Mache dich an
Ok, das ist nur ein eher philosophischer Unterschied, und sollte sich 
nicht im generierten Code nieder schlagen.


Ein Beispiel:
(8 und 3 sind die Arduino Pin Nummern)
1
// Retriggerbares Monoflop / Flankenverzögerer
2
#include <CombiePin.h>
3
#include <CombieTimer.h>
4
5
Combie::Pin::TasterGND<8> taster; // Taster schaltet gegen GND
6
Combie::Pin::OutputPin<3> led;
7
Combie::Timer::FallingEdgeTimer fallingEdge(200);// ms Nachleuchtdauer
8
9
void setup()
10
{
11
  taster.initPullup();
12
  led.init();
13
}
14
15
void loop()
16
{
17
18
  // Datenfluss Schreibweise
19
  led = fallingEdge = taster;
20
  
21
/*
22
  // Functor Schreibweise
23
 led(fallingEdge(taster()));
24
25
  
26
  // Traditionelle Methodenaufruf Schreibweise
27
  led.set(fallingEdge.doTrigger(taster.pressed()));
28
*/
29
}



Wie du siehst, sind mir die Arduino Traditionen nicht ganz so 
wichtig....

Wilhelm M. schrieb:
> Bei Typen ist man sich immer sicher, dass sie
> im Maschinencode ja nicht mehr existieren.
Richtig!
Ist bei mir so.
Irgendwann müssen die Pins ja gesetzt werden, das findet sich dann im 
Code.
z.B. led.toggle() reduziert sich auf 1 ASM Statement.
(wenn der Pin im IO Bereich liegt)

Wilhelm M. schrieb:
> Also statt einer Liste von Zeigerwerten der Ports/Pins, eine Liste von
> Typen, die das repräsentieren.
?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
>> (wie std::integral_constant)
> Leider keine STD Lib
> Leider auch keine STL
> Nur die C-Lib

Ist zum Glück trivial:
1
template<class T, T v>
2
struct integral_constant {
3
    static constexpr T value = v;
4
    typedef T value_type;
5
    typedef integral_constant type; // using injected-class-name
6
    constexpr operator value_type() const noexcept { return value; }
7
    constexpr value_type operator()() const noexcept { return value; } 
8
};
https://en.cppreference.com/w/cpp/types/integral_constant

Arduino Fanboy D. schrieb:
> Ein Beispiel:
> (8 und 3 sind die Arduino Pin Nummern)

Das ginge so. Es haben dann halt alle Pins einen anderen Typ und sind 
nicht (ohne template) an Funktionen zu übergeben oder iterierbar.

von Wilhelm M. (wimalopaan)


Lesenswert?

Niklas G. schrieb:

> Das ginge so. Es haben dann halt alle Pins einen anderen Typ und sind
> nicht (ohne template) an Funktionen zu übergeben oder iterierbar.

Statisch iterierbar ist trivial:
1
using led1 = Combie::Pin::OutputPin<3>;
2
using led2 = Combie::Pin::OutputPin<4>;
3
4
template<typename T> struct List {};
5
6
using listOfLeds = List<led1, led2>;
7
8
template<typename... Leds>
9
void foo(List<Leds...>) {
10
   (Leds::on(), ...);
11
}
12
13
void bla() {
14
   []<typename Leds>(List<Leds...>) {
15
          (Leds::toggle(), ...);
16
   }(List<led1, led2>{]);
17
18
}
19
20
void test()  {
21
    foo(listOfLeds{});
22
    bla();
23
}

Viele Varianten von dem obigen sind denkbar.

Dynamisch geht aber auch (Sichwort Visitor).

Es macht keinen Sinn, dafür (Leds) zur Laufzeit Objekte zu erzeugen. 
Denn es ist einfach falsch, zur Laufzeit eine beliebige Anzahl von 
Led-Objekten zu erzeugen. Das macht man statisch, dann kann man zur 
Compilezeit wesentlich mehr prüfen.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Niklas G. schrieb:
> struct integral_constant
Werde ich mir näher anschauen/üben.

Niklas G. schrieb:
> Es haben dann halt alle Pins einen anderen Typ und sind
> nicht (ohne template) an Funktionen zu übergeben oder iterierbar.
Mit dem iterieren, das ist der am ehesten fühlbare Nachteil.
Ist allerdings auch erstaunlich selten nötig....

Wilhelm M. schrieb:
> Viele Varianten von dem obigen sind denkbar.
Wird auch untersucht.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Statisch iterierbar ist trivial:

Klar. Das erzeugt aber ggf. eine Menge Code.

Wilhelm M. schrieb:
> Dynamisch geht aber auch (Sichwort Visitor).

Das ist nur statisch-auf-dynamisch adaptiert und hat u.U. das selbe 
Problem.

Wilhelm M. schrieb:
> Es macht keinen Sinn, dafür (Leds) zur Laufzeit Objekte zu erzeugen.

Wenn man einen Pin an eine Funktion (z.B. für Soft-UART o.ä.) übergeben 
möchte, muss der Parameter zur Laufzeit zur Verfügung stehen und 
erstellt werden; es sei denn die Funktion ist ein template, was wieder 
bei Mehrfachnutzung u.U. viel Code produzieren könnte.

"Echte" Instanzen von Pins anzulegen, die dann "static constexpr" sind 
und wegoptimiert werden, bieten den Vorteil einer intuitiveren Nutzung, 
mit pin.set statt MyPin::set.

Arduino Fanboy D. schrieb:
> Ist allerdings auch erstaunlich selten nötig....

Ich hatte mal ein LCD mit 24 Pins, da ist es ganz nett das mit einer 
Schleife machen zu können. War aber auch ein Prozessor mit vernünftiger 
Register-Adressierung, sodass die Schleife nur ein bisschen 
Pointer-Magie machen muss.

von Wilhelm M. (wimalopaan)


Lesenswert?

Niklas G. schrieb:
> Wilhelm M. schrieb:
>> Statisch iterierbar ist trivial:
>
> Klar. Das erzeugt aber ggf. eine Menge Code.
>
> Wilhelm M. schrieb:
>> Dynamisch geht aber auch (Sichwort Visitor).
>
> Das ist nur statisch-auf-dynamisch adaptiert und hat u.U. das selbe
> Problem.

Die ganze template-bloat-Problematik wird gewaltig überschätzt. In der 
Praxis ist das aus m.E. nicht relevant.

>
> Wilhelm M. schrieb:
>> Es macht keinen Sinn, dafür (Leds) zur Laufzeit Objekte zu erzeugen.
>
> Wenn man einen Pin an eine Funktion (z.B. für Soft-UART o.ä.) übergeben
> möchte, muss der Parameter zur Laufzeit zur Verfügung stehen

Nein, warum? Ander der Pin sich auf der Hardware?

> erstellt werden; es sei denn die Funktion ist ein template, was wieder
> bei Mehrfachnutzung u.U. viel Code produzieren könnte.

Alles ist ein template ;-)


> "Echte" Instanzen von Pins anzulegen, die dann "static constexpr" sind
> und wegoptimiert werden, bieten den Vorteil einer intuitiveren Nutzung,
> mit pin.set statt MyPin::set.

Würde ich nicht sagen. Die klassische (Laufzeit-) Objektorientierung ist 
nur ein Programmierparadigma von vielen.

>
> Arduino Fanboy D. schrieb:
>> Ist allerdings auch erstaunlich selten nötig....
>
> Ich hatte mal ein LCD mit 24 Pins, da ist es ganz nett das mit einer
> Schleife machen zu können.

Wie gesagt, auch das geht ja.

Das Wichtigste m.E.: wer etwas zur Laufzeit mach, kann nur zur Laufzeit 
prüfen, wer etwas statisch macht, kann zur Compilezeit prüfen.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Niklas G. schrieb:
> was wieder
> bei Mehrfachnutzung u.U. viel Code produzieren könnte.

Was natürlich für aktuelle PCs ein Riesenproblem ist...

Oliver

von A. B. (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Ich meine mich schwach zu erinnern....
> Was das nicht ein riesen Template Berg?
> Finde es aber gerade nicht wieder.....

Beitrag "Re: AVR GPIOR Bit Verwaltung C++"

>wie std::integral_constant)
>Leider keine STD Lib
>Leider auch keine STL
>Nur die C-Lib

example.tgz:  \include\std\... std::integral_constant in type_traits

>Die "Arduino-Philosophie" kann mich mal....
>Einzig die Pin Nummerierung muss ich übernehmen, weil sie auf den Boards
>aufgedruckt ist, und jeder Arduino Jünger sie so erwartet.
>Da steckt der Kompromiss, welchen ich eingehen muss.
>Daher auch der Zwang zu solchen Arrays.

Das heisst doch eigentlich, dass ein Interface fehlt, um eine 
typbasierte c++Port/Pin Funktionalität für Arduino zur Verfügung zu 
stellen !?

von Einer K. (Gast)


Lesenswert?

A. B. schrieb:
> Das heisst doch eigentlich, dass ein Interface fehlt, um eine
> typbasierte c++Port/Pin Funktionalität für Arduino zur Verfügung zu
> stellen !?

Ein wenig habe ich Schwierigkeiten die Frage(?) zu verstehen.

Aber ich versuche zu antworten.

Klar, gibts ein Arduino eigenes Framework um mit Pins umzugehen.
Unabhängig von µC Type und Hersteller.
Funktionsfähig in C++,  C und auch aus *.S (ASM) nutzbar.
Das Konzept hat seine Vorteile, ist aber (oft) recht lahm und Ressourcen 
fressend. (gerade bei den AVR macht sich das bemerkbar)

Für viele der 32 Bit System gibts alles, STD-Lib, STL und auch 
Exceptions.
In Sachen AVR ist die Luft deutlich dünner
Das ist eine Einschränkung auf Grund des gnu AVR-C++ Lieferumfangs, 
keine Arduino Eigenschaft.

-----------

Im Moment versuche ich gerade die STD  Lib von Wilhelm in das Arduino 
Library  Konzept zu überführen.
Einiges ist schon getestet, anderes macht noch Sorgen.
Es gibt störende Überschneidungen.
z.B. Macros im Arduino Framework welche mit Bezeichnern der STD Lib 
kollidieren

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino Fanboy D. schrieb:

> Im Moment versuche ich gerade die STD  Lib von Wilhelm in das Arduino
> Library  Konzept zu überführen.
> Einiges ist schon getestet, anderes macht noch Sorgen.
> Es gibt störende Überschneidungen.
> z.B. Macros im Arduino Framework welche mit Bezeichnern der STD Lib
> kollidieren

Oh, da ist mittlerweile ja viel Zeit vergangen und auch das ganze Thema 
hat sich sehr erfolgreich weiterentwickelt.

Vielleicht noch ein Hinweis:

die Meta-Code-Bibliothek (in meta.h) hat sich über die Zeit massiv 
weiterentwickelt. Man kann schon zur Compilezeit wirklich coole Sachen 
machen. Eine Alternative wäre halt Boost::Hana, wobei mir mein Ansatz, 
mich nicht auf die Compileroptimierung zu verlassen und alle 
Meta-Funktionen klassisch zu schreiben etwas besser gefällt ;-))

Die Modellierung der int. Periperie der Controller selbst kann 
mittlerweile automatisch aus den XML-Dateien von MicroChip erzeugt 
werden.

Durch den Erfolg der ganzen Sache ist es leider so, dass ich das ganze 
nicht mehr als OSS veröffentlichen darf (das geht leider vielen 
Entwicklern in diesem Kontext im Moment so).

Allerdings halte ich eine Zusammenführung der Arduino-Welt und den o.g. 
Ansätzen für vergebliche Liebesmühe.

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Durch den Erfolg der ganzen Sache ist es leider so, dass ich das ganze
> nicht mehr als OSS veröffentlichen darf (das geht leider vielen
> Entwicklern in diesem Kontext im Moment so).

Wer oder was hindert dich? Microchip? Dein Arbeitgeber?

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino Fanboy D. schrieb:

> Wenn mich meine Erinnerung nicht trügt war die Benutzung in etwa so:
> setPin(led); // um eine LED einzuschalten.
>
> Das gefällt mir nicht so...
> Ich hätte es gerne so:
> led.setHigh();
> oder
> led = 1;
> oder
> led.set(HIGH)
> oder
> led(1)

Hier gilt die alte Regel: freie Funktionen sind als Realisierungsform 
vorzuziehen (wenn möglich), da sie i.A. einen generischeren Code liefern 
und auch oft Symmetrien liefern. Einstellige freie Funktionen gehören 
zur öffentlichen Schnittstelle dieses Typs genauso wie öffentliche 
Elementfunktionen. Außerdem tendieren Sie dazu, die Kohäsion eines Code 
zu steigern. Und last but not least: in C++ gibt es ja auch Bestrebungen 
zur einer uniform-call-syntax, wobei dann foo(a) und a.foo() syntaktisch 
gleich wären.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Niklas G. schrieb:
> Johann L. schrieb:
>> Vielleicht so?
>
> Sicher dass das funktioniert? Da sind doch wieder die PORTx-Makros inkl.
> Cast in einem contexpr-Kontext.

Immerhin wird es von g++ v8 geschluckt, ansonsten hätte ich kaum den 
generierten Code posten können.

> Johann L. schrieb:
>> obwohl es wegoptimiert werden könnte:
>
> Kann es ja nicht, weil man durch den nicht-inline Funktionsaufruf
> erzwingt, dass die Instanz angelegt wird...

Natürlich kann es wegoptimiert werden! Schau dir mal aufmerksam den 
generierten Code an:
1
_Z6call_Pv:
2
  lds r24,_ZL5ports
3
  lds r25,_ZL5ports+1
4
  jmp _Z5use_P1P
5
6
  .section  .rodata
7
_ZL5ports:
8
  .word  56
9
  .word  50
Anstatt ".word 56" per LDS aus dem Speicher zu laden hätte der Wert auch 
per LDI in die Register geladen werden können.  Es wird nämlich ein 
Objekt vom Typ P übergeben und nicht etwa eine Referenz darauf; 
letzteres ist ein typischer Reflex bei manchen C++ Programmierern. Bei 
so einem kleinen Objekt (nicht größer als die Breite eines Zeigers) ist 
durchaus in Betracht zu ziehen, es per Value zu übergeben anstatt per 
const Referenz / Zeiger.  Referenz übergeben führt nur zu einer 
unnötigen Dereferenzierung im Callee.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich muss nochmal nachfragen. Weil mir das Problem mit dem Adresszugriff 
und aktuellen Compilervorschriften noch nicht einleuchtet.

Was bisher funktionierte und aktuell nicht mehr ist:
Bsp.
1
constexpr volatile uint16 *pinList[] = {&PIND,&PINC,&PINB,&PINA};

Warum klappt das mit aktuellen Compiler nicht mehr? PIND usw sind in der 
iomxyz.h definiert. Davon wird die Adressnummer geholt und in einem 
Zeiger gespeichert. Alle Beteiltigten sind jederzeit bekannt.

abgeändert in funktioniert wieder:
Bsp.
1
constexpr uint16_t pinList[] {0x002c, 0x100, 0x0023, 0x0103};

Das ist praktisch nichts anderes, funktioniert. Nur das man jetzt die 
Adresse nicht mehr aus der iomxyz.h automatisch ermitteln lassen kann 
sondern quasi von Hand eintragen muss.

Wo liegt das Problem wirklich? Ich meine wozu habe ich denn die vielen 
Headerdateien zur Verfügung wenn ich nicht darauf zugreifen darf?

von Einer K. (Gast)


Lesenswert?

Ich kann dir ja noch mal das minimal Testbeispiel zeigen:
1
using Register = volatile unsigned char *;
2
3
constexpr   Register ports[] { &PORTB,
4
                               &PORTH
5
                             }; 
6
7
void setup() 
8
{
9
}
10
11
void loop() 
12
{
13
}



Nach dem Präprozessor wird daraus:
1
using Register = volatile unsigned char *;
2
3
4
5
constexpr Register ports[] {  &(*(volatile uint8_t *)((0x05) + 0x20)),
6
                              &(*(volatile uint8_t *)(0x102))
7
                           };
8
9
void setup() 
10
{
11
}
12
13
void loop() 
14
{
15
}

Und da schmeckt dem Compiler dieser Ausdruck nicht:
> &(*(volatile uint8_t *)(0x102))

Dieser aber geht ohne Murren durch.
> &(*(volatile uint8_t *)((0x05) + 0x20))


Offensichtlich sind beide falsch.
Nur dass einer "besser" schmeckt.

von DPA (Gast)


Lesenswert?

In reinem C erlebt man keine solchen überraschungen...

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Wo liegt das Problem wirklich?

Das wurde gleich im zweiten Post beantwortet.

Niklas G. schrieb:
> Die "PORTxx" sind Makros welche einen C-Style-Cast der Art "(volatile
> uint8_t*) 0x1234" enthalten. Dieser wird vom C++-Compiler als
> "reinterpret_cast<volatile uint8_t*> (0x1234)" verstanden (named cast),
> daher die Ausgabe. Ein solches reinterpret_cast ist aber in
> constexpr-Kontexten verboten.

von Einer K. (Gast)


Lesenswert?

DPA schrieb:
> In reinem C erlebt man keine solchen überraschungen...

? Was du meinen ?

von Einer K. (Gast)


Lesenswert?

mh schrieb:
> Das wurde gleich im zweiten Post beantwortet.
Der Cast ist in beiden Ausdrücken identisch.

Ich interpretiere die Frage von Veit so:
Was ist die Ursache für die Ungleichbehandlung?

von mh (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> mh schrieb:
>> Das wurde gleich im zweiten Post beantwortet.
> Der Cast ist in beiden Ausdrücken identisch.

Welche Ausdrücke? Die beiden Zeilen in seinem Post?

von Einer K. (Gast)


Lesenswert?

mh schrieb:
> Welche Ausdrücke?

Diese, ich meine:

Arduino Fanboy D. schrieb:
> Und da schmeckt dem Compiler dieser Ausdruck nicht:
>> &(*(volatile uint8_t *)(0x102))
>
> Dieser aber geht ohne Murren durch.
>> &(*(volatile uint8_t *)((0x05) + 0x20))

Die Addition hat da einen Seiteneffekt.
Einen, den eine Addition aus meiner begrenzten Sicht, nicht haben 
dürfte.

Mit der Klammersetzung hat das nichts zu tun.
Die kann man verändern wie mal will, solange es konsistent bleibt.

von DPA (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> DPA schrieb:
> In reinem C erlebt man keine solchen überraschungen...
>
> ? Was du meinen ?

C ist weniger mit features überladen, wird seltener erweitert, hat eine 
simplere syntax, und wird deshalb vom mehr compilern vollständiger 
unterstützt und die Standards (sofern einer ausgewählt) auch 
eingehalten. Die Situation, dass es in einer neueren Kompilerversion mit 
gleichem ausgewählten c standard plötzlich nichtmehr geht, gibt es dort 
eigentlich nicht.

von mh (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> mh schrieb:
>> Welche Ausdrücke?
>
> Diese, ich meine:
>
> Arduino Fanboy D. schrieb:
>> Und da schmeckt dem Compiler dieser Ausdruck nicht:
>>> &(*(volatile uint8_t *)(0x102))
>>
>> Dieser aber geht ohne Murren durch.
>>> &(*(volatile uint8_t *)((0x05) + 0x20))

Interessant, wie er sich auf die Ausdrücke beziehen kann, die erst nach 
seinem Post von dir gepostet wurden.

Und der fehlende Fehler bei deinem zweiten Ausdrück ist vermutlich ein 
Bug. Ersetzt man das + durch einen anderen Operator (außer -) wie z.B. 
*, gibt es einen Fehler.

von Einer K. (Gast)


Lesenswert?

mh schrieb:
> Interessant, wie er sich auf die Ausdrücke beziehen kann, die erst nach
> seinem Post von dir gepostet wurden.

Um das zu verstehen wird man erst die Rekursion verstanden haben müssen.
(nee, es gibt eine Vorgeschichte, von der du wohl nichts wissen kannst)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich hatte mich immer noch gewundert das der händische Eingriff, quasi 
eine manualle Textersetzung, weil in der iomxyz.h nur defines stehen, 
zum funktionieren animiert.

> Niklas G. schrieb:
>> Die "PORTxx" sind Makros welche einen C-Style-Cast der Art "(volatile
>> uint8_t*) 0x1234" enthalten. Dieser wird vom C++-Compiler als
>> "reinterpret_cast<volatile uint8_t*> (0x1234)" verstanden (named cast),
>> daher die Ausgabe. Ein solches reinterpret_cast ist aber in
>> constexpr-Kontexten verboten.

Das muss man dann wohl aktzeptieren.

Jetzt mal gesponnen, um das cast Problem zu lösen müßte man theoretisch 
die Header umschreiben ohne Makros?

In dem Sinne statt
1
# define PORTH  _SFR_MEM8(0x102)

dann ungefähr so:
1
const uint16_t PORTH = 0x0102;

Ich werde das nicht machen, weil man dann zum Rest der µC Welt 
inkompatibel wird, aber rein zum Verständnis gefragt. Wäre das 
theoretisch der Lösungsweg? Dann wäre mir zum verstehen schon geholfen.

von Carl D. (jcw2)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> ich hatte mich immer noch gewundert das der händische Eingriff, quasi
> eine manualle Textersetzung, weil in der iomxyz.h nur defines stehen,
> zum funktionieren animiert.
>
>> Niklas G. schrieb:
>>> Die "PORTxx" sind Makros welche einen C-Style-Cast der Art "(volatile
>>> uint8_t*) 0x1234" enthalten. Dieser wird vom C++-Compiler als
>>> "reinterpret_cast<volatile uint8_t*> (0x1234)" verstanden (named cast),
>>> daher die Ausgabe. Ein solches reinterpret_cast ist aber in
>>> constexpr-Kontexten verboten.
>
> Das muss man dann wohl aktzeptieren.
>
> Jetzt mal gesponnen, um das cast Problem zu lösen müßte man theoretisch
> die Header umschreiben ohne Makros?
.
> In dem Sinne statt
>
1
> # define PORTH  _SFR_MEM8(0x102)
2
>
>
> dann ungefähr so:
>
1
> const uint16_t PORTH = 0x0102;
2
>
>
> Ich werde das nicht machen, weil man dann zum Rest der µC Welt
> inkompatibel wird, aber rein zum Verständnis gefragt. Wäre das
> theoretisch der Lösungsweg? Dann wäre mir zum verstehen schon geholfen.

das Macro _SFR_MEM8(x) kann beliebiges machen, man muß es nur 
umdefinieren.

Und man kann das AVR-Port-Bit-Problem mit C++-Typen und den 
Original-Header-Files in den Griff bekommen, wobei ich zugebe, daß eine 
solche Lösung ein böser Hack ist. Aber mir reicht sie.

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> dann ungefähr so:
>
1
> const uint16_t PORTH = 0x0102;
2
>
>
> Ich werde das nicht machen, weil man dann zum Rest der µC Welt
> inkompatibel wird, aber rein zum Verständnis gefragt. Wäre das
> theoretisch der Lösungsweg?

Nein.

Das Problem steckt ganz tief im Grundkonzept der Behandlung von SFIOR. 
Dadurch, dass du die ganzen Sonderkonzepte für eine Anwendung ausser 
Kraft setzt, machst du vieles andere kaputt, was richtig und nötig ist, 
inbesondere die automagisch angenommene Volatilität von Zugriffen auf 
SFIOR.

Das kann nur schief gehen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

gut, ich denke das Grundproblem habe ich verstanden.
Danke euch allen.

von Rigó M. (rig_m)


Lesenswert?

Ich bin zufällig auf diesem Thread gestoßen.
Gerade vor ein Tagen habe ich meine eigene c++ Implementierung für das 
Problem geschrieben.
Es ist hier zu finden: https://github.com/rigomate/avr8-templates

Im Endeffekt kann man so etwas machen:
1
using PinName = Pin<PortB, 1>;
2
3
void foo()
4
{
5
    PinName::setpin();
6
}

wohl angemerkt ist es mit -O3 genauso Effizient als hätte man
1
PORTB |= (1<<1);
geschrieben

Im Listing sieht das so aus:
1
void foo()
2
{
3
  PinName::setpin();
4
  PORTB |= (1<<1);
5
  48:  29 9a         sbi  0x05, 1  ; 5
6
  4a:  29 9a         sbi  0x05, 1  ; 5
7
    *reinterpret_cast<PortType>(portx) &= ~(1 << pin);
8
  }

Also sind die beiden vollkommen identisch.

Ich habe das ganze mit einen kleiner Makro Magie gelöst: 
https://github.com/rigomate/avr8-templates/blob/18c8c3c39c7f86ed64f88a6a578b2dbf0606247e/cpp/port.hpp#L35

Aber die <avr/io.h> wird nur in dem einen Header modifiziert, und am 
ende wieder zurückgesetzt. Das heißt, in jedem anderen Source File, 
Header etc. steht der normale <avr/io.h> zur Verfügung.

von Wilhelm M. (wimalopaan)


Lesenswert?

Rigó M. schrieb:
> Im Endeffekt kann man so etwas machen:using PinName = Pin<PortB, 1>;
>

Statische Polymorphie und damit Compilezeit-Instanziierungen 
(template-Instanziierungen) statt Laufzeit-Polymorphie und 
Laufzeit-Instanziierungen für HW-Abstraktionen zu verwenden, ist der 
richtige Weg. Gut, dass Du das erkannt hast. Und Du kannst diesen Weg 
für alle interne Peripherie eines µC machen, wie etwa USART<>, etc.

Hier mal die Hintergründe etwas weiter ausgeführt:

Beitrag "Re: AVR GPIOR Bit Verwaltung C++"

Rigó M. schrieb:
> void foo()
> {
>     PinName::setpin();
> }

BTW: Naming is hard! Die Elementfunktion hätte ich einfach nur 
`Pin::set()` genannt, denn `Pin::setPin()` ist redundant.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.