00001 /* FreeEMS - the open source engine management system 00002 * 00003 * Copyright 2008, 2009, 2010 Fred Cooke 00004 * 00005 * This file is part of the FreeEMS project. 00006 * 00007 * FreeEMS software is free software: you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation, either version 3 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * FreeEMS software is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/ 00019 * 00020 * We ask that if you make any changes to this file you email them upstream to 00021 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com! 00022 * 00023 * Thank you for choosing FreeEMS to run your engine! 00024 */ 00025 00026 00055 #include "inc/freeEMS.h" 00056 #include "inc/interrupts.h" 00057 #include "inc/DecoderInterface.h" 00058 #include "inc/utils.h" 00059 00060 00085 void PrimaryRPMISR(){ 00086 /* Clear the interrupt flag for this input compare channel */ 00087 TFLG = 0x01; 00088 00089 /* Save all relevant available data here */ 00090 unsigned short codeStartTimeStamp = TCNT; /* Save the current timer count */ 00091 unsigned short edgeTimeStamp = TC0; /* Save the edge time stamp */ 00092 unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */ 00093 // unsigned short PORTS_BACurrentState = PORTS_BA; /* Save ignition output state */ 00094 00095 /* Calculate the latency in ticks */ 00096 ISRLatencyVars.primaryInputLatency = codeStartTimeStamp - edgeTimeStamp; 00097 00104 /* The LM1815 variable reluctance sensor amplifier allows the output to be 00105 * pulled high starting at the center of a tooth. So, what we see as the 00106 * start of a tooth is actually the centre of a physical tooth. Because 00107 * tooth shape, profile and spacing may vary this is the only reliable edge 00108 * for us to schedule from, hence the trailing edge code is very simple. 00109 */ 00110 if(PTITCurrentState & 0x01){ 00111 /* Echo input condition on J7 */ 00112 PORTJ |= 0x80; 00113 00114 // increment crank pulses TODO this needs to be wrapped in tooth period and width checking 00115 primaryPulsesPerSecondaryPulse++; 00116 00117 // calculate rough rpm (this will be wrong when the var is used correctly) 00118 *RPMRecord = ticksPerCycleAtOneRPMx2 / engineCyclePeriod; /* 0.8us ticks, 150mil = 2 x 60 seconds, times rpm scale factor of 2 */ 00119 00120 // don't run until the second trigger has come in and the period is correct (VERY temporary) 00121 if(!(coreStatusA & PRIMARY_SYNC)){ 00122 primaryTeethDroppedFromLackOfSync++; 00123 return; 00124 } 00125 00126 LongTime timeStamp; 00127 00128 /* Install the low word */ 00129 timeStamp.timeShorts[1] = edgeTimeStamp; 00130 /* Find out what our timer value means and put it in the high word */ 00131 if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */ 00132 timeStamp.timeShorts[0] = timerExtensionClock + 1; 00133 }else{ 00134 timeStamp.timeShorts[0] = timerExtensionClock; 00135 } 00136 00137 // temporary data from inputs 00138 primaryLeadingEdgeTimeStamp = timeStamp.timeLong; 00139 timeBetweenSuccessivePrimaryPulses = lastPrimaryPulseTimeStamp - primaryLeadingEdgeTimeStamp; 00140 lastPrimaryPulseTimeStamp = primaryLeadingEdgeTimeStamp; 00141 // timeBetweenSuccessivePrimaryPulsesBuffer = (timeBetweenSuccessivePrimaryPulses >> 1) + (timeBetweenSuccessivePrimaryPulsesBuffer >> 1); 00142 00143 // TODO make scheduling either fixed from boot with a limited range, OR preferrably if its practical scheduled on the fly to allow arbitrary advance and retard of both fuel and ignition. 00144 00145 /* Check for loss of sync by too high a count */ 00146 if(primaryPulsesPerSecondaryPulse > 12){ 00147 /* Increment the lost sync count */ 00148 Counters.crankSyncLosses++; 00149 00150 /* Clear synced status */ 00151 coreStatusA &= CLEAR_PRIMARY_SYNC; 00152 00153 /* Reset the count of teeth */ 00154 primaryPulsesPerSecondaryPulse = 0; 00155 00156 /* Get the hell out of here before we do something bad */ 00157 return; 00158 } 00159 00160 // CAUTION came to me lying in bed half asleep idea : 00161 00162 // TODO move tooth selection to the calc loop in main such that this routine just iterates through an array of events and schedules those that are destined for this tooth. 00163 00164 // if ign enabled 00165 // iterate through ignition first, schedule all of those 00166 // iterate through dwell next, schedule all of those 00167 // if fuel enabled 00168 // iterate through main fuel next, schedule all of those 00169 // if staging enabled and required 00170 // iterate through staged fuel last, 00171 00172 // TODO should make for a clean compact scheduling implementation. the fuel code doesn't care when/how it has started in the past, and hopefully ign will be the same. 00173 00174 // this will be done with an array and per tooth check in future 00175 if((primaryPulsesPerSecondaryPulse % 2) == 0){ 00176 00177 // TODO sample ADCs on teeth other than that used by the scheduler in order to minimise peak run time and get clean signals 00178 sampleEachADC(ADCArrays); 00179 Counters.syncedADCreadings++; 00180 *mathSampleTimeStampRecord = TCNT; 00181 00182 /* Set flag to say calc required */ 00183 coreStatusA |= CALC_FUEL_IGN; 00184 00185 /* Reset the clock for reading timeout */ 00186 Clocks.timeoutADCreadingClock = 0; 00187 00188 if(masterPulseWidth > injectorMinimumPulseWidth){ // use reference PW to decide. spark needs moving outside this area though TODO 00189 /* Determine if half the cycle is bigger than short-max */ 00190 unsigned short maxAngleAfter; 00191 if((engineCyclePeriod >> 1) > 0xFFFF){ 00192 maxAngleAfter = 0xFFFF; 00193 }else{ 00194 maxAngleAfter = (unsigned short)(engineCyclePeriod >> 1); 00195 } 00196 00197 /* Check advance to ensure it is less than 1/2 of the previous engine cycle and more than codetime away */ 00198 unsigned short advance; 00199 if(totalAngleAfterReferenceInjection > maxAngleAfter){ // if too big, make it max 00200 advance = maxAngleAfter; 00201 }else if(totalAngleAfterReferenceInjection < trailingEdgeSecondaryRPMInputCodeTime){ // if too small, make it min 00202 advance = trailingEdgeSecondaryRPMInputCodeTime; 00203 }else{ // else use it as is 00204 advance = totalAngleAfterReferenceInjection; 00205 } 00206 00207 // determine the long and short start times 00208 unsigned short startTime = primaryLeadingEdgeTimeStamp + advance; 00209 unsigned long startTimeLong = timeStamp.timeLong + advance; 00210 00211 /* Determine the channels to schedule */ 00212 unsigned char fuelChannel = (primaryPulsesPerSecondaryPulse / 2) - 1; 00213 unsigned char ignitionChannel = (primaryPulsesPerSecondaryPulse / 2) - 1; 00214 00215 if(fuelChannel > 5 || ignitionChannel > 5){ 00216 // send("bad fuel : "); 00217 // sendUC(fuelChannel); 00218 // send("bad ign : "); 00219 // sendUC(ignitionChannel); 00220 return; 00221 } 00222 00223 // determine whether or not to reschedule 00224 unsigned char reschedule = 0; 00225 unsigned long diff = startTimeLong - (injectorMainEndTimes[fuelChannel] + injectorSwitchOffCodeTime); 00226 if(diff > LONGHALF){ 00227 reschedule = 1; // http://www.diyefi.org/forum/viewtopic.php?f=8&t=57&p=861#p861 00228 } 00229 00230 // schedule the appropriate channel 00231 if(!(*injectorMainControlRegisters[fuelChannel] & injectorMainEnableMasks[fuelChannel]) || reschedule){ /* If the timer isn't still running, or if its set too long, set it to start again at the right time soon */ 00232 *injectorMainControlRegisters[fuelChannel] |= injectorMainEnableMasks[fuelChannel]; 00233 *injectorMainTimeRegisters[fuelChannel] = startTime; 00234 TIE |= injectorMainOnMasks[fuelChannel]; 00235 TFLG = injectorMainOnMasks[fuelChannel]; 00236 }else{ 00237 injectorMainStartTimesHolding[fuelChannel] = startTime; 00238 selfSetTimer |= injectorMainOnMasks[fuelChannel]; // setup a bit to let the timer interrupt know to set its own new start from a var 00239 } 00240 00241 // TODO advance/retard/dwell numbers all need range checking etc done. some of this should be done in the calculator section, and some here. currently none is done at all and for that reason, this will not work in a real system yet, if it works at all. 00242 // as do array indexs here and in the ISRs... 00243 00244 00245 // TODO implement mechanism for dropping a cylinder in event of over queueing or spark cut/round robin 00246 // important as ignition sequence disrupted when this occurs as it stands. 00247 00248 // TODO check queue length checks to ensure we dont count up to somewhere we can never count down from. This could be causing the hanging long phenomina 00249 00250 // DWELL 00251 00252 // If dwell is not currently enabled, set it all up 00253 if(!(PITCE & DWELL_ENABLE)){ 00254 /* Schedule Dwell event (do this first because it comes earliest. */ 00255 // set the channel to fire 00256 nextDwellChannel = ignitionChannel; 00257 00258 // set the time 00259 PITLD0 = advance; 00260 // PITLD0 = ignitionAdvances[ignitionChannel] - *currentDwellRealtime; BAD for various reasons! 00261 00262 // clear the flags first as they apparently become set any old time whether enabled or not. 00263 PITTF |= DWELL_ENABLE; 00264 00265 // turn on the ints 00266 PITINTE |= DWELL_ENABLE; 00267 00268 // clear the flags first as they apparently become set any old time whether enabled or not. 00269 PITTF |= DWELL_ENABLE; 00270 00271 // enable channels 00272 PITCE |= DWELL_ENABLE; 00273 }else if(dwellQueueLength == 0){ 00274 // load time offset such that next period is correct 00275 PITLD0 = (advance - PITCNT0); 00276 00277 // increment queue length 00278 dwellQueueLength++; 00279 }else if(dwellQueueLength > fixedConfigs1.engineSettings.combustionEventsPerEngineCycle){ //TODO sensible figures here for array index OOBE 00280 // do nothing, or increment a counter or something similar. 00281 }else{ 00282 unsigned short sumOfDwells = PITLD0; 00283 // add up the prequeued time periods 00284 00285 // queue = 1 pitld is all 00286 // queue = 2 one from 0 index of array AND pitld 00287 00288 unsigned char index = 0; 00289 while(index < (dwellQueueLength -1)){ 00290 sumOfDwells += queuedDwellOffsets[index]; 00291 index++; 00292 } 00293 // for(index = 0;index < (dwellQueueLength -1);index++){ // is this right? 00294 // sumOfDwells += queuedDwellOffsets[index]; 00295 // } 00296 00297 // store time offset in appropriate array location 00298 queuedDwellOffsets[dwellQueueLength - 1] = advance - (PITCNT0 + sumOfDwells); 00299 00300 // increment queue length from one or more 00301 dwellQueueLength++; 00302 } 00303 00304 // IGNITION experimental stuff 00305 00306 // If ignition is not currently enabled, set it all up 00307 if(!(PITCE & IGNITION_ENABLE)){ 00308 /* Schedule Ignition event (do this first because it comes earliest. */ 00309 // set the channel to fire 00310 nextIgnitionChannel = ignitionChannel; 00311 00312 // figure out the time to set the delay reg to 00313 PITLD1 = advance + injectorMainPulseWidthsRealtime[fuelChannel]; 00314 // PITLD1 = ignitionAdvances[ignitionChannel + outputBankIgnitionOffset]; 00315 00316 // clear the flags first as they apparently become set any old time whether enabled or not. 00317 PITTF |= IGNITION_ENABLE; 00318 00319 // turn on the ints 00320 PITINTE |= IGNITION_ENABLE; 00321 00322 // clear the flags first as they apparently become set any old time whether enabled or not. 00323 PITTF |= IGNITION_ENABLE; 00324 00325 // enable channels 00326 PITCE |= IGNITION_ENABLE; 00327 }else if(ignitionQueueLength == 0){ 00328 // load timer register 00329 PITLD1 = ((advance + injectorMainPulseWidthsRealtime[fuelChannel]) - PITCNT1); 00330 00331 // increment to 1 00332 ignitionQueueLength++; 00333 }else if(ignitionQueueLength > fixedConfigs1.engineSettings.combustionEventsPerEngineCycle){ //TODO sensible figures here for array index OOBE 00334 // do nothing, or increment a counter or something similar. 00335 }else{ 00336 unsigned short sumOfIgnitions = PITLD1; 00337 // add up the prequeued time periods 00338 00339 // queue = 1 pitld is all 00340 // queue = 2 one from 0 index of array AND pitld 00341 00342 00343 unsigned char index = 0; 00344 while(index < (ignitionQueueLength - 1)){ 00345 sumOfIgnitions += queuedIgnitionOffsets[index]; 00346 index++; 00347 } 00348 // for(index = 0;index < (ignitionQueueLength -1);index++){ // is this right? 00349 // sumOfIgnitions += queuedIgnitionOffsets[index]; 00350 // } 00351 00352 // store time offset in appropriate array location 00353 queuedIgnitionOffsets[ignitionQueueLength - 1] = advance - (PITCNT1 + sumOfIgnitions); 00354 00355 // increment from 1 or more 00356 ignitionQueueLength++; 00357 } 00358 } 00359 } 00360 RuntimeVars.primaryInputLeadingRuntime = TCNT - codeStartTimeStamp; 00361 }else{ 00362 PORTJ &= 0x7F; 00363 RuntimeVars.primaryInputTrailingRuntime = TCNT - codeStartTimeStamp; 00364 } 00365 00366 Counters.primaryTeethSeen++; 00367 // suss out rpm and accurate TDC reference 00368 00369 // if you say it quick, it doesn't sound like much : 00370 // schedule fuel and ign based on spark cut and fuel cut and timing vars and status vars config vars 00371 } 00372 00373 00381 void SecondaryRPMISR(){ 00382 /* Clear the interrupt flag for this input compare channel */ 00383 TFLG = 0x02; 00384 00385 /* Save all relevant available data here */ 00386 unsigned short codeStartTimeStamp = TCNT; /* Save the current timer count */ 00387 unsigned short edgeTimeStamp = TC1; /* Save the timestamp */ 00388 unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */ 00389 // unsigned short PORTS_BACurrentState = PORTS_BA; /* Save ignition output state */ 00390 00391 /* Calculate the latency in ticks */ 00392 ISRLatencyVars.secondaryInputLatency = codeStartTimeStamp - edgeTimeStamp; 00393 00400 /* The LM1815 variable reluctance sensor amplifier allows the output to be 00401 * pulled high starting at the center of a tooth. So, what we see as the 00402 * start of a tooth is actually the centre of a physical tooth. Because 00403 * tooth shape, profile and spacing may vary this is the only reliable edge 00404 * for us to schedule from, hence the trailing edge code is very simple. 00405 */ 00406 if(PTITCurrentState & 0x02){ 00407 // echo input condition 00408 PORTJ |= 0x40; 00409 00410 // display the crank pulses 00411 PORTM = (char)primaryPulsesPerSecondaryPulse; 00412 00413 // was this code like this because of a good reason? 00414 // primaryPulsesPerSecondaryPulseBuffer = primaryPulsesPerSecondaryPulse; 00415 primaryPulsesPerSecondaryPulse = 0; 00416 00417 // if we didn't get the right number of pulses drop sync and start over 00418 if((primaryPulsesPerSecondaryPulse != 12) && (coreStatusA & PRIMARY_SYNC)){ 00419 coreStatusA &= CLEAR_PRIMARY_SYNC; 00420 Counters.crankSyncLosses++; 00421 } 00422 00423 LongTime timeStamp; 00424 00425 /* Install the low word */ 00426 timeStamp.timeShorts[1] = edgeTimeStamp; 00427 /* Find out what our timer value means and put it in the high word */ 00428 if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */ 00429 timeStamp.timeShorts[0] = timerExtensionClock + 1; 00430 }else{ 00431 timeStamp.timeShorts[0] = timerExtensionClock; 00432 } 00433 00434 // get the data we actually want 00435 engineCyclePeriod = 2 * (timeStamp.timeLong - lastSecondaryOddTimeStamp); // save the engine cycle period 00436 lastSecondaryOddTimeStamp = timeStamp.timeLong; // save this stamp for next time round 00437 00438 // Because this is our only reference, each time we get this pulse, we know where we are at (simple mode so far) 00439 coreStatusA |= PRIMARY_SYNC; 00440 RuntimeVars.secondaryInputLeadingRuntime = TCNT - codeStartTimeStamp; 00441 }else{ 00442 PORTJ &= 0xBF; 00443 RuntimeVars.secondaryInputTrailingRuntime = TCNT - codeStartTimeStamp; 00444 } 00445 00446 Counters.secondaryTeethSeen++; 00447 // suss out phase/engine cycle reference showing which bank we are on 00448 00449 /* If the flag is not cleared at the beginning then the interrupt gets rescheduled while it is running, hence it can't be done at the end of the ISR */ 00450 }