Coding my own aftermarket ECU [beta available]

User avatar
antus
Site Admin
Posts: 9002
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 »

C compiles to machine language (usually, in the context of MCUs) so you get a higher level language without an abstraction layer in an OS.
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: 243
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 don't know if you'd find anything interesting in this. A 3rd party variation of Mbed OS.
https://github.com/earlephilhower/ardui ... res/rp2040
Tried compiling with that one instead of Arduino's official version. Output is even worse with 3rd party version.
Don't stress specific units.
hjtrbo
Posts: 227
Joined: Tue Jul 06, 2021 6:57 pm
cars: VF2 R8 LSA
FG XR6T
HJ Ute w/RB25DET

Re: Coding my own aftermarket ECU

Post by hjtrbo »

I've spent some time with mcu's. The more you can offload to dedicated hardware modules and on-pcb pre-input signal conditioning the faster you'll get through a program cycle. Getting your rpm signal would be well worth moving to a hardware module for example. Additionally, first order and kalman filtering in software are your best friends. There are high speed algo's in the public domain for use. Avoid floats for super high speed stuff, keep it to scaled 16 or 32 bit values. That'll allow you to use shift left or right for high speed multiply and divide. And lastly, set up a program timer to run low, med and high speed tasks for example. Think poor mans scheduler, the high speed task interrupts the medium speed for example so as to remain deterministic. C is a great language choice for this sort of thing.
User avatar
AngelMarc
Posts: 243
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 »

That's why Crank decode is in the only interrupt.
Should be just fixed point/ integer at this point. Not enough improvement that way, but some.

If I could make enough sense of

Code: Select all

PICO_FLOAT_IN_RAM=1
To put it in the right place, I'd try it.
https://forums.raspberrypi.com/viewtopic.php?t=337719

I hope to mess with FPGAs at some point.
Don't stress specific units.
User avatar
AngelMarc
Posts: 243
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 »

Also, somehow my interpolated output is able to be zero, no pulse, which shouldn't be possible.
Don't stress specific units.
hjtrbo
Posts: 227
Joined: Tue Jul 06, 2021 6:57 pm
cars: VF2 R8 LSA
FG XR6T
HJ Ute w/RB25DET

Re: Coding my own aftermarket ECU

Post by hjtrbo »

These guys are open source. https://wiki.speeduino.com/en/decoders
User avatar
AngelMarc
Posts: 243
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 »

Well aware of that excessively complex option.
Don't stress specific units.
User avatar
AngelMarc
Posts: 243
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 matter what (don't say self tuning), you have to edit a couple tables to tune EFI. I want to limit this to just that, for people who'd rather install a specific trigger wheel than deal with extra software. There will only be one "supported" option, GM 24x, LS "smart" coils, 12595966 crank sensor. Those 4 giant tables, and a 1D array for IAT, maybe coolant temp. External inputs for wideband fueling correction and torque reduction from a separate controller are planned for final features to add.
No "real world" reference to KPA/PSI, just install whatever BAR MAP sensor you want and start tuning the primary tables.
Datalogging will allows be a separate module running nearly identical crank decode logic, it just won't decode continuously.
Last edited by AngelMarc on Thu Jun 12, 2025 9:39 pm, edited 1 time in total.
Don't stress specific units.
hjtrbo
Posts: 227
Joined: Tue Jul 06, 2021 6:57 pm
cars: VF2 R8 LSA
FG XR6T
HJ Ute w/RB25DET

Re: Coding my own aftermarket ECU

Post by hjtrbo »

Maybe complicated, but it works and is fast. Nothing wrong with using proven field tested code as a springboard.

Consider IAC, accel enrichment & cold start enrichment tables too.
hjtrbo
Posts: 227
Joined: Tue Jul 06, 2021 6:57 pm
cars: VF2 R8 LSA
FG XR6T
HJ Ute w/RB25DET

Re: Coding my own aftermarket ECU

Post by hjtrbo »

Untested, no lookups and no floats. For a common 36-1 trigger wheel

Code: Select all

#include <stdio.h>
#include <stdint.h>

#define NUM_TEETH 36
#define MISSING_TOOTH_GAP_MULTIPLIER 3
#define TOOTH_DEGREES_FIXED 40          // 10° * 4
#define ANGLE_SCALE 4 // Resolution is 0.25 crank degrees
#define MAX_ANGLE (360 * ANGLE_SCALE)

uint32_t last_tooth_time = 0;
uint32_t delta_time = 0;

uint16_t last_known_angle_qdeg = 0;      // in 0.25° units
uint16_t angle_per_us_qdeg = 0;          // angle per µs in 0.25° units
uint8_t synced = 0;
uint8_t tooth_number = 0;

// High speed timer module ISR
void on_tooth_detected(uint32_t timestamp_us) {
    static uint32_t last_delta = 0;
    uint32_t now = timestamp_us;
    delta_time = now - last_tooth_time;
    last_tooth_time = now;

    if (!synced) {
        if (last_delta > 0 && delta_time > (last_delta * MISSING_TOOTH_GAP_MULTIPLIER)) {
            synced = 1;
            tooth_number = 0;
            last_known_angle_qdeg = 0;
            printf(">> Sync achieved\n");
            return;
        }
        last_delta = delta_time;
        return;
    }

    // Calculate angle per microsecond in fixed-point (0.25° units)
    angle_per_us_qdeg = TOOTH_DEGREES_FIXED / delta_time;

    last_known_angle_qdeg = (tooth_number * TOOTH_DEGREES_FIXED);
    if (last_known_angle_qdeg >= MAX_ANGLE)
        last_known_angle_qdeg -= MAX_ANGLE;

    printf("Tooth %d: base angle = %u (x0.25°)\n", tooth_number, last_known_angle_qdeg);

    tooth_number = (tooth_number + 1) % NUM_TEETH;
}

uint16_t get_current_crank_angle_qdeg(uint32_t current_time_us) {
    uint32_t time_since_last = current_time_us - last_tooth_time;
    uint32_t interpolated_angle = last_known_angle_qdeg + (time_since_last * angle_per_us_qdeg);

    if (interpolated_angle >= MAX_ANGLE)
        interpolated_angle -= MAX_ANGLE;

    return (uint16_t)interpolated_angle;
}

Post Reply