Forum: Mikrocontroller und Digitale Elektronik Interrups in Assembler


von Hermann (Gast)


Lesenswert?

Hallo Ich habe eine kleine Schaltung aufgebaut, die 2 Interrups 
beinhaltet. INT0 und INT1.

Wird der Taster 1 betätigt, soll die Grüne Led an PortC 7 mal 
Aufleuchten.
Wird der Taster 2 betätigt, soll die Rote Led an PortB 5 mal 
Aufleuchten.

Wenn ein Taster Betätigt wird, springt das Programm in ein 
Unterprogramm, welches mit CLI beginnt und mit SEI endet. (dies sollte 
die Taster verriegeln) jedoch funktioniert das nicht.

Hier ist mein Listing:

.include"m8def.inc"
.org 0x0000

RJMP MAIN
RJMP TASTER1
RJMP TASTER2
RETI


MAIN:
SER R20
OUT DDRB,R20
OUT DDRC,R20

LDI R16,0x04    ;)
OUT SPH,R16      ;)Stack Configuration
LDI R16,0x5F    ;)  (RAMEND)
OUT SPL,R16      ;)


  SBI DDRD,2        ;)
    SBI PortD,2      ;)Pull Up wiederstand aktivieren
  CBI DDRD,2        ;)

  SBI DDRD,3        ;)
    SBI PortD,3      ;)Pull Up wiederstand aktivieren
  CBI DDRD,3        ;)
CLR R25

  SEI        ;Global Interrupt aktivieren

  LDI R24,0B11000000    ;)Da das SBI nicht bei 0x3B geht ersetzt also 
den befehl SBI 0X3B,6
  OUT GICR,R24          ;)Interrupt INT0 und INT1 freigeben

  LDI R16,0B00001010    ;Lade den Bitbefehl der auf Seite 66 
nachgeschaut wurde in R16 Fallende Flanke INT0,INT1
  OUT MCUCR,R16      ;MCUCR = I/O REG 0x35


Immer: RJMP Immer



TASTER1:
CLI      ;Global Interrupt Disable
  OUT GICR,R25          ;)Interrupt INT0 und INT1 verbieten
SER R20
LDI R21,0x0E

Weiter1:

OUT PORTC,R20

ldi  R17, $16
WGLOOP0:  ldi  R18, $42
WGLOOP1:  ldi  R19, $DD
WGLOOP2:  dec  R19
          brne WGLOOP2
          dec  R18
          brne WGLOOP1
          dec  R17
          brne WGLOOP0
; -----------------------------
; delaying 2 cycles:
          nop
          nop

COM R20
DEC R21
BRNE Weiter1
SEI        ;Global Interrupt Enabel
OUT GICR,R24          ;)Interrupt INT0 und INT1 freigeben
RETI

TASTER2:
CLI      ;Global Interrupt Disable
  OUT GICR,R25          ;)Interrupt INT0 und INT1 verbieten
SER R20
LDI R21,0x0A

Weiter2:

OUT PORTB,R20

ldi  R17, $16
WGLOOP00:  ldi  R18, $42
WGLOOP01:  ldi  R19, $DD
WGLOOP02:  dec  R19
          brne WGLOOP02
          dec  R18
          brne WGLOOP01
          dec  R17
          brne WGLOOP00
; -----------------------------
; delaying 2 cycles:
          nop
          nop

COM R20
DEC R21
BRNE Weiter2
SEI        ;Global Interrupt Enabel
  OUT GICR,R24          ;)Interrupt INT0 und INT1 freigeben
RETI

;_________________________________________________________________



Ich hoffe das mir jemand helfen kann

MFG, Hermann

von Hc Z. (mizch)


Lesenswert?

Nur ein paar Sachen:

Die Sprünge zur Interruptroutine gehören auf bestimmte Adressen.  Da 
fehlt ein „.org” vor den 2 rjmps.

Was Du alles mit cli und sei in den Interruptroutinen und Manipulation 
von GICR veranstaltest, ergibt alles keinen Sinn.  Schau Dir einfach den 
Ablauf eines Interrupts im Manual mal nach und wie sich die Flags 
automatisch setzen bzw. löschen.

Delay-Loops im Interrupt sind auch nicht übermäßig sinnvoll, aber zur 
Übung mag's angehen.  Du musst nur schwören, das in keinem ernst 
gemeinten größeren Programm zu machen.

von Sabb (Gast)


Lesenswert?

Die Interrupts sind Schrott. Wie schon erwaehnt macht man nichts mit 
CLI/SEI im Interrupt. Dafuer muss man das Status Register sichern. Das 
steht aber im Manual. Und Delays in Interupt sind auch nichts. Sowas 
gibt's schlicht nicht.

Falls man eine Taste entprellen will so macht man das mit einem Timer 
und einer Zustandsmaschine.

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hc Zimmerer und Sabb haben wichtige Punkte schon angesprochen.

Im Anhang ist ein simulationsfähiges Programm. Wenn du simulierst, 
siehst du, dass INT0 Interrupt mit den 7x Blinken und Warten den µC fast 
3,5 Sekunden lang in der INT0 ISR blockiert! Deine Endlosschleife (OK 
ist ist im Moment leer) und andere ISRs (Taster2/INT1!) werden in dieser 
Zeit nicht abgearbeitet!

von Hermann (Gast)


Lesenswert?

Hallo, vielen Dank für die schnellen Antwochten.Ich habe mir das 
simulationsprogramm angeschaut und getestet. Den Delay loop habe ich 
daher in die Interrupt schleife getan da ich während dieser Zeit eh 
nichts anderes machen kann. Die Befehle CLI und SBI habe ich verwendet 
um die Taster zu verriegeln. das scheint jedoch nicht zu funktionieren.
Doch wie kann ich das tun? Indem ich die Ports während dem Interrupt als 
Ausgang definiere?

Mir ist aufgefallen das wenn ich den Taster betätige,es sein kann das 
die LED doppelt so oft Blinkt wie sie soll. Ich schätze das es daran 
liegt, das sie nicht entprellt sind, und somit 2 mal als interrupt 
erkannt werden.
Somit zu meiner zweiten Frage, wie kann ich einen Taster entprellen wenn 
er einen Interrupt auslöst? muss ich die +-5ms Entprellzeit in die 
Interruptschleife setzen?

Ich hoffe das ihr mir hier weiter helfen könnt...

MFG Hermann

von Peter D. (peda)


Lesenswert?

Das wird hier fast täglich ad nauseam durchgekaut, warum es falsch ist 
und wie mans richtig macht.

Benutz mal die Suchfunktion.


Interuptbedingungen setzen einfach erstmal nur ein Interruptflag. Ob 
dabei Interupts enabled sind, spielt keine Geige.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Hermann schrieb:

> nichts anderes machen kann. Die Befehle CLI und SBI habe ich verwendet
> um die Taster zu verriegeln. das scheint jedoch nicht zu funktionieren.
> Doch wie kann ich das tun? Indem ich die Ports während dem Interrupt als
> Ausgang definiere?

Ganz abgesehen davon, dass man Tasten anders behandelt (Peter hat das ja 
schon angesprochen) ...  du brauchst gar nichts tun! Kein CLI, kein SEI. 
Während eine Interrupt Routine läuft, sind Interrupts per Default 
gesperrt. Da kann dir also kein andere Interrupt in die Quere kommen!

von Hermann (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Während eine Interrupt Routine läuft, sind Interrupts per Default
> gesperrt. Da kann dir also kein andere Interrupt in die Quere kommen!

Ja das stimmt, jedoch werden sie danach abgearbeitet... und das dürfen 
sie nicht...d.h. kurtz gefasst:

Eine kurtze betätigung von Taster1 generiert 5 rote LED-Impulse,ein 
Drücken von Taster2 erzeugt 7 grüne LED-Impulse.Ein Led impuls = 0,25s 
ON und 0,25s OFF. Während des Blinkens ssind die Taster verriegelt...

von Karl H. (kbuchegg)


Lesenswert?

Hermann schrieb:
> Karl heinz Buchegger schrieb:
>> Während eine Interrupt Routine läuft, sind Interrupts per Default
>> gesperrt. Da kann dir also kein andere Interrupt in die Quere kommen!
>
> Ja das stimmt, jedoch werden sie danach abgearbeitet... und das dürfen
> sie nicht...d.h. kurtz gefasst:

Es steht dir frei, am Ende einer Interrupt Service Routine, alle in der 
Zwischenzeit aufgelaufenen Interrupt-Anforderungen wieder zu löschen. 
Dies geschieht durch Löschen der entsprechenden Bits in den 
entsprechenden Registern.

> Eine kurtze betätigung von Taster1 generiert 5 rote LED-Impulse,

Wenn ein Blinker in der ISR länger dauert, als das Prellen, kann das 
nicht passieren. Bei prellenden Tastern wird genau 2 mal geblinkt. Die 
Interrupt Anforderungs Flags zählen nicht mit, wieviele externe Pulse 
während der ISR eingelaufen sind, sondern nur, dass mindestens ein Puls 
reinkam. Wird am Ende der ISR das entsprechende ISR-Anforderungsflag 
gelöscht, wird auch kein weiterer ISR Aufruf mehr gemacht.

Aber wie auch immer: Die Lösung des Problems liegt nicht darin, da jetzt 
die ISR auf Biegen und Brechen mit Flag löschen und Wartezeiten 
abzusichern, sondern die Lösung liegt in einem Timer, Pollen der 
Tastereingänge und entsprechender Entprellung. Siehe den Wiki Artikel 
über Entprellung.

von Hermann (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wird am Ende der ISR das entsprechende ISR-Anforderungsflag
> gelöscht, wird auch kein weiterer ISR Aufruf mehr gemacht.

Danke Karl Heinz Buchegger...

Ich glaube das ist der Schlüssel zu meinem Problem... wenn ich es 
schaffe diese bits zu veränden kann es doch auch sein das ich gar nichts 
mehr zu entprellen brauch...oder?

Somit kann der dater ja x mal während der ISR gedrückt werden, wenn ich 
die einträge am ende der ISR lösche intressiert das ja keinen mehr.

Nur noch eine frage... Wo finde ich dieses 
Byte,Bit(ISR-Anforderungsflag)?

MFG Hermann

von Georg (Gast)


Lesenswert?

Im DB auf S. 68.

von Karl H. (kbuchegg)


Lesenswert?

Hermann schrieb:

> Ich glaube das ist der Schlüssel zu meinem Problem... wenn ich es
> schaffe diese bits zu veränden kann es doch auch sein das ich gar nichts
> mehr zu entprellen brauch...oder?

Ich wusste es :-)
Machs richtig.
Denn dein nächstes Problem besteht darin, dass in einer ISR nicht 
gewartet werden soll. Auch dann nicht, wenn man Blinken möchte.
Wenn du das aber korrigierst, dann hast du den Fall, dass die ISR viel 
schneller abgearbeitet wird, als der Taster prellen kann.

Und damit löst dir der prellende Taster dann doch wieder den nächsten 
Interrupt aus.

Hör endlich auf, dich zu drehen und zu winden. Hak die Erfahrung als 'so 
gehts nicht vernünftig' ab und benutze endlich die entsprechende 
Entprellroutine, die du im Wiki findest. In der Zeit, in der du hier 
herumphilosophierst und dich windest, hättest du die schon 3 mal fix 
fertig eingebaut und ein funktionierendes Programm.

von Peter D. (peda)


Lesenswert?

Es schad nix, einmal gründlich durchzuspielen, wodurch Interruptflags 
gesetzt werden, wann sie automatisch gelöscht werden, wie man sie 
explizit löscht und was sie bewirken, nachdem die Interruptsource und 
globale Interrupts freigegeben sind.
Am besten macht man es im Simulator.

Und wenn das alles verstanden wurde, schmeißt man es weg und macht es 
richtig.


Peter

von Hermann (Gast)


Lesenswert?

Danke für das rasche antwochten...

Ich sehe jedoch noch immer ein kleines Problem das ich nicht weiss wie 
ich es löden kann...

ich bin vollkommen mit euch einverstanden das man in der ISR kein 
delayloop setzten soll...

Jedoch ist eine Entprell zeit auch eine Zeitkonstannte von +-5ms und die 
muss ich doch dann in die ISR setzten oder nicht??

von Peter D. (peda)


Lesenswert?

Hermann schrieb:
> Jedoch ist eine Entprell zeit auch eine Zeitkonstannte von +-5ms und die
> muss ich doch dann in die ISR setzten oder nicht??

Und genau das ist das hüpfende Komma, wo der Timerinterrupt seine 
Trumpfkarte aus dem Ärmel zieht.
Er stellt einfach die Entprellzeit wieder dem Main oder anderen 
Interrupts zur Verfügung.


Peter

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Hermann schrieb:

> Nur noch eine frage... Wo finde ich dieses
> Byte,Bit(ISR-Anforderungsflag)?

Es könnte helfen, in hermann_int0_int1.asm zu schauen.

von Hermann (Gast)


Lesenswert?

Sorry aber ich blick hier echt nicht mehr durch...

ich habe mir jetzt das simulationsprogramm von Stefan B. zur Hilfe 
geholt und 2-3 sachen hinzugefügt....

Einmal am ende jeder ISR ein "Out GICR,R25" wobei R25 = 0x00

und eine entprellzeit von 7ms in jede ISR, Das verstösst zwar gegen eure 
tipps aber ich weiss nicht wie ich das mit einem Timer machen soll...

Zudem sollte das nur ein ganz einfaches programm werden , indem nie 
etwas ins MAIN kommen sollte...

hier mein Listing:

.include"m8def.inc"

.def temp = r16

.org 0x0000
  RJMP MAIN
.org  INT0addr
  RJMP TASTER1
.org  INT1addr
  RJMP TASTER2

; ##############################
; ##############################
; ##############################

MAIN:
; AVR-Tutorial: Stack
  ldi temp, LOW(RAMEND)
  out SPL, temp
  ldi temp, HIGH(RAMEND)
  out SPH, temp

  SER temp        ; Loads $FF directly to register Rd.
  OUT DDRB,temp   ; PORTB komplett auf Ausgang (Default: LOW)
  OUT DDRC,temp   ; PORTC komplett auf Ausgang (Default: LOW)
  CLR R25
; Taster1 init
  SBI DDRD,PD2    ; PD2 Ausgang
  SBI PortD,PD2   ; PD2 Pull-Up-Widerstand aktivieren
  CBI DDRD,PD2    ; PD2 Eingang

; Taster2 init
  SBI DDRD,PD3    ; PD3 Ausgang
  SBI PortD,PD3   ; PD3 Pull-Up-Widerstand aktivieren
  CBI DDRD,PD3    ; PD3 Eingang

; AVR-Tutorial: Interrupts
  LDI temp,(1<<ISC11)|(1<<ISC01) ; Fallende Flanke INT0,INT1
  OUT MCUCR,temp         ;
  LDI temp,(1<<INT1)|(1<<INT0) ; INT0 und INT1 enable (GIMSK  == GICR)
  OUT GICR,temp
  SEI ;Global Interrupts aktivieren

; Endlosschleife
Immer:
  RJMP MAIN

; ##############################
; ##############################
; ##############################

TASTER1:
; AVR-Tutorial: Interrupts
  push temp             ; Das SREG in temp sichern. Vorher
  in   temp, SREG       ; muss natürlich temp gesichert werden
  push R17
  push R18
  push R19
  push R20
  push R21

  ldi  R17, $2F
WGLOOP000:  ldi  R18, $B6
WGLOOP001:  dec  R18
          brne WGLOOP001
          dec  R17
          brne WGLOOP000        ;7ms Entprellzeit
; -----------------------------
; delaying 2 cycles:
          nop
          nop

  SER R20
  LDI R21,7*2   ; 7 mal Blinken
Blinken1:
  OUT PORTC,R20
  RCALL WARTEN
  COM R20
  DEC R21
  BRNE Blinken1
  pop R21
  pop R20
  pop R19
  pop R18
  pop R17
  out SREG, temp        ; Die Register SREG und temp wieder
  LDI temp,(1<<INTF0)   ; Aufgelaufene INT0 löschen
  OUT GIFR,temp
  pop temp              ; herstellen
  OUT GICR,R25      ;INT0 und INT1 Disable
  RETI

; ##############################
; ##############################
; ##############################

TASTER2:
; AVR-Tutorial: Interrupts
  push temp             ; Das SREG in temp sichern. Vorher
  in   temp, SREG       ; muss natürlich temp gesichert werden
  push R17
  push R18
  push R19
  push R20
  push R21

ldi  R17, $2F
WGLOOP010:  ldi  R18, $B6
WGLOOP011:  dec  R18
          brne WGLOOP011
          dec  R17
          brne WGLOOP010      ;7ms Entprellzeit
; -----------------------------
; delaying 2 cycles:
          nop
          nop

  SER R20
  LDI R21,5*2    ; 5 mal Blinken
Blinken2:
  OUT PORTB,R20
  RCALL WARTEN
  COM R20
  DEC R21
  BRNE Blinken2
  pop R21
  pop R20
  pop R19
  pop R18
  pop R17
  out SREG, temp        ; Die Register SREG und temp wieder
  LDI temp,(1<<INTF1)   ; Aufgelaufene INT1 löschen
  OUT GIFR,temp
  pop temp              ; herstellen
  OUT GICR,R25      ;INT0 und INT1 Disable
  RETI

; ##############################
; ##############################
; ##############################

; Benutzt: R17, R18, R19
; wurden vorher gerettet
WARTEN:
  ldi R17, $16
WGLOOP0:  ldi  R18, $42
WGLOOP1:  ldi  R19, $DD
WGLOOP2:  dec  R19
          brne WGLOOP2
          dec  R18
          brne WGLOOP1
          dec  R17
          brne WGLOOP0
; -----------------------------
; delaying 2 cycles:
  nop
  nop
  RET

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Hermann schrieb:

> ich habe mir jetzt das simulationsprogramm von Stefan B. zur Hilfe
> geholt und 2-3 sachen hinzugefügt....
>
> Einmal am ende jeder ISR ein "Out GICR,R25" wobei R25 = 0x00

Warum? Du willst doch INT0 und INT1 haben. Warum schaltest du die beiden 
nach dem ersten Aufruf von INT0 oder INT1 ab? Dann funktionieren die 
Taster garnicht nicht mehr wie gewünscht.

Wenn du was tun kannst, dann das Flag löschen, welches anzeigt, dass ein 
weiterer INT0 oder INT1 ansteht. Und das ist im Code schon drin z.B. für 
INT1

  LDI temp,(1<<INTF1)   ; Aufgelaufene INT1 löschen
  OUT GIFR,temp

Es kann sein, dass ich das Datenblatt falsch gelesen habe und man vor 
dem Löschen des Flags (durch Schreiben von 1 ins Bit) zuerst prüfen 
muss, ob es gesetzt ist, um zu verhindern, dass man es irrtümlich setzt, 
wenn es gelöscht ist.

Allerdings wenn ich das falsch mache (generell 1 in Bit INTFx schreiben 
statt nur 1 schreiben, wenn Bit INTFx gleich 1), müsste die betreffende 
LED ständig blinken und davon schreibst du nichts.

Das will ich heute abend testen, wenn bis dahin niemand etwas dazu 
schreibt.

Wenn du magst, dann teste selbst die selektive Variante: Ändere diese 
Stellen (entsprechend 2x bei INT0-ISR mit exit1 und INT1-ISR mit exit2)
1
  pop R17
2
  out SREG, temp        ; Die Register SREG und temp wieder
3
  LDI temp,(1<<INTF1)   ; Aufgelaufene INT1 löschen
4
  OUT GIFR,temp
5
  pop temp              ; herstellen
6
  RETI

in
1
  IN R17, GIFR          ; Interruptflags holen
2
  SBRS R17,INTF1        ; und Bit INTF1 prüfen
3
  RJMP exit2            ; wenn Bit INTF1 nicht gesetzt (kein 2. INT1 da)
4
  LDI R17,(1<<INTF1)    ; aufgelaufene INT1 löschen (1 schreiben)
5
  OUT GIFR,R17
6
exit2:
7
  pop R17
8
  out SREG, temp        ; Die Register SREG und temp wieder
9
  pop temp              ; herstellen
10
  RETI

> 7ms Entprellzeit

Nutzen an der Stelle nichts. Der AVR merkt sich den nächsten Interrupt, 
wenn an dem Pin eine weitere fallende Flanke kommt.

Aber du kannst wie oben den Merker (INTFx Flags) löschen. Und wenn das 
Löschen nach 3,5s oder 2,5s gegen Ende der ISR gemacht wird, hat sich 
IMHO jeder Taster ausgeprellt :)

von Hermann (Gast)


Lesenswert?

Hallo Ich habe mich auch vertan... ich wollte auch Gifr schreiben...

aber für den moment verstehe ich gar nichts mehr... ich werde gleich 
noch einen bekannten um einen rat fragen...

trotzem danke für die Hilfe...

von Karl H. (kbuchegg)


Lesenswert?

Hermann schrieb:

> und eine entprellzeit von 7ms in jede ISR, Das verstösst zwar gegen eure
> tipps aber ich weiss nicht wie ich das mit einem Timer machen soll...

In der Leiste links gibt es einen Abschnitt "Artikelübersicht". Klickst 
du drauf, kommst du auf eine Seite in der es ein Suchfeld gibt (wieder 
in der linken Leiste). Dort tippst du "Entprellung" ein und das Wiki 
sucht dir den Artikel Entprellung raus. Auf der Seite findest du dann 
eine 100% full-proof Tastenentprellung, auch in Assembler. Abschnitt 
2.3.1

> Zudem sollte das nur ein ganz einfaches programm werden , indem nie
> etwas ins MAIN kommen sollte...

Genau da liegt schon der nächste Konzeptfehler.
Denn was du als "einfach" ansiehst, hat sich ja wohl mitlerweile zu 
einem Albtraum entwickelt. Unmengen an Arbeit in einer ISR zu erledigen, 
sieht zwar auf den ersten Blick wie ein "einfaches Konzept" aus, ist es 
aber nicht.

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.