Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 26

Thread: PT8211 with irregular clocking signals (non I2S) from T4

  1. #1
    Senior Member
    Join Date
    Feb 2021
    Posts
    172

    PT8211 with irregular clocking signals (non I2S) from T4

    For some time now I've been trying to find a way to have a continuously variable output sampling rate solution for T4.x to take the place of the ever more precarious T3.6 situation.
    While adding a parallel DAC is going to be my last resort, I was wondering if a serial DAC like the PT8211 can be used by 'manually clocking' data from the T4. i.e. rather than set up the I2S interface, use a dedicated bit-toggling routine to send the correct states to three digital output pins (these could still be pins 7,20,21).

    The idea would be to call that routine at the time I would normally write to the DAC on a T3.6, and have it send the three "serial signals" at something close to the max rate the PT8211 can handle, and then have them sit idle until the next DAC output is needed.

    Is this at all plausible with something like the PT8211 (or alternative suggestions), or will serial DACs like this fall over in a heap because they need a steady clock/data/WS stream?

    I looked at the PT8211 data sheet, but it's very concise and the timing diagram is not very helpful. The sheet says the chip allows a wide range of frequencies, and throws away data beyond 16 bits, but I can't see how it determines when the MSB is available.

  2. #2
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    Quote Originally Posted by rvh View Post
    or will serial DACs like this fall over in a heap because they need a steady clock/data/WS stream?
    Interesting question. Actually I have no idea. But I can imagine that this could work since the PT8211 is a very basic DAC.
    I would not be surprised if there is just an ordinary 16 bit shift-register where the DIN data is clocked-in on BCK [MSB first] and latched on a trailing or falling edge of the WS signal. After latching the 16 bits, these are converted to voltage by the R-2R resistor ladder network.

    I assume that you do not have a PT8211 at hand now, otherwise a quick test could bring more clearity.
    If you want, I can do some testing for you. Just send me some bit-banging code [I don't have time to write the code myself] and I'll test it.

    Paul

  3. #3
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Quote Originally Posted by PaulS View Post
    Interesting question. Actually I have no idea. But I can imagine that this could work since the PT8211 is a very basic DAC.
    I would not be surprised if there is just an ordinary 16 bit shift-register where the DIN data is clocked-in on BCK [MSB first] and latched on a trailing or falling edge of the WS signal. After latching the 16 bits, these are converted to voltage by the R-2R resistor ladder network.

    I assume that you do not have a PT8211 at hand now, otherwise a quick test could bring more clearity.
    If you want, I can do some testing for you. Just send me some bit-banging code [I don't have time to write the code myself] and I'll test it.

    Paul
    Thanks Paul, I too was thinking it might be a simple enough chip for this to work. It would be great if you could do a quick test for me, as indeed I don't have a PT8211 board (they seem not available in Australia, so I will order some from you if it works). I'll try to write some simple code as soon as I can.

    I think I read somewhere that some of these DACs need the edge of the WS signal to occur one bit earlier than the first (MSB), so we might need to try a few variations. Also, if running a T4 at 600MHz, what would you suggest the best way would be for me to set the time between clocks/bits so that the PT8211 can handle the rate?

    Thanks,
    Richard

  4. #4
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    p.s. How would you rate the audio quality (and background noise) you get from the PT8211 board vs a T3.6 DAC, when powered by USB (perhaps with an isolator)?

  5. #5
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Paul , here's a first go at a test program that sends 100 and 200Hz sawtooth waves to R and L channels respectively.

    I'm assuming the rudimentary timing diagram in the PT8211 data sheet is accurate where it shows one clock cycle is executed after a WS edge before data starts to be clocked. I've set the clock rate at half the max allowed. If this happens to work, you could try halving the value of HALFCLKns to 25ns to push it to its max.

    I'm assuming also that it's OK to use the result of the & operator in digitalWriteFast to set the pin high for any non-zero result.

    Code:
    #define DIN 7
    #define BCLK 21 // max 20Mhz (50ns), using 2x slower when HALF
    #define WS 20
    #define HALFCLKns 50    // clock cycle duration rate is twice this 
    
    int16_t saw=0;
    int16_t saw2=0;
    int16_t m[16] = {0x8000,0x4000,0x2000,0x1000,0x0800,0x4000,0x0200,0x0100,0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};
    
    elapsedMicros sampletime;  
    
    void setup() 
    {
      digitalWrite(DIN,0);
      digitalWrite(BCLK,0);
      digitalWrite(WS,0);
      pinMode(DIN, OUTPUT);  
      pinMode(BCLK, OUTPUT); 
      pinMode(WS, OUTPUT);  
    }
    
    void loop() {
     
      
     if(sampletime >= 25)  // 40kHz
     {
       sampletime=0;
    
       saw += 160;  // 400 steps per cycle = 100Hz
       if(saw >= 32000) 
       {
          saw -= 64000;          
       } 
       saw2 += 320;  // 200 steps = 200Hz
       if(saw2 >= 32000) 
       {
          saw2 -= 64000;
       }  
      
      // at 10MHz (HALFCLKns 50) bit banging takes up 3.4us for delaynanoseconds calls, plus around 100x digitalWriteFast times 
      // total appears to be about 5us 
       
      //--   WS=0, Right  
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);   
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=15; i>=0; i--)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN,saw & m[i]); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        }  
        
      //-- WS=1, LEFT
        digitalWriteFast(BCLK,0);
        digitalWriteFast(DIN,0);         // probably don't need this    
        digitalWriteFast(WS,1);    
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=15; i>=0; i--)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN,saw2 & m[i]); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        } 
     }
    }

  6. #6
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    I had another read of the data sheet, and now think any WS edge might terminate the data input sequence for a channel. Even though that seems to contradict the statement that only the 'first 16 bit data is valid' - the timing diagram suggests it retains only the final 16 bits if more than 16 are sent. This would make sense in terms of a simple shift register too, which would stop shifting on the WS edge.

    If this is right, the test code above might yet work, but it might be safer to latch the second channel immedaitely after the 16th bit, rather than wait for the next sample cycle. i.e. add three lines at the end like this:
    Code:
    #define DIN 7
    #define BCLK 21 // max 20Mhz (50ns), using 2x slower when HALFCLKns = 50
    #define WS 20
    #define HALFCLKns 50    // clock cycle duration is twice this 
    
    int16_t saw=0;
    int16_t saw2=0;
    int16_t m[16] = {0x8000,0x4000,0x2000,0x1000,0x0800,0x4000,0x0200,0x0100,0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};
    
    elapsedMicros sampletime;  
    
    void setup() 
    {
      digitalWrite(DIN,0);
      digitalWrite(BCLK,0);
      digitalWrite(WS,0);
      pinMode(DIN, OUTPUT);  
      pinMode(BCLK, OUTPUT); 
      pinMode(WS, OUTPUT);  
    }
    
    void loop() {
     
      
     if(sampletime >= 25)  // 40kHz
     {
       sampletime=0;
    
       saw += 160;  // 400 steps per cycle = 100Hz
       if(saw >= 32000) 
       {
          saw -= 64000;          
       } 
       saw2 += 320;  // 200 steps = 200Hz
       if(saw2 >= 32000) 
       {
          saw2 -= 64000;
       }  
      
      // at 10MHz (HALFCLKns 50) bit banging takes up 3.4us for delaynanoseconds calls, plus around 100x digitalWriteFast times 
      // total appears to be about 5us 
       
      //--   WS=0, Right  
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);          
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=15; i>=0; i--)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN,saw & m[i]); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        }  
        
      //-- WS=1, LEFT
        digitalWriteFast(BCLK,0);
        digitalWriteFast(DIN,0);         // probably don't need this    
        digitalWriteFast(WS,1);    
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=15; i>=0; i--)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN,saw2 & m[i]); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        } 
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);         
     }
    }

  7. #7
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    Quote Originally Posted by rvh View Post
    so I will order some from you if it works
    Hi Richard, you're not accidently mixing me up with Paul Stoffregen, founder of PJRC.com, do you? I don't have shop...
    Also I don't have a Teensy 3.6 so can't tell you about its DAC quality.

    Anyway, I tested your code from msg #5 and this what the scope shows:

    Click image for larger version. 

Name:	code#5.png 
Views:	16 
Size:	34.1 KB 
ID:	30689

    And here is the scope image with your code from msg #6:

    Click image for larger version. 

Name:	code#6.png 
Views:	12 
Size:	36.0 KB 
ID:	30690

    Seems identical but not a sawtooth by far.
    Now I'm going to hook up the logic analyzer and see what the I2S signals look like. I will use the code from msg #6.

    Stay tuned,
    Paul

  8. #8
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    Here are the logic analyzer images measured on pins 7, 20 & 21:
    First a complete LR sample:

    Click image for larger version. 

Name:	LAcode#6.jpg 
Views:	16 
Size:	52.6 KB 
ID:	30695

    Then zoomed in around the BCLK sequence:

    Click image for larger version. 

Name:	LAcode#6-zoomed in.jpg 
Views:	9 
Size:	52.3 KB 
ID:	30694

    For reference is the output in CSV format. Took a 5ms sample: decoder--230323-194802.zip

    I2S data is signed, encoded as two's complement with the MSB (most significant bit) first. I don't think your code generates signed, 2's complement encoded data.

    Hope this helps,
    Paul

  9. #9
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Quote Originally Posted by PaulS View Post
    Hi Richard, you're not accidently mixing me up with Paul Stoffregen, founder of PJRC.com, do you? I don't have shop...
    Also I don't have a Teensy 3.6 so can't tell you about its DAC quality.

    Anyway, I tested your code from msg #5 and this what the scope shows:

    Click image for larger version. 

Name:	code#5.png 
Views:	16 
Size:	34.1 KB 
ID:	30689

    And here is the scope image with your code from msg #6:

    Click image for larger version. 

Name:	code#6.png 
Views:	12 
Size:	36.0 KB 
ID:	30690

    Seems identical but not a sawtooth by far.
    Now I'm going to hook up the logic analyzer and see what the I2S signals look like. I will use the code from msg #6.

    Stay tuned,
    Paul
    Thanks Paul, all much appreciated (and sorry about the mix up with the other Paul). I rechecked the code above and realized it's sending the wrong bit order as well as having one value wrong in the 'and' array. So I've corrected that below. You can verify it's sending twos complement data by uncommenting the new lines I added in the setup routine.

    But I'll be surprised if it will work because even with the wrong bit order previously, you should have seen large signals at the DAC output, even though it would be noise rather than a saw tooth. Not the very small signal you showed, which seems to indicate the chip's not happy about something else and not actually converting the data.

    Code:
    #define DIN 7
    #define BCLK 21 // max 20Mhz (50ns), using 2x slower when HALFCLKns = 50
    #define WS 20
    #define HALFCLKns 50    // clock cycle duration is twice this 
    
    int16_t saw=0;
    int16_t saw2=0;
    int16_t m[16] = {0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};
    
    elapsedMicros sampletime;  
    
    void setup() 
    {
      digitalWrite(DIN,0);
      digitalWrite(BCLK,0);
      digitalWrite(WS,0);
      pinMode(DIN, OUTPUT);  
      pinMode(BCLK, OUTPUT); 
      pinMode(WS, OUTPUT);  
    
      //int16_t testval = -16384;
      //for (int i=0; i<16; i++)
      //{
      //    if(testval & m[i]) Serial.print("1");
      //    else Serial.print("0"); 
      //}  
      //Serial.println("");
    }
    
    void loop() {
     
      
     if(sampletime >= 25)  // 40kHz
     {
       sampletime=0;
    
       saw += 160;  // 400 steps per cycle = 100Hz
       if(saw >= 32000) 
       {
          saw -= 64000;          
       } 
       saw2 += 320;  // 200 steps = 200Hz
       if(saw2 >= 32000) 
       {
          saw2 -= 64000;
       }  
      
      // at 10MHz (HALFCLKns 50) bit banging takes up 3.4us for delaynanoseconds calls, plus around 100x digitalWriteFast times 
      // total appears to be about 5us 
       
      //--   WS=0, Right  
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);          
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=0; i<16; i++)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN,saw & m[i]); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        }  
        
      //-- WS=1, LEFT
        digitalWriteFast(BCLK,0);
        digitalWriteFast(DIN,0);         // probably don't need this    
        digitalWriteFast(WS,1);    
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=0; i<16; i++)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN,saw2 & m[i]); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        } 
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);         
     }
    }

    In the data sheet it says "the buffered DIN data then feeding to the DAC after both input register are all settled down, this can eliminated the phase shift happened between two channel output." But I'm not sure what determines that condition. Maybe there are subsequent clock cycles needed after (one of?) the WS edges to synchronously transfer L+R data to the DACs. But there's not enough info.
    Last edited by rvh; 03-23-2023 at 10:51 PM.

  10. #10
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Quote Originally Posted by rvh View Post
    But I'll be surprised if it will work because even with the wrong bit order previously, you should have seen large signals at the DAC output, even though it would be noise rather than a saw tooth
    I realized later that the reverse bit order might actually be enough to cause the result you showed due to the fact that the saw wav is incremented in steps of 160. Normally that would cause the lowest 5 bits to always remain at 0, but in reverse order it would cause the 5 most significant bits to be 0, which would produce a fairly small signal (maybe 80mV max).

  11. #11
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    Hi Richard,

    Using your code from msg #9, the scope shows this:

    Click image for larger version. 

Name:	SDS00083.png 
Views:	14 
Size:	29.6 KB 
ID:	30703

    The logic analyzer shows this:

    Click image for larger version. 

Name:	DSView-230324-160003.jpg 
Views:	10 
Size:	51.7 KB 
ID:	30704

    Here is the CSV data: decoder--230324-155732.csv.zip

    Paul

  12. #12
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Thanks agian Paul. I dusted off my oscilloscope to check the signals I was generating with that code, and discovered the DIN streams were still not setting higher bits. There must be a problem with my use of saw & m[i] in the digital write command. Sorry about that. I've replaced it in the following code with a right shift of the saw values followed by &1. On my oscilloscope the data now look like they're spanning the correct range. Hopefully this will produce saw waves at the PT8211 output.

    Code:
    #define DIN 7
    #define BCLK 21 // max 20Mhz (50ns), using 2x slower when HALFCLKns = 50
    #define WS 20
    #define HALFCLKns 50    // clock cycle duration is twice this 
    int16_t saw=0;
    int16_t saw2=0;
    elapsedMicros sampletime;  
    
    void setup() 
    {
      digitalWrite(DIN,0);
      digitalWrite(BCLK,0);
      digitalWrite(WS,0);
      pinMode(DIN, OUTPUT);  
      pinMode(BCLK, OUTPUT); 
      pinMode(WS, OUTPUT);  
    }
    
    void loop() {
     if(sampletime >= 25)     // 40kHz
     {
       sampletime=0;
       saw += 160;            // 400 steps per cycle = 100Hz
       if(saw >= 32000)    
          saw -= 64000;             
       saw2 += 320;           // 200 steps = 200Hz
       if(saw2 >= 32000)    
          saw2 -= 64000;    
      
      // at 10MHz (HALFCLKns 50) bit banging takes up 3.4us for delaynanoseconds calls, plus around 100x digitalWriteFast times 
      // total appears to be about 5us 
       
      //--   WS=0, Right  
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);          
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=15; i>=0; i--)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN, (saw>>i) & 1); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        }  
        
      //-- WS=1, LEFT
        digitalWriteFast(BCLK,0);
        digitalWriteFast(DIN,0);         // probably don't need this    
        digitalWriteFast(WS,1);    
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        for (int i=15; i>=0; i--)
        {
          digitalWriteFast(BCLK,0); 
          digitalWriteFast(DIN, (saw2>>i) & 1); 
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK,1); 
          delayNanoseconds(HALFCLKns);
        } 
        digitalWriteFast(BCLK,0); 
        digitalWriteFast(DIN,0);         // probably don't need this
        digitalWriteFast(WS,0);         
     }
    }

  13. #13
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    There are your sawtooths:

    Click image for larger version. 

Name:	SDS00084.png 
Views:	15 
Size:	30.4 KB 
ID:	30706 Click image for larger version. 

Name:	SDS00085.png 
Views:	12 
Size:	32.4 KB 
ID:	30707

    and a logic analyzer capture of an LR sample:

    Click image for larger version. 

Name:	DSView-230325-080508.jpg 
Views:	9 
Size:	51.0 KB 
ID:	30708

    and the CSV output: DSView-230325-080508.zip

    Paul

  14. #14
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Quote Originally Posted by PaulS View Post
    There are your sawtooths:
    Paul
    Fabulous! Thanks very much. Looks like this approach should solve my problem and allow me to migrate my T3.6 project (that needs variable DAC write times) to T4.1. Are you using the PJRC PT8211 board here? If so, do you find it has much noise feeding into the audio? It looks fairly clean in your pics, but I noticed that board it has just some DC blocking capacitors and no post LPF as recommended in the PT8211 data sheet for better noise performance.

  15. #15
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    Quote Originally Posted by rvh View Post
    Are you using the PJRC PT8211 board here?
    Yep, shown here:

    Click image for larger version. 

Name:	IMG_20230325_111002.jpg 
Views:	14 
Size:	164.1 KB 
ID:	30715

    Yeah it's a bit noisy, the scope trigger was hardly able to show a stable image. Here is a screenshot of the scope with a 1sec persistence:

    Click image for larger version. 

Name:	SDS00087.png 
Views:	17 
Size:	23.9 KB 
ID:	30718

    You may want to check out this thread where I did some measurements on different audio boards including the PT8211.

    By the way, the logic analyzer was hooked up to a different T4 board assembly because it has long header pins for easy connecting the LA leads.
    As you can see below, there is another audioboard [DAC PCM5102A based]. That I2S DAC does not show the sawtooths with your latest code - not exactly surprised here.

    Click image for larger version. 

Name:	IMG_20230325_092430.jpg 
Views:	15 
Size:	205.9 KB 
ID:	30717

    Paul

  16. #16
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Quote Originally Posted by PaulS View Post
    Yep, shown here:
    You may want to check out this thread where I did some measurements on different audio boards including the PT8211.

    One forum member in the other thread you mention was very negative about the PT8211, saying it was worse than the T4 "medium-quality-audio" output. Is that to your knowledge the general consensus these days? If so, I should check out the medium-quality-audio option, which I had dismissed outright without trying it.

    For my current application, I just need the quality to be no worse than the DAC output of a T3.6 (up to about 40-50kHz). So if anyone has compared the T3.6 and the PT8211 for audio quality, I'd love to hear your impressions.
    Last edited by rvh; 03-25-2023 at 11:55 AM.

  17. #17
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,992
    Both PT8211 and MQS sound pretty good to my ears. If you have a 1K resistor and 0.01uF capacitor handy, give MQS a quick try. I believe you may be surprised how good it sounds.

    MQS uses a noise shaping algorithm, so it can only work reliably if you keep a consistent sample rate. It really isn't adaptable to non-audio usage where you update the DAC with irregular timing.

    If you connect with only a resistor and capacitor, MQS is has no PSRR (power supply rejection ratio), or perhaps 3dB if you consider the PWM is low 50% of the time, but not what you'd expect from a real DAC. It's the same situation as with any other PWM low pass filtered. If your 3.3V power has audio bandwidth noise, it ends up mixed into your audio signal, because it's on the logic level high portion of the PWM. Of course you can create a "clean" power rail and run a buffer chip from it. But if you're going to add an inexpensive buffer chip, PT8211 is so cheap that you're probably better off using it. Then again, how well PT8211 rejects noise from its 3.3V power is an open question. Normally it's used with a resistor and large capacitor to try to clean up the 3.3V power. With either, if you're going to run other circuitry from the 3.3V power, make sure you test your audio quality in the presence of other hardware making the 3.3V power "dirty".

    It's easy to obsess too much about audio specs. None of these are considered high quality. PT8211, MQS, or built in 12 bit DACs aren't going to win any audiophile awards! But they all can sound pretty good. When you just need something which sounds decent, real listening tests are pretty important.

  18. #18
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Thanks for the MQS details Paul. Unfortunately if it's not well suited to variable sampling times it's not going to work for me anyway. So I'll try the PT8211, possibly with a LPF after its output. I'm not looking for audiophile performance in this application. The 12 bit DAC quality of the T3.6 works fine (and presumably it has no LPF?), so I just need the PT8211 to match that. Totally agree that a listening test is the way to decide if the quality is sufficient.

  19. #19
    Junior Member
    Join Date
    May 2022
    Posts
    9
    Many thanks for all the tests and the idea in general. For me these tests indicate that the PT8211 with it's ability of asynchronous communication (no need for a steady data stream) will be perfectly suited as a cheap 16 Bit R2R DAC (actually two in one package) for Control Voltage generation in a DIY polyphonic synth project (with VCOs, VCAs and analog filter chips) where a Teensy 4 will only be used for MIDI processing and modulation generation (LFOs, ADSRs etc.). The PT8211 should easily be able to generate relatively slow changing DC-coupled signals. Maybe its linearity won't be good enough for the VCO frequency CV, but almost certainly for everything else. It will still be a big cost saving factor even if i need better DACs only for the VCO frequency.

    For an 8-voice (2 VCOs per voice) synth i will need at least 32 PT8211 (64 CV channels). I will probably program a very low cost FPGA (LCMXO2-1200HC) to do the correct multiplexing for all these PT8211, so that i can drive them easily with simple SPI FIFO burst write commands from the Teensy (no blocking write conditions)

    Well, we'll see how it goes.

  20. #20
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,992
    I've added a "Using PT8211 Without Audio Library" section which links to this forum thread on the PT8211 page.

    https://www.pjrc.com/store/pt8211_kit.html

    Hopefully it will help everyone who wishes for use PT8211 for non-audio applications to find this conversation.

  21. #21
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Just an update for anyone else looking to replace the T3.6 with its onboard DAC by using T4.x and the PT8211, especially for projects that require asynchronous data writes.

    By using the bit banging method described in this thread with a T4.1 and PT8211, followed by the LPF circuit recommended in the PT8211 data sheets, the audio quality I get is at least as good, and very likely better than what I was getting with the T3.6. Depending on your application, the LPF may not be necessary. But if you are looking for the best quality you can get with this setup, and are powering it via USB, a USB isolator is a must (as with the T3.6).

    Finally I'm able to migrate to T4.1, and can stop running T3.6 boards at 256MHz (when you can get them)!
    Last edited by rvh; 04-19-2023 at 08:52 AM.

  22. #22
    I figured out that if you write only to the left channel, then the same signal comes out of both channels. If HALFCLKns is set to the lowest possible value 28, both output channels can be updated in about 1us:
    Code:
      digitalWriteFast(WS,1);           // write to left channel
      for (int i=15; i>=0; i--)
      {
        digitalWriteFast(DIN, (value>>i) & 1); 
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,1); 
        delayNanoseconds(HALFCLKns);
        digitalWriteFast(BCLK,0); 
      } 
      digitalWriteFast(WS,0);           // both analog outputs are updated

  23. #23
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    That's weird. Would you mind posting your complete code (don't know what "value" is) that shows this behaviour so that I can hook up the logic analyzer and check the data?

    Thanks,
    Paul

  24. #24
    I did only rename "saw" to "value". The pin definitions and setup are the same as in your code. Some unnecessary lines removed.

  25. #25
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    1,092
    Can confirm the observation by mkoch.
    Here is the scope image:

    Click image for larger version. 

Name:	SDS00090.png 
Views:	5 
Size:	22.1 KB 
ID:	31060

    And here is what the logic analyzer shows:

    Click image for larger version. 

Name:	LA_1.68us.png 
Views:	4 
Size:	27.8 KB 
ID:	31061 Click image for larger version. 

Name:	LA_detail.png 
Views:	5 
Size:	29.1 KB 
ID:	31062

    For completeness, here is the code that shows the behaviour:
    Code:
    // https://forum.pjrc.com/threads/72446-PT8211-with-irregular-clocking-signals-(non-I2S)-from-T4
    // code from msg#12 & msg#22
    
    #define DIN 7
    #define BCLK 21 // max 20Mhz (50ns), using 2x slower when HALFCLKns = 50
    #define WS 20
    #define HALFCLKns 28    // clock cycle duration is twice this 
    int16_t saw = 0;
    int16_t saw2 = 0;
    elapsedMicros sampletime;
    
    void setup()
    {
      digitalWrite(DIN, 0);
      digitalWrite(BCLK, 0);
      digitalWrite(WS, 0);
      pinMode(DIN, OUTPUT);
      pinMode(BCLK, OUTPUT);
      pinMode(WS, OUTPUT);
    }
    
    void loop() {
      if (sampletime >= 25)    // 40kHz
      {
        sampletime = 0;
        saw += 160;            // 400 steps per cycle = 100Hz
        if (saw >= 32000)
          saw -= 64000;
        saw2 += 320;           // 200 steps = 200Hz
        if (saw2 >= 32000)
          saw2 -= 64000;
        
        digitalWriteFast(WS, 1);          // write to left channel
        for (int i = 15; i >= 0; i--)
        {
          digitalWriteFast(DIN, (saw >> i) & 1);
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK, 1);
          delayNanoseconds(HALFCLKns);
          digitalWriteFast(BCLK, 0);
        }
        digitalWriteFast(WS, 0);          // both analog outputs are updated
      }
    }
    I suppose the internal logic of the PT8211 DAC copies the left channel data to the right channel when no BLCK's have appeared while WS was low and switched to high. Interesting 'feature'...

    Paul

Posting Permissions

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