Coding my own aftermarket 24x ECU [beta available]

User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Think I'll just do TDC for pattern match. Allows more error in timing with rapid RPM change, but seriously simplifies setup instructions, and the later planned cam sensor logic.
Also allows the most max RPM potential with dwell timing without stopping 0 degree advance from working.
If anybody wants to share their 2 cents on that, I'll consider it.
I'll look into loop code that can dynamicly adjust timing outside after it's initial set time to account for rapid change. Since it's 'if >' it should be tollerant to dynamic updates every 15 degrees.
That's a priority over all previously mentioned features.
CAM TIMING DIAGRAM BY CAMSHAFT ANGLE.jpg
CAM TIMING DIAGRAM BY CAMSHAFT ANGLE.jpg (196.45 KiB) Viewed 250 times
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Shit, I think the AI generated

Code: Select all

 currentOnDelay11 = Scaled;
    currentOffDelay11 = Dwell;

    currentOnDelay4 = ScaledOffset;
    currentOffDelay4 = Scaled3;
  }

  // --- Check for new pulse triggers from crank handler ---
  if (triggerPulse11) {
    triggerPulse11 = false;
    pulseStartTime11 = micros();
    pulseActive11 = true;
  }

  if (triggerPulse4) {
    triggerPulse4 = false;
    pulseStartTime4 = micros();
    pulseActive4 = true;
  }

  // --- Pulse timing for pin 11 (pattern1) ---
  if (pulseActive11) {
    unsigned long now = micros();
    unsigned long elapsed = now - pulseStartTime11;

    if (elapsed < currentOnDelay11) {
      digitalWrite(11, LOW);
    } else if (elapsed < currentOnDelay11 + currentOffDelay11) {
      digitalWrite(11, HIGH);
    } else {
      digitalWrite(11, LOW);
      pulseActive11 = false;
    }
  }

  // --- Pulse timing for pin 4 (pattern0) ---
  if (pulseActive4) {
    unsigned long now = micros();
    unsigned long elapsed = now - pulseStartTime4;

    if (elapsed < currentOnDelay4) {
      digitalWrite(4, LOW);
    } else if (elapsed < currentOnDelay4 + currentOffDelay4) {
      digitalWrite(4, HIGH);
    } else {
      digitalWrite(4, LOW);
      pulseActive4 = false;
    }
  }
}
already dynamically updates as I described. Can't remember if that's part of what I described, or a happy coincidence.
I definitely emphasized 'dynamic' a bit. Took 3 or 4 tries to get results that weren't shit.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

For 8 cylinder support, going to use pins 0-15. Since that's one entire side of the Pico.
So crank sensor is moving to pin 22. Closest digital-only pin to analog pins.
One side of board for outputs, one side for inputs.
Makes board prototyping easier. Make 2 halves.
Capture.PNG
Turns out it's 1 pin short of doing 12 cylinder fuel and spark, and that's before even doing external correction inputs.
Could use 2 and do a little reconfiguring though.
Found and removed some unused variables left from previous prototyping iterations.
Will post an update when I add cylinder count and think I have the corresponding TDCs for pattern matches.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Guess AI is making me learn a little about "helper functions" today.
It did offer to spell them all out individually. But this is smarter. I break all of them or break none.

Code: Select all

void handleSparkPulse(uint8_t pin, unsigned long startTime, bool &activeFlag) {
  unsigned long now = micros();
  unsigned long elapsed = now - startTime;

  if (elapsed < currentOnDelaySpark) {
    digitalWrite(pin, LOW);
  } else if (elapsed < currentOnDelaySpark + currentOffDelaySpark) {
    digitalWrite(pin, HIGH);
  } else {
    digitalWrite(pin, LOW);
    activeFlag = false;
  }
}

void handleFuelPulse(uint8_t pin, unsigned long startTime, bool &activeFlag) {
  unsigned long now = micros();
  unsigned long elapsed = now - startTime;

  if (elapsed < currentOnDelayFuel) {
    digitalWrite(pin, LOW);
  } else if (elapsed < currentOnDelayFuel + currentOffDelayFuel) {
    digitalWrite(pin, HIGH);
  } else {
    digitalWrite(pin, LOW);
    activeFlag = false;
  }
}

Code: Select all

    // --- Update delay values from tables ---
    currentOnDelaySpark = Scaled;
    currentOffDelaySpark = Dwell;

    currentOnDelayFuel = ScaledOffset;
    currentOffDelayFuel = Scaled3;
  }
  // --- Check for new pulse triggers from crank handler ---
   // --- Spark pulse triggers ---
  if (triggerPulseSpark0) { triggerPulseSpark0 = false; pulseStartTimeSpark0 = micros(); pulseActiveSpark0 = true; }
  if (triggerPulseSpark1) { triggerPulseSpark1 = false; pulseStartTimeSpark1 = micros(); pulseActiveSpark1 = true; }
  if (triggerPulseSpark2) { triggerPulseSpark2 = false; pulseStartTimeSpark2 = micros(); pulseActiveSpark2 = true; }
  if (triggerPulseSpark3) { triggerPulseSpark3 = false; pulseStartTimeSpark3 = micros(); pulseActiveSpark3 = true; }
  if (triggerPulseSpark4) { triggerPulseSpark4 = false; pulseStartTimeSpark4 = micros(); pulseActiveSpark4 = true; }
  if (triggerPulseSpark5) { triggerPulseSpark5 = false; pulseStartTimeSpark5 = micros(); pulseActiveSpark5 = true; }
  if (triggerPulseSpark6) { triggerPulseSpark6 = false; pulseStartTimeSpark6 = micros(); pulseActiveSpark6 = true; }
  if (triggerPulseSpark7) { triggerPulseSpark7 = false; pulseStartTimeSpark7 = micros(); pulseActiveSpark7 = true; }

  // --- Fuel pulse triggers ---
  if (triggerPulseFuel0) { triggerPulseFuel0 = false; pulseStartTimeFuel0 = micros(); pulseActiveFuel0 = true; }
  if (triggerPulseFuel1) { triggerPulseFuel1 = false; pulseStartTimeFuel1 = micros(); pulseActiveFuel1 = true; }
  if (triggerPulseFuel2) { triggerPulseFuel2 = false; pulseStartTimeFuel2 = micros(); pulseActiveFuel2 = true; }
  if (triggerPulseFuel3) { triggerPulseFuel3 = false; pulseStartTimeFuel3 = micros(); pulseActiveFuel3 = true; }
  if (triggerPulseFuel4) { triggerPulseFuel4 = false; pulseStartTimeFuel4 = micros(); pulseActiveFuel4 = true; }
  if (triggerPulseFuel5) { triggerPulseFuel5 = false; pulseStartTimeFuel5 = micros(); pulseActiveFuel5 = true; }
  if (triggerPulseFuel6) { triggerPulseFuel6 = false; pulseStartTimeFuel6 = micros(); pulseActiveFuel6 = true; }
  if (triggerPulseFuel7) { triggerPulseFuel7 = false; pulseStartTimeFuel7 = micros(); pulseActiveFuel7 = true; }

  // --- Spark pulse timing (Pins 0 to 7) ---
  if (pulseActiveSpark0) handleSparkPulse(0, pulseStartTimeSpark0, pulseActiveSpark0);
  if (pulseActiveSpark1) handleSparkPulse(1, pulseStartTimeSpark1, pulseActiveSpark1);
  if (pulseActiveSpark2) handleSparkPulse(2, pulseStartTimeSpark2, pulseActiveSpark2);
  if (pulseActiveSpark3) handleSparkPulse(3, pulseStartTimeSpark3, pulseActiveSpark3);
  if (pulseActiveSpark4) handleSparkPulse(4, pulseStartTimeSpark4, pulseActiveSpark4);
  if (pulseActiveSpark5) handleSparkPulse(5, pulseStartTimeSpark5, pulseActiveSpark5);
  if (pulseActiveSpark6) handleSparkPulse(6, pulseStartTimeSpark6, pulseActiveSpark6);
  if (pulseActiveSpark7) handleSparkPulse(7, pulseStartTimeSpark7, pulseActiveSpark7);

  // --- Fuel pulse timing (Pins 8 to 15) ---
  if (pulseActiveFuel0) handleFuelPulse(8, pulseStartTimeFuel0, pulseActiveFuel0);
  if (pulseActiveFuel1) handleFuelPulse(9, pulseStartTimeFuel1, pulseActiveFuel1);
  if (pulseActiveFuel2) handleFuelPulse(10, pulseStartTimeFuel2, pulseActiveFuel2);
  if (pulseActiveFuel3) handleFuelPulse(11, pulseStartTimeFuel3, pulseActiveFuel3);
  if (pulseActiveFuel4) handleFuelPulse(12, pulseStartTimeFuel4, pulseActiveFuel4);
  if (pulseActiveFuel5) handleFuelPulse(13, pulseStartTimeFuel5, pulseActiveFuel5);
  if (pulseActiveFuel6) handleFuelPulse(14, pulseStartTimeFuel6, pulseActiveFuel6);
  if (pulseActiveFuel7) handleFuelPulse(15, pulseStartTimeFuel7, pulseActiveFuel7);
}
Seems to work.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Capture.PNG
Capture.PNG (16.36 KiB) Viewed 122 times
"almost"
duh.PNG
duh.PNG (17.08 KiB) Viewed 122 times
aery.PNG
aery.PNG (22.92 KiB) Viewed 122 times
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Well, extra noise on the fuel output, but pasted in 16 channels of outputs now.
Capture.PNG
Last edited by AngelMarc on Thu Jun 19, 2025 8:37 pm, edited 1 time in total.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Full sequential (but still sorting potential bugs).
Sequential.PNG
Noise zoom
Capture.PNG
Capture.PNG (69.25 KiB) Viewed 108 times
Last edited by AngelMarc on Thu Jun 19, 2025 8:38 pm, edited 1 time in total.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Minimal bug testing.
Full sequential V8.

Code: Select all

#include "TuneArrays.h" 

uint8_t RPMIndex = 0;
uint8_t adcIndex = 0;
unsigned long currentOnDelaySpark = 0;
unsigned long currentOffDelaySpark = 0;
unsigned long currentOnDelayFuel = 0;
unsigned long currentOffDelayFuel = 0;

// Spark patterns (Pin 0 to 7)
const uint8_t patternSpark0 = 0b01111101;
bool pulseActiveSpark0 = false;
bool triggerPulseSpark0 = false;
unsigned long pulseStartTimeSpark0 = 0;

const uint8_t patternSpark1 = 0b01111101;
bool pulseActiveSpark1 = false;
bool triggerPulseSpark1 = false;
unsigned long pulseStartTimeSpark1 = 0;

const uint8_t patternSpark2 = 0b01110011;
bool pulseActiveSpark2 = false;
bool triggerPulseSpark2 = false;
unsigned long pulseStartTimeSpark2 = 0;

const uint8_t patternSpark3 = 0b01110011;
bool pulseActiveSpark3 = false;
bool triggerPulseSpark3 = false;
unsigned long pulseStartTimeSpark3 = 0;

const uint8_t patternSpark4 = 0b11000101;
bool pulseActiveSpark4 = false;
bool triggerPulseSpark4 = false;
unsigned long pulseStartTimeSpark4 = 0;

const uint8_t patternSpark5 = 0b11000101;
bool pulseActiveSpark5 = false;
bool triggerPulseSpark5 = false;
unsigned long pulseStartTimeSpark5 = 0;

const uint8_t patternSpark6 = 0b01000001;
bool pulseActiveSpark6 = false;
bool triggerPulseSpark6 = false;
unsigned long pulseStartTimeSpark6 = 0;

const uint8_t patternSpark7 = 0b01000001;
bool pulseActiveSpark7 = false;
bool triggerPulseSpark7 = false;
unsigned long pulseStartTimeSpark7 = 0;

// Fuel patterns (Pin 8 to 15)
const uint8_t patternFuel0 = 0b01111101;
bool pulseActiveFuel0 = false;
bool triggerPulseFuel0 = false;
unsigned long pulseStartTimeFuel0 = 0;

const uint8_t patternFuel1 = 0b01111101;
bool pulseActiveFuel1 = false;
bool triggerPulseFuel1 = false;
unsigned long pulseStartTimeFuel1 = 0;

const uint8_t patternFuel2 = 0b01110011;
bool pulseActiveFuel2 = false;
bool triggerPulseFuel2 = false;
unsigned long pulseStartTimeFuel2 = 0;

const uint8_t patternFuel3 = 0b01110011;
bool pulseActiveFuel3 = false;
bool triggerPulseFuel3 = false;
unsigned long pulseStartTimeFuel3 = 0;

const uint8_t patternFuel4 = 0b11000101;
bool pulseActiveFuel4 = false;
bool triggerPulseFuel4 = false;
unsigned long pulseStartTimeFuel4 = 0;

const uint8_t patternFuel5 = 0b11000101;
bool pulseActiveFuel5 = false;
bool triggerPulseFuel5 = false;
unsigned long pulseStartTimeFuel5 = 0;

const uint8_t patternFuel6 = 0b01000001;
bool pulseActiveFuel6 = false;
bool triggerPulseFuel6 = false;
unsigned long pulseStartTimeFuel6 = 0;

const uint8_t patternFuel7 = 0b01000001;
bool pulseActiveFuel7 = false;
bool triggerPulseFuel7 = false;
unsigned long pulseStartTimeFuel7 = 0;

int readings[24] {4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095};       // Circular buffer
int bufIndex = 0;       // Buffer index
long total = 4095L * 24;  // Set this to the initial sum

int readings2[24];       // Circular buffer
int bufIndex2 = 0;       // Buffer index
long total2 = 0L * 24;  // Set this to the initial sum

int readings3[24];       // Circular buffer
int bufIndex3 = 0;       // Buffer index
unsigned long total3 = 0UL * 24;  // Set this to the initial sum

unsigned long lastTime = 0;
unsigned long pulseHighTime = 0;
unsigned long pulseLowTime = 0;
uint8_t signalHistory = 0;
bool newSample = false;

void handleCrankSignal() {
  unsigned long currentTime = micros();
  if (digitalRead(22) == HIGH) {
    pulseHighTime = currentTime - lastTime;
  } else {
    pulseLowTime = currentTime - lastTime;
    uint8_t bit = (pulseHighTime > pulseLowTime) ? 0 : 1;
    signalHistory = ((signalHistory << 1) | bit) & 0xFF;

    newSample = true; // flag to loop()
  }
  lastTime = currentTime;
}

void handleSparkPulse(uint8_t pin, unsigned long startTime, bool &activeFlag) {
  unsigned long now = micros();
  unsigned long elapsed = now - startTime;

  if (elapsed < currentOnDelaySpark) {
    digitalWrite(pin, LOW);
  } else if (elapsed < currentOnDelaySpark + currentOffDelaySpark) {
    digitalWrite(pin, HIGH);
  } else {
    digitalWrite(pin, LOW);
    activeFlag = false;
  }
}

void handleFuelPulse(uint8_t pin, unsigned long startTime, bool &activeFlag) {
  unsigned long now = micros();
  unsigned long elapsed = now - startTime;

  if (elapsed < currentOnDelayFuel) {
    digitalWrite(pin, LOW);
  } else if (elapsed < currentOnDelayFuel + currentOffDelayFuel) {
    digitalWrite(pin, HIGH);
  } else {
    digitalWrite(pin, LOW);
    activeFlag = false;
  }
}


void setup() {
  analogReadResolution(12);
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(22, INPUT);
  pinMode(21, INPUT);
  attachInterrupt(digitalPinToInterrupt(22), handleCrankSignal, CHANGE);
}

void loop() {
  if (newSample) {
    newSample = false;

        if (digitalRead(21) == HIGH){
    // Spark patterns
    if (signalHistory == patternSpark0) triggerPulseSpark0 = true;
    if (signalHistory == patternFuel0) triggerPulseFuel0 = true;
    if (signalHistory == patternSpark2) triggerPulseSpark2 = true;
    if (signalHistory == patternFuel2) triggerPulseFuel2 = true;
    if (signalHistory == patternSpark4) triggerPulseSpark4 = true;
    if (signalHistory == patternFuel4) triggerPulseFuel4 = true;
    if (signalHistory == patternSpark6) triggerPulseSpark6 = true;
    if (signalHistory == patternFuel6) triggerPulseFuel6 = true;
    } 
        if (digitalRead(21) == LOW) {
    if (signalHistory == patternSpark1) triggerPulseSpark1 = true;
    if (signalHistory == patternFuel1) triggerPulseFuel1 = true;
    if (signalHistory == patternSpark3) triggerPulseSpark3 = true;
    if (signalHistory == patternFuel3) triggerPulseFuel3 = true;
    if (signalHistory == patternSpark5) triggerPulseSpark5 = true;
    if (signalHistory == patternFuel5) triggerPulseFuel5 = true;
    if (signalHistory == patternSpark7) triggerPulseSpark7 = true;
    if (signalHistory == patternFuel7) triggerPulseFuel7 = true;
    }


    unsigned long RPMTime = pulseHighTime + pulseLowTime;//Calculate RPMtime (µs per 15 degrees)
    total2 -= readings2[bufIndex2];
    unsigned long newVal2 = RPMTime;
    readings2[bufIndex2] = newVal2;
    total2 += newVal2;
    bufIndex2 = (bufIndex2 + 1) % 24;
    unsigned long RPMTimeavg = total2 / 24;

    // FIXED: Rev limiter + low-RPM cutoff
    unsigned long Dwell = 4000UL;
    if (RPMTimeavg < Limiter) {
      Dwell = 0UL;  // spark cut when RPM too high
    } else if (RPMTimeavg > 125000UL) {
     Dwell = 0UL;  // below 20 RPM, also cut spark
    }

    RPMTimeavg = constrain(RPMTimeavg, 261UL, 3000000UL);  // 9600 RPM to 20 RPM. Always round the decimal up, even if it's 0.0001. Not overflowing arrays is important
    // --- ADC rolling average (12-bit to 7-bit scale) ---
    total -= readings[bufIndex];
    int newVal = analogRead(A0);
    readings[bufIndex] = newVal;
    total += newVal;
    bufIndex = (bufIndex + 1) % 24;
    int avg12bit = total / 24;
    adcIndex = avg12bit >> 8; //Discarding LSBs to match array resolution
    uint32_t IndexFull = ((uint64_t)2500000 << 8) / ((uint32_t)RPMTimeavg * 600);
    uint Predicted360Time = RPMTimeavg * 48;//48 for full sequential, 720 degrees
    uint16_t IndexWhole = IndexFull >> 8;
    uint8_t Frac = IndexFull & 0xFF;
    int Frac2 = avg12bit & 0x1F;

    int TerpA = Dstart[IndexWhole][adcIndex]; 
    int TerpB = Dstart[IndexWhole +1][adcIndex];                                   
    int TerpD = TerpA + (((TerpB - TerpA) * Frac) >> 8);
    int TerpA2 = Dstart[IndexWhole][adcIndex +1];                                           
    int TerpB2 = Dstart[IndexWhole +1][adcIndex +1];
    int TerpD2 = TerpA2 + (((TerpB2 - TerpA2) * Frac) >> 8);
    int TerpD5 = TerpD + (((TerpD2 - TerpD) * Frac2) >> 8);  

    int TerpA3 = Iend[IndexWhole][adcIndex+1];                                           
    int TerpB3 = Iend[IndexWhole +1][adcIndex +1];
    int TerpD3 = TerpA3 + (((TerpB3 - TerpA3) * Frac) >> 8);
    int TerpA4 = Iend[IndexWhole][adcIndex];
    int TerpB4 = Iend[IndexWhole +1][adcIndex];
    int TerpD4 = TerpA4 + (((TerpB4 - TerpA4) * Frac) >> 8);
    int TerpD6 = TerpD4 + (((TerpD3 - TerpD4) * Frac2) >> 8); 

    total3 -= readings3[bufIndex3];
    int newVal3 = analogRead(A1);
    readings3[bufIndex3] = newVal3;
    total3 += newVal3;
    bufIndex3 = (bufIndex3 + 1) % 24;
    int IATavg = total3 / 24;
    int IAT7bit = IATavg >> 5;

    int Temp = IATSpark [IAT7bit];
    int Scaled = ((TerpD5 * Temp) >>8);
    int TrimA = EOIT [IndexWhole];
    int TrimB = EOIT [IndexWhole +1];
    int TrimTerp = TrimA + (((TrimB - TrimA) * Frac) >> 8);

    int Temp2 = IATFuel [IAT7bit];
    int Scaled3 = ((TerpD6 * Temp2) >>8);
    int ScaledOffset = Predicted360Time - (Scaled3 + TrimTerp);
    if (ScaledOffset < 0) ScaledOffset = 0;

    if (RPMTimeavg > 125000UL){ //Fuel off if RPM below minimum = 20RPM
      Scaled3 = 0UL; 
    }



    // --- Update delay values from tables ---
    currentOnDelaySpark = Scaled;
    currentOffDelaySpark = Dwell;

    currentOnDelayFuel = ScaledOffset;
    currentOffDelayFuel = Scaled3;
  }
  // --- Check for new pulse triggers from crank handler ---
   // --- Spark pulse triggers ---
  if (triggerPulseSpark0) { triggerPulseSpark0 = false; pulseStartTimeSpark0 = micros(); pulseActiveSpark0 = true; }
  if (triggerPulseSpark1) { triggerPulseSpark1 = false; pulseStartTimeSpark1 = micros(); pulseActiveSpark1 = true; }
  if (triggerPulseSpark2) { triggerPulseSpark2 = false; pulseStartTimeSpark2 = micros(); pulseActiveSpark2 = true; }
  if (triggerPulseSpark3) { triggerPulseSpark3 = false; pulseStartTimeSpark3 = micros(); pulseActiveSpark3 = true; }
  if (triggerPulseSpark4) { triggerPulseSpark4 = false; pulseStartTimeSpark4 = micros(); pulseActiveSpark4 = true; }
  if (triggerPulseSpark5) { triggerPulseSpark5 = false; pulseStartTimeSpark5 = micros(); pulseActiveSpark5 = true; }
  if (triggerPulseSpark6) { triggerPulseSpark6 = false; pulseStartTimeSpark6 = micros(); pulseActiveSpark6 = true; }
  if (triggerPulseSpark7) { triggerPulseSpark7 = false; pulseStartTimeSpark7 = micros(); pulseActiveSpark7 = true; }

  // --- Fuel pulse triggers ---
  if (triggerPulseFuel0) { triggerPulseFuel0 = false; pulseStartTimeFuel0 = micros(); pulseActiveFuel0 = true; }
  if (triggerPulseFuel1) { triggerPulseFuel1 = false; pulseStartTimeFuel1 = micros(); pulseActiveFuel1 = true; }
  if (triggerPulseFuel2) { triggerPulseFuel2 = false; pulseStartTimeFuel2 = micros(); pulseActiveFuel2 = true; }
  if (triggerPulseFuel3) { triggerPulseFuel3 = false; pulseStartTimeFuel3 = micros(); pulseActiveFuel3 = true; }
  if (triggerPulseFuel4) { triggerPulseFuel4 = false; pulseStartTimeFuel4 = micros(); pulseActiveFuel4 = true; }
  if (triggerPulseFuel5) { triggerPulseFuel5 = false; pulseStartTimeFuel5 = micros(); pulseActiveFuel5 = true; }
  if (triggerPulseFuel6) { triggerPulseFuel6 = false; pulseStartTimeFuel6 = micros(); pulseActiveFuel6 = true; }
  if (triggerPulseFuel7) { triggerPulseFuel7 = false; pulseStartTimeFuel7 = micros(); pulseActiveFuel7 = true; }

  // --- Spark pulse timing (Pins 0 to 7) ---
  if (pulseActiveSpark0) handleSparkPulse(0, pulseStartTimeSpark0, pulseActiveSpark0);
  if (pulseActiveSpark1) handleSparkPulse(1, pulseStartTimeSpark1, pulseActiveSpark1);
  if (pulseActiveSpark2) handleSparkPulse(2, pulseStartTimeSpark2, pulseActiveSpark2);
  if (pulseActiveSpark3) handleSparkPulse(3, pulseStartTimeSpark3, pulseActiveSpark3);
  if (pulseActiveSpark4) handleSparkPulse(4, pulseStartTimeSpark4, pulseActiveSpark4);
  if (pulseActiveSpark5) handleSparkPulse(5, pulseStartTimeSpark5, pulseActiveSpark5);
  if (pulseActiveSpark6) handleSparkPulse(6, pulseStartTimeSpark6, pulseActiveSpark6);
  if (pulseActiveSpark7) handleSparkPulse(7, pulseStartTimeSpark7, pulseActiveSpark7);

  // --- Fuel pulse timing (Pins 8 to 15) ---
  if (pulseActiveFuel0) handleFuelPulse(8, pulseStartTimeFuel0, pulseActiveFuel0);
  if (pulseActiveFuel1) handleFuelPulse(9, pulseStartTimeFuel1, pulseActiveFuel1);
  if (pulseActiveFuel2) handleFuelPulse(10, pulseStartTimeFuel2, pulseActiveFuel2);
  if (pulseActiveFuel3) handleFuelPulse(11, pulseStartTimeFuel3, pulseActiveFuel3);
  if (pulseActiveFuel4) handleFuelPulse(12, pulseStartTimeFuel4, pulseActiveFuel4);
  if (pulseActiveFuel5) handleFuelPulse(13, pulseStartTimeFuel5, pulseActiveFuel5);
  if (pulseActiveFuel6) handleFuelPulse(14, pulseStartTimeFuel6, pulseActiveFuel6);
  if (pulseActiveFuel7) handleFuelPulse(15, pulseStartTimeFuel7, pulseActiveFuel7);
}

TuneArrays.h did not change.
Moving all the pattern matching outside the interrupt seemed to help with noise.
Capture.PNG
Capture.PNG (54.05 KiB) Viewed 94 times
That picture may not convince, but it looks different viewing real-time.
Notice the even spread vs discrete steps in previous pictures. I think most of the noise is a shift vs a pulse width change.
Actaully, I can measure that by triggering oscilloscope off the pulse instead of cam signal.
50.PNG
50.PNG (50.29 KiB) Viewed 94 times
Well, make of that what you will. Looks like exactly 50uS and only 50uS.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Changing inputs, you'll see different amounts of steady state noise. Just so we're clear it varies a bit.
Last edited by AngelMarc on Thu Jun 19, 2025 8:40 pm, edited 1 time in total.
Don't stress specific units.
User avatar
AngelMarc
Posts: 259
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU [beta available]

Post by AngelMarc »

Parsing some of that down a bit.
Just like a distributor V8 only needs the 4x crank signal, this full sequential only needs 4 pattern matches 90 degrees out of phase from each other.
That spreads it all out as needed. Now less to change for V6 support, and less compares happening in code. Unneeded optimization.

Code: Select all

const uint8_t pattern0 = 0b01111101;
const uint8_t pattern1 = 0b01110011;
const uint8_t pattern2 = 0b11000101;
const uint8_t pattern3 = 0b01000001;

Code: Select all

        if (digitalRead(21) == HIGH){
    // Spark patterns
    if (signalHistory == pattern0) {triggerPulseSpark0 = true; triggerPulseFuel0 = true;}
    if (signalHistory == pattern1) {triggerPulseSpark2 = true; triggerPulseFuel2 = true;}
    if (signalHistory == pattern2) {triggerPulseSpark4 = true; triggerPulseFuel4 = true;}
    if (signalHistory == pattern3) {triggerPulseSpark6 = true; triggerPulseFuel6 = true;}
    } 
        if (digitalRead(21) == LOW) {
    if (signalHistory == pattern0) {triggerPulseSpark1 = true; triggerPulseFuel1 = true;}
    if (signalHistory == pattern1) {triggerPulseSpark3 = true; triggerPulseFuel3 = true;}
    if (signalHistory == pattern2) {triggerPulseSpark5 = true; triggerPulseFuel5 = true;}
    if (signalHistory == pattern3) {triggerPulseSpark7 = true; triggerPulseFuel7 = true;}
    }
Don't stress specific units.
Post Reply