Dual channel 16bit dac PT8211

it might be needed to disable the "MSB First" bit, too:
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) /* | I2S_TCR4_MF */ | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD;

ah, yes, and my descrition is for AudioOutputPT8211, not AudioOutputI2S

next time, please use the code-tag for code - this makes it way more readable in this forum..
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveform waveform1; //xy=110,75
[B]AudioOutputPT8211[/B] audioOutput; //xy=303,78
AudioConnection patchCord1(waveform1, 0, audioOutput, 0);
AudioConnection patchCord2(waveform1, 0, audioOutput, 1);
// GUItool: end automatically generated code

void setup() {
  AudioMemory(16);
  waveform1.begin(WAVEFORM_TRIANGLE);
  waveform1.amplitude(0.99);
}

void loop() {
  float freq = map(analogRead(A0), 0, 4095, 0.1, 2000);
  waveform1.frequency(freq);

}
 
Last edited:
Hey Frank! Thanks for heads up! I did replaced as you instructed me, now i get some squares generated although it's a sine waveform code.
Where can i disable the oversampling?
 
vincentiuș;139856 said:
Hey Frank! Thanks for heads up! I did replaced as you instructed me, now i get some squares generated although it's a sine waveform code.
Where can i disable the oversampling?

In the corresonding headerfile. But i guess there are other issues. Without having this chip, I can not help you.
Want to swap ? I send you a PT8211 and you send yours back :) I'm want to know what the exact problem is.

F dot boesing at gmx dot de
 
Last edited:
it might be needed to disable the "MSB First" bit, too:

I think not. It says "the most significant bit (bit 1) must always be first" in the datasheet.

I too got some TDA 1543 chips. I actually managed to get some sound out. I didn't use the sine sketch, but a simple USB audio out (PassThroughUSB with AudioControlSGTL5000 commented out). But it only works with 9 or 8 bits, not 16.

Oversampling works, though. For testing, I modified the linear oversampling part to divide every sample word by 128, then it doesn't sound so bad. Maybe these chips are not what they are supposed to be. There is NXP TDA 1543 printed on them, but the markings may be wrong or the chips are faulty.

I found a project using Teensy and TDA 1543 chips: https://purwanto1987.wordpress.com/2016/09/18/dac-tda1543/
That one simply uses an AudioOutputI2S. I also tried that but it didn't work properly. But in that project, there are small resistors in BCK, WS, and DATA lines. Maybe I'll try that before I trash those chips.

EDIT: Tried, didn't help. So these TDAs are crap. Ordering some PT8211s ;)

I do have an Audio Adapter to play with meanwhile. It works like a charm.
 
Last edited:
I finally got it! My TDAs probably are not faulty, they just aren't TDA1543. They are TDA1543A!

The difference is, this variation doesn't use I2S, but "Japanese format". It is similar, but there are 8 bit clock toggles when MSb is set. That explains the behaviour I encountered.

So trivial a difference, but I have no idea how (or to where in code) to insert those extra clocks. The DMAChannel does the transmit?
 
Last edited:
Confirmed. My TDA performs nicely, when I fill the first byte with the first bit, and second byte with sample >> 7.
Like this:
Code:
int16_t t = (d & 0x8000) != 0 ? 0xff00 : 0;
d = ((d >> 7) & 0xff) | t;

But of course I only get 9 bit resolution this way. I should transmit 24 bits per sample to get the whole 16 bit resolution.

The oversampling part of the PT8211 class gave me a hint what I probably should try:

- Allocate a larger i2s_tx_buffer

- Make the customisations to isr() so it generates three bytes per sample

- Change in config_i2s():
Code:
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);
to
Code:
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(23) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S0_TCR5 = I2S_TCR5_WNW(23) | I2S_TCR5_W0W(23) | I2S_TCR5_FBT(23);

But what does the I2S_TCR2_DIV mean exactly? The regular code without oversampling uses I2S_TCR2_DIV(3). Should I use I2S_TCR2_DIV(2) for 24 bit samples?
 
@vincentius
Ok, I got your chip TDA 1543 today - thank you. I got it working.
Use this line:
Code:
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD;
You can enable oversampling, if you want.

Then, most important, the schematic is a bit different.
You cant just connect a line-in. From the Datasheet: "The output current of the DAC is a sink current."
- Either use the schematic from the datasheet,
- Or add resistors:There must a be a way to calculate the values - but I took the try-and error approach :)
Connect 1 K between VREF and GND, 1K between R and GND, 1K between L and GND. You'll see a nice sine on your scope or - now - can hear it if you connect an amplifier. Play a bit with the r-values to set the offset and amplitude.
Edit:After that, you can connect capacitors (10uf??) between the outputs and your amplifier to get a nice AC-output.
 
Last edited:
@Pazi: If it is the japanese format, just try the original PT8211 code! I did not look at the datasheet. If your TDA has a vref, too, perhaps its needed to add op-amps or resistors like for the std TDA 1543.
(See my previous post)
Or send me one of your chips, I try to make it working..
 
Last edited:
@Pazi: If it is the japanese format, just try the original PT8211 code! I did not look at the datasheet. If your TDA has a vref, too, perhaps its needed to add op-amps or resistors like for the std TDA 1543.
Already tried, sounds awful. The "Japanese format" is NOT standard I2S.

I managed to get the sound out with an opamp, that's not the problem.

Copy-pasting code that works with my TDA (but only 9 bit resolution, because I didn't manage to change the transmit to 24 bits).

output_tda.h
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 */

#ifndef output_tda_h_
#define output_tda_h_

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

class AudioOutputTDA : public AudioStream
{
public:
	AudioOutputTDA(void) : AudioStream(2, inputQueueArray) { begin(); }
	virtual void update(void);
	void begin(void);
	//friend class AudioInputI2S;
protected:
	AudioOutputTDA(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
	static void config_i2s(void);
	static audio_block_t *block_left_1st;
	static audio_block_t *block_right_1st;
	static bool update_responsibility;
	static DMAChannel dma;
	static void isr(void);
private:
	static audio_block_t *block_left_2nd;
	static audio_block_t *block_right_2nd;
	static uint16_t block_left_offset;
	static uint16_t block_right_offset;
	audio_block_t *inputQueueArray[2];
};

#endif

output_tda.cpp
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 */

#include "output_tda.h"
#include "memcpy_audio.h"

audio_block_t * AudioOutputTDA::block_left_1st = NULL;
audio_block_t * AudioOutputTDA::block_right_1st = NULL;
audio_block_t * AudioOutputTDA::block_left_2nd = NULL;
audio_block_t * AudioOutputTDA::block_right_2nd = NULL;
uint16_t  AudioOutputTDA::block_left_offset = 0;
uint16_t  AudioOutputTDA::block_right_offset = 0;
bool AudioOutputTDA::update_responsibility = false;

DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
DMAChannel AudioOutputTDA::dma(false);

void AudioOutputTDA::begin(void)
{
	dma.begin(true); // Allocate the DMA channel first

	block_left_1st = NULL;
	block_right_1st = NULL;

	// TODO: should we set & clear the I2S_TCSR_SR bit here?
	config_i2s();
	CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

#if defined(KINETISK)
	dma.TCD->SADDR = i2s_tx_buffer;
	dma.TCD->SOFF = 2;
	dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_16BIT) | DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_16BIT);
	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;
#endif
	dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
	update_responsibility = update_setup();
	dma.enable();

	I2S0_TCSR = I2S_TCSR_SR;
	I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
	dma.attachInterrupt(isr);
}


void AudioOutputTDA::isr(void)
{
#if 0 // defined(KINETISK)
#else
	const int16_t *src;
	//uint8_t *end;
	
	uint16_t *dest;
	audio_block_t *block;
	uint32_t saddr, offset;

	//saddr = (uint32_t)(dma.CFG->SAR);
	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 = (uint16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
		if (AudioOutputTDA::update_responsibility) AudioStream::update_all();
	} else {
		// DMA is transmitting the second half of the buffer
		// so we must fill the first half
		dest = (uint16_t *)i2s_tx_buffer;
	}

	block = AudioOutputTDA::block_left_1st;
	if (block) {
		offset = AudioOutputTDA::block_left_offset;
		src = &block->data[offset];
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {			
			*dest = (*src >> 7) | ((*src & 0x8000) != 0 ? 0xff00 : 0);
			src++;
			dest += 2;
		} 
		offset += AUDIO_BLOCK_SAMPLES/2;
		if (offset < AUDIO_BLOCK_SAMPLES) {
			AudioOutputTDA::block_left_offset = offset;
		} else {
			AudioOutputTDA::block_left_offset = 0;
			AudioStream::release(block);
			AudioOutputTDA::block_left_1st = AudioOutputTDA::block_left_2nd;
			AudioOutputTDA::block_left_2nd = NULL;
		}
	} else {
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
			*dest = 0;
			dest += 2;
		}
	}
	dest -= AUDIO_BLOCK_SAMPLES - 1;
	block = AudioOutputTDA::block_right_1st;
	if (block) {
		offset = AudioOutputTDA::block_right_offset;
		src = &block->data[offset];
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
			*dest = (*src >> 7) | ((*src & 0x8000) != 0 ? 0xff00 : 0);
			dest += 2;
		}
		offset += AUDIO_BLOCK_SAMPLES/2;
		if (offset < AUDIO_BLOCK_SAMPLES) {
			AudioOutputTDA::block_right_offset = offset;
		} else {
			AudioOutputTDA::block_right_offset = 0;
			AudioStream::release(block);
			AudioOutputTDA::block_right_1st = AudioOutputTDA::block_right_2nd;
			AudioOutputTDA::block_right_2nd = NULL;
		}
	} else {
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
			*dest = 0;
			dest += 2;
		}
	}
#endif
}

void AudioOutputTDA::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);
		}
	}
}


// 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 == 180000000
  #define MCLK_MULT 16
  #define MCLK_DIV  255
  #define MCLK_SRC  0
#elif F_CPU == 192000000
  #define MCLK_MULT 1
  #define MCLK_DIV  17
#elif F_CPU == 216000000
  #define MCLK_MULT 8
  #define MCLK_DIV  153
  #define MCLK_SRC  0
#elif F_CPU == 240000000
  #define MCLK_MULT 4
  #define MCLK_DIV  85
#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

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

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

	// if transmitter is not enabled, do nothing
	if (I2S0_TCSR & I2S_TCSR_TE) return;

	// enable MCLK output
	I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
	while (I2S0_MCR & I2S_MCR_DUF) ;
	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;
	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 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
}

Please pay attention to the line
Code:
*dest = (*src >> 7) | ((*src & 0x8000) != 0 ? 0xff00 : 0);

Also note this page from the datasheet: http://www.datasheetlib.com/datasheet/729489/tda1545_nxp-semiconductors.html?page=11#datasheet
(the MSb and the bit clock).
 
I know that Japanese format is different. Guess what ? - the PT8211 uses a japanese format too.
Send me the chip, I make it work within an hour. It's way more easy and saves *really* much time when having the chip (and to see the waveforms live on a scope, or the data on a logic analyzer)
I don't have the time for guesses, and I'm not going to do anything without a chip this time.
 
Last edited:
That's why i wrote "a" japanese format. :rolleyes:

Did you notice that Paul sells a PT8211 board ?
No need to fiddle with these old chips anyway...
(but I really want to make them work)
 
Last edited:
Did you notice that Paul sells a PT8211 board ?
No I didn't. Looks like a nice kit, at a very nice price!

No need to fiddle with these old chips anyway...
(but I really want to make them work)
I know that feeling ;)

I think I got this damn TDA1543A working. I didn't know how to make the transmit 24 bits, but I managed to do 32 bits and it sounds fine. Will make some more tests soon.

But I'll send a chip to you anyway. Maybe you'll develop some better solution.

I'll send it some day next week.

BTW: It came to my mind, if it's possible to do four channel audio (output) also with PT8211 or TDA? Would be useful, I think.
 
Great that it works :) Would you mind to share your code on github, and make it alvailable for others ?

No, your solutions works.. I don't want the chip in this case :) I have enough other projects..
 
Some guessing: The resistor is between 5 VOLT and A small and a large cap parallel to chip gnd and vcc. A capacitor on both outputs.
Edit:Just like in the datasheet.
 
Last edited:
This is how I think the teensy pcb kit pt8211 is laid out by following the traces (which is slightly different to the datasheet)
https://www.pjrc.com/store/pt8211_kit.html
Have i copied this correctly?

pt8211.png

The data sheet puts the 47uf caps to gnd.
But the teensy pcb puts them in series with the output.

Which is best? Ill be connecting the output to an external usb speakers.

Regards
Russell
 
Great that it works :) Would you mind to share your code on github, and make it alvailable for others ?
I'll do that later, but in case I forget it, I'll just copy-paste the code here for now.

I made some more testing and scoping and I think this works good enough. Like I said, I changed the output to 32-bit, because I didn't figure out how to make it 24-bit. The extra bits don't seem to hurt. I left the #if defined(_32_BITS_) directives in place to mark the lines changed from 16 to 32 bits.

So, how this works is it simply sends the MSb 8 times, then the rest of the bits, and then fills the rest of the 32-bit word with zeroes.

So, this is for TDA1543A variation:

output_tda.h (the same as in a previous post):
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 */

#ifndef output_tda_h_
#define output_tda_h_

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

class AudioOutputTDA : public AudioStream
{
public:
	AudioOutputTDA(void) : AudioStream(2, inputQueueArray) { begin(); }
	virtual void update(void);
	void begin(void);
	//friend class AudioInputI2S;
protected:
	AudioOutputTDA(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
	static void config_i2s(void);
	static audio_block_t *block_left_1st;
	static audio_block_t *block_right_1st;
	static bool update_responsibility;
	static DMAChannel dma;
	static void isr(void);
private:
	static audio_block_t *block_left_2nd;
	static audio_block_t *block_right_2nd;
	static uint16_t block_left_offset;
	static uint16_t block_right_offset;
	audio_block_t *inputQueueArray[2];
};

#endif

output_tda.cpp:
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 */

#include "output_tda.h"
#include "memcpy_audio.h"

audio_block_t * AudioOutputTDA::block_left_1st = NULL;
audio_block_t * AudioOutputTDA::block_right_1st = NULL;
audio_block_t * AudioOutputTDA::block_left_2nd = NULL;
audio_block_t * AudioOutputTDA::block_right_2nd = NULL;
uint16_t  AudioOutputTDA::block_left_offset = 0;
uint16_t  AudioOutputTDA::block_right_offset = 0;
bool AudioOutputTDA::update_responsibility = false;

#define _32_BITS_
#if defined(_32_BITS_)
	DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*2];
#else
	DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
#endif
DMAChannel AudioOutputTDA::dma(false);

void AudioOutputTDA::begin(void)
{
	dma.begin(true); // Allocate the DMA channel first

	block_left_1st = NULL;
	block_right_1st = NULL;

	// TODO: should we set & clear the I2S_TCSR_SR bit here?
	config_i2s();
	CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

#if defined(KINETISK)
	dma.TCD->SADDR = i2s_tx_buffer;
#if defined(_32_BITS_)
	dma.TCD->SOFF = 4;
	dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_32BIT) | DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_32BIT);
	dma.TCD->NBYTES_MLNO = 4;
#else
	dma.TCD->SOFF = 2;
	dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_16BIT) | DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_16BIT);
	dma.TCD->NBYTES_MLNO = 2;
#endif
	dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
	dma.TCD->DADDR = &I2S0_TDR0;
	dma.TCD->DOFF = 0;
#if defined(_32_BITS_)
	dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 4;
	dma.TCD->DLASTSGA = 0;
	dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 4;
#else
	dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
	dma.TCD->DLASTSGA = 0;
	dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
#endif
	dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
	dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
	update_responsibility = update_setup();
	dma.enable();

	I2S0_TCSR = I2S_TCSR_SR;
	I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
	dma.attachInterrupt(isr);
}


void AudioOutputTDA::isr(void)
{
#if 0 // defined(KINETISK)
#else
	const int16_t *src;
	//uint8_t *end;
#if defined(_32_BITS_)
	uint32_t *dest;
#else
	uint16_t *dest;
#endif
	audio_block_t *block;
	uint32_t saddr, offset;

	//saddr = (uint32_t)(dma.CFG->SAR);
	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
#if defined(_32_BITS_)
		dest = (uint32_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
#else
		dest = (uint16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
#endif
		if (AudioOutputTDA::update_responsibility) AudioStream::update_all();
	} else {
		// DMA is transmitting the second half of the buffer
		// so we must fill the first half
#if defined(_32_BITS_)
		dest = (uint32_t *)i2s_tx_buffer;
#else
		dest = (uint16_t *)i2s_tx_buffer;
#endif
	}

	block = AudioOutputTDA::block_left_1st;
	if (block) {
		offset = AudioOutputTDA::block_left_offset;
		src = &block->data[offset];
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
#if defined(_32_BITS_)
			*dest = ((uint32_t)*src << 9) | ((*src & 0x8000) != 0 ? 0xff000000 : 0);
#else
			*dest = (*src >> 7) | ((*src & 0x8000) != 0 ? 0xff00 : 0);
#endif
			src++;
			dest += 2;
		} 
		offset += AUDIO_BLOCK_SAMPLES/2;
		if (offset < AUDIO_BLOCK_SAMPLES) {
			AudioOutputTDA::block_left_offset = offset;
		} else {
			AudioOutputTDA::block_left_offset = 0;
			AudioStream::release(block);
			AudioOutputTDA::block_left_1st = AudioOutputTDA::block_left_2nd;
			AudioOutputTDA::block_left_2nd = NULL;
		}
	} else {
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
			*dest = 0;
			dest += 2;
		}
	}
	dest -= AUDIO_BLOCK_SAMPLES - 1;
	block = AudioOutputTDA::block_right_1st;
	if (block) {
		offset = AudioOutputTDA::block_right_offset;
		src = &block->data[offset];
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
#if defined(_32_BITS_)
			*dest = ((uint32_t)*src << 9) | ((*src & 0x8000) != 0 ? 0xff000000 : 0);
#else
			*dest = (*src >> 7) | ((*src & 0x8000) != 0 ? 0xff00 : 0);
#endif
			src++;
			dest += 2;
		}
		offset += AUDIO_BLOCK_SAMPLES/2;
		if (offset < AUDIO_BLOCK_SAMPLES) {
			AudioOutputTDA::block_right_offset = offset;
		} else {
			AudioOutputTDA::block_right_offset = 0;
			AudioStream::release(block);
			AudioOutputTDA::block_right_1st = AudioOutputTDA::block_right_2nd;
			AudioOutputTDA::block_right_2nd = NULL;
		}
	} else {
		for (int i = 0; i < AUDIO_BLOCK_SAMPLES/2; ++i) {
			*dest = 0;
			dest += 2;
		}
	}
#endif
}

void AudioOutputTDA::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);
		}
	}
}


// 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 == 180000000
  #define MCLK_MULT 16
  #define MCLK_DIV  255
  #define MCLK_SRC  0
#elif F_CPU == 192000000
  #define MCLK_MULT 1
  #define MCLK_DIV  17
#elif F_CPU == 216000000
  #define MCLK_MULT 8
  #define MCLK_DIV  153
  #define MCLK_SRC  0
#elif F_CPU == 240000000
  #define MCLK_MULT 4
  #define MCLK_DIV  85
#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

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

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

	// if transmitter is not enabled, do nothing
	if (I2S0_TCSR & I2S_TCSR_TE) return;

	// enable MCLK output
	I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
	while (I2S0_MCR & I2S_MCR_DUF) ;
	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);
#if defined(_32_BITS_)
	const int bitwidth = 32;
	I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(1);
#else
	const int bitwidth = 16;
	I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(3);
#endif
	I2S0_TCR3 = I2S_TCR3_TCE;
	I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(bitwidth-1) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD;
	I2S0_TCR5 = I2S_TCR5_WNW(bitwidth-1) | I2S_TCR5_W0W(bitwidth-1) | I2S_TCR5_FBT(bitwidth-1);

	// 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
}
 
I have a hardware question/issue with the PT8211. I got it to work and playing audio files from a sd card, sounds good. But I am experiencing a lot of "bleeding" from one channel to the other. I mean for instance, I can hear the left channel in the right channel, when the right channel is "silent". I am using op-amps to amplifing the signal for my usage. Could that be related to the analog circuit or is the PT8211 just "that bad" ?

EDIT: PROBLEM SOLVED : the issue is not the PT8211 it's the TL074 quad package opamp is used, to avoid crosstalk keep the opamps for every channel in seperated packages. In my 2 x TL072.

See me schematic, I am listening to OUT1 and OUT2:

 
Last edited:
Silly me! Using quad packages for the amp are not a good idea if you want good seperation between the 2 channels. I switched the TL074 for 2 TL072 and the crosstalk is completely gone. I learnt that lesson!
 
Wow, I'm surprised there was so much crosstalk within the opamp chip. Usually they're pretty good. But I guess switching to separate chips proves it. :)
 
Looking at your schematic, you might have problems because there is no DC bias path for the opamp inputs, Some high value resistors between VOUT1 and VOUT2 to a bias point of half the supply would help.
 
Back
Top