Re: Coding my own aftermarket ECU
Posted: Thu Jun 12, 2025 9:40 pm
Electronic Fuel Injection - Developement & Tuning
https://pcmhacking.net/forums/
Not doing it. Maybe in a separate project, not this one.hjtrbo wrote: ↑Thu Jun 12, 2025 9:35 pm 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; }
Anything LS will be a dog of a thing without them. I've tuned enough to know you'll need at least something to compensate.
Code: Select all
#define CRANK_SENSOR_PIN 2 // Pin connected to crankshaft sensor
#include "TuneArrays.h"
//PICO_FLOAT_IN_RAM=1;
//cmake -DPICO_COPY_TO_RAM=1;
//__attribute__((section(".ramfunc"))) void loop()
enum PinState { WAITING_FOR_ON, WAITING_FOR_OFF };
PinState pinState = WAITING_FOR_ON;
unsigned long previousToggleTime = 0;
uint8_t adcIndex = 0;
bool pulseActive = false;
bool triggerPulse = false;
// Pin 11 (pattern1)
bool pulseActive11 = false;
bool triggerPulse11 = false;
unsigned long pulseStartTime11 = 0;
unsigned long currentOnDelay11 = 1000;
unsigned long currentOffDelay11 = 1000;
// Pin 4 (pattern0)
bool pulseActive4 = false;
bool triggerPulse4 = false;
unsigned long pulseStartTime4 = 0;
unsigned long currentOnDelay4 = 1000;
unsigned long currentOffDelay4 = 1000;
volatile 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
volatile int readings2[24]; // Circular buffer
int bufIndex2 = 0; // Buffer index
long total2 = 0L * 24; // Set this to the initial sum
volatile unsigned long lastTime = 0;
volatile unsigned long pulseHighTime = 0;
volatile unsigned long pulseLowTime = 0;
volatile uint8_t signalHistory = 0;
volatile bool newSample = false;
const uint8_t pattern0 = 0b00001111;
const uint8_t pattern1 = 0b01111101;
uint8_t avg8bit = 0;
void handleCrankSignal() {
unsigned long currentTime = micros();
if (digitalRead(CRANK_SENSOR_PIN) == HIGH) {
pulseHighTime = currentTime - lastTime;
} else {
pulseLowTime = currentTime - lastTime;
uint8_t bit = (pulseHighTime > pulseLowTime) ? 0 : 1;
signalHistory = ((signalHistory << 1) | bit) & 0xFF;
if (signalHistory == pattern1) triggerPulse11 = true;
if (signalHistory == pattern0) triggerPulse4 = true;
//digitalWrite(4, (signalHistory == pattern0) ? HIGH : LOW);
//digitalWrite(11, (signalHistory == pattern1) ? HIGH : LOW);
newSample = true; // Signal to loop() to read ADC
}
lastTime = currentTime;
}
void setup() {
analogReadResolution(12);
pinMode(4, OUTPUT);
pinMode(11, OUTPUT);
digitalWrite(11, LOW);
previousToggleTime = micros();
pinMode(A0, INPUT);
pinMode(CRANK_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(CRANK_SENSOR_PIN), handleCrankSignal, CHANGE);
//Serial.begin(115200);
}
void loop() {
if (newSample) {
newSample = false;
unsigned long RPMTime = pulseHighTime + pulseLowTime;
total2 -= readings2[bufIndex2];
int newVal2 = RPMTime;
readings2[bufIndex2] = newVal2;
total2 += newVal2;
bufIndex2 = (bufIndex2 + 1) % 24;
int RPMTimeavg = total2 / 24;
// --- 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 >> 5) - 1; // () for simple order of opperation, minus 1 for avoiding index wrapping
RPMTimeavg = constrain(RPMTimeavg, 134, 125000);
// Calculate IndexFull in fixed-point 30.2 format (<<2 = 2 fractional bits)
uint32_t IndexFull_fixed = (uint32_t)(((uint32_t)2500000 << 4) / (RPMTimeavg * 600UL));
uint8_t Frac_fixed = IndexFull_fixed & 0x0F;
uint32_t IndexWhole = IndexFull_fixed >> 4;
IndexWhole = constrain(IndexWhole, 0, 31);
uint8_t IndexPlusOne = IndexWhole + 1;
uint32_t TerpA = Iend[20][adcIndex];
uint32_t TerpB = Iend[21][adcIndex];
int32_t Diff = (int32_t)TerpB - (int32_t)TerpA;
uint32_t TerpD = TerpA + ((Diff * Frac_fixed) >> 4);
// --- Update delay values from tables ---
currentOnDelay11 = Dstart[IndexWhole][adcIndex];
currentOffDelay11 = Dend[IndexWhole][adcIndex];
currentOnDelay4 = Istart[IndexWhole][adcIndex];
//currentOnDelay4 = Iend[IndexWhole][adcIndex];
currentOffDelay4 = TerpD;
}
// --- 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;
}
}
}
Code: Select all
#include <mbed/platform/bare_metal/