diff --git a/ECU.ino b/ECU.ino new file mode 100644 index 0000000..c1b5aaa --- /dev/null +++ b/ECU.ino @@ -0,0 +1,36 @@ +#include "pins.h" +#include "data.h" +#include "schedule.h" +#include "tables.h" + +void setup() { + debug_setup(); + + pinMode(pin_map, INPUT); + + pinMode(pin_tach, OUTPUT); + pinMode(pin_coil1, OUTPUT); + pinMode(pin_coil2, OUTPUT); + pinMode(pin_coil3, OUTPUT); + pinMode(pin_coil4, OUTPUT); + + digitalWrite(pin_tach, LOW); + digitalWrite(pin_coil1, LOW); + digitalWrite(pin_coil2, LOW); + digitalWrite(pin_coil3, LOW); + digitalWrite(pin_coil4, LOW); + + map_init(); +} + +void loop() { + cas_process(); + schedule_process(); + +// map_process(); +// cas_process(); +// tach_process(); +// ignition_process(); + + +} diff --git a/cas.ino b/cas.ino new file mode 100644 index 0000000..e9b7cf0 --- /dev/null +++ b/cas.ino @@ -0,0 +1,93 @@ +void cas_process() +{ + // SGT is a pulse for TDC of *any* cylinder. By combining this with SGT it's possible to + // identify which cylinder it is + byte sgt = digitalRead(pin_cas_sgt); + unsigned long timestamp = micros(); + + // Find rising edge + if(sgt == 1) + { + if(cas_sgt_lastvalue == 0) + { + // Handle tach + task_tach_high = micros(); + + cas_sgt_lastrise = timestamp; + } + } + + // Find falling edge + if(sgt == 0) + { + if(cas_sgt_lastvalue == 1) + { + // Difference between last and current is 180 degrees + usec_per_degree = (timestamp - cas_sgt_lastfall) / 180; + + cas_sgt_lastfall = timestamp; + + // If we have crank pulse present then cyl1 is TDC + if(digitalRead(pin_cas_sgc) == HIGH) + { + cylinder_tdc = 1; + } + else + { + cylinder_tdc = cylinder_next[cylinder_tdc]; + } + + // Schedule next cylinder, this one is already past TDC + cylinder_next_fire = cylinder_next[cylinder_tdc]; + if(cylinder_next_fire == 1) + { + task_coil1_fire = micros() + (usec_per_degree * 180); + task_coil1_charge = task_coil1_fire - coil_dwell; + }else if(cylinder_next_fire == 2) + { + task_coil2_fire = micros() + (usec_per_degree * 180); + task_coil2_charge = task_coil2_fire - coil_dwell; + }else if(cylinder_next_fire == 3) + { + task_coil3_fire = micros() + (usec_per_degree * 180); + task_coil3_charge = task_coil3_fire - coil_dwell; + }else if(cylinder_next_fire == 4) + { + task_coil4_fire = micros() + (usec_per_degree * 180); + task_coil4_charge = task_coil4_fire - coil_dwell; + } + } + } + cas_sgt_lastvalue = sgt; + + + // SGC is the once per revolution pulse. Used to identify when Cyl 1 is TDC (as opposed to 4) + // and is also handy for determining RPM if you trigger on the leading edge + byte sgc = digitalRead(pin_cas_sgc); + timestamp = micros(); + + // Find rising edge + if(sgc == 1) + { + if(cas_sgc_lastvalue == 0) + { + // For rising edge (the consitent one) we need to update RPM + rpm_current_value = 60000000 / (timestamp - cas_sgc_lastrise); + + cas_sgc_lastrise = timestamp; + } + } + + // Find falling edge + if(sgc == 0) + { + if(cas_sgc_lastvalue == 1) + { + cas_sgc_lastfall= timestamp; + } + } + cas_sgc_lastvalue = sgc; +} + + + diff --git a/data.h b/data.h new file mode 100644 index 0000000..4a77a49 --- /dev/null +++ b/data.h @@ -0,0 +1,42 @@ +// MAP Sensor +const int map_sensor_min_kpa = 20; // Physical minimum of the hardware +const int map_sensor_max_kpa = 250; // Physical maximum of the hardware +const int map_range_min = 20; // Index 0 (min we care about) +const int map_range_max = 100; // Index 15 (max we care about, anything >100 can be atmospheric as far as we care) +byte map_current_index = 0; // Index +byte map_current_value = 0; // KPA value + +// Rotation +const int rpm_range_min = 0; // Index 0 +const int rpm_range_max = 7200; // Index 15 +byte rpm_current_index = 0; // Table index of current RPM +int rpm_current_value = 0; // Current engine RPM +int usec_per_degree = 0; // uSec per degree of rotation at current RPM + +// Cam Angle Sensor +unsigned long cas_sgt_lastrise = 0; // micros() of the last rise +unsigned long cas_sgt_lastfall = 0; // "" of the last fall +byte cas_sgt_lastvalue = 0; // The state of SGT on previous cycle + +unsigned long cas_sgc_lastrise = 0; // micros() of the last rise +unsigned long cas_sgc_lastfall = 0; // "" of the last fall +byte cas_sgc_lastvalue = 0; // The state of SGC on the previous cycle + +// Cylinder sequencing +byte cylinder_tdc = 0; // Most recent cylinder to hit TDC +byte cylinder_next_fire = 0; // Next cylinder due a spark +//byte cylinder_next_inject = 0; // Next cylinder due fuel +byte cylinder_next[] = {0,3,1,4,2}; // For 1-3-4-2 Current cylinder as index returns next cylinder + +// Ignition +const int coil_dwell = 5000; // 5ms for stock coil + +// Tachometer +const int tach_pulse_length = 5000; // 5ms equiv to dwell time on old coil at sensible RPM (tach pulse length) + +// O2 sensor +//const int o2_min = 200; // 200mV is lean +//const int o2_targe = 450; // 450mV is 14.7:1 apparently... +//const int o2_max = 800; // 800mv is rich +//int o2_current_value = 0; // Curent milivolts + diff --git a/debug.ino b/debug.ino new file mode 100644 index 0000000..72333de --- /dev/null +++ b/debug.ino @@ -0,0 +1,18 @@ +void debug_setup() +{ + Serial.begin(115200); +} + +void task_debug_run() +{ + Serial.print("MAP = "); + Serial.print(map_current_index); + Serial.print(" ("); + Serial.print(map_current_value); + Serial.print(")\t RPM = "); + Serial.print(rpm_current_index); + Serial.print(" ("); + Serial.print(rpm_current_value); + Serial.println(")\t"); +} + diff --git a/ignition.ino b/ignition.ino new file mode 100644 index 0000000..787fe43 --- /dev/null +++ b/ignition.ino @@ -0,0 +1,9 @@ +void task_coilx_fire_run(int pin) +{ + digitalWrite(pin, LOW); +} + +void task_coilx_charge_run(int pin) +{ + digitalWrite(pin, HIGH); +} diff --git a/map.ino b/map.ino new file mode 100644 index 0000000..3d7add2 --- /dev/null +++ b/map.ino @@ -0,0 +1,20 @@ +void map_init() +{ + task_map_run(); + Serial.print("MAP: "); + Serial.print(map_current_value); + if(map_current_value < 87) + Serial.println(" (ERROR: Low)"); + else if(map_current_value > 108) + Serial.println(" (ERROR: High)"); + else + Serial.println(" (OK)"); +} + +void task_map_run() +{ + map_current_value = map(analogRead(A3), 0, 1023, map_sensor_min_kpa, map_sensor_max_kpa); + map_current_index = map(map_current_value, map_range_min, map_range_max, 0, 15); + map_current_index = constrain(map_current_index,0,15); +} + diff --git a/pins.h b/pins.h new file mode 100644 index 0000000..2273f6e --- /dev/null +++ b/pins.h @@ -0,0 +1,23 @@ + +//const int pin_idle_control = 5; // OUT: Idle valve ground +const int pin_cas_sgc = 18; // IN: crank pulse - yellow/blue +const int pin_cas_sgt = 19; // IN: ignition pulse -white +//const int pin_fuel_pump = 45; // OUT: Fuel relay ground +//const int pin_fan = 47; // OUT: Fan relay ground +const int pin_tach = 49; // OUT: pulse for tach (should be 49 +//const int pin_intake_temp = A0; // IN: Intake temperature +//const int pin_coolant_temp = A1; // IN: Cooland temperature +//const int pin_throttle_position = A2; // IN: Throttle position +const int pin_map = A3; // IN: Manifold absolute pressure +//const int pin_o2 = A8; // IN: O2 sensor + +//const int pin_injector1 = 8; +//const int pin_injector2 = 9; +//const int pin_injector3 = 10; +//const int pin_injector4 = 11; + +const int pin_coil1 = 47; +const int pin_coil2 = 45; +const int pin_coil3 = 43; +const int pin_coil4 = 41; + diff --git a/rpm.ino b/rpm.ino new file mode 100644 index 0000000..0e7e100 --- /dev/null +++ b/rpm.ino @@ -0,0 +1,8 @@ +void task_rpm_run() +{ + // Calculate our 4-bit table index + rpm_current_index = map(rpm_current_value, rpm_range_min, rpm_range_max, 0, 15); + + // Dont exceed table + rpm_current_index = constrain(rpm_current_index,0,15); +} diff --git a/schedule.h b/schedule.h new file mode 100644 index 0000000..aa2732e --- /dev/null +++ b/schedule.h @@ -0,0 +1,20 @@ +unsigned long task_map = 1; // Get initial figure +const unsigned long task_map_interval = 93000; // 93ms + +unsigned long task_rpm = 1; // Get initial figure +const unsigned long task_rpm_interval = 199000; // 199ms + +unsigned long task_debug = 1; // Get initial figure +const unsigned long task_debug_interval = 387000; // 2s + +unsigned long task_tach_high = 0; // Ilde +unsigned long task_tach_low = 0; // Idle + +unsigned long task_coil1_charge = 0; // Idle +unsigned long task_coil1_fire = 0; // Idle +unsigned long task_coil2_charge = 0; // Idle +unsigned long task_coil2_fire = 0; // Idle +unsigned long task_coil3_charge = 0; // Idle +unsigned long task_coil3_fire = 0; // Idle +unsigned long task_coil4_charge = 0; // Idle +unsigned long task_coil4_fire = 0; // Idle diff --git a/schedule.ino b/schedule.ino new file mode 100644 index 0000000..81138b7 --- /dev/null +++ b/schedule.ino @@ -0,0 +1,99 @@ + + +void schedule_process() +{ + byte tasks = 0; + + // Coil 1 + if((task_coil1_fire > 0) && (micros() > task_coil1_fire)) + { + task_coilx_fire_run(pin_coil1); + task_coil1_fire = 0; + tasks++; + }else if((task_coil1_charge > 0) && (micros() > task_coil1_charge)) + { + task_coilx_charge_run(pin_coil1); + task_coil1_charge = 0; + tasks++; + } + + // Coil 2 + if((task_coil2_fire > 0) && (micros() > task_coil2_fire)) + { + task_coilx_fire_run(pin_coil2); + task_coil2_fire = 0; + tasks++; + }else if((task_coil2_charge > 0) && (micros() > task_coil2_charge)) + { + task_coilx_charge_run(pin_coil2); + task_coil2_charge = 0; + tasks++; + } + + // Coil 3 + if((task_coil3_fire > 0) && (micros() > task_coil3_fire)) + { + task_coilx_fire_run(pin_coil3); + task_coil3_fire = 0; + tasks++; + }else if((task_coil3_charge > 0) && (micros() > task_coil3_charge)) + { + task_coilx_charge_run(pin_coil3); + task_coil3_charge = 0; + tasks++; + } + + // Coil 4 + if((task_coil4_fire > 0) && (micros() > task_coil4_fire)) + { + task_coilx_fire_run(pin_coil4); + task_coil4_fire = 0; + tasks++; + }else if((task_coil4_charge > 0) && (micros() > task_coil4_charge)) + { + task_coilx_charge_run(pin_coil4); + task_coil4_charge = 0; + tasks++; + } + + // Only carry on to the lower priority stuff if we did nothing else + if(tasks > 0) + return; + + // These should never clash, but if they do then only do MAP, RPM can't have shifted much + if(micros() > task_map) + { + task_map = micros() + task_map_interval; + task_map_run(); + tasks++; + }else if(micros() > task_rpm) + { + task_rpm = micros() + task_rpm_interval; + task_rpm_run(); + tasks++; + } + + // We never high and low at the same time + if((task_tach_high > 0) && (micros() > task_tach_high)) + { + task_tach_high_run(); + task_tach_high = 0; + tasks++; + }else if((task_tach_low > 0) && (micros() > task_tach_low)) + { + task_tach_low_run(); + task_tach_low = 0; + tasks++; + } + + // Dont do debug output unless we have nothing better to do + if(tasks > 0) + return; + + if((task_debug > 0) && (micros() > task_debug)) + { + task_debug_run(); + task_debug = micros() + task_debug_interval; + } +} + diff --git a/tables.h b/tables.h new file mode 100644 index 0000000..4fb0309 --- /dev/null +++ b/tables.h @@ -0,0 +1,41 @@ +// To get value, do map_current_index * 16 + rpm_current_index (or some shit like that) and that gives duty percent or something +byte table_ve[] = { + 36, 36, 40, 40, 19, 16, 14, 26, 42, 42, 42, 41, 41, 41, 41, 41, + 36, 37, 40, 31, 16, 17, 14, 24, 51, 50, 49, 48, 47, 46, 46, 46, + 37, 37, 45, 36, 22, 25, 21, 37, 52, 52, 52, 52, 52, 52, 52, 52, + 36, 38, 41, 50, 33, 33, 29, 44, 53, 54, 54, 55, 55, 56, 56, 56, + 41, 49, 50, 48, 38, 42, 38, 45, 53, 54, 55, 56, 57, 58, 58, 58, + 45, 50, 51, 56, 47, 50, 45, 54, 59, 59, 59, 59, 59, 59, 59, 59, + 47, 50, 52, 65, 53, 52, 52, 59, 69, 68, 66, 65, 64, 63, 62, 62, + 44, 49, 53, 68, 59, 54, 53, 61, 69, 69, 69, 68, 68, 68, 68, 68, + 46, 49, 60, 69, 65, 60, 59, 67, 61, 61, 60, 60, 60, 59, 59, 59, + 47, 50, 64, 71, 66, 65, 64, 67, 61, 61, 62, 62, 62, 63, 63, 63, + 52, 52, 64, 70, 72, 70, 68, 69, 63, 66, 68, 71, 73, 76, 77, 77, + 53, 59, 65, 72, 76, 77, 77, 81, 86, 85, 85, 84, 84, 83, 83, 83, + 53, 61, 65, 76, 88, 90, 89, 86, 93, 93, 93, 93, 93, 93, 93, 93, + 53, 61, 68, 81, 95, 96, 95, 104, 98, 99, 99, 100, 101, 102, 102, 102, + 53, 61, 69, 83, 99, 102, 107, 128, 141, 137, 133, 128, 124, 120, 118, 118, + 53, 58, 76, 97, 106, 108, 115, 119, 122, 121, 121, 120, 120, 119, 119, 119 + }; + +// X = RPM 0 - 7200 +// Y = kPA 250 - 20 +// Value = degrees +byte table_ignition[] = { + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 21, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + }; diff --git a/tach.ino b/tach.ino new file mode 100644 index 0000000..abe13ca --- /dev/null +++ b/tach.ino @@ -0,0 +1,13 @@ +void task_tach_low_run() +{ + digitalWrite(pin_tach, LOW); +} + +void task_tach_high_run() +{ + digitalWrite(pin_tach, HIGH); + + // Auto-schedule the end + task_tach_low = micros() + tach_pulse_length; +} +