Example #1
0
/* Keep disabling calcs as often as we can. */
void SecondaryRPMISR() {
    /* Clear the interrupt flag for this input compare channel */
    TFLG = 0x02;
    DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT1, PORTB);
    /* Reset the clock for reading timeout */
    Clocks.timeoutADCreadingClock = 0;
    DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT1, PORTB);
}
Example #2
0
/** @brief ECT overflow handler
 *
 * When the ECT free running timer hits 65535 and rolls over, this is run. Its
 * job is to extend the timer to an effective 32 bits for longer measuring much
 * longer periods with the same resolution. Please see section 10.5.5 of the
 * 68HC11 reference manual for more information on this technique!
 *
 * @warning The extension var should be incremented before the flag is cleared!
 */
void TimerOverflow() {
    /* Increment the timer extension variable */
    timerExtensionClock++;
    DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT5, PORTB); // TODO Should this go after the flag, or before the timer inc??? 6 possibilities here!
    /* Clear the timer overflow interrupt flag */
    TFLGOF = 0x80;
    DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT5, PORTB);
}
// Just count and check time stamps, sync is handled by the secondary ISR
void PrimaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	edgeTimeStamp = TC0;     /* Save the edge time stamp */
	PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */

	// Prevent main from clearing values before sync is obtained!
	Clocks.timeoutADCreadingClock = 0;

	KeyUserDebugs.primaryTeethSeen++;

	LongTime timeStamp;
	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}
	thisEventTimeStamp = timeStamp.timeLong;

	thisInterEventPeriod = 0;
	thisTicksPerDegree = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastPrimaryEventTimeStamp;
		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * thisInterEventPeriod) / eventAngles[1]);
	}

	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		if(KeyUserDebugs.currentEvent == 23){
			KeyUserDebugs.currentEvent = 0;
		}else{
			KeyUserDebugs.currentEvent++;
		}
		if(KeyUserDebugs.currentEvent == numberOfRealEvents){
			resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
			return;
		}// Can never be greater than without a code error or genuine noise issue, so give it a miss as we can not guarantee where we are now.

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastPrimaryTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}else{
				if(PTITCurrentState & 0x02){
					// TODO Calculate RPM from last primaryLeadingEdgeTimeStamp
				}else{
					// TODO Calculate RPM from last primaryTrailingEdgeTimeStamp
				}
			}
		}/*else*/ if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){ // TODO temp for testing just do rpm this way, fill above out later.
			*ticksPerDegreeRecord = thisTicksPerDegree;
			sampleEachADC(ADCBuffers);
			Counters.syncedADCreadings++;

			// Set flag to say calc required
			coreStatusA |= CALC_FUEL_IGN;

			// Reset the clock for reading timeout
			Clocks.timeoutADCreadingClock = 0;
		}

		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	// do these always at first, and use them with a single 30 degree angle for the first cut
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastPrimaryTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastPrimaryEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
// 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
void SecondaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);
	secondaryPTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */
	KeyUserDebugs.secondaryTeethSeen++;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		// check crankTeethSinceLastCamTooth to see if it's 4 or 12 and if not either of those, lose sync.
		crankTeethSinceLastCamTooth =  KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
		previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
		if(crankTeethSinceLastCamTooth != 4 && crankTeethSinceLastCamTooth != 12){
			if(crankTeethSinceLastCamTooth < 4){
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
			}else{
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
			}
		}
	}else{
		if(camTeethSeen == 0){
			camTeethSeen = 1;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
		}else if(camTeethSeen == 1){
			camTeethSeen = 2;
			crankTeethSinceLastCamTooth =  KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
			if(crankTeethSinceLastCamTooth == 12){ // 12 Crank teeth in the 2 missing Cam teeth
				SET_SYNC_LEVEL_TO(CAM_SYNC);
				KeyUserDebugs.currentEvent = 0;
			}else if(crankTeethSinceLastCamTooth != 4){ // 4 Crank teeth in 1 Cam tooth
				if(crankTeethSinceLastCamTooth > 4){
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
				}else{
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
				}
			}
		}else if(camTeethSeen == 2){
			camTeethSeen = 3;
			crankTeethSinceLastCamTooth =  KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
			if(crankTeethSinceLastCamTooth == 12){
				SET_SYNC_LEVEL_TO(CAM_SYNC);
				KeyUserDebugs.currentEvent = 0;
			}else if(crankTeethSinceLastCamTooth != 4){
				if(crankTeethSinceLastCamTooth > 4){
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
				}else{
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
				}
			}
		}else if(camTeethSeen == 3){
			camTeethSeen = 4;
			crankTeethSinceLastCamTooth =   KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
			if(crankTeethSinceLastCamTooth == 12){
				SET_SYNC_LEVEL_TO(CAM_SYNC);
				KeyUserDebugs.currentEvent = 0;
			}else if(crankTeethSinceLastCamTooth != 4){
				if(crankTeethSinceLastCamTooth > 4){
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
				}else{
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
				}
			}else{
				SET_SYNC_LEVEL_TO(CAM_SYNC); // Set the sync on the eventAngle[12] 360 degrees
				KeyUserDebugs.currentEvent = 12;
			}
		}else{
			resetToNonRunningState(BUG_REACHED_UNREACHABLE_CODE);
		}
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
void SecondaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT1, PORTB);

	/* Save all relevant available data here */
	unsigned short edgeTimeStamp = TC1;    /* Save the timestamp */
	unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */

	if(!(PTITCurrentState & 0x02)){ // TODO Remove this once the configuration can be adjusted to only fire on one edge!
		// Only count one edge, the other is irrelevant, and this comment will be two once the above todo is completed.
		KeyUserDebugs.secondaryTeethSeen++;

		LongTime timeStamp;
		/* Install the low word */
		timeStamp.timeShorts[1] = edgeTimeStamp;
		/* Find out what our timer value means and put it in the high word */
		if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
			timeStamp.timeShorts[0] = timerExtensionClock + 1;
		}else{
			timeStamp.timeShorts[0] = timerExtensionClock;
		}
		unsigned long thisEventTimeStamp = timeStamp.timeLong;

		unsigned long thisInterEventPeriod = 0;
		if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			thisInterEventPeriod = thisEventTimeStamp - lastSecondaryEventTimeStamp;
		}

		// This sets currentEvent to 255 such that when the primary ISR runs it is rolled over to zero!
		if(KeyUserDebugs.decoderFlags & CONFIGURED_SYNC){
			/* If the count is less than 23, then we know that the electrical pulse that triggered
			 * this ISR execution was almost certainly in error and it is NOT valid to stay in sync.
			 *
			 * If the count is greater than 24, then we know that an electrical noise pulse triggered
			 * the other interrupt in between and was missed by the time period checks (unlikely, but
			 * possible) and that, therefore, there could have been a noise pulse on this input too,
			 * and therefore we don't really know where we are.
			 *
			 * In the case where the count is exactly 24 we can only rely on the time period checks in
			 * the other ISR, which should be sufficient unless poorly setup by a user with too wide
			 * of a tolerance level.
			 *
			 * There is zero point adding relative timing checks to this ISR because by nature, the
			 * other N teeth have already checked out good timing wise and therefore the average also
			 * does. Thus if we did check, for it to ever fail it would need to be tighter, and in
			 * reality it must be more loose due to the larger possible variation over the much much
			 * larger time frame.
			 */
			if(KeyUserDebugs.currentEvent < (numberOfRealEvents - 1)){
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
			}else if(KeyUserDebugs.currentEvent > (numberOfRealEvents -1)){
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
			} // ELSE do nothing, and be happy :-)
		}else{ // If not synced, sync, as this is our reference point.
			SET_SYNC_LEVEL_TO(CONFIGURED_SYNC);
		}
		KeyUserDebugs.currentEvent = 0xFF; // TODO reset always, and catch noise induced errors below, this behaviour (now some lines above) may be bad/not fussy enough, or could be good, depending upon determinate nature of the inter event timing between primary and secondary, or not, perhaps move "lose sync or correct sync" as a configuration variable
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT1, PORTB);
}
void SecondaryRPMISR(){
	// Reads the inner slot on the disk.

	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT1, PORTB);

	/* Save all relevant available data here */
	edgeTimeStamp = TC1;				/* Save the timestamp */
	unsigned char PTITCurrentState = PTIT;	// TODO invert tests and other behaviour in this code base and remove this not.			/* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.secondaryTeethSeen++;
	// remember that this is both edges, though... 8 per cycle, 4 per rev for the outter wheel, 2/1 for this wheel.

	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}
	unsigned long thisEventTimeStamp = timeStamp.timeLong;

	unsigned long thisInterEventPeriod = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;
	}

	// Determine the correct event based on post transition state (and toggle debug pins)
	unsigned char correctEvent;
	if(PTITCurrentState & 0x02){
		//temp
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

		// Set flag to say calc required
		coreStatusA |= CALC_FUEL_IGN;

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		correctEvent = 9;
	}else{
		//temp
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

		// Set flag to say calc required
		coreStatusA |= CALC_FUEL_IGN;

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		correctEvent = 6;
	}

	unsigned char lastEvent = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		lastEvent = KeyUserDebugs.currentEvent;
		KeyUserDebugs.currentEvent++;

		// ...and check that it's correct
		if(KeyUserDebugs.currentEvent != correctEvent){
			resetToNonRunningState(STATE_MISMATCH_IN_SECONDARY_RPM_ISR);
			return;
		}
	}else{	// If not synced, sync, as in this ISR we always know where we are.
		KeyUserDebugs.currentEvent = correctEvent;
		lastEvent = KeyUserDebugs.currentEvent - 1;
		SET_SYNC_LEVEL_TO(CAM_SYNC);
	}

	unsigned short thisTicksPerDegree = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		unsigned short thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];

		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

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(SECONDARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(SECONDARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}
		}/*else*/ if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			*ticksPerDegreeRecord = thisTicksPerDegree;
		}
	}

	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT1, PORTB);
}
void PrimaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	edgeTimeStamp = TC0;				/* Save the edge time stamp */
	unsigned char PTITCurrentState = PTIT;	// TODO invert tests and other behaviour in this code base and remove this not.		/* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.primaryTeethSeen++;

	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}
	unsigned long thisEventTimeStamp = timeStamp.timeLong;

	unsigned long thisInterEventPeriod = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;
	}

	// Determine the correct event based on post transition state (and toggle debug pins)
	unsigned char correctEvent = 0;
	if(PTITCurrentState & 0x01){
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

		// Set flag to say calc required
		coreStatusA |= CALC_FUEL_IGN;

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		if(!(PTITCurrentState & 0x02)){
			correctEvent = 8;
		}
	}else{
		//temp
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

		// Set flag to say calc required
		coreStatusA |= CALC_FUEL_IGN;

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		if(PTITCurrentState & 0x02){
			unknownEdges++;
			if(unknownEdges == 3){
				correctEvent = 4;
			}
		}else{
			correctEvent = 7;
			unknownEdges = 0;
		}
	}

	unsigned char lastEvent = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		lastEvent = KeyUserDebugs.currentEvent;
		KeyUserDebugs.currentEvent++;
		if(KeyUserDebugs.currentEvent == numberOfRealEvents){
			KeyUserDebugs.currentEvent = 0;
		}

		// ...and check that it's correct
		if((correctEvent != 0) && (KeyUserDebugs.currentEvent != correctEvent)){
			resetToNonRunningState(STATE_MISMATCH_IN_PRIMARY_RPM_ISR);
			return;
		}
	}else if(correctEvent != 0){
		KeyUserDebugs.currentEvent = correctEvent;
		lastEvent = KeyUserDebugs.currentEvent - 1;
		SET_SYNC_LEVEL_TO(CAM_SYNC);
	}

	unsigned short thisTicksPerDegree = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		unsigned short thisAngle = 0;
		if(KeyUserDebugs.currentEvent == 0){
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] + totalEventAngleRange - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
		}else{
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];
		}

		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

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}else{
				if(PTITCurrentState & 0x01){
					// TODO Calculate RPM from last primaryLeadingEdgeTimeStamp
				}else{
					// TODO Calculate RPM from last primaryTrailingEdgeTimeStamp
				}
			}
		}/*else*/ if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){ // TODO temp for testing just do rpm this way, fill above out later.
			*ticksPerDegreeRecord = thisTicksPerDegree;
		}
	}

	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
Example #8
0
/** @brief Serial Communication Interface 0 ISR
 *
 * SCI0 ISR handles all interrupts for SCI0 by reading flags and acting
 * appropriately. Its functions are to send raw bytes out over the wire from a
 * buffer and to receive bytes from the wire un-escape them, checksum them and
 * store them in a buffer.
 *
 * @todo TODO Move this code into an include file much like the fuel interrupts such that it can be used for multiple UART SCI devices without duplication.
 * @todo TODO Fix the init code such that this doesn't run at boot without a serail device attached. Clear buffer maybe? or flag clearing/isr enabling ordering?
 */
void SCI0ISR(){
	// OK before flag reading because cleared when SCI0DRL accessed (R or W)
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT4, PORTB);

	/* Read the flags register */
	unsigned char flags = SCI0SR1;
	/* Note: Combined with reading or writing the data register this also clears the flags. */

	/* If either of these flags is set, we need to read the data to clear the flag */
	if(flags & (SCISR1_RX_REGISTER_FULL | SCISR1_RX_OVERRUN)){
		/* Grab the received byte from the register to clear the flag, whether we want the data or not */
		unsigned char rawByte = SCI0DRL;

		/* If the RX interrupt is enabled do something useful */
		if(SCI0CR2 & SCICR2_RX_ISR_ENABLE){
			if(flags & (SCISR1_RX_NOISE | SCISR1_RX_FRAMING | SCISR1_RX_PARITY | SCISR1_RX_OVERRUN)){
				/* If there is noise on the receive line record it */
				if(flags & SCISR1_RX_NOISE){
					FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_NOISE_ERRORS_OFFSET);
					KeyUserDebugs.serialHardwareErrors++;
				}

				/* If a framing error occurs record it */
				if(flags & SCISR1_RX_FRAMING){
					FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_FRAMING_ERRORS_OFFSET);
					KeyUserDebugs.serialHardwareErrors++;
				}

				/* If a parity error occurs record it */
				if(flags & SCISR1_RX_PARITY){
					FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PARITY_ERRORS_OFFSET);
					KeyUserDebugs.serialHardwareErrors++;
				}

				/* If an overrun occurs record it */
				if(flags & SCISR1_RX_OVERRUN){
					FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_OVERRUN_ERRORS_OFFSET);
					KeyUserDebugs.serialOverrunErrors++;
				}

				resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
			}else{ // Process the received data
				/* Look for a start byte to indicate a new packet */
				if(rawByte == START_BYTE){
					/* If another interface is using it (Note, clear flag, not normal) */
					if(RXBufferContentSourceID & COM_CLEAR_SCI0_INTERFACE_ID){
						/* Turn off our reception */
						SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
					}else{
						/* If we are using it */
						if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){
							/* Increment the counter */
							FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_STARTS_INSIDE_A_PACKET_OFFSET);
							KeyUserDebugs.serialAndCommsCodeErrors++;
						}
						/* Reset to us using it unless someone else was */
						resetReceiveState(COM_SET_SCI0_INTERFACE_ID);
					}
				}else if((unsigned short)RXBufferCurrentPosition >= ((unsigned short)&RXBuffer + RX_BUFFER_SIZE)){
					/* Buffer was full, record and reset */
					FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_OVER_LENGTH_OFFSET);
					KeyUserDebugs.serialAndCommsCodeErrors++;
					resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
				}else if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){
					if(RXStateFlags & RX_SCI_ESCAPED_NEXT){
						/* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */
						RXStateFlags &= RX_SCI_NOT_ESCAPED_NEXT;

						if(rawByte == ESCAPED_ESCAPE_BYTE){
							*RXBufferCurrentPosition++ = ESCAPE_BYTE;
						}else if(rawByte == ESCAPED_START_BYTE){
							*RXBufferCurrentPosition++ = START_BYTE;
						}else if(rawByte == ESCAPED_STOP_BYTE){
							*RXBufferCurrentPosition++ = STOP_BYTE;
						}else{
							/* Otherwise reset and record as data is bad */
							resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
							FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_ESCAPE_PAIR_MISMATCHES_OFFSET);
							KeyUserDebugs.serialAndCommsCodeErrors++;
						}
					}else if(rawByte == ESCAPE_BYTE){
						/* Drop the escape and set the flag to indicate that the next byte should be un-escaped. */
						RXStateFlags |= RX_SCI_ESCAPED_NEXT;
					}else if(rawByte == STOP_BYTE){
						/* Turn off reception */
						SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
						RXStateFlags |= RX_READY_TO_PROCESS;
					}else{
						*RXBufferCurrentPosition++ = rawByte;
					}
				} /* ELSE: Do nothing : drop the byte */
			}
		}
	}

	/* If the TX interrupt is enabled check the register empty flag. */
	if((SCI0CR2 & SCICR2_TX_ISR_ENABLE) && (flags & SCISR1_TX_REGISTER_EMPTY)){
		/* Get the byte to be sent from the buffer */
		unsigned char rawValue = *TXBufferCurrentPositionSCI0;

		if(TXBufferCurrentPositionSCI0 <= TXBufferCurrentPositionHandler){
			if(TXByteEscaped == 0){
				/* If the raw value needs to be escaped */
				if(rawValue == ESCAPE_BYTE){
					SCI0DRL = ESCAPE_BYTE;
					TXByteEscaped = ESCAPED_ESCAPE_BYTE;
				}else if(rawValue == START_BYTE){
					SCI0DRL = ESCAPE_BYTE;
					TXByteEscaped = ESCAPED_START_BYTE;
				}else if(rawValue == STOP_BYTE){
					SCI0DRL = ESCAPE_BYTE;
					TXByteEscaped = ESCAPED_STOP_BYTE;
				}else{ /* Otherwise just send it */
					SCI0DRL = rawValue;
					TXBufferCurrentPositionSCI0++;
				}
			}else{
				SCI0DRL = TXByteEscaped;
				TXBufferCurrentPositionSCI0++;
				TXByteEscaped = 0;
			}
		}else{ /* Length is zero */
			if(coreStatusA & BIT7){
				/* Turn off transmission interrupt */
				SCI0CR2 &= (SCICR2_TX_ISR_DISABLE & SCICR2_TX_DISABLE);
				/* Clear the TX in progress flag */
				TXBufferInUseFlags &= COM_CLEAR_SCI0_INTERFACE_ID;
				coreStatusA &= NBIT7;
			}else{
				coreStatusA |= BIT7;
				/* Send the stop byte */
				SCI0DRL = STOP_BYTE;
			}
		}
	}

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT4, PORTB);
}
/* Interrupt on rising and falling edges to count the number of teeth that have passed
 * in that window. 4 of the windows on the 8 tooth channel have a unique width. The pulse
 * accumulator will hold that count so there is no need to interrupt on the 360 tooth channel.
 *
 * Note: Primary LT1 Optispark Interrupt wired to the 8x channel.
 */
void PrimaryRPMISR(void) {
    /* Clear the interrupt flag for this input compare channel */
    TFLG = 0x01;
    // Grab this first as it is the most critical var in this decoder
    accumulatorRegisterCount = PACN1;/* save count before it changes */
    DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

    /* Save all relevant available data here */
    unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */
    unsigned short edgeTimeStamp = TC0; /* Save the edge time stamp */

    windowState = PTITCurrentState & 0x01; /* Save the high/low state of the port, HIGH PRIORITY some windows are only 2deg wide */
    unsigned char accumulatorCount = accumulatorRegisterCount - lastPARegisterReading;/* save count before it changes */
    lastPARegisterReading = accumulatorRegisterCount;
    unsigned char i; /* temp loop var */

    KeyUserDebugs.primaryTeethSeen++;
    KeyUserDebugs.secondaryTeethSeen += accumulatorCount;
//	DEBUG = accumulatorCount; // TODO remove DEBUG

    /* always make sure you have two good counts(there are a few windows that share counts) */
    if(!(KeyUserDebugs.decoderFlags & CAM_SYNC)) {
        // FRED do this on a per edge basis to lower chances of false match with +/- 1 counts
        if(accumulatorCount == AMBIGUOUS_COUNT) {
            return;
        } else {
            unsigned char lastEvent = 0xFF;
            for(i = 0; numberOfRealEvents > i; i++) {
                if(windowCounts[i] == accumulatorCount) {
                    if(i == 0) { /* keep our counter from going out of range */
                        KeyUserDebugs.currentEvent = 0xFF; // Will be rolled over to 0
                        lastEvent = NUMBER_OF_REAL_EVENTS - 1;
                    } else {
                        lastEvent = i - 1;
                        KeyUserDebugs.currentEvent = lastEvent; // Will be rolled up to current
                    }
                    break;
                }
            }

            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
                return;
            } else if(windowCounts[lastEvent] == lastAccumulatorCount) { /* if true we are in sync! */
                SET_SYNC_LEVEL_TO(CAM_SYNC);
            } else {
                // TODO missedsync opportunity ++ or something
            }

            lastAccumulatorCount = accumulatorCount;

            // TODO put fuzzy initial sync in place, maybe.
            //		// If still not synced, try to do fuzzy sync
            //		if(!(decoderFlags & CAM_SYNC)){
            //			// loop with +1 and -1
            //			// count fuzzy syncs, if genuine, should only be one + and one -
            //			// if not, give up and clear all state
            //		}
            //		return; // TODO remove and continue on down the thread
        }
    }

    // Not an else block because the if block above can change the state of its own condition
    if(KeyUserDebugs.decoderFlags & CAM_SYNC) {
        KeyUserDebugs.currentEvent++;
        if(KeyUserDebugs.currentEvent == numberOfRealEvents) { /* roll our event over if we are at the end */
            KeyUserDebugs.currentEvent = 0;
        }

        /*
         * bastardTeeth will be zero if things are going well, and a low number
         * if there is some latency, and a large number if totally wrong. This
         * will not catch sequences of same direction errors, though. We need
         * to keep a running track of past bastardTeeth too. TODO
         */

        signed char bastardTeeth = accumulatorCount - windowCounts[KeyUserDebugs.currentEvent];
        cumulativeBastardTeeth += bastardTeeth;

//		DEBUG = cumulativeBastardTeeth; // TODO remove DEBUG
//		DEBUG = bastardTeeth;
//		DEBUG = windowCounts[currentEvent]; // TODO remove DEBUG

        // Cumulative Tolerance Code TODO add counters to monitor aggressiveness of this
        if(windowsPerAllowedCumulativeBastardTooth) {
            cumulativeBastardTeethEroderCounter++;
            if(cumulativeBastardTeethEroderCounter == windowsPerAllowedCumulativeBastardTooth) {
                cumulativeBastardTeethEroderCounter = 0;
                if(cumulativeBastardTeeth > 0) {
                    cumulativeBastardTeeth--;
                    // DEBUG++;
                    // counter for decrement
                } else if(cumulativeBastardTeeth < 0) {
                    cumulativeBastardTeeth++;
                    // counter for increment
                } else {
                    // counter for does nothing
                }
            }
        }

        /* if we are in-sync continue checking and perform required decoder calcs */
        LongTime timeStamp;

        /* Install the low word */
        timeStamp.timeShorts[1] = edgeTimeStamp;
        /* Find out what our timer value means and put it in the high word */
        if(TFLGOF && !(edgeTimeStamp & 0x8000)) { /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
            timeStamp.timeShorts[0] = timerExtensionClock + 1;
        } else {
            timeStamp.timeShorts[0] = timerExtensionClock;
        }

        if((bastardTeeth > MAX_BASTARD_TEETH) || (bastardTeeth < -MAX_BASTARD_TEETH)) {
            resetToNonRunningState(BASTARD_SYNC_LOSS_ID_BASE + bastardTeeth); // TODO move this to the syncLossIDs.h header
            return;
        } else if((cumulativeBastardTeeth > MAX_CUMULATIVE_BASTARD_TEETH) || (cumulativeBastardTeeth < -MAX_CUMULATIVE_BASTARD_TEETH)) {
            resetToNonRunningState(BASTARD_CUMULATIVE_SYNC_LOSS_ID_BASE + cumulativeBastardTeeth); // TODO move this to the syncLossIDs.h header
            return;
        } else { // Tooth count was within spec
            if(!(KeyUserDebugs.decoderFlags & OK_TO_SCHEDULE)) {
                SET_SYNC_LEVEL_TO(CAM_SYNC); // Add confirmation until it is
            }

            /* TODO all required calcs etc as shown in other working decoders */
            if((KeyUserDebugs.currentEvent % 2) == 1) { /* if we captured on a rising edge that is to say an evenly spaced edge perform the calcs */
                // temporary data from inputs
                unsigned long primaryLeadingEdgeTimeStamp = timeStamp.timeLong;
                unsigned long timeBetweenSuccessivePrimaryPulses = primaryLeadingEdgeTimeStamp - lastPrimaryEventTimeStamp;
                lastPrimaryEventTimeStamp = primaryLeadingEdgeTimeStamp;

                /* RPM CALC, KISS for now and only run this part of the ISR when the edge has gone high
                 * this way we have evenly spaced teeth
                 */
                *ticksPerDegreeRecord = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessivePrimaryPulses) / (90 * ANGLE_FACTOR));
                // instead of above:
                // save time difference
                // have angle of time difference as setting
                // do whole rpm calc in main loop to save ISR time, make more sense, and be more coherent to read
                // then it's possible to use different tpdm figures for different RPM levels, thereby allowing a large range AND fine granularity!
                // tpd would still need to be calculated for scheduling reasons, and the different scalings would need to be checked for overflow there.

                // TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
                sampleEachADC(ADCBuffers);
                Counters.syncedADCreadings++;
                /* Set flag to say calc required */
                coreStatusA |= CALC_FUEL_IGN;
                /* Reset the clock for reading timeout */
                Clocks.timeoutADCreadingClock = 0;
            }
        }
        SCHEDULE_ECT_OUTPUTS();
    }

    OUTPUT_COARSE_BBS();

    DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
void PrimaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	unsigned short edgeTimeStamp = TC0;				/* Save the edge time stamp */
	unsigned char PTITCurrentState = PTIT;			/* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.primaryTeethSeen++;

	LongTime timeStamp;

	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}
	unsigned long thisEventTimeStamp = timeStamp.timeLong;

	unsigned char lastEvent = 0;
	unsigned short thisTicksPerDegree = 0;
	if(PTITCurrentState & 0x01){
		// temporary data from inputs
		unsigned long primaryLeadingEdgeTimeStamp = timeStamp.timeLong;
		unsigned long timeBetweenSuccessivePrimaryPulses = primaryLeadingEdgeTimeStamp - lastPrimaryEventTimeStamp;
		lastPrimaryEventTimeStamp = primaryLeadingEdgeTimeStamp;


		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessivePrimaryPulses) / angleOfSingleIteration);
		*ticksPerDegreeRecord = thisTicksPerDegree;

		// TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

		/* Set flag to say calc required */
		coreStatusA |= CALC_FUEL_IGN;

		/* Reset the clock for reading timeout */
		Clocks.timeoutADCreadingClock = 0;

		KeyUserDebugs.currentEvent = 1;
		lastEvent = 0;
	}else{
		// temporary data from inputs
		unsigned long secondaryLeadingEdgeTimeStamp = timeStamp.timeLong;
		unsigned long timeBetweenSuccessiveSecondaryPulses = secondaryLeadingEdgeTimeStamp - lastSecondaryEventTimeStamp;
		lastSecondaryEventTimeStamp = secondaryLeadingEdgeTimeStamp;


		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessiveSecondaryPulses) / angleOfSingleIteration);
		*ticksPerDegreeRecord = thisTicksPerDegree;

		// TODO make this stuff behave correctly, this one will only run at startup, and the other will always run, but do it by generic config and split this stuff out into a shared function, soon.
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

		/* Set flag to say calc required */
		coreStatusA |= CALC_FUEL_IGN;

		/* Reset the clock for reading timeout */
		Clocks.timeoutADCreadingClock = 0;

		KeyUserDebugs.currentEvent = 0;
		lastEvent = 1;
	}

	unsigned long thisInterEventPeriod = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;
	}

	// This should check during gain of sync too and prevent gaining sync if the numbers aren't right, however this is a step up for the Hotel at this time.
	if(KeyUserDebugs.decoderFlags & COMBUSTION_SYNC){
		unsigned short thisAngle = 0;
		if(KeyUserDebugs.currentEvent == 0){
			// Fix this to work for all:
//			thisAngle = eventAngles[KeyUserDebugs.currentEvent] + totalEventAngleRange - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] + angleOfSingleIteration - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
		}else{
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];
		}

		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

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}
		}
	}

	if(KeyUserDebugs.decoderFlags & COMBUSTION_SYNC){
		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	/* TODO this delays outputs until the fourth ISR execution, but we could
	 * get them one execution or 1/8 of a rev sooner if we did a preliminary
	 * calc from the previous edge instead of the previous same edge now, and
	 *
	 * The proper way to do this is set sync when we have it and not set data
	 * as having been recorded until we know the data is good. That way the
	 * scheduler can keep things unscheduled until the time is right.
	 */
	if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
		SET_SYNC_LEVEL_TO(COMBUSTION_SYNC);
	}

	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
void SecondaryRPMISR(){
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT1, PORTB);
	FLAG_AND_INC_FLAGGABLE(FLAG_CALLS_TO_UISRS_OFFSET);
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT1, PORTB);
}
void PrimaryRPMISR(void) {
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	unsigned short edgeTimeStamp = TC0;    /* Save the edge time stamp */
	unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.primaryTeethSeen++;

	LongTime timeStamp;

	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}
	unsigned long thisEventTimeStamp = timeStamp.timeLong;

	if(PTITCurrentState & 0x01){
		// Calc this period
		unsigned char lastEvent = 0;
		unsigned long thisInterEventPeriod = 0;
		if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;

			if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
				unsigned long larger;
				unsigned long smaller;
				unsigned char thisLargerThanLast;
				if(thisInterEventPeriod > lastInterEventPeriod){
					larger = thisInterEventPeriod;
					smaller = lastInterEventPeriod;
					thisLargerThanLast = 1;
				}else{
					larger = lastInterEventPeriod;
					smaller = thisInterEventPeriod;
					thisLargerThanLast = 0;
				}

				// Calculate tolerance, then add and subtract it from whatever required
				unsigned long tolerance = (smaller * MISSING_TEETH * fixedConfigs2.decoderSettings.missingToothTolerance) / 4096;
				// div by 4k = fairly high minimum RPM for low teeth wheels
				// perhaps provide some options for different tolerance on different types of expected widths
				// the wide one on larger missing counts has more time to get to a higher RPM and needs a wider tolerance
				// possible options: different percent of smaller for each type, different percent and based on ideal w/b instead of smaller
				// Another option that keeps the 25% tolerance as the correct amount for any missing count is to simply take the percentage of
				// the smaller component and multiply by the number of missing teeth! This can be a flash config flag option or possibly rpm thresholded
				// it could be done on a per level basis too.
				unsigned long idealWide = 0;
				unsigned long idealBackward = 0;
				if(larger < (smaller + tolerance)){ // has to be first to be most efficient
					matches.pairs.thisPair = MatchedPair; // same period, roughly
				}else{
					idealWide = smaller * (MISSING_TEETH + 1); // has to be second to be most efficient
					if((larger < (idealWide + tolerance)) && (larger > (idealWide - tolerance))){
						if(thisLargerThanLast){
							matches.pairs.thisPair = NarrowWide;
						}else{
							matches.pairs.thisPair = WideNarrow;
						}
					}else{ // We're not in good shape...
						idealBackward = ((smaller * (MISSING_TEETH + 2)) / 2); // this leads to further code running later, so should come next
						if((larger <  (idealBackward + tolerance)) && (larger > (idealBackward - tolerance))){
							if(thisLargerThanLast){
								matches.pairs.thisPair = NarrowBackward;
							}else{
								matches.pairs.thisPair = BackwardNarrow;
							}
						}else if(larger > (idealWide + tolerance)){ // We're in very bad shape...
							if(thisLargerThanLast){
								resetToNonRunningState(yourVRSensorHasALoosePlugFixIt);
							}else{
								resetToNonRunningState(noiseAppearedWayTooEarlyAsIfItWasAVRToothButWasnt);
							}
						}else{ // Fell between the cracks, not matched, settings very tight, therefore was in two possible places on either side of (N+2)/2.
							resetToNonRunningState(yourSyncToleranceIsTighterThanAWellYouGetTheIdea);
						}
					}
				}

				// This all needs a little more complexity for cam only/crank only/crank + cam sync use, hard coded to crank only for now
				if(KeyUserDebugs.decoderFlags & LAST_MATCH_VALID){ // If we have enough data
					if(KeyUserDebugs.decoderFlags & CRANK_SYNC){
						lastEvent = KeyUserDebugs.currentEvent;
						KeyUserDebugs.currentEvent++;
						if(KeyUserDebugs.currentEvent == numberOfRealEvents){
							KeyUserDebugs.currentEvent = 0;
						}

						if((KeyUserDebugs.currentEvent == 0) && (matches.pattern != MatchedPairNarrowWide)){ // First event after gap
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						}else if((KeyUserDebugs.currentEvent == 1) && (matches.pattern != NarrowWideWideNarrow)){ // Second event after gap
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						}else if((KeyUserDebugs.currentEvent == 2) && (matches.pattern != WideNarrowMatchedPair)){ // Third event after gap
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						}else if((KeyUserDebugs.currentEvent > 2) && (matches.pattern != MatchedPairMatchedPair)){ // All other events should be preceeded by two matched pairs
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						} // else carry on happily as always
					}else{
						if(matches.pattern == MatchedPairMatchedPair){      //         | small | small | small | - All periods match, could be anywhere, unless...
							NumberOfTwinMatchedPairs++;
							// Because this method REQUIRES 4 evenly spaced teeth to work, it's only available to 5-1 or greater wheels.
							if((NUMBER_OF_WHEEL_EVENTS > 3) && (NumberOfTwinMatchedPairs == (NUMBER_OF_WHEEL_EVENTS - 3))){ // This can't find a match until it's on it's fourth execution
								// This will match repeatedly then un-sync on next cycle if tolerance is set too high
								KeyUserDebugs.currentEvent = NUMBER_OF_WHEEL_EVENTS - 1; // Zero indexed
								lastEvent = KeyUserDebugs.currentEvent - 1;
								SET_SYNC_LEVEL_TO(CRANK_SYNC); // Probability of this = (N + 1) / M
								// Sample RPM and ADCs here on the basis of cylinders and revolutions
								// IE, sample RPM once (total teeth (inc missing) per engine cycle / cyls) events have passed
								// And, do it from the last matching tooth, and do that on every tooth
								// So have a buffer of time stamps, which would take a LOT of RAM, hmmm, perhaps just wait.
								// Missing teeth users are clearly not fussed about fast starting anyway
								// And once sync is gained good readings can be taken without excess memory usage
							}else if((NUMBER_OF_WHEEL_EVENTS > 3) && (NumberOfTwinMatchedPairs > (NUMBER_OF_WHEEL_EVENTS - 3))){ // More matched pairs than possible with config
								resetToNonRunningState(yourSyncToleranceIsLooserThanAWellYouGetTheIdea);
							} // else fall through to wait.
						}else if(matches.pattern == MatchedPairNarrowWide){ // | small | small |      BIG      | Last tooth is first tooth after missing  - ((M-N)-3)/M = common
							KeyUserDebugs.currentEvent = 0;
							lastEvent = NUMBER_OF_WHEEL_EVENTS - 1; // Zero indexed
							SET_SYNC_LEVEL_TO(CRANK_SYNC);
						}else if(matches.pattern == NarrowWideWideNarrow){  // | small |      BIG      | small | Last tooth is second tooth after missing - 1/M
							KeyUserDebugs.currentEvent = 1;
							lastEvent = 0;
							SET_SYNC_LEVEL_TO(CRANK_SYNC);
						}else if(matches.pattern == WideNarrowMatchedPair){ // |      BIG      | small | small | Last tooth is third tooth after missing  - 1/M
							KeyUserDebugs.currentEvent = 2;
							lastEvent = 1;
							SET_SYNC_LEVEL_TO(CRANK_SYNC);
						}else{
							resetToNonRunningState(matches.pattern); // Where they are defined individually in the error file! Beautiful!!
						}
					}
				}
			}
		}

		unsigned short thisTicksPerDegree = 0;
		if(KeyUserDebugs.decoderFlags & CRANK_SYNC){
			unsigned short thisAngle = 0;
			if(KeyUserDebugs.currentEvent == 0){
				thisAngle = eventAngles[KeyUserDebugs.currentEvent] + angleOfSingleIteration - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
			}else{
				thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];
			}
			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

			if(KeyUserDebugs.decoderFlags & LAST_TPD_VALID){
				unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
				KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
				if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
					resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
					return;
				}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
					resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
					return;
				}
			}

			SCHEDULE_ECT_OUTPUTS();

			KeyUserDebugs.decoderFlags |= LAST_TPD_VALID;

			// sample adcs and record rpm here after scheduling
			*ticksPerDegreeRecord = thisTicksPerDegree;

			sampleEachADC(ADCBuffers);
			Counters.syncedADCreadings++;

			// Set flag to say calc required
			coreStatusA |= CALC_FUEL_IGN;

			// Reset the clock for reading timeout
			Clocks.timeoutADCreadingClock = 0;
		}

		OUTPUT_COARSE_BBS();

		if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
				KeyUserDebugs.decoderFlags |= LAST_MATCH_VALID;
			}
			matches.pairs.lastPair = matches.pairs.thisPair; // Stash var for next time
			lastInterEventPeriod = thisInterEventPeriod;
			lastTicksPerDegree = thisTicksPerDegree;
			KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
		}
		// Always
		lastEventTimeStamp = thisEventTimeStamp;
		KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;
	}else{
		// do checking for width variance too, perhaps optionally.
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
Example #13
0
/* Fire from serial, then repeat X revolutions or seconds or whatever and trigger Z outputs of various types etc
 *
 * Possible modes of repetition:
 * - Iterations
 * - Revolutions
 * - Time units
 */
void PrimaryRPMISR(){
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	unsigned short edgeTimeStamp = TC0;

	/* Reset the clock for reading timeout */
	Clocks.timeoutADCreadingClock = 0;

	// call sched output with args
	LongTime timeStamp;
	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}

	unsigned char shouldFire = 1;
//	unsigned long localPeriod = 0; // mutlifire or busy wait, if doing this, check last period for some min, and if too small, shrink second to last and increase last
//	unsigned short localPeriod = 0; // normal mode
	if(testMode == TEST_MODE_ITERATIONS){
		KeyUserDebugs.currentEvent++;
		if(KeyUserDebugs.currentEvent == testEventsPerCycle){
			KeyUserDebugs.currentEvent = 0;
			testNumberOfCycles--;
			if(testNumberOfCycles == 0){
				// Disable the interrupt again, to be enabled by a serial trigger
				TIE &= NBIT0;
				coreStatusA &= CLEAR_BENCH_TEST_ON;
				return;
			}
		}

		// TODO make this more sophisticated
		TC0 += testTicksPerEvent;
	}else if(testMode == TEST_MODE_DODGY_MISSING_TOOTH){
		KeyUserDebugs.currentEvent++;
		if(KeyUserDebugs.currentEvent == testEventsPerCycle){
			KeyUserDebugs.currentEvent = 0;
			testNumberOfCycles--;
			if(testNumberOfCycles == 0){
				// Disable the interrupt again, to be enabled by a serial trigger
				TIE &= NBIT0;
				coreStatusA &= CLEAR_BENCH_TEST_ON;
				return;
			}
		}

		// Grab updated time period
		testTicksPerEvent = CoreVars->RPM;

		// Output the sync pulse only once per "engine cycle"
		if(KeyUserDebugs.currentEvent == 0){
			// Schedule the cam pulse
			outputEventDelayFinalPeriod[1] = 3*(testTicksPerEvent/4);
			outputEventPulseWidthsMath[1] = testTicksPerEvent;
			outputEventInputEventNumbers[1] = KeyUserDebugs.currentEvent;
		}else{
			outputEventInputEventNumbers[1] = 0xFF;
		}

		// Generate crank strength signal
		unsigned char fakeCurrentEvent = 0;
		if(KeyUserDebugs.currentEvent >= (testEventsPerCycle/2)){
			fakeCurrentEvent = KeyUserDebugs.currentEvent - (testEventsPerCycle/2);
		}else{
			fakeCurrentEvent = KeyUserDebugs.currentEvent;
		}

		// Schedule the main teeth, or not
		if(fakeCurrentEvent < testNumberOfMissing){
			outputEventInputEventNumbers[0] = 0xFF;
		}else{
			outputEventDelayFinalPeriod[0] = decoderMaxCodeTime;
			outputEventInputEventNumbers[0] = KeyUserDebugs.currentEvent;

			unsigned short singleWidth = testTicksPerEvent/2;
			// See if this is the last one before the gap
			if((KeyUserDebugs.currentEvent == ((testEventsPerCycle / 2) - 1)) || (KeyUserDebugs.currentEvent == (testEventsPerCycle - 1))){
				// Migrate this to a safeMultiply() inline function
				unsigned long wideWideWidth = (unsigned long)singleWidth * (testNumberOfMissing + 1);
				if(wideWideWidth < SHORTMAX){
					outputEventPulseWidthsMath[0] = (unsigned short)wideWideWidth;
				}else{
					outputEventPulseWidthsMath[0] = SHORTMAX;
				}
			}else{
				outputEventPulseWidthsMath[0] = singleWidth;
			}
		}

		TC0 += testTicksPerEvent;
	}else if(testMode == TEST_MODE_REVOLUTIONS){
		// sub modes of different patterns, use scheduler for this by setting the ADC array up and probing/triggering/touching/poking/starting/
		// switch statement for selecting different const arrays of angles, use busy wait, or multiple interrupt to do larger gaps for lower rpms/coarse events
		// perhaps accept the pattern in the input packet and busy wait on some "run completed" flag before returning and freeing the buffer.
		// TEMP de-configure timers and leave shouldFire zeroed.
		TIE &= NBIT0;
	}else if(testMode == TEST_MODE_TIME_UNITS_SECONDS){
		// reset all timer modes for first time around, then check for timer >= requested value, check appropriate units of time, obviously...
		// TEMP de-configure timers and leave shouldFire zeroed.
		TIE &= NBIT0;
	}else if(testMode == TEST_MODE_TIME_UNITS_MINUTES){
		// ditto
		// TEMP de-configure timers and leave shouldFire zeroed.
		TIE &= NBIT0;
	}else if(testMode == TEST_MODE_TIME_UNITS_HOURS){
		// ditto again
		// TEMP de-configure timers and leave shouldFire zeroed.
		TIE &= NBIT0;
	}else{
		// de-configure timers and leave shouldFire zeroed.
		TIE &= NBIT0;
	}

	if(shouldFire){
		// configuration for multiple periods setup here?

		// fire outputs here
		unsigned char channel;
		for(channel = 0;channel < 6;channel++){
			if(KeyUserDebugs.currentEvent == outputEventInputEventNumbers[channel]){
				schedulePortTPin(channel, timeStamp);
			}
		}
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
Example #14
0
/** @brief Real Time Interrupt Handler
 *
 * Handles time keeping, including all internal clocks, and generic periodic
 * tasks that run quickly and must be done on time.
 */
void RTIISR() {
    /* Clear the RTI flag */
    CRGFLG = 0x80;

    DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT3, PORTB);

    /* Increment the counter */
    Clocks.realTimeClockMain++;

    /* This function could be performed without the extra variables by rolling over the main ones at the largest multiples of the next ones, but I'm not sure thats better */

    // TODO add content to eighths of a milli RTC ?

    /// @todo TODO refactor this entire file, especially to remove apparently expensive modulus operations which could be replaced with >= instead. Maybe much more.
    /* Every 8th RTI execution is one milli */
    if(Clocks.realTimeClockMain % 8 == 0) {
        /* Increment the milli counter */
        Clocks.realTimeClockMillis++;

        /* Increment the milli roll over variable */
        Clocks.millisToTenths++;

        /* Perform all tasks that are once per millisecond here or preferably main */
        Clocks.timeoutADCreadingClock++;
        if(Clocks.timeoutADCreadingClock > fixedConfigs2.sensorSettings.readingTimeout) {
            /* Set force read adc flag */
            coreStatusA |= FORCE_READING;
            Clocks.timeoutADCreadingClock = 0;
        } else if (CoreVars->RPM > 0) { // turn on very quickly if rpm appears non zero, temp impl...
            PORTA |= BIT7;
        }


#ifdef XGATE_TESTS
#include "xgateTests.c"
#endif


        /* Every 100 millis is one tenth */
        if(Clocks.millisToTenths % 100 == 0) {
            /* Increment the tenths counter */
            Clocks.realTimeClockTenths++;

            /* Increment the tenths roll over variable */
            Clocks.tenthsToSeconds++;

            /* Reset the millis roll over variable */
            Clocks.millisToTenths = 0;

            /* Perform all tasks that are once per tenth of a second here or preferably main */
            // decrement port H debounce variable till it's zero again.
            if(portHDebounce != 0) {
                portHDebounce -= 1;
            }

            /* Every 10 tenths is one second */
            if(Clocks.tenthsToSeconds % 10 == 0) {
                /* Increment the seconds counter */
                Clocks.realTimeClockSeconds++;

                /* Increment the seconds roll over variable */
                Clocks.secondsToMinutes++;

                /* Reset the tenths roll over variable */
                Clocks.tenthsToSeconds = 0;
                /* Perform all tasks that are once per second here or preferably main */

                // Toggle the CEL on the same pin as the SM load/run switch
                PORTA ^= BIT6;

                // temp fuel pump prime and safety off impl
                if(coreStatusA & FUEL_PUMP_PRIME) {
                    if(Clocks.secondsToMinutes == fixedConfigs2.sensorSettings.fuelPumpPrimePeriod) {
                        coreStatusA &= CLEAR_FUEL_PUMP_PRIME;
                        PORTA &= NBIT7;
                    }
                } else if(CoreVars->RPM == 0) { /// @todo TODO This is too quick to turn off, average 0.5 seconds, which is OK, but fastest = 0seconds which is difficult to understand, needs a flag and to be 1 - 2 with average 1.5.
                    PORTA &= NBIT7;
                }

                /* Every 60 seconds is one minute, 65535 minutes is enough for us :-) */
                if(Clocks.secondsToMinutes % 60 == 0) {
                    /* Increment the minutes counter */
                    Clocks.realTimeClockMinutes++;

                    /* Potentially put an hours field in here and below, but that would be excessive */
                    // TODO add hours RTC ?

                    /* Reset the seconds roll over variable */
                    Clocks.secondsToMinutes = 0;

                    /* Perform all tasks that are once per minute here or preferably main */
                    // TODO add content in minutes RTC ?

                    /* Hours if statement here if we do hours which we probably won't */
                }
            }
        }
    }
    DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT3, PORTB);
}