Forum Rule: Always post complete source code & details to reproduce any issue!
Page 5 of 6 FirstFirst ... 3 4 5 6 LastLast
Results 101 to 125 of 141

Thread: Dual channel 16bit dac PT8211

  1. #101
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    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
    AudioOutputPT8211 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 by Frank B; 04-05-2017 at 05:31 PM.

  2. #102
    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?

  3. #103
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    Quote Originally Posted by vincentiuș View Post
    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 by Frank B; 04-11-2017 at 01:45 PM.

  4. #104
    Quote Originally Posted by Frank B View Post
    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/2...8/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 by Pazi; 04-20-2017 at 11:31 AM.

  5. #105
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    A TDA is on the way - I hope it arrives soon.

  6. #106
    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 by Pazi; 04-21-2017 at 07:36 AM.

  7. #107
    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?

  8. #108
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    @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 by Frank B; 04-21-2017 at 08:17 PM.

  9. #109
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    @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 by Frank B; 04-21-2017 at 08:03 PM.

  10. #110

    Angry

    Quote Originally Posted by Frank B View Post
    @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/datashee...e=11#datasheet
    (the MSb and the bit clock).

  11. #111
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    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 by Frank B; 04-22-2017 at 10:16 AM.

  12. #112
    Quote Originally Posted by Frank B View Post
    I know that Japanese format is different. Guess what ? - the PT8211 uses a japanese format too.
    It is different, believe me. Maybe "Japanese format" is not a standard.

    Quote Originally Posted by Frank B View Post
    Send me the chip, I make it work within an hour.
    I might. Please PM me your postal address?

  13. #113
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    That's why i wrote "a" japanese format.

    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 by Frank B; 04-22-2017 at 03:07 PM.

  14. #114
    Quote Originally Posted by Frank B View Post
    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.

  15. #115
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    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..

  16. #116
    Senior Member
    Join Date
    Jun 2015
    Posts
    132
    Does any one have the schematic for the PT8211 teensy kit?

    Regards
    Russell

  17. #117
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,552
    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 by Frank B; 04-24-2017 at 08:28 PM.

  18. #118
    Senior Member
    Join Date
    Jun 2015
    Posts
    132
    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?

    Click image for larger version. 

Name:	pt8211.png 
Views:	490 
Size:	26.5 KB 
ID:	10415

    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

  19. #119
    @ Frank. The TDA is Working! Thanks!

  20. #120
    Quote Originally Posted by Frank B View Post
    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
    }

  21. #121
    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 by nicolas_soundforce; 10-01-2017 at 04:49 PM.

  22. #122
    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!

  23. #123
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,474
    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.

  24. #124
    Quote Originally Posted by PaulStoffregen View Post
    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.
    I am using a gain of 5 on both channels which probably amplifies even more this "defect".

  25. #125
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •