Chapitre 20 — Interfaces numériques simples
Objectif — Finalité du chapitre Comprendre les principes des communications UART, SPI et I²C, puis mettre en œuvre un générateur PWM et un émetteur UART utilisables dans un système FPGA. |
Objectifs pédagogiques
À la fin de ce chapitre, l’étudiant devra être capable de :
- distinguer transmission parallèle et transmission série ;
- calculer un débit binaire et la durée d’une trame ;
- décrire une trame UART 8N1 ;
- concevoir un générateur de baud rate ;
- concevoir un émetteur UART simple ;
- expliquer le principe d’un récepteur UART suréchantillonné ;
- identifier les signaux d’un bus SPI et ses quatre modes ;
- décrire un échange SPI à l’aide d’un registre à décalage ;
- expliquer les conditions START, STOP et ACK d’I²C ;
- comprendre le fonctionnement électrique en drain ouvert ;
- générer un signal PWM paramétrable ;
- piloter la luminosité d’une LED ou la puissance moyenne d’un actionneur.
Prérequis
- machines à états finis ;
- compteurs et générateurs de tick ;
- registres à décalage ;
- entrées asynchrones et synchronisation ;
- contraintes d’E/S et d’horloge ;
- bancs de test automatisés.
Organisation du chapitre
Section | Contenu | Compétence principale |
|---|---|---|
| 20.1 | Communication série | Lire une trame et calculer un débit |
| 20.2 | UART | Émettre et recevoir des octets |
| 20.3 | SPI | Échanger des mots synchrones |
| 20.4 | I²C | Comprendre un bus adressé ouvert |
| 20.5 | PWM | Commander une puissance moyenne |
| TP 16 | Interfaces et communications | Implanter PWM, UART et SPI |
Concept clé — Logique et couche électrique Une description VHDL définit la logique numérique. Le fonctionnement réel dépend aussi des niveaux électriques, du câblage, des résistances, des convertisseurs USB-série et des contraintes de broches. |
20.1 Communication série
Une interface numérique transmet des données entre blocs ou circuits. La communication peut être parallèle ou série, synchrone ou asynchrone.
20.1.1 Transmission parallèle
Plusieurs bits sont transmis simultanément sur plusieurs conducteurs.
Avantages | Limites |
|---|---|
| Mot complet en un transfert | Nombre élevé de broches |
| Logique simple à courte distance | Skew entre les lignes |
| Débit élevé à faible fréquence | Câblage plus complexe |
| Adaptée aux bus internes | Moins adaptée aux longues distances |
20.1.2 Transmission série
Les bits sont transmis successivement sur un nombre réduit de lignes.
Avantages | Limites |
|---|---|
| Peu de conducteurs | Sérialisation et désérialisation |
| Câblage simple | Latence d’une trame |
| Adaptée aux périphériques | Synchronisation nécessaire |
| Possibilité de longues distances selon le standard | Débit utile inférieur au débit brut |
20.1.3 Débit binaire
Le débit binaire est le nombre de bits transmis par seconde. En UART, on parle souvent de baud rate ; pour une modulation binaire simple, un symbole représente un bit.
Débit | Durée approximative d’un bit |
|---|---|
| 9 600 bit/s | 104,167 µs |
| 57 600 bit/s | 17,361 µs |
| 115 200 bit/s | 8,681 µs |
| 1 Mbit/s | 1 µs |
20.1.4 Débit brut et débit utile
Les bits de contrôle réduisent la part consacrée aux données utiles.
| Rendement d’une trame UART 8N1 | Calcul |
| 8 bits de données + 1 bit de départ + 1 bit d'arrêt = 10 bits transmis RENDEMENT = 8 / 10 = 80 % |
20.1.5 Trame de données
Une trame regroupe des bits selon un ordre défini : synchronisation, données, contrôle d’erreur et fin.
Élément | Rôle |
|---|---|
| Préambule ou START | Repérer le début |
| Adresse | Sélectionner un destinataire |
| Données | Transporter l’information |
| Parité ou CRC | Détecter certaines erreurs |
| ACK | Confirmer une réception |
| STOP | Marquer la fin |
20.1.6 Ordre des bits
Les protocoles précisent si le bit de poids faible ou fort est transmis en premier. L’UART envoie habituellement le LSB en premier ; SPI peut être configuré.
20.1.7 Synchronisation
Communication synchrone | Communication asynchrone |
|---|---|
| Une horloge est transmise ou partagée | Pas d’horloge transmise continuellement |
| SPI, I²C | UART |
| Échantillonnage défini par SCLK/SCL | Récepteur reconstruit le timing du bit |
| Fréquence contrôlée par le maître | Émetteur et récepteur configurés au même débit |
20.1.8 Duplex
Mode | Description | Exemple |
|---|---|---|
| Simplex | Un seul sens | Capteur émetteur uniquement |
| Half-duplex | Deux sens, pas simultanément | Bus partagé |
| Full-duplex | Deux sens simultanés | SPI avec MOSI/MISO, UART TX/RX |
20.1.9 Erreurs de transmission
- mauvais débit ;
- mauvaise polarité ;
- ordre des bits incorrect ;
- échantillonnage sur le mauvais front ;
- niveau électrique incompatible ;
- bruit et intégrité du signal ;
- perte d’octets faute de buffer.
20.1.10 Adaptation électrique
Sécurité électrique — UART logique ≠ RS-232 Une sortie FPGA LVCMOS ne doit pas être connectée directement à une interface RS-232 classique. Un convertisseur de niveau adapté ou un pont USB-UART compatible avec la tension de la carte est nécessaire. |
20.2 Introduction à l’UART
L’UART, Universal Asynchronous Receiver/Transmitter, transmet des trames sans ligne d’horloge. Les deux extrémités doivent partager les mêmes paramètres.
20.2.1 Ligne au repos et bit de départ
La ligne TX est généralement à 1 au repos. Le bit de départ force la ligne à 0 pendant une durée de bit.
État | TX |
|---|---|
| Repos | 1 |
| START | 0 |
| Données | LSB en premier |
| Parité éventuelle | Selon configuration |
| STOP | 1 |
20.2.2 Bits de données
Les formats usuels comportent 7, 8 ou 9 bits. Le format 8 bits est le plus courant pour les octets.
| Ordre d’un octet xA5 | Chronologie |
| xA5 = 1010 0101 Transmission LSB d'abord : bit0, bit1, ..., bit7 1, 0, 1, 0, 0, 1, 0, 1 |
20.2.3 Bit de parité
Mode | Règle |
|---|---|
| Aucune | Pas de bit de parité |
| Paire | Nombre total de 1 pair |
| Impaire | Nombre total de 1 impair |
| Mark | Bit forcé à 1 |
| Space | Bit forcé à 0 |
La parité détecte certaines erreurs simples, mais ne remplace pas un contrôle plus robuste comme un CRC.
20.2.4 Bit d’arrêt
Un ou plusieurs bits d’arrêt maintiennent la ligne à 1 et terminent la trame. Un récepteur peut signaler une erreur de framing si le niveau attendu n’est pas présent.
20.2.5 Notation 8N1
Symbole | Signification |
|---|---|
| 8 | Huit bits de données |
| N | No parity, aucune parité |
| 1 | Un bit d’arrêt |
20.2.6 Durée d’une trame
| Exemple à 115 200 bit/s | Calcul |
| Trame 8N1 = 10 bits DUREE = 10 / 115200 ≈ 86,81 microsecondes Débit utile maximal théorique : 115200 * 8 / 10 = 92160 bit/s utiles |
20.2.7 Générateur de baud rate
Le générateur produit une impulsion d’un cycle à chaque durée de bit. Pour une division entière : DIVISEUR = F_CLK / BAUD.
| Entité | VHDL |
| entity generateur_baud is generic ( F_CLK_HZ : positive := 50_000_000; BAUD : positive := 115_200 ); port ( clk : in std_logic; reset : in std_logic; tick : out std_logic ); end entity generateur_baud; |
20.2.8 Division entière et erreur
Fclk | Baud demandé | Diviseur arrondi | Baud obtenu |
|---|---|---|---|
| 50 MHz | 115 200 | 434 | ≈ 115 207 |
| 100 MHz | 115 200 | 868 | ≈ 115 207 |
| 50 MHz | 9 600 | 5 208 | ≈ 9 600,6 |
L’erreur relative doit rester compatible avec la tolérance globale de l’émetteur et du récepteur.
20.2.9 Architecture du générateur
| Générateur de baud — Initialisation | VHDL |
| architecture rtl of generateur_baud is constant DIVISEUR : positive := (F_CLK_HZ + BAUD/2) / BAUD; signal compteur : natural range 0 to DIVISEUR-1 := 0; begin process(clk) begin if rising_edge(clk) then if reset = '1' then compteur <= 0; tick <= '0'; else tick <= '0'; |
| Générateur de baud — Comptage et tick | VHDL |
| if compteur = DIVISEUR-1 then compteur <= 0; tick <= '1'; else compteur <= compteur + 1; end if; end if; end if; end process; end architecture rtl; |
20.2.10 Générateur par accumulateur de phase
Pour améliorer la moyenne lorsque le rapport n’est pas entier, un accumulateur peut produire des intervalles alternés. Cette méthode introduit un léger jitter déterministe mais une fréquence moyenne plus précise.
| Principe d’accumulateur | Principe |
| accumulateur <= accumulateur + INCREMENT; if retenue = '1' then tick <= '1'; else tick <= '0'; end if; |
20.2.11 Émetteur UART : états
État | Action |
|---|---|
| IDLE | TX=1, attente de start |
| START_BIT | TX=0 pendant un bit |
| DATA_BITS | Envoi de 8 bits LSB d’abord |
| STOP_BIT | TX=1 pendant un bit |
20.2.12 Entité de l’émetteur
| uart_tx.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity uart_tx is port ( clk : in std_logic; reset : in std_logic; baud_tick : in std_logic; start : in std_logic; data_in : in std_logic_vector(7 downto 0); tx : out std_logic; busy : out std_logic; done : out std_logic ); end entity uart_tx; |
20.2.13 Émetteur : déclarations
| uart_tx.vhd — Signaux | VHDL |
| architecture rtl of uart_tx is type etat_t is (IDLE, START_BIT, DATA_BITS, STOP_BIT); signal etat : etat_t := IDLE; signal registre : std_logic_vector(7 downto 0) := (others => '0'); signal index_bit : natural range 0 to 7 := 0; begin |
20.2.14 Émetteur : processus
| uart_tx.vhd — FSM, partie 1 | VHDL |
| process(clk) begin if rising_edge(clk) then if reset = '1' then etat <= IDLE; registre <= (others => '0'); index_bit <= 0; tx <= '1'; busy <= '0'; done <= '0'; else done <= '0'; case etat is when IDLE => tx <= '1'; busy <= '0'; if start = '1' then registre <= data_in; index_bit <= 0; busy <= '1'; etat <= START_BIT; end if; |
| uart_tx.vhd — FSM, partie 2 | VHDL |
| when START_BIT => tx <= '0'; busy <= '1'; if baud_tick = '1' then etat <= DATA_BITS; end if; when DATA_BITS => tx <= registre(index_bit); busy <= '1'; if baud_tick = '1' then if index_bit = 7 then etat <= STOP_BIT; else index_bit <= index_bit + 1; end if; end if; |
| uart_tx.vhd — FSM, partie 3 | VHDL |
| when STOP_BIT => tx <= '1'; busy <= '1'; if baud_tick = '1' then busy <= '0'; done <= '1'; etat <= IDLE; end if; end case; end if; end if; end process; end architecture rtl; |
20.2.15 Chronologie de start
Le signal start doit être une impulsion d’un cycle lorsque busy=0. L’octet est mémorisé immédiatement, puis le bit START est présenté jusqu’au tick de baud suivant.
20.2.16 Récepteur UART : principe
- synchroniser rx dans le domaine clk ;
- détecter le passage de 1 à 0 ;
- attendre le milieu du bit START ;
- échantillonner chaque donnée au centre de sa cellule ;
- reconstruire l’octet LSB en premier ;
- vérifier le bit STOP ;
- produire data_valid ou framing_error.
20.2.17 Suréchantillonnage
Un récepteur utilise souvent 8 ou 16 échantillons par bit. Il repère le centre et peut effectuer un vote majoritaire.
Suréchantillonnage | Avantage |
|---|---|
| ×1 | Logique simple, synchronisation moins robuste |
| ×8 | Centre du bit mieux localisé |
| ×16 | Standard pédagogique fréquent, vote possible |
20.2.18 États d’un récepteur
État | Action |
|---|---|
| IDLE | Attente d’un niveau bas |
| START | Validation au centre du bit |
| DATA | Échantillonnage des huit bits |
| STOP | Contrôle du niveau haut |
| VALID | Impulsion octet reçu |
20.2.19 Erreurs UART
Erreur | Cause |
|---|---|
| Framing error | Bit STOP non haut |
| Parity error | Parité reçue différente |
| Overrun | Nouvel octet avant lecture de l’ancien |
| Baud mismatch | Débits trop différents |
| Glitch START | Impulsion basse trop courte |
20.2.20 Bufferisation
Une FIFO permet de découpler l’UART de la logique consommatrice ou productrice. Sans buffer, start doit respecter busy et les octets reçus doivent être traités rapidement.
20.3 Introduction au protocole SPI
SPI est une interface série synchrone, généralement contrôlée par un maître. Elle n’impose pas un format unique de trame : les paramètres sont définis par les périphériques.
20.3.1 Signaux
Signal | Sens typique | Rôle |
|---|---|---|
| SCLK | Maître → esclaves | Horloge série |
| MOSI | Maître → esclave | Donnée envoyée par le maître |
| MISO | Esclave → maître | Donnée renvoyée |
| CS_n / SS_n | Maître → esclave | Sélection active bas |
20.3.2 Maître et esclave
- le maître génère SCLK ;
- le maître active un CS par périphérique ;
- les deux côtés possèdent souvent un registre à décalage ;
- l’échange est naturellement full-duplex ;
- le sens et la longueur des mots dépendent du composant.
20.3.3 Sélection CS
CS_n est généralement placé à 0 avant les premiers fronts, maintenu pendant l’échange, puis remis à 1.
Protocole — Fiche technique Certains périphériques exigent un temps minimal entre CS et le premier front, une pause entre mots ou un ordre précis des octets. |
20.3.4 Modes SPI
Les modes sont définis par CPOL, niveau de repos de SCLK, et CPHA, choix du premier front d’échantillonnage.
Mode | CPOL | CPHA | SCLK au repos | Échantillonnage principal |
|---|---|---|---|---|
| 0 | 0 | 0 | Bas | Front montant |
| 1 | 0 | 1 | Bas | Front descendant |
| 2 | 1 | 0 | Haut | Front descendant |
| 3 | 1 | 1 | Haut | Front montant |
20.3.5 Changement et échantillonnage
Une convention fréquente consiste à changer la donnée sur un front et à l’échantillonner sur l’autre. Les deux extrémités doivent utiliser le même mode.
20.3.6 Registre à décalage
| Échange MSB en premier | VHDL |
| miso_capture <= registre_rx(6 downto 0) & miso; mosi <= registre_tx(7); registre_tx <= registre_tx(6 downto 0) & '0'; |
20.3.7 Diviseur de SCLK
Un compteur produit des impulsions de demi-période. SCLK est inversée à chaque impulsion pendant la transaction.
| Fréquence SPI | Calcul |
| F_SCLK = F_CLK / (2 * DIVISEUR) Exemple : F_CLK = 50 MHz DIVISEUR = 5 F_SCLK = 5 MHz |
20.3.8 Maître SPI mode 0 : interface
| spi_maitre_mode0.vhd — Entité | VHDL |
| entity spi_maitre_mode0 is generic ( DIVISEUR : positive := 5 ); port ( clk : in std_logic; reset : in std_logic; start : in std_logic; tx_data : in std_logic_vector(7 downto 0); miso : in std_logic; sclk : out std_logic; mosi : out std_logic; cs_n : out std_logic; rx_data : out std_logic_vector(7 downto 0); busy : out std_logic; done : out std_logic ); end entity spi_maitre_mode0; |
20.3.9 Maître SPI : principe de FSM
État | Action |
|---|---|
| IDLE | CS=1, SCLK=0, attente start |
| TRANSFER | CS=0, fronts SCLK et décalages |
| FINISH | CS=1, done=1 |
20.3.10 Algorithme de transfert
| Pseudo-VHDL du mode 0 | Algorithme |
| -- Au démarrage : registre_tx <= tx_data; compteur_bits <= 0; cs_n <= '0'; sclk <= '0'; mosi <= tx_data(7); -- Front montant SCLK : -- capturer MISO. -- Front descendant SCLK : -- décaler TX et présenter le bit suivant. -- Après huit bits : cs_n <= '1'; done <= '1'; |
20.3.11 MISO partagé
Avec plusieurs esclaves, les sorties MISO doivent être en haute impédance lorsqu’ils ne sont pas sélectionnés, ou être multiplexées selon la carte.
20.3.12 SPI sans adressage standard
SPI ne définit pas d’adresse de périphérique sur les données. La sélection est assurée par CS ou par un protocole applicatif propre au composant.
20.3.13 Avantages et limites
Avantages | Limites |
|---|---|
| Débit élevé | Plusieurs fils |
| Full-duplex | Un CS par esclave souvent nécessaire |
| Logique simple | Pas d’ACK standard |
| Modes configurables | Protocole applicatif variable |
20.4 Introduction au protocole I²C
I²C est un bus série synchrone à deux fils permettant de connecter plusieurs contrôleurs et cibles adressées.
20.4.1 Signaux SDA et SCL
Signal | Rôle |
|---|---|
| SDA | Données bidirectionnelles |
| SCL | Horloge du bus |
20.4.2 Principe du bus ouvert
Les participants peuvent tirer la ligne à 0, mais ne doivent pas la forcer à 1. Des résistances de rappel ramènent SDA et SCL au niveau haut lorsque personne ne les force à 0.
| Sortie drain ouvert conceptuelle | VHDL |
| sda <= '0' when sda_drive_low = '1' else 'Z'; scl <= '0' when scl_drive_low = '1' else 'Z'; |
Couche électrique — Résistances de pull-up Sans résistances de rappel adaptées à la tension du bus, les lignes ne remontent pas correctement. Leur valeur dépend de la capacité du bus et de la vitesse. |
20.4.3 Câblage d’E/S
| Séparation entrée/commande | VHDL |
| sda_in <= sda; scl_in <= scl; -- La logique interne ne produit qu'une commande -- 'tirer à zéro' ou 'relâcher'. |
20.4.4 Condition START
START correspond à une transition de SDA de 1 vers 0 pendant que SCL reste à 1.
| Condition logique observée | VHDL |
| start_detecte <= '1' when sda_precedent = '1' and sda_courant = '0' and scl_courant = '1' else '0'; |
20.4.5 Condition STOP
STOP correspond à une transition de SDA de 0 vers 1 pendant que SCL reste à 1.
20.4.6 Données stables
En dehors de START et STOP, SDA doit normalement rester stable lorsque SCL est haut. Les changements de données sont préparés pendant SCL bas.
20.4.7 Adressage
Une transaction classique commence par une adresse sur 7 bits suivie du bit R/W.
Bit R/W | Sens |
|---|---|
| 0 | Écriture du contrôleur vers la cible |
| 1 | Lecture de la cible vers le contrôleur |
20.4.8 Adresse 7 bits et octet transmis
| Construction | VHDL |
| octet_adresse <= adresse_7_bits & rw; |
20.4.9 Acquittement ACK/NACK
Après huit bits, l’émetteur relâche SDA. Le récepteur tire SDA à 0 pendant le neuvième cycle pour ACK. Un niveau haut indique NACK.
SDA au 9e bit | Signification |
|---|---|
| 0 | ACK : octet accepté |
| 1 | NACK : absence d’acquittement |
20.4.10 Transaction d’écriture
1. START.
2. Adresse + bit W=0.
3. ACK de la cible.
4. Octet de registre éventuel.
5. ACK.
6. Un ou plusieurs octets de données.
7. ACK après chaque octet.
8. STOP.
20.4.11 Transaction de lecture
1. START.
2. Adresse + bit R=1, ou séquence préalable d’adresse de registre.
3. ACK de la cible.
4. La cible transmet les données.
5. Le contrôleur envoie ACK pour continuer.
6. Le contrôleur envoie NACK sur le dernier octet.
7. STOP.
20.4.12 Repeated START
Un START répété permet de changer le sens ou de poursuivre une transaction sans libérer le bus.
20.4.13 Clock stretching
Une cible peut maintenir SCL à 0 pour ralentir le contrôleur. Un maître robuste doit lire le niveau réel de SCL avant de considérer le front haut comme atteint.
20.4.14 Arbitrage
Dans un bus multi-contrôleur, un participant qui relâche SDA mais lit 0 perd l’arbitrage. Le drain ouvert permet cette détection sans court-circuit.
20.4.15 FSM d’un contrôleur I²C
État | Action |
|---|---|
| IDLE | Bus relâché |
| START | SDA bas pendant SCL haut |
| SEND_BIT | Présenter les bits |
| SCL_HIGH | Relâcher SCL et attendre son niveau haut |
| ACK | Relâcher SDA et échantillonner |
| STOP | SDA remonte pendant SCL haut |
20.4.16 Vitesse et timing
Les vitesses et contraintes exactes dépendent du mode I²C et de la fiche technique des composants. Le contrôleur doit respecter les temps bas, hauts, setup et hold.
20.4.17 I²C dans un FPGA
- utiliser des buffers E/S bidirectionnels ou une inférence tri-state au niveau supérieur ;
- synchroniser les lectures SDA/SCL si elles sont traitées par clk ;
- filtrer éventuellement de très courtes perturbations ;
- gérer ACK, timeout, stretching et erreurs ;
- tester avec un analyseur logique.
20.5 Modulation PWM
La modulation de largeur d’impulsion produit un signal périodique dont le rapport cyclique représente une valeur de commande.
20.5.1 Rapport cyclique
| Définition | Calcul |
| D = TEMPS_HAUT / PERIODE D = 0 % : toujours bas D = 50 % : moitié haut, moitié bas D = 100 % : toujours haut |
20.5.2 PWM par compteur
Un compteur libre parcourt toute sa plage. La sortie vaut 1 lorsque le compteur est inférieur à la consigne.
| PWM 8 bits | VHDL |
| process(clk) begin if rising_edge(clk) then compteur <= compteur + 1; end if; end process; pwm <= '1' when compteur < unsigned(consigne) else '0'; |
20.5.3 Résolution
Bits | Niveaux | Pas approximatif |
|---|---|---|
| 4 | 16 | 6,25 % |
| 8 | 256 | 0,390625 % |
| 10 | 1 024 | 0,097656 % |
| 12 | 4 096 | 0,024414 % |
20.5.4 Fréquence PWM
| PWM à compteur libre | Calcul |
| F_PWM = F_CLK / 2^N Exemple : F_CLK = 50 MHz N = 8 F_PWM ≈ 195,3125 kHz |
20.5.5 PWM à fréquence configurable
Un prescaler génère un tick PWM, puis le compteur de phase avance à ce rythme.
| Structure | VHDL |
| if rising_edge(clk) then if tick_pwm = '1' then phase <= phase + 1; end if; end if; pwm <= '1' when phase < duty else '0'; |
20.5.6 Gestion de 100 %
Avec une consigne 8 bits de 0 à 255 et la comparaison compteur < duty, la valeur 255 produit 255 cycles hauts sur 256, pas exactement 100 %. Une entrée séparée ou une consigne sur 9 bits peut représenter 256.
| Traitement explicite | VHDL |
| if duty_full = '1' then pwm <= '1'; elsif phase < duty then pwm <= '1'; else pwm <= '0'; end if; |
20.5.7 Générateur générique
| pwm_generique.vhd — Entité | VHDL |
| library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity pwm_generique is generic ( N : positive := 8 ); port ( clk : in std_logic; reset : in std_logic; duty : in unsigned(N-1 downto 0); pwm : out std_logic ); end entity pwm_generique; |
| pwm_generique.vhd — Architecture | VHDL |
| architecture rtl of pwm_generique is signal phase : unsigned(N-1 downto 0) := (others => '0'); begin process(clk) begin if rising_edge(clk) then if reset = '1' then phase <= (others => '0'); else phase <= phase + 1; end if; end if; end process; pwm <= '1' when phase < duty else '0'; end architecture rtl; |
20.5.8 Variation de luminosité d’une LED
L’œil intègre les impulsions rapides. Le rapport cyclique modifie la luminosité moyenne perçue.
Duty approximatif | Luminosité moyenne |
|---|---|
| 0 % | Éteinte |
| 25 % | Faible |
| 50 % | Moyenne |
| 75 % | Forte |
| 100 % | Maximale |
20.5.9 Perception non linéaire
La luminosité perçue n’est pas strictement proportionnelle au rapport cyclique. Une table gamma peut améliorer la progressivité visuelle.
| Correction par table | VHDL |
| duty_corrige <= GAMMA_ROM( to_integer(unsigned(niveau)) ); |
20.5.10 Commande d’un moteur
La PWM peut commander la puissance moyenne d’un moteur via un transistor, un MOSFET ou un pont en H adapté.
Sécurité — Ne pas connecter un moteur au FPGA Une broche FPGA ne doit pas alimenter directement un moteur. Il faut un étage de puissance, une protection contre les surtensions inductives et une alimentation correctement dimensionnée. |
20.5.11 Fréquence pour moteur
Une fréquence trop basse peut produire du bruit audible ou des variations de couple. Une fréquence trop élevée augmente les pertes de commutation. Le choix dépend du moteur et de l’étage de puissance.
20.5.12 PWM centrée
Une PWM à compteur montant-descendant place les fronts plus symétriquement dans la période. Elle est utilisée dans certaines commandes de puissance.
20.5.13 Mise à jour sans glitch
Changer duty au milieu d’une période peut produire une impulsion exceptionnellement longue ou courte. Une solution consiste à enregistrer la nouvelle consigne au début de la période.
| Mise à jour au retour de phase | VHDL |
| if rising_edge(clk) then phase <= phase + 1; if phase = 0 then duty_actif <= duty_demande; end if; end if; pwm <= '1' when phase < duty_actif else '0'; |
Méthode de conception d’une interface
1. Lire la fiche technique du périphérique.
2. Identifier les signaux, leur direction et leur polarité.
3. Définir fréquence, débit et format des trames.
4. Choisir une architecture à FSM.
5. Dimensionner les compteurs et registres.
6. Traiter les entrées asynchrones.
7. Définir busy, done, valid et error.
8. Créer un banc de test reproduisant les timings.
9. Ajouter les contraintes de broches et d’horloge.
10. Valider au moyen d’un analyseur logique ou d’un terminal.
Checklist
- Les niveaux électriques sont-ils compatibles ?
- Le débit réel respecte-t-il la tolérance ?
- L’ordre des bits est-il correct ?
- Le front d’échantillonnage correspond-il au mode SPI ?
- SDA et SCL sont-ils réellement en drain ouvert ?
- Les résistances de pull-up sont-elles présentes ?
- La fréquence PWM et la résolution sont-elles adaptées ?
- Les signaux start/busy/done ont-ils une durée claire ?
- Les sorties sont-elles dans leur état de repos au reset ?
Erreurs fréquentes
Erreur | Conséquence | Correction |
|---|---|---|
| TX actif bas au repos | UART illisible | Repos à 1 |
| Bit UART MSB en premier | Octet incorrect | LSB en premier |
| Baud approximatif non vérifié | Erreurs de trame | Calculer l’erreur |
| Mauvais CPOL/CPHA | Décalage des bits SPI | Configurer le mode |
| I²C forcé à 1 | Conflit électrique | Relâcher en Z |
| Absence de pull-up I²C | Ligne flottante/basse | Résistances adaptées |
| PWM vers moteur sans driver | Risque matériel | Étage de puissance |
Travaux pratiques
TP 16 — Interfaces et communications
1. Objectifs du TP
- réaliser un générateur PWM 8 bits ;
- faire varier la luminosité d’une LED ;
- concevoir un émetteur UART 8N1 ;
- transmettre la valeur d’un compteur vers un ordinateur ;
- observer les trames avec un terminal ou analyseur logique ;
- étudier une interface SPI mode 0 d’introduction.
2. Organisation du projet
| Arborescence proposée | Arborescence |
| tp16_interfaces/ ├── src/ │ ├── pwm_generique.vhd │ ├── generateur_baud.vhd │ ├── uart_tx.vhd │ ├── emetteur_compteur.vhd │ ├── spi_maitre_mode0.vhd │ └── top_fpga.vhd ├── sim/ │ ├── tb_pwm.vhd │ ├── tb_uart_tx.vhd │ └── tb_spi_maitre.vhd └── constraints/ └── carte.xdc_ou_qsf |
3. Partie A — Générateur PWM
Utiliser le composant pwm_generique de la section 20.5 avec N=8.
| Connexion à une LED | VHDL |
| U_PWM : entity work.pwm_generique(rtl) generic map ( N => 8 ) port map ( clk => clk_carte, reset => reset, duty => unsigned(interrupteurs), pwm => led_pwm ); |
4. Banc de test PWM
| tb_pwm.vhd — Mesure du rapport | VHDL |
| -- Avec N=4 pour une simulation courte. duty <= to_unsigned(4, 4); compteur_haut := 0; for i in 1 to 16 loop wait until rising_edge(clk); wait for 1 ns; if pwm = '1' then compteur_haut := compteur_haut + 1; end if; end loop; assert compteur_haut = 4 report "Rapport cyclique incorrect" severity error; |
5. Essais de luminosité
Interrupteurs | Duty | Observation |
|---|---|---|
| 00000000 | 0/256 | LED éteinte |
| 01000000 | 64/256 ≈ 25 % | |
| 10000000 | 128/256 = 50 % | |
| 11000000 | 192/256 = 75 % | |
| 11111111 | 255/256 | Luminosité presque maximale |
6. Partie B — Émetteur UART
Instancier generateur_baud et uart_tx. Choisir 115 200 bit/s ou un débit compatible avec le pont USB-UART de la carte.
7. Transmission d’un compteur
Le compteur binaire est converti en deux caractères hexadécimaux, suivis de CR et LF.
Valeur | Caractères transmis |
|---|---|
| 0 | 00\r\n |
| 10 | 0A\r\n |
| 165 | A5\r\n |
| 255 | FF\r\n |
8. Conversion nibble vers ASCII
| Fonction hexadécimale | VHDL |
| function hex_ascii( nibble : unsigned(3 downto 0) ) return std_logic_vector is begin if nibble < 10 then return std_logic_vector( to_unsigned(48 + to_integer(nibble), 8) ); else return std_logic_vector( to_unsigned(55 + to_integer(nibble), 8) ); end if; end function; |
9. Séquence d’émission
Index | Octet |
|---|---|
| 0 | Demi-octet fort en ASCII |
| 1 | Demi-octet faible en ASCII |
| 2 | CR = x0D |
| 3 | LF = x0A |
10. FSM d’émission du compteur
| emetteur_compteur.vhd — Principe | Architecture |
| case etat is when ATTENTE => if tick_envoi = '1' then valeur_capturee <= compteur; index_octet <= 0; etat <= PREPARER; end if; when PREPARER => -- Sélectionner l'octet selon index_octet. etat <= LANCER; when LANCER => if uart_busy = '0' then uart_start <= '1'; etat <= ATTENDRE_FIN; end if; when ATTENDRE_FIN => uart_start <= '0'; if uart_done = '1' then if index_octet = 3 then etat <= ATTENTE; else index_octet <= index_octet + 1; etat <= PREPARER; end if; end if; end case; |
11. Banc de test UART
| Vérification d’une trame xA5 | VHDL |
| constant OCTET_TEST : std_logic_vector(7 downto 0) := x"A5"; data_in <= OCTET_TEST; start <= '1'; wait until rising_edge(clk); start <= '0'; -- START. wait until baud_tick = '1'; assert tx = '0' severity error; -- Bits LSB en premier : 1,0,1,0,0,1,0,1. for i in 0 to 7 loop wait until baud_tick = '1'; assert tx = OCTET_TEST(i) report "Bit UART incorrect" severity error; end loop; -- STOP. wait until baud_tick = '1'; assert tx = '1' severity error; |
Simulation — Chronologie du banc de test Selon l’instant exact du start et la manière dont le tick est généré, le test doit échantillonner au milieu de chaque cellule ou utiliser un tick de simulation contrôlé. Il faut éviter une vérification ambiguë sur le même front que la mise à jour de TX. |
12. Connexion au PC
1. Identifier la broche TX du FPGA et la tension logique.
2. Utiliser un pont USB-UART compatible.
3. Relier les masses.
4. Relier TX FPGA à RX du pont.
5. Configurer le terminal : débit choisi, 8N1, pas de contrôle de flux.
6. Programmer la carte.
7. Observer les lignes hexadécimales.
13. Contraintes conceptuelles
| Exemple XDC | Contraintes |
| set_property PACKAGE_PIN <PIN_TX> [get_ports uart_tx] set_property IOSTANDARD LVCMOS33 [get_ports uart_tx] set_property PACKAGE_PIN <PIN_LED> [get_ports led_pwm] set_property IOSTANDARD LVCMOS33 [get_ports led_pwm] create_clock -period 20.000 -name clk_carte [get_ports clk_carte] |
14. Partie C — Introduction SPI
Réaliser une transaction mode 0 de huit bits, avec un esclave de test qui renvoie un motif.
| Esclave de test conceptuel | Principe |
| -- À chaque front montant SCLK : registre_rx <= registre_rx(6 downto 0) & mosi; -- MISO présente le bit de poids fort -- du registre de réponse. |
15. Vérifications SPI
- SCLK reste bas au repos ;
- CS_n passe à 0 avant le transfert ;
- MOSI présente le MSB en premier ;
- MISO est échantillonné sur les fronts montants ;
- huit bits sont transférés ;
- CS_n revient à 1 ;
- done dure un cycle.
16. Commandes GHDL
| Compilation | Terminal |
| ghdl -a --std=08 src/pwm_generique.vhd ghdl -a --std=08 src/generateur_baud.vhd ghdl -a --std=08 src/uart_tx.vhd ghdl -a --std=08 src/emetteur_compteur.vhd ghdl -a --std=08 src/spi_maitre_mode0.vhd |
| Simulation UART | Terminal |
| ghdl -a --std=08 sim/tb_uart_tx.vhd ghdl -e --std=08 tb_uart_tx ghdl -r --std=08 tb_uart_tx --vcd=tb_uart_tx.vcd gtkwave tb_uart_tx.vcd |
17. Signaux à observer
Bloc | Signaux |
|---|---|
| PWM | phase, duty, pwm |
| UART | baud_tick, start, tx, busy, done, registre, index_bit |
| SPI | cs_n, sclk, mosi, miso, compteur_bits, busy, done |
18. Tableau de résultats
Test | Attendu | Observé | Conforme |
|---|---|---|---|
| PWM duty=25 % | 4 cycles hauts sur 16 en simulation | ||
| PWM LED | Luminosité variable | ||
| UART xA5 | START + 8 bits + STOP | ||
| UART vers PC | Valeur hexadécimale lisible | ||
| SPI mode 0 | 8 bits échangés | ||
| Rapport de synthèse | Compteurs, registres et FSM attendus |
19. Questions d’analyse
1. Quelle différence existe entre communication parallèle et série ?
2. Pourquoi une trame UART 8N1 contient-elle dix bits ?
3. Pourquoi l’UART transmet-il le LSB en premier ?
4. Comment calculer le diviseur du générateur de baud ?
5. Quel est le rôle de busy et done dans l’émetteur ?
6. Pourquoi un récepteur UART suréchantillonne-t-il la ligne ?
7. Quelle différence existe entre CPOL et CPHA ?
8. Pourquoi SPI est-il full-duplex ?
9. Pourquoi I²C ne doit-il pas forcer SDA à 1 ?
10. Comment le rapport cyclique agit-il sur une LED ?
20. Extensions proposées
- Ajouter une parité paire à l’UART.
- Concevoir un récepteur UART ×16.
- Ajouter une FIFO TX.
- Transmettre un nombre décimal plutôt qu’hexadécimal.
- Compléter le maître SPI mode 0.
- Ajouter les modes SPI 1, 2 et 3.
- Créer un contrôleur I²C d’écriture d’un octet.
- Ajouter une correction gamma au PWM.
- Commander un servomoteur avec une PWM adaptée.
21. Barème indicatif
Critère | Points |
|---|---|
| Générateur PWM | 3 |
| Commande de luminosité | 2 |
| Générateur de baud | 3 |
| Émetteur UART | 4 |
| Transmission vers le PC | 3 |
| Introduction SPI | 2 |
| Bancs de test et chronogrammes | 2 |
| Qualité du compte rendu | 1 |
| Total | 20 |
Synthèse du chapitre
Notion | Résumé |
|---|---|
| Communication série | Transmission successive des bits. |
| UART | Interface asynchrone à TX/RX. |
| 8N1 | 8 données, aucune parité, 1 stop. |
| Baud tick | Impulsion définissant la durée d’un bit. |
| SPI | Bus synchrone full-duplex avec SCLK et CS. |
| CPOL/CPHA | Polarité et phase de l’horloge SPI. |
| I²C | Bus adressé à SDA/SCL. |
| Drain ouvert | Les participants forcent 0 ou relâchent la ligne. |
| ACK | Acquittement au neuvième bit I²C. |
| PWM | Commande par rapport cyclique. |
| Résolution PWM | Nombre de niveaux de duty. |
| Étage de puissance | Interface obligatoire pour un moteur. |
Autoévaluation
1. Quelle est la ligne UART au repos ?
Réponse : Le niveau haut.
2. Que signifie 8N1 ?
Réponse : 8 bits, sans parité, un stop.
3. Dans quel ordre partent les bits UART ?
Réponse : LSB en premier.
4. Qui produit SCLK en SPI ?
Réponse : Le maître.
5. Que définissent CPOL et CPHA ?
Réponse : Le niveau de repos et les fronts SPI.
6. Combien de fils principaux utilise I²C ?
Réponse : Deux : SDA et SCL.
7. Comment un participant I²C produit-il un 1 ?
Réponse : Il relâche la ligne.
8. Que signifie ACK ?
Réponse : Le récepteur tire SDA à zéro au neuvième bit.
9. Que contrôle le duty d’une PWM ?
Réponse : La proportion de temps au niveau haut.
10. Peut-on connecter un moteur au FPGA ?
Réponse : Non, un driver de puissance est nécessaire.
Exercice de consolidation
Pour une horloge de 100 MHz, calculer le diviseur entier d’un UART à 9 600 bit/s et la fréquence d’une PWM 10 bits sans prescaler.
| Correction proposée | Calcul |
| UART : DIVISEUR ≈ 100000000 / 9600 ≈ 10416,67 Choix arrondi : 10417 Baud obtenu ≈ 100000000 / 10417 ≈ 9599,69 bit/s PWM 10 bits : F_PWM = 100 MHz / 1024 ≈ 97,65625 kHz |
Suite du cours — Transition vers la suite Après les interfaces numériques, le cours peut aborder les bonnes pratiques d’architecture, de réutilisation, de documentation, de vérification et de gestion de projet VHDL. |