Hallo Leute,
bei der Simulation unten angegebenen Programms habe ich ein Problem:
innerhalb der loop schleife verweilt der µc bis der timer überläuft
anstatt in den Interrupthandler zu springen, springt er in die dritte
Zeile des maincodes!? dabei deaktiviert er I im statusregister und
verändert den Stackpointer(08;5F) um eins(08;5D)). wenn er dann aber in
Zeile vier geht verändert er den Stackpointer auf (01;5D) was
wahrscheinlich der alte ion temp gespeicherte wert ist. Demnach führt er
also kein reset aus, sondern springt beim interrupt einfach falsch. habt
ihr eine Erklärung?
Viele Grüße
.include "m32def.inc"
.def temp = r16
.def durchlauf=r17
.def ausgabe=r18
.def eingabe=r19
.def end_data=r20
.def temp2=r21
.org 0x000 ; kommt ganz an den Anfang des
Speichers
rjmp main ; Interruptvektoren überspringen
; und zum Hauptprogramm
reti ; IRQ0 Handler
reti ; IRQ1 Handler
reti
reti
reti ; Timer1 Capture Handler
reti ; Timer1 CompareA Handler
reti ; Timer1 CompareB Handler
reti ; Timer1 Overflow Handler
rjmp TIM0_OVF ; Timer0 Overflow Handler
reti ; SPI Transfer Complete Handler
reti ; USART RX Complete Handler
reti ; UDR Empty Handler
reti ; USART TX Complete Handler
reti ; ADC Conversion Complete
Interrupthandler
reti ; EEPROM Ready Handler
reti ; Analog Comparator Handler
reti ; Two-wire Serial Interface Handler
reti ; SPM_RDY
;Speicherreservierung für Daten
main:
ldi temp, Low(RAMEND) ;Stackpointer Initialisieren
out SPL, temp
ldi temp, HIGH(RAMEND)
out SPH, temp
ldi temp, 0b11111101 ;Port A: Pin1 ist Eingang, Pin0 ist
Ausgang
out DDRA, temp ;Rest ebenfalls als Ausgang
ldi temp, 0b00000001 ; Prescaler von Timer0 auf eins
gesetzt
out TCCR0, temp ; ~4kHz bei 1MHz Systemtakt
ldi temp, 0b00000001 ; TOIE0: Interrupt bei Timer
Overflow
out TIMSK, temp ;TIMSK=Timer Interrupt Mask Register
erlaubt
;oder verbietet die jeweiligen Timerinterrupts
ldi ZL, LOW(0x60) ;Beginn des Datenbereichs im SRAM
ldi ZH, HIGH(0x60)
ldi durchlauf,0b00000001
ldi eingabe,0b00000000
ldi ausgabe,0b00000000
ldi end_data,0b10111000
sei ; Alle Interrupts werden freigegeben
loop:
jmp loop
TIM0_OVF:
push temp ; Das SREG in temp sichern. Vorher
in temp, SREG ; muss natürlich temp gesichert werden
ldi temp2, 8
cpse durchlauf,temp2 ; wenn bit 8ist muss gespeichert
und gelesen werden
RJMP in_out ;relativer Sprung zu Datenein/-ausgabe
LD ausgabe, Z
ST Z+, eingabe
cpse r30, end_data
rjmp in_out
ldi ZL,Low(0x60)
ldi durchlauf, 0b00000001
ldi eingabe, 0b00000000
in_out:
ldi temp2, 0b10000000
LSR eingabe
sbic PINA ,1
add eingabe, temp2
SBRS ausgabe,0 ;skip if bit in register is set
rjmp ausgabe_aus
rjmp ausgabe_an
ausgabe_aus:
cBI PORTA,0
rjmp weiter
ausgabe_an:
SBI PortA, 0
weiter:
LSR ausgabe
inc durchlauf
out SREG, temp ; Die Register SREG und temp wieder
pop temp ; herstellen
reti
aber dann bleiben verirrte interrupts doch auf immer und ewig bzw. bis
der nächste interrupt kommt hängen!?
Ist die Reihenfolge von code, Interrupttabelle wichtig?
Ich kann trotz der schnellen Antwort meinen Fehler nicht erkennen.
Viele Grüße
Carlos wrote:
> Ist die Reihenfolge von code, Interrupttabelle wichtig?
Die Interrupt-Sprungadressen müssen an der richtigen Stelle im
Programmspeicher stehen (nämlich an der entsprechenden Stelle in der
Vektortabelle). Wo der restlichen Code steht ist wurscht, aber in
Assembler sollte man tunlichst von oben nach unten programmieren, sonst
gibt's Bruch.
> Ich kann trotz der schnellen Antwort meinen Fehler nicht erkennen.
In den AVRs mit bis 8 KiB Flash sind die Interrupt-Vektoren 16 Bit (ein
Wort) breit. In ein Wort passt ein rjmp-Befehl rein, der bis 8 KiB
adressieren kann (und damit den kompletten Speicher der betreffenden
µCs). Bei allen AVRs, die mehr als 8 KiB Flash haben, kann man mit rjmp
nicht mehr den kompletten Speicher adressieren, so dass es da den
jmp-Befehl gibt. Der ist aber 32 Bit (2 Worte) lang, und dementsprechend
sind auch die Vektoren 32 Bit breit, damit ein jmp reinpasst. reti ist
aber auch nur 16 Bit breit und füllt deshalb die Vektoren nur halb aus.
Der jeweils nächste reti bzw. rjmp steht dann an der falschen Stelle (zu
weit vorne).
Entweder wie vorgeschlagen aus nicht benutzten Vektoren in eine
Auffangroutine springen oder vor jedes reti ein nop stellen, dann passt
es. Oder mit der .org-Direktive arbeiten und die Adressen angeben. Am
besten immer die Vektortabelle aus dem jeweiligen Datenblatt 1:1
übernehmen. Dann geht nichts schief.
Leute, ihr seid der Hammer!
Vielen Dank! Hatte die Interruptvektoren aus dem Tutorial entnommen und
mir keine Gedanken darüber gemacht, dass die unterschiedlich breiten
Befehle die gute Ordnung kaputtmachen.
Jetzt klappts!
Besten Dank nochmal
Carlos
Carlos wrote:
> aber dann bleiben verirrte interrupts doch auf immer und ewig bzw. bis> der nächste interrupt kommt hängen!?
Das ist ja auch der Zweck der Übung. Ein "verirrter Interrupt" ist ein
schwerer Programmierfehler, den ich in die Endlosschleife schicke, damit
ich ihn erkenne.
Selbstverständlich kannst Du da auch ein "reti" platzieren, doch dann
besteht halt die Möglichkeit, solche Fehler zu übersehen.
>> Ist die Reihenfolge von code, Interrupttabelle wichtig?> Ich kann trotz der schnellen Antwort meinen Fehler nicht erkennen.>
Siehe die anderen Antworten...
>> Viele Grüße
Dito,
Bit- & Bytebruch,
Hannes
Carlos wrote:
> aber dann bleiben verirrte interrupts doch auf immer und ewig bzw. bis> der nächste interrupt kommt hängen!?
Du kannst ja in der Routine noch ne LED togglen lassen, damit Du merkst,
daß Du einen Programmierfehler gemacht hast.
> Ist die Reihenfolge von code, Interrupttabelle wichtig?
Nein, sie ist im Quelltext völlig wurscht.
Durch die Vektordefinitionen in Deinem Include sicherst Du ja ab, daß im
Hexfile alles an seinen richtigen Platz landet (man muß die Definitionen
natürlich auch benutzen).
Ich schreibe daher die Interrupts genau dahin, wo sie behandelt werden:
1
.include "m168def.inc"
2
3
rjmp init
4
5
.org INT_VECTORS_SIZE ; start after all interrupt vectors
Jeder einzelne Interrupt muss ja ausser mit sei nochmal einzeln
aktiviert werden, daher sollte, sofern man das nicht getan hat ja
eigentlich keiner auftreten können!?
Auf alle Fälle funktioniert es jetzt - zumindest in der Simulation.
Nochmal Danke, habe eine weitere Frage, aber die werde ich
passenderweise in einem neuen Thread stellen:
"Asynchrones Signal einlesen"
Viele Grüße
Carlos