AdaFruit Wave Shield WaveHC 6
Avoir plus de RAM et de Flash!
Note MCHobby: Les plateformes Arduino sont maintenant distribuée avec un ATmega328.
Cette section n'est vraiment pertinente que si vous ne possédez pas d'ATmega328.
Avant d'essayer de jouer de l'audio, vous aurez besoin de faire un peu de place
la mémoire RAM d'Arduino, cela évitera que cela se termine en vilain
dépassement de pile (stack-overflow). Manquer de mémoire RAM est difficile à déboguer et frustrant, d'autant plus si vous disposez d'un ATMega168
Suivez ces instructions (anglais) pour savoir comment avoir plus
de RAM en réduisant la taille de la mémoire tampon de la librairie série (Serial library).
Vous n'aurez pas besoin de faire cela si vous avez un ATmega328.
Notez que la librairie est vraiment grosse (approximativement 10K). Si vous voulez faire beaucoup plus avec Arduino, il est vivement conseillé de faire un Upgrade vers un ATmega328. Ce shield a été développé en prévoyant la disponibilité d'un ATmega328.
Introduction à play6_hc.pde
Ceci est un tutoriel pour la librairie waveHC. Vous pouvez télécharger le fichier play6_hc.pde play6_hc.pde ici
Ce tutoriel un détaillé et un peu intimidant. Mais attardez vous puisque la plupart du code peut être dupliqué d'un sketch à l'autre.
Assurez vosu que vous avez installé la librairie en la téléchargeant depuis le lien ci dessus et placé dans un sous-répertoire WaveHC de votre répertoire "libraries".
Nous avons placé ce sketch en bas de cet article au cas où vous en auriez besoin.
Initialiser la carte
Les tâches complexes nécessaires au rendu musical est assuré par Arduino. Cela nous épargne d'avoir recours à un décodeur MP3ou autre circuit dédicacé. Cela signifie plus de contrôle et de flexibilité mais aussi pas de firmware!
Faisons donc un tour d'horizon du sketch "dapHC.pde". C'est un sketch de type "Digital Audio Player" (dap) utilisant la librairie Wave HC. Nous avions écrit ce projet pour l'utilisé avec notre librairie Adafruit AF_Wave mais Mr. Fat16 à réalisé un fantastique travail en réécrivant notre code en le rendant plus rapide, plus petit et meilleur. Donc nous allons utiliser sa librairie pour ce tutoriel :)
Téléchargez le sketch dapHC.pde et prenez le temps de le lire. La première chose dont nous avons besoin sont quelques objets. La partie la plus difficile est de dialoguer avec la carte. Toutes les cartes sont fabriquée et formattées différemment (juste un peu). Et le formatage repose sur différentes couches - le format de la carte - Le format de la partition et le formatage du système de fichier.
Au début du sketch nous avons l' #include qui incorpore le fichier d'entête de la librairie (fichier header) files ainsi qu'un objet card qui stocke les information concernant la carte, un autre vol relatif à la partition (partition volume) et le système de fichier root.
Nous avons aussi un objet f qui maintient les information du fichier coutant et un objet wave qui gère un simple fichier wave.
#include "WaveUtil.h" #include "WaveHC.h" SdReader card; // Objet qui maintient les information sur la carte SD FatVolume vol; // Maintient les informations sur la partition de la carte FatReader root; // Maintient les informations sur le système de fichier de la carte FatReader f; // Maintient les informations sur le fichier que nous jouons WaveHC wave; // C'est le seul objet (audio) wave, puisque nous jouons un seul morceau à la fois #define DEBOUNCE 100 // déparasitage bouton
La première chose à faire est d'initialiser la carte SD pour faire des lectures. C'est un processus qui se réalise en plusieurs étapes. Dans la fonction setup vous pouvez voir les différentes vérification qui sont faites par le processus d'initialisation.
Voici les étapes:
- Initialisation et affichage sur la connexion série pour indiquer que nous démarrons (OPTIONNEL)
- Vérifier la quantité de mémoire disponible après avoir utilisé la mémoire tampon pour stocker les données audio du Wave, faite en sorte qu'il reste plus de 100 octets (bytes) et gardez un oeil dessus si vous modifiez votre code. Ce test peut être enlevé, il n'est destiné qu'a votre information (OPTIONNEL)
- Initialise les pin modes pour le DAC (lignes de contrôles du DAC). Cela ne devrait pas être modifié à moins que vous ayez aussi modifié la librairie. Il est certainement préférable de les garder tels quels.
- Initialiser la carte SD et vérifier qu'elle réponde. Nous essayons de l'adresser à 8MHz. Si vous disposez d'un Shield Wave 1.0 vous pourriez avoir besoin d'utiliser le mode 4MHz (dé-commentez/re-commentez la ligne appropriée en fonction de la méthode utilisée). Si la carte ne s'initialise pas, alors afficher une erreur et arrêter.
- Autoriser la lecture de bloc partiel. Certaines cartes SD n'apprécient pas vraiment. Commenter en priorité cette ligne si vous avez des problèmes! (OPTIONNEL)
- Essayer de trouver la partition FAT dans les 5 premiers slots. Vous avez bien formatez votre carte en FAT n'est-ce pas? S'il ne peut pas trouver la partition FAT il affichera un message d'erreur. En cas de problème, assurez vous que vous avez bien formaté la carte en FAT (quitte à re-formater la carte).
- Affiche le type de partition FAT qui a été trouvée (OPTIONNEL)
- Essaye d'ouvrir le répertoire racine. Si cela ne fonctionne pas, c'est que quelque-chose s'est mal déroulé au cours du formatage. Essayer de formater la carte une fois de plus!
void setup() { // intialisation du port série Serial.begin(9600); putstring_nl("WaveHC avec 6 boutons"); putstring("Free RAM: "); // Aide au débogage, ce n'est pas bien de ne plus avoir de RAM Serial.println(freeRam()); // Si c'est en dessous de 150 octets (bytes) nous aurons des soucis! // Initialise les pins de sortie pour le contrôle DAC control. // Ces pins sont aussi définies dans la librairie pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); // LED sur pin13 pinMode(13, OUTPUT); // Activer la résistance Pull-Up sur les broches des boutons (entrée analogiques) digitalWrite(14, HIGH); digitalWrite(15, HIGH); digitalWrite(16, HIGH); digitalWrite(17, HIGH); digitalWrite(18, HIGH); digitalWrite(19, HIGH); // if (!card.init(true)) { //SPI cadencé à 4 MHz (à utiliser si 8MHz ne fonctionne pas) if (!card.init()) { //SPI cadencé ) 8 MHz (le plus rapide par défaut!) putstring_nl("Echec init. carte!"); // Qlque chose va de travers, affichons donc pourquoi sdErrorCheck(); while(1); // Et 'arrêter' (ne rien faire!) } // Activer l'optimisation de lecture - Certaines cartes ne répondent pas. // Désactivez la ligne suivante si vous rencontrez des problèmes card.partialBlockRead(true); // Recherhe de la partition FAT! uint8_t part; for (part = 0; part < 5; part++) { // Recherche dans les 5 premiers slots if (vol.init(card, part)) break; // Trouvez? alors sortir de la boucle } if (part == 5) { // Pas trouvé de partition FAT? :( putstring_nl("Partition FAT invalide!"); sdErrorCheck(); // Qlque chose va de travers, afficher pourquoi while(1); // Et 'Arrêter' - (ne rien faire)! } // Indiquons à l'utilisateur ce que nous avons trouvé putstring("Utiliser partition "); Serial.print(part, DEC); putstring(", type de FAT"); Serial.println(vol.fatType(),DEC); // FAT16 ou FAT32? // Ourvir le répertoire racine if (!root.openRoot(vol)) { putstring_nl("Echec ouverture rep. racine!"); // Qlqchose ne fonctionne pas, while(1); // alors 'arrêter' (ne plus rien faire)! } // Whoaw! Nous avons réaliser toutes les étapes. putstring_nl("Prêt!"); dirLevel = 0; }
Interfaçage des boutons
Nous voulons jouer un son à chaque fois qu'un bouton est pressé Nous allons utiliser une fonction nommée check_switches() qui passe les 6 boutons en revue (digital de 14 à 20) pour voir si ils ont été pressés. Si c'est le ca, nous complètement jouons le fichier SOUND1.WAV (par exemple). La fonction que nous appelons pour jouer le morceau s'appelle playcomplete() et nous passons le nom du fichier Wave en paramètre (entre guillemet, comme ci-dessous).
void loop() { // retirez le commentaire de la ligne ci-dessous pour voir si la loop() fonctionne. // Si la loop ne fonctionne pas, c'est qu'il y a un problème d’initialisation de la carte SD. //putstring("."); switch (check_switches()) { case 1: playcomplete("SOUND1.WAV"); break; case 2: playcomplete("SOUND2.WAV"); break; case 3: playcomplete("SOUND3.WAV"); break; case 4: playcomplete("SOUND4.WAV"); break; case 5: playcomplete("SOUND5.WAV"); break; case 6: playcomplete("SOUND6.WAV"); } } byte check_switches() { static byte previous[6]; static long time[6]; byte reading; byte pressed; byte index; pressed = 0; for (byte index = 0; index < 6; ++index) { reading = digitalRead(14 + index); if (reading == LOW && previous[index] == HIGH && millis() - time[index] > DEBOUNCE) { // switch pressed time[index] = millis(); pressed = index + 1; break; } previous[index] = reading; } // return switch number (1 - 6) return (pressed); }
les fonctions Playcomplete et Playfile
Voici le code correspondant à l'ouverture de fichier et au rendu audio de celui-ci.
Playcomplete est une fonction vraiment simple. Elle appelle la fonction qui commence à jouer le fichier audio et qui démarre un boucle d'attente (boucle qui ne fait rien).
Playfile est une fonction importante. Elle trouve le fichier et le joue.
Voici plus de détails sur la fonction Playfile:
- Elle vérifie si un fichier est déjà entrain d'être joué. Si c'est le cas, elle le stoppe.
- Elle ouvre ensuite le répertoire racine (root) et recherche le fichier avec le nom attendu. Si le fichier n'est pas trouver alors l'exécution de la fonction est interrompu (instruction "return")
- Si le fichier est trouvé, elle essaye de l'ouvrir avec l'objet Wave (qui vérifie si l'entête wave est bien présente). Si ce n'est pas le cas alors l'exécution de la fonction est interrompu (instruction "return")
- Si l'opération fonctionne alors la fonction commence à jouer le fichier
// Joue un fichier entier jusqu'à la fin sans faire de pause. void playcomplete(char *name) { // Appelle la fonction (dite "helper") pour trouver et jouer le fichier playfile(name); while (wave.isplaying) { // Ne rien faire tant que le fichier est joué } // Maintenant, le fichier est terminé } void playfile(char *name) { // Vérifier si l'objet wave fait déjà quelque chose if (wave.isplaying) {// Joue déja un morceur, on l'arrête! wave.stop(); // arrêter le morceau } // Chercher dans le répertoire racine et ouvrir le fichier if (!f.open(root, name)) { putstring("Echec ouverture fichier "); Serial.print(name); return; } // OK, lire le fichier et le transformer en objet Wave if (!wave.create(f)) { putstring_nl("Fichier WAV invalide"); return; } // Tout s'est bien passé! Commence la lecture wave.play(); }
play6_hc.pde
Note de MCHobby:
Voici le contenu complet du projet.
Cette fois, les commentaires ne sont pas traduits. Pour plus de détails sur le fonctionnement, vous pouvez vous référez au explications ci-dessus où les commentaires ont étés traduits.
#include <FatReader.h> #include <SdReader.h> #include <avr/pgmspace.h> #include "WaveUtil.h" #include "WaveHC.h" SdReader card; // This object holds the information for the card FatVolume vol; // This holds the information for the partition on the card FatReader root; // This holds the information for the filesystem on the card FatReader f; // This holds the information for the file we're play WaveHC wave; // This is the only wave (audio) object, since we will only play one at a time #define DEBOUNCE 100 // button debouncer // this handy function will return the number of bytes currently free in RAM, great for debugging! int freeRam(void) { extern int __bss_end; extern int *__brkval; int free_memory; if((int)__brkval == 0) { free_memory = ((int)&free_memory) - ((int)&__bss_end); } else { free_memory = ((int)&free_memory) - ((int)__brkval); } return free_memory; } void sdErrorCheck(void) { if (!card.errorCode()) return; putstring("\n\rSD I/O error: "); Serial.print(card.errorCode(), HEX); putstring(", "); Serial.println(card.errorData(), HEX); while(1); } void setup() { // set up serial port Serial.begin(9600); putstring_nl("WaveHC with 6 buttons"); putstring("Free RAM: "); // This can help with debugging, running out of RAM is bad Serial.println(freeRam()); // if this is under 150 bytes it may spell trouble! // Set the output pins for the DAC control. This pins are defined in the library pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); // pin13 LED pinMode(13, OUTPUT); // enable pull-up resistors on switch pins (analog inputs) digitalWrite(14, HIGH); digitalWrite(15, HIGH); digitalWrite(16, HIGH); digitalWrite(17, HIGH); digitalWrite(18, HIGH); digitalWrite(19, HIGH); // if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you if (!card.init()) { //play with 8 MHz spi (default faster!) putstring_nl("Card init. failed!"); // Something went wrong, lets print out why sdErrorCheck(); while(1); // then 'halt' - do nothing! } // enable optimize read - some cards may timeout. Disable if you're having problems card.partialBlockRead(true); // Now we will look for a FAT partition! uint8_t part; for (part = 0; part < 5; part++) { // we have up to 5 slots to look in if (vol.init(card, part)) break; // we found one, lets bail } if (part == 5) { // if we ended up not finding one :( putstring_nl("No valid FAT partition!"); sdErrorCheck(); // Something went wrong, lets print out why while(1); // then 'halt' - do nothing! } // Lets tell the user about what we found putstring("Using partition "); Serial.print(part, DEC); putstring(", type is FAT"); Serial.println(vol.fatType(),DEC); // FAT16 or FAT32? // Try to open the root directory if (!root.openRoot(vol)) { putstring_nl("Can't open root dir!"); // Something went wrong, while(1); // then 'halt' - do nothing! } // Whew! We got past the tough parts. putstring_nl("Ready!"); } void loop() { //putstring("."); // uncomment this to see if the loop isnt running switch (check_switches()) { case 1: playcomplete("SOUND1.WAV"); break; case 2: playcomplete("SOUND2.WAV"); break; case 3: playcomplete("SOUND3.WAV"); break; case 4: playcomplete("SOUND4.WAV"); break; case 5: playcomplete("SOUND5.WAV"); break; case 6: playcomplete("SOUND6.WAV"); } } byte check_switches() { static byte previous[6]; static long time[6]; byte reading; byte pressed; byte index; pressed = 0; for (byte index = 0; index < 6; ++index) { reading = digitalRead(14 + index); if (reading == LOW && previous[index] == HIGH && millis() - time[index] > DEBOUNCE) { // switch pressed time[index] = millis(); pressed = index + 1; break; } previous[index] = reading; } // return switch number (1 - 6) return (pressed); } // Plays a full file from beginning to end with no pause. void playcomplete(char *name) { // call our helper to find and play this name playfile(name); while (wave.isplaying) { // do nothing while its playing } // now its done playing } void playfile(char *name) { // see if the wave object is currently doing something if (wave.isplaying) {// already playing something, so stop it! wave.stop(); // stop it } // look in the root directory and open the file if (!f.open(root, name)) { putstring("Couldn't open file "); Serial.print(name); return; } // OK read the file and turn it into a wave object if (!wave.create(f)) { putstring_nl("Not a valid WAV"); return; } // ok time to play! start playback wave.play(); }
Source: Wave Shield créé par LadyAda pour AdaFruit. Crédit: AdaFruit.com.
Traduit par Meurisse D. pour MCHobby.be.
Traduit avec l'autorisation d'AdaFruit Industries - Translated with the permission from Adafruit Industries - www.adafruit.com
Toute référence, mention ou extrait de cette traduction doit être explicitement accompagné du texte suivant : « Traduction par MCHobby (www.MCHobby.be) - Vente de kit et composants » avec un lien vers la source (donc cette page) et ce quelque soit le média utilisé.
L'utilisation commercial de la traduction (texte) et/ou réalisation, même partielle, pourrait être soumis à redevance. Dans tous les cas de figures, vous devez également obtenir l'accord du(des) détenteur initial des droits. Celui de MC Hobby s'arrêtant au travail de traduction proprement dit.