|
|
@@ -26,9 +26,9 @@ |
|
|
|
|
|
|
|
//Adapted to PT8211, Frank Bösing, Ben-Rheinland |
|
|
|
|
|
|
|
|
|
|
|
#include <Arduino.h> |
|
|
|
#include "output_pt8211.h" |
|
|
|
|
|
|
|
#if !defined(KINETISL) |
|
|
|
#include "memcpy_audio.h" |
|
|
|
#include "utility/imxrt_hw.h" |
|
|
|
|
|
|
@@ -48,7 +48,9 @@ DMAChannel AudioOutputPT8211::dma(false); |
|
|
|
|
|
|
|
void AudioOutputPT8211::begin(void) |
|
|
|
{ |
|
|
|
|
|
|
|
memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer)); |
|
|
|
|
|
|
|
dma.begin(true); // Allocate the DMA channel first |
|
|
|
|
|
|
|
block_left_1st = NULL; |
|
|
@@ -78,12 +80,12 @@ void AudioOutputPT8211::begin(void) |
|
|
|
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; |
|
|
|
return; |
|
|
|
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) |
|
|
|
|
|
|
|
|
|
|
|
#if defined(__IMXRT1052__) |
|
|
|
CORE_PIN6_CONFIG = 3; //1:TX_DATA0 |
|
|
|
#elif defined(__IMXRT1062__) |
|
|
|
arm_dcache_flush_delete(i2s_tx_buffer, sizeof(i2s_tx_buffer)); |
|
|
|
CORE_PIN7_CONFIG = 3; //1:TX_DATA0 |
|
|
|
arm_dcache_flush_delete(i2s_tx_buffer, sizeof(i2s_tx_buffer)); |
|
|
|
CORE_PIN7_CONFIG = 3; //1:TX_DATA0 |
|
|
|
#endif |
|
|
|
|
|
|
|
dma.TCD->SADDR = i2s_tx_buffer; |
|
|
@@ -106,7 +108,7 @@ void AudioOutputPT8211::begin(void) |
|
|
|
dma.attachInterrupt(isr); |
|
|
|
dma.enable(); |
|
|
|
return; |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void AudioOutputPT8211::isr(void) |
|
|
@@ -501,7 +503,7 @@ void AudioOutputPT8211::config_i2s(void) |
|
|
|
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) |
|
|
|
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK |
|
|
|
//CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK |
|
|
|
|
|
|
|
|
|
|
|
#elif ( defined(__IMXRT1052__) || defined(__IMXRT1062__) ) |
|
|
|
|
|
|
|
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); |
|
|
@@ -556,9 +558,173 @@ void AudioOutputPT8211::config_i2s(void) |
|
|
|
I2S1_RCR1 = I2S_RCR1_RFW(0); |
|
|
|
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP | I2S_RCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(div); |
|
|
|
I2S1_RCR3 = I2S_RCR3_RCE; |
|
|
|
// I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //TDA1543 |
|
|
|
// I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //TDA1543 |
|
|
|
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(15) | I2S_RCR4_MF /*| I2S_RCR4_FSE*/ | I2S_RCR4_FSP | I2S_RCR4_FSD; //PT8211 |
|
|
|
I2S1_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15); |
|
|
|
|
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
#elif defined(KINETISL) |
|
|
|
/************************************************************************************** |
|
|
|
* Teensy LC |
|
|
|
***************************************************************************************/ |
|
|
|
// added jan 2021, Frank Bösing |
|
|
|
|
|
|
|
audio_block_t * AudioOutputPT8211::block_left = NULL; |
|
|
|
audio_block_t * AudioOutputPT8211::block_right = NULL; |
|
|
|
bool AudioOutputPT8211::update_responsibility = false; |
|
|
|
|
|
|
|
#define NUM_SAMPLES (AUDIO_BLOCK_SAMPLES / 2) |
|
|
|
|
|
|
|
DMAMEM static int16_t i2s_tx_buffer1[NUM_SAMPLES*2]; |
|
|
|
DMAMEM static int16_t i2s_tx_buffer2[NUM_SAMPLES*2]; |
|
|
|
DMAChannel AudioOutputPT8211::dma1(false); |
|
|
|
DMAChannel AudioOutputPT8211::dma2(false); |
|
|
|
|
|
|
|
|
|
|
|
void AudioOutputPT8211::begin(void) |
|
|
|
{ |
|
|
|
|
|
|
|
memset(i2s_tx_buffer1, 0, sizeof( i2s_tx_buffer1 ) ); |
|
|
|
memset(i2s_tx_buffer2, 0, sizeof( i2s_tx_buffer2 ) ); |
|
|
|
|
|
|
|
dma1.begin(true); // Allocate the DMA channel first |
|
|
|
dma2.begin(true); |
|
|
|
|
|
|
|
SIM_SCGC6 |= SIM_SCGC6_I2S;//Enable I2S periphal |
|
|
|
|
|
|
|
// enable MCLK |
|
|
|
I2S0_MCR = I2S_MCR_MICS(0) | I2S_MCR_MOE; |
|
|
|
//MDR is not available on Teensy LC |
|
|
|
|
|
|
|
// configure transmitter |
|
|
|
I2S0_TMR = 0; |
|
|
|
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(16); |
|
|
|
I2S0_TCR3 = I2S_TCR3_TCE; |
|
|
|
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSP | I2S_TCR4_FSD; |
|
|
|
I2S0_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15); |
|
|
|
|
|
|
|
// configure pin mux |
|
|
|
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 |
|
|
|
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 |
|
|
|
|
|
|
|
//configure both DMA channels |
|
|
|
dma1.sourceBuffer(i2s_tx_buffer1, sizeof(i2s_tx_buffer1)); |
|
|
|
dma1.destination(*(int16_t *)&I2S0_TDR0); |
|
|
|
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); |
|
|
|
dma1.interruptAtCompletion(); |
|
|
|
dma1.disableOnCompletion(); |
|
|
|
dma1.attachInterrupt(isr1); |
|
|
|
|
|
|
|
dma2.destination(*(int16_t *)&I2S0_TDR0); |
|
|
|
dma2.sourceBuffer(i2s_tx_buffer2, sizeof(i2s_tx_buffer2)); |
|
|
|
dma2.interruptAtCompletion(); |
|
|
|
dma2.disableOnCompletion(); |
|
|
|
dma2.attachInterrupt(isr2); |
|
|
|
|
|
|
|
update_responsibility = update_setup(); |
|
|
|
dma1.enable(); |
|
|
|
|
|
|
|
I2S0_TCSR = I2S_TCSR_SR; |
|
|
|
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FWDE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void AudioOutputPT8211::update(void) |
|
|
|
{ |
|
|
|
if (!block_left) block_left = receiveReadOnly(0);// input 0 = left channel |
|
|
|
if (!block_right) block_right = receiveReadOnly(1);// input 1 = right channel |
|
|
|
} |
|
|
|
|
|
|
|
inline __attribute__((always_inline, hot)) |
|
|
|
void interleave(const int16_t *dest,const audio_block_t *block_left, const audio_block_t *block_right, const size_t offset) |
|
|
|
{ |
|
|
|
|
|
|
|
uint32_t *p = (uint32_t*)dest; |
|
|
|
uint32_t *end = p + NUM_SAMPLES; |
|
|
|
|
|
|
|
if (block_left != nullptr && block_right != nullptr) { |
|
|
|
uint16_t *l = (uint16_t*)&block_left->data[offset]; |
|
|
|
uint16_t *r = (uint16_t*)&block_right->data[offset]; |
|
|
|
do { |
|
|
|
*p++ = (((uint32_t)(*l++)) << 16) | (uint32_t)(*r++); |
|
|
|
*p++ = (((uint32_t)(*l++)) << 16) | (uint32_t)(*r++); |
|
|
|
*p++ = (((uint32_t)(*l++)) << 16) | (uint32_t)(*r++); |
|
|
|
*p++ = (((uint32_t)(*l++)) << 16) | (uint32_t)(*r++); |
|
|
|
} while (p < end); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (block_left != nullptr) { |
|
|
|
uint16_t *l = (uint16_t*)&block_left->data[offset]; |
|
|
|
do { |
|
|
|
*p++ = (uint32_t)(*l++) << 16; |
|
|
|
*p++ = (uint32_t)(*l++) << 16; |
|
|
|
*p++ = (uint32_t)(*l++) << 16; |
|
|
|
*p++ = (uint32_t)(*l++) << 16; |
|
|
|
} while (p < end); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (block_right != nullptr) { |
|
|
|
uint16_t *r = (uint16_t*)&block_right->data[offset]; |
|
|
|
do { |
|
|
|
*p++ =(uint32_t)(*r++); |
|
|
|
*p++ =(uint32_t)(*r++); |
|
|
|
*p++ =(uint32_t)(*r++); |
|
|
|
*p++ =(uint32_t)(*r++); |
|
|
|
} while (p < end); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
do { |
|
|
|
*p++ = 0; |
|
|
|
*p++ = 0; |
|
|
|
} while (p < end); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void AudioOutputPT8211::isr1(void) |
|
|
|
{ //DMA Channel 1 Interrupt |
|
|
|
|
|
|
|
//Start Channel 2: |
|
|
|
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); |
|
|
|
dma2.enable(); |
|
|
|
|
|
|
|
//Reset & Copy Data Channel 1 |
|
|
|
dma1.clearInterrupt(); |
|
|
|
dma1.sourceBuffer(i2s_tx_buffer1, sizeof(i2s_tx_buffer1)); |
|
|
|
interleave(&i2s_tx_buffer1[0], AudioOutputPT8211::block_left, AudioOutputPT8211::block_right, 0); |
|
|
|
} |
|
|
|
|
|
|
|
void AudioOutputPT8211::isr2(void) |
|
|
|
{ //DMA Channel 2 Interrupt |
|
|
|
|
|
|
|
//Start Channel 1: |
|
|
|
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); |
|
|
|
dma1.enable(); |
|
|
|
|
|
|
|
//Reset & Copy Data Channel 2 |
|
|
|
dma2.clearInterrupt(); |
|
|
|
dma2.sourceBuffer(i2s_tx_buffer2, sizeof(i2s_tx_buffer2)); |
|
|
|
|
|
|
|
audio_block_t *block_left = AudioOutputPT8211::block_left; |
|
|
|
audio_block_t *block_right = AudioOutputPT8211::block_right; |
|
|
|
|
|
|
|
interleave(&i2s_tx_buffer2[0], block_left, block_right, NUM_SAMPLES); |
|
|
|
|
|
|
|
if (block_left) AudioStream::release(block_left); |
|
|
|
if (block_right) AudioStream::release(block_right); |
|
|
|
|
|
|
|
AudioOutputPT8211::block_left = nullptr; |
|
|
|
AudioOutputPT8211::block_right = nullptr; |
|
|
|
|
|
|
|
if (AudioOutputPT8211::update_responsibility) AudioStream::update_all(); |
|
|
|
} |
|
|
|
|
|
|
|
#else |
|
|
|
//#error Output PT8211: No code for this CPU |
|
|
|
#endif |