2017-02-07 3 views
1

Ich arbeite an einem Projekt, bei dem ich (einfachen) Klang erzeugen möchte, indem ich verschiedene Sinuswellen kombiniere. Ich benutze ein Arduino mkrZero, da es I2S-Schnittstelle eingebaut hat und es scheint genug Rechenleistung für das zu haben, was ich will.Arduino I2S Sinuswelle

ich mein System genau verdrahtet wie im Tutorial für Arduino I2S simpleTone: enter image description here

Und der tutorial Code funktioniert gut, und ich bekomme eine einfache Rechteckwelle Ton aus dem Lautsprecher.

Jetzt habe ich den Code modifizierte Sinuswelle zu erzeugen, gibt es eine Lookup-Tabelle für die sin-Funktion es schnell genug zu machen:

#include <I2S.h> 

uint16_t isin16[] = { 
0, 1144, 2287, 3430, 4571, 5712, 6850, 7987, 9121, 10252, 11380, 
12505, 13625, 14742, 15854, 16962, 18064, 19161, 20251, 21336, 22414, 
23486, 24550, 25607, 26655, 27696, 28729, 29752, 30767, 31772, 32768, 

33753, 34728, 35693, 36647, 37589, 38521, 39440, 40347, 41243, 42125, 
42995, 43851, 44695, 45524, 46340, 47142, 47929, 48702, 49460, 50203, 
50930, 51642, 52339, 53019, 53683, 54331, 54962, 55577, 56174, 56755, 

57318, 57864, 58392, 58902, 59395, 59869, 60325, 60763, 61182, 61583, 
61965, 62327, 62671, 62996, 63302, 63588, 63855, 64103, 64331, 64539, 
64728, 64897, 65047, 65176, 65286, 65375, 65445, 65495, 65525, 65535, 
}; //0-90 


const int sincount = 2; 
int freqs[] = {50*360,51*360}; 
float amps[] ={0.1,0.1}; 

const int sampleRate = 8000; // sample rate in Hz 

short sample = 0; 
double t = 0; 
double dt = 1.0/(1.0*sampleRate); 

short LR[] = {0,0}; 

void setup() { 
Serial.begin(115200); 
    // start I2S at the sample rate with 16-bits per sample 
    if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) { 
    while (1); // do nothing 
    } 
} 

void loop() { 
    sample = 0; 
    for(int n= 0;n<sincount;n++) 
    { 
     sample += fSin(freqs[n]*t)*amps[n]; 
    } 
    t += dt; 
    LR[0] = sample; 
    LR[1] = sample; 

    I2S.write(LR[0]);//left channel 
    I2S.write(LR[1]);//right channel 
} 


float fSin(long x) 
{ 
boolean pos = true; // positive - keeps an eye on the sign. 
if (x < 0) 
{ 
    x = -x; 
    pos = false; 
} 
if (x >= 360) x %= 360; 
if (x > 180) 
{ 
    x -= 180; 
    pos = !pos; 
} 
if (x > 90) x = 180 - x; 
if (pos) return isin16[x]; // = /65535.0 
return isin16[x]; 
} 

Dies funktioniert auch gut.

ABER!

Wenn ich den Code ein wenig ändern und ich schreibe

I2S.write(LR,2); 

statt

I2S.write(LR[0]);//left channel 
I2S.write(LR[1]);//right channel 

Alles nur bricht, klingt der Ton aus dem Lautsprecher wie ein horrible scream

Von der I2S-Bibliotheksreferenz:

Beschreibung

Schreibt binäre Daten an die I2S-Schnittstelle. Diese Daten werden als eine Probe oder eine Reihe von Proben gesendet.

Syntax

I2S.write(val) // Blockierung

I2S.write(buf, len) // nicht blockiert

Parameter

val: ein Wert als eine einzige Probe zu senden

buf :

len ein Array als eine Reihe von Proben zu senden: die Länge des Puffers

Returns

Byte-write() wird die Anzahl der Bytes zurück geschrieben, wenn das Lesen diese Anzahl ist optional .

Ich würde gerne die letztere Version der Schreibfunktion verwenden, weil sie nicht blockiert und ich kann neue Samples erzeugen, während die vorherigen spielen.

Irgendwelche Ideen, wie die gepufferte Version funktioniert auch?

Antwort

2

in Ihrem Code erklären Sie LR als array of 2 short, ein short in Arduino Größe 2 bytes hat.

Wenn Sie schreiben dies:

I2S.write(LR[0]); //left channel 
I2S.write(LR[1]); //right channel 

gegeben

size_t I2SClass::write(uint8_t data) 
{ 
    return write((int32_t)data); 
} 

size_t I2SClass::write(int sample) 
{ 
    return write((int32_t)sample); 
} 

Ihre short Werte automatisch in den int Typ passen sollte gefördert werden. Nun, ich bin ein bisschen unsicher von der Größe des int Sie es zu tun, weil die einzige I2S.h Bibliothek I ist für die, on which an int has 4 bytes sizeSAMD Bord gefunden, aber normalerweise auf Arduino die eine int hat Größe von 2 bytes. In beiden Fällen sollte der Wert unverändert übernommen werden.

Wenn Sie schreiben dies:

I2S.write(LR,2); 

gegeben
size_t I2SClass::write(const uint8_t *buffer, size_t size) 
{ 
    return write((const void*)buffer, size); 
} 

size_t I2SClass::write(const void *buffer, size_t size) 
{ 
    ... 
    written = _doubleBuffer.write(buffer, size); 
    ... 
} 

Ihre array of 2 short sollte const void * gegossen werden.

Von diesem Code in I2SDoubleBuffer:

size_t I2SDoubleBuffer::write(const void *buffer, size_t size) { 
    size_t space = availableForWrite(); 

    if (size > space) { 
    size = space; 
    } 

    if (size == 0) { 
    return 0; 
    } 

    memcpy(&_buffer[_index][_length[_index]], buffer, size); 

    _length[_index] += size; 

    return size; 
} 

es wie _doubleBuffer aussieht, ist nicht bekannt, die erklärte Größe einer Probe (erklärt Sie es von 16 bits zu sein), es nur Kopien size Bytes von der Eingabe zum internen Puffer.

Daher ich denke, das wirklich das, was passiert ist, dass, wenn Sie für 2 shorts fragen nur 2 bytes tatsächlich kopiert gepuffert werden.


Beispiel:

Angenommen, Ihre LR enthält die folgenden Werte

short LR[2] = { 0x89AB, 0xCDEF }; 

dann das ist, was ich vorstellen, es passiert

I2S.write(LR[0]); // actual left sample is 0x89AB 
I2S.write(LR[1]); // actual right sample is 0xCDEF 

I2S.write(LR, 2); // actual left sample is 0x89AB 
        // 0xCDEF is not copied 
/* next loop iteration */ 
I2S.write(LR, 2); // actual right sample is 0x89AB 
        // 0xCDEF is not copied 

Vorgeschlagene Lösung:

Versuchen für 4 bytes fragen kopiert werden:

I2S.write(LR, 4); // actual left sample is 0x89AB 
        // actual right sample is 0xCDEF