Музыка на AVR с помощью ШИМ

Нашел хороший и рабочий код который использует ШИМ, подключил к пьезо-пищалке и заиграла музыка.

Схема подключения:

Скачать файлы Си и другие (исходники):
– скачать zip архив.

Полную теорию на английском языке здесь:
– скачать pdf на англ. языке
– скачать pdf переведённый на русский с помощью Яндекс переводчика.

В музыке, ноты имеют различную длительность, максимальная 32, есть 16-ые, 8-ые, 4-ые, половинные и целые. tempo для всех нот одинаковый поэтому ноты будут определённой длительности, независимо от tempo 16-ые будут продолжительностью в 2 раза меньше чем 8-ые. а вот tempo определяет сколько будет эта эталонная задержка. меняя tempo, меняем кол-во тактов в минуту BPM.

void InitMusic()
{
  DDRB = 0xFF; //OCR1B как вывод

  // настраиваем таймер
  TCCR1A |= ( 1 << COM1B1);  // выставлять 0 на OC1B когда таймер TCNT1 = OCR1B

  TCCR1B |= ( 1 << WGM13)|( 1 << CS11); 
  //mode 8, CTC, Phase and Frequency Correct (TOP value is ICR1)
  // Обнуляем таймер и выставляем 1 в OC1B когда TCNT1 = ICR1

  //CS11 значит что предделитель таймера = 8 то есть он считаем с частотой 1 MHz 
  //(частота МК 8 МГц)
}


//Про то как настроить регистры для работы с AVR я писал тут Таймеры и тут ШИМ на AVR

void PlayMusic( const int* pMusicNotes, uint8_t tempo )
{
// pMusicNotes это указатель на таблицу содержащий музыкальные данные
// tempo Темп от 0 до 100 чем больше тем медленнее
 int duration;
 int note;
 int i;
 uint16_t delay = tempo * 1000;

 while( *pMusicNotes ) // пока не дошли до MUSIC_END == 0
 {
  note = *pMusicNotes;
//Работаем с адресами, берём значение 1 ячейки массива записываем в note
  pMusicNotes++;
//Т.к массив int ++ означает +4 (размер int 4 байта) и теперь у нас адрес
//нашего массива +4 как раз адрес следующей цифры

  duration = *pMusicNotes;
  pMusicNotes++;

  if( note == PAUSE )
  {
   //Пауза, ничего не проигрывать
   OCR1B = 0;
  }
  else
  {
   //не пауза воспроизвести звук
   OCR1B = DEFAULT_VOLUME;
//OCR1B отвечает за ШИРИНУ импульса, когда TCNT1 (таймер) == OCR1B
//то на выводе OC1B выставляется 0
//выставляем необходимую нам частоту
   ICR1H = (note >> 8);
// сначала пишем верхние биты (>> 8 это сдвиг на 8 битов)
// т.к AVR 8 битный нельзя записывать 16 битные числа за 1 такт
   ICR1L = note;
  }

  //длительность ноты
  for(i=0;i<32-duration; i++>//_delay_loop_2(); ждёт 4 такта МК в нашем случае 0.5 мкс
    _delay_loop_2( tempo );


 }

 //turn off any sound
 OCR1B = 0;
}

Добавить комментарий