Zdarza się, że musimy wykonać urządzenie informujące obsługę o ważnych zdarzeniach za pomocą komunikatów głosowych. Jeśli to urządzenie będzie wykorzystywało komputer lub moduł embeded z wbudowanym systemem operacyjnym np. Linux, to jest ono dosyć łatwe do wykonania. Jeżeli jednak nie mamy do dyspozycji takich środków, to nawet przy zastosowaniu popularnego mikrokontrolera można sobie poradzić dołączając kartę pamięci SD i sprzętowy kodek. Jednak wtedy potrzebne będzie napisanie oprogramowania odtwarzania. Będzie to zadanie tym trudniejsze, im „słabszy” będzie to mikrokontroler. W takiej sytuacji najtańsze jest zastosowanie specjalizowanego modułu, na przykład firmy Catalex z układem YX5300. Moduł pokazano na fotografii 1.
Moduł z układem YX5300
Moduł z układem YX5300 jest oferowany przez firmę CATALEX. Po krótkim przeszukaniu Internetu nie znalazłem ani strony tej firmy, ani dokumentacji samego układu YX5300. Jest za to dostępny dokument Serial MP3 Player Manual – zupełnie wystraczający do zaaplikowania układu.
Za kompletną płytka z układem YX5300 , stereofonicznym gniazdem słuchawkowym Jack 3,5 mm i złączem karty microSD razem z przesyłką zapłaciłem około 5 USD . Jest to cena bardzo atrakcyjna zważywszy na możliwości układu:
- Odtwarzanie plików skompresowanych w formacie MP3 i nieskompresowanym WAV.
- Wsparcie dla częstotliwości próbkowania: 8 kHz, 11,025 kHz, 12 kHz, 16 kHz, 22,05 kHz, 24 kHz, 32 kHz, 44,1 kHz i 48 kHz.
- Możliwość użycia kart Micro SD (o maksymalnej pojemności 2 GB) i Micro SDHC (maksymalnie 32 GB). Format FAT16 lub FAT32.
- Sterowanie odtwarzaniem z wykorzystaniem interfejsu szeregowego UART pracującego z prędkością 9600 b/s.
- Poziomy logiczne na magistrali UART 3,3 V lub 5 V TTL.
- Napięcie zasilania 3,2 V…5,2 V. Maksymalny pobór prądu 200 mA dla 5 V DC.
- Wbudowany wzmacniacz słuchawkowy.
- Cyfrowa regulacja poziomu sygnału audio w 30 krokach.
Oprócz wspomnianego gniazda słuchawkowego Jack na płytce umieszczono 4-pinową listwę golpinów przeznaczona do podłączenia zasilania: piny GND i VCC, oraz linii interfejsu UART: RX i TX. Kierunek przesyłania danych jest oznaczony z punktu widzenia modułu. Zielona dioda D1 sygnalizuje gotowość do odtwarzania, lub pauzę odtwarzania ( ciągłe świecenie), lub stan odtwarzania pliku (świecenie przerywane).
Komendy sterujące
Odtwarzacz wymaga, by pliki i katalogi na karcie SD miały nazwy zaczynające się od cyfr. Nazwa katalogu może być wyłącznie liczbą dwucyfrową na przykład 01, 02, 11, 23 itp. Nazwa każdego pliku musi się zaczynać od liczby trzycyfrowej na przykład 001-test1.mp3, 002-test2.mp3. Przykładowa struktura katalogów i nazw plików została pokazana na rysunku 2.
Odtwarzacz identyfikuje pliki na dwa sposoby. Komenda odtwarzania może podać numer katalogu i numer pliku w katalogu (rysunku 2). Można też identyfikować pliki za pomocą indeksu. Indeks jest nadawany w momencie wgrywania na kartę. Inaczej mówiąc o kolejności odtwarzania decyduje kolejność nagrania plików na kartę. Sterowanie odtwarzaczem odbywa się za pomocą komend przesyłanych przez UART. Mają one format pokazany w tabeli 1. Wykaz ważniejszych komend umieszczono w tabeli 2.
Jeżeli bajt feedback ma wartość 01, to po każdym wysłaniu komendy moduł odpowiada sekwencją 7E, FF, 06, 41, 00, 00, FE, BA EF. Wyzerowany szósty bajt oznacza, że dane zostały odebrane prawidłowo.
Oprócz potwierdzenia przesłania komendy mogą być wysyłane przez moduł ramki spontaniczne, niebędące reakcją na komendę, ale na zdarzenie, na przykład zakończenie odtwarzania konkretnego pliku lub włożenie/wyjęcie karty SD. Ponadto, można wysyłać komendy pytające o status urządzenia (tabela 3).
Testy praktyczne
Do testowania odtwarzacza użyłem modułu ewaluacyjnego Microchip Microstick II z 16 bitowym mikrokontrolerem PIC24 FJ64GB002. Moduł ma wbudowany programator/debugger i jest w wspierany przez wtyczkę MCC środowiska MPLAB X IDE. Oprócz modułu odtwarzacza MP3 dołączonego do mikrokontrolera przez interfejs UART, użyłem niewielkiego wyświetlacza OLED z interfejsem SPI oraz impulsatora ze stykiem zwieranym przyciśnięciem osi (fotografia 3).
Za pomocą MCC (MPLAB Code Configurator) skonfigurowałem interfejs UART. Obsługa jest oparta na systemie przerwań i ma programowy bufor nadajnika i odbiornika (rysunek 4).
MCC na podstawie częstotliwości taktowania mikrokontrolera (8 MHz) i zadanej prędkości transmisji (9600 b/s) konfiguruje rejestry liczników odpowiedzialnych za prędkość transmisji interfejsu UART. Przy okazji jest wyliczany błąd. Ten błąd jest nie do uniknięcia, jeśli częstotliwość taktowania nie jest specjalnie dobrana do oczekiwanych prędkości transmisji . Procedurę inicjalizacji UART wygenerowaną przez MCC pokazano na listingu 1. Do sterownia modułem będą nam potrzebne procedury wysyłania(listing 2) i odbierania (listing 3) bajta przez UART.
void UART1_Initialize(void)
{
/* RTSMD enabled; BRGH enabled; STSEL 1; UARTEN enabled; PDSEL 8N;
LPBACK disabled; WAKE disabled; USIDL disabled; RXINV disabled;
ABAUD disabled; IREN disabled; UEN TX_RX; */
U1MODE = 0x8808;
/* UTXEN disabled; UTXINV disabled; URXISEL RX_ONE_CHAR; ADDEN disabled;
UTXISEL0 TX_ONE_CHAR; UTXBRK COMPLETED; OERR NO_ERROR_cleared; */
U1STA = 0x0000;
// U1TXREG 0x0000;
U1TXREG = 0x0000;
// U1RXREG 0x0000;
U1RXREG = 0x0000;
// Baud Rate = 9600; BRG 103;
U1BRG = 0x0067;
IEC0bits.U1RXIE = 1;
U1STAbits.UTXEN = 1;
uart1_obj.txHead = uart1_txByteQ;
uart1_obj.txTail = uart1_txByteQ;
uart1_obj.rxHead = uart1_rxByteQ;
uart1_obj.rxTail = uart1_rxByteQ;
uart1_obj.rxStatus.s.empty = true;
uart1_obj.txStatus.s.empty = true;
uart1_obj.txStatus.s.full = false;
uart1_obj.rxStatus.s.full = false;
}
void UART1_Write(const uint8_t byte)
{
*uart1_obj.txTail = byte;
uart1_obj.txTail++;
if (uart1_obj.txTail == (uart1_txByteQ + UART1_CONFIG_TX_BYTEQ_LENGTH)) {
uart1_obj.txTail = uart1_txByteQ;
}
uart1_obj.txStatus.s.empty = false;
if (uart1_obj.txHead == uart1_obj.txTail) {
uart1_obj.txStatus.s.full = true;
}
if (IEC0bits.U1TXIE == false) {
IEC0bits.U1TXIE = true;
}
}
uint8_t UART1_Read(void)
{
uint8_t data = 0;
data = *uart1_obj.rxHead;
uart1_obj.rxHead++;
if (uart1_obj.rxHead == (uart1_rxByteQ + UART1_CONFIG_RX_BYTEQ_LENGTH)) {
uart1_obj.rxHead = uart1_rxByteQ;
}
if (uart1_obj.rxHead == uart1_obj.rxTail) {
uart1_obj.rxStatus.s.empty = true;
}
uart1_obj.rxStatus.s.full = false;
return data;
}
Wyposażeni w procedury wysyłania i dobierania bajtów przez UART ,możemy przystąpić do napisania procedur wysyłających komendy do modułu odtwarzacza. Podstawowa funkcja wysłania komendy WriteCmdYX ma trzy argumenty: kod komendy, parametr 1 i parametr 2 (listing 4). Jeżeli po wysłaniu komendy moduł ma odpowiedzieć potwierdzeniem, to po bajcie komendy cmd trzeba wysłać bajt 0x01.
//wysłanie komendy
void WriteCmdYX(uint8_t cmd,uint8_t par1, uint8_t par2)
{
UART1_Write(0x7e);
UART1_Write(0xff);
UART1_Write(0x06);
UART1_Write(cmd);
//0x00 bez odpowiedzi, 0x01 z potwierdzeniem
UART1_Write(0);
UART1_Write(par1);
UART1_Write(par2);
UART1_Write(0xef);
}
Za pomocą funkcji WriteCmdYX można już sterować wszystkimi funkcjami odtwarzacza. Dla wygody można napisać szereg funkcji realizujących poszczególne komendy, jak pokazano na listingu 5. Testowe odtwarzanie odbywa się w pętli nieskończonej. Na początku pętli jest sprawdzany warunek przyciśnięcia osi i zwarcia styku. Jeżeli oś jest przyciśnięta, to program najpierw czeka na jej zwolnienie, a potem analizuje stan dwóch zmiennych cont i pauza. Zależnie od ich wartości przyciśnięcie powoduje wysłanie komendy Pauza (PauzaP()), lub kontynuuj (ContP()). Obrócenie ośki enkodera powoduje zmianę poziomu sygnału audio ustawianą komendą VolSet z argumentem zmieniającym się od 0 do 32. Po sprawdzeniu stanu procedury obsługi enkodera program sprawdza czy moduł nie wysłał do mikrokontrolera przez UART danych.
//zerowanie
void ResYX(void)
{
WriteCmdYX(0x0c,0,0);
Delay_ms(500);
}
//wybierz karte
void SelSdYX(void)
{
WriteCmdYX(0x09,0,2);
Delay_ms(200);
}
//odtwarzaj plik o numerze bezwzglednym nrp
void PlayBp(uint8_t nrp)
{
WriteCmdYX(0x03,0,nrp);
}
//odtwarzaj plik o numerze nrp w folderze nrf
void PlayP(uint8_t nrf,uint8_t nrp)
{
WriteCmdYX(0x0f,nrf,nrp);
}
//Nastepny utwor
void NextP(void)
{
WriteCmdYX(0x01,0,0);
}
//Poprzedni utwor
void PrevP(void)
{
WriteCmdYX(0x02,0,0);
}
//Pauza
void PauzaP(void)
{
WriteCmdYX(0x0e,0,0);
}
//Wznow
void ContP(void)
{
WriteCmdYX(0x0d,0,0);
}
//Zatrzymaj odtwarzanie
void StopP(void)
{
WriteCmdYX(0x16,0,0);
}
//VOL++
void VolUp(void)
{
WriteCmdYX(0x04,0,0);
}
//VOL--
void VolDwn(void)
{
WriteCmdYX(0x05,0,0);
}
//VOL setup
void VolSet(uint8_t vol)
{
WriteCmdYX(0x06,0,vol);
}
//DAC ON
void DACOn(void)
{
WriteCmdYX(0x1a,0,0);
}
void SetupYX(void)
{
ResYX();
}
W programie wyłączono wysyłanie potwierdzeń i możemy się spodziewać tylko ramki z danymi sygnalizującymi koniec odtwarzania utworu. Jeżeli dane zostały odebrane i jest to ramka sygnalizująca koniec odtwarzania, to jest wysyłana komenda odtwarzająca kolejny utwór z katalogu. Pętlę pokazano na listingu 6.
while(1)
{
kod=GetEncoder();//odczytaj stan enkodera
if(kod==KOD_IMP_ST)
{
while(ST==0); //przyciśnięto oś enkodera
if(pauza)
{
PauzaP();//komenda Pauza
DispTxt(0,0,"PAUSE ", 16,1);
RefreshRAM();
pauza=0; cont=1;
continue;
}
if(cont)
{
ContP();//komenda kontynuuj (wznów)
DispTxt(0,0,"PLAY TRACK ", 16,1);
RefreshRAM();
pauza=1; cont=0;
continue;
}
}
if(kod==KOD_IMP_UP)
{
++vol;//głośniej
if(vol==31) vol=30;
VolSet(vol);
DispHex(vol,52,20,16);
RefreshRAM();
}
if(kod==KOD_IMP_DWN)
{
--vol;//ciszej
if(vol==0xff) vol=0;
VolSet(vol);
DispHex(vol,52,20,16);
RefreshRAM();
}
status=UART1_TransferStatusGet();//czy odebrano z modułu jakieś dane
if((status&1)==1)
{
UART1_ReadBuffer(buf,10);
if(buf[0]==0x7e&&buf[3]==0x3d)
{
//koniec odtwarzania utworu
//DispTxt(0,0,"STOP", 16,1);
PlayP(nk,++np);//odtwarzaj następny utwór
DispHex(np,88,0,16);
RefreshRAM();
}
}
}
Moduł testowany w ten sposób działa bez problemu. Wszystkie funkcje są wykonywane prawidłowo. Po włączeniu potwierdzenia każde wysłanie komendy skutkuje przesłaniem ramki z danymi. W aplikacjach wymagających dużej niezawodności wskazane byłoby włączenie potwierdzenia i przesyłanie bajtów CRC. Dla mniej wymagających zastosowań nie jest to konieczne.
Moduł z portem USB
Zachęcony pozytywnymi doświadczeniami z modułem YX5300 zakupiłem również w bardzo przystępnej cenie kolejny moduł audio potrafiący odtwarzać pliki z karty SD, ale również z gniazdem pamięci USB, czyli dla tak zwanego pendrivea. Tego rodzaju pamięć jest dużo wygodniejsza od karty SD, bo nie wymaga dodatkowego czytnika, a port USB jest w każdym komputerze.
Po dostarczeniu modułu okazało się, że jest problem z dokumentacją. Sprzedawca przesłał mi link do dokumentacji….po chińsku. Pomimo wielu poszukiwań nie udało mi się dotrzeć do dokumentacji napisanej po angielsku. Z konieczności musiałem się posłużyć Google Translator. Tłumaczenie z chińskiego na polski mówiąc oględnie nie zachwyciło, tłumaczenie z chińskiego na angielski było lepsze, ale tylko trochę. Pozostało posiłkować się częściowymi informacjami i eksperymentować z oprogramowaniem. Taka metoda okazała się bardzo pracochłonna, ale pozwoliła na zweryfikowanie działania przynajmniej najważniejszych komend umożliwiających na uruchomienie odtwarzania.
Jak już wspomniałem, moduł może odtwarzać pliki dźwiękowe z dwóch nośników: karty SD i pamięci pendrive (USB). Podstawowe parametry modułu wymieniono w tabeli 4.
Zasilanie i sterowanie odbywa się przez złącze goldpin (rysunek 5). Wyprowadzenia mają następujące funkcje:
- MUTE – sprzętowe wyciszenie – aktywne, kiedy na wejściu jest poziom wysoki.
- ADKEY – sprzętowe sterowanie funkcjami odtwarzacza.
- VCC – napięcie zasilające (3,3…5,4 V).
- RxD – linia danych odbieranych przez moduł.
- TxD – linia danych wysłanych przez moduł.
- GND – masa.
Poza złączem sterującym można przyłączać słuchawki stereofoniczne do standardowego złącza Jack 3,5 mm lub głośnik do dwóch wyprowadzeń SP- i SP+ (fotografia 6).
Przed opisem interfejsu sterującego warto wspomnieć, że moduł ma wbudowaną możliwość sterowania podstawowymi funkcjami poprzez dołączenie 8 styków zwiernych do specjalnego wejścia ADKEY tak jak to zostało pokazane na rysunku 12. Zwieranie styków połączonych szeregowo z różnymi rezystancjami powoduje wymuszenie różnych napięć na ADKEY. Te napięcia są mierzone przez wewnętrzy przetwornik analogowo cyfrowy i na podstawie tych pomiarów wykonywane są poszczególne komendy. Ponieważ nie testowałem tego trybu pracy, a nie mogłem skopiować chińskich znaków z rysunku do translatora, to przypisanie funkcji do poszczególnych styków należy traktować orientacyjnie. Prawidłowość przypisania funkcji sterujących do styków można łatwo zweryfikować po zbudowaniu układu z rys. 5. Sterowanie poprzez wejście ADKEY można włączyć lub wyłączyć komendą przesyłaną interfejsem UART. Domyślnie ta funkcja jest włączona, co pozwala na pracę w tym trybie bez konieczności użycia sterownika mikroprocesorowego.
Interfejs sterujący UART
Komendy sterujące modułem można przesyłać wykorzystując interfejs sterujący UART. Parametry transmisji to: prędkość 9600 b/s, 1 bit stopu, brak bitu parzystości i brak sterowania przepływem danych. Podobnie jak w YX5300, do sterowania wystarczą dwie linie RxD i TxD.
Rysunek 7. Format ramki sterującej.
Format ramki sterującej pokazano na rysunku 7. Od razu rzucają się w oczy podobieństwa z układem YX5300, co sugeruje tego samego producenta układu scalonego. Ramka rozpoczyna się od bajtu startu 7Eh. Po nim następuje bajt określający ilość przesyłanych bajtów od bajtu komendy do bajtu stopu, lub co na to samo wychodzi ilość przesyłanych bajtów liczy się jako wszystkie bajty w ramce bez bajtu startu i stopu. Pola parametru mogą nie występować w ogóle, lub może ich być różna ilość zależnie od rodzaju komendy. Każda ramka kończy się bajtem stopu o wartości EFh.
Komendy odtwarzacza
Zestaw podstawowych komend umieszczono w tabeli 5. Ramka sterująca nie zawiera pola parametrów. Na przykład, żeby wykonać komendę PLAY trzeba wysłać sekwencję 7E 02 01 EF, a komendę PAUSE sekwencję 7E 02 02 EF.
Po wysłaniu komendy moduł odpowiada wysłaniem sekwencji znaków ASCII:
- „OK” dla prawidłowo rozpoznanej i zdekodowanej komendy.
- „ERR” dla nierozpoznanej komendy.
Kolejny zestaw komend umożliwia zapytanie modułu o ustawione parametry, na przykład o status odtwarzania, ustawioną głośność, ilość plików itp. Po wysłaniu komendy moduł odpowiada maksymalnie 16-bitową liczbą zapisaną za pomocą 4 znaków ASCII (tabela 6).
Na przykład żeby odczytać ustawiony poziom głośności trzeba wysłać 7E 02 11 EF. Moduł odpowie na przykład znakami ASCII 30h, 30h, 30h,39h (0009h), co oznacza liczbę 9dec jeżeli komenda jest prawidłowo rozpoznana (nie jest zwracane „OK”). Jeśli komenda nie zostanie prawidłowo rozpoznana, to jest zwracany kod błędu „Err”.
W tabeli 7 umieszczono zestaw komend z 8-bitowym argumentem (hex) przeznaczonych do konfigurowania pracy modułu.
Ustawienie poziomu głośności może wyglądać następująco: 7E 03 31 1E EF.
Ostatni zestaw komend ma 2-bajtowy (16-bitowy) argument i jest przeznaczony do wybierania pliku do odtwarzania – tabela 8.
Komenda 41h wybiera numer pliku (utworu) do odtwarzania. Na przykład żeby odtworzyć plik numer 8 trzeba wysłać komendę 7E 04 41 00 08 EF. Komendy wyboru pliku działają podobnie jak w module z YX5300. Można identyfikować pliki za pomocą indeksu, lub podając numer folderu i numer pliku w folderze.
W komendzie „Wybierz plik z folderu” (42h) folder musi mieć nazwę od 01 do 99, a pliki nazwę 001 do 255.mp3 (lub 1 do 255.wav). Jeżeli foldery i pliki nie będą tak nazywane, to funkcja wyboru plików 42h nie będzie działała poprawnie. Na rysunku 8 pokazano strukturę folderów i plików zapisanych na pendrive w trakcie testowania modułu odtwarzacza. Aby odtworzyć plik nr 002 z katalogu 01 trzeba wysłać komendę 7E 04 42 01 02 EF
Testy praktyczne
Napięcia zasilające i interfejs sterujący są takie same, jak w module YX5300, więc zastosowałem ten sam układ testowy z modułem Microstick II, wyświetlaczem OLED i impulsatorem. Identycznie skonfigurowałem też UART oraz obsługę wyświetlacza i impulsatora.
Funkcje komend zapisują bufor BufCmd kolejnymi bajtami przesyłanymi przez UART do modułu. Na listingu 7 pokazano funkcję PlayMp3() wysyłająca komendę PLAY o kodzie 01h. Najpierw do bufora BufCmd[] są zapisywane bajty: startu, ilości danych, kodu komendy i stopu. Potem na ekranie wyświetlacza jest wyświetlana informacja o uruchomieniu odtwarzania „PLAY TRACK” i wywoływana jest funkcja WriteCmdMp3() z argumentami: wskaźnikiem na bufor z bajtami do wysłania i ilością danych do wysłania.
void PlayMp3(void)
{
BufCmd[0]=0x7e; //bajt startu
BufCmd[1]=0x02; //bajt liczby danych
BufCmd[2]=0x01; //bajt komendy PLAY
BufCmd[3]=0xef; //bajt stopu
DispTxt(0,0,"PLAY TRACK ", 16,1);
RefreshRAM();
WriteCmdMp3(BufCmd,4);
}
Po wysłaniu bajtów komendy program odczekuje 50 ms na odebranie potwierdzenia. To dłużej, niż potrzeba. Każdy bajt to 10 bitów przesyłanych z prędkością 9600 b/s. A więc przesłanie 1 bajta trwa 1/9600*10=1,04 ms. Trzy bajty potwierdzenia to ok. 3 ms, 4 bajty odpowiedzi po komendach o status modułu to ok 4 ms. Jednak przy obserwowaniu transmisji na oscyloskopie widać było, że moduł nie odsyła odpowiedzi natychmiast, ale wymaga pewnego czasu na przetworzenie danych.
uint8_t WriteCmdMp3(uint8_t *buf,uint8_t len)
{
uint8_t i,status;
uint8_t bufor[10];
//wysłanie zadanej liczby bajtów
for(i=0;i<len;i++) UART1_Write(buf[i]);
//odczekanie na odpowiedź
Delay_ms(50);
status=UART1_TransferStatusGet();
//czy odebrano jakieś znaki
if((status&2)==2)
{
//zapisanie bufora odebranymi znakami
UART1_ReadBuffer(bufor,8);
//czy komenda prawidłowa
if(bufor[0]=='O'&&bufor[1]=='K')
{
DispTxt(0,20,"ok!", 16,1);
RefreshRAM();
return(0);
}
//czy komenda nierozpoznana
if(bufor[0]=='E')
{
DispTxt(0,20,"err", 16,1);
RefreshRAM();
return(1);
}
else
{
//odebrano inne znaki (niż potwierdzenie)
DispTxt(0,20,"NACK", 16,1);
RefreshRAM();
return(2);
}
}
//nie odebrano żadnych znaków
DispTxt(0,20,"NOE", 16,1);
RefreshRAM();
return(3);
}
Program sprawdza czy bajty zostały odebrane testując status transmisji funkcją UART1_TransferStatusGet();. W innych implementacjach użytkownik musi sobie zapewnić możliwość odbierania batów i sygnalizację odebrania. Po odebraniu odpowiedzi następuje analiza odebranych znaków. Wykrycie znaków „OK” oznacza prawidłowo odebrana komendę, wykrycie znaku „E” oznacza nie zidentyfikowane polecenie, wykrycie innych znaków jest sygnalizowane brakiem potwierdzenia NACK. Komendy PAUZA i STOP pokazano na listingu 9.
uint8_t PauseMp3(void)
{
BufCmd[0]=0x7e;
BufCmd[1]=0x02;
BufCmd[2]=0x02;
BufCmd[3]=0xef;
DispTxt(0,0,"PAUSE ", 16,1);
RefreshRAM();
return(WriteCmdMp3(BufCmd,4));
}
uint8_t StopMp3(void)
{
BufCmd[0]=0x7e;
BufCmd[1]=0x02;
BufCmd[2]=0x0e;
BufCmd[3]=0xef;
return(WriteCmdMp3(BufCmd,4));
}
Z zestawu komend konfigurujących najczęściej używana będzie komenda ustawiania głośności – listing 10. W tym przypadku po kodzie komendy jest wysyłany jeden bajt argumentu mogący mieć wartość z zakresu 0…30.
uint8_t VolMp3(uint8_t vol)
{
BufCmd[0]=0x7e;
BufCmd[1]=0x03; //liczba bajtów
BufCmd[2]=0x31; //kod komendy
BufCmd[3]=vol; //ustawiana wartość
BufCmd[4]=0xef; //bajt stopu
return(WriteCmdMp3(BufCmd,5));
}
Do przedstawionych wyżej komend trzeba dołączyć komendę wyboru pliku na podstawie numeru katalogu i numeru pliku (listing 11) i można zbudować nieskomplikowany, ale funkcjonalny odtwarzacz plików audio w formacie MP3 lub WAV.
uint8_t PlayTrackMP3(uint8_t fld,uint8_t plk )
{
BufCmd[0]=0x7e;
BufCmd[1]=0x04;
BufCmd[2]=0x42; //funkcja wybierz folder i plik
BufCmd[3]=fld; //numer folderu
BufCmd[4]=plk; //numer pliku
BufCmd[5]=0xef; //bajt stopu
DispTxt(0,0,"Play ", 16,1);
DispHex(fld,40,0,16); //wyświetl numer folderu
DispHex(plk,60,0,16); //wyświetl numer pliku
RefreshRAM();
return(WriteCmdMp3(BufCmd,6));
}
Podsumowanie
Pokazane tu moduły odtwarzania plików dźwiękowych są głównie przeznaczone do zastosowań przemysłowych. Zbudowanie „konsumenckiego” odtwarzacza plików muzycznych MP3 jest również możliwe, ale kłopotliwe. Przeszkodą mogą być narzucone nazwy plików ale głównie brak możliwości odczytania rzeczywistej nazwy pliku będącej jednocześnie nazwą utworu. Aby skutecznie wybierać utwory do odtwarzania trzeba za każdym razem zmieniać ich nazwę, tak by zaczynała się od trzycyfrowej liczby.
Ta niedogodność jest jednocześnie zaletą kiedy chcemy wybierać komunikaty głosowe nie na podstawie dowolnej nazwy składającej się ze znaków ASCII, ale na podstawie wartości cyfrowej. Takie rozwiązanie bardzo ułatwia odtwarzanie komunikatów zależnie od jakiejś wartości – na przykład wartość napięcia, mocy, temperatury, itp.
Podczas testów okazało się, że oba moduły radzą sobie z podanymi częstotliwościami próbkowania, ale gorzej jest z przepływnością. Na początku przez zupełny przypadek umieściłem na nośniku pliki z przepływnością 320 kbit/s. Moduł YX5300 jakoś sobie poradził. Moduł z interfejsem USB odtwarzał materiał dźwiękowy ze sporym poziomem szumów i chwilowymi zniekształceniami. Widać było ,że wbudowany mikrokontroler nie bardzo radził sobie z takim plikiem. Materiał nagrany z przepływnością od 64 kbit/s do 128 kbit/s był odtwarzany z dobrą jakością. Zważywszy na przeznaczenie modułów, trudno to zakwalifikować jako wadę.
Na pewno ze względu na bardzo korzystny stosunek cena/możliwości warto się zainteresować zastosowaniem tego rozwiązania we własnych konstrukcjach, szczególnie tych wytwarzanych jednostkowo lub w krótkich seriach. W profesjonalnych zastosowaniach problemem może być stabilność dostaw.
Tomasz Jabłoński, EP