SOLVED: Atmega328p - Sleep mode and Clearing PCINT Interrupt Flag?

Hi,

I'd like to ask for some help correctly putting an Atmega328p to sleep. My goals are to have it use the LEAST amount of energy while being able to listen to 3 interrupt inputs to wake up. I've gotten the below coding to work... just looking to see if it needs improvement.

Setup:
-Atmega328P custom barebones PCB design fed by 12v through 5v reg,

2 questions:
-Can I reduce the amount of energy any further than this coding will do? (PCB drawing about 5.5ma)
-How do I write the code to clear the Interrupt Flags for the entire port when using PCINT?

The following code exert is for an automotive project that puts it to Sleep when the Ignition is turned off. Waking occurs from one of 3 Interrupts:

  • INT0 from switch 1 going LOW
  • INT1 from switch 2 going LOW
  • PCINT from Ignition voltage going HIGH

(PCINT is Pin 16 with Atmega328p)

How do I write the code to clear the PCINT flag? I followed Guru Nick for the INT0 and INT1 flags, but don't understand how to do the PCINTs.

THANKS!

#define PCINT_PIN 16              // For PCINT setting
static const int IGN = A2;        // Analog input Ignition voltage
int IGNThreshold = 350;           // Level below which enters Sleep Mode

~~~~~~~~~

void wakeISR() {                  // Wakes from any Interrupt
  sleep_disable();
  detachPinChangeInterrupt(digitalPinToPinChangeInterrupt(PCINT_PIN));
  detachInterrupt (0);
  detachInterrupt (1);
}

void loop() {
// Check Ignition power status
if (analogRead(IGN) <= IGNThreshold)  {           // Check if Ignition voltage is off
  IGNOFF = 1;                                                   // Set Ignition Status to OFF
  LEDSOFF();                                                    // Turns off ALL LEDs
    static byte prevADCSRA = ADCSRA;                            // Copy ADC registers
    ADCSRA = 0;                                                 // disable ADC
    set_sleep_mode (SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    MCUCR = bit (BODS) | bit (BODSE);                        // turn off brown-out enable in software. BODS must be set to one and BODSE must be set to zero within four clock cycles
    MCUCR = bit (BODS);                                         // The BODS bit is automatically cleared after three clock cycles
                                                                // Guarantees that the sleep_cpu call will be done as the processor executes the next instruction after interrupts are turned on.
    noInterrupts ();                                            // Do not interrupt before we go to sleep, or the ISR will detach interrupts and can't wake.
//INSERT CODE HERE  CLEARING PCINT_PIN INTERRUPT
    EIFR = (1 << INTF0);                                        // use before attachInterrupt(0,isr,xxxx) to clear interrupt 0 flag
    EIFR = (1 << INTF1);                                        // use before attachInterrupt(1,isr,xxxx) to clear interrupt 1 flag
    attachInterrupt (digitalPinToInterrupt(2), wakeISR, LOW);   // Calls ISR when INT0 goes low
    attachInterrupt (digitalPinToInterrupt(3), wakeISR, LOW);   // Calls ISR when INT1 goes low
    attachPCINT(digitalPinToPCINT(PCINT_PIN), wakeISR, HIGH);   // Calls ISR when Ignition power is on (line is tied to GND via 10k)
    interrupts ();                                              // Interrupts Activated 
    sleep_cpu ();                                               // CPU is ASLEEP and waiting for Interrupts
    //CPU IS NOW AWAKE
    ADCSRA = prevADCSRA;

may be this will help ?

Pin Change Interrupt Flag Register
Name: PCIFR
Offset: 0x3B
Reset: 0x00
Property:
When addressing as I/O register: address offset is 0x1B

Bit 2 - PCIF2
Pin Change Interrupt Flag 2
When a logic change on any PCINT[23:16] pin triggers an interrupt request, PCIF2 will be set. If the I-bit
in SREG and the PCIE2 bit in PCICR are set, the MCU will jump to the corresponding interrupt vector.
The flag is cleared when the interrupt routine is executed. Alternatively, the flag can be cleared by writing
'1' to it.

5.5 mA sounds high, the microcontroller should be well under 1mA. What else is on the board? Post schematic if possible.

Code looks fine -at least the part of it you posted (you should really post complete sketches, not snippets)

(Note that a manually implemented PCINT interrupt instead of that pcint library, could probably do all that with less overhead - imo all the "attach" style interrupt managemrnt functiond are abominations)

You haven't posted all your code so we can't see what you are doing... You might want to
set all unused pins to INPUT_PULLUP for instance.

DrAzzy:
5.5 mA sounds high, the microcontroller should be well under 1mA. What else is on the board? Post schematic if possible.

Code looks fine -at least the part of it you posted (you should really post complete sketches, not snippets)

(Note that a manually implemented PCINT interrupt instead of that pcint library, could probably do all that with less overhead - imo all the "attach" style interrupt managemrnt functiond are abominations)

I can't post the entire code or PCB. The board does have a few other discreet devices and should add up to about:

  • CD40109BPW .120 ma
  • MCP1703A-5002 .002 ma
  • ITS4060S .01 ma >>>> CORRECTION
  • Atmega328pAU .36 ma

Total PCB (Min): .492 ma (Plus a few resistors that add slightly to the total)

That's why I thought maybe things could be better.

Could you expand on the "PCINT Library" more? What do you mean?

Bwanna:
Can I reduce the amount of energy any further than this coding will do? (PCB drawing about 5.5ma)

Astonishing.

A bare bones Atmega328p, with the right regulator, has a deep sleep current of circa 1.5uA. It will then wake up if you press buttons etc.

Use a battery such as a LifeP04 to directly power the Atmega328p and the deep sleep current is around 0.2uA, again it will wake up on button presses etc.

srnet:
Astonishing.

A bare bones Atmega328p, with the right regulator, has a deep sleep current of circa 1.5uA. It will then wake up if you press buttons etc.

Use a battery such as a LifeP04 to directly power the Atmega328p and the deep sleep current is around 0.2uA, again it will wake up on button presses etc.

That's what I figured! And the reason for wanting feedback. I'm going to test the PCB amperage after the regulator to see what the three components are drawing.

The Amperage Draw Question is SOLVED: Not sure what happened, but I retested the PCB with the coding (below) and it's now showing .204 mA at the main 12v supply with the 328p in Power Down Sleep Mode.

I also cut the PCB trace and measured the amperage AFTER the 5v regulator. This showed .0185 mA draw for all three components below with the 328p in Sleep mode. Not too bad!

The additional .1855 mA draw can be attributed to another component only driven by the 12v supply

SECOND QUESTION: Still working on clearing the PCINT flag....

Have you read answer #1?

J-M-L:
Have you read answer #1?

Was just about to post. YES! Thank you! Looks like that did the trick. With this change I'm now getting .170mA in sleep mode. Not sure if it is the clearing of the PCINT flags or just coincidence, but I'm happy with the results.

:slight_smile: