Sehr geehrtes Forum,
ich habe folgendes Problem :
Ich bin gerade am Lernen von Interrupts, habe mir da ein Programm
geschrieben, und was passiert jetzt? Mein AVR Studio sagt mir beim
Debuggen, dass das Programm zu groß wäre, komischer Weise (Siehe Bild) :
4KB Flash Speicher, gerade ca. 360 Bytes benutzt, warum steht dann da
over?
Mein Code im Folgenden:
Ach und nebenbei :
Ich benutze den Atmega48pa, und will am PC0 einen Interrupt auslösen, so
richtig oder nicht?
Danke für eure Hilfe!
m.f.G. Developer_X
K. R. schrieb:> Ich bin gerade am Lernen von Interrupts, habe mir da ein Programm> geschrieben, und was passiert jetzt? Mein AVR Studio sagt mir beim> Debuggen, dass das Programm zu groß wäre, komischer Weise (Siehe Bild) :> 4KB Flash Speicher, gerade ca. 360 Bytes benutzt, warum steht dann da> over?
1
.ORG 0xC08
Weil du Teile deines Programms außerhalb des existierenden Flash
platziert hast.
Ich dachte das würde meinem Mikrocotnroller sagen, dass er ein Interrupt
von Port CO bekommen soll, PCINT8.
Wie kann ich denn einen Interrupt von PCINT8 setzen, das heißt ihm
sagen, wenn Port C0 an, durch button, also PCInt8 an, dann soll die
Subroutine "interrupt" aufgerufen werden.
Wie kann ich das machen?
Danke,
m.f.G. Developer_X
Entweder an den Anfang die ganze Interrupttabelle hinschreiben oder die
richtige Adresse aus dem Datenblatt suchen. Beim ATMega8 wäre das zum
Beispiel für externe Interrupts die Adressen 1 und 2.
Der Vorteil der Tabelle ist, dass aus Versehen aktivierte Interrupts das
Programm nicht beeinflussen.
1. Der Mega48/88/168/328 hat nur Interruptvektoren für die drei PCINT
Gruppen , nicht für jeden Pin einen eigenen. Pin PCINT8 löst also
Interrupt PCINT1 aus.
2. Deinem Programm fehlt die Vektortabelle ab ORG 0x00. Siehe das
Datenblatt unter 'Interrupt Vectors in ATMega48P':
Da sind alle Vektoren drin, die Interrupts auslösen. Entweder machst du
per
1
.ORG0x004
2
rjmpmeinPCINT1
den einzelnen Vektor oder du baust die komplette Tabelle nach und lässt
unbenutzte IRQs auf ein RETI laufen. Der Reset Vektor bei ORG 0x000 ist
aber zwingend notwendig, sonst startet dein Programm irgendwo.
K. R. schrieb:> Ich dachte das würde meinem Mikrocotnroller sagen, dass er ein Interrupt> von Port CO bekommen soll, PCINT8.
Oh Gott...
Wie wäre es, wenn du erstmal die verschissene Dokumentation lesen
würdest, bevor du dich an's Programmieren machst?
> Wie kann ich denn einen Interrupt von PCINT8 setzen, das heißt ihm> sagen, wenn Port C0 an, durch button, also PCInt8 an, dann soll die> Subroutine "interrupt" aufgerufen werden.
Eigentlich garnicht.
Bestenfalls kannst du dafür sorgen, daß
1) überhaupt ein Interrupt ausgelöst wird, wenn es an diesem Portpin
wackelt.
Dazu mußt du bei PCINT-Interrupts typischerweise das zuständige
Maskenregister für den Portpin setzen und zusätzlich das
Interruptmaskenregister für den zuständigen PCINT-Interrupt. Welche
Register das jeweils genau sind, ist deviceabhängig und steht in dem
verschissenen Datenblatt.
2) Der Interruptvektor der benutzten Vektortabelle den Code deiner ISR
durchläuft.
Dazu mußt du erstmal festlegen, welche Vektortabelle benutzt wird (oder
dich mit der standardmäßig aktiven ab Adresse 0 abfinden), dann dort im
richtigen Vektor entweder einen Sprungbefehl zum Code deiner ISR
platzieren oder direkt deine ISR (was allerdings nur geht, solange du
die dadurch ggf. überschriebenen Vektoren nicht noch für andere Zwecke
benötigst.
Welche Vektortabellen möglich sind, wie man sie auswählt, welche
Vektoren existieren und wo genau sie in der Tabelle liegen, als das ist
wieder deviceabhängig und steht deshalb im verschissenen Datenblatt,
welches du verdammt nochmal einfach mal lesen solltest!
An Alle anderen außer den Vorredner :
Ich habe mir ja das Datenblatt vom Atmega48PA durchgelesen, da steht
aber nichts davon, dass INT1 für PICINT8 bis 14 verantwortlich wäre, das
ist nur bei den Flagveränderungen so.
Des Weiteren steht da ja, dass man einstellen kann, im Register :
EIMSK welche von den Interrupts (0 oder 1) abgefragt werden, aber das
Register existiert in meinem Assembler Editor nicht, und wenn ich im
Datenblatt die Registeraufteilung finde, und die entsprechende Adresse
im Code anstatt EIMSK hinterlege, meint er es wäre ein invalides
Register.
Was mich auch noch wundert ist, dass .ORG und Reset nur am Anfang des
Codes geschrieben werden können, mein Code sieht wie folgt aus :
Was genau muss ich jetzt machen, ich check es nicht, auch nach dem
Tutorial im Forum, das mir bereitgestellt wurde nicht, wenn ich das
ganze einfach am INT1 oder INT0 Pin mache, gehts ja einfach, wie kann
ich aber gezielt den Interrupt auf nur einen PIN, nämlich PC0 setzen?
In meinem Buch wirds nicht erklärt!
Hi
>Was mich auch noch wundert ist, dass .ORG und Reset nur am Anfang des>Codes geschrieben werden können, mein Code sieht wie folgt aus :
Stimmt nicht. Kann man überall benutzen, wenn man weiß was man macht.
>SEI>.ORG 0x004> rjmp interrupt>RESET:
Damit plazierst du das 'RESET' auf die Adresse 0x0005 und das ist
falsch.
MfG Spess
Datenblatt Abschnitt: 12. External Interrupts
"
The
pin change interrupt PCI2 will trigger if any enabled PCINT23...16 pin
toggles. The pin change
interrupt PCI1 will trigger if any enabled PCINT14...8 pin toggles. The
pin change interrupt PCI0
will trigger if any enabled PCINT7...0 pin toggles. The PCMSK2, PCMSK1
and PCMSK0 Registers
control which pins contribute to the pin change interrupts.
"
Erstma danke, also ist INT1 für PCINT8 zuständig^^.
In dem Tutorial heißt es, dass man dann ganz einfach einem Interrupt
eine ISR zuweisen kann indem man folgendes macht :
1
.include "m8def.inc"
2
3
.def temp = r16
4
5
.org 0x000
6
rjmp main ; Reset Handler
7
.org INT0addr
8
rjmp int0_handler ; IRQ0 Handler
9
.org INT1addr
10
rjmp int1_handler ; IRQ1 Handler
11
12
13
main: ; hier beginnt das Hauptprogramm
14
15
ldi temp, LOW(RAMEND)
16
out SPL, temp
17
ldi temp, HIGH(RAMEND)
18
out SPH, temp
19
20
ldi temp, 0x00
21
out DDRD, temp
22
23
ldi temp, 0xFF
24
out DDRB, temp
25
26
ldi temp, (1<<ISC01) | (1<<ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren
27
out MCUCR, temp
28
29
ldi temp, (1<<INT0) | (1<<INT1) ; INT0 und INT1 aktivieren
30
out GICR, temp
31
32
sei ; Interrupts allgemein aktivieren
33
34
loop: rjmp loop ; eine leere Endlosschleife
35
36
int0_handler:
37
sbi PORTB, 0
38
reti
39
40
int1_handler:
41
cbi PORTB, 0
42
reti
Wenn ich das mache, kommen fehlermeldungen auf, weil mein Code dann
irgendwie verschoben ist, komisch, im tutorial klappt es angeblich :
.include "M48PAdef.inc"
Muss am Anfang stehen,vor INT0addr und INT1addr.
Darin sind sie erst definiert.
Und die Interrupts erst freigeben nachdem
der Stack initialisiert wurde.
K. R. schrieb:> Ich habe mir ja das Datenblatt vom Atmega48PA durchgelesen, da steht> aber nichts davon, dass INT1 für PICINT8 bis 14 verantwortlich wäre...
Datenblatt:
13. External Interrupts
The pin change interrupt PCI1 will trigger if any enabled PCINT[14:8]
pin toggles.
Was Du benutzen willst ist nicht INT1 (Vector 3) sondern PCINT1 (Vector
5). Weiter steht dort:
The PCMSK2, PCMSK1 and PCMSK0 Registers control which pins contribute to
the pin change interrupts.
Das heist für Dich, Du mußt im PCMSK1 noch die Maske einstellen, auf
welchen PIN oder welche PINs der Interrupt reagieren soll.
K. R. schrieb:> wie kann> ich aber gezielt den Interrupt auf nur einen PIN, nämlich PC0 setzen?
Im PCICR das Bit PCIE1 setzen (Pin Change Interrupt Enable 1)
Im PCMSK1 das Bit PCINT8 setzen (Pin Change Enable Mask 8)
Interrupts generell erlauben
Auswerteroutine bei Interruptvektor PCINT1 starten (rjmp auf die
Routine).
Die Routine wird bei jedem Pegelwechsel aufgerufen, ob Dein PIN auf L
oder auf H gewechselt ist, mußt Du in der Routine selbst feststellen.
Reinhard
K. R. schrieb:> Wenn ich das mache, kommen fehlermeldungen auf, weil mein Code dann> irgendwie verschoben ist, komisch, im tutorial klappt es angeblich
Und wo im Tutorial-Code steht das ".include ...", und wo bei dir?
Hi
>.org 0x000> rjmp start ; Reset Handler>.org INT0addr> NOP>.org INT1addr> rjmp interrupt ; IRQ1 Handler>.include "M48PAdef.inc">G:\Software Projects\2013\March\Interrupts\ExternInterrupt.asm(6): error: Use of
undefined or forward referenced symbol 'INT0addr' in .org
Das Include gehört an den Anfang des Programms. Vor dem .include
"M48PAdef.inc" weiß der Assemler nicht, was z.B. 'INT0addr' bedeutet.
MfG spess
K. R. schrieb:> Fehlermeldung :
ist ja nun geklärt.
Und tu Dir selbst einen Gefallen und setze an den Anfang Deiner
Programme die gesamte Interruptvektortabelle. Bei nicht genutzen
Interrupts ein reti reinschreiben. Später -in größeren Programmen-
sollte man für nicht genutzte Interrupts eine Routine haben, die
wenigstens die ungewollte Auslösung eines solchen meldet bzw.
registriert. Aber für den Anfang reicht ein reti.
Reinhard
Reinhard R. schrieb:> K. R. schrieb:>> Fehlermeldung :> ist ja nun geklärt.>> Und tu Dir selbst einen Gefallen und setze an den Anfang Deiner> Programme die gesamte Interruptvektortabelle. Bei nicht genutzen> Interrupts ein reti reinschreiben. Später -in größeren Programmen-> sollte man für nicht genutzte Interrupts eine Routine haben, die> wenigstens die ungewollte Auslösung eines solchen meldet bzw.> registriert. Aber für den Anfang reicht ein reti.>> Reinhard
Zwei Probleme sind da aber immer noch:
1)
Der Assembler findet :
Hi
>LDI PCICR, 0b00000010>LDI PCMSK1,0b00000001
ldi funktioniert nur mit den Registern r16...r31.
>.ORG RESET RJMP>.ORG PCINT1 RJMP interrupt ; IRQ1 Handler
Das '.org xyz' und 'rjmp abcd' gehören in verschieden Zeilen.
Hast du dir eigentlich mal das Tutorial hier angesehen? Da steht drin,
wie man es richtig macht.
MfG Spess
K. R. schrieb:> Der Assembler findet :> LDI PCICR, 0b00000010> LDI PCMSK1,0b00000001>> ist nicht möglich...
Steht alles im Datenblatt, aber das ist -zugegebenermaßen- recht groß.
Also, Datenblatt:
8.5 I/O Memory
... For the Extended I/O space from 0x60 - 0xFF in SRAM, only the
ST/STS/STD and LD/LDS/LDD instructions can be used.
PCICR hat die Adresse 0x68
PCMSK1 hat die Adresse 0x6C
fallen also beide in die oben zitierte Kategorie. Wird dann so gemacht:
ldi ZH,high(PCICR)
ldi ZL,low(PCICR)
ldi r16,0b00000010
st Z,r16
ldi ZH,high(PCMSK1)
ldi ZL,low(PCMSK1)
ldi r16,0b00000001
st Z,r16
Dann freut sich Dein AVR-Studio :-)
Reinhard
Hi
>ldi ZH,high(PCICR)>ldi ZL,low(PCICR)>ldi r16,0b00000010>st Z,r16>ldi ZH,high(PCMSK1)>ldi ZL,low(PCMSK1)>ldi r16,0b00000001>st Z,r16>Dann freut sich Dein AVR-Studio :-)
Eher nach dem Motto 'von hinten durch die Brust ins Auge'. Ein
G:\Software Projects\2013\March\Interrupts\ExternInterrupt.asm(11): error: Overlap in .cseg: addr=0x1 conflicts with 0x0:0x4
2
G:\Software Projects\2013\March\Interrupts\ExternInterrupt.asm(12): error: Overlap in .cseg: addr=0x2 conflicts with 0x0:0x4
3
G:\Software Projects\2013\March\Interrupts\ExternInterrupt.asm(13): error: Overlap in .cseg: addr=0x3 conflicts with 0x0:0x4
Sorry Leute, aber in meinem Buch und den meisten Tutorials (ICH BIN NOCH
ANFÄNGER, UND MAßE MIR NICHT AN ALLES ZU KÖNNEN!) wird das anders
gemacht... Deshalb seid mir nicht böse wenn ich wieder was übersehen
habe, aber ich sehe es echt nicht, und die nächste Sache ist, dass es
mich wundert, denn im Tutorial stand nicht explizit dass ein
Zeilenumbruch nötig ist, ich dachte zumindest, weil es in anderen
Programmiersprachen teilweise auch so ist, dass es nicht nötig ist...
Ich hoffe ihr könnt mir bei diesem letzten Problem helfen, danke
m.f.G. Developer_X
K. R. schrieb:> Sorry Leute, aber in meinem Buch und den meisten Tutorials (ICH BIN NOCH> ANFÄNGER, UND MAßE MIR NICHT AN ALLES ZU KÖNNEN!) wird das anders> gemacht...
Dann mach es so, wie du es dort liest.
Aber sieh dir die Dinge um Himmels willen endlich mal GENAU und EXAKT
an! Es reicht nicht, wenn du die Dinge einfach irgendwie wischi waschi
abmalst und nach Gutdünken einfach irgendwie in ein File
zusammenkopierst. Sorgfalt ist einer der Um- Und AUf-Dinge in der ganze
Programmierung. Sorgfalt und die Beachtung von Details.
Dazu gehören Schreibweisen, dazu gehören Reihenfolgen, dazu gehört Doku
lesen, dazu gehört ... alles.
WEnn du das nicht kannst, dann fehlt dir etwas wesentliches was ein
Programmierer haben muss. In dem Fall wäre ein Kunststudium vorzuziehen.
Da kann man hinterher immer noch sagen, das sollte so sein und die
Putzfrau verklagen, die das Kunstwerk mit einem Müllhaufen verwechselt
hat.
Es mag vielleicht sein, das in den Beispielen nur eine der Möglichkeiten
benutzt wird, etwas zu erledigen, ohne Hinweis darauf, das es auch
anders geht. Gerade deswegen ist es empfehlenswert, erstmal genau den
Beispielen zu folgen und ergänzend ein C-Buch zu lesen. Ansonsten musst
Du Dir vorwerfen lassen, nicht in jeder zweiten Zeile den Namen Deiner
Grossmutter genannt zu haben, denn es stand ja nicht da, das das nicht
zulässig ist.
Naja. Gut, das ich den schönen Artikel über css hier gelesen habe. Dann
gehab Dich wohl.
PCINT0 ist nicht das was du denkst das es ist.
Die Konstante für die Speicherposition des Interrupt Vektors des PCINT1
heißt
PCI1addr
Alle symbolischen Namen der Interrupt Vektoren haben IMMER die Endung
'addr'.
1
.ORG 0x000
2
RJMP start
3
RETI
4
RETI
5
RETI
6
.ORG PCI1addr
7
RJMP interrupt ; IRQ1 Handler
8
...
Wenn man sie nicht auswendig weiß, dann hindert einen nichts und niemand
daran, mal das File M48PAdef.inc im Editor aufzumachen und dort drinnen
nachzusehen. Mit einem Suchen nach "addr" ist man recht schnell bei der
Aufzählung der symbolischen Namen für alle Interrupt Vektoren.
Hi
>.ORG PCINT1
Bei meinem Assmbler ( AVR Studio 4.19 Assembler2) nennt sich das
'PCI1addr' und nicht PCINT1.
Aus der Include Datei:
.equ PCI1addr = 0x0004 ; Pin Change Interrupt Request 0
.equ PCINT1 = 1 ; Pin Change Enable Mask 1
Da der Assembler dann statt .org. 0x0004 .Org 1 einsetzt kommt natürlich
das Problem mit dem Overlapping.
MfG Spess
Danke, hat jetzt geklappt :D.
Ein Problem ist da immer noch : Die ISR wird 2x aufgerufen.
Im Tutorial wird ja gesagt :
1
int0_handler:
2
push temp ; Das SREG in temp sichern. Vorher
3
in temp, SREG ; muss natürlich temp gesichert werden
4
5
sbi PORTB, 0
6
7
out SREG, temp ; Die Register SREG und temp wieder
8
pop temp ; herstellen
9
reti
Mit RETI wird die ISR beendet.
Bei meiner ISR :
1
interrupt:
2
CLI
3
PUSH ZL
4
PUSH ZH
5
LDI ZL,LOW (text*2)
6
LDI ZH,HIGH(text*2)
7
8
RCALL LCD_LINE2
9
10
loop:
11
LPM
12
13
; Testen ob Ende
14
TST R0
15
BREQ b
16
17
; Zeichen ausgegeben
18
MOV INFO,R0
19
RCALL LCD_DATA
20
21
; Zeiger auf nächstes Byte setzen
22
ADIW ZL,1
23
RCALL Wait1000MS
24
RJMP loop
25
26
b :
27
RCALL LCD_LINE1
28
RCALL Wait1000MS
29
RCALL Wait1000MS
30
RCALL LCD_CLEAR
31
32
SEI
33
POP ZH
34
POP ZL
35
RETI
ist das ja auch so, und sie dauert so ungefähr 10 Sekunden,
d.h. den button habe ich schon längst wieder los gelassen, während die
ISR läuft, und durch CLI wird ja der erneute Aufruf der ISR während
dieser ISR verhindert. Komisch ist nur, dass nach der Ausführung von der
ISR, die ISR nochmal aufgerufen wird, und dann erst wieder zum
Hauptprogramm springt, was ist denn hier falsch?
Muss mann da den PC manipulieren, oder den Stack, oder soll man in
diesem Falle einen anderen Befehl als "RETI" verwenden, um die ISR
wirklich zu verlassen.
Ach ja, ich weiß dass ich sowohl den Z-Pointer für den Interrupt kram
benutze, als auch für die Textauslese aus dem Flash in der ISR, deshalb
habe ich ZL und ZH auch erstma im Stack gesichert, damit hinterher alles
wieder so wie vorher sein sollte.
Warum klappt es aber trotzdem nicht?
Danke,
m.f.G. Developer_X
Hi
>ist das ja auch so, und sie dauert so ungefähr 10 Sekunden,>d.h. den button habe ich schon längst wieder los gelassen, während die>ISR läuft, und durch CLI wird ja der erneute Aufruf der ISR während>dieser ISR verhindert. Komisch ist nur, dass nach der Ausführung von der>ISR, die ISR nochmal aufgerufen wird, und dann erst wieder zum>Hauptprogramm springt, was ist denn hier falsch?
Das ist ein PINCHANGE-Interrupt. Der löst bei jeder Flanke aus. Also
beim Drücken und beim Loslassen. Außerdem prellt dein Schalter. Da gibt
es sowieso mehrere Flanken.
Mit dem Cli/sei erreichst du gar nichts. Während des Interrupts passiert
das automatisch. Aber wenn andere oder auch der gleiche Interrupt
während der ISR auftreten wird das gespeichert und diese Interrupts
werden nach Beenden Interruptroutine ausgeführt.
MfG Spess
K. R. schrieb:> RCALL Wait1000MS> RCALL Wait1000MS
ist innerhalb einer ISR extrem ungeschickt. Eine ISR sollte so kurz wie
möglich sein. Flag setzen und wieder raus. Den Rest im Hintergrund
erledigen.
Spess53 schrieb:> Mit dem Cli/sei erreichst du gar nichts. Während des Interrupts passiert> das automatisch. Aber wenn andere oder auch der gleiche Interrupt> während der ISR auftreten wird das gespeichert und diese Interrupts> werden nach Beenden Interruptroutine ausgeführt.
Und da ist auch nichts zu umgehen?
Hm tja,
außerdem habe ich diese ISR nur so lang gemacht, um das mal auszutesten,
und was am Bildschirm auszugeben, war ja nur ein Test.
Naja,
Danke dann nochma Leute!
m.f.G. Developer_X
Hi
>Und da ist auch nichts zu umgehen?>Hm tja,
Interrupts für Taster sind zu 99% nicht die sinnvollste Lösung. Das
wurde hier im Forum schon zig mal bis zum Erbrechen diskutiert. Die
Forumssuche nach 'Taster+ Interrupt' bringt 1906 Treffer.
Eine elegante Lösung findest du hier:
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten
MfG Spess
Justus Skorps schrieb:> doch natürlich, einfach Flag vorm Rücksprung zurücksetzen...
Gut, habe ich versucht, klappt aber anscheinend nicht, im Datenblatt
steht :
1
12.2.3 EIFR – External Interrupt Flag Register
2
....
3
• Bit 1 – INTF1: External Interrupt Flag 1
4
When an edge or logic change on the INT1 pin triggers an interrupt request, INTF1 becomes set
5
(one). If the I-bit in SREG and the INT1 bit in EIMSK are set (one), the MCU will jump to the corresponding
6
Interrupt Vector. The flag is cleared when the interrupt routine is executed.
7
Alternatively, the flag can be cleared by writing a logical one to it. This flag is always cleared
8
when INT1 is configured as a level interrupt.
Das heißt ich muss im Byte EIFR das Bit INTF1 setzen.
Jetzt steht in "m48adef.inc" :
1
; EIFR - External Interrupt Flag Register
2
.equ INTF0 = 0 ; External Interrupt Flag 0
3
.equ INTF1 = 1 ; External Interrupt Flag 1
Daher war mein Code, da man ja das Bit mit iener logischen 1 setzen
soll, um das Flag zu löschen:
1
interrupt:
2
CLI
3
4
PUSH ZL
5
PUSH ZH
6
LDI ZL,LOW (text*2)
7
LDI ZH,HIGH(text*2)
8
9
RCALL LCD_LINE2
10
11
loop:
12
LPM
13
14
; Testen ob Ende
15
TST R0
16
BREQ b
17
18
; Zeichen ausgegeben
19
MOV INFO,R0
20
RCALL LCD_DATA
21
22
; Zeiger auf nächstes Byte setzen
23
ADIW ZL,1
24
RCALL Wait1000MS
25
RJMP loop
26
27
b :
28
RCALL LCD_LINE1
29
RCALL Wait1000MS
30
RCALL Wait1000MS
31
RCALL LCD_CLEAR
32
33
SEI
34
POP ZH
35
POP ZL
36
37
ldi ZH,high(INTF1)
38
ldi ZL,low(INTF1)
39
ldi r16,0b00000011 ; Flags löschen, sodass nicht nochmals der Interrupt ausgelöst wird
40
st Z,r16
41
42
RETI
Es läuft aber trotzdem wieder.
Was habe ich da falsch verstanden?
K. R. schrieb:> Ein Problem ist da immer noch : Die ISR wird 2x aufgerufen.
Gähn, postet doch nicht immer und immer wieder die gleichen Fragen.
Schon Fusseln am Mund hab, sonst würd ich die Erklärung zum millionsten
mal runterleiern.
Wie wärs mit Forumsuche, Tutorials, Artikel benutzen?
E$in weiteres Vorgehen waere nun den Taster durch einen Arbitraer
generator oder so zu seretzten und im Pinchange Interrupt einen
Pintoggle zu machen. Beides aufm Scope bringt dann klarheit.
Das GIFR ist ein Register.
Du hast das Assembly Instruction Manual sicher gelesen?
Ja, leider muss man jede Seite des Controller Manuals
mindestens einmal genau gelesen haben.
Ja. die Lernkurve ist da besonders steil, aber sicher wert, wenn man ein
laengerfristiges interesse hat.
und zieh dir mal diesen Beitrag rein :
http://www.ibrtses.com/embedded/avrasmuartint.html
K. R. schrieb:> Ist es so schwer mir endlich zu sagen, wie ich mein Problem lösen kann,> und das Interrupt zurücksetzen kann???
es wurde dir schon zig Mal gesagt: Im richtigen Register das richtige
Bit auf 1 setzen...niemand hier kann was dafür, dass dir anscheinend
selbst die absoluten Basics fehlen! Der Bitname bringt einem nummal
nichts, wenn man nicht das passende Register kennt...
In Artikel
Beitrag "Atmega8 Taster an Interrupt löst zweimal aus"
heißt es :
der mechatroniker schrieb:> Ok, ein Versuch einer Erklärung, was passiert.>> 1. Der Interrupt wird ausgelöst. Das interne Flag für die> Interruptquelle wird zurückgesetzt.
Das heißt, wenn ich dann das flagregister abspeicher, und vor dem Ende
der ISR wieder in das EIFR lade, dann sollte doch alles klappen?
1
interrupt:
2
3
4
PUSH ZL
5
PUSH ZH
6
LDI ZL,LOW (text*2)
7
LDI ZH,HIGH(text*2)
8
9
ldi ZH,high(EIFR) ; GELÖSCHTES FLAG SPEICHERN
10
ldi ZL,low(EIFR)
11
LD r16,Z
12
13
RCALL LCD_LINE2
14
15
loop:
16
LPM
17
18
; Testen ob Ende
19
TST R0
20
BREQ b
21
22
; Zeichen ausgegeben
23
MOV INFO,R0
24
RCALL LCD_DATA
25
26
; Zeiger auf nächstes Byte setzen
27
ADIW ZL,1
28
RCALL Wait1000MS
29
RJMP loop
30
31
b :
32
RCALL LCD_LINE1
33
RCALL Wait1000MS
34
RCALL Wait1000MS
35
RCALL LCD_CLEAR
36
37
POP ZH
38
POP ZL
39
40
ldi ZH,high(EIFR) ; GELÖSCHTES FLAG WIEDERHERSTELLEN
41
ldi ZL,low(EIFR)
42
st Z,r16
43
44
RETI
NEIN TUT ES NICHT.
Und ganz ehrlich, vielleicht hattet ihr ja das Glück diese Sachen zu
studieren, oder in einer Ausbildung zu lernen, ich nicht, und ich
verstehs nicht, viele dinge über die ihr redet, werden bei ner
Googlesuche nicht annähernd erklärt, ok dann hab ich eben keine Basics,
wo soll ich die denn herbekommen? (Komisch, in meinem Buch brauch ich
eure "BASICS" nicht)
Und ganz ehrlich, wenn mir jemand den Code endlich geben würde, dann
könnte ich es auch endlich mal nachvollziehen, falls einer von euch so
kompetent wäre...
K. R. schrieb:> Komisch, in meinem Buch brauch ich> eure "BASICS" nicht
Na, was dabei raus kommt sieht man ja.
K. R. schrieb:> Das heißt, wenn ich dann das flagregister abspeicher, und vor dem Ende> der ISR wieder in das EIFR lade, dann sollte doch alles klappen?
Hast du dir durchgelesen was es heißt, dass dein Taster prellt?
JA schon klar, aber was ist, wenn meine ISR so lange dauert, dass das
prellen keine rolle spielt, (ICH HABE DAS MIT DEM PRELLEN GELESEN),
und ich einfach am ende der ISR ein Byte ändere, das EIFR, und dann wird
es nicht mehr aufgerufen.
WARUM KLAPPT ES DANN NICHT; WAS KANN ICH TUN=?
Und wenn das Flag nicht gelöscht ist? Wieso machst du denn diese Scheiße
mit "gelöschtes Flag speichern".
Lösch es doch einfach am Ende, wo ist das Problem?
>
loop:
LPM
; Testen ob Ende
TST R0
BREQ b
; Zeichen ausgegeben
MOV INFO,R0
RCALL LCD_DATA
; Zeiger auf nächstes Byte setzen
ADIW ZL,1
RCALL Wait1000MS
RJMP loop
.....................................
Die volle Droehnung was ?
Sowas gibt's nicht. Es gibt weder loops, noch einen Delay in einem
Interrupt. Dies wurde schon mehrfach erwaehnt. Lass es sein, das wird
nie was. Pflanze Osterglocken oder sowas. Sorry.
Wenn's denn mit Warten sein soll :
http://www.ibrtses.com/embedded/avrlcd.html
Ich habs ganz pragmatisch hinbekommen,
und ja ich weiß dass interrupts in der regel nicht für langatmige dinge
gedacht sind :
1
interrupt:
2
3
IN r18, PINC
4
SBRC r18,0
5
RETI
6
7
PUSH ZL
8
PUSH ZH
9
LDI ZL,LOW (text*2)
10
LDI ZH,HIGH(text*2)
11
12
RCALL LCD_LINE2
13
14
loop:
15
LPM
16
17
; Testen ob Ende
18
TST R0
19
BREQ b
20
21
; Zeichen ausgegeben
22
MOV INFO,R0
23
RCALL LCD_DATA
24
25
; Zeiger auf nächstes Byte setzen
26
ADIW ZL,1
27
RCALL Wait1000MS
28
RJMP loop
29
30
b :
31
RCALL LCD_LINE1
32
RCALL Wait1000MS
33
RCALL Wait1000MS
34
RCALL LCD_CLEAR
35
36
POP ZH
37
POP ZL
38
39
RETI
Genauso funktionierst, nichts mit manipulation von bytes sondern einfach
gucken, bei neustart der ISR ob der button an ist oder nicht, und dann
sein lassen oder isr durchführen
danke trotzdem, jetzt weiß ich schon ma mehr,
m.f.G. Developer_X
und frohe Ostern
Also. Nimm einen timer interupt, wie in
http://www.ibrtses.com/embedded/avrasmuartint.html
Timer1Int:
push temp
in temp,SREG
push temp
;isr
; reload
ldi temp,0xFF
out TCNT1H,temp
ldi temp,0x80 ;war 0x80
out TCNT1L,temp
;signal : timer came
ldi temp,1
mov Timercame, temp
;end
t1iend:
pop temp
out SREG,temp
pop temp
reti
Dann mach dein Ding im Haupt program
K. R. schrieb:> Genauso funktionierst, nichts mit manipulation von bytes sondern einfach> gucken, bei neustart der ISR ob der button an ist oder nicht, und dann> sein lassen oder isr durchführen
Und du glaubst ernsthaft, dass du weit kommst in der µC-Welt wenn du
nichtmal sowas simples wie Bits in bestimmten Registern setzen kannst?
K. R. schrieb:> Wie soll ich denn bits in registern setzen, was ich kann, ich kann sie> sogar auslesen, wenn ich nicht weiß wie sie heißen???
Lesen kannst du ja irgendwie schon. Wieso schaust du nicht einfach im
Datenblatt nach? Das dauert 2 Minuten und die Wurst ist gegessen.
Hi
>interrupt:>CLI>...> SEI> POP ZH>...
Noch mal: Das cli und sei haben in der Interruptroutine nichts zu
suchen. Das sei vor dem reti bewirkt sogar das Gegenteil von dem was du
vor hast. Denn damit gibst du anderen Interrupts die Möglichkeit deinen
Interrupt zu unterbrechen.
>FUNKTIONIERT AUCH NICHT!
Kann auch nicht. Weil
1. PCIFR für den PCINT zuständig ist
2. es auch so auch mit PCIFR nicht funktioniert:
EIFR und PCIFR liegen in dem Speicherbereich der mit in/out adressierbar
ist. Und der fängt nach den Registern r0...r31 mit Null an. So sind auch
die Adressen in der 'M48PAdef.inc' definiert.
> ldi ZH,high(EIFR)> ldi ZL,low(EIFR)>...> st Z,r16
Der damit adressierte Speicher fängt aber bei $0000 (r0) an. Damit ist
die Adresse die mit EIFR in der 'M48PAdef.inc' definiert ist um $20 zu
klein. Ein
1
ldi r16,xyz
2
out PCIFR,r16
ist weniger umständlich und trifft das richtige Register.
Noch etwas allgemeines:
Deiner Interruptroutine fehlt das Sichern von SREG. Fällt bei dir nicht
auf, führt aber bei richtigen Programmen zu mehr oder weniger lustigen
Effekten.
Statt die IF-Register zu manipulieren ist es bei PCINTs sinnvoller, am
Interruptanfang den Pegel des Pins abzufragen. Damit kannst du
feststellen, welche Flanke den Interrupt ausgelöst hat und entsprechend
reagieren.
MfG Spess
Danke Leute, aber ganz ehrlich ich habe im Datenblatt das Kapitel
Interrupts und External Interrupts gelesen, ich bin doch noch anfänger
und in meinem Buch etc. steht nichts von PCIFR, woher soll ich denn
wissen dass das PCIFR ist, ich weiß ganz ehrlich nicht woher ich das
hätte wissen sollen, fakt ist es klappt jetzt, und das SREG wird auch
gesichert.
Danke euch allen, und seid mir nicht böse, aber ich muss mir das alles
selbst beibringen, und habe nicht so viele quellen und anscheinend auch
nicht dieses "BASIS" Wissen, um die Dinge zu suchen, die ihr meint.
Hi
> woher soll ich denn>wissen dass das PCIFR ist, ich weiß ganz ehrlich nicht woher ich das>hätte wissen sollen,
Na ja, die Namen der Register, die für die externen Interrupts (INT0/1)
zuständig sind fangen alle mit 'E' an. Die Namen der für die Pin Change
Interrupts zuständigen Register sinnigerweise mit PC.
>Danke euch allen, und seid mir nicht böse, aber ich muss mir das alles>selbst beibringen,
Das habe ich vor 30 Jahren auch gemacht.
>und habe nicht so viele quellen und anscheinend auch>nicht dieses "BASIS" Wissen, um die Dinge zu suchen, die ihr meint.
Von der Quellenvielfalt, die heute zur Verfügung steht, konnte ich
damals nur träumen. Man muss sie halt auch nutzen.
MfG Spess