Hardware Interrupts and timing

Well iv’e bitten off more than i can chew as i often do in the programming world… Here’s my project

ECU for snowblower

Goals

  1. Choose baseline RPM (Idle or load)
  2. Read each rotation from the motors flywheel
  3. Adjust throttle plate (based on simple pid loop?) to achieve baseline (with servo not yet added to program or circuit)
  4. Maybe print rpm and load to lcd.

I handle recording my flywheel ping as a hardware interrupt. Within this interrupt routine i look at if a ping ever happened… and then to if a second ping has happened to determine period. I also look at if period one and period two happened… which gives me a sort of accel/deccel time based on time.

My question is two-part. (see code segment below please)

  1. Is my interrupt routine too much code? This routine (at max RPM) will need to run 40-50 times a second and still leave time for the main program to run. Also since millis() doesn’t tick while i’m in the routine… too much time processing this code will affect the rpm timing as a whole so that wouldn’t be good either.

  2. I need MY interrupt to have absolute priority. I’m kinda new to programming so ill make my problem statement this way and it may or may not be correct:

Would an LCD screen added onto this device mess with my interrupts? Something basic to show RPM and LOAD. The way i understand, communication is done VIA hardware interrupts(wrong?)… so would the communication of an LCD interfere with my interrupt pin/routine? My initial assumption is yes.

const byte ledPin = 13;
const byte interruptPin = 10;
volatile byte state = LOW;
volatile long periodOne = 0;
volatile long periodTwo = 0;
volatile long pingOld = 0;
volatile long pingNew = 0;
volatile long deviation = 0;

void setup() {
  //start serial connection
  Serial.begin(9600);
    while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
   }
  pinMode(ledPin, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), count, FALLING);
}

void loop() {
 
  //read the hall sensor value into a variable
  if(pingOld <=0) {
   
    if(pingNew <=0){
      Serial.println("Sorry awaiting first revolution.");
    }
    else{
      Serial.print("First ping to compare (ms): ");
      Serial.println(pingNew);
      Serial.println("Allow for one more revolution");
    }
           
  }
  else{
    Serial.print("New ping to compare (ms): ");
    Serial.println(pingNew);
    Serial.print("Old ping to compare (ms): ");
    Serial.println(pingOld);
    Serial.print("Cycle-Time for set of pings: ");
    Serial.println(periodTwo);
    Serial.print("Period converted to RPM: ");
    Serial.println(60000/periodTwo);
      if(periodOne > 0){
        Serial.print("Accel/Decceleration period to period: ");
        Serial.println(deviation);
      }
  }
 
 
delay(10);
 
}

void count() {
  if (pingNew > 0){
    pingOld = pingNew;
    pingNew = millis(); //everytime after the first run
      if (periodTwo > 0){
        periodOne = periodTwo;
        periodTwo = pingNew - pingOld;
        deviation = periodTwo - periodOne;
      }
      else{
        periodTwo = pingNew - pingOld;
      }
  }
  else {
    pingNew = millis();
   }
}
const byte interruptPin = 10;

Which Arduino board are you using ?

UKHeliBob:

const byte interruptPin = 10;

Which Arduino board are you using ?

Oh Geeze i'm sorry!

This is an Arduino Feather M0 Adalogger with an ATSAMD21G18 ARM Cortex M0 processor, clocked at 48 MHz... i'm reading same as Arduino Zero.

The Arduino is not really the right choice for an ECU. There’s better hardware out there. But, even the 16MHz ones are good enough, particularly if you only have a small number of cylinders and you can still drive yourself to work if it explodes your motor.

48MHz Feather? Sounds like it’s much better than the minimum requirement.

  1. Too much code in the interrupt? Nope. You have a very small amount of code and it’s pure integer math with only a single division operation hidden inside millis(). [Integer division is surprisingly slow on the 8-bit Arduinos. It is not very slow on yours.] If you had a lot of floating-point operations then I would question it. Even doing a floating-point trigonometric calculation like a sin() or cos() would be just fine here, so long as you don’t try to do a lot of them.

Don’t do this…

delay(10);

Learn to use blink-without-delay for Serial printing. For example…

void setup() {
  Serial.begin(9600);
  while(!Serial && millis() < 5000) {
    //do nothing for up to 5 seconds while we wait for Native USB to connect
  }
  Serial.println("Stuff is started!");
}

void loop() {
  doStuff();
  doSerialOutput();
}

void doSerialOutput() {
  const unsigned long printInterval = 500; //milliseconds. Only print this often
  static unsigned long lastPrinted;  //static so it is stored after we exit the function

  if(millis() - lastPrinted > printInterval) {
    Serial.println("Here's the stuff...");
    //more output here
    lastPrinted = millis();
  }
}

void doStuff() {
  //do stuff here
}
  1. Actually you don’t. Interrupts are serviced very quickly and there’s a queue. If it is currently servicing an interrupt and yours occurs, then it will finish that one within a microsecond or two and then start on yours. For your purposes, several microseconds delay occurring very infrequently is going to be totally unmeasurable and undetectable. You could probably tolerate more than a thousand microseconds delay before you could detect a problem.

Where you run into problems is certain tasks that disable interrupts for tens of thousands of microseconds. SoftwareSerial is the main example of this. Don’t use SoftwareSerial on your ECU.

  1. LCD screen? Yes. Good idea. Most of them don’t interfere with interrupts. There are some odd bit-banged shift-register type screens that may be a problem but they are rare. Pick one and share it here. The experts will give you an opinion on the library code for the LCD.

Wow thank you MorganS! That definitely helped, especially with my coding confidence!

  1. Awesome info- thanks again! Based on your response, I should even be fine if I decide to use micros for better accuracy?

  2. Thank you for the delay(10) information. Your assumption is correct, i was essentially slowing the rate of serial data being spit out but that method is much better.

3)Here’s the LCD screen i’m looking at. Just an amazon cheapy but it should do the trick!

Should it not be a good option, having to opt to an adafruit library i would try something like this:

but obviously preferring the amazon choice for my wallets sake(should it be usable for this application).

Thanks again for the input yall!

Maybe print rpm and load to lcd.
Something basic to show RPM and LOAD.

The OLED graphics display you have chosen is not appropriate for your time sensitive program. It is slow, and can be difficult to program in a non blocking manner.

Use a standard 16x2 or 20x4 lcd character display. You can use either an I2C or a parallel display depending on your pin availability. The parallel display will be faster.
The I2C display does use interrupts, but I don't think it will affect your program.

Use Bill Perry's hd44780.h. It is available through the library manager, and is the fastest by far for driving these character displays.

I sort of wanted to stay away from lcd displays as this is for a snowblower. Will be kept outside in Wisconsin sometimes near 0F. LCD would be a sluggish laggy looking display in that.

Are there any oled or other options you think may be more suitable?

I’ve seen 16x2 and 20x4 LCD replacements a couple of years ago.
Expensive though.

Are there any oled or other options you think may be more suitable?

Yes, there are definitely many character oleds with hd44780 compatible controllers that have operating specs from -40C to +80C.

Here's a DigiKey selection page to get you going. The first filter to apply will be operating temperature.
https://www.digikey.com/products/en/optoelectronics/display-modules-lcd-oled-character-and-numeric/99

hanslanda:
Are there any oled or other options you think may be more suitable?

Needle analog gauge. As a nice bonus controlling it can easily be part of the normal engine control loop (e.g. bang bang control).

cattledog:
Yes, there are definitely many character oleds with hd44780 compatible controllers that have operating specs from -40C to +80C.

Here's a DigiKey selection page to get you going. The first filter to apply will be operating temperature.
https://www.digikey.com/products/en/optoelectronics/display-modules-lcd-oled-character-and-numeric/99

Alright i think i'm missing a fundamental you are trying to hit me with. Is anything that is a external board that is direct SPI or I2C a bad idea? Because typically those boards use some type of slow library like an adadruit open source library?

Now i did build an old parallel LCD 16x4 for a high school project where it tied into the computer via parallel serial. This is what you're saying will be the fastest type of communication based on the most common library?

Would the other option be to pass the variables to another arduino that is dedicated to processing the oled library? The master arduino that is doing the rpm encoding could even essentially output Only to the slave arduino (not requiring any information from it) doing the display.

Looking at the options from digikey id look at this option for pricing and availability

https://www.digikey.com/product-detail/en/vishay-dale/O020N002ARPP5N0000/541-3385-ND/7041756

Since it does parallel type communication then this should work with that library?

Alright i think i'm missing a fundamental you are trying to hit me with. Is anything that is a external board that is direct SPI or I2C a bad idea? Because typically those boards use some type of slow library like an adadruit open source library?

No, it is the graphics type oled displays with large pixel fields rather than the character type displays which are slower.

Since it does parallel type communication then this should work with that library?

I believe so. The important issue is that the display have an on board controller chip compatable with the hd44780 controller. Most of them do, but that is a question for the manufacturer.

You may want to consider Coding Badly's suggestion to use an analog gauge rather than a display.

cattledog:
No, it is the graphics type oled displays with large pixel fields rather than the character type displays which are slower.

I believe so. The important issue is that the display have an on board controller chip compatable with the hd44780 controller. Most of them do, but that is a question for the manufacturer.

You may want to consider Coding Badly's suggestion to use an analog gauge rather than a display.

Ahhh it makes sense now. Character LCD's have built-in hardware that creates characters from the pixels where as any graphic lcd has to draw each character out, and relying on the library to do so.

I did a LCD project a while back on a 3.4" tft for an arduino mega. Read a sensor and draw a rectangle corresponding to the value. That code refreshed very quickly. So fast i had to slow it down to make sense of the last digit because it changed so sporadically.

Maybe ill try a cheap amazon graphic just to see what the timing is like and adjust from there. I can always use it on something else. Keep in mind the main loop doesn't need to be fast. If it updates every 100-50ms that's probably fine. The interrupt code is the key. Even a single missed interrupt is now double the cycle time it will read for the next revolution. The loop code is less important.