Browse Source

Merge pull request #374 from FrankBoesing/TeensyLC-PT8211

Add Teensy LC Code for PT8211
dds
Paul Stoffregen 3 years ago
parent
commit
f69d9febec
No account linked to committer's email address
2 changed files with 204 additions and 12 deletions
  1. +174
    -8
      output_pt8211.cpp
  2. +30
    -4
      output_pt8211.h

+ 174
- 8
output_pt8211.cpp View File



//Adapted to PT8211, Frank Bösing, Ben-Rheinland //Adapted to PT8211, Frank Bösing, Ben-Rheinland



#include <Arduino.h>
#include "output_pt8211.h" #include "output_pt8211.h"

#if !defined(KINETISL)
#include "memcpy_audio.h" #include "memcpy_audio.h"
#include "utility/imxrt_hw.h" #include "utility/imxrt_hw.h"




void AudioOutputPT8211::begin(void) void AudioOutputPT8211::begin(void)
{ {

memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer)); memset(i2s_tx_buffer, 0, sizeof(i2s_tx_buffer));

dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first


block_left_1st = NULL; block_left_1st = NULL;
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; return;
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) #elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1052__) #if defined(__IMXRT1052__)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0 CORE_PIN6_CONFIG = 3; //1:TX_DATA0
#elif defined(__IMXRT1062__) #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 #endif


dma.TCD->SADDR = i2s_tx_buffer; dma.TCD->SADDR = i2s_tx_buffer;
dma.attachInterrupt(isr); dma.attachInterrupt(isr);
dma.enable(); dma.enable();
return; return;
#endif
#endif
} }


void AudioOutputPT8211::isr(void) void AudioOutputPT8211::isr(void)
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(__IMXRT1052__) || defined(__IMXRT1062__) )


CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
I2S1_RCR1 = I2S_RCR1_RFW(0); 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_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_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_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); I2S1_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15);


#endif #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

+ 30
- 4
output_pt8211.h View File

* THE SOFTWARE. * THE SOFTWARE.
*/ */


//Adapted to PT8211, Frank Bösing.
//I2S adapted to PT8211, Frank Bösing.


#ifndef output_pt8211_h_ #ifndef output_pt8211_h_
#define output_pt8211_h_ #define output_pt8211_h_


#include <Arduino.h>
#include <AudioStream.h>
#include <DMAChannel.h>


#if !defined(KINETISL)

//uncomment to enable oversampling: //uncomment to enable oversampling:
#define AUDIO_PT8211_OVERSAMPLING #define AUDIO_PT8211_OVERSAMPLING
//uncomment ONE of these to define interpolation type for oversampling: //uncomment ONE of these to define interpolation type for oversampling:
// #define AUDIO_PT8211_INTERPOLATION_LINEAR // #define AUDIO_PT8211_INTERPOLATION_LINEAR
#define AUDIO_PT8211_INTERPOLATION_CIC #define AUDIO_PT8211_INTERPOLATION_CIC


#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"


class AudioOutputPT8211 : public AudioStream class AudioOutputPT8211 : public AudioStream
{ {
}; };




#elif defined(KINETISL)
/**************************************************************************************
* Teensy LC
***************************************************************************************/
class AudioOutputPT8211 : public AudioStream
{
public:
AudioOutputPT8211(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
protected:
static audio_block_t *block_left;
static audio_block_t *block_right;
static DMAChannel dma1;
static DMAChannel dma2;
audio_block_t *inputQueueArray[2];
static void isr1(void);
static void isr2(void);
static bool update_responsibility;
};


#endif


#endif #endif

Loading…
Cancel
Save