Portfolio GEII
Rapport · SAE Asteroids · 4 / 12

Carte son : schéma & génération du son

L'amplificateur LM386 conçu sous KiCad - analyse du besoin, schéma bloc par bloc, rôle de chaque composant - puis la production du signal sonore par le périphérique NCO1.

Conception électronique de la carte son

Le jeu produit des effets sonores (tir, destruction d'astéroïde, etc.) grâce au périphérique NCO1 (Numerically Controlled Oscillator) du PIC16F18877, qui génère un signal sur la broche RA6 (patte 14). Ce signal logique, disponible en sortie du microcontrôleur, n'est toutefois pas exploitable tel quel par un haut-parleur : son courant de sortie est trop faible. Une carte d'amplification dédiée, conçue sous KiCad 9 et réalisée sous la forme d'un circuit imprimé (PCB) double face de 45 mm x 45 mm à composants traversants (THT), a donc été développée pour mettre en forme et amplifier ce signal. Ce chapitre détaille la démarche de conception du schéma électronique, bloc par bloc, et justifie le rôle de chaque composant.

Analyse des besoins audio

Le point de départ de la conception est l'analyse précise du besoin. Le NCO1 est configuré pour produire un signal carré dont la fréquence est pilotée par le programme. Sa source d'horloge est FOSC/4 = 8 MHz et son accumulateur est codé sur 20 bits, de sorte que la fréquence de sortie vaut :

f = (8 000 000 x NCO1INC) / 2^20 = NCO1INC x 7,6294 Hz

Pour une fréquence cible donnée, le code écrit NCO1INC = frequence >> 3, ce qui revient à une division par 8 par décalage de bits (0,125). La fréquence réellement jouée vaut alors environ frequence x (8e6 / (8 x 2^20)) = frequence x 0,9537, soit une valeur très proche de la fréquence demandée. Le décalage >> 3 est en effet une approximation rapide, et peu coûteuse pour un microcontrôleur lent, du facteur exact 0,1311. Le signal obtenu est donc une onde rectangulaire dont les niveaux logiques oscillent entre 0 V et la tension d'alimentation, riche en harmoniques.

Trois besoins fonctionnels se dégagent de cette situation. Premièrement, il faut amplifier ce signal : la sortie d'un port du microcontrôleur ne peut fournir qu'un courant très limité, insuffisant pour faire vibrer la membrane d'un haut-parleur avec un volume audible. Il est donc nécessaire d'intercaler un étage d'amplification de puissance capable de débiter du courant dans une charge de faible impédance. Deuxièmement, l'utilisateur doit pouvoir régler le volume, afin d'adapter le niveau sonore au confort d'écoute. Troisièmement, il faut prévoir une fonction marche/arrêt, permettant de couper l'alimentation de la carte (et donc le son) sans agir sur le reste du système.

À ces besoins s'ajoute une contrainte de simplicité : la carte devant être fabriquée et brasée à la main par le binôme, le circuit doit rester à composants traversants (THT) et reposer sur un nombre réduit de composants externes, pour limiter le risque d'erreur de câblage et faciliter les contrôles.

Choix de l'amplificateur LM386

Le composant central de la carte est l'amplificateur de puissance audio LM386 (référence U1, boîtier DIP-8). Ce choix répond directement aux contraintes énoncées ci-dessus.

Le LM386 est un amplificateur audio basse tension, conçu spécifiquement pour les applications de petite puissance alimentées en faible tension, ce qui correspond bien à un montage embarqué fonctionnant sous une alimentation modeste. Son principal atout pour ce projet est sa simplicité de mise en œuvre : il ne requiert que très peu de composants externes pour fonctionner. Là où un amplificateur construit à partir de transistors discrets ou d'un amplificateur opérationnel générique imposerait un réseau de polarisation et de contre-réaction relativement lourd, le LM386 intègre déjà l'essentiel de ces fonctions dans son boîtier.

Par ailleurs, le LM386 possède un gain fixé à 20 par défaut. Ce gain est obtenu sans aucun composant supplémentaire : il suffit de laisser les broches 1 et 8 (broches de réglage du gain) non pontées. Ce gain de 20 est largement suffisant pour amener le signal du NCO1 à un niveau exploitable par un petit haut-parleur. Le boîtier DIP-8 traversant se brase aisément au fer à souder et autorise, le cas échéant, l'usage d'un support de circuit intégré, ce qui protège le composant pendant le brasage et facilite son remplacement. Le LM386 constitue ainsi un compromis idéal entre performance suffisante, faible encombrement et facilité de réalisation.

Le schéma bloc par bloc

L'architecture de la carte se décompose naturellement en quatre blocs fonctionnels, présentés ci-dessous. Le schéma de principe synthétise leur enchaînement, du signal d'entrée jusqu'au haut-parleur.

Schéma en blocs de la carte son
Figure - Décomposition fonctionnelle de la carte son : alimentation, entrée/volume, amplification et sortie.

Bloc alimentation et découplage

Ce bloc regroupe le connecteur d'alimentation J1 (« Power supply », 2 points), l'interrupteur à glissière SW1 (SPST, marche/arrêt) et les condensateurs de découplage C2 (100 nF) et C3 (10 µF). L'interrupteur SW1 répond directement au besoin de marche/arrêt : placé en série avec l'alimentation, il coupe la tension d'alimentation de l'ensemble de la carte. Les condensateurs C2 et C3 stabilisent cette tension au plus près du circuit intégré, comme on le détaillera dans la justification du rôle de chaque composant.

Bloc entrée et volume

Le signal sonore provenant de RA6 arrive par le connecteur J2 (« Audio_In », 2 points). Il traverse d'abord le condensateur de couplage C1 (1 µF), puis le potentiomètre RV1 (10 kΩ) qui assure le réglage du volume. En agissant sur le curseur du potentiomètre, l'utilisateur prélève une fraction plus ou moins grande du signal d'entrée avant son amplification : c'est ainsi que le volume est réglé, en faisant varier l'amplitude du signal présenté à l'amplificateur. La résistance R1 (1 kΩ) participe à la mise en forme du signal d'entrée et à la polarisation.

Bloc amplification

Le cœur du montage est l'amplificateur U1 (LM386). Le signal utile est appliqué sur l'entrée non inverseuse, broche 3 (+), tandis que l'entrée inverseuse, broche 2 (−), est reliée à la masse. L'alimentation positive V+ est appliquée sur la broche 6, et la masse (GND) sur la broche 4. La broche 7 (BYPASS) est découplée par le condensateur C4 (10 µF) : ce découplage interne contribue à la stabilité et à la propreté du signal amplifié. Comme indiqué précédemment, les broches 1 et 8 ne sont pas pontées, ce qui fixe le gain à sa valeur par défaut de 20.

Bloc sortie

La sortie du LM386 se prend sur la broche 5. On y trouve d'abord un réseau dit de Boucherot (ou réseau de Zobel), constitué de la résistance R2 (100 Ω) en série avec le condensateur C5 (47 nF), placé entre la sortie et la masse ; son rôle est d'assurer la stabilité de l'amplificateur. Le signal amplifié est ensuite transmis au haut-parleur à travers le condensateur de liaison C6 (220 µF), qui débouche sur le connecteur J3 (« Audio_out », 2 points). Les rendus 3D ci-dessous, générés par KiCad à partir du schéma et du routage, donnent un aperçu de l'implantation physique des composants sur le circuit imprimé double face de 45 mm x 45 mm.

Rendu 3D isométrique de la carte son
Figure - Rendu 3D isométrique de la carte son sous KiCad.
Rendu 3D vue de dessus de la carte son
Figure - Rendu 3D de la carte son vue de dessus, montrant l'implantation des composants.

Justification du rôle de chaque composant

Le tableau suivant récapitule la nomenclature (BOM) de la carte son, avec pour chaque composant sa référence, sa valeur et son rôle fonctionnel.

RéférenceValeurRôle
U1LM386 (DIP-8)Amplificateur de puissance audio, gain 20 par défaut
RV110 kΩPotentiomètre de réglage du volume (en entrée)
R11 kΩRésistance d'entrée / polarisation
R2100 ΩRéseau de Boucherot/Zobel (avec C5), stabilité
C11 µFCondensateur de couplage / liaison d'entrée
C2100 nFDécouplage de l'alimentation
C310 µFDécouplage / réservoir d'alimentation
C410 µFDécouplage de la broche BYPASS (broche 7)
C547 nFRéseau de Zobel (avec R2), anti-oscillation
C6220 µFCondensateur de liaison de sortie vers le haut-parleur
SW1SPSTInterrupteur marche/arrêt
J12 pointsConnecteur d'alimentation (Power supply)
J22 pointsEntrée du signal audio (Audio_In, depuis RA6)
J32 pointsSortie vers le haut-parleur (Audio_out)

Trois familles de composants méritent une explication détaillée, car elles illustrent des principes classiques de l'électronique analogique audio.

Les condensateurs de couplage (C1 et C6)

Un condensateur de couplage, aussi appelé condensateur de liaison, a pour fonction de bloquer la composante continue du signal tout en laissant passer la composante variable (alternative) qui porte l'information sonore. En effet, un condensateur se comporte comme un circuit ouvert vis-à-vis du courant continu et comme un passage de plus en plus aisé à mesure que la fréquence augmente. À l'entrée, C1 (1 µF) empêche la tension continue présente sur le signal du NCO1 de perturber le point de fonctionnement de l'amplificateur. À la sortie, C6 (220 µF) isole le haut-parleur de la tension continue présente sur la broche 5 : seul le signal alternatif utile est transmis à la bobine, ce qui évite qu'un courant continu permanent ne traverse le haut-parleur. La valeur élevée de C6 s'explique par la faible impédance d'un haut-parleur : pour laisser passer les fréquences basses sans atténuation, le condensateur de liaison doit présenter une capacité importante.

Les condensateurs de découplage (C2, C3 et C4)

Le découplage consiste à placer des condensateurs entre l'alimentation et la masse, au plus près du circuit intégré, afin de filtrer l'alimentation. Lorsque l'amplificateur débite des pointes de courant pour reproduire le son, il sollicite brutalement la ligne d'alimentation ; sans découplage, ces appels de courant provoqueraient des creux et des oscillations de tension susceptibles de dégrader le signal, voire de rendre l'amplificateur instable. Le condensateur C2 (100 nF) filtre les variations rapides (hautes fréquences), tandis que C3 (10 µF) joue le rôle de petit réservoir d'énergie pour les variations plus lentes. Le condensateur C4 (10 µF) découple spécifiquement la broche 7 (BYPASS) du LM386, ce qui réduit le bruit et améliore la stabilité interne de l'amplificateur.

Le réseau de Boucherot / Zobel (R2 et C5)

Ce réseau série, composé de R2 (100 Ω) et C5 (47 nF), est placé entre la sortie de l'amplificateur (broche 5) et la masse. Sa fonction est d'éviter les oscillations parasites à haute fréquence. La charge présentée par un haut-parleur est en réalité inductive, ce qui peut entraîner un comportement instable de l'étage de sortie. Le réseau de Zobel présente, en haute fréquence, une impédance résistive maîtrisée qui « voit » une charge stable du point de vue de l'amplificateur : il compense ainsi le caractère inductif du haut-parleur et empêche l'apparition d'oscillations indésirables.

Du schéma au circuit fabriqué

Une fois le schéma validé, KiCad permet de réaliser le routage du circuit imprimé puis de générer les fichiers de fabrication, appelés Gerbers : couches de cuivre (F_Cu pour la face avant, B_Cu pour la face arrière), masques de soudure, sérigraphie et fichiers de perçage (PTH pour les trous métallisés, NPTH pour les trous non métallisés). Le PCB nu, de 45 mm x 45 mm, a ensuite été reçu puis garni à la main : les composants traversants ont été insérés et brasés au fer à souder. La phase de vérification a comporté un contrôle visuel des soudures, une recherche systématique de courts-circuits entre pistes voisines et une mesure de continuité afin de s'assurer que chaque liaison prévue au schéma était bien établie. Les photographies montrent la carte son une fois brasée et câblée à l'ensemble du système.

En synthèse, le schéma de la carte son repose sur un découpage clair en quatre blocs et sur un choix de composant central, le LM386, particulièrement adapté à une réalisation embarquée simple. Chaque composant périphérique remplit une fonction précise - couplage, découplage, stabilité, réglage du volume ou commutation - qui contribue à transformer le signal carré brut du NCO1 en un son propre et de niveau réglable, restitué par le haut-parleur.

La génération du son (NCO1)

Le son occupe une place modeste mais essentielle dans le ressenti d'un jeu de type Asteroids : un bruit sec accompagne chaque tir, et une descente sonore grave signale la fin de la partie. Sur le PIC16F18877, cet habillage sonore est produit par un périphérique matériel dédié, le NCO1, sans mobiliser le processeur. Ce choix architectural prolonge le fil rouge du projet : ménager un microcontrôleur lent, dépourvu d'unité de calcul flottant matérielle. Ce chapitre explique d'abord le principe du NCO et la formule qui relie la valeur écrite dans son registre à la fréquence réellement entendue, puis détaille les deux fonctions sonores du jeu, et enfin le cheminement du signal jusqu'au haut-parleur via la carte son.

Le périphérique NCO (Numerically Controlled Oscillator)

Le NCO (oscillateur à commande numérique) est un périphérique du PIC16F18877 capable de générer un signal carré dont la fréquence est déterminée par une simple valeur entière. Son intérêt, pour un microcontrôleur lent et sans matériel de calcul flottant, est qu'il fonctionne de manière totalement autonome : une fois la valeur écrite, le matériel produit le signal sans aucune intervention du processeur. La sortie du NCO1 est routée sur la broche RA6 (patte 14), qui rejoint ensuite l'entrée de la carte son.

Le principe interne repose sur un accumulateur de phase. À chaque coup d'horloge, le NCO ajoute une valeur d'incrément, stockée dans le registre NCO1INC, à un accumulateur de 20 bits. Lorsque cet accumulateur déborde (c'est-à-dire passe au-delà de 2^20), la sortie change d'état. Plus l'incrément est grand, plus l'accumulateur déborde souvent, et plus la fréquence du signal carré est élevée. La fréquence de sortie est donc directement proportionnelle à la valeur NCO1INC : il suffit d'écrire un entier pour choisir une note. Cette configuration des périphériques (source d'horloge, mode du NCO, affectation de la sortie sur RA6) est mise en place à l'initialisation par le code généré par MCC (MPLAB Code Configurator), via la fonction SYSTEM_Initialize.

Ménager un microcontrôleur lent

La génération du signal sonore est entièrement déportée sur le matériel ; le programme se contente d'écrire une valeur, ce qui représente une seule opération. Le processeur reste donc libre de gérer la logique du jeu pendant que le son est produit.

La formule exacte et l'approximation par décalage de bits

La source d'horloge du NCO1 est fixée à FOSC/4, soit 8 MHz (8 000 000 Hz), puisque FOSC vaut 32 MHz (oscillateur interne HFINTOSC). L'accumulateur étant codé sur 20 bits, le facteur de division est 2^20 = 1 048 576. La fréquence de sortie obéit donc à la relation suivante :

f = (8 000 000 x NCO1INC) / 2^20

Le rapport 8 000 000 / 2^20 vaut environ 7,6294. La formule se résume alors à :

f = NCO1INC x 7,6294 Hz

Autrement dit, pour obtenir une fréquence donnée, il faudrait théoriquement écrire NCO1INC = f / 7,6294, c'est-à-dire NCO1INC = f x (2^20 / 8 000 000) = f x 0,1311. Or une multiplication par un facteur fractionnaire impose un calcul flottant, lent et coûteux sur ce PIC. Le code contourne ce problème par une astuce : il remplace la multiplication par 0,1311 par un simple décalage de bits vers la droite de 3 positions, ce qui équivaut à une division par 8 (soit une multiplication par 0,125).

NCO1INC = frequence >> 3;

Un décalage de bits est l'une des opérations les plus rapides du processeur : elle se fait en quelques cycles seulement, sur des entiers, sans aucun recours au flottant. Vérifions maintenant l'erreur introduite. La valeur écrite est NCO1INC = frequence / 8 (division entière). La fréquence réellement produite vaut donc :

f_reelle = (frequence / 8) x 7,6294 = frequence x (7,6294 / 8) = frequence x 0,9537

On constate que le ton entendu vaut environ frequence x 0,9537, c'est-à-dire seulement 4,6 % en dessous de la valeur visée. Pour un effet sonore de jeu, cet écart est totalement inaudible : on ne cherche pas une note juste, mais un bruitage. Le facteur exact aurait été 0,1311 ; le décalage >> 3 (égal à 0,125) en est une approximation rapide et largement suffisante. À cet écart de proportionnalité s'ajoute une légère erreur d'arrondi due à la division entière par 8 (les bits perdus ne sont pas reportés), mais elle reste de l'ordre de quelques hertz et ne s'entend pas davantage. Le gain est net : un décalage de bits sur entier au lieu d'une multiplication flottante, en parfaite cohérence avec la philosophie d'économie de ressources du projet.

La fonction son_tir()

Le tir d'un projectile s'accompagne d'un « piou » très court. La fonction qui le produit est volontairement minimaliste :

void son_tir(void){
    NCO1INC = 1300 >> 3;
    __delay_ms(6);
    NCO1INC = 0;
}

La première ligne écrit dans NCO1INC la valeur 1300 >> 3, soit 162 (1300 divisé par 8). D'après la formule établie plus haut, la fréquence entendue vaut environ 1300 x 0,9537 ≈ 1240 Hz : un son aigu, bref et percutant, adapté à un tir. La deuxième ligne, __delay_ms(6), maintient ce son pendant environ 6 millisecondes. La troisième ligne, NCO1INC = 0, coupe immédiatement le signal : avec un incrément nul, l'accumulateur ne déborde plus, la sortie reste figée et le silence revient.

Ce délai de 6 ms est bloquant : pendant ces 6 ms, le programme n'exécute rien d'autre. C'est un compromis assumé. Une durée aussi courte est imperceptible pour le joueur à l'échelle de la boucle de jeu, et la simplicité du code prime ici sur l'optimisation. Un mécanisme non bloquant (à base de compteur décrémenté à chaque tour de boucle) aurait alourdi inutilement la gestion du tir pour un gain insignifiant. La règle « ne jamais bloquer le jeu inutilement » est donc respectée dans l'esprit : 6 ms ne perturbent pas la fluidité perçue.

La fonction son_gameover()

La fin de partie mérite un effet plus marqué. La fonction son_gameover() produit un son d'extinction de type « power-down » : un balayage descendant en fréquence, suivi de deux notes graves, évoquant un appareil qui s'éteint.

void son_gameover(void){
    for(int f=950; f>=120; f-=22){
        NCO1INC = f>>3;
        __delay_ms(9);
    }
    NCO1INC=0;
    __delay_ms(70);
    NCO1INC = 150>>3;
    __delay_ms(150);
    NCO1INC = 95>>3;
    __delay_ms(280);
    NCO1INC = 0;
}

La boucle for réalise le balayage : la variable f part de 950 Hz et descend jusqu'à 120 Hz par pas de 22 Hz. À chaque itération, NCO1INC = f >> 3 fixe la note courante (toujours via le décalage rapide) et __delay_ms(9) la maintient 9 ms. L'oreille perçoit ainsi une glissade continue vers les graves, sur environ trente-huit paliers, soit à peu près 340 ms au total.

Après la boucle, NCO1INC = 0 impose un court silence de 70 ms (__delay_ms(70)), qui marque une rupture. Viennent ensuite deux notes graves distinctes : NCO1INC = 150 >> 3 produit une note d'environ 150 x 0,9537 ≈ 143 Hz tenue 150 ms, puis NCO1INC = 95 >> 3 une note encore plus grave d'environ 95 x 0,9537 ≈ 90 Hz tenue 280 ms. Le NCO1INC = 0 final coupe définitivement le son. L'enchaînement « descente, silence, deux coups graves » donne l'impression caractéristique d'un système qui s'arrête.

L'usage de délais bloquants est ici parfaitement justifié. Lorsque cette fonction est appelée, la partie est terminée (état GAME OVER) : plus rien ne doit bouger à l'écran, aucun astéroïde ni projectile n'a à être mis à jour. Bloquer le programme le temps de jouer la séquence sonore n'a donc aucune conséquence sur la jouabilité. C'est l'exception qui confirme la règle : on ne bloque jamais le jeu inutilement, mais ici l'attente est utile car le jeu est justement arrêté.

On remarquera aussi que l'attente est découpée en plusieurs petits délais (9 ms, 70 ms, 150 ms, 280 ms) plutôt qu'en une seule longue temporisation. La durée maximale acceptée par la macro __delay_ms est en effet limitée sur le PIC, car elle est traduite par le compilateur XC8 en une boucle de comptage dont le nombre de cycles dépend de la fréquence d'horloge et reste borné. Découper en petits délais respecte cette contrainte tout en composant librement la durée totale de l'effet.

Le lien avec la carte son

Le signal carré généré sur la broche RA6 n'attaque pas directement un haut-parleur : il est d'abord amplifié. Il rejoint le connecteur J2 (Audio_In) de la carte son, bâtie autour de l'amplificateur audio LM386. Celui-ci élève le niveau du signal avec un gain de 20 (valeur par défaut, les broches 1 et 8 du LM386 n'étant pas pontées), puis l'envoie au haut-parleur via le connecteur de sortie J3 (Audio_out). Le potentiomètre RV1 (10 kΩ) placé en entrée permet de régler le volume, tandis que le condensateur de couplage C1 (1 µF) isole l'entrée en continu. L'architecture de cette carte est détaillée dans la première partie de ce rapport ; on en rappelle ci-dessous le découpage fonctionnel.

Schéma bloc de la carte son
Figure - découpage fonctionnel de la carte son LM386 recevant le signal de RA6.
Le silence par défaut

Un dernier détail témoigne du soin apporté à l'expérience sonore : au démarrage du programme, NCO1INC est mis à 0. Cette précaution coupe tout son dès l'initialisation et évite qu'un bourdonnement parasite ne se fasse entendre avant la première note volontaire. Le silence est ainsi l'état par défaut, et le son n'apparaît qu'au moment choisi par le jeu : un tir, ou la fin de la partie.