Как реализовать звук на attiny88
Related items
- Библиотека для опроса кнопок
- Работа с SD картой. Воспроизведение wav файла. Ч3
- Работа с SD картой. Библиотека Petit FatFs. Ч2
- Работа с SD картой. Подключение к микроконтроллеру. Ч1
- AVR315: Использование TWI модуля в качестве ведущего I2C устройства
Comments
n8,b2, n8,c3, n8,b2, n8,g2, n8,e2, n8,b2, n8,g2, n8,b2, n8,b1, n8,b2, n8,g2, n8,b2,
n8,a2, n8,b2, n8,a2, n8,xf2, n8,xd2, n8,a2, n8,xf2, n8,a2, n8,b1, n8,a2, n8,xf2, n8,a2,
n8,g2, n8,a2, n8,g2, n8,e2, n8,b1, n8,g2, n8,xf2, n8,g2, n8,xf2, n8,c2, n8,a1, n8,xf2,
n8,xd2, n8,e2, n8,xd2, n8,b1, n8,xf1, n8,xd2, n2,e2, n4,p,
0
>;
unsigned int Augustin[] = 16,2,
n4,g1, n8,g1, n8,a1, n8,g1, n8,f1, n4,e1, n4,c1, n4,c1, n4,d1, n4,g0, n4,g0, n4,e1, n4,c1, n4,c1,
n4,g1, n8,g1, n8,a1, n8,g1, n8,f1, n4,e1, n4,c1, n4,c1, n4,d1, n4,g0, n4,g0, n2,c1, n2,p,
n4,d1, n4,g0, n4,g0, n4,e1, n4,c1, n4,c1, n4,d1, n4,g0, n4,g0, n4,e1, n4,c1, n4,c1,
n4,g1, n8,g1, n8,a1, n8,g1, n8,f1, n4,e1, n4,c1, n4,c1, n4,d1, n4,g0, n4,g0, n2,c1, n2,p,
0
>;
#define C2 fn(523)
#define Cis2 fn(554)
#define D2 fn(587)
#define Dis2 fn(622)
#define E2 fn(659)
#define F2 fn(698)
#define Fis2 fn(740)
#define G2 fn(784)
#define Gis2 fn(831)
#define A2 fn(880)
#define Ais2 fn(932)
#define H2 fn(988)
#define C3 fn(1047)
#define Cis3 fn(1109)
#define D3 fn(1175)
#define Dis3 fn(1245)
#define E3 fn(1319)
#define F3 fn(1397)
#define Fis3 fn(1480)
#define G3 fn(1568)
#define Gis3 fn(1661)
#define A3 fn(1720)
#define Ais3 fn(1865)
#define H3 fn(1976)
Плата ATtiny88. Начало работы

Из-за кризиса полупроводников цены на микроконтроллеры очень сильно выросли: 2 бакса с бесплатной доставкой за нашу любимую Arduino Nano превратились в… 8 баксов! Спасением может стать esp8266 (урок про неё), но не всегда она может заменить нанку. И тут на сцену выходит плата на базе МК ATtiny88:


Всего 1.5$, а на вид – полный аналог нанки! Так ли это? Рассмотрим подробнее.
Купить на Aliexpress
Дёшево купить плату на ATtiny88 можно тут: ссылка, ссылка, ссылка, ссылка.
Возможности
- 16 МГц кварц
- ISP хэдер для программатора
- USB-UART отсутствует, USB подключен к микроконтроллеру на пины 0 и 1
- Соответственно прошивка по USB, а также работа в качестве HID устройства
ATtiny88 имеет на борту:
- Flash память: 8 кБ (6780 Байт с учетом загрузчика)
- SRAM память: 512 Б
- EEPROM память: 64 Б
- Таймеры: 1х 8 Бит без ШИМ + 1х полноценный 16 БИТ
- GPIO: 26 пинов
- Прерывания: 2х INT, все пины – PCINT
- ШИМ: 2 пина
- АЦП: 8 пинов, внешнего опорного нет
- Интерфейсы: I2C, SPI
Не имеет на борту:
- Аппаратный UART, про Serial можно забыть
- Аппаратное умножение. Вычисления будут медленнее
Начало работы
Нужно установить ядро ATtinyCore:
- Запустить Arduino IDE, перейти в Файл/Настройки/
- В окошко “Дополнительные ссылки…” Вставить http://drazzy.com/package_drazzy.com_index.json
- Нажать ОК
- Перейти в Инструменты/Плата/Менеджер плат… Начать вводить в поиске “attiny”. Выбрать и установить ATTinyCore
- Теперь в списке плат Инструменты/Плата/ появится семейство плат ATTinyCore! Выбираем ATtiny88 (Micronucleous, MH-ET t88 w/16MHz CLOCK). Настройки оставляем стандартные, подробнее о них написано на странице ядра (ссылка выше).
- Также нужно установить драйвера: скачать можно по этой ссылке, на официальном GitHub проекта (в разделе Релизы, вот прямая ссылка на архив), либо с моего FTP . Драйвера есть для Win, MacOS и Linux.
- Пользователям Linux читать здесь
Прошивка загружается следующим образом: ПЛАТУ НЕ ПОДКЛЮЧАЕМ, ПОРТ НЕ ВЫБИРАЕМ, нажимаем загрузка, ждём компиляции. Появится надпись “подключите плату”. Втыкаем плату в USB и прошивка загружается.
Как реализовать звук на attiny88
Музыка в Arduino может быть фоновой. У меня появилась идея написать новую музыкальную программу или библиотеку для музыкальной программы.
Идеи появляются не на пустом месте. Первый музыкальный автомат я собрал 30 лет назад, в нём был генератор, подстроечные резисторы и дешифратор. Недостатки этой схемы побудили меня разработать свою схему и тогда я разработал цифровой таймер-счётчик на D-триггерах, управляемый по программе из прожигаемого ПЗУ. Последние 8 лет я программирую микроконтроллеры. Первый мой музыкальный автомат был разработан на Ассемблере для м-к ATtiny13. Тогда, почему-то, захотелось написать генератор нот без использования таймеров-счётчиков м-к. И это получилось! Писал для Arduino с использованием таймеров-счётчиков м-к. Это просто. Недавно я закончил серьёзный проект на ATmega328. В этом проекте задачи добавлялись несколько раз так, что в конце, чтобы всё заработало, пришлось оптимизировать программу по объёму, и главное по использованию памяти, переписать насколько библиотек и разработать 2 свои. Теперь я более оптимистично смотрю на ресурсы Ардуино.
Сегодня появилась идея к которой не приступишь в лоб. Нужна концепция. Вот об этом и буду писать.
- Музыка в Arduino может быть фоновой, она может звучать в то время как выполняется основная программа. Должна быть многозадачность. Значит надо будет использовать прерывания.
- Одного байта достаточно чтобы хранить код ноты и её длительность. Отведём 3 бита под длительности 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, «Конец». 5 бит под коды нот (31 нота + пауза). Номер паузы 0.
- Так как массив нот будет храниться в шифрованном виде, готовить этот массив надо будет во вспомогательной программе, например в Open Office.
- Так как оперативной памяти в м-к мало, коды нот будем хранить в Flash памяти программ м-к.
- Так как музыкальное произведение чаще всего состоит из повторяющихся фрагментов, в программе должны быть предусмотрены вызовы этих фрагментов.
- В программе должны быть предусмотрены команды управления воспроизведением (вперёд, назад, стоп, по номеру, повтор, повтор всего).
Кажется, с концепцией определились.
Возможная техническая реализация.
Длительность ноты.
Пусть WDT (Watchdog) отвечает за длительность ноты. Распишем коды длительностей:
| Длительность | Сек | Команда | Код команды | Код 3 бита | Инверсный код |
|---|---|---|---|---|---|
| 1/64 | 0.015625 | WDTCSR = (1 << WDIE ); | 0b01000000 | 000 | 7 |
| 1/32 | 0 .03125 | WDTCSR = (1 << WDP0) | (1 << WDIE ); | 0b01000001 | 001 | 6 |
| 1/16 | 0.0625 | WDTCSR = (1 << WDP1) | (1 << WDIE ); | 0b01000010 | 010 | 5 |
| 1/8 | 0.125 | WDTCSR = (1 << WDP0) | (1 << WDP1) | (1 << WDIE ); | 0b01000011 | 011 | 4 |
| 1/4 | 0.25 | WDTCSR = (1 << WDP2) | (1 << WDIE ); | 0b01000100 | 100 | 3 |
| 1/2 | 0.5 | WDTCSR = (1 << WDP0) | (1 << WDP2) | (1 << WDIE ); | 0b01000101 | 101 | 2 |
| 1 | 1 | WDTCSR = (1 << WDP1) | (1 << WDP2) | (1 << WDIE ); | 0b01000110 | 110 | 1 |
| Конец | 111 | 0 |
Табл. 1. Коды длительностей нот.
Лист. 1. Биты регистра WDTCSR микроконтроллера ATmega328
Частота ноты.
Пусть 16-ти битный таймер отвечает за частоту ноты (высоту тона).
В м-к ATmega328 один 16-ти битный таймер-счётчик — это «Timer/Counter1». Timer/Counter1 (ТС1) может работать в режиме генератора частоты.
Таймер-счётчик ТС1 микроконтроллера, работая в режиме СТС (сравнение и сброс таймера при совпадении, один из четырёх режимов), ведёт подсчёт тактовых импульсов до совпадения с числом, заданным программистом в любом из регистров совпадения(OCR1A или OCR1B). В случае совпадения, сигнал, на выходе счётчика меняется на противоположный и счёт начинается сначала. Таймер/счётчик TC1 имеет выходы на порты микроконтроллера PB1 и PB2 (9 и 10 pin Arduino или 15 и 16 pin м-к ATmega328-PU). Таймер-счётчик приводятся в действие от системного тактового генератора, работающего на частоте 16 МГц. Если записать в регистр совпадения 0, Таймер-счётчик делит частоту на входе на 2. Если мы хотим получить с помощью таймера-счётчика, заданную частоту, мы должны записать в 16-ти битный регистр совпадения (OCR1A или OCR1B) коэффициент деления, который можно вычислить по формуле, Формуле. 1.

Формула. 1. Коэффициент деления ТС1,
В datashit на м-к ATmega328 в формуле 1 записано (1+k), но это в нашей задаче не принципиально.
Частота любой музыкальной ноты F может быть вычислена по формуле Ф.2, где N порядковый номер ноты.

Формула. 2. Частота любой музыкальной ноты.
Нота ЛЯ первой октавы имеет порядковый номер 48 и её частота, вычисленная по этой формуле равна 440 Гц. В октаве 7 нот (ДО, РЕ, МИ, ФА, СОЛЬ, ЛЯ, СИ), однако, они распределены по частоте неравномерно. На фортепианной клавиатуре вы видели что есть белые и чёрные клавиши. Белые клавиши мы уже перечислили, это основные ноты. Чёрные клавиши дополняют белые так, что все ноты в октаве, а их теперь 12, распределяются по частоте равномерно, следуя математическому закону по Формуле.2.

Перечислим все 12 нот одной октавы: ДО ДО# РЕ РЕ# МИ ФА ФА# СОЛЬ СОЛЬ# ЛЯ ЛЯ# СИ Знак # читается как диез и ставится на нотной линейке перед нотой или в начале строки. Отменяется знаком бекар . Теперь должно быть понятно, что нота ЛЯ второй октавы имеет номер 60 и её частота 880 Гц. Очевидно, что частота двух одноименных нот из соседних октав отличается в ровно 2 раза. Эти ноты воспринимаются на слух, как очень похожие, только одна выше другой. В программе на языке Си для Ардуино, чтобы извлечь желаемую ноту, мы должны будем записать (присвоить) в 16-ти битный регистр совпадения (OCR1A или OCR1B) коэффициент деления вычисленный для этой ноты.
Для автоматизации расчёта коэффициентов деления для всех нот мы воспользовались электронной таблицей Google документы (Можно использовать LibreOffice Calc). В результате получилась таблица табл. 2.
| A | B | C | D | E | F | |
|---|---|---|---|---|---|---|
| 1 | № | N | Октава | Нота | F Gz | К дел. |
| 2 | 1 | 27 | 0 | До | 131 | 61156 |
| 3 | 2 | 28 | 0 | До# | 139 | 57724 |
| 4 | 3 | 29 | 0 | Ре | 147 | 54484 |
| 5 | 4 | 30 | 0 | Ре# | 156 | 51426 |
| 6 | 5 | 31 | 0 | Ми | 165 | 48540 |
| 7 | 6 | 32 | 0 | Фа | 175 | 45815 |
| 8 | 7 | 33 | 0 | Фа# | 185 | 43244 |
| 9 | 8 | 34 | 0 | Соль | 196 | 40817 |
| 10 | 9 | 35 | 0 | Соль# | 208 | 38526 |
| 11 | 10 | 36 | 0 | Ля | 220 | 36364 |
| 12 | 11 | 37 | 0 | Ля# | 233 | 34323 |
| 13 | 12 | 38 | 0 | Си | 247 | 32396 |
| 14 | 13 | 39 | 1 | До | 262 | 30578 |
| 15 | 14 | 40 | 1 | До# | 277 | 28862 |
| 16 | 15 | 41 | 1 | Ре | 294 | 27242 |
| 17 | 16 | 42 | 1 | Ре# | 311 | 25713 |
| 18 | 17 | 43 | 1 | Ми | 330 | 24270 |
| 19 | 18 | 44 | 1 | Фа | 349 | 22908 |
| 20 | 19 | 45 | 1 | Фа# | 370 | 21622 |
| 21 | 20 | 46 | 1 | Соль | 392 | 20408 |
| 22 | 21 | 47 | 1 | Соль# | 415 | 19263 |
| 23 | 22 | 48 | 1 | Ля | 440 | 18182 |
| 24 | 23 | 49 | 1 | Ля# | 466 | 17161 |
| 25 | 24 | 50 | 1 | Си | 494 | 16198 |
| 26 | 25 | 51 | 2 | До | 523 | 15289 |
| 27 | 26 | 52 | 2 | До# | 554 | 14431 |
| 28 | 27 | 53 | 2 | Ре | 587 | 13621 |
| 29 | 28 | 54 | 2 | Ре# | 622 | 12856 |
| 30 | 29 | 55 | 2 | Ми | 659 | 12135 |
| 31 | 30 | 56 | 2 | Фа | 698 | 11454 |
| 32 | 31 | 57 | 2 | Фа# | 740 | 10811 |
| 33 | 32 | 58 | 2 | Соль | 784 | 10204 |
| 34 | 33 | 59 | 2 | Соль# | 831 | 9631 |
| 35 | 34 | 60 | 2 | Ля | 880 | 9091 |
| 36 | 35 | 61 | 2 | Ля# | 932 | 8581 |
| 37 | 36 | 62 | 2 | Си | 988 | 8099 |
| 38 | 37 | 63 | 3 | До | 1047 | 7645 |
| 39 | 38 | 64 | 3 | До# | 1109 | 7215 |
| 40 | 39 | 65 | 3 | Ре | 1175 | 6810 |
| 41 | 40 | 66 | 3 | Ре# | 1245 | 6428 |
| 42 | 41 | 67 | 3 | Ми | 1319 | 6067 |
| 43 | 42 | 68 | 3 | Фа | 1397 | 5727 |
| 44 | 43 | 69 | 3 | Фа# | 1480 | 5405 |
| 45 | 44 | 70 | 3 | Соль | 1568 | 5102 |
| 46 | 45 | 71 | 3 | Соль# | 1661 | 4816 |
| 47 | 46 | 72 | 3 | Ля | 1760 | 4545 |
| 48 | 47 | 73 | 3 | Ля# | 1865 | 4290 |
| 49 | 48 | 74 | 3 | Си | 1976 | 4050 |
Табл. 2. Коэффициенты деления для всех нот.
В табл. 2 в ячейку E2 мы вставили формулу = ОКРУГЛТ ( 27,5 * 2 ^ ( B2 / 12 ) ; 1 ) и растянули её на всю колонку E. В ячейку F2 мы вставили формулу =ОКРУГЛТ( 8000000 / ( 27,5 * 2 ^ ( B2 / 12 )); 1 ) и растянули её на всю колонку F.
С таким же успехом, можно использовать 12 коэффициентов деления, но тогда прерывания будут выполнятся дольше, за счёт лишних операций по вычислению коэффициентов деления.
Замечание, так как мы планируем использовать в любом музыкальном произведении только 31 ноту (2,5 октавы), то у нас будет необходимость транспонировать, повышать или понижать тональность произведения, что мы легко сможем сделать сдвигая цифры в колонке A вверх или вниз.
Управление таймером-счётчиком TC1.
Лист. 2. Программное управление таймером счётчиком TC1.

Рис. 1. Осциллограмма снятая на 9 ножке платы Arduino Nano.
Принципиальная электрическая схема.
В программе лист. 2 мы организовали подключение выхода таймера-счётчика TC1 на порт микроконтроллера PB1, который, в свою очередь, подключён к pin 9 платы Arduino NANO или UNO.

Рис. 2. Принципиальная электрическая схема музыкального автомата.
На принципиальной электрической схеме рис. 2 однокаскадный импульсный усилитель на транзисторе 2N2222 своим входом подключён к pin 9 платы Arduino, а нагрузкой этого усилителя является высокоомный динамик. У транзистора 2N2222 типичный коэффициент усиления по току в схеме с общим эмиттером равен 300. В ключевом режиме работы ток коллектора транзистора примерно равен 5/30=0,16 Ампер. Где 5 Вольт напряжение питания схемы, а 30 Ом сопротивление динамика. Сопротивлением открытого транзистора можно пренебречь. Для полного открывания транзистора ток базы этого транзистора должен быть не менее 0,16/300=0,00055 Ампер. Где 0,16 Ампер ток коллектора транзистора, а 300 коэффициент усиления по току. Ограничивающий ток базы резистор должен быть не более 4,5/0,00055=8100 Ом. Где 4,5 Вольт напряжение единичного импульса на выходе микроконтроллера.
Например, Вы не нашли высокоомный динамик, типичное сопротивление маломощных динамиков 8 Ом, и такой у Вас есть. Задача: рассчитать сопротивление резистора в цепи базы транзистора. 5/8=0,625А. 0,625/300=0,002А. 4,5/0,02=2160 Ом.
Нотная грамотность.
Хороший программист, создавая свой программный продукт для целей автоматизации какого либо рода деятельности человека в производстве, науке или искусстве должен разбираться в этой области на уровне специалиста. Создавая программу музыкального автомата, программист должен, как минимум, изучить музыкальную нотацию. Не плохо было бы подтянуть себя по сольфеджио.
Рис. . Основы музыкальной нотации.
Лист. 3. Ноты Рождественской песенки Jingle Bells.
Закодируем эти ноты в соответствии с таблицами 1 и 2, для начала, каждой ноте будет соответствовать 2 цифры № ноты и её длительность. Коды сразу пишем в таблицу Google документы или LibreOffice Calc.
| A | B | C | |
|---|---|---|---|
| 1 | № ноты | Длительность | Код |
| 3 | 17 | 3 | 139 |
| 5 | 17 | 3 | 139 |
| 7 | 17 | 2 | 138 |
| 9 | 20 | 3 | 163 |
| 11 | 13 | 4 | 108 |
| 13 | 17 | 1 | 137 |
| 14 | 0 | 0 | 0 |
Табл. 3. Коды нот первых четырёх тактов песенки Jingle Bells.
В ячейку C3 табл. 3 мы поместили формулу = A2 * 8 + B2 и растянули её на все следующие ячейки в колонке C.
В результате, у нас есть массив из кодов нот первых четырёх тактов песенки Jingle Bells и их длительностей:
Управление прерываниями Watchdog таймера (WDT).
Напишем программу инициализации прерываний WDT с примером использования этих прерываний.
Лист. 4. Программа исполняющая первые четыре такта песенки Jingle Bells.
В результате разработки программы лист. 4, был выработан формат массива кодов нот музыкального фрагмента и массива коэффициентов деления для таймера-счётчика TC1. Массив кодов нот музыкального фрагмента должен заканчиваться «0». Массив коэффициентов деления для таймера-счётчика TC1 должен начинаться с «0».
Размещение массива данных в Flash-памяти м-к.
Программа лист. 4 использует память м-к следующим образом:
Если хранить коды нот в динамической памяти, то мы сможем записать в м-к всего 1-2 мелодии. Flash память программ в м-к может хранить в десятки раз больше мелодий, но доступ к данным в Flash памяти специфичен. Перепишем программу лист. 4 так, что-бы массивы данных размещались в Flash памяти.
Лист. 5. Программа исполняющая песенку Jingle Bells с использованием Flash памяти для хранения констант.
Программа лист. 5 использует меньше динамической памяти чем программа лист. 4.
Управление воспроизведением музыкальных фрагментов.
Массив кодов нот песенки Jingle Bells, не содержит повторяющихся частей. Но, практически, любая песня содержит куплет и припев и, как правило, ноты всех куплетов не отличаются друг от друга, а у припева, и подавно.
Но, для дальнейшего программирования, мы возьмём ещё боле сложное и интересное музыкальное произведение чем песенка.
Лист. 6. Ноты фортепианной пьесы Людвига ван Бетховена «К Элизе» (нем. Für Elise).
Обратите внимание на лист. 6, мы нашли в произведении Людвига ван Бетховена «К Элизе» один фрагмент мелодии повторяющийся 3 раза (окрашено в розовый цвет) и один такт повторяющийся 2 раза. Но это ещё не все повторения в этом произведении. Первая половина произведения «К Элизе» повторяется 2 раза. Причём, сначала звучит 9 тактов (в девятом такте нота ля длительностью 1/4). Обратите внимание, девятый и первый такт не полные. После звучания ноты ля длительностью 1/4 из девятого такта, начинается воспроизведение с первого такта в котором 2 ноты длительностью 1/16 каждая. В результате девятый такт вместе с первым имеют длительность 3/8.
Потом звучит 2, 3, 4, 5, 6, 7, 8 такты, а далее 10, 11 . и до конца произведения.
Программист все повторения может просто запрограммировать. Но, для начала нам надо закодировать все музыкальные фрагменты произведения Людвига ван Бетховена «К Элизе» без повторений.
| A | B | C | D | |
|---|---|---|---|---|
| 1 | № ноты | Длительность | Код | Адрес фрагмента |
| 2 | 0 | 2 | 2 | 0 |
| 3 | 29 | 4 | 236 | 1 |
| 4 | 28 | 4 | 228 | 2 |
| 5 | 0 | 0 | 0 | 3 |
| 6 | 29 | 4 | 236 | 4 |
| 7 | 28 | 4 | 228 | 5 |
| 8 | 29 | 4 | 236 | 6 |
| 9 | 24 | 4 | 196 | 7 |
| 10 | 27 | 4 | 220 | 8 |
| 11 | 25 | 4 | 204 | 9 |
| 12 | 22 | 3 | 179 | 10 |
| 13 | 0 | 4 | 4 | 11 |
| 14 | 13 | 4 | 108 | 12 |
| 15 | 17 | 4 | 140 | 13 |
| 16 | 22 | 4 | 180 | 14 |
| 17 | 0 | 0 | 0 | 15 |
| 18 | 24 | 3 | 195 | 16 |
| 19 | 0 | 4 | 4 | 17 |
| 20 | 17 | 4 | 140 | 18 |
| 21 | 21 | 4 | 172 | 19 |
| 22 | 24 | 4 | 196 | 20 |
| 23 | 25 | 3 | 203 | 21 |
| 24 | 17 | 4 | 140 | 22 |
| 25 | 29 | 4 | 236 | 23 |
| 26 | 28 | 4 | 228 | 24 |
| 27 | 0 | 0 | 0 | 25 |
| 28 | 24 | 3 | 195 | 26 |
| 29 | 0 | 4 | 4 | 27 |
| 30 | 17 | 4 | 140 | 28 |
| 31 | 25 | 4 | 204 | 29 |
| 32 | 24 | 4 | 196 | 30 |
| 33 | 0 | 0 | 0 | 31 |
| 34 | 22 | 2 | 178 | 32 |
| 35 | 0 | 0 | 0 | 33 |
| 36 | 22 | 3 | 179 | 34 |
| 37 | 0 | 4 | 4 | 35 |
| 38 | 24 | 4 | 196 | 36 |
| 39 | 25 | 4 | 204 | 37 |
| 40 | 27 | 4 | 220 | 38 |
| 41 | 29 | 3 | 235 | 39 |
| 42 | 0 | 4 | 4 | 40 |
| 43 | 20 | 4 | 164 | 41 |
| 44 | 30 | 4 | 244 | 42 |
| 45 | 29 | 4 | 236 | 43 |
| 46 | 27 | 3 | 219 | 44 |
| 47 | 0 | 4 | 4 | 45 |
| 48 | 18 | 4 | 148 | 46 |
| 49 | 29 | 4 | 236 | 47 |
| 50 | 27 | 4 | 220 | 48 |
| 51 | 25 | 3 | 203 | 49 |
| 52 | 0 | 4 | 4 | 50 |
| 53 | 17 | 4 | 140 | 51 |
| 54 | 27 | 4 | 220 | 52 |
| 55 | 25 | 4 | 204 | 53 |
| 56 | 24 | 3 | 195 | 54 |
| 57 | 0 | 4 | 4 | 55 |
| 58 | 17 | 4 | 140 | 56 |
| 59 | 29 | 4 | 236 | 57 |
| 60 | 28 | 4 | 228 | 58 |
| 61 | 0 | 0 | 0 | 59 |
| 62 | 22 | 2 | 178 | 60 |
| 63 | 0 | 0 | 0 | 61 |
Табл. 4. Коды нот произведения Людвига ван Бетховена «К Элизе» без повторений.
В программе на языке C++ мы должны предусмотреть возможность воспроизводить некоторые музыкальные фрагменты по несколько раз.
Лист. 7. Программа исполняющая фортепианную пьесу Людвига ван Бетховена «К Элизе».
Массивы в музыкальной программе.
Массив order в программе лист. 7 содержит адреса первых нот музыкальных фрагментов в массиве notaCode. Фактически, в массиве order записаны индексы массива notaCode. Каждый элемент массива order — это инструкция, в каком порядке следует исполнять музыкальные фрагменты. Но, в нашей музыкальной программе так же нужны инструкции, позволяющие использовать в музыкальном произведении более 31 ноты. То есть, нужна инструкция, позволяющая тридцать одному номеру ноты поставить в соответствие различные наборы нот.
В программе лист. 7 в строке lengthNote = lengthNote / 8 мы из онобайтного кода ноты и её длительности вычисляем код ноты. Если к полученному коду ноты прибавить число (смещение), то мы получим новый набор соответствий кодов нот (номеров нот) и их частоте (высоте ноты). Чтобы можно было гибко выбирать музыкальный диапазон из 31 ноты, нам нужны смещения от 1 до 49 — 31 = 18 и мы охватим 3 октавы (можно и больше). Для 18 смещений создавать новый массив команд, или изменять структуру массива order, считаю, не рационально. Можно в массиве order считать число от 1 до 18 командой изменить высоту нот в музыкальном фрагменте, а числа от 20 до 32767 считать адресом музыкального фрагмента. Такой подход наложит не существенные ограничения на использование младших 20 адресов в массиве notaCode.
А что если использовать в массиве order значения меньше 32000 как адреса музыкального фрагмента, а числа от 32001 до 32018 как команду изменить высоту нот. Это рационально так как старшие адреса Flash памяти микроконтроллера не будут использоваться для хранения массива notaCode. Значит, так и поступим.

Лист. 8. Ноты Полонеза Огинского.
Лист. 9. Программа исполняющая первую часть Полонеза Огинского.
- Arduino
- Музыка
- Назад
- Вперед
- Вы здесь:
- Главная />
- Робототехника />
- Концепция музыкальной программы для Arduino
Генерация звука на микроконтроллерах AVR методом волновых таблиц с поддержкой полифонии
Микроконтроллеры AVR довольно дешевы и широко распространены. Наверно, с них начинает почти любой embedded разработчик. А среди любителей правит балом Arduino, сердцем которого обычно является ATmega328p. Наверняка многие задумывались: как можно заставить их звучать? Если посмотреть на существующие проекты, то они бывают нескольких типов:
- Генераторы квадратных импульсов. Генерация с помощью ШИМ или дергать пины в прерываниях. В любом случае, получается очень характерный пищащий звук.
- Использование внешнего оборудования типа MP3 декодера.
- Использование ШИМ для вывода 8 битного (иногда 16 битного) звука в формате PCM или ADPCM. Поскольку памяти в микроконтроллерах для этого явно не достаточно, то обычно используют SD карту.
- Использование ШИМ для генерации звука на основе волновых таблиц, подобных MIDI.
Последний тип для меня был особенно интересен, т.к. почти не требует дополнительного оборудования. Представляю сообществу свой вариант. Для начала небольшое демо:
Заинтересовавшихся прошу под кат. Итак, оборудование:
- ATmega8 или ATmega328. Портировать на другие ATmega не сложно. И даже на ATtiny, но об этом позже;
- Резистор;
- Конденсатор;
- Динамик или наушники;
- Питание;
Вроде все.
Простая RC цепочка вместе с динамиком подключается к выводу микроконтроллера. На выходе получаем 8 битный звук с частотой дискретизации 31250Гц. При частоте кристалла в 8МГц можно генерировать до 5 каналов звука + один шумовой канал для перкуссии. При этом используется почти все процессорное время, но после заполнения буфера процессор можно занять чем-то полезным помимо звука:
Данный пример полностью помещается в память ATmega8, 5 каналов + шум обрабатываются при частоте кристалла 8МГц и остается немного времени на анимацию на дисплее.
Так же я хотел показать в этом примере, что библиотеку можно использовать не только как очередную музыкальную открытку, но и подключить звук к уже существующим проектам, например, для уведомлений. И даже при использовании всего одного звукового канала уведомления могут быть гораздо интереснее простой пищалки.
А теперь подробности…
Волновые таблицы или wavetables
Математика предельно проста. Есть периодичная функция тона, например tone(t) = sin(t * freq / (2 * Pi))
.
Так же есть функция изменения громкости основного тона от времени, например volume(t) = e ^ (- t)
В самом простом случае звучание инструмента – это произведение этих функций instrument(t) = tone(t) * volume(t)
На графике это все выглядит примерно так:

Дальше берем все звучащие в данный момент времени инструменты и суммируем их с некоторыми коэффициентами громкости (псевдокод):
for (i = 0; i < CHANNELS; i++) < value += channels
.tone(t) * channels.volume(t) * channels.volume; > Надо только подбирать громкости так, чтобы не было переполнения. И это почти все.
Шумовой канал работает приблизительно так же, только вместо функции тона генератор псевдослучайной последовательности.
Перкуссия — это микс шумового канала и низкочастотной волны, приблизительно в 50-70 Гц. Конечно, качественного звука таким образом добиться сложно. Но у нас же всего 8 килобайт на все. Надеюсь, это можно простить.
Что можно выжать из 8 бит
Изначально я ориентировался на ATmega8. Без внешнего кварца она работает на частоте 8МГц и имеет 8 битный ШИМ, что дает базовую частоту дискретизации 8000000 / 256 = 31250Гц. Один таймер использует ШИМ для вывода звука и он же при переполнении вызывает прерывание для передачи следующего значения в генератор ШИМ. Соответственно, у нас 256 тактов для вычисления значения сэмпла на все, включая накладные расходы на прерывание, обновление параметров звуковых каналов, отслеживание времени, когда надо проигрывать очередную ноту и т.д.
Для оптимизации будем активно использовать следующие трюки:
- Поскольку процессор у нас восьмибитный, то переменные будем стараться делать такими же. Иногда будем пользоваться 16 битными.
- Вычисления условно разделим на частые и не очень. Первые необходимо вычислять для каждого сэмпла, вторые – заметно реже, раз в несколько десятков/сотен сэмплов.
- Для равномерного распределения нагрузки во времени мы используем циклический буфер. В основном цикле программы мы буфер наполняем, в прерывании вычитываем. Если все хорошо, то наполняется буфер быстрее, чем опустошается и у нас есть время на что-то еще.
- Код написан на C с большим количеством inline. Практика показывает, что так заметно быстрее.
- Все что можно просчитать препроцессором, особенно с участием деления, делается препроцессором.
Для начала, разделим время на промежутки по 4 миллисекунды (я назвал их тиками). При частоте дискретизации 31250Гц получаем 125 сэмплов на тик. То, что обязательно нужно считать каждый сэмпл – будем считать каждый сэмпл, а остальное – раз в тик или реже. Например, в рамках одного тика громкость инструмента будет постоянной: instrument(t) = tone(t) * currentVolume
; а сам currentVolume будет пересчитываться раз в тик с учетом volume(t) и выбранной громкости звукового канала.
Длительность тика в 4мс была выбрана исходя из простого 8 битного ограничения: при восьмибитном счетчике сэмплов можно работать с частотой дискретизации до 64кГц, при восьмибитном счетчике тиков мы можем измерять время до 1-й секунды.
- Главная страница
Тонгенератор — воспроизводит звук на определенной частоте и громкости. Поможет Вам настроить эстрадные динамики, компонентную акустику или сабвуфер.
Как же он в этом может Вам помочь? Практически на всех автомобильных усилителях «крутилка» фильтров HPF и LPF имеет слишком широкий разброс по частоте, например, от 10 Гц до 10 000 Гц, и порой крайне затруднительно поймать точно необходимый срез.
Как пользоваться тон генератором.
1. Для начала на вход усилителя нужно подать аудиосигнал с устройства (ПК, смартфон и т.д.), подключенного к интернету и воспроизводящего звук.
2. Все остальные устройства от входа усилителя нужно отключить.
3. Убедившись, что звук с подключенного к усилителю устройства воспроизводится можно начинать настройку фильтров усилителя.
Рассмотрим настройку фильтров усилителя на примере двухполосной системы, построенной на поканальном подключении к 4-х канальному усилителю.
Допустим, высокочастотники (твитера)
подключены на выходы усилителя 1 и 2. Подключаем на соответствующие входы усилителя тонренератор.
Если твитер должен работать с ограничением в 4000 Гц — устанавливаем эту частоту на тонгенераторе. На усилителе, при этом, нужно установить регулятор HPF на более высокое значение (например на 8000 Гц или в крайнее положение ручки регулятора). Включаем тонгенератор и очень плавно и медленно поворачиваем ручку регулятора в обратном направлении до тех пор, пока не услышим в твитерах заданный тонсигнал. Как только громкость тонсигнала перестала прибавляться при повороте ручки — это означает, что фильтр усилителя установлен на заданной частоте в 4000 Гц.
Теперь нужно настроить мидбас
Переключаем устройство с тонгенератором с входов 1 и 2 на входы 3 и 4.
Сначала настраиваем HPF на частоте, к примеру 65 Гц (настраивается так же как и для твитера). После того как настройка HPF закончена, переходим к настройке LPF (фильтра низких частот).
Устанавливается частота, например те же 4000 Гц, на тонгенераторе. Ручкой регулятора LPF на усилителе устанавливаем значение, ниже заданной частоты тонгенератора.
Включаем тонсигнал и медленно поворачиваем регулятор вперед.
Когда мы услышим в настраиваемом динамике сигнал тонгенератора и громкость его перестанет возрастать при повороте ручки — заданное значение фильтра установлено.
Все остальные компоненты системы настраиваются точно так же.
Рассмотрим еще один простой пример — Вам необходимо установить четкий срез в 65 Гц:
- Берем мобильный телефон и подключаем его через кабель Mini Jack-2RCA к автомобильному усилителю.
- Затем выставляем «гейн» фильтра на усилителе на более высокую частоту (например, 100-200 Гц).
- Далее включаем наш тонгенератор на 65 Гц и начинаем медленно поворачивать «крутилку» фильтра на усилителе в сторону значения 65 Гц.
- Как только громкость звука в динамиках (сабвуфере) перестанет увеличиваться по мере поворота «крутилки» — значит Вы достигли необходимого значения в 65 Гц.
Немного кода
- Информация о форме волны, фаза, частота.
waveForm: информация о функции tone(t): ссылка на массив длиной 256 байт. Задает тембр, звучание инструмента.
waveSample: старший байт указывает на текущий индекс массива waveForm.
waveStep: задает частоту, на сколько waveSample будет увеличен при подсчете следующего сэмпла.
Каждый сэмпл считается примерно так:
int8_t tone = channelData.waveForm[channelData.waveSample >> 8]; channelData.waveSample += channelaData.waveStep; return tone * channelData.currentVolume;
Обратите внимание: форма волны – восьмибитная, громкость – тоже восьмибитная, а результат – 16 битный. С небольшой потерей производительности можно сделать звук (почти) 16 битным.
При борьбе за производительность пришлось прибегнуть к некоторой черной магии.
Пример номер 1. Как пересчитывать громкость каналов:
Музыка
Используется восьмибитный счетчик тиков. При достижении нуля начинается новый такт, счетчику присваивается длительность такта (в тиках), чуть позже проверяется массив музыкальных команд.
Музыкальные данные хранятся в массиве байтов. Записывается примерно так:
Например, команда DATA_PLAY разворачивается в 2 байта, в которых хранятся: маркер команды (1 бит), пауза перед следующей командой (3 бита), номер канала, на котором играть ноту (4 бита), информация о ноте (8 бит). Самое существенное ограничение – этой командой нельзя делать большие паузы, максимум 7 тактов. Если надо больше, то надо использовать команду DATA_WAIT (до 63 тактов). К сожалению, я так и не нашел, можно ли макрос развернуть в разное количество байт массива в зависимости от параметра макроса. И даже warning не знаю как вывести. Может быть вы подскажите.
Использование
В каталоге demos есть несколько примеров под разные микроконтроллеры. Но если коротко, то вот кусок из readme, мне добавить особо нечего: #include «../../microsound/devices/atmega8timer1.h» #include «../../microsound/micromusic.h» // Make some settings #define CHANNELS_SIZE 5 #define SAMPLES_SIZE 16 #define USE_NOISE_CHANNEL initMusic(); // Init music data and sound control sei(); // Enable interrupts, silence sound should be generated setSample(0, instrument1); // Use instrument1 as sample 0 setSample(1, instrument2); // Init all other instruments… playMusic(mySong); // Start playing music at pointer mySong while (!isMusicStopped) < fillMusicBuffer(); // Fill music buffer in loop // Do some other stuff >Если хочется еще что-то делать помимо музыки, то можно увеличить размер буфера с помощью BUFFER_SIZE. Размер буфера должен быть 2^n, но, к сожалению, при размере в 256 происходит падение производительности. Пока не разобрался с этим.
Для увеличения производительности можно увеличить частоту внешним кварцем, можно уменьшить количество каналов, можно уменьшить частоту дискретизации. С последним приемом можно использовать линейную интерполяцию, что несколько компенсирует падение качества звука.
Всякие delay не рекомендуется использовать, т.к. процессорное время расходуется впустую. Вместо этого реализован свой метод в файле microsound/delay.h
, который помимо самой паузы занимается наполнением буфера. Данный метод может работать не очень точно на коротких паузах, но на длинных более-менее вменяемо.
Тестирование работы проекта
Для тестирования нашего проекта мы использовали следующие компоненты:
- Трансформатор с отводами 13-0-13.
- Два динамика 4Ω 20W в качестве нагрузки.
- Источник аудио сигнала (смартфон).

Для управления уровнем звука использовались две кнопки в схеме проекта. Более подробно эти процессы можно посмотреть на видео, приведенном в конце статьи.
Чтобы улучшить работу схемы ее можно собрать на печатной плате – это позволит уменьшить нежелательные шумы. Также можно добавить дополнительный фильтр для устранения высокочастотных шумов.
Создание своей музыки
Если писать команды вручную, то надо иметь возможность послушать то, что получается. Заливать каждое изменение в микроконтроллер не удобно, особенно если есть альтернатива.
Существует довольно забавный сервис wavepot.com – онлайн редактор JavaScript, в котором надо задать функцию звукового сигнала от времени, и этот сигнал выводится на звуковую карту. Простейший пример:
function dsp(t) < return 0.1 * Math.sin(2 * Math.PI * t * 440); >Я портировал движок на JavaScript, он находится в
demos/wavepot.js
. Содержимое файла надо вставить в редакторе wavepot.com и можно проводить эксперименты. Пишем свои данные в массив soundData, слушаем, не забываем сохранять.
UPD: На данный момент wavepot сильно поменялся и мой код с ним не совместим. Можно локально поднять старую версию с github, но она работает только с firefox, а в chrome не выводит звук.
Отдельно стоит упомянуть о переменной simulate8bits. Она, согласно названию, симулирует восьмибитный звук. Если вдруг покажется, что барабаны гудят, а в затухающих инструментах при тихом звуке появляются помехи, то это оно, искажение восьмибитного звука. Можно попробовать отключить эту опцию и послушать разницу. Проблема гораздо менее ощутима, если в музыке нет тишины.
Подключение
В простом варианте схема выглядит так: +5V ^ MCU | +——-+ +—+VC | R1 | Pin+—/\/\—+——> OUT | | | +—+GN | === C1 | +——-+ | | | — Grnd — Grnd Выходной пин зависит от микроконтроллера. Резистор R1 и конденсатор C1 надо подбирать исходя из нагрузки, усилителя (если есть) и т.д. Я не электронщик и приводить формулы не буду, их легко нагуглить вместе с онлайн калькуляторами.
У меня R1 = 130 Ом, С1 = 0.33 мкФ. На выход подключаю обычные китайские наушники.
Как работает микросхема PT2258
Данная микросхема передает и принимает данные от микроконтроллера по линиям интерфейса I2C SCL и SDA, которые образуют шину. Для устойчивой работы рекомендуется данные линии использовать с подтягивающими резисторами сопротивлением 4,7 кОм.

Далее мы кратком рассмотрим принципы работы микросхемы PT2258 по линиям интерфейса I2C. Если вам неинтересна данная информация, то вы можете ее пропустить потому что всю эту работу берет на себя библиотека для Arduino.
Валидация (подтверждение) данных
- данные на линии SDA считаются правильными (подтвержденными) только когда на линии SCL состояние HIGH;
- состояния линии SDA (HIGH и LOW) изменяются только когда на линии SCL состояние LOW.

Условия начала и окончания передачи
Условие начала передачи (Start Condition) активируется когда:
- На линии SCL установлено состояние HIGH.
- Линия SDA переключается из состояния HIGH в LOW.
Условие окончания передачи (Stop Condition) активируется когда:
- Линия SCL установлена в состояние HIGH.
- Линия SDA переключается из состояния LOW в HIGH.

Примечание: эта информация очень полезна для отладки сигналов.
Формат данных
Каждый байт, передаваемый по линии SDA, состоит из 8 бит. Каждый байт должен заканчиваться битом подтверждения.
Подтверждение
Подтверждение (Acknowledgment) гарантирует устойчивое и правильное функционирование. Во время импульса подтверждения (Acknowledge Clock Pulse) микроконтроллер устанавливает контакт SDA в состояние HIGH и в этот же самый момент периферийное устройство (к примеру, аудио процессор) устанавливает на контакте SDA состояние LOW.

Периферийное устройство (в нашем случае PT2258) должно сформировать подтверждение после приема байта, линия SDA будет оставаться в состоянии HIGH во время девятого (9th) импульса синхронизации (Clock Pulse). После этого передатчик ведущего устройства (master) должен сформировать сигнал STOP для прекращения передачи.

Выбор адреса (Address Selection)
Адрес I2C микросхемы PT2258 зависит от состояния CODE1 (контакт № 17) и CODE2 (контакт № 4). Возможные при этом адреса представлены в следующей таблице.
| CODE1 (PIN No. 17) | CODE2 (PIN No. 4) | HEX ADDRESS |
| 0 | 0 | 0X80 |
| 0 | 1 | 0X84 |
| 1 | 0 | 0X88 |
| 1 | 1 | 0X8C |
Протокол интерфейса
Состоит из следующих элементов:
- стартовый бит;
- байт адреса чипа;
- бит подтверждения (ACK);
- байт данных;
- стоповый бит.
Вспомогательные операции
После того, как на микросхему будет подано питание, необходимо подождать, по меньшей мере, 200ms перед тем как передавать первый бит данных, иначе передача данных может окончиться передачей.
После этой задержки первое, что нужно сделать, это очистить регистр при помощи передачи “0XC0” по линии I2C, это гарантирует в дальнейшем правильное функционирование устройства.
Описанная операция очищает целый регистр, теперь нам необходимо установить значение в регистре, иначе регистр будет хранить устаревшее значение и мы получим неправильное значение на выходе.
Чтобы обеспечить правильную настройку звука необходимо последовательно передавать коды 10dB и 1dB на аттенюатор, иначе микросхема может работать неправильно. Более наглядно это процесс представлен на следующей диаграмме:
Чтобы обеспечить правильное функционирование микросхемы PT2258 убедитесь в том, скорость (частота) передачи данных по интерфейсу I2C никогда не будет превышать 100KHz.
Более подробно принципы функционирования микросхемы PT2258 можно изучить в даташите на нее.
Что там было про 16 битный звук?
Как я говорил выше, при умножении двух восьмибитных чисел (частота и громкость) мы получаем 16 битное число. Его можно не округлять до восьмибитного, а выводить оба байта в 2 ШИМ канала. Если эти 2 канала смешать в пропорции 1/256, то мы можем получить 16 битный звук. Разницу с восьмибитным особенно легко услышать на плавно затухающих звуках и барабанах в моменты, когда звучит только один инструмент.
Подключение 16 битного выхода:
+5V ^ MCU | +——-+ +—+VCC | R1 | PinH+—/\/\—+——> OUT | | | | | R2 | | PinL+—/\/\—+ +—+GND | | | +——-+ === C1 | | — Grnd — Grnd Здесь важно правильно смешать 2 выхода: сопротивление R2 должно быть в 256 раз больше сопротивления R1. Чем точнее, тем лучше. К сожалению, даже резисторы с погрешностью 1% не дают требуемой точности. Однако, даже с не очень точным подбором резисторов искажения можно заметно ослабить.
К сожалению, при использовании 16 битного звука проседает производительность и 5 каналов + шум уже не успевают обрабатываться за отведенные 256 тактов.
Схема проекта
Схема цифрового регулятора уровня звука на Arduino и микросхеме PT2258 представлена на следующем рисунке.

В демонстрационных целях схема собрана на макетной плате.
Примечание: все компоненты на макетной плате следует размещать как можно ближе друг к другу чтобы уменьшить влияние паразитных емкостей и индуктивностей.
А на Arduino можно?
Да, можно. У меня только китайский клон nano на ATmega328p, на нем работает. Скорее всего другие ардуины на ATmega328p тоже должны работать. ATmega168 вроде бы имеет те же регистры управления таймерами. Скорее всего на них будет работать без изменений. На других микроконтроллерах надо проверять, возможно потребуется дописать драйвер.
В demos/arduino328p
есть скетч, но чтобы он нормально открылся в Arduino IDE, его нужно скопировать в корень проекта.
В примере генерируется 16 битный звук и используются выходы D9 и D10. Для упрощения можно ограничиться 8 битным звуком и использовать только один выход D9.
Поскольку почти все ардуины работают на 16МГц, то, при желании, можно увеличить количество каналов до 8.
Микросхема PT2258
Микросхема PT2258 представляет собой 6-ти канальный электронный контроллер звука и построена на основе CMOS технологии. Микросхема PT2258 изначально была спроектирована для работы с многоканальными аудио-видео приложениями.

Микросхема работает по интерфейсу I2C и обеспечивает диапазон ослабления от 0 до -79dB с шагом 1dB. Изготовляется в 20-ти контактном корпусе DIP или SOP.
Основные технические характеристики микросхемы:
- 6 входных и выходных каналов (для домашних аудио систем формата 5.1);
- выбираемый адрес I2C;
- хорошая развязка между каналами (для приложений с малым уровнем шума);
- отношение сигнал/шум (S/N ratio) более 100 дБ;
- рабочее напряжение от 5 до 9 В.
А что с ATtiny?
В ATtiny нет аппаратного умножения. Программное умножение, которое использует компилятор дико медленное и его лучше не использовать. При использовании оптимизированных ассемблерных вставок производительность падает раза в 2 по сравнению с ATmega. Казалось бы, смысла использовать ATtiny нет вообще, но…
На некоторых ATtiny есть умножитель частоты, PLL. А это значит, что на таких микроконтроллерах есть 2 интересные особенности:
- Частота генератора ШИМ 64МГц, что дает период ШИМ в 250кГц, что гораздо лучше, чем 31250Гц при 8 МГц или 62500Гц с кварцем на 16 МГц на любых ATmega.
- Этот же умножитель частоты позволяет без кварца тактовать кристалл на 16 МГц.
Отсюда вывод: некоторые ATtiny использовать для генерации звука можно. Они успевают обрабатывать те же 5 инструментов + шумовой канал, но на 16 МГц и им не нужен внешний кварц.
Минус в том, что больше частоту уже не повысить, а вычисления занимают почти все время. Для освобождения ресурсов можно уменьшать количество каналов или частоту дискретизации.
Еще один минус в необходимости использования сразу двух таймеров: один для ШИМ, второй для прерывания. На этом, обычно, таймеры и заканчиваются.
Из известных мне микроконтроллеров с PLL могу упомянуть ATtiny85/45/25 (8 ног), ATtiny861/461/261 (20 ног), ATtiny26 (20 ног).
Что касается памяти, то разница с ATmega не велика. В 8кб вполне поместится несколько инструментов и мелодий. В 4кб можно поместить 1-2 инструмента и 1-2 мелодии. В 2 килобайта что-то поместить сложно, но если очень хочется, то можно. Надо разинлайнивать методы, отключать некоторые функции типа контроля громкости по каналам, уменьшать частоту дискретизации и количество каналов. В общем, на любителя, но рабочий пример на ATtiny26 есть.
Проблемы
Проблемы есть. И самая большая проблема – это скорость вычислений. Код полностью написан на C с небольшими ассемблерными вставками умножения для ATtiny. Оптимизация отдается компилятору и он иногда ведет себя странно. При небольших изменениях, которые вроде бы не должны ни на что влиять, можно получить заметное просаживание производительности. Причем изменение с -Os на -O3 не всегда помогает. Один из таких примеров – использование буфера размером 256 байт. Особенно неприятно то, что нет гарантии, что в новых версиях компилятора мы не получим падение производительности на том же коде.
Другая проблема в том, что совсем не реализован механизм затухания перед следующей нотой. Т.е. когда на каком-то канале одна нота сменяется другой, то старое звучание резко прерывается, иногда слышен небольшой щелчок. Хотелось бы найти способ избавиться от этого без потери производительности, но пока так.
Нет команд для плавного нарастания/затухания громкости. Особенно критично для коротких мелодий-уведомлений, где в конце надо сделать быстрое затухание громкости, чтобы не было резкого обрыва звучания. Частично проблема обходится написанием череды команд с ручной установкой громкости и короткой паузы.
Выбранный подход в принципе не способен обеспечить натуралистичное звучание инструментов. Для более натуралистичного звучания нужно разделить звуки инструментов на attack-sustain-release, использовать хотя бы первые 2 части и с гораздо большей длительностью, чем один период колебания. Но тогда данных для инструмента потребуется гораздо больше. Была идея использовать более короткие волновые таблицы, например в 32 байта вместо 256, но без интерполяции сильно падает качество звука, а с интерполяцией падает производительность. А еще 8 бит дискретизации явно мало для музыки, но это можно обойти.
Размер буфера ограничен в 256 сэмплов. Это соответствует примерно 8 миллисекундам и это максимальный цельный промежуток времени, который можно отдать другим задачам. При этом выполнение задач все равно периодически приостанавливается прерываниями.
Замена стандартного delay работает не очень точно на коротких паузах.
Уверен, что это не полный список.
Пассивные регуляторы тембра
В этой статье вниманию читателей предлагается ряд различных по схемотехнике и функциональным возможностям регуляторов тембра, которые могут быть использованы радиолюбителями при разработке и модернизации звуковоспроизводящей аппаратуры.
Основной недостаток еще недавно популярных активных регуляторов тембра состоит в использовании глубокой частотно-зависимой ООС и больших дополнительных искажениях, вносимых ими в регулируемый сигнал. Вот почему в высококачественной аппаратуре желательно применять пассивные регуляторы. Правда, и они не лишены недостатков. Самый крупный из них — значительное затухание сигнала, соответствующее диапазону регулирования. Но так как глубина регулирования тембра в современной звуковоспроизводящей аппаратуре невелика (не более 8…10 дБ), то в большинстве случаев вводить в тракт сигнала дополнительные каскады усиления не требуется.
Другой, не столь существенный недостаток таких регуляторов — необходимость применения переменных резисторов с экспоненциальной зависимостью сопротивления от угла поворота движка (группа «В»), обеспечивающих плавное регулирование. Однако простота конструкции и высокие качественные показатели все же склоняют конструкторов к применению именно пассивных регуляторов тембра.
Следует отметить, что эти регуляторы требуют низкого выходного сопротивления предшествующего им каскада и высокого входного сопротивления последующего.
Разработанный английским инженером Баксандалом еще в 1952 г. регулятор тембра [1] стал, пожалуй, самым распространенным частотным корректором в электроакустике. Классический его вариант состоит из образующих мост двух звеньев фильтра первого порядка — низкочастотного R1C1R3C2R2 и высокочастотного C3R5C4R6R7 (рис. 1,а). Аппроксимированные логарифмические ампли-тудно-частотные характеристики (ЛАЧХ) такого регулятора показаны на рис. 1 ,б. Там же приведены расчетные зависимости для определения постоянных времени точек перегиба ЛАЧХ.
Теоретически максимально достижимая крутизна АЧХ для звеньев первого порядка составляет 6 дБ на октаву, но при практически реализуемых характеристиках из-за незначительного различия частот перегиба (не более декады) и влияния предшествующих и последующих каскадов она не превышает 4…5 дБ на октаву. При регулировании тембра фильтр Баксандала меняет только наклон АЧХ без изменения частот перегиба. Вносимое регулятором на средних частотах затухание определяется соотношением n=R1/R3. Диапазон регулирования АЧХ при этом зависит не только от величины затухания п, но и от выбора частот перегиба частотной характеристики, поэтому для его увеличения частоты перегиба устанавливают в области средних частот, что, в свою очередь, чревато взаимным влиянием регулировок.
В традиционном варианте рассматриваемого регулятора R1/R3=C2/C1= =C4/C3=R5/R6=n, R2=R7=n-R1. При этом достигается приблизительное совпадение частот перегиба АЧХ в области ее подъема и спада (в общем случае они различны), что обеспечивает относительно симметричное регулирование АЧХ (спад даже в этом случае неизбежно получается более крутым и протяженным). При обычно используемом п=10 (для этого случая указаны минимальные значения номиналов элементов на рис. 1,а-3,а) и выборе частот раздела вблизи 1 кГц регулирование тембра на частотах 100 Гц и 10 кГц относительно частоты 1 кГц составляет ±14…18дБ. Как отмечалось выше, для достижения плавного регулирования переменные резисторы R2, R7 должны иметь экспоненциальную характеристику регулирования (группа «В») и, кроме того, для получения линейной АЧХ в среднем положении движков регуляторов соотношение сопротивлений верхнего и нижнего (по схеме) участков переменных резисторов также должно быть равно п. При «хайэндовском» п=2…3, что соответствует диапазону регулирования ±4…8 дБ, вполне допустимо использовать переменные резисторы с линейной зависимостью сопротивления от угла поворота движка (группа «А»), но при этом несколько огрубляется регулировка в области спада АЧХ и растягивается в области подъема, а плоская АЧХ получается отнюдь не в среднем положении движков регуляторов. С другой стороны, сопротивление секций сдвоенных переменных резисторов с линейной зависимостью лучше согласовано, что уменьшает рассогласование АЧХ каналов стереофонического усилителя, так что неравномерное регулирование в этом случае можно считать допустимым.
Наличие резистора R4 не принципиально, его назначение — снизить взаимное влияние звеньев и сблизить частоты перегиба АЧХ в области высших звуковых частот. Как правило, R4= =(0,3…1,2)’R1. Как показано ниже, от него в ряде случаев можно вообще отказаться. Для снижения влияния на регулятор предшествующих и последующих каскадов их выходное Rвых и входное Rвх сопротивления должны быть соответственно Rвых<>R2.
Приведенный «базовый»вариант регулятора применяется обычно в радиоаппаратуре высокого класса. В бытовой аппаратуре используют несколько упрощенный вариант (рис. 2,а). Аппроксимированные логарифмические амплитудно-частотные характеристики (ЛАЧХ) такого регулятора приведены на рис. 2,6. Упрощение его высокочастотного звена привело к некоторой расплывчатости регулирования в области высших частот и к более заметному влиянию предшествующего и последующего каскадов на АЧХ в этой области.
Подобный корректор при п=2 (с переменными резисторами группы «А») был особенно популярен в простых любительских усилителях [2] конца 60-х — начала 70-х годов (главным образом, из-за малого затухания), но вскоре величина п возросла до привычных сегодня значении. Все сказанное выше относительно диапазона регулирования, согласования и выбора регуляторов справедливо и для упрощенного варианта корректора.
Если отказаться от требования симметричного регулирования АЧХ на участках их подъема и спада (кстати, необходимость спада практически не возникает), то можно еще более упростить схему (рис. 3,а). Приведенные на рис. З.б ЛАЧХ регулятора соответствуют крайним положениям движков резисторов R2, R4. Достоинство такого регулятора — простота, но поскольку все его характеристики взаимосвязаны, для удобства регулирования целесообразно выбирать п=3…10. С ростом п крутизна подъема растет, а спада — снижается. Все сказанное выше о традиционных вариантах корректора Баксандала в полной мере относится и к этому, предельно упрощенному варианту.
Однако схема регулятора тембра Баксандала и ее варианты — отнюдь не единственная возможная реализация пассивного двухполосного регулятора тембра. Вторая группа регуляторов выполнена не на базе мостов, а на базе частотно-зависимого делителя напряжения. В качестве примера изящного схемотехнического решения регулятора можно привести темброблок, в свое время использовавшийся в различных вариациях в ламповых усилителях электрогитар. «Изюминкой» данного регулятора является изменение частот перегиба АЧХ в процессе регулирования тембра, что приводит к интересным эффектам в звучании «классической» электрогитары. Базовая его схема изображена на рис. 4,а, а аппроксимированные ЛАЧХ — на рис. 4,6. Там же приведены расчетные зависимости для определения постоянных времени точек перегиба.
Нетрудно заметить, что регулировка в области низших звуковых частот изменяет частоты перегиба, не меняя наклон АЧХ. Когда движок переменного резистора R4 находится в нижнем (по схеме) положении, АЧХ на низших частотах линейна. При перемещении же движка вверх на ней появляется подъем, причем точка перегиба в процессе регулирования сдвигается в область более низких частот. При дальнейшем перемещении движка верхняя (по схеме) секция резистора R4 начинает шунтировать резистор R2, что вызывает сдвиг высокочастотной точки перегиба в область более высоких частот. Таким образом, при регулировании подъем низких частот дополняется спадом средних. Регулятор высших звуковых частот представляет собой простейший фильтр первого порядка и особенностей не имеет.
На базе этой схемы можно построить несколько вариантов темброблоков, позволяющих регулировать АЧХ в области низших и высших частот. Причем в области низших частот возможен и подъем, и спад АЧХ, а на высших — только подъем.
Вариант темброблока с регулированием частоты перегиба АЧХ в низкочастотной области показан на рис. 5,а, его ЛАЧХ — на рис. 5,6. Резистор R2 регулирует частоту перегиба АЧХ, a R5 — ее наклон. Совместное действие регуляторов позволяет получить значительные пределы и большую гибкость регулирования.
Схема упрощенного варианта темброблока приведена на рис. 6,а, его ЛАЧХ — на рис. 6,6. Он представляет собой, в сущности, гибрид низкочастотного звена темброблока, показанного на рис. 3,а, и высокочастотного звена темброблока, показанного на рис.4,а.
Объединив функции регулирования АЧХ в низкочастотной и высокочастотной областях, можно получить простой комбинированный регулятор тембра с одним органом управления, весьма удобный для применения в радиоприемной и автомобильной аппаратуре. Его принципиальная схема показана на рис. 7,а и ЛАЧХ — на рис. 7,6. В нижнем (по схеме) положении движка переменного резистора R1 АЧХ близка к линейной во всем диапазоне частот. При перемещении .его вверх появляется подъем на низших частотах, причем низкочастотная точка перегиба в процессе регулирования сдвигается в область более низших частот. При дальнейшем перемещении движка верхняя (по схеме) секция резистора R1 включает в работу конденсатор С1, что приводит к подъему высших частот.
При замене переменного резистора R1 переключателем (рис. 8,а и 8,6) рассмотренный регулятор превращается в простейший тон-регистр (положение 1 — classic; 2 — jazz; 3 — rock), популярный в 50-х — 60-х годах и вновь используемый в эквалайзерах магнитол и музыкальных центров в 90-х.
Несмотря на то что о регулировании тембра, казалось бы, все давно уже сказано, многообразие пассивных корректирующих цепей не исчерпывается предложенными вариантами. Немало забытых схемотехнических решений переживают сейчас второе рождение на новом качественном уровне. Весьма перспективен, например, регулятор громкости с раздельной регулировкой тонкомпенсации по низким и высоким частотам [З].
ЛИТЕРАТУРА 1. Шкритек П. Справочное руководство по звуковой схемотехнике (пер. с нем.). — М.: Мир, 1991, с. 151-153. 2. Крылов Г. Широкополосный УНЧ. — Радио, 1973, N 9, c.56,57. 3. Шихатов А. Комбинированный блок регулирования АЧХ. — Радио, 1993, N 7, с. 16.