1 /* FreeEMS - the open source engine management system
3 * Copyright 2011-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!
29 * @ingroup interruptHandlers
30 * @ingroup enginePositionRPMDecoders
32 * @brief Echos the input on the first ignition output
34 * This decoder is for any 4 tooth/slot hall or optical cam speed sensor and to
35 * be used for distributor and/or 4 shot batch injection only.
37 * To build a version of this decoder with a specific angle pair for your hall
38 * or optical distributor, just define the DECODER_IMPLEMENTATION_C flag,
39 * include the four required headers, define the angle for E1, and define the
40 * unique decoder name string!
42 * To effectively reverse the polarity, just subtract your angle from 180 and
43 * it will then be correct, assuming that either angle is correct.
47 #define angleOfSingleIteration (180 * ANGLE_FACTOR)
50 // Define E1 in your reverse header!
51 #define E2 (E0 + angleOfSingleIteration)
52 #define E3 (E1 + angleOfSingleIteration)
53 #define E4 (E0 + (2 * angleOfSingleIteration))
54 #define E5 (E1 + (2 * angleOfSingleIteration))
55 #define E6 (E0 + (3 * angleOfSingleIteration))
56 #define E7 (E1 + (3 * angleOfSingleIteration))
59 #if (E1 >= angleOfSingleIteration)
60 #error "Angle E1 defined to be larger than the available angle which it is within!"
64 const unsigned short eventAngles
[] = {E0
, E1
, E2
, E3
, E4
, E5
, E6
, E7
};
65 const unsigned char eventValidForCrankSync
[] = {0,0,0,0,0,0,0,0}; // Unused in this decoder.
68 void decoderInitPreliminary(){
69 TCTL4
= 0x03; /* Capture on both edges of T0 only, capture off for 1,2,3 */
70 } // This decoder works with the defaults
71 void perDecoderReset(){} // Nothing special to reset for this code
75 /* Clear the interrupt flag for this input compare channel */
77 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS
, BIT0
, PORTB
);
79 /* Save all relevant available data here */
80 unsigned short edgeTimeStamp
= TC0
; /* Save the edge time stamp */
81 unsigned char PTITCurrentState
= PTIT
; /* Save the values on port T regardless of the state of DDRT */
83 KeyUserDebugs
.primaryTeethSeen
++;
87 /* Install the low word */
88 timeStamp
.timeShorts
[1] = edgeTimeStamp
;
89 /* Find out what our timer value means and put it in the high word */
90 if(TFLGOF
&& !(edgeTimeStamp
& 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
91 timeStamp
.timeShorts
[0] = timerExtensionClock
+ 1;
93 timeStamp
.timeShorts
[0] = timerExtensionClock
;
95 unsigned long thisEventTimeStamp
= timeStamp
.timeLong
;
97 unsigned char lastEvent
= 0;
98 unsigned short thisTicksPerDegree
= 0;
99 if(PTITCurrentState
& 0x01){
100 // temporary data from inputs
101 unsigned long primaryLeadingEdgeTimeStamp
= timeStamp
.timeLong
;
102 unsigned long timeBetweenSuccessivePrimaryPulses
= primaryLeadingEdgeTimeStamp
- lastPrimaryEventTimeStamp
;
103 lastPrimaryEventTimeStamp
= primaryLeadingEdgeTimeStamp
;
106 thisTicksPerDegree
= (unsigned short)((ticks_per_degree_multiplier
* timeBetweenSuccessivePrimaryPulses
) / angleOfSingleIteration
);
107 *ticksPerDegreeRecord
= thisTicksPerDegree
;
109 // TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
110 sampleEachADC(ADCBuffers
);
111 Counters
.syncedADCreadings
++;
113 /* Set flag to say calc required */
114 coreStatusA
|= CALC_FUEL_IGN
;
116 /* Reset the clock for reading timeout */
117 Clocks
.timeoutADCreadingClock
= 0;
119 KeyUserDebugs
.currentEvent
= 1;
122 // temporary data from inputs
123 unsigned long secondaryLeadingEdgeTimeStamp
= timeStamp
.timeLong
;
124 unsigned long timeBetweenSuccessiveSecondaryPulses
= secondaryLeadingEdgeTimeStamp
- lastSecondaryEventTimeStamp
;
125 lastSecondaryEventTimeStamp
= secondaryLeadingEdgeTimeStamp
;
128 thisTicksPerDegree
= (unsigned short)((ticks_per_degree_multiplier
* timeBetweenSuccessiveSecondaryPulses
) / angleOfSingleIteration
);
129 *ticksPerDegreeRecord
= thisTicksPerDegree
;
131 // TODO make this stuff behave correctly, this one will only run at startup, and the other will always run, but do it by generic config and split this stuff out into a shared function, soon.
132 sampleEachADC(ADCBuffers
);
133 Counters
.syncedADCreadings
++;
135 /* Set flag to say calc required */
136 coreStatusA
|= CALC_FUEL_IGN
;
138 /* Reset the clock for reading timeout */
139 Clocks
.timeoutADCreadingClock
= 0;
141 KeyUserDebugs
.currentEvent
= 0;
145 unsigned long thisInterEventPeriod
= 0;
146 if(KeyUserDebugs
.decoderFlags
& LAST_TIMESTAMP_VALID
){
147 thisInterEventPeriod
= thisEventTimeStamp
- lastEventTimeStamp
;
150 // This should check during gain of sync too and prevent gaining sync if the numbers aren't right, however this is a step up for the Hotel at this time.
151 if(KeyUserDebugs
.decoderFlags
& COMBUSTION_SYNC
){
152 unsigned short thisAngle
= 0;
153 if(KeyUserDebugs
.currentEvent
== 0){
154 // Fix this to work for all:
155 // thisAngle = eventAngles[KeyUserDebugs.currentEvent] + totalEventAngleRange - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
156 thisAngle
= eventAngles
[KeyUserDebugs
.currentEvent
] + angleOfSingleIteration
- eventAngles
[lastEvent
] ; // Optimisable... leave readable for now! :-p J/K learn from this...
158 thisAngle
= eventAngles
[KeyUserDebugs
.currentEvent
] - eventAngles
[lastEvent
];
161 thisTicksPerDegree
= (unsigned short)((ticks_per_degree_multiplier
* thisInterEventPeriod
) / thisAngle
); // with current scale range for 60/12000rpm is largest ticks per degree = 3472, smallest = 17 with largish error
163 if(KeyUserDebugs
.decoderFlags
& LAST_PERIOD_VALID
){
164 unsigned short ratioBetweenThisAndLast
= (unsigned short)(((unsigned long)lastTicksPerDegree
* 1000) / thisTicksPerDegree
);
165 KeyUserDebugs
.inputEventTimeTolerance
= ratioBetweenThisAndLast
;
166 if(ratioBetweenThisAndLast
> fixedConfigs2
.decoderSettings
.decelerationInputEventTimeTolerance
){
167 resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE
);
169 }else if(ratioBetweenThisAndLast
< fixedConfigs2
.decoderSettings
.accelerationInputEventTimeTolerance
){
170 resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY
);
176 if(KeyUserDebugs
.decoderFlags
& COMBUSTION_SYNC
){
177 SCHEDULE_ECT_OUTPUTS();
182 /* TODO this delays outputs until the fourth ISR execution, but we could
183 * get them one execution or 1/8 of a rev sooner if we did a preliminary
184 * calc from the previous edge instead of the previous same edge now, and
186 * The proper way to do this is set sync when we have it and not set data
187 * as having been recorded until we know the data is good. That way the
188 * scheduler can keep things unscheduled until the time is right.
190 if(KeyUserDebugs
.decoderFlags
& LAST_PERIOD_VALID
){
191 SET_SYNC_LEVEL_TO(COMBUSTION_SYNC
);
194 if(KeyUserDebugs
.decoderFlags
& LAST_TIMESTAMP_VALID
){
195 lastTicksPerDegree
= thisTicksPerDegree
;
196 KeyUserDebugs
.decoderFlags
|= LAST_PERIOD_VALID
;
199 lastEventTimeStamp
= thisEventTimeStamp
;
200 KeyUserDebugs
.decoderFlags
|= LAST_TIMESTAMP_VALID
;
202 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS
, NBIT0
, PORTB
);
206 #include "../inc/defaultSecondaryRPMISR.c"