Moin,
ich versuch nun seit 2 Stunden den Timer zum laufen zu bekommen.
Im grunde möchte ich nur den Timer mit 20ms durchlaufen lassen und bei
einem Interrupt die Bits von P2.0 bis P2.2 "durchshiften". D.h. das
immer nur einer von diesen Portpins aktiv ist.
Mein Problem, er springt anscheinend nicht in die ISR des Timers. Ich
weiß nicht ob er nicht anläuft oder ob er einfach nich reinspringt etc.
pp.
Evtl. kann mir auch jemand erklären wie ich mir das anzeigen lassen
könnte beim Debugging (wie in Keil zum beispiel, allerdings ist dies ja
ein Simulator.)
Anbei mein Code und schonmal danke für die Hilfe.
1
/*
2
* main.c
3
* Ein Programm zum Ansteuern und durchlaufen lassen von einem RGB-SMD-LED-Streifen.
4
* Die ausgänge gehen über einen 1k-Widerstand auf die Basis eines BC538, welcher die
Danke für die Codebeispiele ;)
Mir ist beim ansehen aufgefallen, dass ich ein Interrupt nicht aktiviert
habe (CCIE im TACCTL0 / Compare-Capture Register 0).
Allerdings ist es wohl jener welcher, den ich dafür benötige.
Ich benutze CCS5 von TI, muss mich da wohl einfach nur noch richtig
reinfuchsen, damit ich evtl besser Debuggen kann.
Mfg
Edit:
Jetzt springt er zwar in die ISR rein, allerdings is der Programmcounter
nach dem RETI bei 0xFFFF und bleibt dort hängen.
Is irgendwie nich mein Tag heute
Edit2:
"This is typically the case when you have an itnerrupt enabled but no
ISR for it. The CPU will then fetch the default 0xffff from the
interrupt vector table as the ISR's address and jumps there. Of course
there is no source code available or disassembly possible. It's a crash
situation. And yes, the MSP won't forgive this mistake :)" <---
Ich hatte in TACTL noch das TAIE-Interrupt gesetzt, von dem ich ja
dachte es löst den Interrupt für den Vector TIMER0_A0_VECTOR aus.
TAIE geht doch auf den TIMER0_A1_VECTOR und nicht auf den
TIMER0_A0_VECTOR.
Also CCS5 von TI macht's einem doch echt leicht, sowas in Zukunft auch
selbst nachzuschauen, siehe Anlage.
Hallo Sören, geht's nun mit dem TIMER0_A1_VECTOR bei Dir?
Ich selber hatte bis eben noch mit dem Assembler-Beispiel in slau144i,
Kapitel 12.2.6.3 zu kämpfen. Ich arbeite mit Funktions-Vektoren, da ich
die Interrupt-Pointer für die Zustandsautomaten verbiege:
Beitrag "Interrupt-Pointer verbiegen für Zustandsautomat"
1
#include<msp430.h>
2
voiddummy(){volatileinttest=TA0IV;}// für asm("…") - ToDo: kann man TA0IV geschickter definieren?
3
…
4
#pragma vector=TIMER0_A1_VECTOR
5
__interruptvoidTIMER0_TAIV_ISR(void){
6
// aus slau144i, Kapitel 12.2.6.3
7
asm(" ADD &TA0IV,PC ; Add offset to Jump table");
8
asm(" RETI ; Vector 0: No interrupt");
9
asm(" JMP CCIFG_1_HND ; Vector 2: TACCR1");
10
asm(" JMP CCIFG_2_HND ; Vector 4: TACCR2");
11
asm(" RETI ; Vector 6: Reserved");
12
asm(" RETI ; Vector 8: Reserved");
13
asm(" RETI ; Vector 10: TAIFG Flag");
14
asm(" RETI ; Back to main program");
15
asm("CCIFG_2_HND: ; Vector 4: TACCR2");
16
asm(" CALL &I2C_State+0 ; Task starts here");
17
asm(" RETI ; Back to main program");
18
asm("CCIFG_1_HND: ; Vector 2: TACCR1");
19
asm(" CALL &PWM_State+0 ; Task starts here");
20
asm(" RETI ; Back to main program");
21
}
Für TAIE musst Du natürlich das Vector-10-RETI durch ein entsprechendes
CALL ersetzen.
Ich hoffe, ich konnte helfen.
Wenn einer 'ne Idee hat, wie ich/wir wir das "void dummy()" vermeiden
können, nur zu. Es dient nur dazu das Symbol "TA0IV" für die erste
Assembler-Zeile bekannt zu machen.
Torsten C. schrieb:> Ich arbeite mit Funktions-Vektoren, da ich> die Interrupt-Pointer für die Zustandsautomaten verbiege:
Hmmm, ich sehe keine Interrupt-Pointer und keine verbogenen
Funktions-Vektoren. In dem Beispiel von TI geht es um etwas anderes:
Für den einen Timer-Interrupt gibt es mehrere Quellen. Um die Quelle
heraus zu finden, muss man das TAIV auswerten. Üblicherweise macht man
das mit einer switch case in der ISR.
Beim TAIV gibt es eine Besonderheit und zwar wird nur der Wert des
höchstwertigen Interrupts eingetragen. Die Werte der einzelnen
Interrupt-Quellen im TAIV unterscheiden sich immer um zwei (0, 2, 4,
...). Dies macht TI sich zum Nutzen und addiert einfach den Wert vom
TAIV auf den PC. Der Code direkt dahinter ist eine Tabelle, in der die
gewünschten Ausführungen für die jeweilige Interrupt-Quelle stehen. Je
Eintrag stehen nur 2 Byte zur Verfügung, also steht da ein Jump oder
gleich RETI.
Also besser du nimmst die switch case und nutzt die die Option für nur
"gerade" Werte.
Torsten C. schrieb:> Wenn einer 'ne Idee hat, wie ich/wir wir das "void dummy()" vermeiden> können, nur zu.
Ach ja, TAIV ist nur eine Adresse. Du kannst den Wert auch direkt
angeben.
nur mal so schrieb:> … TAIV ist nur eine Adresse. Du kannst den Wert auch direkt angeben.
Danke für die gute Erklärung. Genau das macht das o.g.
Assembler-Beispiel aus dem Datenblatt slau144i in Kapitel 12.2.6.3.
nur mal so schrieb:> Also besser du nimmst die switch case und nutzt die die Option für nur> "gerade" Werte.
Switch Case dauert bei der Ausführung deutlich länger als
1
ADD&TA0IV,PC
Interrupts sollen "so kurz wie möglich" das laufende Programm
unterbrechen, da sonst andere Interrupts verloren gehen könnten, es sei
denn, man erlaubt Interrupts in Interrupts. Das ist aber egal, wenn man
keine zeitkritischen Funktionen hat.
Switch Case ist in C natürlich einfacher zu programmieren.
"&I2C_State" und "&PWM_State" sind übrigens die o.g. "verbogenen
Interrupt-Pointer", aus dem verlinkten Beitrag, die vorher wie folgt
deklariert sind und je nach Zustand der "State-Machine" andere Werte
haben:
1
void(*PWM_State)(void)=&PWM_State_idle;
2
void(*I2C_State)(void)=&I2C_State_idle;
@"nur mal so": kann man TA0IV auch geschickter definieren, als durch die
Dummy-Funktion? Der Assembler kennt sonst das Symbol nicht.
VG Torsten
Torsten C. schrieb:> Interrupts sollen "so kurz wie möglich" das laufende Programm> unterbrechen
Sehe ich auch so, aber
Torsten C. schrieb:> asm(" CALL &I2C_State+0 ; Task starts here");
ist dann eher suboptimal, denn vom CALL will das Programm mit RET wieder
zurück. Und solange ist die ISR noch aktiv, solange bis sie auf ein RETI
läuft.
Torsten C. schrieb:> nur mal so schrieb:>> … TAIV ist nur eine Adresse. Du kannst den Wert auch direkt angeben.Torsten C. schrieb:> Der Assembler kennt sonst das Symbol nicht.
Hinter TAIV oder TA0IV steckt die Adresse eines Registers. Schau ins
Datenblatt.
Torsten C. schrieb:> "&I2C_State" und "&PWM_State" sind übrigens die o.g. "verbogenen> Interrupt-Pointer"
Dat sind käne verbojene Intrups!!!
In der ISR vom Timer nutzt du eine Sprungtabelle, um weiteren Code
auszuführen oder eine Funktion (auch über Funktionszeiger) aufzurufen.
Richtig wäre direkt die Interrupt-Vektortabelle zu verändern und dort
einen Sprung auf die aktuell gewünschte ISR einzutragen. Das geht
natürlich im Flash nicht so gut, schnell und häufig.
Ein guter Compiler erzeugt eine effektive Sprungtabelle automatisch.
Beim MSP430 solltest du einmal nach "even_in_range" suchen. Die
Bedeutung wird meist im Zusammenhang mit dem TAIV erklärt.
nur mal so schrieb:> Torsten C. schrieb:>> Der Assembler kennt sonst das Symbol nicht.>> Hinter TAIV oder TA0IV steckt die Adresse eines Registers. Schau ins> Datenblatt.
Der Assembler kennt das Datenblatt nicht. ;-) Die Zeile sorgt dafür,
dass dem Assembler das Label bekannt wird. Hat niemand eine schlauere
Lösung? Egal, so geht's auch.
nur mal so schrieb:> Dat sind käne verbojene Intrups!!! … Das geht> natürlich im Flash nicht so gut, schnell und häufig.
OK, es sind keine "verbogenen Interruppts", es ist eine Art,
Interrupt-Vektoren zu verbiegen, ohne in's Flash zu schreiben. Die
meisten Alternativen wurden im
Beitrag "Interrupt-Pointer verbiegen für Zustandsautomat" ^^ ja Diskutiert.
> Beim MSP430 solltest du einmal nach "even_in_range" suchen.
Ich hab's mal ausprobiert, so richtig optimerit sieht das noch nicht
aus. "ADD &TA0IV,PC" wäre optimal. Vielleicht mache ich noch was falsch:
Torsten C. schrieb:> Irgendwie kann "even_in_range" das SFR TA0IV nicht vertragen.
Hmmmh, ich weiss nicht wie du das anstelltst, aber mein IAR macht ohne
große Optimierung aus
1
voidfoo(void);
2
void(*funcarr[3])(void)={foo,foo,foo};
3
4
#pragma vector = TIMER0_A1_VECTOR
5
__interruptvoidTIMER0_A1_VECTOR_ISR(void)
6
{
7
switch(__even_in_range(TAIV,10)){
8
case0x02:// TACCR1 CCIFG
9
funcarr[0]();
10
break;
11
case0x04:// TACCR2 CCIFG
12
funcarr[1]();
13
break;
14
case0x0A:// TAIFG
15
funcarr[2]();
16
break;
17
default:
18
while(1);// trap CPU on strange TAIV
19
}
20
}
das
1
\0000000D12PUSH.WR13
2
\0000020C12PUSH.WR12
3
\0000040F12PUSH.WR15
4
\0000060E12PUSH.WR14
5
\00000810522E01ADD.W&0x12e,PC
6
\`?<JumptableforTIMER0_A1_VECTOR_ISR>_0`:
7
\00000C123CJMP??TIMER0_A1_VECTOR_ISR_1
8
\00000E043CJMP??TIMER0_A1_VECTOR_ISR_2
9
\0000100A3CJMP??TIMER0_A1_VECTOR_ISR_3
10
\0000120F3CJMP??TIMER0_A1_VECTOR_ISR_1
11
\0000140E3CJMP??TIMER0_A1_VECTOR_ISR_1
12
\0000160A3CJMP??TIMER0_A1_VECTOR_ISR_4
13
\??TIMER0_A1_VECTOR_ISR_2:
14
\0000189212....CALL&funcarr
15
\??TIMER0_A1_VECTOR_ISR_0:
16
\00001C3E41POP.WR14
17
\00001E3F41POP.WR15
18
\0000203C41POP.WR12
19
\0000223D41POP.WR13
20
\0000240013RETI
21
\??TIMER0_A1_VECTOR_ISR_3:
22
\0000269212....CALL&funcarr+2
23
\00002AF83FJMP??TIMER0_A1_VECTOR_ISR_0
24
\??TIMER0_A1_VECTOR_ISR_4:
25
\00002C9212....CALL&funcarr+4
26
\000030F53FJMP??TIMER0_A1_VECTOR_ISR_0
27
\??TIMER0_A1_VECTOR_ISR_1:
28
\000032FF3FJMP??TIMER0_A1_VECTOR_ISR_1
Da braucht man keinen ASM mehr.
Und nebenbei kannst du sehen, wie anstatt TAIV die Register-Adresse aus
dem Datenblatt eingesetzt wurde.