ATMEGA+SD+WAV
ATMEGA+SD+WAV
Возникла проблемка при воспроизведении WAV с SD карты.
Исходные данные:
Atmega64 11.059200 МГц
Аудио выход OC0(PortB.4) через RC -> УНЧ
Вывод по средствам ШИМ (Fast PWM 8-bit Timer/Counter 0)
Работа с SD(FAT16) при использовании библиотеки Elm-Chan
Звуковой файл WAV PCM 8 бит 11.025 кГц (без RIFF заголовка, формат данных unsigned)
Компилятор WinAVR-20100110
Код(только то что касается вывода звука):
#define BUF_SIZE 512
unsigned char sound_buf[BUF_SIZE];
int count_buff;
ISR(TIMER2_COMP_vect) // Выполняется с частотой 11.025 кГц
{
cli();
OCR0=sound_buf[count_buff]; //Выводим очередной байт из звукового буфера в ШИМ
count_buff++; // Следующий....
TCNT2 = 0xC2; //init //Инициализация значения счётчика TC2
OCR2 = 0x32; //11.025
sei();
if(count_buff==BUF_SIZE+1)
{
count_buff=0;
res = pf_read(sound_buf, BUF_SIZE, &s1);//Считаем следующие 512 байт из 1.wav
}
}
Суть проблемы вот в чём. Собственно качество воспроизведения полностью перекрывает мои задачи, но есть одно НО.
Во время воспроизведения отчётливо слышны периодичесские "потрескивания". Опытным путём (смена частоты дискретизации и размера буффера)
было установлено, что они возникают в момент считывания (обновления буфера).
Вопрос:
Как можно исправить данную ситуацию. Возможно я плохо продумал алгоритм и есть у кого-то отработанные варианты
или кто-то уже наступал на подобные грабли?
Заранее благодарю за помощь!
С ув. SABRAT
Исходные данные:
Atmega64 11.059200 МГц
Аудио выход OC0(PortB.4) через RC -> УНЧ
Вывод по средствам ШИМ (Fast PWM 8-bit Timer/Counter 0)
Работа с SD(FAT16) при использовании библиотеки Elm-Chan
Звуковой файл WAV PCM 8 бит 11.025 кГц (без RIFF заголовка, формат данных unsigned)
Компилятор WinAVR-20100110
Код(только то что касается вывода звука):
#define BUF_SIZE 512
unsigned char sound_buf[BUF_SIZE];
int count_buff;
ISR(TIMER2_COMP_vect) // Выполняется с частотой 11.025 кГц
{
cli();
OCR0=sound_buf[count_buff]; //Выводим очередной байт из звукового буфера в ШИМ
count_buff++; // Следующий....
TCNT2 = 0xC2; //init //Инициализация значения счётчика TC2
OCR2 = 0x32; //11.025
sei();
if(count_buff==BUF_SIZE+1)
{
count_buff=0;
res = pf_read(sound_buf, BUF_SIZE, &s1);//Считаем следующие 512 байт из 1.wav
}
}
Суть проблемы вот в чём. Собственно качество воспроизведения полностью перекрывает мои задачи, но есть одно НО.
Во время воспроизведения отчётливо слышны периодичесские "потрескивания". Опытным путём (смена частоты дискретизации и размера буффера)
было установлено, что они возникают в момент считывания (обновления буфера).
Вопрос:
Как можно исправить данную ситуацию. Возможно я плохо продумал алгоритм и есть у кого-то отработанные варианты
или кто-то уже наступал на подобные грабли?
Заранее благодарю за помощь!
С ув. SABRAT
-
kaspersky89
- Родился
- Сообщения: 12
- Зарегистрирован: Пн янв 03, 2011 12:02:45
- Откуда: Брянск
- Контактная информация:
Re: ATMEGA+SD+WAV
Было дело, разрабатывал речевой информатор для автомобиля. Тоже были щелчки, в самом начале воспроизведения, и по ходу самого воспроизведения. Правдо у меня звук не через ШИМ выводился, а посредством ЦАП на резисторах. Сейчас времени нету, нужно на работу, вечером скину часть исходников, может поможет чем.
Re: ATMEGA+SD+WAV
Вас спасёт кольцевой буфер на 256 байт, где есть голова и хвост. В голову грузятся значения WAV файла, с хвоста считываются в прерывании ISR(TIMER2_COMP_vect). Обратите особое внимание на проверки по опустошению и переполнению кольцевого буфера. Там есть "критические секции".
Т.е. в основном цикле программы считывается сектор в 512 файловый буфер, а оттуда в своём цикле грузится в кольцевой буфер. При переполнении последнего - отдыхаем, ожидая прерывания таймера.
Скорости mega64/11059200/PWM 8 bit 22 kHz мне хватало для воспроизведения с SD FAT16/32 (elm-chan) WAV файлов с дискретизацией 11/22/44 кГц стерео.
Т.е. в основном цикле программы считывается сектор в 512 файловый буфер, а оттуда в своём цикле грузится в кольцевой буфер. При переполнении последнего - отдыхаем, ожидая прерывания таймера.
Скорости mega64/11059200/PWM 8 bit 22 kHz мне хватало для воспроизведения с SD FAT16/32 (elm-chan) WAV файлов с дискретизацией 11/22/44 кГц стерео.
- urry
- Сверлит текстолит когтями
- Сообщения: 1262
- Зарегистрирован: Пн дек 08, 2008 10:58:48
- Откуда: Винница
- Контактная информация:
Re: ATMEGA+SD+WAV
считывать в буфер нужно непркрывно, пока есть возможность (пока не наехали на другой указатель). Буфер должен быть кольцевым.
Пример здесь http://vrtp.ru/index.php?showtopic=14534
упс, уже опоздал
Пример здесь http://vrtp.ru/index.php?showtopic=14534
упс, уже опоздал
Re: ATMEGA+SD+WAV
Благодарю за ответы.
Извиняюсь за наглость, а можно кусочек кода а не весь проэкт?
Извиняюсь за наглость, а можно кусочек кода а не весь проэкт?
-
kaspersky89
- Родился
- Сообщения: 12
- Зарегистрирован: Пн янв 03, 2011 12:02:45
- Откуда: Брянск
- Контактная информация:
Re: ATMEGA+SD+WAV
Как и обещал привожу кусок кода отвечающего за воспроизведение, там где кольцевой буфер, без него никуда. Может будет полезен, там немного прокомментировал. Вобщем кто захочет, разберётся.
Mega32 16Mhz компилятор-IAR
Mega32 16Mhz компилятор-IAR
Код: Выделить всё
unsigned int countmus=0,i;
volatile unsigned char data[256]; //Буфер для считывания звука с карты
unsigned char song;
unsigned char play,is_play;
unsigned char gotoNextSong;
typedef struct
{
unsigned char id_riff[4]; // 4 byte
unsigned long len_riff; // 4 byte
unsigned char id_chuck[4]; // 4 byte
unsigned char fmt[4]; // 4 byte
unsigned long len_chuck; // 4 byte
unsigned int type; // 2 byte
unsigned int channels; // 2 byte
unsigned long freq; // 4 byte
unsigned long bytes; // 4 byte
unsigned int align; // 2 byte
unsigned int bits; // 2 byte
unsigned char id_data[4]; // 4 byte
unsigned long len_data; // 4 byte
} TitleWave;// 44 byte
unsigned char song;
unsigned char play;
unsigned char gotoNextSong;
__flash char FileNames [10][11] =
{
"muson01.wav","muson02.wav","muson03.wav","muson04.wav","muson05.wav",
"muson06.wav","muson07.wav","muson08.wav","muson09.wav","muson10.wav"
};
unsigned char WaveComp (char const __flash name[])
{
//~~~~~~~~~~~~~
FILE *f;
TitleWave tw;
//~~~~~~~~~~~~~
f = fopenc(name,READ);
if ( f==0 ) return (1); // ?? ??????? ????
fread(&tw,sizeof(TitleWave),1,f);
fclose(f);
if ( strncmp_G(tw.id_riff,"RIFF",4) != 0 ) return (1); // ?? ?????? ????????????? RIFF
if ( strncmp_G(tw.id_chuck,"WAVE",4) != 0 ) return (1); // ?? ?????? ????????????? CHUCK
if ( strncmp_G(tw.fmt,"fmt ",4)!= 0 ) return (1); // ?? ?????? ????????????? FMT
if ( tw.type != 1 ) return (1); // ??? ?????? - ?? ???????
if ( tw.channels != 1 ) return (1); // ?? ????
if ( tw.freq != 16000 ) return (1); // ?? 16000 ???
if ( tw.bytes != 16000 ) return (1);
if ( tw.bits != 8 ) return (1); // ?? 8 ???
if ( strncmp_G(tw.id_data,"data",4)!= 0 )return (1); // ?? ?????? ????????????? DATA
return (0);
}
/*
=====================
MAIN.
=====================
*/
void main ()
{
//~~~~~~~~~~
FILE *fp;
//~~~~~~~~~~
PORTA_Bit4 = 1;
while (!initialize_media());
PORTA_Bit4 = 0;
ACSR &=~(1 << ACD); //disable comparator! for safe power
Init_port();
PCMDDR = 0xFF; //порт на котором весит ЦАП
PCMPort = 0x00;
MCUCR |= (1 << SE);
TCCR0 |= (1 << CS01);
TCNT0 = 140;
TIMSK |= (1 << TOIE0);
SEI();
play=1; // разрешаем воспроизведение
song=0; // трэк под индексом 0
while(1)
{
if (play)//----------
{
// Запускаем таимер 0
TCCR0 |= (1 << CS01);
TCNT0 = 140;
// Включаем на нём прерывание
TIMSK |= (1 << TOIE0);
if (!WaveComp (FileNames[song])) // Если тип трэка тот что нужен
{
fp = fopenc(FileNames[song], READ); // открывем его на чтение
while ( ((fp -> position) < (fp -> length)) && !gotoNextSong ) // пока не дошли до конца
{
if (!countmus){
for (i=0;i<256;i++)
data[i]=fgetc(fp); // заполняем побайтно буфер
} // тем что прочитали из трэка
};
SLEEP();
fclose(fp); // если дошли до конца , закрываем файл
if (!gotoNextSong) play = 0;
gotoNextSong = 0;
}
else song = 0;
// Тормозим таймер и прерывание на нём
TCCR0 &= ~(1 << CS01);
TIMSK &= ~(1 << TOIE0);
};//----------
}
}
#pragma vector = TIMER0_OVF_vect
__interrupt void Timer0_Ovf (void)
{
PCMPort=data[countmus];
if (countmus<255) countmus++;
else countmus=0;
TCNT0 = 140;
}
};
-
phanis
- Вымогатель припоя
- Сообщения: 513
- Зарегистрирован: Сб фев 19, 2011 18:04:08
- Откуда: Татарстан, пос. Актюбинский
- Контактная информация:
Re: ATMEGA+SD+WAV
Можно на 2 половины поделить буфер пример
Код: Выделить всё
//IAR 5.50
#define ENABLE_BIT_DEFINITIONS
#include <ioavr.h>
#define OUT1 3 //
#define OUT2 4 //
#define SD_CS 0
#define SDI 5 //
#define SDO 6 //
#define SCK 7 //
#define AUDIO_BUFFER 32
#define CENTER AUDIO_BUFFER>>1
#define SD_CS_OFF() PORTB |= 1<<SD_CS
#define SD_CS_ON() PORTB &= 0xFF-(1<<SD_CS)
#define TRUE 1
#define FALSE 0
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#define schar char
#define sint int
#define slong long
#define START_TIMER_0 TCCR0B = 0x02 //start timer
#define STOP_TIMER_0 TCCR0B = 0x00
//void putchar(char TX);
void PLAY_WAV(void);
unsigned char audio_data_count;
unsigned char play;
//unsigned char temp_uart;
unsigned char SOUND_N;
#include "flash\file_sys.h"
#include "flash\file_sys.c"
void main(void)
{
asm("cli");
PORTB |= (1<<SDO)|(1<<SDI)|(1<<SD_CS);
DDRB |= (1<<SCK)|(1<<SDO)|(1<<SD_CS)|(1<<OUT1)|(1<<OUT2);
USICR = (1<<USIWM0)|(1<<USICS1)|(1<<USICLK);
TIMSK = 0x02; //timer interrupt sources
//UBRRL = 0x67; //set baud rate lower
//UCSRC = 0x06;
//UCSRB = 0x98; //enable
TCCR1A = 0xA1;
SOUND_N=0x30;
asm("sei");
while(1)
{
_FF_buff[0 ]=SOUND_N;
_FF_buff[1 ]=0x20;
_FF_buff[2 ]=0x20;
_FF_buff[3 ]=0x20;
_FF_buff[4 ]=0x20;
_FF_buff[5 ]=0x20;
_FF_buff[6 ]=0x20;
_FF_buff[7 ]=0x20;
_FF_buff[8 ]='W';
_FF_buff[9 ]='A';
_FF_buff[10]='V';
_FF_buff[11]= 0 ;
PLAY_WAV();
SOUND_N++;
if (!SOUND_N) SOUND_N=0x30;
}
}
void PLAY_WAV()
{
int i=4096;
char i2;
STOP_TIMER_0;
if( fopenc() )
{
while(i--)
{if ( ((rp -> position) < (rp -> length)) )
{
GET_BYTE();
if(i<AUDIO_BUFFER) _FF_buff[i]=0x00;
}
}
audio_data_count=0;
play=0;
TCCR1B = 0x09; //start Timer
START_TIMER_0;
while ( ((rp -> position) < (rp -> length)) )
{
switch (play)
{
case 1:
{
play=0;
for (i2=0;i2<CENTER;i2++) _FF_buff[i2]=GET_BYTE();
}break;
case 2:{
play=0;
for (i2=CENTER;i2<AUDIO_BUFFER;i2++) _FF_buff[i2]=GET_BYTE();
}break;
}
}
}
TCCR1B = 0x00; //start Timer
//STAT=0;
STOP_TIMER_0;
SD_CS_OFF();
}
#pragma vector = TIMER0_OVF0_vect
__interrupt void Timer0_Ovf (void)
{
TCNT0 = 0xd5; //reload counter value
OCR1AL = (0x80+_FF_buff[audio_data_count+1]);;
OCR1BL =_FF_buff[audio_data_count] ;
audio_data_count+=2;
switch(audio_data_count)
{
case AUDIO_BUFFER:
{
play=2;
audio_data_count=0;
}break;
case CENTER: play=1; break;
}
}- БАТАРЕЙКУС
- Потрогал лапой паяльник
- Сообщения: 346
- Зарегистрирован: Сб фев 07, 2009 19:21:25
Re: ATMEGA+SD+WAV
необязательно делать кольцевой буфер. намного проще считывать скажем 500байт по очереди. считал воспроизвёл считал воспроизвёл итд. а на последок 12 байт считываешь в буфер и пака SD-карта недоступна эти байты потихоньку выкидываешь. нужно лишь помнить что время обновления буфера у SD-карта около 500мксек. это я замерял на ММС 32Мб от нокиа 6230. такой метод я использовал для своего плеера WAV-16КГц/8бит, на PIC16F876A, который гораздо медленней и обделённый ОЗУ по сравнению с AVR
Мечтатель - не тот, кто сидит на диване и думает о несбыточном, а тот, кто всеми силами стремится воплотить несбыточное в реальность.
Re: ATMEGA+SD+WAV
БАТАРЕЙКУС писал(а):необязательно делать кольцевой буфер. намного проще считывать скажем 500байт по очереди. считал воспроизвёл считал воспроизвёл итд.
Как раз при таком варианте, в паузе при считывании очередных 500 байт происходит "треск" в воспроизведении звука.
У Вас не так?
- БАТАРЕЙКУС
- Потрогал лапой паяльник
- Сообщения: 346
- Зарегистрирован: Сб фев 07, 2009 19:21:25
Re: ATMEGA+SD+WAV
SABRAT писал(а):Как раз при таком варианте, в паузе при считывании очередных 500 байт происходит "треск" в воспроизведении звука.
У Вас не так?
зачем вам считывать 500байт?????
обьесняю алгоритм, допустим вы сделали WAV-16КГц/8бит. это соответствует периуду 62,5 мксек между сменами байтов в модуле ШИМ или ЦАП (через что воспроизводится звук). 62,5 мксек это достаточно много особенно для Атмеги.
512/16 = 32. - 512 байт в секторе, 16-за раз скачиваемых байт в буфер Атмеги, 32- прохода
512/32 = 16. - 512 байт в секторе, 32-за раз скачиваемых байт в буфер Атмеги, 16- прохода
можете использовать первый или второй вариант в зависимости возможностей.
итого получается вы считали 16 байт за раз, между временим пака воспроизводится байт. и ожидаете его окончания, затем заменяете в ШИМ-модуле байт и снова ждёте 62,5 мксек и так пака буфер в Атмеге не опустеет, при записи последнего байта из ОЗУ Атмеги, программа снова считывает 16 байт и так 32 раза. Последний раз, когда программа 32-ой раз считывает 16 байт с сектора SD-карты(32х16=512) у вас в ОЗУ Атмеги остаются 16 байт звука. а SD-карты карта обновляет свой буфер.(это 500мксек). и пака SD-карты недоступна Атмега потихоньку растрачивает байты из своего ОЗУ. кагда весе байты из ОЗУ Атмеги улетят в ШИМ, буфер SD-карты будет обновлён, так как 62,5мксек Х 16 = 1000мксек. что гораздо больше времени обновления буфер SD-карты.
Мечтатель - не тот, кто сидит на диване и думает о несбыточном, а тот, кто всеми силами стремится воплотить несбыточное в реальность.
Re: ATMEGA+SD+WAV
БАТАРЕЙКУС писал(а):SABRAT писал(а):Как раз при таком варианте, в паузе при считывании очередных 500 байт происходит "треск" в воспроизведении звука.
У Вас не так?
зачем вам считывать 500байт?????
обьесняю алгоритм, допустим вы сделали WAV-16КГц/8бит. это соответствует периуду 62,5 мксек между сменами байтов в модуле ШИМ или ЦАП (через что воспроизводится звук). 62,5 мксек это достаточно много особенно для Атмеги.
512/16 = 32. - 512 байт в секторе, 16-за раз скачиваемых байт в буфер Атмеги, 32- прохода
512/32 = 16. - 512 байт в секторе, 32-за раз скачиваемых байт в буфер Атмеги, 16- прохода
можете использовать первый или второй вариант в зависимости возможностей.
итого получается вы считали 16 байт за раз, между временим пака воспроизводится байт. и ожидаете его окончания, затем заменяете в ШИМ-модуле байт и снова ждёте 62,5 мксек и так пака буфер в Атмеге не опустеет, при записи последнего байта из ОЗУ Атмеги, программа снова считывает 16 байт и так 32 раза. Последний раз, когда программа 32-ой раз считывает 16 байт с сектора SD-карты(32х16=512) у вас в ОЗУ Атмеги остаются 16 байт звука. а SD-карты карта обновляет свой буфер.(это 500мксек). и пака SD-карты недоступна Атмега потихоньку растрачивает байты из своего ОЗУ. кагда весе байты из ОЗУ Атмеги улетят в ШИМ, буфер SD-карты будет обновлён, так как 62,5мксек Х 16 = 1000мксек. что гораздо больше времени обновления буфер SD-карты.
вот полностью согласен. Ну нафига устраивать геморрой с буфером в 512 кб (ппц просто) и каким то кольцевыми буфeрАми?! я настраивал звук на 8 бит МК с 256 байтами оперативки из которых я мог использовать только 140 - и всё работало да еще ГЭПЭЭСКА была прикручена)
суть в том чтобы в прерывании сбрасывать счетчик кидать в ШИМ байт звука (уже заранее вкусно приготовленный), потом считать следующий и ждать наступление следующего прерывания. ВСЁ!! Да , еще есть момент когда кончается сектор. Ну тоже не проблема - считали 512 байт, потом 2 КС потом ждём 0xFE , пришёл, читаем байт и ждём конца прерывания -у меня держало даже 44 khz mono при таком раскладе (держало бы и стерео да другой хрени навалом было
- Aheir
- Модератор
- Сообщения: 4517
- Зарегистрирован: Пн апр 03, 2006 11:43:25
- Откуда: Санкт - Петербург
- Контактная информация:
Re: ATMEGA+SD+WAV
Из своего опыта - я как раз с двумя буферами делал. Когда вычитывал один из них до половины, начинал заполнять другой... Достаточно было буферов небольшого размера, все работало совершенно без сбоев.
Оптимизм х (Опыт + Знания) = const
Re: ATMEGA+SD+WAV
Aheir писал(а):Из своего опыта - я как раз с двумя буферами делал. Когда вычитывал один из них до половины, начинал заполнять другой... Достаточно было буферов небольшого размера, все работало совершенно без сбоев.
см сообщение выше
- БАТАРЕЙКУС
- Потрогал лапой паяльник
- Сообщения: 346
- Зарегистрирован: Сб фев 07, 2009 19:21:25
Re: ATMEGA+SD+WAV
HeLiO писал(а):Aheir писал(а):Из своего опыта - я как раз с двумя буферами делал. Когда вычитывал один из них до половины, начинал заполнять другой... Достаточно было буферов небольшого размера, все работало совершенно без сбоев.
см сообщение выше
а я даже мультики воспроизводил на экране WG-12864. нарисованные программой KS0108_3_4 скачанной с http://ikarab.narod.ru/KS0108.html.
Мечтатель - не тот, кто сидит на диване и думает о несбыточном, а тот, кто всеми силами стремится воплотить несбыточное в реальность.
Re: ATMEGA+SD+WAV
Благодарю всех за ответы! Всё получилось!!! Сделал с двумя буферами по 128 байт. Если кому интересно - могу выложить проэктик.
Re: ATMEGA+SD+WAV
SABRAT писал(а):Благодарю всех за ответы! Всё получилось!!! Сделал с двумя буферами по 128 байт. Если кому интересно - могу выложить проэктик.
Конечно интересно-выкладывай!
Re: ATMEGA+SD+WAV
БАТАРЕЙКУС писал(а):HeLiO писал(а):Aheir писал(а):Из своего опыта - я как раз с двумя буферами делал. Когда вычитывал один из них до половины, начинал заполнять другой... Достаточно было буферов небольшого размера, все работало совершенно без сбоев.
см сообщение выше
а я даже мультики воспроизводил на экране WG-12864. нарисованные программой KS0108_3_4 скачанной с http://ikarab.narod.ru/KS0108.html.
ненавижу эту хрень, делаю только на работе эти проги для МК, а дома музыку пишу)) вот до чего ккризис доводит..)))
Re: ATMEGA+SD+WAV
SABRAT писал(а):Благодарю всех за ответы! Всё получилось!!! Сделал с двумя буферами по 128 байт. Если кому интересно - могу выложить проэктик.
а чего не с 1680- ю байтами?
Re: ATMEGA+SD+WAV
вот выкладываю...получилосьчто-то вроде библиотеки, писал под WinAVR
- Вложения
-
- Sound-lib.rar
- (1.25 КБ) 564 скачивания
- Aheir
- Модератор
- Сообщения: 4517
- Зарегистрирован: Пн апр 03, 2006 11:43:25
- Откуда: Санкт - Петербург
- Контактная информация:
Re: ATMEGA+SD+WAV
HeLiO писал(а):Aheir писал(а):Из своего опыта - я как раз с двумя буферами делал. Когда вычитывал один из них до половины, начинал заполнять другой... Достаточно было буферов небольшого размера, все работало совершенно без сбоев.
см сообщение выше
Да умница, умница, я ж пишу: "Из своего опыта" - делюсь, так сказать, не претендуя на истину.
Оптимизм х (Опыт + Знания) = const