Bläddra i källkod

Move input & output objects to their own files

dds
PaulStoffregen 10 år sedan
förälder
incheckning
2bb856fa95
6 ändrade filer med 1004 tillägg och 1011 borttagningar
  1. +0
    -1008
      Audio.cpp
  2. +5
    -3
      Audio.h
  3. +192
    -0
      adc.cpp
  4. +144
    -0
      dac.cpp
  5. +497
    -0
      i2s.cpp
  6. +166
    -0
      pwm.cpp

+ 0
- 1008
Audio.cpp
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 5
- 3
Audio.h Visa fil

@@ -266,10 +266,12 @@ private:



// Multiple input & output objects use the Programmable Delay Block
// to set their sample rate. They must all configure the same
// period to avoid chaos.




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




+ 192
- 0
adc.cpp Visa fil

@@ -0,0 +1,192 @@
#include "Audio.h"
#include "arm_math.h"



DMAMEM static uint16_t analog_rx_buffer[AUDIO_BLOCK_SAMPLES];
audio_block_t * AudioInputAnalog::block_left = NULL;
uint16_t AudioInputAnalog::block_offset = 0;
bool AudioInputAnalog::update_responsibility = false;

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

void AudioInputAnalog::begin(unsigned int pin)
{
uint32_t i, sum=0;

// pin must be 0 to 13 (for A0 to A13)
// or 14 to 23 for digital pin numbers A0-A9
// or 34 to 37 corresponding to A10-A13
if (pin > 23 && !(pin >= 34 && pin <= 37)) return;

//pinMode(2, OUTPUT);
//pinMode(3, OUTPUT);
//digitalWriteFast(3, HIGH);
//delayMicroseconds(500);
//digitalWriteFast(3, LOW);

// Configure the ADC and run at least one software-triggered
// conversion. This completes the self calibration stuff and
// leaves the ADC in a state that's mostly ready to use
analogReadRes(16);
analogReference(INTERNAL); // range 0 to 1.2 volts
//analogReference(DEFAULT); // range 0 to 3.3 volts
analogReadAveraging(8);
// Actually, do many normal reads, to start with a nice DC level
for (i=0; i < 1024; i++) {
sum += analogRead(pin);
}
dc_average = sum >> 10;

// testing only, enable adc interrupt
//ADC0_SC1A |= ADC_SC1_AIEN;
//while ((ADC0_SC1A & ADC_SC1_COCO) == 0) ; // wait
//NVIC_ENABLE_IRQ(IRQ_ADC0);

// set the programmable delay block to trigger the ADC at 44.1 kHz
SIM_SCGC6 |= SIM_SCGC6_PDB;
PDB0_MOD = PDB_PERIOD;
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK;
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG;
PDB0_CH0C1 = 0x0101;

// enable the ADC for hardware trigger and DMA
ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN;

// set up a DMA channel to store the ADC data
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
DMA_CR = 0;
DMA_TCD2_SADDR = &ADC0_RA;
DMA_TCD2_SOFF = 0;
DMA_TCD2_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
DMA_TCD2_NBYTES_MLNO = 2;
DMA_TCD2_SLAST = 0;
DMA_TCD2_DADDR = analog_rx_buffer;
DMA_TCD2_DOFF = 2;
DMA_TCD2_CITER_ELINKNO = sizeof(analog_rx_buffer) / 2;
DMA_TCD2_DLASTSGA = -sizeof(analog_rx_buffer);
DMA_TCD2_BITER_ELINKNO = sizeof(analog_rx_buffer) / 2;
DMA_TCD2_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
DMAMUX0_CHCFG2 = DMAMUX_DISABLE;
DMAMUX0_CHCFG2 = DMAMUX_SOURCE_ADC0 | DMAMUX_ENABLE;
update_responsibility = update_setup();
DMA_SERQ = 2;
NVIC_ENABLE_IRQ(IRQ_DMA_CH2);
}

void dma_ch2_isr(void)
{
uint32_t daddr, offset;
const uint16_t *src, *end;
uint16_t *dest_left;
audio_block_t *left;

//digitalWriteFast(3, HIGH);
daddr = (uint32_t)DMA_TCD2_DADDR;
DMA_CINT = 2;

if (daddr < (uint32_t)analog_rx_buffer + sizeof(analog_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES];
if (AudioInputAnalog::update_responsibility) AudioStream::update_all();
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (uint16_t *)&analog_rx_buffer[0];
end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
}
left = AudioInputAnalog::block_left;
if (left != NULL) {
offset = AudioInputAnalog::block_offset;
if (offset > AUDIO_BLOCK_SAMPLES/2) offset = AUDIO_BLOCK_SAMPLES/2;
//if (offset <= AUDIO_BLOCK_SAMPLES/2) {
dest_left = (uint16_t *)&(left->data[offset]);
AudioInputAnalog::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
do {
*dest_left++ = *src++;
} while (src < end);
//}
}
//digitalWriteFast(3, LOW);
}


#if 0
void adc0_isr(void)
{
uint32_t tmp = ADC0_RA; // read ADC result to clear interrupt
digitalWriteFast(3, HIGH);
delayMicroseconds(1);
digitalWriteFast(3, LOW);
}
#endif


void AudioInputAnalog::update(void)
{
audio_block_t *new_left=NULL, *out_left=NULL;
unsigned int dc, offset;
int16_t s, *p, *end;

// allocate new block (ok if NULL)
new_left = allocate();

__disable_irq();
offset = block_offset;
if (offset < AUDIO_BLOCK_SAMPLES) {
// the DMA didn't fill a block
if (new_left != NULL) {
// but we allocated a block
if (block_left == NULL) {
// the DMA doesn't have any blocks to fill, so
// give it the one we just allocated
block_left = new_left;
block_offset = 0;
__enable_irq();
//Serial.println("fail1");
} else {
// the DMA already has blocks, doesn't need this
__enable_irq();
release(new_left);
//Serial.print("fail2, offset=");
//Serial.println(offset);
}
} else {
// The DMA didn't fill a block, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
//Serial.println("fail3");
}
return;
}
// the DMA filled a block, so grab it and get the
// new block to the DMA, as quickly as possible
out_left = block_left;
block_left = new_left;
block_offset = 0;
__enable_irq();

// find and subtract DC offset....
// TODO: this may not be correct, needs testing with more types of signals
dc = dc_average;
p = out_left->data;
end = p + AUDIO_BLOCK_SAMPLES;
do {
s = (uint16_t)(*p) - dc; // TODO: should be saturating subtract
*p++ = s;
dc += s >> 13; // approx 5.38 Hz high pass filter
} while (p < end);
dc_average = dc;

// then transmit the AC data
transmit(out_left);
release(out_left);
}




+ 144
- 0
dac.cpp Visa fil

@@ -0,0 +1,144 @@
#include "Audio.h"
#include "arm_math.h"



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

#if defined(__MK20DX256__)

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<128; i++) {
analogWrite(A14, i);
delay(2);
}

// set the programmable delay block to trigger DMA requests
SIM_SCGC6 |= SIM_SCGC6_PDB;
PDB0_IDLY = 1;
PDB0_MOD = PDB_PERIOD;
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK;
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG | PDB_SC_PDBIE | PDB_SC_DMAEN;
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);
}

void AudioOutputAnalog::analogReference(int ref)
{
// TODO: this should ramp gradually to the new DC level
if (ref == INTERNAL) {
DAC0_C0 &= ~DAC_C0_DACRFS; // 1.2V
} else {
DAC0_C0 |= DAC_C0_DACRFS; // 3.3V
}
}


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;

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];
} 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;
do {
// TODO: this should probably dither
*dest++ = ((*src++) + 32767) >> 4;
} 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);
}
if (AudioOutputAnalog::update_responsibility) AudioStream::update_all();
}

#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__)




+ 497
- 0
i2s.cpp Visa fil

@@ -0,0 +1,497 @@
#include "Audio.h"
#include "arm_math.h"


// MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate
// Possible to create using fractional divider for all USB-compatible Kinetis:
// MCLK = 16e6 * 12 / 17
// MCLK = 24e6 * 8 / 17
// MCLK = 48e6 * 4 / 17
// MCLK = 72e6 * 8 / 51
// MCLK = 96e6 * 2 / 17
// MCLK = 120e6 * 8 / 85

// TODO: instigate using I2S0_MCR to select the crystal directly instead of the system
// clock, which has audio band jitter from the PLL



audio_block_t * AudioOutputI2S::block_left_1st = NULL;
audio_block_t * AudioOutputI2S::block_right_1st = NULL;
audio_block_t * AudioOutputI2S::block_left_2nd = NULL;
audio_block_t * AudioOutputI2S::block_right_2nd = NULL;
uint16_t AudioOutputI2S::block_left_offset = 0;
uint16_t AudioOutputI2S::block_right_offset = 0;
bool AudioOutputI2S::update_responsibility = false;
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];

void AudioOutputI2S::begin(void)
{
//pinMode(2, OUTPUT);
block_left_1st = NULL;
block_right_1st = NULL;

config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

DMA_CR = 0;
DMA_TCD0_SADDR = i2s_tx_buffer;
DMA_TCD0_SOFF = 2;
DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
DMA_TCD0_NBYTES_MLNO = 2;
DMA_TCD0_SLAST = -sizeof(i2s_tx_buffer);
DMA_TCD0_DADDR = &I2S0_TDR0;
DMA_TCD0_DOFF = 0;
DMA_TCD0_CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
DMA_TCD0_DLASTSGA = 0;
DMA_TCD0_BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
DMA_TCD0_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

DMAMUX0_CHCFG0 = DMAMUX_DISABLE;
DMAMUX0_CHCFG0 = DMAMUX_SOURCE_I2S0_TX | DMAMUX_ENABLE;
update_responsibility = update_setup();
DMA_SERQ = 0;

I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
NVIC_ENABLE_IRQ(IRQ_DMA_CH0);
}




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

saddr = (uint32_t)DMA_TCD0_SADDR;
DMA_CINT = 0;
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
end = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
if (AudioOutputI2S::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 *)i2s_tx_buffer;
end = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
}

// TODO: these copy routines could be merged and optimized, maybe in assembly?
block = AudioOutputI2S::block_left_1st;
if (block) {
offset = AudioOutputI2S::block_left_offset;
src = &block->data[offset];
do {
*dest = *src++;
dest += 2;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S::block_left_offset = offset;
} else {
AudioOutputI2S::block_left_offset = 0;
AudioStream::release(block);
AudioOutputI2S::block_left_1st = AudioOutputI2S::block_left_2nd;
AudioOutputI2S::block_left_2nd = NULL;
}
} else {
do {
*dest = 0;
dest += 2;
} while (dest < end);
}
dest -= AUDIO_BLOCK_SAMPLES - 1;
block = AudioOutputI2S::block_right_1st;
if (block) {
offset = AudioOutputI2S::block_right_offset;
src = &block->data[offset];
do {
*dest = *src++;
dest += 2;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S::block_right_offset = offset;
} else {
AudioOutputI2S::block_right_offset = 0;
AudioStream::release(block);
AudioOutputI2S::block_right_1st = AudioOutputI2S::block_right_2nd;
AudioOutputI2S::block_right_2nd = NULL;
}
} else {
do {
*dest = 0;
dest += 2;
} while (dest < end);
}
}




void AudioOutputI2S::update(void)
{
// null audio device: discard all incoming data
//if (!active) return;
//audio_block_t *block = receiveReadOnly();
//if (block) release(block);

audio_block_t *block;
block = receiveReadOnly(0); // input 0 = left channel
if (block) {
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = block;
block_left_offset = 0;
__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;
block_left_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // input 1 = right channel
if (block) {
__disable_irq();
if (block_right_1st == NULL) {
block_right_1st = block;
block_right_offset = 0;
__enable_irq();
} else if (block_right_2nd == NULL) {
block_right_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block;
block_right_offset = 0;
__enable_irq();
release(tmp);
}
}
}



void AudioOutputI2S::config_i2s(void)
{
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;

// enable MCLK output
I2S0_MCR = I2S_MCR_MICS(3) | I2S_MCR_MOE;
I2S0_MDR = I2S_MDR_FRACT(1) | I2S_MDR_DIVIDE(16);

// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(1); // watermark at half fifo size
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
| I2S_TCR2_BCD | I2S_TCR2_DIV(3);
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_RCR1 = I2S_RCR1_RFW(1);
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV(3);
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 for 3 clock signals
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
}



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

void AudioOutputI2Sslave::begin(void)
{
//pinMode(2, OUTPUT);
block_left_1st = NULL;
block_right_1st = NULL;

AudioOutputI2Sslave::config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

DMA_CR = 0;
DMA_TCD0_SADDR = i2s_tx_buffer;
DMA_TCD0_SOFF = 2;
DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
DMA_TCD0_NBYTES_MLNO = 2;
DMA_TCD0_SLAST = -sizeof(i2s_tx_buffer);
DMA_TCD0_DADDR = &I2S0_TDR0;
DMA_TCD0_DOFF = 0;
DMA_TCD0_CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
DMA_TCD0_DLASTSGA = 0;
DMA_TCD0_BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
DMA_TCD0_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

DMAMUX0_CHCFG0 = DMAMUX_DISABLE;
DMAMUX0_CHCFG0 = DMAMUX_SOURCE_I2S0_TX | DMAMUX_ENABLE;
update_responsibility = update_setup();
DMA_SERQ = 0;

I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
NVIC_ENABLE_IRQ(IRQ_DMA_CH0);
}

void AudioOutputI2Sslave::config_i2s(void)
{
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;

// Select input clock 0
// Configure to input the bit-clock from pin, bypasses the MCLK divider
I2S0_MCR = I2S_MCR_MICS(0);
I2S0_MDR = 0;

// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(1); // watermark at half fifo size
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP;

I2S0_TCR3 = I2S_TCR3_TCE;
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP;

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_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(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 for 3 clock signals
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
}

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


DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];
audio_block_t * AudioInputI2S::block_left = NULL;
audio_block_t * AudioInputI2S::block_right = NULL;
uint16_t AudioInputI2S::block_offset = 0;
bool AudioInputI2S::update_responsibility = false;


void AudioInputI2S::begin(void)
{
//block_left_1st = NULL;
//block_right_1st = NULL;

//pinMode(3, OUTPUT);
//digitalWriteFast(3, HIGH);
//delayMicroseconds(500);
//digitalWriteFast(3, LOW);

AudioOutputI2S::config_i2s();

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

DMA_CR = 0;
DMA_TCD1_SADDR = &I2S0_RDR0;
DMA_TCD1_SOFF = 0;
DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
DMA_TCD1_NBYTES_MLNO = 2;
DMA_TCD1_SLAST = 0;
DMA_TCD1_DADDR = i2s_rx_buffer;
DMA_TCD1_DOFF = 2;
DMA_TCD1_CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
DMA_TCD1_DLASTSGA = -sizeof(i2s_rx_buffer);
DMA_TCD1_BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
DMA_TCD1_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

DMAMUX0_CHCFG1 = DMAMUX_DISABLE;
DMAMUX0_CHCFG1 = DMAMUX_SOURCE_I2S0_RX | DMAMUX_ENABLE;
update_responsibility = update_setup();
DMA_SERQ = 1;

// TODO: is I2S_RCSR_BCE appropriate if sync'd to transmitter clock?
//I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_FRDE | I2S_RCSR_FR;
NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
}

void dma_ch1_isr(void)
{
uint32_t daddr, offset;
const int16_t *src, *end;
int16_t *dest_left, *dest_right;
audio_block_t *left, *right;

//digitalWriteFast(3, HIGH);
daddr = (uint32_t)DMA_TCD1_DADDR;
DMA_CINT = 1;

if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];
if (AudioInputI2S::update_responsibility) AudioStream::update_all();
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int16_t *)&i2s_rx_buffer[0];
end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
}
left = AudioInputI2S::block_left;
right = AudioInputI2S::block_right;
if (left != NULL && right != NULL) {
offset = AudioInputI2S::block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES/2) {
dest_left = &(left->data[offset]);
dest_right = &(right->data[offset]);
AudioInputI2S::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
do {
//n = *src++;
//*dest_left++ = (int16_t)n;
//*dest_right++ = (int16_t)(n >> 16);
*dest_left++ = *src++;
*dest_right++ = *src++;
} while (src < end);
}
}
//digitalWriteFast(3, LOW);
}



void AudioInputI2S::update(void)
{
audio_block_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL;

// allocate 2 new blocks, but if one fails, allocate neither
new_left = allocate();
if (new_left != NULL) {
new_right = allocate();
if (new_right == NULL) {
release(new_left);
new_left = NULL;
}
}
__disable_irq();
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
// the DMA filled 2 blocks, so grab them and get the
// 2 new blocks to the DMA, as quickly as possible
out_left = block_left;
block_left = new_left;
out_right = block_right;
block_right = new_right;
block_offset = 0;
__enable_irq();
// then transmit the DMA's former blocks
transmit(out_left, 0);
release(out_left);
transmit(out_right, 1);
release(out_right);
//Serial.print(".");
} else if (new_left != NULL) {
// the DMA didn't fill blocks, but we allocated blocks
if (block_left == NULL) {
// the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated
block_left = new_left;
block_right = new_right;
block_offset = 0;
__enable_irq();
} else {
// the DMA already has blocks, doesn't need these
__enable_irq();
release(new_left);
release(new_right);
}
} else {
// The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
}
}


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


void AudioInputI2Sslave::begin(void)
{
//block_left_1st = NULL;
//block_right_1st = NULL;

//pinMode(3, OUTPUT);
//digitalWriteFast(3, HIGH);
//delayMicroseconds(500);
//digitalWriteFast(3, LOW);

AudioOutputI2Sslave::config_i2s();

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

DMA_CR = 0;
DMA_TCD1_SADDR = &I2S0_RDR0;
DMA_TCD1_SOFF = 0;
DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
DMA_TCD1_NBYTES_MLNO = 2;
DMA_TCD1_SLAST = 0;
DMA_TCD1_DADDR = i2s_rx_buffer;
DMA_TCD1_DOFF = 2;
DMA_TCD1_CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
DMA_TCD1_DLASTSGA = -sizeof(i2s_rx_buffer);
DMA_TCD1_BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
DMA_TCD1_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;

DMAMUX0_CHCFG1 = DMAMUX_DISABLE;
DMAMUX0_CHCFG1 = DMAMUX_SOURCE_I2S0_RX | DMAMUX_ENABLE;
update_responsibility = update_setup();
DMA_SERQ = 1;

// TODO: is I2S_RCSR_BCE appropriate if sync'd to transmitter clock?
//I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_FRDE | I2S_RCSR_FR;
NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
}



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




+ 166
- 0
pwm.cpp Visa fil

@@ -0,0 +1,166 @@
#include "Audio.h"
#include "arm_math.h"



audio_block_t * AudioOutputPWM::block_1st = NULL;
audio_block_t * AudioOutputPWM::block_2nd = NULL;
uint32_t AudioOutputPWM::block_offset = 0;
bool AudioOutputPWM::update_responsibility = false;
uint8_t AudioOutputPWM::interrupt_count = 0;

DMAMEM uint32_t pwm_dma_buffer[AUDIO_BLOCK_SAMPLES*2];

void AudioOutputPWM::begin(void)
{
//Serial.println("AudioPwmOutput constructor");
block_1st = NULL;
FTM1_SC = 0;
FTM1_CNT = 0;
FTM1_MOD = 543;
FTM1_C0SC = 0x69; // send DMA request on match
FTM1_C1SC = 0x28;
FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
CORE_PIN3_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN4_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
FTM1_C0V = 120; // range 120 to 375
FTM1_C1V = 0; // range 0 to 255
for (int i=0; i<256; i+=2) {
pwm_dma_buffer[i] = 120; // zero must not be used
pwm_dma_buffer[i+1] = 0;
}
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
DMA_CR = 0;
DMA_TCD3_SADDR = pwm_dma_buffer;
DMA_TCD3_SOFF = 4;
DMA_TCD3_ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_DMOD(4);
DMA_TCD3_NBYTES_MLNO = 8;
DMA_TCD3_SLAST = -sizeof(pwm_dma_buffer);
DMA_TCD3_DADDR = &FTM1_C0V;
DMA_TCD3_DOFF = 8;
DMA_TCD3_CITER_ELINKNO = sizeof(pwm_dma_buffer) / 8;
DMA_TCD3_DLASTSGA = 0;
DMA_TCD3_BITER_ELINKNO = sizeof(pwm_dma_buffer) / 8;
DMA_TCD3_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
DMAMUX0_CHCFG3 = DMAMUX_DISABLE;
DMAMUX0_CHCFG3 = DMAMUX_SOURCE_FTM1_CH0 | DMAMUX_ENABLE;
DMA_SERQ = 3;
update_responsibility = update_setup();
NVIC_ENABLE_IRQ(IRQ_DMA_CH3);
}

void AudioOutputPWM::update(void)
{
audio_block_t *block;
block = receiveReadOnly();
if (!block) return;
__disable_irq();
if (block_1st == NULL) {
block_1st = block;
block_offset = 0;
__enable_irq();
} else if (block_2nd == NULL) {
block_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_1st;
block_1st = block_2nd;
block_2nd = block;
block_offset = 0;
__enable_irq();
release(tmp);
}
}

void dma_ch3_isr(void)
{
int16_t *src;
uint32_t *dest;
audio_block_t *block;
uint32_t saddr, offset;

saddr = (uint32_t)DMA_TCD3_SADDR;
DMA_CINT = 3;
if (saddr < (uint32_t)pwm_dma_buffer + sizeof(pwm_dma_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = &pwm_dma_buffer[AUDIO_BLOCK_SAMPLES];
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = pwm_dma_buffer;
}
block = AudioOutputPWM::block_1st;
offset = AudioOutputPWM::block_offset;

if (block) {
src = &block->data[offset];
for (int i=0; i < AUDIO_BLOCK_SAMPLES/4; i++) {
uint16_t sample = *src++ + 0x8000;
uint32_t msb = ((sample >> 8) & 255) + 120;
uint32_t lsb = sample & 255;
*dest++ = msb;
*dest++ = lsb;
*dest++ = msb;
*dest++ = lsb;
}
offset += AUDIO_BLOCK_SAMPLES/4;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputPWM::block_offset = offset;
} else {
AudioOutputPWM::block_offset = 0;
AudioStream::release(block);
AudioOutputPWM::block_1st = AudioOutputPWM::block_2nd;
AudioOutputPWM::block_2nd = NULL;
}
} else {
// fill with silence when no data available
for (int i=0; i < AUDIO_BLOCK_SAMPLES/4; i++) {
*dest++ = 248;
*dest++ = 0;
*dest++ = 248;
*dest++ = 0;
}
}
if (AudioOutputPWM::update_responsibility) {
if (++AudioOutputPWM::interrupt_count >= 4) {
AudioOutputPWM::interrupt_count = 0;
AudioStream::update_all();
}
}
}




// DMA target is: (registers require 32 bit writes)
// 40039010 Channel 0 Value (FTM1_C0V)
// 40039018 Channel 1 Value (FTM1_C1V)

// TCD:
// source address = buffer address
// source offset = 4 bytes
// attr = no src mod, ssize = 32 bit, dest mod = 16 bytes (4), dsize = 32 bit
// minor loop byte count = 8
// source last adjust = -sizeof(buffer)
// dest address = FTM1_C0V
// dest address offset = 8
// citer = sizeof(buffer) / 8 (no minor loop linking)
// dest last adjust = 0 (dest modulo keeps it ready for more)
// control:
// throttling = 0
// major link to same channel
// done = 0
// active = 0
// majorlink = 1
// scatter/gather = 0
// disable request = 0
// inthalf = 1
// intmajor = 1
// start = 0
// biter = sizeof(buffer) / 8 (no minor loop linking)





Laddar…
Avbryt
Spara