Как известно, каждый пьезоизлучатель уникален и производится со скрытым талантом. Наша задача его увидеть. Так получилось, что у моего пьезоизлучателя врожденный талант к музыке. Голосом он, конечно, не вышел, пассивный, маломощный, на АЧХ вообще лучше не смотреть, зато желания воспроизводить музыку хоть отбавляй. Воспроизведение мелодии понадобилось для одного из проектов. Задача простая: действия пользователя должны сопровождаться звуками. Если нажатие кнопки это стандартный «БИП», то разряд батареи уже «ТУРУРУМ», включение «ТУМТУМ-ТУТУ» и т.д. Оказалось, что звуки в моей голове и звуки, получаемые на пьезоизлучателе совершенно не похожи друг на друга. Пришлось немного углубиться в тему.
Сперва небольшой обзор того, что было изучено. За основу взята идея, изложенная китайским коллегой, есть английский перевод. Также стоит упомянуть материалы статьи. Вот что удалось узнать.
Для воспроизведения мелодий необходимо использовать информацию: ноты, длительность нот, темп;
Для воспроизведения нот и установки их длительности необходимо использовать таймеры;
Алгоритм должен быть таким, чтобы не останавливать выполнения основной программы.
И этого достаточно, чтобы сделать небольшую библиотеку для воспроизведения мелодии по нотам. Вот результат:
Для работы с библиотекой необходимо иметь минимальные знания нотной грамоты. Музыкальная нотация присваивает нотам буквенные обозначения c, d, e, f и т.д. Вместо букв можно использовать цифры, за ноль принимаем ноту ДО малой октавы.
Частота, Гц | ||||||
Нота | Малая октава | Вторая октава | ||||
ДО | 0 | c, his | 130,8 | 24 | c2, his2 | 523,2 |
ДО ♯, РЕ ♭ | 1 | cis, des | 138,6 | 25 | cis2, des2 | 554,4 |
РЕ | 2 | d | 146,8 | 26 | d2 | 587,3 |
РЕ ♯, МИ ♭ | 3 | dis, es | 155,6 | 27 | dis2, es2 | 622,3 |
МИ | 4 | e, fes | 164,8 | 28 | e2, fes2 | 659,3 |
ФА | 5 | f, eis | 174,6 | 29 | f2, eis2 | 698,5 |
ФА ♯, СОЛЬ ♭ | 6 | fis, ges | 185 | 30 | fis2, ges2 | 740 |
СОЛЬ | 7 | g | 196 | 31 | g2 | 784 |
СОЛЬ ♯, ЛЯ ♭ | 8 | gis, as | 207,7 | 32 | gis2, as2 | 830,6 |
ЛЯ | 9 | a | 220 | 33 | a2 | 880 |
ЛЯ ♯, СИ ♭ | 10 | ais, b | 233,1 | 34 | ais2, b2 | 932,3 |
СИ | 11 | h, ces1 | 247 | 35 | h2, ces3 | 987,8 |
Нота | Первая октава | Третья октава | ||||
ДО | 12 | c1, his1 | 261,6 | 36 | c3, his3 | 1046,5 |
ДО ♯, РЕ ♭ | 13 | cis1, des1 | 277,2 | 37 | cis3, des3 | 1108,7 |
РЕ | 14 | d1 | 293,7 | 38 | d3 | 1174,7 |
РЕ ♯, МИ ♭ | 15 | dis1, es1 | 311,1 | 39 | dis3, es3 | 1244,5 |
МИ | 16 | e1, fes1 | 329,6 | 40 | e3, fes3 | 1318,5 |
ФА | 17 | f1, eis1 | 349,2 | 41 | f3, eis3 | 1396,9 |
ФА ♯, СОЛЬ ♭ | 18 | fis1, ges1 | 370 | 42 | fis3, ges3 | 1480 |
СОЛЬ | 19 | g1 | 392 | 43 | g3 | 1568 |
СОЛЬ ♯, ЛЯ ♭ | 20 | gis1, as1 | 415,3 | 44 | gis3, as3 | 1661,2 |
ЛЯ | 21 | a1 | 440 | 45 | a3 | 1760 |
ЛЯ ♯, СИ ♭ | 22 | ais1, b1 | 466,2 | 46 | ais3, b3 | 1864,7 |
СИ | 23 | h1, ces2 | 493,9 | 47 | h3, ces4 | 1975,6 |
Допустимую длительность нот ограничим в диапазоне от 1 до 1/16.
Условный номер | Длительность ноты/паузы |
1 | шестнадцатая |
2 | восьмая |
4 | четвертая |
8 | половинная |
16 | целая |
Таким образом, чтобы составить массив входных данных, необходимо указать перечень нот, их длительность и скорость воспроизведения мелодии. Массив мелодии можно записать как uint8_t MUSIC[] = {23, 23, 24, 26 … }, массив длительностей как uint8_t DURATION[] = {4, 4, 4, 4 … }, темп uint8_t TEMPO = 140. Либо с использованием обозначений нот uint8_t MUSIC[] = {h1, h1, c2, d2…}, uint8_t DURATION[] = {T_1_4, T_1_4, T_1_4, T_1_4 … }.
На рисунке номерами сверху обозначены ноты и их длительности согласно таблицам.
Возможности пьезоизлучателя для генерации звука весьма ограничены, получить низкочастотные колебания не удастся. Как правило, максимум громкости излучателя попадает в диапазон частот от двух до четырех кГц. Для используемого мною HPM14A громкость звука на частотах менее 500 Гц будет примерно 40 – 50 дБ.
Ноты, их длительность и темп мелодии определяют временные интервалы, в течение которых производится генерация колебаний. Чтобы лучше понять суть вопроса, взглянем на спектр мелодии, который синтезируется при помощи программы MuseScore 3. Для просмотра спектра используется программа Audacity.
Каждая нота представляет собой «треугольник» определенной высоты. Программа синтезирует затухающий звук, как это происходит при колебаниях струны. Сгенерировать сигнал такой формы на микроконтроллере достаточно сложно. Существенно упростим себе задачу. Каждую ноту будем задавать при помощи интервала сигнала и интервала тишины. Сумма двух интервалов составляет длительность звучания ноты. Интервал сигнала – ШИМ заданной частоты, тишины – нулевой уровень напряжения. Вставка нулевого интервала позволяет различать многократное воспроизведение одной и той же ноты.
Для решения задачи достаточно двух таймеров. Рассмотрим генерацию частоты нот. Возьмем ноту ДО малой октавы в качестве примера. При тактировании таймера частотой 4 МГц, значения для регистра перезагрузки считаются следующим образом.
В регистр сравнения необходимо записать половину рассчитанной величины, чтобы сигнал ШИМ представлял меандр.
Интервалы воспроизведения и тишины задаются вторым таймером, значения которого рассчитываются исходя из длительности нот и темпа мелодии. Темп определяет число ударов метронома в минуту. Используется понятие BPM (beats per minute, удары в минуту). BPM — это количество четвертных нот в минуту, например, 120 BPM означает, что в минуту играется 120 четвертных. Соответственно расчет длительности необходимо выполнять относительно длительности нот в ¼.
Например, темп равен 140. Длительность воспроизведения четвертной ноты составляет:
В итоге интервал воспроизведения выбран как play_time * 9/10, интервал тишины, как play_time * 1/10.
В завершение упомяну о работе с библиотекой. В моем проекте пьезоизлучатель выделен в класс с простым интерфейсом управления и внутренней машиной состояний. Переход между состояниями происходит по прерыванию от таймера интервалов (duration timer). Такая структура необходима для того, чтобы не тормозить остальные задачи программы, минимизировать время пребывания в функции воспроизведения ноты.
Для воспроизведения мелодии достаточно вызвать функцию PlayMusic(...), например так: Buzzer.PlayMusic(MUSIC_WIND_OF_CHANGE, DURATION_WIND_OF_CHANGE, TEMPO_WIND_OF_CHANGE, SIZE_WIND_OF_CHANGE). А затем в прерывании от таймера длительности вызывать функцию PlayNote(). Если устройству требуется переход в сон, то текущее состояние пьезоизлучателя возвращает функция GetState(), а функцией StopMusic() можно принудительно остановить воспроизведение.
Свой проект я запустил на микроконтроллере STM32, однако библиотека построена так, чтобы не зависеть от производителя железа. Если на вашем микроконтроллере имеется два 16-и битных таймера, причем один из них поддерживает генерацию ШИМ сигнала, то код вам подходит на 80%. Необходимо проверить, что воспроизведение тех или иных нот, а также длительностей нот не приводит к переполнению счета. При частоте тактирования таймера нот 4 МГц и таймера длительности 10 кГц всё должно получиться. Ножку для пьезоизлучателя требуется настроить в режим альтернативной функции. Увеличить громкость можно путем коммутации более высоких напряжений при помощи внешней схемотехники. Мной испробованы несколько мелодий, с которыми библиотека успешно справилась. Вместе с тем допускаю наличие ошибок, дайте знать, если найдете. Файлы библиотеки можно найти по ссылке.
История закончилась тем, что приехал пьезик на телевизионное шоу, выступил, получил одобрение. И все шло хорошо, пока не появился старик Монтана. Вот незадача, снова изобрел велосипед…