// 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);
}
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);
}
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);
}
Ejemplo n.º 4
0
/** @brief The main function!
 *
 * The centre of the application is here. From here all non-ISR code is called
 * directly or indirectly. The two coarse blocks are init and the main loop.
 * Init is called first to set everything up and then the main loop is entered
 * where the flow of control continues until the device is switched off or
 * reset (excluding asynchronous ISR code). Currently the main loop only runs
 * the fuel, ignition and scheduling calculation code, and only when actually
 * required. The intention is to maintain a very low latency for calculations
 * such that the behaviour of the device more closely reflects the attached
 * engines rapidly changing requirements. When accessory code is added a new
 * scheduling algorithm will be required to keep the latency low without
 * starving any particular blocks of CPU time.
 *
 * @author Fred Cooke
 */
int  main(){ // TODO maybe move this to paged flash ?
	// Set everything up.
	init();

	//LongNoTime.timeLong = 54;
	// Run forever repeating.
	while(TRUE){
	//	unsigned short start = realTimeClockMillis;
		/* If ADCs require forced sampling, sample now */
		if(coreStatusA & FORCE_READING){
			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure a full set of readings are taken together */

			/* Check to ensure that a reading wasn't take before we entered a non interruptable state */
			if(coreStatusA & FORCE_READING){ // do we still need to do this TODO ?

				sampleEachADC(ADCArraysRecord); // TODO still need to do a pair of loops and clock these two functions for performance.
				//sampleLoopADC(&ADCArrays);
				resetToNonRunningState();
				Counters.timeoutADCreadings++;

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

				/* Clear force reading flag */
				coreStatusA &= CLEAR_FORCE_READING;
			}

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
		}

		/* If required, do main fuel and ignition calcs first */
		if(coreStatusA & CALC_FUEL_IGN){
			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure that we don't clear the flag for the next data set when things are tight */

			/* Switch input bank so that we have a stable set of the latest data */
			if(ADCArrays == &ADCArrays1){
				RPM = &RPM0; // TODO temp, remove
				RPMRecord = &RPM1; // TODO temp, remove
				ADCArrays = &ADCArrays0;
				ADCArraysRecord = &ADCArrays1;
				mathSampleTimeStamp = &ISRLatencyVars.mathSampleTimeStamp0; // TODO temp, remove
				mathSampleTimeStampRecord = &ISRLatencyVars.mathSampleTimeStamp1; // TODO temp, remove
			}else{
				RPM = &RPM1; // TODO temp, remove
				RPMRecord = &RPM0; // TODO temp, remove
				ADCArrays = &ADCArrays1;
				ADCArraysRecord = &ADCArrays0;
				mathSampleTimeStamp = &ISRLatencyVars.mathSampleTimeStamp1; // TODO temp, remove
				mathSampleTimeStampRecord = &ISRLatencyVars.mathSampleTimeStamp0; // TODO temp, remove
			}

			/* Clear the calc required flag */
			coreStatusA &= CLEAR_CALC_FUEL_IGN;

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/

			/* Store the latency from sample time to runtime */
			ISRLatencyVars.mathLatency = TCNT - *mathSampleTimeStamp;
			/* Keep track of how many calcs we are managing per second... */
			Counters.calculationsPerformed++;
			/* ...and how long they take each */
			unsigned short mathStartTime = TCNT;

			/* Generate the core variables from sensor input and recorded tooth timings */
			generateCoreVars();

			RuntimeVars.genCoreVarsRuntime = TCNT - mathStartTime;
			unsigned short derivedStartTime = TCNT;

			/* Generate the derived variables from the core variables based on settings */
			generateDerivedVars();

			RuntimeVars.genDerivedVarsRuntime = TCNT - derivedStartTime;
			unsigned short calcsStartTime = TCNT;

			/* Perform the calculations TODO possibly move this to the software interrupt if it makes sense to do so */
			calculateFuelAndIgnition();

			RuntimeVars.calcsRuntime = TCNT - calcsStartTime;
			/* Record the runtime of all the math total */
			RuntimeVars.mathTotalRuntime = TCNT - mathStartTime;

			RuntimeVars.mathSumRuntime = RuntimeVars.calcsRuntime + RuntimeVars.genCoreVarsRuntime + RuntimeVars.genDerivedVarsRuntime;

			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure that outputBank and outputBank Offsets match */

			/* Switch banks to the latest data */
			if(injectorMainPulseWidthsMath == injectorMainPulseWidths1){
				currentDwellMath = &currentDwell0;
				currentDwellRealtime = &currentDwell1;
				injectorMainPulseWidthsMath = injectorMainPulseWidths0;
				injectorMainPulseWidthsRealtime = injectorMainPulseWidths1;
				injectorStagedPulseWidthsMath = injectorStagedPulseWidths0;
				injectorStagedPulseWidthsRealtime = injectorStagedPulseWidths1;
			}else{
				currentDwellMath = &currentDwell1;
				currentDwellRealtime = &currentDwell0;
				injectorMainPulseWidthsMath = injectorMainPulseWidths1;
				injectorMainPulseWidthsRealtime = injectorMainPulseWidths0;
				injectorStagedPulseWidthsMath = injectorStagedPulseWidths1;
				injectorStagedPulseWidthsRealtime = injectorStagedPulseWidths0;
			}

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
		}else{
			/* In the event that no calcs are required, sleep a little before returning to retry. */
			sleepMicro(RuntimeVars.mathTotalRuntime); // not doing this will cause the ISR lockouts to run for too high a proportion of the time
			/* Using 0.8 ticks as micros so it will run for a little longer than the math did */
		}


		if(!(TXBufferInUseFlags)){
			/* If the flag for com packet processing is set and the TX buffer is available process the data! */
			if(RXStateFlags & RX_READY_TO_PROCESS){
				/* Clear the flag */
				RXStateFlags &= RX_CLEAR_READY_TO_PROCESS;

				/* Handle the incoming packet */
				decodePacketAndRespond();
			}else if(lastCalcCount != Counters.calculationsPerformed){ // substitute true for full speed continuous stream test...

				/* send asynchronous data log if required */
				switch (TablesB.SmallTablesB.datalogStreamType) {
					case asyncDatalogOff:
					{
						break;
					}
					case asyncDatalogBasic:
					{
						/* Flag that we are transmitting! */
						TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
						// SCI0 only for now...

						// headers including length...						*length = configuredBasicDatalogLength;
						TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;

						/* Initialised here such that override is possible */
						TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
						TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;

						/* Set the flags : firmware, no ack, no addrs, has length */
						*TXBufferCurrentPositionHandler = HEADER_HAS_LENGTH;
						TXBufferCurrentPositionHandler++;

						/* Set the payload ID */
						*((unsigned short*)TXBufferCurrentPositionHandler) = responseBasicDatalog;
						TXBufferCurrentPositionHandler += 2;

						/* Set the length */
						*((unsigned short*)TXBufferCurrentPositionHandler) = configuredBasicDatalogLength;
						TXBufferCurrentPositionHandler += 2;

						/* populate data log */
						populateBasicDatalog();
						finaliseAndSend(0);
						break;
					}
					case asyncDatalogConfig:
					{
						/// TODO @todo
						break;
					}
					case asyncDatalogTrigger:
					{
						/// TODO @todo
						break;
					}
					case asyncDatalogADC:
					{
						/// TODO @todo
						break;
					}
					case asyncDatalogCircBuf:
					{
						/// TODO @todo
						break;
					}
					case asyncDatalogCircCAS:
					{
						/// TODO @todo
						break;
					}
					case asyncDatalogLogic:
					{
						/// TODO @todo
						break;
					}
				}
				// mechanism to ensure we only send something if the data has been updated
				lastCalcCount = Counters.calculationsPerformed;
			}
		}
		// on once per cycle for main loop heart beat (J0)
		PORTJ ^= 0x01;


		// debug...
		if(SCI0CR2 & SCICR2_RX_ENABLE){
			PORTK |= BIT2;
		}else{
			PORTK &= NBIT2;
		}

		if(SCI0CR2 & SCICR2_RX_ISR_ENABLE){
			PORTK |= BIT3;
		}else{
			PORTK &= NBIT3;
		}

		// PWM experimentation
		adjustPWM();
	}
}
Ejemplo n.º 5
0
/* 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 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);
}
Ejemplo n.º 8
0
/** @brief The main function!
 *
 * The centre of the application is here. From here all non-ISR code is called
 * directly or indirectly. The two coarse blocks are init and the main loop.
 * Init is called first to set everything up and then the main loop is entered
 * where the flow of control continues until the device is switched off or
 * reset (excluding asynchronous ISR code). Currently the main loop only runs
 * the fuel, ignition and scheduling calculation code, and the communications
 * code and only when actually required. The intention is to maintain a very
 * low latency for calculations such that the behaviour of the device more
 * closely reflects the attached engines rapidly changing requirements. When
 * accessory code is added a new scheduling algorithm will be required to keep
 * the latency low without starving any particular blocks of CPU time.
 */
int  main(){ /// @todo TODO maybe move this to paged flash ?
	// Set everything up.
	init();

	/// @todo TODO Add verification reporting code here that disables the timer interrupts such that no events ever get scheduled, and then sits looping sending error packets out about what is wrong. set a whole bunch of flags and check them here sending a packet for each with a unique errorID for each and thus a unique easy to understand message for each on the PC side. BEFORE the priming code such that no fuel gets injected. Will need to modularise the comms stuff to process packets based on calls from this section too, avoid excess duplication if possible.

	// TODO move this to a function so that it can be called on a hot restart post being asleep.
	#define NUMBER_OF_OUTPUT_PINS 6
	unsigned char outputEvent;
	unsigned char activeFuelChannels[NUMBER_OF_OUTPUT_PINS] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
	for(outputEvent = 0;outputEvent < fixedConfigs1.schedulingSettings.numberOfConfiguredOutputEvents;outputEvent++){
		if(fixedConfigs1.schedulingSettings.schedulingConfigurationBits[outputEvent] == 1 && fixedConfigs1.schedulingSettings.outputEventPinNumbers[outputEvent] < NUMBER_OF_OUTPUT_PINS) { // todo remove second condition?
			activeFuelChannels[fixedConfigs1.schedulingSettings.outputEventPinNumbers[outputEvent]] = outputEvent;
		}
	}
	sampleEachADC(ADCBuffers);       // Read sensors - from utils.h/.c
	generateCoreVars();              // Calculate BRV - from coreVarsGenerator.h/.c
	generateDerivedVars();           // Calculate IDT - from derivedVarsGenerator.h/.c
	// lookupTwoDTanlesUS is in tableLookup.h/.c
	unsigned short primingPulseWidth = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.primingVolumeTable, CoreVars->CHT);
	primingPulseWidth = safeAdd(primingPulseWidth, DerivedVars->IDT);
	unsigned short edgeTimeStamp = TCNT;
	// 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 outputPin;
	for(outputPin = 0; outputPin< NUMBER_OF_OUTPUT_PINS; outputPin++){
		if(activeFuelChannels[outputPin] < MAX_NUMBER_OF_OUTPUT_EVENTS){
			outputEventPulseWidthsMath[activeFuelChannels[outputPin]] = primingPulseWidth;
			outputEventDelayFinalPeriod[activeFuelChannels[outputPin]] = SHORTHALF;
			schedulePortTPin(activeFuelChannels[outputPin], timeStamp); // from 
		}
	}

	// Run forever repeating.
	while(TRUE){
		//unsigned short start = realTimeClockMillis;
		/* If ADCs require forced sampling, sample now */
		if(coreStatusA & FORCE_READING){
			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure a full set of readings are taken together */

			/* Check to ensure that a reading wasn't take before we entered a non interruptable state */
			if(coreStatusA & FORCE_READING){ // do we still need to do this TODO ?

				sampleEachADC(ADCBuffersRecord); // TODO still need to do a pair of loops and clock these two functions for performance.
				//sampleLoopADC(&ADCBuffers);
				resetToNonRunningState(EVENT_ARRIVAL_TIMEOUT);
				Counters.timeoutADCreadings++;

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

				/* Clear force reading flag */
				coreStatusA &= CLEAR_FORCE_READING;
			}

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
		}

		/* If required, do main fuel and ignition calcs first */
		if(coreStatusA & CALC_FUEL_IGN){
			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure that we don't clear the flag for the next data set when things are tight */

			/* Switch input bank so that we have a stable set of the latest data */
			if(ADCBuffers == &ADCBuffers1){
				ticksPerDegree = &ticksPerDegree0; // TODO temp, remove, maybe
				ticksPerDegreeRecord = &ticksPerDegree1; // TODO temp, remove, maybe
				ADCBuffers = &ADCBuffers0;
				ADCBuffersRecord = &ADCBuffers1;
			}else{
				ticksPerDegree = &ticksPerDegree1; // TODO temp, remove, maybe
				ticksPerDegreeRecord = &ticksPerDegree0; // TODO temp, remove, maybe
				ADCBuffers = &ADCBuffers1;
				ADCBuffersRecord = &ADCBuffers0;
			}

			/* Clear the calc required flag */
			coreStatusA &= CLEAR_CALC_FUEL_IGN;

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/

			// TODO DEBUG/TUNING MACRO HERE!
			/* Keep track of how many calcs we are managing per second... */
			Counters.calculationsPerformed++;

			/* Generate the core variables from sensor input and recorded tooth timings */
			generateCoreVars();
			// TODO DEBUG/TUNING MACRO HERE!

			/* Generate the derived variables from the core variables based on settings */
			generateDerivedVars();
			// TODO DEBUG/TUNING MACRO HERE!

			/* Perform the calculations TODO possibly move this to the software interrupt if it makes sense to do so */
			calculateFuelAndIgnition();
			// TODO DEBUG/TUNING MACRO HERE!

			/* Calculate the scheduling based on configuration and previously calculated variables */
			scheduleOutputs();
			// TODO DEBUG/TUNING MACRO HERE!
		}else{
			/* In the event that no calcs are required, sleep a little before returning to retry. */
			sleepMicro(3000); // TODO tune this, and then replace it completely. not doing this will cause the ISR lockouts to run for too high a proportion of the time
			/* Using 0.8 ticks as micros so it will run for a little longer than the math did */
		}


		if(!(TXBufferInUseFlags)){
			/* If the flag for com packet processing is set and the TX buffer is available process the data! */
			if(RXStateFlags & RX_READY_TO_PROCESS){
				/* Clear the flag */
				RXStateFlags &= RX_CLEAR_READY_TO_PROCESS;

				/* Handle the incoming packet */
				decodePacketAndRespond();
			}else{// if(lastCalcCount != Counters.calculationsPerformed){ // substitute true for full speed continuous stream test...

				/* send asynchronous data log if required */
				switch (TablesB.SmallTablesB.loggingSettings.datalogStreamType) {
					case asyncDatalogOff:
					{
						break;
					}
					case asyncDatalogBasic:
					{
						/* Flag that we are transmitting! */
						TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
						// SCI0 only for now...

						// headers including length...                 *length = configuredBasicDatalogLength;
						TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;

						/* Initialised here such that override is possible */
						TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
						TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;

						/* Set the flags : firmware, no ack, no addrs, has length */
						*TXBufferCurrentPositionHandler = HEADER_HAS_LENGTH;
						TXBufferCurrentPositionHandler++;

						/* Set the payload ID */
						*((unsigned short*)TXBufferCurrentPositionHandler) = responseBasicDatalog;
						TXBufferCurrentPositionHandler += 2;

						/* Set the length */
						unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
						TXBufferCurrentPositionHandler += 2;

						/* populate data log */
						*localLength = populateBasicDatalog();
						finaliseAndSend(0);
						break;
					}
					case asyncDatalogScratchPad:
					{
						break;
					}
					case asyncDatalogStructs:
					{
						break;
					}
					case asyncDatalogPosition:
					{
						break;
					}
					case asyncDatalogBlockBytes:
					{
						break;
					}
					case asyncDatalogBlockWords:
					{
						break;
					}
					case asyncDatalogBlockLongs:
					{
						break;
					}
					case asyncDatalogStreamByte:
					{
						/* Flag that we are transmitting! */
						TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
						// SCI0 only for now...

						// headers including length...                 *length = configuredBasicDatalogLength;
						TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;

						/* Initialised here such that override is possible */
						TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
						TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;

						/* Set the flags all zeros */
						*TXBufferCurrentPositionHandler = 0;
						TXBufferCurrentPositionHandler++;

						/* Set the payload ID */
						*((unsigned short*)TXBufferCurrentPositionHandler) = responseByteLADatalog;
						TXBufferCurrentPositionHandler += 2;

						/** Store PTIT for now, later make address of byte configurable TODO @todo */
						*((unsigned char*)TXBufferCurrentPositionHandler) = PTIT;
						TXBufferCurrentPositionHandler++;

						finaliseAndSend(0);
						break;
					}
					case asyncDatalogStreamWord:
					{
						break;
					}
					case asyncDatalogStreamLong:
					{
						break;
					}
				}
				// mechanism to ensure we only send something if the data has been updated
				lastCalcCount = Counters.calculationsPerformed;
			}
		}

		performSimpleGPIO();

		// PWM experimentation
		adjustPWM();
	}
}