Browsed by
Category: Arduino

update on arduino class d amplifier

update on arduino class d amplifier

Changing the ADC prescalar to 128 helps enormously with the sound quality.

ADCSRA = 0b10101111;           // AD-converter on, interrupt enabled, prescaler = 128


Now it sounds like a 1970’s AM radio.  If you had a really bad radio in the 1970’s.  But, it is clearly playing music, and some of it is even intelligible.

arduino uno as a class d amplifier

arduino uno as a class d amplifier

Okay, this is really dorky. I ported George Gardiner’s class d amp for an attiny over to an arduino uno. is what he did. Here’s my code, intended for the arduino IDE and an atmega328.

void setup() {
pinMode(9, OUTPUT); //pwm
pinMode(10, OUTPUT);//pwm inverted
pinMode(A3, INPUT); //analog input
// com1a1 is non-inverted, com1b1 and com1b0 is inverted,
TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<< COM1B0) | (1<<WGM10) | (1<<WGM11);
// this should set 10 bit fast pwm mode, set clock full speed
TCCR1B = (1 << WGM12) | (1 << CS10) | (0<<CS11);
OCR1A = 0; // initialize
OCR1B = 0; // initialize
ICR1 = 0b0000001111111111; //set to 10 bit pwm

DIDR0 = 0x3F; // digital inputs disabled
ADMUX = 0x43; // measuring on ADC3, use the internal 1.1 reference

// edited the below line to improve sound quality from appalling to merely terrible
ADCSRA = 0b10101111;           // AD-converter on, interrupt enabled, prescaler = 128
ADCSRB = 0x40; // AD channels MUX on, free running mode
ADCSRA |= (1<<ADSC);  // Start the conversion by setting bit 6 (=ADSC) in ADCSRA
sei(); // set interrupt flag

void loop() {

ISR(ADC_vect) {
uint16_t analog_value;
analog_value = ADCL; // store lower byte ADC
analog_value += ADCH << 8; // store higher bytes ADC
OCR1A = analog_value;
OCR1B = analog_value;

As he did, I put a cap between VREF and ground, and used another cap to couple the audio signal into the arduino’s analog input. The inverted/noninverted PWM outputs from the arduino drive one of those cheap L298 dual h-bridge boards, which drives the speaker.
The best I can say is that it works. Audio is clearly coming out of the speaker, through the noise and clicks.


Short video clip of it allegedly working:

arduino class d amp

Getting Accurate Values from Arduino Analog Pin Measurements

Getting Accurate Values from Arduino Analog Pin Measurements

I am currently building a battery capacity data logging system with the Arduino so that I can get discharge curves. I plan on discharging batteries at different load rates and since I would be spending days to many weeks and even months with each battery in discharge,  I did not think it would be very useful if the data was inaccurate . I was not really surprised to find out how far the raw measurement on the analog pin was from the actual value presented to the pin.  I made a quick two point gain and offset calibration that will correct the measurements and make them fairly accurate.  After implementing the gain and offset calibration I compared the values from the Arduino to the Fluke 87 DMM and across the range I was using (1.5V to 0.6V) I saw no more than a 2mV difference from the multimeter compared to readings from the analog pin.

To use the code you will need to define these variables in the beginning of the program:

int     meas_ch =    0;// channel used for measuring voltage
int     val =        0;//used for averaging the voltage measured    
//using the mega 2560 2.56V internal reference with this command
// "analogReference(INTERNAL2V56);"    
double  ref =     2.56;//reference voltage 
double  Voltage       ;//reported voltage after being averaged
double  average= 1000;//how many measurements to take for averaging
double  gain         ;// gain value for ADC
double  offset       ;// Offset value for ADC

In the setup() routine I placed this code:

  gain = 1.0;
  offset = 0.0;

    //gain = (mv1-mv2)/(fv1-fv2) //fv = force voltage, mv = measure voltage
    //offset = fv2 - (gain * mv2)
    // example:

    //I set a power supply to 1.5V and I used a multimeter to measure the analog pin input 
    //voltage at 1.5v, running the the readV() routine the analog pin measured 1.534V
    //then I set the supply to read on 0.9V the meter and the arduino
    //analog pin measured 0.916v
    //gain = (1.50 - 0.9)/(1.534 - 0.916);
    //offset = 0.90 - (gain*0.916);

// to use this section of code for the first time comment out the gain and offset example  
// below, since you will have different values
    gain = (1.50 - 0.9)/(1.5406 - 0.921);
    offset = 0.90 - (gain*0.921);

The following routine is what I use to read the voltage back from the analog pin defined as ‘meas_ch’:

void readV()
  Voltage =0.0;
  val = analogRead(average);    // read the input pin
  for (int i=0;i<average;i++)
    val = analogRead(meas_ch);    // read the input pin
    Voltage = Voltage +(ref*(double(val)/1023.0));
  Voltage = (Voltage/average);

  Voltage = Voltage*gain+offset;

  //to report back to serial port use the following:
    //   Serial.print"Volts:");             
    //   Serial.println(Voltage,3);         // debug value
 //to report back to LCD use the following:
     //    lcd.setCursor(0, 0);
     //    lcd.print("Volts:");
     //    lcd.print(Voltage,3);

The following is an example of reading the voltage from the analog pin in the main code: (this code is using the the SD card write logfile.print:

  if (Voltage <= 0.6)

In the next blog I will discuss the auto ranging feature that I am adding to the system, so that when I hook up a battery such as a Li-Ion that is at 4V the system will switch in a divider and read the correct voltage.


[wp_bannerize categories=”12,146,4,47,77,58,68,9,102,111,1″]