Σ' αυτόν τον ατέρμονα βρόχο διαβάζουμε τις τιμές των εισόδων του microcontroller (που είναι προκαθορισμένα από εμάς "ποδαράκια", pins πάνω στον microcontroller) και αναλόγως τις τιμές που παίρνουμε και τον αλγόριθμο για το FSM (finite state machine) που θα υλοποιήσουμε, δίνουμε αντίστοιχες τιμές στις εξόδους του microcontroller (οι οποίες είναι πάλι "ποδαράκια" πάνω στον microcontroller).
Αν π.χ., έχουμε ένα push button κουμπωμένο πάνω σε ένα ποδαράκι του microcontroller που όταν το πατάμε θέλουμε να ανάβει ένα ledάκι που έχουμε κουμπωμένο πάνω σε ένα άλλο ποδαράκι του microcontroller, θα έπρεπε σ' αυτόν τον ατέρμονα βρόχο, σε κάθε κύκλο εκτέλεσής του, να κοιτούσαμε πρώτα την κατάσταση του κουμπιού (αν είναι πατημένο, ή όχι) και αναλόγως να δίναμε ρεύμα ή όχι στο led. Όπως καταλαβαίνεται, αυτό φαίνεται προβληματικό με μια δεύτερη σκέψη. Τι θα γίνει αν στο κύκλο εκτέλεσής μας, δεν μπορέσουμε να πιάσουμε το πάτημα του κουμπιού; Εκτός αυτού, το να διαβάζεις την κατάσταση ενός pin σε κάθε κύκλο εκτέλεσης, "κοστίζει" σε επεξεργαστικό χρόνο. Μπορεί στο παραπάνω παράδειγμα να φαίνεται γελοίο, αλλά στα περιορισμένης ισχύς κυκλώματα που έχουν πάνω τους κουμπωμένους διάφορους αισθητήρες και κουμπιά, αυτός ο φαινομενικά ελάχιστος χρόνος είναι πολύτιμος.
Γι αυτές τις περιπτώσεις, οι διάφοροι microcontrollers έχουν συγκεκριμένα pin (αν όχι όλα) που μπορούν με τον κατάλληλο προγραμματισμό να διακόψουν την λειτουργία του ατέρμονα βρόχου, να εκτελέσουν μια μικρής επεξεργαστικής διάρκειας ρουτίνα και όταν τελειώσουν να αφήσουν τον ατέρμονα βρόχο να συνεχίσει. Κατά συνέπεια, στο παραπάνω παράδειγμα, αντί να κοιτάζουμε συνέχεια σε κάθε πέρασμα του ατέρμονα βρόχου την κατάσταση του κουμπιού, μπορούμε να γράψουμε μια μικρή ρουτίνα η οποία θα αλλάζει την τιμή μια μεταβλητής και με βάση αυτήν την μεταβλητή να παίρνουμε τις κατάλληλες αποφάσεις στον ατέρμονα βρόχο. Έτσι μέσα στον ατέρμονα βρόχο, εκτελούμε μόνο κώδικα που έχει πραγματική αξία, ενώ η αλλαγή της κατάστασης του κουμπιού θα επηρεάσει την εκτέλεσή του μόνο όταν πατηθεί το κουμπί.
Ας περάσουμε στον κώδικα. Για την δική μου ευκολία, αλλά και για την δική σας, θα παραθέσω κώδικα από arduino sdk (το RTOS απαιτεί να θέσεις και προτεραιότητες στα interruptions κάτι που το κάνει ελαφρώς πιο πολύπλοκο).
Κώδικας: Επιλογή όλων
/*
* Το ποδαράκι του led μας. Για την ακρίβεια το
* pin 13 είναι το led που έχει ήδη πάνω του ένας
* arduino. Χρησιμοποιούμε το πρόθεμα const
* για να πούμε στον compiler (στο πρόγραμμα
* που θα μεταγλωττίσει αυτόν τον κώδικα σε
* γλώσσα μηχανής, σε 0 και 1 που να μπορεί
* να τα διαβάζει ο επεξεργαστής) οτι αυτή η
* τιμή και ο τύπος της δεν πρόκειται να αλλάξει
* κατά την εκτέλεση.
*/
const int ledPin = 13;
/*
* Το ποδαράκι του κουμπιού μας.
*/
const int interruptPin = 2;
/*
* Το flag που θα χρησιμοποιήσουμε για να
* αποφασίσουμε αν θα ανάψουμε το led ή όχι.
* Χρησιμοποιούμε το πρόθεμα volatile, γιατί ο
* compiler προσπαθεί πάντα να βελτιστοποιήσει
* τον κώδικα γλώσσας μηχανής που παράγει
* και επειδή δεν βλέπει να μεταβάλλεται αυτή
* η τιμή στη ρουτίνα loop() παρακάτω, αν δεν
* βάλλουμε το πρόθεμα volatile, θα αντικαταστήσει
* την τιμή στο if που υπάρχει στην ρουτίνα loop()
* με false.
*/
volatile int state = 0;
/*
* Αυτή η ρουτίνα εκτελείται μόνο μια φορά κατά
* την εκκίνηση του conrtoller (αλλά και όποτε
* πατάμε το reset button) και σ' αυτήν την ρουτίνα
* προκαθορίζουμε είτε τι κάνει κάθε pin που έχουμε
* συνδεδεμένο στον controller μας, είτε αρχικοποιούμε
* κάποιες μεταβλητές και βιβλιοθήκες που θα χρησιμο-
* ποιήσουμε παρακάτω στην loop(()
*/
void setup() {
// Εδώ ορίζουμε πως το ποδαράκι 13 θα χρησιμοποιηθεί
// σαν έξοδος.
pinMode(ledPin, OUTPUT);
// Εδώ ορίζουμε πως το ποδαράκι 2 θα χρησιμοποιηθεί
// σαν είσοδος, η οποία θα χρησιμοποιεί τις εσωτερικές
// αντιστάσεις του επεξεργαστή μας (διορθώστε με αν
// κάνω λάθος, γι αυτό δεν είμαι και τόσο σίγουρος).
pinMode(interruptPin, INPUT_PULLUP);
// Εδώ ορίζουμε πως το ποδαράκι δύο, μπορεί να πραγματο-
// ποιήσει ένα interrupt όποτε αλλάξει κατάσταση το pin (το
// CHANGE που του έχουμε δώσει σαν παράμετρο), το οποίο
// θα καλέσει και θα εκτελέσει την ρουτίνα stateChange().
attachInterrupt(digitalPinToInterrupt(interruptPin), stateChange, CHANGE);
}
/*
* Ο ατέρμον βρόχος μας
*/
void loop() {
if (state) { // Αν το state είναι 1, άναψε το led
digitalWrite(ledPin, HIGH);
} else { // αλλιώς σβήσ' το.
digitalWrite(ledPin, LOW);
}
}
/*
* Η ρουτίνα που θα εκτελεστεί κατά το interruption.
* Βλέπετε πως είναι "μικρή" ρουτίνα. Δεν πρέπει
* να κάνει "βαριά" πράγματα, όπως π.χ. ένα network
* call. Επίσης, η ρουτίνα που εκτελείται κατά το
* interruption δεν πρέπει να επιστρέφει τίποτα (δεν
* μπορεί να είναι συνάρτηση δηλαδή).
*/
void stateChange() {
// Δώσε την αντίθετη τιμή από αυτήν που είχε
// η μεταβλητή state, στην μεταβλητή state.
// Προγραμματιστικά, το αντίθετο του 1 είναι το
// 0 και το αντίθετο του 0 το 1.
state = !state;
}