Forum: Mikrocontroller und Digitale Elektronik globaler counter funzt net wie erwartet


von S. L. (goldencue)


Lesenswert?

Hallo zusammen.

System:
ATMEGA8, STK500, AVR-Studio4, Gcc-Compiler

Code:
1
#define F_CPU    8000000UL
2
3
#include <avr/io.h>
4
#include <stdint.h>
5
#include <util/delay.h>
6
#include <stdlib.h>
7
#include <avr/interrupt.h>
8
9
10
11
/* HILFSDEFINITIONEN **********  */
12
13
#define CM_Val       3      /*Compare Match Value*/
14
#define MS_INKR     2800     /*Inkrement von ms zu 1 Sekunde */
15
16
17
#define BEEP_DDR    DDRB
18
#define BEEP_PORT   PORTB
19
#define BEEP_Pin    0
20
21
22
23
#define sbi(sfr, bit)   (sfr|=(1<<bit))
24
#define cbi(sfr, bit)   (sfr&=~(1<<bit)) 
25
26
/************** Globale Variablen ****************/
27
28
uint8_t    sec_takt;
29
uint16_t sys_ms_Inkr;
30
31
/******************* Initialisierung *************/
32
void ioinit()
33
{
34
  // Timer init/////////////////////////////// 
35
  TCNT0 = 256 - CM_Val;
36
  TCCR0 |= ((1 << CS00) | (1 << CS02)); //Prescale 1024
37
  // Overflow Interrupt
38
  TIMSK = (1 << TOIE0);
39
  //Timer init////////////////////////////////  
40
41
  DDRB = 0xFF;                 //Beep auf Ausgang
42
  PORTB = 0x00;                //Beep low setzen
43
        
44
}
45
46
/*Interupt von Timer*/
47
48
ISR(TIMER0_OVF_vect)
49
{
50
  
51
  if (sys_ms_Inkr >= MS_INKR){  
52
    sec_takt++;
53
    sys_ms_Inkr = 0;
54
    
55
  }
56
  else
57
  {
58
    sys_ms_Inkr++;
59
  }
60
  
61
  TCNT0 = 256 - CM_Val;
62
  
63
}
64
65
/****************************/
66
/*            MAIN      */
67
/****************************/
68
int main(void)  {
69
  
70
  //INIT
71
  ioinit();
72
  
73
  sec_takt = 0;
74
  sys_ms_Inkr = 0;
75
  
76
  sei();
77
78
while (1)
79
{  
80
81
    cbi(BEEP_PORT,1);
82
    _delay_ms(100);
83
    sbi(BEEP_PORT,1);
84
    _delay_ms(100);
85
86
    
87
    if(sec_takt == 1){
88
    sbi(BEEP_PORT,BEEP_PIN);
89
    }
90
    
91
    if(sec_takt >= 2){
92
    sec_takt = 0;      
93
    cbi(BEEP_PORT,BEEP_PIN);
94
    }
95
    
96
    
97
  
98
}    
99
}

Problem:

So funktioniert es nicht. Der Compiler sagt nix. Der Simulator muckt 
nicht. Der Timer läuft. Die sec_takt Variable wird wie erwartet 
inkrementiert. Die LED auf PINB1 blinkt im 100ms-Takt ( naja - in etwa 
jedenfalls ). Also läuft sowohl der Interrupt, als auch die Main-While. 
Nur funktioniert:
1
    if(sec_takt == 0){
2
         sbi(BEEP_PORT,BEEP_PIN);
3
    }
4
    
5
    if(sec_takt >= 1){
6
         sec_takt = 0;      
7
         cbi(BEEP_PORT,BEEP_PIN);
8
    }

zwar in der ISR ( oder einer von ihr aufgerufenen Routine ). Aber nicht 
in der Main-While.

Das verstehe ich nicht. Die sec_takt ist global und der Compiler sagt 
mir auch nix, in keiner der Optimize-Stufen.

Bitte helft mir. Sicher ist es ein Denkfehler.

Vielen Dank für eure Mühe!

von Peter II (Gast)


Lesenswert?

klassischer fehler:

variabeln die in der ISR und main verwendet werden müssen volatile sein.

von S. L. (goldencue)


Lesenswert?

ich werd es testen. Aber ich hatte auch in meinen bisherigen Steuerungen 
für diese Zwecke kein volatile und es war ok.

Trotzdem - ich teste

von S. L. (goldencue)


Lesenswert?

Tatsache. Danke danke danke! Ich weiss wofür volatile ist. Aber das es 
hier greift konnte ich mir nicht vorstellen.

von gordon51freeman (Gast)


Lesenswert?

Peter II schrieb:
> variabeln die in der ISR und main verwendet werden müssen volatile sein.

MÜSSEN? Seit wann? Habe bei mir in einer ISR div. globale Variabeln als 
normale int deklariert und funzt trotzdem (benutze sie auch in der 
main).


Gruss
Gordon

von Falk B. (falk)


Lesenswert?

@  Matthias T. (goldencue)

>Tatsache. Danke danke danke! Ich weiss wofür volatile ist. Aber das es
>hier greift konnte ich mir nicht vorstellen.

Jetzt weißt du es. Es ist aber immer noch ein Fehler drin. Dein 
Variablenzugiff ist NICHT atomar, muss er aber, siehe Interrupt.

Und NEIN, nur weil es 24h ohne Fehler läuft ist das KEIN Beweis, dass es 
OK ist, siehe Artikel.

Und lass den Käse mit dem Timer laden, das geht mit CTC beium AVR 
deutlich besser.

von Falk B. (falk)


Lesenswert?

@  gordon51freeman (Gast)

>> variabeln die in der ISR und main verwendet werden müssen volatile sein.

>MÜSSEN? Seit wann?

Seit es C gibt.

> Habe bei mir in einer ISR div. globale Variabeln als
>normale int deklariert

Kaum, denn das wären lokale Variablen, an die man ausserhalb der ISR gar 
nicht rankommt.

> und funzt trotzdem (benutze sie auch in der main).

Dann sit es Glück, dass der Optimizer das nicht soweit optimiert hat, 
dass es nicht mehr geht. Und nein, das ist kein Compilerfehler sondern 
ein Programmiererfehler.

von (prx) A. K. (prx)


Lesenswert?

Falk Brunner schrieb:
> Seit es C gibt.

Erst seit es ANSI-C gibt. Davor gab es kein volatile. Weil die Compiler 
noch nicht so agressiv optimierten war es anfangs nicht erforderlich.

von S. L. (goldencue)


Lesenswert?

Falk Brunner schrieb:
> Dein
> Variablenzugiff ist NICHT atomar, muss er aber, siehe Interrupt.

das stimmt. Ich hatte nur den Text minimiert und alle nicht zwingend 
fürs Problem notwendigen Zeilen entfernt. Danke aber für den Tip.

von Peter II (Gast)


Lesenswert?

Falk Brunner schrieb:
> Jetzt weißt du es. Es ist aber immer noch ein Fehler drin. Dein
> Variablenzugiff ist NICHT atomar, muss er aber, siehe Interrupt.

welchen zugriff meinst du überhaupt?

sys_ms_Inkr wird doch nur in der ISR verwendet?

von Falk B. (falk)


Lesenswert?

@  Peter II (Gast)

>> Jetzt weißt du es. Es ist aber immer noch ein Fehler drin. Dein
>> Variablenzugiff ist NICHT atomar, muss er aber, siehe Interrupt.

>welchen zugriff meinst du überhaupt?

if(sec_takt == 1){

von Karl H. (kbuchegg)


Lesenswert?

gordon51freeman schrieb:
> Peter II schrieb:
>> variabeln die in der ISR und main verwendet werden müssen volatile sein.
>
> MÜSSEN? Seit wann?

Eigentlich erst, seit dem es Compiler gibt, deren Optimizer 
Datenflussanylse betreiben.
Wenn man C naiv in Maschinencode übersetzt, braucht man es nicht. Aber 
so einen Compiler würde keiner haben wollen.


> Habe bei mir in einer ISR div. globale Variabeln als
> normale int deklariert und funzt trotzdem (benutze sie auch in der
> main).

Dann hast du noch nicht verstanden, wo das Problem liegt.

FAQ: Was hat es mit volatile auf sich

von Peter II (Gast)


Lesenswert?

Falk Brunner schrieb:
> @  Peter II (Gast)
>
>>> Jetzt weißt du es. Es ist aber immer noch ein Fehler drin. Dein
>>> Variablenzugiff ist NICHT atomar, muss er aber, siehe Interrupt.
>
>>welchen zugriff meinst du überhaupt?
>
> if(sec_takt == 1){

uint8_t    sec_takt;

damit ist es doch immer atomar, oder kommt gcc auf die idee die bits 
einzeln zu setzen?

von S. L. (goldencue)


Lesenswert?

hier stehts:

Variablen, auf die sowohl innerhalb wie auch außerhalb einer 
Interruptserviceroutine zugegriffen wird (schreibend oder lesend), 
müssen (ähnlich wie Hardwareregister) mit dem Schlüsselwort volatile 
(flüchtig) versehen werden, damit der C-Compiler berücksichtigen kann, 
dass diese Variablen jederzeit (durch das Auftreten des Interrupts) 
gelesen oder geschrieben werden können. Ansonsten würde der C-Compiler 
das regelmäßige Abfragen oder Beschreiben dieser Variablen ggf. 
wegoptimieren, da er nicht damit rechnet, dass auf die Variable auch 
"ohne sein Zutun" zugegriffen wird.

Zitat Gcc-Tur

:)

und atomar bedeutet, das ich jegliche "Bearbeitungen" von Interrupts 
z.B. unterbinden muss, WÄREND ich auf die Variable zugreife oder sie 
verändere. Wenn ich also if ( sec_takt == 1) abfrage, könnte ( bei nicht 
atomar ) der Interrupt den Wert der Variable ändern WÄREND ich auslese. 
Und das führt zu sporadischen Laufzeitfehlern.

von Peter II (Gast)


Lesenswert?

Matthias T. schrieb:
> und atomar bedeutet, das ich jegliche "Bearbeitungen" von Interrupts
> z.B. unterbinden muss, WÄREND ich auf die Variable zugreife oder sie
> verändere. Wenn ich also if ( sec_takt == 1) abfrage, könnte ( bei nicht
> atomar ) der Interrupt den Wert der Variable ändern WÄREND ich auslese.
> Und das führt zu sporadischen Laufzeitfehlern.

das ist halt das Problem wenn man text zitiert und nicht versteht was 
der grund ist.

Das gilt hier nicht für 8bit variablen ( bzw. für alle Variabeln die die 
CPU direkt verarbeiten kann)

von S. L. (goldencue)


Lesenswert?

Peter II schrieb:
> das ist halt das Problem wenn man text zitiert und nicht versteht was
> der grund ist.

das war nicht zitiert

von Peter II (Gast)


Lesenswert?

Matthias T. schrieb:
> das war nicht zitiert

dann hast du dir es falsch gemerkt.

> ändern WÄREND ich auslese.
> Und das führt zu sporadischen Laufzeitfehlern.
es kommt auch zu keinen laufzeitfehler sonder der verhalten ist nicht 
das was man erwartet.

eine 16bit variable wird auf einem 8bit system in 2 oder mehr schritten 
geändert, damit gibt es ungwollte zwischenwerte. Beim Vergleichen kann 
es dann zu unerwarteten ergebnissen kommen.

Bei einer 8bit Variable kann das hier aber nicht passieren.

Anderes währe wenn in der main etwas wie

wert++;

und in der isr auch

wert++

gemacht wird, dann können dabei schritte verloren gehen. Aber hier wird 
in der main nur auf 0 gesetzt und in der isr ++ gemacht. Dabei kann kein 
Fehler entstehen.

von Falk B. (falk)


Lesenswert?

@  Peter II (Gast)

>uint8_t    sec_takt;

>damit ist es doch immer atomar,

Ahhh stimmt, Tomaten auf den Augen. sys_ms_Inkr ist ja nur 16 Bit.

>oder kommt gcc auf die idee die bits
>einzeln zu setzen?

Eher nicht.

>gemacht wird, dann können dabei schritte verloren gehen. Aber hier wird
>in der main nur auf 0 gesetzt und in der isr ++ gemacht. Dabei kann kein
>Fehler entstehen.

Stimmt.

von S. L. (goldencue)


Lesenswert?

kannst du mir einen Tip geben, was du mit:

Falk Brunner schrieb:
> Und lass den Käse mit dem Timer laden, das geht mit CTC beium AVR
> deutlich besser.

meintest?

von Karl H. (kbuchegg)


Lesenswert?

Matthias T. schrieb:
> kannst du mir einen Tip geben, was du mit:
>
> Falk Brunner schrieb:
>> Und lass den Käse mit dem Timer laden, das geht mit CTC beium AVR
>> deutlich besser.
>
> meintest?


Siehe
*** FAQ: Timer ***

Dein Timer kann Modi! Der kann das alles auch ganz von alleine.

von S. L. (goldencue)


Lesenswert?

aber das habe ich! Ich habe den Compare Match Mode genutzt und lasse 
immer nur bis
1
TCNT0 = 256 - CM_Val;
 zählen.

Was meintest du genau?

von Karl H. (kbuchegg)


Lesenswert?

Matthias T. schrieb:
> aber das habe ich! Ich habe den Compare Match Mode genutzt

Wo genau hast du den Compare Match Mode benutzt?

> immer nur bis
1
TCNT0 = 256 - CM_Val;
 zählen.

genau das nennt sich Timer vorladen.
Und ist Quatsch. Das kann der Timer im CTC viel besser. Nämlich 
taktgenau.

>
> Was meintest du genau?

Lies den Link. Ich hab mir nicht umsonst die Mühe gemacht, da zumindest 
die häufigsten Timer Sachen im Detail runterzubrechen.

von S. L. (goldencue)


Lesenswert?

ok ich les nochmal!

von Thomas E. (thomase)


Lesenswert?

Matthias T. schrieb:
> System: ATMEGA8, STK500, AVR-Studio4, Gcc-Compiler

Karl Heinz Buchegger schrieb:
> Und ist Quatsch. Das kann der Timer im CTC viel besser. Nämlich
> taktgenau.
Aber nicht der, den er gerade benutzt. Das kann die alte Gurke im Timer0 
nicht, sondern nur in 1 und 2.

mfg.

von Sascha (Gast)


Lesenswert?

Peter II schrieb:
> uint8_t    sec_takt;
>
> damit ist es doch immer atomar, oder kommt gcc auf die idee die bits
> einzeln zu setzen?

Weißt du denn, ob deine Architektur uint8_ts atomar inkrementiert? Weißt 
du, ob dein Compiler die passende Instruktion aufruft?

Vielleicht ja. Aber portabel ist das dann dennoch nicht.

von (prx) A. K. (prx)


Lesenswert?

Sascha schrieb:

> Aber portabel ist das dann dennoch nicht.

Das wird er ohnehin nie sein können. Auf eine Maschine mit 
beispielsweise 4-Bit breiten Datenoperationen ist ein unvermeidlich 
Eigenschaften eines bestimmten AVR Controllers voraussetzender Code 
prinzipiell nicht portabel.

> Weißt du denn, ob deine Architektur uint8_ts atomar inkrementiert? Weißt
> du, ob dein Compiler die passende Instruktion aufruft?

Entweder er arbeitet byteweise, dann ist die Inkrementierung in einer 
normalen AVR-ISR atomar, oder der Compiler wurde nur geschrieben, um 
Anwender in den Irrsinn zu treiben.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Eckmann schrieb:
> Matthias T. schrieb:
>> System: ATMEGA8, STK500, AVR-Studio4, Gcc-Compiler
>
> Karl Heinz Buchegger schrieb:
>> Und ist Quatsch. Das kann der Timer im CTC viel besser. Nämlich
>> taktgenau.
> Aber nicht der, den er gerade benutzt. Das kann die alte Gurke im Timer0
> nicht, sondern nur in 1 und 2.

Ah. Hast du recht.

von Stefan W. (dl6dx)


Lesenswert?

Peter II schrieb:
> Bei einer 8bit Variable kann das hier aber nicht passieren.

Das unterstellt aber, dass der Compiler immer passenden Maschinencode 
erzeugt.

Auch wenn es heute so ist, ich würde mich nicht darauf verlassen.

Grüße

Stefan

von Peter II (Gast)


Lesenswert?

Stefan Wagner schrieb:
> Auch wenn es heute so ist, ich würde mich nicht darauf verlassen.

man sollte halt sein hardware kennen, sonst kann man überhaupt nicht 
sinnvoll programmieren.
Nur ein cli sli ringrum würde schon 200% overhead bedeuten, das muss nun 
wirklich nicht sein.

von Thomas E. (thomase)


Lesenswert?

Stefan Wagner schrieb:
> Das unterstellt aber, dass der Compiler immer passenden Maschinencode
> erzeugt.
Was soll er denn sonst machen? Unpassenden Code erzeugen?
Man kann Probleme, die keine sind, auch an den Haaren herbeiziehen.

mfg.

von S. L. (goldencue)


Lesenswert?

die Diskussion hat sich hier offenbar zu ner öffentlichen Debatte 
entwickelt :)

Mir soll es recht sein. Ich entwickle als Hobby ( muss also nicht alles 
im Detail wissen wie die Vollpros ). Allerdings entwickle ich nur AVR 
und habe nicht vor zu portieren.

Ich finde eure Unterhaltung sehr lehrreich und verfolge mit großem 
Interesse weiter. Lasst euch nicht stören :)

von Stefan W. (dl6dx)


Lesenswert?

Thomas Eckmann schrieb:
> Was soll er denn sonst machen? Unpassenden Code erzeugen?

Ja.

Irgendwann unterzieht jemand den Compiler einem Redesign, und schon ist 
die Annahme nicht mehr erfüllt.

Oder der Code wird auf eine andere Zielplattform portiert. Oder...

Wär mir zu blöd, plötzlich an "Altbaustellen" Fehler suchen zu müssen.

Grüße

Stefan

von Karl H. (kbuchegg)


Lesenswert?

Stefan Wagner schrieb:
> Thomas Eckmann schrieb:
>> Was soll er denn sonst machen? Unpassenden Code erzeugen?
>
> Ja.
>
> Irgendwann unterzieht jemand den Compiler einem Redesign, und schon ist
> die Annahme nicht mehr erfüllt.

Ähm, wir reden über ein Inkrement IN EINER ISR.
DIe ist per Def. atomar, solange die Interrupts nicht explizit innerhalb 
der ISR aufgedreht werden.


Ausserhalb einer ISR muss ein
  i++;
nicht atomar sein. Soweit hast du schon recht.


Ich glaube, ihr redet aneinander vorbei.
Stefan redet vom allgmeinen Fall und der Rest von einem Inkrement in 
einer ISR

von Stefan W. (dl6dx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Stefan redet vom allgemeinen Fall

So ist es! Ich hatte angenommen, dass es um den Code außerhalb der ISR 
geht.

Grüße

Stefan

von Thomas E. (thomase)


Lesenswert?

Stefan Wagner schrieb:
> Irgendwann unterzieht jemand den Compiler einem Redesign, und schon ist
> die Annahme nicht mehr erfüllt.
Ja nee is' klar.

mfg.

von (prx) A. K. (prx)


Lesenswert?

Thomas Eckmann schrieb:
> Ja nee is' klar.

Wahrscheinlich verwendet er grad einen Multiprozessor-AVR mit 
gemeinsamem Speicher, per FPGA dem Mega8 exakt nachempfunden. Wenn dann 
mehrere seiner Cores auf die Variable zugreifen, dann ists Essig mit 
atomarer Inkrementierung in der ISR. ;-)

von (prx) A. K. (prx)


Lesenswert?

Wer es gerne offiziell hat: C99 definiert via signal.h einen atomaren 
Datentyp sig_atomic_t, der entweder mindestens -127..+127 oder 
mindestens 0..255 umfasst. Wer es also auf maximale Portabilität anlegt, 
der verwendet diesen Typ und hat damit definiert atomaren Zugriff 
(freilich keine atomare Inkrementierung).

von Stefan W. (dl6dx)


Lesenswert?

Thomas Eckmann schrieb:
> Ja nee is' klar.

Jetzt mal halblang. Für einen derart sarkastischen Ton sehe ich keinen 
sachlichen Grund.

Ich redete über allgemeinen C-Code, nicht über den Sonderfall einer 
definitiv nicht unterbrechbaren ISR auf einer ganz bestimmten CPU.

Grüße

Stefan

von Thomas E. (thomase)


Lesenswert?

Stefan Wagner schrieb:
> Ich redete über allgemeinen C-Code, nicht über den Sonderfall einer
> definitiv nicht unterbrechbaren ISR auf einer ganz bestimmten CPU.
Ach so.

mfg.

von Walter S. (avatar)


Lesenswert?

Matthias T. schrieb:
> if(sec_takt >= 2){
>     sec_takt = 0;

ich stelle mir hier vor dass hier trotz 8-Bit Variable auch der 
Interrupt gesperrt werden muss.
Sonst kann doch zwischen Abfrage und Nullsetzen der Interrupt kommen und 
so theoretisch eine Sekunde verloren gehen. (Praktisch dann evtl. nicht 
unter der Voraussetzung dass die main-Schleife schnell genug durchlaufen 
wird)
Oder sehe ich das falsch?

von S. L. (goldencue)


Lesenswert?

ich kann lange nicht mehr viel dazu sagen. Aber ich finde die Diskussion 
hier wirklich toll. Auch wenn's hart zur Sache geht ;)...in anderen 
Threads steht sooviel Genörkel und Geningel.

Ich les gern weiter zu :) ...nur zu

von Peter II (Gast)


Lesenswert?

Walter S. schrieb:
> Sonst kann doch zwischen Abfrage und Nullsetzen der Interrupt kommen und
> so theoretisch eine Sekunde verloren gehen. (Praktisch dann evtl. nicht
> unter der Voraussetzung dass die main-Schleife schnell genug durchlaufen
> wird)
> Oder sehe ich das falsch?

ja und nein, die sekunde geht auch denn verloren wenn die main zu lange 
braucht. das muss nicht zwischen abfrage und 0 setzen passieren. Und 
diesen Fall verhindert auch ein atomic nicht.

von Walter S. (avatar)


Lesenswert?

da hast Du recht,
dann also doch die altbewährte Methode, nämlich Zähler nur im IRQ 
verändern, im main nur abfragen.

von Peter II (Gast)


Lesenswert?

Walter S. schrieb:
> da hast Du recht,
> dann also doch die altbewährte Methode, nämlich Zähler nur im IRQ
> verändern, im main nur abfragen.
nein das hilft hier auch nicht.

man müsste ihn nicht auf 0 setzen sonder 2 abziehen;


sec_takt -= 2;

jetzt hat man sich damit aber wirklich ein atomar problem eingehandelt. 
Da aber selbst ISR verloren gehen können, weil sie auch nur ein flag 
haben spielt das überhaupt keine rolle in der Praxis. Man muss es nur 
wissen und dafür sorgen das die main schnell genug ist.

von spontan (Gast)


Lesenswert?

Oder in ISR nur Flags setzen und in der Main die Zähler bedienen.

von Klaus (Gast)


Lesenswert?

spontan schrieb:
> Oder in ISR nur Flags setzen und in der Main die Zähler bedienen.

Wozu dann eine ISR, gleich das Interruptflag in Main auswerten.

MfG Klaus

von Falk B. (falk)


Lesenswert?

@  Walter S. (avatar)

>> if(sec_takt >= 2){
>>     sec_takt = 0;

>ich stelle mir hier vor dass hier trotz 8-Bit Variable auch der
>Interrupt gesperrt werden muss.
>Sonst kann doch zwischen Abfrage und Nullsetzen der Interrupt kommen und
>so theoretisch eine Sekunde verloren gehen. (Praktisch dann evtl. nicht
>unter der Voraussetzung dass die main-Schleife schnell genug durchlaufen
>wird)

Die MUSS so oder so schnell genug sein, dass zwischen zwei Veränderungen 
von sec_takt das Ganze einmal abgearbeitet wird. Sonst funktioniert es 
nicht, mit oder ohne atomaren Zugriff.

Allerding gibt es Konstellationen, wo es atomar sein muss, 
beispielsweise bei einem Software-FIFO, dort werden ja Lese- und 
Schreibzeiger sowie der Füllstand manipuliert.

>dann also doch die altbewährte Methode, nämlich Zähler nur im IRQ
>verändern, im main nur abfragen.

Das ist natürlich noch besser, vor allem wenn die Programme größer 
werden. Wenn da an mehreren Stellen eine Schreibzugriff auf solche 
Variablen gemacht wird, hat man schnell Chaos. UNd wenn man in der ISR 
dann direkt für die jeweiligen Ereignisse die Flags schon ausdekodiert 
und setzt, wird sowohl die Auswertung als auch das Handshake minimiert 
und gesichert. Denn dann kann z.B. die ISR prüfen, ob das FLag, das 
gerade gesetzt werden soll, schon gelöscht ist. Wenn nein, ist die 
main-Schleife zu lang oder irgendwo hängen geblieben und man kann einen 
Fehler ausspucken. Optimale Laufzeitprüfung!

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.