Troubleshooting Interrupt Issues with Attiny85, OLED Display, and Geiger Counter: Exploring Solutions for Using Alternative Pins

Hi!
I have a huge Interrupt problem with Attiny85, an OLED display SD1306 (4 pins: GND, VCC, SDA, SCL), and the COJOE RadiationD-v1.1 Geiger counter. With the code for Arduino NANO/UNO, everything works using the following code:

//ARDUINO NANO
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define integratingTime 1000  //Logging period in milliseconds
unsigned long cps = 0;     //variable for GM Tube events
unsigned long counts = 0;        //variable for CPM
unsigned long previousMillis;  //variable for time measurement

#define OLED_RESET 4
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2


#define LOGO16_GLCD_HEIGHT 16 // do not change this. Error in video
#define LOGO16_GLCD_WIDTH  16 // do not change this. Error in video
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

#if (SSD1306_LCDHEIGHT != 64)
#endif

void tube_impulse() //subprocedure for capturing events from GM board
{       
  counts++;
}

void setup()   {                
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  attachInterrupt(digitalPinToInterrupt(2), tube_impulse, FALLING);
  
  display.clearDisplay();
  outDisplay("CPS: ",cps, 0, 32, 2);
  display.display();
}

void loop(){
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > integratingTime){
    previousMillis = currentMillis;
    cps=counts;
    counts = 0;
   }
  display.clearDisplay();
  outDisplay("CPS: ",cps, 0, 20, 2);
  display.display();   
}

void outDisplay(String text,long cps, int x, int y,float size) {

  display.setTextColor(WHITE);
  display.setCursor(x,y);
  display.setTextSize(size);
  display.print(text+cps);
}

The interrupt here works with the "attachInterrupt(...)" function on pin D2 (INT0) (while with pin D3, INT1 doesn't work). I need to simplify everything for the Attiny85, avoiding the unnecessary use of an Arduino NANO. The Attiny85 has predefined pins for SCL (pin 7) and SDA (pin 5) for the display, but the same INT0 interrupt of Arduino NANO/UNO on pin D2 is pin 7 (already used by the OLED display for SCL).

The following code is one of the many attempts to achieve the same interrupt but with a different pin than pin 7 (INT0):

//Attiny85
#include "Wire.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"

#define I2C_ADDRESS 0x3C
#define RST_PIN -1

SSD1306AsciiWire oled;

#define integratingTime 1000  //Logging period in milliseconds
unsigned long cps = 0;     //variable for GM Tube events 
unsigned long counts = 0; 
unsigned long totcounts = 0;
unsigned long previousMillis;  //variable for time measurement
const int INTERRUPT_PIN = 1; 

ISR(PCINT0_vect) {
  counts++;
  totcounts++;
}

void setup(){  
  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  cli();
  PCMSK |= (1 << digitalPinToPCMSKbit(INTERRUPT_PIN)); // Pin Change Enable
  GIMSK |= (1 << digitalPinToPCICRbit(INTERRUPT_PIN)); // PCIE Pin Change Interrupt Enable
  // equivalent to: GIMSK |= (1 << PCIE);
  sei();

  Wire.begin();
  Wire.setClock(33000L);

  #if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
  oled.setFont(Adafruit5x7);
  oled.set2X();
}

void loop(){
  oled.set2X();
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis > integratingTime){
    previousMillis = currentMillis;
    cps=counts;
    counts = 0;
  }

  oled.home();
  oled.println();
  oled.print("CPS: "); oled.print(cps); oled.print("            ");
  oled.println();
  oled.set1X();
  oled.println();
  oled.println();
  oled.println();
  oled.print("ToT: "); oled.print(totcounts);  
}

Please ignore the part about the display because it works fine. If there are any possible solutions, they are:

  1. Take the interrupt, as in the previous attempt, from a different pin other than pin 7 and pin 5.
  2. Take the interrupt from pin 7 (INT0) and use pin 5 as SDA for the display, and another pin for SCL (in this case, I would need to use different libraries, but I'm not sure how to do it).

The code should be able to receive the signal from the Geiger counter and increment a variable. I have used different libraries for Arduino (which don't work well for Attiny85).

Can anyone provide me with some solutions? It's URGENT XD

This should be in a critical section, i.e. with interrupts disabled - it isn't possible to access a 32 bit variable atomically (irony!) on the AVR architecture.

(This doesn't fix your problem, but you did say it was URGENT, whatever that means.)

1 Like

You are updating two fields in the ISR, so you should declare them volatile

volatile unsigned long counts = 0; 
volatile unsigned long totcounts = 0;

And I recoommend you turn off interrupts while updating value's that can be updated in the ISR

cli();  
cps=counts;
counts = 0;
sei();
1 Like

First of all, I would like to thank you for your participation and the answers you provided.
The problem has been resolved (partially) and improved with the addition of "MCUCR |= (1<< ISC01);" for the FALLING mode.
The only remaining issue is that both counters output twice the expected value: instead of getting 1, I get 2; instead of getting 3, I get 6, and so on. I suspect that the interrupt is triggered twice for each contact. How could I resolve this issue?

Here is the updated code that I'm providing to you:

#include "Wire.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"

#define I2C_ADDRESS 0x3C
#define RST_PIN -1

SSD1306AsciiWire oled;

#define integratingTime 1000  //Logging period in milliseconds
unsigned long cps = 0;     //variable for GM Tube events
volatile unsigned long counts = 0; 
volatile long totcounts = 0;
unsigned long previousMillis;  //variable for time measurement
const int INTERRUPT_PIN = 4; 

ISR(PCINT0_vect) {
    cli();
    counts++;
    totcounts++;
    sei();
}

void setup(){ 
  pinMode(INTERRUPT_PIN, INPUT);
  PCMSK |= (1 << digitalPinToPCMSKbit(INTERRUPT_PIN));
  MCUCR |= (1<< ISC01); 
  GIMSK |= (1 << digitalPinToPCICRbit(INTERRUPT_PIN));

  Wire.begin();
  Wire.setClock(33000L);

  #if RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
  oled.setFont(Adafruit5x7);
  oled.set2X();
}

void loop(){
  oled.set2X();
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis > integratingTime){
    previousMillis = currentMillis;
    cps=counts;
    counts = 0;
  }

  oled.home();
  oled.println();
  oled.print("CPS: "); oled.print(cps/2); oled.print("            ");
  oled.println();
  oled.set1X();
  oled.println();
  oled.println();
  oled.println();
  oled.print("ToT: "); oled.print(totcounts);  
}

You're in an ISR - aren't interrupts already disabled?

In loop(), however...

1 Like

I just tried as you instructed, but now I'm back to the previous issue where it doesn't trigger the interrupt anymore (the counters are no longer functioning).

Post the revised code. Simply eliminating cli() and sei() from the ISR would not have caused any problem.

improved with the addition of "MCUCR |= (1<< ISC01);" for the FALLING mode.

That mode is valid only with INT0. Pin change interrupts are always "change", so you get two counts for every pulse.

Consider changing the library code so that you can use pin 7 as INT0.

1 Like

Regarding cli(); and sei(); , yes, they don't cause any issues. I misunderstood the previous message. As for using PIN 7 (INT0), as I mentioned earlier, I can't use it because it's already assigned to the SCL of the SSD1306. If there is a ""quick and clean"" solution for this approach, I would gladly take it. Alternatively, dividing the values of the "cps" and "totcounts" variables by two would give me the correct result, and that would "solve the problem" for me.

Read the status of the pin in your interrupt code. If it is 1, add to your counter. Otherwise skip the addition. That will tell you if the change is 0 to 1 or is 1 to 0. You only want 0 to 1.

1 Like

Using software I2C, that can be changed. Assign the SCL function in the software I2C library to some other pin.

1 Like

== Add the result of the digitalRead of the pin.

(Tin-hat on, expecting flak for suggesting that HIGH or LOW can be added to an integer variable :slight_smile: )

1 Like

I tried using the library SSD1306AsciiAvrI2c.h, but the Arduino IDE tells me that it is not supported for the chip. So, I tried to change the approach and found that it can be done with "wire.begin(sda, scl)".
I tried it, but it gives an error because it can only accept 1 argument instead of 2.

Yes, alternatively, I can also do that, but I would like to explore the method of changing the pins for the SSD1306 display so that I can use INT0 for the interrupt as usual.

Having some solutions at hand, I finally decided to opt for the CHANGE interrupt along with a condition:

ISR(PCINT0_vect) {
  if (digitalRead(4) == HIGH) {
    counts++;
    totcounts++;
  }
}

I thank everyone for the help, and I'm closing the issue here. Of course, if anyone has any additional advice to give me, I will be glad to listen :blush: .

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.