another take on this concept; this time using the arduino to control a motorized pot (via an h-bridge chip):
source code at:
another take on this concept; this time using the arduino to control a motorized pot (via an h-bridge chip):
source code at:
Hey Everyone,
I'm dangerous, and have no formal training. Half of the stuff I build only half works, and now, I'm trying to get my Arduino to control a PGA2310 with only three legs. ![]()
It all started with just wanting to build another GainClone (LM3886 based) amplifier for my daughter's room. She only needs 4 stereo inputs, and initially, I was just going to use a motorized volume knob. Well, I started reading, and found the PGA2310! (I wished I would have seen the PGA2311, so I could have skipped the +/- 15v power supply, but I digress.)
I have used many atmel chips, and primarily wrote ASM code. However, I started messing around with the Arduino for some LED mood lamps I built and decided I really liked the libraries available. (Warning, another digression. You guys have made me lazy!
The main reason I started messing with these chips is so I would learn the obnoxious bit-banging methods I so admire. I have gotten a few things to work that way, even IR in the past, but now, it seems I just want to make something that works, and not tinker around as much.)
I'm using every pin on the chip (6 for relays - 6 for LCD - 1 for IR - 3 for front panel (8 buttons ala NES controller!) - 3 for PGA - 1 for Alarm sound PWM, yes, thanks to feature creep, it's now an alarm clock too!)
Everything works in my device! except no volume increase when I call my code. Here it is:
const int CS = 11;
const int CLK = 13;
const int SDI = 12;
byte RVOL = 0;
byte LVOL = 0;
void setup() {
pinMode(CS,OUTPUT); //initialize pins
pinMode(CLK,OUTPUT);
pinMode(SDI,OUTPUT);
digitalWrite(CS,HIGH);
digitalWrite(CLK,HIGH);
digitalWrite(SDI,HIGH);
RVOL=EEPROM.read(0);//using eeprom to store values on power off
LVOL=EEPROM.read(1);
}
Just calling volset whenever I change volume using globals.
void volset() {
int b = 0;
digitalWrite(CS,LOW); //enable chip
delayMicroseconds(1); //wait, I have changed these all over the map
for (int i=7;i==0;i--) {
b=bitRead(RVOL,i);
digitalWrite(CLK,LOW); //should it just be low when I start?
delayMicroseconds(1);
if (b) {digitalWrite(SDI,HIGH);} else {digitalWrite(SDI,LOW);}
delayMicroseconds(1);
digitalWrite(CLK,HIGH); //datasheet says read on conversion from low->high
delayMicroseconds(1);
}
for (int i=7;i=0;i--) { //set LVOL the same way
b=bitRead(LVOL,i);
digitalWrite(CLK,LOW);
delayMicroseconds(1);
if (b==0) {digitalWrite(SDI,LOW);} else {digitalWrite(SDI,HIGH);}
delayMicroseconds(1);
digitalWrite(CLK,HIGH);
delayMicroseconds(1);
}
digitalWrite(CS,HIGH); //go to sleep chippy
delayMicroseconds(1);
}
I see from the SPI library that it wants to use four legs to the chip, and I only want to use three. I figured the output pin, which is used to tie another PGA was unneeded. I tried to brute force it by looking at the datasheet, but the chip does not seem to respond. Of course, I've already built a board before testing the PGA too much, and I have no real easy way to tie into the output pin anyway.
I'm not too good with my oscilliscope, but I do see pulses going into the chip, and pulses coming out.
Anyway, sorry for the long message, but I always tend to talk too much. Interestingly, I can't really seem to find ANY code out there for the Arduino in conjunction with a PGA2310. There are lots of references in this thread(and others), but I was simply never able to find some functional code.
Can anyone help? I'd like to get over this hump and finish up the rest of my main logic for the amp.
Thanks!
-Kevin
Hey Kevinwil,
Did you got success controlling pga2310 with the Arduino?
Thanks,
Hey Ricardo,
Sadly, no, I didn't get this going. I wound up building a H-Bridge I'm controlling with only two pins, and using a 20k motorized pot I picked up from goldmine-elec.com forever ago. Since I already had the +/15v, I used two OPA2134s for input/ouput buffers and it sounds really nice. The amp completely works now, but I have yet to polish off the code, and complete the front panel. (need to finish the alarm clock) It's always the box which holds me up, but it fully works via IR for now.
Also, I got side-tracked on another really cool project, that I'm thinking about sharing when I'm done. (Pretty simliar to tronbike.com.) I built an electric go kart, using a motor controller which allows rs-232 polling. After many trials and tribulations, I finally got the RS-232 timing good, and can read amperage, voltage, etc off an alltrax controller. It also controls the main contactor, reverse contactor, break lights, headlights, as well as as a hall sensor reading RPM and thereby giving MPH, odo, etc! It's probably one of the more complicated things I have designed, but thanks to the awesome Arduino libs/sample code, it worked with only 4-5 days of coding/testing.
I'd still be interested if anyone would like to share their PGA2310 code. I bought two of these chips, and as you guys likely know, they were pretty expensive and useful. I'll probably build one more gainclone for my son when he gets a little older, so I have 2-3 years to get that code.
The only other thing I can think that I did wrong is that I didn't follow the guidance in the datasheet for the analog/digital ground-plane. I only made a single-sided board, and did the best I could. I would have expected some noise as a result of this, but I literally got no signal at all while increasing the volume. Interestingly, when I would get to about 40-60% of max vol, (I forget where exactly) it would click through the speaker, kinda like the sound a deskphone makes if your cell phone is sitting too close to it when it rings. That was the only noise this chip ever made.
Thanks!
-Kevin
here's some snippets from my code base that might help:
// PGA volume control chip
#ifdef USE_PGA
#define PGA_CS_PIN 12
#define PGA_SDATA_PIN 11
#define PGA_SCK_PIN 10
#endif
...
void
PGA_set_volume(byte left, byte right)
{
byte shifted_val = (left << 1); // right is the same (we assume that, anyway)
digitalWrite(PGA_CS_PIN, LOW); // assert CS
SPI_write(shifted_val); // right value (0..255)
SPI_write(shifted_val); // right value (0..255)
digitalWrite(PGA_CS_PIN, HIGH); // deassert CS
}
...
static inline void
SPI_write(byte out_spi_byte)
{
byte i;
// loop thru each of the 8-bits in the byte
for (i=0; i < 8; i++) {
// strobe clock
digitalWrite(PGA_SCK_PIN, LOW);
// send the bit (we look at the high order bit and 'print' that to the remtoe device)
if (0x80 & out_spi_byte) // MSB is set
digitalWrite(PGA_SDATA_PIN, HIGH);
else
digitalWrite(PGA_SDATA_PIN, LOW);
// unstrobe the clock ![]()
digitalWrite(PGA_SCK_PIN, HIGH);
out_spi_byte <<= 1; // left-shift the byte by 1 bit (
}
}
Kevinwil,
Thanks for the quick answer.
I've ordered two PGA2310 from eBay for use in a Arduino controled preamp.
As I write this, linux-works posted some usefull snipets for controlling the PGA2310.
Wish me luck.
one thing to be aware of: this chip series has some bugs. some people think so.
it seems to lock up (!) sometimes and a power cycle is needed. the chip gets VERY hot to the touch like some op-amp is running wild inside (runaway). a reboot fixes it.
some of us on diyaudio have noticed this. we don't know what causes it. it is worrying, though ;(
there are some pre-made versions that you can buy, on boards. if you have trouble getting your own hardware to work, maybe try a pre-made version and then get your software working with the 'ref implementation' first.
I've never seen the chip die due to this feedback heat thing going on, but I've also noticed it before it got too 'long' (audio clearly goes out during this process and no volume change is heard).
for now, I've suspended my work on this chip in favor of a relay based volume control. we are just about done with our v1.0 pc boards and you can see some info, here, on our support forum. the relay solution takes up more room but is noiseless (no amp section or active parts in the audio path) and it can support balanced audio as well as single-ended unbal stuff.
http://www.amb.org/forum/lcduino-system-f8/
lcduino is the name we are using for the lcd controller part; delta1 is the volume control relay board and delta2 is the input/output selector board.
code will be posted for THAT project shortly (its almost finished beta testing.) of interest is that it uses latching relays and that complicated the design a lot but gives some really good benefits to justify it.
at some point I'll revisit the pga chips but the bug in the chip series is annoying and so far, no one has gotton to the bottom of it, yet ;(
Hmm, Interesting on the buggy chip. I am pretty sure my hardware is physically connected properly, but maybe it's a good thing I moved to the h-bridge.
Also, thanks for the code example. Just looking through it, it seems to work in a fairly similar manner to what I wrote, so maybe I did have some HW issues. All that aside, I'm shelfing this for now, but will likely pull them back out in a year or two.
Hi everybody!
Lovely job done, linux-works!
Perfect apparatus!
I have a favour to ask of you, linux-works - could you provide me a complete code for your amazing bargraph?
Thank you!
![]()
I made bargraph for my PGA2310 by myself.
Thanks.
Pryanick,
Did you saw the bug noticed by linux-works (Posted on: 27.08.2010 at 18:30:52)?
My PGA2310 didnt arrived from eBay yet. ![]()
2 Ricardo Simoes
I read about this bug at diyAudio
at "Yet another Volume controlers and source selections".
Vikt0r wrote about that. For the first time his PGA2310 "hung-up" for some reason - the chip started heating up, but resetting it helped and that time it started working again. The second time he didnt detect the issue in time and chip went dead.
sorry, didn't mean to ignore you. I've been working on a whole set of libraries that I'm going to release soon and the bargraph will be part of it. also included is my serial lcd display, learning IR (both send and receive), learning resistor buttons, ds1302 RTC, motor-pot and h-bridge driver and a few port expanders and things here and there.
I will post a note when the whole library is ready to be reviewed. its just in a state of flux and clean-up right now which is why I didn't post any code for it quite yet.
also, since I prefer to have a single interface for 'peripherals' (in the arduino case, i2c is pretty much is) - I've put the PGA chip behind a port expander (NXP PCF8574) and it worked just fine in bit-bang mode. you send a whole 8bit message each time you flip any of the bits and you simulate the SPI bus remotely but it works fine with the PGA and it let me 'remote' things a bit more. if you have multiple PGAs you could have each sit on its own i2c addr and share the same 4 wires (2 for data and 2 for pwr/gnd).
using 4 wires to 'remote things' is very useful. I'm even doing it on my espresso machine where my lcd is via a 4wire i2c bundle that goes from the safe area outside (where the lcd is) to the hot boiler area inside, where the control and PE and relay logic is. its also nice that rj11 carries 4 wires and can be a convenient jack for carrying short length of 'remote control and power'.
Has any of you found an example to be used by beginners? I got my Arduino boards this weekend and have so far gotten the IR receiver and LCD working. Next step will be communicating with the PGA2310 PGA2311.
If one has a basic example, please post it so I can continue my quest for an Arduino PreAmp
Here is an example that uses linux-works excellent example code. It will just loop through volume levels between 0 - 127.
Note that I have left out the audio and analog power connections on the right side of the IC for simplicity. Check the datasheet for complete pinout.
Zero Crossing Detection is enabled (ZCEN to 5V)
Mute is disabled (Mute pin to 5V)
#include "SPI.h"
const int PGA_CS_PIN = 11;
const int PGA_SCLK_PIN = 13;
const int PGA_SDI_PIN = 12;
int timedelay = 100;
int volumeLevel = 1;
void setup() {
pinMode(PGA_CS_PIN,OUTPUT); //initialize pins
pinMode(PGA_SCLK_PIN,OUTPUT);
pinMode(PGA_SDI_PIN,OUTPUT);
digitalWrite(PGA_CS_PIN,HIGH);
digitalWrite(PGA_SCLK_PIN,HIGH);
digitalWrite(PGA_SDI_PIN,HIGH);
}
void PGA_set_volume(byte value)
{
byte shifted_val = (value << 1);
digitalWrite(PGA_CS_PIN, LOW); // assert CS
SPI_write(shifted_val); // right value (0..255)
SPI_write(shifted_val); // right value (0..255)
digitalWrite(PGA_CS_PIN, HIGH); // deassert CS
}
static inline void SPI_write(byte out_spi_byte)
{
byte i;
// loop thru each of the 8-bits in the byte
for (i=0; i < 8; i++) {
// strobe clock
digitalWrite(PGA_SCLK_PIN, LOW);
// send the bit (we look at the high order bit and 'print' that to the remtoe device)
if (0x80 & out_spi_byte) // MSB is set
digitalWrite(PGA_SDI_PIN, HIGH);
else
digitalWrite(PGA_SDI_PIN, LOW);
// unstrobe the clock ;)
digitalWrite(PGA_SCLK_PIN, HIGH);
out_spi_byte <<= 1; // left-shift the byte by 1 bit
}
}
void loop()
{
// Reset the volumeLevel if it goes over 128. I think it can actually go 255.
if (volumeLevel > 127) {
volumeLevel = 1;
}
// Set the volume
PGA_set_volume(volumeLevel);
delay(timedelay);
volumeLevel = volumeLevel + 1;
}
Maxw,
Thanks for the excelent example. I just need the power suply (+/-15V) to make my own tests.