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