Hallo, ich habe ein Problem mit dem genannten PIC. Am Pic sind nur Stromversorgung und an den PORTB Pins je eine LED mit Vorwiderstand (außer an RB0 natürlich) Im Anhang mein Quelltext. Es wird eine Dauerschleife aufgerufen in der RB5 blinkt. Beim Interrupt sollte RB7 aufleuchten. Wenn ich high oder low an RB0 anlege, passiert einfach nix. Übersehe ich was? Ich bin recht neu in dem Thema - vielleicht hab ich ja was grundlegendes falsch verstanden.....
>Was passiert wenn du > > call write_ports > >aus dem Interrupt raus nimmst? Vergiss das. Wieso so umständlich? Setz PB7 doch direkt.
Von w_temp solltest du ausser im Interrupt die Finger weglassen. Das ist ausschliesslich zum retten des W Registers im Interrupt da.
Danke für deine Hilfe. Das write_ports benutze ich, weil ich immer Probleme mit meinen Ports hatte. Wenn ich die Bits des Ports einzeln setze, werden die anderen immer genullt. Ich habe nun aber den angehängten Code ausprobiert. Da wird in dem Interrupt kein write_ports mehr aufgerufen und es wird stattdessen gewartet. Zudem gibt es nun ein zweites W_temp. Aber es funktioniert nicht. Also wenn in dem init Programm nichts falsch ist, muss ich langsam davon ausgehen, dass der PIC kaputt ist. Ich werde mir heute mal einen neuen besorgen.
Das Interrupt Flag INTCON,RBIF muss am Ende der Interrupt Routine noch gelöscht werden.
R. S. schrieb: > Übersehe ich was? Ja. Also mal was zur äußeren Form: man braucht nicht ORG 0x000 zu schreiben, ORG 0 reicht aus, dito ORG 4. Bei Unterprogrammen solltest du dir angewöhnen, einen Kommentar zu deren Funktion davor zu schreiben. Das hilft auch dir nach nem halben Jahr. Ansonsten gilt für die Interrupt-Routine folgendes: 1. Register muß man retten, wenn sie in der Interrupt-Routine benutzt werden, sonst nicht. 2. Vorsicht beim Retten und Restaurieren von STATUS, da ist angesagt, nicht nachträglich den Status (insbesondere Zero-Flag) zu versauen. 3. Interrupt-Signale, die zu einem Interrupt geführt haben, müssen auch wieder gelöscht werden. Ansonsten würde ich GIE zu allerletzt setzen, nachdem ich die Peripherie eingerichtet, benötigte Interrupts freigegeben und vorsorglich gelöscht habe, damit mir kein von zuvor stehengebliebenes Interrupt-Signal in die Quere kommt. Und zuletzt: Den tieferen Sinn deines Unterprogramms "write_ports" verstehe ich nicht. Was soll das bitte? movwf w_temp ; aha, W retten, wo auch der Int W hinrettet? movf PORTB_SHDW,w ; Variable nach W movwf PORTB ; W nach Port RETLW w_temp ; Adresse von Variable nach W ??? Früher gab es mal Versuche mit dem Programm "crashme", die bestanden im Wesentlichen darin, einen Bereich im RAM mit Zufallszahlen zu füllen und hineinzuspringen. Gewertet wurde, ob das OS das aushält, ohne selbst abzustürzen. Crashme mit ner allgemeinen Schutzverletzung abzuwürgen war erlaubt. Du betreibst hier was ähnliches. W.S.
Hallo R.S. Dein PIC 16F886 ist wohl nicht kaputt, dafür aber Dein Code! Einiges haben ja schon andere kommentiert. Hier noch mein "Senf" dazu: In der ISR solltest Du das nunmehr gesetzte Interrupt-Flag wieder löschen. bcf INTCON,INTF und am Ende der ISR, wenn der Interrupt wieder aktiv sein soll den automatisch gesperrten Interrupt mit bsf INTCON,GIE wieder zulassen. Zum Interrupt, ausgelöst durch steigende/fallende Flanke Sind diese Register/Bits korrekt, ungefähr in dieser Reihenfolge zu initialisieren: Init bcf INTCON,GIE ;Alle Interupts abschalten ;---------PORTB clrf PORTB banksel TRISB movlw 0x01 ;RB0 als Eingang! movwf TRISB bsf STATUS,RP0 ;---------INTCON S. 33 Interrupts S.222 bsf INTCON,PEIE ;b6 Peripheral Interrupt Enable bsf INTCON,INTE ;INT External Interrupt Enable (RB0) bcf INTCON,INTF ;Clear Flag INT-interrupt occurred ;(must be cleared in software ;---------OPTION_REG; banksel OPTION_REG bsf OPTION_REG,INTEDG ;b6=1 Interrupt on rising Edge ;0/1 falling/rising edge bsf STATUS,RP0 ;bank 0 ; Main bsf INTCON,GIE ;Interrupt global enabled Main_Loop ; ;irgendein Programmcode bei dessen Ausführung irgendwann mal ;eine steigende oder fallende Flanke an RB0 auftreten kann. ; GOTO Main_Loop Subroutinen........ END P.S. Schau doch mal die Beispielprogramme bei www.sprut.de an, dann fällt es Dir wie Schuppen von den Augen! weiterhin viel Spass GroberKlotz
Vielen Dank für die Hilfe aller Beteiligten. Der Code von GroberKlotz funktioniert soweit. Sollte mal jemand das gleiche Problem haben; in dem Code müssen an den Stellen mit Statusbits die Bits gelöscht und nicht gesetzt werden. Ansonsten läuft es aber so. Eine Frage hätte ich noch; bei Sprut steht, dass jede Interrupt Routine mit RETFIE beendet werden muss, weil durch das RETFIE Kommando das GIE gesetzt wird und die Adresse des Aufrufes in den PC geschrieben wird. Nun meine Frage; ist es möglich an eine andere Stelle zu springen, indem ich GIE manuell setze und ein GOTO verwende? Oder muss ich definitiv zurück an die Stelle wo der Interrupt eingesprungen ist? Danke!
R. S. schrieb: > Eine Frage hätte ich noch; bei Sprut steht, dass jede Interrupt Routine > mit RETFIE beendet werden muss, weil durch das RETFIE Kommando das GIE > gesetzt wird und die Adresse des Aufrufes in den PC geschrieben wird. > Nun meine Frage; ist es möglich an eine andere Stelle zu springen, indem > ich GIE manuell setze und ein GOTO verwende? Oder muss ich definitiv > zurück an die Stelle wo der Interrupt eingesprungen ist? NEIN! Da hast Du was Prinzipielles mißverstanden! Lese doch bitte das Datenblatt zum 16F886, da hast Du alle Infos aus erster Hand! Befasse Dich auch damit wozu es einen Stack gibt wie "tief" dieser ist, ob das Last in - First Out Prinzip angewandt wird usw... Zitat (Datenblatt S.220) zum Interrupt ..... The Return from Interrupt instruction, RETFIE, exits the interrupt routine, as well as sets the GIE bit, which re-enables unmasked interrupts. ..... When an interrupt is serviced: • The GIE is cleared to disable any further interrupt. • The return address is pushed onto the stack. • The PC is loaded with 0004h. Im Datenblatt erhältst Du praktisch alle Antworten auf Deine Fragen. Ganz am Ende gibt es sogar einen Index mit Stichworten... mfG GroberKlotz
Hallo, ich dachte das mit dem Stack hätte ich soweit verstanden. Ich dachte bisher, der 8 Ebenen (für meinen Fall) tiefe Stack wird immer dort ausgelesen, wo er zuletzt „bespeichert“ wurde. Beim nächsten Speichern wird einfach die nächste Speicherstelle im Stack verwendet und eben auch diese nächste beim nächsten Auslesen ausgegeben. Dementsprechend dachte ich, dass es kein Problem sein sollte den Stack nach dem Beschreiben (was beim Auslösen des Interrupts geschieht) NICHT auszulesen, indem man am Ende der ISR kein RETFIE verwendet sondern ein GOTO setzt. Die im Stack abgelegte Adresse verweilt dann halt dort – stört doch nicht; zumindest dachte ich das. Ich war mir halt nicht sicher ob ich etwas Wichtiges übersehe, was mit RETFIE passiert, und mit meiner GOTO Lösung nicht. Deshalb die Frage im letzten Beitrag. Warum diese Lösung nicht geht, verstehe ich dann auch nicht nachdem ich das Datenblatt an den entsprechenden Stellen nochmals gelesen (und hoffentlich auch verstanden habe) und zudem noch etwas in Netz geforscht habe. Entschuldigt, dass ich vielleicht etwas unbeholfen rüber komme – aber glaubt mir, dass ich mir zunächst immer Mühe gebe die Probleme selbst zu lösen. Sei es mit Kontroll LED’s in meinem Programm oder mit einer Recherche. Kleine Info am Rande; der RB0 des 16f886 war wirklich defekt. Ich habe den Code vor meinem letzten Beitrag zu einem 16F628A portiert, wo er tadellos läuft.
Hallo R.S. Meine Antwort "NEIN" bezog sich auf Deine Frage: >..meine Frage; ist es möglich an eine andere Stelle zu springen, indem > ich GIE manuell setze und ein GOTO verwende? Möglicherweise habe ICH dabei die Frage so missverstanden... "Springen durch manuelles setzen von GIE :-(" Solange die ISR nicht beendet ist, kannst du von dieser aus überall herumspringen, Unterprogramme aufrufen usw. und danach mit RETFIE oder auch RETURN an der Stelle im Code nach dem Auftreten des Interrupts weiterarbeiten. GIE kannst Du setzen/löschen wann Du willst. Hoffe zur Klarheit hiermit beigetragen zu haben. mfG GroberKlotz
Hi hab mal ne frage zu dem von beispiel von GroberKlotz, fragt ihr sicherheitshalber nicht noch ab welches ISR bit ausgelöst hat ? Meine lösung:
1 | int ; Interupt STATUS und W sichern |
2 | movwf wtemp ; W Sichern |
3 | swapf STATUS, w ; Status Sichern |
4 | movwf stemp ; Status Speichern |
5 | banksel PIR1 |
6 | |
7 | btfss PIR1, RCIF ; Interupt vom USART wenn nicht |
8 | goto intend ; Interupt beenden |
9 | |
10 | Interupt Code uart |
11 | banksel RCSTA |
12 | movf RCSTA ; Uart echo |
13 | call uart_send |
14 | call uart_ferr ; Auf Frame Errors Testen |
15 | call uart_oerr ; Auf Overrunerrors Testen |
16 | |
17 | intend ; Interupt beenden und STATUS + W zurückspielen |
18 | banksel PIR1 |
19 | bcf PIR1, RCIF ; Interuptflag Löschen |
20 | swapf stemp, w ; Status restore |
21 | movwf STATUS |
22 | swapf wtemp, f ; W restore |
23 | swapf wtemp, w |
24 | retfie |
Na ja, es ging hier ja lediglich um den alleine eingerichtete RB0/INT und nicht um mehrere Interruptquellen. Klar dass bei mehreren INT-Sources dann die entsprechenden Flags abzufragen sind, um auf den aktuellen Interrupt richtig reagieren zu können. mfG GroberKlotz
GroberKlotz schrieb: > Solange die ISR nicht beendet ist, kannst du von dieser aus überall > herumspringen, Unterprogramme aufrufen usw. und danach mit RETFIE oder > auch RETURN an der Stelle im Code nach dem Auftreten des Interrupts > weiterarbeiten. GIE kannst Du setzen/löschen wann Du willst. > > Hoffe zur Klarheit hiermit beigetragen zu haben. > > mfG GroberKlotz Hallo, das ist nicht ganz was ich meinte. Ich wollte wissen, ob ich die ISR wirklich mit RETFIE beenden muss, oder ob es genügt GIE zu setzen. So könnte ich nach der ISR direkt in einen anderen Programmteil springen. Ich würde so nicht zu der Stelle zurückspringen, wo der Interrupt eingesetzt hat. Geht das?
Wenn du die Interruptoutine beenden willst, dann geht das nur mit RETFIE. Innerhalb der Routine kannst du natürlich hinspringen wo du willst, aber beenden kannst du sie nur mit RETFIE. Überleg doch mal: Ein Interrupt kann an jeder beliebiger Stelle im Hauptprogramm auftreten. Irgendwie muss die CPU sich doch merken an welcher Adresse sie gerade war, bevor der Interrupt eingetreten ist bzw. eigentlich merkt sich die CPU die Adresse des nächsten Befehls im Hauptprogramm der kommen würde, wäre nicht der Interrupt dazwischen gekommen. Diese Adresse wird im Stack abgelegt und der PC mit 0x04 überschrieben. Damit macht die CPU bei 0c04 weiter. Am Ende der ISR kommt dann das RETFIE damit lädt die CPU den letzten Wert des Stacks in den PC und springt damit genau an die Stelle, wo er vor Eintritt in die ISR gewesen war. Wenn die die ISR mit RETFIE nicht beendest wird dein Stack überlaufen und das Programm sich verabschieden. Gruß Sven
stepp64 schrieb: > Wenn die ISR nicht mit RETFIE endet wird dein Stack überlaufen und das > Programm sich verabschieden. Selbst wenn man den Stack manuell bereinigen würde (geht bei PICs mit Hardware-Stack allerdings nicht) ist es in den meisten Fällen unsinnig, das Programm bei einer festen Adresse fortsetzen zu wollen, ohne zu wissen was vorher als Letztes gemacht wurde.
Ok, das mit dem Überlauf ist natürlich blöd, wenn das Zwangsweise zum Absturz führt. Ich dachte aber, dass immer die nächste Zelle im Stack geschrieben wird und wenn man bei der letzten ankommt, wieder die erste geschrieben wird. Wie kann das zum Überlauf führen? Oder ist meine Annahme falsch?
OK. Hab gerade noch mal nachgelesen. Überlauf ist nicht das richtige Wort gewesen. Der Stack ist tatsächlich als Ringpuffer ausgelegt. Sobald du ein 9. Mal eine PUSH Operation durchführst (also CALL oder Interrupt) wir der 1. Wert des Stacks überschrieben. Damit kommst du aber auch nie wieder an diese Stelle im Programm zurück. Spätestens wenn du das 9. mal aus der ISR oder dem UP zurück springen willst wird dein Programm komische Sachen machen, da es nicht an die von dir erwartete Rücksprungadresse springen wird. Allerding versteh ich nicht, was dein Problem ist. Eine ISR ist ein Unterprogramm, welches durch ein Ereigniss (Interrupt) ausgeführt wird. Im allgemeinen springt man aus einem Unterprogramm irgendwann wieder in das Hauptprogramm und arbeitet dort weiter. Eine ISR aufzurufen aus der du nicht wieder zurück kommst macht doch gar keinen Sinn. Zum GIE: Dieses BIT erlaubt/verbietet lediglich die Ausführung von Interrupts. Es beendet nicht die ISR! Die kannst in einer laufenden ISR durchaus das GIE wieder ein schalten, allerdings wirst du daran keine Freude haben, da verschachtelte Interruptroutinen bei diesem PIC nicht vorgesehen sind.
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.