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
__interruptvoidDMA_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!
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.
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.
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.
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
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.
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ß.
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
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.
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...
Rufus Τ. Firefly schrieb:> Verwendest Du einen MSP430X? Dann ja, sonst nein, ein Funktionspointer> ist auch nur 16 Bit groß.
Guckst du oben: MSP430F2619