FreeEMS  0.2.0-SNAPSHOT-285-g028e24c
GM-LT1-CAS-360and8.c
Go to the documentation of this file.
1 /* FreeEMS - the open source engine management system
2  *
3  * Copyright 2009-2012 Fred Cooke
4  *
5  * This file is part of the FreeEMS project.
6  *
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.
11  *
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.
16  *
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/
19  *
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!
22  *
23  * Thank you for choosing FreeEMS to run your engine!
24  */
25 
26 
27 /** @file
28  *
29  * @ingroup interruptHandlers
30  * @ingroup enginePositionRPMDecoders
31  *
32  * @brief LT1 Optispark
33  *
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.
39  *
40  * @note Pseudo code that does not compile with zero warnings and errors MUST be commented out.
41  *
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.
44  *
45  * @author Sean Keys
46  */
47 #define DECODER_IMPLEMENTATION_C
48 #define LT1_360_8_C
49 
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"
55 
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;
68 
69 
70 // Rolling tolerance to cumulative noise issues feature.
71 //
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
86 
87 
88 // Setup PT Capturing so that we can decode the LT1 pattern
90  /* set pt1 to capture on rising and falling */
91 
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.
100 }
101 
102 
106 }
107 
108 
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.
112  *
113  * Note: Primary LT1 Optispark Interrupt wired to the 8x channel.
114  */
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 */
121 
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 */
125 
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 */
130 
132  KeyUserDebugs.secondaryTeethSeen += accumulatorCount;
133 // DEBUG = accumulatorCount; // TODO remove DEBUG
134 
135  /* always make sure you have two good counts(there are a few windows that share counts) */
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
150  }
151  break;
152  }
153  }
154 
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! */
159  }else{
160  // TODO missedsync opportunity ++ or something
161  }
162 
163  lastAccumulatorCount = accumulatorCount;
164 
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
173  }
174  }
175 
176  // Not an else block because the if block above can change the state of its own condition
179  if(KeyUserDebugs.currentEvent == numberOfRealEvents){ /* roll our event over if we are at the end */
181  }
182 
183  /*
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
188  */
189 
190  signed char bastardTeeth = accumulatorCount - windowCounts[KeyUserDebugs.currentEvent];
191  cumulativeBastardTeeth += bastardTeeth;
192 
193 // DEBUG = cumulativeBastardTeeth; // TODO remove DEBUG
194 // DEBUG = bastardTeeth;
195 // DEBUG = windowCounts[currentEvent]; // TODO remove DEBUG
196 
197  // Cumulative Tolerance Code TODO add counters to monitor aggressiveness of this
202  if(cumulativeBastardTeeth > 0){
204  // DEBUG++;
205  // counter for decrement
206  }else if(cumulativeBastardTeeth < 0){
208  // counter for increment
209  }else{
210  // counter for does nothing
211  }
212  }
213  }
214 
215  /* if we are in-sync continue checking and perform required decoder calcs */
217 
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;
225  }
226 
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;
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
235  SET_SYNC_LEVEL_TO(CAM_SYNC); // Add confirmation until it is
236  }
237 
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;
244 
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
247  */
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.
255 
256  // TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
259  /* Set flag to say calc required */
261  /* Reset the clock for reading timeout */
263  }
264  }
265  SCHEDULE_ECT_OUTPUTS();
266  }
267 
268  OUTPUT_COARSE_BBS();
269 
271 }
272 
273