IO-Register als Template-Parameter

Gast #1453486
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?
#1453531
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 :-)
Gast #1453590
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.
Gast #1453599
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.
#1453625
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.
Gast #1483018
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?
Gast #1483431
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.
Gast #1491336
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.
Moderator Persönliche Seite #1491575
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.
#1733608
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
#1733858
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
Angehängte Dateien:
#1733891
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
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren