www.mikrocontroller.net

Forum: GCC Hardwareabstraktion


Important announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo zusammen

Ich habe eine c-Anwendung für einen Mikrocontroller, die ich möglichst 
unabhängig von der Hardware erstellen möchte. (Z.B. auf Mikrocontroller 
und auf PC lauffähig). U.a. soll auf Anwendungsebene eine Timer-ISR 
aufgerufen werden. In der  Anwendungsebene sollen aber keine 
Mikrocontrollerspezifischen Anweisungen stehen.


Ich habe nun die Hardware von der Anwendung getrennt.
1.) Was hält ihr von der nachfolgenden Lösung?
2.) Ist bei dieser Lösung mit Performanceeinbussen zu rechnen, z.B. 
weil die Initialsierung der ISR in einem anderen Modul wie die ISR 
selbst stattfindet? Fehlt evtl. noch ein "inline":)?
3.) Wie würdet ihr es machen?



// Hardwareebene

static inline void TMR0ISREnter(void)
{
  #ifdef WINARM
     ISR_ENTRY(); 
  #endif  
}

static inline void TMR0ISRExit(void)
{
  VICVectAddr = 0;
  #ifdef WINARM
    ISR_EXIT();
  #endif
}


void InitTimer0ISR(uint32_t Handler)
{ 
    VICVectAddr1 = (uint32_t)Handler;   // set interrupt vector in 0
    // weitere Initialisanweisungen für Time
}



und hier der Code der Applikation
static void Timer0ISR(void) ISR_ATTRIB_C
{
   TMR0ISREnter  
   
   // do something

   TMR0ISRExit 
}  


void InitTimer1ISR(void)
{
  InitTimer0ISR((uint32)Timer1ISR);  
}

Danke für eure Meinung und Tipps

Geri

Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Abgesehen davon, dass mir nicht klar ist, warum du hier ...
static inline void TMR0ISRExit(void)
{
  VICVectAddr = 0;
  #ifdef WINARM
    ISR_EXIT();
  #endif
}
... den 'Weiterreichungspointer' löschen willst:

Ja klar. Kann man so machen. Und ein universelles Betriebssystem wird 
das auch so machen. Die Frage ist halt immer: Wieviel kostet mir diese 
Universalität (in Einheiten der Rechenzeit), kann ich das verschmerzen 
und bin ich gewillt diesen Penalty zu bezahlen.
Denn da fehlt ja noch was: Die eigentliche ISR, die über den 
Funktionspointer die Funktion aufruft. Aber bitte, bitte: 
Funktionspointer nicht als uint32_t anlegen. Mach einen ordentlichen 
Datentyp dafür (mit einem typedef) und leg den entsprechend an. 
Sinnigerweise wird man dann auch noch Möglichkeiten ersinnen, wie sich 
mehrer Funktionen auf Anwendercode-Ebene in einen Interrupt einhängen 
können, d.h. da wird es ein Array geben oder eine Liste von Pointer zu 
Handlern, etc. etc. Und so führt eines zum anderen und im Endeffekt 
kommt da einiges an Code zusammen.

Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo Karlheinz

Danke für deine Kommentare!

Mit VICVectAddr = 0; führt ma ein Interrupt Aknowledge durch. Bei nested 
Interrupts relevant

Timer0ISR ist die ISR und dort würde ich den Code hineinschreiben. Ich 
würde hier also keine Funktionspointer aufrufen. Es gibt nur einen 
Funkionspointer, und den muss man am Anfang kennen und dem 
Interruptsystem mitteilen.
static void Timer0ISR(void) ISR_ATTRIB_C
{
   TMR0ISREnter  
   
   // do something

   TMR0ISRExit 
}  

Ich würde der Initialisierungsroutine also einen Zeiger auf Timer0ISR 
geben. Das macht man ja auch, wenn man den Timer sonst initialsiert.


// Im Hardwarefile codiert
void InitTimer0ISR(uint32_t Handler)
{ 
    VICVectCntl1 = VIC_ENABLE | VIC_TIMER0; // use it for Timer 0 Interrupt:
    VICVectAddr1 = (uint32_t)Handler;   // set interrupt vector in 0
    VICIntEnable = VIC_BIT(VIC_TIMER0);    // Enable Timer0 Interrupt
    T0TCR = 0;  //   Reset timer 1  
    T0PR = 0;  //Set the timer 1 prescale counter    
    T0MR0 = 60000;   // Set timer 1 match register 
    T0MCR = 3;   // Generate interrupt and reset counter on match 
    T0TCR = 2;  // Reset Timer 0 and disableit}
}


// in der Anwendung aufgerufen
void InitTimer1ISR(void)
{
  InitTimer0ISR((uint32)Timer1ISR);  
}

Ich würde evtl. lediglich währende der Ausführung mal einen anderen 
Handler initialisieren.

Der Unterschied im Code ist meiner Ansicht nach nur, dass die ISR in 
einer anderen c-Datei implementiert ist.

Kommt es damit auch zu Geschwindigkeitseinbussen?

Mit einem ordentlichen Datentyp hast du aber jedenfalls recht:)!

Beste Grüsse

Geri

Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
> Es gibt nur einen Funkionspointer, und den muss man am Anfang kennen
> und dem Interruptsystem mitteilen.
>
> static void Timer0ISR(void) ISR_ATTRIB_C
> {
>    TMR0ISREnter
>
>    // do something
>
>    TMR0ISRExit
> }

???
Dann hast du ja wieder die Details, wie das auf einem spezifischen µC 
konkret zu schreiben ist, im Anwendercode. Das hab ich dann 
missverstanden, wie das gemeint war. Vergiss die Sache mit dem 
Funktionspointer.

OK, ein bischen besser ist es, weil du dir über die Makros eine gewisse 
Flexibilität einbaust. Nur darf halt derjenige, der den Mechanismus dann 
letztendlich benutzt, nicht auf die Makros vergessen. Sonst kann es 
natürlich passieren, dass das auf System A funktioniert (weil die Makros 
zu keinem Code expandieren) und auf einem System B aber nicht (weil dort 
die Makros relevanten Code einbringen).

Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo Karlheinz

????, Genauso habe ich es gemeint, wie du zuletzt verstanden hast:)

Den Code nutze ich nur selber. Die Makros würden sich ja danach nur 
änderen, wenn man eine andere Hardware oder Compiler untersützen möchte.

Ich erwarte mir halt einen einfachen Umstieg auf einen anderen 
Controller. Wenn man das Ganze hobbymässig betreibt, dann steigt man 
gerne
wieder mal auf einen anderen Controller um so ist es dann halt doch 
einfacher, wenn der Grossteil von Code noch läuft.

Einen besonders grossen Vorteil erwarte ich mir aber v.a bei Modultests, 
weil diese auf einem System wie Windows besser automatisierbar sind.

Wenn das Programmgerüst so wie gezeigt aufgebaut ist, ergeben sich 
dadurch überhaupt Performanceeinbussen?

Mit compilerdirektiven könnte man im Code auch arbeiten, wenn man aber 
schliesslich für mehrer Compiler code schreibt, dann wird der
code mit der Zeit nicht gerade

Gibt es noch bessere Möglichkeiten?

Was könnte man noch evtl. noch verbessern?

Beste Grüsse

Geri

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Dinge wie ISR_ENTRY() müssen direkt in der ISR ausgeführt werden. Sie in 
Funktionen auszulagern ist auch dann riskant, wenn sie als "inline" 
deklariert werden, denn der Compiler wird sich dennoch seine Gedanken 
dazu machen und wird sie je nach Flags evtl. nicht inlinen. Was dann in 
kräftigst die Hose geht.

Ich sehe auch keinen Sinn in dieser Auslagerung. Die ISR selbst ist 
notwendigerweise nicht portabel, weil abhängig vom Controller. Was wird 
durch diese Pseudo-Abstraktion gewonnen?

Wenn es beispielsweise darum geht, die ähnliche UART aller LPC1000/2000 
gemeinsam handhaben zu können, dann kann man aus den notwendigerweise 
controllerabhängig verschiedenen Handlern heraus den abstrakteren 
Handler eines universelleren UART-HAL aufrufen.

Autor: StinkyWinky (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Ich finde die Idee mit dem Funktionspointer gut.

Das Platformunabhängige Programm gibt dem HW-abhängigen Treiber beim 
Initialisieren den Funktinspointer bekannt.
Wenn nun ein Timer-Event fällig wird, macht der Treiber zuerst ISREnter, 
ruft dann den Funktionspointer auf, und am schluss ISRExit.

Das Hauptprogramm muss keine Makros kennen.

Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Hallo Zusammen

Danke für eure Kommentare!

@Andreas:
Die Befürchtung, dass der Compiler hier unberechenbare Dinge macht, der 
kam mir eben auch:)

>>Was wird durch diese Pseudo-Abstraktion gewonnen?

1.) Der Inhalt meiner ISR von der Ausführungszeit kurz, der Code ist 
aber recht lang und absolut unabhängig von der verwendeten Hardware. Ein 
meiner Wünsche wäre eben, dass der Code, sobald controllerunabhängig in 
der Anwendungsebene codiert wird. Bei den Uarts hast du wahrscheinlich 
recht, ein Timer kann halt sehr universell eingesetzt werden - er ist 
wahrscheinlich auch ein Sonderfall - aber eben wichtig

2.) Mit defines zu arbeiten macht den Code mit der Zeit halt etwas 
unübersichtlich. Kleine Makros dünken mich modularer. Wenn ich z.B. 5 
verschieden Compiler verwende man muss ich an den speziellen stellen 
immer wieder ifdef....

3.) Wie ich oben geschrieben habe, möchte ich einen möglichst einfach 
portieren können.

4.) Das Testen von Programmmodulen könnte auf einen PC verlagert werden. 
=> Erleichterung beim Debugging.

Wenn der Compiler aber wirklich so unberechenbar sind, dann werde ich, 
wenn nicht anderweitig möglich wohl mit defines arbeiten. Das hat bis 
jetzt sehr gut geklappt. Man sucht halt immmer wieder Möglichkeiten zur 
Verbesserung / Generalisierung.


@StinkyWinky

Das würde auch funktionieren, hat aber Performanceeinbussen zur Folge, 
da du dann ja in der ISR eine Prozedur aufrufst. Man könnte sogar eine 
verlinkte Liste von Handlern aufbauen.


Ich bin kein absoluter Crack in c. Vielleicht gibt es aber auch die 
Möglichkeit, einen allgemeinnen Codeblock in einem Programmmodul zu 
definieren, der dann in die ISR beim Compilieren mit Sicherheit 
eigebunden wird.


Beste Grüsse

Geri

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Geri schrieb:

> 1.) Der Inhalt meiner ISR von der Ausführungszeit kurz, der Code ist
> aber recht lang und absolut unabhängig von der verwendeten Hardware. Ein
> meiner Wünsche wäre eben, dass der Code, sobald controllerunabhängig in
> der Anwendungsebene codiert wird.

Und was spricht dann gegen eine hardwareabhängige Funktion

static void Timer0ISR(void) ISR_ATTRIB_C
{
   ISR_ENTRY();
   handler();
   VICVectAddr = 0;
   ISR_EXIT();
}

und eine nicht davon abhängige Funktion handler() ?

Autor: Oliver (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Warum das alles nicht in C++, mit virtuellen Funktioen?

Oliver

Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
@Andreas: Dieser Ansatz gefiele mir auch sehr gut - eigentlich sogar 
noch  besser, allerdings muss man ja hier wieder eine Prozedur (Handler) 
aufrufen. Das kostet Ressourcen, gerade wenn der Timer mit hoher 
Frequenz getaktet wird.
Oder weiss der Compiler hier besser Bescheid, wenn der Handler inline 
definert ist?


@Oliver
Ich habe fast ausschliesslich c-Compiler in Verwendung. Wie würde das 
aber bitte konkret aussehen?

Beste Grüsse

Geri

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Geri schrieb:

> Oder weiss der Compiler hier besser Bescheid, wenn der Handler inline
> definert ist?

Den Handler darfst du problemlos inlinen. Ob dar Unterschied wirklich 
gross ist wäre aber noch abzuwarten.

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Geri schrieb:

> Ich habe fast ausschliesslich c-Compiler in Verwendung. Wie würde das
> aber bitte konkret aussehen?

Hardwareunabhängige Basisklasse, von der die diversen hardwareabhängigen 
Implementierungen abgeleitet werden.

Ich mache das gerne in einer Variation davon für Schnittstellentreiber 
UART/I2C/etc. Eine normale Basisklasse mit dem Löwenanteil vom Code für 
diese Sorte Schnittstelle des Controllers, darunter dem 
Interrupt-Handler, und ein davon abgeleitetes Template für die 
Schnittstelleninstanzen mit den von der Hardware angesprochenen 
Handlern, die nur den Handler der Basisklasse aufrufen.

Ein netter Nebeneffekt dieser Technik ist es, dass irgendwelche 
Pufferspeicher im Template mit pro Instanz wählbarer Grösse direkt 
anlegt werden können, ohne dynamische Speicherverwaltung verwenden zu 
müssen - die versuche ich bei Controllern so weit wie möglich zu 
vermeiden.

Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Vielen Dank für deine Erläuterungen

Das klingt danns sehr gut.

Genau so etwas ähnliches habe ich auf PC-Ebene für Schnittstellen (USB, 
RS232 und Ethernet) gemacht, allerdings in Delphi und ohne ISR.

>Ich mache das gerne in einer Variation davon für Schnittstellentreiber
>UART/I2C/etc. Eine normale Basisklasse mit dem Löwenanteil vom Code für
>diese Sorte Schnittstelle des Controllers
bis hier her ist mir das noch klar:)

>, darunter dem Interrupt-Handler,
du meinst wahrscheinlich davon abgeleitet - oder, wäre dann auch klar:)?


>und ein davon abgeleitetes Template für die Schnittstelleninstanzen mit den von 
der Hardware angesprochenen Handlern, die nur den Handler der Basisklasse 
aufrufen.
Hm, wieso dann bitte noch ein Template? Wenn ich festestelle, dass ich 
z.B. die eine von der Basisklasse (TCommunicationBase) abeleitete Klasse 
TComRS232 intanziere soll, dann habe ich doch vollständigen Zugriff auf 
die Klasse. Gleichzeitig bei der Erzeugung wird ein Zeiger auf die 
Handlermethode übergeben...?

Hast du davon hier bitte vielleicht ein Stück Beispielcode oder eine 
Literarutstelle?

>>Ein netter Nebeneffekt dieser Technik ist es, dass irgendwelche
>>Pufferspeicher im Template mit pro Instanz wählbarer Grösse direkt
>>anlegt werden können, ohne dynamische Speicherverwaltung verwenden zu
>>müssen - die versuche ich bei Controllern so weit wie möglich zu
>>vermeiden.
Dieser Teil wird mir dann wahrscheinlich klarer, wenn ich zuerst obigen 
gecheckt habe:)

Vielen Dank nochmals für deine Mühe!

Geri

Autor: A. K. (prx)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Geri schrieb:

>>, darunter dem Interrupt-Handler,
> du meinst wahrscheinlich davon abgeleitet - oder, wäre dann auch klar:)?

Im Template ist der von der Hardware angesprochene Handler. Wie das 
genau funktioniert hängt vom Controller ab. Bei Controllern mit Vektoren 
in Registern (LPC2000) kann man eine statische Memberfunktion direkt 
verwenden, während es bei Controllern mit Bindung über den Namen (Cortex 
M3) etwas komplizierter wird. In diesem Hardware-Handler steht dann aber 
kaum mehr drin als ein Aufruf einer Funktion aus der Basisklasse.

> Hm, wieso dann bitte noch ein Template?

Auf diese Art lassen sich die Transferpuffer dort als Arrays einbauen, 
mit einer Grösse die per Template-Parameter bestimmt wird. Das ersetzt 
die von mir in solchem Umfeld wenig geliebte dynamische 
Speicherallokation - ich habe es gern, wenn die RAM-Belegung abgesehen 
vom Stack bereits mit dem Linken offenbar wird.

Sowas wie (vereinfachtes Schema, kein realer Code):
  template<int RdBufSz, int WrBufSz> class UART : UART_Base {
      ...
      uint8_t rdbuf[RdBufSz], wrbuf[WrBufSz];
  }

Statt dynamischer Allokation werden im Konstruktur der abgeleiteten 
Klasse den Pointern in der Basisklasse die Adressen dieser Arrays 
zugewiesen.

Autor: Geri (Gast)
Datum:

Diesen Beitrag bewerten:
lesenswert
nicht lesenswert
Ok, Danke! Die Überlegungen mit den Puffer kann ich nun nachvollziehen. 
Den Handler in Verbindung mit dem Template zu bringen, muss ich mir noch 
ein wenig durchdenken:)

Vielen Dank vorab nochmals für deine Hilfe!

Geri

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




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 erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net