mega168 ADC interrupts not working

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Very frustrating. I'm using AS7 and a jtagicemkII and debugWire to develop a 168 system. Try as I might I cannot get ADC interrupts. I'm getting timer interrupts so I know interrupts are working. I can go into I/O View and manually fire a conversion and see results that all look okay, even in interrupt flag. But the ADC ISR never gets hit. I have tried breakpoints and toggling a pin as a sanity check.

 

Init code:

void init_devices(void)
{
	//
	// sets up ADC system
	//
	DIDR0 = (1<<ADC4D)|(1<<ADC3D);
	ADMUX = (1<<REFS0);		//AVCC as reference
	ADCSRA = (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);
	
	// ***SPI***
	SPCR = (1<<SPE)|(1<<MSTR);	// SPI enable	
}

Main loop:

int main(void)
{
	unsigned int dummy;
	cli();
	init_ports();
	init_devices();
	timer_init();
	pgm_init();
	sei();

	dummy = ADC;						// clear IRQ flag, just in case
	ADCSRA |= (1<<ADIE);					// enable ADC interrupts
	ADCSRA |= (1<<ADSC);				//start next conversion

    while(1)
    {
	GetSwitches();
    }
}

What am I missing?

 

--Timbo

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is always easier when we see the entire test program.

 

For one thing, we see no ISR.

 

theusch wrote:
http://www.avrfreaks.net/comment... Post a complete test program that demonstrates the symptoms. Tell AVR model, and clock speed. Tell language, toolchain, version, and optimization settings. Tell what you expect to happen, and tell what >>is<< happening.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

madhun wrote:
dummy = ADC; // clear IRQ flag, just in case

That's a new one on me.  What flag do you think you might be clearing, by reading the ADC result?  [this ain't SPI ;) ]

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
/*
 * DLFO.c
 *
 * Created: 7/18/2017 8:30:12 AM
 *  Author: TimR
 */ 


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "avr/io.h"
#include "avr/interrupt.h"
#include <avr/pgmspace.h>
//#include "expotable.h"
//#include "sinetable.h"

// Prototypes
void	init_ports(void);
void	init_devices(void);
void	pgm_init(void);
void	timer_init(void);
void	SendToShiftyTypes(void);
void	GetSwitches(void);

// I/O DEFS
#define SW_WAVE1_NO		PINC&0x20		
#define SW_WAVE1_NC		PINC&0x04		
#define SW_WAVE2_NO		PIND&0x01		
#define SW_WAVE2_NC		PIND&0x02		
#define SW_RNG1_NO		PIND&0x08		
#define SW_RNG1_NC		PIND&0x10		
#define SW_RNG2_NO		PIND&0x20		
#define SW_RNG2_NC		PIND&0x40		

#define RST1		PINB&0x01
#define RST2		PIND&0x80		

#define GATE1		PIND&0x80
#define GATE2		PINB&0x01

#define	SQU1_HI		PORTD|=0x02;
#define	SQU1_LO		PORTD&=~0x02;
#define	SQU2_HI		PORTD|=0x04;
#define	SQU2_LO		PORTD&=~0x04;


// Shifty Types
#define	SS_LO		PORTB&=~0x04;
#define	SS_HI		PORTB|=0x04;
#define	LDAC_LO		PORTB&=~0x02;
#define LDAC_HI		PORTB|=0x02;

//#define ADCCHANNELMAX	6

#define NUMLFO	2

enum ranges {
	low_range,
	med_range,
	hi_range
};

#define range_count_hi 1;
#define range_count_med 100;
#define range_count_lo 10000;

enum waves {
	sin_wave,
	tri_wave,
	saw_wave
};

// Globals
uint8_t		LfoRange[NUMLFO];
uint8_t		LfoWave[NUMLFO];	//The range and wave for each LFO

volatile uint32_t	Lfo1_DCO,Lfo2_DCO;		// accumulators for the DCOs
volatile uint32_t	Lfo1_INC,Lfo2_INC;		// increments for the DCOs
volatile uint16_t	Lfo1_DAC,Lfo2_DAC;		// increments for the DCOs

volatile uint8_t		thisAdcChannel;
int main(void)
{
	unsigned int dummy;
	cli();
	init_ports();
	init_devices();
	timer_init();
	pgm_init();
	sei();

	dummy = ADC;						// clear IRQ flag, just in case
	ADCSRA |= (1<<ADIE);					// enable ADC interrupts
	ADCSRA |= (1<<ADSC);				//start next conversion

    while(1)
    {
	GetSwitches();
    }
}

void GetSwitches(void) 
{
	// LFO1 Range
	if(!(SW_RNG1_NO)) 
		LfoRange[0]=low_range;		
	else if(!(SW_RNG1_NC))
		LfoRange[0]=hi_range;		
	else
		LfoRange[0]=med_range;		

	// LFO2 Range
	if(!(SW_RNG2_NO))
		LfoRange[1]=low_range;		
	else if(!(SW_RNG2_NC))
		LfoRange[1]=hi_range;		
	else
		LfoRange[1]=med_range;		

	// LFO1 Wave
	if(!(SW_WAVE1_NO))
		LfoWave[0]=sin_wave;		
	else if(!(SW_WAVE1_NC))
		LfoWave[0]=saw_wave;		
	else
		LfoWave[0]=tri_wave;		

	// LFO2 Wave
	if(!(SW_WAVE2_NO))
		LfoWave[1]=sin_wave;		
	else if(!(SW_WAVE2_NC))
		LfoWave[1]=saw_wave;		
	else
		LfoWave[1]=tri_wave;		
	
}


void	init_ports(void)
{
	DDRB = 0x3E;		// PB0 input, PB1-4 outputs
	PORTC = 0x03;		// PC0&1 pullups
	DDRC = 0x00;		// all inputs of the ADC variety
	PORTD = 0x78;		// pull ups on PD3-6
	DDRD = 0x06;		// PD1,2 outputs, rest inputs
}

void init_devices(void)
{
	//
	// sets up ADC system
	//
	DIDR0 = (1<<ADC4D)|(1<<ADC3D);
	ADMUX = (1<<REFS0);		//AVCC as reference
	ADCSRA = (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);
	
	// ***SPI***
	SPCR = (1<<SPE)|(1<<MSTR);	// SPI enable	
}


void pgm_init(void)
{
	// setup mux channel
	thisAdcChannel=3;
	ADMUX |= thisAdcChannel;
	SS_HI;
	LDAC_HI;
	SQU1_LO;
	SQU2_LO;
}

void timer_init(void)
{
	// for 8MHz internal clock
	// timer 0 is the 16 KHz sample clock
	TCCR0A  = (1<<WGM01);					// ctc mode
	TCCR0B  = (1<<CS01);					// divide by 8
	OCR0A   = 0x3E;							// or 62 decimal -- 0.5mS
	TIMSK0	= 1<<OCIE0A;					// output compare A match irq
}

// send data to MCP4922 Dual DAC
void SendToShiftyTypes(void)
{
	uint8_t		dummy;
	uint16_t	sendVal;

	// send first channel
	cli();
	SS_LO;
	sendVal = Lfo1_DAC;
	sendVal = sendVal | 0x7000;			//gain=1,active mode
	SPDR=sendVal>>8;					// send MSB
	do {} while (!(SPSR&(1<<SPIF)));
	dummy=SPDR; 				//dummy read to clear flag
	SPDR=sendVal&0x00FF;				// send LSB
	do {} while (!(SPSR&(1<<SPIF)));
	dummy=SPDR; 				//dummy read to clear flag
	SS_HI;

	// send second channel
	SS_LO;	
	sendVal = Lfo2_DAC;
	sendVal = sendVal | 0xF000;			//gain=1,active mode
	SPDR=sendVal>>8;					// send MSB
	do {} while (!(SPSR&(1<<SPIF)));
	dummy=SPDR; 				//dummy read to clear flag
	SPDR=sendVal&0x00FF;				// send LSB
	do {} while (!(SPSR&(1<<SPIF)));
	dummy=SPDR; 				//dummy read to clear flag
	SS_HI;

	// update DAC
	LDAC_LO;
	LDAC_HI;
	
	sei();
}

//
// waveform convertors
//
unsigned int ConvertToTri(unsigned long thisPhase)
{
    unsigned int temp;

    temp = (thisPhase>>20)&0x7FF;
    if(thisPhase&0x80000000)
        temp = temp<<1;
    else
        temp = 0xfff-(temp<<1);
    return temp;
}

unsigned int ConvertToSaw(unsigned long thisPhase)
{
    return thisPhase>>20;
}


unsigned int ConvertToSin(unsigned long thisPhase)
{
    unsigned int quarterPhase;
    unsigned char thisQuarter;
    unsigned int thisSample;
    
    quarterPhase = (thisPhase>>20) & 0x3FF;    //bottom 10 bits please
    thisQuarter = thisPhase>>30;                //top two bits is the quarter
    switch(thisQuarter) {
        case 0:
            //thisSample=pgm_read_word(&sine_table[quarterPhase>>1]);
            break;
        case 1:
            //thisSample=pgm_read_word(&sine_table[(0x3FF-quarterPhase)>>1]);
            break;
        case 2:
            //thisSample=0xFFF-pgm_read_word(&sine_table[quarterPhase>>1]);
            break;
        case 3:
            //thisSample=0xFFF-pgm_read_word(&sine_table[(0x3FF-quarterPhase)>>1]);
            break;
    }
    return thisSample;
}

void ProcessDCO1(void)
{

	Lfo1_DCO+=Lfo1_INC;

	switch(LfoWave[0]) {
		case sin_wave:
			Lfo1_DAC=ConvertToSin(Lfo1_DCO);
			break;				
		case tri_wave:
			Lfo1_DAC=ConvertToTri(Lfo1_DCO);
			break;				
		case saw_wave:
			Lfo1_DAC=ConvertToSaw(Lfo1_DCO);
			break;	
	}
}

void ProcessDCO2(void)
{
	Lfo2_DCO+=Lfo2_INC;
	switch(LfoWave[1]) {
		case sin_wave:
			Lfo2_DAC=ConvertToSin(Lfo2_DCO);
			break;				
		case tri_wave:
			Lfo2_DAC=ConvertToTri(Lfo2_DCO);
			break;				
		case saw_wave:
			Lfo2_DAC=ConvertToSaw(Lfo2_DCO);
			break;	
	}
}


ISR (TIMER0_COMPA_vect)
{
	static	uint16_t	Lfo1_count, Lfo2_count;

//ProcessDCO1();

	switch(LfoRange[0]) {
		case hi_range:
			ProcessDCO1();
			break;
		case med_range:
			if(Lfo1_count==0) {
				Lfo1_count=range_count_med;
				ProcessDCO1(); }
			else
				Lfo1_count--;
			ProcessDCO1();
			break;
		case low_range:
			if(Lfo1_count==0) {
				Lfo1_count=range_count_lo;
				ProcessDCO1(); }
			else
				Lfo1_count--;
			ProcessDCO1();
			break;
	}
	
		
	switch(LfoRange[1]) {
		case hi_range:
			ProcessDCO2();
			break;
		case med_range:
			if(Lfo2_count==0) {
				Lfo2_count=range_count_med;
				ProcessDCO2(); }
			else
				Lfo2_count--;
			ProcessDCO2();
			break;
		case low_range:
			if(Lfo2_count==0) {
				Lfo2_count=range_count_lo;
				ProcessDCO2(); }
			else
				Lfo2_count--;
			ProcessDCO2();
			break;
	}

	//SendToShiftyTypes();
}

//	ADC interrupt
ISR(ADC_vect)
{

// DEBUG
if(PIND&0x02)
PORTD &= ~0x02;
else
PORTD |= 0x02;
// DEBUG


	//process results
	if(thisAdcChannel==3) {
		//Lfo2_INC = pgm_read_word(&expo_table[ADC]);
		Lfo2_INC = ADC*0xffff;
		thisAdcChannel=4;
	} else {
		//Lfo1_INC = pgm_read_word(&expo_table[ADC]);
		Lfo1_INC = ADC*0xffff;
		thisAdcChannel=3;
	}
	
	//set up for next channel
	ADMUX &= 0xF0;
	ADMUX |= thisAdcChannel;
	ADCSRA |= (1<<ADSC);				//start next conversion

}

 

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Man, you guys are great!  I didn't change anything but now its working!  Thanks!

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I spoke too soon, but I got a clue. It looks like my 2 huge tables are somehow causing an issue. Progmem, maybe?

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My sine table is definitely causing the ADC interrupts to not work.  But why, how?

sine table code:

const uint16_t sine_table[] PROGMEM = {
0x0800,
0x0801,
0x0803,
.
.
.
.
0x0FFF,
0x0FFF,
0x0FFF,
0x0FFF,
0x0FFF };  // 2048 entries

And the code that uses it:

unsigned int ConvertToSin(unsigned long thisPhase)
{
    unsigned int quarterPhase;
    unsigned char thisQuarter;
    unsigned int thisSample;
    
    quarterPhase = (thisPhase>>20) & 0x3FF;    //bottom 10 bits please
    thisQuarter = thisPhase>>30;                //top two bits is the quarter
    switch(thisQuarter) {
        case 0:
            thisSample=pgm_read_word(&sine_table[quarterPhase>>1]);
            break;
        case 1:
            thisSample=pgm_read_word(&sine_table[(0x3FF-quarterPhase)>>1]);
            break;
        case 2:
            thisSample=0xFFF-pgm_read_word(&sine_table[quarterPhase>>1]);
            break;
        case 3:
            thisSample=0xFFF-pgm_read_word(&sine_table[(0x3FF-quarterPhase)>>1]);
            break;
    }
    return thisSample;
}

Yes I am including pgmspace.h.  Any thoughts?

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to note that if this is newly authored code and not some old stuff you are maintaining you should be switching from PROGMEM to __flash which is much easier to use.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the tip on __flash. I'll make the change.

 

My aha moment was premature. Apparently the ADC interrupts started working randomly. Comment out the tables has no effect. next I am ditching the debugWire to see if that is causing the issue.

 

 

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I stripped the code down to minimal just to have ADCs running and it worked, so the problem is in my code. I have no idea where. Something to do with the sequence of setting stuff up?

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Edit:  never mind!

 

 

Jim

 

 

Last Edited: Wed. Aug 30, 2017 - 05:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

madhun wrote:
I stripped the code down to minimal just to have ADCs running and it worked, so the problem is in my code. I have no idea where. Something to do with the sequence of setting stuff up?

You might want to set up a timer interrupt, and blink an LED as a sanity check.

 

I looked over the full code a bit.  I was curious about your DAC driver.  Soon you will want to simplify with a general-purpose SPI send-receive routine; discussed often here including recently. 

 

One example: http://www.avrfreaks.net/forum/a...

    byte SPI_Transfer(volatile byte data)
    {
        SPDR = data;
        while (!(SPSR & (1<<SPIF)));
        return SPDR;
    }

Another: http://www.avrfreaks.net/comment...

 

With the wait loop in there >>with global interrupts turned off<<, it could explain your situation.  1)  No need to turn off interrupts for SPI work; 2)  The commented-out call is in the other ISR.  Global interrupts are already disabled.  Your fussing with cli/sei is enabling nested interrupts.  But that call is commented out...

 

Also in that routine, I looked at /SS handling.  If that reverts to slave from master, that would explain the failure with the SPI loop.  But PB2 is an output...

 

So, examine all paths through the timer ISR to make sure it indeed completes always.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Aug 30, 2017 - 06:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the tip on the SPI. but its moot until I can get p[ast the ADC interrupt issue.

 

A already have a timer and it is getting its interrupts. Also I am toggling an output pin. When in the timer ISR I get the pin toggling, but now when in the ADC ISR i do not.

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FIXED!! The issue was timing. My timer routine is taking so long that as soon as it ends it gets another timer ISR. The ADC never had a chance to run. The next rev of the board will have a 20M crystal. that should fix it.

 

Thanks to all who helped!

 

--Timbo

Tim Ressel
Portland, OR
timr@earthling.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

madhun wrote:
My timer routine is taking so long that as soon as it ends it gets another timer ISR.

I didn't dig through fully, so I don't know how often you are running the ISR.

 

Atmel app notes run a 3-phase motor sine generator at fairly modest clock rates.  I don't know your app, but 2048 points sounds like way overkill.  There is an awful lot of stuff in that ISR.  If it were me, I'd try to streamline setting the next value with pre-calculated indices at least, and maybe even pre-fetched values.

 

But these are going to a DAC.  The SPI service time is going to keep adding microseconds as well.

 

Within parameters, AVR8s have good throughput.  I can't help thinking that an Xmega model  with twin DAC channels and DMA would make child's play of that.  Load up the DMA buffer(s) when parameter(s) change, then tell it to "go" and no further processor intervention.

 

I'm speculating that 20MHz will not be a cure-all.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.