Jump to content

digital attenuator


kevin gilmore

Recommended Posts

Here's the code I used for the rotary encoder and the Uno board:

#include "Wire.h"

// Set up the Variables

int state, prevstate = 0;

int nextEncoderState[4] = { 2, 0, 3, 1 }; int prevEncoderState[4] = { 1, 3, 0, 2 };

const byte attSetAddr = 0x38;

const byte attResetAddr = 0x3E;

const int offVolume = 100;

const int relayLatchTime = 3; // time in milliseconds

int newVol = offVolume ;

void setup() {

delay(1000);

Wire.beginTransmission(attResetAddr);

Wire.send(0);

Wire.send(0xFF);

Wire.endTransmission();

delay(relayLatchTime);

Wire.beginTransmission(attResetAddr);

Wire.send(0);

Wire.send(0x00);

Wire.endTransmission();

delay(relayLatchTime);

// Set up for the encoder

pinMode(5, INPUT);

pinMode(6, OUTPUT);

pinMode(7, INPUT);

digitalWrite(5, HIGH);

digitalWrite(6, LOW);

digitalWrite(7, HIGH);

}

void SetVolume(int newVol)

{

int sendVal;

// now set

sendVal = (byte)newVol ;

Wire.begin();

Wire.beginTransmission(attSetAddr);

Wire.send(0);

Wire.send(sendVal);

Wire.endTransmission();

delay(relayLatchTime) ;

Wire.begin();

Wire.beginTransmission(attSetAddr);

Wire.send(0);

Wire.send(0x00);

Wire.endTransmission();

delay(relayLatchTime);

// now reset

sendVal = 0xFF ^ (byte)newVol;

Wire.beginTransmission(attResetAddr);

Wire.send(0);

Wire.send(sendVal);

Wire.endTransmission();

delay(relayLatchTime);

Wire.beginTransmission(attResetAddr);

Wire.send(0);

Wire.send(0x00);

Wire.endTransmission();

delay(relayLatchTime);

}

void loop() {

state = (digitalRead(7) << 1) | digitalRead(5);

if (state != prevstate) {

if (state == nextEncoderState[prevstate]) {

newVol = newVol + 1;

if (newVol > 255)

newVol = 255;

SetVolume(255-newVol);

} else if (state == prevEncoderState[prevstate]) {

newVol = newVol - 1;

if (newVol < offVolume)

newVol = offVolume;

SetVolume(255-newVol);

}

prevstate = state;

}

}

EDIT:

Quick thanks to Max and the Arduino site where I got some of the code segments :)

Edited by Kerry
  • Like 1
Link to comment
Share on other sites

Has anyone ever tried interfacing a Krell CAST DAC or Audio-Gd ACSS DAC to a KGSS/KGSSHV amp, keeping the signal in the current domain? I would think that whatever you used for a volume control- be it pot, stepped attenuator, resistor/relay or IC switches would have less sonic impact in an ACSS circuit than using a voltage divider in a voltage-domain circuit..... and in using current domain signals you eliminate the I-to-V stage in the DAC, any stage eliminated is a stage that can't screw with the sound.

Link to comment
Share on other sites

Too specific for most people. Besides which, cast does not really work the way

you might think it does. Raw current out of a dac is unusable with even a few

feet of cable, and then there are all the offsets that need to be controlled.

Yeah, you are right, for the ~7(?) DACs or analog preamps out there that have CAST or ACSS outputs, I guess developing a current domain input for the KGSS/KGSSHV would be a fair bit of work that would benefit very few users. While from all accounts it seems this signal transmission scheme does offer real sonic benefit, it is used by almost no one.

Link to comment
Share on other sites

Just wanted to post where I'm at with testing/shipping. Here's everything shipped. I should have some time this weekend to get through some more testing.

Name - Assembled - Bare Boards - Shipped

Beefy - 0 - 4 - S

blubliss - 4 - 0 - S

cetoole - 0 - 10 - S

chinsettawong - 2 - 0 - S

deadlylover - 0 - 6 - S

deepak - 2 - 2 - S

DouglasQuaid - 2 - 2 - S

Fitz - 0 - 4 - S

Flyingsparks - 0 - 4 - S

Horio - 0 - 2 - S

johnwmclean - 0 - 2 - S

kevin gilmore - 4 - 4 - S

luvdunhill - 0 - 2 - S

nattonrice - 0 - 4 - S

Pars - 0 - 2 - S

Shaman - 0 - 2 - S

tcpoint - 0 - 2 - S

Vortex - 0 - 10 - S

Edited by Kerry
Link to comment
Share on other sites

Here is code to control two attenuators (i.e. balanced) with a potentiometer. Wire pot pins 1 and 3 to 5v and ground, and the wiper to A0 (analog input 0). Should work for a single board as well. For balanced configuration jumper id pins 1 and 2 of the second board (to change the address). Based on the code previously posted by Kerry.

#include <Wire.h>

int sensorPin = A0; // select the input pin for the potentiometer

const byte attSetAddr0 = 0x38;

const byte attSetAddr1 = 0x39;

const byte attResetAddr0 = 0x3E;

const byte attResetAddr1 = 0x3F;

const int numReadings = 12; // max readings for averaging

const int relayLatchTime = 3; // time in milliseconds

const int maxVolVariation = 3; // max allowed variation in pot voltage without changing volume

int oldVol = 0;

int readings[numReadings]; // array to hold volumes (for averaging)

int readIdx = 0; // readings array index

void setup()

{

for (int i = 0; i < numReadings; ++i)

readings = GetVolumeSetting();

oldVol = GetVolumeSetting();

delay(1000);

}

void WireTrans(boolean begin, byte addr, int range)

{

if (begin)

Wire.begin();

Wire.beginTransmission(addr);

Wire.send(0);

Wire.send(range);

Wire.endTransmission();

delay(relayLatchTime);

}

void SetVolume(int newVol)

{

// comment out if volume control works in reverse

//newVol = 255 - newVol;

// put avg volume into newVol

readings[readIdx] = newVol;

readIdx += 1;

if (readIdx == numReadings)

readIdx = 0;

int total = 0;

for (int i = 0; i < numReadings; ++i)

total += readings;

newVol = total/numReadings;

// include only if relay chatter because of pot voltage variations

if (abs(newVol-oldVol) < maxVolVariation)

return;

oldVol = newVol;

// now set

int sendVal = (byte)newVol;

WireTrans(true, attSetAddr0, sendVal);

WireTrans(true, attSetAddr0, 0);

WireTrans(true, attSetAddr1, sendVal);

WireTrans(true, attSetAddr1, 0);

// now reset

sendVal = 0xFF ^ (byte)newVol;

WireTrans(false, attResetAddr0, sendVal);

WireTrans(false, attResetAddr0, 0);

WireTrans(false, attResetAddr1, sendVal);

WireTrans(false, attResetAddr1, 0);

}

int GetVolumeSetting()

{

// pot reading

int sensorValue = analogRead(sensorPin);

// convert to new range

sensorValue = map(sensorValue, 0, 1023, 0, 255);

return sensorValue;

}

void loop()

{

SetVolume(GetVolumeSetting());

delay(50);

}

Hope this helps,

Steve

Edited by stv1756
Link to comment
Share on other sites

Here is working code for volume plus balance:

#include <Wire.h>

int sensorPinVol = A0; // select the input pin for the volume potentiometer

int sensorPinBal = A1; // select the input pin for the balance potentiometer

const byte attSetAddr0 = 0x38;

const byte attSetAddr1 = 0x39;

const byte attResetAddr0 = 0x3E;

const byte attResetAddr1 = 0x3F;

const int numReadings = 12; // max readings for averaging

const int relayLatchTime = 3; // time in milliseconds

const int maxPotVariation = 4; // max allowed variation in pot voltage without changing volume

int currVol = 0;

int currBal = 0;

int volReadings[numReadings]; // array to hold volumes (for averaging)

int balReadings[numReadings]; // array to hold balances (for averaging)

int volReadIdx = 0; // readings array index for volume

int balReadIdx = 0; // readings array index for balance

void setup()

{

for (int i = 0; i < numReadings; ++i)

{

volReadings = GetVolumeSetting();

balReadings = GetBalanceSetting();

}

currVol = GetVolumeSetting();

currBal = GetBalanceSetting();

delay(1000);

}

void SetVolume(int newVol)

{

boolean balChanged = SetCurrBalance(GetBalanceSetting());

// put avg volume into newVol

volReadings[volReadIdx] = newVol;

volReadIdx += 1;

if (volReadIdx == numReadings)

volReadIdx = 0;

int total = 0;

for (int i = 0; i < numReadings; ++i)

total += volReadings;

newVol = total/numReadings;

// include only if relay chatter because of pot voltage variations

if (abs(newVol-currVol) < maxPotVariation)

{

if (balChanged)

newVol = currVol;

else

return;

}

else

currVol = newVol;

int leftVol;

int rightVol;

if (currBal < 125)

{

leftVol = newVol;

rightVol = newVol*127/currBal;

if (rightVol > 255)

rightVol = 255;

}

else if (currBal > 130)

{

leftVol = newVol*127/(255-currBal);

rightVol = newVol;

if (leftVol > 255)

leftVol = 255;

}

else

{

leftVol = newVol;

rightVol = newVol;

}

// now set

int sendLeftVal = (byte)leftVol;

int sendRightVal = (byte)rightVol;

WireTrans(true, attSetAddr0, sendLeftVal);

WireTrans(true, attSetAddr0, 0);

WireTrans(true, attSetAddr1, sendRightVal);

WireTrans(true, attSetAddr1, 0);

// now reset

sendLeftVal = 0xFF ^ (byte)leftVol;

sendRightVal = 0xFF ^ (byte)rightVol;

WireTrans(false, attResetAddr0, sendLeftVal);

WireTrans(false, attResetAddr0, 0);

WireTrans(false, attResetAddr1, sendRightVal);

WireTrans(false, attResetAddr1, 0);

}

boolean SetCurrBalance(int newBal)

{

// put avg balance into newBal

balReadings[balReadIdx] = newBal;

balReadIdx += 1;

if (balReadIdx == numReadings)

balReadIdx = 0;

int total = 0;

for (int i = 0; i < numReadings; ++i)

total += balReadings;

newBal = total/numReadings;

// include only if relay chatter because of pot voltage variations

if (abs(newBal-currBal) < maxPotVariation)

return false; // currBal remains the same

currBal = newBal;

return true;

}

void WireTrans(boolean begin, byte addr, int range)

{

if (begin)

Wire.begin();

Wire.beginTransmission(addr);

Wire.send(0);

Wire.send(range);

Wire.endTransmission();

delay(relayLatchTime);

}

int GetVolumeSetting()

{

// pot reading

int sensorValue = analogRead(sensorPinVol);

// convert to new range

sensorValue = map(sensorValue, 0, 1023, 0, 255);

return sensorValue;

}

int GetBalanceSetting()

{

// pot reading

int sensorValue = analogRead(sensorPinBal);

// convert to new range

sensorValue = map(sensorValue, 0, 1023, 255, 0);

return sensorValue;

}

void loop()

{

SetVolume(GetVolumeSetting());

delay(75);

}

BTW, these attenuators sound fantastic. Many thanks to Dr. Gilmore and Kerry!

Steve

Link to comment
Share on other sites

Here's the list of what is shipped:

Name - Assembled - Bare Boards

Beefy - 0 - 4

blubliss - 4 - 0

cetoole - 0 - 10

chinsettawong - 2 - 0

deadlylover - 0 - 6

deepak - 2 - 2

DouglasQuaid - 2 - 2

Driftwood - 4 - 6

dwhat - 4 - 0

el_doug - 2 - 0

Fing - 2 - 0

Fitz - 0 - 4

Flyingsparks - 0 - 4

guzziguy - 2 - 0

Horio - 0 - 2

Jezz - 2 - 2

jgazal - 2 - 0

johnwmclean - 0 - 2

Kerry - 2 - 10

kevin gilmore - 4 - 4

luvdunhill - 0 - 2

manaox2 - 2 - 0

MASantos - 2 - 2

Max - 1 - 0

minivan - 2 - 0

n3rdling - 4 - 0

nattonrice - 0 - 4

Nebby - 4 - 0

Pars - 0 - 2

Samuel - 2 - 0

Shaman - 0 - 2

tcpoint - 0 - 2

Vortex - 0 - 10

EDIT:

Got the last two out as well:

spritzer - 4 - 0

ujamerstand - 4 - 0

Edited by Kerry
Link to comment
Share on other sites

Was there a BOM posted? I looked thru the thread, but may have missed it.

Link to the BOM:

here

It's hard to find all the resistor values from the same manufacturer. I also couldn't find 2 values in the appropriate package or in small quantities. Manufacturers are mixed and matched right now, but all of them are ±100ppm/°C, 1% 1W thick film resistors. Also need the value for the compensation caps. If anyone wants to edit or have it in a excel file let me know.

I used the IRC thin film with .1% tolerance.

Here's the actual values and part numbers.

RP7 15 PFC-W1206R-03-15R0-B

RP6 619 PFC-W1206LF-03-6190-B

RP5 4530 PFC-W1206LF-03-4531-B

RP4 15800 PFC-W1206LF-03-1582-B

RP3 41200 PFC-W1206LF-03-4222-B

RP2 93100 PFC-W1206LF-03-9312-B

RP1 196000 PFC-W1206LF-03-2003-B

RP0 402000 PFC-W1206LF-03-4023-B

RS0 1330 PFC-W1206LF-03-1331-B

RS1 2610 PFC-W1206LF-03-2611-B

RS2 4910 PFC-W1206LF-03-4991-B

RS3 8870 PFC-W1206LF-03-8661-B

RS4 14300 PFC-W1206LF-03-1372-B

RS5 20000 PFC-W1206LF-03-2002-B

RS6 23200 PFC-W1206LF-03-2322-B

RS7 23700 PFC-W1206LF-03-2372-B

RT 24300 PFC-W1206LF-03-2432-B

edited according ujamerstand

Edited by jgazal
Link to comment
Share on other sites

Here's the actual BOM I used to order:

Quantity Ref Def Item Description Mouser Digikey

2 PCF8574AN 595-PCF8574AN 296-13106-5-ND

2 ULN2803AN 595-ULN2803AN 296-19046-5-ND

8 G6SK-2F-DC12 653-G6SK-2F-DC12 Z2666-ND

1 ID 1x3 Pin Header - .1" 571-5-146276-3

1 Mini Jumper 151-8010-E

2 Terminal block 651-1935190

2 PCF8574AN DIP-16 Socket 4816-3004-CP

Resistors

2 3.3K Resistor Pack 71-CSC09A01-3.3K

2 Pullup 3.3K Pullup Resistors CRCW12063K30FKEA

Input Z = 24K

Qty, Ref, Value, Part #

2 RP7 15 PFC-W1206R-03-15R0-B

2 RP6 619 PFC-W1206LF-03-6190-B

2 RP5 4530 PFC-W1206LF-03-4531-B

2 RP4 15800 PFC-W1206LF-03-1582-B

2 RP3 41200 PFC-W1206LF-03-4222-B

2 RP2 93100 PFC-W1206LF-03-9312-B

2 RP1 196000 PFC-W1206LF-03-2003-B

2 RP0 402000 PFC-W1206LF-03-4023-B

2 RS0 1330 PFC-W1206LF-03-1331-B

2 RS1 2610 PFC-W1206LF-03-2611-B

2 RS2 4910 PFC-W1206LF-03-4991-B

2 RS3 8870 PFC-W1206LF-03-8661-B

2 RS4 14300 PFC-W1206LF-03-1372-B

2 RS5 20000 PFC-W1206LF-03-2002-B

2 RS6 23200 PFC-W1206LF-03-2322-B

2 RS7 23700 PFC-W1206LF-03-2372-B

2 RT 24300 PFC-W1206LF-03-2432-B

Capacitors

2 100uF 661-EKZE250ELL101MH0

EDIT:

The three pin header was a bit tall and I had to trim it down.

Edited by Kerry
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.