PDA

View Full Version : Teensy3 I2S with DMA



hpyle
01-12-2013, 01:30 PM
I'm wading through the datasheet, the AN4520 application note (http://cache.freescale.com/files/.../doc/app_note/AN4520.pdf), the AN4369 application note (http://cache.freescale.com/files/32bit/doc/app_note/AN4369.pdf) and its code samples (http://cache.freescale.com/files/32bit/doc/app_note/AN4369SW.zip)...

Lots to learn, and it seems quite a lot of work to initialize.

Does anyone have working Teensy3 code for I2S, using a ping-pong DMA interrupt, already?

hpyle
01-13-2013, 08:36 PM
Beware the AN4369 code sample - it's for the K60 chip, and all the registers are different.


I've put together most of the headers for Transmit, and some code that "should work", but doesn't. The driving sketch isn't talking to a codec or anything. It's just trying to initialize. It doesn't crash... but doesn't call back when the buffer is empty. So far I can't tell what's wrong at all. But if anyone else wants to dig in, feel free!

Most of i2s.h should end up in the mk20dx128.h eventually. For now, I also patched a few of the DMA defs in mk20dx128.h.

https://github.com/hughpyle/teensy-i2s

hpyle
01-20-2013, 02:55 PM
I2C to the Wolfson codec is working (using the Mikro proto board). I2S is **partially** working. The current version is using the FIFO directly -- DMA not working yet. Internally-generated bit clock and frame clock signals are looking good on the oscilloscope. Sync to external should be OK too, although I'm testing with internal clocks.

https://github.com/hughpyle/teensy-i2s

I could use some help with the next steps, if anyone out there is minded to!

Even though the code is pushing data into the TX register when the FIFO needs data, and it appears to be transmitting (because the FIFO keeps calling back to say it wants more), I don't see any signal on the data line. I've tried data on pin 3 (PTA12) as well as 22 (PTC1) with no effect. I'm stumped. :(

hpyle
01-21-2013, 01:04 AM
Moderate success!! Codec-as-slave sounds very distorted. Codec-as-master sounds pure but fades out immediately. Nevertheless, it's doing *something* end-to-end and producing sound from the headphones. Code is checked in on github.

sumotoy
01-22-2013, 10:15 PM
WOW ;) Just found out this! Extremely interesting! I've worked a lot with many wolfson chip (expecially the Wm8805 and it's damn incomplete readout protocol) and found that it's possible communicate in I2S with Tennsy3 it's like a dream to me. I've succesfully communicate via I2C and SPI (I prefere SPI because it's higher freq intefrere less with audio stuff I have around this chip) with WM8805 and DACs, I can share some code if you need.

hpyle
01-23-2013, 10:49 AM
That's great! I'd like to see code to drive the WM8805 -- I have no experience with it, but I do have a SRC4382 waiting for a project that needs S/PDIF (it may be possible to build a Teensy-powered digital crossover).

My first project with I2S is just the transmit side, to make a small MIDI synth. The code shows signs of getting close, but debugging "why does that sound like static" is taking a while!

hpyle
01-23-2013, 01:04 PM
And... it works! Current code on github (https://github.com/hughpyle/teensy-i2s).

Paul, will you take a big patch to mk20dx128.h for all the register flags? My version is here (https://raw.github.com/hughpyle/teensy-i2s/master/mk20dx128.h).

sumotoy
01-23-2013, 09:06 PM
Really great work! I've ordered a chip to test it! Here's some code to work with WM8805 via SPI, if you need help with circuit I can post it.



/*
Wolfson WM8805 Digital Receiver SPI routines, extract
coded by sumotoy

NOTES:
WM8805 it's actually one of the best digital audio receiver chip around
but it's far to be perfect, for example it's not easy to get exaustive
infos about it's current working audio link connection. For example I cannot find a way
to determine if it's linked to 48K or 44.1K since the 2 freq are identified
with the same register out. To determine if chip it's linked or not to an audio stream, best
way to me it's check the GPO1 pin of WM chip and only then send queries to it
for read registers and check the quality and all infos.
WM8805 can communicate via I2C or SPI, the 2 ways are slight different (check
datasheet) and need slight different pin setup.
The following routines are for SPI and I checked and are working on
a ATMEGA1284P (arduino ide 1.0.3) If you need help with WM8805 circuit
I can post my setup for SPI and I2C.
*/

/* ----------------------WM8805 ------------------*/
#define WMRST 0x00
#define DEVID2 0x01
#define DEVREV 0x02
#define PLL1 0x03
#define PLL2 0x04
#define PLL3 0x05
#define PLL4 0x06
#define PLL5 0x07
#define PLL6 0x08
#define SPDMODE 0x09
#define INTMASK 0x0A
#define INTSTAT 0x0B
#define SPDSTAT 0x0C
#define RXCHAN1 0x0D
#define RXCHAN2 0x0E
#define RXCHAN3 0x0F
#define RXCHAN4 0x10
#define RXCHAN5 0x11
#define SDPTX1 0x12
#define SDPTX2 0x13
#define SDPTX3 0x14
#define SDPTX4 0x15
#define SDPTX5 0x16
#define GPO01 0x17
#define GPO23 0x18
#define GPO45 0x19
#define GPO67 0x1A
#define AIFTX 0x1B
#define AIFRX 0x1C
#define SPDXR1 0x1D
#define PWRDN 0x1E
//related to cpu
#define WMCS 10 //cs pin cpu side

byte wm_PLL6 = 0b00011000;
byte wm_PWRDN = 0b00000000;




//powerup chip
void WMPowerUp(){
WMwrite(PWRDN,wm_PWRDN);
}

//enable/disable I2S interface
void WMI2SInterface(byte state){
if (state){
WMwrite(PWRDN,0x00);//power up I2S interface on WM8805
}
else {
WMwrite(PWRDN,0x10);//power down I2S interface on WM8805
}
}

//write to wm8805
void WMwrite(byte reg, byte data){
digitalWrite(WMCS,LOW);
//Bit 7 t 0
SPI.transfer(reg &= ~(1<<7));
SPI.transfer(data);
// stop transmitting
digitalWrite(WMCS, HIGH);
}

//read from wm8805
int WMread(byte reg){
digitalWrite(WMCS,LOW);
//bit 7 to 1
SPI.transfer(reg |= (1<<7));
int _data = SPI.transfer(0x0);
digitalWrite(WMCS,HIGH);
return _data;
}


//select source (I know this is the worst way
//to do but easy to understand)
void WMsource(byte src)
{
switch(src){
case 0://000
bitClear(wm_PLL6,0);
bitClear(wm_PLL6,1);
bitClear(wm_PLL6,2);
break;
case 1://001
bitSet(wm_PLL6,0);
bitClear(wm_PLL6,1);
bitClear(wm_PLL6,2);
break;
case 2://010
bitClear(wm_PLL6,0);
bitSet(wm_PLL6,1);
bitClear(wm_PLL6,2);
break;
case 3://011
bitSet(wm_PLL6,0);
bitSet(wm_PLL6,1);
bitClear(wm_PLL6,2);
break;
case 4://100
bitClear(wm_PLL6,0);
bitClear(wm_PLL6,1);
bitSet(wm_PLL6,2);
break;
case 5://101
bitSet(wm_PLL6,0);
bitClear(wm_PLL6,1);
bitSet(wm_PLL6,2);
break;
case 6://110
bitClear(wm_PLL6,0);
bitSet(wm_PLL6,1);
bitSet(wm_PLL6,2);
break;
case 7://111
bitSet(wm_PLL6,0);
bitSet(wm_PLL6,1);
bitSet(wm_PLL6,2);
break;
}
WMwrite(PLL6,wm_PLL6);
}

/*
Example:
WMwrite(WMRST,0x00);//reset
WMwrite(SPDXR1,B1000011);//reset
//wait a little, wm it's slow to initialize
WMwrite(SPDMODE,0xF4);//SPDIF modes (coax/TTL)
WMwrite(SDPTX1,0x04);//SPDIF TX copyright = no
WMwrite(SDPTX4,0x31);//SPDIF TX source
WMwrite(GPO01,0x7A);//GPO Config - GPO0 = DEEMPH; GPO1 = UNLOCK// NEW!
WMwrite(AIFTX,0x0A);//AIF TX = 24-bit I2S
WMwrite(AIFRX,0x4A);//AIF = master mode
WMsource(1);//select input 1
WMwrite(PWRDN,wm_PWRDN);

//check if chip it's connected
if (WMread(DEVID2) == 0x88) it's connected and working
*/

loglow
01-29-2013, 11:55 PM
And... it works!

This is fantastic, as I'm using the Teensy3 and require I2S support. I've been working on other aspects of the project (including getting setup with the AIC3204 (http://www.ti.com/product/tlv320aic3204) codec that I'm using), while eagerly awaiting an I2S driver. Using your modified header and referencing your example code, I've at the very least gotten what appears to be a suitable 12.288 MHz clock to drive the codec with. I've also experimented with reassigning Teensy3 pins (http://forum.pjrc.com/threads/17288) to make use of their alternate I2S functions. I still have a ridiculous amount to learn though.

Anyway, please let me know if there's anything I can do to assist/expedite the development of solid I2S drivers, even if that means just testing them.

Thanks again, -Dan

hpyle
01-30-2013, 01:27 PM
That's great! I hope you find the library useful. If you're rolling your own, the main trick is to not set the "transmit enable" flag until everything else is set up ;-)
You may find that my clock setup doesn't work for your application. It was done pretty much by trial and error with the Wolfson codec and a cheap single-channel oscilloscope... the only reliable way I could run a data stream was to set up as 32-bit data. YMMV.

Do feel free to fork my library/example code. I'll be working on this very much part-time -- I expect my first project could take months, no particular rush -- so even if you send me pull requests with a big refactoring of the library that's just fine. Especially to add Receive functions (I'm only using Transmit right now).

I'd love to hear more baout the sort of projects you're building, too! Mine, in sequence I think:
- A tiny midi synth (additive synthesis, with a decent range of stringlike & drumlike sounds, but polyphony will be limited by how efficient I can code)
- A high-as-possible-quality S/PDIF digital crossover
- Head-tracking binaural something or other, maybe...

Hugh

sukandar
01-31-2013, 09:29 PM
Hi, thanks for posting this. I've just started using the new Teensy 3.0 and now that I covered the basic stuff, I too would like to get I2S working. However, your I2S example doesn't compile for me. I get this error message:
i2stestB.ino:4:22: fatal error: arm_math.h: No such file or directory
I guess my Teensy 3.0 installation isn't quite right. I've installed beta12 in combination with Arduino 1.0.3
Also, i2s.h can't be found even though it's in the standard folder for libraries.
Thanks,
Sukandar

sumotoy
01-31-2013, 09:58 PM
try this, I found in my old avr set but not sure if it's updated.

sukandar
01-31-2013, 11:21 PM
Thanks sumotoy.
However, I now get this message:
arm_math.h:257:24: fatal error: core_cm4.h: No such file or directory

hpyle
02-01-2013, 03:15 AM
Um, yes, sorry about that. There was really no need for the dependency on arm_math.h, and it's not part of the standard 1.0.3 Arduino (instead it's part of CMSIS).
Try check out from the github; I've refactored the example to not require arm_math, and also reworked the I2S library into C++ to make it easier to use. Let me know if you can run the example now.
-Hugh

hpyle
02-01-2013, 11:57 AM
The library is updated for Teensyduino 1.12; you'll still need to copy the modified mk20dx128.h into arduino/hardware/teensy/cores/teensy3.

PaulStoffregen
02-01-2013, 04:15 PM
Thanks for putting so much awesome work into this!

I'll accept a patch for the missing definitions for Teensyduino 1.13.

sukandar
02-01-2013, 10:17 PM
Thanks much for the updates.
However, I now get this:
i2s.cpp:15:16: error: expected initializer before '_dma_Buffer_A'

thanks, Sukandar

hpyle
02-02-2013, 01:18 AM
Are you using the Teensyduino 1.12 release? The latest I2S library code requires it (this morning I added the "DMAMEM" symbol to its DMA buffers, which means "memory optimized for DMA transfers").

sukandar
02-02-2013, 02:08 AM
silly me
I just saw "12" and thought it's the beta12 I had just installed.
With Teensyduino 1.12 it compiles indeed.
Tomorrow I'll check if it also works - I'm not in the lab right now.

thanks a bundle,
Sukandar

sukandar
02-06-2013, 04:55 PM
Hi again - finally got a chance to test the code in the lab.
All looking good so far.
Thanks much again!

loglow
02-07-2013, 08:07 PM
That's great! I hope you find the library useful.

So far as a reference, it's been very useful. After a fair amount of trial and error, I've got receive working! Using 48 kHz / 32-bit / 12.288 MHz MCLK. I haven't broken it out as a library yet, but I thought I'd share some of my code here anyway. I should note that I'm using your updated mk20dx128.h file. I've also moved and removed some of the code to make it suitable (aka clearer and more concise) for sharing on this forum.

First, I'm using the ALT4 pin settings for I2S (with wires soldered to the bottom pads) enabled with the pinAlt() (http://forum.pjrc.com/threads/17288) macro:


#define I2S_DIN_PIN 13 // I2S data in
#define I2S_BCLK_PIN 27 // I2S bit clock
#define I2S_MCLK_PIN 28 // I2S master clock
#define I2S_WCLK_PIN 29 // I2S word clock

void setup() {

pinAlt(I2S_MCLK_PIN, 4);
pinAlt(I2S_BCLK_PIN, 4);
pinAlt(I2S_WCLK_PIN, 4);
pinAlt(I2S_DIN_PIN, 4);

}

Then the initialization code:


#define AUDIO_NUM_CHANS 2
#define AUDIO_BIT_DEPTH 32

void init_I2S() {

uint8_t nChans = AUDIO_NUM_CHANS - 1;
uint8_t nBits = AUDIO_BIT_DEPTH - 1;

SIM_SCGC6 |= SIM_SCGC6_I2S; // enable clock to the I2S module
I2S0_MDR |= I2S_MDR_FRACT(15); // output = input * (FRACT + 1) / (DIVIDE + 1)
I2S0_MDR |= I2S_MDR_DIVIDE(124); // 12.288 MHz = 96 MHz * (16 / 125)
I2S0_MCR |= I2S_MCR_MOE; // enable MCLK pin as output
// --------------------------------------------------------------------------------
I2S0_RCR1 |= I2S_RCR1_RFW(nChans); // set FIFO watermark
// --------------------------------------------------------------------------------
I2S0_RCR2 |= I2S_RCR2_MSEL(1); // use MCLK as BCLK source
I2S0_RCR2 |= I2S_RCR2_SYNC(0); // use asynchronous mode
I2S0_RCR2 |= I2S_RCR2_DIV(1); // (DIV + 1) * 2, 12.288 MHz / 4 = 3.072 MHz
I2S0_RCR2 |= I2S_RCR2_BCD; // generate BCLK, master mode
I2S0_RCR2 |= I2S_TCR2_BCP; // BCLK is active low
// --------------------------------------------------------------------------------
I2S0_RCR3 |= I2S_RCR3_RCE; // enable receive channel
// --------------------------------------------------------------------------------
I2S0_RCR4 |= I2S_RCR4_FRSZ(nChans); // frame size in words
I2S0_RCR4 |= I2S_RCR4_SYWD(nBits); // bit width of WCLK
I2S0_RCR4 |= I2S_RCR4_MF; // MSB (most significant bit) first
I2S0_RCR4 |= I2S_RCR4_FSD; // generate WCLK, master mode
I2S0_RCR4 |= I2S_RCR4_FSE; // extra bit before frame starts
// --------------------------------------------------------------------------------
I2S0_RCR5 |= I2S_RCR5_W0W(nBits); // bits per word, first frame
I2S0_RCR5 |= I2S_RCR5_WNW(nBits); // bits per word, nth frame
I2S0_RCR5 |= I2S_RCR5_FBT(nBits); // index shifted for FIFO
// --------------------------------------------------------------------------------
I2S0_RCSR |= I2S_RCSR_BCE; // enable the BCLK output
I2S0_RCSR |= I2S_RCSR_RE; // enable receive globally
I2S0_RCSR |= I2S_RCSR_FRIE; // enable FIFO request interrupt

}

Then elsewhere, when I want to start receiving data, I call:


NVIC_ENABLE_IRQ(IRQ_I2S0_RX);

And when I want to stop:


NVIC_DISABLE_IRQ(IRQ_I2S0_RX);

Finally there's the I2S receiver ISR. I'm doing more than just this, like moving the data into various buffers, but that's somewhat outside the scope of just I2S. The strangest (and most annoying) thing to figure out was the extra "dummy" read from the data register. While this seems to be working, I'm not entirely sure it's correct, since this is all still very new to me.


uint32_t audio_ch0;
uint32_t audio_ch1;

void i2s0_rx_isr() {

// read data from FIFO list
audio_ch0 = I2S0_RDR0; // read channel 0
audio_ch1 = I2S0_RDR0; // dummy read
audio_ch1 = I2S0_RDR0; // read channel 1

// reset the FIFO
I2S0_RCSR |= I2S_RCSR_FR;

}

I briefly attempted to integrate some of this into your library on GitHub, but ended up not wanting to mess with the way you've structured things too much. That, and I haven't touched DMA once in my life (yet).


I'd love to hear more baout the sort of projects you're building, too!

I'm working on an audio-recording project of sorts. I'll have more to share about it in the near future!

Your "head-tracking binaural something or other" sounds wicked interesting, if not somewhat mysterious. :p

Best, -Dan

MickMad
02-09-2013, 11:00 AM
hello there, I was wondering if anyone here has tried using the I2S bus for both transmitting and receiving data...

hpyle
02-10-2013, 12:03 AM
MickMad: I don't think anyone has done both transmit and receive yet.

Dan: I've refactored my library (https://github.com/hughpyle/teensy-i2s) to an interface that should work for both transmit and receive; and I've included your setup code in there. I tested transmit-with-DMA to make sure I didn't break anything in my own project, but didn't test receive at all. Can you take a look and see whether it works? I'm interested in the shape of the I2S object as much as the implementation. e.g. does the "pin pattern" stuff make sense?

There are several things that aren't configurable yet, such as bit-depth. Are you actually interested in all 32-bit data? (Even if you're not, I have some hifi projects that would want 24-bit). Any suggestions how to provide a callback API like the DMA one (which hands you a buffer of int16_t right now and says "fill this" or "read from this") but handling different bit-depth usages?

-Hugh

loglow
02-11-2013, 05:24 AM
hey Hugh, just noticed your update, and I'm gonna check it out now. I see you found my basic upload at https://github.com/loglow/I2Sound -- I'm excited to get something working w/ both TX and RX, since I'm going to need TX before long too. I'll reply here again after I've checked it out...

Oh also, I have a request... can you restructure the repo so the top level is the contents of the library? Then I can clone it directly into my arduino lib folder. Unless there's an easy way to clone just a subdir with git?

Also noticed you're not very far from me! I'm in Northampton, MA :) -Dan

hpyle
02-11-2013, 11:44 AM
Thanks. I saw your I2Sound library and picked up some good things from it! It's nice and clean.

I'll try that directory restructure. Gotta learn how to use git properly :)

Hugh

alex
02-15-2013, 08:42 PM
Hi All

Thanks, Hugh for giving me the heads up about your awesome progress! Can't wait to get this working.
I'm having trouble compiling so far, using Arduino 1.0.3 and Teensyduino 1.12:



/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::start()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:81:26: error: 'I2S_RCSR_RE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:82:26: error: 'I2S_RCSR_BCE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:83:26: error: 'I2S_RCSR_FRDE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:89:26: error: 'I2S_TCSR_TE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:90:26: error: 'I2S_TCSR_BCE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:91:26: error: 'I2S_TCSR_FRDE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:101:26: error: 'I2S_RCSR_RE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:102:26: error: 'I2S_RCSR_BCE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:103:26: error: 'I2S_RCSR_FRIE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:110:26: error: 'I2S_TCSR_TE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:111:26: error: 'I2S_TCSR_BCE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:112:26: error: 'I2S_TCSR_FRIE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::clock_init(unsigned char)':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:234:34: error: 'I2S_MCR_MICS' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:240:34: error: 'I2S_MCR_MICS' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:240:38: error: 'I2S_MCR_MOE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:248:43: error: 'I2S_MDR_FRACT' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:248:64: error: 'I2S_MDR_DIVIDE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::i2s_transmit_init()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:291:20: error: 'I2S_TCSR_TE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:300:35: error: 'I2S_TCR1_TFW' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:302:33: error: 'I2S_TCR2_SYNC' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:303:18: error: 'I2S_TCR2_BCP' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:310:18: error: 'I2S_TCR3_TCE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:312:36: error: 'I2S_TCR4_FRSZ' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:313:36: error: 'I2S_TCR4_SYWD' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:314:18: error: 'I2S_TCR4_MF' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:315:18: error: 'I2S_TCR4_FSE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:317:18: error: 'I2S_TCR4_FSD' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:320:35: error: 'I2S_TCR5_W0W' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:321:35: error: 'I2S_TCR5_WNW' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:322:35: error: 'I2S_TCR5_FBT' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::i2s_receive_init()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:328:20: error: 'I2S_RCSR_RE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:337:35: error: 'I2S_RCR1_RFW' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:339:33: error: 'I2S_RCR2_SYNC' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:340:18: error: 'I2S_TCR2_BCP' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:347:18: error: 'I2S_RCR3_RCE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:349:36: error: 'I2S_RCR4_FRSZ' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:350:36: error: 'I2S_RCR4_SYWD' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:351:18: error: 'I2S_RCR4_MF' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:352:18: error: 'I2S_RCR4_FSE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:354:18: error: 'I2S_RCR4_FSD' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:357:35: error: 'I2S_RCR5_W0W' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:358:35: error: 'I2S_RCR5_WNW' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:359:35: error: 'I2S_RCR5_FBT' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::i2s_tx_callback()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:367:20: error: 'I2S_TCSR_FEF' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:368:20: error: 'I2S_TCSR_SEF' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::i2s_rx_callback()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:382:20: error: 'I2S_RCSR_FEF' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:383:20: error: 'I2S_RCSR_SEF' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::dma_transmit_init()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:446:23: error: 'DMA_CSR_ACTIVE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:458:14: error: 'DMA_CR_EMLM' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:460:14: error: 'DMA_CR_ERCA' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:467:46: error: 'DMA_ATTR_SMOD' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:468:46: error: 'DMA_ATTR_SIZE_16BIT' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:468:65: error: 'DMA_ATTR_SSIZE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:469:46: error: 'DMA_ATTR_DMOD' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:470:65: error: 'DMA_ATTR_DSIZE' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:476:49: error: 'DMA_CITER_MASK' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:477:49: error: 'DMA_BITER_MASK' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:478:31: error: 'DMA_CSR_INTMAJOR' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:479:44: error: 'DMA_CSR_BWC' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:482:15: error: 'DMA_ERQ_ERQ0' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:483:31: error: 'DMA_SERQ_SERQ' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::dma_start()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:505:39: error: 'DMA_SERQ_SERQ' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In member function 'void I2S_class::dma_stop()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:511:39: error: 'DMA_CERQ_CERQ' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In function 'void dma_ch0_isr()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:567:31: error: 'DMA_CINT_CINT' was not declared in this scope
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp: In function 'void dma_ch1_isr()':
/Users/alexandersmith/Documents/Arduino/libraries/i2s/i2s.cpp:573:31: error: 'DMA_CINT_CINT' was not declared in this scope


Probably something stupid that I'm doing?
Thanks for any advice, so excited to make some noise!!

Alex

alex
02-16-2013, 04:55 PM
Hey Guys,

Getting closer! I needed to copy the modified mk20dx128.h into arduino/hardware/teensy/cores/teensy3 as stated in this thread and was able to compile!

Still not getting any sound, here is a snap of my setup. Should I not have ADCL or/and DACL wired up?

I can almost hear the synth just around the corner ;)
Many thanks
Alex

251

MickMad
02-16-2013, 08:59 PM
nice job here, alex; are you too using a Wolfson chip? Did you design the board connected to the teensy?

alex
02-16-2013, 09:19 PM
Hi MickMad,

Not my board, I ordered this DAC from Mouser part#932-MIKROE-506

:)

hpyle
02-16-2013, 11:52 PM
For the pins, try adding DACL to pin 4, for the word clock (duh, my examples don't tell you this).
Are you using the DMA interface or straight I2S? Try the DMA-based example sketch. I think the I2S sample is broken (sorry - I haven't been in a position to work on this much very recently).

alex
02-17-2013, 01:02 AM
HI Hugh,

Thanks for your reply, still no luck I'm afraid. Using the i2s_tx_dma_wm8731 example sketch.

Should MISO or ADCL be hooked up to teensy too? Cheers
Alex

hpyle
02-17-2013, 06:24 PM
I'll have to check my hardware when I get home :-) Tomorrow some time.

Meanwhile here's a copy of my additive-synthesizer code, if anyone's interested in hacking on it. It doesn't really work yet, but I figure it's never aimed at being a commercial product, so I should just publish it already. https://github.com/hughpyle/directsynth

hpyle
02-18-2013, 09:08 PM
I got it wrong in the examples' comments. You have SCK to pin 11 (MCLK), but it should be to the bit-clock, pin 9 (BCLK).

Here's the full list to check:
SCK -> Teensy 9 (I2S0_TX_BCLK)
MISO -> not connected for this transmit-only example
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
ADCL -> not connected for this transmit-only example
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25
SDA -> Teensy 18 (I2C0_SDA)
SCL -> Teensy 19 (I2C0_SCL)
3.3V -> Teensy 3.3v
GND -> Teensy GND

alex
02-19-2013, 02:30 AM
YESSSS! THANKS SO MUCH

I'm getting beautiful sine waves now! Thanks so much for posting your synth too. I prefer to "figure it out" from your sine example but appreciate your generosity.

I'll be sure to post my results. Hoping to make some kind of monophonic bassey synth

Cheers
Alex

alex
02-20-2013, 05:07 PM
Hi again,

Having a lot of fun making noises here - quick question: I'm used to working with float values with audio (0-1 range).
I am doing signal*32767 to convert it to Q15 - does that sound correct? no pun intended

Cheers
Alex

hpyle
02-21-2013, 02:14 AM
Yes, close enough. Q15 represents a fraction with one "sign" bit and 15 "fraction" bits; the values go from -1.0 (0x8000 or 32768) to nearly +1.0 (0x7FFF or +32767). Usually of course the exact value won't be critical. But dealing with "not quite +1.0" is a bit of a pain sometimes.

You need to be a bit careful with multiplication. Multiply two Q15 values produces a Q30 value. To get that back to a Q15, first shift left one bit (making a Q31) and then use the high 16-bit word. This pdf explains better than I can:
http://www.cs.washington.edu/education/courses/cse467/08au/pdfs/lectures/11-FixedPointArithmetic.pdf

The ARM processor has some quite nice built-in functions that can add Q15 types without overflow, "saturating" (clipping) instead.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CHDBHGFC.html

Emi
03-06-2013, 10:23 PM
That's great! I hope you find the library useful. If you're rolling your own, the main trick is to not set the "transmit enable" flag until everything else is set up ;-)
You may find that my clock setup doesn't work for your application. It was done pretty much by trial and error with the Wolfson codec and a cheap single-channel oscilloscope... the only reliable way I could run a data stream was to set up as 32-bit data. YMMV.

Do feel free to fork my library/example code. I'll be working on this very much part-time -- I expect my first project could take months, no particular rush -- so even if you send me pull requests with a big refactoring of the library that's just fine. Especially to add Receive functions (I'm only using Transmit right now).

I'd love to hear more baout the sort of projects you're building, too! Mine, in sequence I think:
- A tiny midi synth (additive synthesis, with a decent range of stringlike & drumlike sounds, but polyphony will be limited by how efficient I can code)
- A high-as-possible-quality S/PDIF digital crossover
- Head-tracking binaural something or other, maybe...

Hugh


I am trying to make teensy3 to communicate with WM8731 codec through i2s ,first I tried the WM8731 code ,as you suggested , just to make sure that the clock is going to work and the code was supposed to print some results such as register address ,here it is what should be printed out:
Serial.print( "WM8731 register 0x" );
Serial.print( reg, HEX );
Serial.print( " = 0x" );
Serial.println( value, HEX );
but all i have gotten is a white windows terminal without anything printed on it ,so i am not sure if it is working ,but most likely not,to go for the next stage through your i2s library.I just would like to ask if you have faced such a problem before you succeeded to apply your i2s library completely?It should be mentioned that first i had an error which had an issue with setup and loop that have been discussed a lot in the forum about it,the thing that i did to get rid of the error was just removing the part in where set up and loop have been written because i thought that i wouldn't be needing this part ,but honestly i am not sure if i did right or wrong to do remove them?anyways i got rid of the error in that regard but unfortunately no output i got.I appreciate your time to read my inquiry.
Regards,
Emi

hpyle
03-08-2013, 07:39 PM
Are you using the Mikro breakout board? If so, you should try run the sample sketch -- it should make sound right away.

You're right that starting with the I2C communication is a good strategy. But if you see nothing on the serial monitor, there could be some more basic problem. Have you tried just writing a simple sketch with no audio or i2c, just printing to serial?

Emi
03-08-2013, 10:11 PM
Are you using the Mikro breakout board? If so, you should try run the sample sketch -- it should make sound right away.

You're right that starting with the I2C communication is a good strategy. But if you see nothing on the serial monitor, there could be some more basic problem. Have you tried just writing a simple sketch with no audio or i2c, just printing to serial?

Thanks for replying Hyple,no i am using wm8731 it is Wolfson codec here it is the link for this codec http://www.wolfsonmicro.com/products/codecs/WM8731/
I should say that yes i tried simple audioinputoutput example of the built-in library and it worked i could see the results on the serial monitor.i am new in this and it is more troublesome than i could fix it.I truly appreciate if you could send me your library for the entire i2c you made ,the one you can send right clock and data from teensy3 to Wm8731 codec.will you?if it is so, here is my email goynic@gmail.com .
Regards,
Emi

hpyle
03-09-2013, 07:23 PM
The i2c library that I'm using to control WM8731 is here: https://github.com/hughpyle/machinesalem-arduino-libs/tree/master/WM8731
The i2c interface is not used for audio data. The i2s interface does that.
So far I have only tested the i2s interface with a codec in "master mode" and 12.288MHz clock, and a 48kHz audio sampling rate. Soon I hope to try with the Open Music Labs codec shield, which is WM8731 in "slave" mode and 11.2896MHz master clock, which is suitable for 44.1kHz audio.

john-mike
03-21-2013, 09:36 PM
Has anyone got this working with the Audio Codec Shield?
I believe I have it all hooked up correctly though I don't know about DACL. Does that go to SS? The codec shield has DACL and ADCL tied together.
I see activity on SCK, MOIS and DACL the but not SDA or SCL

One odd this is that when I tried to test if the code was working I didn't see 'nnn' changing in either of the included examples.

Thanks!

john-mike
03-23-2013, 01:06 AM
Yeah I don't think the codec shield will work with the code as is. I got one of the mikro breakouts and it works fine except for one thing.
I have to take the SCK line in and out of the pin for it to work. Sometimes it's good after uploading, sometimes I need to plugg it in a few times as the audio will be distorted or not in one of the channels.

hpyle
03-23-2013, 02:53 AM
Right, the library doesn't work with Open Music Labs codec shield yet. I got one and hope to spend some time this weekend working on the code. It looks like nice hardware.

I wonder what's up with your SCK issue. I've not had distortion-type issues at all.

john-mike
03-23-2013, 05:33 AM
I wonder what's up with your SCK issue. I've not had distortion-type issues at all.

Yeah it's odd. I can never get both channels to work at the same time but one usually works and the other is silent.
It sounds and looks like the the waves are being wrapped around past 64000. I've tried making a wave table and using DDS and it does the same thing.
I've also changed the clock type but it still acts pretty much the same.

It doesn't seem like a bad connection as it's stable.

hpyle
03-23-2013, 03:20 PM
I just checked in a change that makes the OML codec shield work. I think it may still be "wrong" (not deriving the clocks correctly) and I didn't regression-test the Mikro board, but it makes sound... which is all I can do right now :) More later.

stefanovic
03-28-2013, 08:52 PM
Hello,

I'm a newbie here. I found this thread and I'm very interested in what you do. I have a couple of questions, just wondering:
- how many audio input/ouput can a single teensy3 handle?
- do you think a USB class-compliant audio card can be made with a teensy, some audio boards, and the code you are writing?

Cheers,

stéphane

john-mike
04-09-2013, 11:59 PM
I just checked in a change that makes the OML codec shield work. I think it may still be "wrong" (not deriving the clocks correctly) and I didn't regression-test the Mikro board, but it makes sound... which is all I can do right now :) More later.

Thanks!
I haven't had any clock problems with this version.

I see you added recieve but I'm not understanding how "I2SRx0.begin" works. Any hints?

john-mike
04-28-2013, 05:00 PM
I've got a nice basis for a multi-voice wavetable synth working but it seems that in DMA mode IntervalTimer won't work.
In non-DMA they work fine but then only so much can happen in the callback.

stormeporm
05-17-2013, 09:16 PM
I have an old philips cd104 collecting dust and I've been wanting to repurpose the dacs in there for a while. Use it for a synth or a guitar pedal.
I have been makin synths in arduino but without dedicated dacs.
Would that be possible bith the teensy 3?
Its the TDA1540 is does support i2s.
http://www.dutchaudioclassics.nl/philips_tda1540/
http://www.datasheetcatalog.org/datasheets2/37/375579_1.pdf

DIego
06-12-2013, 06:28 PM
any of you been able to use ADC?
I have tried to record with the receipt functions of hpyle (functions i2s_RX)
I have not any result
the buffer that records always gets 0
can someone help me?

chrylis
06-28-2013, 08:33 PM
I just opened up my Teensy 3, and I'm hoping to get hacking on I2S promptly. For those who have been working with it a while, how did you produce test audio in the beginning? I'm planning to be reading audio over Ethernet in the long term, but I'm thinking that right now it might be better to just generate a test signal "by hand" and stuff it in the DMA buffer. Ideas or alternatives?

john-mike
06-28-2013, 10:06 PM
I just opened up my Teensy 3, and I'm hoping to get hacking on I2S promptly. For those who have been working with it a while, how did you produce test audio in the beginning? I'm planning to be reading audio over Ethernet in the long term, but I'm thinking that right now it might be better to just generate a test signal "by hand" and stuff it in the DMA buffer. Ideas or alternatives?

Do you mean as an ouput test? His example produces sine waves.
I was just using my phone to play music and tones into it.

I was talking with hughpyle about getting the library fully working but he simply doesn't have the time. I got input kind of working but there is something wrong with the timing and I don't know where to start with that. So I'm going to stick with SPI DACs I guess.



/*
I2S & DMA digital audio demonstrator for Teensy 3.0
Interfaces using Wolfson WM8731 codec.

To use the Mikro proto board (as master): set clock type to I2S_CLOCK_EXTERNAL
Note: this board doesn't have line-in connections.
SCK -> Teensy 9 (I2S0_TX_BCLK)
MISO -> Teensy 13
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
ADCL -> teensy 12?
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25
SDA -> Teensy 18 (I2C0_SDA)
SCL -> Teensy 19 (I2C0_SCL)
3.3V -> Teensy 3.3v
GND -> Teensy GND

*/


/* Wolfson audio codec controlled by I2C */
/* Library here: https://github.com/hughpyle/machinesalem-arduino-libs/tree/master/WM8731 */
#include <Wire.h>
#include <WM8731.h>


/* I2S digital audio */
//#define I2S_PIN_PATTERN I2S_TX_PIN_PATTERN_1
#include <i2s.h>
const uint8_t clocktype = I2S_CLOCK_EXTERNAL;

int tic,jj;
byte led=0;
// audio data
int16_t audf, audx, audy, audd,b,inx,iny;
int32_t nnn=0;

void initsinevalue()
{
audf = 45 + (30 % 48); // midi note number
float f = (440.0 / 32) * pow(2, ((float)audf - 9) / 12); // Hz. For realz, use a lookup table.
audd = 2.0 * sin(PI*f/48000) * 32767; // delta (q15_t)
audx = 0;
audy = 0.9 * 32767; // start somewhere near full-scale
}

void nextsinevalue()
{
nnn++;
// if(nnn>48000) {nnn=0;initsinevalue();}; // reset every second
// if(nnn>24000){nnn=0;audx=audx<<1;if(audx==0)audx=1;b=audx;};return; // marching blip
// audx+=4;if(nnn>512){nnn=0;audx=-2048;};b=audx;return; // stair
// b = 0xACCF0010; audx=0xACCF; return; // const pattern
audx+=((audd*audy)>>15)&0xFFFFu; audy-=((audd*audx)>>15)&0xFFFFu; // sinewaves http://cabezal.com/misc/minsky-circles.html
}



/* --------------------- Direct I2S data transfer, we get callback to put 2 words into the FIFO ----- */

void i2s_tx_callback( int16_t *pBuf )
{
pBuf[0] = 0;
pBuf[1] = inx;
nextsinevalue() ;
}

void i2s_rx_callback( int16_t *pBuf )
{
inx= pBuf[0];
iny= pBuf[1];


jj++;

}


/* ----------------------- begin -------------------- */

void setup()
{
Serial.begin(9600);
pinMode(13, OUTPUT);

initsinevalue();

unsigned char interface = WM8731_INTERFACE_FORMAT(I2S) | WM8731_INTERFACE_WORDLEN(bits16) | WM8731_INTERFACE_MASTER;
WM8731.begin( low, WM8731_SAMPLING_RATE(hz48000), interface );
WM8731.setActive();
WM8731.setOutputVolume( 0 );
WM8731.set( WM8731_ANALOG, WM8731_ANALOG_DACSEL);
delay(1000);

I2STx0.begin(clocktype, i2s_tx_callback );

I2STx0.start();
delay(1000);

I2SRx0.begin(clocktype, i2s_rx_callback );

I2SRx0.start();
delay(1000);

// digitalWrite(13,1);
WM8731.setInputVolume( 31 );

WM8731.setOutputVolume( 127 );

}


/* --------------------- main loop ------------------ */
void loop()
{


tic++;
if (tic>50000){
Serial.print(jj); Serial.print(" ");
Serial.print(iny); Serial.print(" ");
Serial.println(inx);

tic=0;
}

}

hpyle
07-10-2013, 04:15 AM
I've just posted an update that fixes various things, implements DMA for Receive, and adds a couple "play-through" examples (one with DMA, one without) that listen for audio on the input and play it straight through the output.

The trick to using Tx and Rx simultaneously is to synchronize the Receive channel to Tx. That's hard-wired right now, so I don't know whether Rx will work if Tx is not active. Later...

You still need to patch the Teensy mk20dx128.h header to add various DMA constants. If anyone would prefer I put these in the library header, let me know.

My time on this project has become extremely limited the last couple months, but it's good to be back for a few hours making progress. I hope folks out there find it useful and get some cool audio toys working :)

sumotoy
07-10-2013, 03:56 PM
Thanks a lot, hpyle, really appreciated what you have done! For the first time I can play with waveforms and audio math and this is really great, a brand new world to explore!
I think it's better to put the header in the library since paul is so sweet to update (and improve) constantly the software it's easy to get lost. I personally avoid to touch the core files especially when I have a lot of open projects laying around.
But even better, why not integrate header mods in the next teensy update if it's necessary to the I2S? Or it breaks something else?

john-mike
07-10-2013, 11:52 PM
Thanks so much, Hugh!

mxxx
07-11-2013, 05:09 AM
thanks indeed. helped me indefinitely with this little toy -


664


... now i have to sit down and write some decent sounding code for it.

Ben
07-29-2013, 09:55 PM
Hi everyone,

I plan to use the T3 as a I2C master in an audio DAC. I2S would be a great addition to the project, so I'm trying to understand the I2S tx-with-dma thing. Today I modified the example sketch and I'd like to post it here for two reasons. First, please check if my comments are correct. Second, if they are correct, I believe this sketch is a good example to learn from, because it concentrates on the i2s-topic only. I hope the rather verbose comments make it easier to understand for newbies like me :rolleyes:

So here is the sketch, some parts are shamelessly copied from the example on github :p


/*
I2S & DMA digital audio demonstrator for Teensy 3.0
SCK -> Teensy 9 (I2S0_TX_BCLK)
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25
3.3V -> Teensy 3.3v
GND -> Teensy GND
*/

/* Settings for I2S Interface */
#define clock_per_sec 44100
// I believe the following three define statements are not needed for I2S only:
#define CLOCK_TYPE (I2S_CLOCK_44K_INTERNAL)
#define CODEC_INTERFACE_FLAGS (WM8731_INTERFACE_FORMAT(I2S) | WM8731_INTERFACE_WORDLEN(bits16) )
#define CODEC_BITRATE (WM8731_SAMPLING_RATE(hz44100))

/* I2S digital audio */
#include <i2s.h>

// audio data
int16_t audf, audx, audy, audd;
// timekeeping
int32_t nnn=0, i=0;

/* Since the method nextvalue() modifies the audio sample data gradually depending on the previous data, this method is used to reset the sample data to a known and constant value, which is zero for the x-channel and full scale for the y channel. */
void resetvalues()
{
audx = 0x0000;
audy = 0xFFFF;
}

/* This method is called once for every pair of sample data to transmit. The method provides the I2S hardware with the next sample for x and y channel by storing the sample in the audx and audy variables. */
void nextvalue()
{
nnn++;
if(nnn>(clock_per_sec*10))
{
nnn=0; resetvalues(); // reset every 10 seconds by calling resetvalues()
}
if (i>1000)
{
audx++; audy--; i=0; // change values every 1000 samples by one LSB.
}
else
{
i++; // keep counting up if i is below 1000
}
}


/* ----------------------- DMA transfer, we get callback to fill one of the ping-pong buffers ------ */
/* Please someone explain this method to me.
pBuf seems to point to the memory that is used to store the audio data and is filled with the data provided by nextvalue(),
but what initial value does len have, how many samples are stored in one execution of this method?
This method is called by an interrupt created by the I2S-Hardware, right? */
void dma_tx_callback( int16_t *pBuf, uint16_t len )
{
while( len>0 )
{
*pBuf++ = audx;
*pBuf++ = audy;
nextvalue();
len--;
len--;
}
}


/* ----------------------- begin -------------------- */

void setup()
{
resetvalues();
Serial.println( "Initializing" );

delay(2000);
// Serial.println( "Initializing." );
delay(1000);
I2STx0.stop(); // Stop in case it's not stopped. I2S is in defined state now.
I2STx0.begin( CLOCK_TYPE, dma_tx_callback ); //configure clock (frequency and external/internal. Also define which function to call on interrupt
Serial.println( "Initialized I2S with DMA" );

I2STx0.start(); // Enable I2S hardware to start working
}


/* --------------------- main loop ------------------ */
void loop() // nothing here, because i2s is interrupt-driven once it's initialized.
{
}


This was my measurement setup:
728

I can post a screenshot of the oscilloscope screen tomorrow, don't have that in my cloud :mad:

Thanks hpyle and everyone who contributed for this awesome lib!
With best regards
Ben

Edit: The Screenshots I promised:
739

740
The Measurements in the lower right corner are:
Ansteigen == Rise Time
Abfall == Fall Time
Über == Overshoot (more than 20%, is this normal?)
Freq == Frequency, this is 2 channels, 16 bits each, 44.1kHz sampling rate --> 2*16*441000 = 1.4112 MHz

I hope this is useful to anyone. By the way, I have access to very good measuerment equipment. Scopes, Freq. counters, spectrum analyzers, signal generators... If this is of any use for the development of this library, let me know and I will provide you with the measurement you need.
Regards
Ben

MickMad
07-30-2013, 12:33 PM
Hey there, people! I'm working on an audio interface for the teensy 3, with two wolfson converters, the wm8786 and wm8740; it will also have two pga4311 digital volume controls, and an input selector to switch between low and high impedance inputs; the wm8786 is an ADC, the wm8740 is a DAC; I was wondering if both chips can share the same MCLK signal coming from the teensy, or if they MUST share it, because I don't happen to see two MCLK pins on the K20 datasheet; in fact, in page 1147, tab 46.2, of the K320 datasheet, it talks about one MCLK signal; also, I remember there was a maximum limit for the MCLK when generated by the teensy, and I can't find anymore about it in the datasheet. By the way, if you like I can post some parts of the schematic, maybe you guys could help me :)

Ben
07-30-2013, 08:29 PM
I was wondering if both chips can share the same MCLK signal coming from the teensy, or if they MUST share it, because I don't happen to see two MCLK pins on the K20 datasheet;
I suppose the signal chain will be ADC->T3->DAC? In this setup the Clock needs to be either provided by the teensy or by an external oscillator. I am not shure about this, but maybe you should check if the T3's clk has a low enough jitter for your application.
Regards
Ben

hpyle
08-01-2013, 02:04 PM
@ben Very nice.

In your first scope screenshot: notice that the data word is delayed one bit "behind" the frame. I2S data frames start one bit later than the frame-clock, and so they also finish one bit later.

In the second screenshot, are you looking at overshoot on the bit-clock line? I wish my scope was fast enough to even see that ;)


Your code looks great. You write, /* Please someone explain this method to me... */ dma_tx_callback(...)

The dma_tx_callback, or the i2s_tx_callback if you're not using DMA, is indeed called by an interrupt. The library registers for that interrupt and implements the actual interrupt handler, which is the function i2s0_tx_isr() / i2s0_rx_isr() / dma_ch0_isr() / dma_ch1_isr in i2s.cpp. Eventually those call back to the function you passed when initializing the library.

For DMA, the library manages two buffers, each of DMA_BUFFER_SIZE samples. (That's the value of 'len' passed to you). It ping-pongs between them.
Let's start while the DMA hardware is transmitting one of the buffers. When it's done, the interrupt is raised. The library tells DMA to start transmitting from the second buffer right away; and then calls your dma_tx_callback() passing a pointer to the first buffer. So, you can spend quite a long time writing data into that buffer, and then return; when your data has been transmitted, you'll be called back again to fill the other buffer with new data.

I hope that's helpful :)

hpyle
08-01-2013, 02:06 PM
@MickMad I expect you can produce MCLK from teensy and clock both devices from the same signal, without using an external clock at all. But don't take my word for it; the clocking parts of the datasheet (and I2S altogether) are still very mysterious to me.

mlu
08-01-2013, 02:30 PM
Heres my code for configuring Teensy 3 I2S clocks, has been tested with an WM8762


void i2s_config_clock()
{
/* Enable I2S clock domain */
SIM_SCGC6 |= SIM_SCGC6_I2S;

/* I2S Clocks are for 48kHz */
#if (F_CPU == 96000000)
/* MCLK = 128*Fs = 8/125 System clock at 96 MHz */
I2S0_MDR = (7<<12) | 124;
#elif (F_CPU == 48000000)
/* MCLK = 128*Fs = 16/125 System clock at 48 MHz */
I2S0_MDR = (15<<12) | 124;
#elif (F_CPU == 24000000)
/* MCLK = 128*Fs = 32/125 System clock at 24 MHz */
I2S0_MDR = (31<<12) | 124;
#endif
/* Enable MCK out on pin 11 */
I2S0_MCR = I2S_MCR_MOE;

I2S0_TCR1 = 2; // TX FIFO watermark

/* Select MCLK and generate bitclock */
I2S0_TCR2 = I2S_TCR2_MSEL(I2S_MSEL_MCLK) | I2S_TCR2_BCD | I2S_TCR2_BCP | 0; // DIV(0)

/* Enable transmit channel */
I2S0_TCR3 = I2S_TCR3_TCE; // TCE

/* FRSZ 1 , 32 bits FS and MSB*/
I2S0_TCR4 = (1<<16) | (31<<8) | (1<<4) | I2S_TCR4_FSD; // MSEL(1)

/* TX Word widths and shift */
I2S0_TCR5 = (31<<24) | (31<<16) | (15<<8);

}


Hope it can be some help.

Regards
Magnus

Ben
08-01-2013, 03:55 PM
@ben Very nice.

In your first scope screenshot: notice that the data word is delayed one bit "behind" the frame. I2S data frames start one bit later than the frame-clock, and so they also finish one bit later.

In the second screenshot, are you looking at overshoot on the bit-clock line? I wish my scope was fast enough to even see that ;)

Thanks. Yes, that's the bit clock. The channel clock has less overshoot, but some crosstalk from the bit clock is visible, about 500mVpp. By the way, this is the slowest scope I can work with (70MHz). I can go up to several GHz, if needed.:cool:



Your code looks great. You write, /* Please someone explain this method to me... */ dma_tx_callback(...)

The dma_tx_callback, or the i2s_tx_callback if you're not using DMA, is indeed called by an interrupt. The library registers for that interrupt and implements the actual interrupt handler, which is the function i2s0_tx_isr() / i2s0_rx_isr() / dma_ch0_isr() / dma_ch1_isr in i2s.cpp. Eventually those call back to the function you passed when initializing the library.

For DMA, the library manages two buffers, each of DMA_BUFFER_SIZE samples. (That's the value of 'len' passed to you). It ping-pongs between them.
Let's start while the DMA hardware is transmitting one of the buffers. When it's done, the interrupt is raised. The library tells DMA to start transmitting from the second buffer right away; and then calls your dma_tx_callback() passing a pointer to the first buffer. So, you can spend quite a long time writing data into that buffer, and then return; when your data has been transmitted, you'll be called back again to fill the other buffer with new data.

I hope that's helpful :)
This is very helpful, thank you so much! I added an artificial delay at the end of my nextvalue() method, and it works fine up to DelayMicroseconds(21), but the exact number of course depends on what the method does apart from delaying :D
I noticed that the speed of the whole I2S bus scales with the clock speed set in the Arduino IDE. The I2S speed is roughly correct for 96MHz (44.12 kHz) but is lower for lower clock speeds. It seems the library depends on the T3 running at the maximum frequency. Is this a bug or did I miss something?

mxxx
08-06-2013, 07:49 AM
Hey there, people! I'm working on an audio interface for the teensy 3, with two wolfson converters, the wm8786 and wm8740; it will also have two pga4311 digital volume controls, and an input selector to switch between low and high impedance inputs; the wm8786 is an ADC, the wm8740 is a DAC; ...By the way, if you like I can post some parts of the schematic, maybe you guys could help me :)

do you have the wm8740 running with i2s/dma? ...wondering whether it's worth the hassle to upgrade a bit from the wm8731

MickMad
08-06-2013, 10:10 AM
do you have the wm8740 running with i2s/dma? ...wondering whether it's worth the hassle to upgrade a bit from the wm8731

I still have nothing running, actually :D I'm finishing the circuit layout in the next weeks. By the way, the WM8740 is a state-of-the-art converter, it sure is worth the hassle :) that's why I'm doing this circuit ;)

mxxx
08-06-2013, 05:04 PM
I still have nothing running, actually :D I'm finishing the circuit layout in the next weeks. By the way, the WM8740 is a state-of-the-art converter, it sure is worth the hassle :) that's why I'm doing this circuit ;)

i see. what is it for?

anyways, no doubt the specs look appealing... i was just thinking aloud, whether i should bother swapping that wm8731 for something fancier on my board, that is.

DIego
08-06-2013, 09:38 PM
Hi
I am trying to use the new libraries "hugh pyle" i2s_thru_dma_wm8731 but I'm having problems with the card "mikroE" because i only hear one headset with some noise when i speak in the microphone. Someone with the same problem?
this is the code that I am using.

Code:


/* TODO synchronous receive only if we choose it explicitly */

/*
I2S digital audio demonstrator for Teensy 3.0
Interfaces using Wolfson WM8731 codec.

This example is a "play through delay" test, using I2S with DMA.
Reads input into a circular buffer, writes output from the same buffer,
If the buffer size is DMA_BUFFER_SIZE (128 by default), this is "straight through".
*/

/*
To use the Mikro proto board (as master): set clock type to I2S_CLOCK_EXTERNAL
Note: this board doesn't have line-in connections.
SCK -> Teensy 9 (I2S0_TX_BCLK)
MISO -> Teensy 13
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
ADCL -> Teensy 12
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25
SDA -> Teensy 18 (I2C0_SDA)
SCL -> Teensy 19 (I2C0_SCL)

3.3V -> Teensy 3.3v
GND -> Teensy GND
*/





//Settings for MikroE prototype board
#define CLOCK_TYPE (I2S_CLOCK_EXTERNAL)
#define CODEC_INTERFACE_FLAGS (WM8731_INTERFACE_FORMAT(I2S) | WM8731_INTERFACE_WORDLEN(bits16) | WM8731_INTERFACE_MASTER)
#define CODEC_BITRATE (WM8731_SAMPLING_RATE(hz48000))
#define CODEC_ANALOG_FLAGS (WM8731_ANALOG_DACSEL | WM8731_ANALOG_MICBOOST | WM8731_ANALOG_INSEL)


/* Wolfson audio codec controlled by I2C */
/* Library here: https://github.com/hughpyle/machinesalem-arduino-libs/tree/master/WM8731 */
#include <Wire.h>
#include <WM8731.h>


/* I2S digital audio */
#include <i2s.h>


/* Circular buffer for audio samples, interleaved left & right channel */
const uint16_t buffersize = DMA_BUFFER_SIZE; // must be a multiple of DMA_BUFFER_SIZE
volatile int16_t buffer[buffersize];
uint16_t nTX = 0;
uint16_t nRX = 0;


/* --------------------- DMA I2S Receive, we get callback to read 2 words from the FIFO ----- */

void dma_rx_callback( int16_t *pBuf, uint16_t len )
{
while( len>0 )
{
buffer[nRX++] = *pBuf++;
buffer[nRX++] = *pBuf++;
len--;
len--;
}
if( nRX>=buffersize ) nRX=0;
}

/* --------------------- DMA I2S Transmit, we get callback to put 2 words into the FIFO ----- */

void dma_tx_callback( int16_t *pBuf, uint16_t len )
{
while( len>0 )
{
*pBuf++ = buffer[nTX++];
*pBuf++ = buffer[nTX++];
len--;
len--;
}
if( nTX>=buffersize ) nTX=0;
}


/* ----------------------- begin -------------------- */

void setup()
{
Serial.println( "Initializing");
delay(5000);
Serial.println( "Initializing." );

delay(1000);
WM8731.begin( low, CODEC_BITRATE, CODEC_INTERFACE_FLAGS );
WM8731.setActive();
//WM8731.set()
// WM8731.setOutputVolume( 127 );
WM8731.set( 0x04, 0x15 );
Serial.println( "Initialized I2C Codec" );

delay(1000);
I2SRx0.begin( CLOCK_TYPE, dma_rx_callback );
Serial.println( "Initialized I2S RX with DMA" );

I2STx0.begin( CLOCK_TYPE, dma_tx_callback );
Serial.println( "Initialized I2S TX with DMA" );

// Before starting tx/rx, set the buffer pointers
nRX = 0;
nTX = 0;

I2STx0.start();
I2SRx0.start();
}



/* --------------------- main loop ------------------ */
void loop()
{
/* do nothing */
}

I hope you can help

MickMad
08-07-2013, 02:20 PM
i see. what is it for?

anyways, no doubt the specs look appealing... i was just thinking aloud, whether i should bother swapping that wm8731 for something fancier on my board, that is.

It's an usb audio interface; it will have one wm8740 dac and one wm8786 adc, two pga4311 digital volume control, two fully differential opamps to convert from unbalanced to balanced inputs (wm8786 has balanced inputs), and an input impedance selector, to switch between line input and instrument input (for bass/guitar). BTW I don't think you can swap chips that easily, as the wm8731 is a codec while these chips are dac/adc only, and they need some glue circuitry to work as stated in the datasheets. I hope that the circuit I'm working on will serve as a good starting point for a nice audio interface based on the teensy. I repeat, I will post schematics asap.

Ben
08-07-2013, 03:56 PM
I did another measurement to find out the CPU load while I2S Tx is running. To do so, I held a pin high during the ISR that feeds new data to the I2S transmit buffer. I use Pin 0 for this.
It turns out the pin is high for about 4% of the time and I think this is roughly equal to the CPU load the ISR imposes. The ISR itself is relatively small, it generates a triangular pattern on one channel, changing one LSB every sample. The other channel is constantly at the lowest value.
Here is another screenshot from my scope:
793
D0 D1 and D2 are the I2S bus. D3 is the pin I used to indicate the ISR is running (Teensy 3 Pin 0).
The measurements in the lower right corner mean:
Periode = period
Freq. = frequency (which is 1/period of course)
+Breite = signal high time (how long the ISR is active)
Arbeit = Duty cycle (what percentage of the whole time the ISR is active)

I also changed the audio data to uint16_t so it has a range starting at 0x0000. I don't know why, but this made it easier for me. Is it ok to do so and write the data to *pBuf, which is int16_t, without any conversion? It seems to work so far...

Here's my code:




/*
I2S & DMA digital audio demonstrator for Teensy 3.0
SCK -> Teensy 9 (I2S0_TX_BCLK)
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25
3.3V -> Teensy 3.3v
GND -> Teensy GND
*/

/* Settings for I2S Interface */
#define clock_per_sec 44100
#define CLOCK_TYPE (I2S_CLOCK_44K_INTERNAL)

#define SIGNPIN 0

/* I2S digital audio */
#include <i2s.h>


// audio data
uint16_t aud_mono;
// ramp generator rising or falling:
boolean rising;

int i=0;


/* ------------ begin ----------- */

void setup()
{
pinMode(SIGNPIN, OUTPUT);
Serial.println( "Initializing" );
delay(200);
I2STx0.stop();
I2STx0.begin( CLOCK_TYPE, dma_tx_callback );
Serial.println( "Initialized I2S with DMA" );
Serial.println( "Begin transmission in 2 seconds" );
delay(2000);
I2STx0.start();
aud_mono = 0x0000; rising = 1;
}


/* ----------- main loop ---------- */
void loop()
{
}


/* ------- DMA transfer, we get callback to fill one of the ping-pong buffers ----- */
void dma_tx_callback( int16_t *pBuf, uint16_t len )
{
digitalWrite(SIGNPIN, HIGH);
while( len > 0 )
{
*pBuf++ = aud_mono;
*pBuf++ = 0x0000;
nextvalue();
len--;
len--;
}
digitalWrite(SIGNPIN, LOW);
}

/* -------- Provide next audio sample ---------- */
void nextvalue()
{
if ( aud_mono == 0xFFFF )
{
rising = 0;
}
else if (aud_mono == 0x0000 )
{
rising = 1;
}
if (rising)
{
aud_mono++;
}
else
{
aud_mono--;
}
}



Regards
Ben

mxxx
08-07-2013, 05:12 PM
It's an usb audio interface; it will have one wm8740 dac and one wm8786 adc, two pga4311 digital volume control, two fully differential opamps to convert from unbalanced to balanced inputs (wm8786 has balanced inputs), and an input impedance selector, to switch between line input and instrument input (for bass/guitar). BTW I don't think you can swap chips that easily, as the wm8731 is a codec while these chips are dac/adc only, and they need some glue circuitry to work as stated in the datasheets. I hope that the circuit I'm working on will serve as a good starting point for a nice audio interface based on the teensy. I repeat, I will post schematics asap.

ah, i figured effects pedal or something. curious to see what you come up with in terms of usb audio.

and sure, i'd have to redo my board somewhat, but since i'm not using the adcs on the wm8731 i might as well ... it's running off -12/12v so the "glue circuitry" won't be a big deal.

tni
08-07-2013, 07:20 PM
I also changed the audio data to uint16_t so it has a range starting at 0x0000. I don't know why, but this made it easier for me. Is it ok to do so and write the data to *pBuf, which is int16_t, without any conversion? It seems to work so far...

int16_t is signed two's complement data - exactly what you want for i2s. If your input data was int16_t, casting to uint16_t and then back will work fine. Unsigned input data needs to be converted to signed.

PaulStoffregen
08-15-2013, 11:46 PM
Over the last several days, while waiting for beta testers on the low power improvement and mac bug fix, I've started working on an audio API for Teensy.

I've been fiddling with DMA for dual-PWM output (a pair of 1:256 scaled resistors for high res). Much of this work will also carry over the I2S, but for the moment I'm concentrating on PWM while I refine the API. I hope to get to I2S in a couple weeks.

Here's a scope screenshot of the DMA-based PWM output...

827

The top 2 traces are PWM on pins 3 and 4. The red trace is a pulse from the interrupt that feeds more data into the DMA's buffer.

The blue trace is a simple R-C filtered output, using a 1K resistor on pin 3 and 240K resistor on pin 4, both driving a 10 nF capacitor. Only 4 interrupts occurred, so the buffer didn't get refreshed with new data on the far right side of the trace, where it begins replaying the stored buffer.

The math trace is a FFT. There a fair amount of DC from the initial part of the waveform, so the 1 kHz energy is pretty close to DC on this scale. In the middle, you can see energy at 43 and 45 kHz, and of course a lot of stuff around 88.2 kHz for the PWM carrier.

While this screenshot is just PWM waveforms, most of what I'm actually working on is the API and software infrastructure to make audio projects really easy. The API allows objects to be connected together, so audio data flows automatically. The idea is to free up the Arduino sketch from moving data around, so you can use Arduino style programming to control audio parameters and whatever other hardware you've connected.

For example, the code to create a continuous sine wave output:



#include "Audio.h"

AudioSineWave mysine;
AudioOutputPWM myout;

void setup() {
while(!Serial) ;
delay(20);
AudioMemory(8);
mysine.connect(myout);
mysine.frequency(1000);
mysine.update();
myout.update(); // these update functions will be called automatically from a timer ISR....
}

void loop() {

}


I know this message is something of a tease. I hope to have something ready for an alpha-test release in a week or two....

Ben
08-16-2013, 09:47 AM
This is great news. I understand the API will configure the DMA-Parts of the Kinetis to allow streams of audio data to flow into and out of the buffers to various peripheral devices such as the PWM-Generator, the I2S subsystem, or the DAC. Right? Do you plan to make the source of a stream selectable from an ADC, I2S input or an internal function generating the data?


The math trace is a FFT. There a fair amount of DC from the initial part of the waveform, [...]
The part of the waveform where the sine is played actually has a higher DC component than the "silence" in the first millisecond.
Maybe this question comes a bit early, but is the Sample rate equal to the PWM freq.? It'd be great if the PWM frequency could be made a multiple of the sample rate, thus reducing the required steepness (order) of the low pass filter.


I know this message is something of a tease.[...]
You bet! :D
This really is exciting news. There is so much going on in the DIY electronics community. Without people like you, Paul, we'd still be smashing rocks against each other...(well not literally maybe...) Thank You!

PaulStoffregen
08-16-2013, 11:29 AM
This is great news. I understand the API will configure the DMA-Parts of the Kinetis to allow streams of audio data to flow into and out of the buffers to various peripheral devices such as the PWM-Generator, the I2S subsystem, or the DAC. Right?


Yes.

Well, actually into buffers that get their pointers passed around between objects that do the sound processing operations you want. I'm designing for so much more than simply passing audio from an input to an output!



Do you plan to make the source of a stream selectable from an ADC, I2S input or an internal function generating the data?


You can create any number of audio stream objects, of course within the memory and CPU power available. Objects may be physical input or output, or purely data processing (filters, mixers, reverb, pitch shift, etc). Each object may define any number of inputs and outputs. They can be connected together in any way you like. Well, except of course in a feedback loop, which is illegal.

The API supports fanout, where an output can connect to inputs of several other objects. Internally, the audio data is only duplicated if an object needs write access to modify the data (eg, a filter). Each input can receive only a single stream (no fanin support), so if you want to connect multiple streams to an input (perhaps the input of the AudioOutputPWM object), you'd need to connect them to a mixer object, and then connect the mixer's output to AudioOutputPWM.

The API also tries to make creating new objects easier. It provides functions to receive audio blocks, either read-only or writable, to transmit blocks, and to allocate and release blocks from the memory pool. It also supports (or will soon support...) automatically calling the update function on every object automatically. That's the bit I'm doing now, and it might take me a while to really get the algorithm worked out for what order to call them all so the data flow is correct.

I've been working with a block size of 128 samples, or about 2.9 ms.




Maybe this question comes a bit early, but is the Sample rate equal to the PWM freq.? It'd be great if the PWM frequency could be made a multiple of the sample rate, thus reducing the required steepness (order) of the low pass filter.


The sample rate is 44.1 kHz (actually 48 MHz divided by 1088, which works out to be 44.117647 kHz). The PWM carrier is double the sample rate: 88.2 kHz.

I might try increasing to 176.4 kHz carrier at some point, but that's pushing the limits to very difficult timing. The DMA event occurs at the compare match, not the beginning of the cycle. It's unfortunate Freescale didn't provide a way to create trigger the DMA at the beginning of the PWM cycle (at least not any way I could find). Triggering within those last 16 clocks is dangerously close to the latency imposed by the DMA engine to load the transfer descriptor. If there's any bus arbitration, like another DMA channel, or the USB's DMA controller, or interrupt stack framing, or even a load or store multiple instruction, the DMA might get delayed until the first part of the next PWM cycle. Maybe that's not a huge problem if it's unlikely to occur, since it'll just cause one PWM pulse to be repeated instead of the correct output during that cycle.

PaulStoffregen
08-16-2013, 01:13 PM
Here's a couple more scope screenshots.

828

I've finally got the automatic updating working, so the sine wave keeps updating continuously now. :)

The FFT in this screenshot shows only the 20 kHz audio band. I believe that harmonic distortion at 2 kHz is mostly from my poorly matched resistors. At least I hope it is.

The red trace is the CPU usage.

829

Here you can see a zoom in to the CPU usage. The short pulses are the DMA interrupt. The long pulse is the updating interrupt, which runs at a low priority so the DMA and other interrupts can still work. During this update time, the sine wave data is being created. As more audio processing objects are created, this is where their CPU usage will go.

MickMad
08-30-2013, 03:21 PM
Hey guys, I just wanted to let you know that I just ordered a Mikroelektronica Audio Codec board just like yours, as soon as I get it I will begin testing hyple's library along with USB audio streaming; the board looks like a nice starting point to debug most of the features I need for my project :) it also has line in/out not soldered to anything, so they are still useable! Sweet! While waiting for the codec board to be shipped, I'll try as hard as I can to finish all the modules for my audio adc/dac board, trust me I'm halfway through it :) I already finished the input type selector that is going to be used with an unbalanced guitar to balanced out board, also done already ;)

MickMad
09-05-2013, 06:53 PM
Hello there, my Mikroe Audio Codec board just arrived; I'm trying the i2s_tx_dma_example but it is not working; of course I already set it to work with the Mikroe board by changing #IF 0 to #IF 1 at line 31. I also double checked the connections .... Anybody might know why?

EDIT: Sorry, I don't know why but the headphone plug didn't like the fact that I plugged it too deep, it works flawlessly! :D I cannot wait to try this with my USB audio class :D

daperl
09-05-2013, 09:14 PM
I cannot wait to try this with my USB audio class :D

So, when you plug your teensy 3 into a USB port, your computer will recognize the teensy as a USB Audio Device? And if so, will you then do isochronous transfers?

MickMad
09-06-2013, 12:05 AM
That's what I need to achieve. The big problem is that isochronous transfers are not yet implemented in teensyduino, and so there's some low level USB stuff to handle which is kinda painful. There's more info about this USB stuff on this other topic:

http://forum.pjrc.com/threads/24014-Problems-defining-an-USB-Audio-Class-Device-core-for-Teensy-3-0

daperl
09-06-2013, 01:36 PM
Thanks for the link. I'm sure many of us will be watching with great interest.

MickMad
09-18-2013, 12:05 PM
Hello guys, just a quick update:
I opened a new github repo containing all the software AND hardware I'm currently working on to make a full USB Audio device out of a Teensy 3.0.
I need to finish the USB-specific part first, and as soon as I get that right, I will start to integrate hyple's I2S library with USB. There's also some hardware stuff I'm doing that is complementary to the final design I'm trying to achieve.
Here's the link: http://github.com/MickMad/Teensy3-USBAudio

PanMillenium
12-05-2013, 12:08 PM
Hello,
I revive this topic to ask some help about a project and some pins assignement
but first a big thank you to MickMad for sharing the lib which gives a easy way to jumpin both DMA and I2S

I d like to use the I2S receiver and transmiter separately with their own bitclock, fully assynchronous and both in slave mode.
I thnk it is possible according to the chapter 3.9.5.2.2 which says: "The I2S/SAI transmitter and receiver support asynchronous bit clocks (BCLKs) that can
be generated internally from the audio master clock or supplied externally"

but there is still some mystery around MCLK, even if I dont need it in this project.

well, in fact the goal is to receive an SPDIF signal in I2S from a DIR like CS8416 into a 1k FIFO Buffer, using DMA upon reception
then the program will evaluate the frequency of this stream and will configure a digital oscilator over I2C, to generate the bitclock for outputing the data to a precise DAC via the I2S tranciever
that's a precise-reclocker, this concept is well known and already exist in some FPGA implementation.

One of my first question is about the pin out. I m lost on where are the 3+3 pins (receiver/transmiter) on the teensy3 layout, and if there is a (simple) solution to reroute them....
in the datasheet they are called
SAI_TX_DATA
SAI_TX_BCLK
SAI_TX_SYNC
SAI_RX_DATA
SAI_RX_BCLK
SAI_RX_SYNC

thank you

PanMillenium
12-05-2013, 12:52 PM
in the meantime I found chapter 11.4.1 explaining that a port pin can be configured in alternative mode, and for example SAI_TX_BCLK could be sent to either pin number 27, 41 or 46 if they were configured respectively in Alternative 6, 4, 6

my second problem is to understand how I could send the SAI_TX_CLK and SYNC to 4 different DAC not directly I2S compatible (AD1851) as they use a specific latch (LE) pin ... any thought welcome

bennigraf
12-10-2013, 07:42 PM
Hey guys!

I got an AudioCodecShield from Open Music Labs and tried to use it with my Teensy3 and the teensy-i2s library from this thread. I got it wired up and it kind of works, but there's a lot of clicking noise going on... lots of short impulses, quite hearable (I guess about -15db below the level of the music I'm passing through). I tried all of the examples that came with the library with the same result.

Is this the current state or should it work without the noise? If so, what could I have done wrong? (I got the wiring from the examples and also replaced the mk20...h. Is there anything I forgot?

Thanks a lot,
Benni.

sumotoy
12-19-2013, 10:52 PM
hello,
just get this board from ebay for around 12 buks w free shipping. It's based on uda1380 from ntx and it's 3.3v, 5v tolerant 24bit adc/dac codec I2S with I2C control. Still dunno the limitations since it still have to arrive but wonder if can be used with this librrary or the incoming Paul one.
On datasheet it suppose to decode MD,CD,Mp3 data stream but in some forum I've read they used as codec succesfully. It's a very cheap module, 4 time less expensive than audio codec shield and 24bit and if works can be an cheap entry level for experiments !?!?
http://www.ebay.com/itm/UDA1380-Board-Stereo-MD-CD-Mp3-Audio-Codec-Coder-Decoder-Development-Module-Kit-/251043491316 (http://www.ebay.it/itm/UDA1380-Board-Stereo-MD-CD-Mp3-Audio-Codec-Coder-Decoder-Development-Module-Kit-/251043491316?pt=LH_DefaultDomain_0&hash=item3a735badf4)

MickMad
12-20-2013, 02:14 PM
hello,
just get this board from ebay for around 12 buks w free shipping. It's based on uda1380 from ntx and it's 3.3v, 5v tolerant 24bit adc/dac codec I2S with I2C control. Still dunno the limitations since it still have to arrive but wonder if can be used with this librrary or the incoming Paul one.
On datasheet it suppose to decode MD,CD,Mp3 data stream but in some forum I've read they used as codec succesfully. It's a very cheap module, 4 time less expensive than audio codec shield and 24bit and if works can be an cheap entry level for experiments !?!?
http://www.ebay.com/itm/UDA1380-Board-Stereo-MD-CD-Mp3-Audio-Codec-Coder-Decoder-Development-Module-Kit-/251043491316 (http://www.ebay.it/itm/UDA1380-Board-Stereo-MD-CD-Mp3-Audio-Codec-Coder-Decoder-Development-Module-Kit-/251043491316?pt=LH_DefaultDomain_0&hash=item3a735badf4)

Well, as long as it is an I2S codec it should work with hpyle's library. You should check the datasheet for the uda1380 to see how to configure the chip via I2C. If I were you I'd start writing some UDA1380 control library, like the WM8731 library used to control the mikroe audio shield, while waiting for the shipping. Let us know when you receive it ;)

lkahler
02-15-2014, 03:30 PM
Has anyone had any luck getting 32-bit bit-depth working with hpyle's library with the Mikroe proto board? I've tried the following modifications with no luck:

In i2s.h:

I2S_IO_BIT_DEPTH 32
I2S_BUFFER_BIT_DEPTH 32

In i2s.cpp:

I2S0_TCR |= I2S0_TCR5_FBT(SYWD); // in i2s_transmit_init()

In the main project:

CODEC_INTERFACE_FLAGS (WM8731_INTERFACE_FORMAT(I2S) | WM8731_INTERFACE_WORDLEN(bits32) | WM8731_INTERFACE_MASTER)

I've also modified the dma_tx_callback to use a int32_t buffer.

The example, at 16-bit samples works, but I'd like to take advantage of the codec's full resolution.

Is there anything that I'm missing?

onehorse
11-17-2014, 10:25 PM
Hi, I read this thread with great interest and I am looking forward to trying this I2S proto-library myself.

I just want to make sure I understand this thread. I want to use I2S to take data from a pair (or more) of MEMS microphones that output three-wire I2S data. Are the three I2S ports on the Teensy 3.1 the following?:

SCK -> Teensy 9 (I2S0_TX_BCLK)
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25

and is DACL aka word select, MOSI is serial data, and SCL is CLK.

Are the Teensy 3.1 pin assignments arbitrary or is Teensy 3.1 pin 9 'the' I2S CLK port?

In other words, if I make a shield with microphones to mount on a Teensy 3.1, do I have to use pins 9, 3, and 4 for I2S CLK, SD, and WS? Are there alternative ports also (in addition to the bottom pads listed above, that is)?

I don't want to use a CODEC, but would like to perform FFT and otherwise analyze the sound picked up by the microphones. The application is echo location not audio playback per se.

Thanks.

PaulStoffregen
11-18-2014, 03:51 PM
This thread is very old, about code that was the early stepping stones to developing the audio library.

michaelpro
07-20-2016, 08:37 AM
I got it wrong in the examples' comments. You have SCK to pin 11 (MCLK), but it should be to the bit-clock, pin 9 (BCLK).

Here's the full list to check:
SCK -> Teensy 9 (I2S0_TX_BCLK)
MISO -> not connected for this transmit-only example
MOSI -> Teensy 3 (I2S0_TXD0). Can also be switched to pin 22.
ADCL -> not connected for this transmit-only example
DACL -> Teensy 4 (I2S0_TX_FS). Can also be switched to pin 23 or 25
SDA -> Teensy 18 (I2C0_SDA)
SCL -> Teensy 19 (I2C0_SCL)
3.3V -> Teensy 3.3v
GND -> Teensy GND

Hi,
What if I want to use this codec with Arduino Mega2560?
I connect like this:
wm8731's : sck Arduino : 13
mosi 11
sda sda (20)
scl scl (21)
3.3 3.3
gnd gnd

MickMad
07-20-2016, 09:58 AM
Hi,
What if I want to use this codec with Arduino Mega2560?
I connect like this:
wm8731's : sck Arduino : 13
mosi 11
sda sda (20)
scl scl (21)
3.3 3.3
gnd gnd

You can't.

The Mega2560 microcontroller does not have an I2S bus.

michaelpro
07-20-2016, 10:58 AM
You can't.

The Mega2560 microcontroller does not have an I2S bus.

Thank you for your feedback,
ok, but it has SPI, is this not enough for wm8731?
Which arduino module dou you suggest?

Ben
07-20-2016, 11:20 AM
Have a look at Paul's post on the Arduino forums: http://forum.arduino.cc/index.php?topic=283795.msg2003796#msg2003796

SPI is not enough for the WM8731. You need two interfaces, one for digital audio data (I2S) and one for controlling the CODEC IC (SPI or I2C, the IC supporst both).

Edit: Note that Pauls statement:

However, the Teensy Audio Library really only supports I2S master mode well, where Teensy sends BCLK, LRCLK and MCLK. If your DAC or other external circuitry will be creating the clocks, Teensy probably isn't the best option at this time.
is outdated. Teensy can run in I2S slave mode as well.

michaelpro
07-20-2016, 11:49 AM
Have a look at Paul's post on the Arduino forums: http://forum.arduino.cc/index.php?topic=283795.msg2003796#msg2003796

SPI is not enough for the WM8731. You need two interfaces, one for digital audio data (I2S) and one for controlling the CODEC IC (SPI or I2C, the IC supporst both).

Edit: Note that Pauls statement:

is outdated. Teensy can run in I2S slave mode as well.


Thank you,
This knowledge is so useful for me,
What about raspberry? Is it proper for this purpose?

Ben
07-20-2016, 11:57 AM
What about raspberry? Is it proper for this purpose?

I don't know if this is true for all models of Raspberries, but there seems to be an I2S bus on them:
7687
I don't know what this bus is capable of (bit depth, master/slave, sample rate), so you need to do some research yourself.

michaelpro
07-20-2016, 12:27 PM
I don't know if this is true for all models of Raspberries, but there seems to be an I2S bus on them:
7687
I don't know what this bus is capable of (bit depth, master/slave, sample rate), so you need to do some research yourself.

Thank you for feedback,
I will search it,
Another question : is there "loopback" function in wm8731? I read it's datasheet but I do not find anything related with loopback,
I simply want : speaking to mic and hearing voice from speaker,
Do you have any idea?

MickMad
07-20-2016, 01:10 PM
Thank you for feedback,
I will search it,
Another question : is there "loopback" function in wm8731? I read it's datasheet but I do not find anything related with loopback,
I simply want : speaking to mic and hearing voice from speaker,
Do you have any idea?

If the datasheet says anything about that, you could just configure the WM8731 internal registers for the loopback function using the serial control interface from your Arduino-whatever and be done. You won't need any actual audio data transfer if it is implemented directly in the CODEC :)

Anyway, Arduinos do not have I2S support; the only one that could have support are the ARM based ones. I for sure know that the new ATSAMD21 family of micros (used on the Arduino Zero) has an I2S interface because I'm working with it, but as far as I know no Arduino official library has yet been released for I2S.

Frank B
07-20-2016, 01:58 PM
What's the reason not to use a teensy 3.2 ? It is ready to use for I2S..

PaulStoffregen
07-23-2016, 02:40 PM
ok, but it has SPI, is this not enough for wm8731?


TL;DR = No, not really.

But if your definition of "enough" is sufficiently small, perhaps it is. The Open Music Labs Codec Shield used this approach, so if you really want to go to a lot of effort for very limited results on your Mega, that's the place to start. Don't believe the hype you might read on their site about that shield. Look at the examples in the actual library code (wiki.openmusiclabs.com/wiki/AudioCodecShield) to get an idea of what (very limited) capabilities are actually possible with this technique. If your application is similar to those oscillator & simple effects examples, then maybe you can make use of your Mega and WM8731 this way.

If you want to actually play sounds from a SD card, record, implement filters, synthesize & mix many waveforms, implement more sophisticated effects, or do analysis like FFT without big gaps, get a Teensy 3.2 and save your Arduino Mega for another project where you need lots of pins but not something so demanding as quality audio.