Parcourir la source

Add AudioOutputAnalog object, for audio to Teensy 3.1 DAC

dds
PaulStoffregen il y a 11 ans
Parent
révision
e6f99d8b04
3 fichiers modifiés avec 167 ajouts et 3 suppressions
  1. +142
    -0
      Audio.cpp
  2. +20
    -0
      Audio.h
  3. +5
    -3
      examples/PlayFromSketch/PlayFromSketch.ino

+ 142
- 0
Audio.cpp Voir le fichier

@@ -442,6 +442,8 @@ void dma_ch3_isr(void)





/******************************************************************/


@@ -1014,13 +1016,153 @@ void AudioInputAnalog::update(void)



/******************************************************************/



// #define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT)
// #define PDB_PERIOD 1087 // 48e6 / 44100

#if defined(__MK20DX256__) && defined(DMA_TCD4_SADDR)

DMAMEM static uint16_t dac_buffer[AUDIO_BLOCK_SAMPLES*2];
audio_block_t * AudioOutputAnalog::block_left_1st = NULL;
audio_block_t * AudioOutputAnalog::block_left_2nd = NULL;
bool AudioOutputAnalog::update_responsibility = false;

void AudioOutputAnalog::begin(void)
{
SIM_SCGC2 |= SIM_SCGC2_DAC0;
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 3.3V VDDA is DACREF_2
// slowly ramp up to DC voltage, approx 1/4 second
for (int16_t i=0; i<2047; i++) {
*(int16_t *)&(DAC0_DAT0L) = i;
delayMicroseconds(262);
}

// set the programmable delay block to trigger DMA requests
SIM_SCGC6 |= SIM_SCGC6_PDB;
PDB0_IDLY = 50; // TODO: is this ok?
PDB0_MOD = PDB_PERIOD;
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;
//NVIC_ENABLE_IRQ(IRQ_PDB);
#if 1
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
DMA_CR = 0;
DMA_TCD4_SADDR = dac_buffer;
DMA_TCD4_SOFF = 2;
DMA_TCD4_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
DMA_TCD4_NBYTES_MLNO = 2;
DMA_TCD4_SLAST = -sizeof(dac_buffer);
DMA_TCD4_DADDR = &DAC0_DAT0L;
DMA_TCD4_DOFF = 0;
DMA_TCD4_CITER_ELINKNO = sizeof(dac_buffer) / 2;
DMA_TCD4_DLASTSGA = 0;
DMA_TCD4_BITER_ELINKNO = sizeof(dac_buffer) / 2;
DMA_TCD4_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

DMAMUX0_CHCFG4 = DMAMUX_DISABLE;
DMAMUX0_CHCFG4 = DMAMUX_SOURCE_PDB | DMAMUX_ENABLE;
update_responsibility = update_setup();
DMA_SERQ = 4;

NVIC_ENABLE_IRQ(IRQ_DMA_CH4);
#endif
}

//void pdb_isr(void)
//{
// static uint16_t val=0;
//
// PDB0_SC = PDB_CONFIG | PDB_SC_PDBIE;
// if (val == 0) val = 4095; // testing only, full scale 22.05 kHz output
// else val = 0;
// DAC0_DAT0L = val & 255;
// DAC0_DATH = val >> 8;
//}

void AudioOutputAnalog::update(void)
{
audio_block_t *block;
block = receiveReadOnly(0); // input 0
if (block) {
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = block;
__enable_irq();
} else if (block_left_2nd == NULL) {
block_left_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block;
__enable_irq();
release(tmp);
}
}
}

// TODO: the DAC has much higher bandwidth than the datasheet says
// can we output a 2X oversampled output, for easier filtering?

void dma_ch4_isr(void)
{
const int16_t *src, *end;
int16_t *dest;
audio_block_t *block;
uint32_t saddr, offset;

saddr = (uint32_t)DMA_TCD4_SADDR;
DMA_CINT = 4;
if (saddr < (uint32_t)dac_buffer + sizeof(dac_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int16_t *)&dac_buffer[AUDIO_BLOCK_SAMPLES];
end = (int16_t *)&dac_buffer[AUDIO_BLOCK_SAMPLES*2];
if (AudioOutputAnalog::update_responsibility) AudioStream::update_all();
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (int16_t *)dac_buffer;
end = (int16_t *)&dac_buffer[AUDIO_BLOCK_SAMPLES];
}
block = AudioOutputAnalog::block_left_1st;
if (block) {
src = &block->data[offset];
do {
// TODO: this should probably dither
*dest++ = ((*src++) + 32767) >> 4; // TODO: optimize
} while (dest < end);
AudioStream::release(block);
AudioOutputAnalog::block_left_1st = AudioOutputAnalog::block_left_2nd;
AudioOutputAnalog::block_left_2nd = NULL;
} else {
do {
*dest++ = 2047;
} while (dest < end);
}
Serial.print(".");
}

#else


void AudioOutputAnalog::begin(void)
{
}

void AudioOutputAnalog::update(void)
{
audio_block_t *block;
block = receiveReadOnly(0); // input 0
if (block) release(block);
}

#endif // defined(__MK20DX256__) && defined(DMA_TCD4_SADDR)




+ 20
- 0
Audio.h Voir le fichier

@@ -116,6 +116,26 @@ private:





class AudioOutputAnalog : public AudioStream
{
public:
AudioOutputAnalog(void) : AudioStream(1, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
friend void dma_ch4_isr(void);
private:
static audio_block_t *block_left_1st;
static audio_block_t *block_left_2nd;
static bool update_responsibility;
audio_block_t *inputQueueArray[1];
};





class AudioPrint : public AudioStream
{
public:

+ 5
- 3
examples/PlayFromSketch/PlayFromSketch.ino Voir le fichier

@@ -22,7 +22,8 @@ AudioPlayMemory sound4;
AudioPlayMemory sound5;
AudioMixer4 mix1; // two 4-channel mixers are needed in
AudioMixer4 mix2; // tandem to combine 6 audio sources
AudioOutputI2S dac;
AudioOutputI2S headphones;
AudioOutputAnalog dac; // play to both I2S audio board and on-chip DAC

// Create Audio connections between the components
//
@@ -33,8 +34,9 @@ AudioConnection c4(sound3, 0, mix1, 3);
AudioConnection c5(mix1, 0, mix2, 0); // output of mix1 into 1st input on mix2
AudioConnection c6(sound4, 0, mix2, 1);
AudioConnection c7(sound5, 0, mix2, 2);
AudioConnection c8(mix2, 0, dac, 0);
AudioConnection c9(mix2, 0, dac, 1);
AudioConnection c8(mix2, 0, headphones, 0);
AudioConnection c9(mix2, 0, headphones, 1);
AudioConnection c10(mix2, 0, dac, 0);

// Create an object to control the audio shield.
//

Chargement…
Annuler
Enregistrer