R18A1 decoder as tested on the WikiSpeed car in Seattle.
[freeems-vanilla.git] / src / main / decoders / R18A1-13CrankWith5Cam.c
blob414f575f6cdcec9222b6669e1b3bf4750fd12ae6
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!
27 /** @file
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
54 #define E0 ANGLE( 0)
55 #define E1 ANGLE( 30)
56 #define E2 ANGLE( 60)
57 #define E3 ANGLE( 90)
58 #define E4 ANGLE(120)
59 #define E5 ANGLE(150)
60 #define E6 ANGLE(180)
61 #define E7 ANGLE(210)
62 #define E8 ANGLE(240)
63 #define E9 ANGLE(270)
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)
81 // 3 >> 16
82 // 10 >> 23
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
95 TCTL4 = 0x05;
98 void perDecoderReset(){
99 camTeethSeen = 0;
100 previousCrankTeethSeen = 0;
101 crankTeethSinceLastCamTooth = 0;
103 // Re-enable secondary interrupt so we can sync again!
104 TCTL4 |= BIT2;
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 */
110 TFLG = 0x01;
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++;
122 LongTime timeStamp;
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;
128 }else{
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...
151 }else{
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);
162 return;
163 }else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
164 resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
165 return;
166 }else{
167 if(PTITCurrentState & 0x02){
168 // TODO Calculate RPM from last primaryLeadingEdgeTimeStamp
169 }else{
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();
194 OUTPUT_COARSE_BBS();
197 // Always
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 */
207 TFLG = 0x02;
208 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);
209 KeyUserDebugs.secondaryTeethSeen++;
210 if(camTeethSeen == 0){
211 camTeethSeen = 1;
212 previousCrankTeethSeen = KeyUserDebugs.primaryTeethSeen;
213 }else{
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);
230 }else{
231 resetToNonRunningState(BUG_REACHED_UNREACHABLE_CODE);
233 return;
234 } // else is 6, just ignore.
236 // Disable the interrupt for this ISR
237 if(KeyUserDebugs.decoderFlags & OK_TO_SCHEDULE){
238 TCTL4 &= NBIT2;
242 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);