1

I'm not that good at this. Calculates RPM through Peripheral Speed and Interrupt.

Everything works fine when I show this in the IDE with Serial.print without OLED. When I connect an SSD1306 OLED I2C, the interrupt stops working. No serial.print.

If I uncomment the display.display(), Same problem with display.setRotation(90): everything is fine and serial.print starts working, but of course not the OLED.

In the code I have a void addressing, visadisplay(). If I put this addressing in void loop () then everything works but then there is a risk of unsynchronization, wrong values. I have also removed visadisplay(); without change.

Help...

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Function for serial display.
// Connections for Nano.
// Hall sensor connection: (Signal out) --- (D2).
// Display connection: (SDA) --- (4A) / (SCL) --- (A5)

volatile unsigned long PulsTid = 0UL;
volatile unsigned long StartTid = 0;
volatile unsigned long SlutTid = 0;
volatile unsigned long CalcTid = 0;
volatile float omkrets = 7.5;
volatile float HastighetPerferi = 0;
volatile unsigned long Rpm = 0;
volatile float Vinkel = 0UL;

void isr() { // interrupt service routine
  Vinkel = 0;
  StartTid = millis();
  CalcTid = SlutTid;

  detachInterrupt(0);             //detaches the interrupt while calculating

  PulsTid = (StartTid - SlutTid);
  HastighetPerferi = (omkrets / PulsTid);
  SlutTid = StartTid;
  Rpm = ((HastighetPerferi / (omkrets / 1000) * 60) / 2); // Calculate speed

  visadisplay();
  attachInterrupt(0, isr, FALLING);  //attaching the interrupt again
}

void visadisplay(void) {
  Serial.print("Varvtal: ");
  Serial.println(Rpm);
 
  display.setRotation(90);
  display.clearDisplay();
  display.setTextSize(1);              // Text size.
  display.setTextColor(SSD1306_WHITE); // White text.
  display.setCursor(0, 0);             // Start position
  display.println("RPM: ");
  display.setCursor(40, 10);
  display.setTextSize(2);
  display.print(Rpm);
  display.display();
}

void setup() {
  Serial.begin(115200);
  attachInterrupt(0, isr, FALLING);  //attaching the interrupt

  //SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
}    

void loop(){
}
3
  • 1
    I would guess, that the display code needs interrupts to work during its execution, which is not the case inside an ISR. Serial will just put the data into the buffer, which then gets pushed out later via interrupts. Try to set a flag inside the ISR and then execute the display code in the main loop, if the flag was set
    – chrisl
    Feb 3, 2021 at 9:24
  • You don’t have to disable and re-enable the interrupts inside an ISR (in fact, you really shouldn’t). Also, an ISR should be very short (as few instructions as possible) and yours isn’t. Take @chrisl ‘s advice and set a flag inside the ISR, test for the flag in the main loop and leave handling of the display code to the main loop.
    – StarCat
    Feb 3, 2021 at 10:07
  • I2C communication (usually) requires interrupts. You can't use interrupts inside an interrupt. Ergo, you can't use I2C communication inside an interrupt. There's very little you can do in an interrupt.
    – Majenko
    Feb 3, 2021 at 10:20

2 Answers 2

2

Your problem is (most likely) that you are doing everything from an interrupt. That is not only bad practice, but on Arduinos it is generally impossible.

On small microcontrollers you typically only have one interrupt priority (occasionally two). That means that only one interrupt can run at a time, and if an interrupt is running then no other interrupt can interrupt it.

So anything that relies on interrupts to operate can never be used inside an interrupt service routine, or it will never function.

That includes things like delay() and, depending on the implementation, such things as Wire (I2C) and SPI.

Instead your ISR really should be as short as possible. It should at most do any short calculations and store results, and then indicate (through the use of a semaphore) to the main thread that it should do some work.

The simplest "fix" to your problem would be to move visadisplay() into loop() and in isr() simply set a bool flag when there is data to display. Your loop() then checks that flag and, if set, resets it and calls visadisplay().

Also, you don't need to detach and re-attach the interrupt all the time - if the ISR is running then interrupts can't happen anyway.

// ... snip ...
volatile bool doDisplay = false;

void isr()          //interrupt service routine
{
  Vinkel = 0;
  StartTid = millis();
  CalcTid = SlutTid;

  PulsTid = (StartTid - SlutTid);
  HastighetPerferi = (omkrets/PulsTid);
  SlutTid = StartTid;
  Rpm = ((HastighetPerferi/(omkrets/1000)* 60)/2);  //Calculate speed

  doDisplay = true; // Signal to the main thread there's data to display
}

// ... snip ...

void loop() {
    if (doDisplay) {       // If there is something to display
        doDisplay = false; // then clear the flag for next time
        visadisplay();     // and display the data.
    }
}
1
  • I tested with an LCD display and SPI connection instead of I2C. Then everything works as it should. A bit sad that you have to use circumventing solutions on such elementary needs to connect a display.
    – Ola A
    Feb 7, 2021 at 10:18
0

About rotating text on OLED, rotation codes are 0, 1, 2, 3 (see Adafruit web site).

So your code should look like:

  Serial.print("Varvtal: ");
  Serial.println(Rpm);
 
  display.setRotation(1);
  display.clearDisplay();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.