Forum Rule: Always post complete source code & details to reproduce any issue!
Page 3 of 4 FirstFirst 1 2 3 4 LastLast
Results 51 to 75 of 99

Thread: Teensy3 I2S with DMA

  1. #51
    Junior Member
    Join Date
    Jun 2013
    Posts
    7
    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?

  2. #52
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Quote Originally Posted by chrylis View Post
    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.

    Code:
    /*
      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;
    }
    
    }

  3. #53
    Member
    Join Date
    Jan 2013
    Location
    Salem, MA
    Posts
    49
    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

  4. #54
    Senior Member sumotoy's Avatar
    Join Date
    Nov 2012
    Location
    Venezia, Italia
    Posts
    421
    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?

  5. #55
    Senior Member
    Join Date
    Mar 2013
    Location
    Austin, TX
    Posts
    101
    Thanks so much, Hugh!

  6. #56
    Senior Member
    Join Date
    Feb 2013
    Posts
    563
    thanks indeed. helped me indefinitely with this little toy -


    Click image for larger version. 

Name:	9204766761_6fc16fb842_b.jpg 
Views:	422 
Size:	232.3 KB 
ID:	664


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

  7. #57
    Senior Member Ben's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    401
    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

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

    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
    // 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:
    Click image for larger version. 

Name:	image.jpg 
Views:	454 
Size:	107.4 KB 
ID:	728

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

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

    Edit: The Screenshots I promised:
    Click image for larger version. 

Name:	i2s1.png 
Views:	283 
Size:	17.7 KB 
ID:	739

    Click image for larger version. 

Name:	i2s2.png 
Views:	439 
Size:	25.9 KB 
ID:	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
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	image.jpg 
Views:	280 
Size:	64.5 KB 
ID:	722  
    Last edited by Ben; 07-30-2013 at 12:55 PM. Reason: added picture

  8. #58
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    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

  9. #59
    Senior Member Ben's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    401
    Quote Originally Posted by MickMad View Post
    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

  10. #60
    Member
    Join Date
    Jan 2013
    Location
    Salem, MA
    Posts
    49
    @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

  11. #61
    Member
    Join Date
    Jan 2013
    Location
    Salem, MA
    Posts
    49
    @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.

  12. #62
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    402
    Heres my code for configuring Teensy 3 I2S clocks, has been tested with an WM8762

    Code:
    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
    Last edited by mlu; 08-01-2013 at 04:09 PM.

  13. #63
    Senior Member Ben's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    401
    Quote Originally Posted by hpyle View Post
    @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.

    Quote Originally Posted by hpyle View Post
    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
    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?

  14. #64
    Senior Member
    Join Date
    Feb 2013
    Posts
    563
    Quote Originally Posted by MickMad View Post
    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

  15. #65
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by mxxx View Post
    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 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

  16. #66
    Senior Member
    Join Date
    Feb 2013
    Posts
    563
    Quote Originally Posted by MickMad View Post
    I still have nothing running, actually 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.

  17. #67
    Junior Member
    Join Date
    Jun 2013
    Posts
    4

    problems with i2s_rx_dma Mikroe

    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:

    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/machines.../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

  18. #68
    Senior Member MickMad's Avatar
    Join Date
    Feb 2013
    Location
    Italy
    Posts
    163
    Quote Originally Posted by mxxx View Post
    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.

  19. #69
    Senior Member Ben's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    401
    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:
    Click image for larger version. 

Name:	scope_0.png 
Views:	752 
Size:	21.5 KB 
ID:	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

  20. #70
    Senior Member
    Join Date
    Feb 2013
    Posts
    563
    Quote Originally Posted by MickMad View Post
    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.

  21. #71
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Quote Originally Posted by Ben View Post
    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.

  22. #72
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,008
    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...

    Click image for larger version. 

Name:	scope_5.png 
Views:	325 
Size:	68.2 KB 
ID:	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:

    Code:
    #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....

  23. #73
    Senior Member Ben's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    401
    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?
    Quote Originally Posted by PaulStoffregen View Post
    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.
    Quote Originally Posted by PaulStoffregen View Post
    I know this message is something of a tease.[...]
    You bet!
    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!

  24. #74
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,008
    Quote Originally Posted by Ben View Post
    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.
    Last edited by PaulStoffregen; 08-16-2013 at 11:51 AM.

  25. #75
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,008
    Here's a couple more scope screenshots.

    Click image for larger version. 

Name:	scope_6.png 
Views:	251 
Size:	71.4 KB 
ID:	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.

    Click image for larger version. 

Name:	scope_7.png 
Views:	460 
Size:	44.7 KB 
ID:	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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •