1 /* FreeEMS - the open source engine management system
3 * Copyright 2013-2014 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 * Decoder for the Honda R18A1 I4 engine.
34 * http://pastebin.com/QMdhE55v
38 #define DECODER_MAX_CODE_TIME 150
39 #define NUMBER_OF_REAL_EVENTS 26 // 12+1 Mostly Even Crank teeth, twice
40 #define NUMBER_OF_VIRTUAL_EVENTS 26 // Ditto.
41 #define DECODER_IMPLEMENTATION_C
43 #include "../inc/freeEMS.h"
44 #include "../inc/utils.h"
45 #include "../inc/interrupts.h"
46 #include "../inc/decoderInterface.h"
48 unsigned char camTeethSeen
= 0;
49 unsigned char previousCrankTeethSeen
= 0;
50 unsigned char crankTeethSinceLastCamTooth
= 0;
52 #define FIRST_GAP ANGLE(10) // With the 20 degree balance of the 30 degree segment following
64 #define E10 ANGLE(300)
65 #define E11 ANGLE(330)
66 #define E12 (E11 + FIRST_GAP)
67 #define E13 ANGLE(360)
68 #define E14 ANGLE(390)
69 #define E15 ANGLE(420)
70 #define E16 ANGLE(450)
71 #define E17 ANGLE(480)
72 #define E18 ANGLE(510)
73 #define E19 ANGLE(540)
74 #define E20 ANGLE(570)
75 #define E21 ANGLE(600)
76 #define E22 ANGLE(630)
77 #define E23 ANGLE(660)
78 #define E24 ANGLE(690)
79 #define E25 (E24 + FIRST_GAP)
83 // Note, if used in 360 degree crank only mode, the upper 13 values are simply ignored.
85 CASSERT(E12
> E11
, MID_ANGLE_LOWER
)
86 CASSERT(E12
< E13
, MID_ANGLE_UPPER
)
87 CASSERT(E25
> E24
, FINAL_ANGLE_LOWER
)
88 CASSERT(E25
< ANGLE(720), FINAL_ANGLE_UPPER
)
90 const unsigned short eventAngles
[] = {E0
, E1
, E2
, E3
, E4
, E5
, E6
, E7
, E8
, E9
, E10
, E11
, E12
, E13
, E14
, E15
, E16
, E17
, E18
, E19
, E20
, E21
, E22
, E23
, E24
, E25
};
91 const unsigned char eventValidForCrankSync
[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
93 void decoderInitPreliminary(){
94 // Set PT0 and PT1 to only capture on rising edges
98 void perDecoderReset(){
100 previousCrankTeethSeen
= 0;
101 crankTeethSinceLastCamTooth
= 0;
103 // Re-enable secondary interrupt so we can sync again!
107 // Just count and check time stamps, sync is handled by the secondary ISR
108 void PrimaryRPMISR(){
109 /* Clear the interrupt flag for this input compare channel */
111 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS
, BIT0
, PORTB
);
113 /* Save all relevant available data here */
114 unsigned short edgeTimeStamp
= TC0
; /* Save the edge time stamp */
115 unsigned char PTITCurrentState
= PTIT
; /* Save the values on port T regardless of the state of DDRT */
117 // Prevent main from clearing values before sync is obtained!
118 Clocks
.timeoutADCreadingClock
= 0;
120 KeyUserDebugs
.primaryTeethSeen
++;
123 /* Install the low word */
124 timeStamp
.timeShorts
[1] = edgeTimeStamp
;
125 /* Find out what our timer value means and put it in the high word */
126 if(TFLGOF
&& !(edgeTimeStamp
& 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
127 timeStamp
.timeShorts
[0] = timerExtensionClock
+ 1;
129 timeStamp
.timeShorts
[0] = timerExtensionClock
;
131 unsigned long thisEventTimeStamp
= timeStamp
.timeLong
;
133 unsigned long thisInterEventPeriod
= 0;
134 if(KeyUserDebugs
.decoderFlags
& LAST_TIMESTAMP_VALID
){
135 thisInterEventPeriod
= thisEventTimeStamp
- lastPrimaryEventTimeStamp
;
136 KeyUserDebugs
.zsp7
= (unsigned short)thisInterEventPeriod
; // Could truncate if under ~100rpm TODO DEBUG REMOVE
139 unsigned short thisTicksPerDegree
= 0;
140 if(KeyUserDebugs
.decoderFlags
& CAM_SYNC
){
142 unsigned char lastEvent
= KeyUserDebugs
.currentEvent
;
143 KeyUserDebugs
.currentEvent
++;
144 if(KeyUserDebugs
.currentEvent
== numberOfRealEvents
){
145 KeyUserDebugs
.currentEvent
= 0;
148 unsigned short thisAngle
= 0;
149 if(KeyUserDebugs
.currentEvent
== 0){
150 thisAngle
= eventAngles
[KeyUserDebugs
.currentEvent
] + totalEventAngleRange
- eventAngles
[lastEvent
] ; // Optimisable... leave readable for now! :-p J/K learn from this...
152 thisAngle
= eventAngles
[KeyUserDebugs
.currentEvent
] - eventAngles
[lastEvent
];
155 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
157 if(KeyUserDebugs
.decoderFlags
& LAST_PERIOD_VALID
){
158 unsigned short ratioBetweenThisAndLast
= (unsigned short)(((unsigned long)lastPrimaryTicksPerDegree
* 1000) / thisTicksPerDegree
);
159 KeyUserDebugs
.inputEventTimeTolerance
= ratioBetweenThisAndLast
;
160 if(ratioBetweenThisAndLast
> fixedConfigs2
.decoderSettings
.decelerationInputEventTimeTolerance
){
161 resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE
);
163 }else if(ratioBetweenThisAndLast
< fixedConfigs2
.decoderSettings
.accelerationInputEventTimeTolerance
){
164 resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY
);
167 if(PTITCurrentState
& 0x02){
168 // TODO Calculate RPM from last primaryLeadingEdgeTimeStamp
170 // TODO Calculate RPM from last primaryTrailingEdgeTimeStamp
175 if(KeyUserDebugs
.decoderFlags
& LAST_TIMESTAMP_VALID
){
176 lastPrimaryTicksPerDegree
= thisTicksPerDegree
;
177 KeyUserDebugs
.decoderFlags
|= LAST_PERIOD_VALID
;
180 /*else*/ if(KeyUserDebugs
.decoderFlags
& LAST_TIMESTAMP_VALID
){ // TODO temp for testing just do rpm this way, fill above out later.
181 *ticksPerDegreeRecord
= thisTicksPerDegree
;
182 sampleEachADC(ADCBuffers
);
183 Counters
.syncedADCreadings
++;
185 // Set flag to say calc required
186 coreStatusA
|= CALC_FUEL_IGN
;
188 // Reset the clock for reading timeout
189 Clocks
.timeoutADCreadingClock
= 0;
192 SCHEDULE_ECT_OUTPUTS();
198 lastPrimaryEventTimeStamp
= thisEventTimeStamp
;
199 KeyUserDebugs
.decoderFlags
|= LAST_TIMESTAMP_VALID
;
201 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS
, NBIT0
, PORTB
);
204 // CAM sensor doesn't use time stamps due to timing belt tension vibration, plus the crank has 6 times more events, so any discrepancy would be caught that way
205 void SecondaryRPMISR(){
206 /* Clear the interrupt flag for this input compare channel */
208 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS
, BIT0
, PORTB
);
209 KeyUserDebugs
.secondaryTeethSeen
++;
210 if(camTeethSeen
== 0){
212 previousCrankTeethSeen
= KeyUserDebugs
.primaryTeethSeen
;
214 crankTeethSinceLastCamTooth
= KeyUserDebugs
.primaryTeethSeen
- previousCrankTeethSeen
;
215 previousCrankTeethSeen
= KeyUserDebugs
.primaryTeethSeen
;
217 if(crankTeethSinceLastCamTooth
== 7){
218 KeyUserDebugs
.currentEvent
= 16;
219 SET_SYNC_LEVEL_TO(CAM_SYNC
);
220 }else if(crankTeethSinceLastCamTooth
== 1){
221 KeyUserDebugs
.currentEvent
= 23;
222 SET_SYNC_LEVEL_TO(CAM_SYNC
);
223 }else if(crankTeethSinceLastCamTooth
!= 6){
224 if(crankTeethSinceLastCamTooth
> 7){
225 resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE
);
226 }else if(crankTeethSinceLastCamTooth
< 6){
227 resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE
);
228 }else if(!crankTeethSinceLastCamTooth
){ // Should never be zero :-p
229 resetToNonRunningState(STATE_MISMATCH_IN_SECONDARY_RPM_ISR
);
231 resetToNonRunningState(BUG_REACHED_UNREACHABLE_CODE
);
234 } // else is 6, just ignore.
236 // Disable the interrupt for this ISR
237 if(KeyUserDebugs
.decoderFlags
& OK_TO_SCHEDULE
){
242 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS
, NBIT0
, PORTB
);