Coding my own aftermarket ECU

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

Re: Coding my own aftermarket ECU

Post by AngelMarc »

Looking at this, it might be more accurate to say the range of interpolation "noise" is Frac = 0 to Frac = what the code calculation says it should be.
https://www.youtube.com/watch?v=A4_zRVw8nag
Don't stress specific units.
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

My best guess is, code throws the math at floating point unit, which takes multiple clock cycles to complete the math, but after just one clock cycle, grabs the value before the multiple cycle float math finishes... a race condition. Something like that. Not trying to fix that today. I'll test it later with a short delay (a few microseconds) after lines with float math. See if it get's more consistent. Go for optimizing after that.
EDIT: Didn't work.
Don't stress specific units.
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

Blindly stabbing at it by replacing variables with constants make it seem like its signal generator instability... but I only see a measurable 5 RPM variation.
Don't stress specific units.
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

It's not the interpolation code itself.

Code: Select all

    // --- Calculate RPM timing (µs per event) ---
    unsigned long RPMTime = pulseHighTime + pulseLowTime;
    RPMTime = constrain(RPMTime, 134, 125000);  // 18600 RPM to 20 RPM
    IndexFull = (2500000/(RPMTime*600));
    IndexWhole = (int)IndexFull;
    IndexPlusOne = IndexWhole + 1;
    IndexPlusOne = constrain(IndexPlusOne, 0, 31);
    TerpA = Iend[9][adcIndex];//
    TerpB = Iend[10][adcIndex];//
    Diff = TerpB - TerpA;
    Frac = .5; //IndexFull - IndexWhole;
    TerpC = Diff * Frac;
    TerpD = TerpA + TerpC;
Last edited by AngelMarc on Sat Jun 14, 2025 9:34 pm, edited 1 time in total.
Don't stress specific units.
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

No interpolation, just involving a truncated float. Horrible results

Code: Select all

    unsigned long RPMTime = pulseHighTime + pulseLowTime;
    RPMTime = constrain(RPMTime, 134, 125000);  // 18600 RPM to 20 RPM
    IndexFull = (2500000/(RPMTime*600));
    IndexWhole = (int)IndexFull;
    //IndexPlusOne = IndexWhole + 1;
    //IndexPlusOne = constrain(IndexPlusOne, 0, 31);
    //TerpA = Iend[IndexWhole][adcIndex];//
    //TerpB = Iend[IndexPlusOne][adcIndex];//
    //Diff = TerpB - TerpA;
    //Frac = .5; //IndexFull - IndexWhole;
    //TerpC = Diff * Frac;
    //TerpD = TerpA + TerpC;



    // --- Update delay values from tables ---
    currentOnDelay11 = Dstart[IndexWhole][adcIndex];
    currentOffDelay11 = Dend[IndexWhole][adcIndex];

    currentOnDelay4 = Istart[IndexWhole][adcIndex];
    currentOnDelay4 = Iend[IndexWhole][adcIndex];
    //currentOffDelay4 = TerpD;
https://www.youtube.com/watch?v=Z99Jf7TEtJo

Replacing

Code: Select all

    IndexFull = (2500000/(RPMTime*600));
    IndexWhole = (int)IndexFull;
with

Code: Select all

    IndexWhole = (int)(2500000/(RPMTime*600));
Get's rid of the weird double spikes... somehow.
Don't stress specific units.
User avatar
antus
Site Admin
Posts: 8996
Joined: Sat Feb 28, 2009 8:34 pm
cars: TX Gemini 2L Twincam
TX Gemini SR20 18psi
Datsun 1200 Ute
Subaru Blitzen '06 EZ30 4th gen, 3.0R Spec B
Contact:

Re: Coding my own aftermarket ECU

Post by antus »

It can only be a race condition if you have things running in parallel, eg one in the main thread on the main CPU and code in an interrupt handler that can interrupt the main loop and run something else. Or an MCU with 2 cores and you have code running in both. Since its bare metal there is no operating system to provide threads in software that could create a race condition.
Have you read the FAQ? For lots of information and links to significant threads see here: http://pcmhacking.net/forums/viewtopic.php?f=7&t=1396
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

ChatGPT suggested fixed point code that works better, but still seems awefuly sloppy.

Code: Select all

unsigned long RPMTime = pulseHighTime + pulseLowTime;
RPMTime = constrain(RPMTime, 134, 125000);  // Clamp RPMTime for your RPM range

// Calculate IndexFull in fixed-point 16.16 format
uint32_t IndexFull_fixed = (uint32_t)((2500000ULL << 16) / (RPMTime * 600UL));  // <<16 for fractional bits
uint16_t Frac_fixed = IndexFull_fixed & 0xFFFF;      // Fractional part (0 to 65535)
uint8_t IndexWhole = (IndexFull_fixed >> 16);        // Whole part

IndexWhole = constrain(IndexWhole, 0, 31);
uint8_t IndexPlusOne = constrain(IndexWhole + 1, 0, 31);

// Get lookup values from your table
uint16_t TerpA = Iend[IndexWhole][adcIndex];
uint16_t TerpB = Iend[IndexPlusOne][adcIndex];

// Integer linear interpolation: TerpD = TerpA + ( (TerpB - TerpA) * Frac_fixed ) >> 16;
int16_t Diff = (int16_t)TerpB - (int16_t)TerpA;
uint16_t TerpD = TerpA + ((int32_t)Diff * Frac_fixed >> 16);

// TerpD is your interpolated result, integer, .00 precision (whole number)


I sweep the analog input and see a range that looks just as bad as float.
Last edited by AngelMarc on Thu Jun 12, 2025 6:48 pm, edited 1 time in total.
Don't stress specific units.
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

antus wrote: Thu Jun 12, 2025 6:15 pm It can only be a race condition if you have things running in parallel, eg one in the main thread on the main CPU and code in an interrupt handler that can interrupt the main loop and run something else. Or an MCU with 2 cores and you have code running in both. Since its bare metal there is no operating system to provide threads in software that could create a race condition.
It's my understanding Arduino uses MBed OS as firmware for ARM.
Don't stress specific units.
User avatar
antus
Site Admin
Posts: 8996
Joined: Sat Feb 28, 2009 8:34 pm
cars: TX Gemini 2L Twincam
TX Gemini SR20 18psi
Datsun 1200 Ute
Subaru Blitzen '06 EZ30 4th gen, 3.0R Spec B
Contact:

Re: Coding my own aftermarket ECU

Post by antus »

Hmm thats interesting. I thought arduino was all C on bare metal. A quick google found me this https://forum.arduino.cc/t/is-it-possib ... d/1162356/ and also a few notes that say mbed goes end of life mid 2026. So I think you may have 3 opions. mbed, arduino core or bare metal. Ill have to read some more.
Have you read the FAQ? For lots of information and links to significant threads see here: http://pcmhacking.net/forums/viewtopic.php?f=7&t=1396
User avatar
AngelMarc
Posts: 221
Joined: Sat Apr 08, 2023 9:23 pm
cars: A CB450 running to 8,000RPM with a P59.

Re: Coding my own aftermarket ECU

Post by AngelMarc »

I'm not crazy about the abstraction layers, but I'm certainly not ready to do this in assembly. Extra windows with "live" updates of some of the compilation steps would be cool. Arduino specific library interporated away, then idk plain C, then assembly. Not sure how much it would help me right now; but I'm sure it'd be wonderful for some. Something like Ghidra's function graph, scroll one and it shows you where it is in the other.
Last edited by AngelMarc on Thu Jun 12, 2025 7:44 pm, edited 1 time in total.
Don't stress specific units.
Post Reply