Table des matières:

Synthétiseur audio numérique Basys3 FPGA : 5 étapes
Synthétiseur audio numérique Basys3 FPGA : 5 étapes

Vidéo: Synthétiseur audio numérique Basys3 FPGA : 5 étapes

Vidéo: Synthétiseur audio numérique Basys3 FPGA : 5 étapes
Vidéo: #11 Реализация цифровой безопасной системы на плате Basys 3 FPGA | Верилог | Пошаговые инструкции 2024, Juillet
Anonim
Image
Image
Synthétiseur audio numérique Basys3 FPGA
Synthétiseur audio numérique Basys3 FPGA
Synthétiseur audio numérique Basys3 FPGA
Synthétiseur audio numérique Basys3 FPGA

Ce synthétiseur de clavier à onde sinusoïdale numérique prendra les entrées de l'utilisateur via une série de commutateurs momentanés disposés comme un clavier et produira une onde audio via un haut-parleur. Sur la base des entrées de l'utilisateur, l'appareil générera des ondes sinusoïdales de différentes fréquences de C4 à C6. L'utilisateur peut saisir des notes de C4 à C6 (25 notes au total) et jusqu'à quatre touches en même temps - si plus de quatre touches sont enfoncées, les quatre tonalités les plus basses seront jouées.

Ce projet a été réalisé par Ryan Morris et Mavis Tsoi pour notre cours de conception numérique Cal Poly CPE 133:)

Étape 1: Théorie

Une carte FPGA ne peut émettre que des signaux numériques. En d'autres termes, il ne peut produire qu'une tension élevée (3,3 V) ou une tension faible (0 V). Cependant, les signaux audio sont analogiques et peuvent avoir une infinité d'incréments de tension. Pour contourner ce problème, nous utiliserons un signal PWM (modulation de largeur d'impulsion) pour émuler une onde analogique. Si vous ne savez pas ce qu'est le PWM, consultez ceci:

Étape 2: Ingrédients et outils

  • Ordinateur avec Vivado installé
  • Nous utiliserons Vivado version 2017.2
  • Carte FPGA Basys3
  • 25 interrupteurs de fin de course SPDT (nous les avons utilisés)
  • 30 fils de liaison (une extrémité mâle, l'autre extrémité n'a pas d'importance), 12 pouces
  • Pinces coupantes
  • Pince à dénuder
  • Fil de rechange pour la soudure
  • Soudure à noyau de résine
  • Fer à souder
  • Prise audio femelle ¼"
  • Amplificateur/haut-parleur
  • Quelque chose pour monter les interrupteurs (nous avons utilisé un protoboard + une boîte en bois)

Étape 3: Câblage et configuration du matériel

Câblage et configuration du matériel
Câblage et configuration du matériel
Câblage et configuration du matériel
Câblage et configuration du matériel
Câblage et configuration du matériel
Câblage et configuration du matériel

Architecture du système

Voir Figure 1: 25 entrées disponibles → Carte Basys3 → amplificateur et haut-parleur.

Sortir

Voir Figure 2: Carte Basys3 → Prise audio femelle 1/2 → Haut-parleur (avec amplificateur)

Saisir

Les connexions pmod sur la carte Basys3 doivent être connectées à la terre afin de voir une entrée faible et ne fonctionneront pas correctement si elles sont laissées en circuit ouvert. Pour cette raison, nous devons utiliser des commutateurs SPDT pour toutes nos touches de notes. Un commutateur SPDT permet essentiellement à l'utilisateur de basculer entre les circuits lorsqu'il est enfoncé, nous les utiliserons donc comme nos « boutons » pour entrer des signaux bas (0 V) ou élevés (3,3 V) vers la carte Basys3.

Chaque commutateur aura la borne NO (normalement ouverte) connectée à 3,3 V, la borne NC (normalement fermée) connectée à GND et la borne COM (commune) connectée à l'entrée FPGA. Voir la figure 3.

Parce que nous avons 25 interrupteurs de fin de course, ils partageront tous une ligne 3,3 V commune et une ligne GND commune. Ensuite, la ligne de signal de chaque interrupteur de fin de course sera regroupée par groupes de 8 et connectée aux connexions pmod de la carte Basys3 à l'aide de câbles de connexion zippables pour minimiser le désordre monumental que nous allons créer. Voir la figure 4 ou un exemple des huit premières touches.

Étape 4: Configuration VHDL (Vivado)

Configuration VHDL (Vivado)
Configuration VHDL (Vivado)
Configuration VHDL (Vivado)
Configuration VHDL (Vivado)

Le générateur d'onde sinusoïdale et le générateur PWM ont d'abord été testés pour s'assurer que notre concept fonctionnait, puis le limiteur d'entrée et l'additionneur/décaleur d'amplitude ont été intégrés. Les détails de la fonction et des E/S de chaque bloc de processus sont indiqués sur la figure. Le code est affiché ci-dessous, mais également joint sous forme de fichiers VHD et txt. S'il y a des divergences, utilisez les fichiers VHD.

BTW: nous aurions probablement dû raccourcir nos lignes, mais l'intégration de code sur Instructables s'est également avérée assez ennuyeuse à gérer, donc l'espacement n'est pas le plus grand et il n'y a pas de coloration syntaxique. Si vous avez Vivado et que vous souhaitez suivre le code, nous vous recommandons fortement de simplement télécharger le fichier.

Tout d'abord, regardons le module générateur d'onde sinusoïdale.

bibliothèque IEEE; utiliser IEEE. STD_LOGIC_1164. ALL; utiliser IEEE. NUMERIC_STD. ALL; entité Wave_Generator est Port (Trigger: dans STD_LOGIC; -- Touche Freq_Cnt: dans STD_LOGIC_VECTOR (15 jusqu'à 0); -- Valeur du compteur = 100MHz / (Note Frequency*64 Divisions of Sine Wave) (arrondir au nombre le plus proche) -- renommé à partir de Freq wavegenCLK: dans STD_LOGIC; -- Basys3 100MHz CLK WaveOut: sur STD_LOGIC_VECTOR (9 jusqu'à 0)); -- Amplitude signée de fin d'onde Wave_Generator; architecture Le comportement de Wave_Generator est le signal i: plage entière de 0 à 64:= 0; -- index du type de banque de mémoire d'amplitude memory_type est un tableau (0 à 63) d'entiers compris entre -64 et 63; -- créer une banque de mémoire (ROM) pour conserver les valeurs d'amplitude -- est-ce que cette RAM ou ROM se demande juste… amplitude du signal: memory_type:= (0, 7, 13, 19, 25, 30, 35, 40, 45, 49, 52, 55, 58, 60, 62, 63, 63, 63, 62, 60, 58, 55, 52, 49, 45, 40, 35, 30, 25, 19, 13, 7, 0, -7, -13, -19, -25, -30, -35, -40, -45, -49, -52, -55, -58, -60, -62, -63, -63, -63, -62, - 60, -58, -55, -52, -49, -45, -40, -35, -30, -25, -19, -13, -7); -- banque de mémoire d'amplitude pour le processus de début d'onde sinusoïdale (wavegenCLK, Trigger) compteur variable: unsigned (15 jusqu'à 0):= to_unsigned (0, 16); -- compteur diviseur d'horloge, renommé à partir de count1 begin if (rising_edge(wavegenCLK)) then if (Trigger = '1') then -- la touche est enfoncée counter:= counter + 1; if (counter = unsigned(Freq_Cnt)) then -- Freq_Cnt = 100Mhz / (notez la fréquence * 64 divisions de l'onde sinusoïdale) -- réinitialisez le compteur et attribuez les données d'amplitude au compteur de sortie:= to_unsigned (0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed(amplitude(i), 10)); -- incrémenter i pour la lecture suivante i <= i + 1; -- réinitialiser i si une onde sinusoïdale a été complétée if(i = 63) alors i <= 0; fin si; fin si; -- (counter = unsigned(Freq_Cnt)) else -- la touche n'est pas enfoncée -- réinitialise la sortie, l'indice d'amplitude et le compteur WaveOut <= "0000000000"; je <= 0; compteur:= to_unsigned (0, 16); --output Amplitude = -64 quand aucune note n'est jouée end if; -- (Déclencheur = '1') end if; -- (rising_edge(CLK)) processus de fin; mettre fin au comportement;

Nous allons générer une onde sinusoïdale numérique dans le Basys3 en utilisant l'horloge interne et une ROM. Cette ROM stockera 64 valeurs qui représentent 64 amplitudes sur une onde sinusoïdale. Voir Figure 1. Les 64 valeurs que nous utilisons émulent une onde sinusoïdale avec une assez bonne résolution.

En utilisant l'horloge interne, nous comptons jusqu'à une valeur qui représente la vitesse d'horloge divisée par la fréquence de l'onde que nous voulons et 64: Clk div = 100MHz / (Freq * 64) Chaque fois que notre compteur atteint cette valeur, nous appelons un numéro de la ROM et l'envoyer à partir de notre module générateur d'ondes. La fréquence de notre onde dépendra de la vitesse à laquelle nous appelons ces amplitudes.

Nous aurons 25 sous-modules, chacun associé à une fréquence/note.

Voici le reste du code qui appelle les modules Sine Wave Generator:

bibliothèque IEEE; utiliser IEEE. STD_LOGIC_1164. ALL; utiliser IEEE. NUMERIC_STD. ALL; l'entité Two_Octave_Synth est Port (CLK: dans STD_LOGIC; O4: dans STD_LOGIC_VECTOR (11 jusqu'à 0); O5: dans STD_LOGIC_VECTOR (12 jusqu'à 0); sortie: out STD_LOGIC); fin Two_Octave_Synth; architecture Behavioral of Two_Octave_Synth est le composant Wave_Generator is Port (Trigger: dans STD_LOGIC; Freq_Cnt: dans STD_LOGIC_VECTOR(15 jusqu'à 0); wavegenCLK: dans STD_LOGIC; WaveOut: out STD_LOGIC_VECTOR(9 jusqu'à 0)); composant final; ---------------------------signaux de sortie du générateur d'ondes------------------ ----- signal WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveG5, WaveD5, WaveDs5 WaveAs5, WaveB5, WaveC6: signé(9 jusqu'à 0); ------------------ pour la logique de sélection de note --------------- ------ signal C4, Cs4, D4, Ds4, E4, F4, Fs4, G4, Gs4, A4, As4, B4, C5, Cs5, D5, Ds5, E5, F5, Fs5, G5, Gs5, A5, As5, B5, C6: non signé (4 jusqu'à 0); signal cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntD5, cntDs5, cnt5, cnt, cnt5, cntF5, A: non signé (4 jusqu'à 0); erreur de signal: STD_LOGIC; ----------------------------------- pour l'ajout d'ondes sinusoïdales ----------- --------------- signal Wave0, Wave1, Wave2, Wave3: signé(9 jusqu'à 0); --signaux du signal de sortie du module WaveSum WaveSum: STD_LOGIC_VECTOR (9 jusqu'à 0); --signal pour les ondes sinusoïdales additionnées (complément de 2 -512 à 511) signal positiveWaveSum: STD_LOGIC_VECTOR(9 jusqu'à 0); --unsigned 0 à 1023, à utiliser dans le générateur PWM ------------------------------------pour générer PWM ------------------------------- signal ping_length: unsigned (9 jusqu'à 0):= unsigned(positiveWaveSum); --signal off_length: unsigned (6 jusqu'à 0):= to_unsigned(127, 7) - unsigned(WAVE); signal PWM: non signé (9 jusqu'à 0):= to_unsigned (0, 10); begin Note_C4: carte des ports Wave_Generator (Trigger => O4(0), Freq_Cnt => X"1755", wavegenCLK => CLK,signed(WaveOut) => WaveC4); --5973, 261.63 Hz Note_Cs4: Carte des ports Wave_Generator (Trigger => O4(1), Freq_Cnt => X"1606", wavegenCLK => CLK,signed(WaveOut) => WaveCs4);--5638, 277.18 Hz Note_D4: Carte des ports Wave_Generator (Trigger => O4(2), Freq_Cnt => X"14C9", wavegenCLK => CLK,signed(WaveOut) => WaveD4); --5321, 293,66 Hz Note_Ds4: Carte des ports Wave_Generator (Trigger => O4(3), Freq_Cnt => X"139F", wavegenCLK => CLK,signed(WaveOut) => WaveDs4);--5023, 311.13 Hz Note_E4: Carte des ports Wave_Generator (Trigger => O4(4), Freq_Cnt => X"1285", wavegenCLK => CLK,signed(WaveOut) => WaveE4); --4741, 329,63 Hz Note_F4: Carte des ports Wave_Generator (Trigger => O4(5), Freq_Cnt => X"117B", wavegenCLK => CLK,signed(WaveOut) => WaveF4); --4475, 349,23 Hz Note_Fs4: Carte des ports Wave_Generator (Trigger => O4(6), Freq_Cnt => X"1080", wavegenCLK => CLK,signed(WaveOut) => WaveFs4);--4224, 369,99 Hz Note_G4: Carte des ports Wave_Generator (Trigger => O4(7), Freq_Cnt => X"0F92", wavegenCLK => CLK,signed(WaveOut) => WaveG4); --3986, 392,00 Hz Note_Gs4: Carte des ports Wave_Generator (Trigger => O4(8), Freq_Cnt => X"0EB3", wavegenCLK => CLK,signed(WaveOut) => WaveGs4);--3763, 415,30 Hz Note_A4: Carte des ports Wave_Generator (Trigger => O4(9), Freq_Cnt => X"0DE0", wavegenCLK => CLK,signed(WaveOut) => WaveA4); --3552, 440,00 Hz Note_As4: Carte des ports Wave_Generator (Trigger => O4(10), Freq_Cnt => X"0D18", wavegenCLK => CLK,signed(WaveOut) => WaveAs4);--3352, 466,16 Hz Note_B4: Carte des ports Wave_Generator (Trigger => O4(11), Freq_Cnt => X"0C5C", wavegenCLK => CLK,signed(WaveOut) => WaveB4); --3164, 493,88 Hz ---------------------------------------------- -------------------------------------------------- --------------------------- Note_C5: Carte des ports Wave_Generator (Trigger => O5(0), Freq_Cnt => X"0BAB", wavegenCLK => CLK, signé (WaveOut) => WaveC5); --2987, 523,25 Hz Note_Cs5: Carte des ports Wave_Generator (Trigger => O5(1), Freq_Cnt => X"0B03", wavegenCLK => CLK,signed(WaveOut) => WaveCs5);--2819, 554,37 Hz Note_D5: Carte des ports Wave_Generator (Trigger => O5(2), Freq_Cnt => X"0A65", wavegenCLK => CLK,signed(WaveOut) => WaveD5); --2661, 587,33 Hz Note_Ds5: Carte des ports Wave_Generator (Trigger => O5(3), Freq_Cnt => X"09D0", wavegenCLK => CLK,sign(WaveOut) => WaveDs5);--2512, 622,25 Hz Note_E5: Carte des ports Wave_Generator (Trigger => O5(4), Freq_Cnt => X"0943", wavegenCLK => CLK,signed(WaveOut) => WaveE5); --2371, 659,25 Hz Note_F5: Carte des ports Wave_Generator (Trigger => O5(5), Freq_Cnt => X"08Be", wavegenCLK => CLK,signed(WaveOut) => WaveF5); --2238, 698,46 Hz Note_Fs5: Carte des ports Wave_Generator (Trigger => O5(6), Freq_Cnt => X"0840", wavegenCLK => CLK,signed(WaveOut) => WaveFs5);--2112, 739,99 Hz Note_G5: Carte des ports Wave_Generator (Trigger => O5(7), Freq_Cnt => X"07CA", wavegenCLK => CLK,signed(WaveOut) => WaveG5); --1994, 783,99 Hz Note_Gs5: Carte des ports Wave_Generator (Trigger => O5(8), Freq_Cnt => X"075A", wavegenCLK => CLK,signed(WaveOut) => WaveGs5);--1882, 830,61 Hz Note_A5: Carte des ports Wave_Generator (Trigger => O5(9), Freq_Cnt => X"06F0", wavegenCLK => CLK,signed(WaveOut) => WaveA5); --1776, 880,00 Hz Note_As5: Carte des ports Wave_Generator (Trigger => O5(10), Freq_Cnt => X"068C", wavegenCLK => CLK,signed(WaveOut) => WaveAs5);--1676, 932,33 Hz Note_B5: Carte des ports Wave_Generator (Trigger => O5(11), Freq_Cnt => X"062E", wavegenCLK => CLK,signed(WaveOut) => WaveB5); --1582, 987,77 Hz Note_C6: Carte des ports Wave_Generator (Trigger => O5(12), Freq_Cnt => X"05D6", wavegenCLK => CLK,signed(WaveOut) => WaveC6); --1494, 1046,5 Hz ------------logique de sélection de note------------ C4 <= "0000" & O4(0); Cs4 <= "0000" & O4(1); D4 <= "0000" & O4(2); Ds4 <= "0000" & O4(3); E4 <= "0000" & O4(4); F4 <= "0000" & O4(5); Fs4 <= "0000" & O4(6); G4 <= "0000" & O4(7); Gs4 <= "0000" & O4(8); A4 <= "0000" & O4(9); As4 <= "0000" & O4(10); B4 <= "0000" & O4(11); C5 <= "0000" & O5(0); Cs5 <= "0000" & O5(1); D5 <= "0000" & O5(2); Ds5 <= "0000" & O5(3); E5 <= "0000" & O5(4); F5 <= "0000" & O5(5); Fs5 <= "0000" & O5(6); G5 <= "0000" & O5(7); Gs5 <= "0000" & O5(8); A5 <= "0000" & O5(9); As5 <= "0000" & O5(10); B5 <= "0000" & O5(11); C6 <= "0000" & O5(12); cntC4 <= C4; cntCs4 <= C4 + Cs4; cntD4 <= C4 + Cs4 + D4; cntDs4 <= C4 + Cs4 + D4 + Ds4; cntE4 <= C4 + Cs4 + D4 + Ds4 + E4; cntF4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4; cntFs4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4; cntG4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4; cntGs4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4; cntA4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4; cntAs4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4; cntB4 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4; cntC5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5; cntCs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5; cntD5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5; cntDs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5; cntE5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5; cntF5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5; cntFs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5; cntG5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5; cntGs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5; cntA5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5; cntAs5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5 + As5; cntB5 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5 + As5 + B5; cntC6 <= C4 + Cs4 + D4 + Ds4 + E4 + F4 + Fs4 + G4 + Gs4 + A4 + As4 + B4 + C5 + Cs5 + D5 + Ds5 + E5 + F5 + Fs5 + G5 + Gs5 + A5 + As5 + B5 + C6; Sélection: processus (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5 WaveB5, WaveC6) commencent si (cntC6 = "00000") alors ---------------si aucun signal n'est généré Wave0 <= "0000000000"; Vague1 <= "00000000000"; Vague2 <= "00000000000"; Vague3 <= "00000000000"; sinon si (O4(0) = '1') alors ------------------note C4 joué Vague0 Vague0 Vague1 erreur Vague0 Vague1 Vague2 erreur Vague0 Vague1 Vague2 Vague3 erreur Vague0 Vague1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 erreur Wave0 Wave1 Wave2 Wave3 erreur Wave0 < = VagueC6; Vague1 <= "00000000000"; Vague2 <= "00000000000"; Vague3 Vague1 <= VagueC6; Vague2 <= "00000000000"; Vague3 Vague2 <= VagueC6; Wave3 Erreur Wave3 Wave1 <= "00000000000"; Vague2 <= "00000000000"; Vague3 Vague2 <= "0000000000"; Erreur Wave3 Wave3 <= '1'; cas d'extrémité; fin si; fin si; processus final; ------------- additionneur d'onde sinusoïdale -------------------- WaveSum <= STD_LOGIC_VECTOR(Wave0 + Wave1 + Wave2 + Wave3); ---------faire une onde sinusoïdale positive pour pwm--------------------- positiveWaveSum <= pas WaveSum(9) & WaveSum(8 jusqu'à 0); ------------- Générateur PWM -------------------- processus (CLK) -- nombre de variables: non signé (1 jusqu'à 0):= to_unsigned(0, 2); begin if (rising_edge(CLK)) then --count:= count + 1; --if (count = to_unsigned(4, 2)) then --count:= to_unsigned(0, 2); --if (PWM = to_ if (PWM < ping_length) then output <= '1'; else output <= '0'; end if; PWM <= PWM + 1; ping_length <= unsigned(positiveWaveSum); --end si; fin si; fin du processus; fin Comportement;

Sélecteur à 4 notesLa partie la plus délicate de ce projet consiste à sélectionner seulement quatre fréquences. Nous l'avons fait avec beaucoup d'instructions IF et nous avons utilisé des signaux au lieu de variables afin que le processus puisse être simulé et débogué. Nous avons essayé d'autres méthodes utilisant des variables et des boucles FOR, mais nous avons rencontré des erreurs d'exécution. Donc, à la fin, nous avons décidé que si cela fonctionnait, nous le laisserions tranquille. Ne répare pas ce qui n'est pas de l'amirite cassée ?

Les quatre ondes de sortie sont étiquetées Wave0, Wave1, Wave2, Wave3 -- ce sont celles qui seront additionnées pour former la sortie finale.

En regardant le code, vous verrez un tas de signaux étiquetés C4, Cs4, D4, Ds4, etc. Ce sont des signaux à 5 bits qui prennent le déclencheur correspondant de O4 (octave 4) ou O5 (octave 5) et les rendent 5 bits pour l'ajout.

Ensuite, les variables cntC4, cntCs4, etc. représentent le nombre de notes inférieures à la note cible qui ont été jouées, y compris la note cible. Par exemple, si C4, E4, G4, A#4 et D5 sont joués (accord C9) cntC4 sera 1, cntE4 sera 2, cntG4 sera 3, etc.

Ensuite, chaque fois qu'une note est jouée, le compte de la note cible sera examiné pour voir où accrocher le signal de note. Par exemple, si la note D5 est jouée (ce qui signifie que O5(2) est élevé) et que cntD5 vaut 3, alors il y a actuellement 3 notes jouées, avec 2 notes plus basses que D5, nous allons donc accrocher waveD5 à Wave2 (la troisième vague comptage de signal de Wave0). Alternativement, si cntD5 est 5, alors il y a actuellement 5 notes en cours de lecture, avec 4 notes inférieures à D5, nous allons donc laisser waveD5 suspendu et ne rien faire avec.

Les instructions IF sont ensuite répétées pour couvrir les cas des 25 notes.

Additionneur d'amplitude

Une fois les 4 vagues les plus basses sélectionnées, nous devons les additionner. La raison pour laquelle nous n'ajouterons que quatre notes ensemble est que l'idée PWM que nous utilisons pour notre sortie ne peut avoir qu'une certaine résolution jusqu'à ce que le PWM soit trop lent et que le haut-parleur commence à capter l'onde carrée PWM. Par exemple, si nous devions utiliser une résolution de 8192 (13 bits), chacun de ces 8192 points doit correspondre à un front montant de l'horloge embarquée. Donc, 100 MHz / 8192 = 12,2 kHz, ce qui est bien dans la plage de l'audition humaine.

L'ajout réel des amplitudes est super simple, il suffit de s'assurer qu'il peut fonctionner très rapidement.

Sortie PWM

Le cycle de service du PWM représentera l'amplitude de notre onde de sortie à cet instant. Par exemple, si nous avons une plage d'amplitude de 0 à 128, 0 serait un rapport cyclique de 0%, 64 serait de 50%, 128 serait de 100%, etc. Ce PWM fonctionnera extrêmement rapidement (le nôtre est de 97,6 kHz), si rapide que le haut-parleur ne reconnaîtra pas les ondes carrées individuelles et regardera plutôt la tension moyenne, créant notre signal « analogique ».

Fichier de contraintes

Vous avez peut-être connecté votre matériel différemment, alors assurez-vous simplement que le fichier de contraintes correspond.

Étape 5: Téléchargements de code

Vous trouverez ci-dessous le code, à la fois au format.txt et.vhd pour Vivado. Wave_Generator est le sous-module du générateur d'ondes et Two_Octave_Synth est le module supérieur avec tout le reste.

Conseillé: