Меня в свое время очень впечатлил этот пост о создании светомузыкального устройства на микроконтроллере в подарок любимой. И однажды пришло мое время сделать такой подарок. Учитывая отличия от автора упомянутого проекта в навыках и инструментарии; будучи сильно ограничен во времени подготовки (3-4 дня), я пошел другим путем и разработал свое музыкальное устройство для установки в купленную в сувенирном магазине шкатулку. Оно отличается более простой схемой и легкостью изготовления. В статье описываются подробности моего проекта и их мотивация. Осторожно, фотографии (всего около 1Мб).
Схема
Как видите, деталей очень мало. Питание в диапазоне +3..+4.8В от трех батареек типа AAA подходит без применения стабилизаторов как микроконтроллеру D1 (PIC16F753), так и усилителю DA1 (TDA7052A). Данная микросхема усилителя является уникальной в своем роде, потому что среди своих аналогов она требует минимальное количество внешних элементов. Применение усилителя мощности необходимо: при попытке подключить выход микроконтроллера напрямую к динамику, достаточную громкость получить не удастся.
Для работы усилителя необходим конденсатор C1 емкостью 220мкФ. Без конденсатора нельзя: если внутреннее сопротивление источника питания недостаточно мало, то звучать будет тихо и с сильными искажениями. Также необходим конденсатор C4 для развязки аудиосигнала по постоянному току. Подстроечным резистором R2 регулируется громкость. R1 ограничивает диапазон регулировки. Совместно с конденсатором C3 он также образует фильтр низких частот. В принципе C3 можно не ставить. Я поначалу так и хотел сделать, но потом мне показалось, что для уменьшения искажений звука лучше убрать из него ультразвуковые частоты, поставив C3.
TDA7052A (в отличие от TDA7052) имеет отдельный вход управления громкостью путем подачи на него соответствующего напряжения (на схеме не показан). Но попытка использовать его нисколько не упрощает схему и не улучшает ее работу. К счастью, при оставлении этого входа неподключенным нормальная работа усилителя не нарушается.
Пару слов о выборе микроконтроллера. Самый главный критерий — диапазон напряжения питания. Когда батарея почти разряжена, напряжение на ней проседает до 3В (по 1В на элемент). В свежем же состоянии напряжение может подниматься до +4.8В и даже более. К сожалению, более современные 16-битные микроконтроллеры, имеющие высокую скорость и много памяти, обычно требуют питание в диапазоне +2.7..+3.6В. Чтобы понизить напряжение, пришлось бы применять стабилизатор, причем не любой, а с малым падением напряжения (Low Drop-out), учитывая напряжение на батарее под конец ее службы. Я решил не усложнять. Из контроллеров фирмы Microchip (с ними у меня больше всего опыта и имеется программатор), поэтому, подходят только 8-битные. Также можно было бы использовать 16-битные из серии PIC24F. В следующей музыкальной шкатулке я, наверное, так и сделаю. Все-таки PIC16F753 очень уж тесноват как по скорости, так и по объему памяти. Но зато у него имеется встроенный 9-битный ЦАП, что очень подходит для синтеза звука.
Для простоты и надежности я также решил не использовать какие-либо схемы управления питанием и энергосбережением. Простой выключатель (на схеме не показан) разрывает цепь батареи, и все.
Сборка
Покупаем подходящих размеров и вида шкатулочку. Я нашел вот такую. К сожалению, прямоугольной шкатулки не нашлось, с ней было бы проще работать.
Печатную плату я не делал. Дома нет для этого условий, а на заводе заказывать — слишком долго и дорого. Поэтому собирал схему на макетной плате. Удалось вырезать лобзиком кусок платы по размерам шкатулки. Размеры и форма предопределили компоновку деталей. Вот фотка на промежуточных стадиях сборки:
Проводами на время написания и отладки прошивки подключен программатор. Микросхемы поставил на панельках, и не зря: из купленных на радиобазаре двух микросхем усилителя одна оказалась битой. Удалось избежать ее перепайки.
Вид с обратной стороны платы. Как видите, длинные или пересекающиеся соединения выполнены проводом МГТФ, а короткие — обрезанными ножками от конденсаторов и резисторов. После проверки работоспособности схемы на тестовой прошивке можно приступать к разработке и отладке основной программы. Подробнее о ней ниже.
После того, как прошивка полностью отлажена, отключаем программатор, перепаиваем динамик и ставим выключатель. Последняя проверка.
После этого нужно смонтировать все в шкатулку и закрыть сверху картонкой для эстетики. Из черного картона по форме шкатулки вырезаем и сгибаем такую конструкцию.
Прорезаем ножиком дырку для выключателя. Шилом протыкаются дырочки над динамиком. Далее приклеиваем выключатель к картонке и динамик на штатное место платы:
Окончательный вид открытой шкатулки:
Программа для PIC и подготовка музыки
1. Синтез звука
Имея 9-битный ЦАП, в принципе, можно получить достаточно сложный звуковой сигнал, однако в случае PIC16F753 возможности ограничены из-за малого размера памяти программ микроконтроллера — всего 2048 слов. Как показывает опыт, даже простая программа-проигрыватель, написанная на ассемблере и оптимизированная по размеру кода, занимает около 1000 слов, так что для нот остается совсем немного. И совсем ничего не остается для хранения сэмплов при использовании такого метода синтеза звука, как Wavetable. Использованию же таких мощных методов, как FM-синтез, препятствует недостаточная скорость процессора и отсутствие в нем аппаратного умножителя. Поэтому остается только синтез прямоугольников — симметричных, либо с переменной скважностью. Второй вариант дает некоторое разнообразие тембров — см., например, сборник «This is Tritone 2» (также имеется на Youtube). Этот метод я и реализовал в шкатулке. Удалось реализовать полифонический звук: 4 независимых звуковых канала. Можно управлять громкостью каждого канала.
Находим частоту, соответствующую нужной ноте, по формуле равномерной темперации: f = 440*2^(n/12), где n — номер ноты в полутонах, n=0 соответствует «ля» первой октавы. Так как у нас 4 канала, то нужно одновременно генерировать 4 сигнала и суммировать их перед выводом. Наиболее распространенное решение — использовать для всех каналов общую частоту дискретизации. При этом процессор через равные промежутки времени вычисляет выходной отсчет для каждого канала. Полученные значения суммируются и подаются на ЦАП.
Желаемые периоды прямоугольных сигналов, в общем случае, не являются кратными периоду дискретизации. Скажем, для ноты «ля» второй октавы у нас n=12, f=880 Гц. При частоте дискретизации Fs=27777.8Гц каждый период сигнала должен длиться ~31.57 выходных отсчетов, что нереализуемо. Здесь есть три выхода:
- Округлить период до целого числа отсчетов. При этом получаемый период будет отличаться от заданного, т.е. музыка будет фальшивить.
- Варьировать длительность периода в пределах плюс-минус одного отсчета так, чтобы средний период получаемого сигнала был равен 1/f. С точки зрения теории обработки сигналов это эквивалентно интерполяции методом ближайшего соседа. В результате в звуке возникают существенные негармонические искажения, в спектре появляются пики на посторонних частотах. На слух сигнал просто становится «грязным».
- Провести интерполяцию по Шеннону. Этот подход исключает фальшь и дает наилучшее качество звука, но в 8-битных микроконтроллерах неприемлем из-за сложности вычислений.
Так что на практике можно выбирать между вариантами 1) и 2). Оба они используются при программном синтезе многоканальной музыки на 1-битном звуковом выходе в таких компьютерах, как ZX Spectrum. Я лично предпочитаю вариант 1). При достаточно высокой частоте дискретизации, на не слишком высоких нотах, округление частоты незначительно, и фальшь практически незаметна.
Частота дискретизации должна быть дольной от тактовой частоты процессора и достаточно низкой, чтобы процессор успел провести все необходимые вычисления для каждого выходного отсчета. С другой стороны, она должна быть по возможности высокой, чтобы уменьшить фальшь и расширить диапазон воспроизводимых нот. Для работы программы-плеера необходима таблица с периодами каждой ноты. Для расчета этой таблицы и вычисления отклонения получаемых частот сигнала от желаемых была написана
fs = 2e6/72;
notes = -24:26;
f = zeros(size(notes));
d = zeros(size(notes));
fa = zeros(size(notes));
ndifs = zeros(size(notes));
for i=1:length(notes)
f(i) = 440*2^(notes(i)/12);
k = round(fs/f(i));
fa(i) = fs/k;
na = 12*log2(fa(i)/440);
ndifs(i) = na-notes(i);
d(i) = k;
fprintf('%10.1f %10.1f %3d %4.2fn',f(i),fa(i),notes(i),ndifs(i));
end
plot(ndifs);
ylim([-0.5 0.5]);
fprintf('nn');
for i=1:length(notes)
fprintf('tretlwtH''%02X''n',d(i));
end
Для каждой ноты программа вычисляет период сигнала в отсчетах на выбранной частоте дискретизации, округляет его до целого и производит обратный пересчет в номер ноты. В этих логарифмических единицах и оценивается отклонение, график которого выводится на экран:
Можно поэкспериментировать, меняя частоту дискретизации, то есть количество тактов процессора, приходящихся на один ее период. Эмпирически я подобрал коэффициент 72, который дает минимально достижимые отклонения нот в заданном диапазоне.
2. Архитектура прошивки
У PIC16F753 имеется три таймера, но только таймер 2 можно запрограммировать на генерацию прерываний с заданным периодом. С его помощью получаем прерывания на частоте дискретизации звука, т.е. каждые 72 такта процессора. Процедура обработки прерываний вычисляет очередное значение для вывода на ЦАП. Чтобы избежать искажений звука, необходимо обновлять уровень на ЦАП через строго равные промежутки времени. Так как вычисления могут занимать различное время в зависимости от состояния программы, здесь есть два варианта. Первый — «подравнять» все ветки вычислений, чтобы они исполнялись за одинаковое число тактов. Второй — сразу вывести в ЦАП значение, рассчитанное во время обработки предыдущего прерывания, а потом уже рассчитать значение для вывода в следующем прерывании. Я избрал второй путь. При этом процедура обработки прерывания выполняется каждый раз за разное время, но зато между прерываниями остается в среднем больше процессорного времени для фоновых вычислений.
По прерываниям работает генерация стационарных сигналов — прямоугольников неизменной частоты, скважности и амплитуды. Эти параметры хранятся в соответствующих ячейках памяти. При работе прошивки прерывания никогда не запрещаются. Это обеспечивает отсутствие в звуке каких-либо неоднородностей и разрывов, за исключением моментов переключения параметров генерации. Получается такой же режим работы, как если бы в системе был звуковой чип, наподобие Atari Pokey или AY-3-8910: эти чипы тоже формируют на каждом канале стационарные сигналы до тех пор, пока процессор не изменит значения параметров во внутренних регистрах этих чипов.
Обновление параметров генерации осуществляется процедурами, работающими в фоновом режиме (т.е. между прерываниями). Здесь я задействовал таймер-1 для обеспечения периодичности вызова процедуры обновления параметров — 50Гц. Такая же или близкая частота используется для этих целей в музыкальных проигрывателях на 8-битных компьютерах.
В остальном архитектура прошивки определяется представлением музыки в памяти. Я пошел по принципу трекерной музыки, по которым в основном создавалась музыка на 8-битных компьютерах. Не буду вдаваться здесь в детали, материалов на эту тему много в интернете.
3. Подготовка музыки
Чип-музыку надо в чем-то редактировать, и на сегодняшний день один из наиболее легких путей — это использовать Open Modplug Tracker. Нужно подготовить несколько сэмплов, которые звучат хотя бы приблизительно похоже на звучание чипа, и создать с их помощью музыку в трекере, используя не более 4 каналов. При этом также нельзя пользоваться спецэффектами трекера, кроме тех, которые реализованы в нашем чип-плеере. В результате создается трекерный файл в формате .it. Я также написал программу-конвертор, которая конвертирует ноты из it-файла в формат, распознаваемый моей прошивкой PIC16F753. Конвертор ругается, если встретит в it-файле ноты за пределами диапазона или неподдерживаемые прошивкой команды. Инструменты из it-файла полностью игнорируются конвертором. Они нужны только для контроля звучания музыки во время редактирования.
Сэмплы прямоугольников различной скважности, которые нужны во время редактирования музыки, я сгенерировал специальными программами на Матлабе. Но это было проделано давно в рамках другого проекта — конверсия музыки с ZX Spectrum, так что сейчас я просто взял инструменты из тех старых модулей и сделал на их базе музыку для шкатулки.
В результате работы конвертора создается текстовый файл в формате ассемблера PIC. Его содержимое нужно скопировать в конец исходника прошивки и скомпилировать. В результате получится прошивка в виде hex-файла с нужной музыкой.
К сожалению, у меня нет таланта композитора или аранжировщика, поэтому удалось лишь завести кусок известной вещи В. Моцарта. Часть возможностей плеера при этом даже осталась неиспользованной. Было бы несложно добавить в плеер шумовые эффекты и многое другое, но опять-таки, где взять человека, который сможет на них сделать красивое звучание?
Если среди читателей найдутся желающие и способные создавать красивую чип-музыку для шкатулок и тому подобных музыкальных поделок на микроконтроллерах — буду рад сотрудничеству.
Исходники
Полный исходный текст прошивки, программы-конвертора музыки, а также it-файл с той музыкой, которую я использовал в данной шкатулке, можно скачать с Github.
Автор: MichaelBorisov