Modułowy odtwarzacz MP3 (1)
Praktyczne testy – sprzęt
Do testowania odtwarzacz MP3 użyłem modułu ewaluacyjnego Microchip Microstick II z 16 bitowym mikrokontrolerem PIC24FJ64GB002. Moduł ma wbudowany programator/debugger i jest w pełni obsługiwany przez wtyczkę MCC środowiska MPLAB X IDE. Oprócz modułu odtwarzacza MP3, podłączonego do mikrokontrolera przez interfejs UART, wykorzystałem mały wyświetlacz OLED z interfejsem SPI oraz impulsator ze stykiem zwieranym przyciśnięciem osi – rysunek 2.
Praktyczne testy – oprogramowanie
Za pomocą MCC (MPLAB Code Configurator) szybko skonfigurowałem interfejs UART. Obsługa jest oparta na systemie przerwań i ma programowy bufor nadajnika i odbiornika – rysunek 3. MCC, na podstawie częstotliwości taktowania mikrokontrolera (8 MHz) i zadanej prędkości transmisji (9600 bd), konfiguruje rejestry liczników odpowiedzialnych za prędkość transmisji interfejsu UART.
Procedura inicjalizacji UART wygenerowana przez MCC została pokazana na listingu 1.
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;
Listing 1. Inicjalizacja UART-a
Obsługa komunikacji przez UART
Do sterowania modułem są nam potrzebne procedury wysyłania (listing 2) i odbierania (listing 3) bajtu przez UART.
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; } }
Listing 2. Wysyłanie bajtu przez interfejs UART
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; }
Listing 3. Odbieranie bajtu przez UART
Zapis komendy do modułu
Wyposażeni w procedury wysyłania i odbierania 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.
//********************************************************** //wyslanie komendy //********************************************************** void WriteCmdYX(uint8_t cmd,uint8_t par1, uint8_t par2) { UART1_Write(0x7e); UART1_Write(0xff); UART1_Write(0x06); UART1_Write(cmd); UART1_Write(0);//0x00 bez odpowiedzi, 0x01 z potwierdzeniem UART1_Write(par1); UART1_Write(par2); UART1_Write(0xef); }
Listing 4. Procedura wysyłania komendy do YX5300
Funkcje realizacji komend
Jeżeli po wysłaniu komendy moduł ma odpowiedzieć potwierdzeniem, to po bajcie komendy cmd trzeba wysłać bajt 0x01. Za pomocą funkcji WriteCmdYX można już sterować wszystkimi funkcjami odtwarzacza. Żeby było wygodniej można napisać szereg prostych funkcji realizujących poszczególne komendy (listing 5).
//********************************************************** //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(); }
Listing 5. Funkcje komend odtwarzacza
Główna pętla programu
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 dwu zmiennych: cont i pauza. Zależnie od ich stanu 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. W programie zostało wyłączone 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ętla została pokazana 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(); } }
Listing 6. Pętla testowania modułu YX5300
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.