Forum: Mikrocontroller und Digitale Elektronik Verständnisproblem AVR- Interrupt


von Leo F. (Gast)


Lesenswert?

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

von Herr M. (herrmueller)


Lesenswert?

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

von crazy horse (Gast)


Lesenswert?

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 :-)

von Hannes L. (hannes)


Lesenswert?

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

...

von Leo F. (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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.

von Hannes L. (hannes)


Lesenswert?

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

...

von Leo F. (Gast)


Angehängte Dateien:

Lesenswert?

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

von spess53 (Gast)


Lesenswert?

Hi

'sbis' ist der Befehl für IO-Register. Für r0...r31 nimmt man 'sbrs'.

MfG Spess

von Leo F. (Gast)


Lesenswert?

Kopf >>>>> Tisch
Funktioniert wunderbar, noch einmal vielen Dank für die Hilfestellungen.

                      MfG, leo

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.