【问题标题】:Arduino I2S sine waveArduino I2S 正弦波
【发布时间】:2017-02-07 19:05:33
【问题描述】:

我正在做一个项目,我想通过组合不同的正弦波来生成(简单)声音。我使用的是 arduino mkrZero,因为它内置了 I2S 接口,而且它似乎有足够的处理能力来满足我的需求。

我已经按照 arduino I2S simpleTone 教程中的方式连接了我的系统:

tutorial 代码运行良好,我从扬声器中得到一个简单的方波音。

现在我已经修改了生成正弦波的代码,有一个用于 sin 函数的查找表以使其足够快:

#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];
}

这也很好用。

但是!

如果我稍微修改一下代码然后写

I2S.write(LR,2);

而不是

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

一切都被打破了,扬声器的声音听起来像horrible scream

来自 I2S 库参考:

说明

将二进制数据写入 I2S 接口。此数据作为样本发送 或一系列样品。

语法

I2S.write(val) // 阻塞

I2S.write(buf, len) // 不阻塞

参数

val:作为单个样本发送的值

buf:作为一系列样本发送的数组

len:缓冲区的长度

退货

byte write() 将返回写入的字节数,尽管读取 该数字是可选的。

我想使用后一个版本的 write 函数,因为它不会阻塞,我可以在播放前一个样本的同时生成新样本。

关于如何使缓冲版本也正常工作的任何想法?

【问题讨论】:

    标签: arduino trigonometry


    【解决方案1】:

    在您的代码中,您将LR 声明为array of 2 shortArduino 中的short 的大小为2 bytes

    当你写这个时:

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

    给定

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

    您的short 值应自动提升以适应int 类型。现在,我对您正在处理的 int 的大小有点不确定,因为我发现的唯一 I2S.h 库是用于 SAMD 板 on which an int has 4 bytes size,但通常在 Arduino 上,int 的大小为 2 bytes。在任何一种情况下,都应该按原样取值,没有问题。

    当你写这个时:

    I2S.write(LR,2);
    

    给定

    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);
        ...
    }
    

    您的 array of 2 short 应转换为 const void *

    来自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;
    }
    

    看起来_doubleBuffer 不知道样本声明大小(您声明它属于16 bits),它只是复制@987654343 @ 从输入到内部缓冲区的字节。

    因此,我猜想真正发生的情况是,当您要求缓冲 2 shorts 时,实际上只复制了 2 bytes


    示例:

    假设您的LR 包含以下值

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

    那么这就是我想象的它发生了

    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
    

    建议的解决方案:

    尝试要求复制4 bytes

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

    【讨论】:

      猜你喜欢
      • 2019-08-13
      • 1970-01-01
      • 2011-01-15
      • 1970-01-01
      • 2012-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多