Οι AVR διαθέτουν ενσωματωμένη διάταξη για υποστήριξη
σειριακής επικοινωνίας USART[1] (Universal Synchronous and Asynchronous serial Receiver and
Transmitter). Η ενσωματωμένη διάταξη μας εξυπηρετεί στην απλοποίηση
του κώδικα για την επίτευξη της επικοινωνίας. Ο μικροελεγκτής ATmega2560 διαθέτει 4 ανεξάρτητες διατάξεις USARTn (n=0-3).
Η ασύγχρονη επικοινωνία είναι η περισσότερο χρησιμοποιούμενη γιατί
απαιτεί δύο μόνο συνδέσεις, μια για τη λήψη (TX)
και μια για την αποστολή (RX) δεδομένων. Η
ρύθμιση των χαρακτηριστικών της επικοινωνίας και οι λειτουργίες λήψης και
αποστολής γίνονται μέσω έξι καταχωρητών: UCSRnA,
UCSRnB, UCSRnC,
UBRRnH, UBRRnL και UDRn. Υπάρχει επίσης
δυνατότητα ενεργοποίησης εσωτερικής διακοπής κατά την λήψη ή αποστολή δεδομένων
ή όταν αδειάζει ο καταχωρητής δεδομένων UDRn.
Κατά το initialize ρυθμίζεται
το Baud Rate δίνοντας την κατάλληλη τιμή
(UBRR) στους καταχωρητές UBRRnH
και UBRRnL. Η τιμή αυτή εξαρτάται από τη
συχνότητα λειτουργίας του ρολογιού συστήματος (fosc)
και το επιθυμητό Baud Rate (BAUD) και μπορεί να
υπολογιστεί από τη σχέση[2]:
UBRR=(fosc/16BAUD)-1
Για κάθε συνδυασμό συχνότητας λειτουργίας–Baud Rate υπάρχει και μια πιθανότητα
σφάλματος[3]
στη μετάδοση των δεδομένων, εξ αιτίας της διαίρεσης που πρέπει να γίνει στη
συχνότητα λειτουργίας, ώστε να γίνει ο χρονισμός της διάταξης για να επιτευχθεί
το επιθυμητό Baud Rate. Η πιθανότητα σφάλματος μπορεί να υπολογιστεί από τη
σχέση[4]:
Error[%]=((BaudRateClosest Match/BaudRate)-1)·100%
Κατά το initialize ενεργοποιούνται
επίσης οι διατάξεις λήψης, αποστολής και εσωτερικών διακοπών USARTn. Η ενεργοποίησή τους γίνεται με τοποθέτηση
κατάλληλων bit του UCSRnB.
Ο καθορισμός του frame format της επικοινωνίας γίνεται με
τοποθέτηση κατάλληλων bit του UCSRnC.
Ο καταχωρητής UCSRnA περιέχει τις σημαίες λήψης– αποστολής δεδομένων, διαθεσιμότητας του UDRn και σφαλμάτων και τα bit
διπλασιασμού ταχύτητας και ενεργοποίησης πολλαπλών μικροελεγκτών στον ίδιο
δίαυλο. Στη συνέχεια παρουσιάζεται ένα παράδειγμα αποστολής και λήψης δεδομένων
γραμμένο σε γλώσσα προγραμματισμού C:
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned char trdata;
unsigned char rvdata;
void USART_Init(unsigned int baud) //
Initialization routine (USART0, RX->PE0, TX->PE1)
{
UBRR0H = (unsigned char)(baud>>8); // Set baud rate
UBRR0L = (unsigned char)baud;
UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0); // Enable receiver, transmiter, receiver complete
//
interrupt
//UCSR0C = (1<<URSEL00)|(1<<USBS0)|(3<<UCSZ00); // Set frame format: 2 stop bit, 8 data,
//
asynchronous operation & no parity
}
void USART_Transmit(unsigned char trnsm) // USART Transmit
routine
{
while (!(UCSR0A & (1<<UDRE0))); //
Wait for empty transmit buffer
UDR0 = trnsm; //
Put data into buffer, sends
//
the data
}
unsigned char USART_Receive(void) // USART Receive
routine
{
while (!(UCSR0A & (1<<RXC0))); // Wait for unread data in buffer
return UDR0; // Return data to caller
}
ISR(USART0_RX_vect) // Receive Complete Interrupt
{
rvdata = USART_Receive(); // Send received data to variable rvdata
}
int main(void) //
Main program
{
/* Your code here */
sei(); //
Enable all Interrupts
USART_Init(103); // System Clock 16MHz: 103->9600
while (1)
{
/* Your code here */
USART_Transmit (trdata); // Transmit
variable “trdata”
/* Your code here */
}
}
Στην αρχή του κώδικα εμφανίζεται η ρουτίνα για το initialization USART_Init(unsigned int baud), όπου με τις δύο πρώτες εντολές
ρυθμίζεται το Baud Rate. Η μεταβλητή baud είναι 16bit ενώ οι καταχωρητές UBRR0H, UBRR0L είναι 8bit.
Για αυτό το λόγο είμαστε αναγκασμένοι να χρησιμοποιήσουμε δύο εντολές με
ολίσθηση στην πρώτη, ώστε δώσουμε την τιμή της baud στο πεδίο UBRR11:0 (το οποίο είναι 12bit με τα 4 MSB στο χαμηλό μέρος του UBRR0H
και τα 8 LSB στον UBRR0L). Η επόμενη εντολή ενεργοποιεί τη δυνατότητα λήψης
και αποστολής και τη διακοπή που προκαλείται από την ολοκλήρωση λήψης,
τοποθετώντας τα αντίστοιχα bit και
καθαρίζοντας όλα τα άλλα. Στη συνέχεια η εντολή που ακολουθεί είναι σα σχόλιο
αφού δεν είναι απαραίτητη, αλλά δίνεται ως παράδειγμα πρόσβασης στον καταχωρητή
UCSR0C. Η αρχική κατάσταση της σύνδεσης είναι
ασύγχρονη, με 1 stop bit και χωρίς έλεγχο ισοτιμίας (parity control),
ρυθμίσεις που λειτουργούν κανονικά για τις περισσότερες συνδέσεις. Αν για
κάποιο λόγο θέλουμε να διαμορφώσουμε διαφορετικά τα παραπάνω χαρακτηριστικά
πρέπει να τοποθετήσουμε τα αντίστοιχα bit του καταχωρητή.
Η επόμενη ρουτίνα είναι για αποστολή δεδομένων. Με την πρώτη εντολή
ελέγχεται η σημαία UDRE0 στον
καταχωρητή UCSR0A η οποία είναι 0
όταν ο στον UDR0 υπάρχουν δεδομένα προς αποστολή
και 1 όταν ο UDR0
είναι έτοιμος να δεχτεί δεδομένα για αποστολή. Το πρόγραμμα περιμένει σε
αυτή την εντολή όσο η UDRE0 είναι 0 και όταν γίνει 1
περνάει στην επόμενη με την οποία μεταφέρονται τα δεδομένα της μεταβλητής trnsm[5] στον UDR0. Με αυτόν τον τρόπο ξεκινάει αυτόματα η αποστολή των
δεδομένων από τη θύρα[6].
Μετά ακολουθεί η ρουτίνα της λήψης δεδομένων. Κατά τον ίδιο τρόπο,
ελέγχεται πρώτα σημαία RXC0 στον
καταχωρητή UCSR0A η οποία είναι 0
όταν στον UDR0 δεν υπάρχουν δεδομένα προς
ανάγνωση και 1 όταν ο UDR0 έχει δεχτεί
δεδομένα και είναι έτοιμος να διαβαστούν. Το πρόγραμμα περιμένει σε αυτή την
εντολή όσο η RXC0 είναι 0 και όταν γίνει 1
περνάει στην επόμενη με την οποία αποδίδει τα δεδομένα σύμφωνα με την κλήση της
ρουτίνας[7].
Στη συνέχεια, στο μπλοκ κάτω από τον τίτλο του διανύσματος ISR(USART0_RX_vect),
βρίσκεται ο κώδικας που εκτελείται από το διάνυσμα της διακοπής που προκαλείται
από την ολοκλήρωση λήψης. Η μοναδική εντολή που βρίσκεται στο μπλοκ είναι
απόδοση της τιμής που προέρχεται από τη ρουτίνα λήψης στη μεταβλητή rvdata.
Ακολουθεί το κυρίως πρόγραμμα όπου με την πρώτη εντολή ενεργοποιούνται οι διακοπές και στη συνέχεια καλείται η ρουτίνα για το initialization της σειριακής
επικοινωνίας. Στον κλειστό βρόχο του προγράμματος καλούμε τη ρουτίνα αποστολής όποτε
θέλουμε να στείλουμε δεδομένα από τη σειριακή θύρα, όπου μέσα στην παρένθεση,
κατά τη σύνταξη της κλήσης, γράφουμε τα δεδομένα που θέλουμε να αποσταλούν. Αν
υπάρχουν δεδομένα για λήψη, τότε εκτελείται η διακοπή λήψης και τα δεδομέναοδηγούνται στη μεταβλητή rvdata.
Σε περίπτωση που δε θέλουμε να ενεργοποιήσουμε τη διακοπή λήψης δεδομένων
μπορούμε να συντάξουμε μια ρουτίνα λήψης και να την καλούμε όταν θέλουμε να
γίνει λήψη. Σε αυτή την περίπτωση υπάρχει κίνδυνος απώλειας δεδομένων αν η
συσκευή αποστολής στείλει περισσότερα του ενός byte. Στη
συνέχεια παρουσιάζεται ένα παράδειγμα αποστολής δεδομένων, χωρίς τη χρήση
διακοπής, γραμμένο σε γλώσσα προγραμματισμού C:
#include <avr/io.h>
unsigned char rvdata;
void USART_Init(unsigned int baud) //
Initialization routine (USART0, RX->PE0, TX->PE1)
{
UBRR0H = (unsigned char)(baud>>8); // Set baud rate
UBRR0L = (unsigned char)baud;
UCSR0B = (1<<RXEN0)|(1<<TXEN0); // Enable receiver, transmiter
//UCSR0C = (1<<URSEL00)|(1<<USBS0)|(3<<UCSZ00); // Set frame format: 2 stop bit, 8 data,
//
asynchronous operation & no parity
}
void
USART_Receive(void) //
USART Receive routine
{
while (UCSR0A & (1<<RXC0)) // Wait for unread data in buffer
{
rvdata = UDR0; // Send received data to PORTC
}
}
int main(void)
{
/* Your code here */
USART_Init(103); // System Clock 16MHz: 103->9600
while (1)
{
/* Your code here */
USART_Receive();
// Receive data
/* Your code here */
}
}
Η κυριότερη διαφορά είναι στη ρουτίνα λήψης USART_Receive(void) το πρόγραμμα εισέρχεται στην while όταν η RXC0 γίνει 1 και έτσι τα δεδομένα του UDR0 οδηγούνται στη μεταβλητή rvdata. Αφού διαβαστεί ο UDR0 στη συνέχεια καθαρίζει η RXC0 και το πρόγραμμα βγαίνει από
την while.
©2019 Πορλιδάς Δημήτριος
|