PMD100 to TDA1541 in smultaneous mode

OK, I wrote some code, it should work fine on any MCU, who wants to test and verify...???

C#:
// for ATtiny84
// pin mapping clockwise

byte prgPin = 1;
byte ms2Pin = 3;
byte ms3Pin = 4;
byte mdtPin = 5;
byte menPin = 6;

// 24bit value for PMD100:
unsigned long value = 0b010000011111111100001000; // OS = 4x, dither mode = 2

void setup() {
  pinMode(menPin, OUTPUT);
  pinMode(mdtPin, OUTPUT);
  pinMode(ms2Pin, OUTPUT);
  pinMode(ms3Pin, OUTPUT);
  pinMode(prgPin, OUTPUT);
  digitalWrite(menPin, HIGH);
  digitalWrite(mdtPin, LOW);
  digitalWrite(ms2Pin, LOW);
  digitalWrite(ms3Pin, LOW);
  delay(100);
  digitalWrite(prgPin, HIGH);
  delay(100);
}

void loop() {
  clockout(value >> 16);
  clockout(value >> 8);
  clockout(value);
  digitalWrite(mdtPin, LOW);
  delayMicroseconds(5);

  digitalWrite(ms2Pin, HIGH);
  digitalWrite(ms3Pin, HIGH);
  delayMicroseconds(1);

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

  delayMicroseconds(1);
  digitalWrite(ms2Pin, LOW);
  digitalWrite(ms3Pin, LOW);
  while (1);
}

void clockout(byte val) {
  for (int i = 0; i < 8; i++)  {
    digitalWrite(menPin, LOW);
    delayMicroseconds(1);
    digitalWrite(mdtPin, !!(val & (1 << (7 - i))));
    delayMicroseconds(1);
    digitalWrite(menPin, HIGH);
    delayMicroseconds(2);
  }
}
 
Last edited:
I simulated the code, see attached screenshot. I needed to change some details (as multiple bit fields such as dither are entered LSB first). The code is now easy to configure for the settings of the PMD100:

C#:
byte prgPin = 1;
byte ms2Pin = 3;
byte ms3Pin = 4;
byte mdtPin = 5;
byte menPin = 6;

// change these settings to configure the PMD100:
byte oversampling_rate           = 2;  // 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               = 0;  // 0 = 2s compliment, 1 = COB
byte output_word_clock_polarity  = 0;  // 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                = 15; // set falling edge of DG to 0~31st interval
byte deglitch_high               = 31; // set rising edge of DG to 0~31st interval
// end of settings

void setup() {
  pinMode(menPin, OUTPUT);
  pinMode(mdtPin, OUTPUT);
  pinMode(ms2Pin, OUTPUT);
  pinMode(ms3Pin, OUTPUT);
  pinMode(prgPin, OUTPUT);
  digitalWrite(menPin, HIGH);
  digitalWrite(mdtPin, LOW);
  digitalWrite(ms2Pin, LOW);
  digitalWrite(ms3Pin, LOW);
  delay(100);
  digitalWrite(prgPin, HIGH);
  delay(100);
}

void loop() {
  clockout();
  while (1);
}

uint32_t value = 0;

void clockout() {
  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(menPin, LOW);
    digitalWrite(mdtPin, bitRead(value, i));
    delayMicroseconds(1);
    digitalWrite(menPin, HIGH);
    delayMicroseconds(1);
  }

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

  digitalWrite(ms2Pin, HIGH);
  digitalWrite(ms3Pin, HIGH);
  delayMicroseconds(1);

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

  delayMicroseconds(2);
  digitalWrite(ms2Pin, LOW);
  digitalWrite(ms3Pin, LOW);
}

The code outputs 010000111101111100001000 which means OS = 4x, dither mode = 2, and simulation confirms that the output is correct:
 

Attachments

  • simul.jpg
    simul.jpg
    28.9 KB · Views: 53
Last edited:
I added two minor changes to the code: output_format must be set to COB, and the volume attenuation registers need to be reset after power on in program mode, so I added zeroAttenuationPMD100(). I tested the code on my Arcam, it worked right away:

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

// 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  =  0; // 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                = 15; // set falling edge of DG to 0~31st interval
byte deglitch_high               = 31; // 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 PROG           1  // HARDWARE MODE: LOW
#define MS1            2  // HARDWARE MODE: LOW
#define MS2            3  // HARDWARE MODE: LOW
#define MS3            4  // HARDWARE MODE: LOW
#define MDT            5  // HARDWARE MODE: LOW
#define MEN            6  // HARDWARE MODE: LOW
#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

Button button(BTN, INPUT, HIGH, samplerate, longpress, doubleclick);  // 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(PROG, 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(PROG, HIGH);
  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;}

This is the complete code as I run it on my board in the Arcam. This library is necessary to compile the code. Dither mode 2 sounds so much better BTW :cool:
 
Last edited:
The code is now easy to configure for the settings of the PMD100
Hi
Thanks :)
Yes the pretty much clear instructions about 24bit serial control word in PM datasheets :) in the first version was also OK.
somehov I think it will icrease the memory and maybe it is not be enough for the 44 version? Did You compile last version?
is it file avr/sleep.h already present in lib? I download the debouncem.h from the link thanks
#include <avr/sleep.h>
#include <debouncetm.h>
.
 
No, only the last version of the code works. You need to set attenuation to zero after power on in program mode, and the multi bit fields are LSB first.

Sleep.h is standard Arduino if ATtiny cores are installed.

And you also need debouncetm.cpp!

As I said: compile and you will see the messages about memory. You don't need to buy the chip in order to be able to compile.
 
Last edited:
I recommend you program the Uno and connect it to the PMD100
I cant do it without hardware connection diagram?
Didnt catch that could work for Arduino and PMD only without ATtiny
later you can migrate to ATtiny
I dont understand sorry, original idea is
1. to use arduino to "burn" the control code in ATtiny. Connected With PMD100
2. After setting ATtiny remove arduino.
3. ATtiny will control PMD100 with last uploaded code settings...
.
I tried to verify code, after importing debouncetm-master zip library
and get error:
.
Arduino: 1.8.19 (Mac OS X), Board: "Arduino Uno"
In file included from /Users/zoran/Documents/Arduino/sketch_PMD100_00/sketch_PMD100_00.ino:1:0:
/Users/zoran/Documents/Arduino/libraries/debouncetm-master/debouncetm.h:7:10: fatal error: elapsedMillis.h: No such file or directory
#include "elapsedMillis.h"
^~~~~~~~~~~~~~~~~
compilation terminated.
exit status 1
Error compiling for board Arduino Uno.
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
 
I cant do it without hardware connection diagram?
Didnt catch that could work for Arduino and PMD only without ATtiny

As I said: any mcu can program the PMD100, just wire it accordingly or change the pin numbers in the code.

I dont understand sorry, original idea is
1. to use arduino to "burn" the control code in ATtiny. Connected With PMD100
2. After setting ATtiny remove arduino.
3. ATtiny will control PMD100 with last uploaded code settings...

Good, you are almost there! But you can also use the Uno instead if you don't need the additional step of programming the ATtiny from the Uno.

elapsedMillis.h: No such file or directory

You need ATtiny core if you want to use ATtiny, and if elapsedMillis is missing you need to install that library as well: open the library manager in the IDE and install these 2 items from there.

I tried to verify code, after importing debouncetm-master zip library
and get error

You have to unpack the ZIP into your library folder: please check online tutorials on how to do all of this :cool: (y)
 
I download
from this link https://www.arduinolibraries.info/libraries/elapsed-millis
and this error gone
.
I put in comment line // C#:
.
but there is some error and i cant vrify the cript :(
.
Arduino: 1.8.19 (Mac OS X), Board: "Arduino Uno"
/Users/zoran/Documents/Arduino/PMD100_ATtiny84_00/PMD100_ATtiny84_00.ino: In function 'void gotoSleep()':
PMD100_ATtiny84_00:179:3: error: 'GIMSK' was not declared in this scope
GIMSK |= _BV(PCIE0);
^~~~~
/Users/zoran/Documents/Arduino/PMD100_ATtiny84_00/PMD100_ATtiny84_00.ino:179:3: note: suggested alternative: 'EIMSK'
GIMSK |= _BV(PCIE0);
^~~~~
EIMSK
exit status 1
'GIMSK' was not declared in this scope
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
 
As I said: install the elapsedMillis library from the IDE, and as I said delete the sleep calls for the Uno :cool:

This is basic stuff - if you use Arduino for the first time google is your friend (y)

EDIT: you can import a library known to the IDE from the library manager, or import a ZIP there, or manually import a library by unpacking the ZIP into the library folder. All of this works, but the IDE needs a restart...

EDIT2: link for elapsedMillis

EDIT3: delete the lines "#include <avr/sleep.h>" and "if (sleep_timer > sleep_interval) {gotoSleep();}" and everything down from the line "void gotoSleep() {", because the ATTiny sleep routines don't work on the Uno.

EDIT4: the "C#" is a forum feature to display the code in colors, don't include it in the code!
 
Last edited:
I put to comments all lines that You are noted but I didnt verify the csript :(
.
message:
fork/exec /Users/zoran/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++: no such file or directory
Error compiling for board Arduino Uno.
.
this is some internal issue of the software... I think that is not coming from code? :( I will try to older versions of Arduino software at other computer...
.
How to connect Arduino Uno with PMD100?
.
BTW i dont want to keep Arduino uno inside the dac because it is little bigger...
.
 

Attachments

  • PMD100_ATtiny84_00.ino.zip
    2.3 KB · Views: 44
  • debouncetm-master.zip
    3.8 KB · Views: 52
  • elapsedMillis-1.0.6.zip
    12.1 KB · Views: 52
Last edited:
You might have a problem with your installation. Remove all IDEs (including non-Arduino!), delete all left over files and folders (especially stuff like preferences!!), restart your computer, install again. It seems MacOS has this kind of problem with Arduino. BTW on my Linux machine the location of "avr-g++" is in [drive/folder]/arduino/hardware/tools/avr/bin.

Can you compile successfully the "blink" example? It's in menu/examples/basic/blink, you need to get your system compile this first.

EDIT: your code compiles fine on my system!

EDIT2: found a small bug in debouncetm.h, please download new version!

EDIT3: see attached screen shot of the board manager:
 

Attachments

  • boardmanager.jpg
    boardmanager.jpg
    138.6 KB · Views: 68
Last edited:
Yes, but you need not only debouncetm.h, you need the *.h and the *.cpp file... The location of your libraries you can find in the prefs dialog of Arduino IDE.

EDIT: before you re-install please make sure your system is up to date and stable, and google all error messages so that you know prior to installation if there is a version conflict.

Wow, I am so glad that these kind of problems I never had since I moved to Linux some 15 years ago...