FreeEMS  0.2.0-SNAPSHOT-282-g9efc524
decoderInterface.h
Go to the documentation of this file.
1 /* FreeEMS - the open source engine management system
2  *
3  * Copyright 2010-2012 Fred Cooke
4  *
5  * This file is part of the FreeEMS project.
6  *
7  * FreeEMS software is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * FreeEMS software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
19  *
20  * We ask that if you make any changes to this file you email them upstream to
21  * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
22  *
23  * Thank you for choosing FreeEMS to run your engine!
24  */
25 
26 
27 /** @file
28  *
29  * @ingroup allHeaders
30  * @ingroup enginePositionRPMDecoders
31  *
32  * @brief Objects through which a decoder communicates.
33  *
34  * This file contains the declarations of objects used as a common interface
35  * between all of the different wheel decoders and the main loop scheduler.
36  */
37 
38 
39 /* Header file multiple inclusion protection courtesy eclipse Header Template */
40 /* and http://gcc.gnu.org/onlinedocs/gcc-3.1.1/cpp/ C pre processor manual */
41 #ifndef FILE_DECODER_INTERFACE_H_SEEN
42 #define FILE_DECODER_INTERFACE_H_SEEN
43 
44 
45 #include "syncLossIDs.h"
46 
47 
48 #ifdef EXTERN
49 #warning "EXTERN already defined by another header, please sort it out!"
50 #undef EXTERN /* If fail on warning is off, remove the definition such that we can redefine correctly. */
51 #endif
52 
53 
54 #ifdef DECODER_IMPLEMENTATION_C
55 #define EXTERN
56 #else
57 #define EXTERN extern
58 #endif
59 
60 
61 #define BENCH_TEST_NAME "BenchTest"
62 
63 // http://duckduckgo.com/?q=%281250+*+60%29+*+50
64 // 1250 ticks per milli second
65 // 1250000 ticks per second
66 // 60 seconds per minute
67 // 360 degrees per revolution
68 // 10 ticks per degree multiplier
69 // 2 rpm scaler
70 // unused, already excluded 50 units per degree
71 // 1250000 * 60 = 75000000
72 // 75000000 / 360 = 208333.333333333333333
73 // 208333.333333333333333 * 2 * 10 = 4166666.666666666666667
74 #define degreeTicksPerMinute 4166667 // rounded up by 1/3
75 #define ticks_per_degree_multiplier (10 * ANGLE_FACTOR) // FIX <<< shouldn't be done like this.
76 /// @todo TODO make this ^ scaling better x10 yields 64rpm minimum functional engine speed.
77 
78 
79 // ADC
80 
81 
82 /* Self set flags for starting from ECT out ISR code. */
83 EXTERN unsigned char selfSetTimer; /* Set the start time of injection at the end of the last one in the channels ISR instead of the input ISR */
84 
85 
86 // RPM - need some sort of state to say not to use these first time through...
87 EXTERN unsigned short ticksPerDegree0; // to be replaced with logging scheme for teeth.
88 EXTERN unsigned short ticksPerDegree1; // to be replaced with logging scheme for teeth.
89 EXTERN unsigned short* ticksPerDegree; // final output variable, probably move into inputVars struct?
90 EXTERN unsigned short* ticksPerDegreeRecord; // intermediate storage variable, do something with this?
91 
92 // Time/Sync
93 
94 /*
95 what is common between different setups? Add others here as required/desired/discussed.
96 
97  M-N setup - to sync we need gap that has just passed, and gap before, cases are: two gaps approx equal = who knows where. current gap = N * bigger means tooth after gap, current gap = N times smaller = second tooth after gap. Two opportunities to sync. by definition we always have these two gap measurements once synced. from these two gap measurements we can obtain a rough first RPM, then later RPM can be got from opposing teeth.
98  4and1 setup - sync is obtained via logic, to get a rough RPM we need only one gap measurement, to verify noise free operation we need two. When we sync we may or may not have a previous gap measurement. For one sync opportunity we could have 0, 1, 2 gaps, for another we could have 0 or 1 gaps, and for the rest we will not have a gap measurement till a subsequent event.
99  24and2 setup - sync is obtained simply by watching for second trigger, sync is maintained by checking that second trigger falls when it should again. When a second trigger arrives, we could have 0, 1, 2 - ~12 primary gaps measured
100  Simple setup - sync is not required, fire on all valid pulses. check for noise based on previous gaps, one event per ign event, correctly spaced timing required. Can be V twin, dizzy/points/outer of dsm/4and1 cas units, etc.
101 
102 From this we need:
103 
104 this stamp - have while running only. - so not required in header
105 previous stamp
106 stamp before previous stamp - only required on previous execution, at which time it is actually previous stamp, so not required.
107 
108 this gap - have while running only. - so not required in header
109 previous gap - stored last time
110 
111 flags to know if gap and stamp are valid - if gap is valid, stamp is not the oldest we effectively have.
112 previous gap valid - one flag
113 previous stamp valid - one flag
114 
115 event counter - single unsigned value, 256 enough? probably can't cope with more than that anyway, except at low rpm, which is useless.
116 
117 event angles - array of shorts, range required is 0 - 719.9999999999999 - can we provide sub degree accuracy with an unsigned short array? yes, what is best scale figure?
118 
119 States:
120 * no sync (not of or of other three, no flag required, obviously)
121 * cylinder sync - one flag
122 * crank sync - one flag
123 * cam sync - one flag
124 
125 from the above we can check one gap+angle with the next gap+angle and ensure smooth noise free operation is occurring.
126 
127 */
128 
129 // unsigned long thisEventTimeStamp; recommended variable naming, may be enforced for/with macro use
130 // unsigned long thisInterEventPeriod; ditto
131 /// @todo TODO sync loss/gain semantics - how paranoid? under what circumstances? should we make it configurable whether a decoder that is in a situation where it would find sync if not synced, resets sync, or loses sync. Likewise, at initial sync gain time, should it go "prelim sync found" and only verify sync on the second lap around, or start firing events straight off the bat. Starting will suck if paranoid, but if there is noise at high load/rpm and events get mis-scheduled before sync is lost, that is serious. This is philosophical, and the reality is that you must assume that your signal is clean to some level and verified clean under lower risk conditions.
135 EXTERN unsigned short lastTicksPerDegree;
138 EXTERN unsigned long skipEventFlags;
139 EXTERN unsigned char numberScheduled; /// @todo TODO remove DEBUG
140 EXTERN unsigned char syncConfirmationsRunningCounter; // TODO move to a struct?
141 EXTERN unsigned char syncConfirmationsStartingCounter; // TODO move to a struct?
142 
143 /// @todo Introduce the concept of sync level to schedule for if NOT synced
144 /// @todo and a way of deciding what to do in different sync states
145 /// @todo and proper dividers for pulsewidths
146 /// @todo and ability to lock pulsewidht/dwell for scheduling
147 /// @todo and generalise scheduling to all pins
148 /// @todo and provide a way of choosing a source of pulsewidth dwell or fuel duration
149 /// @todo and a way of allowing overly advanced scheduling instead of none, when its fuel
150 #define COMBUSTION_SYNC BIT0 ///< Sync sufficient for Dizzy/Batch Injection
151 #define CRANK_SYNC BIT1 ///< Sync sufficient for Wasted Spark/Semi-Sequential
152 #define CAM_SYNC BIT2 ///< Sync sufficient for COP/CNP/Sequential
153 #define LAST_TIMESTAMP_VALID BIT3 ///< Set when first decoder ISR runs post a reset
154 #define LAST_PERIOD_VALID BIT4 ///< Set when second decoder ISR runs post a reset
155 #define LAST_MATCH_VALID BIT5 ///< Missing teeth style decoders set this when a valid match is found
156 #define LAST_TPD_VALID BIT6 ///< Set once sync is found and we've stored a Ticks Per Degree value
157 #define OK_TO_SCHEDULE BIT7 ///< Sync confirmed OK by configured number of checks
158 // WARNING: Entire flag var is cleared with loss of sync!
159 
160 
161 #define ARBITRARY_DECODER_NAME_MAX_LENGTH 64
162 #define SIZE_OF_EVENT_ARRAYS 256
163 #if (SIZE_OF_EVENT_ARRAYS > 256)
164 #error "Event array size larger than variable used to index it!"
165 #endif // Is it paranoid to check myself like this? :-)
166 
167 
168 // These are defined per decoder and used elsewhere!
169 EXTERN const unsigned char decoderName[sizeof(BASE_FILE_NAME)];
170 EXTERN const unsigned char numberOfRealEvents; // How many unique events the decoder sees.
171 EXTERN const unsigned char numberOfVirtualEvents; // How many of the members of the eventAngles array are valid. (multiples of real events (1 - 12))
172 EXTERN const unsigned short eventAngles[SIZE_OF_EVENT_ARRAYS]; /// @todo TODO From 0 - totalEventAngleRange degrees, scale: x50
173 EXTERN const unsigned char eventValidForCrankSync[SIZE_OF_EVENT_ARRAYS]; // For decoders with crank sync possible before cam sync, mark which events are eligble for crank scheduling here 0 = not valid, anything else = valid
174 EXTERN const unsigned short totalEventAngleRange; // 720 for a four stroke, 360 for a two stroke, ? for a rotary. move this to code with a single setting for engine type and generate transformations based on that? All decoders will be 720 for now and only support 4 strokes without hackage.
175 EXTERN const unsigned short decoderMaxCodeTime; // The max of how long the primary and secondary ISRs take to run with worst case scheduling loop time!
176 
177 
178 #define SET_SYNC_LEVEL_TO(SYNC_LEVEL) \
179 /* Otherwise caught-on event would be reset constantly */ \
180 if(!(KeyUserDebugs.decoderFlags & SYNC_LEVEL)){ \
181  KeyUserDebugs.decoderFlags |= SYNC_LEVEL; \
182  KeyUserDebugs.syncCaughtOnThisEvent = KeyUserDebugs.currentEvent; \
183 } \
184  \
185 /* Reason for last loss of sync was not timeout (0) */ \
186 if(KeyUserDebugs.syncLostWithThisID){ \
187  if(syncConfirmationsRunningCounter){ \
188  syncConfirmationsRunningCounter--; \
189  }else{ \
190  KeyUserDebugs.decoderFlags |= OK_TO_SCHEDULE; \
191  } \
192 }else{ \
193  if(syncConfirmationsStartingCounter){ \
194  syncConfirmationsStartingCounter--; \
195  }else{ \
196  KeyUserDebugs.decoderFlags |= OK_TO_SCHEDULE; \
197  } \
198 } // End of macro.
199 
200 
201 #define SCHEDULE_ONE_ECT_OUTPUT() \
202 if(outputEventExtendNumberOfRepeats[outputEventNumber] > 0){ \
203  *ectMainControlRegisters[pin] &= ectMainDisableMasks[pin]; \
204  outputEventExtendNumberOfRepeatsRealtime[pin] = outputEventExtendNumberOfRepeats[outputEventNumber]; \
205  outputEventExtendNumberOfRepeatsRealtime[pin]--; \
206  outputEventExtendRepeatPeriodRealtime[pin] = outputEventExtendRepeatPeriod[outputEventNumber]; \
207  outputEventDelayFinalPeriodRealtime[pin] = outputEventDelayFinalPeriod[outputEventNumber]; \
208  *ectMainTimeRegisters[pin] = timeStamp.timeShorts[1] + outputEventExtendRepeatPeriod[outputEventNumber]; \
209  Counters.pinScheduledWithTimerExtension++; \
210 }else{ \
211  *ectMainControlRegisters[pin] |= ectMainEnableMasks[pin]; \
212  *ectMainTimeRegisters[pin] = startTime; \
213  Counters.pinScheduledToGoHigh++; \
214 } \
215 TIE |= ectMainOnMasks[pin]; \
216 TFLG = ectMainOnMasks[pin]; \
217 outputEventPulseWidthsRealtime[pin] = outputEventPulseWidthsMath[outputEventNumber]; \
218 selfSetTimer &= ectMainOffMasks[pin]; // End of macro block!
219 
220 
221 #ifdef DECODER_IMPLEMENTATION_C // See above for information on how to set these values up.
222 
223 /// @todo TODO behave differently depending upon sync level?
224 #define SCHEDULE_ECT_OUTPUTS() \
225 numberScheduled = 0; \
226 if(KeyUserDebugs.decoderFlags & OK_TO_SCHEDULE){ \
227  unsigned char outputEventNumber; \
228  for(outputEventNumber=0;outputEventNumber<fixedConfigs1.schedulingSettings.numberOfConfiguredOutputEvents;outputEventNumber++){ \
229  if(outputEventInputEventNumbers[outputEventNumber] == KeyUserDebugs.currentEvent){ \
230  skipEventFlags &= ~(1UL << outputEventNumber); \
231  schedulePortTPin(outputEventNumber, timeStamp); \
232  numberScheduled++; \
233  }else if(skipEventFlags & (1UL << outputEventNumber)){ \
234  unsigned char eventBeforeCurrent = 0; \
235  if(KeyUserDebugs.currentEvent == 0){ \
236  eventBeforeCurrent = numberOfRealEvents - 1; \
237  }else{ \
238  eventBeforeCurrent = KeyUserDebugs.currentEvent - 1; \
239  } \
240  \
241  if(outputEventInputEventNumbers[outputEventNumber] == eventBeforeCurrent){ \
242  schedulePortTPin(outputEventNumber, timeStamp); \
243  numberScheduled++; \
244  } \
245  } \
246  } \
247 } // End of macro block!
248 
249 
250 // A value of zero = do nothing
251 #define COARSE_BB_IGNORE 0
252 #define COARSE_BB_GO_ON 1
253 #define COARSE_BB_GO_OFF 2
254 #define COARSE_BB_TOGGLE 3
255 #define COARSE_BB_MASK 0x03
256 
257 #define OUTPUT_COARSE_BBS() \
258 if(fixedConfigs1.coarseBitBangSettings.outputActions[KeyUserDebugs.currentEvent]){ \
259  int offset; \
260  for(offset=0;offset<fixedConfigs1.coarseBitBangSettings.numberConfigured;offset++){ \
261  unsigned char behaviour = (fixedConfigs1.coarseBitBangSettings.outputActions[KeyUserDebugs.currentEvent] >> (offset*2)) & COARSE_BB_MASK; \
262  if(behaviour){ \
263  if(behaviour == COARSE_BB_GO_ON){ \
264  *(fixedConfigs1.coarseBitBangSettings.ports[offset]) |= fixedConfigs1.coarseBitBangSettings.masks[offset]; \
265  }else if(behaviour == COARSE_BB_GO_OFF){ \
266  *(fixedConfigs1.coarseBitBangSettings.ports[offset]) &= (unsigned char)~(fixedConfigs1.coarseBitBangSettings.masks[offset]); \
267  }else if(behaviour == COARSE_BB_TOGGLE){ \
268  *(fixedConfigs1.coarseBitBangSettings.ports[offset]) ^= fixedConfigs1.coarseBitBangSettings.masks[offset]; \
269  } \
270  } \
271  } \
272 } // End of macro block!
273 
274 
275 // These give a warning in eclipse because they aren't defined in this file, they are defined per decoder and enforced here.
276 #ifndef DECODER_MAX_CODE_TIME
277 #error "Define your code max runtime conservatively at first, then optimise once the code is complete."
278 #endif
279 #ifndef NUMBER_OF_REAL_EVENTS
280 #error "Define how many unique events your decoder sees!"
281 #endif
282 #ifndef NUMBER_OF_VIRTUAL_EVENTS
283 #error "Define the length of the event array!"
284 #endif
285 #if ((NUMBER_OF_VIRTUAL_EVENTS % NUMBER_OF_REAL_EVENTS) != 0)
286 #error "Virtual events should be a multiple of real events!"
287 #endif
288 
289 const unsigned char numberOfRealEvents = NUMBER_OF_REAL_EVENTS;
290 const unsigned char numberOfVirtualEvents = NUMBER_OF_VIRTUAL_EVENTS;
291 const unsigned short totalEventAngleRange = ANGLE(720); //TOTAL_EVENT_ANGLE_RANGE;
292 const unsigned short decoderMaxCodeTime = DECODER_MAX_CODE_TIME;
293 const unsigned char decoderName[] = BASE_FILE_NAME;
294 
295 #endif
296 
297 
298 /// @todo TODO two unsigned chars, and two unsigned shorts, which is the MAP ADC value, the MAP value is sampled on every event in a cycle, and if less than the previous stored value, which is reset at every zeroth event, with the old value and old event number stored globally.
299 /// @todo TODO the same thing could be done, but with a median filter or similar, perhaps map sampling could be done dymanically like this, though it could yield unpredictable results, it could also yield the best running engines, just a thought...
300 
301 
302 // Scheduling
303 
304 //// Config items: These must exist in flash only config, not here...
305 //EXTERN const unsigned char ADCSampleEvents[12];
306 //EXTERN const unsigned char numberOfOutputEvents;
307 
308 EXTERN unsigned char outputEventInputEventNumbers[MAX_NUMBER_OF_OUTPUT_EVENTS]; // 0xFF (disabled) by default, populated to actual input event numbers by the scheduler
309 
315 
320 
325 
327 
328 
329 
330 /* Register addresses */
331 EXTERN volatile unsigned short * volatile ectMainTimeRegisters[ECT_CHANNELS]; // Static during a run, setup at init, shouldn't be in RAM, FIXME
332 EXTERN volatile unsigned char * volatile ectMainControlRegisters[ECT_CHANNELS]; // Static during a run, setup at init, shouldn't be in RAM, FIXME
333 
334 
335 /* Timer holding vars (init not required) */
336 EXTERN unsigned long ectMainEndTimes[ECT_CHANNELS]; // Used for scheduling calculations
337 /* Channel latencies (init not required) */
338 EXTERN unsigned short ectCodeLatencies[ECT_CHANNELS]; // Used for output control in a dysfunctional way.
339 
340 
341 /* Code time to run variables (init not required) */
342 EXTERN unsigned short ectCodeOpenRuntimes[ECT_CHANNELS]; // Stats only, remove or change to something accessible
343 EXTERN unsigned short ectCodeCloseRuntimes[ECT_CHANNELS]; // Stats only, remove or change to something accessible
344 
345 
346 /// @todo TODO Perhaps use some of the space freed by shrinking all timing tables for this:
347 ////unsigned long wheelEventTimeStamps[numberOfWheelEvents]; // For logging wheel patterns as observed
348 // Could be useful for really nice RPM readings done in the main loop.
349 // Logging of this nature will use the serial buffer which it will hold a lock over for the duration of the log.
350 
351 
352 // Helpers - force all these to be inlined!
353 void decoderInitPreliminary(void);
354 void perDecoderReset(void);
355 void descheduleAll(void);
356 void resetToNonRunningState(unsigned char uniqueLossID);
357 void schedulePortTPin(unsigned char pin, LongTime timeStamp);
358 /** @todo TODO add shared function here that takes a long time stamp and stores
359  * it in an array pointed to by a var with a flag saying "do it or not",
360  * populate array entry, check pointer, set send flag, and unset record flag OR
361  * increment pointer and return. Add call to this from all decoders. Add code
362  * to interact with this in commsCore.c and/or main.c
363  *
364  * probably need to think it through a bit more to support both inputs at the
365  * same time. Should also have 16 bits as an option for the purposes of
366  * increased storage and not needing 32 bit resolution at higher revs. Better
367  * to record stamps or diffs? Perhaps set the relative array sizes in a config
368  * var such that if we expect 8 primary for every 2 secondary, then one array
369  * is 4x as big as the other, and the population routine knows this. Think
370  * about how to decode it later too in olv/mtx.
371  */
372 
373 
374 /// @todo TODO add xgate scheduling functions here! Sean, looking forward to it, but after LT1 goes :-)
375 
376 
377 #undef EXTERN
378 
379 
380 /** @todo TODO IDEA: use a two stage mapping scheme for sched. Such that you
381  * have event number to joiner number in an unsigned char array such that event
382  * 4 and event 7 both are assigned join number 2, then pin X is scheduled to
383  * run on join number 2 which could be on any number of real events!! GREAT!
384  */
385 
386 
387 
388 /**
389  * RPM Calculations:
390  *
391  * Either need a per decoder function that is called from the main loop OR...
392  * RPM calculation is done in real time inside one of the RPM interrupts OR...
393  * The choice of either, up to the decoder to decide how it is done, in this
394  * case the function can either do nothing, or swap some pointers/var values
395  * around or similar.
396  *
397  * MAP Sampling:
398  *
399  * Max number of samples = max number of cylinders, has to be handled by
400  * decoder due to potential mismatch between wheel pattern and cylinder firing
401  * pattern unless it is done on a rough multiple sample basis in generic code
402  * that runs often and can approximate timing/position for sampling.
403  *
404  * Scheduling:
405  *
406  * Fueling pins could be expected to fire once per cylinder event (1 - 12), or
407  * once per engine cycle, or something in between, but what is a reasonable
408  * maximum, and is it workable to allow some cases and not others?
409  *
410  * |-----------------------------------------------------------|
411  * | Pins |
412  * |-----------------------------------------------------------|
413  * | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
414  * -----------|-----------------------------------------------------------|
415  * | | 1 | | | | | | | | | | | | |
416  * | |----|-----------------------------------------------------------|
417  * | | 2 | | | | | | | | | | | | |
418  * | |----|-----------------------------------------------------------|
419  * | | 3 | | | | | | | | | | | | |
420  * | |----|-----------------------------------------------------------|
421  * | | 4 | | | | | | | | | | | | |
422  * | |----|-----------------------------------------------------------|
423  * | | 5 | | | | | | | | | | | | |
424  * | |----|-----------------------------------------------------------|
425  * | | 6 | | | | | | | | | | | | |
426  * | Cyl |----|-----------------------------------------------------------|
427  * | | 7 | | | | | | | | | | | | |
428  * | |----|-----------------------------------------------------------|
429  * | | 8 | | | | | | | | | | | | |
430  * | |----|-----------------------------------------------------------|
431  * | | 9 | | | | | | | | | | | | |
432  * | |----|-----------------------------------------------------------|
433  * | | 10 | | | | | | | | | | | | |
434  * | |----|-----------------------------------------------------------|
435  * | | 11 | | | | | | | | | | | | |
436  * | |----|-----------------------------------------------------------|
437  * | | 12 | | | | | | | | | | | | |
438  * -----------|-----------------------------------------------------------|
439  *
440  *
441  * Ignition pins will only need to be fired once per cycle (COP/CNP), twice per
442  * cycle (Wasted Spark) or once per cylinder event (distributor) unless
443  * multiple-spark startup is required, however this could be done with cascaded
444  * dwell events, timer self-set:
445  *
446  * on dwell off spark on dwell off spark on dwell off and disable spark
447  *
448  * This example is for triple spark, 2 or more than 3 are also possible.
449  *
450  * When coils are wired one per cylinder for COP/CNP, during starting or loss
451  * of cam sync two opposing coils will be fired at the same time in pseudo
452  * wasted spark mode.
453  *
454  * Possible states of sync are as follows:
455  *
456  * Full cycle engine sync - COP/CNP/Sequential
457  * Half cycle revolution sync - Wasted Spark/Semi Sequential
458  * Cylinder sync - Distributor/Many pulses or un-timed batch pulses
459  *
460  * Wheels will have various patterns of rising/falling edges. Scheduling may be
461  * done from either the rising or falling edge on some, or only on one edge of
462  * others. VR sensors have only one reliable edge, the other varies with speed
463  * and associated rise/fall times of the approximately sinusoidal wave form.
464  * Simple patterns shall be required to be timed to the engine such that at low
465  * engine speeds the timer delays available with high accuracy are sufficient
466  * to properly time ignition events. At the least, there should be a wheel
467  * event per cylinder event, and in close proximity to that cylinder event.
468  * Allowing even more relaxed wheel patterns would mean compromising the
469  * performance for other more common setups and/or increasing code complexity
470  * by an unacceptable amount.
471  *
472  * OLD notes:
473  *
474  * arrays of output channels, iterate and check for wheel event number, if matched, fire:
475  * doesn't allow for firing a pin more than once a cycle!! no use.
476  * allows multi channel on a single wheel event (virtually useless) but is slow, one loop
477  * and array per type of output channel.
478  *
479  * array of wheel events, do lookup for wheel event, then if output channel valid, schedule it.
480  * single channel per tooth event (acceptable, wire in parallel if required), fast, memory hog,
481  * need one array per type of channel, array length = max expected tooth count! do we need to
482  * support every single tooth on a Nissan 360 style decoder or just cyl event count, what about
483  * Porsche 130? next stop is 60, seems good. can we use bit-fields to save memory, 1 bit =
484  * 2 possible pins, 2 bits = 4, etc, this will be slower, though. probably just an unsigned char[]
485  */
486 
487 
488 // Init routine:
489 //
490 // Allow configuration of timer details? tick size? If so, need to introduce scaling to calcs to
491 // produce correct tick count and therefore pulsewidth. Migrate dead time to time units and scale
492 // to get ticks to add to final pw.
493 //
494 // We require some configuration to allow the Nissan style decoder to use the pulse accumulators to
495 // count those one degree slots accurately to a high rpm without excessive cpu load.
496 
497 
498 // move the following to fuel calcs or sched code header, it doesn't belong here...
499 //
500 // stuff to do with timing and sync etc. ie, figuring out upon which
501 
502 
503 
504 #else
505  /* let us know if we are being untidy with headers */
506  #warning "Header file DECODER_INTERFACE_H seen before, sort it out!"
507 /* end of the wrapper ifdef from the very top */
508 #endif