Продолжение урока затянулось, оно и понятно, пришлось освоить работу с картами памяти и файловой системой FAT. Но все таки, оно свершилось, урок готов — фактически новогоднее чудо.
Дабы не перегружать статью информацией, я не буду описывать структуру формата wav файла, информации в поисковиках более чем предостаточно. Достаточно сказать, что если открыть файл, каким либо Hex редактором, то в первых 44 байтах содержится вся информация о типе файла, частоте дискретизации, количестве каналов и пр. Если нужно анализировать файл, читайте этот заголовок и будет вам счастье.
Полезные данные начинаются с 44 байта, по сути они содержат уровни напряжений, из которых формируется звук. Мы уже говорили про ступени напряжения, в прошлой части урока. Таким образом, все просто, нужно эти ступеньки вывести на динамик с частотой дискретизации файла.
Как физически заставить динамик дрыгаться? Нужно выводить эти уровни напряжения, при помощи ШИМ, либо использовать R2R. В любом случае, использовать очень просто, прочитал число, засунул его либо в OCR, либо в PORTx. Далее через определенное время, подставил следующее значение и так до конца файла.
Пример, некий wav файл, данные идут с 44=0х2С байта, там записано число 0х80, воспроизводим звук например ШИМом первого таймера, пишем OCR1A=0х80; Допустим, частота дискретизации вавки 8кГц, соответственно прерывание должно быть настроено на эту же частоту. В прерывании, подставляем следующее значение 0x85 через 1/8000=125мкс.
Как настроить прерывание на 8кГц? Вспоминаем, если таймер работает на частоте 250кГц, то регистр сравнения прерывания нужно подставить (250/8)-1=31-1 или 0x1E. С ШИМом тоже все просто, чем выше частота на которой он работает тем лучше.
Чтобы прошивка работала, условимся, что флешка отформатирована в FAT32, используется либа PetitFat из урока 23.2. Файл в формате wav либо 8кГц, либо 22,050кГц, моно. Название файла 1.wav. Анализируем прошивку.
#include <mega8.h> #include "diskio.h" #include "pff.h" unsigned char buffer[512]; /* буфер в который копируется инфа с флешки */ volatile unsigned int count; //счетчик скопированных данных interrupt [TIM2_COMP] void timer2_comp_isr(void) //прерывание в котором подставляются значения { OCR1A = buffer[count]; //выводим звук на динамик if (++count >= 512) //увеличиваем счетчик count = 0; //если 512 обнуляем } void main(void) { unsigned int br; /* счетчик чтения/записи файла */ unsigned char buf = 0; //переменная определяющая какая часть буфера читается FATFS fs; /* Рабочая область (file system object) для логических дисков */ PORTB=0x00; DDRB=0x02; //дрыгаем шимом ocr1a // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 8000,000 kHz // Mode: Fast PWM top=0x00FF // OC1A output: Non-Inv. TCCR1A=0x81; TCCR1B=0x09; TCNT1=0x00; OCR1A=0x00; // Timer/Counter 2 initialization // Clock source: System Clock // Clock value: 250,000 kHz // Mode: CTC top=OCR2 TCCR2=0x0B; TCNT2=0x00; //OCR2=0x1E; //настройка регистра сравнения для 8кГц OCR2=0xA; //для 22кГц #asm("sei") // Timer(s)/Counter(s) Interrupt(s) initialization if(disk_initialize()==0) //инициализируем флешку { pf_mount(&fs); //монтируем файловую систему pf_open("1.wav"); //открываем вавку pf_lseek(44); //перемещаем указатель на 44 pf_read(buffer, 512,&br); //в первый раз заглатываем сразу 512байт TIMSK=0x80; //врубаем музон while(1) { if(!buf && count>255) //если воспроизвелось больше 255 байт, { pf_read(&buffer[0], 256,&br);//то читаем в первую половину буфера инфу с флешки buf=1; if (br < 256) //если буфер не содержит 256 значений значит конец файла break; } if(buf && count<256) { pf_read(&buffer[256], 256,&br); // читаем во вторую часть буфера с флешки buf = 0; if (br < 256) break; } } TIMSK = 0x00; //глушим все pf_mount(0x00); //демонтируем фат } while (1) { } } |
Для проверки, на ножку OCR1A подключаем динамик через конденсатор 100мкФ, «+» на ножку микроконтроллера, «-» на динамик. «-» динамика на землю, «+» на конденсатор.
Не ждите громкого сигнала на выходе, чтобы звучало громко, необходим усилитель. На видео это хорошо видно. Для теста залил петуха 8кГц и трек 22кГц.
Желающие могут смело увеличить частоту таймера2, чтобы проигрывать файлы 44кГц, опыты показывают, что можно добиться вполне неплохого качества звучания. На видео звук слабый и качество плохое, но на самом деле это из-за того, что снимал на фотоаппарат.
Также выкладываю материалы любезно предоставленные Аппаратчиком — исходник для GCC, с которого была написана прошивка под CAVR.
И видео с воспроизведением 44кГц.
Пользуясь случаем поздравляю Всех с Наступающим, желаю чтобы все прошивки и девайсы у вас работали 🙂
Проект wav плеера на Atmega8
С резистором я сам знаю 😛 а вот как организовать регулировку с контроллера?
каждая точка это амплитуда, если домножать все точки на коэффициент, то общая амплитуда будет изменяться
Подскажите, правильно я понимаю, что:
1) Программирование программатором, использующим MISO и т.д. будет далее не доступно?
нет, в момент программирования контроллер находится в состоянии сброса, поэтому основная прога никак не повлияет
Для использования на Atmega32a нужно только порты поменять на соответствующие и библиотеку mega32?
и настройки spi
Скачал проект при компиляции выводит кучу Warning`ов. Это нормально или искать где ошибка?
нормально
Доброго времени суток!
Очень понравился ваш проект! Я его решил перенести на arduino2560 и при компиляции всплывает ошибка
invalid conversion from ‘unsigned int*’ to ‘WORD* {aka short unsigned int*}’ [-fpermissive]
С чем это может быть связано? Все типы выставлены вроде правильно
#include
#include
#include
unsigned int buffer[512]; /* буфер в который копируется инфа с флешки */
volatile unsigned int count; //счетчик скопированных данных
extern unsigned int br;
//volatile unsigned int tcnt2;
void setup()
{ pinMode(53, OUTPUT);
pinMode(51, OUTPUT);
digitalWrite(53, LOW);
digitalWrite(51, LOW);
}
//***************Настройка таймера-счётчика****************
void Timer1_init()
{ TCCR1B |= ((1<<WGM12) | (1<<CS12));
TIMSK1 |= (1<255) //если воспроизвелось больше 255 байт,
{ //pf_read(buffer[0], 256, br);//то читаем в первую половину буфера инфу с флешки
buf=1;
if (br < 256) break; //если буфер не содержит 256 значений значит конец файла
}
if(buf && count<256)
{ pf_read(&buffer[256], 256,&br); // читаем во вторую часть буфера с флешки
buf = 0;
if (br < 256) break;
}
}
//TIMSK = 0x00; //глушим все
pf_mount(0x00); //демонтируем фат
}
}
}
читайте не unsigned int а unsigned char
Правильно ли я понимаю что ШИМ в данном примере работает на частоте 31250 Гц?
Какую роль играет частота ШИМа, или важно только чтобы прерывание было настроено на частоту дискретизации файла?
чем быстрее шим, тем быстрее вы можете перейти от одного уровня напряжения к другому. если скорость будет не достаточная, то тупо напряжение не будет успевать меняться, вместо ожидаемого звука вы услышите совсем не то
Кстати о петухах, новый язык прогирования Petooh появился
А разве не нужно согласование уровней с SD картой? Или MCU от трех вольт запитан?
от 3.3В
Собрал всё работает,не получается таймер 2 настроить чтоб проигрывать файлы на 44кГц,,поставил кварц на 16мГц, дайте пожалуйста код настройки таймера
в статье есть формула, подставьте значения. теорию можно почитать тут http://avr-start.ru/?p=4500
Доброй ночи.
Есть вопрос по схеме. При использовании прошивки wav_player.7z и аудио записи в формате wav 8кГц — звук идет через динамик, но на фоне воспроизведения идет сильный гул в результате которого слушать мелодию не реально. В чем может быть проблема?
кондер стоит для отвязки?
Да, конденсатор тоже ставил согласно схемы, но результат не изменился в лучшую сторону. Может ли быть это из за используемого усилителя? Я использовал усилитель вот такой: ardu.net/ru/moduli/41-usilitel-zvuka-2kh3w-25-5-5v-miniatyurnyj-pam8403-190871911.html
При его использовании в других схемах такого шума нет.
возможно, попробуйте подтянуть выход с мк на землю через 100кОм.
Добрый вечер!
Каким образом в данном проекте заставить воспроизводить один из двух звуковых файлов (1.wav и 2.wav) по нажатию на одну из двух кнопок?
в конце статьи есть что то вроде вав плеера
Здравствуйте! Спасибо огромное за Ваши уроки. Собрал схему данного урока — работает!
Подскажите пожалуйста, как сделать так, чтобы воспроизведение начиналось не с момента подачи питания, а через некоторое время? Создал прерывание по переполнению таймера 0, в нем веду отсчет времени, а где подставить условие на начало воспроизведения не знаю. Помогите пожалуйста.
сделайте флаг while(play!= 1);
Файл WAV должен быть в PCM 8бит а не 16 бит, при 16 бит ВЧ шум.
Полезные данные начинаются с 44 байта)))),народ то не путайте
если формат файла известен заранее, то все так и есть, оттуда начинаются сырые pcm отсчеты.
привет благодарю вас за код, но я хочу, чтобы иметь возможность воспроизводить разные файлы для разных событий, например, когда значение adc становится ниже определенного уровня. Я хочу, чтобы иметь возможность выбрать конкретный wav-файл, это мой модифицированный код. Любая помощь будет очень благодарен спасибо
/*i used 8mhz internal oscillatot
i am playing at 8bit mono 16khz
*/
#include
#include «diskio.h»
#include «pff.h»
#include
#define sw1 PIND.2
#define sw2 PIND.3
unsigned char buffer[512]; /* * the buffer into which the infa is copied from the flash drive */
volatile unsigned int count; //copied data counter
unsigned int br; /* counter read / write file */
unsigned char buf = 0; // the variable that determines which part of the buffer is read by
FATFS fs; /* The working area (file system object) for logical disks */
bit flag1=0,flag2=0;
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
OCR1A = buffer[count]; //output the sound to the speaker
if (++count >= 512) // increase the counter (1 sector)
count = 0;
}
void playfile(char *mywav){
if(disk_initialize()==0) // initialize the flash drive
{
pf_mount(&fs); //mount the drive
pf_open(mywav); //open the wav file
pf_lseek(44); // move the pointer to 44th byte of the wav file
pf_read(buffer, 512,&br); // the first time we swallow 512bytes
TIMSK=0x80;
while(1)
{
if(!buf && count>255) // if more than 255 bytes were reproduced
{
pf_read(&buffer[0], 256,&br); // then read the first half of the buffer from the sd card
buf=1;
if (br < 256) // if the buffer does not contain 256 values, then the end of the file
break;
}
if(buf && count<256)
{
pf_read(&buffer[256], 256,&br); // read the second part of the buffer from the sd card
buf = 0;
if (br < 256)
break;
} }
TIMSK = 0x00; // turn off all
pf_mount(0x00);
}
}
void main(void) //main program
{
PORTB=0x00;
DDRB=0x02;
PORTD=0x0C; //ENABLE INTERNAL PULLUPS
DDRD=(0<<DDD2) | (0<<DDD3);
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: Fast PWM top=0x00FF
// OC1A output: Non-Inv.
TCCR1A=0x81;
TCCR1B=0x09;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1A=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 250,000 kHz
// Mode: CTC top=OCR2
// OC2 output: Disconnected
TCCR2=0x0B;
TCNT2=0x00;
//OCR2=0x1E; /for 8khz (250/(8-1)=0x1E)
OCR2=0x0E; //for 16khz (250/(16-1)=0x0E) —best result
//OCR2=0xA; //for 22khz
#asm("sei") //enable global interrupts
// Timer(s)/Counter(s) Interrupt(s) initialization
delay_ms(100);
while (1)
{
if (!sw1 && flag1==0){
playfile("3.wav");
flag1=1; }
if (!sw2 && flag2==0){
playfile("4.wav");
flag2=1; }
} }
так вроде просто, условие if и читайте значение ADC
Здравствуйте!
Можете отправить подробную схему которая показано на втором видео?
Admin почитайте внимательно статью «http://microsin.net/programming/PC/wav-format.html»
полезные данные в wav начинаются с «data» (0x64617461)+4байта, но никак не с 44 байта,я уже не раз на это налетал.
Точнее после «data» (0x64617461)+4байта 🙂
Здравствуйте! Подскажите, как решить вопрос с памятью. Я хочу один раз записать и проигрывать только одну мелодию. Целую флешку для этого дела отдавать жалко. Какие есть варианты?
можно spi флешку поставить, или eeprom. если мелодия короткая, можно попробовать во внутреннюю память мк ее утолкать