Browse Source

LC input slave

TBD: input as "Master". Do we need this?
dds
Frank Bösing 3 years ago
parent
commit
784879705a
3 changed files with 211 additions and 62 deletions
  1. +133
    -5
      input_i2s.cpp
  2. +15
    -6
      input_i2s.h
  3. +63
    -51
      output_i2s.cpp

+ 133
- 5
input_i2s.cpp View File

*/ */





#include <Arduino.h>
#include "input_i2s.h" #include "input_i2s.h"
#include "output_i2s.h" #include "output_i2s.h"


#if !defined(KINETISL)

DMAMEM __attribute__((aligned(32))) 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;
{ {
dma.begin(true); // Allocate the DMA channel first dma.begin(true); // Allocate the DMA channel first


//block_left_1st = NULL;
//block_right_1st = NULL;

AudioOutputI2Sslave::config_i2s(); AudioOutputI2Sslave::config_i2s();
#if defined(KINETISK) #if defined(KINETISK)
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
#endif #endif
} }


#elif defined(KINETISL)

/**************************************************************************************
* Teensy LC
***************************************************************************************/
#define NUM_SAMPLES (AUDIO_BLOCK_SAMPLES / 2)

DMAMEM static int16_t i2s_rx_buffer1[NUM_SAMPLES * 2];
DMAMEM static int16_t i2s_rx_buffer2[NUM_SAMPLES * 2];
audio_block_t * AudioInputI2S::block_left = NULL;
audio_block_t * AudioInputI2S::block_right = NULL;
DMAChannel AudioInputI2S::dma1(false);
DMAChannel AudioInputI2S::dma2(false);
bool AudioInputI2S::update_responsibility = false;

void AudioInputI2S::begin(void)
{
// TODO
}

void AudioInputI2S::update(void)
{

//Keep it simple
//If we have a block, transmit and release it.
if (block_left) {
transmit(block_left, 0);
release(block_left);
block_left = nullptr;
}

if (block_right) {
transmit(block_right, 1);
release(block_right);
block_right = nullptr;
}

// allocate 2 new blocks, but if one fails, allocate neither
block_left = allocate();
if (block_left != nullptr) {
block_right = allocate();
if (block_right == nullptr) {
release(block_left);
block_left = nullptr;
}
}

}

//todo : ("unroll-loops") or optimize better
inline __attribute__((always_inline, hot, optimize("O2") ))
static void deinterleave(const int16_t *src,audio_block_t *block_left, audio_block_t *block_right, const unsigned offset)
{
//we can assume that we have either two blocks or none

if (!block_left) return;

for (unsigned i=0; i < NUM_SAMPLES; i++) {
block_left->data[i + offset] = src[i*2];
block_right->data[i + offset] = src[i*2+1];
}

}

void AudioInputI2S::isr1(void)
{
//DMA Channel 1 Interrupt

//Start Channel 2:
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
dma2.enable();

//Reset & Copy Data Channel 1
dma1.clearInterrupt();
dma1.destinationBuffer(i2s_rx_buffer1, sizeof(i2s_rx_buffer1));
deinterleave(&i2s_rx_buffer1[0], AudioInputI2S::block_left, AudioInputI2S::block_right, 0);
//Serial.print(".");
}

void AudioInputI2S::isr2(void)
{
//DMA Channel 1 Interrupt

//Start Channel 2:
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
dma1.enable();

//Reset & Copy Data Channel 1
dma2.clearInterrupt();
dma2.destinationBuffer(i2s_rx_buffer2, sizeof(i2s_rx_buffer2));
deinterleave(&i2s_rx_buffer2[0], AudioInputI2S::block_left, AudioInputI2S::block_right, NUM_SAMPLES);
if (AudioInputI2S::update_responsibility) AudioStream::update_all();
//Serial.print("!");
}

void AudioInputI2Sslave::begin(void)
{
memset(i2s_rx_buffer1, 0, sizeof( i2s_rx_buffer1 ) );
memset(i2s_rx_buffer2, 0, sizeof( i2s_rx_buffer2 ) );

dma1.begin(true);
dma2.begin(true);

AudioOutputI2Sslave::config_i2s();
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0

//configure both DMA channels
dma1.CFG->SAR = (void *)((uint32_t)&I2S0_RDR0 + 2);
dma1.CFG->DCR = (dma1.CFG->DCR & 0xF08E0FFF) | DMA_DCR_SSIZE(2);
dma1.destinationBuffer(i2s_rx_buffer1, sizeof(i2s_rx_buffer1));
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
dma1.interruptAtCompletion();
dma1.disableOnCompletion();
dma1.attachInterrupt(isr1);

dma2.CFG->SAR = dma1.CFG->SAR;
dma2.CFG->DCR = dma1.CFG->DCR;
dma2.destinationBuffer(i2s_rx_buffer2, sizeof(i2s_rx_buffer2));
dma2.interruptAtCompletion();
dma2.disableOnCompletion();
dma2.attachInterrupt(isr2);

I2S0_RCSR = 0;
I2S0_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FWDE /*| I2S_RCSR_FR*/;

update_responsibility = update_setup();
dma1.enable();

}

#endif

+ 15
- 6
input_i2s.h View File

#ifndef _input_i2s_h_ #ifndef _input_i2s_h_
#define _input_i2s_h_ #define _input_i2s_h_


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


class AudioInputI2S : public AudioStream class AudioInputI2S : public AudioStream
{ {
protected: protected:
AudioInputI2S(int dummy): AudioStream(0, NULL) {} // to be used only inside AudioInputI2Sslave !! AudioInputI2S(int dummy): AudioStream(0, NULL) {} // to be used only inside AudioInputI2Sslave !!
static bool update_responsibility; static bool update_responsibility;

#if !defined(KINETISL)
static DMAChannel dma; static DMAChannel dma;
static void isr(void); static void isr(void);
#else
static DMAChannel dma1, dma2;
static void isr1(void);
static void isr2(void);
#endif

private: private:
static audio_block_t *block_left; static audio_block_t *block_left;
static audio_block_t *block_right; static audio_block_t *block_right;
#if !defined(KINETISL)
static uint16_t block_offset; static uint16_t block_offset;
#endif
}; };




{ {
public: public:
AudioInputI2Sslave(void) : AudioInputI2S(0) { begin(); } AudioInputI2Sslave(void) : AudioInputI2S(0) { begin(); }
void begin(void);
friend void dma_ch1_isr(void);
void begin(void);
}; };


#endif
#endif

+ 63
- 51
output_i2s.cpp View File

} }
} }


#if defined(KINETISK) || defined(KINETISL)
#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


void AudioOutputI2S::config_i2s(void) void AudioOutputI2S::config_i2s(void)
{ {
#if defined(KINETISK) || defined(KINETISL)
#if defined(KINETISK)
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;
dma1.begin(true); // Allocate the DMA channel first dma1.begin(true); // Allocate the DMA channel first
dma2.begin(true); dma2.begin(true);


SIM_SCGC6 |= SIM_SCGC6_I2S;//Enable I2S periphal

// enable MCLK, 16MHZ
I2S0_MCR = I2S_MCR_MICS(0) | I2S_MCR_MOE;
//MDR is not available on Teensy LC

// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(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);

#if 0 //TODO
// configure receiver (sync'd to transmitter clocks)
I2S0_RMR = 0;
I2S0_RCR1 = I2S_RCR1_RFW(1);
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP;
I2S0_RCR3 = I2S_RCR3_RCE;
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
#endif
// configure pin mux
config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 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 16MHz


//configure both DMA channels //configure both DMA channels
dma1.sourceBuffer(i2s_tx_buffer1, sizeof(i2s_tx_buffer1)); dma1.sourceBuffer(i2s_tx_buffer1, sizeof(i2s_tx_buffer1));


} }


void AudioOutputI2S::config_i2s(void)
{

SIM_SCGC6 |= SIM_SCGC6_I2S;//Enable I2S periphal

// enable MCLK, 16MHZ
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 receiver (sync'd to transmitter clocks)
I2S0_RMR = 0;
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP;
I2S0_RCR3 = I2S_RCR3_RCE;
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(15) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S0_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15);

// configure pin mux
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
//No Mclk here - it would be 16MHz
//CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
}

void AudioOutputI2S::update(void) void AudioOutputI2S::update(void)
{ {
if (!block_left) block_left = receiveReadOnly(0);// input 0 = left channel if (!block_left) block_left = receiveReadOnly(0);// input 0 = left channel
interleave(&i2s_tx_buffer1[0], AudioOutputI2S::block_left, AudioOutputI2S::block_right, 0); interleave(&i2s_tx_buffer1[0], AudioOutputI2S::block_left, AudioOutputI2S::block_right, 0);
} }


void AudioOutputI2S::isr2(void)
void __attribute__((interrupt("IRQ"))) AudioOutputI2S::isr2(void)
{ //DMA Channel 2 Interrupt { //DMA Channel 2 Interrupt


//Start Channel 1: //Start Channel 1:


dma1.begin(true); // Allocate the DMA channels first dma1.begin(true); // Allocate the DMA channels first
dma2.begin(true); dma2.begin(true);

SIM_SCGC6 |= SIM_SCGC6_I2S;//Enable I2S periphal

// enable MCLK, 16MHZ
I2S0_MCR = I2S_MCR_MICS(1) | I2S_MCR_MOE;
//MDR is not available on Teensy LC

// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(0);
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP;
I2S0_TCR3 = I2S_TCR3_TCE;
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

// configure pin mux
config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 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 16MHz

//configure both DMA channels //configure both DMA channels
dma1.sourceBuffer(i2s_tx_buffer1, sizeof(i2s_tx_buffer1)); dma1.sourceBuffer(i2s_tx_buffer1, sizeof(i2s_tx_buffer1));
dma1.CFG->DAR = (void *)((uint32_t)&I2S0_TDR0 + 2); dma1.CFG->DAR = (void *)((uint32_t)&I2S0_TDR0 + 2);
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FWDE; I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FWDE;


} }
void AudioOutputI2Sslave::config_i2s(void)
{
SIM_SCGC6 |= SIM_SCGC6_I2S;//Enable I2S periphal
// enable MCLK, 16MHZ
I2S0_MCR = I2S_MCR_MICS(1) | I2S_MCR_MOE;
//MDR is not available on Teensy LC


// configure transmitter
I2S0_TMR = 0;
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP;
I2S0_TCR3 = I2S_TCR3_TCE;
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);



// configure receiver (sync'd to transmitter clocks)
I2S0_RMR = 0;
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP;
I2S0_RCR3 = I2S_RCR3_RCE;
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP;
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
// configure pin mux
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 !!16MHz!!
}


#endif #endif



Loading…
Cancel
Save