wake up arduino through serial comm & interrupts

Hi,

I am trying to get my arduino wake up the moment it receives serial communication. I am not concerned about losing serial messages during the "wake up" process though...

I think I got the concept right, but since I am new to electronics, I am pretty sure this is a dumb circuit issue...

My objective is to make sure that INT0 receives the RX signal (LOW) only when the IS_AWAKE signal (see fig) is HIGH; effectively we would like to get interrupted by serial
communication ONLY when arduino is sleeping. In this way, we can use INT0 for another purpose when arduino is awake.

Now, if I directly connect RX & the INT0 pin, my interrupt routine works flawlessely. However, when I implement the shown schematic, I see that the LED blinks whenever I
receive serial IO, but the interrupt doesn't get triggered. I tried all sorts of techniques... pull-ups, pull-downs etc but I couldn't figure out whats going on here.

Why does the LED blink successfully when I don't see the interrupt? Am I missing something here? How should I change the circuit so that I can wake up arduino through serial communication and interrupts that trigger only when arduino is asleep?

and my code...

#include <Stepper.h>
#include <avr/sleep.h>
#include <avr/power.h>

#define INT0_PIN 0
#define INT0 2
#define awakePin 10
#define enable_ELE 11
#define enable_AZ 12
#define rs485_cnt 13
#define LISTEN LOW
#define COMMAND HIGH

typedef struct numStruct {
	int a;
	int b;
} num;

void initPins();
void wakeUpNow();
void sleepNow();

int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
volatile int status=3;

void setup() {                
  initPins();
 
  Serial.begin(9600);
  digitalWrite(awakePin, HIGH); 
  digitalWrite(rs485_cnt, LOW); 
}

void loop() {
  int sb=0;
  int flag=0;
  char pp=0;
  num angles;

  if (status<=0) {
	  status=0;
	  sleepNow(); 
	  
  }

  if (status>0) {
	  status--;
	  delay(300);
	  digitalWrite(awakePin, LOW);   
	  delay(300);
	  digitalWrite(awakePin, HIGH);   // wakeUpNow when pin 2 gets LOW+
  }

}

void initPins()
{
//  pinMode(0, INPUT);  
  pinMode(1, OUTPUT);    
  //pinMode(2, OUTPUT);    
  pinMode(4, OUTPUT);    
  pinMode(5, OUTPUT);    
  pinMode(6, OUTPUT);    
  pinMode(7, OUTPUT);    
  pinMode(8, OUTPUT);    
  pinMode(9, OUTPUT);    
  pinMode(10, OUTPUT);    
  pinMode(11, OUTPUT);    
  pinMode(12, OUTPUT);    
  pinMode(13, OUTPUT);    

  digitalWrite(1, LOW); 
  //digitalWrite(2, LOW); 
  digitalWrite(3, LOW); 
  digitalWrite(4, LOW); 
  digitalWrite(5, LOW); 
  digitalWrite(6, LOW); 
  digitalWrite(7, LOW); 
  digitalWrite(8, LOW); 
  digitalWrite(9, LOW); 
  digitalWrite(10, LOW); 
  digitalWrite(rs485_cnt, LOW); 
  //pinMode(INT0, INPUT);
}

void wakeUpNow()        // here the interrupt is handled after wakeup
{
    detachInterrupt(0);      // My board  seems to be hanging if this is not there.... did I screw up something?
                             // wakeUpNow code will not be executed
    status=5;
}


void sleepNow()         // here we put the arduino to sleep
{
    digitalWrite(awakePin, LOW); 
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();
    power_adc_disable();
    power_spi_disable();
    power_timer0_disable();
    power_timer1_disable();
    power_timer2_disable();
    power_twi_disable();

    attachInterrupt(INT0_PIN, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
											   // wakeUpNow when pin 2 gets LOW

    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
							 // 
    power_all_enable();

    digitalWrite(awakePin, HIGH); 							 
    delay(100);
    Serial.flush();
}

Any comments/suggestions are highly appreciated.

Just a thought ... Have you tried enabling the pin change interrupt on RX? That would very likely solve all your problems without the need for any external hardware.

Fixed the issue after a good night sleep...!

Just a thought ... Have you tried enabling the pin change interrupt on RX? That would very likely solve all your problems without the need for any external hardware.

Yes, I tried that approach and it worked fine. I want the rx signal to reach the arduino's INT0 ONLY when the arduino is sleeping. When it is awake, I would like to use INT0 and INT1 for other purposes. I don't want the INT0 to be rx exclusive... and thats the whole purpose of the external hardware.

The logic here is simple:
When the arduino is awake, the IS_AWAKE signal is HIGH and so the output of the NOR gate is always 0. When the arduino is asleep, the IS_AWAKE signal is LOW and so whenever RX goes LOW and we get a HIGH at the nor output. It is this high triggers the INT0.

My current setup (board + other components) is consuming about 50mA under normal operation and about 30mA when the atmega328p is asleep. My next task is to reduce this power consumption as much as possible.

The following schematic works:

#include <Stepper.h>
#include <avr/sleep.h>
#include <avr/power.h>

#define INT0_PIN 0
#define INT0 2
#define STEPS 48
#define awakePin 10
#define rs485_cnt 13
#define LISTEN LOW
#define COMMAND HIGH

typedef struct numStruct {
	int a;
	int b;
} num;


void initPins();
void wakeUpNow();
void sleepNow();

int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
volatile int status=3;

void setup() {                
  initPins();
  
  Serial.begin(9600);
  digitalWrite(awakePin, HIGH); 
  digitalWrite(rs485_cnt, LOW); 
}

void loop() {
  int sb=0;
  int flag=0;
  char pp=0;
  num angles;

  if (Serial.available()) {
      char *ptr=(char*)&angles;
      int counter=0;
      while(Serial.available()){
	delay(2);
	pp=Serial.read();
	*(ptr+counter)=pp;
	flag=1;
	counter++;
      }

   }


  if (status<=0) {
    status=0;
    sleepNow(); 
  }

  if (status>0) {
    status--;
    delay(300);
    digitalWrite(awakePin, LOW);   
    delay(300);
    digitalWrite(awakePin, HIGH);   // wakeUpNow when pin 2 gets LOW+
  } else {
    status=0;
  }

}

void initPins()
{
//  pinMode(0, INPUT);  
  pinMode(1, OUTPUT);    
  //pinMode(2, OUTPUT);    
  pinMode(4, OUTPUT);    
  pinMode(5, OUTPUT);    
  pinMode(6, OUTPUT);    
  pinMode(7, OUTPUT);    
  pinMode(8, OUTPUT);    
  pinMode(9, OUTPUT);    
  pinMode(10, OUTPUT);    
  pinMode(11, OUTPUT);    
  pinMode(12, OUTPUT);    
  pinMode(13, OUTPUT);    

  digitalWrite(1, LOW); 
  //digitalWrite(2, LOW); 
  digitalWrite(3, LOW); 
  digitalWrite(4, LOW); 
  digitalWrite(5, LOW); 
  digitalWrite(6, LOW); 
  digitalWrite(7, LOW); 
  digitalWrite(8, LOW); 
  digitalWrite(9, LOW); 
  digitalWrite(10, LOW); 
  digitalWrite(enable_ELE, LOW); 
  digitalWrite(enable_AZ, LOW); 
  digitalWrite(rs485_cnt, LOW); 
  pinMode(INT0, INPUT);
}

void wakeUpNow()        // here the interrupt is handled after wakeup
{
 
	status=5;
}


void sleepNow()         // here we put the arduino to sleep
{
    digitalWrite(awakePin, LOW); 
    attachInterrupt(INT0_PIN, wakeUpNow, HIGH); // use interrupt 0 (pin 2) and run function

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
    power_adc_disable();
    power_spi_disable();
    power_timer0_disable();
    power_timer1_disable();
    power_timer2_disable();
    power_twi_disable();


    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
							 // 
    power_all_enable();

    detachInterrupt(0);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed

    digitalWrite(awakePin, HIGH); 							 
		     // during normal running time.
    delay(100);
    Serial.flush();
}

Have you tried enabling the pin change interrupt on RX?

Yes, I tried that approach and it worked fine. I want the rx signal to reach the arduino's INT0 ONLY when the arduino is sleeping. When it is awake, I would like to use INT0 and INT1 for other purposes. I don't want the INT0 to be rx exclusive... and thats the whole purpose of the external hardware.

I believe you misunderstood my question. You are using something called "external interrupts" (the symbols "INT0" and "INT1" are the giveaway). I'm asking about "pin change interrupts". They are two different beasts. But, as long as you're happy with your solution so am I.

attachInterrupt(INT0_PIN, wakeUpNow, HIGH); // use interrupt 0 (pin 2) and run function

HIGH is not a valid parameter for attachInterrupt.

In this way, we can use INT0 for another purpose when arduino is awake.

The 14001 is not open drain though so how does the "other purpose" work when connected to a hard voltage?

Another option is a 74xx126 tristate buffer or a 4066 analogue switch.


Rob

I believe you misunderstood my question. You are using something called "external interrupts" (the symbols "INT0" and "INT1" are the giveaway). I'm asking about "pin change interrupts". They are two different beasts. But, as long as you're happy with your solution so am I.

I am trying to ensure that the footprint is under 4K so that I can deploy an Atmega48, instead of 168 or 328; trying to reduce the production costs. The current footprint is about 3.45K (actual optimized code doesn't look like what I have posted above) and unfortunately the current pin change interrupt library seems to be adding 360bytes to the footprint. Of course, we can strip it and optimize it but I prefer to have as much space as possible in the atmega chip for future enhancements. A typical NOR smt gate costs a few cents and so I am trading in an extra NOR gate for some free memory space.

May be I should optimize the source code further and see how things work out...

The 14001 is not open drain though so how does the "other purpose" work when connected to a hard voltage?

I wouldn't need an Open Drain gate for my application. The other signal is the output from a photo interrupter which keeps track of how many steps my stepper motor has turned; this is a closed loop "energy efficient" dual stepper motor controller. The motors can occasionally miss steps and so I would like to precisely track them. The board waits for a few seconds and goes to sleep. It wakes up when we send serial IO and automatically detaches the sleep/awake interrupt handler and attaches the stepper motor interrupt handlers to both INT0 and INT1. In this way, we can use INT0 to wake the arduino and also use it for tracking the steps. The signal from the stepper motor doesn't need to cross the NOR gate. I am able to reduce the power consumption to about 30mA.... not sure where this 30mA is going.... may be to the L7805 or to the L293D or to the MAX485... need to roll my sleeve and see whats going on :0

The other signal is the output from a photo interrupter

Which is probably open collector, but I still don't see how it can pull against the 14001 output :~

BTW you can get logic gates in a single-gate pack (SOT23-5) these days, great for this sort of thing where you only need 1 or 2.

the current pin change interrupt library seems to be adding 360bytes to the footprint.

All you need is an ISR I would think, that will use almost no flash. Get rid of the attachInterrupt() stuff and handle the logic in the ISR.

ISR xx() {
if (sleeping)
do this
else
do that
}


Rob

All you need is an ISR I would think

For waking the processor, an empty ISR is typically all that's needed. My suspicion is that the bit of custom pin change codeX necessary to wake the processor is going to be considerably smaller than the attachInterrupt code.

X I think the code consists of manipulating two registers once when interrupts are enabled and a second time when interrupts are disabled which amounts to eight bytes of code.

Which is probably open collector, but I still don't see how it can pull against the 14001 output

Graynomad, I see what you have been pointing out. I am not even able to get a photointerrupter work in the first
place and I definitely think I see the issue. Thanks for pointing it out; this gave me a good headstart.
I have posted the issue that I came across at http://arduino.cc/forum/index.php/topic,66486.0.html .
Any comments from your side will be highly appreciated.

Coding Badly, yes, I have gone through the atmega manual and yes, the code for pin change interrupt without
having to use the associated arduino library is pretty light weight. I will use the pinchange logic instead
of the current NOR gate logic.

As for the code optimization, the more I am looking into the code the more I am uncovering several interesting aspects
of the gcc-avr compiler. Here are some interesting things that I see:

  1. When I move the attachInterrupt function into the scope of the sketch I am writing, I see a 200byte reduction in the
    footprint... I need to take a closer look at the build process here
  2. When I comment out division operations, I see an optimization of more than 100 bytes; I can probably pass the division
    number through the rs485 and try to eliminate the division operation.... hmm... may be I can optimize the stepper motor
    logic with this approach.
  3. Yes, we can further optimize the code with
    ISR xx() {
    if (sleeping)
    do this
    else
    do that
    }

I suppose I have one more issue with the photointerrupter (link above) before I can order a PCB and try things out.
Thanks so much for your suggestions. I really appreciated your time.

Thank you for the follow-up.