Λήψη και αποστολή δεδομένων μέσω USART

 

Συγγραφέας: Πορλιδάς Δημήτριος

Βιογραφικό Σημείωμα

electronics@porlidas.gr

Facebook

Linkedin


 

 

Οι AVR διαθέτουν ενσωματωμένη διάταξη για υποστήριξη σειριακής επικοινωνίας USART[1] (Universal Synchronous and Asynchronous serial Receiver and Transmitter). Η ενσωματωμένη διάταξη μας εξυπηρετεί στην απλοποίηση του κώδικα για την επίτευξη της επικοινωνίας.

Η ασύγχρονη επικοινωνία είναι η περισσότερο χρησιμοποιούμενη γιατί απαιτεί δύο μόνο συνδέσεις, μια για τη λήψη (TX) και μια για την αποστολή (RX) δεδομένων. Η ρύθμιση των χαρακτηριστικών της επικοινωνίας και οι λειτουργίες λήψης και αποστολής γίνονται μέσω έξι καταχωρητών: UCSRA, UCSRB, UCSRC, UBRRH, UBRRL και UDR. Υπάρχει επίσης δυνατότητα ενεργοποίησης εσωτερικής διακοπής κατά την λήψη ή αποστολή δεδομένων ή όταν αδειάζει ο καταχωρητής δεδομένων UDR.

Κατά το initialize ρυθμίζεται το Baud Rate δίνοντας την κατάλληλη τιμή (UBRR) στους καταχωρητές  UBRRH και UBRRL. Η τιμή αυτή εξαρτάται από τη συχνότητα λειτουργίας του ρολογιού συστήματος (fosc) και το επιθυμητό Baud Rate (BAUD) και μπορεί να υπολογιστεί από τη σχέση[2]: UBRR=(fosc/16BAUD)-1

Για κάθε συνδυασμό συχνότητας λειτουργίας – Baud Rate υπάρχει και μια πιθανότητα σφάλματος[3] στη μετάδοση των δεδομένων, εξ αιτίας της διαίρεσης που πρέπει να γίνει στη συχνότητα λειτουργίας, ώστε να γίνει ο χρονισμός της διάταξης για να επιτευχθεί το επιθυμητό Baud Rate. Η πιθανότητα σφάλματος μπορεί να υπολογιστεί από τη σχέση[4]: Error[%]=((BaudRateClosest Match/BaudRate)-1)·100%

Κατά το initialize ενεργοποιούνται επίσης οι διατάξεις λήψης, αποστολής και εσωτερικών διακοπών USART. Η ενεργοποίησή τους γίνεται με τοποθέτηση κατάλληλων bit του UCSRB. Ο καθορισμός του frame format της επικοινωνίας γίνεται με τοποθέτηση κατάλληλων bit του UCSRC. Ο καταχωρητής UCSRA περιέχει τις σημαίες λήψης –  αποστολής δεδομένων, διαθεσιμότητας του UDR και σφαλμάτων και τα bit διπλασιασμού ταχύτητας και ενεργοποίησης πολλαπλών μικροελεγκτών στον ίδιο δίαυλο. Στη συνέχεια παρουσιάζεται ένα παράδειγμα αποστολής και λήψης δεδομένων γραμμένο σε γλώσσα προγραμματισμού C: 

#include <avr/io.h>

#include <avr/interrupt.h>

unsigned char data;

 

void USART_Init(unsigned int baud)           // USART Initialization routine

{

UBRRH = (unsigned char)(baud>>8);         // Set baud rate

UBRRL = (unsigned char)baud;

UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);   // Enable receiv./transm./receiv. complete interrupt

//UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);// Set frame format: 2 stop bit, 8 data, asynchronous

                                          // operation & no parity

}

 

void USART_Transmit(unsigned char trnsm)     // USART Transmit routine

{

while (!( UCSRA & (1<<UDRE)));            // Wait for empty transmit buffer

UDR = trnsm;                              // Put data into buffer, sends

                                          // the data

}

 

unsigned char USART_Receive(void)            // USART Receive routine

{

while (!(UCSRA & (1<<RXC)));              // Wait for unread data in buffer

return UDR;                               // Return data to caller

}

 

ISR(USART_RXC_vect)                          // Receive Complete Interrupt

{

PORTC = USART_Receive();                  // Send received data to PORTC

}

 

int main(void)                               // Main program

{

/* Your code here */

DDRC = 0b11111111;                        // Port C: All outputs

sei();                                    // Enable all Interrupts

USART_Init(34);                           // Clock 8MHz: 34->14400

 

while (1)

{

/* Your code here */

USART_Transmit (data);                 // Transmit variable “data”

/* Your code here */

}

}

Στην αρχή του κώδικα εμφανίζεται η ρουτίνα για το initialization USART_Init(unsigned int baud), όπου με τις δύο πρώτες εντολές ρυθμίζεται το Baud Rate. Η μεταβλητή baud είναι 16bit ενώ οι καταχωρητές UBRRH, UBRRL είναι 8bit. Για αυτό το λόγο είμαστε αναγκασμένοι να χρησιμοποιήσουμε δύο εντολές με ολίσθηση στην πρώτη, ώστε δώσουμε την τιμή της baud στο πεδίο UBRR (το οποίο είναι 12bit με τα 4 MSB στο χαμηλό μέρος του UBRRH και τα 8 LSB στον UBRRL). Η επόμενη εντολή ενεργοποιεί τη δυνατότητα λήψης και αποστολής και τη διακοπή που προκαλείται από την ολοκλήρωση λήψης, τοποθετώντας τα αντίστοιχα bit και καθαρίζοντας όλα τα άλλα. Στη συνέχεια η εντολή που ακολουθεί είναι σα σχόλιο αφού δεν είναι απαραίτητη, αλλά δίνεται ως παράδειγμα πρόσβασης στον καταχωρητή UCSRC. Η αρχική κατάσταση της σύνδεσης είναι ασύγχρονη, με 1 stop bit και χωρίς έλεγχο ισοτιμίας (parity control), ρυθμίσεις που λειτουργούν κανονικά για τις περισσότερες συνδέσεις. Αν για κάποιο λόγο θέλουμε να διαμορφώσουμε διαφορετικά τα παραπάνω χαρακτηριστικά πρέπει να τοποθετήσουμε τα αντίστοιχα bit του καταχωρητή. Η διεύθυνση του UCSRC είναι ίδια με τον UBRRH με μόνη διαφορά ότι για να γράψουμε στον UCSRC πρέπει να τοποθετήσουμε το bit URSEL, διαφορετικά τα δεδομένα θα επικαλύψουν τα δεδομένα του UBRRH. Παρόμοια, για να γράψουμε στον UBRRH πρέπει να καθαρίσουμε το bit δηλαδή τα δεδομένα που θα στείλουμε να σιγουρευτούμε ότι έχουν 0 στο MSB.    

Η επόμενη ρουτίνα είναι για αποστολή δεδομένων. Με την πρώτη εντολή ελέγχεται η σημαία UDRE στον καταχωρητή UCSRA η οποία είναι 0 όταν ο στον UDR υπάρχουν δεδομένα προς αποστολή και 1 όταν ο UDR είναι έτοιμος να δεχτεί δεδομένα για αποστολή. Το πρόγραμμα περιμένει σε αυτή την εντολή όσο η UDRE είναι 0 και όταν γίνει 1 περνάει στην επόμενη με την οποία μεταφέρονται τα δεδομένα της μεταβλητής trnsm[5] στον UDR. Με αυτόν τον τρόπο ξεκινάει αυτόματα η αποστολή των δεδομένων από τη θύρα[6].    

Μετά ακολουθεί η ρουτίνα της λήψης δεδομένων. Κατά τον ίδιο τρόπο, ελέγχεται πρώτα σημαία RXC στον καταχωρητή UCSRA η οποία είναι 0 όταν στον UDR δεν υπάρχουν δεδομένα προς ανάγνωση και 1 όταν ο UDR έχει δεχτεί δεδομένα και είναι έτοιμος να διαβαστούν. Το πρόγραμμα περιμένει σε αυτή την εντολή όσο η RXC είναι 0 και όταν γίνει 1 περνάει στην επόμενη με την οποία αποδίδει τα δεδομένα σύμφωνα με την κλήση της ρουτίνας[7].

Στη συνέχεια, στο μπλοκ κάτω από τον τίτλο του διανύσματος ISR(USART_RXC_vect), βρίσκεται ο κώδικας που εκτελείται από το διάνυσμα της διακοπής που προκαλείται από την ολοκλήρωση λήψης. Η μοναδική εντολή που βρίσκεται στο μπλοκ είναι απόδοση της τιμής που προέρχεται από τη ρουτίνα λήψης στο PORTC.         

Ακολουθεί το κυρίως πρόγραμμα όπου με την πρώτη εντολή ενεργοποιείται το PORTC να λειτουργεί ως έξοδος, στη συνέχεια ενεργοποιούνται οι διακοπές και μετά καλείται η ρουτίνα για το initialization της σειριακής επικοινωνίας. Στον κλειστό βρόχο του προγράμματος καλούμε τη ρουτίνα αποστολής όποτε θέλουμε να στείλουμε δεδομένα από τη σειριακή θύρα, όπου μέσα στην παρένθεση, κατά τη σύνταξη της κλήσης, γράφουμε τα δεδομένα που θέλουμε να αποσταλούν. Αν υπάρχουν δεδομένα για λήψη, τότε εκτελείται η διακοπή λήψης και τα δεδομένα οδηγούνται στο PORTC.     

Σε περίπτωση που δε θέλουμε να ενεργοποιήσουμε τη διακοπή λήψης δεδομένων μπορούμε να συντάξουμε μια ρουτίνα λήψης και να την καλούμε όταν θέλουμε να γίνει λήψη. Σε αυτή την περίπτωση υπάρχει κίνδυνος απώλειας δεδομένων αν η συσκευή αποστολής στείλει περισσότερα του ενός byte. Στη συνέχεια παρουσιάζεται ένα παράδειγμα αποστολής δεδομένων, χωρίς τη χρήση διακοπής, γραμμένο σε γλώσσα προγραμματισμού C: 

#include <avr/io.h>

 

void USART_Init(unsigned int baud)            // USART Initialization routine

{

UBRRH = (unsigned char)(baud>>8);          // Set baud rate

UBRRL = (unsigned char)baud;

UCSRB = (1<<RXEN)|(1<<TXEN);               // Enable receiver, transmitter

//UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0); // Set frame format: 2 stop bit, 8 data, asynchronous

                                           // operation & no parity

}

 

void USART_Receive(void)                      // USART Receive routine

{

while (UCSRA & (1<<RXC))                   // Wait for unread data in buffer

{

PORTC = UDR;                            // Send received data to PORTC

}

}

 

int main(void)

{

/* Your code here */

DDRC = 0b11111111;                         // Port C: All outputs

USART_Init(34);                            // Clock 8MHz: 34->14400

 

while (1)

{

/* Your code here */

USART_Receive();                        // Receive data

/* Your code here */

}

}

Η κυριότερη διαφορά είναι στη ρουτίνα λήψης USART_Receive(void) το πρόγραμμα εισέρχεται στην while όταν η RXC γίνει 1 και έτσι τα δεδομένα του UDR οδηγούνται στο PORTC. Αφού διαβαστεί ο RXC στη συνέχεια καθαρίζει η RXC και το πρόγραμμα βγαίνει από την while.  

 

©2017 Πορλιδάς Δημήτριος

 


[1] Είναι το πρωτόκολλο RS232 που χρησιμοποιείται στις σειριακές θύρες COM με τη διαφορά ότι εκτελείται σε επίπεδο τάσεων TTL.

[2] Στο Data Sheet του μικροελεγκτή υπάρχει πίνακας με τις τιμές UBRR για κάθε συνδυασμό συχνότητας λειτουργίας – Baud Rate.

[3] Η πιθανότητα σφάλματος προϋποθέτει η συχνότητα λειτουργίας να είναι συγκεκριμένη και σταθερή. Όταν γίνεται χρήση του εσωτερικού ταλαντωτή του μικροελεγκτή για ρολόι συστήματος η συχνότητα που επιλέγουμε δεν έχει αυτά τα χαρακτηριστικά, συνεπώς η πιθανότητα σφάλματος είναι απρόβλεπτη και η ποιότητα της επικοινωνίας ενδέχεται να είναι κακή.

[4] Στο Data Sheet του μικροελεγκτή υπάρχει πίνακας με τις τιμές σφάλματος που προκύπτουν για κάθε συνδυασμό συχνότητας λειτουργίας – Baud Rate. 

[5] Η μεταβλητή trnsm δημιουργείται κατά την κλήση της ρουτίνας από το κυρίως πρόγραμμα και παίρνει τα δεδομένα που γράφονται στην παρένθεση όταν γίνεται η κλήση της είτε αυτά είναι τιμή είτε μεταβλητή.  

[6] Η UDRE γίνεται 0 όταν γραφούν καινούρια δεδομένα στον UDR και όταν ολοκληρωθεί η αποστολή τους γίνεται ξανά 1.

[7] Όταν διαβαστούν τα δεδομένα η RXC γίνεται ξανά 0.

 

Σας ευχαριστώ για την υποστήριξή σας ώστε να γίνει η ιστοσελίδα μου καλύτερη.

© 2017 Πορλιδάς Δημήτριος