Forum: Mikrocontroller und Digitale Elektronik VS1053 klicken in MP3s


von Christian H. (sanjuro2k)


Lesenswert?

Hallo, ich habe ein Problem in meinem selbstgebauten MP3-Player:

Ich verwende einen ATMEGA128 mit 8Mhz, eine SD-Karte und einen VS1053 
MP3-Dekoder von VLSI.
Die Wiedergabe von MP3s über die SPI-Schnittstelle von SD-Karte zum 
MP3-Dekoder funktioniert wunderbar. Er streamt die gesamte Datei zum 
VS1053 und an den angeschlossenen Cinch-Ausgängen höre ich auch die 
Musik in guter Qualität.
Das µC-Programm sucht die DAtei im FAT16-System, liest jeweils einen 512 
Byte-Block der Datei von der SD-Karte, speichert ihn in einem Puffer 
zwischen und schreibt die 512 Byte in 16x 32-Byte-Häppchen in den 
VS1053.

Das Problem ist, das beim abspielen unangenehme Klick-Geräusche 
unregelmässig aber sehr schnell während der gesamten Wiedergabe zu hören 
sind .

Ich habe nicht die geringste Ahnung, was ich dagegen tun soll, ich habe 
schon so ziemlich alles ausprobiert, von anderen Lautsprechern über 
unterschiedliche Bitraten und Abtastraten.

Die Schaltung und das Layout (Masseflächen, 100nF usw.)entspricht der 
aus dem Datenblatt des MP3-Dekoders, .

Kann mir jemand helfen??

MfG
Christian H.

von Martin (Gast)


Lesenswert?

Ich denke nicht, das es ein analoges Problem ist (Lautsprecher, 
Verstärker o.ä.). Es könnte sein, dass der Datenstrom abreißt.

von tooon (Gast)


Lesenswert?

Ich denke auch, daß der Datenstrom abreißt. Du kannst in einem Register 
abfragen, wie voll Dein Puffer ist. Mach eine LED an, falls weniger als 
ca. 64 Byte drin sind, und schau mal ob sie immer beim Knacken 
aufblitzt..

von Narf (Gast)


Lesenswert?

Ich würde auch auf ein abreißen den Datenstroms tippen. Lädst du die 
nächsten 512 Bytes erst nach, wenn du des letzte Byte der aus dem Puffer 
versendet hast? In diesem Fall könntest du ein sleep einbauen und man 
Wert herumspielen. Falls dies der Grund ist, müssten durch das Sleep die 
Aussetzer größer werden.

von Daniel P. (ppowers)


Lesenswert?

Martin wrote:
> Ich denke nicht, das es ein analoges Problem ist (Lautsprecher,
> Verstärker o.ä.). Es könnte sein, dass der Datenstrom abreißt.
Sollte der Datenstrom abreißen, so ließe sich dieser Effekt mit der Wahl 
deutlich geringerer Bitraten (z.B. 96kBit/s) reduzieren oder vollständig 
verhindern. Ändert sich denn tatsächlich bei niedrigeren Bitraten die 
Häufigkeit der Klicks?
Was natürlich auch möglich ist, sind Lesefehler von der SD-Karte, die 
unregelmäßig unter bestimmten Umständen auftreten. Bist Du sicher, dass 
Dein FAT-Code korrekt ist? Vielleicht gibt es Probleme z.B. beim 
Clusterwechsel?

von taucher (Gast)


Lesenswert?

Ich hatte solche Klicks auch in meinem MP3-Player beim Abspielen von 
USb-festplatten. Es lag eindeutig am Datenstrom. Beim Wechsln der 
Cluster wurde die Festplatte plötzlich unerwartet langsam. Da hilft nur 
ein größerer Zwischenpuffer.

von fubu1000 (Gast)


Lesenswert?

Hallo,
also wenn deine SPI Schnittstelle mit 4Mhz(max. bei 8Mhz Takt vom 
Atmega), glaube ich nicht das der Datenstrom nicht ausreicht.
Ich hatte damals auch die 512byte in den Atmega gechrieben und 
anschliessend an den VLSI geschickt. Nebenbei noch Display mit Zeit 
anzeige und lalalala gemacht.

Warteste du denn auch immer auf das DREQ vom VLSI beim senden beiner 
32byte Bröckchen?

Ansonsten wäre Code und Platine interressant.

GRUSS

von Christian H. (sanjuro2k)


Lesenswert?

Ich habe jetzt mal die MP3 von 192kbit/s auf 24kbit/s verändert, die 
Klicks sind immer noch da, aber deutlich seltener.


Zur Info: Ich verwende die FAT-Implementierung von Ulrich Radig, wobei 
ich beim Lesen eines Dateisektors eine Funktion hinzugefügt habe , die 
von der SD-Karte 512 Byte direkt in ein Array Schreibt. Wie erkenne ich 
denn, ob der Cluster-Wechsel schuld daran ist??

Hier die Funktion, die von der SD-Karte liest, sie wird vom Fat-System 
bei jedem auslesen eines Datei-Blocks gerufen:




unsigned char sd_read_data_sector(unsigned long addr)
//###################################################################### 
######
{

  int a=0;
//Commando 16 zum lesen eines Blocks von der SD - Karte
  unsigned char cmd[] = {0x51,0x00,0x00,0x00,0x00,0xFF};

/*Die Adressierung der SD-Karte wird in Bytes angegeben,
addr wird von Blocks zu Bytes umgerechnet danach werden
diese in das Commando eingefügt*/

  addr = addr << 9; //addr = addr * 512

  cmd[1] = ((addr & 0xFF000000) >>24 );
  cmd[2] = ((addr & 0x00FF0000) >>16 );
  cmd[3] = ((addr & 0x0000FF00) >>8 );

//Sendet Commando cmd mit entsprechender Sector-Adresse an SD-Karte, 
dort wird Chip Select auf low gesetzt
  if (sd_write_command (cmd) != 0)
      {
       return;
      }
//Wartet auf Start Byte von der MMC/SD-Karte (FEh/Start Byte)

  while (spi_read_byte() != 0xfe)
  {
  };

  while(a<512)
    {
// Schreibe Daten von SD-Karte in globalen Puffer aus Performancegründen 
16 mal 32
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
      dataptr[a++] = spi_read_byte();
    }
    a=0;
//SD-Karte deaktivieren
  sd_disable();
  return;
}


Zum Finden der Datei im FAT-System und Schreiben der Daten aus dem Array 
in den MP3-Dekoder gibt es folgende Funktion:



void play_file(unsigned char *File_name)
//###################################################################### 
######
{
  //play=1;
  #if DEBUG_Mode
  usart_write("\r\nButton gedrueckt - Abspielmodus gestartet \r\n");
    #endif
  int Cluster;
  unsigned long Size;
  unsigned char Dir_Attrib ;
  int x=0;
  int anzahl_datei_sektoren=0;
  sd_read_csd(Buffer);

// Auf Native-Mode setzen,Stream Mode
  Mp3WriteRegister(SCI_MODE, 0x08, 0x40);


// Suche auf SD-Karte im Root-Verzeichnis nach Datei

  usart_write("\r\n Suche nach Datei %s",File_name);
// Puffer Array löschen
  flush_buffer();

if (fat_search_file((unsigned char 
*)File_name,&Cluster,&Size,&Dir_Attrib,Buffer) == 1)
    {

    usart_write("\r\n Datei %s  gefunden",File_name);
    usart_write("\r Spiele Datei : %s ab",File_name);

        anzahl_datei_sektoren=Size/512; // Anzahl der Cluster, die von 
dieser Datei belegt werden

    for (int b = 0;b<anzahl_datei_sektoren;b++) // Wenn die Datei 
gefunden, lese die Sektoren aus
      {

      #if DEBUG_Mode
      usart_write("\r Schreibe Block %i ab",b);
      #endif

        fat_read_file_sector (Cluster,Buffer,b); // ein Sector wird in 
dataptr geschrieben
        mp3_data_enable();        // MP3-Modul bereit für Datenempfang
        while(x <512)            // Für bessere Performance werden 16 * 
32 Byte an VS1053 übertragen
        {

          spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
            spi_write_byte(dataptr[x++]);
          while (!(PINB & (1<<mp3_dreq)))
           ;
        }
        mp3_data_disable();
        x=0;
      }
    }
  usart_write("\r\n Datei : %s abgespielt",File_name);
  send_zero();
  Mp3SoftReset();
  //play=0;
}


Vielleicht hilft euch das ja weiter

Mfg
Christian

von Daniel P. (ppowers)


Lesenswert?

Christian Har wrote:
> Zur Info: Ich verwende die FAT-Implementierung von Ulrich Radig, wobei
Das hatte ich befürchtet.
Diese FAT-Implementierung ist ohne weitere Optimierungen nicht so gut 
für einen kontinuierlichen Datenstrom (=MP3s abspielen) geeignet.

Das Problem liegt hier (Ausschnitt aus der aktuellen Quelltextdatei von 
U. Radig):
1
//############################################################################
2
//Lesen eines 512Bytes Blocks von einem File
3
void fat_read_file (unsigned int Cluster,//Angabe des Startclusters vom File
4
         unsigned char *Buffer,    //Workingbuffer
5
         unsigned long BlockCount)    //Angabe welcher Bock vom File geladen 
6
                          //werden soll a 512 Bytes
7
//############################################################################
8
{
9
  //Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
10
  //Berechnung welcher Cluster zu laden ist
11
  
12
  unsigned long Block = (BlockCount/cluster_size);
13
  
14
  //Auslesen der FAT - Tabelle
15
  fat_load (Cluster,&Block,Buffer);  // !!! PROBLEM !!!     
16
  Block = ((Block-2) * cluster_size) + cluster_offset;
17
  //Berechnung des Blocks innerhalb des Cluster
18
  Block += (BlockCount % cluster_size);
19
  //Read Data Block from Device
20
  mmc_read_sector (Block,Buffer);  
21
  return;
22
}

Diese Funktion liest ja bekanntlich einen Sektor von der SD-Karte. 
Allerdings wird hier vor dem Lesen JEDES Sektors erstmal die FAT-Tabelle 
ausgelesen (Funktion fat_load(...)). Dies führt zu einer ziemlich 
bescheidenen Performance. Denn eigentlich müsste man nur aus der 
FAT-Tabelle lesen, wenn man das Ende eines Clusters erreicht hat, da man 
dann erst das Folgecluster bestimmen muss.
In Deinem Fall führt das also dazu, dass beim Lesen eines Sektors der 
MP3-Datei erstmal 1 bis x Sektoren aus der FAT-Tabelle gelesen werden, 
um so äußerst ineffizient den zu lesenden Sektor zu bestimmen.

Ein einfacher Optimierungsansatz wäre hier, die aktuelle Sektornummer 
innerhalb des aktuellen Clusters global zu speichern und erst die 
FAT-Tabelle zu lesen, sobald die aktuelle Sektornummer == Anzahl 
Sektoren pro Cluster ist.

gruß
daniel

von fubu1000 (Gast)


Lesenswert?

Hallo,
außerdem würde ich deinem play_file, die ganzen USART Geschichten mal, 
CSD-Register auslesen, sowie bei jeder Übertragung den Modus von deinem 
VLSI einzustellen, rausnehmen. Das  while (!(PINB & (1<<mp3_dreq))) , 
würde ich an den Anfang der Schleife setzen nicht ans Ende, ansonsten 
schiesst du eventuell 32bytes in den Wind.
Und was soll das MP3SoftReset() machen ?

Gruss

von Christian H. (sanjuro2k)


Lesenswert?

Ich habe die fat_read_file folgendermaßen verändert, aber die Clicks 
sind immer noch da.
Ich habe, wie Daniel P. vorgeschlagen hat, eine globale Variable 
einfügt, die von 0 bis cluster_size -1 zählt und nur einen neuen Cluster 
aus der FAT lädt, wenn die clustersize überschritten wird.
Sollten die Clicks denn nicht auch mit konstantem Abstand auftreten, 
wenn es um Clusterwechsel geht??
Sie kommen ein paar dutzend mal pro sekunde, aber eher in unregelmäßigen 
abständen.

@ Fubu:
Die von dir angesprochenen Dinge werden nur einmalig aufgerufen, und 
nicht ständig beim abspielen der MP3. Sie dürften also keinen Einfluss 
haben.

Hier meine Änderung:


//###################################################################### 
######
//Lesen eines 512Bytes Blocks von einem File
void fat_read_file_sector (unsigned int Cluster,//Angabe des 
Startclusters vom File
         unsigned long BlockCount)    //Angabe welcher Block vom File 
geladen
                          //werden soll a 512 Bytes
//###################################################################### 
######
{
  //Berechnung des Blocks aus BlockCount und Cluster aus FATTabelle
  //Berechnung welcher Cluster zu laden ist

  unsigned long Block = (BlockCount/cluster_size);

  //Auslesen der FAT - Tabelle
  if(aktueller_cluster_block > cluster_size-1)
  {
    fat_load (Cluster,&Block,Buffer);
  }

  Block = ((Block-2) * cluster_size) + cluster_offset;
  //Berechnung des Blocks innerhalb des Cluster
  Block += (BlockCount % cluster_size);
  //Read Data Block from Device
  sd_read_data_sector (Block);
  aktueller_cluster_block++;
  return;
}

von Daniel P. (ppowers)


Lesenswert?

Hier mal eine einfache Maßnahme um zu überprüfen, ob Dein Klicken 
tatsächlich seine Ursache im FAT-System hat:

1.) SD-Karte formatieren
2.) einen einzigen song auf die karte kopieren. Da vorher alle sektoren 
frei waren, sollte der Song nun in linear zusammenhängenden Sektoren auf 
der Karte abgespeichert sein.
3.) Wie gewohnt den StartCluster (und damit den ersten Sektor) des Songs 
über die FAT-Funktionen von U. Radig bestimmen.
4.) Song abspielen, dabei aber nicht die FAT-Funktion verwenden, sondern 
nach dem Lesen des Liedstartsektors einfach direkt den nächsten Sektor 
lesen und abspielen.

Ich hoffe das war so irgendwie verständlich!?

Sollte das Klicken danach nicht mehr bestehen, ist tatsächlich eine 
weitere Optimierung der FAT-Funktionen nötig.
Andernfalls solltest Du vielleicht mal Deine Schaltung posten, die 
SPI-Frequenzen überprüfen, mal die Übertragungsrate SD-Karte -> µC 
messen usw...


Die von mir bereits vorgeschlagene Optimierung von U.Radigs FAT war nur 
ein Anfang, sollte allerdings eigentlich schon ein flüssiges Abspielen 
der MP3s ermöglichen... Die Bestimmung des Folgeclusters ist in der 
FAT-Library aber ebenfalls nicht besonders effizient gelöst und kostet 
eine Menge Zeit. Auch hier kann durch eine globale Variable, welche sich 
den aktuellen Cluster merkt, ein deutlicher Performancegewinn erzielt 
werden.

von Lupin (Gast)


Lesenswert?

Falls genug RAM vorhanden kann man die cluster auch vor dem laden der 
datei heraus finden und in einen buffer ab legen.

von Daniel P. (ppowers)


Lesenswert?

Lupin wrote:
> Falls genug RAM vorhanden kann man die cluster auch vor dem laden der
> datei heraus finden und in einen buffer ab legen.
Das wäre natürlich die eleganteste Lösung. Bei FAT32 kommen da 
allerdings schnell einige kB zusammen. Eine Minute MP3-Material mit 
320kBit/s entspricht etwa 4800 Sektoren, bei einer Sektorzahl pro 
Cluster von 32 wären das 150 Cluster pro Minute. Für einen 4 
Minuten-Song wären das bereits 2400 Bytes an Clusternummern, die man 
precachen müsste. Klar, bei 4 Minuten ginge das noch, aber es gibt ja 
auch längere Songs...

von Benedikt K. (benedikt)


Lesenswert?

Es sollte auch ohne diese aufwendigen Tricks ausreichend schnell sein. 
Das Dateisystem von Radig ist etwas dumm für mp3s, das stimmt. Ich 
musste da einiges umbiegen, da in der alten Version die ich damals 
hatte, bei jedem Sector read von Dateianfang aus die Clusterchain neu 
gesucht wurde. Die erste Minute jeder Datei liefen problemlos, danach 
wurde die Ausgabe immer stottriger.
Selbst mit einer verbesserten Clusterverfolgung kommt das Dateisystem 
bei weitem noch nicht an das von ELM Chan ran. Damit habe ich auf einem 
dsPIC mit 13MHz SPI Takt schon durchschnittliche Datenraten in der 
Größenordnung von rund 1MByte/s erreicht, mit Festplatten im langsamen 
PIO Modus sogar >2MByte/s. Auf einem AVR sollte es nicht sehr viel 
langsamer sein.
Mein mp3 Player mit VS1011 und dem modifizierten Radig Dateisystem macht 
zumindest auch bei 320kbit/s keine Probleme, und da steckt nur ein mega8 
mit internem Takt drin...

von Christian H. (sanjuro2k)


Angehängte Dateien:

Lesenswert?

Daniel P. wrote:
> Hier mal eine einfache Maßnahme um zu überprüfen, ob Dein Klicken
> tatsächlich seine Ursache im FAT-System hat:
>
> 1.) SD-Karte formatieren
> 2.) einen einzigen song auf die karte kopieren. Da vorher alle sektoren
> frei waren, sollte der Song nun in linear zusammenhängenden Sektoren auf
> der Karte abgespeichert sein.
> 3.) Wie gewohnt den StartCluster (und damit den ersten Sektor) des Songs
> über die FAT-Funktionen von U. Radig bestimmen.
> 4.) Song abspielen, dabei aber nicht die FAT-Funktion verwenden, sondern
> nach dem Lesen des Liedstartsektors einfach direkt den nächsten Sektor
> lesen und abspielen.
>

So jetz hab ich habe ich mal, wie von Daniel P vorgeschlagen, eine Datei 
auf die SD-Karte gepackt und einfach die Sektoren nacheinander 
gestreamt. Ich kann die MP3 hören, nur leider sind die Klicks immer noch 
da.
Das lässt ja wohl darauf schliessen, daß es sich auch um die Hardware 
handeln kann.
Anbei mal mein Schaltplan:

Zur Info, ich habe das STK525, für das ich ein erweiterungboard gebaut 
habe, das über einen 40Poligen Connector mit dem STK verbunden wird.
Der Audioausgang geht über zwei Cinch Buchsen.

von DerAlbi (Gast)


Lesenswert?

Zeig mal nen Bild von deinem Aufbau.. entweder die Übertragung vno µC zu 
VS ist mistig oder von SD-> µC.

Mache mal nen Foto vom gesamtaufbau..

MFG

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.