| delay(400); | delay(400); | ||||
| write(CHIP_LINE_OUT_VOL, 0x1D1D); // default approx 1.3 volts peak-to-peak | write(CHIP_LINE_OUT_VOL, 0x1D1D); // default approx 1.3 volts peak-to-peak | ||||
| write(CHIP_CLK_CTRL, 0x0004); // 44.1 kHz, 256*Fs | write(CHIP_CLK_CTRL, 0x0004); // 44.1 kHz, 256*Fs | ||||
| write(CHIP_I2S_CTRL, 0x0130); // SCLK=32*Fs, 16bit, I2S format | |||||
| write(CHIP_I2S_CTRL, 0x0030); // SCLK=64*Fs, 16bit, I2S format | |||||
| // default signal routing is ok? | // default signal routing is ok? | ||||
| write(CHIP_SSS_CTRL, 0x0010); // ADC->I2S, I2S->DAC | write(CHIP_SSS_CTRL, 0x0010); // ADC->I2S, I2S->DAC | ||||
| write(CHIP_ADCDAC_CTRL, 0x0000); // disable dac mute | write(CHIP_ADCDAC_CTRL, 0x0000); // disable dac mute |
| /* | |||||
| * A simple hardware test which receives audio on the A2 analog pin | |||||
| * and sends it to the audio shield (I2S digital audio) | |||||
| * | |||||
| * This example code is in the public domain. | |||||
| */ | |||||
| #include <Audio.h> | |||||
| #include <Wire.h> | |||||
| #include <SPI.h> | |||||
| #include <SD.h> | |||||
| #include <SerialFlash.h> | |||||
| // GUItool: begin automatically generated code | |||||
| AudioInputAnalog adc1; //xy=197,73 | |||||
| AudioOutputI2S i2s1; //xy=375,85 | |||||
| AudioConnection patchCord1(adc1, 0, i2s1, 0); | |||||
| AudioConnection patchCord2(adc1, 0, i2s1, 1); | |||||
| AudioControlSGTL5000 sgtl5000_1; //xy=314,158 | |||||
| // GUItool: end automatically generated code | |||||
| void setup() { | |||||
| // Audio connections require memory to work. For more | |||||
| // detailed information, see the MemoryAndCpuUsage example | |||||
| AudioMemory(12); | |||||
| // Enable the audio shield | |||||
| sgtl5000_1.enable(); | |||||
| sgtl5000_1.volume(0.5); | |||||
| } | |||||
| void loop() { | |||||
| // Do nothing here. The Audio flows automatically | |||||
| // When AudioInputAnalog is running, analogRead() must NOT be used. | |||||
| } | |||||
| at altered speed. The grainLength is specified in milliseconds, up to | at altered speed. The grainLength is specified in milliseconds, up to | ||||
| one third of the memory from begin(); | one third of the memory from begin(); | ||||
| </p> | </p> | ||||
| <p class=func><span class=keyword>end</span>();</p> | |||||
| <p class=func><span class=keyword>stop</span>();</p> | |||||
| <p class=desc>Stop granual processing. The input signal is passed to the | <p class=desc>Stop granual processing. The input signal is passed to the | ||||
| output without any changes. | output without any changes. | ||||
| </p> | </p> |
| * THE SOFTWARE. | * THE SOFTWARE. | ||||
| */ | */ | ||||
| #if !defined(__IMXRT1052__) && !defined(__IMXRT1062__) | |||||
| #if defined(KINETISK) | |||||
| #include <Arduino.h> | #include <Arduino.h> | ||||
| #include "input_adc.h" | #include "input_adc.h" | ||||
| #define COEF_HPF_DCBLOCK (1048300<<10) // DC Removal filter coefficient in S1.30 | #define COEF_HPF_DCBLOCK (1048300<<10) // DC Removal filter coefficient in S1.30 | ||||
| DMAMEM static uint16_t analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint16_t analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| audio_block_t * AudioInputAnalog::block_left = NULL; | audio_block_t * AudioInputAnalog::block_left = NULL; | ||||
| uint16_t AudioInputAnalog::block_offset = 0; | uint16_t AudioInputAnalog::block_offset = 0; | ||||
| int32_t AudioInputAnalog::hpf_y1 = 0; | int32_t AudioInputAnalog::hpf_y1 = 0; | ||||
| void AudioInputAnalog::init(uint8_t pin) | void AudioInputAnalog::init(uint8_t pin) | ||||
| { | { | ||||
| int32_t tmp; | |||||
| int32_t tmp; | |||||
| // Configure the ADC and run at least one software-triggered | // Configure the ADC and run at least one software-triggered | ||||
| // conversion. This completes the self calibration stuff and | // conversion. This completes the self calibration stuff and | ||||
| analogReadAveraging(4); | analogReadAveraging(4); | ||||
| #endif | #endif | ||||
| // Note for review: | // Note for review: | ||||
| // Probably not useful to spin cycles here stabilizing | |||||
| // since DC blocking is similar to te external analog filters | |||||
| tmp = (uint16_t) analogRead(pin); | |||||
| tmp = ( ((int32_t) tmp) << 14); | |||||
| hpf_x1 = tmp; // With constant DC level x1 would be x0 | |||||
| hpf_y1 = 0; // Output will settle here when stable | |||||
| // Probably not useful to spin cycles here stabilizing | |||||
| // since DC blocking is similar to te external analog filters | |||||
| tmp = (uint16_t) analogRead(pin); | |||||
| tmp = ( ((int32_t) tmp) << 14); | |||||
| hpf_x1 = tmp; // With constant DC level x1 would be x0 | |||||
| hpf_y1 = 0; // Output will settle here when stable | |||||
| // set the programmable delay block to trigger the ADC at 44.1 kHz | // set the programmable delay block to trigger the ADC at 44.1 kHz | ||||
| #if defined(KINETISK) | |||||
| if (!(SIM_SCGC6 & SIM_SCGC6_PDB) | if (!(SIM_SCGC6 & SIM_SCGC6_PDB) | ||||
| || (PDB0_SC & PDB_CONFIG) != PDB_CONFIG | || (PDB0_SC & PDB_CONFIG) != PDB_CONFIG | ||||
| || PDB0_MOD != PDB_PERIOD | || PDB0_MOD != PDB_PERIOD | ||||
| PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | ||||
| PDB0_CH0C1 = 0x0101; | PDB0_CH0C1 = 0x0101; | ||||
| } | } | ||||
| #endif | |||||
| // enable the ADC for hardware trigger and DMA | // enable the ADC for hardware trigger and DMA | ||||
| ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | ||||
| // set up a DMA channel to store the ADC data | // set up a DMA channel to store the ADC data | ||||
| dma.begin(true); | dma.begin(true); | ||||
| #if defined(KINETISK) | |||||
| dma.TCD->SADDR = &ADC0_RA; | dma.TCD->SADDR = &ADC0_RA; | ||||
| 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->DLASTSGA = -sizeof(analog_rx_buffer); | dma.TCD->DLASTSGA = -sizeof(analog_rx_buffer); | ||||
| dma.TCD->BITER_ELINKNO = sizeof(analog_rx_buffer) / 2; | dma.TCD->BITER_ELINKNO = sizeof(analog_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; | ||||
| #endif | |||||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); | ||||
| update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
| dma.enable(); | dma.enable(); | ||||
| uint16_t *dest_left; | uint16_t *dest_left; | ||||
| audio_block_t *left; | audio_block_t *left; | ||||
| #if defined(KINETISK) | |||||
| daddr = (uint32_t)(dma.TCD->DADDR); | daddr = (uint32_t)(dma.TCD->DADDR); | ||||
| #endif | |||||
| dma.clearInterrupt(); | dma.clearInterrupt(); | ||||
| if (daddr < (uint32_t)analog_rx_buffer + sizeof(analog_rx_buffer) / 2) { | if (daddr < (uint32_t)analog_rx_buffer + sizeof(analog_rx_buffer) / 2) { | ||||
| transmit(out_left); | transmit(out_left); | ||||
| release(out_left); | release(out_left); | ||||
| } | } | ||||
| #endif | |||||
| #endif | |||||
| #if defined(__IMXRT1062__) | |||||
| #include <Arduino.h> | |||||
| #include "input_adc.h" | |||||
| extern "C" void xbar_connect(unsigned int input, unsigned int output); | |||||
| #define FILTERLEN 15 | |||||
| DMAChannel AudioInputAnalog::dma(false); | |||||
| // TODO: how much extra space is needed to avoid wrap-around timing? 200 seems a safe guess | |||||
| static __attribute__((aligned(32))) uint16_t adc_buffer[AUDIO_BLOCK_SAMPLES*4+200]; | |||||
| static int16_t capture_buffer[AUDIO_BLOCK_SAMPLES*4+FILTERLEN]; | |||||
| // TODO: these big buffers should be in DMAMEM, rather than consuming precious DTCM | |||||
| PROGMEM static const uint8_t adc2_pin_to_channel[] = { | |||||
| 7, // 0/A0 AD_B1_02 | |||||
| 8, // 1/A1 AD_B1_03 | |||||
| 12, // 2/A2 AD_B1_07 | |||||
| 11, // 3/A3 AD_B1_06 | |||||
| 6, // 4/A4 AD_B1_01 | |||||
| 5, // 5/A5 AD_B1_00 | |||||
| 15, // 6/A6 AD_B1_10 | |||||
| 0, // 7/A7 AD_B1_11 | |||||
| 13, // 8/A8 AD_B1_08 | |||||
| 14, // 9/A9 AD_B1_09 | |||||
| 255, // 10/A10 AD_B0_12 - only on ADC1, 1 - can't use for audio | |||||
| 255, // 11/A11 AD_B0_13 - only on ADC1, 2 - can't use for audio | |||||
| 3, // 12/A12 AD_B1_14 | |||||
| 4, // 13/A13 AD_B1_15 | |||||
| 7, // 14/A0 AD_B1_02 | |||||
| 8, // 15/A1 AD_B1_03 | |||||
| 12, // 16/A2 AD_B1_07 | |||||
| 11, // 17/A3 AD_B1_06 | |||||
| 6, // 18/A4 AD_B1_01 | |||||
| 5, // 19/A5 AD_B1_00 | |||||
| 15, // 20/A6 AD_B1_10 | |||||
| 0, // 21/A7 AD_B1_11 | |||||
| 13, // 22/A8 AD_B1_08 | |||||
| 14, // 23/A9 AD_B1_09 | |||||
| 255, // 24/A10 AD_B0_12 - only on ADC1, 1 - can't use for audio | |||||
| 255, // 25/A11 AD_B0_13 - only on ADC1, 2 - can't use for audio | |||||
| 3, // 26/A12 AD_B1_14 - only on ADC2, do not use analogRead() | |||||
| 4, // 27/A13 AD_B1_15 - only on ADC2, do not use analogRead() | |||||
| #ifdef ARDUINO_TEENSY41 | |||||
| 255, // 28 | |||||
| 255, // 29 | |||||
| 255, // 30 | |||||
| 255, // 31 | |||||
| 255, // 32 | |||||
| 255, // 33 | |||||
| 255, // 34 | |||||
| 255, // 35 | |||||
| 255, // 36 | |||||
| 255, // 37 | |||||
| 1, // 38/A14 AD_B1_12 - only on ADC2, do not use analogRead() | |||||
| 2, // 39/A15 AD_B1_13 - only on ADC2, do not use analogRead() | |||||
| 9, // 40/A16 AD_B1_04 | |||||
| 10, // 41/A17 AD_B1_05 | |||||
| #endif | |||||
| }; | |||||
| static const int16_t filter[FILTERLEN] = { | |||||
| 1449, | |||||
| 3676, | |||||
| 6137, | |||||
| 9966, | |||||
| 13387, | |||||
| 16896, | |||||
| 18951, | |||||
| 19957, | |||||
| 18951, | |||||
| 16896, | |||||
| 13387, | |||||
| 9966, | |||||
| 6137, | |||||
| 3676, | |||||
| 1449 | |||||
| }; | |||||
| void AudioInputAnalog::init(uint8_t pin) | |||||
| { | |||||
| if (pin >= sizeof(adc2_pin_to_channel)) return; | |||||
| const uint8_t adc_channel = adc2_pin_to_channel[pin]; | |||||
| if (adc_channel == 255) return; | |||||
| // configure a timer to trigger ADC | |||||
| // TODO: sample rate should be slightly lower than 4X AUDIO_SAMPLE_RATE_EXACT | |||||
| // linear interpolation is supposed to resample it to exactly 4X | |||||
| // the sample rate, so we avoid artifacts boundaries between captures | |||||
| const int comp1 = ((float)F_BUS_ACTUAL) / (AUDIO_SAMPLE_RATE_EXACT * 4.0f) / 2.0f + 0.5f; | |||||
| TMR4_ENBL &= ~(1<<3); | |||||
| TMR4_SCTRL3 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE; | |||||
| TMR4_CSCTRL3 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN; | |||||
| TMR4_CNTR3 = 0; | |||||
| TMR4_LOAD3 = 0; | |||||
| TMR4_COMP13 = comp1; | |||||
| TMR4_CMPLD13 = comp1; | |||||
| TMR4_CTRL3 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); | |||||
| TMR4_DMA3 = TMR_DMA_CMPLD1DE; | |||||
| TMR4_CNTR3 = 0; | |||||
| TMR4_ENBL |= (1<<3); | |||||
| // connect the timer output the ADC_ETC input | |||||
| const int trigger = 4; // 0-3 for ADC1, 4-7 for ADC2 | |||||
| CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); | |||||
| xbar_connect(XBARA1_IN_QTIMER4_TIMER3, XBARA1_OUT_ADC_ETC_TRIG00 + trigger); | |||||
| // turn on ADC_ETC and configure to receive trigger | |||||
| if (ADC_ETC_CTRL & (ADC_ETC_CTRL_SOFTRST | ADC_ETC_CTRL_TSC_BYPASS)) { | |||||
| ADC_ETC_CTRL = 0; // clears SOFTRST only | |||||
| ADC_ETC_CTRL = 0; // clears TSC_BYPASS | |||||
| } | |||||
| ADC_ETC_CTRL |= ADC_ETC_CTRL_TRIG_ENABLE(1 << trigger) | ADC_ETC_CTRL_DMA_MODE_SEL; | |||||
| ADC_ETC_DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(trigger); | |||||
| // configure ADC_ETC trigger4 to make one ADC2 measurement on pin A2 | |||||
| const int len = 1; | |||||
| IMXRT_ADC_ETC.TRIG[trigger].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(len - 1) | | |||||
| ADC_ETC_TRIG_CTRL_TRIG_PRIORITY(7); | |||||
| IMXRT_ADC_ETC.TRIG[trigger].CHAIN_1_0 = ADC_ETC_TRIG_CHAIN_HWTS0(1) | | |||||
| ADC_ETC_TRIG_CHAIN_CSEL0(adc2_pin_to_channel[pin]) | ADC_ETC_TRIG_CHAIN_B2B0; | |||||
| // set up ADC2 for 12 bit mode, hardware trigger | |||||
| Serial.printf("ADC2_CFG = %08X\n", ADC2_CFG); | |||||
| ADC2_CFG |= ADC_CFG_ADTRG; | |||||
| ADC2_CFG = ADC_CFG_MODE(2) | ADC_CFG_ADSTS(3) | ADC_CFG_ADLSMP | ADC_CFG_ADTRG | | |||||
| ADC_CFG_ADICLK(1) | ADC_CFG_ADIV(0) /*| ADC_CFG_ADHSC*/; | |||||
| ADC2_GC &= ~ADC_GC_AVGE; // single sample, no averaging | |||||
| ADC2_HC0 = ADC_HC_ADCH(16); // 16 = controlled by ADC_ETC | |||||
| // use a DMA channel to capture ADC_ETC output | |||||
| dma.begin(); | |||||
| dma.TCD->SADDR = &(IMXRT_ADC_ETC.TRIG[4].RESULT_1_0); | |||||
| 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 = adc_buffer; | |||||
| dma.TCD->DOFF = 2; | |||||
| dma.TCD->CITER_ELINKNO = sizeof(adc_buffer) / 2; | |||||
| dma.TCD->DLASTSGA = -sizeof(adc_buffer); | |||||
| dma.TCD->BITER_ELINKNO = sizeof(adc_buffer) / 2; | |||||
| dma.TCD->CSR = 0; | |||||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC_ETC); | |||||
| dma.enable(); | |||||
| // TODO: configure I2S1 to interrupt every 128 audio samples | |||||
| } | |||||
| static int16_t fir(const int16_t *data, const int16_t *impulse, int len) | |||||
| { | |||||
| int64_t sum=0; | |||||
| while (len > 0) { | |||||
| sum += *data++ * *impulse++; // TODO: optimize with DSP inst and filter symmetry | |||||
| len --; | |||||
| } | |||||
| sum = sum >> 15; // TODO: adjust filter coefficients for proper gain, 12 to 16 bits | |||||
| if (sum > 32767) return 32767; | |||||
| if (sum < -32768) return -32768; | |||||
| return sum; | |||||
| } | |||||
| void AudioInputAnalog::update(void) | |||||
| { | |||||
| audio_block_t *output=NULL; | |||||
| output = allocate(); | |||||
| if (output == NULL) return; | |||||
| uint16_t *p = (uint16_t *)dma.TCD->DADDR; | |||||
| //int offset = p - adc_buffer; | |||||
| //if (--offset < 0) offset = sizeof(adc_buffer) / 2 - 1; | |||||
| //Serial.printf("offset = %4d, val = %4d\n", offset + 1, adc_buffer[offset]); | |||||
| // copy adc buffer to capture buffer | |||||
| // FIXME: this should be done from the I2S interrupt, for precise capture timing | |||||
| const unsigned int capture_len = sizeof(capture_buffer) / 2; | |||||
| for (unsigned int i=0; i < capture_len; i++) { | |||||
| // TODO: linear interpolate to exactly 4X sample rate | |||||
| if (--p < adc_buffer) p = adc_buffer + (sizeof(adc_buffer) / 2 - 1); | |||||
| // remove DC offset | |||||
| // TODO: very slow low pass filter for DC offset | |||||
| int dc_offset = 550; // FIXME: quick kludge for testing!! | |||||
| int n = (int)*p - dc_offset; | |||||
| if (n > 4095) n = 4095; | |||||
| if (n < -4095) n = -4095; | |||||
| capture_buffer[i] = n; | |||||
| } | |||||
| //printbuf(capture_buffer, 8); | |||||
| // low pass filter and subsample (this part belongs here) | |||||
| int16_t *dest = output->data + AUDIO_BLOCK_SAMPLES - 1; | |||||
| for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | |||||
| #if 1 | |||||
| // proper low-pass filter sounds pretty good | |||||
| *dest-- = fir(capture_buffer + i * 4, filter, sizeof(filter)/2); | |||||
| #else | |||||
| // just averge 4 samples together, lower quality but much faster | |||||
| *dest-- = capture_buffer[i * 4] + capture_buffer[i * 4 + 1] | |||||
| + capture_buffer[i * 4 + 2] + capture_buffer[i * 4 + 3]; | |||||
| #endif | |||||
| } | |||||
| transmit(output); | |||||
| release(output); | |||||
| } | |||||
| #endif |
| AudioInputAnalog() : AudioStream(0, NULL) { init(A2); } | AudioInputAnalog() : AudioStream(0, NULL) { init(A2); } | ||||
| AudioInputAnalog(uint8_t pin) : AudioStream(0, NULL) { init(pin); } | AudioInputAnalog(uint8_t pin) : AudioStream(0, NULL) { init(pin); } | ||||
| virtual void update(void); | virtual void update(void); | ||||
| friend void dma_ch9_isr(void); | |||||
| private: | private: | ||||
| static audio_block_t *block_left; | static audio_block_t *block_left; | ||||
| static uint16_t block_offset; | static uint16_t block_offset; |
| #define COEF_HPF_DCBLOCK (1048300<<10) // DC Removal filter coefficient in S1.30 | #define COEF_HPF_DCBLOCK (1048300<<10) // DC Removal filter coefficient in S1.30 | ||||
| DMAMEM static uint16_t left_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM static uint16_t right_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint16_t left_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint16_t right_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| audio_block_t * AudioInputAnalogStereo::block_left = NULL; | audio_block_t * AudioInputAnalogStereo::block_left = NULL; | ||||
| audio_block_t * AudioInputAnalogStereo::block_right = NULL; | audio_block_t * AudioInputAnalogStereo::block_right = NULL; | ||||
| uint16_t AudioInputAnalogStereo::offset_left = 0; | uint16_t AudioInputAnalogStereo::offset_left = 0; |
| #include "input_i2s.h" | #include "input_i2s.h" | ||||
| #include "output_i2s.h" | #include "output_i2s.h" | ||||
| static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| audio_block_t * AudioInputI2S::block_left = NULL; | audio_block_t * AudioInputI2S::block_left = NULL; | ||||
| audio_block_t * AudioInputI2S::block_right = NULL; | audio_block_t * AudioInputI2S::block_right = NULL; | ||||
| uint16_t AudioInputI2S::block_offset = 0; | uint16_t AudioInputI2S::block_offset = 0; | ||||
| dest_left = &(left->data[offset]); | dest_left = &(left->data[offset]); | ||||
| dest_right = &(right->data[offset]); | dest_right = &(right->data[offset]); | ||||
| AudioInputI2S::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | AudioInputI2S::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | ||||
| #if IMXRT_CACHE_ENABLED >=1 | |||||
| arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2); | |||||
| #endif | |||||
| arm_dcache_delete((void*)src, sizeof(i2s_rx_buffer) / 2); | |||||
| do { | do { | ||||
| *dest_left++ = *src++; | *dest_left++ = *src++; | ||||
| *dest_right++ = *src++; | *dest_right++ = *src++; |
| #include "input_i2s2.h" | #include "input_i2s2.h" | ||||
| #include "output_i2s2.h" | #include "output_i2s2.h" | ||||
| static uint32_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| audio_block_t * AudioInputI2S2::block_left = NULL; | audio_block_t * AudioInputI2S2::block_left = NULL; | ||||
| audio_block_t * AudioInputI2S2::block_right = NULL; | audio_block_t * AudioInputI2S2::block_right = NULL; | ||||
| uint16_t AudioInputI2S2::block_offset = 0; | uint16_t AudioInputI2S2::block_offset = 0; | ||||
| dest_left = &(left->data[offset]); | dest_left = &(left->data[offset]); | ||||
| dest_right = &(right->data[offset]); | dest_right = &(right->data[offset]); | ||||
| AudioInputI2S2::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | AudioInputI2S2::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | ||||
| #if IMXRT_CACHE_ENABLED >=1 | |||||
| arm_dcache_delete(src, sizeof(i2s_rx_buffer) / 2); | |||||
| #endif | |||||
| arm_dcache_delete((void*)src, sizeof(i2s2_rx_buffer) / 2); | |||||
| do { | do { | ||||
| *dest_left++ = *src++; | *dest_left++ = *src++; | ||||
| *dest_right++ = *src++; | *dest_right++ = *src++; |
| 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); | |||||
| arm_dcache_delete((void*)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]); |
| 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); | |||||
| arm_dcache_delete((void*)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]); |
| // but its performance should be *much* better than the rapid passband rolloff | // but its performance should be *much* better than the rapid passband rolloff | ||||
| // of Cascaded Integrator Comb (CIC) or moving average filters. | // of Cascaded Integrator Comb (CIC) or moving average filters. | ||||
| DMAMEM static uint32_t pdm_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t pdm_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||||
| static uint32_t leftover[14]; | static uint32_t leftover[14]; | ||||
| audio_block_t * AudioInputPDM::block_left = NULL; | audio_block_t * AudioInputPDM::block_left = NULL; | ||||
| bool AudioInputPDM::update_responsibility = false; | bool AudioInputPDM::update_responsibility = false; |
| bool AudioOutputADAT::update_responsibility = false; | bool AudioOutputADAT::update_responsibility = false; | ||||
| //uint32_t AudioOutputADAT::vucp = VUCP_VALID; | //uint32_t AudioOutputADAT::vucp = VUCP_VALID; | ||||
| DMAMEM static uint32_t ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128 | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128 | |||||
| DMAChannel AudioOutputADAT::dma(false); | DMAChannel AudioOutputADAT::dma(false); | ||||
| #if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | #if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) | ||||
| DMAMEM static uint16_t dac_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint16_t dac_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
| audio_block_t * AudioOutputAnalog::block_left_1st = NULL; | audio_block_t * AudioOutputAnalog::block_left_1st = NULL; | ||||
| audio_block_t * AudioOutputAnalog::block_left_2nd = NULL; | audio_block_t * AudioOutputAnalog::block_left_2nd = NULL; | ||||
| bool AudioOutputAnalog::update_responsibility = false; | bool AudioOutputAnalog::update_responsibility = false; |
| #if defined(__MK64FX512__) || defined(__MK66FX1M0__) | #if defined(__MK64FX512__) || defined(__MK66FX1M0__) | ||||
| DMAMEM static uint32_t dac_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t dac_buffer[AUDIO_BLOCK_SAMPLES*2]; | |||||
| audio_block_t * AudioOutputAnalogStereo::block_left_1st = NULL; | audio_block_t * AudioOutputAnalogStereo::block_left_1st = NULL; | ||||
| audio_block_t * AudioOutputAnalogStereo::block_left_2nd = NULL; | audio_block_t * AudioOutputAnalogStereo::block_left_2nd = NULL; | ||||
| audio_block_t * AudioOutputAnalogStereo::block_right_1st = NULL; | audio_block_t * AudioOutputAnalogStereo::block_right_1st = NULL; |
| 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->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); | ||||
| dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
| dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); | ||||
| dma.enable(); | dma.enable(); | ||||
| uint16_t AudioOutputPT8211::block_right_offset = 0; | uint16_t AudioOutputPT8211::block_right_offset = 0; | ||||
| bool AudioOutputPT8211::update_responsibility = false; | bool AudioOutputPT8211::update_responsibility = false; | ||||
| #if defined(AUDIO_PT8211_OVERSAMPLING) | #if defined(AUDIO_PT8211_OVERSAMPLING) | ||||
| static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||||
| #else | #else | ||||
| static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| #endif | #endif | ||||
| DMAChannel AudioOutputPT8211::dma(false); | DMAChannel AudioOutputPT8211::dma(false); | ||||
| uint16_t AudioOutputPT8211_2::block_right_offset = 0; | uint16_t AudioOutputPT8211_2::block_right_offset = 0; | ||||
| bool AudioOutputPT8211_2::update_responsibility = false; | bool AudioOutputPT8211_2::update_responsibility = false; | ||||
| #if defined(AUDIO_PT8211_OVERSAMPLING) | #if defined(AUDIO_PT8211_OVERSAMPLING) | ||||
| static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4]; | |||||
| #else | #else | ||||
| static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | |||||
| #endif | #endif | ||||
| DMAChannel AudioOutputPT8211_2::dma(false); | DMAChannel AudioOutputPT8211_2::dma(false); | ||||
| extern const struct _pwm_pin_info_struct pwm_pin_info[]; | extern const struct _pwm_pin_info_struct pwm_pin_info[]; | ||||
| audio_block_t * AudioOutputPWM::block = NULL; | audio_block_t * AudioOutputPWM::block = NULL; | ||||
| DMAMEM __attribute__((aligned(32))) static uint16_t pwm_tx_buffer[2][AUDIO_BLOCK_SAMPLES * 2]; | DMAMEM __attribute__((aligned(32))) static uint16_t pwm_tx_buffer[2][AUDIO_BLOCK_SAMPLES * 2]; | ||||
| DMAChannel AudioOutputPWM::dma[2](false); | |||||
| DMAChannel AudioOutputPWM::dma[2]; | |||||
| _audio_info_flexpwm AudioOutputPWM::apins[2]; | _audio_info_flexpwm AudioOutputPWM::apins[2]; | ||||
| FLASHMEM | FLASHMEM |