Frank Bösing 9 years ago
parent
commit
d38e6e1de7
11 changed files with 581 additions and 5 deletions
  1. +1
    -0
      Audio.h
  2. +2
    -0
      analyze_peak.cpp
  3. +13
    -5
      control_sgtl5000.cpp
  4. +3
    -0
      control_sgtl5000.h
  5. +67
    -0
      examples/HardwareTesting/SGTL5000/QuadChannelOutput/QuadChannelOutput.ino
  6. +51
    -0
      gui/index.html
  7. +2
    -0
      keywords.txt
  8. +41
    -0
      memcpy_audio.S
  9. +2
    -0
      memcpy_audio.h
  10. +340
    -0
      output_i2s_quad.cpp
  11. +59
    -0
      output_i2s_quad.h

+ 1
- 0
Audio.h View File

@@ -85,6 +85,7 @@
#include "mixer.h"
#include "output_dac.h"
#include "output_i2s.h"
#include "output_i2s_quad.h"
#include "output_pwm.h"
#include "output_spdif.h"
#include "play_memory.h"

+ 2
- 0
analyze_peak.cpp View File

@@ -42,6 +42,8 @@ void AudioAnalyzePeak::update(void)
max = max_sample;
do {
int16_t d=*p++;
// TODO: can we speed this up with SSUB16 and SEL
// http://www.m4-unleashed.com/parallel-comparison/
if (d<min) min=d;
if (d>max) max=d;
} while (p < end);

+ 13
- 5
control_sgtl5000.cpp View File

@@ -495,10 +495,18 @@
#define DAP_COEF_WR_A2_MSB 0x0138
#define DAP_COEF_WR_A2_LSB 0x013A

#define SGTL5000_I2C_ADDR 0x0A // CTRL_ADR0_CS pin low (normal configuration)
//#define SGTL5000_I2C_ADDR 0x2A // CTRL_ADR0_CS pin high
#define SGTL5000_I2C_ADDR_CS_LOW 0x0A // CTRL_ADR0_CS pin low (normal configuration)
#define SGTL5000_I2C_ADDR_CS_HIGH 0x2A // CTRL_ADR0_CS pin high


void AudioControlSGTL5000::setAddress(uint8_t level)
{
if (level == LOW) {
i2c_addr = SGTL5000_I2C_ADDR_CS_LOW;
} else {
i2c_addr = SGTL5000_I2C_ADDR_CS_HIGH;
}
}

bool AudioControlSGTL5000::enable(void)
{
@@ -536,11 +544,11 @@ bool AudioControlSGTL5000::enable(void)
unsigned int AudioControlSGTL5000::read(unsigned int reg)
{
unsigned int val;
Wire.beginTransmission(SGTL5000_I2C_ADDR);
Wire.beginTransmission(i2c_addr);
Wire.write(reg >> 8);
Wire.write(reg);
if (Wire.endTransmission(false) != 0) return 0;
if (Wire.requestFrom(SGTL5000_I2C_ADDR, 2) < 2) return 0;
if (Wire.requestFrom((int)i2c_addr, 2) < 2) return 0;
val = Wire.read() << 8;
val |= Wire.read();
return val;
@@ -549,7 +557,7 @@ unsigned int AudioControlSGTL5000::read(unsigned int reg)
bool AudioControlSGTL5000::write(unsigned int reg, unsigned int val)
{
if (reg == CHIP_ANA_CTRL) ana_ctrl = val;
Wire.beginTransmission(SGTL5000_I2C_ADDR);
Wire.beginTransmission(i2c_addr);
Wire.write(reg >> 8);
Wire.write(reg);
Wire.write(val >> 8);

+ 3
- 0
control_sgtl5000.h View File

@@ -32,6 +32,8 @@
class AudioControlSGTL5000 : public AudioControl
{
public:
AudioControlSGTL5000(void) : i2c_addr(0x0A) { }
void setAddress(uint8_t level);
bool enable(void);
bool disable(void) { return false; }
bool volume(float n) { return volumeInteger(n * 129 + 0.499); }
@@ -92,6 +94,7 @@ protected:
bool muted;
bool volumeInteger(unsigned int n); // range: 0x00 to 0x80
uint16_t ana_ctrl;
uint8_t i2c_addr;
unsigned char calcVol(float n, unsigned char range);
unsigned int read(unsigned int reg);
bool write(unsigned int reg, unsigned int val);

+ 67
- 0
examples/HardwareTesting/SGTL5000/QuadChannelOutput/QuadChannelOutput.ino View File

@@ -0,0 +1,67 @@
// Quad channel output test
// Play two WAV files on two audio shields.
//
// TODO: add info about required hardware connections here....
//
// Data files to put on your SD card can be downloaded here:
// http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html
//
// This example code is in the public domain.

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioPlaySdWav playSdWav1;
AudioPlaySdWav playSdWav2;
AudioOutputI2SQuad audioOutput;
AudioConnection patchCord1(playSdWav1, 0, audioOutput, 0);
AudioConnection patchCord2(playSdWav1, 1, audioOutput, 1);
AudioConnection patchCord3(playSdWav2, 0, audioOutput, 2);
AudioConnection patchCord4(playSdWav2, 1, audioOutput, 3);
AudioControlSGTL5000 sgtl5000_1;
AudioControlSGTL5000 sgtl5000_2;

// Use these with the audio adaptor board
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14

void setup() {
Serial.begin(9600);
AudioMemory(10);
sgtl5000_1.setAddress(LOW);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);

sgtl5000_2.setAddress(HIGH);
sgtl5000_2.enable();
sgtl5000_2.volume(0.5);

SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here, but print a message repetitively
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
}

void loop() {
if (playSdWav1.isPlaying() == false) {
Serial.println("Start playing 1");
playSdWav1.play("SDTEST2.WAV");
delay(10); // wait for library to parse WAV info
}
if (playSdWav2.isPlaying() == false) {
Serial.println("Start playing 2");
playSdWav2.play("SDTEST4.WAV");
delay(10); // wait for library to parse WAV info
}
}


+ 51
- 0
gui/index.html View File

@@ -348,6 +348,7 @@ span.mainfunction {color: #993300; font-weight: bolder}
{"type":"AudioInputAnalog","data":{"defaults":{"name":{"value":"new"}},"shortName":"adc","inputs":0,"outputs":1,"category":"input-function","color":"#E6E0F8","icon":"arrow-in.png"}},
{"type":"AudioInputI2Sslave","data":{"defaults":{"name":{"value":"new"}},"shortName":"i2ss","inputs":0,"outputs":2,"category":"input-function","color":"#E6E0F8","icon":"arrow-in.png"}},
{"type":"AudioOutputI2S","data":{"defaults":{"name":{"value":"new"}},"shortName":"i2s","inputs":2,"outputs":0,"category":"output-function","color":"#E6E0F8","icon":"arrow-in.png"}},
{"type":"AudioOutputI2SQuad","data":{"defaults":{"name":{"value":"new"}},"shortName":"i2s_quad","inputs":4,"outputs":0,"category":"output-function","color":"#E6E0F8","icon":"arrow-in.png"}},
{"type":"AudioOutputSPDIF","data":{"defaults":{"name":{"value":"new"}},"shortName":"spdif","inputs":2,"outputs":0,"category":"output-function","color":"#E6E0F8","icon":"arrow-in.png"}},
{"type":"AudioOutputAnalog","data":{"defaults":{"name":{"value":"new"}},"shortName":"dac","inputs":1,"outputs":0,"category":"output-function","color":"#E6E0F8","icon":"arrow-in.png"}},
{"type":"AudioOutputPWM","data":{"defaults":{"name":{"value":"new"}},"shortName":"pwm","inputs":1,"outputs":0,"category":"output-function","color":"#E6E0F8","icon":"arrow-in.png"}},
@@ -610,6 +611,56 @@ span.mainfunction {color: #993300; font-weight: bolder}
</div>
</script>

<script type="text/x-red" data-help-name="AudioOutputI2SQuad">
<h3>Summary</h3>
<div class=tooltipinfo>
<p>Transmit quad (4) channel 16 bit audio, using I2S master mode.</p>
</div>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center>In 0</td><td>Channel #1</td></tr>
<tr class=odd><td align=center>In 1</td><td>Channel #2</td></tr>
<tr class=odd><td align=center>In 2</td><td>Channel #3</td></tr>
<tr class=odd><td align=center>In 3</td><td>Channel #4</td></tr>
</table>
<h3>Functions</h3>
<p>This object has no functions to call from the Arduino sketch. It
simply streams data from its 4 input ports to the I2S hardware.</p>
<h3>Hardware</h3>
<p>TODO: <a href="https://forum.pjrc.com/threads/29373-Bit-bang-multiple-I2S-inputs-simultaneously?p=79606#post79606" target="_blank">details</a> for how to connect 2 Teensy audio shields</p>
<p>The I2S signals are used in "master" mode, where Teensy creates
all 3 clock signals and controls all data timing.</p>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Pin</th><th>Signal</th><th>Direction</th></tr>
<tr class=odd><td align=center>9</td><td>BCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>11</td><td>MCLK</td><td>Output</td></tr>
<tr class=odd><td align=center>22</td><td>TX (ch 1+2)</td><td>Output</td></tr>
<tr class=odd><td align=center>15</td><td>TX (ch 3+4)</td><td>Output</td></tr>
<tr class=odd><td align=center>23</td><td>LRCLK</td><td>Output</td></tr>
</table>
<p>Audio from
master mode I2S may be used in the same project as ADC, DAC and
PWM signals, because all remain in sync to Teensy's timing</p>
<h3>Examples</h3>
<p class=exam>File &gt; Examples &gt; Audio &gt; HardwareTesting &gt; SGTL5000 &gt; QuadChannelOutput
</p>
<h3>Notes</h3>
<p>Normally, this object is used with two Audio Shields, which
are controlled separately by a pair of "sgtl5000" objects.</p>
</script>
<script type="text/x-red" data-template-name="AudioOutputI2SQuad">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>






<script type="text/x-red" data-help-name="AudioOutputSPDIF">
<h3>Summary</h3>
<div class=tooltipinfo>

+ 2
- 0
keywords.txt View File

@@ -3,6 +3,7 @@ AudioConnection KEYWORD2
AudioInputI2S KEYWORD2
AudioInputI2Sslave KEYWORD2
AudioOutputI2S KEYWORD2
AudioOutputI2SQuad KEYWORD2
AudioOutputI2Sslave KEYWORD2
AudioOutputSPDIF KEYWORD2
AudioOutputPWM KEYWORD2
@@ -78,6 +79,7 @@ output KEYWORD2
trigger KEYWORD2
length KEYWORD2
threshold KEYWORD2
setAddress KEYWORD2
enable KEYWORD2
enableIn KEYWORD2
enableOut KEYWORD2

+ 41
- 0
memcpy_audio.S View File

@@ -164,6 +164,47 @@
BX lr



/* void memcpy_tointerleaveQuad(int16_t *dst, const int16_t *src1, const int16_t *src2, const int16_t *src3, const int16_t *src4) */
.global memcpy_tointerleaveQuad
.thumb_func
memcpy_tointerleaveQuad:

@ r0: dst
@ r1: src1
@ r2: src2
@ r3: src3
@ r4: src4

push {r4-r11}
ldr r4, [sp, #(0+32)] //4th parameter is saved on the stack
add r11,r0,#512 // TODO: 512 = AUDIO_BLOCK_SAMPLES*4
.align 2
.loopQuad:

.irp offset, 1,2

ldr r5, [r1],4
ldr r6, [r3],4
pkhbt r7,r5,r6,LSL #16
pkhtb r9,r6,r5,ASR #16
ldr r5, [r2],4
ldr r6, [r4],4
pkhbt r8,r5,r6,LSL #16
pkhtb r10,r6,r5,ASR #16

stmia r0!, {r7-r10}

.endr

cmp r11, r0
bne .loopQuad

pop {r4-r11}

BX lr


.END

#endif

+ 2
- 0
memcpy_audio.h View File

@@ -36,6 +36,8 @@ extern "C" {
void memcpy_tointerleaveLR(int16_t *dst, const int16_t *srcL, const int16_t *srcR);
void memcpy_tointerleaveL(int16_t *dst, const int16_t *srcL);
void memcpy_tointerleaveR(int16_t *dst, const int16_t *srcR);
void memcpy_tointerleaveQuad(int16_t *dst, const int16_t *src1, const int16_t *src2,
const int16_t *src3, const int16_t *src4);
#ifdef __cplusplus
}
#endif

+ 340
- 0
output_i2s_quad.cpp View File

@@ -0,0 +1,340 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "output_i2s_quad.h"
#include "memcpy_audio.h"

#if defined(__MK20DX256__)

audio_block_t * AudioOutputI2SQuad::block_ch1_1st = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch2_1st = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch3_1st = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch4_1st = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch1_2nd = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch2_2nd = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch3_2nd = NULL;
audio_block_t * AudioOutputI2SQuad::block_ch4_2nd = NULL;
uint16_t AudioOutputI2SQuad::ch1_offset = 0;
uint16_t AudioOutputI2SQuad::ch2_offset = 0;
uint16_t AudioOutputI2SQuad::ch3_offset = 0;
uint16_t AudioOutputI2SQuad::ch4_offset = 0;
//audio_block_t * AudioOutputI2SQuad::inputQueueArray[4];
bool AudioOutputI2SQuad::update_responsibility = false;
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2];
DMAChannel AudioOutputI2SQuad::dma(false);

static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES/4] = {0};

void AudioOutputI2SQuad::begin(void)
{
#if 1
dma.begin(true); // Allocate the DMA channel first

block_ch1_1st = NULL;
block_ch2_1st = NULL;
block_ch3_1st = NULL;
block_ch4_1st = NULL;

// TODO: can we call normal config_i2s, and then just enable the extra output?
config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 -> ch1 & ch2
CORE_PIN15_CONFIG = PORT_PCR_MUX(6); // pin 15, PTC0, I2S0_TXD1 -> ch3 & ch4

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_ATTR_DMOD(3);
dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = &I2S0_TDR0;
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 4;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.enable();

I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
#endif
}

void AudioOutputI2SQuad::isr(void)
{
uint32_t saddr;
const int16_t *src1, *src2, *src3, *src4;
const int16_t *zeros = (const int16_t *)zerodata;
int16_t *dest;

saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
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];
if (update_responsibility) update_all();
} else {
dest = (int16_t *)i2s_tx_buffer;
}

src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;

// TODO: fast 4-way interleaved memcpy...
#if 1
memcpy_tointerleaveQuad(dest, src1, src2, src3, src4);
#else
for (int i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
*dest++ = *src1++;
*dest++ = *src3++;
*dest++ = *src2++;
*dest++ = *src4++;
}
#endif

if (block_ch1_1st) {
if (ch1_offset == 0) {
ch1_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch1_offset = 0;
release(block_ch1_1st);
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = NULL;
}
}
if (block_ch2_1st) {
if (ch2_offset == 0) {
ch2_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch2_offset = 0;
release(block_ch2_1st);
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = NULL;
}
}
if (block_ch3_1st) {
if (ch3_offset == 0) {
ch3_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch3_offset = 0;
release(block_ch3_1st);
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = NULL;
}
}
if (block_ch4_1st) {
if (ch4_offset == 0) {
ch4_offset = AUDIO_BLOCK_SAMPLES/2;
} else {
ch4_offset = 0;
release(block_ch4_1st);
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = NULL;
}
}
}


void AudioOutputI2SQuad::update(void)
{
audio_block_t *block, *tmp;

block = receiveReadOnly(0); // channel 1
if (block) {
__disable_irq();
if (block_ch1_1st == NULL) {
block_ch1_1st = block;
ch1_offset = 0;
__enable_irq();
} else if (block_ch1_2nd == NULL) {
block_ch1_2nd = block;
__enable_irq();
} else {
tmp = block_ch1_1st;
block_ch1_1st = block_ch1_2nd;
block_ch1_2nd = block;
ch1_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // channel 2
if (block) {
__disable_irq();
if (block_ch2_1st == NULL) {
block_ch2_1st = block;
ch2_offset = 0;
__enable_irq();
} else if (block_ch2_2nd == NULL) {
block_ch2_2nd = block;
__enable_irq();
} else {
tmp = block_ch2_1st;
block_ch2_1st = block_ch2_2nd;
block_ch2_2nd = block;
ch2_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(2); // channel 3
if (block) {
__disable_irq();
if (block_ch3_1st == NULL) {
block_ch3_1st = block;
ch3_offset = 0;
__enable_irq();
} else if (block_ch3_2nd == NULL) {
block_ch3_2nd = block;
__enable_irq();
} else {
tmp = block_ch3_1st;
block_ch3_1st = block_ch3_2nd;
block_ch3_2nd = block;
ch3_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(3); // channel 4
if (block) {
__disable_irq();
if (block_ch4_1st == NULL) {
block_ch4_1st = block;
ch4_offset = 0;
__enable_irq();
} else if (block_ch4_2nd == NULL) {
block_ch4_2nd = block;
__enable_irq();
} else {
tmp = block_ch4_1st;
block_ch4_1st = block_ch4_2nd;
block_ch4_2nd = block;
ch4_offset = 0;
__enable_irq();
release(tmp);
}
}
}


// 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
// PLL is at 96 MHz in these modes
#define MCLK_MULT 2
#define MCLK_DIV 17
#elif F_CPU == 72000000
#define MCLK_MULT 8
#define MCLK_DIV 51
#elif F_CPU == 120000000
#define MCLK_MULT 8
#define MCLK_DIV 85
#elif F_CPU == 144000000
#define MCLK_MULT 4
#define MCLK_DIV 51
#elif F_CPU == 168000000
#define MCLK_MULT 8
#define MCLK_DIV 119
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#else
#error "This CPU Clock Speed is not supported by the Audio library";
#endif

#if F_CPU >= 20000000
#define MCLK_SRC 3 // the PLL
#else
#define MCLK_SRC 0 // system clock
#endif

void AudioOutputI2SQuad::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(MCLK_SRC) | I2S_MCR_MOE;
I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

// 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_2CH;
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_2CH;
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
}


#else // not __MK20DX256__


void AudioOutputI2SQuad::begin(void)
{
}

void AudioOutputI2SQuad::update(void)
{
audio_block_t *block;

block = receiveReadOnly(0);
if (block) release(block);
block = receiveReadOnly(1);
if (block) release(block);
block = receiveReadOnly(2);
if (block) release(block);
block = receiveReadOnly(3);
if (block) release(block);
}

#endif

+ 59
- 0
output_i2s_quad.h View File

@@ -0,0 +1,59 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef output_i2s_quad_h_
#define output_i2s_quad_h_

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

class AudioOutputI2SQuad : public AudioStream
{
public:
AudioOutputI2SQuad(void) : AudioStream(4, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
private:
static void config_i2s(void);
static audio_block_t *block_ch1_1st;
static audio_block_t *block_ch2_1st;
static audio_block_t *block_ch3_1st;
static audio_block_t *block_ch4_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
static audio_block_t *block_ch1_2nd;
static audio_block_t *block_ch2_2nd;
static audio_block_t *block_ch3_2nd;
static audio_block_t *block_ch4_2nd;
static uint16_t ch1_offset;
static uint16_t ch2_offset;
static uint16_t ch3_offset;
static uint16_t ch4_offset;
audio_block_t *inputQueueArray[4];
};

#endif

Loading…
Cancel
Save