| @@ -93,6 +93,8 @@ | |||
| #include "input_i2s.h" | |||
| #include "input_i2s2.h" | |||
| #include "input_i2s_quad.h" | |||
| #include "input_i2s_hex.h" | |||
| #include "input_i2s_oct.h" | |||
| #include "input_tdm.h" | |||
| #include "input_tdm2.h" | |||
| #include "input_pdm.h" | |||
| @@ -102,6 +104,8 @@ | |||
| #include "output_i2s.h" | |||
| #include "output_i2s2.h" | |||
| #include "output_i2s_quad.h" | |||
| #include "output_i2s_hex.h" | |||
| #include "output_i2s_oct.h" | |||
| #include "output_mqs.h" | |||
| #include "output_pwm.h" | |||
| #include "output_spdif.h" | |||
| @@ -78,6 +78,11 @@ | |||
| // then un-mute the DACs. | |||
| // 9. Normal operation begins. | |||
| // Some people have found their CS42448 goes into a strange mode where VQ is 1.25V | |||
| // instead of the normal 2.5V. Apparently there is a workaround for this problem | |||
| // which involves writing to an undocumented bit. Details here: | |||
| // https://forum.pjrc.com/threads/41371?p=215881&viewfull=1#post215881 | |||
| static const uint8_t default_config[] = { | |||
| 0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max | |||
| 0x76, // CS42448_Interface_Formats = TDM mode | |||
| @@ -30,7 +30,10 @@ | |||
| #include "AudioStream.h" | |||
| #include "utility/dspinst.h" | |||
| #if defined(__MK66FX1M0__) | |||
| #if defined(__IMXRT1062__) | |||
| // 4.00 second maximum on Teensy 4.0 | |||
| #define DELAY_QUEUE_SIZE (176512 / AUDIO_BLOCK_SAMPLES) | |||
| #elif defined(__MK66FX1M0__) | |||
| // 2.41 second maximum on Teensy 3.6 | |||
| #define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SAMPLES) | |||
| #elif defined(__MK64FX512__) | |||
| @@ -0,0 +1,149 @@ | |||
| /* Show levels (RMS & Peak) for 8 I2S microphone inputs | |||
| * | |||
| * Connect 8 INMP411 I2S microphones to Teensy 4.0 | |||
| * Pin 8 SD on mics #1 and #2 | |||
| * Pin 6 SD on mics #3 and #4 | |||
| * Pin 9 SD on mics #5 and #6 | |||
| * Pin 32 SD on mics #7 and #8 | |||
| * Pin 20 WS on all mics | |||
| * Pin 21 SCK on all mics | |||
| * | |||
| * Each mic needs GND to Teensy GND, VCC to Teensy 3.3V. | |||
| * Connect L/R to GND on the odd numbered mics | |||
| * and L/R to 3.3V on the even numbered mics. | |||
| * | |||
| * Optional - connect a Teensy Audio Shield or other I2S | |||
| * output device, but do not connect it to pin 8, because | |||
| * the INMP411 mics #1 & #2 send their signal to pin 8. | |||
| * | |||
| * This example code is in the public domain | |||
| */ | |||
| #include <Audio.h> | |||
| #include <Wire.h> | |||
| #include <SPI.h> | |||
| #include <SD.h> | |||
| #include <SerialFlash.h> | |||
| // If INMP411 mics are not available, the audio shield mic can | |||
| // be used, but it will send a single signal to inputs #1 & #2 | |||
| // (if connected to pin 8). | |||
| //const int myInput = AUDIO_INPUT_LINEIN; | |||
| const int myInput = AUDIO_INPUT_MIC; | |||
| AudioInputI2SOct audioInput; // audio shield: mic or line-in | |||
| AudioAmplifier amp1; | |||
| AudioAmplifier amp2; | |||
| AudioAmplifier amp3; | |||
| AudioAmplifier amp4; | |||
| AudioAmplifier amp5; | |||
| AudioAmplifier amp6; | |||
| AudioAmplifier amp7; | |||
| AudioAmplifier amp8; | |||
| AudioAnalyzeRMS rms1; | |||
| AudioAnalyzeRMS rms2; | |||
| AudioAnalyzeRMS rms3; | |||
| AudioAnalyzeRMS rms4; | |||
| AudioAnalyzeRMS rms5; | |||
| AudioAnalyzeRMS rms6; | |||
| AudioAnalyzeRMS rms7; | |||
| AudioAnalyzeRMS rms8; | |||
| AudioAnalyzePeak peak1; | |||
| AudioAnalyzePeak peak2; | |||
| AudioAnalyzePeak peak3; | |||
| AudioAnalyzePeak peak4; | |||
| AudioAnalyzePeak peak5; | |||
| AudioAnalyzePeak peak6; | |||
| AudioAnalyzePeak peak7; | |||
| AudioAnalyzePeak peak8; | |||
| AudioOutputI2S audioOutput; // audio shield: headphones & line-out | |||
| // Send all microphone signals to amps | |||
| AudioConnection r1(audioInput, 0, amp1, 0); | |||
| AudioConnection r2(audioInput, 1, amp2, 0); | |||
| AudioConnection r3(audioInput, 2, amp3, 0); | |||
| AudioConnection r4(audioInput, 3, amp4, 0); | |||
| AudioConnection r5(audioInput, 4, amp5, 0); | |||
| AudioConnection r6(audioInput, 5, amp6, 0); | |||
| AudioConnection r7(audioInput, 6, amp7, 0); | |||
| AudioConnection r8(audioInput, 7, amp8, 0); | |||
| // Connect the amps to RMS and Peak analyzers | |||
| AudioConnection a1(amp1, 0, rms1, 0); | |||
| AudioConnection a2(amp2, 0, rms2, 0); | |||
| AudioConnection a3(amp3, 0, rms3, 0); | |||
| AudioConnection a4(amp4, 0, rms4, 0); | |||
| AudioConnection a5(amp5, 0, rms5, 0); | |||
| AudioConnection a6(amp6, 0, rms6, 0); | |||
| AudioConnection a7(amp7, 0, rms7, 0); | |||
| AudioConnection a8(amp8, 0, rms8, 0); | |||
| AudioConnection p1(amp1, 0, peak1, 0); | |||
| AudioConnection p2(amp2, 0, peak2, 0); | |||
| AudioConnection p3(amp3, 0, peak3, 0); | |||
| AudioConnection p4(amp4, 0, peak4, 0); | |||
| AudioConnection p5(amp5, 0, peak5, 0); | |||
| AudioConnection p6(amp6, 0, peak6, 0); | |||
| AudioConnection p7(amp7, 0, peak7, 0); | |||
| AudioConnection p8(amp8, 0, peak8, 0); | |||
| // Also connect 2 of the amps to an I2S output (Pin 8) | |||
| // to be able to listen to the sound. | |||
| AudioConnection c10(amp1, 0, audioOutput, 0); | |||
| AudioConnection c11(amp2, 0, audioOutput, 1); | |||
| AudioControlSGTL5000 audioShield; | |||
| void setup() { | |||
| AudioMemory(26); | |||
| audioShield.enable(); | |||
| audioShield.inputSelect(myInput); | |||
| audioShield.micGain(12); | |||
| audioShield.volume(0.5); | |||
| const float microphoneAmplification = 72.0; | |||
| amp1.gain(microphoneAmplification); | |||
| amp2.gain(microphoneAmplification); | |||
| amp3.gain(microphoneAmplification); | |||
| amp4.gain(microphoneAmplification); | |||
| amp5.gain(microphoneAmplification); | |||
| amp6.gain(microphoneAmplification); | |||
| amp7.gain(microphoneAmplification); | |||
| amp8.gain(microphoneAmplification); | |||
| Serial.begin(9600); | |||
| } | |||
| elapsedMillis fps; | |||
| void loop() { | |||
| if (fps > 24) { | |||
| fps = 0; | |||
| const int digits = 8; | |||
| print_bar(1, rms1.read(), peak1.readPeakToPeak()/2, digits); | |||
| print_bar(2, rms2.read(), peak2.readPeakToPeak()/2, digits); | |||
| print_bar(3, rms3.read(), peak3.readPeakToPeak()/2, digits); | |||
| print_bar(4, rms4.read(), peak4.readPeakToPeak()/2, digits); | |||
| print_bar(5, rms5.read(), peak5.readPeakToPeak()/2, digits); | |||
| print_bar(6, rms6.read(), peak6.readPeakToPeak()/2, digits); | |||
| print_bar(7, rms7.read(), peak7.readPeakToPeak()/2, digits); | |||
| print_bar(8, rms8.read(), peak8.readPeakToPeak()/2, digits); | |||
| Serial.println(); | |||
| } | |||
| } | |||
| void print_bar(int in, float rms, float peak, int digits) { | |||
| Serial.print(in); | |||
| Serial.print(":"); | |||
| int num_rms = roundf(rms * digits); | |||
| int num_peak = roundf(peak * digits); | |||
| for (int i=0; i < digits; i++) { | |||
| if (i < num_rms) { | |||
| Serial.print("*"); | |||
| } else if (i < num_peak) { | |||
| Serial.print(">"); | |||
| } else { | |||
| Serial.print(" "); | |||
| } | |||
| } | |||
| Serial.print(" "); | |||
| } | |||
| @@ -11,17 +11,17 @@ | |||
| // | |||
| // Recommended connections: | |||
| // | |||
| // Mikroe Teensy 3.1 | |||
| // ------ ---------- | |||
| // SCK 9 | |||
| // MISO 13 | |||
| // MOSI 22 | |||
| // ADCL 23 (yes, ADCL & DACL connect together) | |||
| // DACL 23 | |||
| // SDA 18 | |||
| // SCL 19 | |||
| // 3.3V +3.3V | |||
| // GND GND | |||
| // MikroE Teensy 3 Teensy 4 | |||
| // ------ -------- -------- | |||
| // SCK 9 21 | |||
| // MISO 13 8 | |||
| // MOSI 22 7 | |||
| // ADCL 23 20 (yes, ADCL & DACL connect together) | |||
| // DACL 23 20 | |||
| // SDA 18 18 | |||
| // SCL 19 19 | |||
| // 3.3V +3.3V +3.3V | |||
| // GND GND GND | |||
| // | |||
| // For connection using I2S master mode (WM8731 in slave mode, with MCLK): | |||
| // https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733 | |||
| @@ -50,6 +50,7 @@ AudioControlWM8731master wm8731m1; //xy=292,379 | |||
| void setup() { | |||
| delay(1000); // allow the WM7831 extra time to power up | |||
| wm8731m1.enable(); | |||
| AudioMemory(15); | |||
| @@ -162,7 +162,7 @@ void continueRecording() { | |||
| memcpy(buffer+256, queue1.readBuffer(), 256); | |||
| queue1.freeBuffer(); | |||
| // write all 512 bytes to the SD card | |||
| elapsedMicros usec = 0; | |||
| //elapsedMicros usec = 0; | |||
| frec.write(buffer, 512); | |||
| // Uncomment these lines to see how long SD writes | |||
| // are taking. A pair of audio blocks arrives every | |||
| @@ -440,11 +440,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
| <p>The I2S signals are used in "master" mode, where Teensy creates | |||
| all 3 clock signals and controls all data timing.</p> | |||
| <table class=doc align=center cellpadding=3> | |||
| <tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>13</td><td>RX</td><td>Input</td></tr> | |||
| <tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr> | |||
| <tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>13</td><td align=center>8</td><td>RX</td><td>Input</td></tr> | |||
| <tr class=odd><td align=center>23</td><td align=center>20</td><td>LRCLK</td><td>Output</td></tr> | |||
| </table> | |||
| <p>Audio from | |||
| master mode I2S may be used in the same project as ADC, DAC and | |||
| @@ -755,11 +755,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
| <a href="https://forum.pjrc.com/threads/42894">Invensense ICS-52000 microphones</a>. | |||
| </p> | |||
| <table class=doc align=center cellpadding=3> | |||
| <tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||
| <tr class=odd><td align=center>11</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||
| <tr class=odd><td align=center>13</td><td>RX</td><td>Input, 11.3 Mbit/sec</td></tr> | |||
| <tr class=odd><td align=center>23</td><td>FS</td><td>Output</td></tr> | |||
| <tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||
| <tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||
| <tr class=odd><td align=center>13</td><td align=center>8</td><td>RX</td><td>Input, 11.3 Mbit/sec</td></tr> | |||
| <tr class=odd><td align=center>23</td><td align=center>21</td><td>FS</td><td>Output</td></tr> | |||
| </table> | |||
| <p>Audio from | |||
| master mode TDM may be used in the same project as ADC, DAC and | |||
| @@ -896,11 +896,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
| <p>The I2S signals are used in "master" mode, where Teensy creates | |||
| all 3 clock signals and controls all data timing.</p> | |||
| <table class=doc align=center cellpadding=3> | |||
| <tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>22</td><td>TX</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr> | |||
| <tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>22</td><td align=center>7</td><td>TX</td><td>Output</td></tr> | |||
| <tr class=odd><td align=center>23</td><td align=center>20</td><td>LRCLK</td><td>Output</td></tr> | |||
| </table> | |||
| <p>Audio from | |||
| master mode I2S may be used in the same project as ADC, DAC and | |||
| @@ -1325,11 +1325,11 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
| CS42448 Circuit Board</a>. | |||
| </p> | |||
| <table class=doc align=center cellpadding=3> | |||
| <tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||
| <tr class=odd><td align=center>11</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||
| <tr class=odd><td align=center>22</td><td>TX</td><td>Output, 11.3 Mbit/sec</td></tr> | |||
| <tr class=odd><td align=center>23</td><td>WS</td><td>Output</td></tr> | |||
| <tr class=top><th>T3.x<br>Pin</th><th>T4.0<br>Pin</th><th>Signal</th><th>Direction</th></tr> | |||
| <tr class=odd><td align=center>9</td><td align=center>21</td><td>BCLK</td><td>Output, 11.3 MHz</td></tr> | |||
| <tr class=odd><td align=center>11</td><td align=center>23</td><td>MCLK</td><td>Output, 22.6 MHz</td></tr> | |||
| <tr class=odd><td align=center>22</td><td align=center>7</td><td>TX</td><td>Output, 11.3 Mbit/sec</td></tr> | |||
| <tr class=odd><td align=center>23</td><td align=center>20</td><td>WS</td><td>Output</td></tr> | |||
| </table> | |||
| <p>Audio from | |||
| master mode TDM may be used in the same project as ADC, DAC and | |||
| @@ -65,16 +65,12 @@ void AudioInputI2S::begin(void) | |||
| I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | |||
| #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN8_CONFIG = 3; //1:RX_DATA0 | |||
| #elif defined(__IMXRT1052__) | |||
| CORE_PIN7_CONFIG = 3; //1:RX_DATA0 | |||
| #endif | |||
| IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0+2); | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2); | |||
| dma.TCD->SOFF = 0; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLNO = 2; | |||
| @@ -87,15 +83,11 @@ void AudioInputI2S::begin(void) | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE; | |||
| I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||
| I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| #endif | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| dma.attachInterrupt(isr); | |||
| //pinMode(13, OUTPUT); | |||
| } | |||
| void AudioInputI2S::isr(void) | |||
| @@ -105,11 +97,11 @@ void AudioInputI2S::isr(void) | |||
| int16_t *dest_left, *dest_right; | |||
| audio_block_t *left, *right; | |||
| //digitalWriteFast(13, HIGH); | |||
| #if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(KINETISK) || defined(__IMXRT1062__) | |||
| daddr = (uint32_t)(dma.TCD->DADDR); | |||
| #endif | |||
| dma.clearInterrupt(); | |||
| //Serial.println("isr"); | |||
| if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { | |||
| // DMA is receiving to the first half of the buffer | |||
| @@ -142,7 +134,6 @@ void AudioInputI2S::isr(void) | |||
| } while (src < end); | |||
| } | |||
| } | |||
| //digitalWriteFast(13, LOW); | |||
| } | |||
| @@ -233,7 +224,29 @@ void AudioInputI2Sslave::begin(void) | |||
| I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | |||
| dma.attachInterrupt(isr); | |||
| #endif | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN8_CONFIG = 3; //1:RX_DATA0 | |||
| IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2); | |||
| dma.TCD->SOFF = 0; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLNO = 2; | |||
| dma.TCD->SLAST = 0; | |||
| dma.TCD->DADDR = i2s_rx_buffer; | |||
| dma.TCD->DOFF = 2; | |||
| dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; | |||
| dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); | |||
| dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||
| dma.enable(); | |||
| I2S1_RCSR = 0; | |||
| I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| #endif | |||
| } | |||
| @@ -25,7 +25,7 @@ | |||
| */ | |||
| #if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #include <Arduino.h> | |||
| #include "input_i2s2.h" | |||
| #include "output_i2s2.h" | |||
| @@ -48,8 +48,8 @@ void AudioInputI2S2::begin(void) | |||
| // TODO: should we set & clear the I2S_RCSR_SR bit here? | |||
| AudioOutputI2S2::config_i2s(); | |||
| CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||
| IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; | |||
| CORE_PIN5_CONFIG = 2; //EMC_08, 2=SAI2_RX_DATA, page 434 | |||
| IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; // 0=GPIO_EMC_08_ALT2, page 876 | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2); | |||
| dma.TCD->SOFF = 0; | |||
| @@ -63,14 +63,13 @@ void AudioInputI2S2::begin(void) | |||
| dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX); | |||
| dma.enable(); | |||
| I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE; | |||
| I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||
| I2S2_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; // page 2099 | |||
| I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // page 2087 | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| dma.attachInterrupt(isr); | |||
| //pinMode(13, OUTPUT); | |||
| } | |||
| void AudioInputI2S2::isr(void) | |||
| @@ -80,7 +79,6 @@ void AudioInputI2S2::isr(void) | |||
| int16_t *dest_left, *dest_right; | |||
| audio_block_t *left, *right; | |||
| //digitalWriteFast(13, HIGH); | |||
| daddr = (uint32_t)(dma.TCD->DADDR); | |||
| dma.clearInterrupt(); | |||
| @@ -115,7 +113,6 @@ void AudioInputI2S2::isr(void) | |||
| } while (src < end); | |||
| } | |||
| } | |||
| //digitalWriteFast(13, LOW); | |||
| } | |||
| @@ -0,0 +1,247 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #include <Arduino.h> | |||
| #include "input_i2s_hex.h" | |||
| #include "output_i2s.h" | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*3]; | |||
| audio_block_t * AudioInputI2SHex::block_ch1 = NULL; | |||
| audio_block_t * AudioInputI2SHex::block_ch2 = NULL; | |||
| audio_block_t * AudioInputI2SHex::block_ch3 = NULL; | |||
| audio_block_t * AudioInputI2SHex::block_ch4 = NULL; | |||
| audio_block_t * AudioInputI2SHex::block_ch5 = NULL; | |||
| audio_block_t * AudioInputI2SHex::block_ch6 = NULL; | |||
| uint16_t AudioInputI2SHex::block_offset = 0; | |||
| bool AudioInputI2SHex::update_responsibility = false; | |||
| DMAChannel AudioInputI2SHex::dma(false); | |||
| #if defined(__IMXRT1062__) | |||
| void AudioInputI2SHex::begin(void) | |||
| { | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| const int pinoffset = 0; // TODO: make this configurable... | |||
| AudioOutputI2S::config_i2s(); | |||
| I2S1_RCR3 = I2S_RCR3_RCE_3CH << pinoffset; | |||
| switch (pinoffset) { | |||
| case 0: | |||
| CORE_PIN8_CONFIG = 3; | |||
| CORE_PIN6_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||
| break; | |||
| case 1: | |||
| CORE_PIN6_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| CORE_PIN32_CONFIG = 3; | |||
| IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||
| IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875 | |||
| break; | |||
| } | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2 + pinoffset * 4); | |||
| dma.TCD->SOFF = 4; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE | | |||
| DMA_TCD_NBYTES_MLOFFYES_MLOFF(-12) | | |||
| DMA_TCD_NBYTES_MLOFFYES_NBYTES(6); | |||
| dma.TCD->SLAST = -12; | |||
| dma.TCD->DADDR = i2s_rx_buffer; | |||
| dma.TCD->DOFF = 2; | |||
| dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); | |||
| dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||
| //I2S1_RCSR = 0; | |||
| //I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset; | |||
| I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| dma.attachInterrupt(isr); | |||
| } | |||
| void AudioInputI2SHex::isr(void) | |||
| { | |||
| uint32_t daddr, offset; | |||
| const int16_t *src; | |||
| int16_t *dest1, *dest2, *dest3, *dest4, *dest5, *dest6; | |||
| //digitalWriteFast(3, HIGH); | |||
| daddr = (uint32_t)(dma.TCD->DADDR); | |||
| dma.clearInterrupt(); | |||
| if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { | |||
| // DMA is receiving to the first half of the buffer | |||
| // need to remove data from the second half | |||
| src = (int16_t *)((uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2); | |||
| if (update_responsibility) update_all(); | |||
| } else { | |||
| // DMA is receiving to the second half of the buffer | |||
| // need to remove data from the first half | |||
| src = (int16_t *)&i2s_rx_buffer[0]; | |||
| } | |||
| if (block_ch1) { | |||
| offset = block_offset; | |||
| if (offset <= AUDIO_BLOCK_SAMPLES/2) { | |||
| arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2); | |||
| block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||
| dest1 = &(block_ch1->data[offset]); | |||
| dest2 = &(block_ch2->data[offset]); | |||
| dest3 = &(block_ch3->data[offset]); | |||
| dest4 = &(block_ch4->data[offset]); | |||
| dest5 = &(block_ch5->data[offset]); | |||
| dest6 = &(block_ch6->data[offset]); | |||
| for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) { | |||
| *dest1++ = *src++; | |||
| *dest3++ = *src++; | |||
| *dest5++ = *src++; | |||
| *dest2++ = *src++; | |||
| *dest4++ = *src++; | |||
| *dest6++ = *src++; | |||
| } | |||
| } | |||
| } | |||
| //digitalWriteFast(3, LOW); | |||
| } | |||
| void AudioInputI2SHex::update(void) | |||
| { | |||
| audio_block_t *new1, *new2, *new3, *new4, *new5, *new6; | |||
| audio_block_t *out1, *out2, *out3, *out4, *out5, *out6; | |||
| // allocate 6 new blocks | |||
| new1 = allocate(); | |||
| new2 = allocate(); | |||
| new3 = allocate(); | |||
| new4 = allocate(); | |||
| new5 = allocate(); | |||
| new6 = allocate(); | |||
| // but if any fails, allocate none | |||
| if (!new1 || !new2 || !new3 || !new4 || !new5 || !new6) { | |||
| if (new1) { | |||
| release(new1); | |||
| new1 = NULL; | |||
| } | |||
| if (new2) { | |||
| release(new2); | |||
| new2 = NULL; | |||
| } | |||
| if (new3) { | |||
| release(new3); | |||
| new3 = NULL; | |||
| } | |||
| if (new4) { | |||
| release(new4); | |||
| new4 = NULL; | |||
| } | |||
| if (new5) { | |||
| release(new5); | |||
| new5 = NULL; | |||
| } | |||
| if (new6) { | |||
| release(new6); | |||
| new6 = NULL; | |||
| } | |||
| } | |||
| __disable_irq(); | |||
| if (block_offset >= AUDIO_BLOCK_SAMPLES) { | |||
| // the DMA filled 4 blocks, so grab them and get the | |||
| // 4 new blocks to the DMA, as quickly as possible | |||
| out1 = block_ch1; | |||
| block_ch1 = new1; | |||
| out2 = block_ch2; | |||
| block_ch2 = new2; | |||
| out3 = block_ch3; | |||
| block_ch3 = new3; | |||
| out4 = block_ch4; | |||
| block_ch4 = new4; | |||
| out5 = block_ch5; | |||
| block_ch5 = new5; | |||
| out6 = block_ch6; | |||
| block_ch6 = new6; | |||
| block_offset = 0; | |||
| __enable_irq(); | |||
| // then transmit the DMA's former blocks | |||
| transmit(out1, 0); | |||
| release(out1); | |||
| transmit(out2, 1); | |||
| release(out2); | |||
| transmit(out3, 2); | |||
| release(out3); | |||
| transmit(out4, 3); | |||
| release(out4); | |||
| transmit(out5, 4); | |||
| release(out5); | |||
| transmit(out6, 5); | |||
| release(out6); | |||
| } else if (new1 != NULL) { | |||
| // the DMA didn't fill blocks, but we allocated blocks | |||
| if (block_ch1 == NULL) { | |||
| // the DMA doesn't have any blocks to fill, so | |||
| // give it the ones we just allocated | |||
| block_ch1 = new1; | |||
| block_ch2 = new2; | |||
| block_ch3 = new3; | |||
| block_ch4 = new4; | |||
| block_ch5 = new5; | |||
| block_ch6 = new6; | |||
| block_offset = 0; | |||
| __enable_irq(); | |||
| } else { | |||
| // the DMA already has blocks, doesn't need these | |||
| __enable_irq(); | |||
| release(new1); | |||
| release(new2); | |||
| release(new3); | |||
| release(new4); | |||
| release(new5); | |||
| release(new6); | |||
| } | |||
| } else { | |||
| // The DMA didn't fill blocks, and we could not allocate | |||
| // memory... the system is likely starving for memory! | |||
| // Sadly, there's nothing we can do. | |||
| __enable_irq(); | |||
| } | |||
| } | |||
| #else // not supported | |||
| void AudioInputI2SHex::begin(void) | |||
| { | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,54 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #ifndef _input_i2s_hex_h_ | |||
| #define _input_i2s_hex_h_ | |||
| #include "Arduino.h" | |||
| #include "AudioStream.h" | |||
| #include "DMAChannel.h" | |||
| class AudioInputI2SHex : public AudioStream | |||
| { | |||
| public: | |||
| AudioInputI2SHex(void) : AudioStream(0, NULL) { begin(); } | |||
| virtual void update(void); | |||
| void begin(void); | |||
| private: | |||
| static bool update_responsibility; | |||
| static DMAChannel dma; | |||
| static void isr(void); | |||
| static audio_block_t *block_ch1; | |||
| static audio_block_t *block_ch2; | |||
| static audio_block_t *block_ch3; | |||
| static audio_block_t *block_ch4; | |||
| static audio_block_t *block_ch5; | |||
| static audio_block_t *block_ch6; | |||
| static uint16_t block_offset; | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,263 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #include <Arduino.h> | |||
| #include "input_i2s_oct.h" | |||
| #include "output_i2s.h" | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||
| audio_block_t * AudioInputI2SOct::block_ch1 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch2 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch3 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch4 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch5 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch6 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch7 = NULL; | |||
| audio_block_t * AudioInputI2SOct::block_ch8 = NULL; | |||
| uint16_t AudioInputI2SOct::block_offset = 0; | |||
| bool AudioInputI2SOct::update_responsibility = false; | |||
| DMAChannel AudioInputI2SOct::dma(false); | |||
| #if defined(__IMXRT1062__) | |||
| void AudioInputI2SOct::begin(void) | |||
| { | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| AudioOutputI2S::config_i2s(); | |||
| I2S1_RCR3 = I2S_RCR3_RCE_4CH; | |||
| CORE_PIN8_CONFIG = 3; | |||
| CORE_PIN6_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| CORE_PIN32_CONFIG = 3; | |||
| IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||
| IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875 | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2); | |||
| dma.TCD->SOFF = 4; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE | | |||
| DMA_TCD_NBYTES_MLOFFYES_MLOFF(-16) | | |||
| DMA_TCD_NBYTES_MLOFFYES_NBYTES(8); | |||
| dma.TCD->SLAST = -16; | |||
| dma.TCD->DADDR = i2s_rx_buffer; | |||
| dma.TCD->DOFF = 2; | |||
| dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); | |||
| dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||
| I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| dma.attachInterrupt(isr); | |||
| } | |||
| void AudioInputI2SOct::isr(void) | |||
| { | |||
| uint32_t daddr, offset; | |||
| const int16_t *src; | |||
| int16_t *dest1, *dest2, *dest3, *dest4, *dest5, *dest6, *dest7, *dest8; | |||
| //digitalWriteFast(3, HIGH); | |||
| daddr = (uint32_t)(dma.TCD->DADDR); | |||
| dma.clearInterrupt(); | |||
| if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { | |||
| // DMA is receiving to the first half of the buffer | |||
| // need to remove data from the second half | |||
| src = (int16_t *)((uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2); | |||
| if (update_responsibility) update_all(); | |||
| } else { | |||
| // DMA is receiving to the second half of the buffer | |||
| // need to remove data from the first half | |||
| src = (int16_t *)&i2s_rx_buffer[0]; | |||
| } | |||
| if (block_ch1) { | |||
| offset = block_offset; | |||
| if (offset <= AUDIO_BLOCK_SAMPLES/2) { | |||
| arm_dcache_delete((void *)src, sizeof(i2s_rx_buffer) / 2); | |||
| block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||
| dest1 = &(block_ch1->data[offset]); | |||
| dest2 = &(block_ch2->data[offset]); | |||
| dest3 = &(block_ch3->data[offset]); | |||
| dest4 = &(block_ch4->data[offset]); | |||
| dest5 = &(block_ch5->data[offset]); | |||
| dest6 = &(block_ch6->data[offset]); | |||
| dest7 = &(block_ch7->data[offset]); | |||
| dest8 = &(block_ch8->data[offset]); | |||
| for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) { | |||
| *dest1++ = *src++; | |||
| *dest3++ = *src++; | |||
| *dest5++ = *src++; | |||
| *dest7++ = *src++; | |||
| *dest2++ = *src++; | |||
| *dest4++ = *src++; | |||
| *dest6++ = *src++; | |||
| *dest8++ = *src++; | |||
| } | |||
| } | |||
| } | |||
| //digitalWriteFast(3, LOW); | |||
| } | |||
| void AudioInputI2SOct::update(void) | |||
| { | |||
| audio_block_t *new1, *new2, *new3, *new4, *new5, *new6, *new7, *new8; | |||
| audio_block_t *out1, *out2, *out3, *out4, *out5, *out6, *out7, *out8; | |||
| // allocate 8 new blocks | |||
| new1 = allocate(); | |||
| new2 = allocate(); | |||
| new3 = allocate(); | |||
| new4 = allocate(); | |||
| new5 = allocate(); | |||
| new6 = allocate(); | |||
| new7 = allocate(); | |||
| new8 = allocate(); | |||
| // but if any fails, allocate none | |||
| if (!new1 || !new2 || !new3 || !new4 || !new5 || !new6 || !new7 || !new8) { | |||
| if (new1) { | |||
| release(new1); | |||
| new1 = NULL; | |||
| } | |||
| if (new2) { | |||
| release(new2); | |||
| new2 = NULL; | |||
| } | |||
| if (new3) { | |||
| release(new3); | |||
| new3 = NULL; | |||
| } | |||
| if (new4) { | |||
| release(new4); | |||
| new4 = NULL; | |||
| } | |||
| if (new5) { | |||
| release(new5); | |||
| new5 = NULL; | |||
| } | |||
| if (new6) { | |||
| release(new6); | |||
| new6 = NULL; | |||
| } | |||
| if (new7) { | |||
| release(new7); | |||
| new7 = NULL; | |||
| } | |||
| if (new8) { | |||
| release(new8); | |||
| new8 = NULL; | |||
| } | |||
| } | |||
| __disable_irq(); | |||
| if (block_offset >= AUDIO_BLOCK_SAMPLES) { | |||
| // the DMA filled 4 blocks, so grab them and get the | |||
| // 8 new blocks to the DMA, as quickly as possible | |||
| out1 = block_ch1; | |||
| block_ch1 = new1; | |||
| out2 = block_ch2; | |||
| block_ch2 = new2; | |||
| out3 = block_ch3; | |||
| block_ch3 = new3; | |||
| out4 = block_ch4; | |||
| block_ch4 = new4; | |||
| out5 = block_ch5; | |||
| block_ch5 = new5; | |||
| out6 = block_ch6; | |||
| block_ch6 = new6; | |||
| out7 = block_ch7; | |||
| block_ch7 = new7; | |||
| out8 = block_ch8; | |||
| block_ch8 = new8; | |||
| block_offset = 0; | |||
| __enable_irq(); | |||
| // then transmit the DMA's former blocks | |||
| transmit(out1, 0); | |||
| release(out1); | |||
| transmit(out2, 1); | |||
| release(out2); | |||
| transmit(out3, 2); | |||
| release(out3); | |||
| transmit(out4, 3); | |||
| release(out4); | |||
| transmit(out5, 4); | |||
| release(out5); | |||
| transmit(out6, 5); | |||
| release(out6); | |||
| transmit(out7, 6); | |||
| release(out7); | |||
| transmit(out8, 7); | |||
| release(out8); | |||
| } else if (new1 != NULL) { | |||
| // the DMA didn't fill blocks, but we allocated blocks | |||
| if (block_ch1 == NULL) { | |||
| // the DMA doesn't have any blocks to fill, so | |||
| // give it the ones we just allocated | |||
| block_ch1 = new1; | |||
| block_ch2 = new2; | |||
| block_ch3 = new3; | |||
| block_ch4 = new4; | |||
| block_ch5 = new5; | |||
| block_ch6 = new6; | |||
| block_ch7 = new7; | |||
| block_ch8 = new8; | |||
| block_offset = 0; | |||
| __enable_irq(); | |||
| } else { | |||
| // the DMA already has blocks, doesn't need these | |||
| __enable_irq(); | |||
| release(new1); | |||
| release(new2); | |||
| release(new3); | |||
| release(new4); | |||
| release(new5); | |||
| release(new6); | |||
| release(new7); | |||
| release(new8); | |||
| } | |||
| } else { | |||
| // The DMA didn't fill blocks, and we could not allocate | |||
| // memory... the system is likely starving for memory! | |||
| // Sadly, there's nothing we can do. | |||
| __enable_irq(); | |||
| } | |||
| } | |||
| #else // not supported | |||
| void AudioInputI2SOct::begin(void) | |||
| { | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,56 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #ifndef _input_i2s_oct_h_ | |||
| #define _input_i2s_oct_h_ | |||
| #include "Arduino.h" | |||
| #include "AudioStream.h" | |||
| #include "DMAChannel.h" | |||
| class AudioInputI2SOct : public AudioStream | |||
| { | |||
| public: | |||
| AudioInputI2SOct(void) : AudioStream(0, NULL) { begin(); } | |||
| virtual void update(void); | |||
| void begin(void); | |||
| private: | |||
| static bool update_responsibility; | |||
| static DMAChannel dma; | |||
| static void isr(void); | |||
| static audio_block_t *block_ch1; | |||
| static audio_block_t *block_ch2; | |||
| static audio_block_t *block_ch3; | |||
| static audio_block_t *block_ch4; | |||
| static audio_block_t *block_ch5; | |||
| static audio_block_t *block_ch6; | |||
| static audio_block_t *block_ch7; | |||
| static audio_block_t *block_ch8; | |||
| static uint16_t block_offset; | |||
| }; | |||
| #endif | |||
| @@ -27,8 +27,9 @@ | |||
| #include <Arduino.h> | |||
| #include "input_i2s_quad.h" | |||
| #include "output_i2s_quad.h" | |||
| #include "output_i2s.h" | |||
| DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||
| audio_block_t * AudioInputI2SQuad::block_ch1 = NULL; | |||
| audio_block_t * AudioInputI2SQuad::block_ch2 = NULL; | |||
| audio_block_t * AudioInputI2SQuad::block_ch3 = NULL; | |||
| @@ -37,12 +38,13 @@ uint16_t AudioInputI2SQuad::block_offset = 0; | |||
| bool AudioInputI2SQuad::update_responsibility = false; | |||
| DMAChannel AudioInputI2SQuad::dma(false); | |||
| #if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | |||
| #if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) | |||
| void AudioInputI2SQuad::begin(void) | |||
| { | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| #if defined(KINETISK) | |||
| // TODO: should we set & clear the I2S_RCSR_SR bit here? | |||
| AudioOutputI2SQuad::config_i2s(); | |||
| @@ -73,6 +75,54 @@ void AudioInputI2SQuad::begin(void) | |||
| I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | |||
| dma.attachInterrupt(isr); | |||
| #elif defined(__IMXRT1062__) | |||
| const int pinoffset = 0; // TODO: make this configurable... | |||
| AudioOutputI2S::config_i2s(); | |||
| I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset; | |||
| switch (pinoffset) { | |||
| case 0: | |||
| CORE_PIN8_CONFIG = 3; | |||
| CORE_PIN6_CONFIG = 3; | |||
| IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||
| break; | |||
| case 1: | |||
| CORE_PIN6_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873 | |||
| IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||
| break; | |||
| case 2: | |||
| CORE_PIN9_CONFIG = 3; | |||
| CORE_PIN32_CONFIG = 3; | |||
| IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874 | |||
| IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875 | |||
| break; | |||
| } | |||
| dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2 + pinoffset * 4); | |||
| dma.TCD->SOFF = 4; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE | | |||
| DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) | | |||
| DMA_TCD_NBYTES_MLOFFYES_NBYTES(4); | |||
| dma.TCD->SLAST = -8; | |||
| dma.TCD->DADDR = i2s_rx_buffer; | |||
| dma.TCD->DOFF = 2; | |||
| dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); | |||
| dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); | |||
| I2S1_RCSR = 0; | |||
| I2S1_RCR3 = I2S_RCR3_RCE_2CH << pinoffset; | |||
| I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| dma.attachInterrupt(isr); | |||
| #endif | |||
| } | |||
| void AudioInputI2SQuad::isr(void) | |||
| @@ -98,6 +148,7 @@ void AudioInputI2SQuad::isr(void) | |||
| if (block_ch1) { | |||
| offset = block_offset; | |||
| if (offset <= AUDIO_BLOCK_SAMPLES/2) { | |||
| arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2); | |||
| block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||
| dest1 = &(block_ch1->data[offset]); | |||
| dest2 = &(block_ch2->data[offset]); | |||
| @@ -84,12 +84,17 @@ DMAChannel AudioInputPDM::dma(false); | |||
| #define MCLK_MULT 1 | |||
| #define MCLK_DIV 17 | |||
| #elif F_CPU == 216000000 | |||
| #define MCLK_MULT 8 | |||
| #define MCLK_DIV 153 | |||
| #define MCLK_SRC 0 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 240000000 | |||
| #define MCLK_MULT 4 | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 85 | |||
| #define MCLK_SRC 0 | |||
| #elif F_CPU == 256000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 16000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| @@ -1714,4 +1719,4 @@ for ($n=0; $n < 512; $n += 8) { | |||
| print "\n};\n"; | |||
| print "// max=$max, min=$min\n"; | |||
| */ | |||
| #endif | |||
| #endif | |||
| @@ -27,7 +27,7 @@ | |||
| #include <Arduino.h> | |||
| #include "input_tdm.h" | |||
| #include "output_tdm.h" | |||
| #if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(KINETISK) || defined(__IMXRT1062__) | |||
| #include "utility/imxrt_hw.h" | |||
| DMAMEM __attribute__((aligned(32))) | |||
| @@ -66,8 +66,8 @@ void AudioInputTDM::begin(void) | |||
| I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX | |||
| dma.attachInterrupt(isr); | |||
| #else | |||
| CORE_PIN7_CONFIG = 3; //RX_DATA0 | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN8_CONFIG = 3; //RX_DATA0 | |||
| IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; | |||
| dma.TCD->SADDR = &I2S1_RDR0; | |||
| dma.TCD->SOFF = 0; | |||
| @@ -84,8 +84,7 @@ void AudioInputTDM::begin(void) | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; | |||
| I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; | |||
| dma.attachInterrupt(isr); | |||
| #endif | |||
| } | |||
| @@ -24,7 +24,7 @@ | |||
| * THE SOFTWARE. | |||
| */ | |||
| #if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #include <Arduino.h> | |||
| #include "input_tdm2.h" | |||
| #include "output_tdm2.h" | |||
| @@ -47,7 +47,7 @@ void AudioInputTDM2::begin(void) | |||
| // TODO: should we set & clear the I2S_RCSR_SR bit here? | |||
| AudioOutputTDM2::config_tdm(); | |||
| CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||
| CORE_PIN5_CONFIG = 2; //2:RX_DATA0 | |||
| IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; | |||
| dma.TCD->SADDR = &I2S2_RDR0; | |||
| dma.TCD->SOFF = 0; | |||
| @@ -3,6 +3,8 @@ AudioConnection KEYWORD2 | |||
| AudioInputI2S KEYWORD2 | |||
| AudioInputI2S2 KEYWORD2 | |||
| AudioInputI2SQuad KEYWORD2 | |||
| AudioInputI2SHex KEYWORD2 | |||
| AudioInputI2SOct KEYWORD2 | |||
| AudioInputI2Sslave KEYWORD2 | |||
| AudioInputTDM KEYWORD2 | |||
| AudioInputTDM2 KEYWORD2 | |||
| @@ -11,6 +13,8 @@ AudioInputUSB KEYWORD2 | |||
| AudioOutputI2S KEYWORD2 | |||
| AudioOutputI2S2 KEYWORD2 | |||
| AudioOutputI2SQuad KEYWORD2 | |||
| AudioOutputI2SHex KEYWORD2 | |||
| AudioOutputI2SOct KEYWORD2 | |||
| AudioOutputI2Sslave KEYWORD2 | |||
| AudioOutputSPDIF KEYWORD2 | |||
| AudioOutputSPDIF2 KEYWORD2 | |||
| @@ -700,6 +700,9 @@ void AudioOutputADAT::setI2SFreq(int freq) { | |||
| const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} }; | |||
| #elif (F_PLL==240000000) | |||
| const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; | |||
| #elif (F_PLL==256000000) | |||
| // TODO: fix these... | |||
| const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; | |||
| #endif | |||
| for (int f = 0; f < numfreqs; f++) { | |||
| @@ -38,9 +38,9 @@ bool AudioOutputI2S::update_responsibility = false; | |||
| DMAChannel AudioOutputI2S::dma(false); | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | |||
| #if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #include "utility/imxrt_hw.h" | |||
| #endif | |||
| void AudioOutputI2S::begin(void) | |||
| { | |||
| @@ -50,74 +50,54 @@ void AudioOutputI2S::begin(void) | |||
| block_right_1st = NULL; | |||
| config_i2s(); | |||
| #if defined(__IMXRT1052__) | |||
| CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||
| #endif | |||
| #if defined(KINETISK) | |||
| CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | |||
| dma.TCD->SADDR = i2s_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLNO = 2; | |||
| dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); | |||
| dma.TCD->DOFF = 0; | |||
| dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.TCD->DLASTSGA = 0; | |||
| dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||
| I2S1_RCSR |= I2S_RCSR_RE; | |||
| I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||
| dma.enable(); | |||
| } | |||
| #endif | |||
| #if defined(KINETISK) | |||
| void AudioOutputI2S::begin(void) | |||
| { | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| block_left_1st = NULL; | |||
| block_right_1st = NULL; | |||
| // TODO: should we set & clear the I2S_TCSR_SR bit here? | |||
| config_i2s(); | |||
| CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 | |||
| I2S0_TCSR = I2S_TCSR_SR; | |||
| I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||
| dma.TCD->SADDR = i2s_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLNO = 2; | |||
| dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); | |||
| dma.TCD->DOFF = 0; | |||
| dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.TCD->DLASTSGA = 0; | |||
| dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||
| update_responsibility = update_setup(); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||
| dma.enable(); | |||
| I2S0_TCSR = I2S_TCSR_SR; | |||
| I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||
| I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #endif | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| } | |||
| #endif | |||
| void AudioOutputI2S::isr(void) | |||
| { | |||
| #if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(KINETISK) || defined(__IMXRT1062__) | |||
| int16_t *dest; | |||
| audio_block_t *blockL, *blockR; | |||
| uint32_t saddr, offsetL, offsetR; | |||
| @@ -154,10 +134,8 @@ void AudioOutputI2S::isr(void) | |||
| memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); | |||
| } | |||
| #if IMXRT_CACHE_ENABLED >= 2 | |||
| arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | |||
| #endif | |||
| if (offsetL < AUDIO_BLOCK_SAMPLES) { | |||
| AudioOutputI2S::block_left_offset = offsetL; | |||
| } else { | |||
| @@ -323,12 +301,17 @@ void AudioOutputI2S::update(void) | |||
| #define MCLK_MULT 1 | |||
| #define MCLK_DIV 17 | |||
| #elif F_CPU == 216000000 | |||
| #define MCLK_MULT 8 | |||
| #define MCLK_DIV 153 | |||
| #define MCLK_SRC 0 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 240000000 | |||
| #define MCLK_MULT 4 | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 85 | |||
| #define MCLK_SRC 0 | |||
| #elif F_CPU == 256000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 16000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| @@ -386,9 +369,14 @@ void AudioOutputI2S::config_i2s(void) | |||
| CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | |||
| CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | |||
| CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | |||
| #elif ( defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||
| #elif defined(__IMXRT1062__) | |||
| CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S1_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S1_RCSR & I2S_RCSR_RE) return; | |||
| //PLL: | |||
| int fs = AUDIO_SAMPLE_RATE_EXACT; | |||
| // PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
| @@ -408,18 +396,14 @@ void AudioOutputI2S::config_i2s(void) | |||
| | CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | |||
| | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | |||
| | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S1_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S1_RCSR & I2S_RCSR_RE) return; | |||
| // Select MCLK | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 | |||
| & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | |||
| | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); | |||
| CORE_PIN23_CONFIG = 3; //1:MCLK | |||
| CORE_PIN21_CONFIG = 3; //1:RX_BCLK | |||
| CORE_PIN20_CONFIG = 3; //1:RX_SYNC | |||
| // CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||
| // CORE_PIN7_CONFIG = 3; //1:RX_DATA0 | |||
| int rsync = 0; | |||
| int tsync = 1; | |||
| @@ -430,7 +414,8 @@ void AudioOutputI2S::config_i2s(void) | |||
| I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; | |||
| | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | |||
| I2S1_TCR3 = I2S_TCR3_TCE; | |||
| I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||
| I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | |||
| | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||
| I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); | |||
| I2S1_RMR = 0; | |||
| @@ -439,7 +424,8 @@ void AudioOutputI2S::config_i2s(void) | |||
| I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; | |||
| | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | |||
| I2S1_RCR3 = I2S_RCR3_RCE; | |||
| I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||
| I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | |||
| | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||
| I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); | |||
| #endif | |||
| @@ -453,7 +439,6 @@ void AudioOutputI2Sslave::begin(void) | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| //pinMode(2, OUTPUT); | |||
| block_left_1st = NULL; | |||
| block_right_1st = NULL; | |||
| @@ -473,50 +458,45 @@ void AudioOutputI2Sslave::begin(void) | |||
| dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||
| dma.enable(); | |||
| I2S0_TCSR = I2S_TCSR_SR; | |||
| I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #elif 0 && ( defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||
| #if defined(SAI1) | |||
| CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||
| //CORE_PIN7_CONFIG = 3; //1:RX_DATA0 | |||
| #elif defined(SAI2) | |||
| CORE_PIN2_CONFIG = 2; //2:TX_DATA0 | |||
| //CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||
| #endif | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||
| dma.TCD->SADDR = i2s_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLNO = 2; | |||
| dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
| dma.TCD->DADDR = (void *)&i2s->TX.DR16[1]; | |||
| dma.TCD->DOFF = 0; | |||
| dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.TCD->DLASTSGA = 0; | |||
| dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||
| dma.enable(); | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||
| I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #endif | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| dma.attachInterrupt(isr); | |||
| } | |||
| void AudioOutputI2Sslave::config_i2s(void) | |||
| { | |||
| #if defined(KINETISK) | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S0_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S0_RCSR & I2S_RCSR_RE) return; | |||
| SIM_SCGC6 |= SIM_SCGC6_I2S; | |||
| SIM_SCGC7 |= SIM_SCGC7_DMA; | |||
| SIM_SCGC6 |= SIM_SCGC6_DMAMUX; | |||
| // configure pin mux for 3 clock signals | |||
| CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | |||
| CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | |||
| CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S0_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S0_RCSR & I2S_RCSR_RE) return; | |||
| // Select input clock 0 | |||
| // Configure to input the bit-clock from pin, bypasses the MCLK divider | |||
| I2S0_MCR = I2S_MCR_MICS(0); | |||
| @@ -544,73 +524,43 @@ void AudioOutputI2Sslave::config_i2s(void) | |||
| I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | |||
| #elif 0 && (defined(__IMXRT1052__) || defined(__IMXRT1062__) ) | |||
| // configure pin mux for 3 clock signals | |||
| CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) | |||
| CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | |||
| CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | |||
| #if defined(SAI1) | |||
| i2s = ((I2S_STRUCT *)0x40384000); | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (i2s->TX.CSR & I2S_TCSR_TE) return; | |||
| if (i2s->RX.CSR & I2S_RCSR_RE) return; | |||
| #elif defined(__IMXRT1062__) | |||
| CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | |||
| /* | |||
| CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) | |||
| | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4 | |||
| CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK)) | |||
| | CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07 | |||
| | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f | |||
| */ | |||
| //TODO: | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK | ((uint32_t)(1<<20)) )) | |||
| | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | |||
| CORE_PIN23_CONFIG = 3; //1:MCLK | |||
| CORE_PIN21_CONFIG = 3; //1:RX_BCLK | |||
| CORE_PIN20_CONFIG = 3; //1:RX_SYNC | |||
| int rsync = 0; | |||
| int tsync = 1; | |||
| #elif defined(SAI2) | |||
| i2s = ((I2S_STRUCT *)0x40388000); | |||
| if (i2s->TX.CSR & I2S_TCSR_TE) return; | |||
| if (i2s->RX.CSR & I2S_RCSR_RE) return; | |||
| CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | |||
| /* | |||
| CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) | |||
| | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4, | |||
| CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) | |||
| | CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | |||
| */ | |||
| //TODO: | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK | ((uint32_t)(1<<19)) )) | |||
| /*| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR*/ | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0); //Select MCLK | |||
| CORE_PIN5_CONFIG = 2; //2:MCLK | |||
| CORE_PIN4_CONFIG = 2; //2:TX_BCLK | |||
| CORE_PIN3_CONFIG = 2; //2:TX_SYNC | |||
| int rsync = 1; | |||
| int tsync = 0; | |||
| #endif | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S1_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S1_RCSR & I2S_RCSR_RE) return; | |||
| // not using MCLK in slave mode - hope that's ok? | |||
| //CORE_PIN23_CONFIG = 3; // AD_B1_09 ALT3=SAI1_MCLK | |||
| CORE_PIN21_CONFIG = 3; // AD_B1_11 ALT3=SAI1_RX_BCLK | |||
| CORE_PIN20_CONFIG = 3; // AD_B1_10 ALT3=SAI1_RX_SYNC | |||
| IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868 | |||
| IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872 | |||
| // configure transmitter | |||
| i2s->TX.MR = 0; | |||
| i2s->TX.CR1 = I2S_TCR1_RFW(1); // watermark at half fifo size | |||
| i2s->TX.CR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP; | |||
| i2s->TX.CR3 = I2S_TCR3_TCE; | |||
| i2s->TX.CR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | |||
| | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||
| i2s->TX.CR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); | |||
| I2S1_TMR = 0; | |||
| I2S1_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size | |||
| I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP; | |||
| I2S1_TCR3 = I2S_TCR3_TCE; | |||
| I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | |||
| | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD; | |||
| I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); | |||
| // configure receiver | |||
| i2s->RX.MR = 0; | |||
| i2s->RX.CR1 = I2S_RCR1_RFW(1); | |||
| i2s->RX.CR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP; | |||
| i2s->RX.CR3 = I2S_RCR3_RCE; | |||
| i2s->RX.CR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | |||
| | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||
| i2s->RX.CR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | |||
| I2S1_RMR = 0; | |||
| I2S1_RCR1 = I2S_RCR1_RFW(1); | |||
| I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP; | |||
| I2S1_RCR3 = I2S_RCR3_RCE; | |||
| I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | |||
| | I2S_RCR4_FSE | I2S_RCR4_FSP; | |||
| I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | |||
| #endif | |||
| } | |||
| @@ -38,6 +38,14 @@ public: | |||
| virtual void update(void); | |||
| void begin(void); | |||
| friend class AudioInputI2S; | |||
| #if defined(__IMXRT1062__) | |||
| friend class AudioOutputI2SQuad; | |||
| friend class AudioInputI2SQuad; | |||
| friend class AudioOutputI2SHex; | |||
| friend class AudioInputI2SHex; | |||
| friend class AudioOutputI2SOct; | |||
| friend class AudioInputI2SOct; | |||
| #endif | |||
| protected: | |||
| AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! | |||
| static void config_i2s(void); | |||
| @@ -23,7 +23,7 @@ | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #include <Arduino.h> | |||
| #include "output_i2s2.h" | |||
| #include "memcpy_audio.h" | |||
| @@ -49,7 +49,12 @@ void AudioOutputI2S2::begin(void) | |||
| block_right_1st = NULL; | |||
| config_i2s(); | |||
| CORE_PIN2_CONFIG = 2; //2:TX_DATA0 | |||
| // if AudioInputI2S2 set I2S_TCSR_TE (for clock sync), disable it | |||
| I2S2_TCSR = 0; | |||
| while (I2S2_TCSR & I2S_TCSR_TE) ; //wait for transmit disabled | |||
| CORE_PIN2_CONFIG = 2; //EMC_04, 2=SAI2_TX_DATA, page 428 | |||
| dma.TCD->SADDR = i2s2_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| @@ -63,16 +68,17 @@ void AudioOutputI2S2::begin(void) | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); | |||
| // I2S2_RCSR |= I2S_RCSR_RE; | |||
| I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| dma.enable(); | |||
| I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| dma.enable(); | |||
| } | |||
| void AudioOutputI2S2::isr(void) | |||
| { | |||
| int16_t *dest, *dc; | |||
| int16_t *dest; | |||
| audio_block_t *blockL, *blockR; | |||
| uint32_t saddr, offsetL, offsetR; | |||
| @@ -184,6 +190,10 @@ void AudioOutputI2S2::update(void) | |||
| void AudioOutputI2S2::config_i2s(void) | |||
| { | |||
| CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S2_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S2_RCSR & I2S_RCSR_RE) return; | |||
| //PLL: | |||
| int fs = AUDIO_SAMPLE_RATE_EXACT; | |||
| // PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
| @@ -200,20 +210,14 @@ void AudioOutputI2S2::config_i2s(void) | |||
| CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) | |||
| | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4, | |||
| CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) | |||
| | CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | |||
| | CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | |||
| | CCM_CS2CDR_SAI2_CLK_PODF(n2-1); | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | |||
| | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S2_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S2_RCSR & I2S_RCSR_RE) return; | |||
| CORE_PIN5_CONFIG = 2; //2:MCLK | |||
| CORE_PIN4_CONFIG = 2; //2:TX_BCLK | |||
| CORE_PIN3_CONFIG = 2; //2:TX_SYNC | |||
| // CORE_PIN2_CONFIG = 2; //2:TX_DATA0 | |||
| // CORE_PIN33_CONFIG = 2; //2:RX_DATA0 | |||
| CORE_PIN33_CONFIG = 2; //EMC_07, 2=SAI2_MCLK | |||
| CORE_PIN4_CONFIG = 2; //EMC_06, 2=SAI2_TX_BCLK | |||
| CORE_PIN3_CONFIG = 2; //EMC_05, 2=SAI2_TX_SYNC, page 429 | |||
| int rsync = 1; | |||
| int tsync = 0; | |||
| @@ -222,18 +226,20 @@ void AudioOutputI2S2::config_i2s(void) | |||
| //I2S2_TCSR = (1<<25); //Reset | |||
| I2S2_TCR1 = I2S_TCR1_RFW(1); | |||
| I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async; | |||
| | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | |||
| | (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); | |||
| I2S2_TCR3 = I2S_TCR3_TCE; | |||
| I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||
| I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | |||
| | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; | |||
| I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1)); | |||
| I2S2_RMR = 0; | |||
| //I2S2_RCSR = (1<<25); //Reset | |||
| I2S2_RCR1 = I2S_RCR1_RFW(1); | |||
| I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async; | |||
| | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | |||
| | (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); | |||
| I2S2_RCR3 = I2S_RCR3_RCE; | |||
| I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||
| I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | |||
| | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; | |||
| I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1)); | |||
| } | |||
| @@ -0,0 +1,355 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #include <Arduino.h> | |||
| #include "output_i2s_hex.h" | |||
| #include "output_i2s.h" | |||
| #include "memcpy_audio.h" | |||
| #if defined(__IMXRT1062__) | |||
| #include "utility/imxrt_hw.h" | |||
| audio_block_t * AudioOutputI2SHex::block_ch1_1st = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch2_1st = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch3_1st = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch4_1st = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch5_1st = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch6_1st = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch1_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch2_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch3_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch4_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch5_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SHex::block_ch6_2nd = NULL; | |||
| uint16_t AudioOutputI2SHex::ch1_offset = 0; | |||
| uint16_t AudioOutputI2SHex::ch2_offset = 0; | |||
| uint16_t AudioOutputI2SHex::ch3_offset = 0; | |||
| uint16_t AudioOutputI2SHex::ch4_offset = 0; | |||
| uint16_t AudioOutputI2SHex::ch5_offset = 0; | |||
| uint16_t AudioOutputI2SHex::ch6_offset = 0; | |||
| bool AudioOutputI2SHex::update_responsibility = false; | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*3]; | |||
| DMAChannel AudioOutputI2SHex::dma(false); | |||
| static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; | |||
| void AudioOutputI2SHex::begin(void) | |||
| { | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| block_ch1_1st = NULL; | |||
| block_ch2_1st = NULL; | |||
| block_ch3_1st = NULL; | |||
| block_ch4_1st = NULL; | |||
| block_ch5_1st = NULL; | |||
| block_ch6_1st = NULL; | |||
| const int pinoffset = 0; // TODO: make this configurable... | |||
| memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer)); | |||
| AudioOutputI2S::config_i2s(); | |||
| I2S1_TCR3 = I2S_TCR3_TCE_3CH << pinoffset; | |||
| switch (pinoffset) { | |||
| case 0: | |||
| CORE_PIN7_CONFIG = 3; | |||
| CORE_PIN32_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| break; | |||
| case 1: | |||
| CORE_PIN32_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| CORE_PIN6_CONFIG = 3; | |||
| } | |||
| dma.TCD->SADDR = i2s_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | | |||
| DMA_TCD_NBYTES_MLOFFYES_MLOFF(-12) | | |||
| DMA_TCD_NBYTES_MLOFFYES_NBYTES(6); | |||
| dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2 + pinoffset * 4); | |||
| dma.TCD->DOFF = 4; | |||
| dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->DLASTSGA = -12; | |||
| dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||
| dma.enable(); | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||
| I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| I2S1_TCR3 = I2S_TCR3_TCE_3CH << pinoffset; | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| } | |||
| void AudioOutputI2SHex::isr(void) | |||
| { | |||
| uint32_t saddr; | |||
| const int16_t *src1, *src2, *src3, *src4, *src5, *src6; | |||
| const int16_t *zeros = (const int16_t *)zerodata; | |||
| int16_t *dest; | |||
| saddr = (uint32_t)(dma.TCD->SADDR); | |||
| dma.clearInterrupt(); | |||
| if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { | |||
| // DMA is transmitting the first half of the buffer | |||
| // so we must fill the second half | |||
| dest = (int16_t *)((uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2); | |||
| if (update_responsibility) update_all(); | |||
| } else { | |||
| dest = (int16_t *)i2s_tx_buffer; | |||
| } | |||
| src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros; | |||
| src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros; | |||
| src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; | |||
| src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; | |||
| src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros; | |||
| src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros; | |||
| #if 0 | |||
| // TODO: optimized 6 channel copy... | |||
| memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); | |||
| #else | |||
| int16_t *p=dest; | |||
| for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) { | |||
| *p++ = *src1++; | |||
| *p++ = *src3++; | |||
| *p++ = *src5++; | |||
| *p++ = *src2++; | |||
| *p++ = *src4++; | |||
| *p++ = *src6++; | |||
| } | |||
| #endif | |||
| arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2); | |||
| if (block_ch1_1st) { | |||
| if (ch1_offset == 0) { | |||
| ch1_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch1_offset = 0; | |||
| release(block_ch1_1st); | |||
| block_ch1_1st = block_ch1_2nd; | |||
| block_ch1_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch2_1st) { | |||
| if (ch2_offset == 0) { | |||
| ch2_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch2_offset = 0; | |||
| release(block_ch2_1st); | |||
| block_ch2_1st = block_ch2_2nd; | |||
| block_ch2_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch3_1st) { | |||
| if (ch3_offset == 0) { | |||
| ch3_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch3_offset = 0; | |||
| release(block_ch3_1st); | |||
| block_ch3_1st = block_ch3_2nd; | |||
| block_ch3_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch4_1st) { | |||
| if (ch4_offset == 0) { | |||
| ch4_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch4_offset = 0; | |||
| release(block_ch4_1st); | |||
| block_ch4_1st = block_ch4_2nd; | |||
| block_ch4_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch5_1st) { | |||
| if (ch5_offset == 0) { | |||
| ch5_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch5_offset = 0; | |||
| release(block_ch5_1st); | |||
| block_ch5_1st = block_ch5_2nd; | |||
| block_ch5_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch6_1st) { | |||
| if (ch6_offset == 0) { | |||
| ch6_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch6_offset = 0; | |||
| release(block_ch6_1st); | |||
| block_ch6_1st = block_ch6_2nd; | |||
| block_ch6_2nd = NULL; | |||
| } | |||
| } | |||
| } | |||
| void AudioOutputI2SHex::update(void) | |||
| { | |||
| audio_block_t *block, *tmp; | |||
| block = receiveReadOnly(0); // channel 1 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch1_1st == NULL) { | |||
| block_ch1_1st = block; | |||
| ch1_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch1_2nd == NULL) { | |||
| block_ch1_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch1_1st; | |||
| block_ch1_1st = block_ch1_2nd; | |||
| block_ch1_2nd = block; | |||
| ch1_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(1); // channel 2 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch2_1st == NULL) { | |||
| block_ch2_1st = block; | |||
| ch2_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch2_2nd == NULL) { | |||
| block_ch2_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch2_1st; | |||
| block_ch2_1st = block_ch2_2nd; | |||
| block_ch2_2nd = block; | |||
| ch2_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(2); // channel 3 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch3_1st == NULL) { | |||
| block_ch3_1st = block; | |||
| ch3_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch3_2nd == NULL) { | |||
| block_ch3_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch3_1st; | |||
| block_ch3_1st = block_ch3_2nd; | |||
| block_ch3_2nd = block; | |||
| ch3_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(3); // channel 4 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch4_1st == NULL) { | |||
| block_ch4_1st = block; | |||
| ch4_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch4_2nd == NULL) { | |||
| block_ch4_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch4_1st; | |||
| block_ch4_1st = block_ch4_2nd; | |||
| block_ch4_2nd = block; | |||
| ch4_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(4); // channel 5 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch5_1st == NULL) { | |||
| block_ch5_1st = block; | |||
| ch5_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch5_2nd == NULL) { | |||
| block_ch5_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch5_1st; | |||
| block_ch5_1st = block_ch5_2nd; | |||
| block_ch5_2nd = block; | |||
| ch5_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(5); // channel 6 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch6_1st == NULL) { | |||
| block_ch6_1st = block; | |||
| ch6_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch6_2nd == NULL) { | |||
| block_ch6_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch6_1st; | |||
| block_ch6_1st = block_ch6_2nd; | |||
| block_ch6_2nd = block; | |||
| ch6_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| } | |||
| #else // not supported | |||
| void AudioOutputI2SHex::begin(void) | |||
| { | |||
| } | |||
| void AudioOutputI2SHex::update(void) | |||
| { | |||
| audio_block_t *block; | |||
| block = receiveReadOnly(0); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(1); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(2); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(3); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(4); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(5); | |||
| if (block) release(block); | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,65 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #ifndef output_i2s_hex_h_ | |||
| #define output_i2s_hex_h_ | |||
| #include "Arduino.h" | |||
| #include "AudioStream.h" | |||
| #include "DMAChannel.h" | |||
| class AudioOutputI2SHex : public AudioStream | |||
| { | |||
| public: | |||
| AudioOutputI2SHex(void) : AudioStream(6, inputQueueArray) { begin(); } | |||
| virtual void update(void); | |||
| void begin(void); | |||
| private: | |||
| static audio_block_t *block_ch1_1st; | |||
| static audio_block_t *block_ch2_1st; | |||
| static audio_block_t *block_ch3_1st; | |||
| static audio_block_t *block_ch4_1st; | |||
| static audio_block_t *block_ch5_1st; | |||
| static audio_block_t *block_ch6_1st; | |||
| static bool update_responsibility; | |||
| static DMAChannel dma; | |||
| static void isr(void); | |||
| static audio_block_t *block_ch1_2nd; | |||
| static audio_block_t *block_ch2_2nd; | |||
| static audio_block_t *block_ch3_2nd; | |||
| static audio_block_t *block_ch4_2nd; | |||
| static audio_block_t *block_ch5_2nd; | |||
| static audio_block_t *block_ch6_2nd; | |||
| static uint16_t ch1_offset; | |||
| static uint16_t ch2_offset; | |||
| static uint16_t ch3_offset; | |||
| static uint16_t ch4_offset; | |||
| static uint16_t ch5_offset; | |||
| static uint16_t ch6_offset; | |||
| audio_block_t *inputQueueArray[6]; | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,419 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #include <Arduino.h> | |||
| #include "output_i2s_oct.h" | |||
| #include "output_i2s.h" | |||
| #include "memcpy_audio.h" | |||
| #if defined(__IMXRT1062__) | |||
| #include "utility/imxrt_hw.h" | |||
| audio_block_t * AudioOutputI2SOct::block_ch1_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch2_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch3_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch4_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch5_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch6_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch7_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch8_1st = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch1_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch2_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch3_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch4_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch5_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch6_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch7_2nd = NULL; | |||
| audio_block_t * AudioOutputI2SOct::block_ch8_2nd = NULL; | |||
| uint16_t AudioOutputI2SOct::ch1_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch2_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch3_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch4_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch5_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch6_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch7_offset = 0; | |||
| uint16_t AudioOutputI2SOct::ch8_offset = 0; | |||
| bool AudioOutputI2SOct::update_responsibility = false; | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||
| DMAChannel AudioOutputI2SOct::dma(false); | |||
| static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; | |||
| void AudioOutputI2SOct::begin(void) | |||
| { | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| block_ch1_1st = NULL; | |||
| block_ch2_1st = NULL; | |||
| block_ch3_1st = NULL; | |||
| block_ch4_1st = NULL; | |||
| block_ch5_1st = NULL; | |||
| block_ch6_1st = NULL; | |||
| memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer)); | |||
| AudioOutputI2S::config_i2s(); | |||
| I2S1_TCR3 = I2S_TCR3_TCE_4CH; | |||
| CORE_PIN7_CONFIG = 3; | |||
| CORE_PIN32_CONFIG = 3; | |||
| CORE_PIN6_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| dma.TCD->SADDR = i2s_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | | |||
| DMA_TCD_NBYTES_MLOFFYES_MLOFF(-16) | | |||
| DMA_TCD_NBYTES_MLOFFYES_NBYTES(8); | |||
| dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | |||
| dma.TCD->DOFF = 4; | |||
| dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->DLASTSGA = -16; | |||
| dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||
| dma.enable(); | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||
| I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| //I2S1_TCR3 = I2S_TCR3_TCE_4CH; | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| } | |||
| void AudioOutputI2SOct::isr(void) | |||
| { | |||
| uint32_t saddr; | |||
| const int16_t *src1, *src2, *src3, *src4, *src5, *src6, *src7, *src8; | |||
| const int16_t *zeros = (const int16_t *)zerodata; | |||
| int16_t *dest; | |||
| saddr = (uint32_t)(dma.TCD->SADDR); | |||
| dma.clearInterrupt(); | |||
| if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { | |||
| // DMA is transmitting the first half of the buffer | |||
| // so we must fill the second half | |||
| dest = (int16_t *)((uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2); | |||
| if (update_responsibility) update_all(); | |||
| } else { | |||
| dest = (int16_t *)i2s_tx_buffer; | |||
| } | |||
| src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros; | |||
| src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros; | |||
| src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; | |||
| src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; | |||
| src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros; | |||
| src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros; | |||
| src7 = (block_ch7_1st) ? block_ch7_1st->data + ch7_offset : zeros; | |||
| src8 = (block_ch8_1st) ? block_ch8_1st->data + ch8_offset : zeros; | |||
| #if 0 | |||
| // TODO: optimized 8 channel copy... | |||
| memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); | |||
| #else | |||
| int16_t *p=dest; | |||
| for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) { | |||
| *p++ = *src1++; | |||
| *p++ = *src3++; | |||
| *p++ = *src5++; | |||
| *p++ = *src7++; | |||
| *p++ = *src2++; | |||
| *p++ = *src4++; | |||
| *p++ = *src6++; | |||
| *p++ = *src8++; | |||
| } | |||
| #endif | |||
| arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2); | |||
| if (block_ch1_1st) { | |||
| if (ch1_offset == 0) { | |||
| ch1_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch1_offset = 0; | |||
| release(block_ch1_1st); | |||
| block_ch1_1st = block_ch1_2nd; | |||
| block_ch1_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch2_1st) { | |||
| if (ch2_offset == 0) { | |||
| ch2_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch2_offset = 0; | |||
| release(block_ch2_1st); | |||
| block_ch2_1st = block_ch2_2nd; | |||
| block_ch2_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch3_1st) { | |||
| if (ch3_offset == 0) { | |||
| ch3_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch3_offset = 0; | |||
| release(block_ch3_1st); | |||
| block_ch3_1st = block_ch3_2nd; | |||
| block_ch3_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch4_1st) { | |||
| if (ch4_offset == 0) { | |||
| ch4_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch4_offset = 0; | |||
| release(block_ch4_1st); | |||
| block_ch4_1st = block_ch4_2nd; | |||
| block_ch4_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch5_1st) { | |||
| if (ch5_offset == 0) { | |||
| ch5_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch5_offset = 0; | |||
| release(block_ch5_1st); | |||
| block_ch5_1st = block_ch5_2nd; | |||
| block_ch5_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch6_1st) { | |||
| if (ch6_offset == 0) { | |||
| ch6_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch6_offset = 0; | |||
| release(block_ch6_1st); | |||
| block_ch6_1st = block_ch6_2nd; | |||
| block_ch6_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch7_1st) { | |||
| if (ch7_offset == 0) { | |||
| ch7_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch7_offset = 0; | |||
| release(block_ch7_1st); | |||
| block_ch7_1st = block_ch7_2nd; | |||
| block_ch7_2nd = NULL; | |||
| } | |||
| } | |||
| if (block_ch8_1st) { | |||
| if (ch8_offset == 0) { | |||
| ch8_offset = AUDIO_BLOCK_SAMPLES/2; | |||
| } else { | |||
| ch8_offset = 0; | |||
| release(block_ch8_1st); | |||
| block_ch8_1st = block_ch8_2nd; | |||
| block_ch8_2nd = NULL; | |||
| } | |||
| } | |||
| } | |||
| void AudioOutputI2SOct::update(void) | |||
| { | |||
| audio_block_t *block, *tmp; | |||
| block = receiveReadOnly(0); // channel 1 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch1_1st == NULL) { | |||
| block_ch1_1st = block; | |||
| ch1_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch1_2nd == NULL) { | |||
| block_ch1_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch1_1st; | |||
| block_ch1_1st = block_ch1_2nd; | |||
| block_ch1_2nd = block; | |||
| ch1_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(1); // channel 2 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch2_1st == NULL) { | |||
| block_ch2_1st = block; | |||
| ch2_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch2_2nd == NULL) { | |||
| block_ch2_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch2_1st; | |||
| block_ch2_1st = block_ch2_2nd; | |||
| block_ch2_2nd = block; | |||
| ch2_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(2); // channel 3 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch3_1st == NULL) { | |||
| block_ch3_1st = block; | |||
| ch3_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch3_2nd == NULL) { | |||
| block_ch3_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch3_1st; | |||
| block_ch3_1st = block_ch3_2nd; | |||
| block_ch3_2nd = block; | |||
| ch3_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(3); // channel 4 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch4_1st == NULL) { | |||
| block_ch4_1st = block; | |||
| ch4_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch4_2nd == NULL) { | |||
| block_ch4_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch4_1st; | |||
| block_ch4_1st = block_ch4_2nd; | |||
| block_ch4_2nd = block; | |||
| ch4_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(4); // channel 5 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch5_1st == NULL) { | |||
| block_ch5_1st = block; | |||
| ch5_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch5_2nd == NULL) { | |||
| block_ch5_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch5_1st; | |||
| block_ch5_1st = block_ch5_2nd; | |||
| block_ch5_2nd = block; | |||
| ch5_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(5); // channel 6 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch6_1st == NULL) { | |||
| block_ch6_1st = block; | |||
| ch6_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch6_2nd == NULL) { | |||
| block_ch6_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch6_1st; | |||
| block_ch6_1st = block_ch6_2nd; | |||
| block_ch6_2nd = block; | |||
| ch6_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(6); // channel 7 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch7_1st == NULL) { | |||
| block_ch7_1st = block; | |||
| ch7_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch7_2nd == NULL) { | |||
| block_ch7_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch7_1st; | |||
| block_ch7_1st = block_ch7_2nd; | |||
| block_ch7_2nd = block; | |||
| ch7_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| block = receiveReadOnly(7); // channel 8 | |||
| if (block) { | |||
| __disable_irq(); | |||
| if (block_ch8_1st == NULL) { | |||
| block_ch8_1st = block; | |||
| ch8_offset = 0; | |||
| __enable_irq(); | |||
| } else if (block_ch8_2nd == NULL) { | |||
| block_ch8_2nd = block; | |||
| __enable_irq(); | |||
| } else { | |||
| tmp = block_ch8_1st; | |||
| block_ch8_1st = block_ch8_2nd; | |||
| block_ch8_2nd = block; | |||
| ch8_offset = 0; | |||
| __enable_irq(); | |||
| release(tmp); | |||
| } | |||
| } | |||
| } | |||
| #else // not supported | |||
| void AudioOutputI2SOct::begin(void) | |||
| { | |||
| } | |||
| void AudioOutputI2SOct::update(void) | |||
| { | |||
| audio_block_t *block; | |||
| block = receiveReadOnly(0); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(1); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(2); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(3); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(4); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(5); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(6); | |||
| if (block) release(block); | |||
| block = receiveReadOnly(7); | |||
| if (block) release(block); | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,71 @@ | |||
| /* Audio Library for Teensy 3.X | |||
| * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com | |||
| * | |||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||
| * open source software by purchasing Teensy or other PJRC products. | |||
| * | |||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| * of this software and associated documentation files (the "Software"), to deal | |||
| * in the Software without restriction, including without limitation the rights | |||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| * copies of the Software, and to permit persons to whom the Software is | |||
| * furnished to do so, subject to the following conditions: | |||
| * | |||
| * The above copyright notice, development funding notice, and this permission | |||
| * notice shall be included in all copies or substantial portions of the Software. | |||
| * | |||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| * THE SOFTWARE. | |||
| */ | |||
| #ifndef output_i2s_oct_h_ | |||
| #define output_i2s_oct_h_ | |||
| #include "Arduino.h" | |||
| #include "AudioStream.h" | |||
| #include "DMAChannel.h" | |||
| class AudioOutputI2SOct : public AudioStream | |||
| { | |||
| public: | |||
| AudioOutputI2SOct(void) : AudioStream(8, inputQueueArray) { begin(); } | |||
| virtual void update(void); | |||
| void begin(void); | |||
| private: | |||
| static audio_block_t *block_ch1_1st; | |||
| static audio_block_t *block_ch2_1st; | |||
| static audio_block_t *block_ch3_1st; | |||
| static audio_block_t *block_ch4_1st; | |||
| static audio_block_t *block_ch5_1st; | |||
| static audio_block_t *block_ch6_1st; | |||
| static audio_block_t *block_ch7_1st; | |||
| static audio_block_t *block_ch8_1st; | |||
| static bool update_responsibility; | |||
| static DMAChannel dma; | |||
| static void isr(void); | |||
| static audio_block_t *block_ch1_2nd; | |||
| static audio_block_t *block_ch2_2nd; | |||
| static audio_block_t *block_ch3_2nd; | |||
| static audio_block_t *block_ch4_2nd; | |||
| static audio_block_t *block_ch5_2nd; | |||
| static audio_block_t *block_ch6_2nd; | |||
| static audio_block_t *block_ch7_2nd; | |||
| static audio_block_t *block_ch8_2nd; | |||
| static uint16_t ch1_offset; | |||
| static uint16_t ch2_offset; | |||
| static uint16_t ch3_offset; | |||
| static uint16_t ch4_offset; | |||
| static uint16_t ch5_offset; | |||
| static uint16_t ch6_offset; | |||
| static uint16_t ch7_offset; | |||
| static uint16_t ch8_offset; | |||
| audio_block_t *inputQueueArray[8]; | |||
| }; | |||
| #endif | |||
| @@ -26,9 +26,14 @@ | |||
| #include <Arduino.h> | |||
| #include "output_i2s_quad.h" | |||
| #include "output_i2s.h" | |||
| #include "memcpy_audio.h" | |||
| #if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | |||
| #if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #include "utility/imxrt_hw.h" | |||
| #endif | |||
| audio_block_t * AudioOutputI2SQuad::block_ch1_1st = NULL; | |||
| audio_block_t * AudioOutputI2SQuad::block_ch2_1st = NULL; | |||
| @@ -42,16 +47,14 @@ uint16_t AudioOutputI2SQuad::ch1_offset = 0; | |||
| uint16_t AudioOutputI2SQuad::ch2_offset = 0; | |||
| uint16_t AudioOutputI2SQuad::ch3_offset = 0; | |||
| uint16_t AudioOutputI2SQuad::ch4_offset = 0; | |||
| //audio_block_t * AudioOutputI2SQuad::inputQueueArray[4]; | |||
| bool AudioOutputI2SQuad::update_responsibility = false; | |||
| DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||
| DMAChannel AudioOutputI2SQuad::dma(false); | |||
| static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0}; | |||
| void AudioOutputI2SQuad::begin(void) | |||
| { | |||
| #if 1 | |||
| dma.begin(true); // Allocate the DMA channel first | |||
| block_ch1_1st = NULL; | |||
| @@ -59,6 +62,7 @@ void AudioOutputI2SQuad::begin(void) | |||
| block_ch3_1st = NULL; | |||
| block_ch4_1st = NULL; | |||
| #if defined(KINETISK) | |||
| // TODO: can we call normal config_i2s, and then just enable the extra output? | |||
| config_i2s(); | |||
| CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2 | |||
| @@ -82,6 +86,45 @@ void AudioOutputI2SQuad::begin(void) | |||
| I2S0_TCSR = I2S_TCSR_SR; | |||
| I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| dma.attachInterrupt(isr); | |||
| #elif defined(__IMXRT1062__) | |||
| const int pinoffset = 0; // TODO: make this configurable... | |||
| memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer)); | |||
| AudioOutputI2S::config_i2s(); | |||
| I2S1_TCR3 = I2S_TCR3_TCE_2CH << pinoffset; | |||
| switch (pinoffset) { | |||
| case 0: | |||
| CORE_PIN7_CONFIG = 3; | |||
| CORE_PIN32_CONFIG = 3; | |||
| break; | |||
| case 1: | |||
| CORE_PIN32_CONFIG = 3; | |||
| CORE_PIN9_CONFIG = 3; | |||
| break; | |||
| case 2: | |||
| CORE_PIN9_CONFIG = 3; | |||
| CORE_PIN6_CONFIG = 3; | |||
| } | |||
| dma.TCD->SADDR = i2s_tx_buffer; | |||
| dma.TCD->SOFF = 2; | |||
| dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
| dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | | |||
| DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8) | | |||
| DMA_TCD_NBYTES_MLOFFYES_NBYTES(4); | |||
| dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||
| dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2 + pinoffset * 4); | |||
| dma.TCD->DOFF = 4; | |||
| dma.TCD->CITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->DLASTSGA = -8; | |||
| dma.TCD->BITER_ELINKNO = AUDIO_BLOCK_SAMPLES * 2; | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | |||
| dma.enable(); | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||
| I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| I2S1_TCR3 = I2S_TCR3_TCE_2CH << pinoffset; | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| #endif | |||
| } | |||
| @@ -108,7 +151,6 @@ void AudioOutputI2SQuad::isr(void) | |||
| src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros; | |||
| src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros; | |||
| // TODO: fast 4-way interleaved memcpy... | |||
| #if 1 | |||
| memcpy_tointerleaveQuad(dest, src1, src2, src3, src4); | |||
| #else | |||
| @@ -119,6 +161,7 @@ void AudioOutputI2SQuad::isr(void) | |||
| *dest++ = *src4++; | |||
| } | |||
| #endif | |||
| arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); | |||
| if (block_ch1_1st) { | |||
| if (ch1_offset == 0) { | |||
| @@ -245,7 +288,7 @@ void AudioOutputI2SQuad::update(void) | |||
| } | |||
| } | |||
| #if defined(KINETISK) | |||
| // MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate | |||
| // | |||
| #if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000 | |||
| @@ -272,12 +315,17 @@ void AudioOutputI2SQuad::update(void) | |||
| #define MCLK_MULT 1 | |||
| #define MCLK_DIV 17 | |||
| #elif F_CPU == 216000000 | |||
| #define MCLK_MULT 8 | |||
| #define MCLK_DIV 153 | |||
| #define MCLK_SRC 0 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 240000000 | |||
| #define MCLK_MULT 4 | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 85 | |||
| #define MCLK_SRC 0 | |||
| #elif F_CPU == 256000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 16000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| @@ -333,10 +381,10 @@ void AudioOutputI2SQuad::config_i2s(void) | |||
| CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK | |||
| CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK | |||
| } | |||
| #endif // KINETISK | |||
| #else // not __MK20DX256__ | |||
| #else // not supported | |||
| void AudioOutputI2SQuad::begin(void) | |||
| { | |||
| @@ -71,8 +71,11 @@ void AudioOutputPT8211::begin(void) | |||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| dma.enable(); | |||
| I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; | |||
| return; | |||
| #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1052__) | |||
| @@ -96,10 +99,12 @@ void AudioOutputPT8211::begin(void) | |||
| I2S1_RCSR |= I2S_RCSR_RE; | |||
| I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #endif | |||
| update_responsibility = update_setup(); | |||
| dma.attachInterrupt(isr); | |||
| dma.enable(); | |||
| return; | |||
| #endif | |||
| } | |||
| void AudioOutputPT8211::isr(void) | |||
| @@ -429,12 +434,17 @@ void AudioOutputPT8211::update(void) | |||
| #define MCLK_MULT 1 | |||
| #define MCLK_DIV 17 | |||
| #elif F_CPU == 216000000 | |||
| #define MCLK_MULT 8 | |||
| #define MCLK_DIV 153 | |||
| #define MCLK_SRC 0 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 240000000 | |||
| #define MCLK_MULT 4 | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 85 | |||
| #define MCLK_SRC 0 | |||
| #elif F_CPU == 256000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 16000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| @@ -327,12 +327,17 @@ void AudioOutputSPDIF::update(void) | |||
| #define MCLK_MULT 1 | |||
| #define MCLK_DIV 17 | |||
| #elif F_CPU == 216000000 | |||
| #define MCLK_MULT 8 | |||
| #define MCLK_DIV 153 | |||
| #define MCLK_SRC 0 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 240000000 | |||
| #define MCLK_MULT 4 | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 85 | |||
| #define MCLK_SRC 0 | |||
| #elif F_CPU == 256000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 16000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| @@ -72,8 +72,8 @@ void AudioOutputTDM::begin(void) | |||
| I2S0_TCSR = I2S_TCSR_SR; | |||
| I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| CORE_PIN6_CONFIG = 3; //1:TX_DATA0 | |||
| #elif defined(__IMXRT1062__) | |||
| CORE_PIN7_CONFIG = 3; //1:TX_DATA0 | |||
| dma.TCD->SADDR = tdm_tx_buffer; | |||
| dma.TCD->SOFF = 4; | |||
| @@ -91,8 +91,8 @@ void AudioOutputTDM::begin(void) | |||
| update_responsibility = update_setup(); | |||
| dma.enable(); | |||
| I2S1_RCSR |= I2S_RCSR_RE; | |||
| I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; | |||
| I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; | |||
| #endif | |||
| dma.attachInterrupt(isr); | |||
| @@ -125,7 +125,7 @@ static void memcpy_tdm_tx(uint32_t *dest, const uint32_t *src1, const uint32_t * | |||
| void AudioOutputTDM::isr(void) | |||
| { | |||
| uint32_t *dest, *dc; | |||
| uint32_t *dest; | |||
| const uint32_t *src1, *src2; | |||
| uint32_t i, saddr; | |||
| @@ -141,7 +141,11 @@ void AudioOutputTDM::isr(void) | |||
| dest = tdm_tx_buffer; | |||
| } | |||
| if (update_responsibility) AudioStream::update_all(); | |||
| dc = dest; | |||
| #if IMXRT_CACHE_ENABLED >= 2 | |||
| uint32_t *dc = dest; | |||
| #endif | |||
| for (i=0; i < 16; i += 2) { | |||
| src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros; | |||
| src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros; | |||
| @@ -205,12 +209,17 @@ void AudioOutputTDM::update(void) | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 17 | |||
| #elif F_CPU == 216000000 | |||
| #define MCLK_MULT 16 | |||
| #define MCLK_DIV 153 | |||
| #define MCLK_SRC 0 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #elif F_CPU == 240000000 | |||
| #define MCLK_MULT 8 | |||
| #define MCLK_MULT 2 | |||
| #define MCLK_DIV 85 | |||
| #define MCLK_SRC 0 | |||
| #elif F_CPU == 256000000 | |||
| #define MCLK_MULT 12 | |||
| #define MCLK_DIV 17 | |||
| #define MCLK_SRC 1 | |||
| #else | |||
| #error "This CPU Clock Speed is not supported by the Audio library"; | |||
| #endif | |||
| @@ -265,8 +274,12 @@ void AudioOutputTDM::config_tdm(void) | |||
| CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK - 11.2 MHz | |||
| CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK - 22.5 MHz | |||
| #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #elif defined(__IMXRT1062__) | |||
| CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S1_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S1_RCSR & I2S_RCSR_RE) return; | |||
| //PLL: | |||
| int fs = AUDIO_SAMPLE_RATE_EXACT; | |||
| // PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
| @@ -291,10 +304,6 @@ void AudioOutputTDM::config_tdm(void) | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) | |||
| | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S1_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S1_RCSR & I2S_RCSR_RE) return; | |||
| // configure transmitter | |||
| int rsync = 0; | |||
| int tsync = 1; | |||
| @@ -24,7 +24,7 @@ | |||
| * THE SOFTWARE. | |||
| */ | |||
| #if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #include <Arduino.h> | |||
| #include "output_tdm2.h" | |||
| #include "memcpy_audio.h" | |||
| @@ -159,8 +159,11 @@ void AudioOutputTDM2::update(void) | |||
| void AudioOutputTDM2::config_tdm(void) | |||
| { | |||
| CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S2_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S2_RCSR & I2S_RCSR_RE) return; | |||
| //PLL: | |||
| int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ | |||
| // PLL between 27*24 = 648MHz und 54*24=1296MHz | |||
| @@ -186,12 +189,6 @@ void AudioOutputTDM2::config_tdm(void) | |||
| IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | |||
| | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK | |||
| // if either transmitter or receiver is enabled, do nothing | |||
| if (I2S2_TCSR & I2S_TCSR_TE) return; | |||
| if (I2S2_RCSR & I2S_RCSR_RE) return; | |||
| // configure transmitter | |||
| int rsync = 1; | |||
| int tsync = 0; | |||
| @@ -215,10 +212,9 @@ void AudioOutputTDM2::config_tdm(void) | |||
| | I2S_RCR4_FSE | I2S_RCR4_FSD; | |||
| I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); | |||
| CORE_PIN5_CONFIG = 2; //2:MCLK | |||
| CORE_PIN33_CONFIG = 2; //2:MCLK | |||
| CORE_PIN4_CONFIG = 2; //2:TX_BCLK | |||
| CORE_PIN3_CONFIG = 2; //2:TX_SYNC | |||
| } | |||
| #endif | |||
| #endif | |||
| @@ -52,7 +52,7 @@ void AudioPlayQueue::playBuffer(void) | |||
| if (!userblock) return; | |||
| h = head + 1; | |||
| if (h >= 32) h = 0; | |||
| if (h >= max_buffers) h = 0; | |||
| while (tail == h) ; // wait until space in the queue | |||
| queue[h] = userblock; | |||
| head = h; | |||
| @@ -66,7 +66,7 @@ void AudioPlayQueue::update(void) | |||
| t = tail; | |||
| if (t != head) { | |||
| if (++t >= 32) t = 0; | |||
| if (++t >= max_buffers) t = 0; | |||
| block = queue[t]; | |||
| tail = t; | |||
| transmit(block); | |||
| @@ -32,6 +32,12 @@ | |||
| class AudioPlayQueue : public AudioStream | |||
| { | |||
| private: | |||
| #if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) | |||
| static const int max_buffers = 80; | |||
| #else | |||
| static const int max_buffers = 32; | |||
| #endif | |||
| public: | |||
| AudioPlayQueue(void) : AudioStream(0, NULL), | |||
| userblock(NULL), head(0), tail(0) { } | |||
| @@ -44,7 +50,7 @@ public: | |||
| //bool isPlaying(void) { return playing; } | |||
| virtual void update(void); | |||
| private: | |||
| audio_block_t *queue[32]; | |||
| audio_block_t *queue[max_buffers]; | |||
| audio_block_t *userblock; | |||
| volatile uint8_t head, tail; | |||
| }; | |||
| @@ -36,7 +36,7 @@ int AudioRecordQueue::available(void) | |||
| h = head; | |||
| t = tail; | |||
| if (h >= t) return h - t; | |||
| return 53 + h - t; | |||
| return max_buffers + h - t; | |||
| } | |||
| void AudioRecordQueue::clear(void) | |||
| @@ -49,7 +49,7 @@ void AudioRecordQueue::clear(void) | |||
| } | |||
| t = tail; | |||
| while (t != head) { | |||
| if (++t >= 53) t = 0; | |||
| if (++t >= max_buffers) t = 0; | |||
| release(queue[t]); | |||
| } | |||
| tail = t; | |||
| @@ -62,7 +62,7 @@ int16_t * AudioRecordQueue::readBuffer(void) | |||
| if (userblock) return NULL; | |||
| t = tail; | |||
| if (t == head) return NULL; | |||
| if (++t >= 53) t = 0; | |||
| if (++t >= max_buffers) t = 0; | |||
| userblock = queue[t]; | |||
| tail = t; | |||
| return userblock->data; | |||
| @@ -87,7 +87,7 @@ void AudioRecordQueue::update(void) | |||
| return; | |||
| } | |||
| h = head + 1; | |||
| if (h >= 53) h = 0; | |||
| if (h >= max_buffers) h = 0; | |||
| if (h == tail) { | |||
| release(block); | |||
| } else { | |||
| @@ -32,6 +32,12 @@ | |||
| class AudioRecordQueue : public AudioStream | |||
| { | |||
| private: | |||
| #if defined(__IMXRT1062__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) | |||
| static const int max_buffers = 209; | |||
| #else | |||
| static const int max_buffers = 53; | |||
| #endif | |||
| public: | |||
| AudioRecordQueue(void) : AudioStream(1, inputQueueArray), | |||
| userblock(NULL), head(0), tail(0), enabled(0) { } | |||
| @@ -49,7 +55,7 @@ public: | |||
| virtual void update(void); | |||
| private: | |||
| audio_block_t *inputQueueArray[1]; | |||
| audio_block_t * volatile queue[53]; | |||
| audio_block_t * volatile queue[max_buffers]; | |||
| audio_block_t *userblock; | |||
| volatile uint8_t head, tail, enabled; | |||
| }; | |||
| @@ -31,7 +31,7 @@ | |||
| #include "imxrt_hw.h" | |||
| PROGMEM | |||
| void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force = false) // sets PLL4 | |||
| void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4 | |||
| { | |||
| if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return; | |||
| @@ -27,7 +27,7 @@ | |||
| (c) Frank B | |||
| */ | |||
| #if defined(__IMXRT1052__) || defined(__IMXRT1062__) | |||
| #if defined(__IMXRT1062__) | |||
| #ifndef imxr_hw_h_ | |||
| #define imxr_hw_h_ | |||