Forum: Mikrocontroller und Digitale Elektronik AVR Interrupt immer zu 0x000 (Assembler)


von Julian (Gast)


Lesenswert?

Hallo Forum

(ATmega168)

Ich wollte mich eigentlich in das Thema Analogkomparator einarbeiten 
aber bin dann über ein eher unerwartetes Problem gestolpert:

Ich wollte via den Analogcomparator einen Interrupt auslösen und zwar 
wenn der Referenzwert über oder unterschritten (toggle) wird.
Nur ist jetzt das Problem, dass beim Simulieren im Atmelstudio der 
Interrupt immer auf die Adresse 0x000, also Reset oder Init ausgelöst 
wird. Das Programm kommt also gar nie in die ISR.

Hier der benutzte Code:

Das erste LED wird sofort gesetzt, das zweite soll dann wenn der 
Analogkomparator ausgelöst hat seinen Zustand ändern anschliessend gehts 
wieder in die Warteschlaufe.
1
.ORG 0x000 
2
rjmp RESET ; Reset Handler
3
;.ORG 0x001 rjmp EXT_INT0 ; IRQ0 Handler
4
;.ORG 0x002 rjmp EXT_INT1 ; IRQ1 Handler
5
;.ORG 0x003 rjmp PCINT0 ; PCINT0 Handler
6
;.ORG 0x004 rjmp PCINT1 ; PCINT1 Handler
7
;.ORG 0x005 rjmp PCINT2 ; PCINT2 Handler
8
;.ORG 0x006 rjmp WDT ; Watchdog Timer Handler
9
;.ORG 0x007 rjmp TIM2_COMPA ; Timer2 Compare A Handler
10
;.ORG 0x008 rjmp TIM2_COMPB ; Timer2 Compare B Handler
11
;.ORG 0x009 rjmp TIM2_OVF ; Timer2 Overflow Handler
12
;.ORG 0x00A rjmp TIM1_CAPT ; Timer1 Capture Handler
13
;.ORG 0x00B rjmp TIM1_COMPA ; Timer1 Compare A Handler
14
;.ORG 0x00C rjmp TIM1_COMPB ; Timer1 Compare B Handler
15
;.ORG 0x00D rjmp TIM1_OVF ; Timer1 Overflow Handler
16
;.ORG 0x00E rjmp TIM0_COMPA ; Timer0 Compare A Handler
17
;.ORG 0x00F rjmp TIM0_COMPB ; Timer0 Compare B Handler
18
;.ORG 0x010 rjmp TIM0_OVF ; Timer0 Overflow Handler
19
;.ORG 0x011 rjmp SPI_STC ; SPI Transfer Complete Handler
20
;.ORG 0x012 rjmp USART_RXC ; USART, RX Complete Handler
21
;.ORG 0x013 rjmp USART_UDRE ; USART, UDR Empty Handler
22
;.ORG 0x014 rjmp USART_TXC ; USART, TX Complete Handler
23
;.ORG 0x015 rjmp ADC ; ADC Conversion Complete Handler
24
;.ORG 0x016 rjmp EE_RDY ; EEPROM Ready Handler
25
rjmp ANA_COMP ; Analog Comparator Handler
26
;.ORG 0x018 rjmp TWI ; 2-wire Serial Interface Handler
27
;.ORG 0x019 rjmp SPM_RDY ; Store Program Memory Ready Handler
28
29
reset:
30
LDI    R16, LOW(RAMEND)
31
OUT    SPL, R16
32
LDI    R16, HIGH(RAMEND)
33
OUT    SPH, R16
34
35
SBI    DDRB,1      ;KONTROLL LED
36
sbi    ddrb,2      ;KONTROLL LED (TOGGLET BEI ANALOGINT)
37
38
;INIT ANALOG COMPERATOR
39
sbi    portb,2      ;KONTROLL LED
40
41
LDI    R16, 0B00001000
42
OUT    ACSR, R16
43
LDI    R16, 0B00000011
44
sts    DIDR1, R16
45
46
SEI
47
RJMP LOOP
48
49
ANA_COMP:
50
cbi    portb,2
51
SBIC  PORTB,1
52
rjmp LEDON
53
SBI    PORTB,1
54
RETI
55
LEDON:
56
CBI    PORTB,1
57
RETI
58
59
LOOP:
60
NOP
61
RJMP LOOP

Ich weiss, das Programm ist sehr simpel, aber trotzdem sehe ich den 
Fehler nicht.
Vielen Dank für eure Hilfe

von (prx) A. K. (prx)


Lesenswert?

Mega168 und RJMP passt nicht, die Interrupt-Leiste ist also falsch und 
der Interrupt geht in den Wald.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>;.ORG 0x016 rjmp EE_RDY ; EEPROM Ready Handler
>rjmp ANA_COMP ; Analog Comparator Handler

Wo ist das org hin? Das steht nicht zum Spass da.

von (prx) A. K. (prx)


Lesenswert?

holger schrieb:
> Wo ist das org hin? Das steht nicht zum Spass da.

Wär auch mit ORG falsch. Die Interrupt-Leiste ist vom falschen 
Controller abgeschrieben. Der Mega168 braucht 2 Worte pro Handler.

von LostInMusic (Gast)


Lesenswert?

Im Datasheet zum ATmega48/88/168 findest Du im Kapitel "Interrupts" u. 
a. eine komplette Muster-Interrupttabelle. Dort ist auch erklärt, warum 
die so aussieht wie sie aussieht.

von Willi (Gast)


Lesenswert?

Du hast ja fast alle Interruptvectoren auskommentiert.
Damit steht das rjmp ANA_COMP an der völlig falschen Stelle im Speicher.
der Sprung müsste bei 0x17 stehen.

Nimm alle ; vor den .ORGs raus und schreibe .ORG 0x17 vor rjmp ANA_COMP.

MfG Willi

von (prx) A. K. (prx)


Lesenswert?

Willi schrieb:
> der Sprung müsste bei 0x17 stehen.

Nochmal: NEIN. Nicht beim Mega168. Nur beim Mega48/88.

von Willi (Gast)


Lesenswert?

prx hat Recht:
Der Sprung gehört nach 0x2e.

LostInMusic hat auch Recht:
Besser Du guckst ins Datenblatt.

MfG Willi

von Amateur (Gast)


Lesenswert?

Kopieren geht nicht immer über studieren!
In gewissen Grenzen sollte man wissen, was man(n) tut.

von Peter D. (peda)


Lesenswert?

A. K. schrieb:
> Die Interrupt-Leiste ist vom falschen
> Controller abgeschrieben.

Deshalb schreibt man die ja auch nicht ab, sondern nimmt das zum Target 
passende Include:

Beitrag "Re: avr asm: interrupt int0 löst bei fallender flanke nicht aus"

von Julian (Gast)


Lesenswert?

Vielen Dank an euch alle

Ich habe nochmals das Datenblatt zur Hand genommen und habe gesehen, was 
prx gemeint hat (auch mit den Unterschieden zu den mega48, mega88).

Ich werde das anpassen und nochmals versuchen.
(ja, ich gebe es zu, es ist das erste mal, dass ich mich über einen 
ATTiny2313 hinauswage)

Danke und Grüsse
Julian

von Thomas (kosmos)


Lesenswert?

wenn du einzelne Interrupts nicht brauchst setzt du ein reti statt z.B. 
rjmp EXT_INT0

dann gibt es keine Fehlermeldung wenn du keine subroutine EXT_INT0 
erstellt hast und dein Programm macht einfach unbeeindruckt dort weiter 
wo es vor dem Interruptaufruf war und wenn du den Beginn mit .org 0x00 
setzt wird alles andere einfach nachfolgend geschrieben dann braucht man 
nicht jedesmal .org neu zu schreiben.
1
.ORG  0x00
2
rjmp RESET         ; Reset handler
3
reti               ; rjmp EXT_INT0      ; IRQ0 handler
4
rjmp PIN_CHANGE    ; Pin change handler
5
rjmp TIM1_CMP1A    ; Timer1 compare match 1A
6
rjmp TIM1_CMP1B    ; Timer1 compare match 1B
7
rjmp TIM1_OVF      ; Timer1 overflow handler
8
rjmp TIM0_OVF      ; Timer0 overflow handler
9
rjmp USI_STRT      ; USI Start handler
10
rjmp USI_OVF       ; USI Overflow handler
11
rjmp EE_RDY        ; EEPROM Ready handler
12
rjmp ANA_COMP      ; Analog Comparator handler
13
rjmp AD_C          ; ADC Conversion Handler

von Karl H. (kbuchegg)


Lesenswert?

Thomas O. schrieb:

> setzt wird alles andere einfach nachfolgend geschrieben dann
> braucht man
> nicht jedesmal .org neu zu schreiben.

... und tappst wieder in die Falle, dass es unterschiedliche AVR gibt. 
Bei den einen reicht deine Lösung, bei den anderen reicht sie aber 
nicht, weil, banal gesagt, ein rjmp bzw. ein reti nicht genug Bytes 
verbraucht, um von einem Interrupt Einsprungspunkt zum nächsten zu 
gelangen.
Der vom TO verwendete ist genau so einer. Deine angedachte Lösung ist 
genau aus diesem Grund falsch.


Die .org Variante vermeidet dieses Problem. Es hat schon seinen Grund, 
warum sie empfohlen wird.

von Thomas (kosmos)


Lesenswert?

es ist mir vollkommen klar das jeder AVR unterschieliche Interrupts hat 
und dadurch auch einen anderen Interruptvectortabelle erfordern, das war 
nur ein Beispiel eines ATTiny26 aber in jedem Datenblatt gibts auch die 
zugehörige Tabelle.

Die Tabelle des TO ist auch fortlaufend da bringen die ganzen ORG 
Anweisungen nicht.

Wenn man das Problem hat das man mit rjmp nur 2kbyte weit springen kann, 
dann sollte mal hat die Interrupt Unterprogramm halt in diesen 2kByte 
platzieren.

wenn man mit icall arbeiten will und 2 Befehle benötigt. Dann wird der 
Interruptvector bestimmt nicht fortlaufen sein sondern eher eine Stelle 
auslassen.

.ORG  0x00
mov     R30,R0
icall
.ORG    0x02
.....
icall

könnte auch so aussehen wenn man innerhalb der 2 kbytes bleibt
.ORG 0x00
nop
rjmp XYZ
nop
rjmp UVW
....
....

von c-hater (Gast)


Lesenswert?

Thomas O. schrieb:

> Die Tabelle des TO ist auch fortlaufend da bringen die ganzen ORG
> Anweisungen nicht.

Doch, natürlich. Sie bringen genau dann etwas, wenn es 
auskommentierte/unbenutzte Vektoren gibt, dann sorgen sie nämlich dafür, 
daß die benutzten trotzdem an der richtigen Stelle im Flash liegen.

Das tun sie allerdings nur, wenn für das .org auch der richtige 
Parameter angegeben wird. Zahlenwerte zu verwenden ist hier 
kontraproduktiv. Dafür gibt es in den devicespezifischen Include-Dateien 
extra die passenden Symbole. Verwendet man die als .org-Parameter, 
stimmt schonmal die Adresse des Vectors ohne jede weitere Verrenkung, 
Rechnerei und Datenblatt-Recherche.

Das Programm des OP sollte also etwa so beginnen:

.include "m168def.inc"

.org 0        ;leider hat Atmel kein Symbol dafür vorgesehen
  rjmp reset
.org ACIaddr  ;die Definition steht in der includierten Datei
  rjmp acint

> Wenn man das Problem hat das man mit rjmp nur 2kbyte weit springen kann,

Das kann niemals passieren. Mit rjmp kann man +- 2k word weit 
springen, also insgesamt 8kByte Flash erreichen. Genau dieser 
Sachverhalt ist auch der Grund, warum die jmp-Instruktion nur bei den 
Devices nicht verfügbar ist, die 8k Flash oder weniger haben, hier 
reicht schlicht die rjmp-Instruktion aus, um den gesamten Speicher 
erreichen zu können.

von Karl H. (kbuchegg)


Lesenswert?

Thomas O. schrieb:

> Wenn man das Problem hat das man mit rjmp nur 2kbyte weit springen kann,
> dann sollte mal hat die Interrupt Unterprogramm halt in diesen 2kByte
> platzieren.
>
davon war überhaupt nie die Rede und das ist ach gar nicht das Problem

> wenn man mit icall arbeiten will und 2 Befehle benötigt. Dann wird der
> Interruptvector bestimmt nicht fortlaufen sein sondern eher eine Stelle
> auslassen.

und auch davon war nie die Rede.

Die Rede ist davon, dass mit deiner Methode die Interrupt Tabelle mit 
rjmp und reti aufgebaut wird, bei einem Mega168 dieselbe Technik aber 
mit jmp und reti+nop aufgebaut werden muss (oder mit rjmp+nop und 
reti+nop)

was beim Mega48 so aussieht (am Beispiel vom INT1)
1
    rjmp RESET ; Reset Handler
2
    reti ; rjmp EXT_INT0 ; IRQ0 Handler
3
    rjmp EXT_INT1 ; IRQ1 Handler
4
    reti ; rjmp PCINT0 ; PCINT0 Handler
5
    reti ; rjmp PCINT1 ; PCINT1 Handler
6
    reti ; rjmp PCINT2 ; PCINT2 Handler
7
    reti ; rjmp WDT ; Watchdog Timer Handler
8
....

sieht beim Mega168 so aus
1
    jmp  RESET ; Reset Handler
2
    reti ; rjmp EXT_INT0 ; IRQ0 Handler
3
    nop
4
    jmp  EXT_INT1 ; IRQ1 Handler
5
    reti ; rjmp PCINT0 ; PCINT0 Handler
6
    nop
7
    reti ; rjmp PCINT1 ; PCINT1 Handler
8
    nop
9
    reti ; rjmp PCINT2 ; PCINT2 Handler
10
    nop
11
    reti ; rjmp WDT ; Watchdog Timer Handler
12
    nop
13
....


vielleicht bemerkst du den kleinen aber feinen Unterschied zwischen rjmp 
und jmp und warum bei der Version für den Mega168 nach einem reti 
jeweils ein nop stehen muss.

Damit, dass man auf einigen AVR mit einem rjmp nicht den kompletten 
Speicher erreichen kann, hat das ganze nur insofern zu tun, dass bei dem 
einen Controller (im Beispiel ein Mega48) der weiterführende rjmp an der 
Adresse 0x002 im Speicher steht, während er beim größeren Mega168 an der 
Adresse 0x004 im Speicher erwartet wird. Und das, obwohl beide 
Prozessoren ansonsten den gleichen Aufbau und die gleiche Reihenfolge 
der Interrupt Vektoren haben. Die beiden Mega-Typen teilen sich sogar 
das gleiche Datenblatt.

Da man in den wenigsten Fällen wohl die komplette Interrupt Vektor 
Tabelle braucht, so wie im Falle des TO, ist die Version mit dem 
wenigsten Tippaufwand wohl die hier
1
...
2
.org 0x0000
3
     rjmp   RESET
4
5
.org ACIaddr
6
     rjmp   ANA_COMP
7
8
.org INT_VECTORS_SIZE
9
RESET:
10
     ....
11
12
ANA_COMP:
13
     ....
14
     reti

sie hat auch gleichzeitig den Vorteil, dass man sich damit von dem 
Unterschied des Aufbaus der Vektortabelle beim Mega48 und Mega168 gelöst 
hat. Durch den ORG kümmert sich der Assembler darum. Die Lösung 
funktioniert auf Mega48 und Mega168 gleichermassen. Und wenn es bei 
einem Mega168 mal der Fall sein sollte, dass der RJMP nicht weit genug 
reicht, dann teilt einem das der Assembler mit und man ersetzt einfach 
den rjmp durch einen jmp, ohne sich um weiteres kümmern zu müssen.

Zudem ist man von der Reihenfolge der Interrupt Vektoren befreit. Selbst 
ein
1
...
2
.org 0x0000
3
     rjmp   RESET
4
5
.org ACIaddr
6
     rjmp   ANA_COMP
7
8
.org INT0addr
9
     rjmp   EXT_INT0
10
11
.org INT1addr
12
     rjmp   EXT_INT1
13
14
.org INT_VECTORS_SIZE
15
RESET:
16
     ....

mündet in einer korrekt aufgebauten Interrupt Vektor Tabelle, obwohl ich 
die einzelnen Vektoren in einer ganz anderen Reihenfolge im Code 
aufgeführt habe, als sie hinterher im Speicher stehen werden bzw. als 
sie im Datenblatt aufgeführt sind.

: Bearbeitet durch User
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.