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
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.
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.
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
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
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.
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
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 ;-)
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.
>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.
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
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?
Dann gibt es da noch ein, zwei Stellen, die ist nicht falsch, aber...
merkwürdig, hier mal die eine:
1
;INT2aufsteigendeFlankekonfigurieren
2
;(ISC2ändernfallend=0/steigend=1)
3
inakku,EMCUCR;Datenlesen
4
lditempL,0b11111110;Bitmaskeladen,hierfürBit#0
5
andakku,tempL;unddamitalle1auserBit0bleiben
6
lditempL,0b00000001;MaskeerstellenfürBit0setzen
7
orakku,tempL;oderdamitBit0=1
8
outEMCUCR,Akku;Datenzurü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
;INT2abschalteninGICR
2
inakku,GICR;Datenlesen
3
lditempL,0b11011111;Bitmaskeladen,hierfürBit#5
4
andakku,tempL;unddamitalle1auserBit5bleiben
5
outGICR,Akku;Datenzurückschreiben
Guck mal, was der Befehl ANDI macht. Kann man den hier irgendwie
verwenden?(Analog dann: ORI)
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?
>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
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
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 ;-)
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
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).