Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20705 Discussions

Missed Interrupt Problem with Attiny85

Altera_Forum
Honored Contributor II
1,742 Views

I'm trying to use an Attiny85 (Here is the date sheet of attiny85 (http://www.componentschip.com/details/atmel/attiny85.html)) to wake another controller from sleep (an ESP8266). 

 

The Attiny is connected to an IR receiver which has an active low output. Basically I have connected an output from the Attiny to the Reset pin on the ESP8266, so when an IR signal is received, it resets the ESP8266 but then ignores future IR until it gets a signal from the ESP that it is going to sleep again. 

The Attiny should also sleep but wake on pin interrupts on either pin 0 (IR in) or pin 2 (resetEnable from the ESP to signify that the ESP is going to sleep and will need to be woken). 

 

 

I've got code that works sometimes but seems to mostly (but not always) miss the Reset Enable pulse that the ESP sends to notify before it goes to sleep. I have verified with the oscilloscope that the ESP is sending a 3ms High pulse every time it goes to sleep, but the Attiny is missing most of those pulses (ie, the resetEnable flag remains low). 

The way I imagine it should work is that the interrupt routine determines which pin has triggered it, then if it's the Reset Enable pin (pin 2) going high, it sets a flag, so the next low on the IR pin will send an active low reset pulse to the ESP RST pin connected to pin 3 of the Attiny). 

The first IR pulse received after boot of the Attiny does always reset the ESP as it should, since the resetEnable flag is set true in the setup(). This makes me think that most of my code is working, except the interrupt routine to set the resetEnable and resetEsp flags. 

Here's my code, which was mostly pieced together from bits and pieces stolen from the web: 

# include <avr/sleep.h> # ifndef cbi# define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))# endif# ifndef sbi# define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))# endif int pinIR = 0; int pinLed = 1; int pinRSTen = 2; int pinRST = 3; volatile bool resetEsp = false; volatile bool resetEnabled = true; void setup(){ pinMode(pinIR,INPUT); pinMode(pinLed,OUTPUT); pinMode(pinRSTen,INPUT); pinMode(pinRST,OUTPUT); digitalWrite(pinRST,HIGH); // let the Esp boot flash(4,500); // flash the led to show attiny has started sbi(GIMSK,PCIE); // Turn on Pin Change interrupt sbi(PCMSK,PCINT0); // Which pins are affected by the interrupt sbi(PCMSK,PCINT2); sei(); } void loop(){ system_sleep(); if (resetEsp) { digitalWrite(pinRST, LOW); // reset the ESP delayMicroseconds(240); digitalWrite(pinRST, HIGH); // let the ESP Boot flash(10, 50); // flash the led fast to show we're waking the ESP resetEsp = false; // clear the flags resetEnabled = false; } else if (resetEnabled) { flash(10, 500); // mostly never get here } else { flash(2, 500); // these are the flashes I see most of the time } } // From http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/ void system_sleep() { cbi(ADCSRA,ADEN); // Switch Analog to Digital converter OFF set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode sleep_mode(); // System sleeps here //sbi(ADCSRA,ADEN); // Switch Analog to Digital converter ON } ISR(PCINT0_vect) { if (digitalRead(pinRSTen) == HIGH) { resetEnabled = true; // set the flag so the next IR pulse will reset the ESP } if (resetEnabled && (digitalRead(pinIR) == LOW)) { resetEsp = true; // reset the ESP } } void flash(int num, int wait) { for (int i=0;i<num;i++) { digitalWrite(pinLed, HIGH); delay(50); digitalWrite(pinLed, LOW); delay(wait); } } My code does trigger reliably and give the two flashes every time I send IR to it. Just the resetEnable flag doesn't seem to get set reliably when the ESP sends the 3ms high pulse on pin 2. 

After I get the interrupt code working reliably, I would also like to get the Attiny to ignore IR pulses less than 320us, as the IR detector seems to glitch low every now and then for less than 320us, and I don't want the ESP8266 to be woken in that case. 

edit: 

<meta> I never thought something that seems so simple could turn into something so complicated! Massive thanks to jms and JimmyB for your help, your comments were like gold to me, and I’ve learned a lot. It's very difficult to debug an Attiny running interrupt code that I didn’t really understand (the first interrupt code I’ve ever used), with only a single led with which to communicate, and on top of that, while it is flashing the single led, it's actually missing interrupts! </meta> 

Now I've said that, here's the bad news... It's still not working. :-( 

I've used the code that both jms and JimmyB have given, they were almost identical. 

Unfortunately the Attiny still doesn't sleep properly though. 

When the resetEnable pulse comes in on PB2, it doesn't set the resetEnable flag I think. Using the code below it just flashes the led once every time, just the same as when IR comes in on PB0. The only time the led is solidly lit (to show resetEnable is true), is when the Attiny is first booted. It will reset the ESP as it should the first time some IR comes in, but never again as resetEnable isn't getting set again I believe. 

If I comment out the sleep_cpu(); line, then everything works perfectly though (but obviously the Attiny isn't sleeping). I can't work out why it doesn't work with the sleep in there. 

Here's the exact code I'm using now: 

# include <avr/sleep.h> # ifndef cbi# define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))# endif# ifndef sbi# define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))# endif int pinIR = 0; int pinLed = 1; int pinRSTen = 2; int pinRST = 3; volatile bool resetEsp = false; volatile bool resetEnabled = true; void setup() { pinMode(pinIR, INPUT); pinMode(pinLed, OUTPUT); pinMode(pinRSTen, INPUT); pinMode(pinRST, INPUT); // set as input so ESP can auto-reset itself over USB serial //digitalWrite(pinRST, HIGH); // let the Esp boot flash(4, 500); sbi(GIMSK, PCIE); // Turn on Pin Change interrupt sbi(PCMSK, PCINT0); // Which pins are affected by the interrupt sbi(PCMSK, PCINT2); sei(); } void loop() { set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode cli(); // Disable interrupts to avoid race condition if ( !resetEsp && !resetEnabled ) { // Only go to sleep if we have nothing to do right now. // Safe(*) code from the example in avr-libc: sleep_enable(); sei(); sleep_cpu(); // if this line is commented out, it all works perfectly. sleep_disable(); } else { sei(); // Can go on processing IRQs. } if (resetEsp) { cli(); resetEsp = false; resetEnabled = false; sei(); pinMode(pinRST, OUTPUT); // needed to do this so auto-reset works when programming the ESP // over serial digitalWrite(pinRST, LOW); // reset the ESP delayMicroseconds(240); digitalWrite(pinRST, HIGH); // let the ESP Boot pinMode(pinRST, INPUT); flash(10, 50); // show that we're resetting the ESP } else if (resetEnabled) { digitalWrite(pinLed, HIGH); // show that resets are enabled //flash(4, 100); } else { flash(1, 50); } } ISR(PCINT0_vect) { if (resetEnabled && !(PINB & (1 << PB0))) { resetEsp = true; } if ((PINB & (1 << PB2))) { resetEnabled = true; } } void flash(int num, int wait) { for (int i = 0; i < num; i++) { digitalWrite(pinLed, HIGH); delay(50); digitalWrite(pinLed, LOW); delay(wait); } } Also, jms, would you mind showing me exactly how to declare and use a single state byte? I tried googling for it but couldn't find the right keywords to come up with something useful. I don't think it's an enum, maybe a struct? Like you said, volatile uint8_t espIrRstState with three possible values IR_RST_DISABLED, IR_RST_ENABLED, IR_RST_TRIGGERED. 

This thing is doing my head in!! I would love to get it working! please help me so I can move on with my life!
0 Kudos
0 Replies
Reply