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!
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.
47 #define DECODER_IMPLEMENTATION_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 */
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
){
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;
149 KeyUserDebugs
.currentEvent
= lastEvent
; // Will be rolled up to current
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
157 }else if(windowCounts
[lastEvent
] == lastAccumulatorCount
){ /* if true we are in sync! */
158 SET_SYNC_LEVEL_TO(CAM_SYNC
);
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
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
--;
205 // counter for decrement
206 }else if(cumulativeBastardTeeth
< 0){
207 cumulativeBastardTeeth
++;
208 // counter for increment
210 // counter for does nothing
215 /* if we are in-sync continue checking and perform required decoder calcs */
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;
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
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
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
));
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();
270 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS
, NBIT0
, PORTB
);
274 #include "inc/defaultSecondaryRPMISR.c"