Skip to content

I2S library using DMA, ready for try out. #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions libraries/CurieAudio/examples/tone_generator/tone_generator.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
// Arduino Zero / Feather M0 I2S audio tone generation example.
// Author: Tony DiCola
//
// Connect an I2S DAC or amp (like the MAX98357) to the Arduino Zero
// and play back simple sine, sawtooth, triangle, and square waves.
// Makes your Zero sound like a NES!
//
// NOTE: The I2S signal generated by the Zero does NOT have a MCLK /
// master clock signal. You must use an I2S receiver that can operate
// without a MCLK signal (like the MAX98357).
//
// For an Arduino Zero / Feather M0 connect it to you I2S hardware as follows:
// - Digital 0 -> I2S LRCLK / FS (left/right / frame select clock)
// - Digital 1 -> I2S BCLK / SCLK (bit / serial clock)
// - Digital 9 -> I2S DIN / SD (data output)
// - Ground
//
// Depends on the Adafruit_ASFcore library from:
// https://github.com/adafruit/adafruit_asfcore
//
// Released under a MIT license: https://opensource.org/licenses/MIT


#ifdef ARDUINO_ARCH_ARC32
#include <I2SOutput.h>
#else
#include "Adafruit_ASFcore.h"
#include "Adafruit_ZeroI2S.h"
#endif


#ifdef ARDUINO_ARCH_ARC32
#define SAMPLE_RATE I2S_22KHZ
#endif

#define SAMPLERATE_HZ 22000 // The sample rate of the audio. Higher sample rates have better fidelity,
// but these tones are so simple it won't make a difference. 44.1khz is
// standard CD quality sound.

#define AMPLITUDE 10000000 // Set the amplitude of generated waveforms. This controls how loud
// the signals are, and can be any value from 0 to 65535. Start with
// a low value like 5000 or less to prevent damaging speakers!

#define WAV_SIZE 256 // The size of each generated waveform. The larger the size the higher
// quality the signal. A size of 256 is more than enough for these simple
// waveforms.


// Define the frequency of music notes (from http://www.phy.mtu.edu/~suits/notefreqs.html):
#define C4_HZ 261.63
#define D4_HZ 293.66
#define E4_HZ 329.63
#define F4_HZ 349.23
#define G4_HZ 392.00
#define A4_HZ 440.00
#define B4_HZ 493.88

// Define a C-major scale to play all the notes up and down.
float scale[] = { C4_HZ, D4_HZ, E4_HZ, F4_HZ, G4_HZ, A4_HZ, B4_HZ, A4_HZ, G4_HZ, F4_HZ, E4_HZ, D4_HZ, C4_HZ };

// Store basic waveforms in memory.
int32_t sine[WAV_SIZE] = {0};
int32_t sawtooth[WAV_SIZE] = {0};
int32_t triangle[WAV_SIZE] = {0};
int32_t square[WAV_SIZE] = {0};

#ifndef ARDUINO_ARCH_ARC32
/***********************
// Create I2S audio transmitter object.
Adafruit_ZeroI2S_TX i2s = Adafruit_ZeroI2S_TX();
***********************/
//// I2S audio transmitter object has been defined in the CurieI2S file

// Little define to make the native USB port on the Arduino Zero / Zero feather
// the default for serial output.
#define Serial SerialUSB
#endif


void generateSine(int32_t amplitude, int32_t* buffer, uint16_t length) {
// Generate a sine wave signal with the provided amplitude and store it in
// the provided buffer of size length.
for (int i=0; i<length; ++i) {
buffer[i] = int32_t(float(amplitude)*sin(2.0*PI*(1.0/length)*i));
}
}
void generateSawtooth(int32_t amplitude, int32_t* buffer, uint16_t length) {
// Generate a sawtooth signal that goes from -amplitude/2 to amplitude/2
// and store it in the provided buffer of size length.
float delta = float(amplitude)/float(length);
for (int i=0; i<length; ++i) {
buffer[i] = -(amplitude/2)+delta*i;
}
}

void generateTriangle(int32_t amplitude, int32_t* buffer, uint16_t length) {
// Generate a triangle wave signal with the provided amplitude and store it in
// the provided buffer of size length.
float delta = float(amplitude)/float(length);
for (int i=0; i<length/2; ++i) {
buffer[i] = -(amplitude/2)+delta*i;
}
for (int i=length/2; i<length; ++i) {
buffer[i] = (amplitude/2)-delta*(i-length/2);
}
}

void generateSquare(int32_t amplitude, int32_t* buffer, uint16_t length) {
// Generate a square wave signal with the provided amplitude and store it in
// the provided buffer of size length.
for (int i=0; i<length/2; ++i) {
buffer[i] = -(amplitude/2);
}
for (int i=length/2; i<length; ++i) {
buffer[i] = (amplitude/2);
}
}


void playWave(int32_t* buffer, uint16_t length, float frequency, float seconds) {
#ifdef ARDUINO_ARCH_ARC32
// push some silence to get things going
I2SOutput.write(0, 0, 1);
#endif

// Play back the provided waveform buffer for the specified
// amount of seconds.
// First calculate how many samples need to play back to run
// for the desired amount of seconds.
uint32_t iterations = seconds*SAMPLERATE_HZ;

// Then calculate the 'speed' at which we move through the wave
// buffer based on the frequency of the tone being played.
float delta = (frequency*length)/float(SAMPLERATE_HZ);

// Now loop through all the samples and play them, calculating the
// position within the wave buffer for each moment in time.

for (uint32_t i=0; i<iterations; ++i) {
uint16_t pos = uint32_t(i*delta) % length;
int32_t sample = buffer[pos];

// Duplicate the sample so it's sent to both the left and right channel.
// It appears the order is right channel, left channel if you want to write
// stereo sound.

#ifdef ARDUINO_ARCH_ARC32
I2SOutput.write(sample, sample);
#else
i2s.write(sample);
i2s.write(sample);
#endif
}

#ifdef ARDUINO_ARCH_ARC32
// Flush the temporary holding buffer before exiting.
I2SOutput.write(0, 0, 1);
#endif

// Serial.print("Iterations: ");
// Serial.println(iterations);
}

void setup() {
// Configure serial port.
Serial.begin(9600);
while(!Serial);

Serial.println("Zero I2S Audio Tone Generator");

#ifdef ARDUINO_ARCH_ARC32

// setup the I2S hardware in the specified mode, sample rate, bits per sample
// and master/slave mode.
I2SOutput.begin(SAMPLE_RATE, I2S_32bit, I2S_MODE_PHILLIPS);

#else
// Initialize the I2S transmitter.
if (!i2s.begin()) {
Serial.println("Failed to initialize I2S transmitter!");
while (1);
}

// Configure I2S transmitter for stereo 16-bit audio at the desired sampling rate.
// Note it's important to use 2 channels for the MAX98357 I2S amp because it gets
// confused with just 1 channel and plays incorrect sounding audio. For mono sound
// just use 2 channels and either duplicate or blank out the second channel sample.
if (!i2s.configure(2, SAMPLERATE_HZ, 16)) {
Serial.println("Failed to configure I2S transmitter for stereo 16-bit 44.1khz audio!");
while (1);
}
#endif

// Generate waveforms.
generateSine(AMPLITUDE, sine, WAV_SIZE);
generateSawtooth(AMPLITUDE, sawtooth, WAV_SIZE);
generateTriangle(AMPLITUDE, triangle, WAV_SIZE);
generateSquare(AMPLITUDE, square, WAV_SIZE);
}

void loop() {
int numOfNotes = sizeof(scale)/sizeof(float);

Serial.println("Sine wave");
for (int i=0; i<numOfNotes; ++i) {
// Play the note for a quarter of a second.
playWave(sine, WAV_SIZE, scale[i], 0.25);
// Pause for a tenth of a second between notes.
delay(100);
}
Serial.println("Sawtooth wave");
for (int i=0; i<numOfNotes; ++i) {
// Play the note for a quarter of a second.
playWave(sawtooth, WAV_SIZE, scale[i], 0.25);
// Pause for a tenth of a second between notes.
delay(100);
}
Serial.println("Triangle wave");
for (int i=0; i<numOfNotes; ++i) {
// Play the note for a quarter of a second.
playWave(triangle, WAV_SIZE, scale[i], 0.25);
// Pause for a tenth of a second between notes.
delay(100);
}
Serial.println("Square wave");
for (int i=0; i<numOfNotes; ++i) {
// Play the note for a quarter of a second.
playWave(square, WAV_SIZE, scale[i], 0.25);
// Pause for a tenth of a second between notes.
delay(100);
}
}
30 changes: 30 additions & 0 deletions libraries/CurieAudio/keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#######################################
# Syntax Coloring Map For CurieAudio
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################
I2SController KEYWORD1
I2SOutputClass KEYWORD1
I2SInputClass KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

begin KEYWORD2
end KEYWORD2
write KEYWORD2
read KEYWORD2
attachCallback KEYWORD2

#######################################
# Instances (KEYWORD2)
#######################################
I2SOutput KEYWORD2
I2SInput KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################
11 changes: 11 additions & 0 deletions libraries/CurieAudio/library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name=CurieAudio
version=1.0
author=Intel
maintainer=Intel
[email protected]
sentence=Curie Audio library for Arduino/Genuino 101
paragraph=
category=Communication
url=http://makers.intel.com
architectures=arc32
core-dependencies=arduino (>=1.6.3)
80 changes: 80 additions & 0 deletions libraries/CurieAudio/src/I2SController.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//***************************************************************
//
// Copyright (c) 2016 Intel Corporation. All rights reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//***************************************************************

#include "I2SController.h"
#include "variant.h"

uint16_t sampleRateLookupTable[MAX_I2S_SIZE][MAX_I2S_RATE] = {
// 8K 12k 22k 24k 44k 48k
{ 125, 83, 45, 42, 23, 21}, // I2S_16bit
{ 83, 56, 30, 28, 15, 14}, // I2S_24bit
{ 63, 42, 23, 21, 11, 10} // I2S_32bit
};

uint8_t sampleSizeLookupTable[MAX_I2S_SIZE] = {16, 24, 32};


I2SController::I2SController()
{
sampleRateMap = (uint16_t *)sampleRateLookupTable;
sampleSizeMap = sampleSizeLookupTable;
}


void I2SController::muxTX(bool enable)
{
int mux_mode = GPIO_MUX_MODE;
if(enable)
{
mux_mode = I2S_MUX_MODE;
}

/* Set SoC pin mux configuration */
SET_PIN_MODE(g_APinDescription[I2S_TXD].ulSocPin, mux_mode);
SET_PIN_MODE(g_APinDescription[I2S_TWS].ulSocPin, mux_mode);
SET_PIN_MODE(g_APinDescription[I2S_TSCK].ulSocPin, mux_mode);

// g_APinDescription is residing in ROM now, no update to the structure
// g_APinDescription[I2S_TXD].ulPinMode = mux_mode;
// g_APinDescription[I2S_TWS].ulPinMode = mux_mode;
// g_APinDescription[I2S_TSCK].ulPinMode = mux_mode;
}


void I2SController::muxRX(bool enable)
{
int mux_mode = GPIO_MUX_MODE;
if(enable)
{
mux_mode = I2S_MUX_MODE;
}

/* Set SoC pin mux configuration */
SET_PIN_MODE(49, mux_mode); //I2S_RXD
SET_PIN_MODE(51, mux_mode); //I2S_RWS
SET_PIN_MODE(50, mux_mode); //I2S_RSCK

// g_APinDescription is residing in ROM now, no update to the structure
// g_APinDescription[I2S_RXD].ulPinMode = mux_mode;
// g_APinDescription[I2S_RWS].ulPinMode = mux_mode;
// g_APinDescription[I2S_RSCK].ulPinMode = mux_mode;
}


Loading