Forum: Mikrocontroller und Digitale Elektronik Probleme bei Berechnung mit Compare Match


von Sven (Gast)


Lesenswert?

Nabend zusammen,

bei meinem Programm passt etwas nicht bzw. kann ich es nicht ganz 
nachvollziehen.
Prozessor ist ein Atmega8. Hier der relevante Programmausschnitt:
1
volatile uint32_t DisplayBuffer[4];
2
volatile int32_t slDifferenz = 0;
3
volatile uint32_t ulTimeToCompareMatch;
4
5
//Timer1 16-bit Compare Match A Interrupt Service Routine
6
ISR (TIMER1_COMPA_vect){
7
8
    DisplayBuffer[1] = OCR1A;
9
    DisplayBuffer[2] = TCNT1;
10
    DisplayBuffer[3] = ulTimeToCompareMatch;
11
    slDifferenz = ulTimeToCompareMatch - OCR1A;
12
    
13
}
14
15
16
//Timer1 16-bit Overflow Interrupt Service Routine
17
ISR (TIMER1_OVF_vect){
18
    
19
  // Flag setzen für Display aktualisierung ca. jede 1,5 Sekunden
20
  overflowzaehler++;
21
  if (overflowzaehler >= 183){
22
    displayaktualisieren = TRUE;
23
    overflowzaehler = 0;
24
  }
25
    
26
}
27
28
29
int main(void)
30
31
// hier wird noch das Compare Match stetig vorgeladen
32
{
33
34
signed char buffer [12];
35
36
37
  init_ports();    // Ports initialisieren              
38
  init_timer1();    // Initialisiert den Timer1 
39
  
40
  // Display initialisieren
41
    LCD_DDR= 0xFF;
42
    lcd_init();
43
  
44
/*Hauptschleife des Hauptprogramms*/
45
  while (1){
46
  
47
48
  if (displayaktualisieren == TRUE){
49
    lcd_clear();
50
    
51
    // Zeile 1
52
    lcd_setcursor(0,0);
53
    lcd_string("Diff:");   
54
    // umrechnen in Dezimal
55
    ltoa( slDifferenz, buffer, 10 );
56
    // ... ausgeben
57
    lcd_string( buffer );
58
59
    
60
    
61
    // Zeile 2
62
    lcd_setcursor(0,1);
63
    lcd_string("OCR:");   
64
    // umrechnen in Dezimal
65
    ultoa( DisplayBuffer[1], buffer, 10 );
66
    // ... ausgeben
67
    lcd_string( buffer );
68
    
69
    // Zeile 3
70
    lcd_setcursor(0,2);
71
    lcd_string("Timer:");    
72
    // umrechnen in Dezimal
73
    ultoa( DisplayBuffer[2], buffer, 10 );
74
    // ... ausgeben
75
    lcd_string( buffer );
76
    
77
    // Zeile 4
78
    lcd_setcursor(0,3);
79
    lcd_string("Comp:");    
80
    // umrechnen in Dezimal
81
    ultoa( DisplayBuffer[3], buffer, 10 );
82
    // ... ausgeben
83
    lcd_string( buffer );
84
85
    displayaktualisieren = FALSE;
86
  }
87
  
88
89
  } // Ende main loop
90
} // Ende Hauptprogramm

mit ulTimeToCompareMatch lade ich OCR1A vor und aktivere nach dem 
Vorladen den Interrupt entsprechen mit TIMSK |= (1 << OCIE1A);

Direkt am Beginn des Interrupts lese ich folgende Register aus:
OCR1A
TCNT1
sowie meine Variable ulTimeToCompareMatch mit der ich OCR1A vorlade und 
speichere Sie in den Array DisplayBuffer.
So nun lasse ich mir die Sachen auf meinem LCD im Hauptprogramm ausgeben 
alle 1,5ms (die Compare Match-Zeit wird vom Programm berechnet und 
ändert sich stetig).
Allerdings zeigt das Display zwischen OCR1A und ulTimeToCompareMatch 
einen unterschiedlichen Wert an, obwohl OCR1A mit dieser Variable 
vorgeladen wird, was ja nicht sein sollte.
Daher habe ich in das gleiche Compare Match Interrupt noch folgendes 
eingefügt:
1
slDifferenz = ulTimeToCompareMatch - OCR1A;
Den Wert von slDifferenz lasse ich mir ebenfalls auf dem Display 
ausgeben. Paradoxerweise ist aber die Differenz IMMER 0.
Daher verstehe ich das nicht, OCR1A und ulTimeToCompareMatch sind laut 
Display-Ausgabe verschieden, rechnet das Programm aber die Differenz ist 
der Wert 0.

Kann sich das jemand erklären?

Danke!

von Karl H. (kbuchegg)


Lesenswert?

Sven schrieb:

> So nun lasse ich mir die Sachen auf meinem LCD im Hauptprogramm ausgeben
> alle 1,5ms (die Compare Match-Zeit wird vom Programm berechnet und
> ändert sich stetig).

Da wird es interessant. Was muss man sich darunter vorstellen?

> Allerdings zeigt das Display zwischen OCR1A und ulTimeToCompareMatch
> einen unterschiedlichen Wert an, obwohl OCR1A mit dieser Variable
> vorgeladen wird, was ja nicht sein sollte.

Kommt drauf an.
Vom Beginn deiner Ausgabe, bis zum Zeitpunkt an dem du dann 
ulTimeToCompareMatch aufs Display zauberst vergeht Zeit. In dieser zeit 
können ein oder mehrere Compare Match Interrupts dazwischenfunken und 
wie du im Programmlauf den OCR Wert veränderst hast du auch nicht 
gezeigt.

-> die interessanten Teile fehlen wieder mal.


> Den Wert von slDifferenz lasse ich mir ebenfalls auf dem Display
> ausgeben. Paradoxerweise ist aber die Differenz IMMER 0.

Das bedeutet jetzt nur, dass in der ISR alle Werte tatsächlich 
zusammenpassen und auch wirklich aus derselben Messung stammen.
Etwas was du in deinem Anzeigecode nicht garantieren kannst.
Als Minimum würde ich mal die komplette Ausgabe unter Interruptsperre 
laufen lassen. Und nach der Realisierung, dass das eine teure Operation 
ist, am Anfang deines Ausgabecodes
1
  if (displayaktualisieren == TRUE){
2
    lcd_clear();
erst mal nur die aktuellen Daten unter Interruptsperre in einen 2-ten 
Satz Variablen sichern.
1
  if (displayaktualisieren == TRUE){
2
    cli();
3
    UpdateBuffer[0] = DisplayBuffer[0];
4
    UpdateBuffer[1] = DisplayBuffer[1];
5
    UpdateBuffer[2] = DisplayBuffer[2];
6
    sei();
7
   
8
    lcd_clear();
9
    ...

danach hast du alle Zeit der Welt diese Daten aus den neuen Variablen 
heraus auszugeben. Dazwischenfunkende Interrupts können an diesen Kopien 
jetzt nichts mehr ändern, während du ausgibst.

von Sven (Gast)


Lesenswert?

Guten morgen Karl Heinz Buchegger ,

erst einmal danke für die Antwort.

Daran habe ich überhaupt nicht gedacht, das bei der Ausgabe auf dem 
Display zwischen den 3 Variablen ein Interrupt dazwischen Funken könnte. 
Ich war bis dato der Überzeugung, das wenn ich alle 3 Variablen im 
Interrupt einlesen, nicht woanders in der die DisplayBuffer-Variable 
verändern lasse bis zur Ausgabe, bekomme ich auch die Werte angezeigt. 
Im Interrupt-Fall, welches dazwischen funken könnte, wäre dem ja nicht 
so. Ich werde heute Abend nach der Arbeit das mal versuchen Interrupt 
gesperrt auf dem Display auszugeben gemäß deinem Vorschlag.

Da das Problem wie gesagt absolut sporadisch auftritt, von 
schätzungsweise 20 angezeigten Werten stimmt alles überein und beim 21 
ist da eine Differenz, könnte es durchaus sein, dass hier ein Interrupt 
dazwischen funkt.

Ich berichte heute Abend.
Nochmals danke, selbst wenn es nicht der Fehler sein sollte, habe ich 
etwas dazu gelernt..

Grüße
Sven

von Sven (Gast)


Lesenswert?

Hallo nochmal,

ich wollte nur Bescheid geben, dass dies der Fehler war. Habe es so 
gemacht, wie du es beschrieben hattest und siehe da, die Zeiten stimmen 
überein.

Danke!

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.