LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

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.

Rysunek 2. Układ testowy

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.

 Zachęcamy do przeczytania też części drugiej artykułu
Absolwent Wydziału Elektroniki Politechniki Wrocławskiej, współpracownik miesięcznika Elektronika Praktyczna, autor książek o mikrokontrolerach Microchip i wyświetlaczach graficznych, wydanych nakładem Wydawnictwa BTC. Zawodowo zajmuje się projektowaniem zaawansowanych systemów mikroprocesorowych.
Tagi: audio, MP3, pic24