SparkFun Forums 

Where electronics enthusiasts find answers.

For the discussion of Arduino related topics.
User avatar
By TrollHammer
#170361
If I have a program loop that is keeping track of time using micros() and I use an external interrupt ISR that has a delayMicroseconds() up to 51 milliseconds (max, but this condition is highly unlikely, and yes, I'm aware I'd need to stack the delays to get that long), will micros() be off in the main loop by that much, or does it still keep track of time overall regardless of interrupt running?

Essentially, what I'm trying to do is:

(main loop):
Code: Select all
determine the period between interrupt events in the range 60ms to 3ms, probably by a flag set by the ISR using micros()

Read some inputs (analog mostly)

do some math to determine how long an output should be on for based on this information, make it available for the ISR.
(this period will be between 0 and 85% of the current time between interrupts, based on various factors, but expected to be around 25% on average, so between 15ms and 0.75ms, the latter likely to be longer and the former shorter.)

Move a stepper motor to a postion determined by above math
(ISR):
Code: Select all
(if needed set a flag or volatile variable for the main loop to measure the time between events)

Turn on an output

wait for a period of time determined by the main loop math

Turn off the output
Last edited by TrollHammer on Sat Apr 19, 2014 11:37 pm, edited 1 time in total.
By Mee_n_Mac
#170374
TrollHammer wrote:... will micros() be off in the main loop by that much, or does it still keep track of time overall regardless of interrupt running?
I believe it'll be off by that much. When any ISR is running, all other interrupts pend (on an AVR based Arduino). Since the millis() and micros() functions are interrupt driven, they too will pend, and therefore not increment.

http://arduino.cc/en/Reference/attachInterrupt
Note:
Inside the attached function, delay() won't work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function. See the section on ISRs below for more information.


You should be able to code up a test sketch to prove/disprove the above and confirm that your loop will "be off by that much".
By jremington
#170380
There is NEVER a need to have a delay longer than a few machine instruction cycles in an interrupt routine, and it is a bad idea to put one in. An interrupt routine should accomplish its work as quickly and efficiently as possible, and if a delay is needed, set a flag (e.g. a "volatile" global variable) that tells the main program to execute the delay and then return.
User avatar
By TrollHammer
#170381
Hmm.... that gets complicated quick, and still confusing. If interrupts suspend all other interrupts completely, how can millis() and micros() work accurately, especially if micros has a resolution of 4us and an interrupt routine takes over 50us to complete... the only way I can reconcile it in my head is if the time keeps counting, but the outines to access millis and micros wont work within another interrupt. But like you said, I can just write up a test for it.

The delay within the interrupt, yeah, I was being lazy as I dont have anything time critical within the main loop, but highly time critical within the event. Hiw about if I use the interrupt to set a flag or volitile or whatever, which branches the main loop to a function that turns on the output and starts a timer(1 or 0) to turn it off, so the loop can still work on math in the mean time (if Im staying away from a delay in one part, why not get rid of them all?)
By jremington
#170390
Hardware timers can keep counting no matter what the processor does, but the cpu can do only one thing at a time. Using one interrupt to keep time works reasonably well in many situations, but it is very difficult to keep accurate time if more than one interrupt is involved.

You could have the interrupt routine start a hardware timer to initiate a delay (and then exit). When the delay timer overflows, that could trigger a different interrupt to do, or initiate, whatever else needs to be done.
By Mee_n_Mac
#170391
@the OP - what MCU are you using ? Various Arduino functions, like millis() and micros(), use certain timers to do their tasks. If you use those functions, be sure to use a different timer for your timing task. You may find that a timer has multiple compare/match units, each capable of generating it's own interrupt. Thus you can use 1 timer to trigger more than one ISR to run at differing delays. Your program could set the timer to run at the resolution needed. When the ISR above runs it could read the timer/counter value (or reset it) and setup the compare/match unit(s) to generate multiple interrupts to occur at differing times in the future.
User avatar
By TrollHammer
#170403
@me n mac: Uno with ATmega328. Thanks again for the insight. I was not aware that a timer could be variably subdivided.

Thanks for all the input everyone, Gives me a lot to consider. In the last 24 hours I have found other projects like mine, but very little code, and few going the route I intend.

What I'm trying to do is build a brute force, simple TBI conversion for my Chevy LUV and my cousin's Toyota. Both are 4 bangers, with roughly 2 liter engines (mine's 1.8, and his is 2.2). I have a rather odd theory of down and dirty cheating to make it work:

A) Ideally, air only passes into a cylinder on the intake stroke, which begins at top dead center (more or less).
B) On any even-qty cylinder inline engine, when a cylinder is opening for intake, its opposite cylinder is firing, roughly at TDC.
C) On TBI, only a single injector is used, and opens equally for any cylinder, regardless of which one is opening.
D) On these older engines (still running points), the points open equally for each TDC (with slight shifting due to vacuum advance)

Therefore, my thought is to use the grounding signal from the points (via opto-isolator), to start the injector pulse, and use RPM, throttle position sensor, engine temp, and whatever other sensors I decide to account for (initially, not worried about the O2 sensor, as there's no such feedback with a carburetor, and I'm trying to keep it simple and cheap) to determine the length of the pulse.

This means that the pulse will need to be initiated at variable times, and the pulse length will need to be variable.

I figure that the red-line is 6000rpm (I'll add a rev limiter that cuts out the injector above this), and there are two injection/ignition pulses per revolution. This results in in minimum pulse timing of 3ms, and injectors can only use an 85% duty cycle, so at this RPM the pulse width would be variable between the minimum time the injector could be on (which I don't know at this time, something like 1ms) and 2.55ms.

On the low end, my cousin wants his idle to be about 700RPM, but this isn't the lowest RPM that needs accommodated. I'm guessing it needs to be stable down to 250 or so.that makes the period (rounded up to) 125ms... which, indeed would be too long to use as an interrupt. The throttle condition near idle speeds with a manual transmission shouldn't normally be more than like 25-50% I would think, since at this point either you burn the clutch or spin the tires and the RPM goes up. This puts the 'on time' for the injector to be below around 60ms, and I doubt it would be all that much more than the maximum fuel use during peak horsepower (somewhere around 2500rpm I'm guessing, which is 1/((2500/60)x2) x 0.85 = 10.2ms). These are strictly assumptions and the actual values and curves would probably be much different, but I'm not to that point yet.

My goal is to have the main program loop calculate the period and pulse duration continuously based on TPS, RPM, and temp (and map if I add one and can figure out how it adds into the calculations), and use an interrupt off of the points to trigger the injector.

This is strictly an off road project (in terms of the Toyota, at least), and while I will be trying to maintain the fuel efficiency of the carburetor he is currently using, increased mileage is not a prerogative. The purpose is to provide him with an easy to swap TBI with a minimum of wires that can operate at high angles (hill and rock climbs). His current carb's float bowl floods out the engine while in this condition and he loses fine control (half to full throttle or it stalls). He has plenty of access to throttle bodies and other car parts, but has no ability to program stuff and is intimidated by the thought of adding a 22RE wiring harness (his other option). Anyway, I figure this method, if it works, would only cost a controller, power conditioning circuitry, a temp sensor (if I can't tap into the stock one), some I/O isolation (opto for the coil and a driver for the injector), and a throttle body, which, for him, would amount to a grand total of $50 or less.