Set dead time to flat 0.9ms until I can get a chance to measure it with the new drive...
[freeems-vanilla.git] / src / main / decoders / GM-LT1-CAS-360and8.c
blob01aa6d2206fbb3340d6a6f444cda378a1987d59a
1 /* FreeEMS - the open source engine management system
3 * Copyright 2009-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 interruptHandlers
30 * @ingroup enginePositionRPMDecoders
32 * @brief LT1 Optispark
34 * Uses PT1 to interrupt on rising and falling events of the 8x cam sensor track.
35 * A certain number of 360x teeth will pass while PT1 is in a high or low state.
36 * Using that uniquek count we can set the positing of your Virtual CAS clock.
37 * After VCAS's position is set set PT7 to only interrupt on every 5th tooth, lowering
38 * the amount of interrupts generated, to a reasonable level.
40 * @note Pseudo code that does not compile with zero warnings and errors MUST be commented out.
42 * @todo TODO config pulse accumulator to fire its own RPM interrupt to give the wheel more
43 * resoloution. Such as fire on every 10x.
45 * @author Sean Keys
47 #define DECODER_IMPLEMENTATION_C
48 #define LT1_360_8_C
50 #include "../inc/freeEMS.h"
51 #include "../inc/interrupts.h"
52 #include "inc/GM-LT1-CAS-360and8.h"
53 #include "../inc/decoderInterface.h"
54 #include "../inc/utils.h"
56 const unsigned short eventAngles[] = {ANGLE( 0), ANGLE( 86), ANGLE(130), ANGLE(176),
57 ANGLE(180), ANGLE(266), ANGLE(280), ANGLE(356),
58 ANGLE(360), ANGLE(446), ANGLE(470), ANGLE(536),
59 ANGLE(540), ANGLE(626), ANGLE(660), ANGLE(716)};
60 const unsigned char eventValidForCrankSync[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // This is wrong, but will never be used on this decoder anyway.
61 const unsigned char windowCounts[] = {4,86,44,46,4,86,14,76,4,86,24,66,4,86,34,56};
62 unsigned char lastAccumulatorCount = 0xFF; /* set to bogus number */
63 unsigned char lastPARegisterReading = 0xFF;
64 unsigned char windowState = 0;
65 unsigned char lastNumberOfRealEvents = 0;
66 unsigned char accumulatorRegisterCount = 0;
67 signed char cumulativeBastardTeeth = 0;
70 // Rolling tolerance to cumulative noise issues feature.
72 // This rolling tolerance for cumulative bastard teeth count is available to
73 // allow users to tune the system such that mild noise on the fine pitch input
74 // doesn't cause loss of sync over a period of seconds, minutes or hours. This
75 // feature should be tuned to the most conservative level possible and actually
76 // used to improve the quality of the wiring, not to mask bad noise issues.
77 // Default will always be off, hard coded on for now with only one user, Sean.
78 // ----------------------------------------------------------------------------
79 // 0 is disabled, this is the default such that you find out that your system is noisy quickly
80 // 1 means you can have one extra/missing tooth per window continuously without loss of sync
81 // 2 means you can have one extra/missing tooth per 2 windows continuously without loss of sync etc
82 // 65535 is max and the most conservative possible with this feature enabled.
83 #define windowsPerAllowedCumulativeBastardTooth 8
84 // TODO future enhancement allow fractional stuff by having N extra/missing per M windows
85 unsigned short cumulativeBastardTeethEroderCounter = 0;
88 // Setup PT Capturing so that we can decode the LT1 pattern
89 void decoderInitPreliminary(void){
90 /* set pt1 to capture on rising and falling */
92 // set PACMX to 0 which is the default so there should be no need
93 // set to capture on rising and falling this way if we have an odd number in the PA we know something went wrong
94 // disable interrupt on PT1
95 ICPAR = 0x02; // set the second bit in ICPAR (PAC1) to enable PT1's pulse accumulator
96 // enable interrupt on overflow and set count to 0xFF-245 to enable an interrupt on every ten teeth
97 PACN1 = 0x00; // reset our count register
98 TCTL4 = 0xFF; /* Capture on both edges of pin 0 and only on the falling edges of pin 1, capture off for 2,3 */ // FRED why interrupt on the other one at all, there is no code and you're *causing* jitter in your primary rpm by doing this, along with eating CPU up.
99 //TIE = 0x01; // FRED necessary to do this too? I think so, but check the docs.
103 void perDecoderReset(){
104 cumulativeBastardTeeth = 0;
105 cumulativeBastardTeethEroderCounter = 0;
109 /* Interrupt on rising and falling edges to count the number of teeth that have passed
110 * in that window. 4 of the windows on the 8 tooth channel have a unique width. The pulse
111 * accumulator will hold that count so there is no need to interrupt on the 360 tooth channel.
113 * Note: Primary LT1 Optispark Interrupt wired to the 8x channel.
115 void PrimaryRPMISR(void){
116 /* Clear the interrupt flag for this input compare channel */
117 TFLG = 0x01;
118 // Grab this first as it is the most critical var in this decoder
119 accumulatorRegisterCount = PACN1;/* save count before it changes */
120 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);
122 /* Save all relevant available data here */
123 unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */
124 unsigned short edgeTimeStamp = TC0; /* Save the edge time stamp */
126 windowState = PTITCurrentState & 0x01; /* Save the high/low state of the port, HIGH PRIORITY some windows are only 2deg wide */
127 unsigned char accumulatorCount = accumulatorRegisterCount - lastPARegisterReading;/* save count before it changes */
128 lastPARegisterReading = accumulatorRegisterCount;
129 unsigned char i; /* temp loop var */
131 KeyUserDebugs.primaryTeethSeen++;
132 KeyUserDebugs.secondaryTeethSeen += accumulatorCount;
133 // DEBUG = accumulatorCount; // TODO remove DEBUG
135 /* always make sure you have two good counts(there are a few windows that share counts) */
136 if(!(KeyUserDebugs.decoderFlags & CAM_SYNC)){
137 // FRED do this on a per edge basis to lower chances of false match with +/- 1 counts
138 if(accumulatorCount == AMBIGUOUS_COUNT){
139 return;
140 }else{
141 unsigned char lastEvent = 0xFF;
142 for(i = 0; numberOfRealEvents > i; i++){
143 if(windowCounts[i] == accumulatorCount){
144 if(i == 0){ /* keep our counter from going out of range */
145 KeyUserDebugs.currentEvent = 0xFF; // Will be rolled over to 0
146 lastEvent = NUMBER_OF_REAL_EVENTS - 1;
147 }else{
148 lastEvent = i - 1;
149 KeyUserDebugs.currentEvent = lastEvent; // Will be rolled up to current
151 break;
155 if(lastEvent == 0xFF){ // Indicates that we didn't find a match, previously uncaught, would have occasionally matched last event with i = max and no match found on THIS event
156 return;
157 }else if(windowCounts[lastEvent] == lastAccumulatorCount){ /* if true we are in sync! */
158 SET_SYNC_LEVEL_TO(CAM_SYNC);
159 }else{
160 // TODO missedsync opportunity ++ or something
163 lastAccumulatorCount = accumulatorCount;
165 // TODO put fuzzy initial sync in place, maybe.
166 // // If still not synced, try to do fuzzy sync
167 // if(!(decoderFlags & CAM_SYNC)){
168 // // loop with +1 and -1
169 // // count fuzzy syncs, if genuine, should only be one + and one -
170 // // if not, give up and clear all state
171 // }
172 // return; // TODO remove and continue on down the thread
176 // Not an else block because the if block above can change the state of its own condition
177 if(KeyUserDebugs.decoderFlags & CAM_SYNC){
178 KeyUserDebugs.currentEvent++;
179 if(KeyUserDebugs.currentEvent == numberOfRealEvents){ /* roll our event over if we are at the end */
180 KeyUserDebugs.currentEvent = 0;
184 * bastardTeeth will be zero if things are going well, and a low number
185 * if there is some latency, and a large number if totally wrong. This
186 * will not catch sequences of same direction errors, though. We need
187 * to keep a running track of past bastardTeeth too. TODO
190 signed char bastardTeeth = accumulatorCount - windowCounts[KeyUserDebugs.currentEvent];
191 cumulativeBastardTeeth += bastardTeeth;
193 // DEBUG = cumulativeBastardTeeth; // TODO remove DEBUG
194 // DEBUG = bastardTeeth;
195 // DEBUG = windowCounts[currentEvent]; // TODO remove DEBUG
197 // Cumulative Tolerance Code TODO add counters to monitor aggressiveness of this
198 if(windowsPerAllowedCumulativeBastardTooth){
199 cumulativeBastardTeethEroderCounter++;
200 if(cumulativeBastardTeethEroderCounter == windowsPerAllowedCumulativeBastardTooth){
201 cumulativeBastardTeethEroderCounter = 0;
202 if(cumulativeBastardTeeth > 0){
203 cumulativeBastardTeeth--;
204 // DEBUG++;
205 // counter for decrement
206 }else if(cumulativeBastardTeeth < 0){
207 cumulativeBastardTeeth++;
208 // counter for increment
209 }else{
210 // counter for does nothing
215 /* if we are in-sync continue checking and perform required decoder calcs */
216 LongTime timeStamp;
218 /* Install the low word */
219 timeStamp.timeShorts[1] = edgeTimeStamp;
220 /* Find out what our timer value means and put it in the high word */
221 if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
222 timeStamp.timeShorts[0] = timerExtensionClock + 1;
223 }else{
224 timeStamp.timeShorts[0] = timerExtensionClock;
227 if((bastardTeeth > MAX_BASTARD_TEETH) || (bastardTeeth < -MAX_BASTARD_TEETH)){
228 resetToNonRunningState(BASTARD_SYNC_LOSS_ID_BASE + bastardTeeth); // TODO move this to the syncLossIDs.h header
229 return;
230 }else if((cumulativeBastardTeeth > MAX_CUMULATIVE_BASTARD_TEETH) || (cumulativeBastardTeeth < -MAX_CUMULATIVE_BASTARD_TEETH)){
231 resetToNonRunningState(BASTARD_CUMULATIVE_SYNC_LOSS_ID_BASE + cumulativeBastardTeeth); // TODO move this to the syncLossIDs.h header
232 return;
233 }else{ // Tooth count was within spec
234 if(!(KeyUserDebugs.decoderFlags & OK_TO_SCHEDULE)) {
235 SET_SYNC_LEVEL_TO(CAM_SYNC); // Add confirmation until it is
238 /* TODO all required calcs etc as shown in other working decoders */
239 if((KeyUserDebugs.currentEvent % 2) == 1){ /* if we captured on a rising edge that is to say an evenly spaced edge perform the calcs */
240 // temporary data from inputs
241 unsigned long primaryLeadingEdgeTimeStamp = timeStamp.timeLong;
242 unsigned long timeBetweenSuccessivePrimaryPulses = primaryLeadingEdgeTimeStamp - lastPrimaryEventTimeStamp;
243 lastPrimaryEventTimeStamp = primaryLeadingEdgeTimeStamp;
245 /* RPM CALC, KISS for now and only run this part of the ISR when the edge has gone high
246 * this way we have evenly spaced teeth
248 *ticksPerDegreeRecord = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessivePrimaryPulses) / (90 * ANGLE_FACTOR));
249 // instead of above:
250 // save time difference
251 // have angle of time difference as setting
252 // do whole rpm calc in main loop to save ISR time, make more sense, and be more coherent to read
253 // then it's possible to use different tpdm figures for different RPM levels, thereby allowing a large range AND fine granularity!
254 // tpd would still need to be calculated for scheduling reasons, and the different scalings would need to be checked for overflow there.
256 // TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
257 sampleEachADC(ADCBuffers);
258 Counters.syncedADCreadings++;
259 /* Set flag to say calc required */
260 coreStatusA |= CALC_FUEL_IGN;
261 /* Reset the clock for reading timeout */
262 Clocks.timeoutADCreadingClock = 0;
265 SCHEDULE_ECT_OUTPUTS();
268 OUTPUT_COARSE_BBS();
270 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
274 #include "inc/defaultSecondaryRPMISR.c"