Troubleshooting some pin change interrupts (ATMega48/88/168)

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

I'm currently overhauling some working code to include a lot more power management at the valuable advice of people on these forums.

My code does a bit of general housekeeping, enables interrupts and puts the processor to sleep. My timer interrupts wake the system up as planned (at a rate of 125Hz), does its business and then the system goes back to sleep. This is all in a while loop which keeps going until a continue flag is set to zero - the plan is to use a pin change interrupt to "cancel" the current operation.

The code is a time lapse generator, so it delays for however long (say 4 seconds), takes a picture and then delays, etc. It does this forever unless one of the following two conditions are satisfied:

1. The shot count has hit a preset maximum, i.e. the user says "take 512 shots (that's all my memory card will hold)".
2. The shoot button is pressed whilst the time lapse is in progress.

I know interrupts work because besides the pin change interrupt, the timer does its job and keeps the timer going and the shots taking even though the LCD is turned off and the CPU is sleeping.

The buttons are on the following pins:

1. Shoot, port C, pin 3, PCINT11
2. The rest, port B pins 1-5, PCINT1-5

So during my i/o intialisation function i have this:

//Set interrupt pins
	//general buttons
	PCMSK0 |= (1 << PCINT1) | (1 << PCINT2) | (1 << PCINT3) | (1 << PCINT4) | (1 << PCINT5);
	//shoot button
	PCMSK1 |= (1 << PCINT11);

Then, i enable the interrupt in the time lapse code:

void start_interval_shoot(void)
{
	//Initialise and start timer2, turn on pcint bit, enable sleep modes and set sleep mode to awake on timer compare or pin change, enable global interrupts
	sei();

	PCICR |= (0 << PCIE0) | (0 << PCIE1);

	timer2_init();
	sleep_enable();
	set_sleep_mode(SLEEP_MODE_PWR_SAVE);
	interval_continue = 1;
	lcd_clrscr();
	//display the shooting screen
	lcd_puts("Shot No.:0\n");
	lcd_puts("Timer:   0");
	
	for (uint8_t j=0; j < 20; j++)
	{
		//display remaining seconds
		lcd_gotoxy(9,1);
		itoa(timeremain, buffer, 10);
		lcd_puts(buffer);
		//display shot count
		lcd_gotoxy(9,0);
		itoa(shotcount, buffer, 10);
		lcd_puts(buffer);
		Delay_ms(500);
	}
	//Shooting functionality is done in interrupts
	while(interval_continue == 1)
	{
     lcd_command(LCD_DISP_OFF);
		sleep_mode();
	}
	//Disable sleep, disable interrupts, disable the timer
	intervalstart = 0;
	sleep_disable();
	cli();
	timer2_deinit();
}

The vectors are:

ISR(PCINT1_vect)
{
	lcd_command(LCD_DISP_ON);
	lcd_clrscr();
	lcd_puts("Shoot");
	Delay_ms(1000);
}
// Run when shoot is pressed
ISR(PCINT0_vect)
{
	lcd_command(LCD_DISP_ON);
	lcd_clrscr();
	lcd_puts("Other");
	Delay_ms(1000);
}

So, what should happen is:

1. timer starts, interrupts on, etc
2. there's a status display and then the proc. sleeps to conserve power
3. every so often a shot is taken
4. if the shoot or any other button is pressed, the display is turned on and a message is written, then it turns off again.

The ports are connected to Vcc and the switch pulls them to ground. Certainly there IS a logic change on the pins when a button is pressed.

I get the feeling (again) that it's an obvious mistake, i've checked the vectors, the pins and the setup, interrupts are on and working...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 PCICR |= (0 << PCIE0) | (0 << PCIE1); 

I think this line should be this:

PCICR |= (1 << PCIE0) | (1 << PCIE1);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure if you descibed your problem above? Or did I miss it?

Do you want to wait 1s inside the ISRs? Well in fact 2s as you get 2 interrupts for every change so once the ISR finishes (1s) another interrupt occurs (flag set during the 1s delay due to bounce or the switch being released) so you are back into the IRS again (another second).

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

The problem is that the interrupts don't do anything at all. I.e. I change the logic level and nothing happens.

I just wrote two quick and dirty routines that should make things happen when buttons are pressed.

The plan after it works is:

If shoot is pressed, i'd write it so it changes the continue flag and disables interrupts so when the code jumps back into normal flow, it exits the while loop straight away. The first change will disable interrupts so it won't get called again until another time lapse starts.

If any other button is pressed, the pin change interrupt is disabled while the status menu is being displayed. It is then re-enabled before normal flow resumes. So unless the user holds down the button for 5 seconds and then releases, it should be fine.

Oops, yes farmosh203 that's just a miscopied line. In the actual code it's not like that.

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

Quote:
In the actual code it's not like that.
It helps if you post the acual code. :)

ie we don't know if those port pins are really set as inputs...I know silly question..

Quote:
The ports are connected to Vcc and the switch pulls them to ground.
Do you mean you have external pull up resistors? What value?

Is is possible to test the interrupts without the power save on?

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Ah, an interesting revelation.

When i overhauled the code, i redid the entire main function first to deal with a more efficient menu system and then to deal with power management.

It seems i never actually bothered to call io_init (the function that sets all the things like which ports are in/out). I never noticed before because as ports are set by default to be in, it never caused any obvious problems.

And SUCCESS!

I knew it was something obvious :P

Thanks for your kind interest anyway :)

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

So do you mean that the ports were actually set as outputs? :)

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

No no, far worse than that!

I set up all the pin masks in the initialisation code and then turn on/off the respective interrupt flags as i need to.

So it never interrupted because, as far as the AVR was concerned, nothing was happening!

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

:-)

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly