Forum: Mikrocontroller und Digitale Elektronik Counter in ISR unwirksam?


von Alfred (Gast)


Lesenswert?

Hallo Zusammen,

ich bin neu in der MC-Programmierung mit MSP430 und CCS und war schon 
ca. 10Jahre nicht mehr auf dieser Seite - also alles vergessen was mit 
MC zu tun hat... :-)
Wollte ein LED im Sekundentakt blinken lassen, diese Sekunde solle mir 
ein Interrupt zählen.
Leider blinkt nur das LED, das im Interrupt getoggelt wird, nicht das 
LED, das in der while(1) getoggelt wird.

Kann mir jemand sagen, warum?

Grüße,
Alfred
1
#include <msp430.h>
2
#include <stdint.h>
3
#include <stdlib.h>
4
5
volatile uint8_t Cnt = 0;
6
volatile uint8_t Do_1s_Action = 0;
7
8
int main(void)
9
{
10
    WDTCTL = WDTPW | WDTHOLD;                     // Stop WDT
11
12
    BCSCTL3 |= LFXT1S_2;
13
    TACCR0=1200;                                 //10 interuppts/s
14
    TA0CCTL0 |= CCIE;
15
    TA0CTL |= TASSEL_1 | MC_1;
16
17
    P1DIR |= BIT0;                                // P1.0 output
18
    P1OUT |= BIT0;                                // P1.0 high
19
    P1DIR |= BIT6;                                // P1.0 output
20
    P1OUT |= BIT6;                                // P1.0 high
21
22
    __bis_SR_register(LPM0_bits | GIE);
23
24
    while(1)
25
    {
26
        if (Do_1s_Action == 1)
27
        {
28
            // Toggle LED
29
            P1OUT ^= BIT0;
30
            Do_1s_Action = 0;
31
        }
32
    }
33
}
34
35
// Timer A0 interrupt service routine
36
#pragma vector = TIMER0_A0_VECTOR
37
__interrupt void Timer_A (void)
38
{
39
    if (++Cnt >= 10)   // every 1sec
40
    {
41
        P1OUT ^= BIT6;
42
        Do_1s_Action = 1;
43
        Cnt = 0;
44
    }
45
46
    TACCTL0&=~CCIFG;
47
}

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?


von MaWin (Gast)


Lesenswert?

Abgesehen von dem nicht-atomischen Abfragen und Rücksetzen von 
Do_1s_Action, was hier aber nicht das primäre Problem sein sollte, 
sollte das eigentlich funktionieren.

von Xerxes (Gast)


Lesenswert?

Wo wird der Wert von Cnt vergrössert?

von Nop (Gast)


Lesenswert?

Xerxes schrieb:
> Wo wird der Wert von Cnt vergrössert?

In Zeile 39.

von Ein Kommentar (Gast)


Lesenswert?

Wenn alle verwickelten Fehler ausgeschlossenen sind, muss es ein 
trivialer Fehler sein.

Wenn du vor der Schleife P1OUT BIT0 auf 0 setzt, bleibt dann die LED 
aus?

von Alfred (Gast)


Lesenswert?

Vielen Dank für Eure schnellen Antworten!

Leider hatte ich bis jetzt keinen Erfolg... :-(

@Irgend W.:
Was genau habe ich falsch gemacht? Habe ich volatile falsch angewandt?

@ MaWin :
"nicht atomisch"?

Ein Kommentar schrieb:
> Wenn alle verwickelten Fehler ausgeschlossenen sind, muss es ein
> trivialer Fehler sein.
>
> Wenn du vor der Schleife P1OUT BIT0 auf 0 setzt, bleibt dann die LED
> aus?

Nein, die LED ist immer an! Sowohl wenn ich Bit0 vor while(1) oder auch 
innerhalb while(1) schreibe... wie kann das sein?

Grüße,
Alfred

von MaWin O. (mawin_original)


Lesenswert?

Alfred schrieb:
> @ MaWin :
> "nicht atomisch"?

Der Interrupt kann (theoretisch) zwischen der Hauptschleifen-Abfrage des 
Flags und dem Hauptschleifen-Rücksetzen des Flags ausgeführt werden. 
Damit würde ein Setzen des Flags im Interrupt verloren gehen.
Allerdings wird das bei dir hier in diesem speziellen Fall aufgrund des 
langsamen Timings nicht passieren.

von MaWin O. (mawin_original)


Lesenswert?

Alfred schrieb:
> Nein, die LED ist immer an! Sowohl wenn ich Bit0 vor while(1) oder auch
> innerhalb while(1) schreibe... wie kann das sein?

Ich würde einmal sagen du hast ein elektrisches Problem, oder dein 
Controller muss noch besonders konfiguriert werden zur Verwendung dieses 
Pins.

Tausche doch einmal Bit 0 und 6 in Hauptschleife und Interrupt.

von MaWin O. (mawin_original)


Lesenswert?

Ach und:
Ist BIT0 und BIT6 überhaupt eine Bitmaske? Oder ist es eine Bitnummer?

Wenn es eine Bitnummer ist, dann musst du so schreiben:

xxx ^= 1 << BIT0
xxx ^= 1 << BIT6

von Falk B. (falk)


Lesenswert?

MaWin O. schrieb:
> Ach und:
> Ist BIT0 und BIT6 überhaupt eine Bitmaske?

Ja, denn das ist ein MSP430.

> Oder ist es eine Bitnummer?

Nö.

von Falk B. (falk)


Lesenswert?

Alfred schrieb:
> Nein, die LED ist immer an! Sowohl wenn ich Bit0 vor while(1) oder auch
> innerhalb while(1) schreibe... wie kann das sein?
>
> Grüße,

Hat die LED jemals gblinkt? Ist die wirklich richtig angeschlossen? Dein 
Code sieht OK aus, auch volatile ist drin, passt.
Lass mal die LED ohne ISR blinken, einfach mit old school delay.

von Alfred (Gast)


Lesenswert?

Hi MaWin,

nochmal danke für Deine schnelle Reaktion!

Ich konnte das Problem lösen, indem ich das LPM0_bits nicht mehr 
schreibe (Zeile 22), jetzt geht alles wie gewünscht, hat was mit dem 
LowPower-Mode zu tun - muss ich nachher mal genauer nachlesen... (habs 
nur durch ausprobieren rausbekommen).

MaWin O. schrieb:
> Alfred schrieb:
>> @ MaWin :
>> "nicht atomisch"?
>
> Der Interrupt kann (theoretisch) zwischen der Hauptschleifen-Abfrage des
> Flags und dem Hauptschleifen-Rücksetzen des Flags ausgeführt werden.
> Damit würde ein Setzen des Flags im Interrupt verloren gehen.
> Allerdings wird das bei dir hier in diesem speziellen Fall aufgrund des
> langsamen Timings nicht passieren.

Das ist ein interessanter Aspekt! Hättest Du spontan ein Beispiel für 
mich, wie diesen Effekt umgehen kann? (Ich möchte ja ein längeres 
Programm schreiben, als nur eine LED blinken zu lassen ;-))

Grüße,
Alfred

von Alfred (Gast)


Lesenswert?

Hi Falk,

ja, das hatte ich auch probiert, ging auch nicht!
Dachte mir: das kann nicht sein!
Dann hatte ich den global interrupt deaktiviert, braucht man dann ja 
nicht -> dann gings!
...so bin auch auf die Lösung mit LPMO gekommen...

Danke Dir trotzdem!

von Falk B. (falk)


Lesenswert?

Alfred schrieb:
> nochmal danke für Deine schnelle Reaktion!
>
> Ich konnte das Problem lösen, indem ich das LPM0_bits nicht mehr
> schreibe (Zeile 22), jetzt geht alles wie gewünscht, hat was mit dem
> LowPower-Mode zu tun - muss ich nachher mal genauer nachlesen... (habs
> nur durch ausprobieren rausbekommen).

Das ist eine Besonderheit beim MSP430. Der kann in einem der LPMs 
arbeiten, eine ISR ausführen und bei deren Verlassen den automatisch 
wieder einschalten, sodaß die Hauptschleife NIE ausgeführt wird! Will 
man das nicht, muss man in der ISR die LPM-Bits ausschalten. Frag mich 
jetzt aber nicht wie das genau geht, hab ich vor 15 Jahren das letzte 
Mal gemacht 8-0

>> Der Interrupt kann (theoretisch) zwischen der Hauptschleifen-Abfrage des
>> Flags und dem Hauptschleifen-Rücksetzen des Flags ausgeführt werden.

Auch praktisch.

>> Damit würde ein Setzen des Flags im Interrupt verloren gehen.

Nö. Denn das Löschen erfolgt NUR bei vorher erkanntem Flag!

>> Allerdings wird das bei dir hier in diesem speziellen Fall aufgrund des
>> langsamen Timings nicht passieren.

> Das ist ein interessanter Aspekt!

Nö, es ist ein Irrtum.

> Hättest Du spontan ein Beispiel für
> mich, wie diesen Effekt umgehen kann? (Ich möchte ja ein längeres
> Programm schreiben, als nur eine LED blinken zu lassen ;-))

Man kann das Löschen das Flags am Ende der Abarbeitung von Funktionen 
nutzen, ob das Timing schnell genug ist.

https://www.mikrocontroller.net/articles/Multitasking#Verbesserter_Ansatz_mit_Timer

von MaWin O. (mawin_original)


Lesenswert?

Falk B. schrieb:
> Nö. Denn das Löschen erfolgt NUR bei vorher erkanntem Flag!
>
>>> Allerdings wird das bei dir hier in diesem speziellen Fall aufgrund des
>>> langsamen Timings nicht passieren.
>
>> Das ist ein interessanter Aspekt!
>
> Nö, es ist ein Irrtum.

Ach komm Falk.
Rede keinen Unsinn.
Es könnte ein Flagsetzen verloren gehen. Das wird in diesem Konkreten 
Fall nur verhindert, weil das Setzen eine sehr langsame Periode hat und 
in dieser Zeit die Hauptschleife den kritischen Bereich sicher verlassen 
wird.

Alfred schrieb:
> Hättest Du spontan ein Beispiel für
> mich, wie diesen Effekt umgehen kann?

Deaktivieren der Interrupts vor Abfrage und Re-aktivierung der 
Interrupts nach Abfrage + Rücksetzen des Flags.

von Falk B. (falk)


Lesenswert?

MaWin O. schrieb:
>> Nö, es ist ein Irrtum.
>
> Ach komm Falk.
> Rede keinen Unsinn.

Eben. Das tue ich nicht ;-)

> Es könnte ein Flagsetzen verloren gehen.

Ach sooo, jetzt verstehe ich. Ja, könnte. Ist aber wie bei JEDEM 
Interrupt! Wenn die Beantwortung zu lange dauert, werden diese 
verschluckt.

> Das wird in diesem Konkreten
> Fall nur verhindert, weil das Setzen eine sehr langsame Periode hat und
> in dieser Zeit die Hauptschleife den kritischen Bereich sicher verlassen
> wird.

Stimmt.

> Deaktivieren der Interrupts vor Abfrage und Re-aktivierung der
> Interrupts nach Abfrage + Rücksetzen des Flags.

Das löst das Problem nicht wirklich. Denn auch hier kann eine zu lange 
Bearbeitungszeit dazu führen, daß eben die Periodendauer des Timers 
überschritten wird. Und dann verhindert man mit der ISR-Sperre die 
Ausführung der ISR. OK, EINMAL funktioniert der Trick, aber wenn die 
Zeit NOCH länger wird, schon nicht mehr.

Alles in allem MUSS man durch Simulation oder Messung sicher stellen, 
daß die Funktionen zwischen Erkennen des ISR-Flags und dem Löschen 
SICHER kleiner als die Timerperiode ist. Man kann das Flag auch gleich 
nach dem Erkennen löschen. Aber auch dann muss die Bearbeitung von 
wasauchimmer() so kurz sein, daß die nächste Abfrage des Flag das erneut 
gesetzte Flag erkennt und nicht mehrere Timerdurchläufe überspringt! 
Steht alles seit Ewigkeiten im Artikel Interrupt.

https://www.mikrocontroller.net/articles/Interrupt#Zusammenfassung

: Bearbeitet durch User
von MaWin O. (mawin_original)


Lesenswert?

Falk B. schrieb:
> Denn auch hier kann eine zu lange
> Bearbeitungszeit dazu führen

Völlig richtig. Ich wollte hier lediglich in einem Nebensatz auf ein 
Problem hinweisen und nicht das Problem vollumfänglich lösen. (Man 
könnte zum Beispiel zählen statt Flag setzen).

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.