PGA2311 code

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
Hi everyone!

I'm having trouble getting my PGA2311 to work. I have an ATTINY2313 controlling it, but i can't control the volume. Would anyone be so kind as to go through my code (attached) and look for an explanation as to why it wont work.

Thank you!

Best regards

Mads, Denmark
 

Attachments

  • soundbar.txt
    1.4 KB · Views: 206
  • volume c.txt
    1.8 KB · Views: 240
  • volume.txt
    317 bytes · Views: 143
AX tech editor
Joined 2002
Paid Member
Can't see anything wrong.
I wonder though why the long delays in the clocking sequence?
I've done this with a PIC with a 1uS instruction cycle with no delays, reliably.
One thing you can verify is whether the Mute pin is actually set correctly.
Only advise I have is to spend some money for a low cost USB logic analyzer like the Saelig but there are more even lower in cost that do 8 bits. Pays for it very quickly!

jan
 
Thanks for the reply. The reason for the long delays is that I tried with faster delays and that didn't work, so I thought I'd give it a go with longer delays, but no difference. So to answer your question - there is no reason for the long delays... :)

The mute pin is high on startup and throughout the tests I've made, so that should be right!

It's really starting to annoy me, why it won't work... :( But hopefully someday I will find the error.... :)
 
I have BASCOM-AVR code that I can share with you. It is written for an xmega and has a 4x40 LCD display. The basic building blocks for pre-amp/tuner

Config Portc.4 = Output
Pga_cs Alias Portc.4
Set Pga_cs 'Slave Select Pin, Connect to PGA2311-2 (CS), active low
'Dim Select_bit As Bit
'Portc_pin0ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
'Portc_pin1ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
'Portc_pin2ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
'Portc_pin3ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
Portc_pin4ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
Portc_pin5ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
Portc_pin7ctrl = &B10_011_010 ' slew, no invert, Totempole, Pull-up (on input),Sense falling edge
Config Spic = Hard , Master = Yes , Mode = 0 , Clockdiv = Clk32 , Data_order = Msb , Ss = None
Open "SPIC" For Binary As #3

If Encoder2old <> Encoder2 Then 'Use for Volume/Balance Function
If Pga2311_mute = 1 Then '
Volume_count = Encoder2
Encoder2old = 0
Encoder2 = 0
Volume = Volume + Volume_count
'Volume_L = Volume_L + Volume_count
'Volume_R = Volume_R + Volume_count
Disable Interrupts
Encoder2old = Encoder2
Enable Interrupts
'If Volume_L > 212 Then Volume = 212
'If Volume_R > 212 Then Volume = 212
If Volume > Volume_max Then Volume = Volume_max 'Volume Limit, max 255
If Volume < 0 Then Volume = 0
'If Volume_L < 0 Then Volume_L = 0
'If Volume_R < 0 Then Volume_R = 0
Volume_s = Volume
Volume_s = 255 - Volume_s
Volume_s = Volume_s / 2
Volume_s = 31.5 - Volume_s '31.5 -(0.5 *(255 - N))
___lcde = 1
Locate 1 , 1
'Lcd "****************************************"
Lcd "Volume L = "
Locate 1 , 12
Lcd Fusing(volume_s , "#.#") ; " dB "
Locate 1 , 21
Lcd "R = "
Lcd Fusing(volume_s , "#.#") ; " dB"
'Dim Volume_word As Word
'Dim V_right As Byte At Volume_word Overlay
'Dim V_left As Byte At Volume_word + 1 Overlay
V_right = Volume 'Assemble word for PGA2311
V_left = Volume 'Need to add balance function
Reset Pga_cs 'Select SPI Slave
Print #3 , Volume_word 'Send 16 bits to PGA2310
Set Pga_cs 'Deselect SPI Slave
End If
End If

send me a PM if you want my test code!!
 
Hi,

couple questions -
- are the interrupt routines that move volume up or down really called via INT0/INT1?
- if they aren't, during init you set /MUTE to 0, that means you mute the device - so I assume you measured that the pin is set to 1 during normal operation (just to reconfirm, as I don't see how the interrupts are called)
- you start with -86dB and only allow -33.5db max - is that intended? Do you check volume via the out put signal on an oscilloscope, or do you only listen? Is your source line level high enough to be audible at -33.5db?

Not sure if it matters, but on my implementation I left the clock on logical "1" when the SPI was not transmitting. (I did use the build-in serial transmitter on the 2313 in Assembler, so not possible to directly compare the code...)
 
Hi everyone ! Thanks for all your replies!

@rsavas - unfortunately, I have no experience at all with bascom but will take a look non the less... :)

@bonsai - Sounds great!

@rollingtube - the subroutines are called from int0/int1 on any logical change using an rentron TINY IR-II IR decoder (or at least that was what is supposed to happen ;))
The Mute set to 0 during init is a mistake, which have been changed :)
Also the max -33.5dB has been changed to unity gain (0dB) but with no difference.
I haven't checked the output signal on a oscilloscope yet, but will do this monday!
So sorry guys, despite all your help, the solution is not yet found.. :S But I will keep
on going 'till i find it!

Thanks for all your help! just keep it comming :)

- Mads
 
BASCOM-AVR= dead simple, this is why I selected this compiler over gcc!! Free demo available, all you need is a AVRISP mkII and some HW.
My first HW was a xmega BOB and the following mounted on a proto-board: from this basic setup I got my feet wet and then designed my product there-after. My test code is now at line 2357, so I can give this to you for free!! It is a lot of code, so I do not want to post it all here, but the small bit of code i have posted here, will give you an idea of how easy it really is.
The product is much more code, but I will not be releasing that, as it is of no use, as it is, unless you have the HW. If you are interested in my HW we can talk off line.

' Hardware: Sparkfun xmega100 BOB
' Newhaven NHD-0440AZ-FL-YBW
' PGA2310
' PCA9555
' 3x Bourns PC12 rotary encocders with switches
' 6x spst switches with LED (C&K K5V-BU)
' DSS BMP180
' Honeywell HumidIcon Digital Humidity/Temperature Sensor: HIH-6130/6131 Series
Dim Volume_word As Word 'Word to be written to PGA2310/2320/2311

Dim V_right As Byte At Volume_word Overlay
Dim V_left As Byte At Volume_word + 1 Overlay
Config Portk = Input
Config Portk.6 = Output 'PGA2311 Mute, active Low
Set Portk.6
Pga2311_mute Alias Portk.6

Config Portk.7 = Output 'PGA2311 ZEN, active High
Pga2311_zen Alias Portk.7
Reset Portk.7

Config Portc.4 = Output
Pga_cs Alias Portc.4

'This is all it takes to write to the PGA part
Reset Pga_cs 'Select SPI Slave
Print #3 , Volume_word 'Send 16 bits to PGA2310/11/20
Set Pga_cs 'Deselect SPI Slave
 
Last edited:
I'm not an AVR programmer, so it's difficult for me to be sure what's going on here, but I have done this with a PIC, both in assembler and in C.

First of all, I don't understand why you are doing this using interrupts.

Interrupts are always much more difficult to debug than polling loops, and you don't need interrupts to make this work. I'd rewrite the whole thing without the interrupts. I used the timer interrupts to drive a multiplexed 7-segment LED array, that's a different matter, I needed them then.

What does this:-

Code:
    PCMSK |= (1<<PCINT0); // Enable PCINT0 on PB0
...do?

PCMSK gets PCMSK bitwise OR'ed with what? What does (1<<PCINT0) mean? PCINT0 is left shifted once? One is left shifted PCINT0 times? The shift operators syntax is normally (x<<2) (x left shifted twice). Maybe AVR c is different... What is PCINT0? Why do you want to left shift it? I can see what INT0 is, I can see what PCINT is, you've got 3 ISRs INT0, INT1, and PCINT.

????????

You need to avoid comparatively obscure forms of coding like this. I've been coding in C for at least 20 years, it took me half-an-hour to figure out what it means... It's probably not your fault, you probably copied it from some smartass AVR example code.

It sets the bit called PCINT0, the one for PORTB, bit 0, in the interrupt SFR. PCINT0 is an alias for the bit number. Yes? The associated interrupt is INT0, so the ISR is passed INT0_vect?

What was wrong with

Code:
    sbi (PCMSK, PCINT0);
...??? I'd have got that immediately.

Write straightforward code where it is easy to see explicitly what is taking place. Don't combine multiple operations in a single instruction.

You have this:-

Code:
	//Enable mute
	PORTB &= 0b11111101;
and this:-

Code:
	// Disable mute
	PORTB |= (1<<PB1);
... if you want to disable the mute pin (force it to 1), write this:-

Code:
	PORTB |= 0b00000010;
or this:-

Code:
       sbi (PORTB, 1);
I can't see any point where you enable INT1 or PCINT. Presumably they are on other pins of PORTB?

What is PCINT? Why isn't it called INT3 and assigned a bit on PORTB? Is is a special feature of AVRs I can't figure out without reading the manual?

You don't need 3 files. You can keep all this stuff in 1 file. Having it in 3 files just makes it hard to read.

Code:
#include "Volume.h"
#include "avr/io.h"
#include "compat/deprecated.h"
#define F_CPU 1000000UL
#include "avr/delay.h"

int main(void);
int PGA_volume(unsigned char volume_r, unsigned char volume_l);
ISR(INT0_vect);
ISR(INT1_vect);
ISR(PCINT_vect);

unsigned char left, right;

int main(void)
{	
    DDRB = 0xFF;                         	        // Port B as output
    GIMSK |= 0b11100000;                   	        // Initialize interrupts
    MCUCR |= 0b00000101;                                // interrupt on any logical change
    PCMSK |= (1<<PCINT0);                               // Enable PCINT0 on PB0	
    PORTB |= (1<<PB7);                                  // enable ZCEN
    PORTB &= 0b11111101;                                // disable mute	
    left = 20;
    right = 20;
	
    PGA_volume(right, left);	
    while(1)
    {
        sei();                                          // Enable global interrupts
    }		
}

int PGA_volume(unsigned char volume_r, unsigned char volume_l)
{
	unsigned char i;
	unsigned char send_r, send_l;

	cbi (PORTB, 6);                                 // Enable chip select
	for(i = 0 ; i < 8 ; i++)                        // Send volume right
	{
		send_r = volume_r & 0x80; 
		if (send_r == 0x80) 
		{
			sbi (PORTB, 5);                 // Set bit data
			_delay_us(100);
			sbi (PORTB, 2);                 // Set bit clock
			_delay_us(50);
			cbi (PORTB, 2);                 // Clear bit clock
			_delay_us(50);
		}

		if (send_r == 0x00)
		{
			cbi (PORTB, 5);
			_delay_us(100);
			sbi (PORTB, 2);
			_delay_us(50);
			cbi (PORTB, 2);
			_delay_us(50);
		}
		volume_r = volume_r << 1;
	}
	for(i = 0 ; i < 8 ; i++)                        // Send volume left
	{
		send_l = volume_l & 0x80;

		if (send_l == 0x80)
		{
			sbi (PORTB, 5);
			_delay_us(100);
			sbi (PORTB, 2);
			_delay_us(50);
			cbi (PORTB, 2);
			_delay_us(50);
		}

		if (send_l == 0x00)
		{
			cbi (PORTB, 5);
			_delay_us(100);
			sbi (PORTB, 2);
			_delay_us(50);
			cbi (PORTB, 2);
			_delay_us(50);
		}
		volume_l = volume_l << 1;
	}
	sbi (PORTB, 6);                                 // Disable chip select
	cbi (PORTB, 5);                                 // Clear bit data
	cbi (PORTB, 2);                                 // Clear bit clock
	return;
}

ISR(INT0_vect)                                          // Increment volume
{		
	// Disable mute
	PORTB |= (1<<PB1);
	if(left > 125 | right > 125)
	{
		left = 125;
		right = 125;
	}
	else
	{
		left = left + 1;
		right = right + 1;
	}
	PGA_volume(right,left);
}


ISR(INT1_vect)                                          // Decrement volume
{
	// Disable mute
	PORTB |= (1<<PB1);
	if(left > 125 | right > 125)
	{
		left = 125;
		right = 125;
	}
	else
	{
	left = left - 1;
	right = right - 1;
	}	
	PGA_volume(right,left);
}


ISR(PCINT_vect)                                         // Mute volume
{
	//Enable mute
	PORTB &= 0b11111101;
}

Now I can search the whole of the code in one operation for PCINT...

As far as I can tell, you've only enabled interrupts on one of the pins, that can't be helping. If the mute is on PORTB,1 which pin is calling the interrupt for INT1?
 
Last edited:
Sorry, please accept my apologies, you'll have to ignore some of the stuff I've written above, it's incorrect and stupid. I should have looked at the datasheet before commenting.

I had a look at the ATTiny2313 datasheet, I've got a bit clearer idea of what's going on.

I see that all the PCINT0->7 interrupts all use the same vector.

The one thing I did see in your code is that PORTB, 0 is not configured as as input. I read that the inputs respond even when the pins are not enabled as inputs, but this is only in the case when the value is changed by the program outputting to the pin, this permits the creation of a software interrupt. This however should not affect the volume control, only the mute control.

I can see that they use this

Code:
    PCMSK |= (1<<PCINT0);
syntax all over the place, once you get used to it it's easy enough to deal with.

Here's how I think it could be made to work without interrupts.

Code:
#include "avr/io.h"
#include "compat/deprecated.h"
#define F_CPU 1000000UL
#include "avr/delay.h"

int main(void);
void PGA_volume(unsigned char volume_r, unsigned char volume_l);
void volup(void);
void voldn(void);
void mymute(void);

unsigned char left, right, b0det, d2det, d3det;

int main(void){
	DDRB = 0b11111110;                        	        // Port B 7->1 as output, 0 as input
	DDRD = 0b00000000;                        	        // Port D as input
	sbi (PORTB, 7);                                     // enable ZCEN
	sbi (PORTB, 6);                                     // Disable chip select
	cbi (PORTB, 5);                                     // Clear bit data
	cbi (PORTB, 2);                                     // Clear bit clock
	cbi (PORTB, 1);                                     // disable mute
	left = 20;
	right = 20;
	b0det = 0;
	d2det = 0;
	d3det = 0;
	
	PGA_volume(right, left);

	while(1)
	{
		if (PINB & (1<<PB0)){
			b0det=1;
		}
		else{
			b0det=2;
		}
		if (PIND & (1<<PD2)){
			d2det=1;
		}
		else{
			d2det=2;
		}
		if (PIND & (1<<PD3)){
			d3det=1;
		}
		else{
			d3det=2;
		}
		if (b0det==1){
	  	    if (!(PINB & (1<<PB0))){
			    b0det=2;
			    mymute();
		    }
	    }
	    if (b0det==2){
	        if (PINB & (1<<PB0)){
		        b0det=1;
		        mymute();
	        }
        }
        if (d2det==1){
			if (!(PIND & (1<<PD2))){
				d2det=2;
				volup();
			}
        }
        if (d2det==2){
            if (PIND & (1<<PD2)){
                d2det=1;
                volup();            
            }
        }
        if (d3det==1){
            if (!(PIND & (1<<PD3))){
                d3det=2;
                voldn();            
            }
        }
        if (d3det==2){
            if (PIND & (1<<PD3)){
                d3det=1;
                voldn();            
            }
        }
    }		
}

void PGA_volume(unsigned char volume_r, unsigned char volume_l)
{
	unsigned char i;
	unsigned char send_r, send_l;

	cbi (PORTB, 6);                                 // Enable chip select
	for(i = 0 ; i < 8 ; i++)                         // Send volume right
	{
		send_r = volume_r & 0b10000000; 
		if (send_r){
			sbi (PORTB, 5);                 // Set bit data
			_delay_us(100);
			sbi (PORTB, 2);                 // Set bit clock
			_delay_us(50);
			cbi (PORTB, 2);                 // Clear bit clock
			_delay_us(50);
		}
                else{
			cbi (PORTB, 5);
			_delay_us(100);
			sbi (PORTB, 2);
			_delay_us(50);
			cbi (PORTB, 2);
			_delay_us(50);
		}
		volume_r = volume_r << 1;
	}
	for(i = 0 ; i < 8 ; i++)                        // Send volume left
	{
		send_l = volume_l & 0b10000000;
		if (send_l){
			sbi (PORTB, 5);
			_delay_us(100);
			sbi (PORTB, 2);
			_delay_us(50);
			cbi (PORTB, 2);
			_delay_us(50);
		}
                else{
			cbi (PORTB, 5);
			_delay_us(100);
			sbi (PORTB, 2);
			_delay_us(50);
			cbi (PORTB, 2);
			_delay_us(50);
		}
		volume_l = volume_l << 1;
	}
	sbi (PORTB, 6);                                 // Disable chip select
	cbi (PORTB, 5);                                 // Clear bit data
	cbi (PORTB, 2);                                 // Clear bit clock
	return;
}

void volup(void)                                          // Increment volume
{		
	// Disable mute
	sbi (PORTB,1);
	if(left < 125)
	{
		left = left + 1;
		right = right + 1;
	}
	PGA_volume(right,left);
}


void voldn(void)                                          // Decrement volume
{
	// Disable mute
	sbi (PORTB,1);
	if(left > 0)
	{
	left = left - 1;
	right = right - 1;
	}	
	PGA_volume(right,left);
}


void mymute(void)                                         // Toggle mute
{
    if (PINB & (1<<PB1)){
        cbi (PORTB,1);
    }
    else{
	sbi (PORTB,1);
    }
}

One of the things I could suggest for debugging is to lift one of the pins you are using for inputs and driving it directly either by pulling it up or down. You could tie it to ground with a resistor and then toggle it using a flying wire connected to Vcc.

Sorry again about the comments I made earlier, I really was out of line.

Let us know how you get on, and I will try to help you solve this problem. There are very few bugs that you cannot discover with a bit of ingenuity in thinking of a test.
 
@counter culture - no need to apologize. I realize that I sometimes mix the syntaxes together and that it can look messy! The reason for this is that some code is copied from another source and some of it I have written myself. I study electrical engineering and this is the way I was tought to do it :) Thats all I can say... I appreciate all your inputs. Afterall it led me to simplify the code and now

IT WORKS!!!!! :)

I deleted the whole thing and started from scratch myself. The only thing not working is the mute. But that is caused by the Rentron TINY IR remote decoder which only outputs 0.67V on the assigned mute pin when triggered, so I suspect it to be faulty!

Thanks you guys for all your help...!!
 
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.