Forum: Mikrocontroller und Digitale Elektronik volatile Array


von Dieter S. (Gast)


Lesenswert?

Asche auf mein Haupt, aber irgendwie habe ich es immer noch nicht 
verstanden, wie ich mit Arrays im Interrupt umgehen muss...

Das Array mit den Zählern für die einzelnen Parameter ist (noch) nicht 
volatile, der allgemeine Zähler schon.
Folgende Ausgangssituation für Timeouts:
[1]
1
#define FAIL_ABSMAX    200
2
3
//global:
4
uint16_t FailCnt[3]={0,0,0};
5
volatile uint8_t FailGen;
6
7
void IRQ(void)
8
{
9
  //timeout
10
  for (uint8_t i=0; i<3; i++) {
11
    if (FailCnt[i]<FAIL_ABSMAX) {
12
      FailCnt[i]++;
13
    }
14
    else {
15
      Input[i]=0;
16
      FailCnt[i]=0;
17
    }
18
  }
19
20
  if (FailGen<FAIL_ABSMAX) {
21
    FailGen++;
22
  }
23
}
24
25
26
void function(void) {
27
  //...receive something and set flag
28
  if (received==1) { //received successfully
29
    FailGen=0;
30
    
31
    if (Param==0) {
32
      ...
33
      Input[0]=...;
34
      FailCnt[0]=0;
35
    }
36
    else if (Param==1) {
37
      //...
38
      Input[1]=...;
39
      FailCnt[1]=0;
40
    }
41
    else if (Param==2)) {
42
      //...
43
      Input[2]=...;
44
      FailCnt[2]=0;
45
    }
46
  }
47
  
48
  if (FailGen>FAIL_ABSMAX) {
49
    //timeout -> do something
50
    //...
51
  }
52
}

Der allgemeine Zähler FailGen sollte so OK sein.
Bei dem Array muss ich wohl auch etwas tun, aber ein
[2]
1
volatile uint16_t FailCnt[3]={0,0,0};
hilft wohl nicht, so wie ich es verstanden habe...
Außerdem müsste
[3]
1
FailCnt[0]=0;
wohl auch noch atomic gemacht werden, da FailCnt ein uint16_t ist, oder?

Muss ich jetzt wirklich mit extra volatile Flags arbeiten, oder gibt es 
noch andere Wege?

[4]
1
//global:
2
uint16_t FailCnt[3]={0,0,0};
3
volatile uint8_t FailCnt0Reset;
4
volatile uint8_t FailCnt1Reset;
5
volatile uint8_t FailCnt2Reset;
6
volatile uint8_t FailGen;
7
8
void IRQ(void)
9
{
10
  //timeout
11
  if (FailCnt0Reset)
12
      FailCnt[0]=0;
13
  if (FailCnt1Reset)
14
      FailCnt[1]=0;
15
  if (FailCnt2Reset)
16
      FailCnt[2]=0;
17
  
18
  for (uint8_t i=0; i<3; i++) {
19
    if (FailCnt0Reset)
20
      FailCnt[0]=0;
21
    if (FailCnt[i]<FAIL_ABSMAX) {
22
      FailCnt[i]++;
23
    }
24
    else {
25
      Input[i]=0;
26
      FailCnt[i]=0;
27
    }
28
  }
29
30
  if (FailGen<FAIL_ABSMAX) {
31
    FailGen++;
32
  }
33
}
34
35
36
void function(void) {
37
  //...receive something and set flag
38
  if (received==1) { //received successfully
39
    FailGen=0;
40
    
41
    if (Param==0) {
42
      ...
43
      Input[0]=...;
44
      FailCnt0Reset=1;
45
    }
46
    else if (Param==1) {
47
      //...
48
      Input[1]=...;
49
      FailCnt1Reset=1;
50
    }
51
    else if (Param==2)) {
52
      //...
53
      Input[2]=...;
54
      FailCnt2Reset=1;
55
    }
56
  }
57
  
58
  if (FailGen>FAIL_ABSMAX) {
59
    //timeout -> do something
60
    //...
61
  }
62
}

Das erscheint mir sehr unelegant...

Spielt es eigentlich für die mögliche falsche Optimierung eine Rolle, ob 
die Abfrage (if <) im IRQ sitzt und im "normalen" Programm nur eine 
Zuweisung gemacht wird, oder andersherum?

von Karl H. (kbuchegg)


Lesenswert?

Dieter S. schrieb:

> hilft wohl nicht, so wie ich es verstanden habe...

hilft nicht - WOGEGEN?


> wohl auch noch atomic gemacht werden, da FailCnt ein uint16_t ist, oder?

korrekt


> Spielt es eigentlich für die mögliche falsche Optimierung eine Rolle, ob
> die Abfrage (if <) im IRQ sitzt und im "normalen" Programm nur eine
> Zuweisung gemacht wird, oder andersherum?

In der Theorie nicht, in der Praxis schon.
Denn mit Eintritt in die ISR geht der Compiler sowieso nicht davon aus, 
dass irgendeine Variable bereits in einem Register sitzen würde. Und da 
im Normalfall auf einem AVR eine ISR nicht von einer anderen ISR 
unterbrochen wird, spielt es keine Rolle, wenn der Compiler die 
Variablenverwendung innerhalb der ISR in der Registerbelegung optimiert. 
Eine ISR ist aus Compilersicht eine normale Funktion und deren 
Seiteneffekte müssen auf jeden Fall beim Beendigen der Funktion 
abgeschlossen sein. D.h. wenn die ISR durch ist, sind auch die Variablen 
im SRAM mit Sicherheit wieder alle up to date.

Anders sieht es aber in der Hauptschleife in main() aus.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Dieter S. schrieb:
> Asche auf mein Haupt, aber irgendwie habe ich es immer noch nicht
> verstanden, wie ich mit Arrays im Interrupt umgehen muss...

Mit Arrays geht man in der ISR genauso um wie mit anderen Variablen.

Dieter S. schrieb:
> Bei dem Array muss ich wohl auch etwas tun, aber ein
> [2]volatile uint16_t FailCnt[3]={0,0,0};
> hilft wohl nicht, so wie ich es verstanden habe...

Was ist dieses "es", das du so verstanden hast? Im Prinzip muß alles, 
auf das du sowohl von innerhalb als auch von außerhalb der ISR 
zugreifst, volatile sein.

> Außerdem müsste
> [3]FailCnt[0]=0;
> wohl auch noch atomic gemacht werden, da FailCnt ein uint16_t ist, oder?

Ja.

> Muss ich jetzt wirklich mit extra volatile Flags arbeiten, oder gibt es
> noch andere Wege?

Was sind "extra volatile Flags"?

von Dieter S. (Gast)


Lesenswert?

Karl Heinz schrieb:
>> hilft wohl nicht, so wie ich es verstanden habe...
>
> hilft nicht - WOGEGEN?

In einem Beitrag habe ich gelesen, dass das volatile vor dem Array nur 
etwas bringt, wenn man das ganze Array übergibt, nicht aber, wenn man 
wie ich, auf einzelne Elemente zugreift.

von Dieter S. (Gast)


Lesenswert?

Rolf Magnus schrieb:
>> Muss ich jetzt wirklich mit extra volatile Flags arbeiten, oder gibt es
>> noch andere Wege?
>
> Was sind "extra volatile Flags"?

So wie unter [4] gezeigt.
Ich ändere in der Hauptschleife das Array nicht, sondern nur noch im 
IRQ.
In der Hauptschleife ändere ich nur eine volatile Variable, die ich dann 
auch im IRQ auswerten kann.

von Dieter S. (Gast)


Lesenswert?

Karl Heinz schrieb:
>> Spielt es eigentlich für die mögliche falsche Optimierung eine Rolle, ob
>> die Abfrage (if <) im IRQ sitzt und im "normalen" Programm nur eine
>> Zuweisung gemacht wird, oder andersherum?
>
> In der Theorie nicht, in der Praxis schon.
> Denn mit Eintritt in die ISR geht der Compiler sowieso nicht davon aus,
> dass irgendeine Variable bereits in einem Register sitzen würde. Und da
> im Normalfall auf einem AVR eine ISR nicht von einer anderen ISR
> unterbrochen wird, spielt es keine Rolle, wenn der Compiler die
> Variablenverwendung innerhalb der ISR in der Registerbelegung optimiert.
> Eine ISR ist aus Compilersicht eine normale Funktion und deren
> Seiteneffekte müssen auf jeden Fall beim Beendigen der Funktion
> abgeschlossen sein. D.h. wenn die ISR durch ist, sind auch die Variablen
> im SRAM mit Sicherheit wieder alle up to date.
>
> Anders sieht es aber in der Hauptschleife in main() aus.

Könnte der Compiler in Beispiel [1] nicht das FailCnt[0]=0; 
wegoptimieren, weil er denkt, dass diese Variable eh nie benutzt wird?
Alles weitere passiert ja im IRQ...

von Dieter S. (Gast)


Lesenswert?

Ich bin jetzt immer noch nicht ganz sicher...
Reicht volatile uint16_t FailCnt[3]={0,0,0}; doch, oder muss ich doch so 
Verenkungen wie unter [4] machen, oder ganz anders?

von Dieter S. (Gast)


Lesenswert?

push
Kann ich noch einen kleinen Hinweis von den Leuten mit Ahnung bekommen?

von Rolf Magnus (Gast)


Lesenswert?

Dieter S. schrieb:
> Reicht volatile uint16_t FailCnt[3]={0,0,0}; doch, oder muss ich doch so
> Verenkungen wie unter [4] machen, oder ganz anders?

Volatile muß es schon sein. Warum du glaubst, diese Flags zu brauchen, 
weiß ich allerdings nicht. Worauf du halt noch achten mußt, ist, wie du 
schon sagt, daß der Zugriff aus dem Hauptprogramm atomar erfolgen muß, 
also mit gesperrten Interrupts. Das hat aber alles eigentlich nichts 
damit zu tun, daß es sich um ein Array handelt. Und im Bezug darauf:

Dieter S. schrieb:
> In einem Beitrag habe ich gelesen, dass das volatile vor dem Array nur
> etwas bringt, wenn man das ganze Array übergibt, nicht aber, wenn man
> wie ich, auf einzelne Elemente zugreift.

Was meinst du denn mit "das ganze Array übergeben"? Wie und wohin soll 
man das denn als ganzes übergeben?

von Dieter S. (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Warum du glaubst, diese Flags zu brauchen,
> weiß ich allerdings nicht.

Ich hatte unten genannten Link gelesen, was mich aber eigentlich mehr 
verwirrt hat.
Beitrag "Re: Warnung bei volatile (AVR-GCC)"
Daraus ist dann die Frage mit den volatile Flags enstanden...

von Karl H. (kbuchegg)


Lesenswert?

Ich versteh immer noch nicht, warum du denkst, dass (mal abgesehen vom 
atomar-Problem) ein
>
1
> volatile uint16_t FailCnt[3]={0,0,0};
2
>
> hilft wohl nicht, so wie ich es verstanden habe...

Nichts helfen soll.
In dem Codeteil, den du im Eröffnungsposting angegeben hast, ist das 
doch die Lösung. Was soll da jetzt 'nicht helfen'?
Es macht genau das, was man erwartet und was man auch braucht: Es teilt 
dem Compiler mit, dass er bei FailCnt immer auf die Werte zugreifen muss 
und sich keine Optimierungen erlauben darf.
Genau das, was man zur Kommunikation Hauptprogramm/ISR auch braucht.

von Dieter S. (Gast)


Lesenswert?

Ok, ok.
Ich hatte nach "volatile array" gesucht und u.a. den von mir eben 
geposteten Beitrag gefunden. Ich habe wohl nicht verstanden worum es da 
geht, aber das hat bei mir den Eindruck erweckt, dass es bei Arrays eben 
nicht so einfach mit einem volatile davor getan ist.
Aber wenn dem so ist, umso besser.
Vielen Dank für die Klarstellung.

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.