Forum: Mikrocontroller und Digitale Elektronik Timerinterrupt und UART


von Praktikant (Gast)


Lesenswert?

Hallo.

Ich habe mal eine Frage.

Mein Programm hat eine Uhr eingebaut. Diese wird über einen Timer im 
Sekundentakt inkrementiert. Der Timer ist so eingestellt, dass er jede 
Sekunde einen Interrupt auslöst. In der Routine wird nur ein Flag 
gesetzt.
Die Zeit wird in der while(1)-Schleife gebildet wenn sich das Flag 
ändert.

Zugegeben ist die Uhr nicht die Genauste, aber für meine Zwecke reicht 
das.

Nun frage ich in regelmäßigen Abständen (alle 2 Sekunden) drei Werte von 
dem µC über die RS232 mit 9600 Baud ab.

Wenn ich nach einem Tag eine Auswertung in Excel mache, dann kann man 
erkennen, dass irgendwann ein Wert unterschlagen wurde.

Meine Vermutung ist, dass der Timerinterrupt dazwischenhaut. Wie mache 
ich es am geschickteste, dass dieses Problem nicht mehr auftritt?

von Mike (Gast)


Lesenswert?

Praktikant schrieb:
> Meine Vermutung ist, dass der Timerinterrupt dazwischenhaut. Wie mache
> ich es am geschickteste, dass dieses Problem nicht mehr auftritt?

Îndem du die serielle Kommunikation auch über eine ISR laufen läßt und 
in der ISR nicht "stundenlang" den µC blockierst.

> Zugegeben ist die Uhr nicht die Genauste,
Und was fehlt ihr?
Beitrag "Die genaue Sekunde / RTC"

von Praktikant (Gast)


Lesenswert?

Mike schrieb:
> in der ISR nicht "stundenlang" den µC blockierst

?Versteh ich nicht. Was willst du mir sagen?

Mike schrieb:
>> Zugegeben ist die Uhr nicht die Genauste,
> Und was fehlt ihr?
> Beitrag "Die genaue Sekunde / RTC"

Den Artikel kenn ich. Für mein Projekt aber nicht von Belang.


Welche serielle Kommunikation soll ich in einer ISR ablaufen lassen?
Senden, oder Empfangen?

von Thomas E. (thomase)


Lesenswert?

Praktikant schrieb:
> Meine Vermutung ist, dass der Timerinterrupt dazwischenhaut. Wie mache
> ich es am geschickteste, dass dieses Problem nicht mehr auftritt?
Vielleicht.
Deswegen beschreib nicht in Prosa, was das Programm machen soll, sondern 
zeig deinen Quelltext als angehängtes C-File. Sonst kann man nur im 
Nebel stochern.

mfg.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

1. Tue dir einen Gefallen und rechne die Uhr in der ISR fertig. Dann 
musst du nicht im Hauptprogramm oder der Anzeige Routine auch noch an 
der Uhr rumfummeln.
Uhren sind ein simples 60:60:24 Getriebe und in ein paar Zeilen 
erledigt.

2. Wenn du die USART per Receive Interrupt betreibst, ist sicher 
gestellt, das sie sofort am Ende eines etwaigen anderen Interrupts 
behandelt wird.
 Wenn dein MC im Hauptprogramm rumtrödelt, wird sie sogar sofort 
gezündet und empfängt sicher das Zeichen. Entweder zeigst du per Flag, 
das da was neues im Buffer ist, oder du machst den ganzen Stringkram in 
der ISR. Das hängt davon ab, wie komplex die Auswertung ist.

von Praktikant (Gast)


Lesenswert?

Entschuldigung für die späte Reaktion, aber mir ist gestern was 
dazwischen gekommen. Sonst hätte ich mich schon eher gemeldet.

Hier der Auszug meines Quelltextes:
Die While Schleife:
1
    while(1)
2
    {
3
    
4
    //Timer 1 Auswertung
5
    if(bSekunde==1)  //Nur bearbeiten, wenn der Sekunden-Interrupt ausgelöst hat
6
    {
7
      bSekunde=0;  //Flag zurücksetzen
8
      sekunde++;
9
      
10
      if(State.bPumpOnOff==1){Laufzeit++;}    //Laufzeit der Pumpe in Sekunden hochzählen
11
      if(State.bTimerDeltaT==1){ZeitDeltaT++;}//Laufzeit der delta T Erfassung in Sekunden hochzählen
12
      
13
      if(sekunde == 60)
14
      {
15
        minute++;
16
17
        if(State.bPumpDelay==1){ZeitPumpDelay++;}    //Laufzeit der Pumpe in Minuten hochzählen
18
        sekunde = 0;
19
        LCD_String(1,13,itoa(minute,Zeit,10));
20
      }
21
      if(minute == 60)
22
      {
23
        stunde++;
24
        minute = 0;
25
        LCD_String(1,10,itoa(stunde,Zeit,10));
26
        LCD_String(1,13,"  ");
27
      }
28
      if(stunde == 24)
29
      {
30
        stunde = 0;
31
        LCD_String(1,10,"  ");
32
        LCD_String(1,10,"0");
33
      }
34
35
    Temperature1=DS18B20_Read_Addressed_Temperature(rom1,byte);
36
    Temperature2=DS18B20_Read_Addressed_Temperature(rom2,byte);
37
    }
38
39
    if(sekunde%2==0)    //Alle 2 Sekunden die Temperatur ausgeben
40
    {
41
      LCD_String(3,13,dtostrf(Temperature1,3,1,buf));
42
      LCD_String(4,13,dtostrf(Temperature2,3,1,buf));
43
      LCD_String(2,13,dtostrf(Temperaturold,3,1,buf));
44
    }    
45
    
46
    
47
    if((Temperature1>Temperaturold) && State.bPumpDelay==0)  //Nur machen wenn ein neuer Pumpzyklus möglich ist und die Temperatur größer ist als der letzte Wert
48
    {
49
      State.bTimerDeltaT=1;
50
    }
51
    if(Temperature1<=Temperaturold)
52
    {
53
      ZeitDeltaT=0;
54
      Temperaturold=Temperature1;
55
      State.bTimerDeltaT=0;
56
    }
57
  
58
    if(ZeitDeltaT>=10)
59
    {
60
      if(Temperature1>Temperaturold+DELTATEMP)
61
      {
62
      State.bPumpOnOff=1;    //Pumpe anschalten
63
      PumpeAn;
64
      ZeitDeltaT=0;
65
      State.bTimerDeltaT=0;
66
      }      
67
    }
68
  
69
    if(Laufzeit>=LAUFZEITPUMPE)      //Nach Laufzeit Pumpe aus
70
    {
71
      PumpeAus;
72
      State.bPumpOnOff=0;  //Pumpe aus
73
      Laufzeit=0;
74
      Temperaturold=Temperature1;
75
      State.bPumpDelay=1;      //Pumpe hat Zyklus beendet und braucht die nächste Zeit nicht gestartet werden.
76
    }      
77
    
78
    if(ZeitPumpDelay==PUMPDELAY){State.bPumpDelay=0;}  //Ein neuer Pumpzyklus kann gestartet werden
79
        
80
    //RS232 Auswertung
81
    if(uart_str_complete == 1)  //Nur abarbeiten, wenn etwas empfangen wurde
82
    {
83
      if(0==strcmp(uart_string,"Read Romcode"))    //Romcode eines einzelnen Fühlers auslesen
84
      {
85
        DS18B20_Read_RomCode(byte);
86
        for(i=0;i<8;i++){Send_Byte_UART(byte[i]);}  //Rom Code muss Byteweise gesendet werden, da NULL enthalten sein kann        
87
        uart_str_complete=0;  //Flag zurücksetzen
88
      }
89
      if(0==strcmp(uart_string,"Messung"))
90
      {
91
        //"<MS1>xx.x<MS2>yy.y<PS>z<EOD>";
92
93
        strcpy(strSendBuffer,"<MS1>");
94
        strcat(strSendBuffer,dtostrf(Temperature1,3,1,buf));
95
        strcat(strSendBuffer,"<MS2>");
96
        strcat(strSendBuffer,dtostrf(Temperature2,3,1,buf));
97
        strcat(strSendBuffer,"<PS>");
98
        strcat(strSendBuffer,"x");
99
        strcat(strSendBuffer,"<EOD>");
100
        strSendBuffer[22]='0'+State.bPumpOnOff%10;
101
        Send_String_UART(strSendBuffer);
102
        uart_str_complete=0;  //Flag zurücksetzen
103
      }
104
      else
105
      {
106
        for(i=0;i<8;i++){strBuffer[i]=uart_string[i];}
107
        strBuffer[8]='\0';
108
        if(0==strcmp(strBuffer,"Set time"))      //Setzen der Uhrzeit per RS232. Befehl lautet 'Set time hh:mm:ss'
109
        {
110
          
111
          Zeit[0]=uart_string[9];
112
          Zeit[1]=uart_string[10];
113
          Zeit[2]='\0';
114
          stunde=atoi(Zeit);
115
          LCD_String(1,10,Zeit);
116
117
          Zeit[0]=uart_string[12];
118
          Zeit[1]=uart_string[13];
119
          Zeit[2]='\0';
120
          minute=atoi(Zeit);
121
          LCD_String(1,13,Zeit);
122
      
123
          Zeit[0]=uart_string[15];
124
          Zeit[1]=uart_string[16];
125
          Zeit[2]='\0';
126
          sekunde=atoi(Zeit);
127
        }        
128
          uart_str_complete=0;    //Flag zurücksetzen      
129
      }      
130
    }  
131
  
132
  }


Die Interrupt Routinen
1
ISR(USART_RXC_vect)
2
{
3
  
4
  uint8_t nextChar;
5
  
6
  // Daten aus dem Puffer lesen
7
  nextChar = UDR;
8
  if( uart_str_complete == 0 ) {  // wenn uart_string gerade in Verwendung, neues Zeichen verwerfen
9
  
10
  // Daten werden erst in uart_string geschrieben, wenn nicht String-Ende/max Zeichenlänge erreicht ist/string gerade verarbeitet wird
11
  if( nextChar != '\n' )//|| nextChar != '\r' || uart_str_count < 10 )
12
  {
13
    uart_string[uart_str_count] = nextChar;
14
    uart_str_count++;
15
  }
16
  else
17
  {
18
    uart_string[uart_str_count] = '\0';
19
    uart_str_count = 0;
20
    uart_str_complete = 1;
21
  }
22
}
23
24
}
25
26
27
28
ISR (TIMER1_COMPA_vect)
29
{
30
  bSekunde=1;
31
}

von STK500.-Besitzer (Gast)


Lesenswert?

Du solltest dir einen Uhrzeit-String zusammenbasteln und den am Ende der 
Auswertung an das LCD schicken.
Die Kommunikation mit der Anzeige bremst das ganze Programm aus.

von Praktikant (Gast)


Lesenswert?

@STK500.-Besitzer
Versteh ich nicht. Die Uhrzeit wird nur jede Minute und jede Stunde 
aktualisiert.

Womit ich ein Problem habe ist scheinbar dieser Teil:
1
  if(0==strcmp(uart_string,"Messung"))
2
      {
3
        //"<MS1>xx.x<MS2>yy.y<PS>z<EOD>";
4
5
        strcpy(strSendBuffer,"<MS1>");
6
        strcat(strSendBuffer,dtostrf(Temperature1,3,1,buf));
7
        strcat(strSendBuffer,"<MS2>");
8
        strcat(strSendBuffer,dtostrf(Temperature2,3,1,buf));
9
        strcat(strSendBuffer,"<PS>");
10
        strcat(strSendBuffer,"x");
11
        strcat(strSendBuffer,"<EOD>");
12
        strSendBuffer[22]='0'+State.bPumpOnOff%10;
13
        Send_String_UART(strSendBuffer);
14
        uart_str_complete=0;  //Flag zurücksetzen
15
      }

Wenn hier der Timerinterrupt dazwischenhaut, dann habe ich den Murks.
Ich könnte auch die Interrupts vorm Senden deaktivieren und danach 
wieder aktivieren.
Aber mache ich mir damit die Uhr nicht noch ungenauer?

von holger (Gast)


Lesenswert?

>Wenn hier der Timerinterrupt dazwischenhaut, dann habe ich den Murks.

Wie sieht dieser Murks aus?
Welchen Controller verwendest du?

von Paule H. (stk500-besitzer)


Lesenswert?

Praktikant schrieb:
> Versteh ich nicht. Die Uhrzeit wird nur jede Minute und jede Stunde
> aktualisiert.

Was macht denn die Funktion "LCDString"?

von Prakitkant (Gast)


Lesenswert?

Paule H. schrieb:
> Was macht denn die Funktion "LCDString"?

Sie schreibt an LCDString(Zeile,Spalte,String).
Aber das tut sie NUR wenn eine neue Minute berechnet wurde, bzw. NUR 
wenn eine neue Stunde berechnet wurde.

Oder anders formuliert:
NUR einmal pro Minute wird die Uhrzeit aktualisiert.

holger schrieb:
> Wie sieht dieser Murks aus?

Ich übertrage folgenden String:
<MS1>xx.x<MS2>yy.y<PS>z<EOD> (Wobei x, y und z durch Werte ersetz 
werden)

Auf meinem PC kommt dann irgendwann folgendes an

EOD><MS1>xx.x<MS2>yy.y<PS>z<

Die Software die diesen String auswertet verschluckt sich daran.

Ich verwende einen Atmega 8 mit 4 Mhz Quarzoszillator. Ich weiss, dass 
ein "Baudratenquarz" besser wäre. Ist aber nicht vorhanden

von Paule H. (stk500-besitzer)


Lesenswert?

Prakitkant schrieb:
> Oder anders formuliert:
> NUR einmal pro Minute wird die Uhrzeit aktualisiert.

und wie lange braucht sie dafür?
Selbst wenn nur alle paar Stunden die Anzeige auf diese Art aktualisiert 
wird, braucht es eine gewisse Zeit, bis das Prozedere abgearbeitet 
wurde.
Da kann es schon sein, dass ein, zwei oder (Uhren-)Interrupts "verloren" 
gehen.

Kannst du mal bitte das ganze Programm als Anhang hier posten.
Programme in Prosa geschrieben, sind viel länger als Quellcode...

von holger (Gast)


Lesenswert?

>Auf meinem PC kommt dann irgendwann folgendes an
>
>EOD><MS1>xx.x<MS2>yy.y<PS>z<
>
>Die Software die diesen String auswertet verschluckt sich daran.

Woher weisst du das nicht deine PC Software diesen Murks baut?
Vieleicht ist der Atmega ja diesmal unschuldig;)

von Prakitkant (Gast)


Lesenswert?

holger schrieb:
> Woher weisst du das nicht deine PC Software diesen Murks baut?

Weil dann zusätzlich in meinem Display totaler Müll steht.
Nebenbei. Wenn mein String so aussieht "EOD><MS1>xx.x<MS2>yy.y<PS>z<"
dann sieht man schon, dass noch Reste "EOD>" vom alten String 
übermittelt wurden und an den neuen String angehängt wurde. So als ob 
die vorherige  Übertragung durch den Timerinterrupt unterbrochen wurde.

Paule H. schrieb:
> Selbst wenn nur alle paar Stunden die Anzeige auf diese Art aktualisiert
> wird, braucht es eine gewisse Zeit, bis das Prozedere abgearbeitet
> wurde.

Versteh ich wieder nicht.
Zu irgendeiner Zeit muss ich doch die Uhrzeit ausgeben. Mit meiner 
Methode tausche ich pro Minute nur die Minuten bzw. Stunden aus. Das ist 
weniger Aufwand als den kompletten Zeitstring auszugeben.

von holger (Gast)


Lesenswert?

>> Woher weisst du das nicht deine PC Software diesen Murks baut?
>
>Weil dann zusätzlich in meinem Display totaler Müll steht.

DAS wäre noch mal ne wichtige Zusatzinformation gewesen.

>So als ob die vorherige  Übertragung durch den
>Timerinterrupt unterbrochen wurde.

Das dauert nicht lange genug um die Übertragung
wirklich zu unterbrechen.

Hier ist eine undichte Stelle in deinem Programm:

  if( nextChar != '\n' )//|| nextChar != '\r' || uart_str_count < 10 )

das kann zu Bufferoverflow führen falls mal kein \n auftaucht.
Dabei steht die Lösung schon daneben. Ts, ts.

Was ist das für eine Pumpe die du da schaltest?
Das könnte auch der Übeltäter sein wenn die Schalterei
nicht richtig entstört ist. Das würde auch den Müll
im Display erklären.

von Praktikant (Gast)


Lesenswert?

holger schrieb:
> DAS wäre noch mal ne wichtige Zusatzinformation gewesen.

Tschuldigung.

holger schrieb:
> Was ist das für eine Pumpe die du da schaltest?

Im Moment noch Keine.
Meine Pumpe ist zur Zeit nur eine LED.

holger schrieb:
> das kann zu Bufferoverflow führen falls mal kein \n auftaucht.
> Dabei steht die Lösung schon daneben. Ts, ts.

Sehe ich ein. Allerdings überträgt mein Programm ein \n.
Die Lösung die daneben steht habe ich auch schon probiert (Deswegen 
steht es noch daneben). Allerdings besteht auch hier das Problem.

Kurioserweise läuft mein Programm heute schon seit 8h 30 min, ohne Murks 
im Display. Jedoch auch ohne PC, der ständig die Werte abfragt.

Hmmm. Liegt wohl doch irgendwie an der seriellen Übertragung

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.