Hallo an alle PIC-Kenner hier.
Bisher hatte ich stets den Ehrgeiz, meine Programmier-Probleme alleine zu lösen (und damit maximalen Lernerfolg zu haben).
In dem Fall, der hier beschrieben werden soll, bin ich allerdings absolut ratlos und Suche erstmals aktive Hilfe.
Es geht um ein rudimentäres Digital-Oszilloskop auf Basis eines PIC16F877A, programmiert per MPLABX in Assembler.
Die Interruptroutine soll unter Verwendung von FSR und INDF die Messdaten in einen freien Bereich der 3. und 4. SRAM-page schreiben, das Hauptprogramm diese ebenso lesen und zur Anzeige bringen. Im Grunde nichts aufregendes. Denkt man, wenn man schon hunderte Stunden Praxis mit dieser Controllerfamilie auf dem Buckel hat.
Jetzt taucht das Problen auf, dass alleine die - lediglich im ISR-Code anwesenden, jedoch nie ausgeführten - Manipulationen an FSR und der INDF-Zugriff in der ISR den Durchlauf der ISR irgendwie blockieren oder aus dem Gleis werfen. Überspringen der fraglichen Codepassage genügt nicht, die Zeilen müssen handfest auskommentiert werden! Das ist daran erkennbar, dass die Blinkfunktion am Ende der ISR das Lämpeken (bzw. seine Portleitung) nicht mehr anrührt.
Jetzt der ISR-Teil meiner Software:
Es geht um den Teil zwischen den Labels ISR_MEM und ISR_MEM_E.Code:;********************************************************************** ORG 0x0004 ; interrupt vector location ;********************************************************************** ;********** Kontext sichern ********** movwf temp_w ; save off current W register contents movf STATUS,w ; movwf temp_status ; save off contents of STATUS register movf PCLATH,w ; movwf temp_pclath ; save off contents of PCLATH register movf FSR,w ; movwf temp_fsr ; save off contents of FSR register ; Lead-In bcf _RP0 ; bcf _RP1 ; Register-Bank 0 als default bcf STATUS,IRP ; ;********** ISR für den Timer0-Interrupt Request ********** ISR_TMR0 ; auslösendes Flag löschen bcf INTCON,T0IF ; ; Schrittmacher für Timer 0 ; 12MHz :4 :16 :188 ergibt etwa 1000Hz movlw d'70' ; 256-(188-2!) = 70 für etwa 1000 Hz @ 12MHz movwf TMR0 ; Uhr wieder aufziehen ; Umwelt abfragen movf PORTC,w ; movwf sta_rc ; movf PORTD,w ; movwf sta_rd ; movf PORTE,w ; movwf sta_re ; ISR_ADC ; Messwerte unabhängig von der Speicherung bereitstellen, z.B. für Triggerlogik ; !!! ggf. nur tauglich für die Entwicklungsphase !!! movf ADRESH,w ; ADC-Ergebnis blind auslesen movwf messig ; bsf ADCON0,GO ; neue Wandlung anstoßen ISR_MEM ; Triggerauswertung und Messwertspeicher mit Werten befüllen ; btfss TRIGGD ; Speichern nur, wenn getriggert goto ISR_MEM_E ; DIESES ÜBERSPRINGEN HIER SCHAFFT KEINE ABHILFE !!! movlw d'96' ; und nur, wenn er noch nicht voll ist subwf memwx,w ; memwx bis d'95' erlaubt btfsc _C ; C = 0, wenn (memwx - 96) < 0 goto ISR_MEM_E ; wenn der Speicher voll ist movlw 0x10 addwf memwx,w movwf FSR ; init Basisadresse ; movlw 0x10 ; movwf FSR ; init Basisadresse ; movf memwx,w ; addwf FSR,f ; Pointer um <memwx> Elemente weiterdrehen ; Kurzversion, keine Pageüberschreitung ;;;;;;;; movlw d'96' ;MEMPAGE ; MEMPAGE: Größe des zusammenhängenden Bereichs ;;;;;;;; subwf memwx,w ; vergleichen mit aktuellem Index ;;;;;;;; ; ;;;;;;;; movlw d'32' ; ggf. Korrektur: 16 Rest- +16 Vorlauf-Bytes überspringen ;;;;;;;; btfsc _C ; _C = 0 heist: (memwx - MEMPAGE) < 0, ;;;;;;;; ; also memwx < MEMPAGE --> erster MEM-Abschnitt ;;;;;;;; addwf FSR,f ; Ja, Lückenkompensation erforderlich movf messig,w bsf STATUS,IRP ; Vorbereitung INDF-Zugriff auf 3. / 4. RAM-Page movwf INDF ; indirekt adressiert speichern bcf STATUS,IRP ; zurück zum INDF-Defaultwert ; incf memwx,f movlw d'96' ; subwf memwx,w ; btfsc _Z ; Index zu groß ? bcf TRIGGD ; Abschaltung, wenn der Speicher voll ist ISR_MEM_E ; ISR_TMR0_1 ; Millisekunden-Eieruhr movf dlycnt,f ; Z-Flag wird generiert btfss _Z ; decf dlycnt,f ; dekr., wenn nicht null (Z=0) ; virtuelle Unruh incf ticker,f ; ISR_LED ; Lebenszeichen für den verunsicherten User generieren bsf LED ; btfss ticker,7 ; bcf LED ; movf buf_ra,w ; movwf PORTA ; nach aussen durchreichen ISR_RESTORE ; allgemeine Aufgaben bsf WAS_HERE ; ISR-Marker setzen ; Kontext wiederherstellen, dann ISR beenden movf temp_fsr,w ; retrieve copy of FSR register movwf FSR ; restore pre-isr FSR register contents movf temp_pclath,w ; retrieve copy of PCLATH register movwf PCLATH ; restore pre-isr PCLATH register contents movf temp_status,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf temp_w,f ; Kniff: W laden, ohne den Status zu verändern ! swapf temp_w,w ; restore pre-isr W register contents retfie ; return from interrupt
So, wie es hier steht, stockt die ISR. Erst, wenn dieser Bereich ab der zweiten Zeile hinter ISR_MEM komplett auskommentiert wird, ist die Störung samt der gewünschten Funktionalität weg. Ich konnte trotz schrittweiser Auskommentierung und Entkommentierung nicht eindeutig trennen, ob der Fehler am FSR-Zugriff oder am INDF-Zugriff hängt. Beim schrittweisen auskommentieren von Codeteilen schien wiederholt eine "Softwarehysterese" aufzutreten:
...keine Blockade...Zeile in den Code einfügen...Blockade...Zeile wieder auskommentieren...immer noch Blockade ... (grübel). Das könnte aber auch mit der Triggerung der Aufzeichnung anhand des eingehenden Analogsignals liegen; dem hatte ich noch nicht volle Aufmerksmkeit gewidmet.
Der vorhergehende Programmstand ohne Pufferspeicher hat bereits recht solide gearbeitet, nur eben zu langsam; es sollen aber auch Abtastraten möglich sein, die kürzer als die Bearbeitungszeit des Displays sind, z.B. wenn vertikale Verbindungslinien zwischen den Messwertpixeln gewünscht sind.
Jetzt meine Fragen in Klartext: Hat schon mal jemand diese Probleme gehabt und gelöst, oder eben auch nicht?
A) das nichtausgeführte GOTO oder aber
B) konkurrierende FSR- und INDF-Verwendung
Vielleicht klingelt es ja bei jemandem ...
Es braucht jetzt keiner meinen Code in der Tiefe zu analysieren. So wichtig ist das nicht - es steht und fällt nur mein Hobbyprojekt damit
Was ebenfalls nichts geholfen hat:
- während der indirekten Adressierung im Hauptprogramm die Interrupts zu disablen
- Zeitverzögerung zwischen Hauptprogramm und ISR, sodass der Speicher bereits beschrieben und ruhend sein sollte, bevor er vom Hauptprogramm ausgelesen wird
- Initialisierung von kritischen Variablen geprüft (da bin ich eigentlich ganz gewissenhaft, aber man weiss ja nie, was man nach Mitternacht so übersieht)
- mittels WAS_HERE-Flag schreiben und lesen zu synchronisieren
- Codevariation bei der Berechnung des FSR-Wertes
- explizite Löschung des Bits STATUS,IRP am Anfang der ISR
- intensiver Review der Kontextspeicherung und -Wiederherstellung in der ISR
- und noch gefühlte tausend andere Eingriffe
Mein Problem wurde gelöst!
Wesentlichen Anteil daran hat Forumsmitglied witkatz !
@witkatz:
Auch an dieser Stelle ganz herzlichen Dank für deine tolle Hilfe und für den entscheidenden Hinweis!
Unsere Kommunikation hatte sich unbeabsichtigt hin zu PN und eMail verlagert. Daher möchte ich die Lösung nachträglich auch hier noch kurz skizzieren:
witkatz hatte durch debuggen festgestellt, dass ISR-interne Sprünge zu ISR_MEM_E gelegentlich in einer höheren Codepage landen, während die ISR in Codepage #0 liegt.
Dreh- und Angelpunkt ist das Register PCLATH.
Das Fehlverhalten dürfte jedesmal aufgetreten sein, wenn der Controller zum Zeitpunkt des Interrupt Requests in einer anderen Codepage unterwegs war als in derjenigen, welche die ISR beherbergt.
Abhilfe schuf die Einfügung eines 'pagesel ISR_MEM' gleich hinter der Kontextspeicherung der ISR.
Ärgerlicherweise kannte ich diese Problematik schon, habe mir aber den Fehler eingefangen, als ich von einem PIC16F871-Controller (kein Code Paging !!!) zu einem PIC16F877A wechselte.
Die Erkenntnis lautet also: Der PIC 16 Assembler spinnt _nicht_.
Gruß
RoboHolIC
Lesezeichen