Hallo und ein frohes neues Jahr!
ich benutze den AT90USB1287 (16 MHz)und habe Timer 1 2 und 3 laufen mit
einem Prescaler von 64. Die Timer funktionieren auch wie sie sollen.
Nun benötige ich noch einen Timer (Timer 0), der eine sehr viel höhere
Frequenz erzeugen soll, und daher ohne Prescaler (Prescaler = 1) laufen
soll.
Schreibe ich das Programm nur mit Timer 0 funktioniert alles wie es
soll.
Integriere ich den Timer in mein bestehendes Programm mit den drei
anderen Timern so scheint es nicht mehr zu funktionieren.
Laut Datenblatt nutzen zwar Timer 0 1 und 3 das selbe Prescaler-Modul,
aber dennoch soll es möglich sein, verschiedene Faktoren zu nutzen:
> 12. Timer/Counter0, Timer/Counter1, and Timer/Counter3 Prescalers>> Timer/Counter0, 1, and 3 share the same prescaler module, but the> Timer/Counters can have different prescaler settings. The description> below applies to all Timer/Counters. Tn is used as a general name, n = 0,> 1 or 3
Die Timer sind wie folgt konfiguriert:
1
voidtimer_init()
2
{
3
sei();// Aktiviere Interrupts global
4
5
// 16Bit-Timer1
6
TCCR1A=(1<<COM1A0);// Toggle Pin on Compare Match, siehe Datenblatt Tabelle 14-1
7
TCCR1B=(1<<WGM12)|(1<<CS10)|(1<<CS11);// Aktiviere CTC Mode non PWM, siehe Datenblatt Tabelle 14-4
8
TCCR1C=(1<<FOC1A);// Aktiviere Output Compare für Channel A
Wieviel % der Gesamtrechenzeit verbrät dein sehr schnell laufender
Timer0?
Wieviele Timer1,2,3 Interrupts gehen durch den häufig auftretenden
Timer0 IRQ verloren?
Wenn du ein Oszi hast kannst du beides durch geschicktes Togglen von
Pins in den jeweiligen ISRs heraus finden.
Noname schrieb:>>scheint es nicht mehr zu funktionieren.>>Hat jemand eine Idee woran es liegen kann?>> Was soll woran liegen? Was ist denn der Fehler?
Also ich sende die 8-Bit Werte für die PWM-Pulsbreite über UART an den
µC. Lasse ich ein Programm laufen, in dem ich nur Timer0 eingerichtet
habe, dann kann ich über UART Pulsbreiten vorgeben und der Schrittmotor
dreht auf die richtige Position.
Nun habe ich den Timer0 in ein anderes Programm integriert und er
scheint die Frequenzen nicht richtig zu erzeugen. Ich kann das
allerdings nicht genau nachverfolgen, denn mein
"Soundkarten-Oszilloskop" kann die Frequenzen im 100kHz bereich
natürlich nicht darstellen.
Dann habe ich zu testzwecken den Timer0 auf den Prescaler 64 eingestellt
und dann konnte ich am Oszilloskop die Frequenzen und Pulsbreiten sehen
die ich über UART einstelle. Das scheint also zu funktionieren!
Den Interrupt in Timer0 brauche ich theoretisch nicht dringend, nur ist
es schöner die Vergleichswerte zu setzen, wenn der Timer gerade
übergelaufen ist, als den Wert zufällig mitten im Zählvorgang zu setzen!
Hat noch jemand eine Idee wieso die Frequenzen vielleicht nicht richtig
erzeugt werden?
Viele Grüße
Christian
Nun, leider stehen wir immer noch bei Aussagen wie: "scheint die
Frequenzen nicht richtig zu erzeugen". Was heisst "nicht richtig"? Was
erwartest Du und was geschieht wirklich? Siehe:
http://www.mikrocontroller.net/articles/Forum-Fragenformulierung
Der Hinweis von Krapao scheint zumindest in eine sinnvolle Richtung zu
deuten, denn zweifellos kommt es bei vier unterschiedlichen
Interruptquellen (bzw. 5 falls der USART auch interrupts auslöst) auf
eine durchdachte und kontrollierte Abstimmung der Ereignisse und der
Dauer ihrer Verarbeitung an.
Weiter können wir Dir so nicht helfen. Denn da Du nur sehr allgemeine
Aussagen machst, können wir auch nur allgemeine Antworten liefern. Du
musst also detaillierte Beschreibungen des Ist- und Sollzustandes
liefern, bevor wir weiter darüber nachdenken können. Poste bitte auch
den kompletten Code, denn typischerweise steckt der Fehler gerade in den
Codestellen deren Bedeutung Menschen mit geringen Analysefähigkeiten
unterschätzen.
Das lernst Du schon noch. Das ist hier eine gute Gelegenheit dazu.
Hi
>TCCR1C = (1<<FOC1A); // Aktiviere Output Compare für Channel A>TCCR3C = (1<<FOC3A);>TCCR2B = ... | (1<<FOC2A);>TCCR0B = ... | (1<<FOC0A);
Ist zwar nicht für dein Problem zuständig. Aber diese Bits machen nicht
das, was du denkst.
MfG Spess
Okay dann versuche ich das nochmal zu konkretisieren und zeige euch den
gesamten Quellcode der an sich auch abgesehen von Timer0 für mich super
funktioniert!
Grundlegend:
Ich verwende das usb_serial-Projekt von
http://www.pjrc.com/teensy/usb_serial.html
Ich übermittle eine Zeichenkette mit 4 Werten die jeweils 3 Stellen
haben:
123123123123
im Main-Programm wird diese Zeichenkette eingelesen, zerlegt und den
Timern zugeordnet.
Zum Problem:
Was soll Timer0 tun:
Er soll ein PWM Signal erzeugen mit einer Grundfrequenz von 100kHz
Nach Formel aus dem Datenblatt errechnet sich die Grundfrequenz wie
folgt:
f_pwm = f_clk/(n*256) = 16MHz/(1*160) = 100kHz
Daraus folgt, dass ich OCR0A auf 160 setze
Danach soll über OCR0B das Tastverhältnis eingestellt werden, welches
ich in der Zeichenkette übergebe.
z.B.: 016 -> 10% Pulsbreite
Mache ich das ganze mit Timer0 und einem Prescaler von 64 funktioniert
alles wie es soll, dass kann ich aufm Oszi sehen. Allerdings ist die
Grundfrequenz zu niedrig und der Schrittmotor bewegt sich nicht.
Stelle ich den Timer0 auf Prescaler = 1 dann bewegt sich der Motor nicht
so, wie ich es zuvor in dem anderen Programm ausprobiert habe (anderes
Programm, NUR Timer0 mit Prescaler = 1)
Da mein Oszilloskop jedoch die hohen Frequenzen nicht anzeigen kann,
weiß ich auch nicht, wo der Fehler liegt.
Meine Frage ist also, ob es irgendetwas besonderes zu beachten gibt,
wenn ich mehrere Timer mit unterschiedlichen Vorteilern nutzen möchte.
Ich hoffe so kommen wir irgendwie weiter =)
Viele Dank schonmal!
Christian
spess53 schrieb:> Hi>>>TCCR1C = (1<<FOC1A); // Aktiviere Output Compare für Channel A>>TCCR3C = (1<<FOC3A);>>TCCR2B = ... | (1<<FOC2A);>>TCCR0B = ... | (1<<FOC0A);>> Ist zwar nicht für dein Problem zuständig. Aber diese Bits machen nicht> das, was du denkst.>> MfG Spess
Achja, danke Spess, dass Thema hatten wir ja neulich schon. Werd ich
nochmal überprüfen bei Zeiten! Danke!
Christian B. schrieb:> Den Interrupt in Timer0 brauche ich theoretisch nicht dringend, nur ist> es schöner die Vergleichswerte zu setzen, wenn der Timer gerade> übergelaufen ist, als den Wert zufällig mitten im Zählvorgang zu setzen!
Soweit ich mich erinnere sind die OCR Register in den PWM-Modi
entsprechend gepuffert, um ebendies zu verhindern.
A. K. schrieb:> Christian B. schrieb:>>> Den Interrupt in Timer0 brauche ich theoretisch nicht dringend, nur ist>> es schöner die Vergleichswerte zu setzen, wenn der Timer gerade>> übergelaufen ist, als den Wert zufällig mitten im Zählvorgang zu setzen!>> Soweit ich mich erinnere sind die OCR Register in den PWM-Modi> entsprechend gepuffert, um ebendies zu verhindern.
Das ist gut zu wissen. IM CTC Mode sind sie es nicht denke ich,
zumindest sind die Zeiger manchmal willkürlich gesprungen wenn neue
Werte kamen und das erkläre ich mir damit, das dass Register beschrieben
wird während der Zähler noch zählt und dann kann es passieren, dass der
Counter bis TOP (16bit) zählt und die Frequenz dementsprechent abfällt.
Die Temperaturanzeige (Timer2) funktionierte vorher auch ohne Interrupt,
ich dachte nur so ist es "richtiger".
>> IM CTC Mode sind sie es nicht denke ich,
Schön. Aber Du benutzt ja nicht den CTC Mode sondern den Fast-PWM-Mode.
Da sind die Verhältnisse anders. (Mach Dir nichts draus. Den selben
Fehler habe ich neulich beim STM gemacht).
Ich sehe Du bist willens ausführlich zu antworten. Das ist gut.
Was hier eigentlich eine Rolle spielt, ist das tatsächliche Verhalten.
Das es mit dem gewünschten Prescaler nicht so läuft wie erwartet habe
ich verstanden. OK. Genauer soll das wohl heissen, dass das
Tastverhältnis der PWM dem via USB kommandierten entspricht.
Wie wir jetzt sehen benutzt Du den USB Port und nicht die serielle
Schnittstelle. Dazu fehlt der Code für die Initialisierung und das
auslesen.
Da Du leider nichts messen kannst muss man den Code verändern um weitere
Informationen zu bekommen.
Mir fällt auf, das Du in der while-Schleife in main für relativ lange
Zeit die Interrupts sperrst.
while(!usb_serial_available()){;}// Tue nichts, solange keine Daten im Puffer
3
cli();// Deaktiviere Interrupts, damit Lesen.. der Daten nicht unterbrochen wird
4
usb_serial_readline(data,DATA_NC);// Daten aus Puffer auf Char-Array lesen
5
data[DATA_NC]='\0';// Char-Array terminieren
6
split_data(data);// Daten aus Protkoll extrahieren
7
usb_serial_flush_input();// Puffer löschen
8
sei();// Interrupts wieder aktivieren
Generell ist es eine schlechte Idee für so lange Zeit die Interrupts zu
sperren. Es ist zu vermuten, das da was schief laufen kann.
Um auszuschliessen (ich vermute es), das dort zuviel Zeit verbraucht
wird, schlage ich vor, das Du dort einmal die PWM Ratio in einer
Schleife sozusagen "manuell" setzt, also nicht aufgrund der eingelesenen
Daten und die Interrupts nicht sperrst.
1
uint8_ti=0;
2
while(1){
3
fuel_val=i++;
4
delay(..);// so eine Zeit bei der die PWM mindestens einen Zyklus durchläuft
5
}
Dann wird also immer der compare wert von 0 bis 255 laufen und wieder
bei 0 beginnen.
> Diese Terminierung landet aber hinter dem Array.
Achja richtig... früher stand mal char data[DATA_NC+1]. Zuzeit addiere
ich das +1 im Makro. sollte ich ändern, danke!
@Noname:
Danke ich werde damit mal experimentieren und mich wieder melden!
Edit: Aber wenn ich jetzt für Timer0 gar keine Interrupts mehr benutze,
sondern direkt nach einlesen der USB-Daten das Register beschreibe und
dann nichts weiter tue, dürfte das Deaktivieren und Aktivieren der
Interrupts gar keine Auswirkungen haben oder? Oder nutzen die Timer
intern auch irgendwelche Interrupts die mich mit cli(); deaktiviere?
Edit 2: Ich hab jetzt mal alles in Main in der while(1) schleife
auskommentiert und durch den folgenden Code ersetzt. Die Overflow
Interrupts für die beiden PWM Timer habe ich wieder deaktiviert. Sodass
der Wert direkt ins Register geschrieben wird.
1
inti=0;
2
while(1)
3
{
4
OCR0B=i;
5
i++;
6
_delay_ms(100);
7
}
Zeiger macht keinen Mucks...
Den usb_serial Code habe ich angehängt.
>Aber wenn ich jetzt für Timer0 gar keine Interrupts mehr benutze,>sondern direkt nach einlesen der USB-Daten das Register beschreibe und>dann nichts weiter tue, dürfte das Deaktivieren und Aktivieren der>Interrupts gar keine Auswirkungen haben oder? Oder nutzen die Timer>intern auch irgendwelche Interrupts die mich mit cli(); deaktiviere?
Schreibe am besten mal Code als Beispiel. Wenigstens Pseudocode. Mir
wird nicht klar, wie Du das meinst.
Der Knackpunkt ist, das Du die Interrupts überhaupt deaktivierst. Dein
Kommentar, das dadurch sichergestellt sein soll, das die Daten korrekt
gelesen werden, deutet auf einen Designfehler hin. Es ist grundsätzlich
eine schlechte Idee Interrupts für lange Zeit zu deaktivieren.
Du magst für den Timer 0 keinen Interrupt mehr benutzen, hast dann aber
immer noch das grundsätzliche Problem nicht gelöst. Das besteht darin,
das Du das einlesen und verarbeiten der Daten mit einem Minimum an
Interruptsperren, bzw. einer minimalen Zeit während der die Sperre gilt,
erledigen musst. Im konkreten Fall, hast Du ja noch andere
Timer-Interrupts die auch auf Probleme führen können.
An dieser Stelle schliesst sich der Kreis in Hinblick auf konkrete und
detaillierte Beschreibung. Du solltest eine Vorstellung davon haben, wie
die zeitlichen Abläufe in Deinem Programm genau sind. Wie höufig treten
Interrupts auf, wie lange dauert die Verarbeitung von Eingaben und wann
wird sie in Relation zu den Interrupts erledigt? Ist das Design so, das
die Verarbeitung von Eingabedaten von der eigentlichen Eingabe
entkoppelt ist?
Um Deine konkrete Frage zu beantworten: Nein. Timer benutzen keine
sekundären (undokumentierten) Interrupts. Keine anderen als genau die,
welche Du aktivierst.
Danke nochmal, dass du deine Zeit am Neujahrestag für sowas opferst! ;-)
Also später kommen die Daten im 10ms Takt an den µC und mit Drehzahl
Geschwindigkeit und Temperatur funktioniert das auch alles schon.
Für Testzwecke sende ich die Daten einmalig mit einem Terminalprogramm,
sodass die Zeiger auf fixe Werte springen und da verharren ( zb. 1000rpm
und 100kmh und 90°C) normalerweise kommen die Werte im 10ms Takt und die
Zeiger bewegen sich dann.
Die Timer für Geschwindigkeit und Drehzahl arbeiten im CTC Mode und
generieren Frequenzen für die Schrittmotoren. Diese Frequenzen werden
jedoch nicht direkt nach dem einlesen gesetzt, sondern
zwischengespeichert und gesetzt wenn der Compare Match Interrupt
ausgelöst wird.
Die Timer für Temp. und Tank laufen im FastPWM Modus. Die Grundfrequenz
wird fest im Programm vorgegeben und die Tastverhätlnisse werden
eingelesen und direkt gesetzt ohne irgendwelche
while(1)
{
warte_auf_werte{};
werte_lesen{};
}
werte_lesen
{
drehzahl auslesen
geschw. auslesen
temp. auslesen UND direkt in OCR Register schreiben
tank auslesen UND direkt in OCR Register schreiben
}
ISR_1
{
drehzahl in OCR Register schreiben
}
ISR_3
{
geschw. in OCR Register schreiben
}
Ich verstehe nicht ganz wieso das Deaktivieren der Interrupts so ein
Problem ist. Wenn die Interrupts deaktiviert sind, dann bekommen die
Timer halt keine neuen Werte und behalten die aktuellen Compare-Werte
bei.
sein.
>Zeiger macht keinen Mucks...
Mach evtl. das Delay länger, falls der Motor zu träge ist (Hier fehlt
wieder Information)!
Du kannst auch den Prescaler nochmal hochsetzen, damit man sieht ob es
prinzipiell geht.
Am besten versuche erstmal das zum laufen zu bringen ohne USB (und ohne
das zu initialisieren).
Ich habe mal so über den USB-Code drüber geschaut. Ich bin zwar nicht
wirklich qualifiziert, dass zu beurteilen, was den konkreten Code
betrifft, aber ich sehe das die Interruptroutine elend lang ist und
haufenweise die Interrupts disabled werden, damit, laut Kommentar, die
Funktionen auch im Hauptprogramm zu benutzen sind.
Die USB-Interrupts haben sowieso höhere Priorität als die
Timer-Interrupts. Da kann schon was schiefgehen.
Als das mit dem schnellen Timer 0 funktioniert hat, war das auch mit
USB, oder ohne?
So funktioniert der Timer wie er soll und ich kann Pulsbreiten übers
Terminalprogramm vorgeben und die Tankanzeige stellt sich auf die Werte
ein.
Nagut also mir wird nicht viel übrig bleiben als rumzuprobieren bis
irgendwas läuft...
Ich danke euch und besonders dir, Noname, für die Hilfe!
>Wenn die Interrupts deaktiviert sind, dann bekommen die>Timer halt keine neuen Werte und behalten die aktuellen Compare-Werte bei.
Genau. Aber was ist dann das Problem? Da drehen wir uns im Kreis, denn
es fehlt nach wie vor eine Fehlerbeschreibung welche die
Ursache-Wirkungskette entlanggeht, die beteiligten Elemente und
Schaltung beschreibt und erwartetes in eine Beziehung zu tatsächlichem
Verhalten setzt.
Es ist doch so. Entnehme ich Deinen Texten, das alles geht, auch die
USB-Kommunikation resp. das dynamische setzen der PWM-Ratio, wenn der
Takt für Timer 0 langsam ist, aber nicht mehr wenn er schnell ist, dann
untersuche ich die Auswirkungen dieses schnelleren Takts.
Da ich aber nicht in Realzeit untersuchen kann (Du hast kein Oszi) muss
ich anhand von plausiblen Vermutungen den Code ändern um Informationen
zu bekommen.
Es geht jetzt nicht zuerst darum das Pgm. zum laufen zu bringen,
sondern die Auswirkungen zu sehen um dann Rückschlüsse auf die
eigentliche Ursache zu ziehen.
>So funktioniert der Timer wie er soll und ich kann Pulsbreiten übers>Terminalprogramm vorgeben und die Tankanzeige stellt sich auf die Werte>ein.
Na super. Und ich bin hier noch am schreiben. :-)
>Nagut also mir wird nicht viel übrig bleiben als rumzuprobieren bis>irgendwas läuft...
Das ist Unsinn (mit Verlaub), ist ineffizient und lehrt Dich nichts.
Sieh es mal positiv. Wir können jetzt mit groesserer Sicherheit
vermuten, dass das Problem die so elendig lang gesperrten Interrupts
sind.
Ich würde mich nicht sehr wundern, falls Deine ursprüngliche Schleife
(Beitrag "Re: Problem mit unterschiedlichen Prescalern") funktioniert
sobald Du nur die cli/sei herausnimmst. Zum testen in grossen Abständen
(im 5s Bereich neue Daten senden).
Falls das so ist müssen wir uns eine Lösung für
> // Deaktiviere Interrupts, damit Lesen.. der Daten nicht unterbrochen wird
überlegen.
Du hast ja recht...
also:
ich habe den Code "entschlackt", d.h. ich habe das usb_init
auskommentiert, den ganzen anderen Kram in der while(1) auskommentiert
und im timer_init alle Timer ausser Timer0 auskommentiert.
OCR2A = 255; // Wert für PWM-Grundfrequenz (ca. 245 Hz)
56
OCR2B = 95; // Wert für Tastverhältnis bei Initialisierung -> 90°C
57
*/
58
59
// 8Bit-Timer0 für Tank
60
TCCR0A=(0<<COM0B0)|(1<<COM0B1)|(0<<COM0A0)|
61
(0<<COM0A1)|(1<<WGM00)|(1<<WGM01);
62
TCCR0B=(0<<WGM02)|(0<<CS02)|(0<<CS01)|
63
(1<<CS00)|(1<<FOC0A);
64
OCR0A=160;
65
OCR0B=5;
66
}
Die Tankanzeige geht langsam von leer zu voll! --> funktioniert!
Also usb_init() wieder aktiviert --> funktioniert immer noch!
Die anderen Timer wieder aktiviert --> funktioniert nicht mehr!
Da sind wir doch schonmal einen Schritt weiter =)
Okay also die FOC Bits sind raus. Dennoch geht Timer0 nicht wenn Timer3
aktiviert wird.
Beim Thema Schaltung hast du mich gerade auf eine Idee gebracht!
Ich habe eben mal das Tacho-Signal abgezogen und dann hat sich der
Tankzeiger angefangen zu bewegen! Das ganze scheint kein Problem in
meinem Programm zu sein, sondern irgendeine Plausibilitätsprüfung in der
Tacho-Elektronik oder so...
Oh man -.-
>Okay also die FOC Bits sind raus. Dennoch geht Timer0 nicht wenn Timer3
aktiviert wird.
Egal. Die waren ohnehin falsch. Also lass sie draussen.
>Oh man -.-
Ich will ja nicht sagen: "Ich habs ja gesagt", aber an der Stelle zeigt
sich wie wichtig vollständige Information ist.
Es gibt jedenfalls keinen Zusammenhang zwischen Timer 0 und Timer 3 der
das Verhalten erklären würde. Habe eben nochmal das Datenblatt
angeschaut.
Ja ich habe selber leider kaum Information zu dem Tacho finden können,
ausser der Pin-Beleguung (Passat B5 KI)
Jetzt muss ich den Tacho also noch irgendwie "austricksen"...
Danke für deine Hilfe!!!
>Ja ich habe selber leider kaum Information zu dem Tacho finden können,>ausser der Pin-Beleguung (Passat B5 KI)
Aber wenigsten der Tachotyp wären schon eine Information gewesen. Kann
ja sein, das einer schonmal das Problem hatte.
Naja. Schwamm drüber. Viel Spass noch. Wenn ich Dich treffe kost das n
Kaffee. ;-)
Noname schrieb:>>Ja ich habe selber leider kaum Information zu dem Tacho finden können,>>ausser der Pin-Beleguung (Passat B5 KI)>> Aber wenigsten der Tachotyp wären schon eine Information gewesen. Kann> ja sein, das einer schonmal das Problem hatte.>> Naja. Schwamm drüber. Viel Spass noch. Wenn ich Dich treffe kost das n> Kaffee. ;-)
Nichts lieber als das! ;-)
Schönen Abend noch!
Falls es noch jemanden interessieren sollte:
Es scheint so zu sein:
Beim Einschalten der Zündung im Stand liegt kein Tachosignal vor und die
Tankanzeige kann direkt auf den aktuellen Wert springen.
Sobald ein Geschwindigkeitssignal anliegt wird der Wert scheinbar
gefiltert oder über lange Zeit ein Mittelwert gebildet, denn wenn ich
lange genug Warte bewegt sich der Zeiger tatsächlich! Das ist mir zuvor
gar nicht aufgefallen!