Arduino Wiring & Test

The classic ATmega328P-based Arduino's like the UNO and Metro 328 don't have I2S interfaces, so you can't use this breakout with them

But the newer ATSAMD21-based boards like the Zero, Metro M0, Feather M0 can! (Note, Gemma M0 & Trinket M0 do not have I2S pins available). And so can the even newer ATSAMD51-based boards like the Metro M4 and Feather M4.

To use I2S with M0 or M4 boards, you'll need to install the Adafruit Zero I2S library. It is available through the Library Manager. You can search for (see below) and then just click the install button.

Wiring

Wiring connections are the same as those used for CircuitPython. So go to the CircuitPython Wiring & Test page to see how to wire the breakout for your specific board.

Basic Test

To test things out, try running the demo below. It comes with the library installation, so you can find it by going to:

File -> Examples ->  Adafruit Zero I2S Library -> basic

Be sure to change this line:

Download: file
Adafruit_ZeroI2S i2s(0, 1, 9, 2);

to match the pins used for your setup. If you've wired as shown in this guide, then you can try using the default pins by changing that line to this:

Download: file
Adafruit_ZeroI2S i2s;
Temporarily unable to load content:

DMA Test

The basic test above created the output directly by using the i2s.write() function in a loop. Another approach is to use DMA to generate the output. With this approach, you do some initial setup to configure the DMA engine for playback. It can then take care of generating the output in the background allowing you to do other things in your code.

To take this approach, you will need to install the Zero DMA library. You can do that through the Library Manager:

And then you can use the DMA example found in the Zero I2S library:

File -> Examples ->  Adafruit Zero I2S Library -> dma

#include <Adafruit_ZeroI2S.h>
#include <Adafruit_ZeroDMA.h>
#include "utility/dma.h"
#include <math.h>

/* max volume for 32 bit data */
#define VOLUME ( (1UL << 31) - 1)

/* create a buffer for both the left and right channel data */
#define BUFSIZE 256
int data[BUFSIZE];

Adafruit_ZeroDMA myDMA;
ZeroDMAstatus    stat; // DMA status codes returned by some functions

Adafruit_ZeroI2S i2s;

void dma_callback(Adafruit_ZeroDMA *dma) {
  /* we don't need to do anything here */
}

void setup()
{
  Serial.begin(115200);
  //while(!Serial);                 // Wait for Serial monitor before continuing

  Serial.println("I2S output via DMA");

  int *ptr = data;

  /*the I2S module will be expecting data interleaved LRLR*/
  for(int i=0; i<BUFSIZE/2; i++){
      /* create a sine wave on the left channel */
        *ptr++ = sin( (2*PI / (BUFSIZE/2) ) * i) * VOLUME;

        /* create a cosine wave on the right channel */
        *ptr++ = cos( (2*PI / (BUFSIZE/2) ) * i) * VOLUME;
  }

  Serial.println("Configuring DMA trigger");
  myDMA.setTrigger(I2S_DMAC_ID_TX_0);
  myDMA.setAction(DMA_TRIGGER_ACTON_BEAT);

  Serial.print("Allocating DMA channel...");
  stat = myDMA.allocate();
  myDMA.printStatus(stat);

  Serial.println("Setting up transfer");
    myDMA.addDescriptor(
      data,                    // move data from here
#if defined(__SAMD51__)
      (void *)(&I2S->TXDATA.reg), // to here (M4)
#else
      (void *)(&I2S->DATA[0].reg), // to here (M0+)
#endif
    BUFSIZE,                      // this many...
      DMA_BEAT_SIZE_WORD,               // bytes/hword/words
      true,                             // increment source addr?
      false);
  myDMA.loop(true);
  Serial.println("Adding callback");
  myDMA.setCallback(dma_callback);

  /* begin I2S on the default pins. 24 bit depth at
   * 44100 samples per second
   */
  i2s.begin(I2S_32_BIT, 44100);
  i2s.enableTx();

  stat = myDMA.startJob();
}

void loop()
{
  Serial.println("do other things here while your DMA runs in the background.");
  delay(2000);
}
This guide was first published on Nov 21, 2017. It was last updated on Nov 21, 2017. This page (Arduino Wiring & Test) was last updated on Jul 17, 2019.