Hallo,
ich beschäftige mich z.Z. intensiv mit einem Drehimpulsgeber den ich an
die beiden Interrupt Eingänge meines Atmega16 angeschlossen habe.
Habe programmiertechnisch soweit alle fertig, nur will es halt nicht so
laufen wie ich mir das vorstelle.
Irgendwie komme ich jetzt nicht so ganz klar mit den Ablauf eines
Interrupts.
Mein Programm ist nach dem folgende Schema aufgebaut:
1.Stack einrichten
2.PortD Eingang (Drehimpulsgeber)
3.PortC Ausgang (LC-Display)
4.Into & Int1 enablen
5.LCD INIT
6.LCD CLEAR
dann kommen die
Interrupt Vektoren
------------------
7.rjmp reset2
rjmp Int0
rjmp Int1
reset2:
Ausgangszustand Drehimpulsgeber ermitteln
Interrupts erlauben (sei)
main:
(HP)
rjmp main
INTO:
(Auswertung Drehrichtung)
INT1:
(Auswertung Drehrichtung)
ENDE
Das Hauptpogramm funbktioniert ohne die Interruptroutine einwandfrei.
Nur wenn ich meine Routine einbau und ich den Drehgeber drehe. Flackert
das LCD und zeigt wirres zeug.
Frag ist : liegt es jetzt generell am Ablauf des kompletten Programms
mitsamt dem Interrupt ?
Wenn der Ablauf denn so stimmt muss es ja an der Routine selber liegen.
Mein Auto steht in der Garage und springt nicht an. Liegt das jetzt am
nicht eingeschaltetem Licht oder an den Kaninchen, die meine Tochter im
Garten züchtet.
Man gebe mir eine Glaskugel, mal sehen, was sich machen lässt.
Versuch mal den Drehimpulsgeber nicht mit Interrupt auszuwerten. Frag
ihn einfach in nem Abstand von z.B. 1 ms ab...
Kann es vlt. auch sein, dass du die Interruptvektoren an der falschen
Adresse angegeben hast?
MFG
Kai
Ich habe gerade in meiner Datenblatt-Sammlung etwas über Drehimpulsgeber
gefunden.
Da kann man lesen(sehen): Entprellen!!!
Und zwei Flußdiagramme zur Abfrage mit µC. Einmal jede ms, das andere
mir Interrupt.
MFG
Kai
Auszug aus Datenblatt angehängt...
Mein Programm ist nach dem folgende Schema aufgebaut:
>> 1.Stack einrichten> 2.PortD Eingang (Drehimpulsgeber)> 3.PortC Ausgang (LC-Display)> 4.Into & Int1 enablen> 5.LCD INIT> 6.LCD CLEAR>> dann kommen die> Interrupt Vektoren> ------------------> 7.rjmp reset2> rjmp Int0> rjmp Int1
Wie muss ich das jetzt interpretieren?
Du kannst die Interruptvektoren nicht einfach
dorthin schieben wo's Dir passt. Die muessen
ganz am Anfang stehen.
Datenblatt lesen.
@alle: Danke für die Hilfe.....
@Karl Heinz
Danke, genau das wollte ich wissen.
Die müssen also ganz am Anfang stehen. Ich habe wohl an der falschen
stelle im Datenblatt geschaut ("External Int" anstatt "Interrupts"
Kapitel schäm)....
Ich habe es aber jetzt gefunden. DAnke
Trotzdem noch ne Frage:
Was passiert nachdem der Interrupt Vektor nach einem INT durchlaufen
ist ?
fängst dann wieder bei Reset an oder da wo ursprügnlich die
Unterbrechung war ?
Ich hab natürlich mit Push und Pop das SREG und andere Register
gesichert.
Am Ende deiner Interruptroutine muss ein RETI stehen. Damit springt der
Controller wieder an die Stelle im Programm zurück, an welcher er in
den Interrupt gesprungen ist. Beispiel:
rjmp reset2
rjmp Int0
rjmp Int1
... weiterer Code ...
Int0: ; Start der Interrupt Routine
push SREG ; Register sichern
push XYZ
... viel Code für Interrupt ...
pop XYZ ; Register wieder herstellen
pop SREG
reti ; Interrupt verlassen und weiter um Hautprogramm
MFG
Kai
Nein.
Ein Interrupt ist wie ein normaler CALL (*), nur dass der
Unterprogrammaufruf eben nicht durch einen Assemblerbefehl
ausgeloest wird, sondern durch irgendein Ereignis. Daher
muessen die Vektoren auch auf definierten Stellen stehen.
Bei einem normalen CALL gibt man ja die Adresse an an die
gesprungen werden soll. Bei einem externen Pin, der ein
Spannungssignal kriegt, geht das aber nicht. (**)
(*) Im Prinzip. Im Detail gibt es natuerlich Unterschiede.
RETI statt RET verwenden. Waehrend ein Interrupt abgearbeitet
wird, sind andere Interrupts automatisch gesperrt. ...
(**) Zumindest nicht auf einem AVR. Auf anderen Prozessoren
geht das sehr wohl, dann braucht man aber eine zusaetzliche
Hardware: einen Interruptcontroller.
Hallo,
noch eine kleine Falle: Die Interrupttabelle beim Mega16 besteht aus 2
Worten pro Eintrag, sollte also aus JMPs bestehen, nicht RJMPs. Das
wird so gemacht, da man bei AVRs mit mehr als 8K Flash sonst nicht den
ganzen Adreßraum anspringen könnte. Siehe Datenblatt.
MfG Oil
Ok..danke
So noch mehr Newbie fragen .....
Den Befehl "sei", den kann ich doch hinsetzten wo ich möchte oder ?
Ich möchte nämlich erst zu einen bestimmten Zeitpunkt im Programm einen
Interrupt erlauben.
Im GICR habe ich zuvor die Ints enabled. Das ist doch korrekt oder ?
Ich würde mal die von Olaf angesprochene Sache mit den
Interruptverktoren ändern. Sonst fällt mir auf den ersten Blick nichts
auf - habs nur kurz überflogen.
MFG
Kai
Dankeschön, aber ich bekomm diesen Code auch nicht am laufen.
Ich verzweifel hier schon.
Ich möchte auf meinem Display einfach, zum testen, zwei Wert von 0-9
einstellen können.
Ich kann beim Druck auf die Achse zwischen den Werten hin und her
springen (Cursor springt, Cursor blinkt).
Sobald ich aber drehe funktioniert nix mehr.
Es soll doch einfach nur Decrement oder Increment ausgeführt werden,
den neuen Wert auf dem Display angezeigt werden und danach soll
entweder auf einen Interrupt oder auf den nächsten Tasterdruck gewartet
werden.
Kann es an den Fusebits liegen ? (BOOTRST)
Oder an IVSEL ?
Oder leigt es daran wann und wo ich mit dem Befehl "SEI" die
Interrupts erlaube ?
Ich habe mal den Quellcode angehangen....
Über ein paart Tipps freue ich mich sehr....
@Christoph Kessler: Was is dasn fürn Quatsch? Der Peter Danegger hat in
der Codesammlung einen C-Code liegen der wesentlich kürzer ist. Sollte
ohne Probleme auf ASM portierbar sein.
Hallo TomTom,
wie Olaf oben schon gesagt hat, solltest du die Sprungbefehle von rjmp
in jmp ändern (in der angehängten Datei noch nicht geschehen!).
Bei den Fusebits musst du nur darauf achten, dass deine Reset- und
Interruptvektoren nicht in den Bootbereich verschoben werden. BOOTRST
sollte unprogrammed sein (= 1) und IVSEL sollte = 0 sein. Siehe S.44
Tabelle 19 und S.45.
MFG
Kai
Du kannst entweder alle Interrupts mit CLI ausschalten oder im GICR die
Bits 6 und 7 wieder auf 0 setzen. Wenn du nur die Int Eingänge
verwendest, würde ich den CLI Befehl verwenden.
MFG
Kai