Mysterious behavior of ATTiny85 and hardware interrupts

Hi, all. This is my first post here, so please bear with me.

I have some months of experience with Arduino, so not much. But I have a background in Electronics and do a lot of coding for work, so I've learnt relatively quickly.

I am working on a low-power project. I'm planning to use an ATTiny85 which controls a DS3231 real-time clock (RTC) using I2C and is woken by the RTC's alarms. I2C needs SDA (PB0) and SCL (PB2). The only hardware interrupt is INT0 (PB2).

ATTiny85 pinout

This means that I have two options (I think):

  1. Use INT0 (PB2) for the alarm and bit-banging for I2C (using, e.g., BitBang_I2C)
  2. Use PB0 and PB2 for I2C and a pin-change interrupt (PCINT) for the alarm

I haven't been able to make option 1 work: I'm not even able to set and read the time correctly.

Option 2 should be easier. I have been having some issues with the PCINTs, so first I want to make my testing code with INT0. I'll probably ask about PCINTs soon, in different post.

I've written a sketch that adds 1 to a counter and sets a flag when the signal at PB2 falls to LOW. In the loop, an LED blinks counter times and unsets the flag:

#include <Arduino.h>

const byte LED = 3;
const byte LED_FLAG = 0;
const byte SWITCH = 2;

volatile byte count;
boolean flag = false;
void blink(int);

void isr() {
  count++;
  flag = true;
}

void setup () {
  pinMode(LED, OUTPUT);
  pinMode(LED_FLAG, OUTPUT);
  pinMode(SWITCH, INPUT);
  attachInterrupt(digitalPinToInterrupt(SWITCH), isr, FALLING);
  blink(5);
  count = 2;
}

void loop () {
  // digitalWrite(LED_FLAG, flag);
  if (flag) {
    blink(count);
    flag = false;
  }
}

void blink(int n) {
  for (int i = 0; i < n; i++) {
    digitalWrite(LED, HIGH);
    delay (100);
    digitalWrite(LED, LOW);
    delay (100);
  }
}

I'm using PlatformIO to upload the code through an Arduino Nano as ISP. The signal from SWITCH is actually from pin 13 of an Arduino Mega with a blink sketch modified to change every five seconds, instead of one. All grounds are connected, and power comes from the 5V pin of the Nano, which is connected to the computer through USB. The ATTiny85 clock is set to 8 MHz (fuses: E:FF, H:D7, L:E2).

PB3 is connected to a white LED (the one that blinks), which is connected to GND through a 330 ohm resistor.

PB0 is connected to a red LED (the one that shows the state of flag), which is connected to GND through a 330 ohm resistor.

The mystery is that the sketch works only if I uncomment the commented line, i.e., if I write to the flag (red) LED. The white LED blinks 5 times at the beginning, and then blinks counter times every time the built-in LED of the Mega turns off.

If I comment that line, the white LED blinks 5 times at setup and never again.

Here's what the breadboard looks like. The yellow cable is connected to pin 13 of the Mega. Not sure why the white LED looks funny.

Do you know what could be happening? Am I doing anything obviously wrong? I feel like I need to understand what's going on here before I move forward to the PCINTs.

Three. Pin change interrupt.

Hi, @Coding_Badly. Thanks for replying. I'm not sure what you mean. Pin change interrupt (PCINT) is my option 2.

So it is. Sorry about that.

Although this is far from the worst I've seen, Fritzing diagrams are more or less useless. Please avoid posting them in the future. Instead, post a schematic, with pins appropriately numbered and/or labeled, and showing power supply connections.

The yellow cable is connected to pin 13 of the Mega.

What do you expect that to accomplish, in the absence of a common ground?

Thanks for the compliment and the tip. I will post the schematic tomorrow.

Note that I mentioned that there is a common ground.

This should be volatile:

This should probably be INPUT_PULLUP :

You may have trouble using external interrupts to trigger the ATtiny85 when you progress on to using sleep modes. I'll try to find a reference.

Edit:

1 Like

I believe @JChristensen is the reference. Someone found that the documentation is not accurate and posted their findings on this forum. I recall it being @JChristensen.

I found this.

Ah yes. There is a rich history to this and it catches a few people out from time to time. I'm quite alert to this problem, when I see other people potentially facing it, having spent many hours troubleshooting exactly that. I'd forgotten that @JChristensen discovered it before @nickgammon .
At least pin change interrupts appear to work as expected when waking a sleeping device.

Thanks, @6v6gt. That fixed it. And more importantly, now I understand why it was failing. The commented line digitalWrite(LED_FLAG, flag); was the one that made flag not necessarily volatile. I wish compilers told us about these.

Yes, thanks. It will be for the RTC.

In that case, I guess I'll jump directly to adapting this sketch to PCINTs, and I'll try the sleep modes right after.

Thank you all for your replies!

The unfortunate thing is that the compiler cannot see this situation, hence the necessity to use the volatile keyword to tell it. The compiler usually attempts to use variables directly from registers if possible to avoid reloading these each time. However, it cannot see potential asynchronous activity, say in an ISR, which could render as stale the register copy of such variables.

1 Like