de legscar le 11 Juil 2008 11:26
Présentation
Que peut on trouver de mieux pour relier un microcontrôleur aux PC qu'une liaison RS232 ? En effet, cette liaison, bien que plus très récente, offre l'avantage d'etre facile et rapide à mettre en oeuvre par rapport à une liaison plus moderne comme par exemple l'USB.
Un UART (Universal Asynchronous Receiver Transmitter) ou son évolution l'USART (Universal Synchronous Asynchronous Receiver Transmitter) est un composant courant qui de plus est intégré au sein de la plupart des microcontrôleur AVR.
Il permet de gérer facilement la communication sur une ligne RS232.
Dans la suite, je parlerai d'USART car j'utilise un ATMEGA16 dans mes projets et celui-ci dispose d'un USART. L'aspect synchrone du composant n'étant pas utilisé, le fonctionnement est identique à celui d'un UART.
Je vais donc vous présenter dans cette partie le fonctionnement de ce composant, et illustrer mes propos par quelques fonctions que j'ai développé en langage C et permettant d'utiliser l'USART du microcontrôleur.
Je ne vous présenterai ici que le fonctionnement par interruption, plus propre que le fonctionnement par attente. De plus, pour être encore plus rigoureux, l'emission et la réception se font via deux buffers circulaires, un pour l'émission et un pour la réception. Jetez donc un coup d'oeil sur la gestion d'un buffer circulaire.
Fonctionnement de l'USART
Emission d'un caractère
L'émission d'un caractère suit les étapes suivantes (L'initialisation aillant été faite auparavant) :
Un caractère est placé dans le registre de données UDR.
Dès qu'un caractère est placé dans ce registre, l'USART déplace le caractère dans le registre à décalage et commence l'emission.
Le registre UDR étant vide, le flag UDRE (UDR Empty) du registre UCSRA est placé à 1 et une interruption est générée. C'est cette interruption qui est utilisée pour envoyer les caractères les uns après les autres. Il en existe une autre mais que je n'expose pas ici.
L'UDR étant vide, on peut y placer un nouveau caractère à émettre.
Dès la fin de l'émission du caractère précedent, le cycle recommence.
Réception d'un caractère
La réception d'un caractère est trivial : Lorsqu'un caractère est reçu, une interruption est génerée.
USART_Init
La fonction USART_Init initialise le module USART du microcontrôleur. Pour le moment, les paramêtres sont inscrit dans le code de la fonction mais je vais prendre le temps de modifier ceci afin qu'ils puissent être passés en tant que paramêtres de la fonction.
Néanmoins, le fait de placer les paramètres dans le code permet de réduire la taille de celui-ci.
Le registe UCSRA renseigne principalement sur l'état de l'USART (UDRE par exemple). Le registre UCSRB quant à lui permet de le controler (TXEN et RXEN qui active l'emission et la réception).
En plus de ces deux registres viennent s'ajouter les deux registres UBRRH et UBRRL qui reçoivent le nombre diviseur de la fréquence d'horloge afin de définir la vitesse de transmission.
void USART_Init(void)
{
/* Wait latest receive character was pull from UDR */
while(UCSRA & (1<<RXC));
/* Wait end of transmission */
while(!(UCSRA & (1<<UDRE)));
/* Disable transmitter and receiver */
UCSRB &= ~((1<<RXEN)|(1<<TXEN));
/* Init buffers */
Buffer_Reset(&in_buf);
Buffer_Reset(&out_buf);
/* Set baud rate */
UBRRH = (0<<URSEL)|(unsigned char)(BAUDRATE>>8);
UBRRL = (unsigned char)BAUDRATE;
/* Async mode, parity even, 1 stop bit, 8 data bits, * UCPOL set to zero for async transfert mode */
UCSRC = (1<<URSEL)|ASYNCHRONOUS|PARITY_EVEN|STOP_BIT_1|CHAR_SIZE_8;
/* Enable transmitter and receiver (RXEN and TXEN), * enable reception interrupt (RXCIE) */
UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
}
USART_Send
La fonction attend d'abord la fin d'une éventuelle transmission de données avant d'en commencer une autre. Ceci étant car si une intérruption se produit lorsque on est en train de modifier le buffer, les données de celui-ci serait corrompues.
Ensuite, on copie les données à emettre dans le buffer de sortie (ou d'émission) et on active l'interruption.
Remarque : La fonction ne teste pas si le buffer est plein pendant le remplissage de celui-ci. Veillez donc à ne pas émettre des données plus longue que la taille du buffer (la taille de celui-ci étant modifiable par un #define dans usart.h).
void USART_Send(char *p_data, unsigned short length)
{
/* Wait end of transmission */
while(!(UCSRA & (1<<UDRE)));
/* Copy data into the buffer */
for(u16_t i=0; i<length; i++)
Buffer_Push(&out_buf, *(p_data+i));
/* Enable UDR Empty interrupt */
UCSRB |= (1<<UDRIE);
}
SIGNAL(SIG_UART_DATA)
Lorsque le registe UDR de l'USART est vide, il génere cette interruption (pardonnez moi l'abus de langage, c'est plus simple :-p). C'est cette interruption qui est utilisée pour alimenter en continu le flot de caractères.
Un caractère est extrait du buffer de sortie et est placé dans le registre UDR si il est différent de -1. -1 indique que le buffer est vide et l'opération consiste donc à désactiver l'intérruption car sinon, l'interruption se repetera indefiniment jusq'au nouveau remplissage du registre UDR.
SIGNAL(SIG_UART_DATA)
{
char c = Buffer_Pull(&out_buf);
if(c == -1) // Buffer empty ?
UCSRB &= ~(1<<UDRIE); // Yes, stop UDR empty interrupt
else
UDR = c; // No, put c in UDR
}
SIGNAL(SIG_UART_RECV)
Lorsqu'un caractère est reçu, cette interruption est executée. Si il n'ya pas eu d'erreur de parité ou de format (bit stop ...), le caractère est stocké dans le buffer de réception (in_buf). Dans le cas contraire, il est tout simplement jeté.
SIGNAL(SIG_UART_RECV)
{
char garbage;
if((UCSRA & (1<<FE))||(UCSRA & (1<<PE))) // Frame or parity error ?
garbage = UDR; // Yes, UDR -> Garbage
else
Buffer_Push(&in_buf, UDR); // No, Char -> Input buffer
}
.