Формирование звука с помощью микроконтроллера не составляет большого труда. Достаточно взять за основу программу мигающего светодиода и подключить вместо него динамик, а в самой программе поменять константу задержки таким образом, чтобы частота на выходе повысилась до звукового диапазона. Человек может услышать звуки с частотой от 50Hz до 15kHz. Светодиод в одном из наших уроков мигает с частотой 4Hz, а если уменьшить время задержки в 1000 раз, то можно получить частоту на выходе 4kHz.
Задача: разработаем программу, при помощи которой микроконтроллер Atmega8 будет воспроизводить простую однотональную мелодию. Тактовая частота микроконтроллера 8МГц.
Музыкальный ряд делится на октавы, каждая октава делится на 12 нот, это 7 основных(До, Ре, Ми, Фа, Соль, Ля, Си) и пять дополнительных(До диез, Ре диез, Фа диез, Соль диез, Ля диез). Частоты двух соседних нот отличаются друг от друга в одинаковое количество раз, а частоты двух одноименных нот двух соседних октав отличаются в 2 раза.
Для формирования звука используем шестнадцатиразрядный таймер/счетчик1, он будет работать в режиме СТС (сброс при совпадении), для управления коэффициентом пересчета используем регистр ICR1. Режим СТС позволяет осуществлять непосредственное управление частотой сигнала. Для активизации этого режима биты WGM13 и WGM12 устанавливаем в единицу.
Для того чтобы в режиме СТС на выходе формировался периодический сигнал, необходимо настроить выход OC1A таким образом, чтобы при каждом совпадении сигнал на выходе менял свое значение на противоположное. Для этого установим бит COM1A0 в единицу и подключим к нему динамик, а также настраиваем порт PB1 на выход.
Частота сигнала на выходе OC1A определяется по формуле:
где N - коэффициент пересчета предварительного делителя, вместо OCR1A в нашем случае ICR1.
Исходя из этой формулы расчитываем коэффициенты для ICR1, зная частоты основных нот. Так как для большинства разобраться с нотной портитурой не очень просто, названия нот я определил как в редакторе мелодий старого телефона Сименс. Коды таких мелодий еще можно найти в интернете и в дальнейшем поменять в исходном тексте. Также важно понятие как длительность тона - это время звучания одной ноты, она выражается долями от целой. В общем за проигрывание ноты у нас отвечает функция play_note.
Кроме нот любая мелодия содержит паузы, это промежуток времени когда ни один звук не звучит. За паузы отвечает функция set_tempo, она попросту в нужное время отключает таймер/счетчик.
Ниже представлен исходный код с подробным описанием, если что-то не понятно задавайте вопросы. На одном из следующих заданий разберем как можно сделать синтезатор голоса.
// Воспроизведение однотональных мелодий с помощью AVR #include <avr/io.h> // 1 октава #define A2 9008 // Ля #define Ais2 8580 // Ля диез #define B2 8098 // Си // 2 октава #define C3 7644 // До #define Cis3 7214 // До диез #define D3 6810 // Ре #define Dis3 6427 // Ре диез #define E3 6066 // Ми #define F3 5726 // Фа #define Fis3 5404 // Фа диез #define G3 5101 // Соль #define Gis3 4815 // Соль диез #define A3 4544 // Ля #define Ais3 4289 // Ля диез #define B3 4049 // Си // 3 октава #define C4 3821 // До #define Cis4 3607 // До диез #define D4 3404 // Ре #define Dis4 3213 // Ре диез #define E4 3033 // Ми #define F4 2862 // Фа #define Fis4 2702 // Фа диез #define G4 2550 // Соль #define Gis4 2407 // Соль диез #define A4 2272 // Ля #define Ais4 2144 // Ля диез #define B4 2024 // Си // 4 октава #define C5 1910 // До #define Cis5 1803 // До диез #define D5 1702 // Ре #define Dis5 1606 // Ре диез #define E5 1516 // Ми #define F5 1431 // Фа #define Fis5 1350 // Фа диез #define G5 1275 // Соль #define Gis5 1203 // Соль диез #define A5 1135 // Ля #define Ais5 1072 // Ля диез #define B5 1011 // Си // 5 октава #define C6 955 // До #define Cis6 901 // До диез #define D6 850 // Ре #define Dis6 803 // Ре диез #define E6 757 // Ми // Длительность тона #define LE32 1*3 // 1/32 #define LE16 2*3 // 1/16 #define LE16D 3*3 #define LE16T 2*2 #define LE8 4*3 // 1/8 #define LE8D 6*3 #define LE8T 4*2 #define LE4 8*3 // 1/4 #define LE4D 12*3 #define LE4T 8*2 #define LE2 16*3 // 1/2 #define LE2D 24*3 #define LE1 32*3 // 1 // Функция задержки в us void Delay_us(unsigned char time_us) { register unsigned char i; for(i = 0; i < time_us; i++) // 4 цикла { asm (" PUSH R0 "); // 2 цикла asm (" POP R0 "); // 2 цикла // 8 циклов = 1 us для 8MHz } } // Функция задержки в ms void Delay_ms(unsigned int time_ms) { register unsigned int i; for(i = 0; i < time_ms; i++) { Delay_us(250); Delay_us(250); Delay_us(250); Delay_us(250); } } unsigned char temp; // Функция установки темпа и паузы void set_tempo(unsigned char number) { temp = number; // Установка темпа TCCR1A = (1 << COM1A0); // Используем вывод таймера OC1A TCCR1B = 0; // Звук выключен } // Функция проигрывания ноты void play_note(unsigned int note, unsigned int LE) { ICR1 = note; // Установка ICR1 TCNT1 = 0x0000; // Очистка Timer/Counter1 TCCR1B |= (1 << WGM13)|(1 << WGM12) // CTC режим |(1 << CS10); // Без предделителя Delay_ms(LE*temp*7); // Длительность ноты TCCR1B = 0; // Звук выключен } int main(void) { DDRB |= (1 << PB1); PORTB = 0x00; Delay_ms(50); set_tempo(3); // Установка темпа play_note(Dis4,LE8); play_note(Cis4,LE8); play_note(Fis3,LE4); play_note(Fis4,LE4); play_note(Fis4,LE4); play_note(Dis4,LE8); play_note(Cis4,LE8); play_note(Fis3,LE4); play_note(Fis4,LE4); play_note(Fis4,LE4); play_note(Dis4,LE8); play_note(Cis4,LE8); play_note(Fis3,LE4); play_note(Fis4,LE4); play_note(Dis3,LE4); play_note(Fis4,LE4); play_note(Cis3,LE4); play_note(F4,LE4); play_note(F4,LE4); while(1); }
Комментарии
_________________
кстати большое спасибо вашему сайту очень познавательные занятия.. много чего полезного узнал для себя. спасибо большое!!!
как это сделать просто без музыкального образования ? может есть какие-то программы ?
integrator.adior.ru/.../436 - здесь теория
integrator.adior.ru/.../437 - здесь музыкальный автомат. Все на Ассемблере, но с подробными комментариями и с инструкцией по компиляции и заливке.
В примере Полонез Огинского.
#define A4 2272 // Ля
. Частота 8 МГц, делитель 8. По формуле получаем 8000000/( 2 * 440 * 8 ) = 1136.Код:
8000000/(2*1*(1+2272)) = 1759,7 Гц
частота соответствует ноте ля третей октавы
99-я страница:
ww1.microchip.com/.../...
Да, действительно, без предделителя, в коде исправил, спасибо
Во-первых, хотел бы отметить, что в статье опечатка: для того чтобы состояние вывода OC1A менялось на противоположное в единицу нужно установить бит COM1A0, а COM1A1 оставить нулём. В коде, кстати, правильно написано.
Во-вторых, почему используется регистр захвата ICR1, а не регистр сравнения? Я с регистром захвата только разбираюсь, но здесь, мне кажется, можно было бы с помощью блока сравнения реализовать или я ошибаюсь и не вижу каких-то подводных камней?
Заранее спасибо!
Спасибо, исправил