Remove all superfluous author tags with just my name in them. Update many copyright...
[freeems-vanilla.git] / src / inc / decoderInterface.h
blob0c7a481b5b3383a8b482e0bc4d2cc5a2a8912946
1 /* FreeEMS - the open source engine management system
3 * Copyright 2010-2012 Fred Cooke
5 * This file is part of the FreeEMS project.
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.
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.
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/
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!
23 * Thank you for choosing FreeEMS to run your engine!
27 /** @file
29 * @ingroup allHeaders
30 * @ingroup enginePositionRPMDecoders
32 * @brief Objects through which a decoder communicates.
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.
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
45 #include "syncLossIDs.h"
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
54 #ifdef DECODER_IMPLEMENTATION_C
55 #define EXTERN
56 #else
57 #define EXTERN extern
58 #endif
61 #define BENCH_TEST_NAME "BenchTest"
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.
79 // ADC
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 */
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?
92 // Time/Sync
95 what is common between different setups? Add others here as required/desired/discussed.
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.
102 From this we need:
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.
108 this gap - have while running only. - so not required in header
109 previous gap - stored last time
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
115 event counter - single unsigned value, 256 enough? probably can't cope with more than that anyway, except at low rpm, which is useless.
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?
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
125 from the above we can check one gap+angle with the next gap+angle and ensure smooth noise free operation is occurring.
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.
132 EXTERN unsigned long lastEventTimeStamp;
133 EXTERN unsigned long lastPrimaryEventTimeStamp;
134 EXTERN unsigned long lastSecondaryEventTimeStamp;
135 EXTERN unsigned short lastTicksPerDegree;
136 EXTERN unsigned short lastPrimaryTicksPerDegree;
137 EXTERN unsigned short lastSecondaryTicksPerDegree;
138 EXTERN unsigned long skipEventFlags;
139 EXTERN unsigned char numberScheduled; /// @todo TODO remove DEBUG
141 /// @todo Introduce the concept of sync level to schedule for if NOT synced
142 /// @todo and a way of deciding what to do in different sync states
143 /// @todo and proper dividers for pulsewidths
144 /// @todo and ability to lock pulsewidht/dwell for scheduling
145 /// @todo and generalise scheduling to all pins
146 /// @todo and provide a way of choosing a source of pulsewidth dwell or fuel duration
147 /// @todo and a way of allowing overly advanced scheduling instead of none, when its fuel
148 #define COMBUSTION_SYNC BIT0 ///< Sync sufficient for Dizzy/Batch Injection
149 #define CRANK_SYNC BIT1 ///< Sync sufficient for Wasted Spark/Semi-Sequential
150 #define CAM_SYNC BIT2 ///< Sync sufficient for COP/CNP/Sequential
151 #define LAST_TIMESTAMP_VALID BIT3 ///< Set when first decoder ISR runs post a reset
152 #define LAST_PERIOD_VALID BIT4 ///< Set when second decoder ISR runs post a reset
153 #define LAST_MATCH_VALID BIT5 ///< Missing teeth style decoders set this when a valid match is found
154 #define LAST_TPD_VALID BIT6 ///< Set once sync is found and we've stored a Ticks Per Degree value
155 #define DF_SPARE_7 BIT7
156 // WARNING: Entire flag var is cleared with loss of sync!
159 #define ARBITRARY_DECODER_NAME_MAX_LENGTH 64
160 #define SIZE_OF_EVENT_ARRAYS 256
161 #if (SIZE_OF_EVENT_ARRAYS > 256)
162 #error "Event array size larger than variable used to index it!"
163 #endif // Is it paranoid to check myself like this? :-)
166 // These are defined per decoder and used elsewhere!
167 EXTERN const unsigned char decoderName[sizeof(BASE_FILE_NAME)];
168 EXTERN const unsigned char numberOfRealEvents; // How many unique events the decoder sees.
169 EXTERN const unsigned char numberOfVirtualEvents; // How many of the members of the eventAngles array are valid. (multiples of real events (1 - 12))
170 EXTERN const unsigned short eventAngles[SIZE_OF_EVENT_ARRAYS]; /// @todo TODO From 0 - totalEventAngleRange degrees, scale: x50
171 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
172 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.
173 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 #define SET_SYNC_LEVEL_TO(SYNC_LEVEL) \
177 KeyUserDebugs.decoderFlags |= SYNC_LEVEL; \
178 KeyUserDebugs.syncCaughtOnThisEvent = KeyUserDebugs.currentEvent; // End of macro.
181 #define SCHEDULE_ONE_ECT_OUTPUT() \
182 if(outputEventExtendNumberOfRepeats[outputEventNumber] > 0){ \
183 *injectorMainControlRegisters[pin] &= injectorMainDisableMasks[pin]; \
184 outputEventExtendNumberOfRepeatsRealtime[pin] = outputEventExtendNumberOfRepeats[outputEventNumber]; \
185 outputEventExtendNumberOfRepeatsRealtime[pin]--; \
186 outputEventExtendRepeatPeriodRealtime[pin] = outputEventExtendRepeatPeriod[outputEventNumber]; \
187 outputEventDelayFinalPeriodRealtime[pin] = outputEventDelayFinalPeriod[outputEventNumber]; \
188 *injectorMainTimeRegisters[pin] = timeStamp.timeShorts[1] + outputEventExtendRepeatPeriod[outputEventNumber]; \
189 Counters.pinScheduledWithTimerExtension++; \
190 }else{ \
191 *injectorMainControlRegisters[pin] |= injectorMainEnableMasks[pin]; \
192 *injectorMainTimeRegisters[pin] = startTime; \
193 Counters.pinScheduledToGoHigh++; \
195 TIE |= injectorMainOnMasks[pin]; \
196 TFLG = injectorMainOnMasks[pin]; \
197 outputEventPulseWidthsRealtime[pin] = outputEventPulseWidthsMath[outputEventNumber]; \
198 selfSetTimer &= injectorMainOffMasks[pin]; // End of macro block!
201 #ifdef DECODER_IMPLEMENTATION_C // See above for information on how to set these values up.
203 /// @todo TODO behave differently depending upon sync level?
204 #define SCHEDULE_ECT_OUTPUTS() \
205 numberScheduled = 0; \
206 unsigned char outputEventNumber; \
207 for(outputEventNumber=0;outputEventNumber<fixedConfigs1.schedulingSettings.numberOfConfiguredOutputEvents;outputEventNumber++){ \
208 if(outputEventInputEventNumbers[outputEventNumber] == KeyUserDebugs.currentEvent){ \
209 skipEventFlags &= ~(1UL << outputEventNumber); \
210 schedulePortTPin(outputEventNumber, timeStamp); \
211 numberScheduled++; \
212 }else if(skipEventFlags & (1UL << outputEventNumber)){ \
213 unsigned char eventBeforeCurrent = 0; \
214 if(KeyUserDebugs.currentEvent == 0){ \
215 eventBeforeCurrent = numberOfRealEvents - 1; \
216 }else{ \
217 eventBeforeCurrent = KeyUserDebugs.currentEvent - 1; \
220 if(outputEventInputEventNumbers[outputEventNumber] == eventBeforeCurrent){ \
221 schedulePortTPin(outputEventNumber, timeStamp); \
222 numberScheduled++; \
225 } // End of macro block!
228 // A value of zero = do nothing
229 #define COARSE_BB_IGNORE 0
230 #define COARSE_BB_GO_ON 1
231 #define COARSE_BB_GO_OFF 2
232 #define COARSE_BB_TOGGLE 3
233 #define COARSE_BB_MASK 0x03
235 #define OUTPUT_COARSE_BBS() \
236 if(fixedConfigs1.coarseBitBangSettings.outputActions[KeyUserDebugs.currentEvent]){ \
237 int offset; \
238 for(offset=0;offset<fixedConfigs1.coarseBitBangSettings.numberConfigured;offset++){ \
239 unsigned char behaviour = (fixedConfigs1.coarseBitBangSettings.outputActions[KeyUserDebugs.currentEvent] >> (offset*2)) & COARSE_BB_MASK; \
240 if(behaviour){ \
241 if(behaviour == COARSE_BB_GO_ON){ \
242 *(fixedConfigs1.coarseBitBangSettings.ports[offset]) |= fixedConfigs1.coarseBitBangSettings.masks[offset]; \
243 }else if(behaviour == COARSE_BB_GO_OFF){ \
244 *(fixedConfigs1.coarseBitBangSettings.ports[offset]) &= (unsigned char)~(fixedConfigs1.coarseBitBangSettings.masks[offset]); \
245 }else if(behaviour == COARSE_BB_TOGGLE){ \
246 *(fixedConfigs1.coarseBitBangSettings.ports[offset]) ^= fixedConfigs1.coarseBitBangSettings.masks[offset]; \
250 } // End of macro block!
253 // These give a warning in eclipse because they aren't defined in this file, they are defined per decoder and enforced here.
254 #ifndef DECODER_MAX_CODE_TIME
255 #error "Define your code max runtime conservatively at first, then optimise once the code is complete."
256 #endif
257 #ifndef NUMBER_OF_REAL_EVENTS
258 #error "Define how many unique events your decoder sees!"
259 #endif
260 #ifndef NUMBER_OF_VIRTUAL_EVENTS
261 #error "Define the length of the event array!"
262 #endif
263 #if ((NUMBER_OF_VIRTUAL_EVENTS % NUMBER_OF_REAL_EVENTS) != 0)
264 #error "Virtual events should be a multiple of real events!"
265 #endif
267 const unsigned char numberOfRealEvents = NUMBER_OF_REAL_EVENTS;
268 const unsigned char numberOfVirtualEvents = NUMBER_OF_VIRTUAL_EVENTS;
269 const unsigned short totalEventAngleRange = ANGLE(720); //TOTAL_EVENT_ANGLE_RANGE;
270 const unsigned short decoderMaxCodeTime = DECODER_MAX_CODE_TIME;
271 const unsigned char decoderName[] = BASE_FILE_NAME;
273 #endif
276 /// @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.
277 /// @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...
280 // Scheduling
282 //// Config items: These must exist in flash only config, not here...
283 //EXTERN const unsigned char ADCSampleEvents[12];
284 //EXTERN const unsigned char numberOfOutputEvents;
286 EXTERN unsigned char outputEventInputEventNumbers[MAX_NUMBER_OF_OUTPUT_EVENTS]; // 0xFF (disabled) by default, populated to actual input event numbers by the scheduler
288 EXTERN unsigned short outputEventPulseWidthsMath[MAX_NUMBER_OF_OUTPUT_EVENTS];
289 EXTERN unsigned char outputEventExtendNumberOfRepeats[MAX_NUMBER_OF_OUTPUT_EVENTS];
290 EXTERN unsigned short outputEventExtendRepeatPeriod[MAX_NUMBER_OF_OUTPUT_EVENTS];
291 EXTERN unsigned short outputEventDelayFinalPeriod[MAX_NUMBER_OF_OUTPUT_EVENTS];
292 EXTERN unsigned long outputEventDelayTotalPeriod[MAX_NUMBER_OF_OUTPUT_EVENTS];
294 EXTERN unsigned short outputEventPulseWidthsHolding[INJECTION_CHANNELS];
295 EXTERN unsigned char outputEventExtendNumberOfRepeatsHolding[INJECTION_CHANNELS];
296 EXTERN unsigned short outputEventExtendRepeatPeriodHolding[INJECTION_CHANNELS];
297 EXTERN unsigned short outputEventDelayFinalPeriodHolding[INJECTION_CHANNELS];
299 EXTERN unsigned short outputEventPulseWidthsRealtime[INJECTION_CHANNELS];
300 EXTERN unsigned char outputEventExtendNumberOfRepeatsRealtime[INJECTION_CHANNELS];
301 EXTERN unsigned short outputEventExtendRepeatPeriodRealtime[INJECTION_CHANNELS];
302 EXTERN unsigned short outputEventDelayFinalPeriodRealtime[INJECTION_CHANNELS];
304 EXTERN unsigned short injectorMainStartOffsetHolding[INJECTION_CHANNELS];
308 /* Register addresses */
309 EXTERN volatile unsigned short * volatile injectorMainTimeRegisters[INJECTION_CHANNELS]; // Static during a run, setup at init, shouldn't be in RAM, FIXME
310 EXTERN volatile unsigned char * volatile injectorMainControlRegisters[INJECTION_CHANNELS]; // Static during a run, setup at init, shouldn't be in RAM, FIXME
313 /* Timer holding vars (init not required) */
314 EXTERN unsigned long injectorMainEndTimes[INJECTION_CHANNELS]; // Used for scheduling calculations
315 /* Channel latencies (init not required) */
316 EXTERN unsigned short injectorCodeLatencies[INJECTION_CHANNELS]; // Used for injector control in a dysfunctional way.
319 /* Code time to run variables (init not required) */
320 EXTERN unsigned short injectorCodeOpenRuntimes[INJECTION_CHANNELS]; // Stats only, remove or change to something accessible
321 EXTERN unsigned short injectorCodeCloseRuntimes[INJECTION_CHANNELS]; // Stats only, remove or change to something accessible
324 /// @todo TODO Perhaps use some of the space freed by shrinking all timing tables for this:
325 ////unsigned long wheelEventTimeStamps[numberOfWheelEvents]; // For logging wheel patterns as observed
326 // Could be useful for really nice RPM readings done in the main loop.
327 // Logging of this nature will use the serial buffer which it will hold a lock over for the duration of the log.
330 // Helpers - force all these to be inlined!
331 EXTERN void decoderInitPreliminary(void);
332 EXTERN void perDecoderReset(void);
333 EXTERN void resetToNonRunningState(unsigned char uniqueLossID);
334 EXTERN void schedulePortTPin(unsigned char pin, LongTime timeStamp);
335 /** @todo TODO add shared function here that takes a long time stamp and stores
336 * it in an array pointed to by a var with a flag saying "do it or not",
337 * populate array entry, check pointer, set send flag, and unset record flag OR
338 * increment pointer and return. Add call to this from all decoders. Add code
339 * to interact with this in commsCore.c and/or main.c
341 * probably need to think it through a bit more to support both inputs at the
342 * same time. Should also have 16 bits as an option for the purposes of
343 * increased storage and not needing 32 bit resolution at higher revs. Better
344 * to record stamps or diffs? Perhaps set the relative array sizes in a config
345 * var such that if we expect 8 primary for every 2 secondary, then one array
346 * is 4x as big as the other, and the population routine knows this. Think
347 * about how to decode it later too in olv/mtx.
351 /// @todo TODO add xgate scheduling functions here! Sean, looking forward to it, but after LT1 goes :-)
354 #undef EXTERN
357 /** @todo TODO IDEA: use a two stage mapping scheme for sched. Such that you
358 * have event number to joiner number in an unsigned char array such that event
359 * 4 and event 7 both are assigned join number 2, then pin X is scheduled to
360 * run on join number 2 which could be on any number of real events!! GREAT!
366 * RPM Calculations:
368 * Either need a per decoder function that is called from the main loop OR...
369 * RPM calculation is done in real time inside one of the RPM interrupts OR...
370 * The choice of either, up to the decoder to decide how it is done, in this
371 * case the function can either do nothing, or swap some pointers/var values
372 * around or similar.
374 * MAP Sampling:
376 * Max number of samples = max number of cylinders, has to be handled by
377 * decoder due to potential mismatch between wheel pattern and cylinder firing
378 * pattern unless it is done on a rough multiple sample basis in generic code
379 * that runs often and can approximate timing/position for sampling.
381 * Scheduling:
383 * Fueling pins could be expected to fire once per cylinder event (1 - 12), or
384 * once per engine cycle, or something in between, but what is a reasonable
385 * maximum, and is it workable to allow some cases and not others?
387 * |-----------------------------------------------------------|
388 * | Pins |
389 * |-----------------------------------------------------------|
390 * | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
391 * -----------|-----------------------------------------------------------|
392 * | | 1 | | | | | | | | | | | | |
393 * | |----|-----------------------------------------------------------|
394 * | | 2 | | | | | | | | | | | | |
395 * | |----|-----------------------------------------------------------|
396 * | | 3 | | | | | | | | | | | | |
397 * | |----|-----------------------------------------------------------|
398 * | | 4 | | | | | | | | | | | | |
399 * | |----|-----------------------------------------------------------|
400 * | | 5 | | | | | | | | | | | | |
401 * | |----|-----------------------------------------------------------|
402 * | | 6 | | | | | | | | | | | | |
403 * | Cyl |----|-----------------------------------------------------------|
404 * | | 7 | | | | | | | | | | | | |
405 * | |----|-----------------------------------------------------------|
406 * | | 8 | | | | | | | | | | | | |
407 * | |----|-----------------------------------------------------------|
408 * | | 9 | | | | | | | | | | | | |
409 * | |----|-----------------------------------------------------------|
410 * | | 10 | | | | | | | | | | | | |
411 * | |----|-----------------------------------------------------------|
412 * | | 11 | | | | | | | | | | | | |
413 * | |----|-----------------------------------------------------------|
414 * | | 12 | | | | | | | | | | | | |
415 * -----------|-----------------------------------------------------------|
418 * Ignition pins will only need to be fired once per cycle (COP/CNP), twice per
419 * cycle (Wasted Spark) or once per cylinder event (distributor) unless
420 * multiple-spark startup is required, however this could be done with cascaded
421 * dwell events, timer self-set:
423 * on dwell off spark on dwell off spark on dwell off and disable spark
425 * This example is for triple spark, 2 or more than 3 are also possible.
427 * When coils are wired one per cylinder for COP/CNP, during starting or loss
428 * of cam sync two opposing coils will be fired at the same time in pseudo
429 * wasted spark mode.
431 * Possible states of sync are as follows:
433 * Full cycle engine sync - COP/CNP/Sequential
434 * Half cycle revolution sync - Wasted Spark/Semi Sequential
435 * Cylinder sync - Distributor/Many pulses or un-timed batch pulses
437 * Wheels will have various patterns of rising/falling edges. Scheduling may be
438 * done from either the rising or falling edge on some, or only on one edge of
439 * others. VR sensors have only one reliable edge, the other varies with speed
440 * and associated rise/fall times of the approximately sinusoidal wave form.
441 * Simple patterns shall be required to be timed to the engine such that at low
442 * engine speeds the timer delays available with high accuracy are sufficient
443 * to properly time ignition events. At the least, there should be a wheel
444 * event per cylinder event, and in close proximity to that cylinder event.
445 * Allowing even more relaxed wheel patterns would mean compromising the
446 * performance for other more common setups and/or increasing code complexity
447 * by an unacceptable amount.
449 * OLD notes:
451 * arrays of output channels, iterate and check for wheel event number, if matched, fire:
452 * doesn't allow for firing a pin more than once a cycle!! no use.
453 * allows multi channel on a single wheel event (virtually useless) but is slow, one loop
454 * and array per type of output channel.
456 * array of wheel events, do lookup for wheel event, then if output channel valid, schedule it.
457 * single channel per tooth event (acceptable, wire in parallel if required), fast, memory hog,
458 * need one array per type of channel, array length = max expected tooth count! do we need to
459 * support every single tooth on a Nissan 360 style decoder or just cyl event count, what about
460 * Porsche 130? next stop is 60, seems good. can we use bit-fields to save memory, 1 bit =
461 * 2 possible pins, 2 bits = 4, etc, this will be slower, though. probably just an unsigned char[]
465 // Init routine:
467 // Allow configuration of timer details? tick size? If so, need to introduce scaling to calcs to
468 // produce correct tick count and therefore pulsewidth. Migrate dead time to time units and scale
469 // to get ticks to add to final pw.
471 // We require some configuration to allow the Nissan style decoder to use the pulse accumulators to
472 // count those one degree slots accurately to a high rpm without excessive cpu load.
475 // move the following to fuel calcs or sched code header, it doesn't belong here...
477 // stuff to do with timing and sync etc. ie, figuring out upon which
481 #else
482 /* let us know if we are being untidy with headers */
483 #warning "Header file DECODER_INTERFACE_H seen before, sort it out!"
484 /* end of the wrapper ifdef from the very top */
485 #endif