Сердце на ATtiny13
Около трёх месяцев назад я начал увлекаться радиоэлектроникой и затянуло меня в изучение микроконтроллеров.
Давалось мне это достаточно тяжело, граблей было найдено немало (до этого ни программированием не занимался, ни в радиоэлектронике ничего не соображал, разве что паяльник в руках умел держать). Когда всё начало немного получаться, встал вопрос о сборке своего первого устройства на микроконтроллере.
Было принято решение порадовать свою девушку и собрать светодиодное сердце (в интернете есть вариации на эту тему, которые меня к этому и побудили). И вот, спустя четыре версии, устройство готово.
Итак, начну с описания аппаратной составляющей устройства. Принципиальная схема устройства предельно проста и выглядит следующим образом:
При подаче питания с разъема USB, микроконтроллер запускается и начинает выполнять записанную в память программу алгоритма световых эффектов, зажигая и выключая светодиоды в определенной последовательности.
На плате размером 60х50мм размещено десять красных 3528 светодиодов, соединенных попарно. Сердцем сердца (вот такой каламбур получился) выступает микроконтроллер ATtiny13. Так же на плате присутствует разъем USB от старой флешки, от которого устройство питается и четыре SMD 1206 перемычки. Для управления светодиодами используются пять выводов микроконтроллера.
Вывод Reset подтянут к питанию через 10 КОм резистор. Конденсаторы C1 и C2- стандартная обвязка по питанию микроконтроллера. В качестве корпуса выступает полиморфный пластик (пластик имеющий температуру плавления 60- 65 градусов по Цельсию) или shapelock.
Разогрел в горячей воде и вылепил форму, по мере остывания подогревал феном и продолжал формовать до получения нужного результата.
Извиняюсь за качество фото, фотографировал на телефон.
Программная составляющая устройства. Программа писалась в среде CVAVR. Управление свечением осуществляется пятиканальным программным ШИМ-ом.
В программе реализовано четыре разных эффекта свечения и один эффект на плавный розжиг светодиодов при включении (выполняется один раз, при подаче питания на устройство, остальные эффекты сменяют друг друга по кругу). Программа предельно проста и легко можно написать новые эффекты или переделать старые, исходник прилагается.
Для изменения яркости (светит без пластика довольно ярко и бьет по глазам) нужно лишь изменить значение переменной bri. На плате в спринте отмечены пары светодиодов буквами, соответствующими переменным, которые отвечают за их яркость.
В битах конфигурации МК убран лишь предделитель тактовой частоты на 8, всё остальное по умолчанию. BODLEVEL=11, SPIEN=0, CKSEL=10, SUT=10.
Обновление от 15.09.2013:
Добавлены конденсаторы по питанию, для сглаживания пульсаций и подавления ВЧ составляющей. Подтянул RESET к плюсу питания через резистор 10 кОм.
Для людей, считающих, что светодиоды подключены неверно:
Питание МК от USB: 5V.
По даташиту высокий уровень Attiny13 при питании 5V равен 4,2 V.
По даташиту падение напряжения на светодиоде при номинальном токе 20мА 1,7- 2,4V. В данном случае на один светодиод приходится 2,1 V.
Попробуйте убедить меня в необходимости токоограничивающих резисторов и я их добавлю.
ОбозначениеТипНоминалКоличествоПримечаниеIC1HL1-HL10C1С2R1МК AVR 8-бит | ATtiny13 | 1 |
1206 SMD | 0 Ом | 4 |
3528 SMD | LED RED | 10 |
Разъем | USB-A вилка | 1 |
1206 smd | 100 нФ | 1 |
Tantal_D | 100 мкФ | 1 |
1206 SMD | 10 кОм | 1 |
Скачать список элементов (PDF)
Прикрепленные файлы:
Источник: http://malatok.at.ua/news/serdce_na_attiny13/2013-09-17-40
ATTiny13 – небольшой размер – хороший потенциал
ATTiny13 небольшой микроконтроллер (8 ног), наверное, самый дешевый в серии AVR. Выпускается в двух вариантах – обычном (ATTiny13) и с пониженным питанием (ATTiny13V). За пониженное питание приходится платить понижением тактовой частоты микроконтроллера (более медленная работа).
Общие характеристики:
- 120 инструкций оптимизированных для программирования на языках высокого уровня;
- 32 регистра общего назначения;
- почти каждая инструкция выполняется за 1 такт генератора, за счет чего быстродействие достигает 20 MIPS (20 миллионов операций за секунду);
- 1килобайт флеш-памяти для программ (самопрограмируется);
- 64байт EEPROM (энергонезависимая память);
- 64байт SRAM (оперативная память).
Что мы имеем из периферии?
- один 8 битных таймера/счетчика;
- два ШИМ канала;
- 4 канальный 10ти битный АЦП;
- аналоговый компаратор;
- SPI последовательный интерфейс;
- Watchdog таймер, внешние прерывания на всех ножках.
Особые плюшки:
- внутрисхемная отладка по одному проводу debugWIRE;
- программирование по последовательному SPI интерфейсу;
- различные источники прерывания как внешние, так и внутренние, 3 режима «Сна», детектор понижения питания, встроенный задающий генератор.
Питание, частота:
1.8 – 5.5В (для ATTiny13V) до 10МГц
2.7 – 5.5В (для ATTiny13) до 20МГц
В рабочем режиме потребляет 240мкА при питании 1.8В и частоте задающего генератора 1МГц. В режиме энергосбережения Power-down кушает меньше 0.1мкА при 1.8В
Программирование:
Для микроконтроллера наиболее удобен режим программирования по последовательному SPI интерфейсу. Для реализации этого режима необходимо подключить микроконтроллер к программатору по SPI интерфейсу (MOSI, MISO, SCK, RESET, GRD), запитать микроконтроллер номинальным напряжением.
Микроконтроллер может программироваться прямо в рабочей схеме (внутрисхемное программирование) но при этом должно соблюдаться условие – линиям SPI интерфейса при программировании не должно ничего мешать (большие емкости, маленькие сопротивления относительно общего провода и т.д.).
Более подробно почитать про внутрисхемное программирование и программаторы >
ATTiny13 был обновлен (буковка А в конце).
В результате обновления ATTiny13 перешел на новую технологию Atmel picoPower, а значит уменьшилось потребление энергии как в штатном режиме, так и в режимах энергосбережения, убрано разделение микроконтроллера на обычное и низковольтное питание (теперь все микроконтроллер можно запитать от 1.8 до 5.5В, при этом лишь необходимо соблюдать ограничения по частоте от 4МГц (для 1.8В) до 20Мгц).
Заключение:
ATTiny13 маленький, но быстрый микроконтроллер. Наверное, самый дешевый из серии AVR. Широко доступен в продаже. Наличие в ATTiny13 АЦП позволяет использовать его для обработки аналоговых выносных датчиков и передачи цифрового сигнала в основной контроллер (как пример – применение ATTiny13+датчик температуры обойдется дешевле и функциональнее чем DS18B20).
Маленький планарный корпус позволяет делать устройства небольшого размера. Из недостатков стоит отметить скромную функциональность, малое количество памяти и небольшое число ножек. Вывод: ATTiny13 просто незаменим для обработки сигналов с датчиков, для применения в компактных устройствах и устройствах не требующих сложных вычислений и большой функциональности.
Даташит для ATTiny13 берем здесь:
ATTiny13.pdf [1.71 MB] – Даташит для ATTiny13/13V
Datasheet ATTiny13A, Errata, Application Notes смотрим на официальной страничке.
(Visited 40 750 times, 4 visits today)
Источник: http://www.GetChip.net/posts/attiny13-nebolshojj-razmer-khoroshijj-potencial/
Переходим от Arduino к программированию ATtiny13 на C
Программирование даёт не только огромный простор для фантазии и возможностей, но, как и любой , одновременно навязывает свой стиль и ограничивает возможности.
Поэтому, если чувствуется, что Arduino становится тесноват — можно не только перейти на 32-битные контроллеры (например, STM32), но и попробовать более низкоуровневое программирование контроллеров.
Уходя ближе «к железу» — программировать придётся на более близком к железу уровне — и если это не ассемблер, то уж язык программирования Си — точно.
Пример подобного программирования уже приводился в статье .
У такого стандартного программирования микроконтроллеров есть существенное преимущество перед использованием Arduino-вских скетчей. Однако, за низкоуровневый полный контроль и возможность использовать все ресурсы микроконтроллера, приходится расплачиваться долгим и внимательным изучением документации (datasheet-а) на микроконтроллер. Т.е.
, если у вас ещё не было опыта работы с конкретным микроконтроллером — то вместо быстренького набрасывания скетча для решения своей задачи — вам придётся потратить дополнительное время на изучение мат. части.
Разумеется, не всегда это может быть оправдано и если задачу нужно и можно быстро решить при помощи Arduino — то почему бы и нет? Однако, если решение задачи на Arduino невозможно, то придётся потратить время на получение ценных опыта и знаний, которые помогут открыть все возможности, которые под силу микроконтроллеру.
Для примера, возьмём меленький, простой и дешёвый контроллер ATtiny13.
8-битный AVR микроконтроллер с 1 КБ программируемой Flash памяти — RISC архитектура — 120 команд, (большинство выполняется за один такт) — 32 8-битных регистра общего применения — 1 КБ программируемой Flash памяти программы — 64 байта EEPROM памяти данных, (до 100 000 циклов записи/стирания) — 64 байта SRAM памяти (статическое ОЗУ) — Один 8-разрядный таймер/счётчик с отдельным предделителем и два ШИМ канала — 4-канальный 10-битный АЦП со встроенным ИОН — Программируемый сторожевой таймер (watchdog) со встроенным генератором — Встроенный аналоговый компаратор — Внутрисистемное программирование через SPI порт — Внешние и внутренние источники прерывания Корпусное исполнение: — 8-выводные PDIP и SOIC корпуса: 6 программируемых линий ввода-вывода Диапазон напряжения питания, частота: 1.8 – 5.5В (для ATtiny13V) — до 10МГц 2.7 – 5.5В (для ATtiny13) — до 20МГц
Выводы микроконтроллера ATtiny13:
Как видим, микросхема микроконтроллера — маленькая — всего 8 ножек. Чтобы заставить её работать — нужно просто воткнуть её в макетную плату, подтянуть RESET (первый пин — на схеме обозначается — PB5) к шине питания через 10-килоомный резистор и подать питание — например, 5V снятые с пинов питания контроллера Arduino / CraftDuino.
В статье , уже подробно расписано как нужно подключить микроконтроллер ATtiny13 к контроллеру Arduino или CraftDuino, чтобы его можно было программировать через выводы микросхемы FT232RL используя режим bit-bang (режим управления отдельными выводам микросхемы). Поэтому сразу переходим к софтовой части. Раз решили программировать «по-взрослому», то и среда разработки нужна «взрослая».
Идём на сайт -a, и скачиваем свежую версию .
Atmel Studio — (наследница AVR Studio) — это бесплатная среда разработки для микроконтроллеров Atmel.
Сама IDE должна быть знакома, т.к. используется оболочка от Microsoft Visual Studio, однако следует обратить внимание, что в качестве компилятора используется GCC. После установки, на рабочем столе появится ярлык с симпатичной красной божьей коровкой. Запускаем IDE и привычным образом, быстренько создаём проект.
File -> New -> Project…
Выбираем С/С++ и GCC C Executable Project, пишем имя проекта, например, blink 🙂
Затем, среда предложит выбрать тип используемого микроконтроллера — выбираем ATtiny13.
Всё — шаблонный файл уже создан и можно начинать программировать:
Предлагаемый шаблон программы — напоминает что-то :
#include int main(void) { while(1) { //TODO:: Please write your application code } }
Описание работы портов микроконтроллера и используемых для насткройки и работы регистров, очень подробно приводится в документации на микроконтроллер — . Как увидим далее, конфигурирование и работа с портами сводится к установке соответствующих битов в нужных регистрах микроконтроллера. Если вы уже имели дело с установкой/проверкой/очисткой битов (работа с битовыми масками), то вам будет проще разобраться в происходящем. Но, на всякий случай, напомню: чтобы установить бит N — нужно выполнить побитовое ИЛИ с числом, где этот бит установлен (чтобы получить такое число — мы побитово сдвигаем влево единицу на заданное число позиций). Соответственно, чтобы сбросить бит N — нужно выполнить побитовое И с числом в котором установлены все биты кроме заданного (чтобы получить такое «интвертированное число» — мы сначала получаем число в котором бит установлен, а потом применяем к нему операцию побитового НЕ). value |= 1
Источник: http://robocraft.ru/blog/arduino/3423.html
Вежливая подсветка на микроконтроллере ATtiny13
Наверное многие видели, как включается и выключается салонный свет в иномарках… Плавно, красиво… Теперь и мы такое можем сделать! А мы это сделаем на микропроцессоре ATtiny13, просто, доступно и со вкусом.:) Идея, или что должно уметь устройство.
- при открытии двери свет в салоне плавно загорается;
- при закрытии двери свет плавно гаснет через 10 сек (по умолчанию), если двигатель не работает;
- при закрытии двери свет плавно гаснет без задержки, если двигатель работает;
- при закрытой двери и горящем освещении, после завода двигателя освещение плавно погаснет не дожидаясь 10 сек.;
- при оставленной открытой двери освещение будет автоматически погашено по истечении 10 мин после открытия двери;
- возможность программирования паузы от 0 до 60 сек с шагом 5 сек.;
- возможность программирования уровня чувствительности для игнорирования паузы после завода двигателя (6 уровней).
Схема.
Во главе сего дэвайса супер мощный «процессор» ATtiny13. В качестве управляющего транзистора выбран N-канальный 60T03H (AP60T03H, SSM60T03H, и т.д.) в корпусе TO-252. Выбор пал на него, т.к. такие легко достать со старых материнок, которых у меня накопилось предостаточно…
Как видно, плотность монтажа приличная, но изготовить её в домашних условиях не составит труда. Приступим!
Печатаем,
Переносим на фольгу,
Травим,
Собираем!
Прошивка.
Теперь пора подумать о прошивке. Писал я её почти пол года, правда с перерывом в 5,9 месяца… Короче с первого раза не пошла, потом не было времени, и вот я добрался до неё!
Тинька работает на частоте 9,6 МГц, ШИМ в режиме Top=FFh, т.е. частота на выходе ШИМа = 9600 кГц / 255 = 37,64 кГц. Вход от концевика дверей и кнопка работает по прерываниям, что позволяет вгонять МК в спячку для экономии электроэнергии. Заведён двигатель или нет определяем с помощью АЦП подключенного через делитель прямо на вход питания.
Больше писать не вижу смысла — кому надо, тот разберётся по исходникам, кому оно не надо — тупо шьёт и читает как оно работает…
· Прошивка v0.4 ·
dimmer1— Flash + eeprom Автодиммер
dimmer_flash2 — Отдельно Flash для Автодиммера.
dimmer_eeprom3 — Отдельно EEPROM для Автодиммера.
firmware_v0.5-4 — Исходники прошивки для Автодиммера (CodeVisionAVR)
pcb1 — Печатная плата в формате Sprint-Layout 5.0(83.86 КБ)
Flash + eeprom — это для тех, кто шьёт PonyProg`ом. Открыл файл, прошил и всё готово. Отдельно флэш и еепром — для остальных программаторов, где нужны отдельно файлы прошивки и данных…
FUSES:
Как это работает.
Собрали, прошили, теперь разберёмся, как это устройство подключить, и как оно функционирует. Подключить можно двумя способами — без вмешательства в плафон и с некоторыми переделками плафона (если это возможно).
Вариант 1. Без вмешательства в плафон. При таком подключении плавное включение/выключение работает только при работе плафона от концевика двери, также фонкция выключения света при работе дольше 10 минут не будет работать при включении света на постоянно.
Вариант 2. С переделками плафона. Тут устройство подключается после переключателя в плафоне, что позволяет управлять лампой не только от концевика, но и при включении лампы «постоянно».
У меня подключено по второй схеме, но вмешательство было не в плафон, а в «лампу»:
подпаиваем «лапки»
монтируем на лампу
хвост — земля
Всё! наше устройство уже должно работать! Открываем дверь и лампочка плавно загорается, закрываем дверь и лампочка через 10 секунд плавно гаснет. Если двигатель заведён, то при закрытии двери лампочка плавно гаснет сразу же!
Если не нравится длительность задержки, то её можно поменять. Для этого при открытой двери (когда лампа полностью загорелась) нажимаем кратковременно кнопочку. Лампочка начнёт мигать. Длительность задержки зависит от количества морганий (выключений) — каждое моргание, кроме первого = 5 сек. То есть:
Таким же образом регулируется чувствительность входа по определению заведённого двигателя. Для этого при закрытой двери (лампа полностью выключена) нажимаем кратковременно кнопочку. Лампа начнёт вспыхивать. Количество вспышек соответствует чувствительности:
Вот собственно и всё!
Источник: http://xn—-7sbbil6bsrpx.xn--p1ai/vezhlivaya-podsvetka-na-mikrokontroll.html
Многоголосая “музыкальная шкатулка” на ATtiny13
Даже на микроконтроллерах с ограниченными ресурсами можно реализовать многоголосую музыкальную шкатулку.
ATtiny13 обладает 1 килобайтом флеш-памяти (чего хватает на 512 программных инструкций, включая вектора прерываний) и 64 байтами оперативной памяти
Описание
Для вывода звука используется широтно-импульсная модуляция (ШИМ, PWM), микроконтроллеры AVR реализуют широтно-импульсную модуляцию при помощи таймеров.
Поскольку ATtiny13 обладает только одним таймером, его приходится использовать как для генерации ШИМ-сигнала, так и для синхронизации по времени при генерировании звука.
Частота ШИМ должна быть не меньше 20кГц, чтобы его шум не был слышен. При работе микроконтроллера на частоте 9,6МГц, 8-битный таймер переполняется через каждые 256 тактов, 37500 раз в секунду.
Однако, эта частота слишком высока для формирования звука, поэтому используется делитель.
В прерывании таймера некая переменная уменьшается на 1, а формирование очередного сэмпла звука происходит в основном теле программы, когда значение этой переменной достигнет нуля, после чего её значение увеличивается на величину делителя (в данном случае – 4).
Такой подход позволяет выравнивать звучание, если формирование очередного сэмпла заняло больше времени чем 4 цикла таймера, при этом не сбивается периодичность вызыва обработчика прерывания по переполнению таймера.
Для того чтобы придать формируемому звуку живость, чтобы он не казался монотонным, сигнал, формирующий его, разбит на две части: форманта – основная частота в виде синусоиды, и обертона – дополнительные высокие частоты, задающие тембр звука. Амплитуда этих двух сигналов меняется различным образом во времени, и, вместе с тем, изменяется тембр звука.
Поскольку ATtiny не обладают аппаратной поддержкой операции умножения, то умножение при вычислении амплитуды сигнала является узким местом. Для того чтобы ускорить операцию умножения, функция, реализующая его, написана на ассемблере.
Используемые формы сигналов, а также значения частот и мелодия хранятся во флеш-памяти.
Исходный код для avr-gcc
#define F_CPU 9600000UL #include #include #include #ifdef __AVR_MEGA__ inline int8_t mul_s_u_shr8(int8_t s, uint8_t u) { return (s * u) >> 8; } #else extern int8_t mul_s_u_shr8(int8_t, uint8_t); #endif typedef uint16_t fixed_6_10; const int8_t wave1[] PROGMEM = { 0, 6, 12, 19, 24, 30, 36, 41, 45, 49, 53, 56, 59, 61, 63, 64, 64, 64, 63, 61, 59, 56, 53, 49, 45, 41, 36, 30, 24, 19, 12, 6, 0, -6, -12, -19, -24, -30, -36, -41, -45, -49, -53, -56, -59, -61, -63, -64, -64, -64, -63, -61, -59, -56, -53, -49, -45, -41, -36, -30, -24, -19, -12, -6 }; const int8_t wave2[] PROGMEM = { 0, 27, 47, 59, 62, 59, 53, 45, 37, 28, 20, 12, 6, -1, -6, -11, -14, -18, -20, -22, -23, -23, -23, -22, -21, -19, -17, -15, -12, -10, -6, -3, 0, 3, 6, 10, 12, 15, 17, 19, 21, 22, 23, 23, 23, 22, 20, 18, 14, 11, 6, 1, -6, -12, -20, -28, -37, -45, -53, -59, -62, -59, -47, -27 }; #define SOUND_ONE_HERTZ_SPEED (65536 / (F_CPU / 256 / 4)) const fixed_6_10 notes[] PROGMEM = { 0, (fixed_6_10)(0.5 + 185.00 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 196.00 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 207.65 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 220.00 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 233.08 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 246.94 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 261.63 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 277.18 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 293.66 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 311.13 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 329.63 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 349.23 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 369.99 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 392.00 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 415.30 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 440.00 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 466.16 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 493.88 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 523.25 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 554.37 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 587.33 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 622.25 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 659.26 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 698.46 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 739.99 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 783.99 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 830.61 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 880.00 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 932.33 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 987.77 * SOUND_ONE_HERTZ_SPEED), (fixed_6_10)(0.5 + 1046.50 * SOUND_ONE_HERTZ_SPEED) }; const uint8_t song[] PROGMEM = { 0x30, 0x55, 0xC8, 0x74, 0x45, 0xC8, 0x1C, 0xA1, 0x44, 0xD0, 0x5B, 0x88, 0xC8, 0x1C, 0xC8, 0x8D, 0xC8, 0x50, 0xC8, 0x1B, 0xC8, 0x5C, 0xC8, 0x89, 0xC8, 0x50, 0xC8, 0x17, 0x84, 0xC8, 0x5C, 0xC8, 0x8B, 0xC8, 0x10, 0xC8, 0x55, 0x89, 0xC8, 0x10, 0xC8, 0x54, 0x84, 0xD0, 0x15, 0x8B, 0xD0, 0x17, 0x50, 0x88, 0xD0, 0x14, 0xD0, 0x12, 0x4F, 0x83, 0xD0, 0x86, 0xD0, 0x8B, 0xD0, 0x10, 0xC8, 0x4F, 0xC8, 0x10, 0x81, 0xD0, 0x88, 0xD0, 0x8D, 0xC8, 0x4B, 0xC8, 0x10, 0xC8, 0x52, 0xC8, 0x14, 0x89, 0xD0, 0x84, 0xD0, 0x8B, 0xD0, 0x14, 0xC8, 0x55, 0xC8, 0x17, 0x84, 0xD0, 0x8B, 0xD0, 0x88, 0xD0, 0x55, 0xC8, 0x14, 0xC8, 0x52, 0x8B, 0xD0, 0x86, 0xD0, 0x8F, 0xD0 }; typedef struct { fixed_6_10 wavePos; fixed_6_10 waveSpeed; uint8_t wave1amp; uint8_t wave2amp; uint8_t volMark; } chan_info; #define NUM_CHANNELS 3 chan_info chans[NUM_CHANNELS]; #define WAVE1_FADE_SPEED 1 #define WAVE2_FADE_SPEED 3 volatile uint8_t amp = 128; volatile int8_t vec = 0; volatile int8_t itskip = 3; uint8_t runTick = 0; uint8_t songPos = 0; uint8_t ticksToNote = 1; inline void doTick(); inline void doSample(); #ifdef TIMER0_OVF_vect ISR(TIMER0_OVF_vect) { #else ISR(TIM0_OVF_vect) { #endif uint8_t is = itskip; if (is > 0) { itskip = is – 1; uint8_t a = amp + vec; amp = a; OCR0A = a; } } inline void doSample() { int16_t sum = 127; for (chan_info * ch = &chans[0]; ch < &chans[NUM_CHANNELS]; ch++) { uint8_t wp = ch->wavePos >> 10; ch->wavePos += ch->waveSpeed; sum += mul_s_u_shr8(pgm_read_byte(&wave1[wp]), ch->wave1amp); sum += mul_s_u_shr8(pgm_read_byte(&wave2[wp]), ch->wave2amp); } if (sum > 255) sum = 255; if (sum < 0) sum = 0; vec = (sum - amp - 1) >> 2; if (runTick–) return; doTick(); } inline void doTick() { for (chan_info * ch = &chans[0]; ch < &chans[NUM_CHANNELS]; ch++) { if (ch->wave1amp >= WAVE1_FADE_SPEED) { ch->wave1amp -= WAVE1_FADE_SPEED; } else { ch->wave1amp = 0; } if (ch->wave2amp >= WAVE2_FADE_SPEED) { ch->wave2amp -= WAVE2_FADE_SPEED; } else { ch->wave2amp = 0; } } ticksToNote–; while (ticksToNote == 0) { uint8_t n = pgm_read_byte(&song[songPos++]); if ((n & 0xC0) == 0xC0) { if ((n & 0x3F) == 0) { ticksToNote = 64; } else { ticksToNote = n & 0x3F; } } else { chan_info * ch = &chans[n >> 6]; if (n & 0x20) { ch->volMark = pgm_read_byte(&song[songPos++]); } uint8_t tone = n & 0x1F; if (tone == 0) { ch->waveSpeed = 0; ch->wave1amp = 0; ch->wave2amp = 0; } else { ch->waveSpeed = pgm_read_word(¬es[tone]); ch->wave1amp = (ch->volMark & 0xF) wave2amp = ch->volMark & 0xF0; } ch->wavePos = 0; } if (songPos >= sizeof(song)) { songPos = 0; } } } int main(void) { DDRB = 0b00000001; TCCR0A = 0b10000011; TCCR0B = 0b00000001; TIMSK0 = TIMSK0 | (1 0) ; itskip += 4; doSample(); } }
Быстрое умножение на ассемблере
#include .global mul_s_u_shr8 mul_s_u_shr8: mov r21, r24 clr r23 clr r24 clr r20 tst r22 breq end cycle: brpl no_bit asr r21 ror r20 add r23, r20 adc r24, r21 lsl r22 brne cycle ret no_bit: asr r21 ror r20 lsl r22 brne cycle end: ret
Другие варианты мелодии
Поскольку память очень ограничена, мелодия может вмещать в себя всего несколько тактов звучания.
В программе используется несколько тактов из мелодии “River flows in you” композитора Yiruma.
Предлагаю несколько альтернативных фрагментов мелодий:
Joe Dassin – L'ete Indien
const uint8_t song[] PROGMEM = { 0x33, 0x97, 0x64, 0x66, 0xA7, 0x66, 0xD4, 0x10, 0xCA, 0x0B, 0xF2, 0x44, 0x87, 0xDE, 0x0B, 0xCA, 0x10, 0xCA, 0x13, 0xCA, 0x12, 0xCA, 0x10, 0xCA, 0x12, 0x43, 0x86, 0xD4, 0x0F, 0xCA, 0x0B, 0xF2, 0x43, 0x86, 0xE8, 0x43, 0x86, 0xE8, 0x10, 0x44, 0x88, 0xCA, 0x11, 0xCA, 0x10, 0xCA, 0x0E, 0xCA, 0x0B, 0xCA, 0x09, 0xCA, 0x08, 0xD4, 0x44, 0x88, 0xDE, 0x08, 0xCA, 0x09, 0xD4, 0x0B, 0xD4, 0x0E, 0x44, 0x89, 0xCA, 0x0C, 0xCA, 0x0B, 0xCA, 0x0C, 0xF2, 0x44, 0x89, 0xE8, 0x44, 0x89, 0xE8, 0x18, 0x45, 0x89, 0xD4, 0x15, 0xCA, 0x11, 0xF2, 0x45, 0x89, 0xDE, 0x11, 0xCA, 0x15, 0xCA, 0x18, 0xCA, 0x17, 0xCA, 0x15, 0xCA, 0x17, 0x44, 0x87, 0xD4, 0x13, 0xCA, 0x10, 0xF2, 0x44, 0x87, 0xE8, 0x44, 0x87, 0xE8, 0x13, 0x44, 0x8A, 0xD4, 0x10, 0xCA, 0x16, 0xF2, 0x44, 0x8A, 0xF2, 0x13, 0xCA, 0x12, 0xCA, 0x10, 0xCA, 0x12, 0x43, 0x8B, 0xE8, 0x43, 0x8B, 0xE8, 0x43, 0x8B, 0xE8, 0x43, 0x8B, 0xE8 }
Vangelis – La petite Fille de la Mer
const uint8_t song[] PROGMEM = { 0x3B, 0x76, 0xAB, 0x66, 0xE4, 0x83, 0xE4, 0x88, 0xE4, 0x83, 0xE4, 0x76, 0x76, 0x87, 0xD2, 0x17, 0xD2, 0x59, 0x8A, 0xD2, 0x1B, 0xD2, 0x57, 0x8B, 0xE4, 0x83, 0xE4, 0x88, 0xE4, 0x16, 0x83, 0xE4, 0x87, 0xE4, 0x8A, 0xE4, 0x1B, 0x8B, 0xE4, 0x83, 0xE4, 0x88, 0xE4, 0x83, 0xE4, 0x56, 0x87, 0xD2, 0x17, 0xD2, 0x59, 0x8A, 0xD2, 0x1B, 0xD2, 0x57, 0x8B, 0xE4, 0x83, 0xE4, 0x88, 0xE4, 0x16, 0x83, 0xE4, 0x87, 0xE4, 0x8A, 0xE4, 0x54, 0x88, 0xE4, 0x18, 0x83, 0xE4, 0x5B, 0x86, 0xE4, 0x1E, 0x88, 0xE4, 0x5C, 0x83, 0xE4, 0x1B, 0x86, 0xE4, 0x1B, 0x81, 0xE4, 0x84, 0xE4, 0x88, 0xE4, 0x59, 0x81, 0xE4, 0x84, 0xE4, 0x88, 0xE4, 0x12, 0x86, 0xE4, 0x56, 0x81, 0xE4, 0x19, 0x84, 0xE4, 0x5C, 0x86, 0xE4, 0x1B, 0x81, 0xE4, 0x59, 0x84, 0xE4, 0x59, 0x89, 0xE4, 0x81, 0xE4, 0x84, 0xE4, 0x18, 0x88, 0xE4, 0x83, 0xE4, 0x8C, 0xE4 }
Формат мелодии
Вы можете создавать свои мелодии, формат довольно простой:
Каждая нота кодируется одним байтом, старшие 2 бита (7й и 6й) кодируют номер канала (0, 1, 2), или паузу (3).
Если старшие два бита приняли значения 0, 1, или 2, то они кодируют номер канала (голоса) и:
– Если следующий за ними бит (5й) установлен, значит за этой нотой идёт байт кодирующий громкость по этому каналу. Старшие 4ре бита кодируют начальную громкость обертонов (от 0 до 15), нижние – форманты. Громкость запоминается для канала, и эта и все последующие ноты будут звучать с заданной громкостью.
– Младшие 5ть бит (от 4го по 0й) кодируют ноту. 0 – означает остановка звучания на канале. Остальные значения (от 1 до 31) кодируют ноту с шагом в полутон, начиная от “фа#” малой октавы, заканчивая “до” третьей окатвы.
Если старшие два бита – оба единицы, то это означает паузу в тиках до следующей команды. Один тик – это 1/36,6 секунды (=23,7 мс). То есть допустимы паузы от 27,3 мс до 1,72 секунды.
Проект для AtmelStudio 6
Исходный код проекта доступен для скачивания здесь: zip-файл, 33 кБ.
Помещённый здесь код является свободным. То есть, допускается его свободное использование для любых целей, включая коммерческие, при условии указания ссылки на автора (Погребняк Дмитрий, http://aterlux.ru/).
Источник: http://aterlux.ru/article/tiny_musicbox