Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Interrupt problem - stack overflow

Status
Not open for further replies.

elec123

New Member
Hi,

I'm currently having problems setting up an interrupt in my c code - this is the first time i have used interrupts and am having difficulty understanding where i'm going wrong....

In my initialise function i set up the interrupt bits:

Code:
	OPTION = 0x00;
	INTCON = 0x00;
	
	OPTION = 0x02;

	GIE = 1;
	PEIE = 1;
	TMR0IE = 1;

	TMR0 = 0x82;

Outside of the main, my ISR consists of:

Code:
void interrupt_isr()
{

unsigned char dig_output;



// Output error voltage correction
	
		ADCON0 = 0x88; 
		a2d_conversion();
		dig_output = ADRESH;


if	(dig_output<97) // output voltage is less than 99V
	{	

	--bodge; //	increment duty cycle

	}

else if (dig_output>100) // output voltage is greater then 101V
            {	

            ++bodge; //	decrement duty cycle

            }

	TMR0 = 0x82;
	TMR0IF = 0; 
}

My code builds successfully but in MPLAB SIM I get the error message:

CORE-E0001: Stack over flow error occurred from instruction at 0x0000d8

Is this because i'm trying to call a function within the interrupt? Have i set up the interrupt bits correctly?

Help on this would be greatly appreciated, as i'm very confused!

Thanks in advance,

elec123
 
Being in C makes it dificult to know what might be happening, and you don't mention what processor you're using? - but generally it's a BAD idea to call subroutines or functions within an ISR (due to the very limited stack size).
 
Need to see the whole thing

We will need to see the whole program. The questions are what is going on in the main() also.

:rolleyes:

Nigel is correct. Bad to do subroutine calls in the ISR.
 
Thanks for your replies, i'm using a PIC16F877A. The interrupt is being used as a method for output voltage control of a boost converter (trying to maintain a constant 100V output). Reading Vin and Vout through ADC of PIC.

Code:
Full code is:

// Descritpion: Program to read voltage of solar panel and adjust the PWM accordingley to produce 100V at the output.
// Author:
// Version: v.1

#include <pic.h>


const char look1 []= {
0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	
0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,
0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,
0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,	0x02,


0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,
0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,
0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,
0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,
0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,	0x03,
0x03,

0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,
0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,
0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04,	0x04}; // look up table array for ccpr1l.

const char look2 []= {
0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	
0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,
0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	
0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	
0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	
0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,	0x2C,
0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,	0x3C,
0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	0x0C,	
0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	0x1C,	
0x2C,	0x2C,	0x2C}; // look up table array for ccp1con. 

void main(void);
void Initialize_ADC(void);
void a2d_conversion(void);
void interrupt isr (void);

unsigned char bodge;

void main()
{

unsigned char dig_input;
unsigned int george;

Initialize_ADC();
	
//Read_ADC_input_Vin

	while(1)
	{
		//Set ADCON0

		ADCON0 = 0x80; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
		a2d_conversion();
	
		dig_input = ADRESH;

		if (dig_input<128) //if solar panel (Vin) to boost converter is less than 10V don't boost.
		{
		CCPR1L = 0x64; //sets pwm to 100% this gets inverteted to 0% by driver i.e don't boost.
		CCP1CON = 0x0C; 
		}

	else
			{
	
		dig_input -= 128; // scaling dig_input to number between 0-127 (128 values)
		
		george = dig_input + bodge;
		if (george < 128)
		{george = 0;}
		else
		{george -= 128;}
		
		CCPR1L = look1[george];
		CCP1CON = look2[george];

			}
	}
}

void Initialize_ADC()
{
	TRISA = 0x01;
	TRISC = 0x00;
	PORTC = 0x00;
	PR2 = 	0x18;				
	T2CON = 0x05;

	OPTION = 0x00;
	INTCON = 0x00;

	//Set ADCON1

	ADCON1 = 0x00; // clear ADCON1 register.//
	ADCON1 = 0x02; // set PCFG1 bit i.e AN0 is analogue input.
	
	GIE = 1;
	PEIE = 1;
	TMR0IE = 1;

	OPTION = 0x02;

	TMR0 = 0x82;

	//intialise Global Variables
	bodge = 0x80;
}

void a2d_conversion()
{
unsigned char delay = 0x05;

		//Read_ADC_input_Vout
	
		ADON = 1; // turn A2D on


		while (delay != 0)
		{
			delay -= 1;
		}
	
		ADGO = 1; //captures analogue voltage on pin.
	
		while (ADGO)
		{
			//dig_output = 0x00;		
		}

};


void interrupt isr()
{
unsigned char dig_output;

	
// Output error voltage correction
	
	
		ADCON0 = 0x88; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
		a2d_conversion();
		dig_output = ADRESH;

			if	(dig_output<96) // output voltage is less than 100V
				{	

				-- bodge; //	increment duty cycle

				}

			else if (dig_output>102) // output voltage is greater then 100V
				{	

				++ bodge; //	decrement duty cycle

				}

		TMR0 = 0x82;
	             TMR0IF = 0;
	
}

So is calling the a2d conversion function in the interrupt, causing this stack overflow error message? Any ideas on how to solve this?

Your help on this would be greatly appreciated.

Thanks
 
Collect Data in ISR

Collect the data in the ISR, convert it in the mainline.

You can use a flag to indicate that data exists by setting it in the ISR and clear it in the main.
 
thanks for your reply, but i don't quite understand....

do you mean that my isr wld just have....

Code:
	TMR0 = 0x82;
	TMR0IF = 0;

and put this in the main?

Code:
	ADCON0 = 0x88; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
		a2d_conversion();
		dig_output = ADRESH;

			if	(dig_output<96) // output voltage is less than 100V
				{	

				-- bodge; //	increment duty cycle

				}

			else if (dig_output>102) // output voltage is greater then 100V
				{	

				++ bodge; //	decrement duty cycle

				}

pretty new to programming so guidance on this would be great....

thanks
 
Delay loops in ISR are BADDDDD

This is what you are calling from the ISR:

void a2d_conversion()
{
unsigned char delay = 0x05;

//Read_ADC_input_Vout

ADON = 1; // turn A2D on


while (delay != 0)
{
delay -= 1;
}

ADGO = 1; //captures analogue voltage on pin.

while (ADGO)
{
//dig_output = 0x00;
}

};

You just don't do delay loops in ISRs.



What should be going on:

Globally

char read_flag = 0;
char raw_data;

In the ISR

Capture the data from the analog input;

raw_data = (wherever it comes from);
read_flag = 1;


Now in the main loop

if (read_flag) {
convert_value(raw_data);
read_flag = 0;
now go process the converted value
}
 
EDIT: August posted before I could hit submit... see his C version above

You *can* call a routine from within an ISR provided it's a *low* priority ISR. It's still *not* best practice but it does work.

As August says, dealing with the interrupt in the ISR and then dealing with the action in the main line code is the best way to deal with interrupt-triggered code that must do subroutine calls. By this he means:

ISR:
Clear reason for interrupt (if needed, depends on peripheral)
Set a global flag to say "interrupt happened"
Reset peripheral values (e.g. load timer)
ISR Return

MAIN:
Loop:
Look for the global flag being set - clear the flag and do what needs to be done
Do anything else you need to
EndLoop

You can see that this is in pseudo-code (and therefore useful for assembler programmers too - hi Nigel :)) but hopefully you get the idea.

P.
 
thanks for your replies, your advice has cleared up my confusion over using interrupts.

I have made some changes to my code and am no longer receiving the stack overflow error but wanted to check that i have set up my interrupt service routine correctly as i have tested my code on my circuit and when i increase the voltage to the boost converter it reaches about 12V (should be able to go up to 20V) but then the current drawn from the supply exceeds the current limit (i set it to 4A) and the inductor buzzes loudly!

I'm pretty sure that it must be an interrupt issue as my previous code (without interrupt) wasn't having the problems described above, and my circuit components are all rated for high power applications....

I'd really appreciate it if someone could look over my code and check the interrupt section looks alright...

Code:
const char look1 []= { (128 values!) }; // look up table array for ccpr1l.

const char look2 []= { (128 values!) }; // look up table array for ccp1con. 

void main(void);

void Initialize_ADC(void);
void a2d_conversion(void);
void interrupt isr (void);

unsigned char bodge;
unsigned char read_flag = 0x00;

/************
 * MAIN LOOP
 ************/

void main()
{

unsigned char dig_input;
unsigned char dig_output;

unsigned int george;

Initialize_ADC();
	
	while(1)
	{
	
		ADCON0 = 0x80; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.

		a2d_conversion();
	
		dig_input = ADRESH;


		if (dig_input<128) //if solar panel (Vin) to boost converter is less than 10V don't boost.
		{
		CCPR1L = 0x64; //sets pwm to 100% this gets inverteted to 0% by driver i.e don't boost.
		CCP1CON = 0x0C; 
		}

	else
			{
	
		dig_input -= 128; // scaling dig_input to number between 0-127 (128 values)
		
		george = dig_input + bodge;
		if (george < 128)
		{george = 0;}
		else
		{george -= 128;}	
		
		CCPR1L = look1[george];
		CCP1CON = look2[george];	

if (read_flag == 0xFF) 
				{

/*********************************
 * Output Voltage Error Correction
 *********************************/
	
		ADCON0 = 0x88; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
		a2d_conversion();
		read_flag = 0x00;
		dig_output = ADRESH;


			if	(dig_output<97) // output voltage is less than 99V
					{	

				--bodge; //	increment duty cycle

					}

			else if (dig_output>100) // output voltage is greater then 101V
					{	

				++bodge; //	decrement duty cycle

					}

				}
			}
	}
}

/*******************
 * Initialisation
 *******************/

void Initialize_ADC()
{

/********** Set up ports ********/

	TRISA = 0x01; // Port A as Outputs
	TRISC = 0x00; // Port C as Inputs
	PORTC = 0x00;

/********** Set up PWM **********/

	PR2 = 	0x18;				
	T2CON = 0x05;

/********** Set up A2D **********/

	ADCON1 = 0x00; 
	ADCON1 = 0x02; 

/********** Set up ISR **********/

	OPTION = 0x00;
	INTCON = 0x00;
	
	OPTION = 0x02;

	GIE = 1;
	PEIE = 1;

	TMR0IE = 1;
	TMR0 = 0x82;

/***** Set Global Variables ****/

	bodge = 0x80;
	read_flag = 0x00;

}

/*********************************
 * Analogue to Digital Conversion
 *********************************/

void a2d_conversion()
{

unsigned char delay;

delay = 0x05;
ADON = 1; // turn A2D on

	while (delay != 0)
		{
			delay -= 1;
		}
	
ADGO = 1; //captures analogue voltage on pin.
	
	while (ADGO)
		{
			//dig_output = 0x00;		
		}
};


/*******************************
 * Interrupt Service Routine
 *******************************/


void interrupt isr()
{

read_flag = 0xFF;

TMR0IF = 0; 
TMR0 = 0x82; 

}

Thanks for your help!
 
anyone? could really do with someone having a quick look over my interrupt service routine and provide any advice.....

Thanks
 
I think you problem is in your main code. I have indented it so you can see what is being executed when. I.E. the if (read_flag == 0xFF) line only gets executed in the else part of the above code. Was this your intention?
Code:
void main(){
unsigned char dig_input;
unsigned char dig_output;
unsigned int george;

    Initialize_ADC();
    
    while(1)
    {    
        ADCON0 = 0x80; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
        a2d_conversion();    
        dig_input = ADRESH;
        if (dig_input<128) //if solar panel (Vin) to boost converter is less than 10V don't boost.
        {
            CCPR1L = 0x64; //sets pwm to 100% this gets inverteted to 0% by driver i.e don't boost.
            CCP1CON = 0x0C; 
        }
        else
        {
            dig_input -= 128; // scaling dig_input to number between 0-127 (128 values)    
            george = dig_input + bodge;
            if (george < 128)
                george = 0;
            else
                george -= 128;            
            CCPR1L = look1[george];
            CCP1CON = look2[george];    
            if (read_flag == 0xFF) 
            {
                ADCON0 = 0x88; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
                a2d_conversion();
                read_flag = 0x00;
                dig_output = ADRESH;
                if (dig_output<97) // output voltage is less than 99V
                {    
                    --bodge; //    increment duty cycle
                }
                else if (dig_output>100) // output voltage is greater then 101V
                {    
                    ++bodge; //    decrement duty cycle
                }
            }
        }
    }
}

Mike.
 
yes this was my intention as i'm trying to slow down the "output voltage error correction" section so the adc is sampling at 2kHz, a tenth of the switching frequency ( i have now set TMR0 to 131 - still having same problems).

I think the inductor is reaching saturation due to the loud buzz sounds and all the current being drawn from the supply, which suggests to me that the Toff time is not long enough for the inductor to discharge.....but i don't have these problems if i take the interrupt section out....

As this is the first time i have used interrupts, i'm not really sure what i'm doing wrong...

Any other suggestions?
 
In that case, I would suggest getting rid of the interrupt and polling the flag.

Code:
void main(){
unsigned char dig_input;
unsigned char dig_output;
unsigned int george;

    Initialize_ADC();
    
    while(1)
    {    
        ADCON0 = 0x80; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
        a2d_conversion();    
        dig_input = ADRESH;
        if (dig_input<128) //if solar panel (Vin) to boost converter is less than 10V don't boost.
        {
            CCPR1L = 0x64; //sets pwm to 100% this gets inverteted to 0% by driver i.e don't boost.
            CCP1CON = 0x0C; 
        }
        else
        {
            dig_input -= 128; // scaling dig_input to number between 0-127 (128 values)    
            george = dig_input + bodge;
            if (george < 128)
                george = 0;
            else
                george -= 128;            
            CCPR1L = look1[george];
            CCP1CON = look2[george];    
            [COLOR="Blue"]if (TMR0IF) [/COLOR]
            {[COLOR="Blue"]
                TMR0IF=0;		//clear the interupt flag
                TMR0 = 0x82;[/COLOR]
                ADCON0 = 0x88; //clear ADCON0 A2D starts sampling again, selects A2D pin AN0.
                a2d_conversion();
                dig_output = ADRESH;
                if (dig_output<97) // output voltage is less than 99V
                {    
                    --bodge; //    increment duty cycle
                }
                else if (dig_output>100) // output voltage is greater then 101V
                {    
                    ++bodge; //    decrement duty cycle
                }
            }
        }
    }
}
You should of course disable the interrupt (GIE=0).

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top