Guten Abend,
Ich bin seit Kurzem dabei, mich mit den Programmieren von Atmels AVR µC
zu beschäftigen.
Nun habe ich ein Programm geschrieben, welches, ausgelöst über einen
steigende-Flanke Interrupt INT0 den Zustand von PB2 toggelt.
Beim Kompilieren sagt das Avr- Studio leider stets "illegal attempt to
re-use "Int0" als label.(L.26) Ich denke, das Problem entsteht durch die
im Interrupt verwendeten Unterprogrammaufrufe, kann mir da jemand helfen
?
Anbei der Quellcode:
; Schaltet bei einem Low Pegel an PB0
; den Zustand von PortB2 um.
.include "tn13def.inc"
.def temp= r16
.def Used= r17
rjmp Start
.org 0x001
rjmp INT0
Start:
ldi temp, RAMEND
out SPL, temp ; Stackpointer initialisieren
ldi temp, 0
out GIMSK, temp ; External Interrupt enable
ldi temp,0b00110011
out MCUCR, temp ; Sleep und INTO steigende Flanke enable
cbi Used, 0 ; Used muss auf 0 sein
sbi PORTB, portb0 ; Pull- up aktivieren
rjmp Main
Main:
SEI
SLEEP
rjmp Main
INT0: ; 26
cpi Used, 0b00000000
breq An ; Wenn Used= 0, PortB2 = 1
cpi Used, 0b00000000
brne Aus ; Wenn Used= 1, PortB2 = 0
reti
An:
sbi PORTB, portb2
sbi Used ; Used beschreiben
ret
Aus:
cbi PORTB, portb2
cbi Used, 0 ; Used löschen
ret
Die Fehlermeldung kommt, weil INT0 schon einmal als Label in der Datei
tn13def.inc definiert ist. Du musst Dir einen anderen Namen ausdenken.
Folgendes ist auch nicht ok:
Du kannst nicht aus der IR Routine in ein Unterprogramm springen und
dort mit RET enden.
Sleep (falls es Power down ist, ich habe keine Lust, das 0b00110011
auseinander zu klamüsern) geht nur mit Level Interrupt ( Taste auf 0
schalten)
Table 24. Interrupt 0 Sense Control
ISC01 ISC00 Description
0 0 The low level of INT0 generates an interrupt
request.
Du vergleichst USED immer mit 0 ?
sbi Used ; Used beschreiben --- mit was? ...
In der Interruptroutine muss das Statusregister gesichert werden ( und
wieder hergestellt).
>; Schaltet bei einem Low Pegel an PB0>; den Zustand von PortB2 um.
PB0 wird nicht abgefragt ?
INT0 ist an PB1.
Herrmueller
Hm, abgesehen von deinem merkwürdigen Programm: sbi/cbi funktionieren
nur im I/O-Bereich.
Gewöhn dir gleich an, zumindest das SREG bei Interrupt zu sichern und am
Ende wieder herzustellen.
.def sreg_bak r0
INT0:
in sreg_bak, sreg
.
.
.
out sreg, sreg_bak
reti
Ein weiterer grober Fehler: die springst mit jmp an die Funktionen an
und aus, beendest diese aber ret. Das ist sicher nicht das, was du
willst.
Würde nämlich bedeuten, zum Hauptprogramm zurückzukehren, ohne
allerdings das I-flag zurückzusetzen (das passiert nur bei reti)
Und zu deinem eigentlichen Programm - du kannst natürlich schon den
Zustand des PORTB.2 als Variable benutzen, verzweigen mit den Befehlen
sbic/sbis. Ansonsten eben ein Register.
INT0:
in sreg_bak, sreg
sbis portb, 2
rjmp an
aus:
cbi portb, 2
rjmp end_int0
an:
sbi portb, 2
end_int0:
out sreg, sreg_bak
reti
Hoffentlich habe ich jetzt nicht selbst Fehler drin, lange nichts mehr
mit Assembler gemacht :-)
crazy horse schrieb:> Hoffentlich habe ich jetzt nicht selbst Fehler drin, lange nichts mehr> mit Assembler gemacht :-)
INT0 ist der Name von Bit 6 in GIMSK, geht also nicht als Label, ist
bereits vergeben.
Die SREG-Sicherung ist hier ausnahmsweise mal nicht nötig, da sbis,
rjmp, cbi, sbi keine Flags beeinflussen. Trotzdem sollte man sich die
SREG-Sicherung angewöhnen...
...
Guten Morgen,
Erst einmal vielen Dank für die Hilfestellungen.
Wenn ich das noch einmal kurz zusammenfasse, darf ich also die Interrupt
Routine nicht INT0 nennen, der Name ist schon vergeben. Außerdem darf
ich aus einem Interrupt hinaus keine Unterprogramme aufrufen und diese
mit -ret beenden. Wenn ich mit dem Interrupt dennoch ein Programm,
welches Unterprogramme verwendet, starten will, ginge das mit
Interrupt:
rjmp Programm
Programm:
SEI
...
rcall Unterprogramm
...
Unterprogramm:
...
ret ?
leo
Hannes Lux schrieb:> Die SREG-Sicherung ist hier ausnahmsweise mal nicht nötig, da sbis,> rjmp, cbi, sbi keine Flags beeinflussen. Trotzdem sollte man sich die> SREG-Sicherung angewöhnen...
Die erste Instruktion der ISR ist aber:
1
cpi Used, 0b00000000
und die verändert selbstverständlich die Flags. Die Sicherung ist
dennoch ausnahmsweise nicht nötig, weil in der "Hauptschleife" des
Programms keine Flags benutzt werden.
Leo F. schrieb:> Wenn ich das noch einmal kurz zusammenfasse, darf ich also die Interrupt> Routine nicht INT0 nennen, der Name ist schon vergeben.
Richtig.
> Außerdem darf ich aus einem Interrupt hinaus keine Unterprogramme> aufrufen und diese mit -ret beenden.
Doch, das darfst du schon. Nur tut dein Programm das nicht. Ein
Unterprogramm ruft man mit rcall auf. Mit breq dagegen machst du nur
einen Sprung.
Du mußt dich entscheiden: Entweder sind An und Aus Unterprogramme, die
du mit rcall aufrufst und mit ret wieder beendest, oder es sind nur
Teile deiner ISR, an die du springst und die dann wahlweise wieder
explizit zurückspringen oder halt die ISR mit reti beenden.
Rolf Magnus schrieb:> Die erste Instruktion der ISR ist aber:
cpi Used, 0b00000000
Aber nicht im Code von crazy horse, auf den ich mich bezog.
crazy horse schrieb:> INT0:> in sreg_bak, sreg> sbis portb, 2> rjmp an> aus:> cbi portb, 2> rjmp end_int0> an:> sbi portb, 2> end_int0:> out sreg, sreg_bak> reti
...
Guten Abend,
Es ist zwar schon eine Weile her, aber ich habe nun vesucht, alle
Ratschläge zu berücksichtigen, es tritt beim Assemblieren stets der
Fehler:
[line 37] : errror: invalid number
Nimmt AVR Studio die 0 als Varible an, denn gegen das Setzen bzw.
Ausführung des "sibis"- Befehls des 7. Bits im r17 spricht ja eigentlich
nichts, oder ?
Hier noch einmal der Code, auch angehängt:
;Durch ein Low Level an Pb1 wird Pb3 im 1Hz Takt im Verhältnis 998:2
;ein-und ausgeschaltet, eine weiteres Low Level schaltet zurück in den
Sleep Mode.
.include "tn13def.inc"
.def SREGbak = r15
.def temp = r16
.def Used = r17
rjmp Anfang
.org 0x0001
rjmp Interrupt
Anfang:
ldi temp,RAMEND
out SPL, temp ; Stack Pointer definieren
ldi temp, 0b00000001
out ADCSRA, temp ; ADC ausschalten
ldi temp, 0b10000000
out ACSR, temp ; AC ausschalten
ldi temp, 0b00000000
out WDTCR, temp ; WDT ausschalten
ldi temp, 0b01000000
out GIMSK, temp ; External Interrupt Enable
ldi temp, 0b01110000
out MCUCR, temp ; Sleep enable, INTO steigende Flanke
enable
ldi Used, 0x00 ; Used muss auf 0 sein
sbi PORTB, PORTB1 ; Pull- up aktivieren
rjmp Main
Main:
SEI ; Interrupts global freigeben
SLEEP ; Power Down Mode
rjmp Main
Interrupt:
in SREGbak, SREG
sbis Used, 7 ; >>>>>>>37<<<<<<<
rcall Blink ; Wenn "Used" noch nicht beschrieben
wurde, Blinken
ldi Used, 0xFF
rjmp IntEnd
Blink:
SEI
ldi Used, 0xFF ; Used = 1 , zeigt, dass Routine
aufgerufen wurde
sbi DDRB, 3 ; PortB3 als Ausgang
sbi PORTB, 3
WartenLow: ; PortB3 bleibt 998mS lang auf 0
ldi r18, 72 ; 1*1
Warten1:
ldi r19, 73 ; 72*1
Warten2:
dec r18 ; 72*73*1
brne Warten2 ; 72*73*2
dec r19 ; 72*1 = ca.1s
brne Warten1 ; 72*2
sbi PORTB, 3
ldi r19, 32
WartenHigh: ; PortB3 2mS lang auf 1
dec r19 ;
brne WartenHigh
cbi PORTB, 3
rjmp Blink
IntEnd:
out SREG, SREGbak ; Alles wiederherstellen, LIFO
beachtet
reti
Leo