Forum: Mikrocontroller und Digitale Elektronik Interrupt?!?!


von Pfi (Gast)


Lesenswert?

Hallo!!

Habe gerade neu mit Assembler angefangen und habe mir ein Programm 
schreiben wollen: Ich habe nur den Anfang programmiert und wollte es mit 
AVRStudio 4 simulieren. Leider gibt es immer einen Interrupt, wenn ich 
gar keinen erwarte: Hier der Code und wo der Interrupt kommt:

.include "m8def.inc"
.def temp=r16
.org 0x000
rjmp reset
reti
rjmp extint
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti
rjmp adcfertig
reti
reti
reti
reti
reti
reset:
ldi temp, LOW(RAMEND)  ;LOW-Byte der obersten RAM-Adresse
out SPL, temp
ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, temp
ldi temp,0b00001010             ;ext int bei steigender Flanke
out mcucr,temp
sei
**ldi temp,0b11000000    ;Beide ext int aktivieren
out gicr,temp
ldi temp,0b01000000             ;Admux auf Eingang 0 stellen out 
admux,temp
ldi temp,0b11000111    ;ADC aktivieren, und starten
out adcsr,temp
rjmp reset
extint:
nop
ret
adcfertig:

Dort wo ** gibt es einen Interrupt. Ich weiss, der Fehler ist 
wahrscheinlich doof, aber ich bin noch doofer und kann ihn nicht finden!

Danke für eure Hilfe

von Sven (Gast)


Lesenswert?

Hast du dir schon das Tutorial für Interrupts hier auf der Webseite 
ddurchgelesen?

von Pfi (Gast)


Lesenswert?

Jepp, habe ich, weiss aber immer noch nicht wo der Fehler liegt! ;-(

von Dennis (Gast)


Lesenswert?

Hey!

Also bin auch noch nicht lange dabei, aber ich frag mich wieso du 
soviele Reti-anweisungen am anfang hast, obwohl ich glaube, daß es nur 
14 gibt, außerdem muß es in deiner Interrupt-Marke extint: RETI heißen 
statt ret.

Außerdem glaube ich, dass deine ADCfertig anweisung zwei Zeilen höher 
stehen mußte!

Gruß Dennis

von Pfi (Gast)


Lesenswert?

Wenn ich reti schreibe, dann spielt es total verrückt, springt wie wild 
umher....
wegen der Interrupts, ich verwende einen AtMega8, der hat 19 
Interruptquellen

von Dennis (Gast)


Lesenswert?

Also Reti ist eigentlich die richtige Anweisung, bei Rücksprüngen aus 
Interrupts.
Wie simulierst du denn die Interrupts?
Oder läßt du alles so wie es ist, startest und dann kommt Murks raus!
Ich würde in einzelschritten durchs ganze Programm gehen und mir die 
Register anschauen. Der Computer ist ja immer nur genauso schlau wie 
sein Programmierer.

von Sven (Gast)


Lesenswert?

Hi Pfi!

Wenn ich es richtig sehe, solltest Du kein rjmp reset machen. Dadurch 
wird in jedem Durchlauf die Initialisierung neu gemacht. Da der 
AD-Wandler schon läuft, gibt es einen Interrupt, wenn sei ausgeführt 
wird.

Stattdessen eine Endlosschleife am Ende. Und die Interrupt-Routinen mit 
reti verlassen, dass ist schon richtig, funktioniert nur wegen Deinem 
rjmp reset nicht.

Sven

reset:
ldi temp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, temp
ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, temp
ldi temp,0b00001010 ;ext int bei steigender Flanke
out mcucr,temp
sei
**ldi temp,0b11000000 ;Beide ext int aktivieren
out gicr,temp
ldi temp,0b01000000 ;Admux auf Eingang 0 stellen out admux,temp
ldi temp,0b11000111 ;ADC aktivieren, und starten
out adcsr,temp

wait_loop:
  ;Endlosschleife
rjmp wait_loop

extint:
nop
reti

adcfertig:
  ;Anweisung
  ;event. ADC neu starten
reti

von Pfi (Gast)


Lesenswert?

@Sven: Danke klingt logisch, werde es aber erst später versuchen!
@Dennis: Ich lasse bei den Interrupts alles so wies ist (ausser das was 
im Code an den Einstellungen geändert wird!) Ich mache auch immer 
Einzelschritt, aber er "biegt" eben immer so komisch ab....
Danke für die Hilfe

von Pfi (Gast)


Lesenswert?

Habs trotzdem grad probiert: Nun kommt es noch einwenig anders heraus, 
hab den Code von Sven genommen (nach dem Interrupthandler am Anfang des 
Programms) nun kommt es bis "ldi temp,0b01000000" in zeile 33, dann 
springt es zum Externen Interrupt Zeile 6, dann zum Unterprogramm das 
aufgerufen wurde und dann bei reti am Schluss des Unterprogramms wieder 
auf Zeile 34, das könnte man ja noch akzeptieren, aber ab da wird 
anscheinend jeder Takt ein Interrupt ausgelöst. Muss man, wenn ein 
Interrupt ausgelöst wurde diesen zuerst deaktivieren? gerade wenn das 
Unterprogramm aufgerufen wurde??? Sollte doch nicht so sein?? Oder liegt 
es am AVRStudio 4?

von Sven (Gast)


Lesenswert?

Oder muß man eventuell das entsprechende Interrupt-Flag wieder löschen? 
Manchmal durch Reinschreiben einer 1, logischerweise ;-)

Schau mal, welche Flags gesetzt werden und wo diese wieder gelöscht 
werden, oder auch nicht.

Sven

von Pfi (Gast)


Lesenswert?

Hallo!!
Bin nach längerer Suche nicht fündig geworden: Habe nirgends gelesen, 
dass ich etwas wieder zurückstellen muss, oder kann mir jemand was 
anderes sagen? ich wäre wirklich froh um jeden Ratschlag!!

Grüsse

von Sven (Gast)


Lesenswert?

Also, normalerweise wird beim Auftreten eines Interrupt-Ereignisses und 
wenn der entsprechende Interrupt enabled ist, ein spezifisches Flag in 
einem Register gesetzt. Darauf wird dann bei der nächsten passenden 
Gelegenheit, z.B. nach einem sei oder auch sofort der Interrupt 
ausgeführt. Das Flag wird mitunter beim Aufruf des Interrupts (Timer) 
zurückgesetzt, mitunter beim Abrufen eines bestimmten Registers (Ich 
glaube, beim UART ist das so), mitunter muß es auch "per Hand" 
zurückgesetzt werden. Dateils dazu sollten bei der Beschreibung der 
Interrupts stehen. Ich habe zum Mega8 leider kein Datenblatt.

Schau Dir doch mal im Simulator die entsprechenden Register an, die zum 
ADC / ext. Interrupt gehören.

Sven

von Pfi (Gast)


Lesenswert?

Hab nochmals nachgeschaut, und habe gesehen, dass man etwas ins GIFR 
(Bit 7 und Bit 6) schreiben muss, habe ich auch gemacht, ohne Erfolg, 
beim Simulieren kamen keine Häkchen Bei den oben genannten Bits, das 
finde ich sehr merkwürdig.
Hat jemand ein Programm in ASM, dass Interrupts verwendet (am liebsten 
auch den Externen) dann könnte ich mir das mal anschauen, vielleicht 
fällt mir was auf!

Grüsse
Pfi

von Uwe (Gast)


Lesenswert?

Hi!
Normalerweise muss es so gehen, versuche es doch mal direkt auf dem uC. 
Ich trau dem AVR-Studio nicht über den Weg.

für den Rest deines Progr.kannst du die Zeilen

ldi temp,0b00001010 ;ext int bei steigender Flanke
out mcucr,temp

totlegen.

"sei" solltest du erst schreiben wenn alle Setup's gemacht sind.

Nochwas Wichtiges: wenn in deinen ISR's Befehle verwendet werden die 
Flag's verändern, musst du das SREG sichern und und vor "reti" 
zurückschreiben!

ISR:
in r16,SREG
.
TST......
.
out SREG,r16
reti

und nochwas:
**ldi temp,0b11000000 ;Beide ext int aktivieren

stimmt denn da die INT-Tabelle(Einsprung in ISR,sehe nur einen Ext INT 
!)????

viel Erfolg Uwe

von Pfi (Gast)


Lesenswert?

Dank Uwe, es hat daran gelegen, dass beide Ext interrupt aktiviert 
wurden, mit einem gehts jetzt!

Was meinst du mit dem SREG sichern? Das habe ich noch nie gehört, was 
macht das für einen Sinn??

von Sven (Gast)


Lesenswert?

SREG ist das Status-Register, es enthält Flags, die bei Operationen 
gesetzt werden. Zum Beispiel für Verweigungen

cp r17,r18
breq ist_gleich

cp vergleicht r17 mit r18 und setzt das Zero-Flag, wenn gleich. breq 
verzweigt dann nach ist_gleich:, wenn das Zero-Flag gesetzt ist. Wird 
jetzt bei breq der Interrupt aufgerufen und im Interrupt das Zero-Flag 
verändert, zum Beispiel durch eine arithmetische Operation, wird das 
breq nicht ausgeführt. Dummerweise muß man das SREG immer erst in ein 
Arbeitsregister laden, um damit arbeiten zu können.

ISR:
in r16,SREG  ;lade Status-Register in r16
push r16  ;r16 auf Stack
          ;damit r16 wieder frei
.
TST......
.
pop r16  ;hole Status-Register aus Stack in r16
out SREG,r16  ;Status-Register wieder herstellen
reti  ;Interrupt beenden

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.