www.mikrocontroller.net

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


Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
template<typename T, volatile T* reg>
class Class
{
public:
    void doSomething() { *reg = 5; }
}

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:
 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?

Autor: Klaus Wachtler (mfgkw)
Datum:

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

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

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Abgesehen davon: wenn schon C++, dann würde ich Referenzen nehmen:
template<typename T, volatile T &reg>
class Class
{
public:
  void doSomething()
  {
    reg = 5;
  }
};

...

  Class< uint8_t, PORT > x;

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

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
typ x
Class<typ, &x>
funktioniert es. Es müsste also etwas mit dem Makro zu tun haben.

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry für den falschen Code

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

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Kupfer Michi (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Kupfer Michi (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Thorsten M... (thorstenm)
Datum:

Bewertung
0 lesenswert
nicht 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):
template<volatile uint8_t &  DDR, volatile uint8_t & PORT, uint8_t PIN>
class OutputPin
{
        OutputPin(){DDR |= (1 << PIN);}
        void Toggle(){PORT ^= (1 << PIN);}
        void On(){PORT |= (1 << PIN);}
        void Off(){PORT &= ~(1 << PIN);}
};

int main()
{
        // ...
        OutputPin<DDRA, PORTA, PA3> MyLed;
        OutputPin<DDRB, PORTB, PB0> MyRelay;
        MyLed.On();
        MyRelay.Toggle();
        // ...
}

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

Autor: Andreas Ferber (aferber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
#include <avr_io_cpp.h>

template<typename R>
struct C {
        static R m_reg;
        void do_something() { m_reg |= 1; }
};

void test(void)
{
        C<AVR_IOR_PARAM(PORTA)> x;
        x.do_something();
}

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

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Andreas Ferber (aferber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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):
#include <avr_io_cpp.h>

template<typename R>
struct C {
        R m_reg;
        void do_something() { m_reg |= 1; }
};

void test(void)
{
        C<AVR_IOR_PARAM(PORTA)> x;
        x.do_something();
}

Andreas

Autor: Thorsten M... (thorstenm)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.