8 апреля 2014 г.

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

Как играть музыку с помощью AVR микроконтроллеров и ШИМ.

Я пытался заставить играть музыку флоппи дисковод, вначале использовал обыкновенный delay, получилось генерировать импульсы нужной мне частоты, но была большая погрешность.
Пытался использовать прерывания по переполнению таймера, чем больше частота звука тем больше были погрешности и не удавалось играть ноты определённой длительности 8-ые 16-ые и т.д
Нашел хороший и рабочий код который использует ШИМ, подключил к пьезо-пищалке и заиграла музыка.

Спасибо aquaticus.info также здесь вся инфа про код
исходники на Github

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

Комментариев нет :

Отправить комментарий