Forum: Mikrocontroller und Digitale Elektronik Interrrupt während logischer Überprüfung verhindern


von Silvio G. (technofreak)


Lesenswert?

Hallo

ich habe momentan ein kleines Problem mit folgendem Code
1
for (i_messung=0; i_messung<=durchlaufe-1; i_messung++)  //Anzahl der geforderten Durchläufe abarbeiten
2
{
3
  zaehlstart = 1;                    //Merker setzen damit Messung beginnen kann
4
  while ((ergebnis_da == 0) || (zaehlstart == 1))    //warten auf Fertigstellung der Messung
5
  {
6
    NOP();
7
  }
8
  zaehler_low = zaehler_byte2;            //oberen 8 Bit in Variable
9
  zaehler_low = zaehler_low << 8;            //nach oben schieben
10
  zaehler_low = zaehler_low + zaehler_byte1;      //unteren 8 Bit dazu
1
void interrupt timer0(void)
2
{
3
  if (zaehlstart == 1)
4
  {
5
    zaehler_reset_aktiv;                  //Hardwarezähler auf null
6
    zaehler_high = 0;                    //obere 16 bit auf Null
7
    zaehler_reset_inaktiv;                  //Zähler freigeben
8
    zaehler_messzeit = 0;                  //Zähler für Messzeit zurücksetzen
9
    ergebnis_da = 0;                    //Ergebnis verarbeitet, Flag zurücknehmen
10
    zaehler_enable;                      //Messsignale freigeben
11
    zaehlstart = 0;                      //Merker zurücksetzen da abgearbeitet
12
    bit_16 = 0;                        //Marker für Bit 16 auf Null
13
  }
14
15
  if ((zaehler_messzeit > messzeit) && (ergebnis_da == 0))  //Zeitdauer für Messung abgelaufen
16
  {
17
    zaehler_disable;                    //Messung stoppen
18
    ergebnis_da = 1;                    //Merker setzen
19
  }
20
21
  if (bit_16 == 0 && zaehler_16 == 1)              //Merker auf Null und Eingang auf Eins? (Wechsel von Low nach High)
22
  {

Es kommt ab und zu mal vor, dass genau zwischen den beiden Überprüfungen 
der while-Schleife der Interrupt ausgeführt wird. Das Problem ist dann, 
dass die Überprüfung von "ergebnis_da" vor dem Interrupt ein False 
geliefert hat und jetzt nach dem Interrupt aber auch "zaehlstart" ein 
False liefert und damit die Schleife verlassen wird. Meine einzige Idee 
wäre die beiden Überprüfungen während ausgeschaltetem Interrupt in eine 
weitere Variable zu stecken und die dann an dieser Stelle auszuwerten.

Gibt es evtl. auch eine andere Lösung?

Silvio

von STK500-Besitzer (Gast)


Lesenswert?

Reicht es nicht, nur zu überprüfen, ob "ergebnis_da" gesetzt ist?

von Silvio G. (technofreak)


Lesenswert?

Nee leider nicht, da "ergebnis_da" ja noch vom letzten Durchlauf gesetzt 
ist und erst im Interrupt bei "zaehlstart ==1" gelöscht wird. Wenn ich 
"ergebnis_da" als letzten Befehl der For-Schleife lösche, kann mir der 
Interrupt immer noch dazwischen funken und dann wird die zweite 
if-Abfrage im Interrupt aktiv und verhindert die korrekte Funktion.

Aber das Nachdenken über deine Frage hat mich auf eine Idee gebracht, 
die ich gerade teste. Wenn ich sowohl "ergebnis_da" als auch 
"zaehler_messzeit am Ende der For-Schleife auf Null setze und zusätzlich 
noch mal im Interrupt sollten die beiden eignetlich immer einen Wert 
haben, der die korrekte Funktion garantiert. Denn mehr als ein Interrupt 
sollte nicht zwischen

zaehler_messzeit = 0;
ergebnis_da = 0;

am Ende der Schleife und dem

zaehlstart = 1;

am Anfang der Schleife stattfinden.

Silvio

von Andreas M. (amesser)


Lesenswert?

Sowas macht man mit einer Statemachine:
1
enum {
2
  STATUS_NIX    = 0,
3
  STATUS_W_START,
4
  STATUS_W_ENDE,
5
  STATUS_FERTIG,
6
};
7
8
...
9
volatile uint8_t zaehler_status = STATUS_NIX;
10
...
11
12
for (i_messung=0; i_messung<=durchlaufe-1; i_messung++)
13
{
14
  /* warten bis zähler bereit - braucht man hier wohl nicht*/
15
  while((zaehler_status != STATUS_FERTIG) &&
16
        (zaehler_status != STATUS_NIX)) NOP();
17
18
  /* starten */
19
  zaehler_status = STATUS_W_START;
20
21
  /* warten auf ende*/
22
  while (zaehler_status != STATUS_FERTIG)
23
    NOP(); /* Warum hier nicht sleep? */
24
25
26
void interrupt timer0(void)
27
{
28
  switch(zaehler_status)
29
  {
30
  case STATUS_W_START:
31
    zaehler_reset_aktiv;
32
    zaehler_high = 0;
33
    zaehler_reset_inaktiv;
34
    zaehler_messzeit = 0;
35
    zaehler_enable;
36
    bit_16 = 0;
37
    zaeler_status = STATUS_W_ENDE;
38
    break;
39
  case STATUS_W_ENDE:
40
    if (zaehler_messzeit > messzeit)
41
    {
42
      zaehler_disable;
43
      zaehleR_status = STATUS_FERTIG;
44
    }
45
    break;
46
  }
47
48
  ...

von Karl H. (kbuchegg)


Lesenswert?

Silvio G. schrieb:

> Aber das Nachdenken über deine Frage hat mich auf eine Idee gebracht,
> die ich gerade teste. Wenn ich sowohl "ergebnis_da" als auch
> "zaehler_messzeit am Ende der For-Schleife auf Null setze und zusätzlich
> noch mal im Interrupt sollten die beiden eignetlich immer einen Wert
> haben, der die korrekte Funktion garantiert. Denn mehr als ein Interrupt
> sollte nicht zwischen

zaehlstart könnte die angedachte Aufgabe übernehmen.

zaehlstart == 0   nichts tun
zaehlstart == 1   mit der Messung beginnen
zaehlstart == 2   Messung im Gange
zaehlstart == 3   Messung fertig


Damit weiß die ISR was zu tun ist, bzw kann umgekehrt der Hauptschleife 
mit einer einzigen Variablen signalisieren, dass eine Messung fertig 
ist.

Du hast da sowieso schon zu viele Flags, die sich alle irgendwie 
gegenseitig beeinflussen und irgendwelche Aussagen treffen. Bereinige 
das erst mal.

von Peter II (Gast)


Lesenswert?

Andreas Messer schrieb:
> while (zaehler_status != STATUS_FERTIG)
>     NOP(); /* Warum hier nicht sleep? */

wozu überhaupt NOP?

while (zaehler_status != STATUS_FERTIG) {}; macht das gleich und sogar 
schneller.

Und auch hier ist das Problem vorhanden:

while((zaehler_status != STATUS_FERTIG) &&
        (zaehler_status != STATUS_NIX) NOP();

wenn der Status beim ersten vergleich STATUS_NIX ist und sich dann auf 
STATUS_FERTIG ändert, dann schlägt die Prüfung fehl. Mit 2 Variabeln 
geht es nicht, es darf nur ein vergleich geben. Oder wirklich IRQ 
abschalten.

von Silvio G. (technofreak)


Lesenswert?

Habe meine Idee jetzt mal getestet, scheint zu funktionieren. Manchmal 
hilft es ja doch wenn ein anderer einen wieder auf einen schon 
verworfenen Gedanken zurückbringt und man nochmal genauer überlegt ob es 
so gehen kann.

Besten Dank

Silvio

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:

> wenn der Status beim ersten vergleich STATUS_NIX ist und sich dann auf
> STATUS_FERTIG ändert, dann schlägt die Prüfung fehl.

Ja, ok.
Dann dreht er eine Ehrenrunde in der while Schleife zu viel. Im nächsten 
Durchgang hat ers dann.

von Andreas M. (amesser)


Lesenswert?

Peter II schrieb:
> Und auch hier ist das Problem vorhanden:
>
> while((zaehler_status != STATUS_FERTIG) &&
>         (zaehler_status != STATUS_NIX) NOP();
>
> wenn der Status beim ersten vergleich STATUS_NIX ist und sich dann auf
> STATUS_FERTIG ändert, dann schlägt die Prüfung fehl. Mit 2 Variabeln
> geht es nicht, es darf nur ein vergleich geben. Oder wirklich IRQ
> abschalten.

Und wo ist jetzt das Problem? Sinn dieser Schleife ist es solange zu 
blockieren bis der Status eben entweder  _NIX oder _FERTIG ist. Außerdem 
gibt es nirgends einen Übergang von _NIX nach _FERTIG oder 
andersherum...

von Peter II (Gast)


Lesenswert?

Andreas Messer schrieb:
> Und wo ist jetzt das Problem? Sinn dieser Schleife ist es solange zu
> blockieren bis der Status eben entweder  _NIX oder _FERTIG ist.
richtig, und diese code kann das nicht zu 100%

> Außerdem
> gibt es nirgends einen Übergang von _NIX nach _FERTIG oder
> andersherum...
noch nicht, spätestens wenn er den übergang einbaut hat er vergessen das 
es dann bei der abfrage schief geht.


Solange es nur ein NOP ist was ausgeführt werden soll mag das noch 
gehen, wenn eine aktion ausführt wird geht so eine abfrage schief. Warum 
nicht gleich richitg machen?

while(1){
  uint8_t aktStatus = zaehler_status;
  if ( aktStatus == STATUS_FERTIG || aktStatus == STATUS_NIX {
    break;
  }

das ganze sollte sogar schneller sein, weil es nicht 2mal auf eine 
volatile variabel zugreifen muss.

von Andreas M. (amesser)


Lesenswert?

Peter II schrieb:
>> Außerdem
>> gibt es nirgends einen Übergang von _NIX nach _FERTIG oder
>> andersherum...
> noch nicht, spätestens wenn er den übergang einbaut hat er vergessen das
> es dann bei der abfrage schief geht.

Und welchen Sinn würde dieser Übergang im Kontext der gewünschten 
Funktionalität haben?

> Solange es nur ein NOP ist was ausgeführt werden soll mag das noch
> gehen, wenn eine aktion ausführt wird geht so eine abfrage schief. Warum
> nicht gleich richitg machen?
>
> while(1){
>   uint8_t aktStatus = zaehler_status;
>   if ( aktStatus == STATUS_FERTIG || aktStatus == STATUS_NIX {
>     break;
>   }

Das sehe ich ein...

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.