Esempio n. 1
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);
}
Esempio n. 2
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();
	}
}