Forum: Mikrocontroller und Digitale Elektronik ATmega644: Timer2_COMPA_vect wird falsch ausgelöst


von A. P. (adrian_p)


Lesenswert?

Hallo zusammen

Ich implementiere gerade eine Strommessung für einen 
Brushless-Controller. Hierzu ist es von Nöten quasi die "Mitte" einer 
PWM-Phase zu ermitteln.

Da zwar einen Overflow-Interrupt aber kein Top-Interrupt vorhanden ist, 
benutze ich den Compare-Match-Interrupt A mit OCR2A = 255. Mein Problem 
ist, dass dieser aus einem mir unerfindlichen Grund zwar ausgeführt 
wird, sich aber kein Bisschen an OCR2A hält.

Die Initialisierung erfolgt folgendermassen:
1
inline void PWM_init()
2
{
3
  //MOSFET-Pins
4
  DDRD |= (1<<M_AP) | (1<<M_BP) | (1<<M_CP);
5
  DDRC |= (1<<M_AN) | (1<<M_BN) | (1<<M_CN);
6
  
7
  //Timer 1 (16Bit): PhaseCorrect-PWM, 8bit bis 255
8
  TCCR1A = (0<<WGM11) | (1<<WGM10) | (1<<COM1B1) | (1<<COM1A1);
9
  TCCR1B = PRESCALER_1;
10
11
  //Timer 2 (8Bit): PhaseCorrect-PWM bis 255
12
  TCCR2A = (0<<WGM21) | (1<<WGM20) | (1<<COM2B1);
13
  TCCR2B = PRESCALER_1_Timer2;
14
  
15
  //Compare-Match Interrupt der Strommessung
16
  OCR2A = 255;
17
}

Der PWM-Wert (bei Timer2 wird hierzu einfach nur OCR2B benutzt) wird 
live gesetzt. Sobald ein AC-Interrupt der sensorlosen Motor-Steuerung 
erfolgt, läuft eine Phase-Erkennung durch. Jede Sekunde wird nun der 
Compare-Match Interrupt (TOP_INT_ON) eingeschaltet:
1
ISR(ANALOG_COMP_vect)
2
{  
3
  switch (Phase)
4
  {
5
    //... Teil entfernt
6
7
    case 2:
8
      SENSE_A;
9
      SENSE_RISING;
10
      COM_2;
11
      CommutationCounter++;
12
      if(compare_time(SenderWatchdog))
13
      {
14
        SenderWatchdog = calculate_time(625);
15
        sei();
16
        TOP_INT_ON;
17
      }
18
      Phase++;
19
      break;
20
21
    //... Teil entfernt
22
  }
23
}

Ich verstehe somit nicht, warum folgende Interrupt-Routine ausgeführt 
wird, der ausgelesene TCNT2-Wert sich aber unabhängig von OCR2A (schon 
völlig verschiedene Werte getestet) meistens bei ca 230 befindet. Meiner 
Meinung nach, sollte dieser ca. bei 254 liegen.

Was mir auch noch aufgefallen ist: der Interrupt erfolgt immer vor der 
ersten PWM-Wave. Will heissen: der  Interrupt wird bei Aktivierung 
sofort ausgeführt und wartet anscheinend nicht einmal bis zum ersten 
möglichen Compare-Match.
1
ISR(TIMER2_COMPA_vect)
2
{
3
  uint8_t data = TCNT2;
4
  u_transmit(data);
5
  PORTA ^= (1<<GREEN);
6
  TOP_INT_OFF;
7
}

Zu Testzwecken wird lässt dieser Interrupt einfach ein LED togglen und 
übermittelt den TCNT2-Wert per UART. Anschliessend schaltet er sich 
selbst ab, da er nur ca jede Sekunde erfolgen soll.

Vielen Dank

Gruss, AP

von Carsten R. (kaffeetante)


Lesenswert?

Hallo Adrian,

es fehlt halt der Code und damit der vollständige Überblick wann die 
Interrupts wie maskiert werden. Sei es drum. Ich denke mir die 
Interrupts hinzu ohne diese Spekulation auch noch zu skizzieren. Dieses 
Konstrukt verursacht die Probleme wie folgt.

- Dir fehlt eine konsequente und sinnvolle Strategie für die 
Interruptmaskierung. Interrupts sollten während der 
Initialisierungsphasen möglichst deaktiviert sein.

- OCR2A wird nach dem Prescaler gesetzt. Dies verursacht den zusätlichen 
Compare-Match. Nach einem reset sind TCNT2 und OCR2A immer 0. Das 
setzten des Prescalers startet den Timer. 0=0 liefert den Compare-Match. 
Erst danach wird OCR2A auf 255 gesetzt. Setze den Prescaler erst zum 
Schluß der Initialisierung um den Timer erst dann zu starten.

- Deine ISR sind in C und völlig überladen. Das Interruptflag wird zwar 
bei TCNT2=255 gesetzt, aber bis Deine Routine beim Lesen von TCNT2 
ankommt sind einige Takte vergangen (Stichwort: Latenz) und der Timer 
hat schon wieder entsprechend weit heruntergezählt. Wenn dir 255-230 = 
25 Takte eine zu große Abweichung sind, müssen deine ISR inklusive 
Stackoperationen und Sprünge in weniger as 25 Takten erledigt werden 
können. Du kannst aber auch oder zusätzlich durch verändern von OCR2A 
die Latenz ausgleichen indem du den Interrupt dadurch vorziehst.

Zudem ist mangels Code nicht ersichtlich ob sichergestellt ist, daß das 
Compare-Match-Flag gelöscht ist unmittelbar bevor dieser Interrupt 
erlaubt wird. Das Flag könnte noch von einem vorerigen PWM-Impuls 
stehengeblieben sein, da die entsprechende ISR nicht bei jedem Match 
ausgeführt wird. Das fürt zu unvorersehbarem Verhalten.

Lösung: Halte die IS-Routinen so kurz wie möglich. Verwende 
(Inline)-Assembler wenn es zeitkritisch ist. Insbesondere die 
ISR(ANALOG_COMP_vect) ist zu aufgebläht.

Zum Beispiel: Warum wird TOP_INT_ON dort ausgeführt? Es ist nicht 
vorhersagbar wieviele Takte zwischen diesem Phasenwechsel und dem 
Comparematch liegen. Dazwischen (immerin 255 Takte) können sogar weitere 
Phasenwechsel liegen. Brushlessmotoren sind teilweise sehr hochtourig 
und es erfolgen mehrere Phasenwechsel pro Umdrehung. Die gewünschte 
Aktion erfolgt letztendlich synchron zur PWM und nicht zum 
Phasenwechsel. Bestenfalls läuft es pseudosynchron zum Phasenwechsel und 
keineswegs stabil synchron da drehzal- und lastabhängig.

Du hast dort eine Zeitberechnung (Watchdog, calculate_Time), wozu auch 
immer. Benutze den Zeitgeber um TOP_INT_ON einmal die Sekunde 
auszuführen oder lasse die ISR zu Compare-Match hochzählen bis 
PWM-Frequenz/256 um dann einmal eine Messung vorzunehmen.

Je nachdem was Du im Detail vorhast könnte es auch eine Option sein das 
PWM-Signal zu invertieren. Damit würden Overflow und Top die Plätze in 
der Mitte des High/Low-Pegels tauschen. Man müßte dann aber umdenken. 
255 wäre Minimum und 0 wäre Maximum. Das Singnal wäre dann im Verlauf 
das Gleiche aber Anfang und Ende und !!!Updateverhalten!!! wären etwas 
anders. Wenn das kein Probem darstellt oder wenn man dies 
berücksichtigen kann könnte man so den zweiten Timer sparen und den 
Overflow anstelle des fehlenden TOP-Signals verwenden.



Dies erst einmal zur Anregung

Viele Grüße
Carsten

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.