Forum: Mikrocontroller und Digitale Elektronik Interrupt Problem


von Carlos (Gast)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

Der Mega32 hat 2 Words je Eintrag in der Sprungtabelle.

Meine Sprungtabelle sieht so aus:
1
.include "m32def.inc"   ;Namen für Systemkonstanten (I/Os, Bits...)
2
3
;(es folgen Vereinbarungen...)
4
5
.cseg       ;Code-Segment (Flash, Programmspeicher)
6
.org 0      ;Interrupt-Sprungtabelle ATmega32
7
 jmp RESET          ;Reset Handler
8
 jmp nix;EXT_INT0       ;IRQ0 Handler
9
 jmp nix;EXT_INT1       ;IRQ1 Handler
10
 jmp nix;EXT_INT2       ;IRQ2 Handler
11
 jmp nix;TIM2_COMP      ;Timer2 Compare Handler
12
 jmp nix;TIM2_OVF       ;Timer2 Overflow Handler
13
 jmp nix;TIM1_CAPT      ;Timer1 Capture Handler
14
 jmp nix;TIM1_COMPA     ;Timer1 CompareA Handler
15
 jmp nix;TIM1_COMPB     ;Timer1 CompareB Handler
16
 jmp nix;TIM1_OVF       ;Timer1 Overflow Handler
17
 jmp nix;TIM0_COMP      ;Timer0 Compare Handler
18
 jmp nix;TIM0_OVF       ;Timer0 Overflow Handler
19
 jmp nix;SPI_STC        ;SPI Transfer Complete Handler
20
 jmp nix;USART_RXC      ;USART RX Complete Handler
21
 jmp nix;USART_UDRE     ;UDR Empty Handler
22
 jmp nix;USART_TXC      ;USART TX Complete Handler
23
 jmp nix;ADCC           ;ADC Conversion Complete Handler
24
 jmp nix;EE_RDY         ;EEPROM Ready Handler
25
 jmp nix;ANA_COMP       ;Analog Comparator Handler
26
 jmp nix;TWI            ;Two-wire Serial Interface Handler
27
 jmp nix;SPM_RDY        ;Store Program Memory Ready Handler
28
nix: rjmp nix           ;Falle für falschen Interrupt-Aufruf
29
reset:

Bei den benutzten Vektoren wird das "nix;" entfernt.

...

von Carlos (Gast)


Lesenswert?

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

von obake (Gast)


Lesenswert?

Hi Carlos,

sieh mal in ""m32def.inc" nach, an welcher Adresse der Timer0 OVF Vektor 
stehen müsste!

von Johannes M. (johnny-m)


Lesenswert?

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.

von Albrecht H. (alieninside)


Lesenswert?

In etwa so, wenn du die RETIs behalten willst:


1
#ifdef mym16
2
.ORG $000 ;RESET External Pin, Power-on Reset, Brown-out, Reset, Watchdog Reset, and JTAG AVR Reset
3
rjmp  start 
4
.ORG $002 ;INT0 External Interrupt Request 0
5
reti
6
.ORG $004 ;INT1 External Interrupt Request 1
7
reti
8
.ORG $006 ;TIMER2 COMP Timer/Counter2 Compare Match
9
reti
10
.ORG $008 ;TIMER2 OVF Timer/Counter2 Overflow
11
reti
12
.ORG $00A ;TIMER1 CAPT Timer/Counter1 Capture Event
13
reti
14
.ORG $00C ;TIMER1 COMPA Timer/Counter1 Compare Match A
15
reti
16
.ORG $00E ;TIMER1 COMPB Timer/Counter1 Compare Match B
17
reti
18
.ORG $010 ;TIMER1 OVF Timer/Counter1 Overflow
19
reti
20
.ORG $012 ;TIMER0 OVF Timer/Counter0 Overflow
21
;reti
22
rjmp Timer0Overflow
23
.ORG $014 ;SPI, STC Serial Transfer Complete
24
reti
25
.ORG $016 ;USART, RXC USART, Rx Complete
26
reti
27
.ORG $018 ;USART, UDRE USART Data Register Empty
28
reti
29
.ORG $01A ;USART, TXC USART, Tx Complete
30
reti
31
.ORG $01C ;ADC ADC Conversion Complete
32
reti
33
.ORG $01E ;EE_RDY EEPROM Ready
34
reti
35
.ORG $020 ;ANA_COMP Analog Comparator
36
reti
37
.ORG $022 ;TWI Two-wire Serial Interface
38
reti
39
.ORG $024 ;INT2 External Interrupt Request 2
40
reti
41
.ORG $026 ;TIMER0 COMP Timer/Counter0 Compare Match
42
reti
43
.ORG $028 ;SPM_RDY Store Program Memory Ready
44
reti
45
#endif ;mym16
46
47
#ifdef mym88
48
.ORG 0x000 ;RESET External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset
49
rjmp  start 
50
.ORG 0x001 ;INT0 External Interrupt Request 0
51
reti
52
.ORG 0x002 ;INT1 External Interrupt Request 1
53
reti
54
.ORG 0x003 ;PCINT0 Pin Change Interrupt Request 0
55
reti
56
.ORG 0x004 ;PCINT1 Pin Change Interrupt Request 1
57
reti
58
.ORG 0x005 ;PCINT2 Pin Change Interrupt Request 2
59
reti
60
.ORG 0x006 ;WDT Watchdog Time-out Interrupt
61
reti
62
.ORG 0x007 ;TIMER2 COMPA Timer/Counter2 Compare Match A
63
reti
64
.ORG 0x008 ;TIMER2 COMPB Timer/Counter2 Compare Match B
65
reti
66
.ORG 0x009 ;TIMER2 OVF Timer/Counter2 Overflow
67
reti
68
.ORG 0x00A ;TIMER1 CAPT Timer/Counter1 Capture Event
69
reti
70
.ORG 0x00B ;TIMER1 COMPA Timer/Counter1 Compare Match A
71
reti
72
.ORG 0x00C ;TIMER1 COMPB Timer/Coutner1 Compare Match B
73
reti
74
.ORG 0x00D ;TIMER1 OVF Timer/Counter1 Overflow
75
reti
76
.ORG 0x00E ;TIMER0 COMPA Timer/Counter0 Compare Match A
77
reti
78
.ORG 0x00F ;TIMER0 COMPB Timer/Counter0 Compare Match B
79
reti
80
.ORG 0x010 ;TIMER0 OVF Timer/Counter0 Overflow
81
rjmp Timer0Overflow
82
.ORG 0x011 ;SPI, STC SPI Serial Transfer Complete
83
reti
84
.ORG 0x012 ;USART, RX USART Rx Complete
85
reti
86
.ORG 0x013 ;USART, UDRE USART, Data Register Empty
87
reti
88
.ORG 0x014 ;USART, TX USART, Tx Complete
89
reti
90
#endif ;mym88

von Carlos (Gast)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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
6
init:
7
        nop
8
; place your init stuff, main loop and so on
9
; ...
10
11
; e.g. insert a SPM interruppt handler:
12
.set    curr_addr = PC
13
.org    SPMRaddr
14
        rjmp    SPM_handler     ; jump to handler
15
.org    curr_addr               ; restore address
16
SPM_handler:
17
;insert interrupt stuff here
18
        reti

Peter

von Hannes L. (hannes)


Lesenswert?

Sorry, das wäre mir (mir persönlich) zu unübersichtlich, ich habe es 
nicht so mit dem Abstrahieren...

...

von Carlos (Gast)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.