Hallo,
folgendes Problem tritt bei mir auf, den Code werde ich bei bedarf auch
noch posten, nur brauch ich etwas um die relevanten Teile rauszusuchen.
Jedenfals, bei unserem Programm nutzen wir den Timer0 um eine CLK für
einen D/A-Wandler zu generieren. Mit folgenden Einstellungen für Timer0:
Teiler steht auf 0x01, Vorzähler steht auf (256-120). Der Timer1 wird
für die Tasterabfrage verwednet und verwendet folgende Einstellungen:
Teiler ist auf 0x01 und Vorzähler ist (65536 - 3125). Die CLK soll also
ca. 64kHz haben und die Tastaturabfrage alle 50ms den Tastenstatus
abfragen.
Leider stimmt das Timing der CLK nicht mehr sobald Timer1 eingeschaltet
wird, dass heißt auch schon ohne den ISR Befehl.
Woran liegt das und wie kann ich das Problem umgehen/beheben?
Grüße Sebastian
Sebastian Baier wrote:
> Hallo,> folgendes Problem tritt bei mir auf, den Code werde ich bei bedarf auch> noch posten, nur brauch ich etwas um die relevanten Teile rauszusuchen.
Wie wäre es denn erstmal mit Informationen über den verwendeten
Controller und die Programmiersprache? Ohne die kann man nämlich
überhaupt nichts Konkretes sagen!
> Jedenfals, bei unserem Programm nutzen wir den Timer0 um eine CLK für> einen D/A-Wandler zu generieren. Mit folgenden Einstellungen für Timer0:> Teiler steht auf 0x01, Vorzähler steht auf (256-120).
Wer ist der Vorzähler?
Meinst Du vielleich den Reload-Wert?
> Leider stimmt das Timing der CLK nicht mehr sobald Timer1 eingeschaltet> wird, dass heißt auch schon ohne den ISR Befehl.
ISR-Befehl? Also ist es vielleicht ein AVR und C?
Tut mir leid,
es ist ein ATmega16 und programmiert wird in C mit dem AVR Studio.
Außerdem sollte ich vll noch sagen das der uC mit 16MHz betrieben wird.
Ich hab jetzt einfach mal ein kleines Programm geschrieben um das
Problem zu verdeutlichen. Hier werden einfach 2 Clock Signale erzeugt,
Timer0 erzeigt ein Signal auf PINA0 und Timer1 auf PINA7. Ich habe
mittleerweile ein wenig damit gespielt und festgestellt, sobald ich
einen Startwert für den Timer1 bestimme (in diesem Code INTRP1), ist das
Signal vom Timer0 nicht mehr zu gebrauchen und zwar verschiebt sich die
Phase ständig. Wenn ich die Timer allerdings nur einen Takt zählen
lasse, so wie in meinem Beispielcode, dann ist das Signal sauber.
Es wäre nett wenn mir einer den Zusammenhang bei diesem Problem erklären
könnte und vll eine Lösung hat.
P.S.: Manche Werte für den Startwert funktinieren gut und manche nicht,
(65536 - 32768) für INTPR1 funktiniert super, (65536 - 2075) ist richtig
schlecht.
P.P.S: Wenn ich INTPR1 und INTPR0, also die beiden Startwerte
undefiniert lasse und alle zeilen in denen die Variablen vorkommen
auskommentiere, so das beide Timer voll durchzählen. Dann ist das Signal
des Timer0 auch richtig unbrauchbar, Timer1 allerdings ist in Ordnung.
DA-CLK.c
Weshalb nicht den langsamen Timer begraben un den schnellen mit einem
counter versehen ? Dann alle 1000 Durchgaenge mal ein flag setzen, dass
dem Main erlaubt, die Taststur abzufragen ?
Beim ATMega16 haben alle Timer eine Compare-Einheit und können im
CTC-Modus betrieben werden. Das ungenaue und Ressourcen-raubende
Timer-Nachladen ist da überflüssig!
Abgesehen davon solltest Du mal überlegen, wie lange die Bearbeitung
eines Interrupts dauert und wieviel Zeit der Controller zwischen zwei
Interrupts hat, wenn Du die Timer beide mit ungeteiltem CPU-Takt
versorgst und schon nach jeweils einem einzigen Takt einen Interrupt
haben willst. Das geht so überhaupt nicht! Wenn Du Taktsignale in dem
Frequenzbereich erzeugen willst, dann geht das nur per Hardware. Über
die Compare-Ausgänge der Timer kannst Du maximal F_CPU/2 ausgeben, und
das komplett ohne Interrupt.
@ Aha
Am Ende wird uns nichts anderes übrig bleiben, aber der Gedanke dahinter
warum wir das so gemacht haben war, warum den Prozessor mit einer
Zählvariablen belasten wenn ich doch Timer habe die das erledigen.
Allerdings interessiert mich trotzdem warum das so ist.
@ Johannes M.
Aber es funktioniert ja wenn ich schon nur nach einem Takt einen
Interrupt haben will. Es sind nur bestimmt Werte die kein sauberes
Ergebnis liefern. Über den Comparebetrieb habe ich noch garnicht
nachgedacht, um ein CLK auszugeben ist das wohl die beste Lösung.
Sebastian Baier wrote:
> Am Ende wird uns nichts anderes übrig bleiben, aber der Gedanke dahinter> warum wir das so gemacht haben war, warum den Prozessor mit einer> Zählvariablen belasten wenn ich doch Timer habe die das erledigen.
Genau. Aber Du nutzt die zur Verfügung stehende Timer-Hardware gar nicht
richtig. Die kann nämlich nach einmaliger Konfiguration völlig
selbständig Signale an bestimmten Pins ausgeben, ohne dass das Programm
überhaupt noch eingreifen muss. Wie oben schon gesagt: Timer-Reload ist
Murks.
> Allerdings interessiert mich trotzdem warum das so ist.
Siehe oben. Und mit den Startwerten im Beispielcode geht es eben gar
nicht, weil der µC theoretisch jeden einzelnen CPU-Takt zwei Interrupts
bearbeiten müsste.
Sebastian Baier wrote:
> @ Johannes M.> Aber es funktioniert ja wenn ich schon nur nach einem Takt einen> Interrupt haben will.
Das ist eine glatte Lüge! Ein Interrupt-Handler hat bereits einen
Overhead von mindestens 10 CPU-Takten (absolutes Minimum, durch die
Hardware gegeben), wo noch mal durch Register-Sichern einige Takte
hinzukommen. Ein Interrupt-Handler kann realistisch höchstens ungefähr
alle 30-40 CPU-Takte aufgerufen werden, ohne dass Ereignisse verloren
gehen.
Wenn ein Interrupt-Ereignis auftritt, wird zunächst mal der gerade
laufende Befehl zuende abgearbeitet, was schon mal 1-3 Takte Verzögerung
bedeuten kann. Anschließend benötigt der Sprung in den Interrupt-Vektor
4 CPU-Takte. Dort steht dann i.d.R. ein Sprungbefehl zum eigentlichen
Handler, der nochmal 2 oder 3 Zyklen (je nachdem, ob es ein rjmp oder
ein jmp ist, beim Mega16 i.d.R. ein jmp). Dann werden vom Compiler
Register gesichert, was im Minimalfall (nur SREG sichern) allein 3
Zyklen (ein in und ein push ) benötigt. Erst danach, also nach
minimal 10 Zyklen beim Mega16, wird das gemacht, was in dem Handler
steht. In der Realität ist es eher mehr.
Hinzu kommt noch das Nachladen des Timers, was speziell beim
16-Bit-Timer 1 einige Zyklen benötigt. Dann wird ein Flag gesetzt. Dann
werden die Register wieder zurückgeschrieben. Dann wird ins
Hauptprogramm zurückgesprungen. Dann muss die Flag-Abfrage-Schleife
erstmal zu der richtigen Stelle kommen. Erst dann wird überhaupt auf das
Ereignis reagiert, indem ein Portpin getoggelt wird. Hochrechnungen
ergeben vom Auftreten des Interrupt-Ereignisses bis der Portpin geändert
ist minimal ungefähr 50-60 CPU-Takte...
Natürlich sind die Takte die der Prozessor selber für die Interrupt
braucht nicht mitgerechnet. Aber Fakt ist, wenn ich den Code so wie er
oben steht verwende, hab ich 2 gute Rechntecksignal. Trotzdem hast du
recht das der Comparebetrieb besser wäre, allerdings bin ich noch nicht
auf den Trick gekommen wie ich 64kHz mit dem Timer0 und einem Systemtakt
von 16MHz erreiche.
Sebastian Baier wrote:
> Natürlich sind die Takte die der Prozessor selber für die Interrupt> braucht nicht mitgerechnet. Aber Fakt ist, wenn ich den Code so wie er> oben steht verwende, hab ich 2 gute Rechntecksignal.
Klar. Aber die Frequenz Deiner Rechtecksignale ist nicht die Frequenz,
mit der der Timer überlaufen sollte, sondern die Periodendauer
repräsentiert die Zeit, die der Controller für den ganzen oben
beschriebenen Rotz benötigt. Und es kann passieren, dass es bei
bestimmten Werten zufällig ein stabiles Rechtecksignal gibt.
Gut, schließen wir das ab, es ist also nicht auf meinem Weg klug ein
Rechtecksignal zu konztruieren. Ich beschäftige mich jetzt gerade mit
dem Compare betrieb, lese ich das richtig, dass der Asugabepin
festgelegt ist auf PB3 beim ATmega16?
> Trotzdem hast du> recht das der Comparebetrieb besser wäre, allerdings bin ich noch nicht> auf den Trick gekommen wie ich 64kHz mit dem Timer0 und einem Systemtakt> von 16MHz erreiche.
Wieso Trick? Du willst ein Rechtecksignal mit 64 kHz? Dann musst Du nur
ein bisschen Rechnen. Erstmal CTC-Modus für den Timer wählen. Dann den
Compare-Output-Pin so konfigurieren, dass er bei jedem Compare-Ereignis
getoggelt wird. Dann überlegen, wie man von 16 MHz auf 64 kHz kommt.
16000000/64000 = 250. Irgendwie müssen also 250 CPU-Takte für eine
Periode des Signals vergehen. Da eine Periode eines Rechtecksignals aber
aus zwei mal Umschalten des Pins besteht, sind es 125 Takte zwischen
zwei mal Toggeln. Mit einem Prescaler größer als 1 ist da sinnvoll
nichts zu machen, also Prescaler auf 1 (ungeteilter Systemtakt) stellen.
Und jetzt noch bedenken, dass das Umschalten des Portpins erst einen
Takt nach dem Auftreten der Übereinstimmung von Zählregister und
Compare-Wert geschieht. Daraus ergibt sich für den Compare-Wert 124. Das
gibt ein sauberes 64 kHz-Signal an dem betreffenden Pin.
Da 124 kleiner als 255 ist, kann man das mit jedem der drei Timer
machen, auch mit den 8-Bit-Timern.
Sebastian Baier wrote:
> Gut, schließen wir das ab, es ist also nicht auf meinem Weg klug ein> Rechtecksignal zu konztruieren. Ich beschäftige mich jetzt gerade mit> dem Compare betrieb, lese ich das richtig, dass der Asugabepin> festgelegt ist auf PB3 beim ATmega16?
Es gibt mehrere Ausgänge. Die Timer 0 und 2 haben je einen, Timer 1 hat
zwei davon. Und ja, die sind fest verdrahtet.
PB3 (OC0) wäre der Ausgang von Timer 0.
Sebastian Baier wrote:
> Wäre es denn möglich 2 verschiedene Signal mit dem Timer1 zu erzeugen?
2 Unterschiedliche Signale: Ja. Aber mit derselben Frequenz!
> Denn er hat ja auch 2 Ausgabepins.
Ja, für zwei PWM-Signale mit gleicher Frequenz, aber unterscheidlichem
Tastverhältnis.