Counter in ISR unwirksam?

Gast #7257837
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
}
Gast #7257888
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
Gast #7257907
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
Gast #7257910
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!
#7257915
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
#7257922
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.
#7257938
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren