@@ -93,6 +93,8 @@ | |||
#include "input_i2s.h" | |||
#include "input_i2s2.h" | |||
#include "input_i2s_quad.h" | |||
#include "input_i2s_hex.h" | |||
#include "input_i2s_oct.h" | |||
#include "input_tdm.h" | |||
#include "input_tdm2.h" | |||
#include "input_pdm.h" | |||
@@ -102,6 +104,8 @@ | |||
#include "output_i2s.h" | |||
#include "output_i2s2.h" | |||
#include "output_i2s_quad.h" | |||
#include "output_i2s_hex.h" | |||
#include "output_i2s_oct.h" | |||
#include "output_mqs.h" | |||
#include "output_pwm.h" | |||
#include "output_spdif.h" |
@@ -78,6 +78,11 @@ | |||
// then un-mute the DACs. | |||
// 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[] = { | |||
0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max | |||
0x76, // CS42448_Interface_Formats = TDM mode |
@@ -30,7 +30,10 @@ | |||
#include "AudioStream.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 | |||
#define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SAMPLES) | |||
#elif defined(__MK64FX512__) |
@@ -0,0 +1,149 @@ | |||
/* 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(" "); | |||
} |
@@ -11,17 +11,17 @@ | |||
// | |||
// 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): | |||
// https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733 | |||
@@ -50,6 +50,7 @@ AudioControlWM8731master wm8731m1; //xy=292,379 | |||
void setup() { | |||
delay(1000); // allow the WM7831 extra time to power up | |||
wm8731m1.enable(); | |||
AudioMemory(15); |
@@ -162,7 +162,7 @@ void continueRecording() { | |||
memcpy(buffer+256, queue1.readBuffer(), 256); | |||
queue1.freeBuffer(); | |||
// write all 512 bytes to the SD card | |||
elapsedMicros usec = 0; | |||
//elapsedMicros usec = 0; | |||
frec.write(buffer, 512); | |||
// Uncomment these lines to see how long SD writes | |||
// are taking. A pair of audio blocks arrives every |
@@ -440,11 +440,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
<p>The I2S signals are used in "master" mode, where Teensy creates | |||
all 3 clock signals and controls all data timing.</p> | |||
<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> | |||
<p>Audio from | |||
master mode I2S may be used in the same project as ADC, DAC and | |||
@@ -755,11 +755,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
<a href="https://forum.pjrc.com/threads/42894">Invensense ICS-52000 microphones</a>. | |||
</p> | |||
<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> | |||
<p>Audio from | |||
master mode TDM may be used in the same project as ADC, DAC and | |||
@@ -896,11 +896,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
<p>The I2S signals are used in "master" mode, where Teensy creates | |||
all 3 clock signals and controls all data timing.</p> | |||
<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> | |||
<p>Audio from | |||
master mode I2S may be used in the same project as ADC, DAC and | |||
@@ -1325,11 +1325,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
CS42448 Circuit Board</a>. | |||
</p> | |||
<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> | |||
<p>Audio from | |||
master mode TDM may be used in the same project as ADC, DAC and |
@@ -65,16 +65,12 @@ void AudioInputI2S::begin(void) | |||
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 | |||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1062__) | |||
#elif defined(__IMXRT1062__) | |||
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; | |||
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0+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; | |||
@@ -87,15 +83,11 @@ void AudioInputI2S::begin(void) | |||
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; | |||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
#endif | |||
update_responsibility = update_setup(); | |||
dma.enable(); | |||
dma.attachInterrupt(isr); | |||
//pinMode(13, OUTPUT); | |||
} | |||
void AudioInputI2S::isr(void) | |||
@@ -105,11 +97,11 @@ void AudioInputI2S::isr(void) | |||
int16_t *dest_left, *dest_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); | |||
#endif | |||
dma.clearInterrupt(); | |||
//Serial.println("isr"); | |||
if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { | |||
// DMA is receiving to the first half of the buffer | |||
@@ -142,7 +134,6 @@ void AudioInputI2S::isr(void) | |||
} while (src < end); | |||
} | |||
} | |||
//digitalWriteFast(13, LOW); | |||
} | |||
@@ -233,7 +224,29 @@ void AudioInputI2Sslave::begin(void) | |||
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 | |||
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 | |||
} | |||
@@ -25,7 +25,7 @@ | |||
*/ | |||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1062__) | |||
#include <Arduino.h> | |||
#include "input_i2s2.h" | |||
#include "output_i2s2.h" | |||
@@ -48,8 +48,8 @@ void AudioInputI2S2::begin(void) | |||
// TODO: should we set & clear the I2S_RCSR_SR bit here? | |||
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->SOFF = 0; | |||
@@ -63,14 +63,13 @@ void AudioInputI2S2::begin(void) | |||
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2; | |||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
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(); | |||
dma.enable(); | |||
dma.attachInterrupt(isr); | |||
//pinMode(13, OUTPUT); | |||
} | |||
void AudioInputI2S2::isr(void) | |||
@@ -80,7 +79,6 @@ void AudioInputI2S2::isr(void) | |||
int16_t *dest_left, *dest_right; | |||
audio_block_t *left, *right; | |||
//digitalWriteFast(13, HIGH); | |||
daddr = (uint32_t)(dma.TCD->DADDR); | |||
dma.clearInterrupt(); | |||
@@ -115,7 +113,6 @@ void AudioInputI2S2::isr(void) | |||
} while (src < end); | |||
} | |||
} | |||
//digitalWriteFast(13, LOW); | |||
} | |||
@@ -0,0 +1,247 @@ | |||
/* 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 | |||
@@ -0,0 +1,54 @@ | |||
/* 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 |
@@ -0,0 +1,263 @@ | |||
/* 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 | |||
@@ -0,0 +1,56 @@ | |||
/* 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 |
@@ -27,8 +27,9 @@ | |||
#include <Arduino.h> | |||
#include "input_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_ch2 = NULL; | |||
audio_block_t * AudioInputI2SQuad::block_ch3 = NULL; | |||
@@ -37,12 +38,13 @@ uint16_t AudioInputI2SQuad::block_offset = 0; | |||
bool AudioInputI2SQuad::update_responsibility = 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) | |||
{ | |||
dma.begin(true); // Allocate the DMA channel first | |||
#if defined(KINETISK) | |||
// TODO: should we set & clear the I2S_RCSR_SR bit here? | |||
AudioOutputI2SQuad::config_i2s(); | |||
@@ -73,6 +75,54 @@ void AudioInputI2SQuad::begin(void) | |||
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 | |||
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) | |||
@@ -98,6 +148,7 @@ void AudioInputI2SQuad::isr(void) | |||
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]); |
@@ -84,12 +84,17 @@ DMAChannel AudioInputPDM::dma(false); | |||
#define MCLK_MULT 1 | |||
#define MCLK_DIV 17 | |||
#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 | |||
#define MCLK_MULT 4 | |||
#define MCLK_MULT 2 | |||
#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 | |||
#define MCLK_MULT 12 | |||
#define MCLK_DIV 17 | |||
@@ -1714,4 +1719,4 @@ for ($n=0; $n < 512; $n += 8) { | |||
print "\n};\n"; | |||
print "// max=$max, min=$min\n"; | |||
*/ | |||
#endif | |||
#endif |
@@ -27,7 +27,7 @@ | |||
#include <Arduino.h> | |||
#include "input_tdm.h" | |||
#include "output_tdm.h" | |||
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(KINETISK) || defined(__IMXRT1062__) | |||
#include "utility/imxrt_hw.h" | |||
DMAMEM __attribute__((aligned(32))) | |||
@@ -66,8 +66,8 @@ void AudioInputTDM::begin(void) | |||
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 | |||
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; | |||
dma.TCD->SADDR = &I2S1_RDR0; | |||
dma.TCD->SOFF = 0; | |||
@@ -84,8 +84,7 @@ void AudioInputTDM::begin(void) | |||
update_responsibility = update_setup(); | |||
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); | |||
#endif | |||
} |
@@ -24,7 +24,7 @@ | |||
* THE SOFTWARE. | |||
*/ | |||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1062__) | |||
#include <Arduino.h> | |||
#include "input_tdm2.h" | |||
#include "output_tdm2.h" | |||
@@ -47,7 +47,7 @@ void AudioInputTDM2::begin(void) | |||
// TODO: should we set & clear the I2S_RCSR_SR bit here? | |||
AudioOutputTDM2::config_tdm(); | |||
CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||
CORE_PIN5_CONFIG = 2; //2:RX_DATA0 | |||
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; | |||
dma.TCD->SADDR = &I2S2_RDR0; | |||
dma.TCD->SOFF = 0; |
@@ -3,6 +3,8 @@ AudioConnection KEYWORD2 | |||
AudioInputI2S KEYWORD2 | |||
AudioInputI2S2 KEYWORD2 | |||
AudioInputI2SQuad KEYWORD2 | |||
AudioInputI2SHex KEYWORD2 | |||
AudioInputI2SOct KEYWORD2 | |||
AudioInputI2Sslave KEYWORD2 | |||
AudioInputTDM KEYWORD2 | |||
AudioInputTDM2 KEYWORD2 | |||
@@ -11,6 +13,8 @@ AudioInputUSB KEYWORD2 | |||
AudioOutputI2S KEYWORD2 | |||
AudioOutputI2S2 KEYWORD2 | |||
AudioOutputI2SQuad KEYWORD2 | |||
AudioOutputI2SHex KEYWORD2 | |||
AudioOutputI2SOct KEYWORD2 | |||
AudioOutputI2Sslave KEYWORD2 | |||
AudioOutputSPDIF KEYWORD2 | |||
AudioOutputSPDIF2 KEYWORD2 |
@@ -700,6 +700,9 @@ void AudioOutputADAT::setI2SFreq(int freq) { | |||
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) | |||
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 | |||
for (int f = 0; f < numfreqs; f++) { |
@@ -38,9 +38,9 @@ bool AudioOutputI2S::update_responsibility = false; | |||
DMAChannel AudioOutputI2S::dma(false); | |||
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" | |||
#endif | |||
void AudioOutputI2S::begin(void) | |||
{ | |||
@@ -50,74 +50,54 @@ void AudioOutputI2S::begin(void) | |||
block_right_1st = NULL; | |||
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->SOFF = 2; | |||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
dma.TCD->NBYTES_MLNO = 2; | |||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); | |||
dma.TCD->DOFF = 0; | |||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
dma.TCD->DLASTSGA = 0; | |||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
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(); | |||
} | |||
#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->SOFF = 2; | |||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
dma.TCD->NBYTES_MLNO = 2; | |||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); | |||
dma.TCD->DOFF = 0; | |||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
dma.TCD->DLASTSGA = 0; | |||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
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(); | |||
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); | |||
} | |||
#endif | |||
void AudioOutputI2S::isr(void) | |||
{ | |||
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(KINETISK) || defined(__IMXRT1062__) | |||
int16_t *dest; | |||
audio_block_t *blockL, *blockR; | |||
uint32_t saddr, offsetL, offsetR; | |||
@@ -154,10 +134,8 @@ void AudioOutputI2S::isr(void) | |||
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); | |||
} | |||
#if IMXRT_CACHE_ENABLED >= 2 | |||
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | |||
#endif | |||
if (offsetL < AUDIO_BLOCK_SAMPLES) { | |||
AudioOutputI2S::block_left_offset = offsetL; | |||
} else { | |||
@@ -323,12 +301,17 @@ void AudioOutputI2S::update(void) | |||
#define MCLK_MULT 1 | |||
#define MCLK_DIV 17 | |||
#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 | |||
#define MCLK_MULT 4 | |||
#define MCLK_MULT 2 | |||
#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 | |||
#define MCLK_MULT 12 | |||
#define MCLK_DIV 17 | |||
@@ -386,9 +369,14 @@ void AudioOutputI2S::config_i2s(void) | |||
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 | |||
#elif ( defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||
#elif defined(__IMXRT1062__) | |||
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: | |||
int fs = AUDIO_SAMPLE_RATE_EXACT; | |||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
@@ -408,18 +396,14 @@ void AudioOutputI2S::config_i2s(void) | |||
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | |||
| 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_PIN21_CONFIG = 3; //1:RX_BCLK | |||
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 tsync = 1; | |||
@@ -430,7 +414,8 @@ void AudioOutputI2S::config_i2s(void) | |||
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)); | |||
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_RMR = 0; | |||
@@ -439,7 +424,8 @@ void AudioOutputI2S::config_i2s(void) | |||
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)); | |||
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)); | |||
#endif | |||
@@ -453,7 +439,6 @@ void AudioOutputI2Sslave::begin(void) | |||
dma.begin(true); // Allocate the DMA channel first | |||
//pinMode(2, OUTPUT); | |||
block_left_1st = NULL; | |||
block_right_1st = NULL; | |||
@@ -473,50 +458,45 @@ void AudioOutputI2Sslave::begin(void) | |||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||
dma.enable(); | |||
I2S0_TCSR = I2S_TCSR_SR; | |||
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->SOFF = 2; | |||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
dma.TCD->NBYTES_MLNO = 2; | |||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
dma.TCD->DADDR = (void *)&i2s->TX.DR16[1]; | |||
dma.TCD->DOFF = 0; | |||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
dma.TCD->DLASTSGA = 0; | |||
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 | |||
update_responsibility = update_setup(); | |||
dma.enable(); | |||
dma.attachInterrupt(isr); | |||
} | |||
void AudioOutputI2Sslave::config_i2s(void) | |||
{ | |||
#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_SCGC7 |= SIM_SCGC7_DMA; | |||
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 | |||
// Configure to input the bit-clock from pin, bypasses the MCLK divider | |||
I2S0_MCR = I2S_MCR_MICS(0); | |||
@@ -544,73 +524,43 @@ void AudioOutputI2Sslave::config_i2s(void) | |||
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_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 | |||
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 | |||
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 | |||
} |
@@ -38,6 +38,14 @@ public: | |||
virtual void update(void); | |||
void begin(void); | |||
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: | |||
AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! | |||
static void config_i2s(void); |
@@ -23,7 +23,7 @@ | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1062__) | |||
#include <Arduino.h> | |||
#include "output_i2s2.h" | |||
#include "memcpy_audio.h" | |||
@@ -49,7 +49,12 @@ void AudioOutputI2S2::begin(void) | |||
block_right_1st = NULL; | |||
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->SOFF = 2; | |||
@@ -63,16 +68,17 @@ void AudioOutputI2S2::begin(void) | |||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); | |||
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(); | |||
dma.attachInterrupt(isr); | |||
dma.enable(); | |||
} | |||
void AudioOutputI2S2::isr(void) | |||
{ | |||
int16_t *dest, *dc; | |||
int16_t *dest; | |||
audio_block_t *blockL, *blockR; | |||
uint32_t saddr, offsetL, offsetR; | |||
@@ -184,6 +190,10 @@ void AudioOutputI2S2::update(void) | |||
void AudioOutputI2S2::config_i2s(void) | |||
{ | |||
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: | |||
int fs = AUDIO_SAMPLE_RATE_EXACT; | |||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
@@ -200,20 +210,14 @@ void AudioOutputI2S2::config_i2s(void) | |||
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_PRED(n1-1) | |||
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | |||
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 | |||
// 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 tsync = 0; | |||
@@ -222,18 +226,20 @@ void AudioOutputI2S2::config_i2s(void) | |||
//I2S2_TCSR = (1<<25); //Reset | |||
I2S2_TCR1 = I2S_TCR1_RFW(1); | |||
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_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_RMR = 0; | |||
//I2S2_RCSR = (1<<25); //Reset | |||
I2S2_RCR1 = I2S_RCR1_RFW(1); | |||
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_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)); | |||
} |
@@ -0,0 +1,355 @@ | |||
/* 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 |
@@ -0,0 +1,65 @@ | |||
/* 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 |
@@ -0,0 +1,419 @@ | |||
/* 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 |
@@ -0,0 +1,71 @@ | |||
/* 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 |
@@ -26,9 +26,14 @@ | |||
#include <Arduino.h> | |||
#include "output_i2s_quad.h" | |||
#include "output_i2s.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_ch2_1st = NULL; | |||
@@ -42,16 +47,14 @@ uint16_t AudioOutputI2SQuad::ch1_offset = 0; | |||
uint16_t AudioOutputI2SQuad::ch2_offset = 0; | |||
uint16_t AudioOutputI2SQuad::ch3_offset = 0; | |||
uint16_t AudioOutputI2SQuad::ch4_offset = 0; | |||
//audio_block_t * AudioOutputI2SQuad::inputQueueArray[4]; | |||
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); | |||
static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; | |||
void AudioOutputI2SQuad::begin(void) | |||
{ | |||
#if 1 | |||
dma.begin(true); // Allocate the DMA channel first | |||
block_ch1_1st = NULL; | |||
@@ -59,6 +62,7 @@ void AudioOutputI2SQuad::begin(void) | |||
block_ch3_1st = NULL; | |||
block_ch4_1st = NULL; | |||
#if defined(KINETISK) | |||
// TODO: can we call normal config_i2s, and then just enable the extra output? | |||
config_i2s(); | |||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2 | |||
@@ -82,6 +86,45 @@ void AudioOutputI2SQuad::begin(void) | |||
I2S0_TCSR = I2S_TCSR_SR; | |||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
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 | |||
} | |||
@@ -108,7 +151,6 @@ void AudioOutputI2SQuad::isr(void) | |||
src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; | |||
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; | |||
// TODO: fast 4-way interleaved memcpy... | |||
#if 1 | |||
memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); | |||
#else | |||
@@ -119,6 +161,7 @@ void AudioOutputI2SQuad::isr(void) | |||
*dest++ = *src4++; | |||
} | |||
#endif | |||
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | |||
if (block_ch1_1st) { | |||
if (ch1_offset == 0) { | |||
@@ -245,7 +288,7 @@ void AudioOutputI2SQuad::update(void) | |||
} | |||
} | |||
#if defined(KINETISK) | |||
// 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 | |||
@@ -272,12 +315,17 @@ void AudioOutputI2SQuad::update(void) | |||
#define MCLK_MULT 1 | |||
#define MCLK_DIV 17 | |||
#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 | |||
#define MCLK_MULT 4 | |||
#define MCLK_MULT 2 | |||
#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 | |||
#define MCLK_MULT 12 | |||
#define MCLK_DIV 17 | |||
@@ -333,10 +381,10 @@ void AudioOutputI2SQuad::config_i2s(void) | |||
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 | |||
} | |||
#endif // KINETISK | |||
#else // not __MK20DX256__ | |||
#else // not supported | |||
void AudioOutputI2SQuad::begin(void) | |||
{ |
@@ -71,8 +71,11 @@ void AudioOutputPT8211::begin(void) | |||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
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; | |||
return; | |||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1052__) | |||
@@ -96,10 +99,12 @@ void AudioOutputPT8211::begin(void) | |||
I2S1_RCSR |= I2S_RCSR_RE; | |||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
#endif | |||
update_responsibility = update_setup(); | |||
dma.attachInterrupt(isr); | |||
dma.enable(); | |||
return; | |||
#endif | |||
} | |||
void AudioOutputPT8211::isr(void) | |||
@@ -429,12 +434,17 @@ void AudioOutputPT8211::update(void) | |||
#define MCLK_MULT 1 | |||
#define MCLK_DIV 17 | |||
#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 | |||
#define MCLK_MULT 4 | |||
#define MCLK_MULT 2 | |||
#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 | |||
#define MCLK_MULT 12 | |||
#define MCLK_DIV 17 |
@@ -327,12 +327,17 @@ void AudioOutputSPDIF::update(void) | |||
#define MCLK_MULT 1 | |||
#define MCLK_DIV 17 | |||
#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 | |||
#define MCLK_MULT 4 | |||
#define MCLK_MULT 2 | |||
#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 | |||
#define MCLK_MULT 12 | |||
#define MCLK_DIV 17 |
@@ -72,8 +72,8 @@ void AudioOutputTDM::begin(void) | |||
I2S0_TCSR = I2S_TCSR_SR; | |||
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->SOFF = 4; | |||
@@ -91,8 +91,8 @@ void AudioOutputTDM::begin(void) | |||
update_responsibility = update_setup(); | |||
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 | |||
dma.attachInterrupt(isr); | |||
@@ -125,7 +125,7 @@ static void memcpy_tdm_tx(uint32_t *dest, const uint32_t *src1, const uint32_t * | |||
void AudioOutputTDM::isr(void) | |||
{ | |||
uint32_t *dest, *dc; | |||
uint32_t *dest; | |||
const uint32_t *src1, *src2; | |||
uint32_t i, saddr; | |||
@@ -141,7 +141,11 @@ void AudioOutputTDM::isr(void) | |||
dest = tdm_tx_buffer; | |||
} | |||
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) { | |||
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros; | |||
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros; | |||
@@ -205,12 +209,17 @@ void AudioOutputTDM::update(void) | |||
#define MCLK_MULT 2 | |||
#define MCLK_DIV 17 | |||
#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 | |||
#define MCLK_MULT 8 | |||
#define MCLK_MULT 2 | |||
#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 | |||
#error "This CPU Clock Speed is not supported by the Audio library"; | |||
#endif | |||
@@ -265,8 +274,12 @@ void AudioOutputTDM::config_tdm(void) | |||
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 | |||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#elif defined(__IMXRT1062__) | |||
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: | |||
int fs = AUDIO_SAMPLE_RATE_EXACT; | |||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
@@ -291,10 +304,6 @@ void AudioOutputTDM::config_tdm(void) | |||
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; | |||
// configure transmitter | |||
int rsync = 0; | |||
int tsync = 1; |
@@ -24,7 +24,7 @@ | |||
* THE SOFTWARE. | |||
*/ | |||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1062__) | |||
#include <Arduino.h> | |||
#include "output_tdm2.h" | |||
#include "memcpy_audio.h" | |||
@@ -159,8 +159,11 @@ void AudioOutputTDM2::update(void) | |||
void AudioOutputTDM2::config_tdm(void) | |||
{ | |||
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: | |||
int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ | |||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
@@ -186,12 +189,6 @@ void AudioOutputTDM2::config_tdm(void) | |||
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 | |||
// 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 | |||
int rsync = 1; | |||
int tsync = 0; | |||
@@ -215,10 +212,9 @@ void AudioOutputTDM2::config_tdm(void) | |||
| I2S_RCR4_FSE | I2S_RCR4_FSD; | |||
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_PIN3_CONFIG = 2; //2:TX_SYNC | |||
} | |||
#endif | |||
#endif |
@@ -52,7 +52,7 @@ void AudioPlayQueue::playBuffer(void) | |||
if (!userblock) return; | |||
h = head + 1; | |||
if (h >= 32) h = 0; | |||
if (h >= max_buffers) h = 0; | |||
while (tail == h) ; // wait until space in the queue | |||
queue[h] = userblock; | |||
head = h; | |||
@@ -66,7 +66,7 @@ void AudioPlayQueue::update(void) | |||
t = tail; | |||
if (t != head) { | |||
if (++t >= 32) t = 0; | |||
if (++t >= max_buffers) t = 0; | |||
block = queue[t]; | |||
tail = t; | |||
transmit(block); |
@@ -32,6 +32,12 @@ | |||
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: | |||
AudioPlayQueue(void) : AudioStream(0, NULL), | |||
userblock(NULL), head(0), tail(0) { } | |||
@@ -44,7 +50,7 @@ public: | |||
//bool isPlaying(void) { return playing; } | |||
virtual void update(void); | |||
private: | |||
audio_block_t *queue[32]; | |||
audio_block_t *queue[max_buffers]; | |||
audio_block_t *userblock; | |||
volatile uint8_t head, tail; | |||
}; |
@@ -36,7 +36,7 @@ int AudioRecordQueue::available(void) | |||
h = head; | |||
t = tail; | |||
if (h >= t) return h - t; | |||
return 53 + h - t; | |||
return max_buffers + h - t; | |||
} | |||
void AudioRecordQueue::clear(void) | |||
@@ -49,7 +49,7 @@ void AudioRecordQueue::clear(void) | |||
} | |||
t = tail; | |||
while (t != head) { | |||
if (++t >= 53) t = 0; | |||
if (++t >= max_buffers) t = 0; | |||
release(queue[t]); | |||
} | |||
tail = t; | |||
@@ -62,7 +62,7 @@ int16_t * AudioRecordQueue::readBuffer(void) | |||
if (userblock) return NULL; | |||
t = tail; | |||
if (t == head) return NULL; | |||
if (++t >= 53) t = 0; | |||
if (++t >= max_buffers) t = 0; | |||
userblock = queue[t]; | |||
tail = t; | |||
return userblock->data; | |||
@@ -87,7 +87,7 @@ void AudioRecordQueue::update(void) | |||
return; | |||
} | |||
h = head + 1; | |||
if (h >= 53) h = 0; | |||
if (h >= max_buffers) h = 0; | |||
if (h == tail) { | |||
release(block); | |||
} else { |
@@ -32,6 +32,12 @@ | |||
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: | |||
AudioRecordQueue(void) : AudioStream(1, inputQueueArray), | |||
userblock(NULL), head(0), tail(0), enabled(0) { } | |||
@@ -49,7 +55,7 @@ public: | |||
virtual void update(void); | |||
private: | |||
audio_block_t *inputQueueArray[1]; | |||
audio_block_t * volatile queue[53]; | |||
audio_block_t * volatile queue[max_buffers]; | |||
audio_block_t *userblock; | |||
volatile uint8_t head, tail, enabled; | |||
}; |
@@ -31,7 +31,7 @@ | |||
#include "imxrt_hw.h" | |||
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; | |||
@@ -27,7 +27,7 @@ | |||
(c) Frank B | |||
*/ | |||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
#if defined(__IMXRT1062__) | |||
#ifndef imxr_hw_h_ | |||
#define imxr_hw_h_ |