Skyduino:~#
Articles
arduino, programmation

[Arduino] Lecteur MP3 avec une breakout VS1053B

Bonjour tout le monde !

Comme promis dans mon précédent article, aujourd’hui je vais vous faire une petite démonstration du chipset audio VS1053B 😉
Au programme : lecture de fichiers mp3 avec une carte arduino !

Le montage :

Rien de bien compliqué pour cette démonstration !

Il vous faut :
- une carte arduino
- une shield (ou breakout) pour carte SD
- une breakout (ou shield) VS1053B
- des câbles et des pinces croco (ça peut toujours servir)
- un lot de haut parleurs amplifié, ou à défaut un lot de haut parleur 30 ohms (comme les miens)

Le câblage :

Plus de câbles que d’habitude mais ça reste raisonnable 😉
Arduino -> VS1053
VCC -> VCC
GND -> GND
RST -> RST
D2 -> BSYNC
D3 -> DREQ
D9 -> CS
D11 -> SI
D12 -> SO
D13 -> SCLK

Remarque : la prise jack :

Pour ceux qui n’ont pas de prise jack femelle sous la main vous pouvez faire comme moi et utiliser de simple pinces croco 😉

Une prise jack audio est constituer comme ceci :
< tête | anneau 1 | anneau 2 ] —-

tête : canal gauche
anneau 1 : canal droit
anneau 2 : point commun (la masse analogique en gros)

(et vive les schémas en ascii art ;))

Le code :

Celui ci lit tout les fichiers mp3 ayant un nom du type track000.mp3 (avec 000 un nombre allant de 001 à 100 (ne pas oublier les 0 de pagination avant le nombre)), présent sur la carte SD.

/* Includes */
#include <SPI.h>
#include <SD.h>

/* câblage de la breakout */
const byte PIN_MP3_CS = 9;
const byte PIN_MP3_BSYNC = 2;
const byte PIN_MP3_DREQ = 3;
const byte PIN_SD_CS = 4;

/* Registres SCI de la gamme VS10xx */
enum {
  SCI_MODE, SCI_STATUS, 
  SCI_BASS, SCI_CLOCKF, 
  SCI_DECODE_TIME, SCI_AUDATA, 
  SCI_WRAM, SCI_WRAMADDR, 
  SCI_HDAT0, SCI_HDAT1, 
  SCI_AIADDR, SCI_VOL, 
  SCI_AICTRL0, SCI_AICTRL1, 
  SCI_AICTRL2, SCI_AICTRL3
};

/**
 * Ecrit une valeur 16 bits dans un registre du VS10xx
 *
 * @param address Adresse du registre
 * @param data_msb Valeur 16 bits : MSB
 * @param data_lsb Valeur 16 bits : LSB
 */
void vs1053_write_register(uint8_t address, uint8_t data_msb, uint8_t data_lsb){

  /* Initialise la communication */
  while(!digitalRead(PIN_MP3_DREQ)); // Attend que le VS1053 soit disponible
  digitalWrite(PIN_MP3_CS, LOW);     // Passe en mode "commande"

  /* Envoi des données */
  SPI.transfer(0x02);     // Commande "Write"
  SPI.transfer(address);  // Envoi l'adresse du registre
  SPI.transfer(data_msb); // Envoi la valeur (MSB)
  SPI.transfer(data_lsb); // Envoi la valeur (LSB)

  /* Finalise la communication */
  while(!digitalRead(PIN_MP3_DREQ)); // Attend la fin de la communication
  digitalWrite(PIN_MP3_CS, HIGH);    // Quitte le mode "commande"
}

/**
 * Lit la valeur d'un registre du VS10xx
 *
 * @param address Adresse du registre
 * @return Register Valeur 16 bits du registre
 */
uint16_t vs1053_read_register (uint8_t address){

  /* Initialise la communication */
  while(!digitalRead(PIN_MP3_DREQ)); // Attend que le VS1053 soit disponible
  digitalWrite(PIN_MP3_CS, LOW);     // Passe en mode "commande"

  /* Envoi les données */
  SPI.transfer(0x03);    // Commande "Read"
  SPI.transfer(address); // Envoi l'adresse du registre

  /* Recoit les données */
  uint16_t data = (uint16_t)SPI.transfer(0xFF) << 8; // Lit la valeur du registre (MSB)
  while(!digitalRead(PIN_MP3_DREQ)); // Attend la fin de la communication
  data |= SPI.transfer(0xFF);        // Lit la valeur du registre (LSB)
  while(!digitalRead(PIN_MP3_DREQ)); // Attend la fin de la communication

  /* Finalise la communication */
  digitalWrite(PIN_MP3_CS, HIGH); // Quitte le mode "commande"

  /* Retourne la valeur du registre */
  return data;
}

/**
 * Change le volume du signal sonore de sortie
 * 0 -> Max, 100 -> silence
 * 
 * @param left_channel Volume du canal gauche
 * @param right_channel Volume du canal droit
 */
inline void vs1053_set_volume(uint8_t left_channel, uint8_t right_channel) {
  vs1053_write_register(SCI_VOL, left_channel, right_channel);
}

/* -------------------------------------------------------------------------- */

/* setup() */
void setup() {

  /* Place les broches de controle en sorties */
  pinMode(PIN_MP3_CS, OUTPUT);
  pinMode(PIN_MP3_DREQ, INPUT);
  pinMode(PIN_MP3_BSYNC, OUTPUT);
  pinMode(PIN_SD_CS, OUTPUT);

  /* Initialise le port série (pour debug) */
  Serial.begin(115200);
  Serial.println("VS1053 MP3 Player");

  /* Initialise le port SPI */
  pinMode(10, OUTPUT);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);

  /* Initialise la carte SD */
  if (!SD.begin(PIN_SD_CS)) { // Gestion des erreurs
    Serial.println("Erreur de carte SD ...");
    for(;;);
  }

  /* Initialise le VS1053 en mode SPI */
  /* Le VS1053 tourne à 1.75MHz par défaut, par sécurité on reste à 1MHz (pour le moment) */
  SPI.setClockDivider(SPI_CLOCK_DIV16); // 16MHz / 16 = 1MHz
  SPI.transfer(0xFF); // Envoi d'un octet pour activer le mode SPI

  /* Initialise le broches de controle */
  digitalWrite(PIN_MP3_CS, HIGH);    // Quitte le mode "commandes"
  digitalWrite(PIN_MP3_BSYNC, HIGH); // Quitte le mode "Données"

  /* Change le volume par défaut par 50% / 50% */
  vs1053_set_volume(50, 50);

  /* Lit la valeur du registre de statut (pour vérification) */
  uint16_t vs1053_status = vs1053_read_register(SCI_STATUS);

  /* Détecte et vérifie le type de VS10xx connecté */
  uint8_t chip_version = (vs1053_status >> 4) & 0xF;
  if(chip_version != 4) {
    Serial.println("Le chipset VS10xx detecte n'est pas un VS1053 !");
    Serial.print("Chipset detecte : ");
    switch(chip_version) {
    case 0: 
      Serial.println("VS1001");
      break;
    case 1: 
      Serial.println("VS1011");
      break;
    case 2: 
      Serial.println("VS1002");
      break;
    case 3: 
      Serial.println("VS1003");
      break;
    default : 
      Serial.println("chipset inconnu !");
      break;
    }
  }

  /* Change le prescaler de l'horloge du VS10xx par x3 (max = 5.25MHz désormais) */
  vs1053_write_register(SCI_CLOCKF, 0x60, 0x00);
  
  /* Change le prescaler du port SPI */
  SPI.setClockDivider(SPI_CLOCK_DIV4); // 16MHz / 4 = 4MHz
}

/* loop() */
void loop(){

  /* Variables d'usage */
  static char track_name[15]; // Nom du fichier en cours de lecture
  static uint8_t counter = 0; // Numero du fichier en cours de lecture

  /* Converti le numéro de fichier en nom de fichier (de la forme track000.mp3) */
  sprintf(track_name, "track%03d.mp3", counter);

  /* Lit le fichier (si il existe) */
  if(SD.exists(track_name))
    play_MP3(track_name);

  /* Passe au fichier suivant */
  ++counter;

  /* Repars de zéro si le fichier numéro 100 est atteint */
  if(counter > 100) {
    delay(5000); // Delai de 5 secondes
    counter = 0;
  }
}

/**
 * Lit un fichier mp3 stocké sur la carte SD
 *
 * @param filename Nom du fichier
 */
void play_MP3(const char filename[]) {

  /* Buffer de données */
  static uint8_t buffer[32];
  uint16_t buf_count = 0;

  /* Affichage du nom du fichier (pour debug) */
  Serial.print("En cours : ");
  Serial.println(filename);

  /* Ouverture du fichier */
  File mp3_file = SD.open(filename, FILE_READ);
  if(!mp3_file) { // Gestion des erreurs
    Serial.println("Erreur d'ouverture du fichier ...");
    return;
  }

  /* Tant que la fin de fichier n'as pas était atteinte */
  while(mp3_file.available() > 0) {

    /* Lit 32 octets depuis le fchiers */
    memset(buffer, 32, 0);     // Vide le buffer précédent (optionnel)
    mp3_file.read(buffer, 32); // Remplis le buffer avec 32 octets depuis le fichier

    /* Initialise la communication */
    while(!digitalRead(PIN_MP3_DREQ)); // Attend que le VS1053 soit disponible
    digitalWrite(PIN_MP3_BSYNC, LOW);  // Passe en mode "données"

    /* Envoi le buffer complet */
    for(uint8_t i = 0 ; i < 32 ; ++i)
      SPI.transfer(buffer[i]);

    /* Finalise la communication */
    digitalWrite(PIN_MP3_BSYNC, HIGH); // Quitte le mode "données"
  }

  /* Envoi la fin de fichier (2052 zéro) */
  digitalWrite(PIN_MP3_BSYNC, LOW); // Passe en mode "données"
  for (uint16_t i = 0 ; i < 2052 ; ++i) {
    while(!digitalRead(PIN_MP3_DREQ)); // Attend que le VS1053 soit disponible
    SPI.transfer(0x00);
  }

  /* Finalise la communication */
  while(!digitalRead(PIN_MP3_DREQ)); // Attend la fin de la communication
  digitalWrite(PIN_MP3_BSYNC, HIGH); // Quitte le mode "données"

  /* Next bitch please ! */
  Serial.println("Done!");
}

Le résultat :

Suite à un probléme technique lors du traitement par youtube, je suis obligé de re-uploader la vidéo.
Un peu de patience d’ici ce soir ça devrait être bon

EDIT: Il n’y aura pas de vidéo de démonstration finalement !
Vous pouvez dire merci aux majors, qui pour 30 secondes de musique viennent me faire chier avec ma vidéo de démo du VS1053 !
Il est hors de question qu’ils se fassent du fric sur mon dos avec des publicités !
J’ai toujours était contre les pub sur le blog, les vidéo, etc … et je ne compte pas changer d’avis !

Ps: Ironie quand tu nous tient, j’ai récupéré ce sample audio de 30 secondes sur … youtube, avec youtube-mp3.org.

EDIT 2: Comme apparemment cette vidéo tient à coeur à mes followers (sur twitter) voici une version découpé sans la partie qui pose probléme :

Publicités

Discussion

5 réflexions sur “[Arduino] Lecteur MP3 avec une breakout VS1053B

  1. salut

    as tu pu faire des essais sur la récupération des informations ID3 des fichiers MP3 ?

    merci

    Publié par Matthieu | 7 janvier 2013, 16 h 44 min
    • Le chipset VS1053 ne gère pas les tags ID3.
      Si tu veut lire le contenu de l’entête ID3 il te faut faire ton propre parser, bonjour la galère …
      Regarde comment marche la taglib mais c’est vraiment relou …

      Publié par skywodd | 7 janvier 2013, 19 h 30 min
      • merci pour ces precisions
        dommage pour les tag c’est quand meme plus sympa de pouvoir s’en servir
        et je viens juste de me rendre compte qu’il n’a pas de sortie spdif mais uniquement i2s
        je crois que j’ai oublié des infos pendant ma recherche
        je n’ai plus qu’ retourner me chercher le bon chip pour decoder mes MP3, les id3 et avoir une sortie spdif 😉

        Publié par Matthieu | 8 janvier 2013, 10 h 21 min
  2. Salut! voila j’aimerais acheter un VSs1053 pour contrôler à partir de mon arduino des fichier son ou j’attribut à chaque boutons une musique.
    mes questions:
    1- tu as branché le shield lcd sur 13,12,11,10 comme indiqué pour la librairy?
    2- pourquoi change tu le perscaler?
    3- il n’est pas indispensable de verifier quel typs de VS est branché?
    4- je voudrais que quand on re-appuis sur le bouton cela coupe la lecture de la musique, pour cela je peux mettre dans le « while(mp3_file.available() > 0) { » un ‘if (button==HIGH){ digitalWrite(PIN_MP3_BSYNC, HIGH)} pour stoper; tu n’y vois pas d’objection ?

    Merci beaucoup !!!

    Publié par Corentin | 15 décembre 2015, 19 h 47 min
    • >> 1- tu as branché le shield lcd sur 13,12,11,10 comme indiqué pour la librairy?

      Oui mais je n’ai pas mis l’écran par dessus le shield. J’utilise juste la partie carte SD.

      >> 2- pourquoi change tu le perscaler?

      Parce que par défaut le prescaler est de 4, soit 4MHz, et que le VS1053 ne fonctionne qu’à 1.75MHz au maximum (voir commentaire juste au dessus de la ligne de code en question).

      >> 3- il n’est pas indispensable de verifier quel typs de VS est branché?

      Oui. Il existe plusieurs versions du chipset avec différent type de décodeurs intégrés (mp3, wav, midi, etc).

      >> 4- je voudrais que quand on re-appuis sur le bouton cela coupe la lecture de la musique, pour cela je peux mettre dans le « while(mp3_file.available() > 0) { » un ‘if (button==HIGH){ digitalWrite(PIN_MP3_BSYNC, HIGH)} pour stoper; tu n’y vois pas d’objection ?

      J’ai plus le code en tête donc je ne saurais pas dire. Le plus simple c’est d’essayer 😉

      Publié par Skywodd | 25 décembre 2015, 17 h 41 min

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Skyduino devient Carnet du Maker

Le site Carnet du Maker remplace désormais le blog Skyduino pour tout ce qui touche à l'Arduino, l'informatique et au DIY.