PMD100 to TDA1541 in smultaneous mode

Member
Joined 2022
I am just about to make a Mouser order and thought while I was at it I would get the parts for the schematic in post #122. What is the part number for 74LVC74 on the schematics, I assume it is an eight pin part, as a mouser search for 74LVC74 only shows 14 pin parts with VCC as pin 14.
 
Member
Joined 2020
BTW: check post #1 for the latest schematic. Earlier versions could do without the programming mode of the PMD100, but the latest sounds best and uses customizable dithering settings for the PMD100.
 
Member
Joined 2020
Correct: it's only needed in case you omit the glue logic and connect the output of the PMD100 to a "normal" DAC and still want WCKO to be reclocked. But for the TDA1541 it's not needed.
 
Member
Joined 2020
I tried 4x oversampling and found out that there is no different timing: all frequencies stay same from the PMD100, output happens only half as often compared to 8x, so we can simplify the circuit and simulation and use DG of PMD100 to generate LE for the TDA1541A as it's reclocked in any case. The updated code:

Java:
// for ATtiny84
// pin mapping clockwise
// set clock to 4MHz internal

// works with DIR9001 -> PMD100 -> TDA1541A digital interface board

// change these settings to configure the PMD100:
byte oversampling_rate           =  3; // 1 = 2x oversampling, 2 = 4x oversampling, 3 = 8x oversampling
byte dither_mode                 =  2; // 0~7 = dither mode 0 to 7
byte input_data_justification    =  0; // 0 = left justified, 1 = right justified (16bit)
byte input_bit_clock_polarity    =  0; // 0 = rising edge, 1 = falling edge
byte input_frame_sync_polarity   =  0; // 0 = LRCI high means left channel, 1 = LRCI low means left channel
byte output_word_length          =  0; // 0 = 16bit, 1 = 18bit, 2 = 20bit, 3 = 24bit
byte output_format               =  1; // 0 = 2s compliment, 1 = COB
byte output_word_clock_polarity  =  1; // 0 = high to low at the end of the output word, 1 = low to high at the end of the output word
byte deglitch_low                = 31; // set falling edge of DG to 0~31st interval
byte deglitch_high               = 25; // set rising edge of DG to 0~31st interval
// end of settings

#include <avr/sleep.h>
#include <debouncetm.h>

#define MUTE           0  // MUTE (HIGH = muted)
#define MS1            2
#define MS2            3
#define MS3            4
#define MDT            5
#define MEN            6
#define LED            7  // LED (active HIGH)
#define TOSL          10  // TOSLINK (active LOW with 74LVC125, active HIGH with 74LVC126)
#define COAX           9  // COAX (active LOW)
#define BTN            8  // button pin (HIGH = TOSLINK)

#define samplerate   0.75 // poll interval for update() in msec (a safe starting point would be samplerate = 0.1 * bounce-duration in msec)
#define longpress    0.90 // longpress duration in sec
#define doubleclick  0.50 // doubleclick window in sec
#define history         8 // internal history length: 8, 16 or 32 bit

Button button(BTN, INPUT, HIGH, samplerate, longpress, doubleclick, history);  // specify pin, pinMode, polarity, samplerate, longpress duration, doubleclick window

elapsedMillis sleep_timer;
unsigned int sleep_interval = 500;   // mute ramp down takes 260ms, so 300 is a good value here

void setup() {
  delay(1000);
  pinMode(COAX, OUTPUT);
  pinMode(TOSL, OUTPUT);
  pinMode(MUTE, OUTPUT);
  pinMode(LED, OUTPUT);

  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(MS3, OUTPUT);
  pinMode(MDT, OUTPUT);
  pinMode(MEN, OUTPUT);

  digitalWrite(LED, LOW);
  digitalWrite(MS1, LOW);
  digitalWrite(MS2, LOW);
  digitalWrite(MS3, LOW);
  digitalWrite(MDT, LOW);
  digitalWrite(MEN, HIGH);
  delay(100);
  digitalWrite(MUTE, HIGH);
  digitalWrite(COAX, HIGH);
  digitalWrite(TOSL, LOW);

  delay(100);
  configurePMD100();
  delay(100);
  zeroAttenuationPMD100();
  delay(3000);

  if (!digitalRead(BTN)) {
    digitalWrite(COAX, LOW);
    delay(50);
    digitalWrite(LED, HIGH);
    delay(300);
    digitalWrite(MUTE, LOW);
    delay(300);
  }
  sleep_timer = 0;
}

void loop() {
  button.update();
  if (button.pressed()) {select_TOSL();}
  if (button.released()) {select_coax();}
  if (sleep_timer > sleep_interval) {gotoSleep();}
}

void zeroAttenuationPMD100() {
  digitalWrite(MS1, HIGH);
  digitalWrite(MS2, HIGH);
  delayMicroseconds(1);
  digitalWrite(MEN, LOW);
  delayMicroseconds(1);
  digitalWrite(MEN, HIGH);
  delayMicroseconds(1);
  digitalWrite(MS1, LOW);
  digitalWrite(MS2, LOW);
}

uint32_t value = 0;

void configurePMD100() {
  value = 0;
  value |= uint32_t(dither_mode) << 19;
  value |= uint32_t(output_word_length) << 17;
  value |= uint32_t(input_frame_sync_polarity) << 16;
  value |= uint32_t(deglitch_high) << 11;
  value |= uint32_t(deglitch_low) << 6;
  value |= uint32_t(output_format) << 5;
  value |= uint32_t(output_word_clock_polarity) << 4;
  value |= uint32_t(input_data_justification) << 3;
  value |= uint32_t(input_bit_clock_polarity) << 2;
  value |= uint32_t(oversampling_rate);

  for (int i = 0; i < 24; i++) {
    digitalWrite(MEN, LOW);
    digitalWrite(MDT, bitRead(value, i));
    delayMicroseconds(1);
    digitalWrite(MEN, HIGH);
    delayMicroseconds(1);
  }

  digitalWrite(MDT, LOW);
  delayMicroseconds(4);

  digitalWrite(MS2, HIGH);
  digitalWrite(MS3, HIGH);
  delayMicroseconds(1);

  digitalWrite(MEN, LOW);
  delayMicroseconds(1);
  digitalWrite(MEN, HIGH);

  delayMicroseconds(2);
  digitalWrite(MS2, LOW);
  digitalWrite(MS3, LOW);
}

void select_TOSL() {
  digitalWrite(MUTE, HIGH);
  delay(300);
  digitalWrite(LED, LOW);
  delay(200);
  digitalWrite(COAX, HIGH);
  delay(50);
  digitalWrite(TOSL, HIGH);
  delay(50);
  digitalWrite(LED, HIGH);
  delay(300);
  digitalWrite(MUTE, LOW);
  delay(300);
  sleep_timer = 0;
}

void select_coax() {
  digitalWrite(MUTE, HIGH);
  delay(300);
  digitalWrite(LED, LOW);
  delay(200);
  digitalWrite(TOSL, LOW);
  delay(50);
  digitalWrite(COAX, LOW);
  delay(50);
  digitalWrite(LED, HIGH);
  delay(300);
  digitalWrite(MUTE, LOW);
  delay(300);
  sleep_timer = 0;
}

void gotoSleep() {
  GIMSK |= _BV(PCIE0);
  GIMSK |= _BV(PCIE1);
  PCMSK1 |= _BV(PCINT10);                 // use PB2 as interrupt pin
  // PCMSK0 |= _BV(PCINT4);               // use PA4 as interrupt pin
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
  sleep_enable();                         // sets the sleep enable bit in the MCUCR register (SE BIT)
  sleep_cpu();                            // sleep
  sleep_disable();                        // clear SE bit
  PCMSK1 &= ~_BV(PCINT10);                // turn off PB2 as interrupt pin
  // PCMSK0 &= ~_BV(PCINT4);              // turn off PA4 as interrupt pin
  ADCSRA |= _BV(ADEN);                    // ADC on
  sleep_timer = 0;
}

// ISR(PCINT0_vect) {sleep_timer = 0;}
ISR(PCINT1_vect) {sleep_timer = 0;}

Updated schematics attached:
 

Attachments

  • schematic.jpg
    schematic.jpg
    361.2 KB · Views: 86
  • simulation.jpg
    simulation.jpg
    63.3 KB · Views: 80
Member
Joined 2020
I played around with dither settings and found out that best is to disable dither, so here updated schematics and code:

Java:
// for ATtiny84
// pin mapping clockwise
// set clock to 4MHz internal

// works with DIR9001 -> PMD100 -> TDA1541A digital interface board for Arcam Delta Black Box v1

// change these settings to configure the PMD100:
byte oversampling_rate           =  3; // 1 = 2x oversampling, 2 = 4x oversampling, 3 = 8x oversampling
byte dither_mode                 =  3; // 0~7 = dither mode 0 to 7
byte input_data_justification    =  0; // 0 = left justified, 1 = right justified (16bit)
byte input_bit_clock_polarity    =  0; // 0 = rising edge, 1 = falling edge
byte input_frame_sync_polarity   =  0; // 0 = LRCI high means left channel, 1 = LRCI low means left channel
byte output_word_length          =  0; // 0 = 16bit, 1 = 18bit, 2 = 20bit, 3 = 24bit
byte output_format               =  1; // 0 = 2s compliment, 1 = COB
byte output_word_clock_polarity  =  1; // 0 = high to low at the end of the output word, 1 = low to high at the end of the output word
byte deglitch_low                = 31; // set falling edge of DG to 0~31st interval
byte deglitch_high               = 25; // set rising edge of DG to 0~31st interval
// end of settings

#include <avr/sleep.h>
#include <debouncetm.h>

#define MUTE           0  // MUTE (HIGH = muted)
#define DITH           1  // DITHER (HIGH = on)
#define MS1            2
#define MS2            3
#define MS3            4
#define MDT            5
#define MEN            6
#define LED            7  // LED (active HIGH)
#define TOSL          10  // TOSLINK (active LOW with 74LVC125, active HIGH with 74LVC126)
#define COAX           9  // COAX (active LOW)
#define BTN            8  // button pin (HIGH = TOSLINK)

#define samplerate   0.75 // poll interval for update() in msec (a safe starting point would be samplerate = 0.1 * bounce-duration in msec)
#define longpress    0.90 // longpress duration in sec
#define doubleclick  0.50 // doubleclick window in sec
#define history         8 // internal history length: 8, 16 or 32 bit

Button button(BTN, INPUT, HIGH, samplerate, longpress, doubleclick, history);  // specify pin, pinMode, polarity, samplerate, longpress duration, doubleclick window

elapsedMillis sleep_timer;
unsigned int sleep_interval = 500;   // mute ramp down takes 260ms, so 300 is a good value here

void setup() {
  delay(1000);
  pinMode(COAX, OUTPUT);
  pinMode(TOSL, OUTPUT);
  pinMode(MUTE, OUTPUT);
  pinMode(LED, OUTPUT);

  pinMode(DITH, OUTPUT);
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(MS3, OUTPUT);
  pinMode(MDT, OUTPUT);
  pinMode(MEN, OUTPUT);

  digitalWrite(LED, LOW);
  digitalWrite(MS1, LOW);
  digitalWrite(MS2, LOW);
  digitalWrite(MS3, LOW);
  digitalWrite(MDT, LOW);
  digitalWrite(MEN, HIGH);
  delay(100);
  digitalWrite(DITH, LOW);
  digitalWrite(MUTE, HIGH);
  digitalWrite(COAX, HIGH);
  digitalWrite(TOSL, LOW);

  delay(100);
  configurePMD100();
  delay(100);
  zeroAttenuationPMD100();
  delay(3000);

  if (!digitalRead(BTN)) {
    digitalWrite(COAX, LOW);
    delay(50);
    digitalWrite(LED, HIGH);
    delay(300);
    digitalWrite(MUTE, LOW);
    delay(300);
  }
  sleep_timer = 0;
}

void loop() {
  button.update();
  if (button.pressed()) {select_TOSL();}
  if (button.released()) {select_coax();}
  if (sleep_timer > sleep_interval) {gotoSleep();}
}

void zeroAttenuationPMD100() {
  digitalWrite(MS1, HIGH);
  digitalWrite(MS2, HIGH);
  delayMicroseconds(1);
  digitalWrite(MEN, LOW);
  delayMicroseconds(1);
  digitalWrite(MEN, HIGH);
  delayMicroseconds(1);
  digitalWrite(MS1, LOW);
  digitalWrite(MS2, LOW);
}

uint32_t value = 0;

void configurePMD100() {
  value = 0;
  value |= uint32_t(dither_mode) << 19;
  value |= uint32_t(output_word_length) << 17;
  value |= uint32_t(input_frame_sync_polarity) << 16;
  value |= uint32_t(deglitch_high) << 11;
  value |= uint32_t(deglitch_low) << 6;
  value |= uint32_t(output_format) << 5;
  value |= uint32_t(output_word_clock_polarity) << 4;
  value |= uint32_t(input_data_justification) << 3;
  value |= uint32_t(input_bit_clock_polarity) << 2;
  value |= uint32_t(oversampling_rate);

  for (int i = 0; i < 24; i++) {
    digitalWrite(MEN, LOW);
    digitalWrite(MDT, bitRead(value, i));
    delayMicroseconds(1);
    digitalWrite(MEN, HIGH);
    delayMicroseconds(1);
  }

  digitalWrite(MDT, LOW);
  delayMicroseconds(4);

  digitalWrite(MS2, HIGH);
  digitalWrite(MS3, HIGH);
  delayMicroseconds(1);

  digitalWrite(MEN, LOW);
  delayMicroseconds(1);
  digitalWrite(MEN, HIGH);

  delayMicroseconds(2);
  digitalWrite(MS2, LOW);
  digitalWrite(MS3, LOW);
}

void select_TOSL() {
  digitalWrite(MUTE, HIGH);
  delay(300);
  digitalWrite(LED, LOW);
  delay(200);
  digitalWrite(COAX, HIGH);
  delay(50);
  digitalWrite(TOSL, HIGH);
  delay(50);
  digitalWrite(LED, HIGH);
  delay(300);
  digitalWrite(MUTE, LOW);
  delay(300);
  sleep_timer = 0;
}

void select_coax() {
  digitalWrite(MUTE, HIGH);
  delay(300);
  digitalWrite(LED, LOW);
  delay(200);
  digitalWrite(TOSL, LOW);
  delay(50);
  digitalWrite(COAX, LOW);
  delay(50);
  digitalWrite(LED, HIGH);
  delay(300);
  digitalWrite(MUTE, LOW);
  delay(300);
  sleep_timer = 0;
}

void gotoSleep() {
  GIMSK |= _BV(PCIE0);
  GIMSK |= _BV(PCIE1);
  PCMSK1 |= _BV(PCINT10);                 // use PB2 as interrupt pin
  // PCMSK0 |= _BV(PCINT4);               // use PA4 as interrupt pin
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
  sleep_enable();                         // sets the sleep enable bit in the MCUCR register (SE BIT)
  sleep_cpu();                            // sleep
  sleep_disable();                        // clear SE bit
  PCMSK1 &= ~_BV(PCINT10);                // turn off PB2 as interrupt pin
  // PCMSK0 &= ~_BV(PCINT4);              // turn off PA4 as interrupt pin
  ADCSRA |= _BV(ADEN);                    // ADC on
  sleep_timer = 0;
}

// ISR(PCINT0_vect) {sleep_timer = 0;}
ISR(PCINT1_vect) {sleep_timer = 0;}
 

Attachments

  • schematic.jpg
    schematic.jpg
    377.4 KB · Views: 72
Member
Joined 2009
Congratulations! And how about the sound improvement? More detailed and more clarity? BTW I think it's not a bad idea to improve the 1K I/V resistors for something more fancy than a regular metal film resistor (and also the 2 last 22 ohm resistors in the signal path).
 
Member
Joined 2009
At least, I mean:

I/V resistors: R24 and R124 (I've tried here with nice results, Vishay Z-Foil, or less expensive, non-magnetic Dale RN60D);

Last two resistors: R23 and R123 (I have here Riken or Allen Bradley 2W).
 
Member
Joined 2020
Ok thanks! And what about these ones:
Screenshot_20240423_004454_Firefox.jpg


Do we also need to upgrade resistors in the DEEM section?

Screenshot_20240423_005025_Firefox.jpg
 
Last edited:
Member
Joined 2009
I've changed every stock resistor you've marked in red for PRP or Takman metal film - it's the signal path.

But, in my humble opinion, the most relevant ones are the I/V resistors and maybe the last ones.

Until now, I've done nothing at all in the DEEM section.
 
Member
Joined 2020
I changed the IV resistors for RN60, a noticeably improvement. The analog transformer buzzed: I don't like that in a quiet room, so I changed it for a toroidal. The sound is again much better, didn't expect that to make such a difference!

Arcam recapped and modded.jpg
 
  • Like
Reactions: 2 users
Top