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
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.
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.
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
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
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.
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.
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
....
....
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.
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.