Passing values between main loop an interrupt

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include 
#include 
#include 
int cycles,TF,TS,cycletobbCMP,GPLF,Tcharge;	//TF=tappet flag, TS=tappet sensor, GPL=gearbox position loop flag

int main()
{
DDRD  |= 0x40;		//set PD6 as output (motor drive)
DDRB  |= 0x02;		//set PB1 as output (motor brake)
PORTD &= ~0x40;		//turns off PD6(motor drive)
PORTC &= ~0x40;		//turns off PC6(motor brake)
PORTC |= 0x04;		//enables pullup resistor on PC2(input)

sei();				//enable global interrupts 
PCICR  |= 0x02;		//enable PCIE1 for PCINT 14-8
PCMSK1 |= 0x04;		//enable PCINT10 on PC2 (Trigger), PCMSKx enables individual pin interrupts

ADCSRA |= 0x06;  	//ADC Prescalar set to 64 - 312.5kHz@20MHz 
ADMUX  |= 0x20;		//changes ADC to 8-bit resolution, read from ADCH		
ADCSRA |= 0x80;  	//enable ADC
DIDR0  |= 0xFF;		//disables digital input buffer on ADC's when reading analog signals to save power
	
	while(1)
	{
	ADMUX |= 0x03;					//PC4(BB detection), PC5(Tappet Position Sensor), PC3(Tcharge sensor)
	ADCSRA |= (1 <<ADSC);			//starts ADC conversion
	while(ADCSRA & (1<<ADSC));		//wait for conversion complete		
		if(ADCH > 132)				
			Tcharge = 1;
	}
}

ISR(PCINT1_vect)		//interrupt vector, detects trigger
{
	if(!(PINC&_BV(PINC2)) && Tcharge == 1)		//or possibly if ((PINC & (1<<2)) == 0)
	{
		PORTD |= (1 << 6);
		GPLF = 1;
	}
		
	
	while((GPLF == 1 || !(PINC&_BV(PINC2))) && Tcharge == 1)
	{	
		ADMUX |= 0x04;					//PC4(BB detection), PC5(Tappet Position Sensor), PC3(Tcharge sensor)
		ADCSRA |= (1 <<ADSC);			//starts ADC conversion
		while(ADCSRA & (1<<ADSC));		//wait for conversion complete		
			if(ADCH <= 10)				//trigger logic START
				TS=1;	
			else
	  			TS=0;

			if(TF != TS)	
			{
			if(TS)
				cycles = cycles + 1;	//Incremented only once each time TS is true
				TF=TS;
			}							//trigger logic END

			if(cycles == 3)				//this segment breaks the loop after 1 full gearbox cycle is completed(2 tappet plate cycles)
			{
				cycles=0;
				cycletobbCMP = cycletobbCMP + 1;
				if(PINC&_BV(PINC2))
					GPLF = 0;
			}	

			if(cycletobbCMP == 5)
			{
				Tcharge = 0;
				cycletobbCMP = 0;
			}
	}

	if(GPLF == 0)
	{
		_delay_ms(10);
		PORTD &= ~(1<< 6);
		PORTC |= (1 << 6);	
		_delay_ms(10);
		PORTC &= ~(1<< 6);
	}
}

What is not happening is Tcharge is not being seen as a 1. I verified that the sensor I am using to set Tcharge functions, however when its set as 1 in the main loop, the interrupt in not seeing the value in Tcharge or for some reason it is not being saved. Since Tcharge is declared before the main loop shouldnt it be a global value? I do not understand why this is happening.

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

any variables that are shared between to process threads (ie the main loop, and an interrupt) need to be declared as 'volatile' Otherwise the optimizer will remove various read/write accesses to the memory location to increase performance. (the optimizer cannot see that the same variable is being used in a separate execution thread)

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include 
#include 
#include 
int cycles,TF,TS,cycletobbCMP,GPLF;	//TF=tappet flag, TS=tappet sensor, GPL=gearbox position loop flag
int volatile Tcharge,SF;	//volatile tells compile that the value of the variable may change at any time-without
							//and action being taken by the code the compiler finds nearby. SF=scan flag

int main()
{
DDRD  |= 0x40;		//set PD6 as output (motor drive)
DDRB  |= 0x02;		//set PB1 as output (motor brake)
PORTD &= ~0x40;		//turns off PD6(motor drive)
PORTC &= ~0x40;		//turns off PC6(motor brake)
PORTC |= 0x04;		//enables pullup resistor on PC2(input)

sei();				//enable global interrupts 
PCICR  |= 0x02;		//enable PCIE1 for PCINT 14-8
PCMSK1 |= 0x04;		//enable PCINT10 on PC2 (Trigger), PCMSKx enables individual pin interrupts

ADCSRA |= 0x06;  	//ADC Prescalar set to 64 - 312.5kHz@20MHz 
ADMUX  |= 0x20;		//changes ADC to 8-bit resolution, read from ADCH		
ADCSRA |= 0x80;  	//enable ADC
DIDR0  |= 0xFF;		//disables digital input buffer on ADC's when reading analog signals to save power
	
	while(1)
	{
		while(!SF)
		{
		ADMUX |= 0x03;					//PC4(BB detection), PC5(Tappet Position Sensor), PC3(Tcharge sensor)
		ADCSRA |= (1 <<ADSC);			//starts ADC conversion
		while(ADCSRA & (1<<ADSC));		//wait for conversion complete		
			if(ADCH > 135)	
			{			
				Tcharge = 1;
				SF = 1;
			}
		}
	}
}

ISR(PCINT1_vect)		//interrupt vector, detects trigger
{
	if(!(PINC&_BV(PINC2)) && Tcharge)		//or possibly if ((PINC & (1<<2)) == 0)
	{
		PORTD |= (1 << 6);
		GPLF = 1;
	}
		
	
	while((GPLF || !(PINC&_BV(PINC2))) && Tcharge)
	{	
		ADMUX |= 0x04;					//PC4(BB detection), PC5(Tappet Position Sensor), PC3(Tcharge sensor)
		ADCSRA |= (1 <<ADSC);			//starts ADC conversion
		while(ADCSRA & (1<<ADSC));		//wait for conversion complete		
			if(ADCH <= 7)				//trigger logic START
				TS=1;	
			else
	  			TS=0;

			if(TF != TS)	
			{
			if(TS)
				cycles = cycles + 1;	//Incremented only once each time TS is true
				TF=TS;
			}							//trigger logic END

			if(cycles == 3)				//this segment breaks the loop after 1 full gearbox cycle is completed(2 tappet plate cycles)
			{
				cycles=0;
				cycletobbCMP = cycletobbCMP + 1;
				if(PINC&_BV(PINC2))
					GPLF = 0;
				if(cycletobbCMP == 5)
				{
					Tcharge = 0;
					cycletobbCMP = 0;
					SF = 0;
				}
			}	
	}
		PORTD &= ~(1<< 6);
		PORTC |= (1 << 6);	
		_delay_ms(10);
		PORTC &= ~(1<< 6);
}

declaring Tcharge as volatile fixed that problem, however another problem has popped up which I do not understand. The main loop that is under while(1) is just polling for a the ADC to read a value that will set Tcharge to 1. However it seems to mess up my interrupt code, it no longer functions correctly, if I remove the code from the while loop and Tcharge everything works as it should. It seems as though the ADC is not updating once the interrupt breaks the main loop. Should the interrupt break the main loop as soon as it is triggered, run the code in the interrupt until it is complete, which includes the while loop in the interrupt, then return the main loop? The only thing I can think of that would cause this problem is that some how when it breaks the main loop because it is using the ADC the ADC is still waiting for the value or retaining the value it had when it was reading from PC3, when I need it to switch over to PC4. What I want it to do is read PC3 until the interrupt occures, then I want only the code in the interrupt to have priority until it is finished, then it can go back to reading PC3.

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

variables that are larger than one byte in size, that are shared, must also be accessed in an atomic manner. That is you need to prevent the access from being interrupted by any process that relies on the value, or is going to alter the value. The easiest way to do this is bracket any of these accesses with cli/sei.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

_delay_ms(10) in an ISR? You sure about that?

I suppose it's true that the majority of the program happens within one ISR but it's never usually a great idea to spend more than a few microseconds in an ISR (otfen simply to flag than an event occurred) with all the none timing critical stuff done at leisure in an interruptible main() (possibly with small parts cli/sei protected to prevent them being interrupted if necessary)

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

You also cannot be using the same hardware resource between your two threads of execution. That is, do all your ADC stuff in your ISR, or do it in main... not both.

And just for clarification, variables of type 'int' are 16 bits in size, if you only need a true/false flag, use a variable of one of the char (8 bit) types.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

The reason I have all of this in an interrupt is because the code that runs in the interrupt is the highest priority and to save processing power, why run it in a forever loop if it only needs to run when certain conditions are true? I guess I could use a forever loop, place the code inside the ISR into the forever loop and run that code only after a flag is set from the ISR. I may give that a try and see if it solves this issue.

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

char cycles,TF,TS,cycletobbCMP,GPLF,Tcharge = 1;	//TF=tappet flag, TS=tappet sensor, GPL=gearbox position loop flag
volatile char Trigger =0;

//volatile tells compile that the value of the variable may change at any time-without
//and action being taken by the code the compiler finds nearby. SF=scan flag

int main()
{
DDRD  |= 0x40;		//set PD6 as output (motor drive)
DDRB  |= 0x02;		//set PB1 as output (motor brake)
PORTD &= ~0x40;		//turns off PD6(motor drive)
PORTC &= ~0x40;		//turns off PC6(motor brake)
PORTC |= 0x04;		//enables pullup resistor on PC2(input)

sei();				//enable global interrupts 
PCICR  |= 0x02;		//enable PCIE1 for PCINT 14-8
PCMSK1 |= 0x04;		//enable PCINT10 on PC2 (Trigger), PCMSKx enables individual pin interrupts

ADCSRA |= 0x06;  	//ADC Prescalar set to 64 - 312.5kHz@20MHz 
ADMUX  |= 0x20;		//changes ADC to 8-bit resolution, read from ADCH		
ADCSRA |= 0x80;  	//enable ADC
DIDR0  |= 0xFF;		//disables digital input buffer on ADC's when reading analog signals to save power
	
	while(1)
	{	//START main

		while(!Trigger)
		{	//START tcharge loop
			ADMUX |= 0x03;					//PC4(BB detection), PC5(Tappet Position Sensor), PC3(Tcharge sensor)
			ADCSRA |= (1 <<ADSC);			//starts ADC conversion
			while(ADCSRA & (1<<ADSC));		//wait for conversion complete		
				if(ADCH > 135)			
					Tcharge = 1;
		}	//END tcharge loop

		while(Trigger)
		{	//START Trigger Loop
			if(!(PINC&_BV(PINC2)) && Tcharge)		//or possibly if ((PINC & (1<<2)) == 0)
			{
				PORTD |= (1 << 6);
				GPLF = 1;
			}
		
	
			while( ((GPLF || !(PINC&_BV(PINC2))) && Tcharge) )
			{	//START greabox control
				ADMUX |= 0x04;					//PC4(BB detection), PC5(Tappet Position Sensor), PC3(Tcharge sensor)
				ADCSRA |= (1 <<ADSC);			//starts ADC conversion
				while(ADCSRA & (1<<ADSC));		//wait for conversion complete		
					if(ADCH <= 7)//START cycles logic
						TS=1;	
					else
			  			TS=0;

					if(TF != TS)	
					{
					if(TS)
						cycles = cycles + 1;
						TF=TS;
					}			//END cycles logic 

					if(cycles == 3)	//START cycle completion
					{
						cycles=0;
						cycletobbCMP = cycletobbCMP + 1;

						if(PINC&_BV(PINC2))
							GPLF = 0;

						if(cycletobbCMP == 5)//START completed cycles compare
						{						
							Tcharge = 0;
							cycletobbCMP = 0;
						}					 //END completed cycles compare
					}				//END cycle completion
						
		
			}	//END gearbox control
				PORTD &= ~(1<< 6);
				PORTC |= (1 << 6);	
				_delay_ms(10);
				PORTC &= ~(1<< 6);
				Trigger = 0;
		}	//END Trigger Loop
	}	//END main
}

ISR(PCINT1_vect)		//interrupt vector, detects trigger
{
	Trigger = 1;
}

I modified the code so everything is done in the main loop save setting the "Trigger" value which enables my gearbox control loop. However I still have the exact same problem, for the life of me I cannot logically see why this is happening. It once again works fine if I remove the ADC conversion involving PC3 and just set Tcharge to 1 at the beginning. Of course once Tcharge is set to 0 at the completion it cannot be reset to 1 as that is supposed to be dependant upon PC3, but that proves that the rest of the logic works. Its almost as if when I do an ADC conversion for PC3 it freeses the ADC once the interrupt occurs or messes with values. I shouldn't have to re-enable it when jumping back into the main loop from the ISR should i?