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

ATmega2560

 

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

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

electronics@porlidas.gr

Facebook

Linkedin


 

 

Οι 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 Πορλιδάς Δημήτριος

 


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

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

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

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

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

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

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

 

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

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