• 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 29/01/2024

Communiquez en série

Les microcontrôleurs disposent de nombreux moyens pour communiquer avec d’autres systèmes. Des périphériques dédiés aux différents bus industriels sont souvent disponibles. On trouve généralement une liaison série (UART), mais aussi des bus industriels comme l’I2C, le SPI, ou le CAN. D'autres bus plus génériques et communs en informatique sont aussi souvent disponibles tels que l’USB ou l’Ethernet. Nous allons approfondir dans ce chapitre la liaison UART et voir comment on peut la configurer sur un STM32F103. Les bus I2C, SPI et CAN seront simplement esquissés.

Principes de la liaison série (UART)

Une liaison série consiste à envoyer une information bit après bit avec un délai entre chacun. Le bus Universal Asynchronous Receiver Transmitter (UART) est une implémentation de ce principe.

La communication via l’UART se fait par l’envoi d’une trame c’est-à-dire un paquet de bits que l’on ne peut pas subdiviser. Le protocole RS232 définit une trame comme étant constituée de :

  • un bit de démarrage (start bit) dont le niveau logique est zéro,

  • entre 7 et 8 bits de données,

  • éventuellement un bit de parité paire ou impaire (parity),

  • et un ou deux bits de stop avec un niveau logique de zéro (stop bits).

Trame
Une trame

Le bit de parité peut être utilisé pour détecter des erreurs. La parité est générée par l’émetteur et vérifiée par le receveur. Pour une parité paire, le nombre de 1 dans la donnée plus le bit de parité est pair, alors que pour une parité impaire le nombre de 1 de la donnée plus celui de la parité est impaire. Utiliser une parité implique un surcoût temporel à la communication, ainsi, si la probabilité d’apparition d’une erreur est faible et qu’elle n’entraîne pas de conséquence grave pour le système, ne pas utiliser de parité est à privilégier.

Caractéristiques d’une liaison série

Dans une communication UART, les horloges entre le receveur et l'émetteur ne sont pas communes. Ainsi, afin d’identifier les différents bits d’une trame, le récepteur se base sur la durée de ses bits. Cette durée doit donc être fixée et identique pour l’émetteur et le récepteur. La durée d’un bit est le temps entre chaque bit. Ainsi l’émetteur émet un bit, attend la durée d’un bit puis émet un nouveau bit. Le receveur attend jusqu’à ce qu’il détecte un bit de démarrage puis attend la moitié d’une durée d’un bit pour se caler sur la lecture des bits qu’il reçoit.

En général, la caractéristique associée à la durée d’un bit est décrite en terme de baud rate (unité bps), c’est-à-dire le nombre de symboles (ici des bits) par seconde qui est transmis par la communication série. On a donc

$\[\text{baud rate} = \frac {1}{\text{durée d’un bit}}\]$

La bande passante (bandwith) d’une communication quantifie l’information transmise sans prendre en considération les surcoûts induits par les bits de démarrage, de parité et de stop, soit

$\[\text{Bande passante} = \frac{\text{nombre de bits de la donnée par trame}}{\text{nombre de bits total de la trame}} \text{baud rate}\]$

Une autre caractéristique de la communication est de savoir si le système est full duplex, c’est-à-dire si des informations peuvent être transmises dans les deux sens simultanément, ou half duplex, c’est-à-dire si l’information ne peut transiter que dans un sens à un même moment et qu’en cas de collision (les deux systèmes envoient des informations en même temps) un bus doit assurer la retransmission.

Une communication UART est par définition asynchrone, c’est-à-dire que l’émetteur et le récepteur ne partagent pas la même horloge et donc qu’elles peuvent dérivées l’une par rapport à l’autre. Cela ne pose normalement pas de problème si les horloges ne diffèrent pas trop car une synchronisation à lieu à chaque bit de démarrage. Par contre cela limite la bande passante.

Un mode de communication dit synchrone, ou parle alors d’USART pour Universal Synchronous Asynchronous Receiver Transmitter, permet d’avoir des baud rates élevés sans l’apparition de problèmes dus aux dérives d'horloge. Dans ce mode, une horloge commune est partagée par les deux systèmes communicants. Cela permet aussi de réduire les surcoûts dus au bit de démarrage et de stop car les données peuvent être plus longues. Par contre cela nécessite de partager physiquement le signal de l’horloge entre l’émetteur et le récepteur.

Configurer une liaison série sur STM32F103

Le microcontrôleur STM32F103 dispose d’un périphérique USART full-duplex avec la possibilité de sélectionner un baud rate pouvant aller jusqu’à 4.5 Mbps.

Une communication bidirectionnelle via l’USART nécessite au moins deux broches :

  • une broche, notée RX, pour recevoir les bits,

  • une broche, notée TX, pour transmettre les bits. Quand l’émetteur est désactivé, la broche prend l’état imposée par la configuration du port, alors que si l’émetteur est activé et que rien n’est à transmettre, la broche TX est dans l’état haut.

Par la suite, vous allez configurer un périphérique USART en émetteur avec 1 bit de démarrage, 8 bits de données et 1 bit de stop (pas de bit de parité) avec un baud rate de 9600 bps. Pour cela la documentation indique page 797 qu’il faut :

  • activer l’USART en mettant à 1 le bit UE du registre CR1,

  • choisir la taille des données (8 ou 9 bits) à l’aide du bit M du registre CR1,

  • indiquer le nombre de bits stop dans le champ de bits STOP du registre CR2,

  • sélectionner le baud rate à l’aide du registre BRR,

  • mettre le bit TE de CR1 à 1 pour envoyer la première trame d’attente.

Pour régler le baud rate, la documentation page 803 montre qu’il faut diviser la fréquence d’entrée du périphérique, 72 MHz dans notre cas, par une valeur représentée par un nombre à virgule fixe. Le tableau 192 page 804 donne comme diviseur la valeur 468,75 pour obtenir un baud rate de 9600bps. Cette valeur doit être saisie dans le registre BRR avec les 4 premier bits utilisé pour représenter la partie fractionnaire et les 12 suivants la partie entière.

Tout cela peut donc se traduire par la fonction :

void configure_usart1_9600bps(){
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // validation horloge USART1
    USART1->CR1 |= USART_CR1_UE; // Activation de l'USART
    USART1->CR1 &= ~USART_CR1_M; // Choix d'une taille de 8 bits de données
    USART1->CR2 &= USART_CR2_STOP; // Choix d'un seul bit de stop
    USART1->BRR |= 468 << 4; // Fixe le baud rate à 9600bps
    USART1->BRR |= 75; // Fixe le baud rate à 9600bps
    USART1->CR1 |= USART_CR1_TE; // Envoi de la première trame d'attente
}

Pour envoyer une donnée, il faut ensuite :

  • écrire la donnée dans le registre DR,

  • attendre que le bit TC du registre SR passe à 1 ce qui indique que la dernière trame a été transmise.

Cela s’écrit

void send(char data){
    USART1->DR |= data; // Ecriture de la donnée dans le registre DR
    while(!(USART1->SR & USART_SR_TC)) {} // Attente de la fin de transmission
}

Si vous voulez transmettre des données, il ne faut pas non plus oublier de configurer la broche connectée à l’USART, ici la broche PA.9 en mode alternate output push-pull. Pour cela réutilisez la fonction écrite dans le chapitre sur le PWM, mais en la rendant plus générique afin de configurer n’importe quelle broche de n’importe quel port en alternate output push-pull, soit :

int configure_gpio_alternate_push_pull(GPIO_typdef *gpio, int pin){
    If (gpio == GPIOA) {
        RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;	
    } else if (gpio == GPIOB){
        RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    } else if (gpio == GPIOC){
        RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    } else {
	    return -1;
    }
    if (pin < 8){
        gpio->CRL &= ~((0xF << 4*pin)| (0xF << 4*pin));
        gpio ->CRL |= (0xA << 4*pin);
    } else if (pin < 15){
        pin = pin – 8;
        gpio ->CRH &= ~((0xF << 4*pin) | (0xF << 4*pin));
        gpio ->CRH |= (0xA << 4*pin);
    } else {
        return -1;
    }
    return 0;
}

Maintenant si vous voulez transmettre les lettres de l’alphabet, il suffit d’écrire le main suivant

#include “stm32f10x.h”
int main (void)
{
	char data = ‘A’;
	int cmpt = 0;
	configure_gpio_alternate_push_pull (GPIOA, 9);
	configure_usart1_9600bps ();
	while (cmpt < 26)	{
	    send(data+cmpt);
	    cmpt ++;
	}
	while (1);
	return 0;
}

Vous pouvez observer en simulation le résultat dans la fenêtre UART#1.

Autres bus de communication

Sans entrer dans les détails des autres bus de communication et des moyens pour les utiliser, regardons quand même les grands principes qui régissent l'I2C, le SPI et le CAN.

Le bus SPI

Le bus SPI (Synchronous Peripheral Interface) est un bus série assurant une communication en full-duplex synchrone. Il est multipoint avec un maître communiquant avec plusieurs esclaves. La communication est réalisée entre le maître et un esclave, le choix de l'esclave se faisant par une sélection directe (donc sans adressage). La communication est unidirectionnelle dans le sens où c'est toujours le maître qui initie la communication.

La ligne entre le maître et les esclaves est constituée de quatre fils :

  • SCLK (Serial Clock): l'horloge du bus qui est produit par le maître,

  • MOSI (Master Out Slave In) : la ligne de donnée du maître vers l'esclave,

  • MISO (Master In Slave Out) : la ligne de donnée de l'esclave vers le maître,

  • SSn (Slave Select n) : la ligne permettant de sélectionner l'esclave destinataire de la communication.

Le principe de communication du SPI repose sur des registres à décalage présents côté maître et côté esclave. La taille du registre peut être configurée (dans le cas du STM32 par exemple ce registre peut-être de 8 ou 16 bits). À chaque période d'horloge, un bit est transféré du maître vers l'esclave et réciproquement de l'esclave vers le maître. Dans le cas d'un registre de 16 bits, il faut donc 16 périodes d'horloge pour effectuer le transfert des données.

Transfert de bits
Transfert de bits 

La synchronisation se faisant sur l'horloge, la communication est initiée par les front de l'horloge. Quatre modes de transmission existent en fonction de l'état au repos de l'horloge et du front de l'horloge sur lequel se fait la transmission. Pour qu'une communication fonctionne, il faut que le maître et l'esclave partage le même mode.

Bien qu'ayant des débits élevés, ce bus présente l'inconvénient de ne fonctionner que sur des faibles distances (en général il est utilisé pour de la communication entre des éléments sur la même carte) et qu'il n'y pas d'acquittement, donc le maître ne sait pas s'il est écouté.

Au niveau d'un STM32F103, un périphérique est dédié au SPI et est facilement configurable à l'aide de quelques registres. La gestion et la lecture des données est ensuite à gérer au niveau de l'applicatif, par exemple en les traitant dans l'interruption de fin de transmission ou à l'aide de la DMA pour des fréquences élevées.

Le bus I2C

Le bus I2C (Inter-Intergrated Circuit) est un bus série synchrone half-duplex. Il est multi-maître et multi-esclaves et n'utilise que deux lignes (et une masse commune) :

  • SCL (Serial Clock) : l'horloge commune à tous les équipements,

  • SDA (Serial Data) : la ligne portant les données.

Les lignes étant au niveau logique haut quand il n'y a pas de communication, les broches connectées aux lignes DCL et SDA doivent être en open-drain.

Lorsqu'un maître émet sur la ligne SDA, la trame qu'il envoie est décomposée en un bit de démarrage, suivi de l'adresse de l'esclave (7 ou 10 bits) puis d'un bit indiquant si le maître souhaite envoyer une donnée ou en recevoir de l'esclave. À chaque envoi d'un octet de donnée (maître ou esclave) un bit d'acquittement est produit par le récepteur. La communication se termine par un bit de stop.

Bus I2C
Bus I2C

Dans le cas où il y a plusieurs maîtres, un mécanisme d'arbitrage est ajouté. Ainsi lorsqu'un maître commence à transmettre un message, les autres maîtres ne peuvent pas émettre tant que le bit stop n'est pas arrivé. Les maîtres monitorent aussi ce qu'ils émettent afin d'éviter le cas où deux maîtres accèdent simultanément au bus. Ainsi si un maître constate que le niveau est bas alors qu'il a envoyé un bit haut, c'est qu'un autre maître émet. Le maître constatant ce cas interrompt alors immédiatement sa transmission.

Le STM32 dispose d'un périphérique I2C et peut être configuré comme maître et esclave. Le fonctionnement du bus est entièrement pris en considération par le circuit, et ST fournit un ensemble de librairies permettant de facilement le configurer.

Le bus CAN

Le bus CAN (Controller Area Network) est un bus série half-duplex couramment utilisé dans l'industrie automobile et avionique. La transmission suit le principe de transmission en paire différentielle et possèdent donc deux lignes CAN L (CAN LOW) et CAN H (CAN HIGH). Tous les équipements, appelés nœuds, souhaitant communiquer via le bus sont connectés et peuvent échanger des informations.

Lors de la communication, un nœud envoi son message à l'ensemble des autres nœuds. Ces derniers filtrent ensuite le message et le traitent ou non. Lors d'un envoi de message, les nœuds pouvant demander l'accès simultané au bus, un principe d'arbitrage basé sur la priorité est mis en place. Ainsi, chaque trame transmise porte un champ d'arbitrage de 12 ou 30 bits. Remarquez que ce champ doit être unique pour chaque message du réseau. Si plusieurs nœuds tentent de communiquer en même temps, le message ayant la plus petite valeur est le plus prioritaire. Une fois qu'un message est envoyé plus aucun nœud ne peut transmettre de donnée avant la fin du message. Si un nœud perd l'arbitrage, son message est de nouveau envoyé dès que le bus est de nouveau disponible.

La taille des données d'un message varie de 0 à 64 bits. À cela s'ajoute dans une trame, des bits CRC pour vérifier l'intégrité des données transmises ainsi que d'autres bits pour indiquer la taille de la donnée, pour valider la transmission et pour indiquer la fin.

De nombreux autres mécanismes sont aussi utilisés afin d'assurer la fiabilité du bus et de maîtriser les cas d'erreur. Tous ces mécanismes assurent une grande disponibilité du bus et il peut ainsi être utilisé pour des applications industrielles ayant besoin d'être sûr, ce qui est par exemple le cas pour des applications dans le domaine de l'automobile ou de l'avionique.

À la fin de ce chapitre, vous êtes capable :

  • de décrire les principes d'une liaison UART,

  • de configurer un UART sur un STM32,

  • d'expliquer les principes de fonctionnements des bus SPI, I2C et CAN.

Exemple de certificat de réussite
Exemple de certificat de réussite