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:
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 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 }

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:
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 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 }


Generated on Wed May 26 03:59:56 2010 for FreeEMS by  doxygen 1.5.6