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