Forum: Mikrocontroller und Digitale Elektronik AVR: Timer0 interrupt bei 1us - Ausführungszeit & PS/2 timing


von Heiko P. (Gast)


Lesenswert?

Mahlzeit,

und zwar habe ich folgendes Assembler Verständnis-Fragen:

Situation:

Ich möchte auf einem AVR einen Timer0 interrupt nach 1 us (microsekunde 
10^-6) auslösen und in der ISR lediglich einen Befehl ausführen lassen.

Der Systemtakt beträgt 16 MHz, als Vorteiler benutze ich 8, dem 
entsprechend muss der Timer ja mit 253 (255-2) vorgeladen werden, damit 
er 1.000.000 Mal in der Sekunde einen interrupt auslöst...

Hintergrund ist der, dass ich das PS/2-Protokoll bedienen will, um einen 
AVR als Eingabegerät an einem PC zu verwenden. Leider lässt sich darüber 
recht wenig in Assembler finden, selbst Atmel bietet in seiner 
AppNote313 nur eine Routine in C-Kot, was mir recht wenig nützt...

Frage 1:
Sofern ich das richtig verstanden habe, zählt der Timer doch im 
Hintergrund weiter während, der Timer0 Interrupt ausgelöst wurde, und 
die ISR behandelt wird?

Frage 2:
Wenn ich ihn am Ende meiner ISR wieder neu vorlade, heisst das doch, 
dass der folgende Interrupt erst nach der 
ISR-Ausführunszeit+16-Taktzyklen (16.000.000 / 1.000.000) auftritt und 
somit eine erhebliche Ungenauigkeit auftritt?

Frage 3: Wenn ich diese Abweichung bei dem vorladen in der ISR mit 
einbeziehe, bleiben mir doch insgesamt, von Vorladen zu Vorladen, 16 
Taktzyklen für Befehle?

Frage 4: Wenn ich mir nun meine ISR anschaue und die Taktzyklen, die die 
einzelnen Befehle zur Ausführung benötige summiere (Register backup & 
recover + 1 Befehl), fällt auf, dass diese 17-Taktzyklen zur Ausführung 
benötigt und so eine Verschiebung bzw. Ungenauigkeit auftritt, gibt es 
einen Weg, dass zu umgehen?

Frage 5: Ausserdem würde das doch bedeuten, dass die Main loop niemals 
abgearbeitet werden kann, da permanent Timer0 interrupts stattfinden, 
oder nicht?

Frage 6: Hat jemand vielleicht noch einen anderen Vorschlag wie es 
möglich ist, dass PS/2 Timing einzuhalten ohne so eine geringe 
Auflösung, wie 1us verwenden zu müssen?
Vielleicht habe ich auch einfach einen Knoten im Gehirn und der Groschen 
fällt mit einem kleinen Stuppser..

Ich wäre euch wirklich für Hilfe dankbar - falls die Fragen schonmal 
beantwortet wurden entschuldige ich mich im vorraus, habe leider nichts 
passendes gefunden und bin hier kurz vor dem verzweifeln!!!

P.S.: Falls jemand eine Link zu einer Assembler (Bitte nicht Bascom oder 
C!!!) Routine zum senden von Bytes an die PS/2 Schnittstelle eines PCs 
kennt, wäre ich ebenfalls sehr dankbar...

Gruß,
Heiko


P.P.S.:
Beim schreiben dieses Textes ist mir soeben noch etwas eingefallen:
Und zwar bedeutet doch 16 MHz = 16.000.000 Taktzyklen / Sekunde = 16 
Taktzyklen / 1 u-Sekunde.
Wenn ich den Timer overflow nun nicht jede u-Sekunde stattfinden lasse, 
sondern alle 10 u-Sekunden, dann bleiben mir doch zwischen den 
Interrupts 10 * 16 = 160 Taktzyklen zur Befehlsverarbeitung zwischen den 
Interrupts. In diesem Rahmen müsste sich doch das PS/2 timing bzw. die 
Befehle zum senden der Bytes ralisieren lassen... was haltet ihr von 
diesem Ansatz?

von Kachel - Heinz (Gast)


Lesenswert?

Recherchiere doch mal, wieviele Takte es dauert, bis die Interrupt-Logik 
einen Interrupt erkannt hat.

Und dann rechne mal, wieviele Takte der den Sprung in die 
Interrupt-Service-Routine und der Rücksprung ins Programm dauert.

Und dann musst Du in der ISR das SREG sichern und wiederherstellen und 
etwas Arbeit soll die ISR ja auch noch verrichten.

KH

von Stefan E. (sternst)


Lesenswert?

Frage 1:
Ja.

Frage 2:
Deshalb lädt man den Timer in solchen Fällen auch nicht manuell vor, 
sondern verwendet gleich den CTC-Mode. Wenn Timer 0 keinen CTC-Mode hat, 
nimm einen anderen Timer (oder anderen AVR).

Frage 3:
Nein, weniger. Im Datenblatt steht, wie viel Takte der Controller für 
das Interrupt-Handling braucht.

Frage 4:
Verstehe ich nicht. Wenn du nur 1 Befehl ausführen willst, was gibt es 
dann da an Register zu retten? Und das SREG sichern und wiederherstellen 
(sofern überhaupt nötig) braucht nur 6 Takte.

Frage 5:
Nach einem Interrupt wird immer mindestens 1 Befehl ausgeführt, bevor 
wieder in einen Interrupt gesprungen wird. Das restliche Programm wird 
also weiter ausgeführt, aber nur sehr langsam.

Frage 6:
Ich kenne das PS/2-Protokoll nicht im Detail. Wozu braucht man denn da 
ein 1µs-Timing?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Hintergrund ist der, dass ich das PS/2-Protokoll bedienen will,
> um einen AVR als Eingabegerät an einem PC zu verwenden.
Für einen PS/2 braucht kein Mensch eine Auflösung von 1us. PS/2 ist 
jetzt mehr als 20 Jahre alt, damals war noch ein Tastaturderivat des 
guten alten 8048er drin. Da gings nicht um 1us hin oder her. Allein 1 
Bit dauert schon etwa 100us.

Mir scheint, du hast da was falsch verstanden am PS/2-Protokoll.
Sieh dir doch mal das an: http://www.marjorie.de/ps2/ps2_protocol.htm

von Heiko P. (Gast)


Lesenswert?

Hallo zusammen,

danke, so schnell hatte ich ja gar icht auf AntworteN zu hoffen gewagt!

Also, habe mich schon eingehender mit dem Thema PS/2 beschäftigt und 
diverse Suchmaschinen bemüht, letzten Endes waren dabei die 
wissenswertesten Links diese:

Gute Beschreibung des PS/2 Protokoll:
http://www.marjorie.de/ps2/ps2_protocol.htm

Atmel AppNote AVR313 - Interfacing the AT Keyboard (leider C-Code):
http://www.atmel.com/dyn/resources/prod_documents/doc1235.pdf

Interessanter Beitrag hier aus dem Forum (auch in Assembler):
Beitrag "PC-Tastatur mit AVR ersetzen"

Ein nettes Anwendungsbeispiel, gewinner des Atmel Design Contest 2006 
(auch wieder in C):
http://www.circuitcellar.com/avr2006/winners/AT3296.htm

Wie die Kommunikation laut PS/2-Protokoll aussieht, bilde ich mir 
zumindest ein verstanden zu haben, vielleicht verstehe ich nur irgendwas 
an der praktischen Umsetzung nicht korrekt...

Ich habe mir das prinzipiell so vorgestellt, dass ich mit Hilfe des 
Timer0 Interrupts überprüfe, ob der Host die Leitungen auf Low zieht 
(eventuell ließe sich das ja vielleicht auch mit einem Interrupt an 
INT0/INT1 regeln) und um das Senden der Bits zu timen.

Doch irgendwie zweifle ich gerade daran, ob das überhaupt so umzusetzen 
ist, da 1µs schon sehr wenig Zeit ist. Habe dass auch mal durch 
gerechnet, dabei kommen schon mindestens 16 Taktzyklen zum retten und 
zurückschreiben der Register und zum neu-initialisieren des Timers:
1
timer0_overflow:
2
; Sichern
3
push  temp               ; #CLOCKS-2  (2 clocks)
4
in    backup, SREG       ; #CLOCKS-1  (3 clocks)
5
push  backup             ; #CLOCKS-2  (5 clocks)
6
7
; Anweisung(en)
8
; mindestens 1 Anweisung ; #CLOCKS-1  (6 clocks)
9
10
11
; Timer laden
12
ldi  temp, timerwert     ; #CLOCKS-1  (7 clocks)
13
out  TCNT0, temp         ; #CLOCKS-1  (8 clocks)
14
15
; Wiederherstellen
16
pop  backup              ; #CLOCKS-2  (10 clocks)
17
out  SREG, backup        ; #CLOCKS-1  (11 clocks)
18
pop  temp                ; #CLOCKS-2  (13 clocks)
19
20
reti                     ; #CLOCKS-4  (17 clocks)

Und hierbei sind noch nicht mal die benötigten Taktzyklen für die 
Interrupt-erkennung und den ISR-Einsprung berücksichtigt, geschweige für 
die auszuführenden Befehle (der eine im Kommentar dient dabei mehr als 
Platzhalter)...

Die Auflösung 1µs wollte ich eigentlich wählen, um möglichst genau zu 
arbeiten, u.a. dafür:
1
Zwischen einem Wechsel von Data und der fallenden Flanke von Clock müssen mindestens 5 µs und maximal 25 µs liegen...
2
3
Wenn Sie eine Tastatur, Maus oder einen Host Emulator entwickeln, sollten Sie die Datenleitung in der Mitte jedes Clockimpulses ändern bzw. abtasten, d.h 15-25 µs nach der entsprechenden Clock-Flanke...

Ausserdem müssen bei der Datenübertragung 11-Bits in <1,5 ms gesendet 
werden.

Fahre ich vielleicht doch besser mit einer Timer-overflowfrequenz von 
10µs,
rechne dann haar klein aus wieviel jeder einzelne Befehl dazwischen 
benötigt um dann im richtigen Augenblick für die bestimmte Zeit die 
Pegel zu ändern?

Wie man vielleicht auch merkt, bin ich noch nicht ganz sooo fit, was die 
Assemblerprogrammierung angeht, allerdings will ich es lernen und 
deshalb nicht der Einfachheit wegen auf bereits vorhandenen C-Code 
zurückgreifen, auch wenn es dafür mehr Beispiele gibt, die ich per 
Copy&Paste hätte einfügen können und wahrscheinlich niemals verstanden 
hätte, was dort eigentlich genau vor sich geht...

Kann mir vielleicht auch jemand erklären, warum es so viele C-Beispiele 
gibt und kein einziges (zumindest öffentlich) zugängliches 
Assembler-Beispiel, so schwer kann das doch nicht sein???
(Ich kann mir auch nicht vorstellen, dass sich das in C effizienter 
lösen lässt)

P.S.: Achso, der verwendete uC ist ein ATTiny2313, hätte zur Not aber 
auch einen Mega8 oder 16.

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.