Forum: Mikrocontroller und Digitale Elektronik C: Timer Overflow Interrupt löst nicht aus


von Erekosё (Gast)


Angehängte Dateien:

Lesenswert?

Ho, Männer!

Ich habe ein Problem bei meiner Motorsteuerung mittels ATmega32.

Zunächst vermochte die Software so zu laufen wie ich es haben wollte!

Jedoch habe ich anschließend die PWM-Frequenz geändert von 25Hz (alles 
prima!) auf 10kHz, da die bürstenbehafteten Motoren sonst sehr ruckelig 
liefen.

PLÖTZLICH muss ich feststellen, dass der Timer Overflow Interrupt nicht 
mehr kommt, dies schließe ich daraus, dass ich die Impulse des 
Drehencoders am Motor1 nicht mehr per 'hterm' über die USART auslesen 
konnte (immer 0).

Von vorne:

•  mittels Timer1 wird eine PWM generiert & an OCR1 (vorerst 25Hz, nun 
10 kHz) [timer_init ();

•  über den ADC0 [adc_init ();] wird die zu fahrende Anzahl der 
Umdrehungen pro Richtungswechsel (rechts - links) mittels Drehschalter 
zehnmal über eine Einzelwandlung eingelesen [ADC_10_Messung ();], ein 
Mittelwert aus den Messungen gebildet [ADC_Mittelwert ();] und die Werte 
für die Anzahl der Umdrehungen gesetzt [Umdrehungszahl ();] und in 
‚Umdrehungen‘ geschrieben

•  ‚Umdrehungen‘ lasse ich mit über die USART [usart_init ();] auf dem 
PC ausgeben[UDZ_zu_USART ();], bisher reibungslos!


Was mich nun wundert ist folgendes:

•  ich berechne die Drehzahl (PWM des Timer1 – OCR1A/B) aus der analogen 
Spannung am ADC1 [Drehzahl_R ();],
das klappt & deshalb wundert mich, dass der Timer1 dennoch seinen 
Capture Interrupt auslöst, jedoch (anscheinend) nicht den Overflow 
Interrupt,
denn folgender Programmteil läuft nicht (mehr):

t_Drehzahl ();      // Drehzahl für eine bestimmte Zeit messen
M1_zu_USART ();    // Ausgabe der gemessenen Impulse von M1 an USART

Zu t_Drehzahl (); gehört der folgende Code:
1
/* ### Drezahlmessung der Impulse der Drehgeber ###
2
     über einen festgelegten Zeitraum von 200ms messen */
3
4
  void t_Drehzahl () {
5
    vect_INT2 = 1;                /* externen Interrupt INT2 "freigeben"
6
        warten auf Impuls an INT2 --> 1 impuls pro Umdrehung */                  
7
    if (t_Messung == 1000){            // 100ms um ?
8
      vect_INT2 = 0;              // externen Interrupt INT2 "sperren"
9
      M1 = M1_Impulse;            // Impulse des Motor1 in Register 'M1' schreiben
10
      M2 = M2_Impulse;            // Impulse des Motor2 in Register 'M2' schreiben
11
      M1_Impulse = 0;              // gezählte Impulse zurücksetzen
12
      M2_Impulse = 0;
13
      t_Messung = 0;              // Zeitkonstante zurücksetzen
14
}
15
}
16
17
// #### Interrupt Service Routines
18
19
ISR (INT2_vect){  
20
  if (vect_INT2 == 1){          // kann der Timer für die Dauer der Messung eingeschaltet werden?
21
    OVF_Timer = 1;            // Timer einschalten
22
  }
23
}
24
25
ISR (TIMER1_OVF_vect) {
26
  if (OVF_Timer = 1){            // darf die Messzeit ablaufen?
27
    t_Messung++;            // pro 100µs die Variable inkrementieren
28
    INT_0_1 = 1;            // INT0 & INT 1 aktivieren
29
  }
30
}
31
32
  
33
  
34
ISR (INT1_vect){
35
  if (INT_0_1 == 1){            // Zählung Motor 1 aktiviert?
36
  M1_Impulse++;              // Zähle Impulse am Drehencoder des Motor1
37
  }
38
}
39
40
41
42
ISR (INT0_vect){
43
  if (INT_0_1 == 1){            // Zählung Motor 2 aktiviert?
44
  M2_Impulse++;              // Zähle Impulse am Drehencoder des Motor2
45
  }
46
}

Wäre nett, wenn mir damit jemand helfen kann.
Die ISRs habe ich miteinander verschachtelt wie man unschwer erkennen 
kann (if-Abfragen).
Vielleicht kann jemand noch einen Hinweis für einen charmanteren 
Lösungsweg geben.

M1_zu_USART();   ist langweilig und sollte funktionieren, da ich die 
Umdrehungen nach dem gleichen Prinzip ausgebe.

Den kompletten Quellcode habe ich dem Beitrag beigefügt.

Habt Dank für jede Hilfe.

Gruß

von Stephan (Gast)


Lesenswert?

10Khz sind für die Bürste schon sportlich.

Übliche Modellbauregler arbeiten im Bereich von 1-4Khz, welcher sich 
sich so ein wenig als Optimum etabliert hat, wenn man das so sagen darf.
Du kannst also ruhig etwas entspannter da ran gehen.

von Michael (Gast)


Lesenswert?

Hallo,
überprüf mal diese Zeile !!!  (= oder == )


Erekosё schrieb:
> if (OVF_Timer = 1){            // darf die Messzeit ablaufen?

von Erekosё (Gast)


Angehängte Dateien:

Lesenswert?

Hi, Leute

Stephan schrieb:
> 10Khz sind für die Bürste schon sportlich.
>
> Übliche Modellbauregler arbeiten im Bereich von 1-4Khz, welcher sich
> sich so ein wenig als Optimum etabliert hat, wenn man das so sagen darf.
> Du kannst also ruhig etwas entspannter da ran gehen.

habe mit einer PWM von 2kHz gearbeitet

Michael schrieb:
> überprüf mal diese Zeile !!!  (= oder == )
>
> Erekosё schrieb:
>> if (OVF_Timer = 1){            // darf die Messzeit ablaufen?

und gleichzeitig diesen Fehler behoben.

Es will mir jedoch einfach nicht gelingen.

Im Anhang, liegt das Programm, welches ordnungsgemäß arbeitet.
Dort muss nur die PWM-Frequenz verändert werden & die Rechnung 
Drehzahl_R();, sowie bei t_Drehzahl die Dauer der Messung
an die Werte des PWM-TopWertes angepasst werden.

Ein Unterschied zu dem ersten programm ist, dass ich eine weitere 
Unterfunktion aufrufe PWM_OCR1 ();, um die an ADC1 gemessenen 
Drehzahlwerte
in die passenden register zu schreiben.

gruß

von Stephan H. (stephan-)


Lesenswert?

mit C kann ich leider nicht weiter helfen.
Prüfe die üblichen Verdächtigen.

Stack, Enable Interrupt generell, löschen von IR Flags die nicht 
automatisch gelöscht werden, Variablen die nicht exclusiv sind, 
Overflow, TIMING.....

Wenn das Timing zB. zu kurz ist, kannst Du einen Int verlieren, weil Du 
ihn nicht schnell genug behandeln kannst.

Im Ernstfall den Code Zeile für Zeile im Handbetrieb am PC laufen 
lassen.
Mein ASM kann das Hexfile Schritt für Schritt durchlaufen, wobei ich 
alle Register, Interrupts und den RAM sehe. Sogar ein LCD läßt sich 
simulieren.
Sowas sollte beim AVR auch machbar sein. Ist etwas zeitaufwändig, führt 
aber definitiv zum Ziel, wenn man weis, was die MCU machen sollte.

von Thomas E. (thomase)


Lesenswert?

Das ist ein Programm wie aus dem Lehrbuch.

Aus dem Und-so-schon-gar-nicht-Kapitel.

Das kann gar nicht funktionieren.
1
ISR (TIMER1_CAPT_vect)
2
{ 
3
 ADMUX |= (1<<MUX0);      
4
 ADCSRA |= (1<<ADSC);        
5
 while (ADCSRA & (1<<ADSC));
6
 ADC1 = ADC;            
7
 ADMUX &=~ (1<<MUX0);      
8
}

Was passiert da?
 while (ADCSRA & (1<<ADSC));

Du lässt deinen Controller in einer ISR warten, bis das langsamste Teil 
auf demselben, mit seiner Arbeit fertig ist. 13 x 128 = 1664 Takte.
In einer ISR, die alle 1600 Takte aufgerufen wird. Und zwischendurch 
soll er noch einen weiteren Interrupt bedienen. Das schafft nicht mal 
die 100GHz-32-Core-mit-Hyperthreading-und-1000-Terabyte-RAM-Granate 
unter deinem Schreibtisch.

Ich gehöre auch nicht zu den ISR-so-kurz-wie-möglich-Dogmatikern, die 
Flags setzen und dann 2 Ports in der main schalten. Kleinigkeiten kann 
man auch gleich an Ort und Stelle erledigen. Und ein bisschen rechnen 
ist auch OK.

AAAAAABER in einer ISR warten, bis ein verschissener AD-Wandler mit 
seiner verschissenen Wandlung fertig ist, ist nicht das Allerletzte 
sondern noch Lichtjahre darüber hinaus.

Du schreibst jetzt tausend mal: "In einer ISR wird nicht gewartet. Und 
schon gar nicht auf verschissene AD-Wandler".

Verlassen wir den technisch sachlichen Teil.

Dein Programm ist recht ausführlich kommentiert. Aber die Kommentare 
sind sowas von überflüssig.
1
usart_init ();   // USART initialisieren

Also ich hätte jetzt glatt gedacht, damit wird eine LED zum blinken 
gebracht. Aber da steht ja Gott sei Dank "USART initialisieren" hinter.
Lass solche überflüssigen Kommentare. Das ist jedem klar, was die 
Funktion machen soll. Und deine Oma versteht das Programm auch mit 
Kommentar nicht. Und wer es versteht, für den liest sich das schwerer 
als mit ohne.

Deine Interrupt-Konstruktionen: Das ist nun auch nicht gerade das gelbe 
vom Ei. Wenn du einen Interrupt nicht brauchst, schalte ihn ab.

Deine Funktionen: Funktionen sind notwendig. Alles, was man mehr als 
einmal benötigt, kommt in eine Funktion. Alles, was über mehr als eine 
Bildschirmseite geht, könnte man, der Übersichtlichkeit halber, in ein 
paar Funtionen aufteilen. Natürlich nur, wenn das auch sinnvoll ist. 
Manchmal geht es aber auch nicht. Dann ist das eben so.
Aber man kann es auch übertreiben. Eine Funktion, die einen Mittelwert 
ermittelt, welch ein Audruck, kann diesen auch gleich fertig verpackt 
und verschnürt zurück liefern. Da muss man nicht noch eine weitere 
Funktion aufrufen, die den Wert erst noch durch 10 teilt.
Und wenn man die OCR-Register neu schreibt, kann man das auch direkt 
tun. Es hindert dich niemand daran, den Registern Aliasnamen zu geben.
1
#define PWM1 OCR1A //definiere PWM1 als OCR1A
2
#define PWM2 OCR1B //definiere PWM2 als OCR1B

Hatte ich das mit den bescheuerten Kommentaren schon geschrieben?

Mittelwert: So macht man das nicht. Erstmal teilt man das nicht durch 
10. Sondern durch 8 oder 16. Da muss der Controller nämlich nicht 
rechnen, sondern nur um 3 oder 4 Bit nach rechts verschieben. Das kann 
ein AVR zwar auch nicht besonders gut. Aber immer noch besser als 
dividieren.
Und dann nimmt man nicht n Werte auf, addiert die und teilt sie dann 
durch n, sondern man nimmt einen Wert und liefert sofort den Mittelwert.
???
Man schreibt die einzelen Werte in ein Array von der Größe n.
Dann subtrahiert man den ältesten Wert von der Mittelwertsumme und 
addiert den neuesten, teilt durch n und ab dafür. Die ersten n-1 Werte 
liegen natürlich daneben. Aber das geht schnell vorbei.

Wenn du mit dem tausend mal "In einer ISR..."-Schreiben fertig bist, 
guckst du dir in deinem C-Buch an, wie man Funktionen einsetzt, wie man 
Parameter übergibt und was Rückgabewerte sind. Und dann noch, wie oft 
oder besser wie selten, man globale Variablen benutzt.

Warum bist du eigentlich von den 25Hz auf 10KHz gegangen. Also ich und 
wahrscheinlich jeder andere auch, hätte erstmal 50, 100, 200 Hz und so 
weiter probiert.


mfg.

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.