Witam wszystkich :)
Chcia³bym zaprezentowaæ wykonany i uruchomiony przeze mnie ma³y odtwarzacz MP3. Wykona³em go w ramach poznawania mo¿liwo¶ci mikrokontrolera AT91SAM7S256 i nauki programowania w jêzyku C.
Dane techniczne odtwarzacza:
1. obs³uga kart SD (uruchamia³em na karcie SDHC 8GB) z systemem plików FAT32
2. odczytywanie d³ugich nazw plików i podkatalogów (obs³ugiwane nazwy do 80 znaków)
3. mo¿liwo¶æ wymiany dowolnych plików z komputerem poprzez USB (odtwarzacz widziany przez komputer jako Mass Storage Device). Testowane pod Win2000 i WinXP
4. zasilanie z 1 akumulatora NiMH AA/R6
5. czas pracy przy zasilaniu z akumulatora: ok.10...11h (z akumulatora 2450mAh)
6. mo¿liwo¶æ ³adowania akumulatora poprzez USB lub ³adowark± 5V / 500mA (np. odpowiednio dostosowan± do tego ³adowark± od telefonu Sony Ericsson T280i, któr± akurat mam)
7. mo¿liwo¶æ programowania i debugowania poprzez z³±cze "mini" JTAG. "Mini", bo wykorzystane s± tylko sygna³y SRST, TDO, TCK, TDI, TMS oraz GND i Vcc, co pozwoli³o mi zastosowaæ listwê goldpin 2x5 pin, widoczn± na zdjêciach wnêtrza odtwarzacza (normalnie jest to 2x10 pin)
8. interfejs u¿ytkownika: LCD 2x16 znaków, 5 pod¶wietlanych przycisków (4 steruj±ce i 1 w³±czaj±cy zasilanie) oraz 2 LED
9. odtwarzanie plików MP3 VBR i CBR, 128...320kb/s, fs=44,1kHz i 48kHz
10. wymiary odtwarzacza (D³ugo¶æ x Szeroko¶æ x Grubo¶æ) 84x60x22mm
11. masa 105g
Odtwarzacz do dekodowania MP3 u¿ywa biblioteki dekodera MP3 z Atmel'owego projektu "AT91SAM Internet Radio" (PDF i kod ¼ród³owy na www.atmel.com). Biblioteka dekodera MP3 jest z Helix Community, ale dopiero skompilowa³a mi siê biblioteka z Atmel'owego projektu. Obs³ugê USB zrobi³em na podstawie pliku CDC_ENUMERATE.C z archiwum at91sam7s_usart_usb_20080916.zip (przejrzy¶cie i zrozumiale wed³ug mnie napisana funkcja obs³ugi USB w tym pliku). Niestety, nie pamiêtam ju¿, z jakiej www ¶ci±gn±³em to archiwum, tote¿ do³±czam go do dokumentacji odtwarzacza.
Dzia³anie odtwarzacza jest nastêpuj±ce: po w³±czeniu zasilania, przez 2 sekundy na LCD wy¶wietlana jest nazwa partycji (Volume Label). W przypadku karty SD bêdzie to prawdopodobnie nazwa producenta karty (u mnie tak jest). Po up³ywie tych 2 sekund, gdy nie jest do³±czone USB, na LCD pojawia siê lista z zawarto¶ci± g³ównego katalogu karty. Jedyne sortowanie, jakie zaimplementowa³em w programie odtwarzacza, to wy¶wietlanie najpierw nazw podkatalogów, a poni¿ej nich nazw plików. Tote¿ podkatalogi s± wy¶wietlane na tej li¶cie w takiej kolejno¶ci, w jakiej s± zapisane na karcie. To samo dotyczy plików. Przewijanie listy odbywa siê za pomoc± przycisków przewijania/regulacji g³o¶no¶ci w górê i w dó³. Zawsze tylko pierwsza pozycja na tej li¶cie jest aktywna, t.zn. wybierana jest przyciskiem odtwarzaj/wejd¼ do/wyjd¼ z podkatalogu. Czyli gdy na tej pozycji jest nazwa podkatalogu, to wci¶niêcie tego przycisku spowoduje wej¶cie do tego podkatalogu. Po wej¶ciu do podkatalogu znów wy¶wietlana jest lista z zawarto¶ci± podkatalogu (inne podkatalogi oraz pliki) oraz dochodzi jeszcze wy¶wietlanie ". [RootDir]" oraz ".. [UpDir]". Wybranie ". [RootDir]" powoduje wyj¶cie do g³ównego katalogu karty (niezale¿nie od tego, jak g³êboko siê wesz³o w podkatalogi), wybranie ".. [UpDir]" powoduje wyj¶cie do nadrzêdnego podkatalogu w stosunku do tego, z którego siê wysz³o. Przed nazw± podkatalogu wy¶wietlany jest ma³y prostok±t (widoczny na zdjêciach), co pozwala odró¿niæ nazwê podkatalogu od nazwy pliku.
Gdy natomiast na tej pozycji wy¶wietlana jest nazwa pliku, to wci¶niêcie tego przycisku spowoduje jego odtwarzanie. Sposób poruszania siê po tej li¶cie przypomina trochê sposób poruszania siê w Windows Commander, co by³o takim moim zamiarem. Odtwarzane s± wszystkie pliki w wybranym podkatalogu, pocz±wszy od wybranego pliku do koñca listy. Podczas odtwarzania na LCD jest wy¶wietlana d³uga nazwa odtwarzanego pliku, bie¿±cy czas utworu, ca³kowity czas trwania utworu oraz bitrate. By zmie¶ciæ tak± ilo¶æ informacji na 16-znakowym LCD, zastosowa³em samoczynne przewijanie tekstu podczas odtwarzania. Wykorzysta³em mo¿liwo¶æ wy¶wietlacza - przesuwania ca³ego tekstu o 1 znak w lewo. Wystarczy cyklicznie, podczas odtwarzania, wysy³aæ komendê przesuwu o 1 znak wy¶wietlanego tekstu i otrzyma siê taki efekt. Efekt ten jest pokazany na filmie (przepraszam za nienajlepsz± jako¶æ filmu - aparat trzymany w rêku) Po zakoñczeniu odtwarzania odtwarzacz powraca do wy¶wietlania listy podkatalogów i plików w aktualnie wybranym podkatalogu.
Funkcje oszczêdzaj±ce energiê w akumulatorku:
1. wygaszanie pod¶wietlenia LCD i przycisków po 20sek. od momentu wykrycia braku aktywno¶ci przycisków. Oczywi¶cie, naci¶niêcie którego¶ z 4 przycisków steruj±cych powoduje max rozja¶nienie pod¶wietlenia.
2. Ca³kowite wy³±czanie odtwarzacza, gdy ten przez 2 minuty wy¶wietla tylko listê na LCD, a nie odtwarza
3. W przypadku braku karty SD w slocie przy w³±czeniu odtwarzacza, wy¶wietlenie przez kilka sekund stosownego komunikatu na LCD i odtworzenie d¼wiêku ostrzegawczego, po czym ca³kowite wy³±czenie odtwarzacza. To samo jest w przypadku wyjêcia karty podczas odtwarzania muzy
4. Zastosowanie najni¿szej mo¿liwej czêstotliwo¶ci taktowania mikrokontrolera: 45,1584MHz przy odtwarzaniu plików z fs=44,1kHz i 49,152MHz przy odtwarzaniu plików z fs=48kHz. Dla tych czêstotliwo¶ci uzyska³em p³ynne odtwarzanie bez trzasków, przy jednoczesnej programowej obs³udze innych rzeczy.
Doda³em równie¿ pomiar napiêcia akumulatora podczas samodzielnej pracy odtwarzacza (bez wspó³pracy z komputerem). Napiêcie akumulatorka mierzone jest przez przetwornik A/D wewn±trz mikrokontrolera. Gdy napiêcie akumulatorka spadnie do 1V, to odtwarzany jest d¼wiêk ostrzegawczy, wy¶wietlany odpowiedni komunikat na LCD, po czym odtwarzacz wy³±cza siê.
Schemat ideowy i projekt PCB zrobi³em w Protelu 99.
Program odtwarzacza napisa³em w jêzyku C w ¶rodowisku Rowley Crossworks ver.1.7 i zaprogramowa³em mikrokontroler przez JTAG przy u¿yciu Macraigor Wiggler. Podczas kompilowania trzeba ustawiæ opcje:
Optimize for Size, bez tego kod wykonuje siê zbyt wolno, co objawia siê trzeszczeniem podczas odtwarzania.
Heap size na rozmiar 23918 bajtów, by zaalokowaæ zmienne u¿ywane przez dekoder MP3
Stack size (IRQ mode) na wartosc 384 bajtów
Stack size (User/System mode) na warto¶æ 1024 bajty.
U¿ycie pamiêci przez program: 93kB pamiêci programu Flash i 36kB RAM. ¯adnych funkcji nie przenosi³em do RAM, wszystkie s± we Flash-u.
Koszty - niestety niema³e. PCB 60z³, czê¶ci elektroniczne i obudowa (z TME) 280z³. Drobnicê SMD (kondensatory, rezystory, tranzystory) mia³em w zapasach. Ale moim za³o¿eniem podczas prac nad odtwarzaczem by³o czego¶ siê nauczyæ, a nie konkurowaæ z producentami mp-trójek.
Elektronika odtwarzacza i akumulatorek umieszczone s± w obudowie KM-22 - st±d takie wymiary odtwarzacza, jak w danych technicznych.
Etapy prac nad odtwarzaczem (powa¿niejsze problemy i sposoby rozwi±zania ich) s± tu: http://www.elektroda.pl/rtvforum/topic1413095.html Kod ¼ród³owy napisany przeze mnie mo¿na dowolnie modyfikowaæ. Proszê tylko o powiadomienie mnie, co zosta³o zmienione i w którym miejscu w programie i jakie da³o to usprawnienie :) Oczywi¶cie nadal obowi±zuj± warunki licencyjne na kod dekodera MP3 i kod obs³ugi USB.
Pozdrawiam, KT_priv
Trzy rzeczy, które bym koniecznie zmieni³ na Twoim miejscu:
- po rozpoczêciu odtwarzania odtwarzaæ powinien kolejne utwory;
- oddzielny przycisk do wchodzenia i wychodzenia z menu;
- dodatkowe tryby odtwarzania zmieniane przyciskiem: pojedynczy utwór w kó³ko, wszystkie utwory w katalogu w kó³ko, wszystkie utwory na karcie w kó³ko; losowy utwór w katalogu, losowy utwór na karcie. Domy¶lnie odtwarzanie katalogu w kó³ko.
Dwie, które bym zmieni³, ale nieobowi±zkowo:
- ³adniejsza obudowa(barwiona pleksa by³aby ¶wietna);
- graficzny wy¶wietlacz, np. od komórki, sterowany bezpo¶rednio przez mikrokontroler lub z w³asnym sterownikiem(kilka miesiêcy temu kto¶ prezentowa³ taki uk³ad).
Moim zdaniem bardzo fajny i warto¶ciowy projekt...
Witam
Nie wiem, nie my¶la³em nad tym. Ale nie wydaje mi siê, ¿eby to by³o trudne, by³aby to tylko kwestia zast±pienia bibliotek dekodera MP3 bibliotekami dekoduj±cymi wspomniane formaty. Lub ewentualnie dodania tych bibliotek do obecnego programu z dekoderem MP3, by rozszerzyæ mo¿liwo¶ci odtwarzacza. Pytanie tylko, czy starczy na to RAM-u? Ale... wpisa³em w Googlach "dekoder aac na arm". Pierwszy link: PRD10-GENC-001288-4-0.PDF z którego wynika, ¿e chyba by starczy³o RAM-u. Trza poszukaæ w Sieci odpowiednich bibliotek dekoderów. Pozdrawiam, KT_priv
Dodano po 11 [minuty]:
Do Urgon: Dziêki :) Odpowiadam:
1. To jest. Napisa³em( mo¿e niejasno, to wyja¶niam) odtwarzacz gra kolejno wszystkie pliki od wybranego przyciskiem Play, do koñca listy. Je¶li bêdzie wybrany pierwszy plik na li¶cie, to od pierwszego, drugi, trzeci,... Je¶li bêdzie wybrany trzeci plik, to od trzeciego, czwarty,... a¿ do koñca.
2. i 3. Do pomy¶lenia, mo¿e w jakiej¶ wolnej chwili...
4. Mo¿na by, ale ja bym musia³ raczej komu¶ zleciæ zrobienie takiej obudowy
5. Zabieram siê ju¿ za obs³ugê graficznego LCD tym razem na STM32 Butterfly. Mo¿e za jaki¶ czas powstanie kolejna,bogatsza wersja odtwarzacza...
Mam pytanie: Jak zdj±æ op³atê za plik? Czy jeszcze raz to samo wys³aæ, ale z wy³±czeniem punktów? Szybko wys³a³em i nie zauwa¿y³em odpowiedniej opcji.
Pozdrawiam, KT_priv
Dodano po 1 [godziny] 5 [minuty]:
OK, widzê, ¿e punkty za dokumentacjê s± ju¿ usuniête.
Hej
Bardzo dobry pomys³, bardzo dobrze, ¿e takie rzeczy s± robione. Ten rozmiar obudowy jest bardzo porêczny dla u¿ytkownika, ale i stawia pewne wyzwania przy upchniêciu wszystkiego :)
Czy mo¿na prosiæ o schemat w pliku graficznym dla nie-posiadaj±cych Protela? Albo o listê czê¶ci chocia¿.
Jako¶æ d¼wiêku s³uchowo jest dobra. Jako¶æ CD mo¿e by³aby tu nadu¿yciem(chocia¿, kto wie, nie mam audiofilskiego s³uchu), ale gra to jak dobre radio FM. Porównywalnie z mp-trójk± na STA013 i CS4334, któr± robi³em ok. 1,5 roku temu. Zastosowa³em w tym odtwarzaczu DAC UDA1330ATS (do kupienia tu: www.seguro.pl/sklep/?zobacz=4301&producent= ). Do regulacji g³o¶no¶ci wykorzysta³em mo¿liwo¶æ regulacji poziomu sygna³u wyj¶ciowego w UDA1330 za po¶rednictwem jego interfejsu L3 (z PDF-a tego scalaka). Taki interfejs zrealizowa³em programowo w ARM wykorzystuj±c przerwania od timera TC1.
Poni¿ej kod realizuj±cy programowy interfejs L3:
Code:
void compare_tc1_irq_handler(void) __attribute__ ((interrupt ("TC1")));
void compare_tc1_irq_handler(void) //procedura obs³uguj±ca przerwanie pochodz±ce od zrównania siê zawarto¶ci TC1 z zawarto¶ci± rejestru TC1_RC
{
unsigned int dummy;
dummy=*AT91C_AIC_IVR; //odczyt z rejestru IVR,sygnalizuje, ¿e rozpoczê³± sie obs³uga przerwania
dummy=*AT91C_TC1_SR; //odczyt z rejestru statusu TC1.Musi byæ ten odczyt,by mo¿liwe by³o przyjmowanie dalszych przerwañ od TC1
//gdy tego odczytu nie by³o, to przerwanie przyjmowane i wykonywane by³o tylko raz
if ( (AT91F_PIO_GetInput(AT91C_BASE_PIOA) & L3CLK ) == L3CLK ) //realizacja sygna³u zegarowego na wej¶ciu L3CLK przetwornika UDA1330
{ //teraz przygotowanie nowego bitu z dana_do_wyslania do wystawienia na liniê L3DATA
AT91F_PIO_ClearOutput( AT91C_BASE_PIOA, L3CLK ); //je¶li by³o L3CLK = H ,to teraz wyzerowanie L3CLK = L (wytworzenie zbocza opadaj±cego na L3CLK)
if ((dana_do_wyslania & 0x01) == 0) //gdy najm³odszy bit danej do wys³ania == 0,to
AT91F_PIO_ClearOutput( AT91C_BASE_PIOA, L3DATA); //wyzerowanie linii L3DATA
else //w przeciwnym razie, gdy najm³odszy bit danej do wys³ania <> 0, to
AT91F_PIO_SetOutput( AT91C_BASE_PIOA, L3DATA); //ustawienie linii L3DATA.W efekcie stan linii L3DATA bêdzie pod±¿aæ za stanem najm³odszego bitu danej do wys³ania
dana_do_wyslania = dana_do_wyslania >> 1;
}
else
{
AT91F_PIO_SetOutput( AT91C_BASE_PIOA, L3CLK ); //w przeciwnym razie, gdy by³o L3CLK = L, to teraz ustawienie L3CLK = H (wytworzenie zbocza narastaj±cego na L3CLK (zapisuj±cego bit do DAC))
licznik_wyslanych_bitow++; //zwiêkszenie o 1 licznika wys³anych bitów, bo poprzez wytworzenie zbocza narastaj±cego na L3CLK dany bit ju¿ zosta³ wys³any do DAC
}
if ((licznik_wyslanych_bitow == 8) & (AT91F_PIO_GetInput(AT91C_BASE_PIOA) & L3CLK ) == L3CLK) dana_juz_wyslana = true;
//gdy ju¿ liczba wys³anych bitów = 8 i poziom na L3CLK = H (ju¿ ostatnie zbocze narastaj±ce zosta³o wytworzone), to ustawienie dana_juz_wyslana = true
*AT91C_AIC_EOICR = 0; //zapis do rejestru EOICR sygnalizuje, ¿e zakoñczy³a siê obs³uga bie¿±cego przerwania
}
Sama regulacja g³o¶no¶ci przyciskami Scroll/Volume Up / Down odbywa siê poprzez zmianê warto¶ci elementu nastawa_glosnosci:
Code:
unsigned char konfig_glosnosci[] = {0x14, //0b00010100 - adres UDA1330 i wybrany rejestr danych (2 najm³odsze bity = 00)
nastawa_glosnosci / 2}; //dana wpisywana do rejestru danych (warto¶æ nastawy g³o¶no¶ci jest dzielona przez 2, by uzyskaæ
//zmniejszenie szybko¶ci regulacji g³o¶no¶ci w górê i w dó³ )
i pó¼niej wysy³anie tablicy konfig_glosnosci do DAC podczas naciskania w.w. przycisków. Odbywa siê to w funkcji volume_control w pliku main.c
Nie, gdy¿ Atmega8 nie jest w stanie ud¼wign±æ tak du¿ego i skomplikowanego programu. Ponadto jest mikrokontrolerem o¶miobitowym, a ARMy s± trzydziestodwubitowe...
Witam
My¶lê, ¿e dowolny ARM7 ze przynajmniej 128kB Flash i 48kB RAM (by zmie¶ci³ siê program) móg³by byæ (np. LPC2148). PDF, w którym przedstawiono budowê odtwarzacza MP3 na tym mikrokontrolerze (ale z wykorzystaniem biblioteki libmad), jest tu: http://www.nxp.com/documents/application_note/AN10583.pdf U mnie ARM chodzi z ustawionym 0WS dla takich czêstotliwo¶ci, o jakich mówi³em na pocz±tku tego w±tku. Producent podaje, ¿e 0WS mo¿e byæ dla fclk_max 30MHz, ale u mnie ten mikrokontroler chodzi stabilnie na takich czêstotliwo¶ciach. Gdyby jednak wybrany mikrokontroler wymaga³by ustawienia 1WS dla takiej fclk, to wtedy niestety, trzeba kombinowaæ z programem, by uzyskaæ na tyle szybkie wykonywanie kodu, by odtwarzany d¼wiêk by³ bez trzasków i przerw.
Niestety, Atmega8 nie nadaje siê do budowy takiego odtwarzacza. Za ma³o Flash-a, RAM-u i 8-bit architektura. Owszem, do sterowania jakim¶ sprzêtowym dekoderem MP3 (np. STA013, VS1001) to jak najbardziej. Temat taki do poszukania na Sieci, na elektrodzie równie¿. Sam robi³em taki odtwarzacz MP3, gdzie STA013 by³ sterowany ale przez Atmega162, gdy¿ musia³em podpi±æ jeszcze zewnêtrzn± RAM 62256.
Pozdrawiam, KT_priv
Witam :) Bardzo fajna MP3ka. Od jakiego¶ czasu projektuje co¶ podobnego te¿ na UDA1330 i ARM7 ATMELa, tylko bardziej z nastawieniem na narzêdzie pracy (wyprowadzone interfejsy itp) mp3 to tylko dodatek. Chcia³em siê spytaæ jak jest z poziomem sygna³u z UDA1330? Czy bezpo¶rednio mo¿na podpi±æ s³uchawki czy trzeba stosowaæ wzmacniacz? Widzia³em na Twoim schemacie bezpo¶rednio wyj¶cie z DACa, czy to siê sprawdza?
Pozdrawiam :) Mam nadzieje, ¿e ju¿ nied³ugo podzielê siê swoj± konstrukcj± :)
Witam
G³o¶no¶æ jest du¿a przy ustawieniu max g³o¶no¶ci. Powiedzmy, jak g³os ze s³uchawki tel. kom. W tej chwili s³uchawki odtwarzacza le¿± na stole, g³o¶no¶æ mam ustawion± na ok. 2/3 max i jeszcze je s³yszê. Oczywi¶cie w ha³a¶liwym otoczeniu mo¿e to jeszcze nie wystarczyæ.
W pierwszej wersji mia³em jeszcze wzmacniacz TDA7050 ze swoich zapasów, ale mia³em du¿e zak³ócenia. Tote¿ stwierdzi³em, ¿e go nie bêdzie, tym bardziej, ¿e g³o¶no¶æ przy bezpo¶rednim podpiêciu s³uchawek do DAC-a mi te¿ odpowiada³a.
Równie¿ pozdrawiam :)
KT_priv
PS:Wprowadzam jeszcze poprawki w kodzie (w main.c) - wyeliminowanie krótkiego zaniku d¼wiêku przy wychodzeniu z menu regulacji g³o¶no¶ci. Jak ju¿ bêdê pewny poprawnego dzia³ania kodu, to wystawiê go na elektrodê.
Prototyp, czyli co? Uk³ad uruchomieniowy tego mikrokontrolera i dzia³aj±cy odtwarzacz na tym uk³adzie? Je¶li o to chodzi, to mam, jeszcze nie zdemontowa³em pajêczyny kabelków w tym uk³adzie.
Witam
Dziêki za link. Spróbujê odpaliæ ten kod w prototypie w wolnej chwili. Na razie jestem na etapie zgrywania plików z tego linku.
Pozdrawiam,KT_priv
Witam
Zmianê fs uzyskujê poprzez zmianê czêstotliwo¶ci zegara mikrokontrolera przed rozpoczêciem odtwarzania pliku z fs=48kHz. Poni¿ej fragment kodu, który mi to realizuje:
Code:
if (! (configured)) // gdy by³a to dopiero pierwsza poprawnie zdekodowana ramka (wtedy configured == false), to najpierw przed wys³aniem danych do portu SSC
{ //skonfigurowanie interfejsu SSC
sampling_freq = mp3FrameInfo.samprate; //odczytanie czêstotliwo¶ci próbkowania
AT91F_PMC_EnablePeriphClock( AT91C_BASE_PMC, 1 << AT91C_ID_SSC ) ; //enable the clock of the SSC
if (sampling_freq == 48000)
{ //gdy czêstotliwo¶æ próbkowania odczytana z odtwarzanego pliku = 48kHz, to poni¿ej takie ustawienie clk mikrokontrolera, by przy jeszcze 0WS fsample by³o jak najbli¿sze 48kHz (co wp³ywa na szybko¶æ odtwarzania)
AT91F_CKGR_CfgPLLReg(AT91C_BASE_CKGR, AT91C_CKGR_USBDIV_0 | (256 << 16) | AT91C_CKGR_OUT_0 | AT91C_CKGR_PLLCOUNT | 100); //mikrokontroler taktowany jest zegarem (18,432 * (256+1) / 100) ~= 47,4MHz (dla takiej fclk mikrokontroler pracuje jeszcze stabilnie (bez zwisów))
// AT91F_CKGR_CfgPLLReg(AT91C_BASE_CKGR, AT91C_CKGR_USBDIV_0 | (7 << 16) | AT91C_CKGR_OUT_0 | AT91C_CKGR_PLLCOUNT | 3); //mikrokontroler taktowany jest zegarem (18,432 * (7+1) / 3) = 49,152MHz (taka powinna byæ fclk mikrokontrolera)
while (! (AT91F_PMC_IsStatusSet(AT91C_BASE_PMC, AT91C_PMC_LOCK))){}; //w pêtli while odczekanie na ustabilizowanie siê pêtli PLL
AT91F_SSC_Configure( AT91C_BASE_SSC, (47370240), (2*16*46260), 0, 0, AT91C_I2S_ASY_MASTER_TX_SETTING(16, 2), AT91C_I2S_ASY_TX_FRAME_SETTING(16, 2)); //(dla zegara 47,4MHz; fsample = fclk/1024 ~= 46,3kHz i jeszcze 0WS)
// AT91F_SSC_Configure( AT91C_BASE_SSC, (49152000), (2*16*48000), 0, 0, AT91C_I2S_ASY_MASTER_TX_SETTING(16, 2), AT91C_I2S_ASY_TX_FRAME_SETTING(16, 2)); //(dla zegara 49,152MHz; fsample = fclk/1024 = 48kHz i jeszcze 0WS)
config_TC0(24000); //i ustawienie TC0,by generowa³ przerwanie co 500ms dla takiej czêstotliwo¶ci zegara
}
else { //w przeciwnym razie ustawienie zegara mikrokontrolera dla domy¶lnej warto¶ci fsample=44,1kHz
AT91F_CKGR_CfgPLLReg(AT91C_BASE_CKGR, AT91C_CKGR_USBDIV_0 | (48 << 16) | AT91C_CKGR_OUT_0 | AT91C_CKGR_PLLCOUNT | 20); //mikrokontroler taktowany jest zegarem (18,432 * (48+1) / 20) = 45,1584MHz; fsample=44,1kHz
while (! (AT91F_PMC_IsStatusSet(AT91C_BASE_PMC, AT91C_PMC_LOCK))){}; //w pêtli while odczekanie na ustabilizowanie siê pêtli PLL
AT91F_SSC_Configure( AT91C_BASE_SSC, (45158400), (2*16*sampling_freq), 0, 0, AT91C_I2S_ASY_MASTER_TX_SETTING(16, 2), AT91C_I2S_ASY_TX_FRAME_SETTING(16, 2)); //(dla zegara 45,1584MHz; fsample=44,1kHz)
config_TC0(22050); //i ustawienie TC0,by generowa³ przerwanie co 500ms dla takiej czêstotliwo¶ci zegara
}
AT91F_SSC_EnableTx( AT91C_BASE_SSC); //Enable the TX transmitter
out_Samples = mp3FrameInfo.outputSamps;
AT91F_SSC_SendFrame( AT91C_BASE_SSC, (char*)outBuf, out_Samples, (char*)outBuf, out_Samples); //wys³anie do portu SSC poprzez kana³ DMA danych dla przetwornika D/A i wype³nienie drugiego bufora DMA
configured = true; //interfejs SSC jest ju¿ skonfigurowany
}
Niestety, w czasie wielu prób okaza³o siê, ¿e dla fclk = 49,152MHz i 0WS mikrokontroler ju¿ nie pracowa³ stabilnie - czasem udawa³o siê odtworzyæ pliki z fs=48kHz, czasem nie (zawieszenie mikrokontrolera - tylko reset pomaga³). Ustawienie 1WS powodowa³o trzaski podczas odtwarzania. Musia³em zmniejszyæ fclk do ok. 47,4MHz, co da³o mi fs=46240Hz. Ale, wed³ug mnie, szybko¶æ odtwarzania nie uleg³a s³yszalnemu spowolnieniu :)
Aby uzyskaæ dok³adnie fs=48kHz i odtwarzanie bez trzasków, to fclk mikrokontrolera musi byæ 73,728MHz i nastawione 1WS. Wtedy DAC trzeba tak skonfigurowaæ, by pracowa³ przy zegarze systemowym = 384*48000 = 18,432MHz na pinie SYSCLK (6). A tak± czêstotliwo¶æ na tym pinie bêdzie, po podziale fclk mikrokontrolera przez 4, st±d taka (du¿a) fclk mikrokontrolera. Obecnie mam fs = 44,1kHz; fs ~= 48kHz (dok³adnie 46,24kHz); czêstotliwo¶æ zegara systemowego DAC-a równ± 256 * fs i tak DAC jest skonfigurowany.
Pozdrawiam, KT_priv
Witam wszystkich:)
OK, wprowadzi³em kilka usprawnieñ w programie odtwarzacza MP3. Oto one:
1. Zmniejszenie trzasku /stuku w s³uchawkach po zakoñczeniu regulacji g³o¶no¶ci. Trzask ten by³ powodowany od¶wie¿eniem zawarto¶ci LCD (czyli ponownym wy¶wietleniem nazwy odtwarzanego pliku i jego podstawowych parametrów) po wyj¶ciu z regulacji g³o¶no¶ci. Podczas tego od¶wie¿ania przerywane by³o wysy³anie danych do DAC, st±d by³ ten trzask. Uzyska³em to poprzez jednokrotne przypisanie
Code:
*AT91C_RTTC_RTMR = 1; //ustawienie preskalera RTT w celu przyspieszenia przyjmowania przerwañ od RTT, by uzyskaæ szybsze wystawianie znaków na LCD
przed rozpoczêciem pêtli dekoduj±cej ramki MP3 w funkcji play_mp3_file w pliku main.c. Jednokrotne, bo nigdzie wiêcej w pêtli dekoduj±cej MP3 nie wystêpuje to przypisanie (w pierwszej wersji programu odtwarzacza wystêpowa³o). Dodatkowo przez to pozby³em siê krótkich, nieregularnie wystêpuj±cych przerw podczas odtwarzania (wygl±da³o to tak, jakby zawieszanie siê mikrokontrolera). Domy¶lam siê, ¿e powodem tych przerw by³a zmiana zawarto¶ci rejestru RTT_MR podczas obs³ugi przerwania od RTT. Przywrócenie oryginalnej nastawy RTT_MR odbywa siê poprzez wywo³anie funkcji:
Code:
config_RTT(); //ponowne ustawienie preskalera RTT,by przerwanie od niego nastêpowa³o co 1ms przy fclk=32768Hz
po wyj¶ciu z pêtli dekoduj±cej ramki MP3. Samo cia³o funkcji config_RTT() jest w pliku IO_init.c.
Równie¿ skróci³em czas wy¶wietlenia na LCD podstawowych parametrów odtwarzanego pliku poprzez wy¶wietlanie tylko nazw tych parametrów (pominiête s± liczby):
w funkcji volume_control w pliku main.c. Liczbowe warto¶ci tych parametrów s± aktualizowane i wy¶wietlane podczas ka¿dorazowego obiegu pêtli dekoduj±cej MP3.
2. Usuniêcie trzasku pojawiaj±cego siê podczas zakoñczenia odtwarzania jednego pliku i rozpoczêcia odtwarzania nastêpnego. To zjawisko wystêpowa³o przy niektórych odtwarzanych plikach. Pomog³o na to zmniejszanie warto¶ci zmiennej bytesLeft, gdy funkcji MP3FindSyncWord nie uda³o siê znale¼æ bajtów rozpoczynaj±cych nag³ówek ramki MP3 w tablica_bajtow_danych przechowuj±cej sektor odczytany z karty SD. Ilustruje to poni¿szy kod:
Code:
offset = MP3FindSyncWord(readPtr, (sizeof(tablica_bajtow_danych) - (readPtr - tablica_bajtow_danych))); //poszukiwanie nag³ówka ramki MP3 w tablica_bajtow_danych pocz±wszy od elementu wskazywanego wska¼nikiem readPtr; przeszukiwane bêdzie jeszcze (sizeof(tablica_bajtow_danych) - (readPtr - tablica_bajtow_danych)) pozosta³ych bajtów w tablica_bajtow_danych
if (offset < 0) //gdy zosta³a przepatrzona ca³a tablica_bajtow_danych i nie uda³o siê w niej znale¶æ nag³ówka ramki MP3, to poni¿ej
{
bytesLeft = bytesLeft - (sizeof(tablica_bajtow_danych) - (readPtr - tablica_bajtow_danych)); //zmniejszenie bytesLeft (liczby pozosta³ych bajtów pliku) o liczbê przepatrzonych ju¿ bajtów, w¶ród których nie by³o nag³ówka ramki MP3
result = read_sector_from_current_or_next_cluster(numer_wpisu_poczatku_listy); //wype³nienie tablicy_bajtow_danych now± porcj± danych z karty SD
if ((result == 0xFF) | (result == 0)) //gdy koniec pliku (wtedy result == 0xFF) lub b³±d odczytu karty (wtedy result == 0), to:
outOfData = 1; //ustawienie zmiennej outOfData, by mo¿na by³o wyj¶æ z pêtli dekoduj±cej MP3
else readPtr = tablica_bajtow_danych; //w przeciwnym razie nowe dane z karty SD s± poprawnie odczytane i teraz wska¼nik readPtr wskazuje pierwszy element tablicy_bajtow_danych z nowymi danymi
continue; //i skok do instrukcji while (!outOfData) - koñca pêtli dekoduj±cej ramki MP3
}
Zmiana ta jest w funkcji play_mp3_file w pliku main.c
3. Zaimplementowanie komend: CMD25 (Write multiple block) i CMD18 (Read multiple block) w celu przyspieszenia transferu plików pomiêdzy kompem i odtwarzaczem, gdy ten jest do³aczony do kompa przez USB. Powiem szczerze, liczy³em na znaczne zwiêkszenie prêdko¶ci transferu po tym zabiegu, niestety przeliczy³em siê :( Zyska³em ok. 4kB/s i obecnie mam ok. 200kB/s przy przesyle plików z odtwarzacza do kompa i ok. 75kB/s w odwrotnym kierunku.
Funkcje obs³uguj±ce komendy CMD25 i CMD18, to odpowiednio write_multiple_sectors i read_multiple_sectors, obydwie umieszczone w pliku SD_card_functions.c. Funkcje te wywo³ywane s± z poziomu funkcji odpowiednio: Write_To_Card i Read_From_Card, umieszczonych w pliku SCSI_commands.c i obs³uguj±cych transmisjê danych przez USB. Je¶li kto¶ mia³by jeszcze jaki¶ pomys³ odno¶nie przyspieszenia transferu plików (szczególnie podczas kopiowania plików z kompa do odtwarzacza), to chêtnie, chêtnie... :)
4. Uruchomienie mo¿liwo¶ci przewijania w poziomie d³ugich nazw plików i podkatalogów nie mieszcz±cych siê w ca³o¶ci na 16-znakowym LCD podczas wy¶wietlania listy plików i podkatalogów na nim. Zaprezentowane jest to na filmie. Ta mo¿liwo¶æ jest zrealizowana w funkcji scroll_horizontal_long_names umieszczonej w pliku main.c. Funkcja ta jest wywo³ywana cyklicznie podczas obiegu g³ównej pêtli programu umieszczonej w funkcji main() [wtedy tylko wy¶wietlana jest lista plików i podkatalogów], natomiast przesuw d³ugiej nazwy pliku lub podkatalogu o 1 znak w lewo w tej funkcji odbywa siê wtedy, gdy ustawiona jest zmienna tick (czyli co ka¿de 500ms)
5. Usuniêcie wy¶wietlania przez 2sek nazwy producenta karty SD podczas wyj¶cia z podkatalogu do katalogu g³ównego karty. Nazwa ta wy¶wietlana jest tylko przy w³±czeniu zasilania odtwarzacza lub po od³±czeniu USB od odtwarzacza, potem ju¿ nie. Uzyska³em to sprawdzaj±c stan zmiennej power_on_or_USB_connected przed wy¶wietleniem tej nazwy na LCD w funkcji read_dir w pliku File_system_functions.c.
6. Rozszerzenie mo¿liwo¶ci wykrywania obecno¶ci karty SD w slocie i odpowiednia reakcja odtwarzacza w przypadku braku tej¿e równie¿ podczas wchodzenia do podkatalogu i przy wychodzeniu z niego do katalogu nadrzêdnego (pierwsza wersja programu mia³a t± mo¿liwo¶æ tylko podczas odtwarzania pliku i po w³±czeniu zasilania odtwarzacza. Próba wej¶cia do podkatalogu / wyj¶cia z podkatalogu przy wyjêtej karcie SD koñczy³a siê zawieszeniem odtwarzacza). Uzyska³em to poprzez sprawdzanie rezultatu odczytu zawarto¶ci karty przez funkcjê read_dir po wykryciu naci¶niêcia przycisku Play/Enter/Exit SubDir w funkcji main w pliku main.c. Poni¿ej kod, który realizuje mi t± mo¿liwo¶æ:
Code:
if (! (tablica_wpisow.is_directory)) //gdy wybrany wpis jest wpisem pliku,to
{
... //tu realizacja pêtli odtwarzaj±cej wszystkie pliki w aktywnym podkatalogu pocz±wszy od wybranego pliku a¿ do koñca listy plików w aktywnym podkatalogu
}
else //w przeciwnym razie wybrany z listy wpis jest wpisem podkatalogu i wtedy:
{
unsigned int first_cluster_number = tablica_wpisow.first_cluster_nbr; //numer pierwszego klastra zajêtego przez wybrany z listy podkatalog
liczba_wpisow = read_dir(rodzaj_karty, first_cluster_number, start_address); //odczytanie zawarto¶ci wybranego z listy podkatalogu, w zmiennej liczba_wpisow jest ilo¶æ plików i podkatalogów w wybranym podkatalogu
if (liczba_wpisow == 0xFF) //gdy liczba_wpisow == 0xFF, to jest to b³±d odczytu zawarto¶ci karty w powy¿szej funkcji read_dir, i wtedy:
{
AT91F_SPI_Disable( AT91C_BASE_SPI); //wy³±czenie SPI
init_LCD(); //inicjacja wy¶wietlacza LCD
wyswietl_tekst_na_LCD(0x80,"No SDcard!Insert"); //tekst wy¶wietlany w pierwszym wierszu LCD
wyswietl_tekst_na_LCD(0xC0,"SD with FAT32"); //tekst wy¶wietlany w drugim wierszu LCD
play_mp3_sound(sound1, size1); //odtworzenie MP3 z d¼wiêkiem ostrzegawczym o ¼le w³o¿onej karcie SD
play_mp3_sound(sound1, size1); //odtworzenie MP3 z d¼wiêkiem ostrzegawczym o ¼le w³o¿onej karcie SD
wait(2000);
shutdown_player(240); //wy³±czenie odtwarzacza
}
numer_wpisu_poczatku_listy = list_files_directories(1/*=numer_wpisu_poczatku_listy*/, liczba_wpisow, 2, 16); //teraz wylistowanie na LCD 2*16 nazw plików i podkatalogów pocz±wszy od pierwszej pozycji zapamiêtanej w zmiennej tablica_wpisow
// w zmiennej numer_wpisu_poczatku_listy jest numer wpisu pliku lub podkatalogu, którego nazwa jest wyswietlana w pierwszym wierszu LCD
opoznienie_przewijania = 0; //wyzerowanie tej zmiennej,by rozpocz±æ przewijanie w poziomie d³ugich nazw plików i podkatalogów podczas wy¶wietlania listy dopiero po osi±gniêciu przez t± zmienn± warto¶ci czasu opó¼nienia
}//koniec warunku if (! (tablica_wpisow.is_directory)) spr.czy wybrany wpis jest wpisem pliku
Dodatkowo, aby powy¿szy kod dzia³a³ poprawnie, musia³em do³o¿yæ jeszcze jeden rezystor 15k miêdzy ga³±¼ zasilania +3,3V i liniê portu PA12 (linia MISO mikrokontrolera). W moim odtwarzaczu rezystor ten jest w obudowie 0603 (SMD) i na PCB odtwarzacza wlutowa³em go pomiêdzy plusowy pad kondensatora C26 i pin 2 scalaka U5 (przy odrobinie zrêczno¶ci da siê go ³adnie wlutowaæ :) )
Do³±czam uaktualniony kod ¼ród³owy programu, poprawiony schemat ideowy odtwarzacza oraz aktualne wsady do mikrokontrolera - dosz³y do mnie g³osy, ¿e czê¶æ osób ma problemy z wygenerowaniem odpowiednich plików wynikowych z wykorzystaniem mojego kodu ¼ród³owego.
Pozdrawiam, KT_priv
Czy chodzi Ci o wynajdywanie tylko plików MP3 na karcie i wy¶wietlanie ich na LCD? Je¶li tak, to ¿adna, bo nie implementowa³em w kodzie funkcji filtracji plików MP3 spo¶ród wszystkich plików na SD. Jest to na razie TYLKO odtwarzacz mp-trójek, tote¿ za³o¿y³em ¿e na SD powinny byæ tylko pliki MP3. Niemniej jednak, ta mp-trójka funkcjonuje jako Mass Storage Device i mo¿liwe jest wgrywanie na kartê SD z kompa dowolnych plików, które po wgraniu pojawi± siê na LCD podczas wy¶wietlania na nim listy plików i podkatalogów. Co wiêcej, mo¿na takie pliki wybraæ do "odtwarzania" - skutkuje to niekiedy fajnymi efektami d¼wiêkowymi :D i tyle.
Je¶li nie udzieli³em satysfakcjonuj±cej Ciebie odpowiedzi, to proszê o doprecyzowanie pytania.
Pozdrawiam, KT_priv
Po wybraniu pliku do odtwarzania (realizuje to poni¿szy fragment kodu umieszczony w g³ównej pêtli programu w pliku main.c):
Code:
if ((! (AT91F_PIO_GetInput(AT91C_BASE_PIOA) & SW2)) & (~ play_key_pressed)) //gdy dopiero teraz naci¶niêty przycisk SW2 - Play (czyli jeszcze w tym miejscu play_key_pressed == false),to:
{
current_time = 0; //wyzerowanie bie¿±cego czasu, by prawid³owo ¶ciemniæ pod¶wietlanie LCD podczas odtwarzania pliku
dimming_LCD_backlight(current_time, aktualizacja_LCD_odtwarzanie); //¶ciemnienie pod¶wietlania LCD po 10sek od momentu wej¶cia do podkatalogu
play_key_pressed = true; //to zmiana stanu play_key_pressed.Potrzebne,by unikn±æ sytuacji naprzemiennego wchodzenia do podkatalogu i wychodzenia z niego w przypadku przytrzymania przycisku Play
wait(300);//i odczekanie na zanik drgañ zestyków przycisku Play
start_address = play_file_enter_subdir(numer_wpisu_poczatku_listy); //wywo³anie funkcji play_file_enter_subdir,która zwraca adres pierwszego sektora karty SD zajêtego przez wybrany plik lub podkatalog
if (! (tablica_wpisow.is_directory)) //gdy wybrany wpis jest wpisem pliku,to
{
unsigned char licznik_odtworzonych_plikow = numer_wpisu_poczatku_listy; //zainicjowanie licznika odtworzonych plików
do //poni¿sza pêtla odtwarza wszystkie pliki pocz±wszy od wybranego warto¶ci± numer_wpisu_poczatku_listy pliku do koñca listy plików
{
play_mp3_file(licznik_odtworzonych_plikow); //wywo³anie funkcji odtwarzaj±cej wybrany plik.Funkcja ta korzysta równie¿ ze zmiennej start_address
odtwarzacz przechodzi do realizacji funkcji play_mp3_file. W tej funkcji tu¿ po deklaracji u¿ytych w niej zmiennych jest:
Code:
result = read_sector(start_address + licznik, rodzaj_karty); //odczytanie nowych danych z pierwszego sektora karty SD nale¿±cego do klastra zajêtego przez odtwarzany plik
readPtr = tablica_bajtow_danych; //teraz wska¼nik readPtr wskazuje pierwszy element tablicy_bajtow_danych
W efekcie zmienna tablica_bajtow_danych jest wype³niona pierwsz± 512-bajtow± paczk± danych odczytanych z wybranego pliku, a wska¼nik readPtr wskazuje pierwszy element tej tablicy. Nastêpnie w pêtli do...while dekoduj±cej kolejne ramki MP3, po uprzednim wykonaniu funkcji od¶wie¿aj±cych zawarto¶æ LCD i reguluj±cych g³o¶no¶æ, jest sprawdzenie warunku:
Code:
if (((readPtr - tablica_bajtow_danych) + 4) > sizeof(tablica_bajtow_danych)) //spr.czy nag³ówki kolejnych ramek mieszcz± siê w ca³o¶ci w tablicy_bajtow_danych. Gdy nie mieszcz±
{ //siê,to oznacza, ¿e fragment kolejnego nag³ówka ramki MP3 jest na koñcu tablicy_bajtow_danych i wtedy poni¿ej
...
}
else //w przeciwnym razie nag³ówki kolejnych ramek MP3 mieszcz± siê w ca³o¶ci w tablicy_bajtow_danych i wtedy
{
offset = MP3FindSyncWord(readPtr, (sizeof(tablica_bajtow_danych) - (readPtr - tablica_bajtow_danych))); //poszukiwanie nag³ówka ramki MP3 w tablica_bajtow_danych pocz±wszy od elementu wskazywanego wska¼nikiem readPtr; przeszukiwane bêdzie jeszcze (sizeof(tablica_bajtow_danych) - (readPtr - tablica_bajtow_danych)) pozosta³ych bajtów w tablica_bajtow_danych
Jako, ¿e zaczynamy odtwarzanie od pocz±tku, to najpierw wykonaj± siê instrukcje po s³owie kluczowym else. A tu jest funkcja MP3FindSyncWord, której zadaniem jest znalezienie nastêpuj±cego ci±gu binarnego 11111111 111 (czyli hex: 0xFF 0xEy, gdzie y to kolejne 5 bitów w tym drugim bajcie) w tej zmiennej tablica_bajtow_danych. Samo cia³o tej funkcji (trochê zmodyfikowane przeze mnie - szczegó³y w komentarzach) jest w pliku mp3dec.c . Taki ci±g binarny jest s³owem synchronizuj±cym pocz±tek ka¿dego nag³ówka ka¿dej kolejnej ramki MP3. Funkcja ta zwraca warto¶æ offset. Offset ten jest sprawdzany nastêpnie przez dalsz± czê¶æ tego warunku:
Code:
if (offset < 0) //gdy zosta³a przepatrzona ca³a tablica_bajtow_danych i nie uda³o siê w niej znale¶æ nag³ówka ramki MP3, to poni¿ej
{
bytesLeft = bytesLeft - (sizeof(tablica_bajtow_danych) - (readPtr - tablica_bajtow_danych)); //zmniejszenie bytesLeft (liczby pozosta³ych bajtów pliku) o liczbê przepatrzonych ju¿ bajtów, w¶ród których nie by³o nag³ówka ramki MP3
result = read_sector_from_current_or_next_cluster(numer_wpisu_poczatku_listy); //wype³nienie tablicy_bajtow_danych now± porcj± danych z karty SD
if ((result == 0xFF) | (result == 0)) //gdy koniec pliku (wtedy result == 0xFF) lub b³±d odczytu karty (wtedy result == 0), to:
outOfData = 1; //ustawienie zmiennej outOfData, by mo¿na by³o wyj¶æ z pêtli dekoduj±cej MP3
else readPtr = tablica_bajtow_danych; //w przeciwnym razie nowe dane z karty SD s± poprawnie odczytane i teraz wska¼nik readPtr wskazuje pierwszy element tablicy_bajtow_danych z nowymi danymi
continue; //i skok do instrukcji while (!outOfData) - koñca pêtli dekoduj±cej ramki MP3
}
readPtr += offset; //w przeciwnym razie uda³o siê znale¶æ nag³ówek i wtedy zwiêkszenie readPtr, tak, by wskazywa³ na pierwszy bajt tego nag³ówka
err = MP3GetNextFrameInfo(hMP3Decoder, &mp3FrameInfo, readPtr); //pobranie informacji o ramce MP3 na podstawie znalezionego wy¿ej nag³ówka
if (err) { //gdy nie uda siê pobraæ informacji (to nie by³ nag³ówek, wtedy err <> 0)
readPtr = readPtr + 1; //to zwiêkszenie o 1 wska¼nika readPtr, by dalsze poszukiwanie nowego nag³ówka rozpoczê³o siê od nastêpnego bajtu po tym mylnie zakwalifikowanym jako pocz±tek nag³ówka ramki MP3
continue; //i skok do instrukcji while (!outOfData) - koñca pêtli dekoduj±cej ramki MP3
}
//w przeciwnym razie uda³o siê pobraæ informacje o kolejnej ramce MP3 i wtedy poni¿ej obliczenie rozmiaru tej ramki (rozmiar ramki zale¿y równie¿ od wersji MPEG)
i gdy jest on < 0, to oznacza, ¿e w bie¿±cej zawarto¶ci tablica_bajtow_danych nie uda³o siê znale¼æ takiego ci±gu binarnego. Wtedy (nadal w tym warunku) nastêpuje wywo³anie funkcji read_sector_from_current_or_next_cluster (w pliku main.c), która wype³nia zmienn± tablica_bajtow_danych now± 512-bajtow± porcj± danych z pliku i ca³y cykl poszukiwania s³owa synchronizuj±cego w pêtli do...while dekoduj±cej ramki MP3 rozpoczyna siê od pocz±tku. Natomiast, gdy uda³o siê w bie¿±cej zawarto¶ci tablica_bajtow_danych znale¼æ taki ci±g binarny (wtedy offset > = 0), to najpierw nastêpuje zwiêkszenie wska¼nika readPtr o warto¶æ offset i nastêpnie wywo³ywana jest funkcja MP3GetNextFrameInfo (równie¿ z mp3dec.c), która sprawdza, czy rzeczywi¶cie jest to nag³ówek kolejnej ramki MP3. Funkcja ta zwraca warto¶æ err = 0,gdy jest to s³owo synchronizuj±ce kolejny nag³ówek ramki MP3 i wtedy realizowane s± dalsze instrukcje (m.in. obliczenie zmiennej Frame_length - d³ugo¶æ ramki MP3), w przeciwnym razie err<>0 i wtedy tylko zwiêkszenie wska¼nika readPtr o 1 i powrót na pocz±tek pêtli do...while dekoduj±cej ramki MP3.
Mam nadziejê, ¿e w miarê wyja¶ni³em. Komentarze w kodzie te¿ powinny byæ pomocne w zrozumieniu dzia³ania fragmentów kodu.
Pozdrawiam, KT_priv
Witam ponownie po d³u¿szej przerwie. W miêdzyczasie usun±³em jeszcze jeden zauwa¿ony b³±d powoduj±cy zawieszenie siê odtwarzacza, a przedtem wy¶wietlenie ¶mieci na LCD. B³±d ten pojawia³ siê, gdy do odtwarzacza w³o¿y³o siê nowo zakupion± lub pust± po formacie kartê SD. B³±d ten usun±³em modyfikuj±c fragment funkcji main, jak poni¿ej:
Code:
if (! (AT91F_PIO_GetInput(AT91C_BASE_PIOA) & AT91C_PIO_PA30))
{ //gdy PA30 = L, to odtwarzacz NIE jest do³±czony do portu USB w kompie i wtedy:
liczba_wpisow = read_dir(rodzaj_karty, opis_partycji.first_cluster_num, opis_partycji.first_data_sector_address); //odczytanie zawarto¶ci g³ównego katalogu na karcie SD, w zmiennej liczba_wpisow jest ilo¶æ plików i podkatalogów w katalogu g³ównym na karcie SD
if (liczba_wpisow == 0) //gdy na karcie SD nie ma plików i podkatalogów (karta by³a formatowana), to:
{
AT91F_SPI_Disable( AT91C_BASE_SPI); //wy³±czenie SPI
wyswietl_tekst_na_LCD(0x80,"No directories/"); //tekst wy¶wietlany w pierwszym wierszu LCD
wyswietl_tekst_na_LCD(0xC0,"files on SD Card"); //tekst wy¶wietlany w drugim wierszu LCD
play_mp3_sound(sound1, size1); //odtworzenie MP3 z d¼wiêkiem ostrzegawczym o ¼le w³o¿onej karcie SD
play_mp3_sound(sound1, size1); //odtworzenie MP3 z d¼wiêkiem ostrzegawczym o ¼le w³o¿onej karcie SD
wait(2000);
shutdown_player(240); //wy³±czenie odtwarzacza
}
numer_wpisu_poczatku_listy = list_files_directories(1/*=numer_wpisu_poczatku_listy*/, liczba_wpisow, 2, 16); //teraz wylistowanie na LCD 2*16 nazw plików i podkatalogów pocz±wszy od pierwszej pozycji zapamiêtanej w zmiennej tablica_wpisow. W zmiennej numer_wpisu_poczatku_listy jest numer wpisu pliku lub podkatalogu, którego nazwa jest wyswietlana w pierwszym wierszu LCD
}
Powy¿szy fragment sprawdza, czy nie jest do³±czony USB do odtwarzacza. Gdy nie jest, to odczytywana jest zawarto¶æ g³ównego katalogu karty. Realizuje to funkcja read_dir, która zwraca ilo¶æ plików i podkatalogów w g³ównym katalogu karty. Ta ilo¶æ przypisana jest do zmiennej liczba_wpisow. W przypadku, gdy karta by³a pusta, zmienna ta mia³a warto¶æ 0. Przed poprawk± przekazanie takiej warto¶ci do nastêpnej wywo³ywanej funkcji list_files_directories powodowa³o wy¿ej opisany b³±d. Teraz taka sytuacja powoduje wy³±czenie odtwarzacza (bo nie ma czego odtwarzaæ) po wy¶wietleniu stosownego komunikatu. Oczywi¶cie po do³±czeniu USB powy¿szy fragment jest pomijany, gdy odtwarzacz pracuje jako Mass Storage Device, st±d sprawdzenie warunku, czy do³±czony USB.
Nastêpn± poprawk± jest pomijanie nieistniej±cej nazwy karty (volume label). Realizuje mi to nastêpuj±cy fragment funkcji read_dir (z pliku File_system_functions.c):
Code:
switch (tablica_bajtow_danych[11 + (32 * licznik_paczek)]) //sprawdzanie 11.bajtu w ka¿dej paczce
{
case 0x08: //analizowana paczka zawiera nazwê partycji
if (power_on_or_USB_connected) //wy¶wietlenie tej nazwy tylko po w³aczeniu zasilania lub po od³±czeniu USB
{
configure_LCD(0x01); //wyczyszczenie wy¶wietlacza LCD (Display Clear)
wait(3);
for (licznik_petli = 0; licznik_petli < 11; licznik_petli++) //pêtla wystawiaj±ca znaki etykiety karty SD na wy¶wietlaczu LCD
{
if (tablica_bajtow_danych[licznik_petli + (32 * licznik_paczek)] < 0x80) //spr,czy jest to 7-bitowy kod ASCII. Gdy tak,to:
wyswietl_znak_ASCII_na_LCD(0x82 + licznik_petli, tablica_bajtow_danych[licznik_petli + (32 * licznik_paczek)]);//wystawianie na LCD 7-bit ASCII znaków etykiety karty SD
else //w przeciwnym razie mo¿e byæ to kod polskiej litery (w 8-bit ASCII) i wtedy
{
if (tablica_bajtow_danych[licznik_petli + (32 * licznik_paczek)] == 0xE5) break; //gdy jest skasowana etykieta karty SD, to opuszczenie pêtli wystawiaj±cej znaki etykiety karty na LCD
else //w przeciwnym razie jest to kod polskiej litery i poni¿ej wy¶wietlenie tej polskiej litery na LCD
{
wyswietl_polski_znak_na_LCD(0x82 + licznik_petli, (adres_CGRAM << 3), tablica_bajtow_danych[licznik_petli + (32 * licznik_paczek)]);
adres_CGRAM++; //zwiêkszenie adresu CGRAM, bo jedna polska litera zosta³a ju¿ wy¶wietlona
}
}
}
if (tablica_bajtow_danych[licznik_petli + (32 * licznik_paczek)] != 0xE5) //gdy jest obecna etykieta karty SD, to wy¶wietlenie jej przez 2 sekundy
{ //razem z wy¶wietleniem napiêcia baterii
wyswietl_tekst_na_LCD(0xC0,"Vbatt=");
AT91F_ADC_StartConversion(AT91C_BASE_ADC); //uruchomienie przetwornika A/C
while (! (AT91C_BASE_ADC->ADC_SR & AT91C_ADC_EOC5)); //i po odczekaniu, gdy Vbat jest ju¿ przetworzone na warto¶æ cyfrow±
converted_Vbat = AT91F_ADC_GetConvertedDataCH5(AT91C_BASE_ADC); //to odczytanie cyfrowej warto¶ci Vbat
Vbat = ((2500 * converted_Vbat) + 14625) / 1979; //obliczenie rzeczywistego napiêcia baterii na podstawie warto¶ci odczytanej z ADC
itoa(Vbat,txt_Vbat,10); //konwersja liczbowej warto¶ci napiêcia baterii na postaæ ³añcuchow±
if (Vbat < 100) //gdy napiêcie baterii < 1V to odpowiedni format ³añcucha txt_Vbat, tak, by poprawnie by³y wy¶wietlane warto¶ci napiêcia < 1V
{
txt_Vbat[2] = txt_Vbat[1];//przeniesienie czê¶ci u³amkowej napiêcia baterii na odpowiednie pozycje w ³añcuchu txt_Vbat
txt_Vbat[1] = txt_Vbat[0];
txt_Vbat[0] = 0x30; //przed przecinkiem bêdzie wy¶wietlana liczba 0
}
wyswietl_znak_ASCII_na_LCD(0xC6,txt_Vbat[0]);
wyswietl_znak_ASCII_na_LCD(0xC7,',');
wyswietl_znak_ASCII_na_LCD(0xC8,txt_Vbat[1]);
wyswietl_znak_ASCII_na_LCD(0xC9,txt_Vbat[2]);
wyswietl_znak_ASCII_na_LCD(0xCA,'V');
wait(2000);
}
}
break;
Dzia³anie: po formatowaniu karty wszytkie wpisy plików i podkatalogów w tablicy FAT s± zerowane, tote¿ si³± rzeczy ka¿dy 11.bajt w poszczególnych 32-bajtowych wpisach plików i podkatalogów w FAT równy jest 0 i wtedy nie bêd± wykonywane instrukcje po linii case 0x08. Natomiast, gdy istnia³a wcze¶niej nazwa karty, ale zosta³a ona skasowana, to na jej wy¶wietlenie nie pozwoli instrukcja:
Code:
if (tablica_bajtow_danych[licznik_petli + (32 * licznik_paczek)] == 0xE5) break; //gdy jest skasowana etykieta karty SD, to opuszczenie pêtli wystawiaj±cej znaki etykiety karty na LCD
gdy¿ skasowanie nazwy karty, tak jak skasowanie pliku, odbywa siê poprzez zast±pienie pierwszego znaku nazwy kodem 0xE5.
Przy okazji niejako uzyska³em mo¿liwo¶æ stosowania polskich znaków w nazwie karty, gdy¿ pêtla wystawiaj±ca znaki nazwy na LCD sprawdza kod ka¿dego znaku nazwy (poczynaj±c od pierwszego znaku) i gdy kod ten > 0x80, to wpierw dalej sprawdza, czy jest to czasem kod 0xE5. Gdy nie jest (wtedy to jest kod polskiej litery w standardzie CP852, DOS Latin II), to nastêpuje wystawienie odpowiadaj±cej polskiej litery na LCD podczas wywo³ania funkcji wyswietl_polski_znak_na_LCD (plik polish_characters.c).
Dorobi³em równie¿ taki "feature" :) w postaci pomiaru i wy¶wietlenia napiêcia baterii podczas pokazywania nazwy karty, co widaæ na zaprezentowanym wy¿ej fragmencie kodu, oraz podczas odtwarzania pliku, gdy naci¶nie siê jednocze¶nie przyciski Volume UP i Volume DOWN. Fragment kodu realizuj±cy mi t± w³a¶ciwo¶æ umieszczony jest w funkcji play_mp3_file w pliku main.c i pokazany jest poni¿ej:
Code:
if (! ( (AT91F_PIO_GetInput(AT91C_BASE_PIOA) & SW1) | ((AT91F_PIO_GetInput(AT91C_BASE_PIOA) & SW0) << 1) ))//gdy naci¶niête naraz przyciski SW1 i SW0(=L) (wtedy odczyt napiêcia baterii),to:
{
if (! (show_battery_voltage)) //gdy wy¶wietlenie napiêcia jest pierwszy raz po naci¶niêciu przycisków SW1 i SW0
{
aktualizacja_LCD_odtwarzanie = false; //to wyzerowanie zmiennych aktualizacja_LCD_odtwarzanie i aktualizacja_LCD_glosnosc w celu
aktualizacja_LCD_glosnosc = false; //prawid³owego dzia³ania funkcji volume_control po zwolnieniu jednego z tych przycisków lub dwóch naraz
configure_LCD(0x01); //wyczyszczenie wy¶wietlacza LCD (Display Clear)
wait(15);
configure_LCD(0x02); //dana konfiguruj±ca wy¶wietlacz LCD(Cursor/Display Home)
wait(15);
wyswietl_tekst_na_LCD(0x81,"Ubattery=");
wyswietl_znak_ASCII_na_LCD(0x8B,',');
wyswietl_znak_ASCII_na_LCD(0x8E,'V');
}
show_battery_voltage = true; //i ustawienie zmiennej show_battery_voltage, by przytrzymanie naci¶niêtych przycisków SW1 i SW0 nie powtarza³o powy¿szych instrukcji
AT91F_ADC_StartConversion(AT91C_BASE_ADC); //uruchomienie przetwornika A/C
while (! (AT91C_BASE_ADC->ADC_SR & AT91C_ADC_EOC5)); //i po odczekaniu, gdy Vbat jest ju¿ przetworzone na warto¶æ cyfrow±
converted_Vbat = AT91F_ADC_GetConvertedDataCH5(AT91C_BASE_ADC); //to odczytanie cyfrowej warto¶ci Vbat
Vbat = ((2500 * converted_Vbat) + 14625) / 1979; //obliczenie rzeczywistego napiêcia baterii na podstawie warto¶ci odczytanej z ADC
itoa(Vbat,txt_Vbat,10); //konwersja liczbowej warto¶ci napiêcia baterii na postaæ ³añcuchow±
wyswietl_znak_ASCII_na_LCD(0x8A,txt_Vbat[0]); //i wy¶wietlenie tej warto¶ci na LCD
wyswietl_znak_ASCII_na_LCD(0x8C,txt_Vbat[1]);
wyswietl_znak_ASCII_na_LCD(0x8D,txt_Vbat[2]);
}
else //w przeciwnym razie :
{
show_battery_voltage = false; //wyzerowanie zmiennej show_battery_voltage,by prawid³owo pokazaæ warto¶æ napiêcia baterii gdy znów bêd± naci¶niête przyciski SW0 i SW1
volume_control(numer_wpisu_poczatku_listy); //oraz wywo³ywana jesrt procedura nastawiaj±ca g³o¶no¶æ odtwarzania
}
Zastosowanie dodatkowej zmiennej show_battery_voltage by³o potrzebne, by tylko raz po naci¶niêciu tych przycisków by³a wy¶wietlana informacja na LCD o pomiarze napiêcia baterii. Natomiast non stop podczas naciskania tych przycisków odczytywana i prezentowana na LCD jest liczbowa warto¶æ napiêcia baterii.
Dodatkowo wprowadzi³em mo¿liwo¶æ formatowania karty SD, gdy odtwarzacz wspó³pracuje z kompem jako Mass Storage Device. Sprowadzi³o siê to do zmodyfikowania instrukcji wyboru:
Code:
switch (packet_CBW.CBWCB_Table[0]) //pole Operation Code w ramce SCSI
umieszczonej w pêtli obs³uguj±cej transmisjê USB w funkcji main, do której doda³em:
Code:
case 0x2F: //komenda Verify
nbr_of_data = 0;
Send_CSW_packet(0, packet_CBW.CBWCB_Table[0], nbr_of_data); //teraz wype³nienie zmiennej tablica_bajtow_danych danymi z pakietu CSW i wys³anie jej do hosta
break;
Jeszcze odno¶nie plików MP3 z fsample=48kHz: podczas odtwarzania takich plików zdecydowa³em siê na taktowanie ARM-a zegarem 73,728MHz i ustawionym 1WS. Ta zmiana powoduje mi wzrost pr±du pobieranego z akumulatora do 200mA (dla plików z fsample=44,1kHz i taktowaniu ARM-a zegarem 45,1584MHz, 0WS, pr±d ten jest 160mA), ale w zamian mam poprawne i stabilne (bez zawieszeñ) odtwarzanie plików z tym fs. Poni¿ej fragment kodu (w funkcji play_mp3_file z pliku main.c), w którym wybieram fclk ARM-a:
Code:
MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo); //uzyskanie informacji o zdekodowanej ramce MP3
if (! (configured)) // gdy by³a to dopiero pierwsza poprawnie zdekodowana ramka, to najpierw przed wys³aniem danych do portu SSC
{ //skonfigurowanie interfejsu SSC
sampling_freq = mp3FrameInfo.samprate;
if (sampling_freq == 48000)
{ //gdy czêstotliwo¶æ próbkowania odczytana z odtwarzanego pliku = 48kHz, to poni¿ej ustawienie clk ARM-a na warto¶æ 73,728MHz i ustawienie cyklu 1WS
*AT91C_RTTC_RTMR = 3; //ustawienie preskalera RTT w celu przyspieszenia przyjmowania przerwañ od RTT, by uzyskaæ szybsze wystawianie znaków na LCD
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_1FWS)); //ustawienie 1 cyklu WaitState
AT91F_CKGR_CfgPLLReg(AT91C_BASE_CKGR, AT91C_CKGR_USBDIV_0 | (3 << 16) | AT91C_CKGR_OUT_0 | AT91C_CKGR_PLLCOUNT | 1); //mikrokontroler taktowany jest zegarem (18,432 * (3+1) / 1) = 73,728MHz
while (! (AT91F_PMC_IsStatusSet(AT91C_BASE_PMC, AT91C_PMC_LOCK))){}; //w pêtli while odczekanie na ustabilizowanie siê pêtli PLL
AT91F_SSC_Configure( AT91C_BASE_SSC, (73728000), (2*16*48000), 0, 0, AT91C_I2S_ASY_MASTER_TX_SETTING(16, 2), AT91C_I2S_ASY_TX_FRAME_SETTING(16, 2)); //(dla zegara 73,728MHz; fsample = 48kHz)
config_TC0(36000); //i ustawienie TC0,by generowa³ przerwanie co 500ms dla czêstotliwo¶ci zegara = 73,728MHz
*AT91C_TC1_RC = 8; //oraz ustawienie TC1 tak,by prawid³owo dzia³a³ interfejs L3 w UDA1330 dla czêstotliwo¶ci zegara = 73,728MHz
}
else //w przeciwnym razie ustawienie zegara mikrokontrolera dla domy¶lnej warto¶ci fsample=44,1kHz
{
AT91F_CKGR_CfgPLLReg(AT91C_BASE_CKGR, AT91C_CKGR_USBDIV_0 | (48 << 16) | AT91C_CKGR_OUT_0 | AT91C_CKGR_PLLCOUNT | 20); //mikrokontroler taktowany jest zegarem (18,432 * (48+1) / 20) = 45,1584MHz; fsample=44,1kHz
while (! (AT91F_PMC_IsStatusSet(AT91C_BASE_PMC, AT91C_PMC_LOCK))){}; //w pêtli while odczekanie na ustabilizowanie siê pêtli PLL
AT91F_SSC_Configure( AT91C_BASE_SSC, (45158400), (2*16*sampling_freq), 0, 0, AT91C_I2S_ASY_MASTER_TX_SETTING(16, 2), AT91C_I2S_ASY_TX_FRAME_SETTING(16, 2)); //(dla zegara 45,1584MHz; fsample=44,1kHz)
config_TC0(22050); //i ustawienie TC0,by generowa³ przerwanie co 500ms dla czêstotliwo¶ci zegara = 45,1584MHz
*AT91C_TC1_RC = 4; //oraz ustawienie TC1 tak,by prawid³owo dzia³a³ interfejs L3 w UDA1330 dla czêstotliwo¶ci zegara = 45,1584MHz
}
AT91F_SSC_EnableTx( AT91C_BASE_SSC); //Enable the TX transmitter
out_Samples = mp3FrameInfo.outputSamps;
AT91F_SSC_SendFrame( AT91C_BASE_SSC, (char*)outBuf, out_Samples, (char*)outBuf, out_Samples); //wys³anie do portu SSC poprzez kana³ DMA danych dla przetwornika D/A i wype³nienie drugiego bufora DMA
AT91F_PMC_EnablePeriphClock( AT91C_BASE_PMC, 1 << AT91C_ID_SSC ) ; //enable the clock of the SSC
configured = true; //interfejs SSC jest ju¿ skonfigurowany
}
I jeszcze najwa¿niejsza rzecz: uda³o mi siê znacz±co zwiêkszyæ prêdko¶æ transferu plików podczas zapisywania i odczytywania plików przez komputer, gdy odtwarzacz jest po³±czony z nim przez USB. Obecnie mam ok. 350kB/s przy zapisie plików na kartê SD i ok. 400kB/s przy odczycie plików z niej (w porównaniu z poprzednimi odpowiednio 75kB/s i 200kB/s). Osi±gn±³em to poprzez powa¿n± przeróbkê funkcji: Write_To_Card i Read_From_Card, umieszczonych w pliku SCSI_commands.c i obs³uguj±cych transmisjê danych przez USB. Poni¿ej te funkcje:
Code:
/*funkcja wk³adaj±ca do tablicy bajtów danych w odpowiedzi na komendê Read odczytany blok danych z karty SD i pó¼niej
wysy³aj±ca t± tablicê do hosta USB.
Argumentami funkcji sa:
1. ilo¶æ bloków, które maj± byæ odes³ane hostowi
2. adres bloku, od którego zaczyna siê odczyt karty SD
Zwracan± warto¶ci± jest liczba bajtów odes³anych hostowi w odpowiedzi na komendê Read = ilo¶æ bloków * 512 */
unsigned int Read_From_Card(unsigned short length, unsigned int LBA)
{
signed short licznik_blokow = length;
if (length == 0) AT91F_USB_SendZlp(pCDC, AT91C_EP_IN); //wys³anie Zero Length Packet do hosta USB, gdy ten wys³a³ komendê Read,ale bez podania ilo¶ci bloków do odczytu
else
{
if (length > 1) //gdy ilo¶æ bloków,które maj± byæ odczytane z karty, jest wiêksza od 1
{ //to poni¿ej realizacja odczytu wielu bloków z karty SD
unsigned char *dane_z_karty_SD = malloc(32768); //zarezerwowanie w heap obszaru na tablicê o rozmiarze 32kB przechowuj±c± dane odczytywane z karty
// if (dane_z_karty_SD == 0) goto SINGLE_BLOCK; //(gdyby by³o potrzebne)gdy nie uda³o siê zaalokowaæ RAM-u na tablicê dane_z_karty_SD,to odczyt pojedynczych bloków
AT91F_SPI_CfgCs( AT91C_BASE_SPI, SD_Card_Access, (0x0 << 24) | (0x0 << 16) | (AT91C_SPI_SCBR - 0xFC00) | AT91C_SPI_BITS_8 | !(AT91C_SPI_CSAAT) | !(AT91C_SPI_NCPHA) | (AT91C_SPI_CPOL) ); //ust. SPI mode 3 (AT91C_SPI_CPOL) | !(AT91C_SPI_NCPHA), 8 bitów danych, bez opó¼nieñ miêdzy zegarem i danymi, bez opó¼nieñ pomiêdzy poszczególnymi danymi(potrzebne dla kart SD), czêstotliwo¶æ zegara = MainCLK / (0xFF - 0xFC)
while (licznik_blokow > 0) //gdy jeszcze s± do odczytania dane z karty, wtedy licznik_blokow > 0 i poni¿sza pêtla siê wykonuje
{
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_3FWS)); //ustawienie 3 cykli Wait States przed odczytem dnych z karty (zegar MainCLK = 96MHz)
read_multiple_sectors(dane_z_karty_SD, (licznik_blokow > 0x40 ? 0x40 : licznik_blokow), LBA, (CSD.CSD_Structure ? true : false));//odczytanie (64 lub licznik_blokow) bloków pocz±wszy od adresu w LBA; gdy zwyk³a karta SD (CSD_Structure = 0),to false, w przeciwnym razie true
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_1FWS)); //ustawienie 1 cyklu WS przed wys³aniem odczytanych danych do kompa przez USB
AT91F_UDP_Write(&pCDC, dane_z_karty_SD, (512 * (licznik_blokow > 0x40 ? 0x40 : licznik_blokow))); //wys³anie wype³nionej tablicy dane_z_karty_SD do hosta USB
LBA = LBA + (licznik_blokow > 0x40 ? 0x40 : licznik_blokow); //zwiêkszenie adresu LBA o liczbê odczytanych ju¿ bloków
licznik_blokow = licznik_blokow - 0x40; //i zmniejszenie licznika bloków; gdy ta warto¶æ bêdzie <= 0, t.zn. ju¿ wszystkie bloki zosta³y odczytane z karty
}
free(dane_z_karty_SD);
}
else
//SINGLE_BLOCK:
{
AT91F_SPI_CfgCs( AT91C_BASE_SPI, SD_Card_Access, (0x0 << 24) | (0x0 << 16) | (AT91C_SPI_SCBR - 0xFC00) | AT91C_SPI_BITS_8 | !(AT91C_SPI_CSAAT) | !(AT91C_SPI_NCPHA) | (AT91C_SPI_CPOL) ); //ust. SPI mode 3 (AT91C_SPI_CPOL) | !(AT91C_SPI_NCPHA), 8 bitów danych, bez opó¼nieñ miêdzy zegarem i danymi, bez opó¼nieñ pomiêdzy poszczególnymi danymi(potrzebne dla kart SD), czêstotliwo¶æ zegara = MainCLK / (0xFF - 0xFC)
while (licznik_blokow) //w przeciwnym razie odczyt pojedynczych bloków z karty
{
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_3FWS)); //ustawienie 3 cykli Wait States przed odczytem dnych z karty (zegar MainCLK = 96MHz)
read_sector(LBA,(CSD.CSD_Structure ? true : false));//odczytanie bloku o adresie w LBA; gdy zwyk³a karta SD (CSD_Structure = 0),to false, w przeciwnym razie true
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_1FWS)); //ustawienie 1 cyklu WS przed wys³aniem odczytanych danych do kompa przez USB
AT91F_UDP_Write(&pCDC, tablica_bajtow_danych, 512); //wys³anie wype³nionej tablicy bajtów danych do hosta USB
LBA++; //adres nastêpnego bloku do odczytu
licznik_blokow--; //zmniejszenie o 1 licznika bloków, bo jeden odczytany blok zosta³ ju¿ wys³any do hosta USB
}
}
}
return (length * pow(2, CSD.Block_Length));
}
/*funkcja odczytuj±ca bloki danych z hosta USB i zapisuj±ca odczytane bloki na kartê SD
Argumentami funkcji sa:
1. ilo¶æ bloków, które maj± byæ odebrane od hosta
2. adres bloku, od którego zaczyna siê zapis danych na kartê SD
Zwracan± warto¶ci± jest liczba bajtów odebranych od hosta w odpowiedzi na komendê Write = ilo¶æ bloków * 512 */
unsigned int Write_To_Card(unsigned short length, unsigned int LBA)
{
signed short liczba_pozostalych_blokow = length; //liczba pozosta³ych bloków, które musz± jeszcze byæ zapisane na kartê SD
unsigned char *dane_na_karte_SD = malloc(32768); //zarezerwowanie w heap obszaru na tablicê o rozmiarze 32kB przechowuj±c± dane przeznaczone do zapisu na kartê SD
AT91F_SPI_CfgCs( AT91C_BASE_SPI, SD_Card_Access, (0x0 << 24) | (0x0 << 16) | (AT91C_SPI_SCBR - 0xFB00) | AT91C_SPI_BITS_8 | !(AT91C_SPI_CSAAT) | !(AT91C_SPI_NCPHA) | (AT91C_SPI_CPOL) ); //ust. SPI mode 3 (AT91C_SPI_CPOL) | !(AT91C_SPI_NCPHA), 8 bitów danych, bez opó¼nieñ miêdzy zegarem i danymi, bez opó¼nieñ pomiêdzy poszczególnymi danymi(potrzebne dla kart SD), czêstotliwo¶æ zegara = MainCLK / (0xFF - 0xFB)
while (liczba_pozostalych_blokow > 0) //gdy jeszcze s± do zapisania dane na kartê, wtedy liczba_pozostalych_blokow > 0 i poni¿sza pêtla siê wykonuje
{
AT91F_UDP_Read(&pCDC, dane_na_karte_SD, (512 * (liczba_pozostalych_blokow > 0x40 ? 0x40 : liczba_pozostalych_blokow))); //wype³nienie tablicy dane_na_karte_SD danymi odebranymi z hosta USB
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_2FWS)); //ustawienie 2 cykli Wait States przed zapisem danych na kartê (zegar MainCLK = 96MHz)
write_multiple_sectors(dane_na_karte_SD, (liczba_pozostalych_blokow > 0x40 ? 0x40 : liczba_pozostalych_blokow), LBA, (CSD.CSD_Structure ? true : false));//zapis na kartê (liczba_pozostalych_blokow) bloków pocz±wszy od adresu w LBA; gdy zwyk³a karta SD (CSD_Structure = 0),to false, w przeciwnym razie true
AT91F_MC_EFC_CfgModeReg( AT91C_BASE_MC, (AT91C_MC_FMCN | AT91C_MC_FWS_1FWS)); //ustawienie 1 cyklu WS przed przes³aniem przez USB danych z kompa do zapisu na kartê
LBA = LBA + (liczba_pozostalych_blokow > 0x40 ? 0x40 : liczba_pozostalych_blokow); //zwiêkszenie adresu LBA o liczbê zapisanych ju¿ bloków
liczba_pozostalych_blokow = liczba_pozostalych_blokow - 0x40; //i zmniejszenie liczba_pozostalych_blokow; gdy ta warto¶æ bêdzie <= 0, t.zn. ju¿ wszystkie bloki zosta³y zapisane na kartê
}
free(dane_na_karte_SD);
return (length * pow(2, CSD.Block_Length));
}
Poprzednie, b³êdne wersje tych funkcji wynika³y z mojego b³êdnego za³o¿enia, ¿e komputer bêdzie zawsze ¿±daæ odczytu lub zapisu ilo¶ci bloków, które tworz± 1 cluster. Okaza³o siê byæ to nieprawd± (co wysz³o podczas debugowaia), st±d nowa postaæ powy¿szych funkcji. Bufory: dane_na_karte_SD i dane_z_karty_SD zaalokowa³em w odpowiednio powiêkszonym obszarze pamiêci przeznaczonej dla zmiennych dekodera MP3, który podczas wspó³pracy odtwarzacza z komputerem nie by³ wykorzystywany. St±d we w³a¶ciwo¶ciach projektu musia³em zwiêkszyæ Heap Size do 33000B (wobec dotychczasowych 23918B).
Pozdrawiam, KT_priv
Tytus Kosiarski, mam pytanie dotycz±cej Twojej funkcji play_mp3_file, mianowicie masz mo¿e tê funkcjê w ubo¿szej wersji tzn. bez lcd itp. Bo Twoja jest bardzo rozbudowana. Chodzi mi o samo dekodowanie mp3 i wys³anie do DAC.
Witam
Jest w za³±czniku. Drobna uwaga: ca³e dekodowanie zrealizowa³em w funkcji main w pliku main.c. Po skompilowaniu i wgraniu tego do ARM-a z do³±czonym DAC-em, jak na schemacie ideowym, ten powinien od razu odtwarzaæ kawa³ek MP3 zapisany w pamiêci Flash.
Pozdrawiam, KT_priv
Witam ponownie. Wprowadzi³em jeszcze kilka usprawnieñ w programie odtwarzacza po ponad trzymiesiêcznym, intensywnym jego eksploatowaniu. Podyktowane to by³o faktem zakupu kolejnej karty SD (SDHC 8GB Kingston, niby Class 4, w czarnej obudowie), która, jak siê pó¼niej okaza³o, mia³a bardzo d³ugi czas oczekiwania na wystawienie pierwszego bajtu poprzedzaj±cego 512-bajtow± paczkê danych. Ilustruje to poni¿szy fragment kodu:
Code:
attempt = 0;
do //w poni¿szej pêtli do...while oczekiwanie na pojawienie siê pierwszego bajtu <> 0xFF poprzedzaj±cego 512-bajtow± paczkê danych.
{ //Gdy s± same 0xFF, to karta SD jeszcze nie wystawi³a danych po wys³aniu odpowiedzi i wtedy poni¿sza pêtla ci±gle siê wykonuje
AT91F_SPI_PutChar(AT91C_BASE_SPI, 0xFF, SD_Card_Access); //wystawianie 0xFF na MOSI, by utrzymaæ aktywno¶æ CLK
odebrany_bajt = AT91F_SPI_GetChar( AT91C_BASE_SPI); //odczytywanie dalszych bajtów danych z karty
if ( !(AT91F_PIO_GetInput(AT91C_BASE_PIOA) & AT91C_PIO_PA30)) //gdy NIE jest do³±czone USB,to
{
attempt++;
if (attempt > 3500) return read_write_OK = false; //gdy liczba prób odczytania danych z karty > 3500, to wyj¶cie ze zwrotem warto¶ci read_write_OK = false
}
}
while (odebrany_bajt == 0xFF); //ta pêtla wykonuje siê tak d³ugo, jak d³ugo odebrany_bajt == 0xFF
Fragment ten wywo³ywany jest kilkakrotnie w funkcji sd_get_response_and_read_write_data w pliku SD_Card_functions.c
W poprzednich wersjach programu ta pêtla koñczy³a siê, gdy attempt > 1000 (w przypadku gdy non stop odebrany_bajt == 0xFF). Dla tego, konkretnego egzemplarza karty SD pozostawienie dotychczasowej warto¶ci, z któr± by³a porównywana zmienna attempt, skutkowa³o b³êdami w odczycie drzewa plików i podkatalogów umieszczonych na karcie po w³±czeniu odtwarzacza oraz wy³±czeniem odtwarzacza (z komunikatem o braku karty SD) podczas prób odtworzenia plików MP3 z tej karty. Wymiana tej karty na inn± w sklepie nie wchodzi³a w grê, bo karta jest formalnie na chodzie, w laptopie funkcjonuje prawid³owo. Pozostawa³o zatem dostosowanie programu odtwarzacza tak, by ten egzemparz karty prawid³owo wspó³pracowa³ z odtwarzaczem.
Na pierwszy ogieñ posz³o zwiêkszenie warto¶ci, z któr± jest porównywana zmienna attempt (w powy¿szym fragmencie kodu). Zabieg ten spowodowa³ poprawne odczytywanie drzew plików i podkatalogów na tej karcie oraz poprawne odczytywanie zawarto¶ci odtwarzanego pliku MP3. Niestety, okaza³o siê, ¿e tak d³ugi czas oczekiwania na dane z karty powoduje przedwczesne opró¿nianie bufora nadawczego SSC podczas odtwarzania plików VBR i plików CBR z bitrate > 160kb/s, co powodowa³o trzaski podczas odtwarzania takich plików.
Postanowi³em przyjrzeæ siê funkcji play_mp3_file w pliku main.c. W tej funkcji oprócz czasoch³onnego dekodowania strumienia MP3 odbywa siê odczyt karty SD w celu dostarczania nowych danych do dekodowania, odbywa siê równie¿ regulacja g³o¶no¶ci, wy¶wietlanie informacji na LCD i pomiar napiêcia baterii. Pomny do¶wiadczeñ nabytych podczas wcze¶niejszych prób implementacji odczytu na raz wielu sektorów z karty, postanowi³em równie¿ zaimplementowaæ komendê ReadMultipleSector podczas odtwarzania plików. Poprzednio ta komenda dzia³a³a tylko przy wspó³pracy odtwarzacza z kompem, przy odtwarzaniu plików odbywa³ siê odczyt pojedynczych sektorów z karty. Osi±gn±³em to poprzez umieszczenie fragmentu:
Code:
result = read_multiple_sectors(tablica_bajtow_danych, 2, start_address + licznik, rodzaj_karty); //odczytanie nowych danych z 2 pierwszych sektorów karty SD nale¿±cych do klastra zajêtego przez odtwarzany plik
licznik = licznik + 2; //zwiêkszenie licznika odczytanych sektorów, bo powy¿ej zosta³y ju¿ odczytane 2 sektory;
na pocz±tku funkcji play_mp3_file i wewn±trz funkcji read_sector_from_current_or_next_cluster (plik main.c). Oczywi¶cie musia³em te¿ odpowiednio dostosowaæ cia³o wywo³ywanej funkcji read_multiple_sector tak, by ta prawid³owo wspó³pracowa³a z reszt± programu odtwarzacza podczas grania muzy (bo wcze¶niej ta funkcja by³a wykorzystywana tylko podczas wspó³pracy odtwarzacza z kompem). Najlepsze efekty podczas odtwarzania uzyskiwa³em przy odczycie naraz 2 sektorów, co pozwoli³o mi ju¿ na p³ynne odtwarzanie plików z bitrate <= 192kb/s. Niestety, nadal pozostawa³ problem z plikami z wy¿szym bitrate.
Aby rozwi±zaæ ten problem, spróbowa³em zwiêkszyæ czêstotliwo¶æ zegara mikrokontrolera do 67,7376MHz (4 * fsysclk UDA1330 = 4 * 384 * 44100). W efekcie uzyska³em poprawne odtwarzanie plików <=256kb/s. Pozosta³o jeszcze 320kb/s. W pierwszej wersji przeróbek nastêpn± warto¶ci± zegara mikrokontrolera by³o 90,3168MHz (4 * 512 * 44100). W efekcie uzyska³em poprawne odtwarzanie wszystkich plików MP3. Jednak¿e nie podoba³a mi siê tak du¿a warto¶æ zegara mikrokontrolera. Pamiêtaj±c o moich wcze¶niejszych próbach zmniejszania fsample bez s³yszalnego spowolnienia tempa odtwarzania utworu, próbowa³em zmniejszaæ fsample z krokiem 300Hz (z oryginalnej warto¶ci 44100Hz) poprzez zmniejszanie fclk mikrokontrolera, pamiêtaj±c o zachowaniu zale¿no¶ci fclk = 4 * mno¿nik_UDA1330 * fsample; gdzie mno¿nik_UDA1330 mo¿e tylko przyj±æ jedn± z warto¶ci: 256, 384, 512 (tak s±dzi³em po lekturze PDF-a tego uk³adu). Przyj±³em warto¶æ mno¿nik_UDA1330 = 512. Postêpuj±c w taki sposób doszed³em do warto¶ci fsample = 42900Hz (tempo odtwarzania by³o jeszcze akceptowalne). Da³oby to zegar mikrokontrolera ~= 87,8MHz (4* 512 * 42900). Trochê lepiej, ale jeszcze nie to... Ale, odwróæmy tok my¶lenia: zale¿no¶æ
Code:
(1) fclk = 4 * mno¿nik_UDA1330 * fsample
mo¿na przekszta³ciæ do postaci:
Code:
(2) fclk / 4 = mno¿nik_UDA1330 * fsample
A w³a¶nie iloczyn
Code:
(3) mno¿nik_UDA1330 * fsample = fsysclk UDA1330
Czyli zmniejszaj±c fclk zmniejszamy fsysclk UDA1330 na pinie 6. tego scalaka. Czyli zobaczmy, ile bêdzie wynosi³ mno¿nik_UDA1330, dla fsample równe jednak 44100Hz i zegara mikrokontrolera równego 87,8592MHz:
Czyli zegar fsysclk UDA1330 na pinie 6. tego scalaka niekoniecznie musi mieæ warto¶æ 512 * fsample; mno¿nik_UDA1330 mo¿e mieæ mniejsz± warto¶æ bez zauwa¿alnego wp³ywu na jako¶æ odtwarzania. Tote¿ zegar mikrokontrolera dobiera³em tak, aby uzyskaæ mo¿liwie najmniejsz± jego warto¶æ dla plików z bitrate = 320kb/s, ale jednocze¶nie ¿eby warto¶æ zegara dzieli³a siê bez reszty przez 2048 (by uzyskaæ generacjê przerwania od TC0 co dok³adnie 500ms) i jednocze¶nie warto¶æ mno¿nika_UDA1330 by³a równie¿ liczb± ca³kowit±. Po uwzglêdnieniu tych za³o¿eñ wybra³em zegar mikrokontrolera równy 78,4896MHz i przyj±³em fsample = 43800Hz. I taki zegar na razie zostawi³em przy odtwarzaniu plików z bitrate 320kb/s.
Pozosta³o jeszcze zrobiæ w programie automatykê wybieraj±c± odpowiedni zegar zale¿nie od warto¶ci bitrate odtwarzanego pliku. Wykorzysta³em do tego fakt obecno¶ci pêtli oczekuj±cej na wys³anie do UDA1330 uprzednio dostarczonych danych. Przedstawia to poni¿szy kod:
Code:
unsigned char Vbat_not_converted = true;
wait_counter++; //zwiêkszenie o 1 zmiennej wait_counter przed wej¶ciem w poni¿sz± pêtlê
while (! (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_ENDTX)) //odczekanie w pêtli, a¿ wszystkie poprzednie dane z portu SSC pójd± do przetwornika D/A
{
if (Vbat_not_converted) //podczas oczekiwania w tej pêtli:
{
wait_counter--; //i zmniejszenie o 1 tej zmiennej podczas jednokrotnego wykonania tej pêtli. W efekcie ,gdy mikrokontroler dostarcza dane do DAC w sposób ci±g³y (bez przerw),to ta zmienna == 0
AT91F_ADC_StartConversion (AT91C_BASE_ADC); //uruchomienie przetwornika A/C
while (! (AT91C_BASE_ADC->ADC_SR & AT91C_ADC_EOC5)); //i po odczekaniu, gdy Vbat jest ju¿ przetworzone na warto¶æ cyfrow±
converted_Vbat = AT91F_ADC_GetConvertedDataCH5 (AT91C_BASE_ADC); //to odczytanie cyfrowej warto¶ci Vbat
Vbat_not_converted = false; //i odp.ustawienie zmiennej Vbat_not_converted, by podczas dalszego wykonywania siê pêtli while (! (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_ENDTX)) ju¿ nie przetwarzaæ Vbat
}
}
if ((wait_counter > 2) & (clock_selector < 2)) //gdy mikrokontoler 2 razy nie zd±¿y dostarczyæ danych dla DAC dla fclk mikrokontrolera wybranej zmienn± clock_selector(przyczyn± najczê¶ciej zbyt d³ugi odczyt z karty) i gdy clock_selector nie osi±gn±³ jeszcze max warto¶ci (==2)
{
SSC_configured = false; //to ustawienie zmiennej SSC_configured = false, by móc ponownie skonfigurowaæ zegar mikrokontrolera przy nastêpnym obiegu pêtli dekoduj±cej MP3
clock_selector++; //i zwiêkszenie clock_selector, by wybraæ wiêksz± czêstotliwo¶æ fclk mikrokontrolera. Zwiêkszanie tej zmiennej trwa tak d³ugo, a¿ clock_selector == 2
}
Natomiast zmiana w powy¿szym kodzie warto¶ci zmiennych SSC_configured i clock_selector powoduje przy nastêpnym obiegu pêtli dekoduj±cej MP3 ponowne wykonanie kodu:
Code:
if (! (SSC_configured)) // gdy by³a to dopiero pierwsza poprawnie zdekodowana ramka lub wyst±pi³a zmiana warto¶ci clock_selector, to najpierw przed wys³aniem danych do portu SSC
{ //skonfigurowanie interfejsu SSC
sampling_freq = mp3FrameInfo.samprate;
switch (sampling_freq)
{
...
}
AT91F_SSC_EnableTx( AT91C_BASE_SSC); //Enable the TX transmitter
out_Samples = mp3FrameInfo.outputSamps;
AT91F_SSC_SendFrame( AT91C_BASE_SSC, (char*)outBuf, out_Samples, (char*)outBuf, out_Samples); //wys³anie do portu SSC poprzez kana³ DMA danych dla przetwornika D/A i wype³nienie drugiego bufora DMA
AT91F_PMC_EnablePeriphClock( AT91C_BASE_PMC, 1 << AT91C_ID_SSC ) ; //enable the clock of the SSC
SSC_configured = true; //interfejs SSC jest ju¿ skonfigurowany
wait_counter = 0; //i wyzerowanie zmiennej wait_counter po wyborze zegara mikrokontrolera
}
gdzie wykonanie instrukcji wewn±trz switch (sampling_freq) odpowiednio ustawia zegar mikrokontrolera i konfiguruje interfejs SSC. Przy okazji dorobi³em te¿ w powy¿szym kodzie konfiguruj±cym interfejs SSC mo¿liwo¶æ odtwarzania plików z fsample = 32kHz (trafi³ mi siê jeden taki plik).
Dosz³y do mnie uwagi, ¿e nie ka¿dy egzemplarz mikrokontrolera AT91SAM7S256 pracuje stabilnie z zegarem 45,1584MHz przy 0WS (podstawowy zegar mikrokontrolera przy odtwarzaniu plików 44,1kHz) Dlatego do³±czam dwa wsady do mikrokontrolera: 1. domy¶lny, z pocz±tkowym zegarem 45,1584MHz (podkatalog wsady\zegar_45MHz); 2. z pocz±tkowym zegarem 67,7376MHz (podkatalog wsady\zegar_67MHz).
Witam ponownie
Do³o¿y³em ju¿ mo¿liwo¶æ odtwarzania plików AAC w programie tego odtwarzacza. Obecnie odtwarzacz oprócz dotychczasowego odtwarzania plików MP3 mo¿e równie¿ odtwarzaæ pliki AAC VBR, tylko Low Complexity standardu MPEG-2 i MPEG-4. Do tego celu wykorzysta³em bibliotekê dekodera AAC z tego projektu: http://embdev.net/articles/ARM_MP3/AAC_Player . Kod tej biblioteki musia³em trochê przerobiæ, by dostosowaæ j± do mojego odtwarzacza. Zmiany objê³y:
1. wyliczanie warto¶ci bitrate ka¿dej ramki AAC. Realizuje to linia:
umieszczona w funkcji UnpackADTSHeader w pliku filefmt.c. Oryginalnie do zmiennej aacDecInfo->bitRate by³a podstawiana warto¶æ 0.
2. Usuniêcie z tej biblioteki plików umo¿liwiaj±cych dekodowanie strumienia AAC-SBR. Powodem by³o brak wystarczaj±cej ilo¶ci RAM w mikrokontrolerze. O ile sam dekoder plików AAC potrzebowa³ ok.21kB heap + 3kB stack, to na dekodowanie strumienia AAC-SBR potrzeba by³o znacznie wiêcej jak 64kB heap, by funkcja AACInitDecoder zwróci³a warto¶æ ró¿n± od 0.
3. do³o¿enie jeszcze jednego argumentu do funkcji AACDecode, a mianowicie adresu zmiennej nFrames. W ciele tej funkcji pod ten adres jest wpisywana warto¶æ aacDecInfo->frameCount po ka¿dorazowym, poprawnym zdekodowaniu ramki AAC.
Maj±c ju¿ zaimplementowany dekoder AAC, pokusi³em siê jeszcze o dorobienie mo¿liwo¶ci odtwarzania plików MP4 i M4A, przy czym z plików MP4 odtwarzam tylko ¶cie¿kê d¼wiêkow±. Budowa tych plików jest zupe³nie inna. Pliki te s± zbudowane z ró¿nych segmentów danych, zwanych atomami. Do uzyskania informacji o sposobie kodowania d¼wiêku, ekstrakcji próbek d¼wiêku z pliku MP4 czy te¿ M4A potrzebne mi by³y nastêpuj±ce atomy:
1. atom "mdat". Tu potrzebna mi by³a informacja o ilo¶ci atomów "mdat", ich rozmiarów oraz po³o¿eniu tych atomów w odtwarzanym pliku MP4. Na podstawie tej informacji mog³em okre¶liæ zgrubnie po³o¿enie innych potrzebnych atomów w pliku (czy s± przed, czy za atomem / atomami "mdat". Dlatego poszukiwanie tego atomu / tych atomów zrealizowa³em zaraz na pocz±tku funkcji play_mp4_file (plik play_files.c)
2. atomy: "mdia", "mdhd", "mp4a", "esds". Znajomo¶æ offsetu atomu "mdia" potrzebna mi by³a do przyspieszenia wyszukiwania pozosta³ych trzech atomów w pliku. Atom "mdhd" przechowuje informacje o wersji strumienia audio, czasie jego utworzenia, czasie trwania ¶cie¿ki d¼wiêkowej oraz czêstotliwo¶ci próbkowania. Nastêpny atom "mp4a" jest znacznikiem, ¿e dane w tych atomach dotycz± ¶cie¿ki d¼wiêkowej. Zawiera te¿ informacjê o czêstotliwo¶ci próbkowania audio oraz o d³ugo¶ci (w bitach) próbek audio - najczê¶ciej 16-bit. Nastêpnym atomem jest "esds" który zawiera informacjê o typie ¶cie¿ki audio - najczê¶ciej bêdzie to strumieñ MPEG-4. Dalsze bajty tego atomu precyzuj± typ ¶cie¿ki audio - najczê¶ciej AAC LC, dlatego mo¿na by³o wykorzystaæ ten dekoder AAC do odtwarzania plików MP4. Kolejne bajty tego atomu okre¶laj± równie¿ bitrate, indeks czêstotliwo¶ci próbkowania oraz ilo¶æ kana³ów audio - najczê¶ciej 2, choæ ¶cie¿ki d¼wiêkowe niektórych filmów w pliku MP4 s± monofoniczne (ilo¶æ kana³ów audio - 1). Wszystkie te informacje s± potrzebne do wype³nienia pól struktury audio_stream_info i w dalszej kolejno¶ci poprawnego skonfigurowania dekodera AAC. Wyszukiwaniem tych atomów zajmuje siê funkcja get_audio_stream_info w pliku mp4dec.c. Równie¿ ta funkcja przy okazji znajduje offsety i rozmiary atomów "stco", "stsc" i "stsz".
3. atomy: "stco", "stsc", "stsz". Atomy te s± niezbêdne do ekstrakcji strumienia audio z pliku MP4. Po znalezieniu ich offsetów i rozmiarów, dane z tych atomów s± u¿yte do wype³nienia tablic stco_atom_table, stsc_atom_table i stsz_atom_table jeszcze przed wej¶ciem w pêtlê odtwarzaj±c± wybrany plik w funkcji play_mp4_file w pliku play_files.c. Przy czym z uwagi na znaczne niekiedy rozmiary tych atomów (zale¿nie od konkretnego pliku), konieczne by³o wype³nianie tych tablic kolejnymi fragmentami danych z odpowiednich atomów odczytywanymi z karty SD "w locie" podczas wykonywania siê pêtli odtwarzaj±cej wybrany plik.
W efekcie uzyska³em odtwarzanie strumienia audio z plików MP4 z bitrate do 128kb/s, 44,1kHz i 48kHz. Wy¿szych bitrate'ów nie próbowa³em. Do³±czam kod ¼ród³owy i wsady do mikrokontrolera (plik BIN i Intel HEX).
Pozdrawiam, KT