# write/listen at same time?

One of the other topics (RPM monitor) made me think of something. The catch is, the controller would need to read a PWM while simultaneously writing a digitally sequenced PWM.

Applic would be an LED recreation of an automotive fuel injection system, using the 3-pulse theory. The code I have below would work fine, but I forgot the critical problem, that it's going to take time to read back the pulseIn from the crankshaft Hall sensor.

The only workaround I can think of, would to have 2 arduino's communicating over I2C, where one gets the crankshaft RPM as a stored variable, and the other pulses the outputs based on that stored variable, and subtract the read time of I2C from the very last delay to make up the difference. That way the communication time is short enough to subtract the difference (unlike the crankshaft read time). Even with a 4 magnet ring, the pulseIn would still be always greater than 1875, which still couldn't be subtracted from the last delay of 594.

To test this, I would use a 3 pin PC fan, and use the TACH output to feed the PWM input, where crankshaft hall sensor would be.

Pseudo:

``````Going with the 3 pulses per stroke theory:

8000 crankshaft RPM
133.33 crankshaft RPS
66.67 camshaft RPS
66.67 distributor RPS
1/66.67 = 15ms per rotation
15ms per rotation / 8 plugs per rotation = 1875?s between plugs

8000 crankshaft RPM
133.33 crankshaft RPS
0.007500 seconds per crankshaft rotation
2 crankshaft rotations per fire = 15ms per fire
15ms per fire / 8 fires in between = 1875?s

133.33 crankshaft rotations per second
it takes 7500?s for the crankshaft to make one rotation
it takes 15ms for the camshaft to make one rotation
it takes 15ms for the distributor to make one rotation
15ms per fire / 8 fires in between = 1875?s

The formula:

take the PulseIn time from crankshaft, divide by 4, that's the time between fires on-center. For 3-fires-per, divide by 12.

---------------------------------------------------------------------------------------------

void loop(){

int input = pulseIn(rpmIn,HIGH);
float output = (input / 12) * 0.95;
// holds 95% between pulses (594?s @ 8000 rpm, 4750?s @ 1000 rpm), decrease if lean/increase if rich
float pulse = (input / 12) * 0.05;
// holds injector open for 5% (31?s @ 8000 rpm, 250?s @ 1000 rpm), increase if lean/decrease if rich

digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);

// Spark plug 1 fires

digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);

// spark plug 8 fires

digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);

// spark plug 7 fires

digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);

// spark plug 3 fires

digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);

// spark plug 6 fires

digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);

// spark plug 5 fires

digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);

// spark plug 4 fires

digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);

// spark plug 2 fires, restart loop

}
``````

The code I have below would work fine, but I forgot the critical problem, that it's going to take time to read back the pulseIn from the crankshaft Hall sensor.

Yup.

The only workaround I can think of, would to have 2 arduino's communicating over I2C

Nope. Timer 1 is capable of "input capture" which is designed to solve just this sort of problem. Essentially pulseIn is performed by a combination of the hardware and a simple interrupt service routine. I believe this is how Paul Stoffregen's FreqMeasure Library works...

http://www.pjrc.com/teensy/td_libs_FreqMeasure.html

I’m not seeing many google results for “timer 1 input capture”, could you explain this a bit more please?

I found this post from 2009, but it doesn’t have a very good process explanation: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1252747876

At 8000 RPM input, that would be a 1-pole of 133.33hz (rotations per second @ 1 pole per rotation), 2-pole of 266.66, and 4-pole of 533.33hz.

So I would want to use the freqMeasure fxn from the link you provided. But I’m still not fully understanding how it can read pin 8 ‘in the background’ while firing pins ‘in the foreground’.

If I am understanding things right, if there is no reading avaliable (i.e., between reads), then it will resolve to the previously recorded value?

``````#include <FreqMeasure.h>

double frequency = "0";

void setup() {
Serial.begin(57600);
FreqMeasure.begin();
}

void loop(){

if (FreqMeasure.available()) {
// if RPM has been read
}

//int input = pulseIn(rpmIn,HIGH);
input = frequency;
float output = (input / 12) * 0.95;
// holds 95% between pulses (594?s @ 8000 rpm, 4750?s @ 1000 rpm), decrease if lean/increase if rich
float pulse = (input / 12) * 0.05;
// holds injector open for 5% (31?s @ 8000 rpm, 250?s @ 1000 rpm), increase if lean/decrease if rich

digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);

// Spark plug 1 fires

digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);

// spark plug 8 fires

digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);

// spark plug 7 fires

digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);

// spark plug 3 fires

digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);

// spark plug 6 fires

digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);

// spark plug 5 fires

digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);

// spark plug 4 fires

digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);

// spark plug 2 fires, restart loop

}
``````

magnethead794: I'm not seeing many google results for "timer 1 input capture", could you explain this a bit more please?

There appear to be some good hits from here... http://www.google.com/search?q=site:arduino.cc+input+capture

The datasheet is also a fairly good source of information about input capture.

If you decide to go "low level"... Basically, you get timer 1 running the way you'd like. My preference is "free" (no prescaler; TOP = MAX). But other configurations may work better for your application (may eliminate some math). Then, you configure and enable input capture. When an event occurs (e.g. rising edge), the timer value is captured and an interrupt service routine is called. In the interrupt service routine, you save the value and typically set a flag

At 8000 RPM input, that would be a 1-pole of 133.33hz (rotations per second @ 1 pole per rotation), 2-pole of 266.66, and 4-pole of 533.33hz.

16000000/(8000/60*4) = 30000 machine instructions per pole. Should be more than enough horsepower available.

So I would want to use the freqMeasure fxn from the link you provided. But I'm still not fully understanding how it can read pin 8 'in the background' while firing pins 'in the foreground'.

I've not used Paul's library so I cannot advise you on how to use it. I have used the timer 1 input capture directly so I may be able to answer "low level" questions.

Basically, when a "trigger event" occurs on pin 8, the counter value is captured into a processor register and an interrupt service routine is called. The rest is up to you (or the library). In your case, you are interested in the difference between the previous captured value and the current captured value. This tells you very precisely how much time elapsed between the two trigger events. You can then calculate the frequency or whatever else you need to calculate.

If I am understanding things right, if there is no reading avaliable (i.e., between reads), then it will resolve to the previously recorded value?

That is very likely true. You may have to modify the code to include "aging". A simple strategy is to increment a variable in loop and reset the variable in the interrupt service routine. If the value gets too large (say 2) then fresh data is no longer available and, for an engine controller, I assume the strategy is to shutdown the engine.

I’m still confused. I would need to see some examples to fully understand how the timer works, and how it can be listening in the background while writing data/pins in the foreground. Also, a critical part of this, is that I can’t interrupt the firing process mid-cycle- it needs to take effect at the end/beginning of each cycle. From my understanding, interrupts jump right in the middle of loop(), and interrupt whatever it was doing.

Also, i chose to use delayMicroseconds between pulses…but will the delay function also halt the pulse reading process?

In testing I’ll be using the PWM output of a PC fan (powering fan with 12V rail, arduino with 5V rail from my PC power supply that I converted to a benchtop unit, built in common ground), but in application, I’ll be using a hall sensor between the 5V out, ground, and pin 8 of the arduino board.

found this: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1201890734 but I’m completely lost in how the ISR is called, because i don’t see it being called. Plus I don’t understand what ICES, TCCR1B, and ICR1 are.

``````volatile unsigned int Ticks;	   // holds the pulse count as .5 us ticks
int icpPin = 8;			     // this interrupt handler must use pin 8

ISR(TIMER1_CAPT_vect){
if( bit_is_set(TCCR1B ,ICES1)){	 // was rising edge detected ?
TCNT1 = 0;				// reset the counter
}
else {					  // falling edge was detected
Ticks = ICR1;
}
TCCR1B ^= _BV(ICES1);		     // toggle bit value to trigger on the other edge
}

void setup()			  // run once, when the sketch starts
{
Serial.begin(9600);
pinMode(icrPin,INPUT);
TCCR1A = 0x00;	   // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
TCCR1B = 0x02;	   // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
Ticks = 0;		 // default value indicating no pulse detected
TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

int getTick() {
int akaTick;	 // holds a copy of the tick count so we can return it after re-enabling interrupts
cli();		 //disable interrupts
akaTick = Ticks;
sei();		 // enable interrupts
return akaTick;
}

void loop()			   // run over and over again
{
static int prevTick = 0;

if( getTick()  != prevTick) {
prevTick = getTick();
Serial.print( prevTick);     // print the tick value only when it changes
}
}
``````

If I’m thinking right… (for arduino uno)

``````volatile unsigned int Ticks;	   // holds the pulse count as .5 us ticks
int icpPin = 8;			     // this interrupt handler must use pin 8

ISR(TIMER1_CAPT_vect){
if( bit_is_set(TCCR1B ,ICES1)){	 // was rising edge detected ?
TCNT1 = 0;				// reset the counter
}
else {					  // falling edge was detected
Ticks = ICR1;
}
TCCR1B ^= _BV(ICES1);		     // toggle bit value to trigger on the other edge
}

void setup()			  // run once, when the sketch starts
{
Serial.begin(9600);
pinMode(icrPin,INPUT);
TCCR1A = 0x00;	   // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
TCCR1B = 0x02;	   // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set)
Ticks = 0;		 // default value indicating no pulse detected
TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

int getTick() {
int akaTick;	 // holds a copy of the tick count so we can return it after re-enabling interrupts
cli();		 //disable interrupts
akaTick = Ticks;
sei();		 // enable interrupts
return akaTick;
}

void loop()			   // run over and over again
{
float input = getTick();
float output = (input / 12) * 0.95;
// holds 95% between pulses (594?s @ 8000 rpm, 4750?s @ 1000 rpm), decrease if lean/increase if rich
float pulse = (input / 12) * 0.05;
// holds injector open for 5% (31?s @ 8000 rpm, 250?s @ 1000 rpm), increase if lean/decrease if rich

digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);

// Spark plug 1 fires

digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);

// spark plug 8 fires

digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);

// spark plug 7 fires

digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);

// spark plug 3 fires

digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);

// spark plug 6 fires

digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);

// spark plug 5 fires

digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);

// spark plug 4 fires

digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);

// spark plug 2 fires, restart loop

}
``````

So the more I think about this, the more I think I'm going to want to I2C a pair of Unos together.

The master board would have an on/off switch on a digital pin, the interrupt for the hall sensor input, and run the digital outputs. Additionally, I'd like to add in a digital signal to fire the spark plugs right after the last injector activation, but that wouldn't be hard at all. The slave would basically run a serial display in the form of a digital dash display, as well as have pressure sensor inputs from numerous sub-systems.

I won't say the entire big picture plans, but lets just say I'm looking at designing a poor man's ECU. I would be saying too much if I said why an off-the-shelf unit wouldn't work.

So I was looking at these:

http://www.ms3efi.com/

and I realized a very important thing: I also need to add a "Throttle Position Sensor", which is basically a potentiometer attached to the throttle body, and tells the computer exactly how much fuel to dump in (basically, it lengthens or shortens the pulse). So in addition to the interrupt that is listening to the RPM hall sensor input, I also need it to listen for the throttle position sensor. Basically, from my understanding, I could just run the potentiometer between 5V, Ground, and an analog pin, and use a comparator against a known table of values, for how much to affect the pulse length. The key is that the throttle body shaft only turns 90 degrees, so the readings off the potentiometer will be very close together- I could use a stock throttle body, which means I could also use the stock TP sensor. So that is no problem, as they're already designed to run on the 5 volt scale from stock ECU's. I'm just having to build my own for my own specific application (yes I could use a mega squirt, but now that just wouldn't be fun, and would cost more- \$432). So the fundamentals would be no problem- every time the crankshaft makes a rotation, get a read off the voltage on the analog pin and compare it to the known table, and lengthen or shorten the pulse widths from there.

A standard TPS is 0.6 volts closed and 3.8 volts open. On the assumption that the resistance is linear, at 3.2 volts over 90 degrees, thats 28.125 degrees of rotation per volt.

If that assumption is true, then conics come into play, to figure out where to consider "volumetric middle" in the motion of the throttle blades. For arbitration, I'm going to say 60% (54 degrees) is the midpoint.

closed- 0.6 volts 9 degrees- 0.92 18 degrees- 1.24 27 degrees- 1.56 36 degrees- 1.88 45 degrees- 2.20 54 degrees- 2.52 63 degrees- 2.84 72 degrees- 3.16 81 degrees- 3.48 90 degrees- 3.80

``````volatile unsigned int Ticks;       // holds the pulse count as .5 us ticks
int icpPin = 8;              // this interrupt handler must use pin 8
volatile float TPS;
float input;

ISR(TIMER1_CAPT_vect){
if( bit_is_set(TCCR1B ,ICES1)){  // was rising edge detected ?
TCNT1 = 0;             // reset the counter
}
else {                    // falling edge was detected
Ticks = ICR1;
}
TCCR1B ^= _BV(ICES1);            // toggle bit value to trigger on the other edge
}

void setup()              // run once, when the sketch starts
{
Serial.begin(9600);
pinMode(icrPin,INPUT);
TCCR1A = 0x00;      // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
TCCR1B = 0x02;      // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set)
Ticks = 0;        // default value indicating no pulse detected
TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

int getTick() {
int akaTick;  // holds a copy of the tick count so we can return it after re-enabling interrupts
cli();        //disable interrupts
akaTick = Ticks;
sei();        // enable interrupts
return akaTick;
}

void loop()            // run over and over again
{
input = getTick();
float output = (input / 12) * 0.95;
// holds 95% between pulses (594?s @ 8000 rpm, 4750?s @ 1000 rpm), decrease if lean/increase if rich
float pulse = (input / 12) * 0.05;
// holds injector open for 5% (31?s @ 8000 rpm, 250?s @ 1000 rpm), increase if lean/decrease if rich
if (TPS >= 3.80){
output *= 0.96;
pulse *= 1.042;
} else if (TPS >= 3.48){ // Full throttle, shorten output/length pulse
output *= 0.97;
pulse *= 1.031;
} else if (TPS >= 3.16){
output *= 0.98;
pulse *= 1.02;
} else if (TPS >= 2.84){
output *= 0.99;
pulse *= 1.01;
} else if (TPS >= 2.52){ //60% opening
output *= 1;
pulse *= 1;
} else if (TPS >= 2.20){
output *= 1.01;
pulse *= 0.99;
} else if (TPS >= 1.88){
output *= 1.02;
pulse *= 0.98;
} else if (TPS >= 1.56){
output *= 1.03;
pulse *= 0.97;
} else if (TPS >= 1.24){
output *= 1.04;
pulse *= 0.9615;
} else if (TPS >= 0.92){
output *= 1.05;
pulse *= 0.9524;
} else if (TPS >= 0.60){ // Throttle closed, shorten pulse lengthen output
output *= 1.06;
pulse *= 0.9434;
} else { // if less than 0.60
output *= 1.06;
pulse *= 0.9434;
}

digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);
digitalWrite(cyl1,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl1,LOW);
delayMicroseconds(output);

// Spark plug 1 fires

digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);
digitalWrite(cyl8,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl8,LOW);
delayMicroseconds(output);

// spark plug 8 fires

digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);
digitalWrite(cyl7,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl7,LOW);
delayMicroseconds(output);

// spark plug 7 fires

digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);
digitalWrite(cyl3,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl3,LOW);
delayMicroseconds(output);

// spark plug 3 fires

digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);
digitalWrite(cyl6,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl6,LOW);
delayMicroseconds(output);

// spark plug 6 fires

digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);
digitalWrite(cyl5,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl5,LOW);
delayMicroseconds(output);

// spark plug 5 fires

digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);
digitalWrite(cyl4,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl4,LOW);
delayMicroseconds(output);

// spark plug 4 fires

digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);
digitalWrite(cyl2,HIGH);
delayMicroseconds(pulse);
digitalWrite(cyl2,LOW);
delayMicroseconds(output);

// spark plug 2 fires, restart loop

}
``````
``````TCNT1 = 0;				// reset the counter
``````

…is guaranteed to cause a very difficult to debug problem. You need to work with deltas between captured values.