Hallo, ich habe einen Atmel Mega 8 mit 3,6864 MHZ. Ich möchte einen timer erstellen, der genau 1 mal in der Sekunde aktiviert wird. Wie ich den Tutorial http://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer habe ich überlegt: 3,6864 Mhz bedeutet in der Sek. werden 3686400 Taktzyklen generiert. Durch eine nVorteiler von 64 wird der timer 57600 mal in der Sekunde erhöht. Es werden in der Sekunde also 57600/256 = 225 overflows generiert. Ich habe also überlegt, nur jedes 225 te zu werten und dann immer die Aktion, hier Led umschalten durchzuführen. Jedoch blinken die Leds wesentlich schneller als einmal in der Sekunde. Was könnte das sein ? Hier der Quellcode .include "m8def.inc" .def temp = r16 .def leds = r17 .org 0x0000 rjmp main ; Reset Handler .org OVF0addr rjmp timer0_overflow ; Timer Overflow Handler main: ldi temp, LOW(RAMEND) ; Stackpointer initialisieren out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ldi temp, 0xFF ; Port B auf Ausgang out DDRB, temp ldi leds, 0xFF ldi temp, 0b00000011 ; CS00 setzen: Teiler 1 out TCCR0, temp ldi r20,0b11100001 ldi temp, 0b00000001 ; TOIE0: Interrupt bei Timer Overflow out TIMSK, temp sei loop: rjmp loop timer0_overflow: ; Timer 0 Overflow Handler go: inc r20 brne end out PORTB, leds com leds ldi r20,0b11100001 end: reti
Jedes mal wenn ein overflov von R20 ist, wird R20 auf 0xE1 gesetzt. Du solltest R20 auf 256-225 setzen.
inc r20 brne end out PORTB, leds com leds ldi r20,0b11100001 entweder dec r20 oder ldi r20, -225 Du zählst nur von 225 bis 0, dementsprechend nur 30 Timer-overflows.
Danke. Wenn ich es auf 256 minus 225 stelle funktionert es. Jetzt habe ich aber ein Verständnusproblem. Ich möchte doch 225 und nicht 31 Durchläufe machen. und m.E. macht timer0_overflow: ; Timer 0 Overflow Handler go: inc r20 brne end out PORTB, leds com leds ldi r20,0b11100001 end: reti 225 Durchläufe, ich verstehe das so: Wenn ein overflow ist, dann wird zu timer0_overflow: gesprungen. Dann wird r20 um eins kleiner. Ist es nicht identisch mit null, wird auf den nächsten timer overflow gewartet und es waren noch keine 225 Durchläufe. Ist es Null, dann wird "brne end" übersprungen und die Led gewechselt
incrementieren heisst erhöhen, also +1. Dementsprechend decrementieren -1.
Der Hund liegt in der ISR (Interrupt Service Routine) des Timers begraben. timer0_overflow: ; Timer 0 Overflow Handler go: das go: kannst du dir sparen. Du hast ja schon timer0_overflow als Einsprungmarke. Stören tut es aber auch nicht. Was du aber unbedingt machen solltest, ist das sreg auf den Stack zu schieben. Und nicht vergessen, es vor Beenden der ISR wieder abzuholen. inc r20 Bevor du das Register verwendest solltest du dessen Inhalt löschen. Meines Wissens nach ist der nach dem Neustart nämlich zufällig. Demnach ist auch die Dauer der "ersten Sekunde" die du abzählst zufällig brne end Vor brne musst du immer sagen, was du mit was verglichen hast. Also zum beispiel "cpi r20, 225" würde den Inhalt von r20 mit 225 vergleichen und je nach Wert von r20 bestimmte Bits im status-Register (sreg) setzten. brne schaut dann lediglich nach, welche Bits gesetzt sind, und springt dann entweder zur angegebenen Adresse, oder eben nicht. out PORTB, leds com leds Das sollte soweit passen ldi r20,0b11100001 Hier wirds dann etwas arg seltsam. Wenn ich das richtig verstanden habe, willst du doch mit r20 zählen, wie oft die ISR schon durchlafen wurde. Wenn das Programm diesen Punkt hier erreicht, dann ist r20 ja genau 225 (wenn du das Prog so änderst wie ich es vorgeschlagen habe). Wenn du hier also nochmal 225 reinschreibst ändert sich der Wert von r20 nicht. Beim Nächsten Aufruf der ISR wird r20 wieder um eins erhöht. Erreicht r20 den Wert 255 folgt als nächstes 0. Danach wird weiter aufaddiert bist 225. Dann werden die LEDs erst wieder geschaltet. Insgesamt hast du also 256 ISR-Aufrufe zwischen zweimal schalten. Besser ist es, wenn du "clr r20" schreibst. Dann wird der Inhalt von r20 hier gelöscht. Es wird also wieder von vorne mit Zählen angefangen. Bis eben wieder 225 erreicht ist und die LEDs geschalten werden. end: Wie schon erwähnt: das sreg wieder zurückspielen. reti Übrigens wird deine LED im 2-Sekunden-Takt blinken: AN-eine Sekunde-AUS-eine Sekunde-AN-... Ansonsten solltest du dir vielleicht noch das Tutorial hier auf der Seite durchlesen (link ist links oben). Das dürfte bei klarheit schaffen. Gruß, Sebastian
Hi Sebastian, Danke für deine Anregungen. Ist die erste Sekunde wirklich Zufall? Ich habe ja in der main Schleife, die ganz am Anfang durchlaufen wird auch ldi r22.... geschrieben, genau das war meine Absicht, dass es eben beim ersten Durchlauf auch einen Wert hat. "Hier wirds dann etwas arg seltsam." ich habe an dieser Stelle r22 wieder den vollen Wert gegeben, für die weitere Runde. Es geht ja dann alle wieder von vorne los.
Oh, sorry. Habe übersehen, dass r20 initialisisert wird. Das ist dann in Ordnung. So richtig funktionieren kann es aber halt nicht. An sich hast du zwei möglichkeiten. 1. die von dir angedachte: Man lädt 255 in r20, zieht jedesmal eins ab. wenn Null erreicht ist, schaltet man die LEDs und lädt wieder 255 nach r20. Dann muss man aber aus "inc r20" "dec r20" machen. inc erhöht um ein, dec verringert um eins. und halt die Sache mit dem "brne". Das muss wissen, was verglichen wurde. Dafür schaut es ins sreg. Im sreg steht aber halt nichts über deinen Vergleich von r20 mit 0, weil du ihn ja nicht ausgeführt hast. Die andere Möglichkeit wäre eben, dass man "0" nach r20 lädt und r20 immer um eins erhöht und dann eben mit 255 vergleicht Schau einfach mal ins Tutorial. Dann werden auch die Hintergründe klarer. Sebastian
Ja inc und dec habe ich verwechselt. Mit dieser Korrektur funktioniert es Bei brne habe ich nichts korrigiert, es klappt trotzdem.
"im sreg steht aber halt nichts über deinen Vergleich von r20 mit 0, weil du ihn ja nicht ausgeführt hast." Noch mal nachdenken!
Oh, richtig. inc setzt das zero-flag. Bleibt nur noch die Frage, ob das dem Programmierer auch bewusst war ;-) Sebastian
ja, das hab ich mir so gedacht. Danke, dass ihr mir geholfen habt ich habe vor eine Uhr zu machen.
Jochen wrote: > ja, das hab ich mir so gedacht. > Danke, dass ihr mir geholfen habt ich habe vor eine Uhr zu machen. Dann solltest du dir den Overflow Interrupt gleich wieder aus dem Kopf schlagen. Im angegebenen Tutoriums Artikel steht auch warum das so nichts wird. Und es steht auch drinnen, wie man es stattdessen mit dem CTC Modus des Timers machen kann.
Da steht beim overflow gibt es keien ganzen Zahlen, deswegen entstehen Ungenauigkeiten. Mit meinem Takt geht es aber exakt auf, siehe Rechnung ganz oben.
@Karl heinz: Im Prinzip hast Du recht. Aber hier ist der Sonderfall, dass Jochen ohne Timer-Reload arbeitet, da er den vollen Zählumfang von 256 bei Vorteiler 64 nutzt. Die Reload-bedingten Differenzen treten also nicht auf. Auch der Interrupt-Abstand von 4,44 ms ist groß genug, um keinen Interrupt zu verpassen. Und wie es aussieht, hat er den nachfolgenden Software-Timer inzwischen auch in den Griff bekommen. Und die ISR-Frequenz von 225 Hz (4,44 ms) ist auch noch geeignet, die Anzeige zu multiplexen und die Taster effizient zu entprellen, letzteres notfalls nur zu jedem zweiten oder vierten Aufruf. Wenn er keine Zehntel oder Hundertstel Anzeigen will, dann ist das keine schlechte Wahl, ich habe (hier) schon schlechtere Vorgehensweise bei Timer-Berechnungen gesehen. Also alles im grünen Bereich, weiter so, Jochen... Die Anzeigen (siehe anderen Thread) kannst Du direkt am AVR verwenden. Die 7 Segmente über Widerstände 330 Ohm an die Portpins, die 4 Digit-Anoden über Transistoren an Vcc, angesteuert über 4 weitere Portpins. Dabei gehen sowohl PNP-Transistoren mit Basiswiderstand in Emitterschaltung als auch NPN-Transistoren in Kollektorschaltung (als Emitterfolger). ...
Sebastian wrote: > Oh, richtig. inc setzt das zero-flag. Bleibt nur noch die Frage, ob das > dem Programmierer auch bewusst war ;-) > > Sebastian Jau, dec übrigens auch :D Btw: Endlich mal jemand der nicht versucht eine Uhr per Delay-Schleife zu bauen *räusper. Jochen, ich würd dir empfehlen statt der Binärwerte bei zB
1 | ldi temp, 0b00000011 ; CS00 setzen: Teiler 1 |
eine andere Schreibweise anzugewöhnen. Und zwar die, mit dem Schiebeoperator. zB:
1 | ldi temp, (3<<CS00) ;CS00 und CS01 setzen |
oder
1 | ldi temp, (1<<CS00)|(1<<CS01) ;CS00 und CS01 setzen |
1 | x << y |
verschiebt den wert "x" um "y" binäre Stellen nach links, falls du es noch nicht weißt. Ist so jedenfalls viel lesbarer und fehler-unanfälliger (Also finde ich jedenfalls).
Vielen vielen Dank für die Tipps. Ich werde mir diese Schreibweise in Zukunft angewöhnen.
Jochen wrote: > Ich werde mir diese Schreibweise in > Zukunft angewöhnen. Alles zu seiner Zeit... Nimm am besten die Schreibweise, die für die jeweilige Zeile die beste Aussagekraft hat. Es ist sinnfrei, einen Schleifenzähler oder Timer-Vergleichswert binär, oktal, hex oder als ASCII-Zeichen anzugeben, wenn die stinknormale dezimale Angabe bedeutend besser lesbar ist. Einstellungen an Steuerregistern der internen Pripherie sollte man dann bei den Bitnamen nennen, wenn jedes Bit eine andere Bedeutung hat, den Vorteiler eines Timers fasse ich dabei gern zu einer Dezimalzahl zusammen, die als 'Nummer' für die Vorteiler-Liste aufgefasst werden kann. Es gibt nunmal mehrere (gleichberechtigte) Darstellungsformen für Konstanten. Man sollte daher nicht die wählen, die am coolsten aussieht, sondern die, die den Zweck am besten widerspiegelt. Für oft benötigte konstante Werte lohnt sich auch das Verwenden von Alias-Namen (sprechende Namen). Sie werden im Kopf des Quelltextes (mit .equ) vereinbart und können dann überall verwendet werden. Aber diese Feinheiten kommen so nach und nach. ...
Ja die Schreibweise mit den Pfeilen mag ich auch nich so ;) Nagut dann werde ich jetzt mal an der Uhr weitermachen. Um mit meinen Bauteilen auszukommen hab ich mir jetzt übrigens überlegt 4x Multiplexer zu nehmen. die können ja mit 4 Eingangskombinationen angesteuert werden und dann hab ich das in etwa so vor: 00 wird an Pins vom MC ausgegeben: In dieser Zeit wird die erste Stelle der Sekunden Anzeige beschrieben 01 wird an Pins vom MC ausgegeben: Die zweite Anzeige der Sekundenanzeige wird beschrieben 10 wird vom MC ausgegeben: Die erste Anzeige der Stunden wird ausgegeben 11 wird ausgegeben: Die zweite Anzeige der Stunden wird ausgegeben und wenn das ganz schnell immer wieder von vorne durchlaufen wird hoffe ich, dass die Uhrzeit angezeigt wird...
Jochen wrote: > Ja die Schreibweise mit den Pfeilen mag ich auch nich so ;) Ich finde die Tipperei auch nicht ergötzend, aber in bestimmten Fällen muss es zwecks Lesbarkeit einfach sein. > Nagut dann werde ich jetzt mal an der Uhr weitermachen. Um mit meinen > Bauteilen auszukommen hab ich mir jetzt übrigens überlegt 4x Multiplexer > zu nehmen. die können ja mit 4 Eingangskombinationen angesteuert werden > und dann hab ich das in etwa so vor: In Anbetracht der Tatsache, dass da für Dich Vieles noch Neuland ist, würde ich davon abraten. > > 00 wird an Pins vom MC ausgegeben: > In dieser Zeit wird die erste Stelle der Sekunden Anzeige beschrieben > > 01 wird an Pins vom MC ausgegeben: > Die zweite Anzeige der Sekundenanzeige wird beschrieben > > 10 wird vom MC ausgegeben: > Die erste Anzeige der Stunden wird ausgegeben > > 11 wird ausgegeben: > Die zweite Anzeige der Stunden wird ausgegeben > > > und wenn das ganz schnell immer wieder von vorne durchlaufen wird hoffe > ich, dass die Uhrzeit angezeigt wird... Überleg' doch mal, was willst Du und was hast Du. Du hast: - 'nen Mega8 mit - 8 Pins an Port D (bei Verzicht auf UART) - 6 Pins an Port B (nur 6 wegen Quarz) - 6 Pins an Port C (die gehen auch digital) Du willst: - 'ne Uhr mit 4 Ziffernanzeigen auf 7-Segment-LED-Basis Die braucht: - 7 (8) Portpins für die Segmente (Kathoden), sind die innerhalb eines Ports, dann wird die Programmierung einfacher - 4 Portpins für die Digit-Treiber, also für die Transistoren, die immer nur eine der 4 Anoden an H schalten - ein paar entprellte Taster zum Stellen der Uhr, möglichst auf einem Port Es bietet sich an: - PortD: (7) Segmente der Anzeige über Widerstände 330 Ohm - PortB: (4) Transistoren für Ziffern, (2) frei - PortC: einige Taster Du brauchst also: - ein Stück Lochraster-Platine - Mega8 - Keramik-Kondensatoren 100nF (Abblocken der Betriebsspannung) - Quarz mit 2 Keramik-Kondensatoren 22pF - 7 (8) Segmentwiderstände 330 Ohm - 4 NPN-Transistoren, z.B. BC337 - 4 7-Segment-Anzeigen - 3 oder 4 Taster (je nach Stellalgorithmus) - eine stabilisierte Stromversorgung 5V / 200mA Mehr fällt mir jetzt nicht ein, aber ich habe sicher was vergessen... ...
Jochen wrote: > Da steht beim overflow gibt es keien ganzen Zahlen, deswegen entstehen > Ungenauigkeiten. Mit meinem Takt geht es aber exakt auf, siehe Rechnung > ganz oben. Und denkst, der Quarz schwingt wirklich auf der Frequenz die aufgedruckt ist, ... Auch Quarze haben (kleine) Abweichungen. Gerade bei Uhren wirkt sich das aber aus, wenn nach 1 Woche 1 Sekunde fehlt. Aber du hast recht: Lass es erst mal mit dem Overflow und sieh zu, dass du den Multiplex noch hinkriegst. Die Timersteuerung der Uhr selbst kann man später immer noch ändern.
Wenn die Uhr dann falsch geht (dazu muss sie aber erstmal überhaupt "gehen", bis dahin ist noch ein weiter Weg), kann man immernoch auf "die genaue Sekunde" in der Codesammlung verweisen... ;-) ...
Hallo, ich habe mir jetzt diverse Bauteile bestellt und werde mal mein Bestes geben wenn sie angekommen sind. Was meint ihr denn wie ungenau wird die Uhr werden so wie ich sie gestern aufgeführt habe? Grüße Jochen
Da kann man nix meinen, mit viiiieeel Glück geht die genauer als 'ne Atomuhr der PTB, mit etwas Pech wird die Ungenauigkeit schon nach einer Woche (Oder weniger) spürbar.
Hannes Lux wrote: > ... > Es gibt nunmal mehrere (gleichberechtigte) Darstellungsformen für > Konstanten. Man sollte daher nicht die wählen, die am coolsten aussieht, > sondern die, die den Zweck am besten widerspiegelt. > ... Äh, schon klar, dass man nicht plötzlich alle Konstanten mit Bitnamen und Schiebeoperator angeben soll. Zum Beispiel bei eben diesem Prescaler, um den es hier geht. Der sollte, wie Hannes schon gesagt hat am besten dezimal angegeben werden. Aber: Gerade wenn man ein Stück Code in ein Forum stellt, wird man ein höheres "Publikum" erreichen, wenn man diese Schreibweise wählt um ein solches "Einstellungsregister" zu befüllen. Und nicht etwa binär oder gar dezimal. So kann man wirklich sicher gehen, dass wenigstens das funktioniert.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.