Формирование звука с помощью микроконтроллера не составляет большого труда. Достаточно взять за основу программу мигающего светодиода и подключить вместо него динамик, а в самой программе поменять константу задержки таким образом, чтобы частота на выходе повысилась до звукового диапазона. Человек может услышать звуки с частотой от 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, а не регистр сравнения? Я с регистром захвата только разбираюсь, но здесь, мне кажется, можно было бы с помощью блока сравнения реализовать или я ошибаюсь и не вижу каких-то подводных камней?
Заранее спасибо!
Спасибо, исправил