#include "input_i2s.h" | #include "input_i2s.h" | ||||
#include "input_i2s2.h" | #include "input_i2s2.h" | ||||
#include "input_i2s_quad.h" | #include "input_i2s_quad.h" | ||||
#include "input_i2s_hex.h" | |||||
#include "input_i2s_oct.h" | |||||
#include "input_tdm.h" | #include "input_tdm.h" | ||||
#include "input_tdm2.h" | #include "input_tdm2.h" | ||||
#include "input_pdm.h" | #include "input_pdm.h" | ||||
#include "output_i2s.h" | #include "output_i2s.h" | ||||
#include "output_i2s2.h" | #include "output_i2s2.h" | ||||
#include "output_i2s_quad.h" | #include "output_i2s_quad.h" | ||||
#include "output_i2s_hex.h" | |||||
#include "output_i2s_oct.h" | |||||
#include "output_mqs.h" | #include "output_mqs.h" | ||||
#include "output_pwm.h" | #include "output_pwm.h" | ||||
#include "output_spdif.h" | #include "output_spdif.h" |
// then un-mute the DACs. | // then un-mute the DACs. | ||||
// 9. Normal operation begins. | // 9. Normal operation begins. | ||||
// Some people have found their CS42448 goes into a strange mode where VQ is 1.25V | |||||
// instead of the normal 2.5V. Apparently there is a workaround for this problem | |||||
// which involves writing to an undocumented bit. Details here: | |||||
// https://forum.pjrc.com/threads/41371?p=215881&viewfull=1#post215881 | |||||
static const uint8_t default_config[] = { | static const uint8_t default_config[] = { | ||||
0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max | 0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max | ||||
0x76, // CS42448_Interface_Formats = TDM mode | 0x76, // CS42448_Interface_Formats = TDM mode |
#include "AudioStream.h" | #include "AudioStream.h" | ||||
#include "utility/dspinst.h" | #include "utility/dspinst.h" | ||||
#if defined(__MK66FX1M0__) | |||||
#if defined(__IMXRT1062__) | |||||
// 4.00 second maximum on Teensy 4.0 | |||||
#define DELAY_QUEUE_SIZE (176512 / AUDIO_BLOCK_SAMPLES) | |||||
#elif defined(__MK66FX1M0__) | |||||
// 2.41 second maximum on Teensy 3.6 | // 2.41 second maximum on Teensy 3.6 | ||||
#define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SAMPLES) | #define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SAMPLES) | ||||
#elif defined(__MK64FX512__) | #elif defined(__MK64FX512__) |
/* 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(" "); | |||||
} |
// | // | ||||
// Recommended connections: | // Recommended connections: | ||||
// | // | ||||
// Mikroe Teensy 3.1 | |||||
// ------ ---------- | |||||
// SCK 9 | |||||
// MISO 13 | |||||
// MOSI 22 | |||||
// ADCL 23 (yes, ADCL & DACL connect together) | |||||
// DACL 23 | |||||
// SDA 18 | |||||
// SCL 19 | |||||
// 3.3V +3.3V | |||||
// GND GND | |||||
// MikroE Teensy 3 Teensy 4 | |||||
// ------ -------- -------- | |||||
// SCK 9 21 | |||||
// MISO 13 8 | |||||
// MOSI 22 7 | |||||
// ADCL 23 20 (yes, ADCL & DACL connect together) | |||||
// DACL 23 20 | |||||
// SDA 18 18 | |||||
// SCL 19 19 | |||||
// 3.3V +3.3V +3.3V | |||||
// GND GND GND | |||||
// | // | ||||
// For connection using I2S master mode (WM8731 in slave mode, with MCLK): | // For connection using I2S master mode (WM8731 in slave mode, with MCLK): | ||||
// https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733 | // https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733 | ||||
void setup() { | void setup() { | ||||
delay(1000); // allow the WM7831 extra time to power up | |||||
wm8731m1.enable(); | wm8731m1.enable(); | ||||
AudioMemory(15); | AudioMemory(15); |
memcpy(buffer+256, queue1.readBuffer(), 256); | memcpy(buffer+256, queue1.readBuffer(), 256); | ||||
queue1.freeBuffer(); | queue1.freeBuffer(); | ||||
// write all 512 bytes to the SD card | // write all 512 bytes to the SD card | ||||
elapsedMicros usec = 0; | |||||
//elapsedMicros usec = 0; | |||||
frec.write(buffer, 512); | frec.write(buffer, 512); | ||||
// Uncomment these lines to see how long SD writes | // Uncomment these lines to see how long SD writes | ||||
// are taking. A pair of audio blocks arrives every | // are taking. A pair of audio blocks arrives every |
<p>The I2S signals are used in "master" mode, where Teensy creates | <p>The I2S signals are used in "master" mode, where Teensy creates | ||||
all 3 clock signals and controls all data timing.</p> | all 3 clock signals and controls all data timing.</p> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>13</td><td>RX</td><td>Input</td></tr> | |||||
<tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr> | |||||
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>13</td><td align=center>8</td><td>RX</td><td>Input</td></tr> | |||||
<tr class=odd><td align=center>23</td><td align=center>20</td><td>LRCLK</td><td>Output</td></tr> | |||||
</table> | </table> | ||||
<p>Audio from | <p>Audio from | ||||
master mode I2S may be used in the same project as ADC, DAC and | master mode I2S may be used in the same project as ADC, DAC and | ||||
<a href="https://forum.pjrc.com/threads/42894">Invensense ICS-52000 microphones</a>. | <a href="https://forum.pjrc.com/threads/42894">Invensense ICS-52000 microphones</a>. | ||||
</p> | </p> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||||
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||||
<tr class=odd><td align=center>13</td><td>RX</td><td>Input, 11.3 Mbit/sec</td></tr> | |||||
<tr class=odd><td align=center>23</td><td>FS</td><td>Output</td></tr> | |||||
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||||
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||||
<tr class=odd><td align=center>13</td><td align=center>8</td><td>RX</td><td>Input, 11.3 Mbit/sec</td></tr> | |||||
<tr class=odd><td align=center>23</td><td align=center>21</td><td>FS</td><td>Output</td></tr> | |||||
</table> | </table> | ||||
<p>Audio from | <p>Audio from | ||||
master mode TDM may be used in the same project as ADC, DAC and | master mode TDM may be used in the same project as ADC, DAC and | ||||
<p>The I2S signals are used in "master" mode, where Teensy creates | <p>The I2S signals are used in "master" mode, where Teensy creates | ||||
all 3 clock signals and controls all data timing.</p> | all 3 clock signals and controls all data timing.</p> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>22</td><td>TX</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr> | |||||
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>22</td><td align=center>7</td><td>TX</td><td>Output</td></tr> | |||||
<tr class=odd><td align=center>23</td><td align=center>20</td><td>LRCLK</td><td>Output</td></tr> | |||||
</table> | </table> | ||||
<p>Audio from | <p>Audio from | ||||
master mode I2S may be used in the same project as ADC, DAC and | master mode I2S may be used in the same project as ADC, DAC and | ||||
CS42448 Circuit Board</a>. | CS42448 Circuit Board</a>. | ||||
</p> | </p> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||||
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||||
<tr class=odd><td align=center>22</td><td>TX</td><td>Output, 11.3 Mbit/sec</td></tr> | |||||
<tr class=odd><td align=center>23</td><td>WS</td><td>Output</td></tr> | |||||
<tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||||
<tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||||
<tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||||
<tr class=odd><td align=center>22</td><td align=center>7</td><td>TX</td><td>Output, 11.3 Mbit/sec</td></tr> | |||||
<tr class=odd><td align=center>23</td><td align=center>20</td><td>WS</td><td>Output</td></tr> | |||||
</table> | </table> | ||||
<p>Audio from | <p>Audio from | ||||
master mode TDM may be used in the same project as ADC, DAC and | master mode TDM may be used in the same project as ADC, DAC and |
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | ||||
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | ||||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN8_CONFIG = 3; //1:RX_DATA0 | CORE_PIN8_CONFIG = 3; //1:RX_DATA0 | ||||
#elif defined(__IMXRT1052__) | |||||
CORE_PIN7_CONFIG = 3; //1:RX_DATA0 | |||||
#endif | |||||
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | ||||
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0+2); | |||||
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2); | |||||
dma.TCD->SOFF = 0; | dma.TCD->SOFF = 0; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->NBYTES_MLNO = 2; | dma.TCD->NBYTES_MLNO = 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | ||||
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE; | |||||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||||
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||||
#endif | #endif | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | dma.enable(); | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
//pinMode(13, OUTPUT); | |||||
} | } | ||||
void AudioInputI2S::isr(void) | void AudioInputI2S::isr(void) | ||||
int16_t *dest_left, *dest_right; | int16_t *dest_left, *dest_right; | ||||
audio_block_t *left, *right; | audio_block_t *left, *right; | ||||
//digitalWriteFast(13, HIGH); | |||||
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(KINETISK) || defined(__IMXRT1062__) | |||||
daddr = (uint32_t)(dma.TCD->DADDR); | daddr = (uint32_t)(dma.TCD->DADDR); | ||||
#endif | #endif | ||||
dma.clearInterrupt(); | dma.clearInterrupt(); | ||||
//Serial.println("isr"); | |||||
if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { | if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { | ||||
// DMA is receiving to the first half of the buffer | // DMA is receiving to the first half of the buffer | ||||
} while (src < end); | } while (src < end); | ||||
} | } | ||||
} | } | ||||
//digitalWriteFast(13, LOW); | |||||
} | } | ||||
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | ||||
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
#endif | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN8_CONFIG = 3; //1:RX_DATA0 | |||||
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | |||||
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2); | |||||
dma.TCD->SOFF = 0; | |||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||||
dma.TCD->NBYTES_MLNO = 2; | |||||
dma.TCD->SLAST = 0; | |||||
dma.TCD->DADDR = i2s_rx_buffer; | |||||
dma.TCD->DOFF = 2; | |||||
dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; | |||||
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); | |||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; | |||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||||
dma.enable(); | |||||
I2S1_RCSR = 0; | |||||
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||||
update_responsibility = update_setup(); | |||||
dma.attachInterrupt(isr); | |||||
#endif | |||||
} | } | ||||
*/ | */ | ||||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "input_i2s2.h" | #include "input_i2s2.h" | ||||
#include "output_i2s2.h" | #include "output_i2s2.h" | ||||
// TODO: should we set & clear the I2S_RCSR_SR bit here? | // TODO: should we set & clear the I2S_RCSR_SR bit here? | ||||
AudioOutputI2S2::config_i2s(); | AudioOutputI2S2::config_i2s(); | ||||
CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||||
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; | |||||
CORE_PIN5_CONFIG = 2; //EMC_08, 2=SAI2_RX_DATA, page 434 | |||||
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; // 0=GPIO_EMC_08_ALT2, page 876 | |||||
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2); | dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2); | ||||
dma.TCD->SOFF = 0; | dma.TCD->SOFF = 0; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX); | ||||
dma.enable(); | |||||
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE; | |||||
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||||
I2S2_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; // page 2099 | |||||
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // page 2087 | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | |||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
//pinMode(13, OUTPUT); | |||||
} | } | ||||
void AudioInputI2S2::isr(void) | void AudioInputI2S2::isr(void) | ||||
int16_t *dest_left, *dest_right; | int16_t *dest_left, *dest_right; | ||||
audio_block_t *left, *right; | audio_block_t *left, *right; | ||||
//digitalWriteFast(13, HIGH); | |||||
daddr = (uint32_t)(dma.TCD->DADDR); | daddr = (uint32_t)(dma.TCD->DADDR); | ||||
dma.clearInterrupt(); | dma.clearInterrupt(); | ||||
} while (src < end); | } while (src < end); | ||||
} | } | ||||
} | } | ||||
//digitalWriteFast(13, LOW); | |||||
} | } | ||||
/* 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 | |||||
/* 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 |
/* 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 | |||||
/* 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 |
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "input_i2s_quad.h" | #include "input_i2s_quad.h" | ||||
#include "output_i2s_quad.h" | #include "output_i2s_quad.h" | ||||
#include "output_i2s.h" | |||||
DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
audio_block_t * AudioInputI2SQuad::block_ch1 = NULL; | audio_block_t * AudioInputI2SQuad::block_ch1 = NULL; | ||||
audio_block_t * AudioInputI2SQuad::block_ch2 = NULL; | audio_block_t * AudioInputI2SQuad::block_ch2 = NULL; | ||||
audio_block_t * AudioInputI2SQuad::block_ch3 = NULL; | audio_block_t * AudioInputI2SQuad::block_ch3 = NULL; | ||||
bool AudioInputI2SQuad::update_responsibility = false; | bool AudioInputI2SQuad::update_responsibility = false; | ||||
DMAChannel AudioInputI2SQuad::dma(false); | DMAChannel AudioInputI2SQuad::dma(false); | ||||
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | |||||
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) | |||||
void AudioInputI2SQuad::begin(void) | void AudioInputI2SQuad::begin(void) | ||||
{ | { | ||||
dma.begin(true); // Allocate the DMA channel first | dma.begin(true); // Allocate the DMA channel first | ||||
#if defined(KINETISK) | |||||
// TODO: should we set & clear the I2S_RCSR_SR bit here? | // TODO: should we set & clear the I2S_RCSR_SR bit here? | ||||
AudioOutputI2SQuad::config_i2s(); | AudioOutputI2SQuad::config_i2s(); | ||||
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | ||||
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
#elif defined(__IMXRT1062__) | |||||
const int pinoffset = 0; // TODO: make this configurable... | |||||
AudioOutputI2S::config_i2s(); | |||||
I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset; | |||||
switch (pinoffset) { | |||||
case 0: | |||||
CORE_PIN8_CONFIG = 3; | |||||
CORE_PIN6_CONFIG = 3; | |||||
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873 | |||||
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||||
break; | |||||
case 1: | |||||
CORE_PIN6_CONFIG = 3; | |||||
CORE_PIN9_CONFIG = 3; | |||||
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||||
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||||
break; | |||||
case 2: | |||||
CORE_PIN9_CONFIG = 3; | |||||
CORE_PIN32_CONFIG = 3; | |||||
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||||
IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875 | |||||
break; | |||||
} | |||||
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2 + pinoffset * 4); | |||||
dma.TCD->SOFF = 4; | |||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||||
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE | | |||||
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) | | |||||
DMA_TCD_NBYTES_MLOFFYES_NBYTES(4); | |||||
dma.TCD->SLAST = -8; | |||||
dma.TCD->DADDR = i2s_rx_buffer; | |||||
dma.TCD->DOFF = 2; | |||||
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||||
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); | |||||
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||||
I2S1_RCSR = 0; | |||||
I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset; | |||||
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||||
update_responsibility = update_setup(); | |||||
dma.enable(); | |||||
dma.attachInterrupt(isr); | |||||
#endif | |||||
} | } | ||||
void AudioInputI2SQuad::isr(void) | void AudioInputI2SQuad::isr(void) | ||||
if (block_ch1) { | if (block_ch1) { | ||||
offset = block_offset; | offset = block_offset; | ||||
if (offset <= AUDIO_BLOCK_SAMPLES/2) { | if (offset <= AUDIO_BLOCK_SAMPLES/2) { | ||||
arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2); | |||||
block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | ||||
dest1 = &(block_ch1->data[offset]); | dest1 = &(block_ch1->data[offset]); | ||||
dest2 = &(block_ch2->data[offset]); | dest2 = &(block_ch2->data[offset]); |
#define MCLK_MULT 1 | #define MCLK_MULT 1 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
#elif F_CPU == 216000000 | #elif F_CPU == 216000000 | ||||
#define MCLK_MULT 8 | |||||
#define MCLK_DIV 153 | |||||
#define MCLK_SRC 0 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 240000000 | #elif F_CPU == 240000000 | ||||
#define MCLK_MULT 4 | |||||
#define MCLK_MULT 2 | |||||
#define MCLK_DIV 85 | #define MCLK_DIV 85 | ||||
#define MCLK_SRC 0 | |||||
#elif F_CPU == 256000000 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 16000000 | #elif F_CPU == 16000000 | ||||
#define MCLK_MULT 12 | #define MCLK_MULT 12 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
print "\n};\n"; | print "\n};\n"; | ||||
print "// max=$max, min=$min\n"; | print "// max=$max, min=$min\n"; | ||||
*/ | */ | ||||
#endif | |||||
#endif |
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "input_tdm.h" | #include "input_tdm.h" | ||||
#include "output_tdm.h" | #include "output_tdm.h" | ||||
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(KINETISK) || defined(__IMXRT1062__) | |||||
#include "utility/imxrt_hw.h" | #include "utility/imxrt_hw.h" | ||||
DMAMEM __attribute__((aligned(32))) | DMAMEM __attribute__((aligned(32))) | ||||
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | ||||
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
#else | |||||
CORE_PIN7_CONFIG = 3; //RX_DATA0 | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN8_CONFIG = 3; //RX_DATA0 | |||||
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | ||||
dma.TCD->SADDR = &I2S1_RDR0; | dma.TCD->SADDR = &I2S1_RDR0; | ||||
dma.TCD->SOFF = 0; | dma.TCD->SOFF = 0; | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | dma.enable(); | ||||
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||||
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
#endif | #endif | ||||
} | } |
* THE SOFTWARE. | * THE SOFTWARE. | ||||
*/ | */ | ||||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "input_tdm2.h" | #include "input_tdm2.h" | ||||
#include "output_tdm2.h" | #include "output_tdm2.h" | ||||
// TODO: should we set & clear the I2S_RCSR_SR bit here? | // TODO: should we set & clear the I2S_RCSR_SR bit here? | ||||
AudioOutputTDM2::config_tdm(); | AudioOutputTDM2::config_tdm(); | ||||
CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||||
CORE_PIN5_CONFIG = 2; //2:RX_DATA0 | |||||
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; | IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; | ||||
dma.TCD->SADDR = &I2S2_RDR0; | dma.TCD->SADDR = &I2S2_RDR0; | ||||
dma.TCD->SOFF = 0; | dma.TCD->SOFF = 0; |
AudioInputI2S KEYWORD2 | AudioInputI2S KEYWORD2 | ||||
AudioInputI2S2 KEYWORD2 | AudioInputI2S2 KEYWORD2 | ||||
AudioInputI2SQuad KEYWORD2 | AudioInputI2SQuad KEYWORD2 | ||||
AudioInputI2SHex KEYWORD2 | |||||
AudioInputI2SOct KEYWORD2 | |||||
AudioInputI2Sslave KEYWORD2 | AudioInputI2Sslave KEYWORD2 | ||||
AudioInputTDM KEYWORD2 | AudioInputTDM KEYWORD2 | ||||
AudioInputTDM2 KEYWORD2 | AudioInputTDM2 KEYWORD2 | ||||
AudioOutputI2S KEYWORD2 | AudioOutputI2S KEYWORD2 | ||||
AudioOutputI2S2 KEYWORD2 | AudioOutputI2S2 KEYWORD2 | ||||
AudioOutputI2SQuad KEYWORD2 | AudioOutputI2SQuad KEYWORD2 | ||||
AudioOutputI2SHex KEYWORD2 | |||||
AudioOutputI2SOct KEYWORD2 | |||||
AudioOutputI2Sslave KEYWORD2 | AudioOutputI2Sslave KEYWORD2 | ||||
AudioOutputSPDIF KEYWORD2 | AudioOutputSPDIF KEYWORD2 | ||||
AudioOutputSPDIF2 KEYWORD2 | AudioOutputSPDIF2 KEYWORD2 |
const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} }; | const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} }; | ||||
#elif (F_PLL==240000000) | #elif (F_PLL==240000000) | ||||
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; | const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; | ||||
#elif (F_PLL==256000000) | |||||
// TODO: fix these... | |||||
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; | |||||
#endif | #endif | ||||
for (int f = 0; f < numfreqs; f++) { | for (int f = 0; f < numfreqs; f++) { |
DMAChannel AudioOutputI2S::dma(false); | DMAChannel AudioOutputI2S::dma(false); | ||||
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | ||||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#include "utility/imxrt_hw.h" | #include "utility/imxrt_hw.h" | ||||
#endif | |||||
void AudioOutputI2S::begin(void) | void AudioOutputI2S::begin(void) | ||||
{ | { | ||||
block_right_1st = NULL; | block_right_1st = NULL; | ||||
config_i2s(); | config_i2s(); | ||||
#if defined(__IMXRT1052__) | |||||
CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||||
#endif | |||||
#if defined(KINETISK) | |||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | |||||
dma.TCD->SADDR = i2s_tx_buffer; | dma.TCD->SADDR = i2s_tx_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->NBYTES_MLNO = 2; | dma.TCD->NBYTES_MLNO = 2; | ||||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | ||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); | |||||
dma.TCD->DOFF = 0; | dma.TCD->DOFF = 0; | ||||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->DLASTSGA = 0; | dma.TCD->DLASTSGA = 0; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||||
I2S1_RCSR |= I2S_RCSR_RE; | |||||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
update_responsibility = update_setup(); | |||||
dma.attachInterrupt(isr); | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||||
dma.enable(); | dma.enable(); | ||||
} | |||||
#endif | |||||
#if defined(KINETISK) | |||||
void AudioOutputI2S::begin(void) | |||||
{ | |||||
dma.begin(true); // Allocate the DMA channel first | |||||
block_left_1st = NULL; | |||||
block_right_1st = NULL; | |||||
// TODO: should we set & clear the I2S_TCSR_SR bit here? | |||||
config_i2s(); | |||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | |||||
I2S0_TCSR = I2S_TCSR_SR; | |||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||||
dma.TCD->SADDR = i2s_tx_buffer; | dma.TCD->SADDR = i2s_tx_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->NBYTES_MLNO = 2; | dma.TCD->NBYTES_MLNO = 2; | ||||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | ||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); | |||||
dma.TCD->DOFF = 0; | dma.TCD->DOFF = 0; | ||||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->DLASTSGA = 0; | dma.TCD->DLASTSGA = 0; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||||
update_responsibility = update_setup(); | |||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||||
dma.enable(); | dma.enable(); | ||||
I2S0_TCSR = I2S_TCSR_SR; | |||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||||
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
#endif | |||||
update_responsibility = update_setup(); | |||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
} | } | ||||
#endif | |||||
void AudioOutputI2S::isr(void) | void AudioOutputI2S::isr(void) | ||||
{ | { | ||||
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(KINETISK) || defined(__IMXRT1062__) | |||||
int16_t *dest; | int16_t *dest; | ||||
audio_block_t *blockL, *blockR; | audio_block_t *blockL, *blockR; | ||||
uint32_t saddr, offsetL, offsetR; | uint32_t saddr, offsetL, offsetR; | ||||
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); | memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); | ||||
} | } | ||||
#if IMXRT_CACHE_ENABLED >= 2 | |||||
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | ||||
#endif | |||||
if (offsetL < AUDIO_BLOCK_SAMPLES) { | if (offsetL < AUDIO_BLOCK_SAMPLES) { | ||||
AudioOutputI2S::block_left_offset = offsetL; | AudioOutputI2S::block_left_offset = offsetL; | ||||
} else { | } else { | ||||
#define MCLK_MULT 1 | #define MCLK_MULT 1 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
#elif F_CPU == 216000000 | #elif F_CPU == 216000000 | ||||
#define MCLK_MULT 8 | |||||
#define MCLK_DIV 153 | |||||
#define MCLK_SRC 0 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 240000000 | #elif F_CPU == 240000000 | ||||
#define MCLK_MULT 4 | |||||
#define MCLK_MULT 2 | |||||
#define MCLK_DIV 85 | #define MCLK_DIV 85 | ||||
#define MCLK_SRC 0 | |||||
#elif F_CPU == 256000000 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 16000000 | #elif F_CPU == 16000000 | ||||
#define MCLK_MULT 12 | #define MCLK_MULT 12 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | ||||
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | ||||
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | ||||
#elif ( defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||||
#elif defined(__IMXRT1062__) | |||||
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S1_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S1_RCSR & I2S_RCSR_RE) return; | |||||
//PLL: | //PLL: | ||||
int fs = AUDIO_SAMPLE_RATE_EXACT; | int fs = AUDIO_SAMPLE_RATE_EXACT; | ||||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | // PLL between 27*24 = 648MHz und 54*24=1296MHz | ||||
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | | CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | ||||
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f | | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f | ||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | |||||
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | |||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S1_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S1_RCSR & I2S_RCSR_RE) return; | |||||
// Select MCLK | |||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 | |||||
& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | |||||
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); | |||||
CORE_PIN23_CONFIG = 3; //1:MCLK | CORE_PIN23_CONFIG = 3; //1:MCLK | ||||
CORE_PIN21_CONFIG = 3; //1:RX_BCLK | CORE_PIN21_CONFIG = 3; //1:RX_BCLK | ||||
CORE_PIN20_CONFIG = 3; //1:RX_SYNC | CORE_PIN20_CONFIG = 3; //1:RX_SYNC | ||||
// CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||||
// CORE_PIN7_CONFIG = 3; //1:RX_DATA0 | |||||
int rsync = 0; | int rsync = 0; | ||||
int tsync = 1; | int tsync = 1; | ||||
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; | I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; | ||||
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | ||||
I2S1_TCR3 = I2S_TCR3_TCE; | I2S1_TCR3 = I2S_TCR3_TCE; | ||||
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||||
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | |||||
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||||
I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); | I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); | ||||
I2S1_RMR = 0; | I2S1_RMR = 0; | ||||
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; | I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; | ||||
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | ||||
I2S1_RCR3 = I2S_RCR3_RCE; | I2S1_RCR3 = I2S_RCR3_RCE; | ||||
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||||
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | |||||
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||||
I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); | I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); | ||||
#endif | #endif | ||||
dma.begin(true); // Allocate the DMA channel first | dma.begin(true); // Allocate the DMA channel first | ||||
//pinMode(2, OUTPUT); | |||||
block_left_1st = NULL; | block_left_1st = NULL; | ||||
block_right_1st = NULL; | block_right_1st = NULL; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | ||||
dma.enable(); | |||||
I2S0_TCSR = I2S_TCSR_SR; | I2S0_TCSR = I2S_TCSR_SR; | ||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | ||||
#elif 0 && ( defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||||
#if defined(SAI1) | |||||
CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||||
//CORE_PIN7_CONFIG = 3; //1:RX_DATA0 | |||||
#elif defined(SAI2) | |||||
CORE_PIN2_CONFIG = 2; //2:TX_DATA0 | |||||
//CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||||
#endif | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||||
dma.TCD->SADDR = i2s_tx_buffer; | dma.TCD->SADDR = i2s_tx_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.TCD->NBYTES_MLNO = 2; | dma.TCD->NBYTES_MLNO = 2; | ||||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | ||||
dma.TCD->DADDR = (void *)&i2s->TX.DR16[1]; | |||||
dma.TCD->DOFF = 0; | dma.TCD->DOFF = 0; | ||||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.TCD->DLASTSGA = 0; | dma.TCD->DLASTSGA = 0; | ||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); | |||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||||
dma.enable(); | |||||
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||||
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
#endif | #endif | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | |||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
} | } | ||||
void AudioOutputI2Sslave::config_i2s(void) | void AudioOutputI2Sslave::config_i2s(void) | ||||
{ | { | ||||
#if defined(KINETISK) | #if defined(KINETISK) | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S0_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S0_RCSR & I2S_RCSR_RE) return; | |||||
SIM_SCGC6 |= SIM_SCGC6_I2S; | SIM_SCGC6 |= SIM_SCGC6_I2S; | ||||
SIM_SCGC7 |= SIM_SCGC7_DMA; | SIM_SCGC7 |= SIM_SCGC7_DMA; | ||||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX; | SIM_SCGC6 |= SIM_SCGC6_DMAMUX; | ||||
// configure pin mux for 3 clock signals | |||||
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | |||||
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | |||||
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | |||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S0_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S0_RCSR & I2S_RCSR_RE) return; | |||||
// Select input clock 0 | // Select input clock 0 | ||||
// Configure to input the bit-clock from pin, bypasses the MCLK divider | // Configure to input the bit-clock from pin, bypasses the MCLK divider | ||||
I2S0_MCR = I2S_MCR_MICS(0); | I2S0_MCR = I2S_MCR_MICS(0); | ||||
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | ||||
#elif 0 && (defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||||
// configure pin mux for 3 clock signals | |||||
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | |||||
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | |||||
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | |||||
#if defined(SAI1) | |||||
i2s = ((I2S_STRUCT *)0x40384000); | |||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (i2s->TX.CSR & I2S_TCSR_TE) return; | |||||
if (i2s->RX.CSR & I2S_RCSR_RE) return; | |||||
#elif defined(__IMXRT1062__) | |||||
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | ||||
/* | |||||
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) | |||||
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4 | |||||
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK)) | |||||
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | |||||
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f | |||||
*/ | |||||
//TODO: | |||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK | ((uint32_t)(1<<20)) )) | |||||
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | |||||
CORE_PIN23_CONFIG = 3; //1:MCLK | |||||
CORE_PIN21_CONFIG = 3; //1:RX_BCLK | |||||
CORE_PIN20_CONFIG = 3; //1:RX_SYNC | |||||
int rsync = 0; | |||||
int tsync = 1; | |||||
#elif defined(SAI2) | |||||
i2s = ((I2S_STRUCT *)0x40388000); | |||||
if (i2s->TX.CSR & I2S_TCSR_TE) return; | |||||
if (i2s->RX.CSR & I2S_RCSR_RE) return; | |||||
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | |||||
/* | |||||
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) | |||||
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4, | |||||
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) | |||||
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | |||||
*/ | |||||
//TODO: | |||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK | ((uint32_t)(1<<19)) )) | |||||
/*| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR*/ | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0); //Select MCLK | |||||
CORE_PIN5_CONFIG = 2; //2:MCLK | |||||
CORE_PIN4_CONFIG = 2; //2:TX_BCLK | |||||
CORE_PIN3_CONFIG = 2; //2:TX_SYNC | |||||
int rsync = 1; | |||||
int tsync = 0; | |||||
#endif | |||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S1_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S1_RCSR & I2S_RCSR_RE) return; | |||||
// not using MCLK in slave mode - hope that's ok? | |||||
//CORE_PIN23_CONFIG = 3; // AD_B1_09 ALT3=SAI1_MCLK | |||||
CORE_PIN21_CONFIG = 3; // AD_B1_11 ALT3=SAI1_RX_BCLK | |||||
CORE_PIN20_CONFIG = 3; // AD_B1_10 ALT3=SAI1_RX_SYNC | |||||
IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868 | |||||
IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872 | |||||
// configure transmitter | // configure transmitter | ||||
i2s->TX.MR = 0; | |||||
i2s->TX.CR1 = I2S_TCR1_RFW(1); // watermark at half fifo size | |||||
i2s->TX.CR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP; | |||||
i2s->TX.CR3 = I2S_TCR3_TCE; | |||||
i2s->TX.CR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | |||||
| I2S_TCR4_FSE | I2S_TCR4_FSP; | |||||
i2s->TX.CR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); | |||||
I2S1_TMR = 0; | |||||
I2S1_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size | |||||
I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP; | |||||
I2S1_TCR3 = I2S_TCR3_TCE; | |||||
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | |||||
| I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD; | |||||
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); | |||||
// configure receiver | // configure receiver | ||||
i2s->RX.MR = 0; | |||||
i2s->RX.CR1 = I2S_RCR1_RFW(1); | |||||
i2s->RX.CR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP; | |||||
i2s->RX.CR3 = I2S_RCR3_RCE; | |||||
i2s->RX.CR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | |||||
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||||
i2s->RX.CR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | |||||
I2S1_RMR = 0; | |||||
I2S1_RCR1 = I2S_RCR1_RFW(1); | |||||
I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP; | |||||
I2S1_RCR3 = I2S_RCR3_RCE; | |||||
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | |||||
| I2S_RCR4_FSE | I2S_RCR4_FSP; | |||||
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | |||||
#endif | #endif | ||||
} | } |
virtual void update(void); | virtual void update(void); | ||||
void begin(void); | void begin(void); | ||||
friend class AudioInputI2S; | friend class AudioInputI2S; | ||||
#if defined(__IMXRT1062__) | |||||
friend class AudioOutputI2SQuad; | |||||
friend class AudioInputI2SQuad; | |||||
friend class AudioOutputI2SHex; | |||||
friend class AudioInputI2SHex; | |||||
friend class AudioOutputI2SOct; | |||||
friend class AudioInputI2SOct; | |||||
#endif | |||||
protected: | protected: | ||||
AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! | AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! | ||||
static void config_i2s(void); | static void config_i2s(void); |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
* THE SOFTWARE. | * THE SOFTWARE. | ||||
*/ | */ | ||||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "output_i2s2.h" | #include "output_i2s2.h" | ||||
#include "memcpy_audio.h" | #include "memcpy_audio.h" | ||||
block_right_1st = NULL; | block_right_1st = NULL; | ||||
config_i2s(); | config_i2s(); | ||||
CORE_PIN2_CONFIG = 2; //2:TX_DATA0 | |||||
// if AudioInputI2S2 set I2S_TCSR_TE (for clock sync), disable it | |||||
I2S2_TCSR = 0; | |||||
while (I2S2_TCSR & I2S_TCSR_TE) ; //wait for transmit disabled | |||||
CORE_PIN2_CONFIG = 2; //EMC_04, 2=SAI2_TX_DATA, page 428 | |||||
dma.TCD->SADDR = i2s2_tx_buffer; | dma.TCD->SADDR = i2s2_tx_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; | ||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); | dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); | ||||
// I2S2_RCSR |= I2S_RCSR_RE; | |||||
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
dma.enable(); | |||||
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
dma.enable(); | |||||
} | } | ||||
void AudioOutputI2S2::isr(void) | void AudioOutputI2S2::isr(void) | ||||
{ | { | ||||
int16_t *dest, *dc; | |||||
int16_t *dest; | |||||
audio_block_t *blockL, *blockR; | audio_block_t *blockL, *blockR; | ||||
uint32_t saddr, offsetL, offsetR; | uint32_t saddr, offsetL, offsetR; | ||||
void AudioOutputI2S2::config_i2s(void) | void AudioOutputI2S2::config_i2s(void) | ||||
{ | { | ||||
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S2_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S2_RCSR & I2S_RCSR_RE) return; | |||||
//PLL: | //PLL: | ||||
int fs = AUDIO_SAMPLE_RATE_EXACT; | int fs = AUDIO_SAMPLE_RATE_EXACT; | ||||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | // PLL between 27*24 = 648MHz und 54*24=1296MHz | ||||
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) | CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) | ||||
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4, | | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4, | ||||
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) | CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) | ||||
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | |||||
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | |||||
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | | CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | ||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | ||||
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S2_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S2_RCSR & I2S_RCSR_RE) return; | |||||
CORE_PIN5_CONFIG = 2; //2:MCLK | |||||
CORE_PIN4_CONFIG = 2; //2:TX_BCLK | |||||
CORE_PIN3_CONFIG = 2; //2:TX_SYNC | |||||
// CORE_PIN2_CONFIG = 2; //2:TX_DATA0 | |||||
// CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||||
CORE_PIN33_CONFIG = 2; //EMC_07, 2=SAI2_MCLK | |||||
CORE_PIN4_CONFIG = 2; //EMC_06, 2=SAI2_TX_BCLK | |||||
CORE_PIN3_CONFIG = 2; //EMC_05, 2=SAI2_TX_SYNC, page 429 | |||||
int rsync = 1; | int rsync = 1; | ||||
int tsync = 0; | int tsync = 0; | ||||
//I2S2_TCSR = (1<<25); //Reset | //I2S2_TCSR = (1<<25); //Reset | ||||
I2S2_TCR1 = I2S_TCR1_RFW(1); | I2S2_TCR1 = I2S_TCR1_RFW(1); | ||||
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; | I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; | ||||
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | |||||
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | |||||
I2S2_TCR3 = I2S_TCR3_TCE; | I2S2_TCR3 = I2S_TCR3_TCE; | ||||
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||||
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | |||||
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||||
I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); | I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); | ||||
I2S2_RMR = 0; | I2S2_RMR = 0; | ||||
//I2S2_RCSR = (1<<25); //Reset | //I2S2_RCSR = (1<<25); //Reset | ||||
I2S2_RCR1 = I2S_RCR1_RFW(1); | I2S2_RCR1 = I2S_RCR1_RFW(1); | ||||
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; | I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; | ||||
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | |||||
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | |||||
I2S2_RCR3 = I2S_RCR3_RCE; | I2S2_RCR3 = I2S_RCR3_RCE; | ||||
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||||
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | |||||
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||||
I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); | I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); | ||||
} | } |
/* 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 |
/* 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 |
/* 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 |
/* 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 |
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "output_i2s_quad.h" | #include "output_i2s_quad.h" | ||||
#include "output_i2s.h" | |||||
#include "memcpy_audio.h" | #include "memcpy_audio.h" | ||||
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | |||||
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#include "utility/imxrt_hw.h" | |||||
#endif | |||||
audio_block_t * AudioOutputI2SQuad::block_ch1_1st = NULL; | audio_block_t * AudioOutputI2SQuad::block_ch1_1st = NULL; | ||||
audio_block_t * AudioOutputI2SQuad::block_ch2_1st = NULL; | audio_block_t * AudioOutputI2SQuad::block_ch2_1st = NULL; | ||||
uint16_t AudioOutputI2SQuad::ch2_offset = 0; | uint16_t AudioOutputI2SQuad::ch2_offset = 0; | ||||
uint16_t AudioOutputI2SQuad::ch3_offset = 0; | uint16_t AudioOutputI2SQuad::ch3_offset = 0; | ||||
uint16_t AudioOutputI2SQuad::ch4_offset = 0; | uint16_t AudioOutputI2SQuad::ch4_offset = 0; | ||||
//audio_block_t * AudioOutputI2SQuad::inputQueueArray[4]; | |||||
bool AudioOutputI2SQuad::update_responsibility = false; | bool AudioOutputI2SQuad::update_responsibility = false; | ||||
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
DMAChannel AudioOutputI2SQuad::dma(false); | DMAChannel AudioOutputI2SQuad::dma(false); | ||||
static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; | static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; | ||||
void AudioOutputI2SQuad::begin(void) | void AudioOutputI2SQuad::begin(void) | ||||
{ | { | ||||
#if 1 | |||||
dma.begin(true); // Allocate the DMA channel first | dma.begin(true); // Allocate the DMA channel first | ||||
block_ch1_1st = NULL; | block_ch1_1st = NULL; | ||||
block_ch3_1st = NULL; | block_ch3_1st = NULL; | ||||
block_ch4_1st = NULL; | block_ch4_1st = NULL; | ||||
#if defined(KINETISK) | |||||
// TODO: can we call normal config_i2s, and then just enable the extra output? | // TODO: can we call normal config_i2s, and then just enable the extra output? | ||||
config_i2s(); | config_i2s(); | ||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2 | CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2 | ||||
I2S0_TCSR = I2S_TCSR_SR; | I2S0_TCSR = I2S_TCSR_SR; | ||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
#elif defined(__IMXRT1062__) | |||||
const int pinoffset = 0; // TODO: make this configurable... | |||||
memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer)); | |||||
AudioOutputI2S::config_i2s(); | |||||
I2S1_TCR3 = I2S_TCR3_TCE_2CH << pinoffset; | |||||
switch (pinoffset) { | |||||
case 0: | |||||
CORE_PIN7_CONFIG = 3; | |||||
CORE_PIN32_CONFIG = 3; | |||||
break; | |||||
case 1: | |||||
CORE_PIN32_CONFIG = 3; | |||||
CORE_PIN9_CONFIG = 3; | |||||
break; | |||||
case 2: | |||||
CORE_PIN9_CONFIG = 3; | |||||
CORE_PIN6_CONFIG = 3; | |||||
} | |||||
dma.TCD->SADDR = i2s_tx_buffer; | |||||
dma.TCD->SOFF = 2; | |||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||||
dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | | |||||
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) | | |||||
DMA_TCD_NBYTES_MLOFFYES_NBYTES(4); | |||||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||||
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2 + pinoffset * 4); | |||||
dma.TCD->DOFF = 4; | |||||
dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||||
dma.TCD->DLASTSGA = -8; | |||||
dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||||
dma.enable(); | |||||
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||||
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
I2S1_TCR3 = I2S_TCR3_TCE_2CH << pinoffset; | |||||
update_responsibility = update_setup(); | |||||
dma.attachInterrupt(isr); | |||||
#endif | #endif | ||||
} | } | ||||
src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; | src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; | ||||
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; | src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; | ||||
// TODO: fast 4-way interleaved memcpy... | |||||
#if 1 | #if 1 | ||||
memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); | memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); | ||||
#else | #else | ||||
*dest++ = *src4++; | *dest++ = *src4++; | ||||
} | } | ||||
#endif | #endif | ||||
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | |||||
if (block_ch1_1st) { | if (block_ch1_1st) { | ||||
if (ch1_offset == 0) { | if (ch1_offset == 0) { | ||||
} | } | ||||
} | } | ||||
#if defined(KINETISK) | |||||
// MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate | // MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate | ||||
// | // | ||||
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000 | #if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000 | ||||
#define MCLK_MULT 1 | #define MCLK_MULT 1 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
#elif F_CPU == 216000000 | #elif F_CPU == 216000000 | ||||
#define MCLK_MULT 8 | |||||
#define MCLK_DIV 153 | |||||
#define MCLK_SRC 0 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 240000000 | #elif F_CPU == 240000000 | ||||
#define MCLK_MULT 4 | |||||
#define MCLK_MULT 2 | |||||
#define MCLK_DIV 85 | #define MCLK_DIV 85 | ||||
#define MCLK_SRC 0 | |||||
#elif F_CPU == 256000000 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 16000000 | #elif F_CPU == 16000000 | ||||
#define MCLK_MULT 12 | #define MCLK_MULT 12 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | ||||
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | ||||
} | } | ||||
#endif // KINETISK | |||||
#else // not __MK20DX256__ | |||||
#else // not supported | |||||
void AudioOutputI2SQuad::begin(void) | void AudioOutputI2SQuad::begin(void) | ||||
{ | { |
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | ||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | ||||
update_responsibility = update_setup(); | |||||
dma.attachInterrupt(isr); | |||||
dma.enable(); | |||||
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; | I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; | ||||
return; | |||||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | ||||
#if defined(__IMXRT1052__) | #if defined(__IMXRT1052__) | ||||
I2S1_RCSR |= I2S_RCSR_RE; | I2S1_RCSR |= I2S_RCSR_RE; | ||||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | ||||
#endif | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
dma.enable(); | dma.enable(); | ||||
return; | |||||
#endif | |||||
} | } | ||||
void AudioOutputPT8211::isr(void) | void AudioOutputPT8211::isr(void) | ||||
#define MCLK_MULT 1 | #define MCLK_MULT 1 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
#elif F_CPU == 216000000 | #elif F_CPU == 216000000 | ||||
#define MCLK_MULT 8 | |||||
#define MCLK_DIV 153 | |||||
#define MCLK_SRC 0 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 240000000 | #elif F_CPU == 240000000 | ||||
#define MCLK_MULT 4 | |||||
#define MCLK_MULT 2 | |||||
#define MCLK_DIV 85 | #define MCLK_DIV 85 | ||||
#define MCLK_SRC 0 | |||||
#elif F_CPU == 256000000 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 16000000 | #elif F_CPU == 16000000 | ||||
#define MCLK_MULT 12 | #define MCLK_MULT 12 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 |
#define MCLK_MULT 1 | #define MCLK_MULT 1 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
#elif F_CPU == 216000000 | #elif F_CPU == 216000000 | ||||
#define MCLK_MULT 8 | |||||
#define MCLK_DIV 153 | |||||
#define MCLK_SRC 0 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 240000000 | #elif F_CPU == 240000000 | ||||
#define MCLK_MULT 4 | |||||
#define MCLK_MULT 2 | |||||
#define MCLK_DIV 85 | #define MCLK_DIV 85 | ||||
#define MCLK_SRC 0 | |||||
#elif F_CPU == 256000000 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 16000000 | #elif F_CPU == 16000000 | ||||
#define MCLK_MULT 12 | #define MCLK_MULT 12 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 |
I2S0_TCSR = I2S_TCSR_SR; | I2S0_TCSR = I2S_TCSR_SR; | ||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | ||||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||||
#elif defined(__IMXRT1062__) | |||||
CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||||
dma.TCD->SADDR = tdm_tx_buffer; | dma.TCD->SADDR = tdm_tx_buffer; | ||||
dma.TCD->SOFF = 4; | dma.TCD->SOFF = 4; | ||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma.enable(); | dma.enable(); | ||||
I2S1_RCSR |= I2S_RCSR_RE; | |||||
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||||
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||||
#endif | #endif | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
void AudioOutputTDM::isr(void) | void AudioOutputTDM::isr(void) | ||||
{ | { | ||||
uint32_t *dest, *dc; | |||||
uint32_t *dest; | |||||
const uint32_t *src1, *src2; | const uint32_t *src1, *src2; | ||||
uint32_t i, saddr; | uint32_t i, saddr; | ||||
dest = tdm_tx_buffer; | dest = tdm_tx_buffer; | ||||
} | } | ||||
if (update_responsibility) AudioStream::update_all(); | if (update_responsibility) AudioStream::update_all(); | ||||
dc = dest; | |||||
#if IMXRT_CACHE_ENABLED >= 2 | |||||
uint32_t *dc = dest; | |||||
#endif | |||||
for (i=0; i < 16; i += 2) { | for (i=0; i < 16; i += 2) { | ||||
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros; | src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros; | ||||
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros; | src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros; | ||||
#define MCLK_MULT 2 | #define MCLK_MULT 2 | ||||
#define MCLK_DIV 17 | #define MCLK_DIV 17 | ||||
#elif F_CPU == 216000000 | #elif F_CPU == 216000000 | ||||
#define MCLK_MULT 16 | |||||
#define MCLK_DIV 153 | |||||
#define MCLK_SRC 0 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#elif F_CPU == 240000000 | #elif F_CPU == 240000000 | ||||
#define MCLK_MULT 8 | |||||
#define MCLK_MULT 2 | |||||
#define MCLK_DIV 85 | #define MCLK_DIV 85 | ||||
#define MCLK_SRC 0 | |||||
#elif F_CPU == 256000000 | |||||
#define MCLK_MULT 12 | |||||
#define MCLK_DIV 17 | |||||
#define MCLK_SRC 1 | |||||
#else | #else | ||||
#error "This CPU Clock Speed is not supported by the Audio library"; | #error "This CPU Clock Speed is not supported by the Audio library"; | ||||
#endif | #endif | ||||
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK - 11.2 MHz | CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK - 11.2 MHz | ||||
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK - 22.5 MHz | CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK - 22.5 MHz | ||||
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#elif defined(__IMXRT1062__) | |||||
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S1_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S1_RCSR & I2S_RCSR_RE) return; | |||||
//PLL: | //PLL: | ||||
int fs = AUDIO_SAMPLE_RATE_EXACT; | int fs = AUDIO_SAMPLE_RATE_EXACT; | ||||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | // PLL between 27*24 = 648MHz und 54*24=1296MHz | ||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | ||||
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S1_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S1_RCSR & I2S_RCSR_RE) return; | |||||
// configure transmitter | // configure transmitter | ||||
int rsync = 0; | int rsync = 0; | ||||
int tsync = 1; | int tsync = 1; |
* THE SOFTWARE. | * THE SOFTWARE. | ||||
*/ | */ | ||||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "output_tdm2.h" | #include "output_tdm2.h" | ||||
#include "memcpy_audio.h" | #include "memcpy_audio.h" | ||||
void AudioOutputTDM2::config_tdm(void) | void AudioOutputTDM2::config_tdm(void) | ||||
{ | { | ||||
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S2_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S2_RCSR & I2S_RCSR_RE) return; | |||||
//PLL: | //PLL: | ||||
int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ | int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ | ||||
// PLL between 27*24 = 648MHz und 54*24=1296MHz | // PLL between 27*24 = 648MHz und 54*24=1296MHz | ||||
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | ||||
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | ||||
// if either transmitter or receiver is enabled, do nothing | |||||
if (I2S2_TCSR & I2S_TCSR_TE) return; | |||||
if (I2S2_RCSR & I2S_RCSR_RE) return; | |||||
// configure transmitter | // configure transmitter | ||||
int rsync = 1; | int rsync = 1; | ||||
int tsync = 0; | int tsync = 0; | ||||
| I2S_RCR4_FSE | I2S_RCR4_FSD; | | I2S_RCR4_FSE | I2S_RCR4_FSD; | ||||
I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | ||||
CORE_PIN5_CONFIG = 2; //2:MCLK | |||||
CORE_PIN33_CONFIG = 2; //2:MCLK | |||||
CORE_PIN4_CONFIG = 2; //2:TX_BCLK | CORE_PIN4_CONFIG = 2; //2:TX_BCLK | ||||
CORE_PIN3_CONFIG = 2; //2:TX_SYNC | CORE_PIN3_CONFIG = 2; //2:TX_SYNC | ||||
} | } | ||||
#endif | |||||
#endif |
if (!userblock) return; | if (!userblock) return; | ||||
h = head + 1; | h = head + 1; | ||||
if (h >= 32) h = 0; | |||||
if (h >= max_buffers) h = 0; | |||||
while (tail == h) ; // wait until space in the queue | while (tail == h) ; // wait until space in the queue | ||||
queue[h] = userblock; | queue[h] = userblock; | ||||
head = h; | head = h; | ||||
t = tail; | t = tail; | ||||
if (t != head) { | if (t != head) { | ||||
if (++t >= 32) t = 0; | |||||
if (++t >= max_buffers) t = 0; | |||||
block = queue[t]; | block = queue[t]; | ||||
tail = t; | tail = t; | ||||
transmit(block); | transmit(block); |
class AudioPlayQueue : public AudioStream | class AudioPlayQueue : public AudioStream | ||||
{ | { | ||||
private: | |||||
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) | |||||
static const int max_buffers = 80; | |||||
#else | |||||
static const int max_buffers = 32; | |||||
#endif | |||||
public: | public: | ||||
AudioPlayQueue(void) : AudioStream(0, NULL), | AudioPlayQueue(void) : AudioStream(0, NULL), | ||||
userblock(NULL), head(0), tail(0) { } | userblock(NULL), head(0), tail(0) { } | ||||
//bool isPlaying(void) { return playing; } | //bool isPlaying(void) { return playing; } | ||||
virtual void update(void); | virtual void update(void); | ||||
private: | private: | ||||
audio_block_t *queue[32]; | |||||
audio_block_t *queue[max_buffers]; | |||||
audio_block_t *userblock; | audio_block_t *userblock; | ||||
volatile uint8_t head, tail; | volatile uint8_t head, tail; | ||||
}; | }; |
h = head; | h = head; | ||||
t = tail; | t = tail; | ||||
if (h >= t) return h - t; | if (h >= t) return h - t; | ||||
return 53 + h - t; | |||||
return max_buffers + h - t; | |||||
} | } | ||||
void AudioRecordQueue::clear(void) | void AudioRecordQueue::clear(void) | ||||
} | } | ||||
t = tail; | t = tail; | ||||
while (t != head) { | while (t != head) { | ||||
if (++t >= 53) t = 0; | |||||
if (++t >= max_buffers) t = 0; | |||||
release(queue[t]); | release(queue[t]); | ||||
} | } | ||||
tail = t; | tail = t; | ||||
if (userblock) return NULL; | if (userblock) return NULL; | ||||
t = tail; | t = tail; | ||||
if (t == head) return NULL; | if (t == head) return NULL; | ||||
if (++t >= 53) t = 0; | |||||
if (++t >= max_buffers) t = 0; | |||||
userblock = queue[t]; | userblock = queue[t]; | ||||
tail = t; | tail = t; | ||||
return userblock->data; | return userblock->data; | ||||
return; | return; | ||||
} | } | ||||
h = head + 1; | h = head + 1; | ||||
if (h >= 53) h = 0; | |||||
if (h >= max_buffers) h = 0; | |||||
if (h == tail) { | if (h == tail) { | ||||
release(block); | release(block); | ||||
} else { | } else { |
class AudioRecordQueue : public AudioStream | class AudioRecordQueue : public AudioStream | ||||
{ | { | ||||
private: | |||||
#if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) | |||||
static const int max_buffers = 209; | |||||
#else | |||||
static const int max_buffers = 53; | |||||
#endif | |||||
public: | public: | ||||
AudioRecordQueue(void) : AudioStream(1, inputQueueArray), | AudioRecordQueue(void) : AudioStream(1, inputQueueArray), | ||||
userblock(NULL), head(0), tail(0), enabled(0) { } | userblock(NULL), head(0), tail(0), enabled(0) { } | ||||
virtual void update(void); | virtual void update(void); | ||||
private: | private: | ||||
audio_block_t *inputQueueArray[1]; | audio_block_t *inputQueueArray[1]; | ||||
audio_block_t * volatile queue[53]; | |||||
audio_block_t * volatile queue[max_buffers]; | |||||
audio_block_t *userblock; | audio_block_t *userblock; | ||||
volatile uint8_t head, tail, enabled; | volatile uint8_t head, tail, enabled; | ||||
}; | }; |
#include "imxrt_hw.h" | #include "imxrt_hw.h" | ||||
PROGMEM | PROGMEM | ||||
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force = false) // sets PLL4 | |||||
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4 | |||||
{ | { | ||||
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return; | if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return; | ||||
(c) Frank B | (c) Frank B | ||||
*/ | */ | ||||
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||||
#if defined(__IMXRT1062__) | |||||
#ifndef imxr_hw_h_ | #ifndef imxr_hw_h_ | ||||
#define imxr_hw_h_ | #define imxr_hw_h_ |