/* Bibliothèque de fonctions pour l'utilisation de l'USART Documentation API : voir usart.h Cible : ATMega8 Quartz : 8MHz Compilateur : avr-gcc (WInAVR) */ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #include "usart.h" // descripteur de buffer circulaire pour les communications série typedef struct { volatile unsigned char head ; // pointeur d'écriture volatile unsigned char tail ; // pointeur de lecture volatile unsigned char nbchars ; // nombre de caractères en attente volatile unsigned char size ; // taille du buffer } BUFFER_DESC ; // buffer de réception static char rxbuff[32] ; static volatile BUFFER_DESC rxdesc ; // buffer d'émission static char txbuff[32] ; static volatile BUFFER_DESC txdesc ; /* Interrupt handler USART Rx complete Déclenchée dès qu'un caractère est disponible dans le registre de réception */ SIGNAL (SIG_UART_RECV) { // lecture du caractère reçu char c = UDR ; // si buffer pas plein, on le stocke if (rxdesc.nbchars < rxdesc.size) { // stockage du caractère rxbuff[rxdesc.head] = c ; // avance du pointeur de tête avec gestion du rebouclage if (++rxdesc.head == rxdesc.size) rxdesc.head = 0 ; // actualisation du nombre de caractères en attente rxdesc.nbchars++ ; } } /* Interrupt handler USART Data Register Empty Déclenchée tant que le registre de transmission est vide */ SIGNAL (SIG_UART_DATA) { char c ; // extrait le caractère à envoyer depuis le buffer c = txbuff[txdesc.tail] ; // met à jour le nombre de caractères en attente dans le buffer --txdesc.nbchars ; // s'il n'y en a plus d'autre, on désactive l'interruption if (txdesc.nbchars == 0) UCSRB &= ~(1 << UDRIE) ; // met à jour le pointeur d'extraction, en gérant le rebouclage if (++txdesc.tail == txdesc.size) txdesc.tail = 0 ; // et envoie le caractère UDR = c ; } /* Initialisation de l'USART */ void usart_init(int ubrr) { UBRRH = (unsigned char)(ubrr >> 8) ; UBRRL = (unsigned char)ubrr ; // format de frame : 8bits, pas de parité, 1 stop bit UCSRC = (1 << URSEL) // sélection de UCSRC (cf DS p 149 & 153) | (1 << UCSZ1) | (1 << UCSZ0) // 8-bits ; // enable Rx et Tx UCSRB = (1 << RXEN) // enable receive | (1 << TXEN) // enable transmit | (1 << RXCIE) // enable Receive Complete interrupt ; rxdesc.head = 0 ; rxdesc.tail = 0 ; rxdesc.nbchars = 0 ; rxdesc.size = sizeof(rxbuff) ; txdesc.head = 0 ; txdesc.tail = 0 ; txdesc.nbchars = 0 ; txdesc.size = sizeof(txbuff) ; } /* Lecture d'un caractère sur l'USART */ unsigned char usart_getc(void) { unsigned char c ; // attente de disponibilité d'un caractère while (rxdesc.nbchars == 0) ; // masquage des interruptions pour éviter une altération du buffer pendant // le traitement cli() ; // extraction du caractère c = rxbuff[rxdesc.tail] ; // mise à jour du pointeur d'extraction, avec rebouclage éventuel if (++rxdesc.tail == rxdesc.size) rxdesc.tail = 0 ; // ... et du nombre de caractères en attente rxdesc.nbchars-- ; // rétablissement des interruptions sei() ; return c ; } /* Envoi d'un caractère sur l'USART */ void usart_putc(unsigned char c) { // si le buffer de transmission est plein, on attend qu'il y ait de la place while (txdesc.nbchars == txdesc.size) ; // masquage des interruptions pendant l'accès au buffer, pour être certain // que personne d'autre ne va le modifier cli() ; // ajout du caractère txbuff[txdesc.head] = c ; // mise à jour du pointeur d'injection, avec rebouclage éventuel if (++txdesc.head == txdesc.size) txdesc.head = 0 ; // si c'est le premier caractère dans le buffer, on active l'interruption // Data Register Empty de l'USART pour commencer à "pomper" le buffer via // le handler de l'interruption if (txdesc.nbchars == 0) UCSRB |= (1 << UDRIE) ; // mise à jour du nombre de caractères en attente txdesc.nbchars++ ; // autorisation des interruptions pour continuer le travail sei() ; } /* Envoi d'une chaîne de caractères sur l'USART */ void usart_puts(char *s) { unsigned char c ; while ((c = *s++) != 0) { usart_putc(c) ; } }