Fully working scheduler (With 2 schedules), initial crank timing work.

This commit is contained in:
Josh Stewart 2013-02-14 17:34:29 +11:00
parent feff0b244d
commit 1537249958
3 changed files with 100 additions and 19 deletions

View File

@ -11,8 +11,10 @@ Need to calculate the req_fuel figure here, preferably in pre-processor macro
#define engineInjectorSize 100 // In cc/min #define engineInjectorSize 100 // In cc/min
#define engineStoich 14.7 // Stoichiometric ratio of fuel used #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 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 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 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 //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 #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 "testing.h"
#include "scheduler.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 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 int rpm = 0; //Stores the last recorded RPM value
struct table fuelTable; struct table fuelTable;
struct table ignitionTable;
unsigned long injectTime[engineCylinders]; //The system time in uS that each injector needs to next fire at 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 boolean intjectorNeedsFire[engineCylinders]; //Whether each injector needs to fire or not
unsigned long counter; unsigned long counter;
unsigned long scheduleStart;
unsigned long scheduleEnd;
void setup() { void setup() {
@ -80,6 +83,7 @@ void setup() {
Serial.begin(9600); Serial.begin(9600);
dummyFuelTable(&fuelTable); dummyFuelTable(&fuelTable);
dummyIgnitionTable(&ignitionTable);
initialiseScheduler(); initialiseScheduler();
counter = 0; counter = 0;
} }
@ -102,12 +106,25 @@ void loop()
//Get the current MAP value //Get the current MAP value
int MAP = 20; //Placeholder int MAP = 20; //Placeholder
//Begin the fuel calculation
//Perform lookup into fuel map for RPM vs MAP value //Perform lookup into fuel map for RPM vs MAP value
int VE = getTableValue(fuelTable, MAP, rpm); int VE = getTableValue(fuelTable, MAP, rpm);
//Calculate an injector pulsewidth form the VE
//From all of the above, calculate an injector pulsewidth
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 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.println(VE);
//Serial.print("VE: "); //Serial.print("VE: ");
//Serial.println(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(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)(req_fuel * (float)(VE/100)) );
//Serial.println( (float)(VE/100.0)); //Serial.println( (float)(VE/100.0));
//920 out
if (counter > 100000) { if (counter > 100000) {
Serial.print("Calling schedule at: "); scheduleStart = micros();
Serial.println(micros());
setSchedule1(openInjector2, 1000000); setSchedule1(openInjector2, 1000000);
counter = 0; counter = 0;
} }
counter++; counter++;
if (scheduleEnd != 0) {
Serial.print("The schedule took (uS): ");
Serial.println(scheduleEnd - scheduleStart);
scheduleEnd = 0;
}
} }
else else
@ -153,7 +175,7 @@ void getSync()
//Interrupts //Interrupts
//These 2 functions simply trigger the injector driver off or on. //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 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 void closeInjector() { digitalWrite(pinInjector, LOW); } // Is called x ms after the open time where x is calculated by the rpm, load and req_fuel

View File

@ -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 It functions by waiting for the overflow vectors from each of the timers in use to overflow, which triggers an interrupt
//Technical //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) Max Period = (Prescale)*(1/Frequency)*(2^17)
(See http://playground.arduino.cc/code/timer1) (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 means that the precision of the scheduler is 16uS (+/- 8uS of target)
This also means that the precision of the scheduler is 16uS
/Features /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) 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 <avr/interrupt.h> #include <avr/interrupt.h>
#include <avr/io.h> #include <avr/io.h>
#define clockspeed 16000000 //#define clockspeed 16000000
int schedule1Active; int schedule1Active;
int schedule2Active; int schedule2Active;
void (*schedule1Callback)(); void (*schedule1Callback)(); //Callback function for schedule1
void (*schedule2Callback)(); void (*schedule2Callback)();
void initialiseScheduler() void initialiseScheduler()
{ {
// Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html // 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 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 TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK3 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable TIMSK3 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
TCCR3A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal 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 << 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 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 //This needs to call the callback function if one has been provided and rest the timer
ISR(TIMER3_OVF_vect) ISR(TIMER3_OVF_vect)
{ {
@ -79,5 +94,17 @@ ISR(TIMER3_OVF_vect)
TCNT3 = 0; //Reset Timer to 0 out of 255 TCNT3 = 0; //Reset Timer to 0 out of 255
TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag 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
} }

View File

@ -73,3 +73,35 @@ void dummyFuelTable(struct table *myFuelTable)
for (int x = 0; x< myFuelTable->xSize; x++) { myFuelTable->values[7][x] = tempRow8[x]; } 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]; }
}