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 ?
@Oli Danke, ich denke da hat bisher auch noch keiner dran gedacht! Habs auch erst gesehen, als ich deine Antwort gelesen hab.
@Olaf Aha...das ist ja mal interressant. Das habe ich im Datneblatt nicht gesehen
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
Meine Drehgeberroutine hab ich auch mal angehängt, ist vielleicht etwas kompakter
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
Ohhhh man....... Ich habe natürlich alle Rjmps geändert, ausser die Sprünge für die Vektoren selber, ich depp..... Jetzt gehts....... SUPER
die jumps im code kannst du als rjmp lassen. aber die befehle für die vektoren müssen jmps sein.
Eine Frage habe ich noch : Wie kann ich das Int Eingänge wieder ausschalten wenn ich keinen Interrupt mehr zulassen möchte ?
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
Da kaum ein sinnvolles Programm ohne Timer-Interrupt auskommt, ist es wohl besser, die Enable-Flags in GICR zurückzusetzen. ...
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.