AVR - Interruptpin in der Interruptroutine abfragen ASM

OP #666044
Lesenswert?

Hallo,

ich möchte innerhalb der Interruptroutine, welche durch einen externen 
Interrupt an INT0 (Tiny13) ausgelöst wurde, überwachen wie der Status 
dieses Pins weiterhin ist.
Kann ich einfach mit cbi DDRB,1 (Pin als Eingang schalten) und sbis bzw 
sbic den PIN weiterhin überwachen ?

Hintergrund: Ich möchte ein 8Bit über den INT0 Eingang empfangen.
Sobald der Level auf der Leitung von 5 auf 0 Volt wechselt löst der 
Interrupt aus. Innerhalb dieser Routine möchte dann die wieteren 
Pegeländerungen (Bits) auswerten um dann nach 8 Bits oder auch nach 
einem möglichen Timeout (Störimpuls auf der Leitung) die 
Interruptroutine zu verlassen und auf das nächste Byte warten.

Gruß

Günter
#666049
Lesenswert?

Ich nehme an du willst ne Serielle schnittstelle simulieren,

dazu gibt es 2 Möglichkeiten die zum gewünschten Ziel führen

Zum einen kannst du deine Aktivität am Pin über einen gewissen Zeitraum 
überwachen.

Zum anderen deaktivierst du den nterrupt und somit kannst du dn Pin in 
irgendwelchen anderen schleifen als ganz "normalen" eingang verwenden.

Und erst wenn du mit der auswertung fertig bist enablest du den 
Interrupt wieder.

Gruß

Dennis
OP #666055
Lesenswert?

Ich war immer davon ausgegangen, dass der Interrupt innerhalb der 
Routine automatisch deaktiviert und erst am Ende durch reti wieder 
aktiviert wird.
Ich möcht nicht ständig den Eingangspin abfragen, sondern sobald ein 
Byte kommt in die Interruptroutine springen und dort dann die 8 Bits 
auswerten um dann die Routine verlassen.

Beispiel
interrupt:
Abfrage an Pin int0 (PB1)
Bitzeiten der 8 Bit bzw eventuellen Timeout durch Zähler auswerten.
Anschliessend
Status 1 oder 0
RXBxte = ???
Reti
#666059
Lesenswert?

Achso,

ja, die Abfrage des einzelnen Pins in der Interrupt routine ist kein 
Problem, man sollte nur bedenken, das für die Zeit der Routine wie du 
oben auch schon beschrieben hast die anderen Interrupts blockiert 
werden. Wenn in dieser Zeit zum Beispiel ein Timeroverflow passiert, 
wird dieser erst nach der "Empfangsroutine" ausgeführt, was vielleiht zu 
zeitlichen Problemen führen kann. Deshalb sollte versucht werden, diese 
Routinen so kurz wie möglic zu halten.
OP #666069
Lesenswert?

Timer benutze ich aufgrund fehlender Erfahrung noch nicht.
Die Bit_Auswertung mache ich mit Schleifen und Abfragen.
Ich hatte nur Bedenken, ob es möglich ist, den Pin, der vorher als 
Interrupt-Pin gedient hat, in der Interruptroutine selber als einfachen 
Eingang zu verwenden.
#666165
Lesenswert?

Guenter Bru wrote:
> Timer benutze ich aufgrund fehlender Erfahrung noch nicht.

Schade eigentlich, denn das Verwenden eines Timers macht Vieles 
einfacher.

> Die Bit_Auswertung mache ich mit Schleifen und Abfragen.
> Ich hatte nur Bedenken, ob es möglich ist, den Pin, der vorher als
> Interrupt-Pin gedient hat, in der Interruptroutine selber als einfachen
> Eingang zu verwenden.

In der ISR eines externen Interrupts das gesamte Telegramm einzulesen, 
ist keine gute Idee, aber sicherlich nicht unmöglich.

Ich würde (für UART) beim Int den Timer starten und mir vom Timer die 
nächsten Abfragetermine generieren lassen. Bei anderen seriellen 
Übertragungsarten (z.B. 25/75%-Protokoll oder Servoimpuls) würde ich im 
ext. Interrupt den Timerstand abfragen und auswerten, also bei 
Impulsbeginn Zeitstempel merken und bei Impulsende den gemerkten vom 
neuen Zeitstempel subtrahieren. Das erlaubt genaue Zeitmessung 
(Software-ICP), während die Compare-Einheiten nebenher noch den Takt für 
andere Dinge bereitstellen können.

...
OP #666670
Lesenswert?

>>In der ISR eines externen Interrupts das gesamte Telegramm einzulesen,
>>ist keine gute Idee, aber sicherlich nicht unmöglich.

Was meinst du mit "sicherlich nicht unmöglich" ?
So schwierig ? Ich programmiere eher selten.
Timer mögen eleganter sein, aber während der uP das Byte einliest, 
braucht er nicht unbedingt etwas anderes zu machen.
Anbei meine noch nicht so ganz funktionierende Interruptroutine:
1
int0_handler: ;Empfängt Byte 
2
cbi ddrb,bus  ;Pin als Eingang schalten
3
ldi RXByte,0  ;Byte löschen
4

5
rcall Empfang_Bit   ;Bit empfangen
6
cpi status,1        ;Bit ok ?
7
brne Byte_verwerfen ;Wenn nicht Byte verwerfen, Status=0, Exit Routine
8

9
ldi Bitzaehler,7    ;restliche 7 Bits
10
loop_Bits:
11

12
rcall Empfang_Pause ;Pause empfangen
13
cpi status,1        ;Bit ok ?
14
brne Byte_verwerfen ;Wenn nicht Byte verwerfen, Status=0, Exit Routine
15

16
rcall Empfang_Bit   ;Bit empfangen
17
cpi status,1        ;Bit ok ?
18
brne Byte_verwerfen ;Wenn nicht Byte verwerfen, Status=0, Exit Routine
19

20
dec Bitzaehler      ;Letztes Bit ?
21
brne loop_Bits       ;wenn nicht weiter auswerten
22

23
Byte_ok:
24
ldi Status,1
25
com rxbyte
26
reti
27

28
Byte_verwerfen:
29
ldi Status,0
30
reti
31

32

33
Empfang_Bit:
34

35
ldi Zaehler,0    ;Zähler auf Null
36
ErhoehenA:
37
inc Zaehler    ;Zähler um 1 erhoehen
38
rcall bit_delay    ;Delay
39
cpi Zaehler, 20   ;Zählerüberlauf ? dann Status=0 und Ret
40
brne loopA
41
ldi status,0       ;Fehler Status auf 0 setzten   
42
ret
43
loopA:
44
sbis Pinb,Bus    ;Pin = 1 dann fertig
45
rjmp ErhoehenA    ;sonst weiterzählen
46
cpi Zaehler,laenge  ;Wenn über 5 dann Carry clear sonst Carry set
47
ror RXByte    ;Carry in Empfangsbyte schieben
48
ldi Status,1    ;Status ok
49

50
ret
51

52
Empfang_Pause:
53
   
54
ldi Zaehler,0    ;Zähler auf Null
55
ErhoehenB:
56
inc Zaehler      ;Zähler um 1 erhoehen
57
rcall bit_delay      ;Delay
58
cpi Zaehler, 20    ;Zählerüberlauf ? dann Status=0 und Ret
59
brne loopB
60
ldi status,0        ;Fehler Status auf 0 setzten
61
ret
62
loopB:
63
sbic Pinb,Bus    ;Pin = 0 dann fertig
64
rjmp ErhoehenB    ;sonst weiterzählen
65
cpi Zaehler,laenge    ;Wenn über 5 dann gültig (Carry Clear)
66
brcs endeB      ;Status auf Fehler,lassen wenn Carry Set
67
ldi Status,1    ;und Status ok setzten
68
EndeB:
69
ret
OP #670788
Lesenswert?

Habe jetzt einige Zeit (erfolglos) herumprobiert:
Folgender Code funktioniert nicht bzw die Befehle sbic oder auch sbis 
funktionieren nicht.
Kann mir einer sagen warum das so ist ?
Habe ich irgendetwas übersehen ?
Bei L-Pegel soll der Interrupt auslösen, was er auch macht.
Aber jede weitere Abfrage des Pins in der Interruptroutine ignoriert der 
Tiny13.
Hier die Interruptroutine:
1
MCUCR 01000000  ;LowPegel -> Interrupt
2
GIMSK 01000000  ;INT0 enabled
3

4
int0_handler: ;Empfängt Byte 
5
cbi ddrb,1  ;Pin als Eingang schalten
6
sbic portb,1
7
LED_aus:
8
sbi portb,3 ;LED aus
9
rjmp loop
10
reti
11
LED_an:
12
cbi portb,3 ;LED an
13
reti

Was passiert eigentlich mit weiteren INT0 Interrupts, welche während 
eines INT0 Interrupts auftreten ?
Werden die verworfen oder in eine Warteschlange gestellt ?

Gruß

Günter
Gast #670800
Lesenswert?

>Was passiert eigentlich mit weiteren INT0 Interrupts, welche während
>eines INT0 Interrupts auftreten ?
>Werden die verworfen oder in eine Warteschlange gestellt ?

Oh ja, die werden in eine gestellt, aber in eine, deren Länge auf 1 
begrenzt ist. :-) Die "Warteschlange" ist das INTF0-Flag im GIFR.
#670801
Lesenswert?

int0_handler:         ;Empfängt Byte
 cbi ddrb,1  ;Pin als Eingang schalten
Unfug, der Int-Pin sollte schon beim Warten auf den Int. Eingang sein...

 sbic portb,1
LED_aus:              ;Label ist eigentlich nicht erforderlich
 sbi portb,3 ;LED aus ;Richtig, LED aus, wenn PB1 gesetzt war

 rjmp loop
 reti
Was soll das denn??? LED_an wird dadurch nie erreicht...

LED_an:
 cbi portb,3 ;LED an
 reti


Versuch's mal so:

int0_handler:       ;Empfängt Byte
 sbic portb,1           ;ist H-Pegel an PB1? - nein, skip...
 sbi portb,3            ;ja, mach LED aus
 sbis portb,1           ;ist L-Pegel an PB1? - nein, skip...
 cbi portb,3            ;ja, mach LED an
 reti                   ;fertig und zurück...

...
Gast #670807
Lesenswert?

>Ich möchte ein 8Bit über den INT0 Eingang empfangen.
>Sobald der Level auf der Leitung von 5 auf 0 Volt wechselt löst der
>Interrupt aus. Innerhalb dieser Routine möchte dann die wieteren
>Pegeländerungen (Bits) auswerten um dann nach 8 Bits oder auch nach
>einem möglichen Timeout (Störimpuls auf der Leitung) die
>Interruptroutine zu verlassen und auf das nächste Byte warten.

Mit externen Interrupts wird man da erfahrungsgemäß wenig glücklich. 
Die Alternative ist: Konfigurier einen Timer so, das er mit einer 
passenden Frequenz tickt, und frag in dessen Interrupthandler den 
Leitungspegel zyklisch ab. Mit dem jeweils aktuell eingelesenen Pegel 
fütterst Du eine State Machine, in der der Bitstrom geeignet verrechnet 
wird.
Gast #670811
Lesenswert?

Ich habe den Code nochmal berichtigt. (Habe schon sehr viele Versuche 
probiert und den Code entsprechend oft geändert gehabt .)

@spess53
Nur zur Sicherheit. Ich habe in der Initroutine auch schon ein cbi 
DDRB,1 stehen.
@all
Wie gesagt der Interrupt wird ausgelöst. Nur sbis und sbic funktionieren 
in der Interruptroutine nicht.
sbis portb,1 müsste doch eigentlich prüfen, ob Eingang 1 an Port B H 
oder L ist und dementsprechend den RJMP Befehl ausführen oder 
überspringen.
Bei unten stehendem Code wird der sbic folgende Befehls immer 
ausgeführt.
Da bei portb,1 = L der Interrupt ausgeführt wird, müsste der kurze Zeit 
später immer noch auf L liegende Eingang doch bewirken, dass rjmp 
übersprungen wird.
Bin ziemlich ratlos.
1
MCUCR 01000000  ;LowPegel -> Interrupt
2
GIMSK 01000000  ;INT0 enabled
3

4
int0_handler: ;Empfängt Byte 
5
cbi ddrb,1  ;Pin als Eingang schalten
6
sbic portb,1
7
rjmp LED_an
8
LED_aus:
9
sbi portb,3 ;LED aus (gegen 5V)
10
reti
11
LED_an:
12
cbi portb,3 ;LED an (gegen 5V)
13
reti


Gruß

Günter

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren