PCICR VS CLI ?? which is better to disable a group of PCInterupts

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

Greetings,

 

Im building a Remote control and would like to put the ATMEGA328p to sleep in between actions.

Im just brainstorming the concept now and roughing things, so I have not written any code yet.

 

My thought is to have the 328p sleep until an button is pressed. 

There will be 4 buttons all on the PD register. My plan is to use PCINT 16-23

 

But once the Interrupt happens and wakes the 328p I don't want another interrupt to hit (multiple Button presses).

 

My question is since I'm using just a single port for inputs should I use PCICR or cli to disable the interupts once one has been triggered.

I will be using the SPI to send data to a nRF24L01+ .... will the cli command alter SPI comms ?

For example

 

ISR(PCINT2_vect)

      { PCICR = 0b00000000;

         sleep_disable();

         Button_Flag=1;

      }

 

OR

 

ISR(PCINT2_vect)

    { cli;

       sleep_disable();

       Button_Flag=1;

     }

 

Any thoughts ?

 

Thank you

Michael

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

After an interrupt, interrupts are automatically disabled until re-enabled,

usually by the RETI and the end of an ISR.

If you want PCIs to remain disabled after said return,

The first might do what you want.

You might want to add PCIFR=(1<<PCIF2); at the end

to clear any pending PCIs.

Strictly speaking, your code does not disable the interrupt,

but the distinction might not be important.

PCICR &= ~(1<<PCIE2);

would disable the interrupt.

 

 

What is sleep_disable() for?

Iluvatar is the better part of Valar.

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

Skeeve,

 

To answer your questions I have to go deeper in to the concept.

1: handheld remote with 4 buttons and a joystick.

2: unit sleeps until a buttons pressed, (NOTE: I want to be able to use multiple button press giving me a total of 15 'selections' using only 4 buttons) 

3: due to multiple buttons will be pressed I want to disable the interrupts after the first button press is registered to prevent a new interrupt after the second button press if it has already completed the ISR.

4: the 328p will then power up the ADC & SPI & Transmitter, delay , Read buttons , Read Joystick ADC, read a Battery sense, format data , send to nRF24l01+,

5: Read buttons again , If no button pressed power down transmitter, enable Interupts, sleep .

    If, buttons press is still same, transmit data again.( used for moving a stepper motor, each data packet is one step.)

 

My concern is that since I will be using multi button press that I may not hit the two buttons in close enough timing that to generate only ONE ISR call , And also I don't want another ISR call when I let go of the buttons.

And, since they are PCINT 's they will generate a interrupt on CHANGE.

 

Thanks for the Reply,

Michael

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

After an interrupt, interrupts are automatically disabled until re-enabled,

Well, interrupts are disabled when the ISR is entered. This could be an indeterminate amount of time after the interrupt flag is triggered.

 

If you are using solely to wake the CPU (and not handling the button presses themselves) then do as skeeve said and clear the PCIE2 flag in the ISR. The button presses themselves should be handled by polling outside the ISR.

Regards,
Steve A.

The Board helps those that help themselves.

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

To be clear, the button-reading algorithm is

enable PCI
sleep
wake up from PCI
disale PCI
wait a bit // the debouncing part
read PIN register

Correct?

Iluvatar is the better part of Valar.

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

Koshchi, Yes was planning on Reading the PIND separately 

 

 

Skeeve, 

Yes that is the idea,  And on two of the buttons It will continue to transmit the same data until they are released

and on the other two buttons are a one shot. ( these will trigger and MP3 event in the main device).

 

 

Thank you again for your thoughts,  Im moving from an Arduino environment to a Atmel Studio environment...

More to learn but one line of code verses  separate attachInterrupt() for each button is faster and cleaner ... 

 

Michael

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

I have had a chance to try things out tonight....

And from my tests it appears that I am getting Both the button press and the release, even tho I am disabling the PCICR (PCIE2 bit).

When i do a cli(); I don't get the double hit (press and release) .

BUT on the two one shot buttons, If I press and hold one 1-shot and then press the other 1-shot button I get an interrupt EVEN THO it should be disabled.

 


#include <avr/sleep.h>
#include <avr/power.h>
#include <PinChangeInt/PinChangeInt.h>
#include <avr/interrupt.h>
#include <avr/io.h>

#define F_CPU 8000000UL
#define Xp A3	// X in
#define Yp A1	// Y In
#define Bp A0	// Battery Input
#define Read_Buttons	(~PIND&B11101000)

uint8_t		Button_Flag		= 0;	
uint8_t		B_value			= 0;
uint16_t	Xjoy			= 0;
uint16_t	Yjoy			= 0;
uint8_t		sleep_flag		= 0;
uint16_t	Bat_value		= 0;

ISR(PCINT2_vect)
{	cli();
	//PCICR &= ~(1<<PCIE2);		// Disables PortD Interrupts
	//sleep_disable();
	Button_Flag = 1;
	sleep_flag = 0;
}
void GotoSleep()
{
	sei();
	//PCICR |= (1<<PCIE2);		// Enable PortD Interrupts
	sleep_mode();				// Goes to sleep HERE
} 
void setup()
{
  Serial.begin(38400);	
  DDRD |= ~(B11101000);		// sets up PortD inputs
  PORTD |= B11101000;		// Turn one Pull up resistors
  PCICR	|= (1<< PCIE2);		// Enable Port D Pin Change Interrupts
  PCMSK2 |= B11101000;		// setup Pin Change INT mask PortD
  DDRC |= ~(B1101011);		// Setup up Port D I/O ADC & Joystick power
  sei();
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);	//Sets Sleep Mode
  sleep_enable();								// Enables Sleep
 // power_twi_disable();		// Disables TWI Module
 Serial.println("Setup Complete");
 }

void loop()

{if (Button_Flag == 1) 
	{if (Read_Buttons !=0) 	// Read Buttons
		{
		//power_adc_enable();
		//power_spi_enable();
		//delay(50);
		PORTC |= B00010100 ;		// Turn on Joystick 
		delay(10);
		B_value = Read_Buttons ;	// Read Buttons
		Xjoy = (analogRead(Xp));
		Yjoy = (analogRead(Yp));
		PORTC &= ~(B00010100) ;	// Turn off Joystick 
		Bat_value = (analogRead(Bp));
		
		if ((B_value&B01000000) == B01000000) {Serial.print(" W ");} else {Serial.print("   ");}
		if ((B_value&B10000000) == B10000000) {Serial.print(" C ");} else {Serial.print("   ");}
		if ((B_value&B00100000) == B00100000) {Serial.print(" Z ");} else {Serial.print("   ");}
		if ((B_value&B00001000) == B00001000) {Serial.print(" T ");} else {Serial.print("   ");}
			
		Serial.print(" X-");Serial.print(Xjoy);
		Serial.print("   Y-");Serial.print(Yjoy);
		Serial.print("  B-");Serial.println(Bat_value);
		}
		else {Button_Flag=0;}
	}

  	if (Button_Flag == 1)		//Check for Transmission END
  		{B_value = Read_Buttons;
		 if (B_value == 8 || B_value == 64)
		  {Button_Flag = 1;}
		 else
		  {Button_Flag =0;
		  B_value=0;}
		}
	if (Button_Flag == 0 && Read_Buttons == 0 && sleep_flag == 0)	  
		{
		
		sleep_flag = 1;
		Serial.println("sleep");
		//delay(150);
		Serial.println(PCIFR);
		PCIFR = 0;					// CLEAR ANY PCINT Flags
		Serial.println(PCIFR);
		//PCICR |= (1<< PCIE2);		// Enable Port D Pin Change Interrupts
		sei();
		}
  	

}

I REM'ed out the PCICR here.... BUT if i activate them and reprogram I get double hit ... BUT in the bottom part you can see that I am monitoring the PCIFR (PinChangeInterruptFlagRegister) ... and it appears in my serial debug that when I release the button it SETS the Flag ... I attempted to clear the flag before going to sleep (not actually for testing purposes) But is does not clear until it goes through the ISR..... 

Is there a way to force a clear on the PCIFR ?? below is the Serial monitor for a 'Z' button press..

Setup Complete                                                                  
sleep                                                                           
0                                                                               
0                                                                               
       Z     X-1021   Y-1021  B-0                                               
sleep                                                                           
4                                                                               
4                                                                               
sleep                                                                           
0                                                                               
0  

Anyone have any ideas?? 

Thanks

Michael

 

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

OK I think I got it to work the way I want...

 

Instead of 

PCICR &= ~(1<<PCIE2);		// Disables PortD Interrupts

If I clear the Mask if will not detect any changes in PortD. like this:

PCMSK2 &= 0;				// CLEAR Pin Change INT mask PortD

Then re setup the mask before SLEEP like this :

PCMSK2 |= B11101000;		// Set PCINT mask

 This works and I don't get multiple ISR hits from a single button press.  

 

It is odd that using PCICR &=~(1<<PCIE2) does not turn off the Interrupts... 

Sometimes Experimentation is the best way to figure things out...

 

Michael

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

When i do a cli(); I don't get the double hit (press and release) .

Doing a cli() inside the ISR does nothing. The global interrupt flag is already disabled inside an ISR. 

Regards,
Steve A.

The Board helps those that help themselves.

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

You need to write a '1' bit to clear the interrupt flag in PCIFR.

Realistically, only use the pcints to wake the device up. Use a timer tick to poll and debounce the switches.

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

This all was for NOT ... in trying to work this and program my Bread Board Mega328p... Atmel Studio 6.1 stopped recognizing ATMEGA328p as a processor ......  tried using 7.0 and it does not like the RF24 Library ... so ... im tired of fighting with using Arduino and C in Atmel Studio at the same ... 

 

Time to learn C for AVR ....  and Im too used to the ease of using Arduino commands for ADC and Serial and TWI ....  <SIGH> ...

 

 

Thank you everyone for your response

Michael

 

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

For for single-bit tests, x & mask == mask is not necessary, x & mask is sufficient and easier to read, especially if mask has a hard-to-read name.

The 10 ms of debounce might not be enough.

50 ms might not be enough if you are hoping the user will press two keys at once.

Debouncing a connected set of keys is different from debouncing each key separately.

Iluvatar is the better part of Valar.

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

Skeeve,

 

Thanks for the info ...

Yes I did move the "READ" macro to after the Analog read to provide more debounce timing, that way i was not adding more delay.

The 10 ms delay was to allow the voltage on the joystick stabilize a little before reading it.

 

Thanks again for your response

 

Michael