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

#include "mixer.h" #include "mixer.h"
#include "output_dac.h" #include "output_dac.h"
#include "output_i2s.h" #include "output_i2s.h"
#include "output_i2s_quad.h"
#include "output_pwm.h" #include "output_pwm.h"
#include "output_spdif.h" #include "output_spdif.h"
#include "play_memory.h" #include "play_memory.h"

+ 2
- 0
analyze_peak.cpp View File

max = max_sample; max = max_sample;
do { do {
int16_t d=*p++; 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<min) min=d;
if (d>max) max=d; if (d>max) max=d;
} while (p < end); } while (p < end);

+ 13
- 5
control_sgtl5000.cpp View File

#define DAP_COEF_WR_A2_MSB 0x0138 #define DAP_COEF_WR_A2_MSB 0x0138
#define DAP_COEF_WR_A2_LSB 0x013A #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) bool AudioControlSGTL5000::enable(void)
{ {
unsigned int AudioControlSGTL5000::read(unsigned int reg) unsigned int AudioControlSGTL5000::read(unsigned int reg)
{ {
unsigned int val; unsigned int val;
Wire.beginTransmission(SGTL5000_I2C_ADDR);
Wire.beginTransmission(i2c_addr);
Wire.write(reg >> 8); Wire.write(reg >> 8);
Wire.write(reg); Wire.write(reg);
if (Wire.endTransmission(false) != 0) return 0; 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() << 8;
val |= Wire.read(); val |= Wire.read();
return val; return val;
bool AudioControlSGTL5000::write(unsigned int reg, unsigned int val) bool AudioControlSGTL5000::write(unsigned int reg, unsigned int val)
{ {
if (reg == CHIP_ANA_CTRL) ana_ctrl = 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 >> 8);
Wire.write(reg); Wire.write(reg);
Wire.write(val >> 8); Wire.write(val >> 8);

+ 3
- 0
control_sgtl5000.h View File

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

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

// 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

{"type":"AudioInputAnalog","data":{"defaults":{"name":{"value":"new"}},"shortName":"adc","inputs":0,"outputs":1,"category":"input-function","color":"#E6E0F8","icon":"arrow-in.png"}}, {"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":"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":"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":"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":"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"}}, {"type":"AudioOutputPWM","data":{"defaults":{"name":{"value":"new"}},"shortName":"pwm","inputs":1,"outputs":0,"category":"output-function","color":"#E6E0F8","icon":"arrow-in.png"}},
</div> </div>
</script> </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"> <script type="text/x-red" data-help-name="AudioOutputSPDIF">
<h3>Summary</h3> <h3>Summary</h3>
<div class=tooltipinfo> <div class=tooltipinfo>

+ 2
- 0
keywords.txt View File

AudioInputI2S KEYWORD2 AudioInputI2S KEYWORD2
AudioInputI2Sslave KEYWORD2 AudioInputI2Sslave KEYWORD2
AudioOutputI2S KEYWORD2 AudioOutputI2S KEYWORD2
AudioOutputI2SQuad KEYWORD2
AudioOutputI2Sslave KEYWORD2 AudioOutputI2Sslave KEYWORD2
AudioOutputSPDIF KEYWORD2 AudioOutputSPDIF KEYWORD2
AudioOutputPWM KEYWORD2 AudioOutputPWM KEYWORD2
trigger KEYWORD2 trigger KEYWORD2
length KEYWORD2 length KEYWORD2
threshold KEYWORD2 threshold KEYWORD2
setAddress KEYWORD2
enable KEYWORD2 enable KEYWORD2
enableIn KEYWORD2 enableIn KEYWORD2
enableOut KEYWORD2 enableOut KEYWORD2

+ 41
- 0
memcpy_audio.S View File

BX lr 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 .END


#endif #endif

+ 2
- 0
memcpy_audio.h View File

void memcpy_tointerleaveLR(int16_t *dst, const int16_t *srcL, const int16_t *srcR); 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_tointerleaveL(int16_t *dst, const int16_t *srcL);
void memcpy_tointerleaveR(int16_t *dst, const int16_t *srcR); 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 #ifdef __cplusplus
} }
#endif #endif

+ 340
- 0
output_i2s_quad.cpp View File

/* 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

/* 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