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?
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
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?
> 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
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.