Robert Fletcher
Published

Build an Arduino-Controlled Analog Audio VCA with SSM2018S

Explore voltage-controlled analog audio with the Analog Devices SSM2018S VCA and an Arduino-compatible controller.

IntermediateWork in progress18
Build an Arduino-Controlled Analog Audio VCA with SSM2018S

Things used in this project

Hardware components

Analog Devices SSM2018S
Main voltage-controlled audio amplifier; verify the exact package and pinout against its datasheet
×1
Arduino Uno R4 WiFi
Arduino Uno R4 WiFi
Generates digital control commands
×1
MCP4725 DAC module
Converts I²C commands into an analog control voltage
×1
Dual regulated bench power supply
Provides the required positive and negative analog rails
×1
Audio signal generator
Supplies a repeatable test signal
×1
Oscilloscope
Monitors input and output waveforms
×1
Audio source and line-level output connectors
Connect the circuit to test equipment
×1
Precision resistors and ceramic capacitors
Implement the manufacturer reference circuit and supply decoupling
×1
Breadboard
Assembly; a PCB is preferable for low-noise testing
×1
Jumper wires and connectors
Signal and control connections
×1

Software apps and online services

Arduino IDE
Arduino IDE
upload the controller firmware.
KiCad
KiCad
draw the schematic and design a PCB.
Oscilloscope software
inspect waveforms and compare signal levels.
Analog Devices SSM2018 datasheet
use the manufacturer's reference circuit for the analog stage.

Hand tools and fabrication machines

Digital multimeter
Oscilloscope probes and test leads
Soldering iron (generic)
Soldering iron (generic)
solder
Wire cutters
wire strippers
Tweezers
small screwdrivers

Story

Read more

Code

Code

C/C++
#include <Wire.h>
#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 dac;

// MCP4725: commonly 0x60 or 0x61, depending on board
const uint8_t DAC_ADDRESS_UNUSED = 0x60;

// Calibrate this for your DAC reference and analog scaling circuit.
// These values represent the desired voltage at the SSM2018 VC pin,
// NOT necessarily the raw MCP4725 output voltage.
const float VC_MIN = 0.0;  // Unity gain
const float VC_MAX = 2.4;  // Example attenuation target; verify hardware

const float ATTENUATION_MIN_DB = 0.0;
const float ATTENUATION_MAX_DB = 80.0;

float clampFloat(float x, float lo, float hi) {
  if (x < lo) return lo;
  if (x > hi) return hi;
  return x;
}

// Datasheet control law is approximately -30 mV/dB.
// Positive VC therefore produces attenuation.
// This conversion describes the desired VC voltage.
float attenuationToVC(float attenuationDb) {
  attenuationDb = clampFloat(
    attenuationDb,
    ATTENUATION_MIN_DB,
    ATTENUATION_MAX_DB
  );

  return attenuationDb * 0.030;
}

// Convert a desired VC voltage to a 12-bit DAC code.
// This simple version assumes a 0-5 V DAC output and a
// direct connection, which is ONLY appropriate if the external
// scaling/buffer circuit has been designed for that relationship.
uint16_t voltageToDacCode(float voltage, float dacFullScale = 5.0) {
  voltage = clampFloat(voltage, 0.0, dacFullScale);
  return (uint16_t)(voltage * 4095.0 / dacFullScale + 0.5);
}

void setAttenuation(float attenuationDb) {
  float targetVC = attenuationToVC(attenuationDb);

  // In a real build, convert targetVC to the raw DAC voltage
  // using the measured transfer function of your scaling circuit.
  float rawDacVoltage = targetVC;

  uint16_t code = voltageToDacCode(rawDacVoltage);
  dac.setVoltage(code, false);

  Serial.print("Requested attenuation: ");
  Serial.print(attenuationDb, 1);
  Serial.print(" dB, target VC: ");
  Serial.print(targetVC, 3);
  Serial.print(" V, DAC code: ");
  Serial.println(code);
}

void setup() {
  Serial.begin(115200);
  Wire.begin();

  // Set the I2C address appropriate for your DAC board.
  dac.begin(0x60);

  setAttenuation(0.0);

  Serial.println();
  Serial.println("SSM2018 VCA controller");
  Serial.println("Enter attenuation in dB, from 0 to 80.");
}

void loop() {
  if (Serial.available()) {
    float attenuationDb = Serial.parseFloat();

    if (attenuationDb >= ATTENUATION_MIN_DB &&
        attenuationDb <= ATTENUATION_MAX_DB) {
      setAttenuation(attenuationDb);
    } else {
      Serial.println("Enter a value between 0 and 80 dB.");
    }

    while (Serial.available()) {
      Serial.read();
    }
  }
}

Credits

Robert Fletcher
22 projects • 1 follower
Senior semiconductor engineer at AVAQ Semiconductor. With extensive experience in electronic components, supply chain solutions & sourcing.

Comments