Outils pour utilisateurs

Outils du site


tdcom1questions

TD Communication dans les systèmes informatiques: Communications séries asynchrones – UART Arduino

1. Etude d’une trame en communication série asynchrone

Question 1.1

Quel est le rôle du bit de parité dans une trame série asynchrone ?

Question 1.2

En considérant une communication 9600bds, 8 bits de données, pas de parité, 1 bit de stop, quelle est la durée du bit de stop ? Quel est l'impact du nombre de bits de stop sur la communication ?

Question 1.3

A débit fixé (par exemple 9600 bds) comment rendre la communication plus rapide ?

Question 1.4

Dans une transmission asynchrone à 9600 bds 8N1, quelle est la durée nécessaire à l’émission d’un caractère?

Question 1.5

Représentez le chronogramme de la transmission (sans attente) de 0x02A01358 en msB first (au format décrit en 1.2).


2. Gestion d’un buffer circulaire

Dans les communications entre systèmes informatiques, on utilise une mémoire tampon pour le stockage des caractères reçus ou en attente d’émission. Cette mémoire tampon permet :

  • de limiter l’utilisation des signaux de protocole pour le contrôle de flux,
  • de construire facilement le programme par couches (par ex. une couche de réception, et une couche d’utilisation, cf. réseaux).

Cette démarche est très adaptée à une gestion par interruption.

Nous proposons dans cet exercice de gérer la mémoire de façon circulaire. Cette gestion se fait aussi bien pour le « remplissage » du tampon que pour le « vidage ».

  • Remplissage du tampon : les données reçues sont écrites dans le tampon tant qu’il y a de la place ; si la fin du tampon est atteinte, on recommence à écrire dès le début.
  • Vidage du tampon : on ne peut venir lire dans le tampon uniquement si des données sont disponibles.

Le tampon circulaire est caractérisé par :

  • un indice de lecture : read_index,
  • un indice d'écriture : write_index,
  • une variable définissant le nombre de données présentes dans le tampon : nb_token_available
  • une variable indiquant la nombre maximum de données stockables dans le tampon: buffer_size

Dans tout l’exercice nous utiliserons la structure suivante :

structfifo.cpp
struct charFifo{
char * buffer ; //la zone mémoire contenant les données du buffer, chaque donnée est un char dans cet exemple
unsigned int buffer_size; //une variable définissant la taille du buffer
unsigned int write_index ; //l'index d'écriture dans le tableau de données
unsigned int read_index ; //l'index de lecture dans le tableau de données
unsigned int nb_token_available ; //le nombre de données disponibles dans le buffer
};

Une variable de type struct charFifo doit être définie pour chaque Fifo manipulée par le programme, par exemple:

structfifo1.cpp
struct charFifo fifo1;

Attention, cette structure ne définit pas le tableau servant à stocker les données dans la fifo mais uniquement la structure permettant de les manipuler. Un tableau de taille FIFOSIZE1 caractères doit être réservé de la manière suivante:

structfifo2.cpp
#define FIFOSIZE1 3
char fifoBuffer1[FIFOSIZE1];

Question 2.0

Revoir l'explication des slides 43 et 44 sur la vidéo https://youtu.be/-El8ZcLixFU?t=2123 et déterminer ce qui change entre l'exemple du cours et cet exercice de TD. Essayer de trouver un intérêt à l'approche du TD par rapport à celle du cours.

Question 2.1

Donner l’algorithme de la procédure d'initialisation du remplissage circulaire : void fifo_init(struct charFifo * ptr_fif, char * ptr_buf, const unsigned int buf_size) .

En entrée :

  • struct charFifo * ptr_fif : pointeur sur la structure de file utilisée
  • char * ptr_buf : adresse de départ du tableau destiné à stocker les données de la fifo
  • const unsigned int buf_size : taille de la fifo

REMARQUE IMPORTANTE: ptr_fif est un pointeur passé en paramètre à la fonction. La fonction peut donc accéder à la zone mémoire pointée par ptr_fif pour en lire ou en modifier le contenu. La zone pointée par ptr_fif est donc en entrée/sortie pour la fonction.

Ici le type de ce pointeur est struct charFifo * . La zone mémoire indiquée par ptr_fif contient donc (au moins) une variable de type structure charFifo. Grâce à cet unique paramètre, il est donc possible de passer à la fonction un ensembles de données associée à une même variable charFifo, et accessible en entrée/sortie. Cette approche est un premier pas vers l'approche de programmation appelée “Orientée Objet” qui vous sera présentée bientôt. Dans le corps de la fonctions, il faudra utiliser 2 opérateurs que vous connaissez pour accéder aux champs de la variable structure:

  • L'opérateur * pour accéder à la variable pointée par le pointeur
  • L'opérateur . pour accéder à chacun des champs de la sturcture

par exemple pour accéder au champ read_index, il faudra faire: (*ptr_fif).read_index

Ceci peut également être fait à l'aide d'un seul opérateur → qui remplit le rôle des 2 opérateurs * et . La ligne précédente peut donc s'écrire ptr_fif→read_index

Pour l'appel de la fonction, il faudra veiller à fournir une valeur effective de paramètre du bon type, donc un pointeur vers une variable charFifo. Pour cela, il faut utiliser l'opérateur & qui permet d'obtenir l'adresse d'une variable.

Question 2.2

Dessiner l’espace mémoire occupé par fifoBuffer1 si le tableau commence à l’adresse 4 et l’état des différents champs de la structure fifo1 après appel de : fifo_init(& fifo1, fifoBuffer1, FIFOSIZE1);

Question 2.3

Donner l’algorithme de la procédure de remplissage circulaire : char fifo_write(struct charFifo * ptr_fif, const char token) permettant de stocker une donnée dans le buffer circulaire. On admettra que si le tampon est plein, les données supplémentaires ne sont pas stockées et la fonction est non bloquante.

En entrée :

  • struct charFifo * ptr_fif: pointeur sur la structure de file utilisée
  • const char token : valeur à écrire dans la file

En sortie :

  • un caractère = 0 : valeur NON écrite dans la file, = 1 : valeur écrite dans la file

Question 2.4

Donner l’algorithme de la procédure de vidage circulaire : char fifo_read(struct charFifo * ptr_fif, char * ptr_token) permettant de lire une donnée dans le buffer circulaire. La fonction est non bloquante.

En entrée :

  • struct charFifo * ptr_fif: pointeur sur la structure de file utilisée
  • char * ptr_token : pointeur vers la variable dans laquelle ranger la donnée lue depuis la file.

En sortie :

  • un caractère = 0 : valeur NON lue depuis la file (aucun caractère n’est disponible), = 1 : valeur lue depuis la file via le paramètre *ptr_c.

Question 2.5

En considérant la variable fifo1 telle qu’initialisée à l’exercice 2.2, compléter les colonnes du tableau après l’exécution de chaque appel de fonction :


3 : Utilisation de la FIFO (buffer circulaire) dans une application

On considère une communication série 9600 bauds, 8 bits, 1 stop, pas de parité entre un ordinateur et une imprimante disposant d’une FIFO (tampon circulaire) de 256 octets. La vitesse d’impression est de 5 ms pour un caractère (la vitesse d’impression correspond à la vitesse de vidage du buffer).

Question 3.1

En admettant que la taille du buffer circulaire de l'imprimante est de 256 octets, au bout de combien de temps le buffer sera-t-il plein si l'imprimante reçoit les caractères à vitesse maximale (sans pause entre les caractères)?

Question 3.2

Même question si il y a 2 ms d'attente entre l'envoi de chaque caractère.

Question 3.3

On cherche à imprimer une page d'un maximum de 3500 caractères sans provoquer de débordement du buffer circulaire. Calculer la taille minimum nécessaire du buffer circulaire si l'imprimante reçoit les caractères à vitesse maximale (sans pause entre les caractères)?


——————————————————————————————————–

4. Révision plateforme Ardiuno UNO

Lire le texte suivant qui est un rappel de ce qui a été présenté oralement pendant la première séance de TP:

Arduino est un plateforme de développement open-source qui se compose d'une carte microcontrôleur et d'un environnement de développement IDE associé. La carte Arduino UNO se compose de :

  • un microcontrôleur 8-bit Atmega328p cadencé à 16Mhz (exécutant une instruction par cycle d'horloge)
  • un convertisseur USB/UART TTL
  • une régulation de tension 5v
  • un connecteur au pas de 2.54mm au standard Arduino

bvdp.inetdoc.net_files_iut_td1_capt_arduinounor3.jpg

L'environnement logiciel est composé d'un éditeur/compilateur (Arduino IDE) et d'un ensemble de librairies pour contrôler les différents périphériques de la carte. De nombreuses librairies sont également proposées par la communauté de développeurs. Le langage de développement est basé sur C++.

Le canevas d'un programme Arduino, se compose de deux fonctions principales à implémenter :

  • void setup() : fonction d'initialisation appelée une fois au démarrage du microcontrôleur
  • void loop() : fonction dont le corps est exécute en boucle (programme principal)

Les librairies exposent le plus souvent des classes permettant de voir les périphériques comme des objets manipulables au travers de leur méthodes. Par exemple l'interaction avec le port série de la carte se fait au travers de la classe Serial (classe statique) qui dispose des fonctions :

  • void begin(int baud, MODE) : permettant d'initialiser le baudrate, la parité, le nombre de bits de stop
  • char read() : permet de lire un octet depuis le port série
  • void write(char c) : permet d'écrire un octet sur le port série
  • int available() : permet de vérifier si un octet est disponible en lecture sur le port
  • d'autres fonctions facilitant l'envoi et la réception de données (println)

L'utilisation des entrées/sorties numériques de la carte se fait au travers des fonctions:

  • void pinMode(int pin, MODE) : configure la pin désignée en IN ou OUT
  • int digitalRead(int pin) : renvoie la valeur binaire présente sur la pin désignée
  • void digitalWrite(int pin, VALUE) : écrit la valeur LOW ou HIGH sur la pin designée

L'utilisation des entrées analogiques de la carte se fait au travers de la fonction:

  • AnalogRead(int pin) : lit la valeur analogique présente sur la pin designée A0-5. La valeur retournée est entre 0-1023, un incrément de 1 correspondant à une valeur de 5/1024v soit 4.9mv.

D'autres classes (SPI, Wire) permettent d'interagir avec les bus SPI, I2C présents sur la carte.


5. Utilisation à bas niveau de la liaison série Hardware sur Arduino

Le processeur de l'arduino dispose de plusieurs périphériques USART intégrés. La librairie Arduino Serial permet d'utiliser un de ces périphériques en tant qu'UART à haut niveau (pas d'interaction avec les registres du processeur) mais il est possible d'utiliser ce périphérique en configurant “à la main” les registres du processeur.

La figure suivante montre la vue interne de l'USART:

Question 5.1

Ouvrir dans un nouvel onglet la documentation du composant ATMEGA328P: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

Lire les pages 1 et 2 et faire le lien avec les informations données lors du premier TP.

Observer le schéma en page 6 et repérer le coeur de processeur ainsi que différents périphériques, notamment l'USART 0 intégré dont la documentation détaillée se trouve en page 143. Lire en détail la partie 19.1

Recopier le schéma de la page 144 et indiquer les éléments que vous reconnaissez. Pour vous aider à comprendre le schéma:

La lettre n est un indice indiquant qu'il y a plusieurs fois le même composant USART dans l'ATMEGA et qu'il faut remplacer n par le numéro de l'USART que l'on souhaite utiliser.

Les blocs UDRn sont en fait un même registre. La donnée écrite par le programme dans le registre se retrouve dans le bloc UDRn (Transmit) pour transmission sur le bus alors qu'une donnée reçue depuis le bus se retrouve dans UDRn (Receive) et peut être lue par le programme. UDRn ne se comporte donc pas comme un registre de mémoire: Une donnée écrite dans UDRn ne peut pas être relue par le programme.

Les blocs UCSRnA, UCSRnB et UCSRnC sont des registres de configurations et de contrôle dont les détails sont donnés à partir de la page 159.

Question 5.2: Configuration du baudrate

Le baudrate est configurable au travers du(es) registre(s) UBRR0 (UBRRn, n=0 pour l'USART 0) (voir tableau de baudrate en page 163 à 165).

La valeur à charger peut être établie par la formule: UBRRn = (fosc/16)/baudrate

1) Pourquoi l'utilisation d'un oscillateur principal à une fréquence fosc=16Mhz ne permet pas toujours d'obtenir un taux d'erreur de 0% ?

2) Proposer une fréquence d'oscillateur permettant un baudrate à 9600bds et un taux d'erreur de 0%

3) Dans le cas de l'utilisation d'un oscillateur à 16Mhz et d'un baudrate à 9600bauds, calculer la longueur de la séquence de bits avant qu'une erreur survienne (on considérera que l’échantillonnage du premier bit (le start) est réalisé au milieu de sa durée).

4) Déterminer la valeur à charger dans UBBRn pour fosc=16Mhz et un baudrate souhaité de 2400Bauds. Cette valeur tient elle sur 8bits?

Question 5.3: Configuration du format de la trame

Le registre USCRnC (USART Status and Control register C) permet de configurer le format de la trame UART (nombre de bits, parité, nombre de bits de stop).

1) Établir la configuration pour une communication 8N1 et proposer une séquence de configuration en langage C (indiquer les valeurs à charger dans les différents registres)

2) Établir la configuration pour une communication 8E2 et proposer une séquence de configuration en langage C (indiquer les valeurs à charger dans les différents registres)

Question 5.4: Envoi et réception de données

L'envoi de données sur la liaison UART se fait par l'écriture dans le registre UDRn. Avant d'effectuer l'envoi d'une donnée, il faut d'abord vérifier que l'UART et disponible pour l'émission (dernière donnée envoyée) à l'aide du registre UCSRnA. Pour recevoir des données, il faut tout d'abord vérifier qu'une donnée est disponible à l'aide du même registre

1) Proposer une séquence en langage C permettant d'émettre un caractère sur l'UART

2) Proposer une séquence en langage C permettant de recevoir un caractère sur l'UART

tdcom1questions.txt · Dernière modification : 2022/01/10 22:42 de bvandepo