Оглавление:

Цифровой аудиосинтезатор Basys3 FPGA: 5 шагов
Цифровой аудиосинтезатор Basys3 FPGA: 5 шагов

Видео: Цифровой аудиосинтезатор Basys3 FPGA: 5 шагов

Видео: Цифровой аудиосинтезатор Basys3 FPGA: 5 шагов
Видео: 10. 1-Wire на ПЛИС(FPGA). Это просто. 2024, Ноябрь
Anonim
Image
Image
Цифровой аудиосинтезатор Basys3 FPGA
Цифровой аудиосинтезатор Basys3 FPGA
Цифровой аудиосинтезатор Basys3 FPGA
Цифровой аудиосинтезатор Basys3 FPGA

Этот цифровой синусоидальный клавишный синтезатор будет принимать вводимые пользователем данные через серию переключателей мгновенного действия, расположенных как клавиатура, и выводить звуковую волну через динамик. На основании ввода данных пользователем устройство будет генерировать синусоидальные волны различной частоты от C4 до C6. Пользователь может вводить ноты от C4 до C6 (всего 25 нот) и до четырех клавиш одновременно - при нажатии более четырех клавиш будут воспроизводиться четыре самых низких тона.

Этот проект был выполнен Райаном Моррисом и Мавис Цой для нашего класса цифрового дизайна Cal Poly CPE 133:)

Шаг 1: теория

Плата FPGA может выводить только цифровые сигналы. Другими словами, он может производить только высокое (3,3 В) или низкое (0 В) напряжение. Однако аудиосигналы являются аналоговыми и могут иметь бесконечно много приращений напряжения. Чтобы обойти это, мы будем использовать сигнал ШИМ (широтно-импульсная модуляция) для имитации аналоговой волны. Если вы не знаете, что такое ШИМ, проверьте это:

Шаг 2: ингредиенты и инструменты

  • Компьютер с установленным Vivado
  • Мы будем использовать Vivado версии 2017.2.
  • Плата Basys3 FPGA
  • 25 концевых выключателей SPDT (мы использовали эти)
  • 30 перемычек (один конец вилка, другой конец не имеет значения), 12 дюймов
  • Кусачки
  • Инструмент для зачистки проводов
  • Запасной провод для пайки
  • Припой с полимерным сердечником
  • Паяльник
  • ¼”женский аудиоразъем
  • Усилитель / динамик
  • Что-то для крепления переключателей (мы использовали макетную плату + деревянный ящик)

Шаг 3: Подключение и настройка оборудования

Подключение и настройка оборудования
Подключение и настройка оборудования
Подключение и настройка оборудования
Подключение и настройка оборудования
Подключение и настройка оборудования
Подключение и настройка оборудования

Архитектура системы

См. Рис. 1:25 доступных входов → плата Basys3 → усилитель и динамик.

Выход

См. Рис. 2. Плата Basys3 → Аудиоразъем 1/2 дюйма → Динамик (с усилителем)

Вход

Соединения pmod на плате Basys3 должны быть заземлены, чтобы увидеть низкий входной сигнал, и они не будут работать должным образом, если оставить их в виде разомкнутой цепи. Из-за этого мы должны использовать переключатели SPDT для всех клавиш нот. Переключатель SPDT в основном позволяет пользователю переключаться между цепями при нажатии, поэтому мы будем использовать их в качестве «кнопок» для ввода сигналов низкого (0 В) или высокого (3,3 В) на плату Basys3.

Каждый переключатель будет иметь NO (нормально открытый) контакт, подключенный к 3.3V, NC (нормально закрытый) контакт, подключенный к GND, и COM (общий) контакт, подключенный к входу FPGA. См. Рисунок 3.

Поскольку у нас есть 25 концевых выключателей, все они будут иметь общую линию 3,3 В и общую линию GND. Затем сигнальная линия от каждого концевого выключателя будет объединена в группы по 8 и подключена к разъемам pmod на плате Basys3 с помощью перемычек на молнии, чтобы свести к минимуму беспорядок, который мы создадим. См. Рисунок 4 или пример первых восьми клавиш.

Шаг 4: Настройка VHDL (Vivado)

Настройка VHDL (Vivado)
Настройка VHDL (Vivado)
Настройка VHDL (Vivado)
Настройка VHDL (Vivado)

Генератор синусоидальной волны и генератор ШИМ были сначала протестированы, чтобы убедиться, что наша концепция работает, затем были интегрированы входной ограничитель и сумматор / сдвиг амплитуды. Подробная информация о функциях и вводе / выводе каждого блока процесса показана на рисунке. Код показан ниже, но также прикреплен в виде файлов VHD и txt. Если есть расхождения, используйте файлы VHD.

Кстати: мы, вероятно, должны были сделать наши строки короче, но внедрение кода в Instructables также оказалось довольно утомительным, поэтому интервалы не самые большие и нет подсветки синтаксиса. Если у вас есть Vivado и вы хотите следовать коду, мы настоятельно рекомендуем вам просто загрузить файл.

Во-первых, давайте посмотрим на модуль генератора синусоидальной волны.

библиотека IEEE; используйте IEEE. STD_LOGIC_1164. ALL; используйте IEEE. NUMERIC_STD. ALL; объект Wave_Generator - это порт (триггер: в STD_LOGIC; - нажатие клавиши Freq_Cnt: в STD_LOGIC_VECTOR (от 15 до 0); - значение счетчика = 100 МГц / (частота нот * 64 деления синусоиды) (округлить до ближайшего числа) - переименовано from Freq wavegenCLK: in STD_LOGIC; - Basys3 100MHz CLK WaveOut: out STD_LOGIC_VECTOR (9 вниз до 0)); - Знаковая амплитуда конца волны Wave_Generator; архитектура Поведение Wave_Generator - это сигнал i: целочисленный диапазон от 0 до 64: = 0; - индекс типа банка амплитудной памяти memory_type - массив (от 0 до 63) целого диапазона от -64 до 63; - создать банк памяти (ПЗУ) для хранения значений амплитуды - это ОЗУ или ПЗУ просто интересно… амплитуда сигнала: 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); - банк памяти амплитуды для счетчика переменных процесса начала синусоидальной волны (wavegenCLK, Trigger): беззнаковый (от 15 до 0): = to_unsigned (0, 16); - счетчик делителя часов, переименованный из count1 begin if (rise_edge (wavegenCLK)) then if (Trigger = '1') then - нажатие клавиши counter: = counter + 1; if (counter = unsigned (Freq_Cnt)) then - Freq_Cnt = 100Mhz / (обратите внимание на частоту * 64 деления синусоидальной волны) - сбросить счетчик и присвоить данные амплитуды выходному счетчику: = to_unsigned (0, 16); WaveOut <= STD_LOGIC_VECTOR (to_signed (амплитуда (i), 10)); - увеличить i для следующего чтения i <= i + 1; - сбросить i, если завершилась одна синусоида if (i = 63), то i <= 0; конец, если; конец, если; - (counter = unsigned (Freq_Cnt)) else - клавиша не нажата - сбросить вывод, индекс амплитуды и счетчик WaveOut <= "0000000000"; я <= 0; счетчик: = to_unsigned (0, 16); --output Amplitude = -64, когда нота не воспроизводится end if; - (Trigger = '1') конец, если; - (rise_edge (CLK)) завершение процесса; конец Поведенческий;

Мы будем генерировать цифровую синусоидальную волну в Basys3, используя внутренние часы и ПЗУ. Это ПЗУ будет хранить 64 значения, которые представляют 64 амплитуды синусоидальной волны. См. Рисунок 1. 64 значения, которые мы используем, имитируют синусоидальную волну с довольно хорошим разрешением.

Используя внутренние часы, мы подсчитываем до значения, которое представляет тактовую частоту, деленную на частоту волны, которую мы хотим, и 64: Clk div = 100MHz / (Freq * 64) Каждый раз, когда наш счетчик достигает этого значения, мы вызываем число из ПЗУ и отправьте его из нашего модуля генератора волн. Частота нашей волны будет зависеть от того, насколько быстро мы называем эти амплитуды.

У нас будет 25 подмодулей, каждый из которых связан с одной частотой / нотой.

Вот оставшаяся часть кода, вызывающего модули генератора синусоидальной волны:

библиотека IEEE; используйте IEEE. STD_LOGIC_1164. ALL; используйте IEEE. NUMERIC_STD. ALL; сущность Two_Octave_Synth - это порт (CLK: в STD_LOGIC; O4: в STD_LOGIC_VECTOR (от 11 до 0); O5: в STD_LOGIC_VECTOR (от 12 до 0); вывод: из STD_LOGIC); конец Two_Octave_Synth; архитектура Поведение Two_Octave_Synth - это компонент Wave_Generator - это порт (Триггер: в STD_LOGIC; Freq_Cnt: в STD_LOGIC_VECTOR (15 вниз до 0); wavegenCLK: в STD_LOGIC; WaveOut: из STD_LOGIC_VECTOR) (9 вниз); конечный компонент; --------------------------- выходные сигналы от генератора волн ------------------ ----- сигнал WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveA WaveAs5, WaveB5, WaveC6: со знаком (от 9 до 0); -------------------------------- для логики выбора заметок -------------- ------ сигнал 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: без знака (с 4 до 0); сигналы cntC4, cntCs4, cntD4, cntDs4, cntE4, cntF4, cntFs4, cntG4, cntGs4, cntA4, cntAs4, cntB4, cntC5, cntCs5, cntD5, cntDs5, cntE5, cntGsnt5, cntGsF5, cntGsF5, cntGsF5, cntGsF5, cntGsF5, cntGsF5: беззнаковый (4 до 0); ошибка сигнала: STD_LOGIC; ----------------------------------- для добавления синусоидальных волн ----------- --------------- сигнал Wave0, Wave1, Wave2, Wave3: подписанный (9 вниз до 0); --сигналы от выходного сигнала модуля Wave Generator WaveSum: STD_LOGIC_VECTOR (от 9 до 0); --сигнал для суммированных синусоид (дополнение 2 от -512 до 511) signal positiveWaveSum: STD_LOGIC_VECTOR (9 вниз до 0); - без знака от 0 до 1023, для использования в генераторе ШИМ ----------------------------------- для генерации ШИМ ------------------------------- signal ping_length: unsigned (9 вниз до 0): = unsigned (positiveWaveSum); --signal off_length: unsigned (от 6 до 0): = to_unsigned (127, 7) - без знака (WAVE); сигнал ШИМ: без знака (от 9 до 0): = to_unsigned (0, 10); begin Note_C4: Карта портов Wave_Generator (Trigger => O4 (0), Freq_Cnt => X "1755", wavegenCLK => CLK, signed (WaveOut) => WaveC4); --5973, 261,63 Гц Note_Cs4: Карта портов Wave_Generator (Trigger => O4 (1), Freq_Cnt => X "1606", wavegenCLK => CLK, signed (WaveOut) => WaveCs4); - 5638, 277,18 Гц Note_D4: Карта портов Wave_Generator (Trigger => O4 (2), Freq_Cnt => X "14C9", wavegenCLK => CLK, signed (WaveOut) => WaveD4); --5321, 293,66 Гц Note_Ds4: Карта портов Wave_Generator (Trigger => O4 (3), Freq_Cnt => X "139F", wavegenCLK => CLK, signed (WaveOut) => WaveDs4); - 5023, 311,13 Гц Note_E4: Карта портов Wave_Generator (Trigger => O4 (4), Freq_Cnt => X "1285", wavegenCLK => CLK, signed (WaveOut) => WaveE4); --4741, 329,63 Гц Примечание_F4: Карта портов Wave_Generator (Trigger => O4 (5), Freq_Cnt => X "117B", wavegenCLK => CLK, signed (WaveOut) => WaveF4); --4475, 349,23 Гц Note_Fs4: Карта портов Wave_Generator (Trigger => O4 (6), Freq_Cnt => X "1080", wavegenCLK => CLK, signed (WaveOut) => WaveFs4); - 4224, 369,99 Гц Note_G4: Карта портов Wave_Generator (Trigger => O4 (7), Freq_Cnt => X "0F92", wavegenCLK => CLK, signed (WaveOut) => WaveG4); --3986, 392,00 Гц Note_Gs4: Карта портов Wave_Generator (Trigger => O4 (8), Freq_Cnt => X "0EB3", wavegenCLK => CLK, signed (WaveOut) => WaveGs4); - 3763, 415,30 Гц Note_A4: Карта портов Wave_Generator (Trigger => O4 (9), Freq_Cnt => X "0DE0", wavegenCLK => CLK, signed (WaveOut) => WaveA4); --3552, 440,00 Гц Note_As4: Карта портов Wave_Generator (Trigger => O4 (10), Freq_Cnt => X "0D18", wavegenCLK => CLK, signed (WaveOut) => WaveAs4); - 3352, 466,16 Гц Note_B4: Карта портов Wave_Generator (Trigger => O4 (11), Freq_Cnt => X "0C5C", wavegenCLK => CLK, signed (WaveOut) => WaveB4); --3164, 493,88 Гц -------------------------------------------- -------------------------------------------------- --------------------------- Note_C5: Карта портов Wave_Generator (Trigger => O5 (0), Freq_Cnt => X "0BAB", wavegenCLK => CLK, подписанный (WaveOut) => WaveC5); --2987, 523,25 Гц Note_Cs5: Карта портов Wave_Generator (Trigger => O5 (1), Freq_Cnt => X "0B03", wavegenCLK => CLK, signed (WaveOut) => WaveCs5); - 2819, 554,37 Гц Note_D5: Карта портов Wave_Generator (Trigger => O5 (2), Freq_Cnt => X "0A65", wavegenCLK => CLK, signed (WaveOut) => WaveD5); --2661, 587,33 Гц Note_Ds5: Карта портов Wave_Generator (Trigger => O5 (3), Freq_Cnt => X "09D0", wavegenCLK => CLK, signed (WaveOut) => WaveDs5); - 2512, 622,25 Гц Note_E5: Карта портов Wave_Generator (Trigger => O5 (4), Freq_Cnt => X "0943", wavegenCLK => CLK, signed (WaveOut) => WaveE5); --2371, 659,25 Гц Note_F5: Карта портов Wave_Generator (Trigger => O5 (5), Freq_Cnt => X "08Be", wavegenCLK => CLK, signed (WaveOut) => WaveF5); --2238, 698,46 Гц Note_Fs5: Карта портов Wave_Generator (Trigger => O5 (6), Freq_Cnt => X "0840", wavegenCLK => CLK, signed (WaveOut) => WaveFs5); - 2112, 739,99 Гц Note_G5: Карта портов Wave_Generator (Trigger => O5 (7), Freq_Cnt => X "07CA", wavegenCLK => CLK, signed (WaveOut) => WaveG5); --1994, 783,99 Гц Note_Gs5: Карта портов Wave_Generator (Trigger => O5 (8), Freq_Cnt => X "075A", wavegenCLK => CLK, signed (WaveOut) => WaveGs5); - 1882, 830,61 Гц Note_A5: Карта портов Wave_Generator (Trigger => O5 (9), Freq_Cnt => X "06F0", wavegenCLK => CLK, signed (WaveOut) => WaveA5); --1776, 880,00 Гц Note_As5: Карта портов Wave_Generator (Trigger => O5 (10), Freq_Cnt => X "068C", wavegenCLK => CLK, signed (WaveOut) => WaveAs5); - 1676, 932,33 Гц Note_B5: Карта портов Wave_Generator (Trigger => O5 (11), Freq_Cnt => X "062E", wavegenCLK => CLK, signed (WaveOut) => WaveB5); --1582, 987,77 Гц Note_C6: Карта портов Wave_Generator (Trigger => O5 (12), Freq_Cnt => X "05D6", wavegenCLK => CLK, signed (WaveOut) => WaveC6); --1494, 1046,5 Гц ------------ логика выбора примечания ------------ 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; Выбор: процесс (WaveC4, WaveCs4, WaveD4, WaveDs4, WaveE4, WaveF4, WaveFs4, WaveG4, WaveGs4, WaveA4, WaveAs4, WaveB4, WaveC5, WaveCs5, WaveD5, WaveDs5, WaveE5, WaveF5, WaveFs5, WaveG5, WaveGs5, WaveGs5, WaveGs5, WaveGs5, WaveB5, WaveC6) начинаются, если (cntC6 = "00000"), то --------------- если сигналы не генерируются Wave0 <= "0000000000"; Волна1 <= "0000000000"; Волна2 <= "0000000000"; Волна3 <= "0000000000"; иначе if (O4 (0) = '1') then ------------------- примечание C4 проиграно Wave0 Wave0 Wave1 error Wave0 Wave1 Wave2 error Wave0 Wave1 Wave2 Wave3 error Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 Wave1 Wave2 Ошибка Wave3 Wave0 < = WaveC6; Волна1 <= "0000000000"; Волна2 <= "0000000000"; Волна3 Волна1 <= Волна C6; Волна2 <= "0000000000"; Wave3 Wave2 <= WaveC6; Wave3 Ошибка Wave3 Wave1 <= "0000000000"; Волна2 <= "0000000000"; Волна3 Волна2 <= "0000000000"; Wave3 Ошибка Wave3 <= '1'; конец корпуса; конец, если; конец, если; завершить процесс; ------------- сумматор синусоидальных волн -------------------- WaveSum <= STD_LOGIC_VECTOR (Wave0 + Wave1 + Wave2 + Wave3); --------- сделать синусоидальную волну положительной для pwm --------------------- positive); ------------- Генератор ШИМ --------------------- процесс (CLK) - количество переменных: без знака (от 1 до 0): = to_unsigned (0, 2); начать, если (возрастание_еджа (CLK)), то --count: = count + 1; --if (count = to_unsigned (4, 2)), затем --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 если; конец, если; конец процесса; конец Поведенческий;

4 Note Selector Самая сложная часть этого проекта - выбрать всего четыре частоты. Мы сделали это с целым набором операторов IF и использовали сигналы вместо переменных, чтобы процесс можно было моделировать и отлаживать. Мы пробовали другие методы с использованием переменных и циклов FOR, но столкнулись с ошибками во время выполнения. В итоге мы решили, что если это сработает, оставим это в покое. Не исправить, что не сломанный амирит?

Четыре выходных волны обозначены как Wave0, Wave1, Wave2, Wave3 - это то, что будет сложено вместе, чтобы сформировать окончательный результат.

Посмотрев на код, вы увидите набор сигналов, обозначенных C4, Cs4, D4, Ds4 и т. Д. Это 5-битные сигналы, которые принимают соответствующий триггер от O4 (октава 4) или O5 (октава 5) и заставляют их 5-битный для добавления.

Затем переменные cntC4, cntCs4 и т. Д. Представляют, сколько нот было сыграно ниже целевой ноты, включая целевую ноту. Например, если воспроизводятся C4, E4, G4, A # 4 и D5 (аккорд C9) cntC4 будет 1, cntE4 будет 2, cntG4 будет 3 и т. Д.

Затем, всякий раз, когда играется нота, будет проверяться счетчик целевой ноты, чтобы увидеть, к чему привязать сигнал ноты. Например, если играется нота D5 (что означает высокий уровень O5 (2)) и cntD5 равно 3, то в настоящее время воспроизводятся 3 ноты, из которых 2 ноты ниже, чем D5, поэтому мы подключим waveD5 к Wave2 (третья волна отсчет сигнала от Волны0). В качестве альтернативы, если cntD5 равно 5, то в настоящее время проигрывается 5 нот, из которых 4 ноты ниже, чем D5, поэтому мы просто оставим waveD5 висеть и ничего с ним не будем делать.

Затем операторы IF повторяются, чтобы охватить все 25 примечаний.

Сумматор амплитуды

После того, как выбраны 4 самые низкие волны, мы должны сложить их вместе. Причина, по которой мы добавим только четыре ноты вместе, заключается в том, что идея ШИМ, которую мы используем для нашего вывода, может иметь только определенное разрешение, пока ШИМ не будет работать слишком медленно и динамик не начнет улавливать прямоугольную волну ШИМ. Например, если бы мы использовали разрешение 8192 (13 бит), каждая из этих 8192 точек должна соответствовать нарастающему фронту встроенных часов. Итак, 100 МГц / 8192 = 12,2 кГц, что вполне приемлемо для человеческого слуха.

Фактическое сложение амплитуд очень просто, вам просто нужно убедиться, что он работает очень быстро.

Выход ШИМ

Рабочий цикл ШИМ будет представлять амплитуду нашей выходной волны в этот момент. Например, если у нас есть диапазон амплитуд от 0 до 128, 0 будет 0% рабочим циклом, 64 будет 50%, 128 будет 100% и т. Д. Этот ШИМ будет работать очень быстро (наш - 97,6 кГц), настолько быстро, что динамик не распознает отдельные прямоугольные волны и вместо этого смотрит на среднее напряжение, создавая наш «аналоговый» сигнал.

Файл ограничений

Возможно, вы подключили свое оборудование по-другому, поэтому просто убедитесь, что файл ограничений совпадает.

Шаг 5. Загрузка кода

Ниже приведен код в формате.txt и.vhd для Vivado. Wave_Generator - это подмодуль генератора волн, а Two_Octave_Synth - это верхний модуль со всем остальным.

Рекомендуемые: