AVR ATmega16+Timer

Hallo,

ich habe das folgende Problem, und es will mir einfach keine Lösung einfallen:

Ich habe den Timer0 so programmiert, dass er alle 1ms einen Interrupt auslößt. In der Interrupt-Routine erhöhe ich einen 32-Bit-Zähler um eins und erzeuge mir so eine Systemzeit. Funktioniert auch prima.

Dann und wann brauche ich aber eine genauere Zeitangabe. Da habe ich mir gedacht ich kombiniere den 32-Bit-Zähler mit dem TCNT0-Register. Dieses Register läuft bei mir immer bis 250 (Inhalt von OCR0), dann wird es auf Null zurückgesetzt und ein Interrupt wird erzeugt.

Wie muß man das Auslesen des TCNT0-Registers und des 32-Bit-Zählers programmieren, so das man eine korrekte Zeit erhält?

Die Kombination

IN R16,TCNT0 CLI LDS R17,SYSTEMTIME ; uR17:R18:R19:R20="SystemTime" LDS R18,SYSTEMTIME+1 LDS R19,SYSTEMTIME+2 LDS R20,SYSTEMTIME+3 SEI

funktioniert am besten. Wie zu erwarten kommt es aber manchmal zu Problemen. Ich denke diese entstehen, wenn zwischen dem IN und dem CLI der Zähler auf Null springt und ein Interrupt erzeugt wird. Vertauscht man die Befehle jedoch gibt es viel häufiger Probleme. Das überrascht auch nicht, denn der Zähler läuft ja um, egal ob Interrupts erzeugt werden oder nicht.

Hat schon einmal jemand ein ähnliches Problem gehabt und eine Lösung ersonnen?

mfg Helmut

Reply to
Helmut Neemann
Loading thread data ...

Hallo

Du vergleichst am Ende nochmal TCNT0 mit R16. Wenn TCNT0 kleiner ist, weisst du, dass inzwischen ein Overflow stattgefunden hat und du springst einfach zurück und lädst alles nochmal. CLI und SEI können ganz entfallen.

Georg

"Helmut Neemann" schrieb im Newsbeitrag news: snipped-for-privacy@uni-berlin.de...

Reply to
Georg Meister

Ich denke der Vorschlag hat das gleiche Problem. Der Overflow kann immer noch nach dem Lesen von TIFR erfolgen und wird dann nicht bemerkt.

Georg

Reply to
Georg Meister

Georg Meister schrieb:

Das hat jedoch wenig Effekt auf die Gueltigkeit von TCNT0 und SYSTEMTIME. SYSTEMTIME wird nicht zwischenzeitlich upgedated (Interrupt gesperrt) und gilt fuer den gelesenen TCNT0.

servus thomas «

--
Die 4. Österreichische Fan-Convention zum Thema Japanische Popkultur
** AniNite 2004 ** 20.-22.August 2004 ** http://www.aninite.at/ **
 Click to see the full signature
Reply to
Thomas Mozgan

Georg Meister schrieb:

Stimmt. Ist die eleganteste Loesung.

servus thomas «

--
Die 4. Österreichische Fan-Convention zum Thema Japanische Popkultur
** AniNite 2004 ** 20.-22.August 2004 ** http://www.aninite.at/ **
 Click to see the full signature
Reply to
Thomas Mozgan

Ja richtig. Hatte ich nicht gesehen.

Georg

Reply to
Georg Meister

Hallo,

das hört sich richtig gut an, liefert aber das gleiche Ergebnis wie die Simpelimplementierung aus meinem ersten Posting.

Ich habe folgenden Code getestet:

hs1: IN R3,TCNT0 LDS R17,SYSTEMTIME LDS R18,SYSTEMTIME+1 LDS R19,SYSTEMTIME+2 LDS R20,SYSTEMTIME+3 IN R4,TCNT0 CP R4,R3 BRLO hs1

Bei einem Versuch ist in R3=9 zu finden und SYSTEMTIME wurde noch nicht erhöht. Aufgefallen ist mir jedoch, dass das Problem nur auftritt, wenn externe Interrupts, die ja eine höhere Priorität haben als der Timer, ausgelößt werden. Die Interrupts kommen aber nur mit 100Hz, können also nicht den Timer-Int auf Dauer lahmlegen.

Gruss vom ratlosen Helmut

Georg Meister wrote:

Reply to
Helmut Neemann

Ich hab den Verdacht, dass deine externen Interruptroutinen einfach zu lange dauern.

Das Programm funktioniert natürlich nicht, wenn irgendeine Interruptroutine den obigen Code für länger als 1/2 ms blockiert. Ausserdem muss auch der Timerint alle 1 ms seine Chance bekommen, damit die Systemzeit überhaupt Sinn macht.

Georg

Reply to
Georg Meister

Hallo,

gerade habe ich einmal die Zyklen gezählt: 533 Zyklen braucht meine externe Interrupt-Routine maximal. Bei 16MHz Systemtakt sind das etwa

33us. Bei 100Hz ergeben sich 53300 Zyklen/s was nur 3 Promille Auslastung ergibt. Die Interrupt-Routine für den Timer braucht nur maximal 76 Zyklen. Bei einem 1kHz sind das 76000 Zyklen/s, ist also nicht wesentlich mehr. Daran kann es eigentlich nicht liegen.

mfg Helmut

Georg Meister wrote:

Reply to
Helmut Neemann

Ist das simuliert oder hast den ATmega16 am JTAG-Debugger hängen?

Was du noch machen kannst:

Lass mal den 16-Bit Timer mit dem gleichen Takt wie den Timer 0 laufen und merk dir die TCNT1L/H Werte vor und nach den kritischen Routinen (H/L in der richtigen Reihenfolge auslesen!). Sozusagen als Stoppuhr.

Noch was: Wie stellst du das Problem eigentlich genau fest? Und die Häufigkeit?

Georg

Reply to
Georg Meister

Hast du geprüft daß sie 533 Zyklen immer nur einmal duechlaufen?

--
MFG Gernot
Reply to
Gernot Fink

Die Zyklen habe ich einfach abgezählt: Alle Zyklenzahlen der Assemblerbefehle addiert. ;-) Und die Diagnose des Problems ist einfach: Ich berechne pausenloss SYSTEMTIME*250+TCNT0 und erhöhe einen Zähler, wenn eine Berechnung ein kleineres Ergebnis liefert wie die letzte Berechnung. Diesen Zähler schaue ich mir dann an.

Der externe Interrupt wird von einem CAN-Controller SJA1000 ausgelöst, der einen Interrupt erzeugt, wenn er eine CAN-Botschaft empfangen hat. Meine Interrupt-Routine ließt dann die Botschaft aus dem Controller aus und schreibt sie in einen Buffer. Das Hauptprogramm beantwortet die Botschaft dann. Der AVR hat einen Teil des CCP-Protokolls implementiert, welches mir erlaubt beliebige RAM-Zellen per CAN auszulesen. Mit entsprechenden CAN-Diagnoseprogrammen kann man dann sehr komfortabel Analysen durchführen. Das geht natürlich längst nicht so Taktgenau wie mit einem JTAG-Interface, aber meistens reicht es.

mfg Helmut

Reply to
Helmut Neemann

Habe ich gerade getestet: Die Interrupt-Routine wird genau so oft Ausgeführt wie erwartet.

mfg Helmut

Reply to
Helmut Neemann

Ich denke ich weiss was dein Problem ist.

OCR0 ist falsch sollte 249 sein. TCNT0 läuft von 0 bis OCR0 nicht OCR0 - 1.

Als Ferndiagnose im laufenden Betrieb sicher praktisch. Zur Firmwareentwicklung aber unbrauchbar.

Mit einem JTAG-ICE setzt du ein paar Breaks (Timer wird dabei gestoppt), schaust dir die Register an und weisst sofort woran es liegt. Besorg dir doch so ein Ding (Nachbauten ab EUR 40), alles andere ist nur mühselige Raterei.

Schöne Grüsse Georg

Reply to
Georg Meister

Leider habe ich daran gedacht: OCR0 ist 249. ;-)

Dieses Interface ist eigentlich auch nicht für die Firmware-Entwicklung gedacht gewesen, sondern für die Diagnose der Applikation die unter der Firmware ausgeführt wird. Es ist im Moment aber das beste was ich habe.

Darüber habe ich auch schon einmal nachgedacht. Nur leider habe ich in diesem Projekt die Pins für das JTAG-Interface schon belegt. Wie sieht es denn mit Debugger-Software aus? Ich habe keine Erfahrung mit JTAG. Beinhaltet das AVRStudio einen brauchbaren JTAG-Debugger, oder ist weitere Software notwendig?

mfg Helmut

Reply to
Helmut Neemann

Vielleicht so:

NochEinmal: IN R16,TCNT0

LDS R17,SYSTEMTIME ; uR17:R18:R19:R20="SystemTime" LDS R18,SYSTEMTIME+1 LDS R19,SYSTEMTIME+2 LDS R20,SYSTEMTIME+3

LDS R?,SYSTEMTIME cp R?,R17 brne NochEinmal

Gruß Klaus Beier

Reply to
Klaus Beier

Schade. Du hattest im OP geschrieben, TCNT0 läuft bis 250 (Wert von OCR0).

Kann es nicht vielleicht doch sein, dass deine CAN-Interruptroutine zu lange dauert? Könnte ja ein Fehler drinnen sein und eine Schleife wird zu oft ausgeführt. Deswegen mein Vorschlag mit der Timer 1 als Stoppuhr. Oder der nächste CAN-Interrupt kommt schon, während die Routine noch läuft.

Das ist tatsächlich oft ein Nachteil, dass man dafür 4 Pins opfern muss. Aber Atmel will ja die neuen ATmegas mit DebugWire ausrüsten, dann läuft das über den Reset-Pin.

Nein geht alles mit AVR Studio.

Georg

Reply to
Georg Meister

Hallo,

ich glaube ich habe die Lösung gefunden. Der Vorschlag von Thomas Mozgan funktioniert nicht, da ich, wenn ich dem Timer-Int nur einmal die Chance gebe durchzulaufen, er nicht zum Zuge kommt, wenn ein Hardware-Int mit höherer Priorität stattdessen ausgeführt wird.

Diese leicht abgewandelte Routine wartet, bis tatsächlich der Timer-Int ausgeführt wurde:

p_hs1: CLI LDS R17,SYSTEMTIME ; uR17:R18:R19:R20="SystemTime" LDS R18,SYSTEMTIME+1 LDS R19,SYSTEMTIME+2 LDS R20,SYSTEMTIME+3 IN R16,TCNT0 IN R21,TIFR SEI ANDI R21,1

Reply to
Helmut Neemann

Ja das glaube ich auch dass das auf jeden Fall funktioniert.

Allerdings kann ich mir das Nichtfunktionieren der bisher diskutierten Vorschläge nur erklären wenn deine CAN-Interruptroutine tatsächlich viel länger dauert als angegeben. Das ist wahrscheinlich auch der Grund warum bei dir der Vorschlag von Thomas nicht funktioniert hat. Wenn du ein SEI machst, werden sämtliche offenen Interrupts in der Reihenfolge ihrer Priorität abgearbeitet bevor das Hauptprogramm auch nur einen Befehl weiterläuft.

Ich würde mir das Ganze noch etwas anschauen. Wenn meine Vermutung stimmt, würde nämlich der Timerinterrupt ab und zu blockiert werden und dann hinkt deine Systemzeit etwas hinterher.

Georg

Reply to
Georg Meister

Georg Meister schrieb:

Das Nichtfunktionieren interressiert mich auch. Um aehnliche Denk-, Konzept- oder Programmierfheler zu vermeiden.

Vielleicht ein Fehler in der Interrupt Sense Control des ext. Interrupt.

Bei "low level" es dann ein Interrupt nach dem anderen.

  • When the AVR exits from an interrupt, it will always return to
  • the main program and execute one more instruction before any
  • pending interrupt is served. (ATMega16 Manual, Seite 12)
  • The instruction following SEI will be executed before any pending
  • interrupts. (AVR Instruction Set Manual)

Nach RETI und SEI wird ein Befehl ausgefuehrt.

Hab schon Code (zu Demozwecken) von ein Singlestep-ISR gesehn. (level gesteuerter externer Interrupt mit serieller Ausgabe der Register)

Ich habe Interrupts gerne mit dem Oszi kontrolliert. Am Beginn der ISR setze ich einen unbenutzen IO-Pin auf High und am Ende wieder auf Low. So kann ich Haeufigkeit und Dauer kontrollieren.

servus thomas «

--
Die 4. Österreichische Fan-Convention zum Thema Japanische Popkultur
** AniNite 2004 ** 20.-22.August 2004 ** http://www.aninite.at/ **
 Click to see the full signature
Reply to
Thomas Mozgan

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.