Also ersteinmal vorweg. Ich programmiere einen MPSF2619.
In diesem Programm wird eine ISR nach Timerüberlauf aufgerufen. In diese
wird der AD Wandler gestartet und der aktuelle AD Wert in ein Array
geschrieben. Dies darf allerdings nicht öfters wie 60-mal geschehen.
Danach soll das "komplette" Programm beendet werden.
Soweit klappt auch alles, bis auf das sich der Zählwert der Variable
jedesmal auf Null setzt wenn ich die ISR aufrufe. Habe die Zählvariable
schon global als static definiert.
Das seltsame an der Sache ist, das es im Simulator funktioniert aber auf
dem Prozesseor nicht.
Hoffe die Angaben sind ausreichend um mich bei meinem Problem zu
unterstützen.
Danke ersteinmal für die schnelle Antwort.
Zwischendurch war sie es kurzzeitig. Die Kombination aus static und
volatile habe ich allerdings noch nicht getestet.
entweder in der funktion static definieren wenn nur diese funktion die
variable benutzt,
oder in main global definieren, dann können alle funktionen darauf
zugreifen.
global & static weis nicht ob es da nicht einen nesty gibt und die
Variable automatisch zurückgesetzt wird?
am besten testroutine und testmain um die routine isoliert zu testen
dann in zielprogramm einbinden
wie wäre es du zeigtest mal so ein testmain mit testroutine. dann könnte
jemand dein vorgehen auf schlüssigkeit oder fehler prüfen.
Du hast doch erzählt, die Variable sei global! In Deinem Programm ist
sie es aber gar nicht (Dein Programm enthält keine einzige globale
Variable)! volatile macht auch nur bei globalen Variablen überhaupt
Sinn.
Vielleicht solltest Du Dich mal über die Unterschiede von globalen und
funktionslokalen Variablen erkundigen...
Was soll das Programm tun?
'i' wird nie zurückgesetzt, und zusammen mit 'array[i] = ADC12MEM0' gibt
das dann einen amoklaufenden Pointer.
Zudem wird 'anz' nur einmal hochgezählt (und nirgends deklariert).
@ Johannes M.:
Also der Unterschied zwischen Global und lokal ist mir bekannt. Hatte
das kurz vorm posten nochmal geändert.Sorry für die Verwirrung.
@Lothar Miller:
Der Sinn und Zweck sei erst einmal dahingestellt. Mir geht es
Hauptsächlich darum i nach verlassen der ISR zu erhalten. Und im
Simulator tut es ja auch. Nur wenn ich den FET Debugger nutzesetzt er i
immer auf Null sobald er die ISR betritt.
Also ich habe mich für die zweite variante entschieden und noch den
Zeiger entfernt, da dieser keinerlei Funktion hat.
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
static int i;
i++;
ADC12CTL0 |= ENC + ADC12SC; //Wandlung starten
//TACTL |= MC1; //Timer starten
P1OUT ^= 0x01;
CCR0 +=0xF400;
array[i] = ADC12MEM0;
if(i ==60)
{ i=0;
}
zaehl();
}
Ausser in der ISR kommt mein i jetzt nirgens mehr vor aber es will
dennoch nicht.
OK habs hinbekommen jetzt tut es was es soll, wobei mir die lösung nicht
ganz klar ist. hab eig nur die Abfrage auf 60 in den main teil genommen.
static int i;
void main (void) {
if(i==0){
P1DIR |=0x01;
//TimerA_Init
TACCTL0 = CCIE; //Capture/compate Int enable
TACCR0 =0x3FFF; //11kHz
//*************Achtung, nur zu Testzwecken TASSEL_2*************
TACTL = TASSEL_2 + MC_2 + ID_0 +TAIE; //ACLK, divided 1, Continuous
mode, Int enable
TACTL |= MC1; //Timer starten
//ADC12_Init
ADC12CTL0 = ADC12ON; //AD Wandler wird eingeschaltet
ADC12CTL1 = SHS_0 + SHP + ADC12DIV_0 + ADC12SSEL_2 + CONSEQ_0;
//sampling Time, Clock divider=1, single conversion
ADC12MCTL0 = SREF_0 + INCH_2; } //Channel 2
//ADC12CTL0 |= ADC12SC + ENC; //Wandlung starten
_BIS_SR(GIE);
while(1);
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
ADC12CTL0 |= ENC + ADC12SC; //Wandlung starten
//TACTL |= MC1; //Timer starten
P1OUT ^= 0x01;
CCR0 += 0x3FFF;
i++;
main();
}
wenn mir jetzt noch jemand erklären kann was da jetzt genau der
Unterschied ist, ist mein Tag gerettet.
wuddy wrote:
> wenn mir jetzt noch jemand erklären kann was da jetzt genau der> Unterschied ist, ist mein Tag gerettet.
Dein letzter Code ist von hinten durchs Knie in die Brust geschossen. Es
mag eine Zeit lang funktionieren, aber 99% der Programmierer schütteln
sich.
main() wieder innerhalb der ISR aufrufen ist IMHO ein No-Go. Du erzeugst
damit eine Rekursion, die nie aufgelöst wird und die dir wahrscheinlich
mit einem Stacküberlauf als Programmfehler endet.
Auch wird erste Interrupt formal gesehen nie verlassen, da die ISR nicht
verlassen wird. Der Programmzeiger kommt nicht zum } hinter dem dortigen
main(). (Ich habe hierbei nicht geprüft, ob _BIS_SR(GIE); den aktuellen
Interrupt löscht)
Ein "normaler" Programmierer würde in main() den Timer initialisieren
und starten und dann in eine endlose Hauptarbeitsschleife anstossen.
In der ISR würde ggf. eine Aktion ausgeführt (z.B. Hochzählen einer
globalen, volatilen Variable) und ggf. der Timer neu initialisiert.
Hm..ja ok, das ist wirklich ziemlich konfus gemacht. Aber wie sieht es
aus wenn das ganz selber als Unterprogramm zu realisieren ist und nach
erreichen der 60 Werte ins eigentliche Hauptprogramm gesprungen werden
soll?
Kann es sein das der Fehler wo anders liegt. Folgendes Szenario:
Ich debugge das ganze im Stepbetrieb. Komme bis zur while() und warte
dann dort bis der Timer die gewünschte Zahl erreicht hat.Von Hand stelle
ich dann das TAR-Reg auf den gewünschten Wert. Infolgedessen wird
Interrupt ausgelöst und Sprung in die ISR. Soweit, sogut. Weiter im
Stepbetrieb bis zur letzten Klammer der ISR }. Nach Ausführen der
Klammer springt der Debugger in RUN und es kommt kein INT mehr. Erst
wenn ich ihn stoppe und von Hand erneut einen INT auslöse.
Da ist das nicht ganz nachvollziehen kann habe ich diese ganze "main" -
Springerrei eingebau.
main() // das Hauptprogramm
Ist üblicherweise mit warten und auf Ereignisse und Aufrufen von
Unterprogrammen (Funktionen und Routinen) beschäftigt, welche ihrerseits
nach abarbeiten ihres jobs ihren status an main zurückmelden.
ISR sind ereignisbasierte Routinen, welche dazu dienen ein zeitlich
nicht vorhersehbares Ereignis zu erfassen oder eben bei timer IRQ
regelmäsig das laufende Programm zu unterbrechen um notwendige
Aktualisierungen anzustoßen. Üblicher weise setzen sie ein Flag damit
das Hauptpprgram eine passende Subroutine aufrufen kann. Diese erledigt
dann den eigentlichen Job und kehrt anschließend nach main zurück.
Wäre es möglich das der TimerA falsch initialisiert ist und so die
Interrupts nicht richtig auslösen?
TimerA soll im Compare Mode und Continuous mode laufen. Grundsätzlich
ist mir µC Programmieren bekannt da ich schon mit PIC und ARM gearbeitet
hab. Mit MSP habe ich allerdings erst seit 3 Wochen zu tun.
wird auch noch anders. Die eigentliche Arbeit kommt jetzt erst noch :/
Das eben war nur Vorgeplänkel. Na toll und das hat so Kopfzerbrechen
bereitet.
Danke nochmal für die schnelle und ausführliche Hilfe!
Ich habe das Programm am Freitag noch einmal auf Herz und Nieren
getestet und debugged und das Problem besteht nachwievor. Mir ist aber
beim debuggen aufgefallen, dass er nach der ISR nicht in die while geht
und wartet sondern die main komplett neu durchgeht.
1
voidmain(void){
2
3
//if(i==0){
4
WDTCTL=WDTPW+WDTHOLD;
5
P1DIR|=0x01;
6
//TimerA_Init
7
TACCTL0=CCIE;//Capture/compate Int enable
8
TACCR0=50000;//11kHz
9
//*************Achtung, nur zu Testzwecken TASSEL_2*************
10
TACTL=TASSEL_2+MC_2+ID_0+TAIE;//ACLK, divided 1, Continuous mode, Int enable
11
//TACTL |= MC1; //Timer starten
12
13
//ADC12_Init
14
ADC12CTL0=ADC12ON;//AD Wandler wird eingeschaltet
15
ADC12CTL1=SHS_0+SHP+ADC12DIV_0+ADC12SSEL_2+CONSEQ_0;//sampling Time, Clock divider=1, single conversion
16
ADC12MCTL0=SREF_0+INCH_2;//} //Channel 2
17
18
//ADC12CTL0 |= ADC12SC + ENC; //Wandlung starten
19
_BIS_SR(GIE);
20
while(1);
21
22
}
23
24
#pragma vector=TIMERA0_VECTOR
25
__interruptvoidTimer_A(void)
26
27
{
28
staticinta;
29
ADC12CTL0|=ENC+ADC12SC;//Wandlung starten
30
TACTL|=MC1;//Timer starten
31
P1OUT^=0x01;
32
CCR0+=50000;
33
a++;
34
}
Mir ist jetzt allerdings nicht mehr so ganz klar wie und wo ich die
zählvariable definieren soll, damit beide Funktionen drauf zugreifen
können und sie auch ändern können.
> Mir ist jetzt allerdings nicht mehr so ganz klar wie und wo ich die> zählvariable definieren soll, damit beide Funktionen drauf zugreifen> können und sie auch ändern können.
Die Zählvariable scheint Dein 'a' zu sein. Anstelle es in der ISR als
static zu deklarieren, musst Du es nur außerhalb der ISR und außerhalb
von main als volatile deklarieren.
> Mir ist aber beim debuggen aufgefallen, dass er nach der ISR> nicht in die while geht und wartet sondern die main komplett> neu durchgeht.
Dann geht da wohl irgendwas schief.
> TACTL |= MC1; //Timer starten
Bist Du Dir sicher, daß diese Zeile in die Timer-ISR gehört?
> CCR0 += 50000;
Willst Du das? Nicht TACCR0?
Habe jetzt festgestellt das der Interrupt auslöst sobalt der Wert im
Compare Reg erreicht ist, was ja auch gewünscht. Aber sobald das TAR von
0xFFFF nach 0x0000 wechseln sollte Schwachsinn passiert und nach
unbestimmter Zeit wieder die main von vorn aufgerufen wird.
OK jetzt weiß ich wo der Fehler ist. Sobald ein Überluaf von FFFF->0000
stattfindet wird ein zweiter, "nicht definierter" Interrupt ausgelöst,
der den Programmzeiger ins Nirvana zeigen lässt. Anschließend wird die
main "neugestartet" Allerdings ist mir nicht so ganz klar warum der
Inhalt der Variablen dann verloren geht. Bestimmt wird einfach alles neu
initiallisiert, was dann auch die Register zurücksetzt.
Meine Frage wäre jetzt wie ich den zweiten, nicht erwünschten, Interrupt
abschalte.
> Meine Frage wäre jetzt wie ich den zweiten,> nicht erwünschten, Interrupt abschalte.
Du darfst den nicht einschalten, ich würde auf diese Zeile tippen:
1
TACTL=TASSEL_2+MC_2+ID_0+TAIE;//ACLK, divided 1, Continuous mode, Int enable
Hm...ja...OK...auf dem falschen Fuss erwischt. Leider ist mir die
Funktion des Watchdog nicht ganz geläufig. Weiß nur das das auch eine
Art Timer ist.
Hab WDTCTL = WDTPW + WDTHOLD jetzt mal auskommentiert aber TAIFG wird
dennoch gesetzt.
TAIE gibt nur den Overflow-Interrupt frei, die CCR-Interrupts sind in
den passenden TACCTLx Registern zu finden (Bit CCIE). TAIFG wird bei
Überlauf immer gesetzt, egal, ob der Interrupt freigegeben ist oder
nicht, TAIE schaltet nur den IRQ zum Interrupt-Controller durch.
Watchdog auskommentieren ist auch schlecht, dann startet dein Programm
in regelmäßigen Abständen neu. Alle Variablen usw. sind dann natürlich
weg, weil quasi ein Kaltstart vorgenommen wird. Lass die Zeile mit
WDTHOLD mal schön drin.
Außerdem nimmst du den falschen Interrupt-Vektor. TIMER_A0 ist nur für
den Overflow. Die CCR-Interrupts teilen sich den TIMER_A1 Vektor, da
musst du dann auch das TAIV auswerten, um festzustellen, welcher CCR-Int
jetzt kam.
Du solltest dich erst mal eingehend mit dem User Guide beschäftigen, da
steht alles wichtige drin.
So der Watchdog ist jetzt wieder drin und der Overflow ist "deaktivier".
Sieht bis jetzt auch gut aus. Das Ding läuft durch und erhöht ständig
den Wert, so wie es auch soll.
Bedanke mich recht herzlich für die ausführliche und schnelle Hilfe.
Den Rat mit dem User Guide werd ich mir zur Brust nehmen. Hab es auf die
gute, alte "learnig by doing" Variante versucht und bin, wie man dem
Thread entnehmen kann, kläglich gescheitert :D
wuddy wrote:
> OK auf den ersten Blick sieht es gut aus, jetzt wird nur ein INT> ausgelöst.> Will mal versuchen den Fehler zu rekonstruieren.> Durch
1
TACTL=TASSEL_2+MC_2+ID_0+TAIE;
habe ich alle
> Interrups freigegeben, also auch den Overflow.
Wie wäre es mit einer Interrupt Vektor Tabelle?
Du definierst für jeden aktivierten Interrupt eine ISR.
Aber das sicherste ist, nur die Interrupts zu aktivieren, die man
wirklich verwenden möchte. Dazu nützt es das Handbuch (Manual) des
Controllers beim Programmieren zur Hand zu haben. Das erspart viel Ärger
;-)
Was mir an deinem Code auch noch aufgefallen ist, du verlässt dich beim
Initialisieren deiner Variablen immer auf den Compiler. Zur Sicherheit
würde ich aber immer ein
1
=0
schreiben, damit man sicher ist, dass die Variable einen definierten
Wert hat.
MFG
Ja ok den Wink mit dem Handbuch habe ich verstanden :)
Das mit dem initialisiren ist schon klar. Das ganze diente nur zu
Testzwecken. Das eigentliche Programm ist dann ausführlicher und besser
durchdacht, natürlich auch mit entsprechenden Vorabdefinitionen. Aber
danke für den Hinweiß, hätte es später dann bestimmt vergessen.