#include "inc/freeEMS.h"
#include "inc/interrupts.h"
#include "inc/utils.h"
Go to the source code of this file.
Functions | |
void | PrimaryRPMISR () |
void | SecondaryRPMISR () |
Use the rising and falling edges................... |
This file contains the two interrupt service routines for handling engine position and RPM signals from mainly Toyota engines using this sensor style.
One ISR handles the 24 evenly spaced teeth and the other handles the two adjacent teeth. This signal style provides enough information for wasted spark ignition and semi sequential fuel injection.
Supported engines include:
Definition in file NipponDenso.c.
void PrimaryRPMISR | ( | void | ) |
Primary RPM ISR
Summary of intended engine position capture scheme (out of date as at 3/1/09)
Position/RPM signal interpretation : Discard edges that have arrived too soon (lose sync here?) Check to ensure we haven't lost sync (pulse arrives too late) Compare time stamps of successive edges and calculate RPM Store RPM and position in globals
Schedule events : loop through all events (spark and fuel), schedule those that fall sufficiently after this tooth and before the next one we expect.
Sample ADCs : Grab a unified set of ADC readings at one time in a consistent crank location to eliminate engine cycle dependent noise. Set flag stating that New pulse, advance, etc should be calculated.
Definition at line 84 of file NipponDenso.c.
References ADCArrays, CALC_FUEL_IGN, CLEAR_PRIMARY_SYNC, Clocks, engineSetting::combustionEventsPerEngineCycle, coreStatusA, Counters, Counter::crankSyncLosses, DWELL_ENABLE, dwellQueueLength, engineCyclePeriod, fixedConfig1::engineSettings, fixedConfigs1, IGNITION_ENABLE, ignitionQueueLength, injectorMainControlRegisters, injectorMainEnableMasks, injectorMainEndTimes, injectorMainOnMasks, injectorMainPulseWidthsRealtime, injectorMainStartTimesHolding, injectorMainTimeRegisters, injectorMinimumPulseWidth, injectorSwitchOffCodeTime, ISRLatencyVars, lastPrimaryPulseTimeStamp, LONGHALF, masterPulseWidth, mathSampleTimeStampRecord, nextDwellChannel, nextIgnitionChannel, PITCE, PITCNT0, PITCNT1, PITINTE, PITLD0, PITLD1, PITTF, PORTJ, PRIMARY_SYNC, ISRLatencyVar::primaryInputLatency, RuntimeVar::primaryInputLeadingRuntime, RuntimeVar::primaryInputTrailingRuntime, primaryLeadingEdgeTimeStamp, primaryPulsesPerSecondaryPulse, primaryTeethDroppedFromLackOfSync, Counter::primaryTeethSeen, PTIT, queuedDwellOffsets, queuedIgnitionOffsets, RPMRecord, RuntimeVars, sampleEachADC(), selfSetTimer, Counter::syncedADCreadings, TC0, TCNT, TFLG, TFLGOF, ticksPerCycleAtOneRPMx2, TIE, timeBetweenSuccessivePrimaryPulses, LongTime::timeLong, Clock::timeoutADCreadingClock, timerExtensionClock, LongTime::timeShorts, totalAngleAfterReferenceInjection, and trailingEdgeSecondaryRPMInputCodeTime.
00084 { 00085 /* Clear the interrupt flag for this input compare channel */ 00086 TFLG = 0x01; 00087 00088 /* Save all relevant available data here */ 00089 unsigned short codeStartTimeStamp = TCNT; /* Save the current timer count */ 00090 unsigned short edgeTimeStamp = TC0; /* Save the edge time stamp */ 00091 unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */ 00092 // unsigned short PORTS_BACurrentState = PORTS_BA; /* Save ignition output state */ 00093 00094 /* Calculate the latency in ticks */ 00095 ISRLatencyVars.primaryInputLatency = codeStartTimeStamp - edgeTimeStamp; 00096 00103 /* The LM1815 variable reluctance sensor amplifier allows the output to be 00104 * pulled high starting at the center of a tooth. So, what we see as the 00105 * start of a tooth is actually the centre of a physical tooth. Because 00106 * tooth shape, profile and spacing may vary this is the only reliable edge 00107 * for us to schedule from, hence the trailing edge code is very simple. 00108 */ 00109 if(PTITCurrentState & 0x01){ 00110 /* Echo input condition on J7 */ 00111 PORTJ |= 0x80; 00112 00113 // increment crank pulses TODO this needs to be wrapped in tooth period and width checking 00114 primaryPulsesPerSecondaryPulse++; 00115 00116 // calculate rough rpm (this will be wrong when the var is used correctly) 00117 *RPMRecord = ticksPerCycleAtOneRPMx2 / engineCyclePeriod; /* 0.8us ticks, 150mil = 2 x 60 seconds, times rpm scale factor of 2 */ 00118 00119 // don't run until the second trigger has come in and the period is correct (VERY temporary) 00120 if(!(coreStatusA & PRIMARY_SYNC)){ 00121 primaryTeethDroppedFromLackOfSync++; 00122 return; 00123 } 00124 00125 LongTime timeStamp; 00126 00127 /* Install the low word */ 00128 timeStamp.timeShorts[1] = edgeTimeStamp; 00129 /* Find out what our timer value means and put it in the high word */ 00130 if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */ 00131 timeStamp.timeShorts[0] = timerExtensionClock + 1; 00132 }else{ 00133 timeStamp.timeShorts[0] = timerExtensionClock; 00134 } 00135 00136 // temporary data from inputs 00137 primaryLeadingEdgeTimeStamp = timeStamp.timeLong; 00138 timeBetweenSuccessivePrimaryPulses = lastPrimaryPulseTimeStamp - primaryLeadingEdgeTimeStamp; 00139 lastPrimaryPulseTimeStamp = primaryLeadingEdgeTimeStamp; 00140 // timeBetweenSuccessivePrimaryPulsesBuffer = (timeBetweenSuccessivePrimaryPulses >> 1) + (timeBetweenSuccessivePrimaryPulsesBuffer >> 1); 00141 00142 // 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. 00143 00144 /* Check for loss of sync by too high a count */ 00145 if(primaryPulsesPerSecondaryPulse > 12){ 00146 /* Increment the lost sync count */ 00147 Counters.crankSyncLosses++; 00148 00149 /* Clear synced status */ 00150 coreStatusA &= CLEAR_PRIMARY_SYNC; 00151 00152 /* Reset the count of teeth */ 00153 primaryPulsesPerSecondaryPulse = 0; 00154 00155 /* Get the hell out of here before we do something bad */ 00156 return; 00157 } 00158 00159 // CAUTION came to me lying in bed half asleep idea : 00160 00161 // 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. 00162 00163 // if ign enabled 00164 // iterate through ignition first, schedule all of those 00165 // iterate through dwell next, schedule all of those 00166 // if fuel enabled 00167 // iterate through main fuel next, schedule all of those 00168 // if staging enabled and required 00169 // iterate through staged fuel last, 00170 00171 // 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. 00172 00173 // this will be done with an array and per tooth check in future 00174 if((primaryPulsesPerSecondaryPulse % 2) == 0){ 00175 00176 // TODO sample ADCs on teeth other than that used by the scheduler in order to minimise peak run time and get clean signals 00177 sampleEachADC(ADCArrays); 00178 Counters.syncedADCreadings++; 00179 *mathSampleTimeStampRecord = TCNT; 00180 00181 /* Set flag to say calc required */ 00182 coreStatusA |= CALC_FUEL_IGN; 00183 00184 /* Reset the clock for reading timeout */ 00185 Clocks.timeoutADCreadingClock = 0; 00186 00187 if(masterPulseWidth > injectorMinimumPulseWidth){ // use reference PW to decide. spark needs moving outside this area though TODO 00188 /* Determine if half the cycle is bigger than short-max */ 00189 unsigned short maxAngleAfter; 00190 if((engineCyclePeriod >> 1) > 0xFFFF){ 00191 maxAngleAfter = 0xFFFF; 00192 }else{ 00193 maxAngleAfter = (unsigned short)(engineCyclePeriod >> 1); 00194 } 00195 00196 /* Check advance to ensure it is less than 1/2 of the previous engine cycle and more than codetime away */ 00197 unsigned short advance; 00198 if(totalAngleAfterReferenceInjection > maxAngleAfter){ // if too big, make it max 00199 advance = maxAngleAfter; 00200 }else if(totalAngleAfterReferenceInjection < trailingEdgeSecondaryRPMInputCodeTime){ // if too small, make it min 00201 advance = trailingEdgeSecondaryRPMInputCodeTime; 00202 }else{ // else use it as is 00203 advance = totalAngleAfterReferenceInjection; 00204 } 00205 00206 // determine the long and short start times 00207 unsigned short startTime = primaryLeadingEdgeTimeStamp + advance; 00208 unsigned long startTimeLong = timeStamp.timeLong + advance; 00209 00210 /* Determine the channels to schedule */ 00211 unsigned char fuelChannel = (primaryPulsesPerSecondaryPulse / 2) - 1; 00212 unsigned char ignitionChannel = (primaryPulsesPerSecondaryPulse / 2) - 1; 00213 00214 if(fuelChannel > 5 || ignitionChannel > 5){ 00215 // send("bad fuel : "); 00216 // sendUC(fuelChannel); 00217 // send("bad ign : "); 00218 // sendUC(ignitionChannel); 00219 return; 00220 } 00221 00222 // determine whether or not to reschedule 00223 unsigned char reschedule = 0; 00224 unsigned long diff = startTimeLong - (injectorMainEndTimes[fuelChannel] + injectorSwitchOffCodeTime); 00225 if(diff > LONGHALF){ 00226 reschedule = 1; // http://www.diyefi.org/forum/viewtopic.php?f=8&t=57&p=861#p861 00227 } 00228 00229 // schedule the appropriate channel 00230 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 */ 00231 *injectorMainControlRegisters[fuelChannel] |= injectorMainEnableMasks[fuelChannel]; 00232 *injectorMainTimeRegisters[fuelChannel] = startTime; 00233 TIE |= injectorMainOnMasks[fuelChannel]; 00234 TFLG = injectorMainOnMasks[fuelChannel]; 00235 }else{ 00236 injectorMainStartTimesHolding[fuelChannel] = startTime; 00237 selfSetTimer |= injectorMainOnMasks[fuelChannel]; // setup a bit to let the timer interrupt know to set its own new start from a var 00238 } 00239 00240 // 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. 00241 // as do array indexs here and in the ISRs... 00242 00243 00244 // TODO implement mechanism for dropping a cylinder in event of over queueing or spark cut/round robin 00245 // important as ignition sequence disrupted when this occurs as it stands. 00246 00247 // 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 00248 00249 // DWELL 00250 00251 // If dwell is not currently enabled, set it all up 00252 if(!(PITCE & DWELL_ENABLE)){ 00253 /* Schedule Dwell event (do this first because it comes earliest. */ 00254 // set the channel to fire 00255 nextDwellChannel = ignitionChannel; 00256 00257 // set the time 00258 PITLD0 = advance; 00259 // PITLD0 = ignitionAdvances[ignitionChannel] - *currentDwellRealtime; BAD for various reasons! 00260 00261 // clear the flags first as they apparently become set any old time whether enabled or not. 00262 PITTF |= DWELL_ENABLE; 00263 00264 // turn on the ints 00265 PITINTE |= DWELL_ENABLE; 00266 00267 // clear the flags first as they apparently become set any old time whether enabled or not. 00268 PITTF |= DWELL_ENABLE; 00269 00270 // enable channels 00271 PITCE |= DWELL_ENABLE; 00272 }else if(dwellQueueLength == 0){ 00273 // load time offset such that next period is correct 00274 PITLD0 = (advance - PITCNT0); 00275 00276 // increment queue length 00277 dwellQueueLength++; 00278 }else if(dwellQueueLength > fixedConfigs1.engineSettings.combustionEventsPerEngineCycle){ //TODO sensible figures here for array index OOBE 00279 // do nothing, or increment a counter or something similar. 00280 }else{ 00281 unsigned short sumOfDwells = PITLD0; 00282 // add up the prequeued time periods 00283 00284 // queue = 1 pitld is all 00285 // queue = 2 one from 0 index of array AND pitld 00286 00287 unsigned char index = 0; 00288 while(index < (dwellQueueLength -1)){ 00289 sumOfDwells += queuedDwellOffsets[index]; 00290 index++; 00291 } 00292 // for(index = 0;index < (dwellQueueLength -1);index++){ // is this right? 00293 // sumOfDwells += queuedDwellOffsets[index]; 00294 // } 00295 00296 // store time offset in appropriate array location 00297 queuedDwellOffsets[dwellQueueLength - 1] = advance - (PITCNT0 + sumOfDwells); 00298 00299 // increment queue length from one or more 00300 dwellQueueLength++; 00301 } 00302 00303 // IGNITION experimental stuff 00304 00305 // If ignition is not currently enabled, set it all up 00306 if(!(PITCE & IGNITION_ENABLE)){ 00307 /* Schedule Ignition event (do this first because it comes earliest. */ 00308 // set the channel to fire 00309 nextIgnitionChannel = ignitionChannel; 00310 00311 // figure out the time to set the delay reg to 00312 PITLD1 = advance + injectorMainPulseWidthsRealtime[fuelChannel]; 00313 // PITLD1 = ignitionAdvances[ignitionChannel + outputBankIgnitionOffset]; 00314 00315 // clear the flags first as they apparently become set any old time whether enabled or not. 00316 PITTF |= IGNITION_ENABLE; 00317 00318 // turn on the ints 00319 PITINTE |= IGNITION_ENABLE; 00320 00321 // clear the flags first as they apparently become set any old time whether enabled or not. 00322 PITTF |= IGNITION_ENABLE; 00323 00324 // enable channels 00325 PITCE |= IGNITION_ENABLE; 00326 }else if(ignitionQueueLength == 0){ 00327 // load timer register 00328 PITLD1 = ((advance + injectorMainPulseWidthsRealtime[fuelChannel]) - PITCNT1); 00329 00330 // increment to 1 00331 ignitionQueueLength++; 00332 }else if(ignitionQueueLength > fixedConfigs1.engineSettings.combustionEventsPerEngineCycle){ //TODO sensible figures here for array index OOBE 00333 // do nothing, or increment a counter or something similar. 00334 }else{ 00335 unsigned short sumOfIgnitions = PITLD1; 00336 // add up the prequeued time periods 00337 00338 // queue = 1 pitld is all 00339 // queue = 2 one from 0 index of array AND pitld 00340 00341 00342 unsigned char index = 0; 00343 while(index < (ignitionQueueLength - 1)){ 00344 sumOfIgnitions += queuedIgnitionOffsets[index]; 00345 index++; 00346 } 00347 // for(index = 0;index < (ignitionQueueLength -1);index++){ // is this right? 00348 // sumOfIgnitions += queuedIgnitionOffsets[index]; 00349 // } 00350 00351 // store time offset in appropriate array location 00352 queuedIgnitionOffsets[ignitionQueueLength - 1] = advance - (PITCNT1 + sumOfIgnitions); 00353 00354 // increment from 1 or more 00355 ignitionQueueLength++; 00356 } 00357 } 00358 } 00359 RuntimeVars.primaryInputLeadingRuntime = TCNT - codeStartTimeStamp; 00360 }else{ 00361 PORTJ &= 0x7F; 00362 RuntimeVars.primaryInputTrailingRuntime = TCNT - codeStartTimeStamp; 00363 } 00364 00365 Counters.primaryTeethSeen++; 00366 // suss out rpm and accurate TDC reference 00367 00368 // if you say it quick, it doesn't sound like much : 00369 // schedule fuel and ign based on spark cut and fuel cut and timing vars and status vars config vars 00370 }
void SecondaryRPMISR | ( | void | ) |
Use the rising and falling edges...................
Secondary RPM ISR
Similar to the primary one.
Definition at line 380 of file NipponDenso.c.
References CLEAR_PRIMARY_SYNC, coreStatusA, Counters, Counter::crankSyncLosses, engineCyclePeriod, ISRLatencyVars, lastSecondaryOddTimeStamp, PORTJ, PORTM, PRIMARY_SYNC, primaryPulsesPerSecondaryPulse, PTIT, RuntimeVars, ISRLatencyVar::secondaryInputLatency, RuntimeVar::secondaryInputLeadingRuntime, RuntimeVar::secondaryInputTrailingRuntime, Counter::secondaryTeethSeen, TC1, TCNT, TFLG, TFLGOF, LongTime::timeLong, timerExtensionClock, and LongTime::timeShorts.
00380 { 00381 /* Clear the interrupt flag for this input compare channel */ 00382 TFLG = 0x02; 00383 00384 /* Save all relevant available data here */ 00385 unsigned short codeStartTimeStamp = TCNT; /* Save the current timer count */ 00386 unsigned short edgeTimeStamp = TC1; /* Save the timestamp */ 00387 unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */ 00388 // unsigned short PORTS_BACurrentState = PORTS_BA; /* Save ignition output state */ 00389 00390 /* Calculate the latency in ticks */ 00391 ISRLatencyVars.secondaryInputLatency = codeStartTimeStamp - edgeTimeStamp; 00392 00399 /* The LM1815 variable reluctance sensor amplifier allows the output to be 00400 * pulled high starting at the center of a tooth. So, what we see as the 00401 * start of a tooth is actually the centre of a physical tooth. Because 00402 * tooth shape, profile and spacing may vary this is the only reliable edge 00403 * for us to schedule from, hence the trailing edge code is very simple. 00404 */ 00405 if(PTITCurrentState & 0x02){ 00406 // echo input condition 00407 PORTJ |= 0x40; 00408 00409 // display the crank pulses 00410 PORTM = (char)primaryPulsesPerSecondaryPulse; 00411 00412 // was this code like this because of a good reason? 00413 // primaryPulsesPerSecondaryPulseBuffer = primaryPulsesPerSecondaryPulse; 00414 primaryPulsesPerSecondaryPulse = 0; 00415 00416 // if we didn't get the right number of pulses drop sync and start over 00417 if((primaryPulsesPerSecondaryPulse != 12) && (coreStatusA & PRIMARY_SYNC)){ 00418 coreStatusA &= CLEAR_PRIMARY_SYNC; 00419 Counters.crankSyncLosses++; 00420 } 00421 00422 LongTime timeStamp; 00423 00424 /* Install the low word */ 00425 timeStamp.timeShorts[1] = edgeTimeStamp; 00426 /* Find out what our timer value means and put it in the high word */ 00427 if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */ 00428 timeStamp.timeShorts[0] = timerExtensionClock + 1; 00429 }else{ 00430 timeStamp.timeShorts[0] = timerExtensionClock; 00431 } 00432 00433 // get the data we actually want 00434 engineCyclePeriod = 2 * (timeStamp.timeLong - lastSecondaryOddTimeStamp); // save the engine cycle period 00435 lastSecondaryOddTimeStamp = timeStamp.timeLong; // save this stamp for next time round 00436 00437 // Because this is our only reference, each time we get this pulse, we know where we are at (simple mode so far) 00438 coreStatusA |= PRIMARY_SYNC; 00439 RuntimeVars.secondaryInputLeadingRuntime = TCNT - codeStartTimeStamp; 00440 }else{ 00441 PORTJ &= 0xBF; 00442 RuntimeVars.secondaryInputTrailingRuntime = TCNT - codeStartTimeStamp; 00443 } 00444 00445 Counters.secondaryTeethSeen++; 00446 // suss out phase/engine cycle reference showing which bank we are on 00447 00448 /* 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 */ 00449 }