Музыкальный звонок. Муз звонок дип. Пояснительная записка к дипломному проекту 88 страниц, 15 рисунков, 21 таблица, 24 источника, 5 приложений, 3 листа чертежей формата А1
![]()
|
20 Источник питания Рисунок 2.4 - Функциональная схема электромузыкального звонка 2.5 Разработка алгоритма работы устройства Для начала нам нужно придумать, как мы будем хранить мелодии в памяти. Для того, чтобы в памяти можно было что-либо хранить, нужно сначала это что-то каким-либо способом закодировать. Любая мелодия состоит из нот. Каждая нота имеет свой тон (частоту) и длительность звучания. Для того, чтобы закодировать тон ноты, можно просто все ноты пронумеровать по порядку. Удобнее нумеровать, начиная с самого низкого тона. Известно, что весь музыкальный ряд делится на октавы. В современном музыкальном ряду каждая октава делится на 12 нот. Семь основных нот и пять дополнительных. Деление на основные и дополнительные ноты сложилось исторически. В настоящее время используется музыкальный строй, в котором все 12 нот одной октавы равнозначны. Частоты любых двух соседних нот отличаются друг от друга в одинаковое количество раз. При этом частоты одноименных нот в двух соседних октавах отличаются ровно в два раза. Для нас же важно то, что коды всем этим нотам мы должны присваивать в порядке возрастания частоты. Начнем мы с ноты «До» первой октавы. Для электромузыкального звонка более низкие ноты не нужны. В таблице 2.1 показаны коды для всей первой октавы. Следующая, вторая октава продолжает первую и по кодировке, и по набору частот. Так нота «До» второй октавы будет иметь код 13, а частоту f12= fo ∙ 2. А нота «Ре» второй октавы будет иметь код 14 и частоту f13=f1 ∙ 2. И так далее. Музыкальная длительность тоже легко кодируется. В музыке применяют не произвольную длительность, а длительность, выраженную долями от целой (см. таблицу 2.2). В зависимости от темпа реальная длительность целой ноты меняется. Для сохранения мелодии необходимо соблюдать лишь соотношения между длительностями. Поэтому нам необходимо закодировать лишь семь вариантов длительности. Присвоим им коды от 0 до 6. Например так, как это показано в графе «Код» таблицы 2.2. Назначение графы «Коэффициент деления» мы пока опустим. Таблица 2.1- Кодировка нот первой октавы
Для справки: ![]() Таблица 2.2 - Кодирование музыкальных длительностей
Кроме нот, любая мелодия обязательно содержит музыкальные паузы. Определение. Паузы — это промежутки времени, когда ни один звук не звучит. Длительность музыкальных пауз принимает точно такие же значения, как и длительность нот. В связи с этим удобно представить паузу как еще одну ноту. Ноту без звука. Такой ноте логично присвоить нулевой код. Кодируем мелодии. Для экономии памяти удобнее каждую ноту кодировать одним байтом. Договоримся, что три старших бита мы будем использовать для кодирования длительности ноты, а оставшиеся пять битов — для кодирования ее тона. Пятью битами можно закодировать до 32 разных нот, что вполне хватит для электромузыкального звонка. Итак, если использовать приведенный выше способ кодирования, то код ноты ля первой октавы длительностью 1/4 в двоичном виде будет равен: ![]() Теперь мы можем приступать к кодированию мелодий. Для того, чтобы закодировать мелодию, нам нужна ее нотная запись. Используя нотную запись, мы должны присвоить каждой ноте и каждой музыкальной паузе свой код. Цепочка таких кодов и будет представлять собой закодированную мелодию. По условиям задачи наш электромузыкальный звонок должен уметь воспроизводить семь разных мелодий. Коды всех семи мелодий мы разместим в программной памяти микроконтроллера. Как определить конец каждой мелодии? Для того, чтобы компьютер знал, где заканчивается каждая мелодия, используем код 255 в качестве признака конца. Теперь нам нужно придумать, как микроконтроллер будет находить начало каждой мелодии. Все мелодии имеют разную длину, а в памяти они будут записаны одна за другой. Поэтому адрес начала каждой мелодии зависит от длины всех предыдущих. Удобнее всего просто по факту определить адрес начала каждой мелодии и поместить все семь адресов в специальную таблицу. Кроме этой таблицы нам еще понадобится таблица коэффициентов деления для всех 32 нот и таблица, хранящая константы задержки для всех используемых нами музыкальных длительностей. Алгоритм работы электромузыкального звонка (Рисунок 2.5): 1. Просканировать и определить номер нажатой кнопки. 2. Извлечь из таблицы начал мелодий значение элемента, номер которого соответствует только что определенному номеру нажатой кнопки. Это значение будет равно адресу в программной памяти, где начинается нужная нам мелодия. 3. Начать цикл воспроизведения мелодии. Для этого поочередно извлекать коды нот из памяти, начиная с адреса, который мы определили в пункте 2 алгоритма. 4. Каждый код ноты разложить на код тона и код длительности. 5. Если код тона равен нулю, отключить звук и перейти к формированию задержки (к п. 9 настоящего алгоритма). 6. Если код тона не равен нулю, извлечь из таблицы коэффициентов деления значение элемента с номером, равным коду тона. 7. Записать коэффициент деления, который мы нашли в пункте 6 настоящего алгоритма, в регистр совпадения таймера Т1. 8. Включить звук (подключить вывод ОС1А к выходу таймера Т1). 9. Извлечь из таблицы длительностей задержки значение элемента с номером, равным коду длительности. 10. Сформировать паузу с использованием константы задержки, которую мы нашли в пункте 9 настоящего алгоритма. 11. По окончании паузы выключить звук (отключить ОС1А от выхода таймера). 12. Повторять цикл (пункты 4—11 настоящего алгоритма) до тех пор, пока нажата соответствующая кнопка. 13. Если очередной код ноты окажется равным 255, перейти к началу текущей мелодии, то есть вернуться к п. 3 настоящего алгоритма Р ![]() исунок 2.5 – Алгоритм прогаммы для электромузыкального звонка 2.6 Разработка программного обеспечения микроконтроллера Возможный вариант программы на языке Ассемблер приведен в листинге 1 (См. Приложение А). 2.6.1 Описание программы Описание программы удобнее начать с конца. Начиная со строки 136 программы располагается описание так называемых таблиц данных. На самом деле каждая из этих "таблиц" представляет собой цепочку кодов, записываемых в программную память микроконтроллера и предназначенных для кодирования того либо иного вида данных. Для описания этих данных используются как операторы db, так и операторы dw. Первая таблица содержит коэффициенты задержки для формирования всех вариантов музыкальной длительности. Таблица начинается с адреса, соответствующего метке tabz. Вся таблица занимает одну строку программы (строка 136). Так как в нашей программе мы будем применять лишь семь вариантов длительности, таблица имеет 7 элементов. Каждый элемент записывается в память как двухбайтовое слово. В строках 137—140 описывается таблица коэффициентов деления для всех нот. Начало таблицы соответствует метке tabkd. Каждый элемент этой таблицы также имеет размер в два байта. Первый элемент таблицы равен нулю. Это неиспользуемый элемент. Ноты номер ноль у нас не существует. Ноль мы использовали для кодирования паузы. В паузе не формируется звуковой сигнал, поэтому и коэффициент деления там не имеет смысла. Поэтому значение нулевого элемента массива несущественно. Описание таблицы разбито на строки. Для удобства каждая строка описывает коэффициенты деления для одной октавы. Нулевая нота выделена в отдельную строку. Последняя октава неполная, так как наш электромузыкальный звонок будет использовать всего 32 ноты. В строках 143—200 описана таблица мелодий. Вернее, это не одна таблица, а семь таблиц (своя таблица для каждой из мелодий). Каждая таблица помечена своей отдельной меткой (те 11, те 12 — те 17). Значение каждой метки — это адрес начала соответствующей мелодии. Каждое значение таблицы мелодий записывается в память в виде одного байта. Поэтому все строки, кроме последней, для каждой таблицы имеют четное число значений. В строках 141,142 описана таблица начал всех мелодий. Начало этой таблицы отмечено меткой tabm. Таблица используется для того, чтобы программа могла найти адрес начала нужной мелодии по ее номеру. В качестве элементов массива выступают удвоенные значения меток mell, mel2 —mel7. Применение удвоенных значений обусловлено необходимостью перевода адресов из основной адресации в альтернативную. При трансляции программы вместо меток в память будут записаны конкретные адреса. 2.6.2 Процедура вычисления адреса Большое количество таблиц в нашей программе заставляет позаботиться о процедуре вычисления адреса. Однотипные вычисления удобно оформить в виде подпрограммы. Эта подпрограмма занимает строки 78—84. Вызов подпрограммы производится по имени addw. Подпрограмма получает номер элемента таблицы и адрес ее начала. Номер элемента передается в подпрограмму при помощи регистра YL, а адрес — через регистровую пару Z. Используя эти данные, подпрограмма вычисляет адрес нужного элемента. Для этого она сначала удваивает номер элемента (строка 79). Затем дополняет полученное значение до шестнадцатиразрядного путем записи в YH нулевого байта (строка 80). И, наконец, производит сложение двух шестнадцатиразрядных величин, находящихся к этому моменту в регистровых парах Y и Z (строки 81, 82). Результат вычислений при этом попадает, в регистровую пару Z. 2.6.3 Текст программы Теперь рассмотрим текст программы с самого начала. В строках 3...10 расположен модуль описания переменных (рабочих регистров). В строках 13...31 располагается модуль переопределения векторов прерываний, в строках 32...41 — модуль команд инициализации. 2.6.4 Особенности программы Процедура, расположенная в строках 45—52 программы, сканирует клавиатуру и находит код первой из нажатых кнопок. Найденый код находится в регистре count. Затем управление переходит к строке 53. С этого места начинается процедура выбора мелодии (строки 53—58). Суть процедуры — прочитать из таблицы tabm значение адреса начала этой мелодии. То есть прочитать элемент таблицы, номер которого равен коду нажатой кнопки. Прежде чем прочитать элемент, необходимо найти его адрес. Для вычисления адреса используем подпрограмму addw. Перед тем, как вызвать подпрограмму, подготовим все данные. Номер нажатой кнопки помещаем в регистр YL (строка 53). Адрес начала таблицы записываем в регистровую пару Z (строки 54, 55). И лишь затем в строке 56 вызывается подпрограмма addw. После выхода из подпрограммы в регистровой nape Z находится результат вычислений — адрес нужного нам элемента таблицы tabm. Следующие две команды (строки 57 и 58) извлекают тот элемент (адрес начала мелодии) и помещают его в регистровую пару X. Там этот адрес будет храниться все время, пока воспроизводится именно эта мелодия. Следующий этап — воспроизведение мелодии. Воспроизведением мелодии занимается процедура, расположенная в строках 59—77. Для последовательного воспроизведения нот нам понадобится указатель текущей ноты. В качестве указателя текущей ноты используется регистровая пара Z. В самом начале процедуры воспроизведения мелодии в регистровую пару Z помещается адрес начала мелодии их регистровой пары X (строки 59, 60). Затем начинается цикл воспроизведения (строки 61—77). В этом цикле программа извлекает код ноты по адресу, на который указывает наш указатель, выделяет из кода ноты код тона и код длительности, воспроизводит ноту, а затем увеличивает значение указателя на единицу. Затем весь цикл повторяется. Этот процесс происходит до тех пор, пока код очередной ноты не окажется равным 255 (метка конца мелодии). Прочитав этот код, программа передает управление на строку 62, где в регистр Z снова записывается адрес начала мелодии. Воспроизведение мелодии начнется сначала. Этот процесс должен прерваться лишь в одном случае — при отпускании управляющей кнопки S8. Для проверки состояния кнопок в цикл воспроизведения мелодии включена специальная процедура (строки 61—63). Процедура упрощенно проверяет состояние сразу всех кнопок. Она считывает содержимое порта PD (строка 61) и сравнивает его с кодом 0 x 7F (строка 62). Прочитанное из порта значение может быть равно 0x7F только в одном случае — если все кнопки отпущенны. Если хотя бы одна кнопка нажата, то при чтении порта мы получим другое значение. Проверкой вышеописанного условия занимается оператор breq в строке 63. Если все кнопки оказались отпущены, этот оператор завершает цикл воспроизведения мелодии и передает управление на метку ml, то есть на самое начало основного цикла программы. Там происходит выключение звука, а затем новое сканирование клавиатуры. Если хотя бы одна кнопка окажется нажатой, то цикл воспроизведения звука продолжается дальше, и управление переходит к строке 64, где происходит извлечение кода ноты. Так как адрес этой ноты находится в регистровой паре Z (указатель текущей ноты), то для извлечения ноты просто используется команда 1pm. В строке 65 происходит проверка признака конца мелодии. Только что прочитанный код ноты сравнивается с кодом 0xFF. Оператор breq в строке 66 передает управление по метке т4, если мелодия действительно закончилась (условие выполняется). Если код ноты не равен 0xFF, перехода не происходит, и управление переходит к строке 67. В строках 67—75 происходит обработка кода ноты. То есть из кода ноты выделяется код тона и код длительности. Сначала на код ноты накладывается маска, которая оставляет пять младших разрядов, а три старших сбрасывает (строка 67). Под действием маски в регистре temp остается код тона, который затем помещается в регистр fnotа (строка 68). Теперь нам нужно найти код длительности ноты. Для этого нам заново придется извлечь код ноты из памяти программ. Так как до этого момента мы не изменяли положение указателя текущей ноты, то для извлечения нет никаких препятствий. В строке 69 мы повторно извлекаем код ноты из памяти программ. Но на этот раз значение указателя увеличивается. Теперь можно приступать к выделению кода длительности. Длительность кодируется тремя младшими битами кода ноты. Для выделения этих битов нам также нужно использовать маску. Но одной маской нам не обойтись. Нам нужно не просто выделить три старших разряда, а сделать их младшими, как это показано на Рисунке 2.6. Процедура выделения кода длительности занимает строки 70—74. ![]() Рисунок 2.6 - Разложение кода ноты Сначала программа производит многократный циклический сдвиг кода ноты до тех пор, пока три старших разряда не станут тремя младшими. Для сдвига используется команда rо 1. Так как сдвиг происходит через ячейку признака переноса, то нам понадобится четыре команды сдвига. Эти команды занимают в программе строки 70—73. Затем в строке 74 на полученное в результате сдвигов число накладывается маска, которая выделяет три младшие бита, а пять старших сбрасывает в ноль. Полученный таким образом код длительности записывается в регистр dnota (строка 75). Когда код тона и код длительности определены, производится вызов подпрограммы воспроизведения ноты (строка 76). Оператор rjmp в строке 77 передает управление на начало цикла воспроизведения мелодии, и цикл повторяется для следующей ноты. Подпрограмма воспроизведения ноты занимает строки 85—110. Она выполняет следующие действия: - извлекает из таблицы tabkd коэффициент деления, соответствующий коду ноты; - программирует таймер и включает звук; - затем выдерживает паузу и звук выключает. Если код тона равен нулю (нужно воспроизвести паузу без звука), извлечение коэффициента деления и включение звука не выполняется. Подпрограмма сразу переходит к формированию паузы. Начинается подпрограмма воспроизведения ноты с сохранения всех используемых регистров (строки 85—88). Затем производится проверка кода ноты на равенство нулю (строка 89). Если код ноты равен нулю, то оператор breq в строке 90 передает управление по метке ntl, то есть к строке, где происходит вызов процедуры формирования задержки. Если код ноты не равен нулю, то программа приступает к извлечению коэффициента деления. Для вычисления адреса элемента таблицы tabkd, где находится этот коэффициент, снова используется подпрограмма addw. Код тона помещается в регистр YL (строка 91), а адрес начала таблицы — в регистровую пару Z (строки 92, 93). Вызов подпрограммы addw производится в строке 94. В регистровой паре Z подпрограмма возвращает адрес элемента таблицы, где находится нужный нам коэффициент деления. В строках 95, 96 из таблицы извлекается этот коэффициент. А в строках 97,98 он помещается в регистр совпадения таймера. В строках 99,100 включается звук. В строке 104 вызывается специальная подпрограмма, предназначенная для формирования задержки. Подпрограмма называется wait и формирует задержку с переменной длительностью. Длительность задержки зависит от значения регистра dnota. По окончании задержки звук выключается (строки 102,103). На этом можно было бы закончить процесс воспроизведения ноты. Однако это еще не все. Для правильного звучания мелодии между двумя соседними нотами необходимо обеспечить хотя бы небольшую паузу. Если такой паузы не будет, ноты будут звучать слитно. Это исказит мелодию, особенно если подряд идет несколько нот с одинаковым тоном. Формирование паузы между нотами происходит в строках 104,105. Вспомогательная пауза формируется при помощи уже знакомой нам подпрограммы задержки. В строке 104 коду паузы присваивается нулевое значение (выбирается самая минимальная пауза). Затем в строке 105 вызывается подпрограмма wait. После окончания паузы остается только восстановить содержимое всех сохраненных регистров из стека (строки 106—109) и выйти из подпрограммы (строка 110). 2.6.5 Подпрограмма формирования задержки И последнее, что нам еще осталось рассмотреть, — это подпрограмма формирования задержки. Текст подпрограммы занимает строки 111—135. Как и любая другая подпрограмма, подпрограмма wait в начале сохраняет (строки 111—114) |