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<typenameT,volatileT*reg>
2
classClass
3
{
4
public:
5
voiddoSomething(){*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<unsignedchar,&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?
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
typx
2
Class<typ,&x>
funktioniert es. Es müsste also etwas mit dem Makro zu tun haben.
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.
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?
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.
> 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.
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.
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):
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
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<typenameR>
4
structC{
5
staticRm_reg;
6
voiddo_something(){m_reg|=1;}
7
};
8
9
voidtest(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
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
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):
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.