Forum: Compiler & IDEs IO-Register als Template-Parameter


von Christian (Gast)


Lesenswert?

Hllo

Ich möchte eine kleine C++ Bibliothek schreiben welche die grundlegenden 
Funktionen eines Avr kapselt. Dazu verwende ich unteranderem ein 
Klassen-Template welches folgendermaßen definiert ist:
1
template<typename T, volatile T* reg>
2
class Class
3
{
4
public:
5
    void doSomething() { *reg = 5; }
6
}

reg soll dabei auf ein beliebiges Register zeigen. Für T wird unsigned 
char übergeben, wenn es sich um ein 8-Bit Register handelt. Handelt es 
sich um ein 16-Bit Register wird unsigned int übergeben.

Wenn ich nun eine Template-Klasse instantiere:
1
 Class<unsigned char, &PORTD> x

kommt es zu folgendem Fehler:
"error: template argument 2 is invalid"

Warum kommt es zu diesem Fehler? Gibt es eine Möglichkeit den Fehler zu 
umgehen?

von Klaus W. (mfgkw)


Lesenswert?

Das wird daran liegen, daß PORTD nicht den Typ unsigned char hat, 
sondern uint8_t.

Deshalb könnte es so klappen:
1
  Class< uint8_t, &PORT > x;

von Klaus W. (mfgkw)


Lesenswert?

Abgesehen davon: wenn schon C++, dann würde ich Referenzen nehmen:
1
template<typename T, volatile T &reg>
2
class Class
3
{
4
public:
5
  void doSomething()
6
  {
7
    reg = 5;
8
  }
9
};
10
11
...
12
13
  Class< uint8_t, PORT > x;

Ansonsten wäre ja der Name reg falsch und müsste zu p_reg geändert 
werden :-)

von Christian (Gast)


Lesenswert?

Beide Möglichkeiten hatte ich bereits probiert, bei beiden kommt es zum 
selben Fehler. unsigned char und uint8_t sind doch Ausdrücke für ein und 
dasselbe oder nicht? Ich verwende im Prinzip nur unsigned char und hatte 
damit noch keine Probleme.

für
1
typ x
2
Class<typ, &x>
funktioniert es. Es müsste also etwas mit dem Makro zu tun haben.

von Christian (Gast)


Lesenswert?

Sorry für den falschen Code

muss natürlich so heißen:
1
typ x;
2
Class<typ, &x> y;
1
unsigned char x;
2
Class<uint8_t, &x> y;
geht übrigens auch. unsigned char und uint8_t sollten also das selbe 
sein.

von Klaus W. (mfgkw)


Lesenswert?

Hm, wenn es mit einer eigenen Variablen geht, aber mit PORT...
nicht, wird das wohl kein passender const-Ausdruck sein.
(PORTD ist ja eine irgendwie gecastete int-Konstante, nötig
ist als template-Argument ein const-Ausdruck.)

Dann sehe ich ziemlich schwarz, das so zu machen.
Notfalls bleibt dann noch der Ausweg, aus dem Port keinen
template-Parameter zu machen, sondern ein Argument an den
Konstruktor.

von Kupfer Michi (Gast)


Lesenswert?

Bin gerade über das selbe Problem gestolpert. grrrr....

Im AVR GCC spezifischen Teil werden PORTs so deffiniert:

#define PORTB  _SFR_IO8(0x18)

was mittels <sfr_defs.h> aufgelöst wird zu:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + 0x20)

was dann für GCC keine gültige Template Konstante mehr ist und sich auch 
nicht mehr zu einer solchen zürückcasten lässt.

Man kann zwar die Portübergabe auf die Konstruktor und 
Funktionsparameter Eben runterziehen, aber dann hat man immer einen 
Laufzeit / Speicherplatz Overhead.

Man kann kaum eine AVR Hilfklasse definieren, ohne soffort in das 
Problem der Portparametriesierung zu laufen.

Gibt es da wirklich keine C++ konforme Lösung oder bleibt einem nur die 
Makrosteinzeit?

Ist Jörg Wunsch noch in dieser Ecke tätig und könnte was dazu sagen?

von Rolf Magnus (Gast)


Lesenswert?

Ich habe danach auch mal gesucht und auch keinen Weg gefunden. Man 
könnte sich aber relativ einfach ein Skript schreiben, das aus den 
XML-Beschreibungen der Prozessoren Header mit passenden Definitionen 
erzeugt. So werden meines Wissens auch die C-Header erzeugt.

von Kupfer Michi (Gast)


Lesenswert?

> Man könnte sich aber relativ einfach ein Skript schreiben, das aus den
> XML-Beschreibungen der Prozessoren Header mit passenden Definitionen
> erzeugt

Ja schon, aber ich hatte gehofft, dass Jörg Wunsch anbeisst (er hat 
nähmlich die AVR Header offiziell generiert).

Es wäre schön wenn C++ kompatible Header zum Auslieferungsstandard 
gehören würde.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Kupfer Michi schrieb:

> Ja schon, aber ich hatte gehofft, dass Jörg Wunsch anbeisst (er hat
> nähmlich die AVR Header offiziell generiert).

Nö, das macht für alle neuen AVRs Eric Weddington.

> Es wäre schön wenn C++ kompatible Header zum Auslieferungsstandard
> gehören würde.

Naja, kompatibel sind sie ja, nur halt nicht template-fähig.  Aber
wenn da jemand eine schlaue Idee hat, lässt sich sowas sicher künftig
erweitern.

von Thorsten M. (thorstenm)


Lesenswert?

Hallo zusammen,
entschuldigt die Ausgraberei.

>  Aber
> wenn da jemand eine schlaue Idee hat, lässt sich sowas sicher künftig
> erweitern.

Hatte jemand inzwischen eine  schlaue Idee?

Ich hatte gerade eine ähnliche Idee wie Christian und wollte sowas 
basteln  (hier nur vereinfacht, soll später mit open collector, active 
low etc. identisch funktionieren):
1
template<volatile uint8_t &  DDR, volatile uint8_t & PORT, uint8_t PIN>
2
class OutputPin
3
{
4
        OutputPin(){DDR |= (1 << PIN);}
5
        void Toggle(){PORT ^= (1 << PIN);}
6
        void On(){PORT |= (1 << PIN);}
7
        void Off(){PORT &= ~(1 << PIN);}
8
};
9
10
int main()
11
{
12
        // ...
13
        OutputPin<DDRA, PORTA, PA3> MyLed;
14
        OutputPin<DDRB, PORTB, PB0> MyRelay;
15
        MyLed.On();
16
        MyRelay.Toggle();
17
        // ...
18
}

Wie müsste ein Headerfile mit den Registerdefinitionen aussehen, damit 
sowas klapppt? Oder gibt es eine andere halbwegs elegant benutzbare 
Möglichkeit, sowas umzusetzen?


Grüße,
Thorsten

von Andreas F. (aferber)


Angehängte Dateien:

Lesenswert?

Heureka, es geht doch :-) Siehe der angehängte Header, zu verwenden 
statt <avr/io.h>.

Allerdings mit dem kleinen Wermutstropfen, dass erstens eine 
undokumentierte Eigenschaft der originalen avr-libc-Header (nämlich dass 
alle IO-Port-Macros letztlich auf _MMIO_BYTE, _MMIO_WORD oder 
_MMIO_DWORD zurückführen) und zweitens eine GCC-Extension (_typeof_) 
benutzt werden. Dafür hat es den Vorteil, dass nicht noch einmal alle 
IO-Ports in C++-Form neu aufgeführt werden müssen.

Bei Verwendung des Headers können erstmal im normalen Code die IO-Ports 
weiter verwendet werden wie bisher (also auch in Pointer speichern 
etc.). Soll ein Port als Templateparameter benutzt werden geht das so:
1
#include <avr_io_cpp.h>
2
3
template<typename R>
4
struct C {
5
        static R m_reg;
6
        void do_something() { m_reg |= 1; }
7
};
8
9
void test(void)
10
{
11
        C<AVR_IOR_PARAM(PORTA)> x;
12
        x.do_something();
13
}

Verwendung des Optimizers ist dringend zu empfehlen. Mit -Os oder -O2 
erzeugt der GCC dann aber für gängige Dinge wie "PORTA |= 1;" trotz der 
"Template-Orgien" den gleichen Code wie er ihn auch mit der originalen 
<avr/io.h> erzeugt hätte. Selbst das Beispiel mit der Übergabe an das 
Template erzeugt den gleichen Code, als wenn man den Port direkt in die 
do_something()-Funktion geschrieben hätte :-)

Andreas

von Andreas F. (aferber)


Lesenswert?

Andreas Ferber schrieb:
> Verwendung des Optimizers ist dringend zu empfehlen.

Ups, Korrektur: es funktioniert nur mit Optimizer, da nur so alle 
Referenzen auf die static-member-Variablen verschwinden. Ohne Optimizer 
gibt es beim Linken einen Fehler.

Andreas

von Andreas F. (aferber)


Angehängte Dateien:

Lesenswert?

Andreas Ferber schrieb:
> Ups, Korrektur: es funktioniert nur mit Optimizer,

So, jetzt aber, diese Version funktioniert auch ohne Optimizer. 
Allerdings ist der Overhead ohne Optimizer ziemlich hoch, also 
Vorsicht.

Das Beispiel muss dann so aussehen (das "static" bei m_reg fällt weg):
1
#include <avr_io_cpp.h>
2
3
template<typename R>
4
struct C {
5
        R m_reg;
6
        void do_something() { m_reg |= 1; }
7
};
8
9
void test(void)
10
{
11
        C<AVR_IOR_PARAM(PORTA)> x;
12
        x.do_something();
13
}

Andreas

von Thorsten M. (thorstenm)


Angehängte Dateien:

Lesenswert?

Hallo Andreas,

vieeeleeeen, vielen Dank. Das Teil im Anhang ergibt jetzt mit oder ohne 
#definetem FOO ein byte-identisches .hex bei -Os.

Grüße,
Thorsten.

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.