Arduino library for PAM8803 audio amplifier module

PAM8803 audio amplifier module:

/*
 Example: Control a PAM8803 amplifier module from an Arduino board.
 Created by Diego J. Arevalo, November 20, 2012.
 Released into the public domain.
 */

#include <Pam8803.h>

int ampVolumeDownPin = 8; // The pin number of the volume down pin.
int ampVolumeUpPin = 9; // The pin number of the volume up pin.
int ampResetPin = 10; // The pin number of the reset pin.

/*
Create an instance of the Pam8803 class.
 1st parameter: Reset pin number.
 2nd parameter: Volume Up pin number.
 3rd parameter: Volume Down pin number.
 */

Pam8803 pam8803(ampResetPin, ampVolumeUpPin, ampVolumeDownPin);

void setup() {
  //Initializes the amplifier module.
  pam8803.reset();
  //Set amplifier volumen in half output power.
  pam8803.setVolume(50);
}

void loop() {
};

Documentation:
http://www.poweranalog.com/pdf/PAM8803.pdf

Unzip Pam8803.zip into your library folder and that's all. There is a .volumeUp, .volumenDown that raise 1 level or decrement in 1 level the volume. You will have a getVolume to get the current level and setVolumen which accepts from 0 to 100 in percentage. Check Pam8803.h for more details or just ask me. Hope this helps.

PD: Thanks robtillaart for your suggestions. They were considered and implemented.

Pam8803.zip (4.65 KB)

Thanks for sharing!

Simpler implementation?

  • getting rid of the if
int Pam8803::setVolumeInHalf()
{
  while (_gainSetting <= MAX_GAIN_SETTING/2) volumeUp();
  while (_gainSetting >= MAX_GAIN_SETTING/2) volumeDown();
  return MAX_GAIN_SETTING/2;
}

why not a generic percentage function, ?

int Pam8803::setVolumePercentage(int percentage)
{
  int newGain = (MAX_GAIN_SETTING * percentage)/100;
  newGain = constrain(newGain , MIN_GAIN_SETTING, MAX_GAIN_SETTING);  // to prevent 125% ;)
  while (_gainSetting <= newGain ) volumeUp();
  while (_gainSetting >= newGain ) volumeDown();
  return newGain;
}

// the setVolumeInHalf() becomes then much simpler
int Pam8803::setVolumeInHalf()
{
  return setVolumePercentage(50);
}

better is to return _gainsetting; in both cases as that is the inner state representing the volume.

I've thought for a while about this variation which removes (some of) the function calls, and unneeded tests,

int Pam8803::setVolumeInHalf()
{
  while (_gainSetting <= MAX_GAIN_SETTING/2) 
  {
    setGainSetting(_volumeUpPin);
    _gainSetting++;
  }
  while (_gainSetting >= MAX_GAIN_SETTING/2) 
  {
    setGainSetting(_volumeDownPin);
    _gainSetting--;
  }
  return _gainSetting;
}

but most of the time is spent in setGainSetting(_volumeDownPin); so the above would not be a significant optimization @ cost of readability.

This line is trouble delay(30*3.5); == delay(105); but the compiler will optimize that. The real trouble is delay() blocks, and for 0.105 second.
If I want to put the volume to the max it blocks 64 x 0.105 sec == 6.5 seconds!! That is way to long imho, but for now this works.

idea for release 0.2

Imagine a timer interrupt that runs in the background and takes care of setting the volume.
It has the following pseudo code. (not engineered just a concept)

void TimerISR()
{
  if (_gainSetting < desiredGain)
  {
    digitalWrite(volumePinUp, HIGH);  // This takes care of the pulse run it with pen and paper
    digitalWrite(volumePinUp, LOW);  
    rescheduleISR(105);
    return;
  }
  if (_gainSetting > desiredGain)
  {
    digitalWrite(volumePinDown, HIGH);  
    digitalWrite(volumePinDown, LOW);  
    rescheduleISR(105);
    return;
  }
 // reached the desired gain => pull pins HIGH and stop timer interrupt
  digitalWrite(volumePinUp, HIGH);  
  digitalWrite(volumePinDown, HIGH);  
  stopISR();
}

The other functions only need to set the (volatile!!) desiredGain variable and start the ISR() routine [if not started] to adjust the volume in the background.

this gives the lib + responsiveness + allows the arduino to do other things simultaneously like leds and servo's and ...
This allows to set the volume to MAX or a certain percentage and change your mind directly and set a new desired gain value. ==> responsiveness

void setVolume(percentage)
{
  desiredGain = percentage * max;
  if (not started ISR) startISR();
}

Got the idea? (but then again for a next release)

Updated the library. Now, I will work in improve the volumeUp and Down speed without modify class protocol.

Nice contribution, thanks.

By the way I did a quick search on ebay for this type module and found a very nice one that already has terminal connectors and jacks and has an internal voltage regulator so you can power it from either batteries or external DC wall wart type power packs. It also has solder pads to make all connections via wires instead of the on board terminals and jacks, so it would still be easy to connect to an arduino. The price is just $5.78 with free shipping. I've bought from this ebay seller many times and have never been disappointed with any of their offerings. So I bought one.
:wink:

http://www.ebay.com/itm/350604041652?ssPageName=STRK:MEWAX:IT&_trksid=p3984.m1423.l2649

Lefty

Updated the library. Now, I will work in improve the volumeUp and Down speed without modify class protocol.

Link to the latest version?

Latest lib could be downloaded from the very first post once you are logged in. About the PAM8803 retrolefty found, the price is great but the size is huge. In my laser gun project size and room are quite important. The one I bought, even If the a few bucks expensive, only measures 2cm x 2xm and could be powered from 3.3 up to 6 v.

In the library code:

void Pam8803::setDigitalVolumeControl(int volumePin){
  digitalWrite(volumePin, LOW);
  delay(30*3.5);
  digitalWrite(volumePin, HIGH);
  delay(30);
};

What is the purpose of the 3.5 value in the delay statement? Would that cause the floating point math library to be pulled into the compiled code? Why not just a delay(105) statement?

Lefty

That's why each volume transition requires VUP or VDOWN pin remais active low for 3.5 clock cycles. I did not floating point lib would be included. I will put 105 instead. Thanks for the suggestion.

This library and module work nicely.

However, I'm using it for a clock like application for late night use, and even at the lowest setting

pam8803.setVolume(0);

it's still far too loud...

It's definitely adjusting by setVolume. setVolume(100) is very loud, and setVolume(10) is modest, but even setVolume(0) is far louder than what a simple PWM out would be (which is what I was using before). I had assumed that setVolume(0) would effectively be no volume.

I'm wondering if I'm using it correctly?

I'm using this one:
http://www.ebay.com/itm/2-2W-4O-PAM8803-Class-D-Audio-Amplifier-Board-Simple-/350604041652?pt=US_Home_Audio_Amplifiers_Preamps&hash=item51a1a11db4

--creatrope

on this module:

http://www.ebay.com/itm/2-2W-4O-PAM8803-Class-D-Audio-Amplifier-Board-Simple-/350604041652?pt=US_Home_Audio_Amplifiers_Preamps&hash=item51a1a11db4

There's no explicit access to the reset RST pin as far as I can see, which pam8803.reset() invokes. It's apparently pin 15 on the main chip, and it's brought out to a tiny switch on this module instead.

Is the ability to reset critical? If so it could be wired into this module.

-creatrope

That's the problem. reset() method set the vol to level close to 20%. That's why setVolumen(10) is not working the proper way.

the-rebel-agent:
That's the problem. reset() method set the vol to level close to 20%. That's why setVolumen(10) is not working the proper way.

Sorry, I'm still not clear.

The volumes are decreasing noticeably as setVolume decreases.

setVolume(100) is quite loud - even distorted. setVolume(10) is still pretty loud. and setVolume(0) is more amplified than I'd expected.

I've manually wired in the reset pin, however, is my understanding correct that the setVolume() parameter is a percentage of the entire range of the amplifier?

e.g. setVolume(0) is OFF and setVolume(100) is maximum? It's not a percentage of the previous state or something odd like that is it?

If so, I'd expect setVolume(0) to be (nearly) silent no matter whether I've reset() the module or not? Is setVolume(0) silent for you?
If yours is silent at setVolume(0) then something is off with my unit or setup.

thanks! --creatrope

As a matter of fact, If you don't reset the module, internal volume won't match internal variable holding the current volume. So, If you decrement the vol without reset or initialize the module, you will suffer of weird behavior like this. I will re-check the source code and will post some recommendations.

I was able to get nearly inaudible levels to reasonable amplification by putting a fairly large resistor inline from the speaker pin output to the input of the PAM8803. It still surprises me that level 0 is so loud, but at least I have a work-around.

I was able to ensure a reset by soldering a pin to connect to the arduino to the reset pin input.

-creatrope

On the 3W PAM8803 module, which PIN is the RST input?

http://www.ebay.com/itm/Mini-2-3W-4-PAM8803-Class-D-Audio-Amplifier-Board-For-PIC-AVR-/170850491058
I'm not seeing a schematic from the board pinout.

There's 3 different GND inputs - I've tried all of them.

thanks!

Since there is no obvious reset RST pin brought out to the connectors I used the SDN pin. This pin must be kept at logic high for the module to work. The library reset function toggles it low then leaves it high so it appears to be acting as a Reset.

Pin 15 RST on the pam8803 module i have appears to be tied to GND on my module so I don't see how'd you'd use that as a reset and retain the connection to GND.

If you are getting unreliable odd behavior as I did try putting LEDs on Vup, Von, and reset and make sure you can see them changing.

-rrhb

creatrope:
On the 3W PAM8803 module, which PIN is the RST input?

http://www.ebay.com/itm/Mini-2-3W-4-PAM8803-Class-D-Audio-Amplifier-Board-For-PIC-AVR-/170850491058
I'm not seeing a schematic from the board pinout.

There's 3 different GND inputs - I've tried all of them.

thanks!

Yes, I used SND too as reset method.

So, I'm trying out the amp, and right now I'm just trying to get it to work with the sound module, unfortunately, it's not working. I have tested it so far with just the WTV020 sd 16p,and I can get it work there, then I run my DAC+ line from the wtv into the INL and run the speaker to the LO+ and LO-.

I have 5v going to the 5v, all 3 gnds are grounded. I'm not using the vup/Vdn at all yet, I'm just trying to get some noise out of it. Is there a really good wiring schematic out there I can go off of?

Thanks for all your work with this module

Hello everybody,

I would like to add sound to my Tardis using a WTV020 and the PAM8803 controlled by a Mini Pro - unfortunately when searching for the PAM8803 module shown in this thread I can only find is this one:
http://www.ebay.com/itm/Mini-2-3W-4-PAM8803-Class-D-Audio-Amplifier-Board-For-PIC-AVR-/170850491058?pt=LH_DefaultDomain_0&hash=item27c77b9ab2
Seems extremely expensive and is far out of budget for me.

Do you have any source for this module?

Cheers
Johannes