Forum: Compiler & IDEs Hardwareabstraktion


von Geri (Gast)


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
1
static inline void TMR0ISREnter(void)
2
{
3
  #ifdef WINARM
4
     ISR_ENTRY(); 
5
  #endif  
6
}
7
8
static inline void TMR0ISRExit(void)
9
{
10
  VICVectAddr = 0;
11
  #ifdef WINARM
12
    ISR_EXIT();
13
  #endif
14
}
15
16
17
void InitTimer0ISR(uint32_t Handler)
18
{ 
19
    VICVectAddr1 = (uint32_t)Handler;   // set interrupt vector in 0
20
    // weitere Initialisanweisungen für Time
21
}



und hier der Code der Applikation
1
static void Timer0ISR(void) ISR_ATTRIB_C
2
{
3
   TMR0ISREnter  
4
   
5
   // do something
6
7
   TMR0ISRExit 
8
}  
9
10
11
void InitTimer1ISR(void)
12
{
13
  InitTimer0ISR((uint32)Timer1ISR);  
14
}

Danke für eure Meinung und Tipps

Geri

von Karl H. (kbuchegg)


Lesenswert?

Abgesehen davon, dass mir nicht klar ist, warum du hier ...
1
static inline void TMR0ISRExit(void)
2
{
3
  VICVectAddr = 0;
4
  #ifdef WINARM
5
    ISR_EXIT();
6
  #endif
7
}
... 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.

von Geri (Gast)


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.
1
static void Timer0ISR(void) ISR_ATTRIB_C
2
{
3
   TMR0ISREnter  
4
   
5
   // do something
6
7
   TMR0ISRExit 
8
}

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

1
// Im Hardwarefile codiert
2
void InitTimer0ISR(uint32_t Handler)
3
{ 
4
    VICVectCntl1 = VIC_ENABLE | VIC_TIMER0; // use it for Timer 0 Interrupt:
5
    VICVectAddr1 = (uint32_t)Handler;   // set interrupt vector in 0
6
    VICIntEnable = VIC_BIT(VIC_TIMER0);    // Enable Timer0 Interrupt
7
    T0TCR = 0;  //   Reset timer 1  
8
    T0PR = 0;  //Set the timer 1 prescale counter    
9
    T0MR0 = 60000;   // Set timer 1 match register 
10
    T0MCR = 3;   // Generate interrupt and reset counter on match 
11
    T0TCR = 2;  // Reset Timer 0 and disableit}
12
}
13
14
15
// in der Anwendung aufgerufen
16
void InitTimer1ISR(void)
17
{
18
  InitTimer0ISR((uint32)Timer1ISR);  
19
}

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

von Karl H. (kbuchegg)


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).

von Geri (Gast)


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

von (prx) A. K. (prx)


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.

von StinkyWinky (Gast)


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.

von Geri (Gast)


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

von (prx) A. K. (prx)


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() ?

von Oliver (Gast)


Lesenswert?

Warum das alles nicht in C++, mit virtuellen Funktioen?

Oliver

von Geri (Gast)


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

von (prx) A. K. (prx)


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.

von (prx) A. K. (prx)


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.

von Geri (Gast)


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

von (prx) A. K. (prx)


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.

von Geri (Gast)


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

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.