Forum: Compiler & IDEs MSP430: Auswertung Interrupt-Flags


von kai (Gast)


Lesenswert?

Hallo!

Habe eine Frage an euch. Ich verwende einen MSP430F2619 und bin gerade 
dabei die ISR von der DMA zu schreiben (in C).
Das sieht so aus:
1
#pragma vector = DMA_VECTOR
2
__interrupt void DMA_ISR(void)
3
{
4
  do
5
  {
6
    if(DMAIV & DMAIV_DMA0IFG)
7
    {
8
      /* DMA0 Interrupt */
9
      if(DMA0Interrupt_CB != NULL)
10
      {
11
        DMA0Interrupt_CB();
12
      }
13
    }
14
    if(DMAIV & DMAIV_DMA1IFG)
15
    {
16
      /* DMA1 Interrupt */
17
      if(DMA1Interrupt_CB != NULL)
18
      {
19
        DMA1Interrupt_CB();
20
      }
21
    }
22
    if(DMAIV & DMAIV_DMA2IFG)
23
    {
24
      /* DMA2 Interrupt */
25
      if(DMA2Interrupt_CB != NULL)
26
      {
27
        DMA2Interrupt_CB();
28
      }
29
    }
30
  } while (DMAIV);
31
}

Beim anschauen des Anwenderhandbuchs ist mir der Assemblercode dazu 
aufgefallen:
1
;Interrupt handler for DMA0IFG, DMA1IFG, DMA2IFG Cycles
2
DMA_HND ... ; Interrupt latency 6
3
ADD &DMAIV,PC ; Add offset to Jump table 3
4
RETI ; Vector 0: No interrupt 5
5
JMP DMA0_HND ; Vector 2: DMA channel 0 2
6
JMP DMA1_HND ; Vector 4: DMA channel 1 2
7
JMP DMA2_HND ; Vector 6: DMA channel 2 2
8
RETI ; Vector 8: Reserved 5
9
RETI ; Vector 10: Reserved 5
10
RETI ; Vector 12: Reserved 5
11
RETI ; Vector 14: Reserved 5
12
DMA2_HND ; Vector 6: DMA channel 2
13
... ; Task starts here
14
RETI ; Back to main program 5
15
DMA1_HND ; Vector 4: DMA channel 1
16
... ; Task starts here
17
RETI ; Back to main program 5
18
DMA0_HND ; Vector 2: DMA channel 0
19
... ; Task starts here
20
RETI ; Back to main program 5

Der Code sieht wesentlich kompakter aus, als meine If-Abfragen in der 
ISR.

Mache ich da etwas grundlegend Falsch?

Danke für eure konstruktive Kritik!

Gruß
Kai

P.S.:
Um es gleich zu sagen: Ich kann kein Assembler! Schon gar nicht in 
Assembler entwickeln!

von Christian R. (supachris)


Lesenswert?

Die machen da den MSP430-ISR Trick. Die Interrupt Flags bei den mehrfach 
belegten IFGs sind so gestaltet, dass man einfach das gaze IFG Register 
auf den Programmcounter addieren kann. Ist kein Interrupt anstehend, 
gehts dann mit dem Befehl direkt danach weiter, weil 0 addiert wurde. 
Ist einer anstehend, gehts mit einem der (im obigen Beispiel 3) Befehlen 
weiter, die danach kommen. Das spart jede Menge Latenz. Natürlich klappt 
das mit deinem C-Code nicht, weil du ja erst (wieso überhaupt?) 
überprüfst, ob die ISR Funktion vorhanden ist....wenn du die Abfrage 
weglässt, wird das der Compiler entsprechend umsetzen.

von kai (Gast)


Lesenswert?

Christian R. schrieb:
> Natürlich klappt
> das mit deinem C-Code nicht, weil du ja erst (wieso überhaupt?)
> überprüfst, ob die ISR Funktion vorhanden ist....wenn du die Abfrage
> weglässt, wird das der Compiler entsprechend umsetzen.

Und wie müsste ich den Code schreiben, dass das so gemacht wird?

Außerdem überprüfe ich ja nicht beim Betreten der ISR ob sie vorhanden 
ist, sondern welches Flag gesetzt ist. Im DMAIV steht doch drin, welcher 
DMA-Interrupt aufgetreten ist. Und es kann ja sein, das wärend ich die 
ISR abarbeite eine zweite DMA fertig ist und wieder einen Interrupt 
auslöst. Dann muss ich nicht erst die ISR verlassen und wieder betreten, 
sonden bleibe in der ISR, bis auch dieser Interrupt abgearbeitet ist.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

kai schrieb:
> Und wie müsste ich den Code schreiben, dass das so gemacht wird?

Mit einem Array von Funktionspointern, dessen Startadresse Du auf void* 
castest, den Inhalt des IFG draufaddierst und das Ergebnis wieder zu 
einem Funktionspointer machst, den Du dann aufrufst.

Wichtig ist, daß das Array ausreichend groß dimensioniert ist -- im 
Assemblerbeispiel enthält die Sprungtabelle 8 Einträge.

von Stefan (Gast)


Lesenswert?

Bei IAR gibt es die intrinsische Funktion __even_in_range,
die zumindest bei TAIV, TBIV, usw. funktioniert. Ob's auch bei DMAIV 
funktioniert müsstest Du mal näher untersuchen

von kai (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Mit einem Array von Funktionspointern, dessen Startadresse Du auf void*
> castest, den Inhalt des IFG draufaddierst und das Ergebnis wieder zu
> einem Funktionspointer machst, den Du dann aufrufst.
>
> Wichtig ist, daß das Array ausreichend groß dimensioniert ist -- im
> Assemblerbeispiel enthält die Sprungtabelle 8 Einträge.

Dauert das Verwenden von Funktionspointern nicht länger?
Bis ich das Element berechnet habe und da hinspringe (wobei hierbei 
wieder der ganze Stack und so gesichert werden müssten) vergeht doch 
mehr Zeit, wie wenn ich das Register maskiere und auf > 0 abfrage, oder 
nicht!?
Davon abgesehen, dass ein Array mit Funktionspointer mehr Speicherplatz 
braucht. Hier wären es 4 Byte * 8 = 32 Byte ...

Stefan schrieb:
> Bei IAR gibt es die intrinsische Funktion __even_in_range,
> die zumindest bei TAIV, TBIV, usw. funktioniert. Ob's auch bei DMAIV
> funktioniert müsstest Du mal näher untersuchen

Ich verwende CCE und nicht IAR.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

kai schrieb:
> Dauert das Verwenden von Funktionspointern nicht länger?

Wieso sollte es das tun?

> Davon abgesehen, dass ein Array mit Funktionspointer mehr Speicherplatz
> braucht.

Verwendest Du einen MSP430X? Dann ja, sonst nein, ein Funktionspointer 
ist auch nur 16 Bit groß.

von Peter D. (peda)


Lesenswert?

Die Frage ist, ob DMAIV eine Bitmaske oder eine Nummer ist.
Dein &-Test macht nur Sinn, wenn es eine Bitmaske ist und dann ist aber 
eine Sprungtabelle Unsinn.

Ist es dagegen eine Nummer, dann ist der &-Test Unsinn und man nimmt 
einfach ein switch/case.
Dann entscheidet der Compiler automatisch, ob ein Vergleich oder eine 
Sprungtabelle besser ist.
Ein Sprungtabelle lohnt sich nämlich erst ab einer größeren Zahl und bei 
aufeinanderfolgenden Case.

Dein zusätzlicher Test der Funktionsadresse auf Null ist in der Tat sehr 
merkwürdig.
Entweder eine Funktion existiert nicht, dann meckert der Linker oder sie 
existiert und dann ist sie immer != Null.
Auf der Adresse 0x0000 kann keine Funktion stehen.


Peter

von Christian R. (supachris)


Lesenswert?

Peter Dannegger schrieb:
> Dein zusätzlicher Test der Funktionsadresse auf Null ist in der Tat sehr
> merkwürdig.
> Entweder eine Funktion existiert nicht, dann meckert der Linker oder sie
> existiert und dann ist sie immer != Null.
> Auf der Adresse 0x0000 kann keine Funktion stehen.

Genau das meine ich, wenn du den Quatsch weglässt und statt dessen das 
Switch-Case nimmst wie in den Beispielcodes von TI, macht der Compiler 
da automatisch die Tabelle draus. Jedenfalls klappt das bei den Timer 
Interrupts wunderbar.

von kai (Gast)


Lesenswert?

Das Abrüfen auf Null muss deswegen erfolgen, weil die Funktionspointer 
zur Programmlaufzeit gesetzt und auch wieder gelöscht werden können.
Wenn es keinen Registrierte Funktion auf einen DMA-Event gibt, dann 
steht eben Null drin und dann darf ich auch nicht springen...

von kai (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Verwendest Du einen MSP430X? Dann ja, sonst nein, ein Funktionspointer
> ist auch nur 16 Bit groß.

Guckst du oben: MSP430F2619

von Christian R. (supachris)


Lesenswert?

Dann deaktivier doch beim Löschen des Funktionspointers einfach den 
entsprechenden DMA Kanal. Bringt ja dann auch nix.

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.