Fuck it... I've got too much time into this one to quit with the finish line being (seeming?) so close.
I've made a swap over to Arduino. The control libraries are awesome and support it good. I've heard their libraries do have a bit of overhead, but I'm hoping at four million operations per second, it won't matter too much.
So there we have it. Running code for a 180° twin. Totally untested.
What I really like about this setup() function approach over my spreadsheet is that in the long term, I can add an interface to the Arduino and make it programmable on the fly. Buttons to adjust initial and final timing and then recalculate the whole lot... Save the values to the EEPROM, reset the chip, and we have a new timing map. I guess I just need someone to machine me a rotor to fit over my cam, now, and then I can get testing. ;D
I've made a swap over to Arduino. The control libraries are awesome and support it good. I've heard their libraries do have a bit of overhead, but I'm hoping at four million operations per second, it won't matter too much.
Code:
const int MIN_RPM = 800;
const int MAX_INDEX = 205;
const int DWELL = 1500;
const int COIL1 = 4;
const int COIL2 = 7;
const int SENSOR1 = 2; //idle sensor
const int SENSOR2 = 3; //cam sensor
bool triggered = false;
long lastDuration = 0;
unsigned short currentIndex = 0;
unsigned int RPMS[MAX_INDEX];
unsigned int HALVES[MAX_INDEX];
unsigned int DELAYS[MAX_INDEX];
///Build all of the values in the arrays based on our starting RPM and initial and final advance
///Redline is (MAX_INDEX-1) * 50 + MIN_RPM
void calculateArrayValues()
{
double startRPM = MIN_RPM; //use a double for increased precision in the calculations
int startAdvance = 18; //18 degrees of advance coming on at 800 rpm
int endAdvance = 38; //total advance of 38 degrees
int endRPM = 4000; //total advance to be achived at 4000 rpm
int hallAdvance = 180; //assume sensor is 180 degrees advanced from TDC
double slope = abs(endAdvance - startAdvance)/abs(endRPM - startRPM); //function to calculate linear advance is y = mx+b where m = (y1-y2)/(x1-x2)
int intercept = startAdvance - (startRPM * slope); //intercept is b from above formula
//build the arrays, 50 rpm at a time
for(int i = 0; i < MAX_INDEX; i++)
{
double usPerDegree = 1000000 / (startRPM / 60) / 360;
double advance = startRPM * slope + intercept; //use our function to calculate the advance
int advanceDelay;
if(startRPM >= endRPM) //if our current RPM is >= the to ending RPM, just use 38 degrees
{
advance = 38;
}
advanceDelay = ((hallAdvance - advance) * usPerDegree) - 1500; //calculate the time we wait before picking up the sensor and starting the spark process on the coil(s)
DELAYS[i] = advanceDelay;
HALVES[i] = usPerDegree * 180; //we're on a 180 twin, so calculate the delay between the first coil (left) and second coil (right) firing. This way, we only need one pickup.
startRPM += 50; //increment the RPMs for the next iteration
}
}
///Fires a coil by grounding the selected pin, waiting the dwell period, then ungrounding it
void dwellAndFire(int pin)
{
digitalWrite(pin, HIGH); //ground the coil
delayMicroseconds(DWELL);
digitalWrite(pin, LOW); //trigger the coil
}
///Fires both coils with the currently mapped delay values
void fireWithDelays()
{
delayMicroseconds(DELAYS[currentIndex]);
dwellAndFire(COIL1);
delayMicroseconds(HALVES[currentIndex]);
dwellAndFire(COIL2);
}
///Interrupt for the idle sensor. At RPMs less than RPMS[0], use this sensor to immediately trigger the coil(s) with no delay.
///This sensor will be located around 6 to 10 degrees BTDC
void idleSensor()
{
if(lastDuration > RPMS[0])
{
int coil2Delay = (lastDuration / 4); //engine spinning slower than our map, calculate delay for second coil (divide by four is 90 degrees of rotation)
dwellAndFire(COIL1); //fire left coil
delayMicroseconds(coil2Delay); //wait 180 degrees
dwellAndFire(COIL2); //fire right coil
}
}
///Interrupt for the "running" sensor
void camSensor()
{
triggered = true;
}
///Set the pins appropriately, calculate the values we'll be using, and then attach the interrupts
void setup()
{
pinMode(COIL1, OUTPUT);
pinMode(COIL2, OUTPUT);
pinMode(SENSOR1, INPUT);
pinMode(SENSOR2, INPUT);
calculateArrayValues();
attachInterrupt(0, idleSensor, RISING); //idle sensor interrupt
attachInterrupt(1, camSensor, RISING); //cam sensor interrupt
}
///If we pickup a trigger from the running sensor, adjust the timing map and then fire the coils
void loop()
{
if(triggered)
{
noInterrupts(); //turn off interrupts
triggered = false;
lastDuration = (micros() - lastDuration); //number of microseconds since last rotation
if(lastDuration > RPMS[MAX_INDEX]) //not past redline, OK to fire spark
{
if(lastDuration < RPMS[0]) //cam sensor (not idle sensor) handling spark check index
{
if(lastDuration < RPMS[currentIndex]) //engine speed increasing
{
currentIndex++; //jump to the next value in the array
}
else if (lastDuration > RPMS[currentIndex]) //engine speed decreasing
{
currentIndex--; //drop to the previous value in the array
}
fireWithDelays();
}
}
interrupts(); //turn interrupts back on
}
}
So there we have it. Running code for a 180° twin. Totally untested.
What I really like about this setup() function approach over my spreadsheet is that in the long term, I can add an interface to the Arduino and make it programmable on the fly. Buttons to adjust initial and final timing and then recalculate the whole lot... Save the values to the EEPROM, reset the chip, and we have a new timing map. I guess I just need someone to machine me a rotor to fit over my cam, now, and then I can get testing. ;D