#error "The Audio Library only works with Teensy 3.X. Teensy 2.0 is unsupported." | #error "The Audio Library only works with Teensy 3.X. Teensy 2.0 is unsupported." | ||||
#endif | #endif | ||||
#include "DMAChannel.h" | |||||
#ifndef DMACHANNEL_HAS_BEGIN | |||||
#error "You need to update DMAChannel.h & DMAChannel.cpp" | |||||
#error "https://github.com/PaulStoffregen/cores/blob/master/teensy3/DMAChannel.h" | |||||
#error "https://github.com/PaulStoffregen/cores/blob/master/teensy3/DMAChannel.cpp" | |||||
#endif | |||||
// When changing multiple audio object settings that must update at | // When changing multiple audio object settings that must update at | ||||
// the same time, these functions allow the audio library interrupt | // the same time, these functions allow the audio library interrupt | ||||
// to be disabled. For example, you may wish to begin playing a note | // to be disabled. For example, you may wish to begin playing a note |
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; | ||||
bool AudioInputI2S::update_responsibility = false; | bool AudioInputI2S::update_responsibility = false; | ||||
DMAChannel AudioInputI2S::dma; | |||||
void AudioInputI2S::begin(void) | void AudioInputI2S::begin(void) | ||||
{ | { | ||||
dma(); // Allocate the DMA channel first | |||||
dma.begin(true); // Allocate the DMA channel first | |||||
//block_left_1st = NULL; | //block_left_1st = NULL; | ||||
//block_right_1st = NULL; | //block_right_1st = NULL; | ||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 | CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 | ||||
dma().TCD->SADDR = &I2S0_RDR0; | |||||
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_I2S0_RX); | |||||
dma.TCD->SADDR = &I2S0_RDR0; | |||||
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_I2S0_RX); | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma().enable(); | |||||
dma.enable(); | |||||
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); | |||||
} | } | ||||
void AudioInputI2S::isr(void) | void AudioInputI2S::isr(void) | ||||
audio_block_t *left, *right; | audio_block_t *left, *right; | ||||
//digitalWriteFast(3, HIGH); | //digitalWriteFast(3, HIGH); | ||||
daddr = (uint32_t)(dma().TCD->DADDR); | |||||
dma().clearInterrupt(); | |||||
daddr = (uint32_t)(dma.TCD->DADDR); | |||||
dma.clearInterrupt(); | |||||
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 | ||||
void AudioInputI2Sslave::begin(void) | void AudioInputI2Sslave::begin(void) | ||||
{ | { | ||||
dma(); // Allocate the DMA channel first | |||||
dma.begin(true); // Allocate the DMA channel first | |||||
//block_left_1st = NULL; | //block_left_1st = NULL; | ||||
//block_right_1st = NULL; | //block_right_1st = NULL; | ||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 | CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 | ||||
dma().TCD->SADDR = &I2S0_RDR0; | |||||
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_I2S0_RX); | |||||
dma.TCD->SADDR = &I2S0_RDR0; | |||||
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_I2S0_RX); | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma().enable(); | |||||
dma.enable(); | |||||
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); | |||||
} | } | ||||
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; | ||||
static inline DMAChannel &dma() __attribute__((always_inline)) { | |||||
static DMAChannel mydma; | |||||
return mydma; | |||||
} | |||||
static DMAChannel dma; | |||||
static void isr(void); | static void isr(void); | ||||
private: | private: | ||||
static audio_block_t *block_left; | static audio_block_t *block_left; |
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; | ||||
DMAChannel AudioOutputAnalog::dma; | |||||
void AudioOutputAnalog::begin(void) | void AudioOutputAnalog::begin(void) | ||||
{ | { | ||||
dma(); // Allocate the DMA channel first | |||||
dma.begin(true); // Allocate the DMA channel first | |||||
SIM_SCGC2 |= SIM_SCGC2_DAC0; | SIM_SCGC2 |= SIM_SCGC2_DAC0; | ||||
DAC0_C0 = DAC_C0_DACEN; // 1.2V VDDA is DACREF_2 | DAC0_C0 = DAC_C0_DACEN; // 1.2V VDDA is DACREF_2 | ||||
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; | PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; | ||||
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG | PDB_SC_PDBIE | PDB_SC_DMAEN; | PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG | PDB_SC_PDBIE | PDB_SC_DMAEN; | ||||
dma().TCD->SADDR = dac_buffer; | |||||
dma().TCD->SOFF = 2; | |||||
dma().TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||||
dma().TCD->NBYTES_MLNO = 2; | |||||
dma().TCD->SLAST = -sizeof(dac_buffer); | |||||
dma().TCD->DADDR = &DAC0_DAT0L; | |||||
dma().TCD->DOFF = 0; | |||||
dma().TCD->CITER_ELINKNO = sizeof(dac_buffer) / 2; | |||||
dma().TCD->DLASTSGA = 0; | |||||
dma().TCD->BITER_ELINKNO = sizeof(dac_buffer) / 2; | |||||
dma().TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma().triggerAtHardwareEvent(DMAMUX_SOURCE_PDB); | |||||
dma.TCD->SADDR = dac_buffer; | |||||
dma.TCD->SOFF = 2; | |||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||||
dma.TCD->NBYTES_MLNO = 2; | |||||
dma.TCD->SLAST = -sizeof(dac_buffer); | |||||
dma.TCD->DADDR = &DAC0_DAT0L; | |||||
dma.TCD->DOFF = 0; | |||||
dma.TCD->CITER_ELINKNO = sizeof(dac_buffer) / 2; | |||||
dma.TCD->DLASTSGA = 0; | |||||
dma.TCD->BITER_ELINKNO = sizeof(dac_buffer) / 2; | |||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB); | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma().enable(); | |||||
dma().attachInterrupt(isr); | |||||
dma.enable(); | |||||
dma.attachInterrupt(isr); | |||||
} | } | ||||
void AudioOutputAnalog::analogReference(int ref) | void AudioOutputAnalog::analogReference(int ref) | ||||
audio_block_t *block; | audio_block_t *block; | ||||
uint32_t saddr; | uint32_t saddr; | ||||
//saddr = (uint32_t)DMA_TCD4_SADDR; | |||||
//DMA_CINT = 4; | |||||
saddr = (uint32_t)(dma().TCD->SADDR); | |||||
dma().clearInterrupt(); | |||||
saddr = (uint32_t)(dma.TCD->SADDR); | |||||
dma.clearInterrupt(); | |||||
if (saddr < (uint32_t)dac_buffer + sizeof(dac_buffer) / 2) { | if (saddr < (uint32_t)dac_buffer + sizeof(dac_buffer) / 2) { | ||||
// DMA is transmitting the first half of the buffer | // DMA is transmitting the first half of the buffer | ||||
// so we must fill the second half | // so we must fill the second half |
static audio_block_t *block_left_2nd; | static audio_block_t *block_left_2nd; | ||||
static bool update_responsibility; | static bool update_responsibility; | ||||
audio_block_t *inputQueueArray[1]; | audio_block_t *inputQueueArray[1]; | ||||
static inline DMAChannel &dma() __attribute__((always_inline)) { | |||||
static DMAChannel mydma; | |||||
return mydma; | |||||
} | |||||
static DMAChannel dma; | |||||
static void isr(void); | static void isr(void); | ||||
}; | }; | ||||
uint16_t AudioOutputI2S::block_right_offset = 0; | uint16_t AudioOutputI2S::block_right_offset = 0; | ||||
bool AudioOutputI2S::update_responsibility = false; | bool AudioOutputI2S::update_responsibility = false; | ||||
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; | ||||
DMAChannel AudioOutputI2S::dma; | |||||
void AudioOutputI2S::begin(void) | void AudioOutputI2S::begin(void) | ||||
{ | { | ||||
dma(); // Allocate the DMA channel first | |||||
dma.begin(true); // Allocate the DMA channel first | |||||
block_left_1st = NULL; | block_left_1st = NULL; | ||||
block_right_1st = NULL; | block_right_1st = NULL; | ||||
config_i2s(); | 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 | ||||
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_MLNO = 2; | |||||
dma().TCD->SLAST = -sizeof(i2s_tx_buffer); | |||||
dma().TCD->DADDR = &I2S0_TDR0; | |||||
dma().TCD->DOFF = 0; | |||||
dma().TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma().TCD->DLASTSGA = 0; | |||||
dma().TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma().TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma().triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||||
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_MLNO = 2; | |||||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||||
dma.TCD->DADDR = &I2S0_TDR0; | |||||
dma.TCD->DOFF = 0; | |||||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma.TCD->DLASTSGA = 0; | |||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma().enable(); | |||||
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; | ||||
dma().attachInterrupt(isr); | |||||
dma.attachInterrupt(isr); | |||||
} | } | ||||
audio_block_t *block; | audio_block_t *block; | ||||
uint32_t saddr, offset; | uint32_t saddr, offset; | ||||
saddr = (uint32_t)(dma().TCD->SADDR); | |||||
dma().clearInterrupt(); | |||||
saddr = (uint32_t)(dma.TCD->SADDR); | |||||
dma.clearInterrupt(); | |||||
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { | if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { | ||||
// DMA is transmitting the first half of the buffer | // DMA is transmitting the first half of the buffer | ||||
// so we must fill the second half | // so we must fill the second half | ||||
void AudioOutputI2Sslave::begin(void) | void AudioOutputI2Sslave::begin(void) | ||||
{ | { | ||||
dma(); // Allocate the DMA channel first | |||||
dma.begin(true); // Allocate the DMA channel first | |||||
//pinMode(2, OUTPUT); | //pinMode(2, OUTPUT); | ||||
block_left_1st = NULL; | block_left_1st = NULL; | ||||
AudioOutputI2Sslave::config_i2s(); | AudioOutputI2Sslave::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 | ||||
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_MLNO = 2; | |||||
dma().TCD->SLAST = -sizeof(i2s_tx_buffer); | |||||
dma().TCD->DADDR = &I2S0_TDR0; | |||||
dma().TCD->DOFF = 0; | |||||
dma().TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma().TCD->DLASTSGA = 0; | |||||
dma().TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma().TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma().triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||||
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_MLNO = 2; | |||||
dma.TCD->SLAST = -sizeof(i2s_tx_buffer); | |||||
dma.TCD->DADDR = &I2S0_TDR0; | |||||
dma.TCD->DOFF = 0; | |||||
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma.TCD->DLASTSGA = 0; | |||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; | |||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; | |||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); | |||||
update_responsibility = update_setup(); | update_responsibility = update_setup(); | ||||
dma().enable(); | |||||
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; | ||||
dma().attachInterrupt(isr); | |||||
dma.attachInterrupt(isr); | |||||
} | } | ||||
void AudioOutputI2Sslave::config_i2s(void) | void AudioOutputI2Sslave::config_i2s(void) |
static audio_block_t *block_left_1st; | static audio_block_t *block_left_1st; | ||||
static audio_block_t *block_right_1st; | static audio_block_t *block_right_1st; | ||||
static bool update_responsibility; | static bool update_responsibility; | ||||
static inline DMAChannel &dma() __attribute__((always_inline)) { | |||||
static DMAChannel mydma; | |||||
return mydma; | |||||
} | |||||
static DMAChannel dma; | |||||
static void isr(void); | static void isr(void); | ||||
private: | private: | ||||
static audio_block_t *block_left_2nd; | static audio_block_t *block_left_2nd; |