Forum: Mikrocontroller und Digitale Elektronik INT2 beide Flanken nutzen (ATmega8515 PE0)


von Walter (Gast)


Lesenswert?

Hallo, liebe Mitstreiter,

erst einmal bedanke ich mich ganz herzlich bei euch allen, denn ich 
durfte durch diese Seiten und das großartige AVR-Turial schon sehr viel 
lernen.

Ich schreibe in Assembler und habe ein auf den ersten Blick einfach 
anmutendes Problem. Es geht um den ATmega 8515, genauer gesagt um INT 2 
(Port E0). Ihn kann man anscheinende nur auf ansteigende oder fallende 
Flanke Programmieren.

Ich möchte/muss aber beide Flanken nutzen. Wie geht das?

Mein Lösungsansatz:

1. Int2 - Sprungadresse definieren … Klappt
2. Int2 – aktivieren … Klappt teilweise- nur mit „sei“ (aktiviert alle 
Int)
3. Flanke festlegen … am liebsten schon hier beide. Ich konnte aber für 
Int2 nur eine finden … Klappt
4. Innerhalb der Interruptschleife die Flanke umschalten (Bit 
invertieren) … hier steckt das Problem

Hier ein Auszug aus meinem Programm:

.org $00d
  rjmp Test    ; External Interrupt2 Vector Address

-----
; Interrupt definieren

         ldi akku, (1<<IVCE) ; wechsel erlauben
         out MCUCR, akku

; war vorher mcucr
         ldi akku, (1<<ISC2) ; INT2 auf steigende Flanke konfigurieren
         out EMCUCR, akku

;         ldi akku, (1<<INT2) ; INT2 aktivieren ... klappt nicht
;         out GICR, akku

         sei                   ; Interrupts allgemein aktivieren

-----
Test:
...

;*** umschalten der Flanke des Interrupt ***
;vieleicht mit Invertieren Bit (1<<ISC2) in EMCUCR Bit0 ...
;                                  --   scheint nicht zu klappen --


  in     akku, EMCUCR   ; Daten lesen
  ldi    tempL, 0x01    ; Bitmaske laden, hier Bit #0
  eor    Akku, tempL     ; Exklusiv ODER
  out    EMCUCR, Akku   ; Daten zurückschreiben

Vielleicht hat je einer von euch Erfahrung.
Viele Grüße
Walter

von chris (Gast)


Lesenswert?

wenn du hast dein DB zur hand gucke doch mal auf seite 77 da stehts 
drinne wie du es machen musst:

The Asynchronous External Interrupt 2 is activated by the external pin 
INT2 if the SREG I-bit and the corresponding interrupt mask in GICR are 
set. If ISC2 is written to zero, a falling edge on INT2 activates the 
interrupt. If ISC2 is written to one, a rising edge on INT2 activates 
the interrupt. Edges on INT2 are registered asynchronously. Pulses on 
INT2 wider than the minimum pulse width given in Table 42 will generate 
an interrupt. Shorter pulses are not guaranteed to generate an 
interrupt.

!!!!!!!!!!
When changing the ISC2 bit, an interrupt can occur. Therefore, it is 
recommended to first disable INT2 by clearing its Interrupt Enable bit 
in the GICR Register. Then, the ISC2 bit can be changed. Finally, the 
INT2 Interrupt Flag should be cleared by writing a logical one to its 
Interrupt Flag bit (INTF2) in the GIFR Register before the interrupt is 
re-enabled
!!!!!!!!!!!

INT2 abschalten in GICR
ISC2 ändern fallend=0/steigend=1

INTF2 gesetzt ? -> ja=1 dann
sbic GIFR,INTF2  ;Wenn =1 dann clear mit 1 auf 0
sbi  GIFR,INTF2
INT2 einschalten in GICR

Hoffe ein wenig verständlich.

von katastrophenheinz (Gast)


Lesenswert?

nabend,

es gibt beim 8515 ( auch bei anderen Typen ) keine Harware-Interrupts 
oder nicht maskierbare Interrupts oder wie auch immer die bei anderen 
Prozessoren auch heißen. D.h. Pegeländerungen an Input-Pins mit dem 
Namen "IntX" lösen nur dann einen Interrupt aus, wenn der entsprechende 
Interrupt überhaupt aktiviert ist ( hast du gemacht ) und wenn 
Interrupts generell zugelassen sind ( per SEI ).

INT2 ist eine schelchte Wahl für steigende und fallende Flanken, weil, 
wie du richtig erkannt hast, nur entweder steigende oder fallende 
Flanken programmiert werden können ( im Gegensatz zu Int0 oder Int1 ).

D.h. Besser Int1 oder Int0 verwenden.

Die von dir skizzierte Vorgehensweise mit Int2 ist prinzipiell unsicher, 
denn bei sehr kurzen Pulsen passiert folgendes: Du reagierst z.B. auf 
eine steigende Flanke, per Interruptroutine (ISR). Während du dich in 
der ISR befindest, sind alle Interrupts erstmal von Haus aus gesperrt. 
Du programmierst dann in der ISR auf fallende Flanke um, die ist aber 
schon längst gekommen, während du noch in der ISR am Umprogrammieren 
bist, so daß du nach dem Ende der ISR vergebens auf eine fallende Flanke 
wartest.

Dieses Problem wirst du zwar auch bei kurzen Pulsen an Int0 / Int1 
haben, jedoch kannst du dann auch erkennen, daß du eine Flanke verpennt 
hast ( nämlich wenn in der ISR zwei steigende Flanken hintereinander 
erkannt werden)

Grundsätzlich zu deiner ISR: es fehlt dort noch das Push von SREG und 
der in der ISR verwendeten Register auf den Stack, die entsprechenden 
Pop und vor allen Dingen das RETI.

von Walter (Gast)


Lesenswert?

Lieber chris,

vielen Dank für deine Antwort, ich werde es allerdings erst morgen 
angehen können … bin schon zu müde ;-)

Das hatte ich völlig überlesen: Int. ausschalten, ändern, einschalten – 
oh man!


Auch dir katastrophenheinz möchte ich danken aber deine bedenken waren 
mir klar…
Es fehlen ja ganz viele Dinge in meinem Auszug - es währe einfach zu 
verwirrend gewesen alles mit aufzuführen.

Gerne berichte ich wie es klappt
Bis bald
Walter

von Walter (Gast)


Lesenswert?

So, nun habe ich es geschafft! Dank eurer Hilfe!!! Vielen lieben Dank 
dafür.

Ja es ist genau das Problem welches Chris aufgezeigt hat.

Weil ich selbst lange mit INT2 zu kämpfen hatte und auch über die von 
"katastrophenheinz" beschriebenen Probleme gestolpert bin, habe ich mir 
die Mühe gemacht einen lauffähigen Test zu schreiben.

Auch mit Push/Pop und Fifo ...

Ich würde mich freuen, wenn dies an anderer Stelle auch weiterhilft.

Ganz liebe Grüße und viel Erfolg weiterhin.


Hier der ASM Code:

------------------

/**** A P P L I C A T I O N   N O T E 
************************************
*
* Title           : IRQ2 Testprogramm beide Flanken
* Last updated    : 2.1.2013
* Target          : controler Rev.1.0 [ATmega8515]
*
* written by Walter
************************************************************************ 
/

.include "m8515def.inc"


;Register and Pin definitions

.DEF      akku    = r16        ; für Daten 8Bit
.DEF      tempL   = r17        ; für Beispiel
.DEF      Save    = r18        ; für Stack


;********************** set interrupt-routines *************************

.ORG $000        ; Die Adresse auf Null
  rjmp init      ; reset vector address
  reti           ; External Interrupt0 Vector Address
  reti           ; External Interrupt1 Vector Address
  reti           ; Input Capture1 Interrupt Vector Address
  reti           ; Output Compare1A Interrupt Vector Address
  reti           ; Output Compare1B Interrupt Vector Address
  reti           ; Overflow1 Interrupt Vector Address
  reti           ; Overflow0 Interrupt Vector Address
  reti           ; SPI Interrupt Vector Address
  reti           ; UART Receive Complete Interrupt Vector Address
  reti           ; UART Data Register Empty Interrupt Vector Address
  reti           ; UART Transmit Complete Interrupt Vector Address
  reti           ; Analog Comparator Interrupt Vector Address
.org $00d
  rjmp I2Test    ; External Interrupt2 Vector Address
  reti           ; Output Compare0 Interrupt Vector Address
  reti           ; EEPROM Interrupt Vector Address
  reti           ; SPM complete Interrupt Vector Address
  reti           ; SPM complete Interrupt Vector Address

;********************** intitalisirung ********************************
init:

; set stackpointer

         ldi   akku,  low(RAMEND)
         out   SPL,akku
         ldi   akku, high(RAMEND)
         out   SPH,akku

; Interrupt definieren

         ldi   akku, (1<<IVCE) ; wechsel erlauben
         out   MCUCR, akku


         ldi   akku, (1<<ISC2) ; INT2 auf steigende Flanke konfigurieren
; (ISC2 ändern fallend=0/steigend=1)
         out   EMCUCR, akku


         ldi   akku, (1<<INT2) ; INT2 aktivieren
         out   GICR, akku

         sei                   ; Interrupts allgemein aktivieren

;********************** Programmatart **********************************

main:                          ; Hier nur Testschleife

  rjmp main

;********************** Interrupt 2 ************************

I2Test:


;sichern der Register und Flags
        push   akku
        push   tempL
        in     Save, SREG        ; Register muss natürlich gesichert 
werden


;*** umschalten der Flanke des Interruptes ***
; INT2 abschalten in GICR
        ldi    akku, (0<<INT2) ; INT2 deaktivieren
        out    GICR, akku


; vieleicht mit Invertieren Bit (1<<ISC2) in EMCUCR Bit0
; ISC2 ändern fallend=0/steigend=1
;sbic GIFR,INTF2  ;Wenn =1 dann clear mit 1 auf 0
;sbi  GIFR,INTF2
; oder:

        in     akku, EMCUCR   ; Daten lesen
        ldi    tempL, 0x01    ; Bitmaske laden, hier Bit #0
        eor    Akku, tempL    ; Exklusiv ODER
        out    EMCUCR, Akku   ; Daten zurückschreiben


; INT2 einschalten in GICR
        ldi    akku, (1<<INT2) ; INT2 aktivieren
        out    GICR, akku

; rückschreiben
        out    SREG, Save        ; Die Register SREG und temp wieder
        pop    tempL             ; herstellen
        pop    akku
    reti

;****************************** Programmende **************************

.exit

von Karl H. (kbuchegg)


Lesenswert?

Hier
1
; INT2 abschalten in GICR
2
        ldi    akku, (0<<INT2) ; INT2 deaktivieren
3
        out    GICR, akku

setzt du GICR komplett auf 0

0 mit irgendwas anderem nach links (oder rechts schieben) ist keine 
sinnvolle Operation. Denn das Ergebnis ist immer wieder einfach nur 0. 
Hier hast du also trickreich geschrieben
1
; INT2 abschalten in GICR
2
        ldi    akku, 0
3
        out    GICR, akku
und das dürfte nicht das sein, was du willst.

genauso hier
1
; INT2 einschalten in GICR
2
        ldi    akku, (1<<INT2) ; INT2 aktivieren
3
        out    GICR, akku
Das schaltet zwar tatsächlich den INT2 ein. Aber im selben Atemzug 
schaltet es alle anderen Interrupts, die in GICR beheimatet sind ab.


Gewöhn dir die Vorgehensweise an:
Bei Funktionen ein/aus schaltetn, IMMER nur das eine relevante Bit 
verändern (ausser beim Start des Programmes, aber der Fall liegt hier 
nicht vor). Dazu brauchst du UND bzw. ODER Operationen, wenn dein µC 
keine speziellen Bitinstruktionen besitzt, um gezielt Einzelbits zu 
beeinflussen. In deinem speziellen Fall des Testprogrammes spielt es 
keine Rolle, weil der INT2 der einzige Interrupt ist, der vorkommt. Aber 
generell ist es IMMER eine gute Idee, ein bischen vorausschauend zu 
arbeiten. Denn Programme werden im Lauf der Zeit komplexer und je mehr 
Features dazu kommen, umso schwieriger wird es, bereits 'verbaute' 
Seiteneffekte zu finden und bei Bedarf zu beheben. Arbeitest du von 
Anfang an möglichst seiteneffektfrei, dann hast du später weniger 
Arbeit.


Und ich vermisse das Löschen des INTF2 Bits in GIFR.

von Walter (Gast)


Lesenswert?

Lieber Karl Heinz,

gut dass ich nochmal vorbeigeschaut habe. Vielen Dank für deinen 
wichtigen Hinweis!

Ich habe diese Verschiebegeschichte wohl immernoch nicht verstanden.

Ich dachte bei (1<<INT2) wird eine 1 auf die Bitposition INT2 geschoben 
-
mmm aber der akku hat ja den aktuellen Bittwert noch nicht...verstehe... 
.

Verstehe ich es richtig, das ich erst das Register laden muss und dann 
die Bitmanipulation (1<<INT2)durchführen darf?


Du schreibst dir fehlt das Löschen INTF2, in der GA steht, dass es 
automatisch gelöscht wird:

• Bit 5 – INTF2: External Interrupt Flag 2
When an event on the INT2 pin triggers an interrupt request, INTF2 
becomes set (one).
If the I-bit in SREG and the INT2 bit in GICR are set (one), the MCU 
will jump to the corresponding
Interrupt Vector. The flag is cleared when the interrupt routine is 
executed.

Habe ich das falsch verstanden?

Dank für deine Unterstützung.
Walter

von Walter (Gast)


Lesenswert?

Ich hoffe jetzt habe ich verstanden:

Beispiel aus

http://www.mikrocontroller.net/articles/AVR-Tutorial:_ADC
***
; ADC initialisieren: ADC0, Vcc als Referenz, Single Conversion, 
Vorteiler 128

    ldi     temp1, (1<<REFS0)                   ; Kanal 0, interne 
Referenzspannung 5V
    out     ADMUX, temp1
    ldi     temp1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
    out     ADCSRA, temp1
***
Hier war templ vorher belegt und wird nun gelöscht und mit den 1 Bits an 
den Betreffenden Stellen geladen.

und einzelne Bits werden so eingeschaltet:

    sbi     UCSRB, TXEN                         ; TX einschalten

Um zu Int2 zu kommen:
sbi GICR,INT2


Ich hoffe deine Anmerkung trägt Früchte ;-)

von Walter (Gast)


Lesenswert?

Schade, ich konnte mit keiner der zuletzt genannten Ideen das Programm 
zum laufen Bringen.

Es funktioniert bei mir nur so wie Version: Datum: 02.01.2013 20:50
da aber ausgezeichnett.

von katastrophenheinz (Gast)


Lesenswert?

>Und ich vermisse das Löschen des INTF2 Bits in GIFR.
bezieht sich auf diesen Passus:
>!!!!!!!!!!
>When changing the ISC2 bit, an interrupt can occur. Therefore, it is
>recommended to first disable INT2 by clearing its Interrupt Enable bit
>in the GICR Register. Then, the ISC2 bit can be changed. Finally, the
>INT2 Interrupt Flag should be cleared by writing a logical one to its
>Interrupt Flag bit (INTF2) in the GIFR Register before the interrupt is
>re-enabled
>!!!!!!!!!!!
das fehlt in deiner Interrupt-Routine, nachdem du ISC2 geändert hast.

Überflüssig (aber nicht fehlerverursachend) in deiner Interrupt-Routine 
sind das Sperren und Wiederuzlassen von Int2: Da während der Abarbeitung 
eines Interrupts (bis zum RETI) sowieso alle Interrupts gesperrt sind, 
brauchst dich um Int2 innerhalb der Interrupt-Routine nicht speziell zu 
kümmern.

Poste doch noch mal deinen kompletten Code, sonst bleibt das 
Rätselraten.

von Walter (Gast)


Lesenswert?

Hallo katastrophenheinz, hallo Ihr,

Danke für´s dranbleiben. Ich habe hier eine Menge mitnehmen können und 
letztlich die Sache wie ich glaube richtig hinbekommen – und das habe 
ich euch zu verdanken.

Hier nun das Laufende und saubere Programm:





/**** A P P L I C A T I O N   N O T E   *******************************
*
* Title         : IRQ2 Testprogramm beide Flanken
* Last updated  : 5.1.2013
* Target        : controler Rev.1.1 [ATmega8515]
*
* written by Walter
***********************************************************************/

.include "m8515def.inc"


;Register and Pin definitions
.DEF      Icheck  = r19        ; Kontrolle für Abfrage M4
.DEF      akku    = r16        ; für Daten 8Bit
.DEF      tempL   = r17        ; für Beispiel
.DEF      Save    = r18        ; für Stack


;********************** set interrupt-routines ************************

.ORG $000       ; Die Adresse auf Null
  rjmp init     ; reset vector address
  reti          ; External Interrupt0 Vector Address
  reti          ; External Interrupt1 Vector Address
  reti          ; Input Capture1 Interrupt Vector Address
  reti          ; Output Compare1A Interrupt Vector Address
  reti          ; Output Compare1B Interrupt Vector Address
  reti          ; Overflow1 Interrupt Vector Address
  reti          ; Overflow0 Interrupt Vector Address
  reti          ; SPI Interrupt Vector Address
  reti          ; UART Receive Complete Interrupt Vector Address
  reti          ; UART Data Register Empty Interrupt Vector Address
  reti          ; UART Transmit Complete Interrupt Vector Address
  reti          ; Analog Comparator Interrupt Vector Address
.org $00d
  rjmp I2lesen      ; External Interrupt2 Vector Address
  reti          ; Output Compare0 Interrupt Vector Address
  reti          ; EEPROM Interrupt Vector Address
  reti          ; SPM complete Interrupt Vector Address
  reti          ; SPM complete Interrupt Vector Address

;********************** intitalisirung ********************************
init:

; set stackpointer

        ldi    akku,  low(RAMEND)
        out    SPL,akku
        ldi    akku, high(RAMEND)

        out    SPH,akku

; Interrupt definieren

; INT2 abschalten in GICR
        in     akku, GICR         ; Daten lesen
        ldi    tempL, 0b11011111  ; Bitmaske laden, hier für Bit #5
        and    akku, tempL        ; und damit alle 1 auser Bit 5 bleiben
        out    GICR, Akku         ; Daten zurückschreiben


; INT2 auf steigende Flanke konfigurieren
; (ISC2 ändern fallend=0/steigend=1)
        in     akku, EMCUCR       ; Daten lesen
        ldi    tempL, 0b11111110  ; Bitmaske laden, hier für Bit #0
        and    akku, tempL        ; und damit alle 1 auser Bit 0 bleiben
        ldi    tempL, 0b00000001  ; Maske erstellen für Bit0 setzen
    or     akku, tempL            ; oder damit Bit 0 =1
        out    EMCUCR, Akku       ; Daten zurückschreiben


; Löschen des Interupt Flags durch setzen auf 1 (Vermeidung von
                                  ; Zufalsfehlern)
        in     akku, GIFR         ; Daten lesen
        ldi    tempL, 0b11011111  ; Bitmaske laden, hier für Bit #0
        and    akku, tempL        ; und damit alle 1 auser Bit 0 bleiben
        ldi    tempL, 0b00100000  ; Maske erstellen für Bit0 setzen
        or     akku, tempL        ; oder damit Bit 0 =1
        out    GIFR, Akku         ; Daten zurückschreiben


; INT2 einschalten in GICR
        in     akku, GICR         ; Daten lesen
        ldi    tempL, 0b11011111  ; Bitmaske laden, hier für Bit #5
        and    akku, tempL        ; und damit alle 1 auser Bit 5 bleiben
        ldi    tempL, 0b00100000  ; Bit 5 (Stelle 6) setzen Maske 
erstellen
        or     akku, tempL        ; oder damit Bit 5 =1
        out    GICR, Akku         ; Daten zurückschreiben


        sei                       ; Interrupts allgemein aktivieren


;********************** Programmatart **********************************

main:

        nop              ;Hier könnte das Hauptprogramm stehen

        rjmp   main


;********************** Interrupt 2 Werte holen ************************

I2lesen:

;sichern der Register und Flags
        push  akku
        push  tempL
        in    Save, SREG      ; muss natürlich vorübergehend mit
                              ; gesichert werden

; Mit Invertieren Bit (1<<ISC2) in EMCUCR Bit0
; ISC2 ändern fallend=0/steigend=1
        in     akku, EMCUCR   ; Daten lesen
        ldi    tempL, 0x01    ; Bitmaske laden, hier Bit #0
        eor    Akku, tempL    ; Exklusiv ODER
        out    EMCUCR, Akku   ; Daten zurückschreiben

; Hier kann Interrupt Programm hin
;    ****

; rückschreiben
        out   SREG, Save       ; Die Register SREG und temp wieder
        pop   tempL            ; herstellen
        pop    akku

reti

;****************************** Programmende ***************************

.exit

von katastrophenheinz (Gast)


Lesenswert?

Hallo,

schön, daß dein Programm funktioniert.
Ein Fehler ( zugegebenermassen ein ganz mieser und einer, der erst dann 
auftritt, wenn du Int0 und/oder Int1 zusätzlich verwenden solltest ) ist 
noch drin: Interrupt-Flag bits werden gelöscht, indem man 1 
reinschreibt.
Preisfrage: An welche Bitpositionen schreibt deine Sequenz denn 1en?
Und welche Flag-Bits werden dadurch in GIFR gelöscht?
1
; Löschen des Interupt Flags durch setzen auf 1 (Vermeidung von
2
                                  ; Zufalsfehlern)
3
        in     akku, GIFR         ; Daten lesen
4
        ldi    tempL, 0b11011111  ; Bitmaske laden, hier für Bit #0
5
        and    akku, tempL        ; und damit alle 1 auser Bit 0 bleiben
6
        ldi    tempL, 0b00100000  ; Maske erstellen für Bit0 setzen
7
        or     akku, tempL        ; oder damit Bit 0 =1
8
        out    GIFR, Akku         ; Daten zurückschreiben

Dann gibt es da noch ein, zwei Stellen, die ist nicht falsch, aber... 
merkwürdig, hier mal die eine:
1
; INT2 auf steigende Flanke konfigurieren
2
; (ISC2 ändern fallend=0/steigend=1)
3
        in     akku, EMCUCR       ; Daten lesen
4
        ldi    tempL, 0b11111110  ; Bitmaske laden, hier für Bit #0
5
        and    akku, tempL        ; und damit alle 1 auser Bit 0 bleiben
6
        ldi    tempL, 0b00000001  ; Maske erstellen für Bit0 setzen
7
        or     akku, tempL            ; oder damit Bit 0 =1
8
        out    EMCUCR, Akku       ; Daten zurückschreiben
Letzendlich willst du doch nur Bit 0 in EMCUCR auf 1 setzen. Brauchst du 
dafür die AND-Operation wirklich?

Und schließlich  das hier, auch nicht falsch, aber geht auch kürzer:
1
; INT2 abschalten in GICR
2
        in     akku, GICR         ; Daten lesen
3
        ldi    tempL, 0b11011111  ; Bitmaske laden, hier für Bit #5
4
        and    akku, tempL        ; und damit alle 1 auser Bit 5 bleiben
5
        out    GICR, Akku         ; Daten zurückschreiben
Guck mal, was der Befehl ANDI macht. Kann man den hier irgendwie 
verwenden?(Analog dann: ORI)

von Walter (Gast)


Lesenswert?

Hallo,

Ich konnte mit den Bitmanipulationsbefehlen die Geschichte nicht zum 
laufen bringen (s.o.).

Eigentlich dachte ich, dass ich den akku lade und dann dort nur das eine 
Bit manipuliere aber das klappte nicht.

So kommt es, dass ich alles einzeln gemacht habe.

Schön währe gewesen:

        in     akku, GIFR         ; Daten lesen
        ldi    temp1, (1<<INTF2)  ; Setzen Bit
        out    GIFR, akku

oder

        sbi    GIFR, INTF2        ; Setzen Bit

Aber wie ich schon berichtet habe klappt das zwar beim AD-Wandler im 
ATmega16, nicht aber hier.

Zu deiner Preisfrage … ich manipuliere nur Bit 5 die Anderen bleiben 
unverändert. Werden alle Bits im Interrupt fehlgeleitet?
Meinst Du damit ich muss alle IRQ mit eins löschen?

von katastrophenheinz (Gast)


Lesenswert?

>Schön wäre gewesen:
>
>       in     akku, GIFR         ; Daten lesen
>       ldi    temp1, (1<<INTF2)  ; Setzen Bit
>        out    GIFR, akku

Was du vermutlich machen wolltest wäre gewesen:

        ; Intf2-Flag rücksetzen durch schreiben von 1
        in     akku, GIFR         ; GIFR lesen
        ori    akku, (1<<INTF2)   ; Setzen Bit INTF2
        out    GIFR, akku         ; GIFR zurückschreiben

Richtig wäre gewesen:

        ; Intf2-Flag rücksetzen durch schreiben von 1
        ldi    akku, (1<<INTF2)   ; Setze nur INTF2-Bit
        out    GIFR, akku         ; Schreiben von 1 löscht das INTF2-Bit

Denn du willst ja nur das INTF2-bit zurücksetzen. Mit deinem Konstrukt 
werden aber alle Flagbits, die in GIFR den Wert 1 haben, auf 0 
gesetzt. Da du Int0 und Int1 nicht nutzt, ist das hier unerheblich, kann 
sich aber später (wenn du Int0 oder Int1 mal nutzt) als schwer zu 
findender Fehler rausstellen.

>; INT2 auf steigende Flanke konfigurieren
>; (ISC2 ändern fallend=0/steigend=1)
>        in     akku, EMCUCR       ; Daten lesen
>        ldi    tempL, 0b11111110  ; Bitmaske laden, hier für Bit #0
>        and    akku, tempL        ; und damit alle 1 auser Bit 0 bleiben
>        ldi    tempL, 0b00000001  ; Maske erstellen für Bit0 setzen
>        or     akku, tempL            ; oder damit Bit 0 =1
>        out    EMCUCR, Akku       ; Daten zurückschreiben

Könnte man auch kürzer machen:
        in     akku, EMCUCR        ; EMCUCR lesen
        ori    akku, 0b00000001    ; ISC2 auf 1
        out    EMCUCR, akku        ; zurückschreiben

>        sbi    GIFR, INTF2        ; Setzen Bit
>Aber wie ich schon berichtet habe klappt das zwar beim AD-Wandler im
>ATmega16, nicht aber hier.

Das liegt daran, das SBI/CBI nur auf den IO-Registerbereich 0-31 
zugreifen können. Bei den ADC-Registern des mega16 ist das der Fall, 
beim EMCUCR des 8515 aber nicht. Daher funktioniert SBI/CBI dort nicht 
und es bleibt dir nur der Umweg über Register einlesen -> CBR/ORI -> 
Register schreiben

von Walter (Gast)


Lesenswert?

Lieber Heinz in deiner E-Mail vom Datum: 02.01.2013 21:27 schriebst Du

genauso hier
; INT2 einschalten in GICR
        ldi    akku, (1<<INT2) ; INT2 aktivieren
        out    GICR, akku
Das schaltet zwar tatsächlich den INT2 ein. Aber im selben Atemzug
schaltet es alle anderen Interrupts, die in GICR beheimatet sind ab.

Daraufhin bearbeitete ich nur das Bit 5:

; Löschen des Interupt Flags durch setzen auf 1 (Vermeidung von
                                  ; Zufalsfehlern)
        in     akku, GIFR         ; Daten lesen
        ldi    tempL, 0b11011111  ; Bitmaske laden, hier für Bit #5
        and    akku, tempL        ; damit alle 1 auser Bit 5 bleiben 
können
        ldi    tempL, 0b00100000  ; Maske erstellen für Bit0 setzen
        or     akku, tempL        ; oder damit Bit 0 =1
        out    GIFR, Akku         ; Daten zurückschreiben

Und jetzt schreibst Du:

Richtig wäre gewesen:

        ; Intf2-Flag rücksetzen durch schreiben von 1
        ldi    akku, (1<<INTF2)   ; Setze nur INTF2-Bit
        out    GIFR, akku         ; Schreiben von 1 löscht das INTF2-Bit

Warum hast du geschrieben, das sich so alle Bits löschen? Und jetzt ist 
es ok?

Schreibfehler? Meintest Du:

        in     akku, GIFR         ; Daten lesen
        ldi    akku, (1<<INTF2)   ; Setze nur INTF2-Bit
        out    GIFR, akku         ; Schreiben von 1 löscht das INTF2-Bit

In meiner Version werden Die Bits und verknüpft – bits die vorher 1 
waren bleiben 1

0  0  = 0
0  1  = 0
1  0  = 0
1  1  = 1

Wenn die Maske also auf 1 steht bleiben die Bits unverändert – warum ist 
das falsch?

Anschließend setze ich definitiv das eine Bit mit OR.

Jetzt schreibst Du ich soll nur das eine setzen?

Verstehe mich bitte nicht falsch, du hast mir wirklich sehr geholfen – 
aber jetzt bin ich verwirrt.

L.G.
Walter

von katastrophenheinz (Gast)


Lesenswert?

Hallo Walter,

das ist genau der springende Punkt: Der Unterschied zwischen den 
Enable-Bits in GICR und den Flag-Bits in GIFR. Und die Verfahren, wie 
man die setzt oder rücksetzt, ohne die anderen Bits zu verändern.

Los gehts mit *GICR*: Hier stehen im 8515 in den Bits 7-5 die 
IntX-Enable-Flags. Mal angenommen, du willst Int2 auf Enable setzen, 
ohne den restlichen Kram, der in GICR steht, zu verändern, dann geht das 
so:

        ; Int2 zulassen in GICR
        in     akku, GICR         ; GICR lesen
        ori    akku, (1<<INT2)    ; Setzen Bit INTF2
        out    GICR, akku         ; GICR zurückschreiben

Das ist einfach. Aber jetzt kommt GIFR. In diesem Register stehen in 
den Bits 7-5 die Flag-Bits für IntX, d.h. die Bits, die anzeigen, ob ein 
INTx-Interrupt zur Abarbeitung ansteht. Für diese Bits hat Atmel die 
Bearbeitung ziemlich krude gestaltet, nämlich das Manual sagt so:
1
Bit 5 – INTF2: External Interrupt Flag 2
2
... The flag is cleared when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it...
D.h. du löscht das Flag, indem du eine 1 an die entsprechede Bitposition 
schreibst. Und dazu ist es völlig unerheblich, was in dem Register sonst 
noch so drinsteht. Einfach eine 1 an die entsprechende Bitposition, 
also:

        ; Intf2-Flag rücksetzen durch schreiben von 1
        ldi    akku, (1<<INTF2)   ; Setze nur INTF2-Bit
        out    GIFR, akku         ; Schreiben von 1 löscht das INTF2-Bit

Was passiert, wenn man das Zurücksetzen der Flagbits durch Veroderung 
des urprünglichen Inhaltes versucht, kannst du ja mal an folgendem 
fehlerhaften Code durchprobieren, mit einem gedachten Wert von 
GIFR=0b11100000

        ; INTF2 in GIFR zurücksetzen
        ; fehlerhafte Implementierung
        in     akku, GIFR        ; GIFR lesen        [akku=0b11100000]
        ori    akku, (1<<INTF2)  ; Setzen Bit INTF2  [akku=0b11100000]
        out    GIFR, akku        ; INTF2 löschen     [akku=0b11100000]

Du siehst, mit der letzten Anweisung werden drei 1en nach GIFR 
geschrieben, d.h. alle drei Flag-Bits gelöscht. Das war so nicht 
gewollt.

D.h. Zusammengefasst: Du musst die Bitmanipulationen in drei Fälle 
unterscheiden:
a) Setzen von einzelnen Bits in Registern
b) Rücksetzen von einzelnen Bits in Registern
c) Rücksetzen von Flagbits in Registern

Der Code dazu sieht immer ähnlich aus, [NAME] und [REGISTER] sind durch 
entsprechende Bitnamen bzw. Registername zu ersetzen, akku ist ein 
Hilfsregister aus {R16,...,R31}

a) Setzen
       ; Setzen von Bit [NAME] in [REGISTER]
       ; alle anderen Bits bleiben unverändert
        in     akku, [REGISTER]   ; [REGISTER] lesen
        ori    akku, (1<<[NAME])  ; Setzen Bit [NAME]
        out    [REGISTER], akku   ; [REGISTER] zurückschreiben

b) Rücksetzen
       ; Rücksetzen von Bit [NAME] in [REGISTER]
       ; alle anderen Bits bleiben unverändert
        in     akku, [REGISTER]   ; [REGISTER] lesen
        andi   akku, ~(1<<[NAME]) ; Rücksetzen Bit [NAME]
        out    [REGISTER], akku   ; [REGISTER] zurückschreiben

c) Rücksetzen von Interrupt-Flagbits
       ; Rücksetzen von Flagbit [NAME] in [REGISTER]
       ; erfolgt durch Schreiben von 1 an die entsprechende Bitposition
        ldi    akku, (1<<[NAME])  ; lade Bitmuster
        out    [REGISTER], akku   ; lösche bit [NAME]

d) Sonderfälle : Wenn [Register] im Adressbereich 0x00 - 0x1f liegt, 
dann können die Setz- und Rücksetzoperationen a) und b) mit einem 
einzigen Maschinenbefehl ausgeführt werden (die längere Sequenz von oben 
ist aber genauso möglich)

   a1) Setzen eines Bits in einem Register, Adresse < 0x20
         SBI [REGISTER], [NAME]
   b1) Rücksetzen eines Bits in einem Register, Adresse < 0x20
         CBI [REGISTER], [NAME]

Ich hoffe, das hilft dir weiter.
Gruss, Katastrophenheinz
PS: Katastrophenheinz != Karl Heinz Buchegger, vielleicht sollte ich mal 
ein anderes Pseudonym verwenden ;-)

von Walter (Gast)


Lesenswert?

Lieber Heinz,

nein, bleib wie du bist - ist genau richtig!!!

Punkt C) war mir so gar nicht klar. Also setze ich alle Bits mit 0 und 
lösche (mit 1) nur den einen Interrupt.

Vielen Dank für Deine wirklich große Mühe!

Ganz liebe Grüße
Walter

von Ralf G. (ralg)


Lesenswert?

Walter schrieb:
> Punkt C) war mir so gar nicht klar. Also setze ich alle Bits mit 0 und
> lösche (mit 1) nur den einen Interrupt.

Ich glaube, setzen kann man ein Interrupt-Bit gar nicht. Das mach nur 
die Hardware per Hintereingang. Nach meinem Verständnis:
Löschen (sozusagen per Definition) mit einer 1 - bei 0 passiert nichts 
(sonst würden ja nicht eingeschaltete Interrupts zugeschaltet).

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.