Ο αριθμός των εξωτερικών διακοπών μεταβάλλεται από τον ένα στον άλλο τύπο AVR. Ο ATmega328 έχει δυο εξωτερικές διακοπές: πιν PD2 (PORTD.2) και πιν PD3 (PORTD.3) που ονομάζονται σαν ΙΝΤ0 και ΙΝΤ1 αντίστοιχα. Όταν συμβεί μια διακοπή σε αυτά τα πινς ο AVR διακόπτεται ότι κι αν κάνει και μεταβαίνει στο vector table για να εκτελέσει την αντίστοιχη ρουτίνα εξυπηρέτησης της διακοπής.
Οι εξωτερικές διακοπές ΙΝΤ0 και ΙΝΤ1
Υπάρχουν δυο εξωτερικές διακοπές στον ATmega328 οι ΙΝΤ0 και ΙΝΤ1 οι οποίες βρίσκονται στα πινς PD2 και PD3 αντίστοιχα. Στον interrupt vector table οι θέσεις $2 και $4 είναι εκχωρημένες για την εξυπηρέτηση των ΙΝΤ0 και ΙΝΤ1 αντίστοιχα. Οι εξωτερικές διακοπές πρώτα πρέπει να ενεργοποιηθούν για να μπορέσουν να λειτουργήσουν. Αυτό μπορεί να γίνει χρησιμοποιώντας το bit INTx που βρίσκεται ορισμένο στον καταχωρητή EIMSK. Για παράδειγμα, οι ακόλουθες εντολές ενεργοποιούν τον ΙΝΤ0:
LDI R20, 0x01
STS EIMSK, R20
Προκαθορισμένα ο ΙΝΤ0 είναι διαμορφωμένος να προκαλεί σκανδαλισμό διακοπής όταν βρίσκεται σε επίπεδο LOW. Αυτό σημαίνει ότι όταν ένα LOW σήμα εφαρμοστεί στο πιν PD2 (PORTD.2), στη CPU θα προκληθεί διακοπή με αποτέλεσμα να μεταβεί στη θέση $0002 του interrupt vector table για να εκτελέσει την αντίστοιχη ρουτίνα εξυπηρέτησης διακοπής ISR.
Στο ακόλουθο παράδειγμα βλέπουμε πως οι εξωτερικές διακοπές λειτουργούν. Σε αυτό το παράδειγμα η CPU βρίσκεται σε ατέρμον λειτουργία μέσα στο βρόγχο HERE. Ένας διακόπτης συνδέεται με το ένα του άκρο στο ΙΝΤ0 και το άλλο του άκρο στα 0 Volt. Όταν ο διακόπτης στον ΙΝΤ0 (πιν PD2) κλείσει η CPU βγαίνει έξω από τον βρόγχο και μεταβαίνει στη θέση $0002 του vector table. Η ρουτίνα ISR για την ΙΝΤ0 εναλλάσσει την κατάσταση του PC0. Εάν την στιγμή που εκτελείται η RETI το πιν ΙΝΤ0 είναι ακόμα LOW, η CPU εκτελεί την ρουτίνα διακοπής ξανά. Επομένως, εάν θέλουμε η ρουτίνα ISR να εκτελεστεί μια φορά, το πιν ΙΝΤ0 θα πρέπει να τεθεί σε HIGH πριν η RETI εκτελεστεί ή θα πρέπει να διαμορφώσουμε την διακοπή σε τύπο edge-triggered όπως θα μάθουμε στην συνέχεια.
Πρόγραμμα (1): Θεώρησε ότι το πιν ΙΝΤ0 είναι συνδεμένο σε ένα διακόπτη που προκαθορισμένα είναι high. Γράψε ένα πρόγραμμα το οποίο να εναλλάσσει το PORTB.5 κάθε φορά που το πιν ΙΝΤ0 γίνεται low.
Απάντηση:
.ORG 0
JMP MAIN ;θέση για το reset
.ORG 0x02 ;θέση για την εξωτερική διακοπή 0
JMP EX0_ISR
MAIN: LDI R20, HIGH(RAMEND)
OUT SPH, R20
LDI R20, LOW(RAMEND)
OUT SPL, R20 ;θέση για την ενεργοποίηση του σωρού
SBI DDRB,5 ;PORTB.5 σαν έξοδος
SBI PORTD,2 ;ενεργοποίηση της pull-up αντίστασης
LDI R20, (1<<INT0) ;ενεργοποίηση της διακοπής ΙΝΤ0
OUT EIMSK, R20
SEI ;καθολική ενεργοποίηση διακοπών
HERE: JMP HERE
EX0_ISR:
IN R21, PORTB
LDI R22, (1<<5) ;R22 = 0b00100000
EOR R21, R22
OUT PORTB,R21
RETI
Edge-triggered και level-triggered διακοπές
Υπάρχουν δυο τύποι λειτουργίας των εξωτερικών διακοπών: (1) level-triggered και (2) edge-triggered. Οι ΙΝΤ0 και ΙΝΤ1 μπορούν να λειτουργήσουν σαν level ή edge triggered. Όπως αναφέραμε πριν, κατά το reset οι ΙΝΤ0 και ΙΝΤ1 είναι low-level-triggered διακοπές. Τα bits του καταχωρητή EICRA ορίζουν τον τρόπο σκανδαλισμού των ΙΝΤ0 και ΙΝΤ1, όπως φαίνεται στην ακόλουθη εικόνα:
Πρόγραμμα (2): Γράψε τις εντολές για τις οποίες: (α) κάνει τον ΙΝΤ0 σαν falling edge triggered (β) κάνει τον ΙΝΤ1 triggered on any change και (γ) κάνει τον ΙΝΤ1 rising edge triggered.
Απάντηση:
(α) LDI R20, (1<<ISC01) ;R20 = 0x02
STS EICRA, R20
(β) LDI R20, (1<<ISC10) ;R20 = 0x04
STS EICRA, R20
(γ) LDI R20, (1<<ISC11)|(1<<ISC10) ;R20 = 0x0C
STS EICRA, R20
Πρόγραμμα (3): Ξαναγράψτε το πρόγραμμα (1) στο οποίο όταν το ΙΝΤ0 πηγαίνει σε LOW, να εναλλάσσει την PORTB.5 μόνο μια φορά.
Απάντηση
.ORG 0 ;θέση για το reset
JMP MAIN
.ORG 0x02 ;θέση για την εξωτερική διακοπή 0
JMP EX0_ISR
MAIN:
LDI R20, HIGH(RAMEND)
OUT SPH, R20
LDI R20, LOW(RAMEND)
OUT SPL, R20 ;αρχικοποίηση του σωρού
LDI R20, 0x02 ;διαμόρφωσε το ΙΝΤ0 σαν falling edge triggered
STS EICRA, R20
SBI DDRB,5 ;PORTB.5 σαν έξοδος
SBI PORTD,2 ;ενεργοποίησε την pull-up αντίσταση
LDI R20, (1<<INT0) ;ενεργοποίησε το ΙΝΤ0
OUT EIMSK, R20
SEI ;καθολική ενεργοποίηση διακοπών
HERE: JMP HERE
EX0_ISR: IN R21, PORTB
LDI R22, (1<<5) ;R22=0b00100000 για εναλλαγή του ΡΒ5
EOR R21, R22
OUT PORTB, R21
RETI
Η μόνη διαφορά του προγράμματος (3) με το προηγούμενο πρόγραμμα (1), είναι οι ακόλουθες εντολές
LDI R20, 0x02
STS EICRA, R20
Το οποίο κάνει την ΙΝΤ0 σαν edge-triggered διακοπή δηλαδή στην ακμή πτώσης του σήματος που εφαρμόζεται στο πιν ΙΝΤ0 έχουμε εναλλαγή κατάστασης του PORTB.5 και για να εναλλάξουμε το LED ξανά θα πρέπει ένας άλλος high-to-low παλμός θα πρέπει να εφαρμοστεί στο ΙΝΤ0
Στο πρόγραμμα (1) λόγω της level-triggered φύσης της διακοπής, όσο το ΙΝΤ0 κρατιέται LOW η PORTB.5 εναλλάσσει κατάσταση. Όμως στο πρόγραμμα (3) για να θέσουμε σε “on” το PORTB.5 ξανά θα πρέπει να εφαρμόσουμε ένα HIGH το LOW παλμό για να δημιουργήσει ένα falling edge που να ενεργοποιήσει τη διακοπή.
Σημαίες εξωτερικών διακοπών
Δες την ακόλουθη εικόνα. Η ακμοπυροδότητη διακοπή (falling edge, rising edge ή change level) μανταλώνεται από τη CPU και κρατιέται στα bits INTFx του καταχωρητή EIFR. Αυτό σημαίνει ότι όταν μια εξωτερική διακοπή είναι του τύπου edge-triggered (falling edge, rising edge ή change level) κατά την διάρκεια σκανδαλισμού μιας διακοπής, η αντίστοιχη σημαία INTFx τίθεται σε HIGH. Εάν η διακοπή είναι ενεργή (το bit INTx είναι HIGH και το I-bit του SREG είναι 1) η CPU θα μεταβεί στην αντίστοιχη θέση του interrupt vector table και η INTFx θα μηδενιστεί αυτόματα από τη CPU, διαφορετικά η σημαία θα μείνει σε 1. Η σημαία μπορεί να μηδενιστεί γράφοντας το 1 σε αυτή. Για παράδειγμα η INTF1 μπορεί να μηδενιστεί με τις ακόλουθες εντολές:
LDI R20, (1<<INTF1) ; R20 = 0x02
OUT EIFR, R20 ; clear the INTF1 flag
Σημειώστε ότι στις edge-triggered διακοπές (falling edge, rising edge και change level) ο παλμός θα πρέπει να διαρκεί το λιγότερο ενός κύκλου ρολογιού για να είμαστε σίγουροι ότι η μετάβαση θα αναγνωριστεί από τη CPU. Αυτό σημαίνει ότι παλμοί μικρότεροι σε διάρκεια του ενός κύκλου ρολογιού, δεν είναι σίγουρο ότι θα προκαλέσουν διακοπή.
Όταν η εξωτερική διακοπή είναι του τύπου level – triggered η σημαία INTFx παραμένει αμετάβλητη όταν συμβεί διακοπή. Όταν μια εξωτερική διακοπή είναι του τύπου level – triggered, το πιν INTx θα πρέπει να είναι LOW το λιγότερο πέντε κύκλων ρολογιού για να αναγνωριστεί η διακοπή.