Browse Source

Merge pull request #1 from PaulStoffregen/master

update
dds
Frank 4 years ago
parent
commit
6decc27093
No account linked to committer's email address
37 changed files with 2094 additions and 286 deletions
  1. +4
    -0
      Audio.h
  2. +5
    -0
      control_cs42448.cpp
  3. +4
    -1
      effect_delay.h
  4. +149
    -0
      examples/Analysis/PeakAndRMSMeter8Channel/PeakAndRMSMeter8Channel.ino
  5. +12
    -11
      examples/HardwareTesting/WM8731MikroSine/WM8731MikroSine.ino
  6. +1
    -1
      examples/Recorder/Recorder.ino
  7. +20
    -20
      gui/index.html
  8. +29
    -16
      input_i2s.cpp
  9. +6
    -9
      input_i2s2.cpp
  10. +247
    -0
      input_i2s_hex.cpp
  11. +54
    -0
      input_i2s_hex.h
  12. +263
    -0
      input_i2s_oct.cpp
  13. +56
    -0
      input_i2s_oct.h
  14. +53
    -2
      input_i2s_quad.cpp
  15. +10
    -5
      input_pdm.cpp
  16. +4
    -5
      input_tdm.cpp
  17. +2
    -2
      input_tdm2.cpp
  18. +4
    -0
      keywords.txt
  19. +3
    -0
      output_adat.cpp
  20. +86
    -136
      output_i2s.cpp
  21. +8
    -0
      output_i2s.h
  22. +26
    -20
      output_i2s2.cpp
  23. +355
    -0
      output_i2s_hex.cpp
  24. +65
    -0
      output_i2s_hex.h
  25. +419
    -0
      output_i2s_oct.cpp
  26. +71
    -0
      output_i2s_oct.h
  27. +60
    -12
      output_i2s_quad.cpp
  28. +16
    -6
      output_pt8211.cpp
  29. +9
    -4
      output_spdif.cpp
  30. +24
    -15
      output_tdm.cpp
  31. +7
    -11
      output_tdm2.cpp
  32. +2
    -2
      play_queue.cpp
  33. +7
    -1
      play_queue.h
  34. +4
    -4
      record_queue.cpp
  35. +7
    -1
      record_queue.h
  36. +1
    -1
      utility/imxrt_hw.cpp
  37. +1
    -1
      utility/imxrt_hw.h

+ 4
- 0
Audio.h View File

#include "input_i2s.h" #include "input_i2s.h"
#include "input_i2s2.h" #include "input_i2s2.h"
#include "input_i2s_quad.h" #include "input_i2s_quad.h"
#include "input_i2s_hex.h"
#include "input_i2s_oct.h"
#include "input_tdm.h" #include "input_tdm.h"
#include "input_tdm2.h" #include "input_tdm2.h"
#include "input_pdm.h" #include "input_pdm.h"
#include "output_i2s.h" #include "output_i2s.h"
#include "output_i2s2.h" #include "output_i2s2.h"
#include "output_i2s_quad.h" #include "output_i2s_quad.h"
#include "output_i2s_hex.h"
#include "output_i2s_oct.h"
#include "output_mqs.h" #include "output_mqs.h"
#include "output_pwm.h" #include "output_pwm.h"
#include "output_spdif.h" #include "output_spdif.h"

+ 5
- 0
control_cs42448.cpp View File

// then un-mute the DACs. // then un-mute the DACs.
// 9. Normal operation begins. // 9. Normal operation begins.


// Some people have found their CS42448 goes into a strange mode where VQ is 1.25V
// instead of the normal 2.5V. Apparently there is a workaround for this problem
// which involves writing to an undocumented bit. Details here:
// https://forum.pjrc.com/threads/41371?p=215881&viewfull=1#post215881

static const uint8_t default_config[] = { static const uint8_t default_config[] = {
0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max 0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max
0x76, // CS42448_Interface_Formats = TDM mode 0x76, // CS42448_Interface_Formats = TDM mode

+ 4
- 1
effect_delay.h View File

#include "AudioStream.h" #include "AudioStream.h"
#include "utility/dspinst.h" #include "utility/dspinst.h"


#if defined(__MK66FX1M0__)
#if defined(__IMXRT1062__)
// 4.00 second maximum on Teensy 4.0
#define DELAY_QUEUE_SIZE (176512 / AUDIO_BLOCK_SAMPLES)
#elif defined(__MK66FX1M0__)
// 2.41 second maximum on Teensy 3.6 // 2.41 second maximum on Teensy 3.6
#define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SAMPLES) #define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SAMPLES)
#elif defined(__MK64FX512__) #elif defined(__MK64FX512__)

+ 149
- 0
examples/Analysis/PeakAndRMSMeter8Channel/PeakAndRMSMeter8Channel.ino View File

/* Show levels (RMS & Peak) for 8 I2S microphone inputs
*
* Connect 8 INMP411 I2S microphones to Teensy 4.0
* Pin 8 SD on mics #1 and #2
* Pin 6 SD on mics #3 and #4
* Pin 9 SD on mics #5 and #6
* Pin 32 SD on mics #7 and #8
* Pin 20 WS on all mics
* Pin 21 SCK on all mics
*
* Each mic needs GND to Teensy GND, VCC to Teensy 3.3V.
* Connect L/R to GND on the odd numbered mics
* and L/R to 3.3V on the even numbered mics.
*
* Optional - connect a Teensy Audio Shield or other I2S
* output device, but do not connect it to pin 8, because
* the INMP411 mics #1 & #2 send their signal to pin 8.
*
* This example code is in the public domain
*/

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// If INMP411 mics are not available, the audio shield mic can
// be used, but it will send a single signal to inputs #1 & #2
// (if connected to pin 8).
//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC;

AudioInputI2SOct audioInput; // audio shield: mic or line-in
AudioAmplifier amp1;
AudioAmplifier amp2;
AudioAmplifier amp3;
AudioAmplifier amp4;
AudioAmplifier amp5;
AudioAmplifier amp6;
AudioAmplifier amp7;
AudioAmplifier amp8;
AudioAnalyzeRMS rms1;
AudioAnalyzeRMS rms2;
AudioAnalyzeRMS rms3;
AudioAnalyzeRMS rms4;
AudioAnalyzeRMS rms5;
AudioAnalyzeRMS rms6;
AudioAnalyzeRMS rms7;
AudioAnalyzeRMS rms8;
AudioAnalyzePeak peak1;
AudioAnalyzePeak peak2;
AudioAnalyzePeak peak3;
AudioAnalyzePeak peak4;
AudioAnalyzePeak peak5;
AudioAnalyzePeak peak6;
AudioAnalyzePeak peak7;
AudioAnalyzePeak peak8;
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Send all microphone signals to amps
AudioConnection r1(audioInput, 0, amp1, 0);
AudioConnection r2(audioInput, 1, amp2, 0);
AudioConnection r3(audioInput, 2, amp3, 0);
AudioConnection r4(audioInput, 3, amp4, 0);
AudioConnection r5(audioInput, 4, amp5, 0);
AudioConnection r6(audioInput, 5, amp6, 0);
AudioConnection r7(audioInput, 6, amp7, 0);
AudioConnection r8(audioInput, 7, amp8, 0);

// Connect the amps to RMS and Peak analyzers
AudioConnection a1(amp1, 0, rms1, 0);
AudioConnection a2(amp2, 0, rms2, 0);
AudioConnection a3(amp3, 0, rms3, 0);
AudioConnection a4(amp4, 0, rms4, 0);
AudioConnection a5(amp5, 0, rms5, 0);
AudioConnection a6(amp6, 0, rms6, 0);
AudioConnection a7(amp7, 0, rms7, 0);
AudioConnection a8(amp8, 0, rms8, 0);
AudioConnection p1(amp1, 0, peak1, 0);
AudioConnection p2(amp2, 0, peak2, 0);
AudioConnection p3(amp3, 0, peak3, 0);
AudioConnection p4(amp4, 0, peak4, 0);
AudioConnection p5(amp5, 0, peak5, 0);
AudioConnection p6(amp6, 0, peak6, 0);
AudioConnection p7(amp7, 0, peak7, 0);
AudioConnection p8(amp8, 0, peak8, 0);

// Also connect 2 of the amps to an I2S output (Pin 8)
// to be able to listen to the sound.
AudioConnection c10(amp1, 0, audioOutput, 0);
AudioConnection c11(amp2, 0, audioOutput, 1);
AudioControlSGTL5000 audioShield;


void setup() {
AudioMemory(26);
audioShield.enable();
audioShield.inputSelect(myInput);
audioShield.micGain(12);
audioShield.volume(0.5);
const float microphoneAmplification = 72.0;
amp1.gain(microphoneAmplification);
amp2.gain(microphoneAmplification);
amp3.gain(microphoneAmplification);
amp4.gain(microphoneAmplification);
amp5.gain(microphoneAmplification);
amp6.gain(microphoneAmplification);
amp7.gain(microphoneAmplification);
amp8.gain(microphoneAmplification);
Serial.begin(9600);
}

elapsedMillis fps;

void loop() {
if (fps > 24) {
fps = 0;
const int digits = 8;
print_bar(1, rms1.read(), peak1.readPeakToPeak()/2, digits);
print_bar(2, rms2.read(), peak2.readPeakToPeak()/2, digits);
print_bar(3, rms3.read(), peak3.readPeakToPeak()/2, digits);
print_bar(4, rms4.read(), peak4.readPeakToPeak()/2, digits);
print_bar(5, rms5.read(), peak5.readPeakToPeak()/2, digits);
print_bar(6, rms6.read(), peak6.readPeakToPeak()/2, digits);
print_bar(7, rms7.read(), peak7.readPeakToPeak()/2, digits);
print_bar(8, rms8.read(), peak8.readPeakToPeak()/2, digits);
Serial.println();
}
}

void print_bar(int in, float rms, float peak, int digits) {
Serial.print(in);
Serial.print(":");
int num_rms = roundf(rms * digits);
int num_peak = roundf(peak * digits);
for (int i=0; i < digits; i++) {
if (i < num_rms) {
Serial.print("*");
} else if (i < num_peak) {
Serial.print(">");
} else {
Serial.print(" ");
}
}
Serial.print(" ");
}

+ 12
- 11
examples/HardwareTesting/WM8731MikroSine/WM8731MikroSine.ino View File

// //
// Recommended connections: // Recommended connections:
// //
// Mikroe Teensy 3.1
// ------ ----------
// SCK 9
// MISO 13
// MOSI 22
// ADCL 23 (yes, ADCL & DACL connect together)
// DACL 23
// SDA 18
// SCL 19
// 3.3V +3.3V
// GND GND
// MikroE Teensy 3 Teensy 4
// ------ -------- --------
// SCK 9 21
// MISO 13 8
// MOSI 22 7
// ADCL 23 20 (yes, ADCL & DACL connect together)
// DACL 23 20
// SDA 18 18
// SCL 19 19
// 3.3V +3.3V +3.3V
// GND GND GND
// //
// For connection using I2S master mode (WM8731 in slave mode, with MCLK): // For connection using I2S master mode (WM8731 in slave mode, with MCLK):
// https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733 // https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733




void setup() { void setup() {
delay(1000); // allow the WM7831 extra time to power up
wm8731m1.enable(); wm8731m1.enable();


AudioMemory(15); AudioMemory(15);

+ 1
- 1
examples/Recorder/Recorder.ino View File

memcpy(buffer+256, queue1.readBuffer(), 256); memcpy(buffer+256, queue1.readBuffer(), 256);
queue1.freeBuffer(); queue1.freeBuffer();
// write all 512 bytes to the SD card // write all 512 bytes to the SD card
elapsedMicros usec = 0;
//elapsedMicros usec = 0;
frec.write(buffer, 512); frec.write(buffer, 512);
// Uncomment these lines to see how long SD writes // Uncomment these lines to see how long SD writes
// are taking. A pair of audio blocks arrives every // are taking. A pair of audio blocks arrives every

+ 20
- 20
gui/index.html View File

<p>The I2S signals are used in "master" mode, where Teensy creates <p>The I2S signals are used in "master" mode, where Teensy creates
all 3 clock signals and controls all data timing.</p> all 3 clock signals and controls all data timing.</p>
<table class=doc align=center cellpadding=3> <table class=doc align=center cellpadding=3>
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>13</td><td>RX</td><td>Input</td></tr>
<tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr>
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>13</td><td align=center>8</td><td>RX</td><td>Input</td></tr>
<tr class=odd><td align=center>23</td><td align=center>20</td><td>LRCLK</td><td>Output</td></tr>
</table> </table>
<p>Audio from <p>Audio from
master mode I2S may be used in the same project as ADC, DAC and master mode I2S may be used in the same project as ADC, DAC and
<a href="https://forum.pjrc.com/threads/42894">Invensense ICS-52000 microphones</a>. <a href="https://forum.pjrc.com/threads/42894">Invensense ICS-52000 microphones</a>.
</p> </p>
<table class=doc align=center cellpadding=3> <table class=doc align=center cellpadding=3>
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr>
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr>
<tr class=odd><td align=center>13</td><td>RX</td><td>Input, 11.3 Mbit/sec</td></tr>
<tr class=odd><td align=center>23</td><td>FS</td><td>Output</td></tr>
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr>
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr>
<tr class=odd><td align=center>13</td><td align=center>8</td><td>RX</td><td>Input, 11.3 Mbit/sec</td></tr>
<tr class=odd><td align=center>23</td><td align=center>21</td><td>FS</td><td>Output</td></tr>
</table> </table>
<p>Audio from <p>Audio from
master mode TDM may be used in the same project as ADC, DAC and master mode TDM may be used in the same project as ADC, DAC and
<p>The I2S signals are used in "master" mode, where Teensy creates <p>The I2S signals are used in "master" mode, where Teensy creates
all 3 clock signals and controls all data timing.</p> all 3 clock signals and controls all data timing.</p>
<table class=doc align=center cellpadding=3> <table class=doc align=center cellpadding=3>
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>22</td><td>TX</td><td>Output</td></tr>
<tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr>
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>22</td><td align=center>7</td><td>TX</td><td>Output</td></tr>
<tr class=odd><td align=center>23</td><td align=center>20</td><td>LRCLK</td><td>Output</td></tr>
</table> </table>
<p>Audio from <p>Audio from
master mode I2S may be used in the same project as ADC, DAC and master mode I2S may be used in the same project as ADC, DAC and
CS42448 Circuit Board</a>. CS42448 Circuit Board</a>.
</p> </p>
<table class=doc align=center cellpadding=3> <table class=doc align=center cellpadding=3>
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr>
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr>
<tr class=odd><td align=center>22</td><td>TX</td><td>Output, 11.3 Mbit/sec</td></tr>
<tr class=odd><td align=center>23</td><td>WS</td><td>Output</td></tr>
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr>
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr>
<tr class=odd><td align=center>22</td><td align=center>7</td><td>TX</td><td>Output, 11.3 Mbit/sec</td></tr>
<tr class=odd><td align=center>23</td><td align=center>20</td><td>WS</td><td>Output</td></tr>
</table> </table>
<p>Audio from <p>Audio from
master mode TDM may be used in the same project as ADC, DAC and master mode TDM may be used in the same project as ADC, DAC and

+ 29
- 16
input_i2s.cpp View File



I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)


#if defined(__IMXRT1062__)
#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //1:RX_DATA0 CORE_PIN8_CONFIG = 3; //1:RX_DATA0
#elif defined(__IMXRT1052__)
CORE_PIN7_CONFIG = 3; //1:RX_DATA0
#endif
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0+2);

dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2);
dma.TCD->SOFF = 0; dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);


I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;

I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
#endif #endif

update_responsibility = update_setup(); update_responsibility = update_setup();
dma.enable(); dma.enable();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
//pinMode(13, OUTPUT);
} }


void AudioInputI2S::isr(void) void AudioInputI2S::isr(void)
int16_t *dest_left, *dest_right; int16_t *dest_left, *dest_right;
audio_block_t *left, *right; audio_block_t *left, *right;


//digitalWriteFast(13, HIGH);
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(KINETISK) || defined(__IMXRT1062__)
daddr = (uint32_t)(dma.TCD->DADDR); daddr = (uint32_t)(dma.TCD->DADDR);
#endif #endif
dma.clearInterrupt(); dma.clearInterrupt();
//Serial.println("isr");


if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer // DMA is receiving to the first half of the buffer
} while (src < end); } while (src < end);
} }
} }
//digitalWriteFast(13, LOW);
} }




I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
#endif


#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //1:RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;

dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
dma.enable();

I2S1_RCSR = 0;
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
#endif
} }



+ 6
- 9
input_i2s2.cpp View File

*/ */




#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1062__)
#include <Arduino.h> #include <Arduino.h>
#include "input_i2s2.h" #include "input_i2s2.h"
#include "output_i2s2.h" #include "output_i2s2.h"
// TODO: should we set & clear the I2S_RCSR_SR bit here? // TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputI2S2::config_i2s(); AudioOutputI2S2::config_i2s();


CORE_PIN33_CONFIG = 2; //2:RX_DATA0
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
CORE_PIN5_CONFIG = 2; //EMC_08, 2=SAI2_RX_DATA, page 434
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; // 0=GPIO_EMC_08_ALT2, page 876


dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2); dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2);
dma.TCD->SOFF = 0; dma.TCD->SOFF = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2; dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
dma.enable();


I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;
I2S2_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; // page 2099
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // page 2087


update_responsibility = update_setup(); update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
//pinMode(13, OUTPUT);
} }


void AudioInputI2S2::isr(void) void AudioInputI2S2::isr(void)
int16_t *dest_left, *dest_right; int16_t *dest_left, *dest_right;
audio_block_t *left, *right; audio_block_t *left, *right;


//digitalWriteFast(13, HIGH);
daddr = (uint32_t)(dma.TCD->DADDR); daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt(); dma.clearInterrupt();


} while (src < end); } while (src < end);
} }
} }
//digitalWriteFast(13, LOW);
} }





+ 247
- 0
input_i2s_hex.cpp View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <Arduino.h>
#include "input_i2s_hex.h"
#include "output_i2s.h"

DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*3];
audio_block_t * AudioInputI2SHex::block_ch1 = NULL;
audio_block_t * AudioInputI2SHex::block_ch2 = NULL;
audio_block_t * AudioInputI2SHex::block_ch3 = NULL;
audio_block_t * AudioInputI2SHex::block_ch4 = NULL;
audio_block_t * AudioInputI2SHex::block_ch5 = NULL;
audio_block_t * AudioInputI2SHex::block_ch6 = NULL;
uint16_t AudioInputI2SHex::block_offset = 0;
bool AudioInputI2SHex::update_responsibility = false;
DMAChannel AudioInputI2SHex::dma(false);

#if defined(__IMXRT1062__)

void AudioInputI2SHex::begin(void)
{
dma.begin(true); // Allocate the DMA channel first

const int pinoffset = 0; // TODO: make this configurable...
AudioOutputI2S::config_i2s();
I2S1_RCR3 = I2S_RCR3_RCE_3CH << pinoffset;
switch (pinoffset) {
case 0:
CORE_PIN8_CONFIG = 3;
CORE_PIN6_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874
break;
case 1:
CORE_PIN6_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
CORE_PIN32_CONFIG = 3;
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874
IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875
break;
}
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2 + pinoffset * 4);
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-12) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(6);
dma.TCD->SLAST = -12;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);

//I2S1_RCSR = 0;
//I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset;
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
}

void AudioInputI2SHex::isr(void)
{
uint32_t daddr, offset;
const int16_t *src;
int16_t *dest1, *dest2, *dest3, *dest4, *dest5, *dest6;

//digitalWriteFast(3, HIGH);
daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();

if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int16_t *)((uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2);
if (update_responsibility) update_all();
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int16_t *)&i2s_rx_buffer[0];
}
if (block_ch1) {
offset = block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES/2) {
arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2);
block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
dest1 = &(block_ch1->data[offset]);
dest2 = &(block_ch2->data[offset]);
dest3 = &(block_ch3->data[offset]);
dest4 = &(block_ch4->data[offset]);
dest5 = &(block_ch5->data[offset]);
dest6 = &(block_ch6->data[offset]);
for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
*dest1++ = *src++;
*dest3++ = *src++;
*dest5++ = *src++;
*dest2++ = *src++;
*dest4++ = *src++;
*dest6++ = *src++;
}
}
}
//digitalWriteFast(3, LOW);
}


void AudioInputI2SHex::update(void)
{
audio_block_t *new1, *new2, *new3, *new4, *new5, *new6;
audio_block_t *out1, *out2, *out3, *out4, *out5, *out6;

// allocate 6 new blocks
new1 = allocate();
new2 = allocate();
new3 = allocate();
new4 = allocate();
new5 = allocate();
new6 = allocate();
// but if any fails, allocate none
if (!new1 || !new2 || !new3 || !new4 || !new5 || !new6) {
if (new1) {
release(new1);
new1 = NULL;
}
if (new2) {
release(new2);
new2 = NULL;
}
if (new3) {
release(new3);
new3 = NULL;
}
if (new4) {
release(new4);
new4 = NULL;
}
if (new5) {
release(new5);
new5 = NULL;
}
if (new6) {
release(new6);
new6 = NULL;
}
}
__disable_irq();
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
// the DMA filled 4 blocks, so grab them and get the
// 4 new blocks to the DMA, as quickly as possible
out1 = block_ch1;
block_ch1 = new1;
out2 = block_ch2;
block_ch2 = new2;
out3 = block_ch3;
block_ch3 = new3;
out4 = block_ch4;
block_ch4 = new4;
out5 = block_ch5;
block_ch5 = new5;
out6 = block_ch6;
block_ch6 = new6;
block_offset = 0;
__enable_irq();
// then transmit the DMA's former blocks
transmit(out1, 0);
release(out1);
transmit(out2, 1);
release(out2);
transmit(out3, 2);
release(out3);
transmit(out4, 3);
release(out4);
transmit(out5, 4);
release(out5);
transmit(out6, 5);
release(out6);
} else if (new1 != NULL) {
// the DMA didn't fill blocks, but we allocated blocks
if (block_ch1 == NULL) {
// the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated
block_ch1 = new1;
block_ch2 = new2;
block_ch3 = new3;
block_ch4 = new4;
block_ch5 = new5;
block_ch6 = new6;
block_offset = 0;
__enable_irq();
} else {
// the DMA already has blocks, doesn't need these
__enable_irq();
release(new1);
release(new2);
release(new3);
release(new4);
release(new5);
release(new6);
}
} else {
// The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
}
}

#else // not supported

void AudioInputI2SHex::begin(void)
{
}



#endif


+ 54
- 0
input_i2s_hex.h View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef _input_i2s_hex_h_
#define _input_i2s_hex_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioInputI2SHex : public AudioStream
{
public:
AudioInputI2SHex(void) : AudioStream(0, NULL) { begin(); }
virtual void update(void);
void begin(void);
private:
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
static audio_block_t *block_ch1;
static audio_block_t *block_ch2;
static audio_block_t *block_ch3;
static audio_block_t *block_ch4;
static audio_block_t *block_ch5;
static audio_block_t *block_ch6;
static uint16_t block_offset;
};


#endif

+ 263
- 0
input_i2s_oct.cpp View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <Arduino.h>
#include "input_i2s_oct.h"
#include "output_i2s.h"

DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*4];
audio_block_t * AudioInputI2SOct::block_ch1 = NULL;
audio_block_t * AudioInputI2SOct::block_ch2 = NULL;
audio_block_t * AudioInputI2SOct::block_ch3 = NULL;
audio_block_t * AudioInputI2SOct::block_ch4 = NULL;
audio_block_t * AudioInputI2SOct::block_ch5 = NULL;
audio_block_t * AudioInputI2SOct::block_ch6 = NULL;
audio_block_t * AudioInputI2SOct::block_ch7 = NULL;
audio_block_t * AudioInputI2SOct::block_ch8 = NULL;
uint16_t AudioInputI2SOct::block_offset = 0;
bool AudioInputI2SOct::update_responsibility = false;
DMAChannel AudioInputI2SOct::dma(false);

#if defined(__IMXRT1062__)

void AudioInputI2SOct::begin(void)
{
dma.begin(true); // Allocate the DMA channel first

AudioOutputI2S::config_i2s();
I2S1_RCR3 = I2S_RCR3_RCE_4CH;
CORE_PIN8_CONFIG = 3;
CORE_PIN6_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
CORE_PIN32_CONFIG = 3;
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874
IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875

dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2);
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-16) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
dma.TCD->SLAST = -16;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);

I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
}

void AudioInputI2SOct::isr(void)
{
uint32_t daddr, offset;
const int16_t *src;
int16_t *dest1, *dest2, *dest3, *dest4, *dest5, *dest6, *dest7, *dest8;

//digitalWriteFast(3, HIGH);
daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();

if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int16_t *)((uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2);
if (update_responsibility) update_all();
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int16_t *)&i2s_rx_buffer[0];
}
if (block_ch1) {
offset = block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES/2) {
arm_dcache_delete((void *)src, sizeof(i2s_rx_buffer) / 2);
block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
dest1 = &(block_ch1->data[offset]);
dest2 = &(block_ch2->data[offset]);
dest3 = &(block_ch3->data[offset]);
dest4 = &(block_ch4->data[offset]);
dest5 = &(block_ch5->data[offset]);
dest6 = &(block_ch6->data[offset]);
dest7 = &(block_ch7->data[offset]);
dest8 = &(block_ch8->data[offset]);
for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
*dest1++ = *src++;
*dest3++ = *src++;
*dest5++ = *src++;
*dest7++ = *src++;
*dest2++ = *src++;
*dest4++ = *src++;
*dest6++ = *src++;
*dest8++ = *src++;
}
}
}
//digitalWriteFast(3, LOW);
}


void AudioInputI2SOct::update(void)
{
audio_block_t *new1, *new2, *new3, *new4, *new5, *new6, *new7, *new8;
audio_block_t *out1, *out2, *out3, *out4, *out5, *out6, *out7, *out8;

// allocate 8 new blocks
new1 = allocate();
new2 = allocate();
new3 = allocate();
new4 = allocate();
new5 = allocate();
new6 = allocate();
new7 = allocate();
new8 = allocate();
// but if any fails, allocate none
if (!new1 || !new2 || !new3 || !new4 || !new5 || !new6 || !new7 || !new8) {
if (new1) {
release(new1);
new1 = NULL;
}
if (new2) {
release(new2);
new2 = NULL;
}
if (new3) {
release(new3);
new3 = NULL;
}
if (new4) {
release(new4);
new4 = NULL;
}
if (new5) {
release(new5);
new5 = NULL;
}
if (new6) {
release(new6);
new6 = NULL;
}
if (new7) {
release(new7);
new7 = NULL;
}
if (new8) {
release(new8);
new8 = NULL;
}
}
__disable_irq();
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
// the DMA filled 4 blocks, so grab them and get the
// 8 new blocks to the DMA, as quickly as possible
out1 = block_ch1;
block_ch1 = new1;
out2 = block_ch2;
block_ch2 = new2;
out3 = block_ch3;
block_ch3 = new3;
out4 = block_ch4;
block_ch4 = new4;
out5 = block_ch5;
block_ch5 = new5;
out6 = block_ch6;
block_ch6 = new6;
out7 = block_ch7;
block_ch7 = new7;
out8 = block_ch8;
block_ch8 = new8;
block_offset = 0;
__enable_irq();
// then transmit the DMA's former blocks
transmit(out1, 0);
release(out1);
transmit(out2, 1);
release(out2);
transmit(out3, 2);
release(out3);
transmit(out4, 3);
release(out4);
transmit(out5, 4);
release(out5);
transmit(out6, 5);
release(out6);
transmit(out7, 6);
release(out7);
transmit(out8, 7);
release(out8);
} else if (new1 != NULL) {
// the DMA didn't fill blocks, but we allocated blocks
if (block_ch1 == NULL) {
// the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated
block_ch1 = new1;
block_ch2 = new2;
block_ch3 = new3;
block_ch4 = new4;
block_ch5 = new5;
block_ch6 = new6;
block_ch7 = new7;
block_ch8 = new8;
block_offset = 0;
__enable_irq();
} else {
// the DMA already has blocks, doesn't need these
__enable_irq();
release(new1);
release(new2);
release(new3);
release(new4);
release(new5);
release(new6);
release(new7);
release(new8);
}
} else {
// The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
}
}

#else // not supported

void AudioInputI2SOct::begin(void)
{
}



#endif


+ 56
- 0
input_i2s_oct.h View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef _input_i2s_oct_h_
#define _input_i2s_oct_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioInputI2SOct : public AudioStream
{
public:
AudioInputI2SOct(void) : AudioStream(0, NULL) { begin(); }
virtual void update(void);
void begin(void);
private:
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
static audio_block_t *block_ch1;
static audio_block_t *block_ch2;
static audio_block_t *block_ch3;
static audio_block_t *block_ch4;
static audio_block_t *block_ch5;
static audio_block_t *block_ch6;
static audio_block_t *block_ch7;
static audio_block_t *block_ch8;
static uint16_t block_offset;
};


#endif

+ 53
- 2
input_i2s_quad.cpp View File

#include <Arduino.h> #include <Arduino.h>
#include "input_i2s_quad.h" #include "input_i2s_quad.h"
#include "output_i2s_quad.h" #include "output_i2s_quad.h"
#include "output_i2s.h"


DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*2];
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*2];
audio_block_t * AudioInputI2SQuad::block_ch1 = NULL; audio_block_t * AudioInputI2SQuad::block_ch1 = NULL;
audio_block_t * AudioInputI2SQuad::block_ch2 = NULL; audio_block_t * AudioInputI2SQuad::block_ch2 = NULL;
audio_block_t * AudioInputI2SQuad::block_ch3 = NULL; audio_block_t * AudioInputI2SQuad::block_ch3 = NULL;
bool AudioInputI2SQuad::update_responsibility = false; bool AudioInputI2SQuad::update_responsibility = false;
DMAChannel AudioInputI2SQuad::dma(false); DMAChannel AudioInputI2SQuad::dma(false);


#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__)


void AudioInputI2SQuad::begin(void) void AudioInputI2SQuad::begin(void)
{ {
dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first


#if defined(KINETISK)
// TODO: should we set & clear the I2S_RCSR_SR bit here? // TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputI2SQuad::config_i2s(); AudioOutputI2SQuad::config_i2s();


I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr); dma.attachInterrupt(isr);

#elif defined(__IMXRT1062__)
const int pinoffset = 0; // TODO: make this configurable...
AudioOutputI2S::config_i2s();
I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset;
switch (pinoffset) {
case 0:
CORE_PIN8_CONFIG = 3;
CORE_PIN6_CONFIG = 3;
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
break;
case 1:
CORE_PIN6_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874
break;
case 2:
CORE_PIN9_CONFIG = 3;
CORE_PIN32_CONFIG = 3;
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874
IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875
break;
}
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2 + pinoffset * 4);
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(4);
dma.TCD->SLAST = -8;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);

I2S1_RCSR = 0;
I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset;

I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
#endif
} }


void AudioInputI2SQuad::isr(void) void AudioInputI2SQuad::isr(void)
if (block_ch1) { if (block_ch1) {
offset = block_offset; offset = block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES/2) { if (offset <= AUDIO_BLOCK_SAMPLES/2) {
arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2);
block_offset = offset + AUDIO_BLOCK_SAMPLES/2; block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
dest1 = &(block_ch1->data[offset]); dest1 = &(block_ch1->data[offset]);
dest2 = &(block_ch2->data[offset]); dest2 = &(block_ch2->data[offset]);

+ 10
- 5
input_pdm.cpp View File

#define MCLK_MULT 1 #define MCLK_MULT 1
#define MCLK_DIV 17 #define MCLK_DIV 17
#elif F_CPU == 216000000 #elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000 #elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85 #define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000 #elif F_CPU == 16000000
#define MCLK_MULT 12 #define MCLK_MULT 12
#define MCLK_DIV 17 #define MCLK_DIV 17
print "\n};\n"; print "\n};\n";
print "// max=$max, min=$min\n"; print "// max=$max, min=$min\n";
*/ */
#endif
#endif

+ 4
- 5
input_tdm.cpp View File

#include <Arduino.h> #include <Arduino.h>
#include "input_tdm.h" #include "input_tdm.h"
#include "output_tdm.h" #include "output_tdm.h"
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(KINETISK) || defined(__IMXRT1062__)
#include "utility/imxrt_hw.h" #include "utility/imxrt_hw.h"


DMAMEM __attribute__((aligned(32))) DMAMEM __attribute__((aligned(32)))
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
#else
CORE_PIN7_CONFIG = 3; //RX_DATA0
#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
dma.TCD->SADDR = &I2S1_RDR0; dma.TCD->SADDR = &I2S1_RDR0;
dma.TCD->SOFF = 0; dma.TCD->SOFF = 0;
update_responsibility = update_setup(); update_responsibility = update_setup();
dma.enable(); dma.enable();


I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
#endif #endif
} }

+ 2
- 2
input_tdm2.cpp View File

* THE SOFTWARE. * THE SOFTWARE.
*/ */


#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1062__)
#include <Arduino.h> #include <Arduino.h>
#include "input_tdm2.h" #include "input_tdm2.h"
#include "output_tdm2.h" #include "output_tdm2.h"
// TODO: should we set & clear the I2S_RCSR_SR bit here? // TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputTDM2::config_tdm(); AudioOutputTDM2::config_tdm();


CORE_PIN33_CONFIG = 2; //2:RX_DATA0
CORE_PIN5_CONFIG = 2; //2:RX_DATA0
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
dma.TCD->SADDR = &I2S2_RDR0; dma.TCD->SADDR = &I2S2_RDR0;
dma.TCD->SOFF = 0; dma.TCD->SOFF = 0;

+ 4
- 0
keywords.txt View File

AudioInputI2S KEYWORD2 AudioInputI2S KEYWORD2
AudioInputI2S2 KEYWORD2 AudioInputI2S2 KEYWORD2
AudioInputI2SQuad KEYWORD2 AudioInputI2SQuad KEYWORD2
AudioInputI2SHex KEYWORD2
AudioInputI2SOct KEYWORD2
AudioInputI2Sslave KEYWORD2 AudioInputI2Sslave KEYWORD2
AudioInputTDM KEYWORD2 AudioInputTDM KEYWORD2
AudioInputTDM2 KEYWORD2 AudioInputTDM2 KEYWORD2
AudioOutputI2S KEYWORD2 AudioOutputI2S KEYWORD2
AudioOutputI2S2 KEYWORD2 AudioOutputI2S2 KEYWORD2
AudioOutputI2SQuad KEYWORD2 AudioOutputI2SQuad KEYWORD2
AudioOutputI2SHex KEYWORD2
AudioOutputI2SOct KEYWORD2
AudioOutputI2Sslave KEYWORD2 AudioOutputI2Sslave KEYWORD2
AudioOutputSPDIF KEYWORD2 AudioOutputSPDIF KEYWORD2
AudioOutputSPDIF2 KEYWORD2 AudioOutputSPDIF2 KEYWORD2

+ 3
- 0
output_adat.cpp View File

const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} }; const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000) #elif (F_PLL==240000000)
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#elif (F_PLL==256000000)
// TODO: fix these...
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif #endif


for (int f = 0; f < numfreqs; f++) { for (int f = 0; f < numfreqs; f++) {

+ 86
- 136
output_i2s.cpp View File

DMAChannel AudioOutputI2S::dma(false); DMAChannel AudioOutputI2S::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];


#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1062__)
#include "utility/imxrt_hw.h" #include "utility/imxrt_hw.h"
#endif


void AudioOutputI2S::begin(void) void AudioOutputI2S::begin(void)
{ {
block_right_1st = NULL; block_right_1st = NULL;


config_i2s(); config_i2s();
#if defined(__IMXRT1052__)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
#endif

#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0


dma.TCD->SADDR = i2s_tx_buffer; dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2; dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2);
dma.TCD->DOFF = 0; dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0; dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);

I2S1_RCSR |= I2S_RCSR_RE;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;

update_responsibility = update_setup();
dma.attachInterrupt(isr);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
dma.enable(); dma.enable();
}

#endif

#if defined(KINETISK)
void AudioOutputI2S::begin(void)
{
dma.begin(true); // Allocate the DMA channel first

block_left_1st = NULL;
block_right_1st = NULL;


// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;


#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer; dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2; dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2);
dma.TCD->DOFF = 0; dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0; dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable(); dma.enable();



I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
} }
#endif


void AudioOutputI2S::isr(void) void AudioOutputI2S::isr(void)
{ {
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(KINETISK) || defined(__IMXRT1062__)
int16_t *dest; int16_t *dest;
audio_block_t *blockL, *blockR; audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR; uint32_t saddr, offsetL, offsetR;
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
} }


#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 );
#endif

if (offsetL < AUDIO_BLOCK_SAMPLES) { if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S::block_left_offset = offsetL; AudioOutputI2S::block_left_offset = offsetL;
} else { } else {
#define MCLK_MULT 1 #define MCLK_MULT 1
#define MCLK_DIV 17 #define MCLK_DIV 17
#elif F_CPU == 216000000 #elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000 #elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85 #define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000 #elif F_CPU == 16000000
#define MCLK_MULT 12 #define MCLK_MULT 12
#define MCLK_DIV 17 #define MCLK_DIV 17
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#elif ( defined(__IMXRT1052__) || defined(__IMXRT1062__) )

#elif defined(__IMXRT1062__)


CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL: //PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT; int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz // PLL between 27*24 = 648MHz und 54*24=1296MHz
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f


IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK

// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
// Select MCLK
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1
& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));


CORE_PIN23_CONFIG = 3; //1:MCLK CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC CORE_PIN20_CONFIG = 3; //1:RX_SYNC
// CORE_PIN6_CONFIG = 3; //1:TX_DATA0
// CORE_PIN7_CONFIG = 3; //1:RX_DATA0


int rsync = 0; int rsync = 0;
int tsync = 1; int tsync = 1;
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE; I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));


I2S1_RMR = 0; I2S1_RMR = 0;
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE; I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));


#endif #endif


dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first


//pinMode(2, OUTPUT);
block_left_1st = NULL; block_left_1st = NULL;
block_right_1st = NULL; block_right_1st = NULL;


dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
dma.enable();

I2S0_TCSR = I2S_TCSR_SR; I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;


#elif 0 && ( defined(__IMXRT1052__) || defined(__IMXRT1062__) )
#if defined(SAI1)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0
//CORE_PIN7_CONFIG = 3; //1:RX_DATA0
#elif defined(SAI2)
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
//CORE_PIN33_CONFIG = 2; //2:RX_DATA0
#endif
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer; dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2; dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2; dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)&i2s->TX.DR16[1];
dma.TCD->DOFF = 0; dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0; dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();

I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif #endif


update_responsibility = update_setup(); update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
} }


void AudioOutputI2Sslave::config_i2s(void) void AudioOutputI2Sslave::config_i2s(void)
{ {
#if defined(KINETISK) #if defined(KINETISK)
// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;

SIM_SCGC6 |= SIM_SCGC6_I2S; SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA; SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX; SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
// configure pin mux for 3 clock signals


CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;

// Select input clock 0 // Select input clock 0
// Configure to input the bit-clock from pin, bypasses the MCLK divider // Configure to input the bit-clock from pin, bypasses the MCLK divider
I2S0_MCR = I2S_MCR_MICS(0); I2S0_MCR = I2S_MCR_MICS(0);


I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);


#elif 0 && (defined(__IMXRT1052__) || defined(__IMXRT1062__) )
// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK


#if defined(SAI1)
i2s = ((I2S_STRUCT *)0x40384000);
// if either transmitter or receiver is enabled, do nothing
if (i2s->TX.CSR & I2S_TCSR_TE) return;
if (i2s->RX.CSR & I2S_RCSR_RE) return;
#elif defined(__IMXRT1062__)


CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
/*
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
*/
//TODO:
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK | ((uint32_t)(1<<20)) ))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK


CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
int rsync = 0;
int tsync = 1;
#elif defined(SAI2)
i2s = ((I2S_STRUCT *)0x40388000);
if (i2s->TX.CSR & I2S_TCSR_TE) return;
if (i2s->RX.CSR & I2S_RCSR_RE) return;

CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
/*
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
*/
//TODO:

IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK | ((uint32_t)(1<<19)) ))
/*| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR*/ | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0); //Select MCLK

CORE_PIN5_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
int rsync = 1;
int tsync = 0;

#endif
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;

// not using MCLK in slave mode - hope that's ok?
//CORE_PIN23_CONFIG = 3; // AD_B1_09 ALT3=SAI1_MCLK
CORE_PIN21_CONFIG = 3; // AD_B1_11 ALT3=SAI1_RX_BCLK
CORE_PIN20_CONFIG = 3; // AD_B1_10 ALT3=SAI1_RX_SYNC
IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872


// configure transmitter // configure transmitter
i2s->TX.MR = 0;
i2s->TX.CR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
i2s->TX.CR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP;
i2s->TX.CR3 = I2S_TCR3_TCE;
i2s->TX.CR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP;
i2s->TX.CR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP;
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);


// configure receiver // configure receiver
i2s->RX.MR = 0;
i2s->RX.CR1 = I2S_RCR1_RFW(1);
i2s->RX.CR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP;
i2s->RX.CR3 = I2S_RCR3_RCE;
i2s->RX.CR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
i2s->RX.CR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP;
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);


#endif #endif
} }

+ 8
- 0
output_i2s.h View File

virtual void update(void); virtual void update(void);
void begin(void); void begin(void);
friend class AudioInputI2S; friend class AudioInputI2S;
#if defined(__IMXRT1062__)
friend class AudioOutputI2SQuad;
friend class AudioInputI2SQuad;
friend class AudioOutputI2SHex;
friend class AudioInputI2SHex;
friend class AudioOutputI2SOct;
friend class AudioInputI2SOct;
#endif
protected: protected:
AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void); static void config_i2s(void);

+ 26
- 20
output_i2s2.cpp View File

* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1062__)
#include <Arduino.h> #include <Arduino.h>
#include "output_i2s2.h" #include "output_i2s2.h"
#include "memcpy_audio.h" #include "memcpy_audio.h"
block_right_1st = NULL; block_right_1st = NULL;


config_i2s(); config_i2s();
CORE_PIN2_CONFIG = 2; //2:TX_DATA0

// if AudioInputI2S2 set I2S_TCSR_TE (for clock sync), disable it
I2S2_TCSR = 0;
while (I2S2_TCSR & I2S_TCSR_TE) ; //wait for transmit disabled

CORE_PIN2_CONFIG = 2; //EMC_04, 2=SAI2_TX_DATA, page 428


dma.TCD->SADDR = i2s2_tx_buffer; dma.TCD->SADDR = i2s2_tx_buffer;
dma.TCD->SOFF = 2; dma.TCD->SOFF = 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
// I2S2_RCSR |= I2S_RCSR_RE;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
dma.enable();

I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;

update_responsibility = update_setup(); update_responsibility = update_setup();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
dma.enable();
} }


void AudioOutputI2S2::isr(void) void AudioOutputI2S2::isr(void)
{ {
int16_t *dest, *dc;
int16_t *dest;
audio_block_t *blockL, *blockR; audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR; uint32_t saddr, offsetL, offsetR;


void AudioOutputI2S2::config_i2s(void) void AudioOutputI2S2::config_i2s(void)
{ {
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);

// if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_RCSR & I2S_RCSR_RE) return;
//PLL: //PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT; int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz // PLL between 27*24 = 648MHz und 54*24=1296MHz
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4, | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK


// if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_RCSR & I2S_RCSR_RE) return;

CORE_PIN5_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
// CORE_PIN2_CONFIG = 2; //2:TX_DATA0
// CORE_PIN33_CONFIG = 2; //2:RX_DATA0
CORE_PIN33_CONFIG = 2; //EMC_07, 2=SAI2_MCLK
CORE_PIN4_CONFIG = 2; //EMC_06, 2=SAI2_TX_BCLK
CORE_PIN3_CONFIG = 2; //EMC_05, 2=SAI2_TX_SYNC, page 429


int rsync = 1; int rsync = 1;
int tsync = 0; int tsync = 0;
//I2S2_TCSR = (1<<25); //Reset //I2S2_TCSR = (1<<25); //Reset
I2S2_TCR1 = I2S_TCR1_RFW(1); I2S2_TCR1 = I2S_TCR1_RFW(1);
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S2_TCR3 = I2S_TCR3_TCE; I2S2_TCR3 = I2S_TCR3_TCE;
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));


I2S2_RMR = 0; I2S2_RMR = 0;
//I2S2_RCSR = (1<<25); //Reset //I2S2_RCSR = (1<<25); //Reset
I2S2_RCR1 = I2S_RCR1_RFW(1); I2S2_RCR1 = I2S_RCR1_RFW(1);
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S2_RCR3 = I2S_RCR3_RCE; I2S2_RCR3 = I2S_RCR3_RCE;
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));


} }

+ 355
- 0
output_i2s_hex.cpp View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <Arduino.h>
#include "output_i2s_hex.h"
#include "output_i2s.h"
#include "memcpy_audio.h"

#if defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"

audio_block_t * AudioOutputI2SHex::block_ch1_1st = NULL;
audio_block_t * AudioOutputI2SHex::block_ch2_1st = NULL;
audio_block_t * AudioOutputI2SHex::block_ch3_1st = NULL;
audio_block_t * AudioOutputI2SHex::block_ch4_1st = NULL;
audio_block_t * AudioOutputI2SHex::block_ch5_1st = NULL;
audio_block_t * AudioOutputI2SHex::block_ch6_1st = NULL;
audio_block_t * AudioOutputI2SHex::block_ch1_2nd = NULL;
audio_block_t * AudioOutputI2SHex::block_ch2_2nd = NULL;
audio_block_t * AudioOutputI2SHex::block_ch3_2nd = NULL;
audio_block_t * AudioOutputI2SHex::block_ch4_2nd = NULL;
audio_block_t * AudioOutputI2SHex::block_ch5_2nd = NULL;
audio_block_t * AudioOutputI2SHex::block_ch6_2nd = NULL;
uint16_t AudioOutputI2SHex::ch1_offset = 0;
uint16_t AudioOutputI2SHex::ch2_offset = 0;
uint16_t AudioOutputI2SHex::ch3_offset = 0;
uint16_t AudioOutputI2SHex::ch4_offset = 0;
uint16_t AudioOutputI2SHex::ch5_offset = 0;
uint16_t AudioOutputI2SHex::ch6_offset = 0;
bool AudioOutputI2SHex::update_responsibility = false;
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*3];
DMAChannel AudioOutputI2SHex::dma(false);

static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0};

void AudioOutputI2SHex::begin(void)
{
dma.begin(true); // Allocate the DMA channel first

block_ch1_1st = NULL;
block_ch2_1st = NULL;
block_ch3_1st = NULL;
block_ch4_1st = NULL;
block_ch5_1st = NULL;
block_ch6_1st = NULL;

const int pinoffset = 0; // TODO: make this configurable...
memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer));
AudioOutputI2S::config_i2s();
I2S1_TCR3 = I2S_TCR3_TCE_3CH << pinoffset;
switch (pinoffset) {
case 0:
CORE_PIN7_CONFIG = 3;
CORE_PIN32_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
break;
case 1:
CORE_PIN32_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
CORE_PIN6_CONFIG = 3;
}
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-12) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(6);
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2 + pinoffset * 4);
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->DLASTSGA = -12;
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
I2S1_TCR3 = I2S_TCR3_TCE_3CH << pinoffset;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
}

void AudioOutputI2SHex::isr(void)
{
uint32_t saddr;
const int16_t *src1, *src2, *src3, *src4, *src5, *src6;
const int16_t *zeros = (const int16_t *)zerodata;
int16_t *dest;

saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int16_t *)((uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2);
if (update_responsibility) update_all();
} else {
dest = (int16_t *)i2s_tx_buffer;
}

src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;
src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros;
src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros;
#if 0
// TODO: optimized 6 channel copy...
memcpy_tointerleaveQuad(dest, src1, src2, src3, src4);
#else
int16_t *p=dest;
for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
*p++ = *src1++;
*p++ = *src3++;
*p++ = *src5++;
*p++ = *src2++;
*p++ = *src4++;
*p++ = *src6++;
}
#endif
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2);

if (block_ch1_1st) {
if (ch1_offset == 0) {
ch1_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch1_offset = 0;
release(block_ch1_1st);
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = NULL;
}
}
if (block_ch2_1st) {
if (ch2_offset == 0) {
ch2_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch2_offset = 0;
release(block_ch2_1st);
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = NULL;
}
}
if (block_ch3_1st) {
if (ch3_offset == 0) {
ch3_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch3_offset = 0;
release(block_ch3_1st);
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = NULL;
}
}
if (block_ch4_1st) {
if (ch4_offset == 0) {
ch4_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch4_offset = 0;
release(block_ch4_1st);
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = NULL;
}
}
if (block_ch5_1st) {
if (ch5_offset == 0) {
ch5_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch5_offset = 0;
release(block_ch5_1st);
block_ch5_1st = block_ch5_2nd;
block_ch5_2nd = NULL;
}
}
if (block_ch6_1st) {
if (ch6_offset == 0) {
ch6_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch6_offset = 0;
release(block_ch6_1st);
block_ch6_1st = block_ch6_2nd;
block_ch6_2nd = NULL;
}
}
}


void AudioOutputI2SHex::update(void)
{
audio_block_t *block, *tmp;

block = receiveReadOnly(0); // channel 1
if (block) {
__disable_irq();
if (block_ch1_1st == NULL) {
block_ch1_1st = block;
ch1_offset = 0;
__enable_irq();
} else if (block_ch1_2nd == NULL) {
block_ch1_2nd = block;
__enable_irq();
} else {
tmp = block_ch1_1st;
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = block;
ch1_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // channel 2
if (block) {
__disable_irq();
if (block_ch2_1st == NULL) {
block_ch2_1st = block;
ch2_offset = 0;
__enable_irq();
} else if (block_ch2_2nd == NULL) {
block_ch2_2nd = block;
__enable_irq();
} else {
tmp = block_ch2_1st;
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = block;
ch2_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(2); // channel 3
if (block) {
__disable_irq();
if (block_ch3_1st == NULL) {
block_ch3_1st = block;
ch3_offset = 0;
__enable_irq();
} else if (block_ch3_2nd == NULL) {
block_ch3_2nd = block;
__enable_irq();
} else {
tmp = block_ch3_1st;
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = block;
ch3_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(3); // channel 4
if (block) {
__disable_irq();
if (block_ch4_1st == NULL) {
block_ch4_1st = block;
ch4_offset = 0;
__enable_irq();
} else if (block_ch4_2nd == NULL) {
block_ch4_2nd = block;
__enable_irq();
} else {
tmp = block_ch4_1st;
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = block;
ch4_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(4); // channel 5
if (block) {
__disable_irq();
if (block_ch5_1st == NULL) {
block_ch5_1st = block;
ch5_offset = 0;
__enable_irq();
} else if (block_ch5_2nd == NULL) {
block_ch5_2nd = block;
__enable_irq();
} else {
tmp = block_ch5_1st;
block_ch5_1st = block_ch5_2nd;
block_ch5_2nd = block;
ch5_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(5); // channel 6
if (block) {
__disable_irq();
if (block_ch6_1st == NULL) {
block_ch6_1st = block;
ch6_offset = 0;
__enable_irq();
} else if (block_ch6_2nd == NULL) {
block_ch6_2nd = block;
__enable_irq();
} else {
tmp = block_ch6_1st;
block_ch6_1st = block_ch6_2nd;
block_ch6_2nd = block;
ch6_offset = 0;
__enable_irq();
release(tmp);
}
}
}


#else // not supported

void AudioOutputI2SHex::begin(void)
{
}

void AudioOutputI2SHex::update(void)
{
audio_block_t *block;

block = receiveReadOnly(0);
if (block) release(block);
block = receiveReadOnly(1);
if (block) release(block);
block = receiveReadOnly(2);
if (block) release(block);
block = receiveReadOnly(3);
if (block) release(block);
block = receiveReadOnly(4);
if (block) release(block);
block = receiveReadOnly(5);
if (block) release(block);
}

#endif

+ 65
- 0
output_i2s_hex.h View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef output_i2s_hex_h_
#define output_i2s_hex_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioOutputI2SHex : public AudioStream
{
public:
AudioOutputI2SHex(void) : AudioStream(6, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
private:
static audio_block_t *block_ch1_1st;
static audio_block_t *block_ch2_1st;
static audio_block_t *block_ch3_1st;
static audio_block_t *block_ch4_1st;
static audio_block_t *block_ch5_1st;
static audio_block_t *block_ch6_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
static audio_block_t *block_ch1_2nd;
static audio_block_t *block_ch2_2nd;
static audio_block_t *block_ch3_2nd;
static audio_block_t *block_ch4_2nd;
static audio_block_t *block_ch5_2nd;
static audio_block_t *block_ch6_2nd;
static uint16_t ch1_offset;
static uint16_t ch2_offset;
static uint16_t ch3_offset;
static uint16_t ch4_offset;
static uint16_t ch5_offset;
static uint16_t ch6_offset;
audio_block_t *inputQueueArray[6];
};

#endif

+ 419
- 0
output_i2s_oct.cpp View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <Arduino.h>
#include "output_i2s_oct.h"
#include "output_i2s.h"
#include "memcpy_audio.h"

#if defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"

audio_block_t * AudioOutputI2SOct::block_ch1_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch2_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch3_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch4_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch5_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch6_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch7_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch8_1st = NULL;
audio_block_t * AudioOutputI2SOct::block_ch1_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch2_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch3_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch4_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch5_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch6_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch7_2nd = NULL;
audio_block_t * AudioOutputI2SOct::block_ch8_2nd = NULL;
uint16_t AudioOutputI2SOct::ch1_offset = 0;
uint16_t AudioOutputI2SOct::ch2_offset = 0;
uint16_t AudioOutputI2SOct::ch3_offset = 0;
uint16_t AudioOutputI2SOct::ch4_offset = 0;
uint16_t AudioOutputI2SOct::ch5_offset = 0;
uint16_t AudioOutputI2SOct::ch6_offset = 0;
uint16_t AudioOutputI2SOct::ch7_offset = 0;
uint16_t AudioOutputI2SOct::ch8_offset = 0;
bool AudioOutputI2SOct::update_responsibility = false;
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4];
DMAChannel AudioOutputI2SOct::dma(false);

static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0};

void AudioOutputI2SOct::begin(void)
{
dma.begin(true); // Allocate the DMA channel first

block_ch1_1st = NULL;
block_ch2_1st = NULL;
block_ch3_1st = NULL;
block_ch4_1st = NULL;
block_ch5_1st = NULL;
block_ch6_1st = NULL;

memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer));
AudioOutputI2S::config_i2s();
I2S1_TCR3 = I2S_TCR3_TCE_4CH;
CORE_PIN7_CONFIG = 3;
CORE_PIN32_CONFIG = 3;
CORE_PIN6_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-16) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(8);
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->DLASTSGA = -16;
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
//I2S1_TCR3 = I2S_TCR3_TCE_4CH;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
}

void AudioOutputI2SOct::isr(void)
{
uint32_t saddr;
const int16_t *src1, *src2, *src3, *src4, *src5, *src6, *src7, *src8;
const int16_t *zeros = (const int16_t *)zerodata;
int16_t *dest;

saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int16_t *)((uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2);
if (update_responsibility) update_all();
} else {
dest = (int16_t *)i2s_tx_buffer;
}

src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;
src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros;
src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros;
src7 = (block_ch7_1st) ? block_ch7_1st->data + ch7_offset : zeros;
src8 = (block_ch8_1st) ? block_ch8_1st->data + ch8_offset : zeros;
#if 0
// TODO: optimized 8 channel copy...
memcpy_tointerleaveQuad(dest, src1, src2, src3, src4);
#else
int16_t *p=dest;
for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
*p++ = *src1++;
*p++ = *src3++;
*p++ = *src5++;
*p++ = *src7++;
*p++ = *src2++;
*p++ = *src4++;
*p++ = *src6++;
*p++ = *src8++;
}
#endif
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2);

if (block_ch1_1st) {
if (ch1_offset == 0) {
ch1_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch1_offset = 0;
release(block_ch1_1st);
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = NULL;
}
}
if (block_ch2_1st) {
if (ch2_offset == 0) {
ch2_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch2_offset = 0;
release(block_ch2_1st);
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = NULL;
}
}
if (block_ch3_1st) {
if (ch3_offset == 0) {
ch3_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch3_offset = 0;
release(block_ch3_1st);
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = NULL;
}
}
if (block_ch4_1st) {
if (ch4_offset == 0) {
ch4_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch4_offset = 0;
release(block_ch4_1st);
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = NULL;
}
}
if (block_ch5_1st) {
if (ch5_offset == 0) {
ch5_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch5_offset = 0;
release(block_ch5_1st);
block_ch5_1st = block_ch5_2nd;
block_ch5_2nd = NULL;
}
}
if (block_ch6_1st) {
if (ch6_offset == 0) {
ch6_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch6_offset = 0;
release(block_ch6_1st);
block_ch6_1st = block_ch6_2nd;
block_ch6_2nd = NULL;
}
}
if (block_ch7_1st) {
if (ch7_offset == 0) {
ch7_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch7_offset = 0;
release(block_ch7_1st);
block_ch7_1st = block_ch7_2nd;
block_ch7_2nd = NULL;
}
}
if (block_ch8_1st) {
if (ch8_offset == 0) {
ch8_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch8_offset = 0;
release(block_ch8_1st);
block_ch8_1st = block_ch8_2nd;
block_ch8_2nd = NULL;
}
}
}


void AudioOutputI2SOct::update(void)
{
audio_block_t *block, *tmp;

block = receiveReadOnly(0); // channel 1
if (block) {
__disable_irq();
if (block_ch1_1st == NULL) {
block_ch1_1st = block;
ch1_offset = 0;
__enable_irq();
} else if (block_ch1_2nd == NULL) {
block_ch1_2nd = block;
__enable_irq();
} else {
tmp = block_ch1_1st;
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = block;
ch1_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // channel 2
if (block) {
__disable_irq();
if (block_ch2_1st == NULL) {
block_ch2_1st = block;
ch2_offset = 0;
__enable_irq();
} else if (block_ch2_2nd == NULL) {
block_ch2_2nd = block;
__enable_irq();
} else {
tmp = block_ch2_1st;
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = block;
ch2_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(2); // channel 3
if (block) {
__disable_irq();
if (block_ch3_1st == NULL) {
block_ch3_1st = block;
ch3_offset = 0;
__enable_irq();
} else if (block_ch3_2nd == NULL) {
block_ch3_2nd = block;
__enable_irq();
} else {
tmp = block_ch3_1st;
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = block;
ch3_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(3); // channel 4
if (block) {
__disable_irq();
if (block_ch4_1st == NULL) {
block_ch4_1st = block;
ch4_offset = 0;
__enable_irq();
} else if (block_ch4_2nd == NULL) {
block_ch4_2nd = block;
__enable_irq();
} else {
tmp = block_ch4_1st;
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = block;
ch4_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(4); // channel 5
if (block) {
__disable_irq();
if (block_ch5_1st == NULL) {
block_ch5_1st = block;
ch5_offset = 0;
__enable_irq();
} else if (block_ch5_2nd == NULL) {
block_ch5_2nd = block;
__enable_irq();
} else {
tmp = block_ch5_1st;
block_ch5_1st = block_ch5_2nd;
block_ch5_2nd = block;
ch5_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(5); // channel 6
if (block) {
__disable_irq();
if (block_ch6_1st == NULL) {
block_ch6_1st = block;
ch6_offset = 0;
__enable_irq();
} else if (block_ch6_2nd == NULL) {
block_ch6_2nd = block;
__enable_irq();
} else {
tmp = block_ch6_1st;
block_ch6_1st = block_ch6_2nd;
block_ch6_2nd = block;
ch6_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(6); // channel 7
if (block) {
__disable_irq();
if (block_ch7_1st == NULL) {
block_ch7_1st = block;
ch7_offset = 0;
__enable_irq();
} else if (block_ch7_2nd == NULL) {
block_ch7_2nd = block;
__enable_irq();
} else {
tmp = block_ch7_1st;
block_ch7_1st = block_ch7_2nd;
block_ch7_2nd = block;
ch7_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(7); // channel 8
if (block) {
__disable_irq();
if (block_ch8_1st == NULL) {
block_ch8_1st = block;
ch8_offset = 0;
__enable_irq();
} else if (block_ch8_2nd == NULL) {
block_ch8_2nd = block;
__enable_irq();
} else {
tmp = block_ch8_1st;
block_ch8_1st = block_ch8_2nd;
block_ch8_2nd = block;
ch8_offset = 0;
__enable_irq();
release(tmp);
}
}
}


#else // not supported

void AudioOutputI2SOct::begin(void)
{
}

void AudioOutputI2SOct::update(void)
{
audio_block_t *block;

block = receiveReadOnly(0);
if (block) release(block);
block = receiveReadOnly(1);
if (block) release(block);
block = receiveReadOnly(2);
if (block) release(block);
block = receiveReadOnly(3);
if (block) release(block);
block = receiveReadOnly(4);
if (block) release(block);
block = receiveReadOnly(5);
if (block) release(block);
block = receiveReadOnly(6);
if (block) release(block);
block = receiveReadOnly(7);
if (block) release(block);
}

#endif

+ 71
- 0
output_i2s_oct.h View File

/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef output_i2s_oct_h_
#define output_i2s_oct_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioOutputI2SOct : public AudioStream
{
public:
AudioOutputI2SOct(void) : AudioStream(8, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
private:
static audio_block_t *block_ch1_1st;
static audio_block_t *block_ch2_1st;
static audio_block_t *block_ch3_1st;
static audio_block_t *block_ch4_1st;
static audio_block_t *block_ch5_1st;
static audio_block_t *block_ch6_1st;
static audio_block_t *block_ch7_1st;
static audio_block_t *block_ch8_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
static audio_block_t *block_ch1_2nd;
static audio_block_t *block_ch2_2nd;
static audio_block_t *block_ch3_2nd;
static audio_block_t *block_ch4_2nd;
static audio_block_t *block_ch5_2nd;
static audio_block_t *block_ch6_2nd;
static audio_block_t *block_ch7_2nd;
static audio_block_t *block_ch8_2nd;
static uint16_t ch1_offset;
static uint16_t ch2_offset;
static uint16_t ch3_offset;
static uint16_t ch4_offset;
static uint16_t ch5_offset;
static uint16_t ch6_offset;
static uint16_t ch7_offset;
static uint16_t ch8_offset;
audio_block_t *inputQueueArray[8];
};

#endif

+ 60
- 12
output_i2s_quad.cpp View File



#include <Arduino.h> #include <Arduino.h>
#include "output_i2s_quad.h" #include "output_i2s_quad.h"
#include "output_i2s.h"
#include "memcpy_audio.h" #include "memcpy_audio.h"


#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__)

#if defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"
#endif


audio_block_t * AudioOutputI2SQuad::block_ch1_1st = NULL; audio_block_t * AudioOutputI2SQuad::block_ch1_1st = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch2_1st = NULL; audio_block_t * AudioOutputI2SQuad::block_ch2_1st = NULL;
uint16_t AudioOutputI2SQuad::ch2_offset = 0; uint16_t AudioOutputI2SQuad::ch2_offset = 0;
uint16_t AudioOutputI2SQuad::ch3_offset = 0; uint16_t AudioOutputI2SQuad::ch3_offset = 0;
uint16_t AudioOutputI2SQuad::ch4_offset = 0; uint16_t AudioOutputI2SQuad::ch4_offset = 0;
//audio_block_t * AudioOutputI2SQuad::inputQueueArray[4];
bool AudioOutputI2SQuad::update_responsibility = false; bool AudioOutputI2SQuad::update_responsibility = false;
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2];
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2];
DMAChannel AudioOutputI2SQuad::dma(false); DMAChannel AudioOutputI2SQuad::dma(false);


static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0};


void AudioOutputI2SQuad::begin(void) void AudioOutputI2SQuad::begin(void)
{ {
#if 1
dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first


block_ch1_1st = NULL; block_ch1_1st = NULL;
block_ch3_1st = NULL; block_ch3_1st = NULL;
block_ch4_1st = NULL; block_ch4_1st = NULL;


#if defined(KINETISK)
// TODO: can we call normal config_i2s, and then just enable the extra output? // TODO: can we call normal config_i2s, and then just enable the extra output?
config_i2s(); config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2 CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2
I2S0_TCSR = I2S_TCSR_SR; I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
dma.attachInterrupt(isr); dma.attachInterrupt(isr);

#elif defined(__IMXRT1062__)
const int pinoffset = 0; // TODO: make this configurable...
memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer));
AudioOutputI2S::config_i2s();
I2S1_TCR3 = I2S_TCR3_TCE_2CH << pinoffset;
switch (pinoffset) {
case 0:
CORE_PIN7_CONFIG = 3;
CORE_PIN32_CONFIG = 3;
break;
case 1:
CORE_PIN32_CONFIG = 3;
CORE_PIN9_CONFIG = 3;
break;
case 2:
CORE_PIN9_CONFIG = 3;
CORE_PIN6_CONFIG = 3;
}
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(4);
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2 + pinoffset * 4);
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->DLASTSGA = -8;
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
I2S1_TCR3 = I2S_TCR3_TCE_2CH << pinoffset;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
#endif #endif
} }


src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;


// TODO: fast 4-way interleaved memcpy...
#if 1 #if 1
memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); memcpy_tointerleaveQuad(dest, src1, src2, src3, src4);
#else #else
*dest++ = *src4++; *dest++ = *src4++;
} }
#endif #endif
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 );


if (block_ch1_1st) { if (block_ch1_1st) {
if (ch1_offset == 0) { if (ch1_offset == 0) {
} }
} }


#if defined(KINETISK)
// MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate // MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate
// //
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000 #if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
#define MCLK_MULT 1 #define MCLK_MULT 1
#define MCLK_DIV 17 #define MCLK_DIV 17
#elif F_CPU == 216000000 #elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000 #elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85 #define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000 #elif F_CPU == 16000000
#define MCLK_MULT 12 #define MCLK_MULT 12
#define MCLK_DIV 17 #define MCLK_DIV 17
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
} }
#endif // KINETISK




#else // not __MK20DX256__

#else // not supported


void AudioOutputI2SQuad::begin(void) void AudioOutputI2SQuad::begin(void)
{ {

+ 16
- 6
output_pt8211.cpp View File

dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;


dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.attachInterrupt(isr);
dma.enable();
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;

return;
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) #elif defined(__IMXRT1052__) || defined(__IMXRT1062__)


#if defined(__IMXRT1052__) #if defined(__IMXRT1052__)


I2S1_RCSR |= I2S_RCSR_RE; I2S1_RCSR |= I2S_RCSR_RE;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup(); update_responsibility = update_setup();
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
dma.enable(); dma.enable();
return;
#endif
} }


void AudioOutputPT8211::isr(void) void AudioOutputPT8211::isr(void)
#define MCLK_MULT 1 #define MCLK_MULT 1
#define MCLK_DIV 17 #define MCLK_DIV 17
#elif F_CPU == 216000000 #elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000 #elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85 #define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000 #elif F_CPU == 16000000
#define MCLK_MULT 12 #define MCLK_MULT 12
#define MCLK_DIV 17 #define MCLK_DIV 17

+ 9
- 4
output_spdif.cpp View File

#define MCLK_MULT 1 #define MCLK_MULT 1
#define MCLK_DIV 17 #define MCLK_DIV 17
#elif F_CPU == 216000000 #elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000 #elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85 #define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000 #elif F_CPU == 16000000
#define MCLK_MULT 12 #define MCLK_MULT 12
#define MCLK_DIV 17 #define MCLK_DIV 17

+ 24
- 15
output_tdm.cpp View File



I2S0_TCSR = I2S_TCSR_SR; I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0


dma.TCD->SADDR = tdm_tx_buffer; dma.TCD->SADDR = tdm_tx_buffer;
dma.TCD->SOFF = 4; dma.TCD->SOFF = 4;
update_responsibility = update_setup(); update_responsibility = update_setup();
dma.enable(); dma.enable();


I2S1_RCSR |= I2S_RCSR_RE;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;


#endif #endif
dma.attachInterrupt(isr); dma.attachInterrupt(isr);


void AudioOutputTDM::isr(void) void AudioOutputTDM::isr(void)
{ {
uint32_t *dest, *dc;
uint32_t *dest;
const uint32_t *src1, *src2; const uint32_t *src1, *src2;
uint32_t i, saddr; uint32_t i, saddr;


dest = tdm_tx_buffer; dest = tdm_tx_buffer;
} }
if (update_responsibility) AudioStream::update_all(); if (update_responsibility) AudioStream::update_all();
dc = dest;

#if IMXRT_CACHE_ENABLED >= 2
uint32_t *dc = dest;
#endif
for (i=0; i < 16; i += 2) { for (i=0; i < 16; i += 2) {
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros; src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros;
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros; src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros;
#define MCLK_MULT 2 #define MCLK_MULT 2
#define MCLK_DIV 17 #define MCLK_DIV 17
#elif F_CPU == 216000000 #elif F_CPU == 216000000
#define MCLK_MULT 16
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000 #elif F_CPU == 240000000
#define MCLK_MULT 8
#define MCLK_MULT 2
#define MCLK_DIV 85 #define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#else #else
#error "This CPU Clock Speed is not supported by the Audio library"; #error "This CPU Clock Speed is not supported by the Audio library";
#endif #endif
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK - 11.2 MHz CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK - 11.2 MHz
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK - 22.5 MHz CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK - 22.5 MHz


#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#elif defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL: //PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT; int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz // PLL between 27*24 = 648MHz und 54*24=1296MHz
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK


// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;

// configure transmitter // configure transmitter
int rsync = 0; int rsync = 0;
int tsync = 1; int tsync = 1;

+ 7
- 11
output_tdm2.cpp View File

* THE SOFTWARE. * THE SOFTWARE.
*/ */


#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1062__)
#include <Arduino.h> #include <Arduino.h>
#include "output_tdm2.h" #include "output_tdm2.h"
#include "memcpy_audio.h" #include "memcpy_audio.h"


void AudioOutputTDM2::config_tdm(void) void AudioOutputTDM2::config_tdm(void)
{ {

CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);

// if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_RCSR & I2S_RCSR_RE) return;
//PLL: //PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ
// PLL between 27*24 = 648MHz und 54*24=1296MHz // PLL between 27*24 = 648MHz und 54*24=1296MHz
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK




// if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_RCSR & I2S_RCSR_RE) return;

// configure transmitter // configure transmitter
int rsync = 1; int rsync = 1;
int tsync = 0; int tsync = 0;
| I2S_RCR4_FSE | I2S_RCR4_FSD; | I2S_RCR4_FSE | I2S_RCR4_FSD;
I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);


CORE_PIN5_CONFIG = 2; //2:MCLK
CORE_PIN33_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC CORE_PIN3_CONFIG = 2; //2:TX_SYNC

} }


#endif
#endif

+ 2
- 2
play_queue.cpp View File



if (!userblock) return; if (!userblock) return;
h = head + 1; h = head + 1;
if (h >= 32) h = 0;
if (h >= max_buffers) h = 0;
while (tail == h) ; // wait until space in the queue while (tail == h) ; // wait until space in the queue
queue[h] = userblock; queue[h] = userblock;
head = h; head = h;


t = tail; t = tail;
if (t != head) { if (t != head) {
if (++t >= 32) t = 0;
if (++t >= max_buffers) t = 0;
block = queue[t]; block = queue[t];
tail = t; tail = t;
transmit(block); transmit(block);

+ 7
- 1
play_queue.h View File



class AudioPlayQueue : public AudioStream class AudioPlayQueue : public AudioStream
{ {
private:
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
static const int max_buffers = 80;
#else
static const int max_buffers = 32;
#endif
public: public:
AudioPlayQueue(void) : AudioStream(0, NULL), AudioPlayQueue(void) : AudioStream(0, NULL),
userblock(NULL), head(0), tail(0) { } userblock(NULL), head(0), tail(0) { }
//bool isPlaying(void) { return playing; } //bool isPlaying(void) { return playing; }
virtual void update(void); virtual void update(void);
private: private:
audio_block_t *queue[32];
audio_block_t *queue[max_buffers];
audio_block_t *userblock; audio_block_t *userblock;
volatile uint8_t head, tail; volatile uint8_t head, tail;
}; };

+ 4
- 4
record_queue.cpp View File

h = head; h = head;
t = tail; t = tail;
if (h >= t) return h - t; if (h >= t) return h - t;
return 53 + h - t;
return max_buffers + h - t;
} }


void AudioRecordQueue::clear(void) void AudioRecordQueue::clear(void)
} }
t = tail; t = tail;
while (t != head) { while (t != head) {
if (++t >= 53) t = 0;
if (++t >= max_buffers) t = 0;
release(queue[t]); release(queue[t]);
} }
tail = t; tail = t;
if (userblock) return NULL; if (userblock) return NULL;
t = tail; t = tail;
if (t == head) return NULL; if (t == head) return NULL;
if (++t >= 53) t = 0;
if (++t >= max_buffers) t = 0;
userblock = queue[t]; userblock = queue[t];
tail = t; tail = t;
return userblock->data; return userblock->data;
return; return;
} }
h = head + 1; h = head + 1;
if (h >= 53) h = 0;
if (h >= max_buffers) h = 0;
if (h == tail) { if (h == tail) {
release(block); release(block);
} else { } else {

+ 7
- 1
record_queue.h View File



class AudioRecordQueue : public AudioStream class AudioRecordQueue : public AudioStream
{ {
private:
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
static const int max_buffers = 209;
#else
static const int max_buffers = 53;
#endif
public: public:
AudioRecordQueue(void) : AudioStream(1, inputQueueArray), AudioRecordQueue(void) : AudioStream(1, inputQueueArray),
userblock(NULL), head(0), tail(0), enabled(0) { } userblock(NULL), head(0), tail(0), enabled(0) { }
virtual void update(void); virtual void update(void);
private: private:
audio_block_t *inputQueueArray[1]; audio_block_t *inputQueueArray[1];
audio_block_t * volatile queue[53];
audio_block_t * volatile queue[max_buffers];
audio_block_t *userblock; audio_block_t *userblock;
volatile uint8_t head, tail, enabled; volatile uint8_t head, tail, enabled;
}; };

+ 1
- 1
utility/imxrt_hw.cpp View File

#include "imxrt_hw.h" #include "imxrt_hw.h"


PROGMEM PROGMEM
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force = false) // sets PLL4
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
{ {
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return; if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;



+ 1
- 1
utility/imxrt_hw.h View File

(c) Frank B (c) Frank B
*/ */


#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1062__)


#ifndef imxr_hw_h_ #ifndef imxr_hw_h_
#define imxr_hw_h_ #define imxr_hw_h_

Loading…
Cancel
Save