diff --git a/kartduino.ino b/kartduino.ino index d0a05a08..1f140521 100644 --- a/kartduino.ino +++ b/kartduino.ino @@ -11,8 +11,10 @@ Need to calculate the req_fuel figure here, preferably in pre-processor macro #define engineInjectorSize 100 // In cc/min #define engineStoich 14.7 // Stoichiometric ratio of fuel used #define engineStrokes 4 //Can be 2 stroke or 4 stroke, any other value will cause problems +#define engineDwell 3000 //The spark dwell time in uS #define triggerTeeth 12 //The full count of teeth on the trigger wheel if there were no gaps #define triggerMissingTeeth 1 //The size of the tooth gap (ie number of missing teeth) +#define triggerAngle 110 // The angle (Degrees) from TDC that No 1 cylinder is at when tooth #1 passes the sensor //The following lines are configurable, but the defaults are probably pretty good for most applications #define engineInjectorDeadTime 1.5 //Time in ms that the injector takes to open @@ -24,8 +26,6 @@ Need to calculate the req_fuel figure here, preferably in pre-processor macro #include "testing.h" #include "scheduler.h" -//#include "TimerThree.h" //Enable this when switching to Mega -#include "TimerOne.h" //Enable this when using Leo based test board // float req_fuel = ((engineCapacity / engineInjectorSize) / engineCylinders / engineStoich) * 100; // This doesn't seem quite correct, but I can't find why. It will be close enough to start an engine @@ -47,11 +47,14 @@ volatile unsigned long toothLastMinusOneToothTime = 0; //The time (micros()) tha int rpm = 0; //Stores the last recorded RPM value struct table fuelTable; +struct table ignitionTable; unsigned long injectTime[engineCylinders]; //The system time in uS that each injector needs to next fire at boolean intjectorNeedsFire[engineCylinders]; //Whether each injector needs to fire or not unsigned long counter; +unsigned long scheduleStart; +unsigned long scheduleEnd; void setup() { @@ -80,6 +83,7 @@ void setup() { Serial.begin(9600); dummyFuelTable(&fuelTable); + dummyIgnitionTable(&ignitionTable); initialiseScheduler(); counter = 0; } @@ -102,12 +106,25 @@ void loop() //Get the current MAP value int MAP = 20; //Placeholder + //Begin the fuel calculation //Perform lookup into fuel map for RPM vs MAP value int VE = getTableValue(fuelTable, MAP, rpm); - - //From all of the above, calculate an injector pulsewidth + //Calculate an injector pulsewidth form the VE int pulseWidth = PW(req_fuel, VE, MAP, 100, engineInjectorDeadTime); //The 100 here is just a placeholder for any enrichment factors (Cold start, acceleration etc). To add 10% extra fuel, this would be 110 + //Perform a lookup to get the desired ignition advance + int advance = getTableValue(ignitionTable, MAP, rpm); + + //Determine the current crank angle + int crankAngle = (toothCurrentCount - 1) * triggerToothAngle + triggerAngle; //Number of teeth that have passed since tooth 1, multiplied by the angle each tooth represents, plus the angle that tooth 1 is from TDC + if (crankAngle > 360) { crankAngle -= 360; } //Not sure if this is actually required + + //Determine next firing angles + + + //Finally calculate the time (uS) until we reach the firing angles + + //Serial.println(VE); //Serial.print("VE: "); //Serial.println(VE); @@ -117,14 +134,19 @@ void loop() //Serial.println(req_fuel * (float)(VE/100.0) * (float)(MAP/100.0) * (float)(100/100.0) + engineInjectorDeadTime); //Serial.println( (float)(req_fuel * (float)(VE/100)) ); //Serial.println( (float)(VE/100.0)); - //920 out + if (counter > 100000) { - Serial.print("Calling schedule at: "); - Serial.println(micros()); + scheduleStart = micros(); setSchedule1(openInjector2, 1000000); counter = 0; } counter++; + + if (scheduleEnd != 0) { + Serial.print("The schedule took (uS): "); + Serial.println(scheduleEnd - scheduleStart); + scheduleEnd = 0; + } } else @@ -153,7 +175,7 @@ void getSync() //Interrupts //These 2 functions simply trigger the injector driver off or on. -void openInjector2() { Serial.print("Interrupt finished at: "); Serial.println(micros());} +void openInjector2() { scheduleEnd = micros();} void openInjector() { digitalWrite(pinInjector, HIGH); } // Set based on an estimate of when to open the injector void closeInjector() { digitalWrite(pinInjector, LOW); } // Is called x ms after the open time where x is calculated by the rpm, load and req_fuel diff --git a/scheduler.h b/scheduler.h index c37cebd7..bcddc0fd 100644 --- a/scheduler.h +++ b/scheduler.h @@ -3,11 +3,10 @@ This scheduler is designed to maintain 2 schedules for use by the fuel and ignit It functions by waiting for the overflow vectors from each of the timers in use to overflow, which triggers an interrupt //Technical -Currently I am prescaling the 8-bit timers to 256. This means that the counter increments every 16us and will overflow every 2017mS +Currently I am prescaling the 16-bit timers to 256. This means that the counter increments every 16us and will overflow every 1048576uS Max Period = (Prescale)*(1/Frequency)*(2^17) (See http://playground.arduino.cc/code/timer1) -Because the maximum overflow occurs roughly every 2 seconds, you cannot schedule anything to be more than 2 seconds in the future. -This also means that the precision of the scheduler is 16uS +This means that the precision of the scheduler is 16uS (+/- 8uS of target) /Features This differs from most other schedulers in that its calls are non-recurring (IE You schedule an event at a certain time and once it has occurred, it will not reoccur unless you explicitely ask for it) @@ -27,25 +26,32 @@ See page 136 of the processors datasheet: http://www.atmel.com/Images/doc2549.pd #include #include -#define clockspeed 16000000 +//#define clockspeed 16000000 -int schedule1Active; +int schedule1Active; int schedule2Active; -void (*schedule1Callback)(); +void (*schedule1Callback)(); //Callback function for schedule1 void (*schedule2Callback)(); void initialiseScheduler() { // Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html - //Timer 2, which is actually timer 1 + //Schedule 1, which is uses timer 3 TCCR3B = 0x00; //Disbale Timer2 while we set it up - TCNT3 = 130; //Reset Timer Count to 130 out of 255 + TCNT3 = 0; //Reset Timer Count TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag TIMSK3 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable TCCR3A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal TCCR3B = (1 << CS12); //Timer2 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg - //TCCR3B = (1 << CS11) | (1 << CS10); //Timer2 Control Reg B: Timer Prescaler set to 64 + + //Schedule 2, which is uses timer 4 + TCCR4B = 0x00; //Disbale Timer2 while we set it up + TCNT4 = 0; //Reset Timer Count + TIFR4 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag + TIMSK4 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable + TCCR4A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal + TCCR4B = (1 << CS12); //Timer2 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg } @@ -67,7 +73,16 @@ void setSchedule1(void (*callback)(), unsigned long timeout) schedule1Active = 1; //Turn this schedule on } -//Timer2 (schedule 1) Overflow Interrupt Vector +//As above, but for schedule2 +void setSchedule2(void (*callback)(), unsigned long timeout) + { + //TODO: Need to add check for timeout > 1048576 ???? + TCNT4 = 65536 - (timeout / 16); //Each tick occurs every 16uS with a 256 prescaler so divide the timeout by 16 to get ther required number of ticks. Subtract this from the total number of tick (65536 for 16-bit timer) + schedule2Callback = callback; //Name the callback function + schedule2Active = 1; //Turn this schedule on + } + +//Timer3 (schedule 1) Overflow Interrupt Vector //This needs to call the callback function if one has been provided and rest the timer ISR(TIMER3_OVF_vect) { @@ -79,5 +94,17 @@ ISR(TIMER3_OVF_vect) TCNT3 = 0; //Reset Timer to 0 out of 255 TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag - + } + +//AS above for schedule2 +ISR(TIMER4_OVF_vect) + { + if (schedule2Active > 0) //Check to see if this schedule is turn on + { + schedule2Callback(); //Replace with user provided callback + schedule2Active = 0; //Turn off the callback + } + + TCNT3 = 0; //Reset Timer to 0 out of 255 + TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag } diff --git a/testing.h b/testing.h index 04e39abc..ff6828c6 100644 --- a/testing.h +++ b/testing.h @@ -73,3 +73,35 @@ void dummyFuelTable(struct table *myFuelTable) for (int x = 0; x< myFuelTable->xSize; x++) { myFuelTable->values[7][x] = tempRow8[x]; } } + +/* +Populates a table with some reasonably realistic ignition advance data +*/ +void dummyIgnitionTable(struct table *mySparkTable) + { + int tempXAxis[8] = {500,1500,2000,2500,3000,4000,5000,6000}; + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->axisX[x] = tempXAxis[x]; } + //*myFuelTable->axisX = *tempXAxis; + int tempYAxis[8] = {100,88,75,63,50,38,25,13}; + for (int x = 0; x< mySparkTable->ySize; x++) { mySparkTable->axisY[x] = tempYAxis[x]; } + //*myFuelTable->axisY = *tempYAxis; + + //Go through the 8 rows and add the column values + int tempRow1[8] = {10,15,20,26,35,40,43,44}; + int tempRow2[8] = {10,88,75,63,50,38,25,44}; + int tempRow3[8] = {12,88,75,63,50,38,25,40}; + int tempRow4[8] = {12,88,75,63,50,38,25,36}; + int tempRow5[8] = {28,88,75,63,50,38,25,13}; + int tempRow6[8] = {22,23,75,63,50,38,25,13}; + int tempRow7[8] = {17,21,75,63,50,38,25,13}; + int tempRow8[8] = {15,20,25,63,50,38,25,13}; + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[0][x] = tempRow1[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[1][x] = tempRow2[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[2][x] = tempRow3[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[3][x] = tempRow4[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[4][x] = tempRow5[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[5][x] = tempRow6[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[6][x] = tempRow7[x]; } + for (int x = 0; x< mySparkTable->xSize; x++) { mySparkTable->values[7][x] = tempRow8[x]; } + + }