Exiting an interrupt?

I am in the beginning stages of building an electronic ignition for a motorcycle using an UNO as my base. I have a hall sensor (2AV54) tied to pin 2, and a BIP373 on pin 13 triggering an external 12V spark coil. I want to have a rising edge signal from the hall sensor make the BIP373 brought HIGH for 3.2 milliseconds, then brought LOW until the next rising edge.

Trouble is, it seems to jump into an infinite loop when it sees its very first rising edge, and can only be exited by hitting the reset button. I checked the hall signal on a scope, it looks very clean to me, so I do not think it is bouncy signal. Can anyone help me?

NOTE: The variable "sparked" and all statements attached to it are pretty much useless, I thought I could just implement a counter to stop the interrupt loop, but to no avail.

I have the code below:

volatile int sparked = 1;
int sparkPin = 13;

void setup()
{
pinMode(sparkPin, OUTPUT);
attachInterrupt(0, spark, RISING);
}

void loop()
{
}

void spark()
{
if (sparked = 1);
digitalWrite(sparkPin, HIGH);
delay(3.2);
digitalWrite(sparkPin, LOW);
sparked = 0;
}

void spark()
{
if (sparked = 1);
digitalWrite(sparkPin, HIGH);
delay(3.2); // This is a problem, you can't use a delay command inside a ISR routine as all
// interrupts are disabled and delay uses a timer interrupt, so it causes your
// sketch to just 'freeze' at this point. Also Delay won't except a floating point
// number as it's argument which 3.2 is. You would you use
// delayMicroseconds(3200) instead, however again you can't use these timer
// based based functions inside of a ISR function.
digitalWrite(sparkPin, LOW);
sparked = 0;
}

Read the note section of the attachInterrupt reference page:
http://arduino.cc/en/Reference/AttachInterrupt

Lefty

Dang. I read that, too, and it just slipped by me. I am absolutely positively brand new to coding, if that's an excuse for inattentiveness.

How can I then turn off the signal to the BIP373? That duration is critical, as too long will cook both the BIP373 and the spark coil.

OK, if I remove the delay instruction from the interrupt routine, it seems to work; on a rising edge I see ever so faint a flicker on the pin 13 LED. However, this is not enough time to charge the coil to get a discharge.

Problem remains of leaving pin 13 HIGH long enough to reach SS in the coil. Any help would be greatly appreciated.

How can I then turn off the signal to the BIP373? That duration is critical, as too long will cook both the BIP373 and the spark coil.

well maybe you could just build a for loop that 'wastes' enough time to use up 3.2 milliseconds of time. Perhaps something like:

void spark()
{ 
for (long i=0; i <= 50000; i++)
   {
      digitalWrite(sparkPin, HIGH);
   } 
digitalWrite(sparkPin, LOW);
}

50000 is just a example value, I have no idea what number would consume 3.2 milliseconds, however whatever the number works out to be it should be also varified with a scope if it's a critical item.

Lefty

Tried that, it just slows down the infinite loop, so now the result is more like Chinese spark torture.

So now, half the problem is solved; I have a spark (the number for i will require tuning), but I still cannot escape the ISR!

I'm guessing that the 3.2mS on-time is to drive a capacitor-discharge ignition system, in which case the 3.2mS is not critical but needs to be short enough to let the capacitor charge again before the next spark. Is this correct?

If so, I can see a couple of options:

  1. If the Arduino is not busy doing anything else, don't use interrupts at all. Just poll the Hall sensor pin in loop() waiting for the rising edge, then when you detect it you can turn the spark on and use delay() to time when you need to turn it off. Then wait fof the Hall sensor pin to go low again, and start again.

  2. Alternatively, use the interrupt to turn on the spark (as you are doing), but also in the ISR call micros() to fetch and store the current time in a variable, call it lastSparkTime. In loop() keep calling micros() and when its value minus lastSparkTime is greater than 3.2ms, turn off the spark. You'll need to be careful that you don't turn it off just after getting a new interrupt, but that isn't difficult.

My example will escape the interrupt service routing after the number of loops in the for statement is reached. Maybe you need to write a new complete sketch so we can see where you are at.

What is frequency of the signal from the hall effect sensor, i.e. what is the maximum speed that the sensor (and arduino sketch) is being asked to process? There will be a limit that can be handled.

Lefty

So here is the code as it stands, this gives me a signal on pin 13 of intermittent length, and so I get a spark sometimes, but not always.

long int lastSpark;
int sparkPin = 13;

void setup()
{
pinMode(sparkPin, OUTPUT);
attachInterrupt(0, spark, RISING);
}

void loop()
{
if (micros() - lastSpark >= 3200)
digitalWrite(sparkPin, LOW);
}

void spark()
{
digitalWrite(sparkPin, HIGH);
lastSpark = micros();
}

Again how fast are the interrupts coming?

Lefty

hikerjohnson:
So here is the code as it stands, this gives me a signal on pin 13 of intermittent length, and so I get a spark sometimes, but not always.

...

long int lastSpark;
...

micros() returns unsigned long so I would change lastSpark to that. Also, make it volatile. ie.

...
volatile unsigned long lastSpark;
...

For future reference, this is wrong in two ways:

if (sparked = 1);

digitalWrite(sparkPin, HIGH);

First, it isn't testing sparked. It is assigning 1 to it. Second, it won't do anything because of the semicolon.

You want:

if (sparked == 1)
  digitalWrite(sparkPin, HIGH);

Now please get into the habit of using [ code ] tags when posting code so it is indented properly.

You're in luck. delayMicroseconds() is implemented as a busy loop (see: wiring.c) so you can use it in an ISR. Try this code:

const int sparkPin = 13;  // Output pin for spark coil

void setup()
  {
  pinMode(sparkPin, OUTPUT);
  attachInterrupt(0, spark, RISING);
  }

void loop()
  {
  }

void spark()
  { 
  digitalWrite(sparkPin, HIGH);
  delayMicroseconds(3200);
  digitalWrite(sparkPin, LOW);
  }

hikerjohnson:
So here is the code as it stands, this gives me a signal on pin 13 of intermittent length, and so I get a spark sometimes, but not always.

Like I said, you have to be careful not to turn off the spark just after getting a new interrupt. Something like this:

unsigned long int lastSpark;
int sparkPin = 13;
bool newSpark = false;

void setup()
{
pinMode(sparkPin, OUTPUT);
attachInterrupt(0, spark, RISING);
}

void loop()
{
  if (newSpark && (micros() - lastSpark >= 3200)) {
     newSpark = false;
     digitalWrite(sparkPin, LOW);
  }
}

void spark()
{
digitalWrite(sparkPin, HIGH);
lastSpark = micros();
newSpark = true;
}

However, John's solution of using delayMicros() in the ISR is simpler. The main problem I can see with turning off interrupts for that long is that will disrupt the system timekeeping because timer overflow interrupts will be missed - but that won't affect your call to delayMicros.

dc42:
John's solution of using delayMicros() in the ISR is simpler. The main problem I can see with turning off interrupts for that long is that will disrupt the system timekeeping because timer overflow interrupts will be missed - but that won't affect your call to delayMicros.

Good point. You should re-enable interrupts (add the line "sei();") at the start of the ISR so that the other interrupts can happen during the 3.2 millisecond delay. As long as you are getting less than 300 sparks per second (18,000 per minute) you shouldn't have a problem with the ISR being re-entered before it completes.

Don't forget to declare variables changed in the interrupt for use outside as volatile - i.e. lastSpark and newSpark in the snippet above.

Why not just use another interrupt to disable the signal at the specified duration? Playground even provides a Timer library to make this easy to do: Arduino Playground - Timer1

Something like:

#include "TimerOne.h"

const int sparkPin = 13;  // Output pin for spark coil

void setup()
  {
  pinMode(sparkPin, OUTPUT);
  attachInterrupt(0, spark, RISING);
  Timer1.initialize(3200); //Set Timer1 period to 3200usec
  }

void loop()
  {
  }

void spark()
  { 
  digitalWrite(sparkPin, HIGH);
  //Enable our timer here by attaching our callback, which will fire 3200usec after attached
  //It is in this callback that we'll end our spark event
  Timer1.attachInterrupt(endspark);
  }

void endspark()
{
  digitalWrite(sparkPin, LOW);
  //We disable our Timer here as we don't want it firing again
  //It'll get reenabled when the next spark event starts
  Timer1.detachInterrupt();
}

Clean, effective, reliable, and will work fine with other code running on the Arduino as well.

retrolefty:
void spark()
{
if (sparked = 1);
digitalWrite(sparkPin, HIGH);
delay(3.2); // This is a problem, you can't use a delay command inside a ISR routine as all
// interrupts are disabled and delay uses a timer interrupt, so it causes your
// sketch to just 'freeze' at this point. Also Delay won't except a floating point
// number as it's argument which 3.2 is. You would you use
// delayMicroseconds(3200) instead, however again you can't use these timer
// based based functions inside of a ISR function.
digitalWrite(sparkPin, LOW);
sparked = 0;
}

Read the note section of the attachInterrupt reference page:
attachInterrupt() - Arduino Reference

Lefty

While delay() spins looking at data that is modified in interrupt handlers,
delayMicroseconds() does not depend on interrupts. It is a pure CPU spin on local information.
While creating large delays inside an ISR can potentially cause many other problems and I would recommend
considering a different approach, delayMicroseconds() can be used inside an ISR.

--- bill

This is probably a stupid idea, maybe it doesn't even work.

I am working with mechanical rotary encoder, and I am trying to get rid of bounce problem.
What I try, was that I used detachInterrupt to avoid premature interrupt until it was sure contacts were bouncing no more.

Simply like this inside the interrupt routines:

void Encoder0A()  //interrupt from pin D2, for up counting
{
detachInterrupt(0);
detachInterrupt(1);
Add2=HIGH; // flag for loop routine where actual count up/down will be done, and after 80ms interrupts will be turned on again
}

void Encoder0B()  //interrupt from pin D3, for down counting
{
detachInterrupt(1);
detachInterrupt(0);
Subt3=HIGH;
}

My goal was the get out from the interrupt in reasonable time.
And even if I try to do that, sometimes I got +2 or -2 with one interrupt.

Just tested the encoder with oscillospe, and 30ms should be enough between changes, but no...

BTW, could it be possible to change the "#"-sign from the button to "Code"-button?
Of course not, just like the orange will be there until the server goes down for good...?
Yeah, wrong forum for that.

Cheers,
Kari

Check out this:

I got a rotary encoder to work with interrupts. The debunking debouncing was done with a couple of capacitors.

(if only I could stop Safari from auto-correcting )