NipponDenso.c File Reference

Reads Nippon Denso 24/2 sensors. More...

#include "inc/freeEMS.h"
#include "inc/interrupts.h"
#include "inc/utils.h"

Include dependency graph for NipponDenso.c:

Go to the source code of this file.

Functions

void PrimaryRPMISR ()
void SecondaryRPMISR ()
 Use the rising and falling edges...................


Detailed Description

Reads Nippon Denso 24/2 sensors.

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:

Author:
Fred Cooke
Note:
Pseudo code that does not compile with zero warnings and errors MUST be commented out.
Todo:
TODO make this generic for evenly spaced teeth with a pulse per revolution from the second input.

Definition in file NipponDenso.c.


Function Documentation

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.

Author:
Fred Cooke
Warning:
These are for testing and demonstration only, not suitable for driving with just yet.
Todo:
TODO bring the above docs up to date with reality

TODO finish this off to a usable standard

Todo:
TODO fill in or remove the else

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 }

Here is the call graph for this function:

void SecondaryRPMISR ( void   ) 

Use the rising and falling edges...................

Secondary RPM ISR

Similar to the primary one.

Todo:
TODO bring this documentation up to date.

TODO finish this off to a usable standard.

Todo:
TODO fill in or remove the else

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 }


Generated on Mon Jan 26 00:17:12 2009 for FreeEMS by  doxygen 1.5.8