Geluid uit een PIC via een DAC

Ik ben een tijdje bezig geweest om het geluid van overwegbellen in een PIC te zetten. Dat is ook gelukt, en dit is hoe ik het gedaan heb:
Als eerst moet je het originele geluid hebben uiteraard. Het maakt niet zoveel uit of het mp3, wav of een ander bestandsformaat is want het moet toch omgezet worden. We moeten eerst het geluid bewerken en omzetten in een mono 8bit .wav bestand. Ik heb daarvoor het programma Audacity gebruikt. Open nu je geluidsbestand in Audacity. Links onderin kun je eventueel de sample frequentie wijzigen. Voor een goede kwaliteit heb je een frequentie nodig die groter is dan twee maal de hoogste toon in je geluidsfragment. Ik heb voor mijn fragment dezelfde sample frequentie als van een cd aangehouden: 44100Hz. Als je klaar bent met eventuele geluidsbewerkingen (ruis verwijderen, volume maximaliseren etc) dan komt het belangrijkste punt, het exporteren:

Klik op bestand en vervolgens op exporteren

Klik op bestand en vervolgens op exporteren

Kies dan voor'Andere bestanden zonder compressie' en vervolgens op opties. Zet de header op WAV (Microsoft) en de coderingen op'Unsigned 8 bit PCM'. Klik dan op OK en vervolgens op opslaan

Kies dan voor 'Andere bestanden zonder compressie' en vervolgens op opties. Zet de header op WAV (Microsoft) en de coderingen op 'Unsigned 8 bit PCM'.
Klik dan op OK en vervolgens op opslaan

Het geluid staat nu in het juiste bestandsformaat. Nu moet het nog omgezet worden naar een binaire code die de PIC kan begrijpen. Voor mensen met kennis van MatLab zal dit geen probleem zijn, helaas heb ik die kennis niet. Maar na goed zoeken heb ik het programma WavToCode gevonden. Dit is een gratis programma dat gepubliceerd is onder de bsd licentie. Op de homepagina van de website vind je onderaan wel een donatie knop, misschien iets om eens aan te denken :).
Mocht de link niet werken dan kan je het programma hier downloaden: 

Wav-to-code (156.0 KiB, 55 downloads)

Nadat je WavToCode geïnstalleerd hebt kan je daar het zojuist opgeslagen geluidsbestand weer openen. Omdat we alle geluidsbewerkingen al in Audacity hebben gedaan kunnen we direct op MIX klikken onderaan het scherm:

wavtocode1

Klik vervolgens op file->Save Marked Section As C Code. Je ziet dan bovenstaand scherm. Laat'unsigned' geselecteerd

Klik vervolgens op file->Save Marked Section As C Code. Je ziet dan bovenstaand scherm. Laat 'unsigned' geselecteerd

Het resultaat is een .C bestand. Deze kan je met notepad (kladblok) openen. Je ziet dan zoiets:

 
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 
/**********************************************************************   * Written by WAVToCode * Date: Mon Mar 17 02:26:50 PM * FileName: ar-example.C* Interleaved: Yes * Signed: No * No. of channels: 1 * No. of samples: 10498 * Bits/Sample: 8**********************************************************************/   #define NUM_ELEMENTS 10498   BYTE data[NUM_ELEMENTS] = { 96, 99, 103, 113, 115, 127, 122, 121, // 0-7 119, 117, 119, 121, 123, 118, 124, 129, // 8-15   etc etc

Nu kunnen we in MikroC de code schrijven. De BYTE data[NUM_ELEMENTS] is de array met de geluidsdata die we nodig hebben. Ik vervang BYTE data door de naam die ik aan mijn geluid wil geven, bijvoorbeeld 'audioloop'. De [NUM_ELEMENTS] wordt vervangen door het getal wat achter #define NUM_ELEMENTS staat.

1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 
const  unsigned short audioloop[10498] = {   // gebruik const om het in het programma geheugen te zetten ipv het werkgeheugen // Hier staat de hele array. // Deze plaatst ik hier niet omdat het meer dan 1300 regels in beslag neemt }  void Init(){   OSCCON  = 0;   CCP1CON = 0;   CCP2CON = 0;    TRISB   = 0;  // PORTB is output voor de DAC }   void main() {   Init();  while(1){     for (i=0;i<=10497;i++){       PORTB = audioloop[i];       Delay_us(20); // 20 microsec vertraging voor de volgende sample     }  } }

De for loop zal nu de hele array afgaan en PORTB steeds de updates met de volgende geluidssample.
Ik had een PIC18F2550 gebruikt voor dit experiment met een 4Mhz crystal en de 96Mhz PLL/2 waardoor de PIC op 48Mhz draaide. Later ben ik overgegaan op de PIC18F25K22, deze is goedkoper en kan via een interne oscillator van 16Mhz en 4xPLL op 64Mhz draaien.
Zoals je hiervoor gezien hebt zijn de samples op 44100hz genomen. Dat betekend 1/44100= elke 22,67us een sample. Omdat de PIC nog wat tijd nodig heeft om de handelingen te verrichten is 20us een goed startpunt. In mijn geval moest de PIC ook nog wat ledjes aansturen en poorten checken via een interrupt. Maar dit kan je goed op het gehoor doen. Is het geluid te snel heb je meer vertraging nodig.
Als je geluidsfragment een duidelijk herkenbare vorm heeft dan kan je ook via de scoop zien hoelang de PIC er over doet om de for loop te doorlopen

Het aansluiten van de hardware is redelijk simpel en kan op een breadboard opgezet worden. Ik zal de PIC18F2550 als voorbeeld nemen. Om het digitale signaal van PORTB om te zetten in een analoog geluidssignaal gebruiken we een Digital Analog Converter (kortweg DAC). Ik heb een DAC0832 (gelijkwaardig aan DAC0830) gebruikt en een LF356 als opamp. Verder nog 3 weerstanden en een spanningsreferentie van 2,5V:

schema-dac2

Volgens de datasheet wordt de opamp gevoed met +-15V. Dit kan ook makkelijk met + en -5V gedaan worden omdat het signaal uit de DAC minimaal -2,5V is (als PORTB=0) en maximaal +2,5V zal zijn (als PORTB=255). Vcc moet echter wel minimaal 12V zijn om een goede werking te garanderen. Omdat ik op dat moment alleen 5V voor handen had heb ik een convertor gebruikt, de MCA05D15D. Deze heeft een input van +5V en 2 outputs van -15V en +15V.

Via de jack plug kan je een speakerset aansluiten zoals die bij computers gebruikt worden. Als het goed is krijg je dan zoiets:

  • Ik heb nooit een opleiding gehad in C, Javascript of PHP, alles wat ik weet komt van Youtube en Google.
    Ik weet dat mijn code vaak korter kan, maar ik geef de voorkeur aan leesbaarheid zodat ik na een jaar mezelf nog snap. Mochten er andere fouten in zitten dan hoor ik het graag.