Печать

Воспроизведение однотональных мелодий с помощью AVR

Автор: AntonChip Опубликовано . Опубликовано в Программирование на Си

Рейтинг:   / 13
ПлохоОтлично 

Формирование звука с помощью микроконтроллера не составляет большого труда. Достаточно взять за основу программу мигающего светодиода и подключить вместо него динамик, а в самой программе поменять константу задержки таким образом, чтобы частота на выходе повысилась до звукового диапазона. Человек может услышать звуки с частотой от 50Hz до 15kHz. Светодиод в одном из наших уроков мигает с частотой 4Hz, а если уменьшить время задержки в 1000 раз, то можно получить частоту на выходе 4Hz.

Задача: разработаем программу, при помощи которой микроконтроллер Atmega8 будет воспроизводить простую однотональную мелодию.

Воспроизведение однотональных мелодий с помощью AVR

Музыкальный ряд делится на октавы, каждая октава делится на 12 нот, это 7 основных(До, Ре, Ми, Фа, Соль, Ля, Си) и пять дополнительных(До диез, Ре диез, Фа диез, Соль диез, Ля диез). Частоты двух соседних нот отличаются друг от друга в одинаковое количество раз, а частоты двух одноименных нот двух соседних октав отличаются в 2 раза.

Для формирования звука используем шестнадцатиразрядный таймер/счетчик1, он будет работать в режиме СТС (сброс при совпадении), для управления коэффициентом пересчета используем регистр ICR1. Режим СТС позволяет осуществлять непосредственное управление частотой сигнала. Для активизации этого режима биты WGM13 и WGM12 устанавливаем в единицу.

Для того чтобы в режиме СТС на выходе формировался периодический сигнал, необходимо настроить выход OC1A таким образом, чтобы при каждом совпадении сигнал на выходе менял свое значение на противоположное. Для этого установим бит COM1A1 в единицу и подключим к нему динамик,а также настраиваем порт PB1 на выход.

Частота сигнала на выходе OC1A определяется по формуле:



где N - коэффициент пересчета предварительного делителя, вместо OCR1A в нашем случае ICR1.

Исходя из этой формулы расчитываем коэффициенты для ICR1, зная частоты основных нот. Так как для большинства разобраться с нотной портитурой не очень просто, названия нот я определил как в редакторе мелодий старого телефона Сименс. Коды таких мелодий еще можно найти в интернете и в дальнейшем поменять в исходном тексте. Также важно понятие как длительность тона - это время звучания одной ноты, она выражается долями от целой. В общем за проигрывание ноты у нас отвечает функция Play_LE.

Кроме нот любая мелодия содержит паузы, это промежуток времени когда ни один звук не звучит. за паузы отвечает функция Set_temp, она попросту в нужное время отключает таймер/счетчик.

Ниже представлен исходный код с подробным описанием, если что-то не понятно задавайте вопросы. На одном из следующих заданий разберем как можно сделать синтезатор голоса.

/*** Воспроизведение однотональных мелодий ***/

#include <avr/io.h>

#define	A2	    9008 // Ля (2 октава)
#define	Ais2    8580 // Ля диез
#define	B2	    8098 // Си
#define	C3	    7644 // До (3 октава)
#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 // Си
#define	C4  	3821 // До (4 октава)
#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 // Си
#define	C5	    1910 // До (5 октава)
#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 // Си
#define	C6	    955	// До (6 октава)
#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

void Delay_us(unsigned char time_us) // функция задержки в 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
    }
}

void Delay_ms(unsigned int time_ms)	// функция задержки в 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_temp(unsigned char number)   // функция установки темпа и паузы
{
  temp = number;                      // установка темпа
  TCCR1A = (1 << COM1A0);                // CTC режим, использование OC1A
  TCCR1B = (1 << WGM13)|(1 << WGM12);     // звук выключен
}

void Play_LE(unsigned int sound, unsigned int LE) // функция проигрывания ноты
{
  ICR1 = sound;   	                 // установка ICR1
  TCNT1 = 0x0000;                     // очистка Timer/Counter1
  TCCR1B |= (1 << WGM13)|(1 << WGM12)     // CTC режим
                      |(1 << CS10);    // предделитель = 8

  Delay_ms(LE*temp*7);               // длительность ноты

  TCCR1B = (1 << WGM13)|(1 << WGM12); // звук выключен
}

int main(void)
{
  DDRB |= (1 << PB1);	
  PORTB = 0x00;

  Delay_ms(50); 

      Set_temp(3);		 // установка темпа
      Play_LE(Dis4,LE8); 
      Play_LE(Cis4,LE8); 
      Play_LE(Fis3,LE4); 
      Play_LE(Fis4,LE4); 
      Play_LE(Fis4,LE4); 
      Play_LE(Dis4,LE8); 
      Play_LE(Cis4,LE8); 
      Play_LE(Fis3,LE4); 
      Play_LE(Fis4,LE4); 
      Play_LE(Fis4,LE4); 
      Play_LE(Dis4,LE8); 
      Play_LE(Cis4,LE8); 
      Play_LE(Fis3,LE4); 
      Play_LE(Fis4,LE4);           	
      Play_LE(Dis3,LE4); 
      Play_LE(Fis4,LE4); 
      Play_LE(Cis3,LE4); 
      Play_LE(F4,LE4);  
      Play_LE(F4,LE4); 
      Delay_ms(1000);
      while(1);
}

Комментарии  

+1 #1 nevermoresk 08.06.2011 14:20
Подскажите пожалуйста, как можно передать мелодию по COM порту ?
Сообщить модератору
0 #2 AntonChip 09.06.2011 07:00
Написать другую программу, использовать выводы RX/TX
Сообщить модератору
+1 #3 nevermoresk 09.06.2011 15:39
А можете выкинуть небольшой примерчик, передачи некоторого сообщения, не обязательно мелодии, на COM порт, именно в AVR Studio ?
Сообщить модератору
+1 #4 AntonChip 09.06.2011 17:10
Собирался написать такую статью в течение месяца, а пока можете поискать на других ресурсах
Сообщить модератору
0 #5 tabarigenn 12.07.2011 13:45
доброе время суток.. на данном примере небольшая заминка возникла. не понятно где в примере используется вывод OC1A и почему нет обработчика прерываний??

_________________

кстати большое спасибо вашему сайту очень познавательные занятия.. много чего полезного узнал для себя. спасибо большое!!!
Сообщить модератору
0 #6 AntonChip 12.07.2011 17:50
В данном примере прерывания так таковые не используются, таймер 1 работает в режиме СТС, и его вывод меняет свое состояние с большой частотой, тут и получается звук
Сообщить модератору
0 #7 tabarigenn 12.07.2011 23:56
оооойййй ссори.. я забыл что вывод ОС1А у atmega162 на которой я содеоирую не вторая ножка порта В как в вашем примере.. побежал поправлять..
Сообщить модератору
0 #8 tabarigenn 13.07.2011 10:17
ну все заработало :-)
Сообщить модератору
0 #9 FreshMan 31.10.2013 17:42
прога хорошая, спору нет....., но вот как переводить ноты с нотного стана celtic-music.ru/.../... в понятные нам A2, B2, D3 и так далее ?
как это сделать просто без музыкального образования ? может есть какие-то программы ?
Сообщить модератору
0 #10 spyphy 11.12.2013 22:25
только здесь не все ноты определены, которые встречаются в мелодиях формата сименс. как оставшиеся добавить? я в этом совсем не шарю.
Сообщить модератору
0 #11 spyphy 12.12.2013 00:22
TCCR1B = (1
Сообщить модератору
0 #12 Артём 31.01.2014 17:18
подскажите как в Attiny13 сделать 16 разрядный программный счетчик чтобы точно также можно было выводить мелодии. Можно хотя бы просто алгоритм в двух словах, как это сделать, а дальше сам как нибудь.
Сообщить модератору
0 #13 Александр Диордица 13.03.2015 02:30
подскажите как в Attiny13 сделать точно также как нибудь...
integrator.adior.ru/.../436 - здесь теория
integrator.adior.ru/.../437 - здесь музыкальный автомат. Все на Ассемблере, но с подробными комментариями и с инструкцией по компиляции и заливке.
В примере Полонез Огинского.
Сообщить модератору
0 #14 Ренат 11.04.2016 19:17
не понимаю как паузы делать... :sad:
Сообщить модератору

Рекомендуем посмотреть