NipponDenso.c File Reference

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

#include "inc/freeEMS.h"
#include "inc/interrupts.h"
#include "inc/DecoderInterface.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 discard narrow ones! test for tooth width and tooth period the width should be based on how the hardware is setup. IE the LM1815 is adjusted to have a pulse output of a particular width. This noise filter should be matched to that width as should the hardware filter.

Definition at line 85 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.

00085                     {
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 }

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 discard narrow ones! test for tooth width and tooth period the width should be based on how the hardware is setup. IE the LM1815 is adjusted to have a pulse output of a particular width. This noise filter should be matched to that width as should the hardware filter.

Definition at line 381 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.

00381                       {
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 }

Generated on Sat Oct 16 21:29:19 2010 for FreeEMS by  doxygen 1.6.3