ESP32 Multiple changing timer interrupts - Motorcycle CDI

Hi Guys,

I have been using the code below to run my DIY CDI system.

The code is working perfectly,

What I'm doing is getting an external interrupt from the variable reluctance sensor (though a conditioning circuit). This external interrupt then start the process of waiting....charging a capacitor, and then discharging said capacitor.

As it is I have all four timers at work, and everything is working as it should. However, I want to trim it down to just using one timer, as I'm sure that it can be done, but I have been trying for hours and can't get it to work properly through a full range of rpm. Just bench testing with a scope right now.

Here's some info from a previous thread I had on this where I tried (and failed) to use an Arduino Pro Mini to do the same thing:

https://forum.arduino.cc/t/counting-timer-overflow-before-interrupt/1002150/102

Here's the code that is working fine:

/////////////
//  Pins   //
/////////////

const int transistorPin = 25/*Purple*/;     //  PB1. Turns PWM output on and off on PIN 1 of UC3843 (COMP pin)
const int SCRPin = 23/*Blue*/;              //  PB2. Turns SCR on and off
const int flywheelPulse = 36/*White*/;      // Trigger input from VR Sensor
const int LEDpin = 2/*Grey*/;               //  PB5. Turns on board LED on and off

///////////////////////
//  Variables of CDI //
///////////////////////

unsigned int rpm = 302;
byte baseAdv = 10;                             // Base advance in degree. Measured at TDC. End of lobe to centre of VR sensor in degrees
float ignAdv = 5.;                             // Ignition Advance #1 in degree BTDC.
float ignErr = 0.88;                           // Ignition advance error correction in degree. Increases or decreases timing.
unsigned long ticksPerRev = 7934965;           // Number of ticks per rpm @ 302rpm
unsigned int ticksBaseAdv = 101110;            // Number of ticks @ 302rpm. 10°
unsigned long ticksTransistorLow = 80000;      // Number of ticks @ 302rpm. 2ms.
unsigned long ticksSCRHIGH = 1000;             // Number of ticks to keep SCR HIGH
unsigned long ticksToSCR = 7885623;            // Number of ticks to delay before SCR HIGH @ 302rpm
unsigned long ticksTransistorDelay = 7805623;  // Number of ticks to delay before transistor LOW @ 302rpm
unsigned int ticksPerDeg = 10111;              // Number of ticks @ 302rpm
volatile bool flywheelPulseFlag = false;       // triggered on flywheelPulse interrupt to disable interrupts while timer is running

/////////////////////////////////
//  Calibration of rev counter //
/////////////////////////////////

volatile unsigned long timeOfInterrupt, oldTimeOfInterrupt, timeBetweenflywheelPulses;  // Calculate frequency
long freq = 504098;

///////////////
//  Printing //
///////////////

unsigned long lastTimePrinted;
unsigned long loopTime = 0;

///////////////////////
//  Ignition Timing  //
///////////////////////

const int row = 15;
const int col = 2;
float rpmArray [row] [col] = {
  {100./*rpm*/, 4./*deg BTDC*/},     //  0
  {1000./*rpm*/, 4./*deg BTDC*/},    //  1
  {2000./*rpm*/, 21./*deg BTDC*/},   //  2
  {3000./*rpm*/, 21./*deg BTDC*/},   //  3
  {4000./*rpm*/, 21./*deg BTDC*/},   //  4
  {5000./*rpm*/, 21./*deg BTDC*/},   //  5
  {6000./*rpm*/, 21./*deg BTDC*/},   //  6
  {7000./*rpm*/, 21.5/*deg BTDC*/},  //  7
  {8000./*rpm*/, 16./*deg BTDC*/},   //  8
  {9000./*rpm*/, 14./*deg BTDC*/},   //  9
  {10000./*rpm*/, 13./*deg BTDC*/},  //  10
  {11000./*rpm*/, 9./*deg BTDC*/},   //  11
  {12000./*rpm*/, 9./*deg BTDC*/},   //  12
  {13000./*rpm*/, 9./*deg BTDC*/},   //  13
  {14000./*rpm*/, 9./*deg BTDC*/}    //  14. Just an arbitrary number to avoid problems in for loop when checking rpm to set ignition timing.
};

////////////////////
//  Setup Timers  //
////////////////////

hw_timer_t * baseAdvDelay = NULL;    //  Delay base timing
hw_timer_t * transistorDelay = NULL; //  Delay before switching transistor to LOW to turn on PWM on UC3843 ic
hw_timer_t * transistorLOW = NULL;   //  Transistor
hw_timer_t * SCRon = NULL;           //  Transistor

portMUX_TYPE RPMsynch = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE baseAdvDelayMux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE transistorDelayMux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE transistorLOWMux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE SCRonMUX = portMUX_INITIALIZER_UNLOCKED;

void IRAM_ATTR RPM_Interrupt(void);

void IRAM_ATTR onBaseAdvDelay(void);

void IRAM_ATTR onTransistorDelay(void);

void IRAM_ATTR onTransistorLOW(void);

void IRAM_ATTR onSCRon(void);

void setup() {

  Serial.begin(250000);

  digitalWrite(transistorPin, HIGH);
  digitalWrite(SCRPin, LOW);
  digitalWrite(LEDpin, LOW);

  pinMode(transistorPin, OUTPUT);
  pinMode(SCRPin, OUTPUT);
  pinMode(LEDpin, OUTPUT);
  pinMode(flywheelPulse, INPUT);

  attachInterrupt(digitalPinToInterrupt(flywheelPulse), RPM_Interrupt, RISING);  // Enable interruption pin 36 when going from LOW to HIGH.

  ////////////////////
  //  Setup Timers  //
  ////////////////////

  baseAdvDelay = timerBegin(0, 2, true);                            //  baseAdvDelay, 40MHz, count up
  transistorDelay = timerBegin(1, 2, true);                         //  transistorDelay, 40MHz, count up
  transistorLOW = timerBegin(2, 2, true);                           //  transistorLOW, 40MHz, count up
  SCRon = timerBegin(3, 2, true);                                   //  SCRon, 40MHz, count up

  timerAttachInterrupt(baseAdvDelay, &onBaseAdvDelay, true);        //  baseAdvDelay, function "onBaseAdvDelay", interrupt on edge
  timerAttachInterrupt(transistorDelay, &onTransistorDelay, true);  //  transistorDelay, function "onTransistorDelay", interrupt on edge
  timerAttachInterrupt(transistorLOW, &onTransistorLOW, true);      //  transistorLOW, function "onTransistorLOW", interrupt on edge
  timerAttachInterrupt(SCRon, &onSCRon, true);                      //  SCRon, function "onSCRon", interrupt on edge

}

void loop() {

  if (flywheelPulseFlag) {

    if (timeOfInterrupt - oldTimeOfInterrupt >= 4500) {

      unsigned long startMicros = ESP.getCycleCount();

      portENTER_CRITICAL_ISR(&RPMsynch);
      timeBetweenflywheelPulses = timeOfInterrupt - oldTimeOfInterrupt;
      oldTimeOfInterrupt = timeOfInterrupt;

      freq = 100000000000 / timeBetweenflywheelPulses;
      rpm = freq * 60 / 100000;

      for (int i = 13; i >= 0; i--) {
        if (rpm >= rpmArray[i][0]) {
          ignAdv = rpmArray[i][1] + ((rpmArray[i + 1][1] - rpmArray[i][1]) / (rpmArray[i + 1][0] - rpmArray[i][0])) * (rpm - rpmArray[i][0]); // IGN=IGNL+((IGNH-IGNL)/(RPMH-RPML))*(RPM-RPML)
          break;
        }
      }

      ticksPerRev = 4000000000000 / freq; //  40,000,000 ticks per second
      ticksPerDeg = ticksPerRev * 1000 / 360000;
      ticksBaseAdv = ticksPerDeg * baseAdv;
      ticksToSCR = ticksPerRev - (ignAdv * ticksPerDeg) - (ignErr * ticksPerDeg);
      ticksTransistorDelay = ticksToSCR - ticksTransistorLow;

      portEXIT_CRITICAL_ISR(&RPMsynch);

      flywheelPulseFlag = false;

      unsigned long stopMicros = ESP.getCycleCount();
      loopTime = stopMicros - startMicros;
    }

    if (millis() - lastTimePrinted >= 50) {
      Serial.println("------------------");
      Serial.println(freq);
      Serial.println(rpm);
      Serial.println(ticksPerRev);
      Serial.println(ticksPerDeg);
      Serial.println(ticksBaseAdv);
      Serial.println(ticksToSCR);
      Serial.println(ticksTransistorDelay);
      Serial.println(loopTime);
      Serial.println(ignAdv);
      lastTimePrinted = millis();
    }
  }

}

/////////////////////////////
// RPM Interrupt on pin 36 //
/////////////////////////////

void IRAM_ATTR RPM_Interrupt() { // The interrupt runs this to calculate the period between flywheelPulses

  portENTER_CRITICAL_ISR(&RPMsynch);

  timeOfInterrupt = esp_timer_get_time();

  timerAlarmWrite(baseAdvDelay, ticksBaseAdv, false);
  timerWrite(baseAdvDelay, 0);
  timerAlarmEnable(baseAdvDelay);

  flywheelPulseFlag = true;

  portEXIT_CRITICAL_ISR(&RPMsynch);
}

/////////////////////////////
// Delay for base advance  //
/////////////////////////////

void IRAM_ATTR onBaseAdvDelay() {

  portENTER_CRITICAL_ISR(&baseAdvDelayMux);

  timerAlarmWrite(transistorDelay, ticksTransistorDelay, false);
  timerWrite(transistorDelay, 0);
  timerAlarmEnable(transistorDelay);

  portEXIT_CRITICAL_ISR(&baseAdvDelayMux);
}

////////////////////////////////////////////
// Delay before switching transistor LOW  //
////////////////////////////////////////////

void IRAM_ATTR onTransistorDelay() {

  portENTER_CRITICAL_ISR(&transistorDelayMux);

  GPIO.out_w1tc = (1 << 25);  //  Set transistor pin LOW to turn on PWM for 2ms

  timerAlarmWrite(transistorLOW, ticksTransistorLow, false);
  timerWrite(transistorLOW, 0);
  timerAlarmEnable(transistorLOW);

  portEXIT_CRITICAL_ISR(&transistorDelayMux);
}

/////////////////////////////////////////////////////
// Delay before switching transistor back to HIGH  //
// Then switch on SCR                              //
/////////////////////////////////////////////////////

void IRAM_ATTR onTransistorLOW() {

  portENTER_CRITICAL_ISR(&transistorLOWMux);

  GPIO.out_w1ts = (1 << 25);  //  Set transistor pin HIGH to turn off PWM

  timerAlarmWrite(SCRon, ticksSCRHIGH, false);
  timerWrite(SCRon, 0);
  timerAlarmEnable(SCRon);

  GPIO.out_w1ts = (1 << 23);  //  Turn on SCR to discharge capacitor and fire spark plug

  portEXIT_CRITICAL_ISR(&transistorLOWMux);
}

/////////////////////////////////////////////
// Delay before switching SCR back to LOW  //
/////////////////////////////////////////////

void IRAM_ATTR onSCRon() {

  portENTER_CRITICAL_ISR(&SCRonMUX);

  GPIO.out_w1tc = (1 << 23);  //  Turn off SCR

  portEXIT_CRITICAL_ISR(&SCRonMUX);
}

The above runs the CDI without issue. I can explain it in detail if necessary.

The following code sort of works, but won't run below approx 1300rpm (22Hz), or above approx 10500rpm (175Hz). At the low end it just stops working. At the top it seems to lose synch, or something.

I'm no expert on using the timers (unless you just want to flash an LED of course!), and I'm pretty sure that I've got it wrong with all the start, stops and enables etc. As much as I've googled or looked through the Arduino/ESP32 core I can't figure it out. I've tried several combos but to no avail.

Anyway, here's the code that sort of works:

/////////////
//  Pins   //
/////////////

const int transistorPin = 25/*Purple*/;     //  PB1. Turns PWM output on and off on PIN 1 of UC3843 (COMP pin)
const int SCRPin = 23/*Blue*/;              //  PB2. Turns SCR on and off
const int flywheelPulse = 36/*White*/;      // Trigger input from VR Sensor
const int LEDpin = 2/*Grey*/;               //  PB5. Turns on board LED on and off

///////////////////////
//  Variables of CDI //
///////////////////////

unsigned int rpm = 302;
byte baseAdv = 10;                             // Base advance in degree. Measured at TDC. End of lobe to centre of VR sensor in degrees
float ignAdv = 5.;                             // Ignition Advance #1 in degree BTDC.
float ignErr = 0.88;                           // Ignition advance error correction in degree. Increases or decreases timing.
unsigned long ticksPerRev = 7934965;           // Number of ticks per rpm @ 302rpm
unsigned int ticksBaseAdv = 101110;            // Number of ticks @ 302rpm. 10°
unsigned long ticksTransistorLow = 80000;      // Number of ticks @ 302rpm. 2ms.
unsigned long ticksSCRHIGH = 10000;             // Number of ticks to keep SCR HIGH
unsigned long ticksToSCR = 7885623;            // Number of ticks to delay before SCR HIGH @ 302rpm
unsigned long ticksTransistorDelay = 7805623;  // Number of ticks to delay before transistor LOW @ 302rpm
unsigned int ticksPerDeg = 10111;              // Number of ticks @ 302rpm
volatile bool flywheelPulseFlag = false;       // triggered on flywheelPulse interrupt to disable interrupts while timer is running
volatile bool firstLoop = true;

/////////////////////////////////
//  Calibration of rev counter //
/////////////////////////////////

volatile unsigned long timeOfInterrupt, oldTimeOfInterrupt, timeBetweenflywheelPulses;  // Calculate frequency
long freq = 504098;

///////////////
//  Printing //
///////////////

unsigned long lastTimePrinted;
unsigned long loopTime = 0;

///////////////////////
//  Ignition Timing  //
///////////////////////

const int row = 15;
const int col = 2;
float rpmArray [row] [col] = {
  {100./*rpm*/, 4./*deg BTDC*/},     //  0
  {1000./*rpm*/, 4./*deg BTDC*/},    //  1
  {2000./*rpm*/, 21./*deg BTDC*/},   //  2
  {3000./*rpm*/, 21./*deg BTDC*/},   //  3
  {4000./*rpm*/, 21./*deg BTDC*/},   //  4
  {5000./*rpm*/, 21./*deg BTDC*/},   //  5
  {6000./*rpm*/, 21./*deg BTDC*/},   //  6
  {7000./*rpm*/, 21.5/*deg BTDC*/},  //  7
  {8000./*rpm*/, 16./*deg BTDC*/},   //  8
  {9000./*rpm*/, 14./*deg BTDC*/},   //  9
  {10000./*rpm*/, 13./*deg BTDC*/},  //  10
  {11000./*rpm*/, 9./*deg BTDC*/},   //  11
  {12000./*rpm*/, 9./*deg BTDC*/},   //  12
  {13000./*rpm*/, 9./*deg BTDC*/},   //  13
  {14000./*rpm*/, 9./*deg BTDC*/}    //  14. Just an arbitrary number to avoid problems in for loop when checking rpm to set ignition timing.
};

////////////////////
//  Setup Timers  //
////////////////////

hw_timer_t * Timer0 = NULL;    //  Delay base timing
//hw_timer_t * transistorDelay = NULL; //  Delay before switching transistor to LOW to turn on PWM on UC3843 ic
//hw_timer_t * transistorLOW = NULL;   //  Transistor
//hw_timer_t * SCRon = NULL;           //  Transistor

portMUX_TYPE timerMux  = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE baseAdvDelayMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE transistorDelayMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE transistorLOWMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE SCRonMUX = portMUX_INITIALIZER_UNLOCKED;

void IRAM_ATTR RPM_Interrupt(void);

void IRAM_ATTR onBaseAdvDelay(void);

void IRAM_ATTR onTransistorDelay(void);

void IRAM_ATTR onTransistorLOW(void);

void IRAM_ATTR onSCRon(void);

void setup() {

  Serial.begin(250000);

  digitalWrite(transistorPin, HIGH);
  digitalWrite(SCRPin, LOW);
  digitalWrite(LEDpin, LOW);

  pinMode(transistorPin, OUTPUT);
  pinMode(SCRPin, OUTPUT);
  pinMode(LEDpin, OUTPUT);
  pinMode(flywheelPulse, INPUT);

  attachInterrupt(digitalPinToInterrupt(flywheelPulse), RPM_Interrupt, RISING);  // Enable interruption pin 36 when going from LOW to HIGH.

  ////////////////////
  //  Setup Timers  //
  ////////////////////

  Timer0 = timerBegin(0, 2, true);                            //  baseAdvDelay, 40MHz, count up
  //  transistorDelay = timerBegin(1, 2, true);                         //  transistorDelay, 40MHz, count up
  //  transistorLOW = timerBegin(2, 2, true);                           //  transistorLOW, 40MHz, count up
  //  SCRon = timerBegin(3, 2, true);                                   //  SCRon, 40MHz, count up

  timerAttachInterrupt(Timer0, &onBaseAdvDelay, true);        //  baseAdvDelay, function "onBaseAdvDelay", interrupt on edge
  //  timerAttachInterrupt(transistorDelay, &onTransistorDelay, true);  //  transistorDelay, function "onTransistorDelay", interrupt on edge
  //  timerAttachInterrupt(transistorLOW, &onTransistorLOW, true);      //  transistorLOW, function "onTransistorLOW", interrupt on edge
  //  timerAttachInterrupt(SCRon, &onSCRon, true);                      //  SCRon, function "onSCRon", interrupt on edge

}

void loop() {

  if (flywheelPulseFlag) {

    if (timeOfInterrupt - oldTimeOfInterrupt >= 4500) {

      unsigned long startMicros = ESP.getCycleCount();

      portENTER_CRITICAL_ISR(&timerMux );
      timeBetweenflywheelPulses = timeOfInterrupt - oldTimeOfInterrupt;
      oldTimeOfInterrupt = timeOfInterrupt;

      freq = 100000000000 / timeBetweenflywheelPulses;
      rpm = freq * 60 / 100000;

      for (int i = 13; i >= 0; i--) {
        if (rpm >= rpmArray[i][0]) {
          ignAdv = rpmArray[i][1] + ((rpmArray[i + 1][1] - rpmArray[i][1]) / (rpmArray[i + 1][0] - rpmArray[i][0])) * (rpm - rpmArray[i][0]); // IGN=IGNL+((IGNH-IGNL)/(RPMH-RPML))*(RPM-RPML)
          break;
        }
      }

      ticksPerRev = 4000000000000 / freq; //  40,000,000 ticks per second
      ticksPerDeg = ticksPerRev * 1000 / 360000;
      ticksBaseAdv = ticksPerDeg * baseAdv;
      ticksToSCR = ticksPerRev - (ignAdv * ticksPerDeg) - (ignErr * ticksPerDeg);
      ticksTransistorDelay = ticksToSCR - ticksTransistorLow;

      portEXIT_CRITICAL_ISR(&timerMux );

      flywheelPulseFlag = false;

      unsigned long stopMicros = ESP.getCycleCount();
      loopTime = stopMicros - startMicros;
    }

    if (millis() - lastTimePrinted >= 50) {
      Serial.println("------------------");
      Serial.println(freq);
      Serial.println(rpm);
      Serial.println(ticksPerRev);
      Serial.println(ticksPerDeg);
      Serial.println(ticksBaseAdv);
      Serial.println(ticksToSCR);
      Serial.println(ticksTransistorDelay);
      Serial.println(loopTime);
      Serial.println(ignAdv);
      lastTimePrinted = millis();
    }
  }

}

/////////////////////////////
// RPM Interrupt on pin 36 //
/////////////////////////////

void IRAM_ATTR RPM_Interrupt() { // The interrupt runs this to calculate the period between flywheelPulses

  portENTER_CRITICAL_ISR(&timerMux );

  timeOfInterrupt = esp_timer_get_time();

  timerAttachInterrupt(Timer0, &onBaseAdvDelay, true);        //  baseAdvDelay, function "onBaseAdvDelay", interrupt on edge
  timerAlarmWrite(Timer0, ticksBaseAdv, true);
  timerWrite(Timer0, 0);

  //  if (firstLoop) {
  timerAlarmEnable(Timer0);
  //    firstLoop = false;
  //  }

  flywheelPulseFlag = true;

  portEXIT_CRITICAL_ISR(&timerMux );
}

/////////////////////////////
// Delay for base advance  //
/////////////////////////////

void IRAM_ATTR onBaseAdvDelay() {

  portENTER_CRITICAL_ISR(&timerMux );

  timerStop(Timer0);
  timerAttachInterrupt(Timer0, &onTransistorDelay, true);        //  baseAdvDelay, function "onTransistorDelay", interrupt on edge
  timerAlarmWrite(Timer0, ticksTransistorDelay, true);
  timerWrite(Timer0, 0);
  timerAlarmEnable(Timer0);
  timerStart(Timer0);

  portEXIT_CRITICAL_ISR(&timerMux );
}

////////////////////////////////////////////
// Delay before switching transistor LOW  //
////////////////////////////////////////////

void IRAM_ATTR onTransistorDelay() {

  portENTER_CRITICAL_ISR(&timerMux );

  GPIO.out_w1tc = (1 << 25);  //  Set transistor pin LOW to turn on PWM for 2ms

  timerStop(Timer0);
  timerAttachInterrupt(Timer0, &onTransistorLOW, true);        //  baseAdvDelay, function "onTransistorLOW", interrupt on edge
  timerAlarmWrite(Timer0, ticksTransistorLow, true);
  timerWrite(Timer0, 0);
  timerAlarmEnable(Timer0);
  timerStart(Timer0);

  portEXIT_CRITICAL_ISR(&timerMux );
}

/////////////////////////////////////////////////////
// Delay before switching transistor back to HIGH  //
// Then switch on SCR                              //
/////////////////////////////////////////////////////

void IRAM_ATTR onTransistorLOW() {

  portENTER_CRITICAL_ISR(&timerMux );

  GPIO.out_w1ts = (1 << 25);  //  Set transistor pin HIGH to turn off PWM

  timerStop(Timer0);
  timerAttachInterrupt(Timer0, &onSCRon, true);        //  baseAdvDelay, function "onSCRon", interrupt on edge
  timerAlarmWrite(Timer0, ticksSCRHIGH, false);
  timerWrite(Timer0, 0);
  timerAlarmEnable(Timer0);
  timerStart(Timer0);

  GPIO.out_w1ts = (1 << 23);  //  Turn on SCR to discharge capacitor and fire spark plug

  portEXIT_CRITICAL_ISR(&timerMux );
}

/////////////////////////////////////////////
// Delay before switching SCR back to LOW  //
/////////////////////////////////////////////

void IRAM_ATTR onSCRon() {

  portENTER_CRITICAL_ISR(&timerMux );

  GPIO.out_w1tc = (1 << 23);  //  Turn off SCR

  //  timerAlarmDisable(baseAdvDelay);

  portEXIT_CRITICAL_ISR(&timerMux );
}

Can anyone offer any insight into what I'm doing wrong? Lots probably.

I was thinking that maybe another option is to let the timer run and keep attaching a new interrupts at the present interrupt, but haven't tried that yet as I'm getting stressed and confused :slight_smile:

Any help or tips very much appreciated.

Cheers,

Matt

The code is working perfectly,

Starting like this makes people think it's an open question just to give hints on how to do it.

Few people will scroll down to read this part:

but won't run below approx 1300rpm (22Hz), or above approx 10500rpm (175Hz). At the low end it just stops working. At the top it seems to lose synch, or something.

I'm no expert on using the timers (unless you just want to flash an LED of course!), and I'm pretty sure that I've got it wrong with all the start, stops and enables etc. As much as I've googled or looked through the Arduino/ESP32 core I can't figure it out. I've tried several combos but to no avail.

https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/timer.html

@rtek1000

Thanks for your comments.

I think that I've figured it out...eventually.

I see now what I was doing.

Basically, using one timer will not work unless the variable for ignAdv is great than the variable for baseAdv. If it is less then the external interrupt fires before the timer sequence has run it's course. My bad!

I had coded a single timer to just run through a bunch of interrupts. Each interrupt attaching a new interrupt and so on, and this worked fine. So couldn't get my head around why it wasn't working for real. Then I drew it out on some paper, with some numbers and the reason was obvious. Oh well.

Anyway, changed my rpmArray from:

float rpmArray [row] [col] = {
  {100./*rpm*/, 4./*deg BTDC*/},     //  0
  {1000./*rpm*/, 4./*deg BTDC*/},    //  1
  {2000./*rpm*/, 21./*deg BTDC*/},   //  2
  {3000./*rpm*/, 21./*deg BTDC*/},   //  3
  {4000./*rpm*/, 21./*deg BTDC*/},   //  4
  {5000./*rpm*/, 21./*deg BTDC*/},   //  5
  {6000./*rpm*/, 21./*deg BTDC*/},   //  6
  {7000./*rpm*/, 21.5/*deg BTDC*/},  //  7
  {8000./*rpm*/, 16./*deg BTDC*/},   //  8
  {9000./*rpm*/, 14./*deg BTDC*/},   //  9
  {10000./*rpm*/, 13./*deg BTDC*/},  //  10
  {11000./*rpm*/, 9./*deg BTDC*/},   //  11
  {12000./*rpm*/, 9./*deg BTDC*/},   //  12
  {13000./*rpm*/, 9./*deg BTDC*/},   //  13
  {14000./*rpm*/, 9./*deg BTDC*/}    //  14. Just an arbitrary number to avoid problems in for loop when checking rpm to set ignition timing.
};

To:

float rpmArray [row] [col] = {
  {0./*rpm*/, 14./*deg BTDC*/},     //  0
  {1000./*rpm*/, 14./*deg BTDC*/},    //  1
  {2000./*rpm*/, 21./*deg BTDC*/},   //  2
  {3000./*rpm*/, 21./*deg BTDC*/},   //  3
  {4000./*rpm*/, 21./*deg BTDC*/},   //  4
  {5000./*rpm*/, 21./*deg BTDC*/},   //  5
  {6000./*rpm*/, 21./*deg BTDC*/},   //  6
  {7000./*rpm*/, 21.5/*deg BTDC*/},  //  7
  {8000./*rpm*/, 16./*deg BTDC*/},   //  8
  {9000./*rpm*/, 14./*deg BTDC*/},   //  9
  {10000./*rpm*/, 13./*deg BTDC*/},  //  10
  {11000./*rpm*/, 19./*deg BTDC*/},   //  11
  {12000./*rpm*/, 19./*deg BTDC*/},   //  12
  {13000./*rpm*/, 19./*deg BTDC*/},   //  13
  {14000./*rpm*/, 19./*deg BTDC*/}    //  14. Just an arbitrary number to avoid problems in for loop when checking rpm to set ignition timing.
};

And it now runs. though the full range.

But, I can't have that, so my next plan will have to be to use two timers (better than using all four), and use one just to get through the baseAdv delay, and then a second timer to run through the next sequence of interrupts.

I think that'll work.

I'll give it a try later and report back.

Cheers,

Matt