1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2012 Fred Cooke
5 * This file is part of the FreeEMS project.
7 * FreeEMS software is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * FreeEMS software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
20 * We ask that if you make any changes to this file you email them upstream to
21 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 * Thank you for choosing FreeEMS to run your engine!
29 * @brief The main function!
31 * The function main is traditionally an applications starting point. For us
32 * it has two jobs. The first is to call init() which initialises everything
33 * before any normal code runs. After that main() is simply an infinite loop
34 * from which low priority non-realtime code runs. The most important units of
35 * code that runs under the main loop umbrella are the injection, ignition and
36 * scheduling calculations.
43 /** @brief The main function!
45 * The centre of the application is here. From here all non-ISR code is called
46 * directly or indirectly. The two coarse blocks are init and the main loop.
47 * Init is called first to set everything up and then the main loop is entered
48 * where the flow of control continues until the device is switched off or
49 * reset (excluding asynchronous ISR code). Currently the main loop only runs
50 * the fuel, ignition and scheduling calculation code, and the communications
51 * code and only when actually required. The intention is to maintain a very
52 * low latency for calculations such that the behaviour of the device more
53 * closely reflects the attached engines rapidly changing requirements. When
54 * accessory code is added a new scheduling algorithm will be required to keep
55 * the latency low without starving any particular blocks of CPU time.
57 int main(){ /// @todo TODO maybe move this to paged flash ?
61 /// @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.
63 // TODO move this to a function so that it can be called on a hot restart post being asleep.
64 #define NUMBER_OF_OUTPUT_PINS 6
65 unsigned char outputEvent
;
66 unsigned char activeFuelChannels
[NUMBER_OF_OUTPUT_PINS
] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
67 for(outputEvent
= 0;outputEvent
< fixedConfigs1
.schedulingSettings
.numberOfConfiguredOutputEvents
;outputEvent
++){
68 if(fixedConfigs1
.schedulingSettings
.schedulingConfigurationBits
[outputEvent
] == 1 && fixedConfigs1
.schedulingSettings
.outputEventPinNumbers
[outputEvent
] < NUMBER_OF_OUTPUT_PINS
) { // todo remove second condition?
69 activeFuelChannels
[fixedConfigs1
.schedulingSettings
.outputEventPinNumbers
[outputEvent
]] = outputEvent
;
72 sampleEachADC(ADCBuffers
); // Read sensors
73 generateCoreVars(); // Calculate BRV
74 generateDerivedVars(); // Calculate IDT
75 unsigned short primingPulseWidth
= lookupTwoDTableUS((twoDTableUS
*)&TablesA
.SmallTablesA
.primingVolumeTable
, CoreVars
->CHT
);
76 primingPulseWidth
= safeAdd(primingPulseWidth
, DerivedVars
->IDT
);
77 unsigned short edgeTimeStamp
= TCNT
;
78 // call sched output with args
80 /* Install the low word */
81 timeStamp
.timeShorts
[1] = edgeTimeStamp
;
82 /* Find out what our timer value means and put it in the high word */
83 if(TFLGOF
&& !(edgeTimeStamp
& 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
84 timeStamp
.timeShorts
[0] = timerExtensionClock
+ 1;
86 timeStamp
.timeShorts
[0] = timerExtensionClock
;
88 unsigned char outputPin
;
89 for(outputPin
= 0; outputPin
< NUMBER_OF_OUTPUT_PINS
; outputPin
++){
90 if(activeFuelChannels
[outputPin
] < MAX_NUMBER_OF_OUTPUT_EVENTS
){
91 outputEventPulseWidthsMath
[activeFuelChannels
[outputPin
]] = primingPulseWidth
;
92 outputEventDelayFinalPeriod
[activeFuelChannels
[outputPin
]] = SHORTHALF
;
93 schedulePortTPin(activeFuelChannels
[outputPin
], timeStamp
);
97 // Run forever repeating.
99 //unsigned short start = realTimeClockMillis;
100 /* If ADCs require forced sampling, sample now */
101 if(coreStatusA
& FORCE_READING
){
102 ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
103 /* Atomic block to ensure a full set of readings are taken together */
105 /* Check to ensure that a reading wasn't take before we entered a non interruptable state */
106 if(coreStatusA
& FORCE_READING
){ // do we still need to do this TODO ?
108 sampleEachADC(ADCBuffersRecord
); // TODO still need to do a pair of loops and clock these two functions for performance.
109 //sampleLoopADC(&ADCBuffers);
110 resetToNonRunningState(EVENT_ARRIVAL_TIMEOUT
);
111 Counters
.timeoutADCreadings
++;
113 /* Set flag to say calc required */
114 coreStatusA
|= CALC_FUEL_IGN
;
116 /* Clear force reading flag */
117 coreStatusA
&= CLEAR_FORCE_READING
;
120 ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
123 /* If required, do main fuel and ignition calcs first */
124 if(coreStatusA
& CALC_FUEL_IGN
){
125 ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
126 /* Atomic block to ensure that we don't clear the flag for the next data set when things are tight */
128 /* Switch input bank so that we have a stable set of the latest data */
129 if(ADCBuffers
== &ADCBuffers1
){
130 ticksPerDegree
= &ticksPerDegree0
; // TODO temp, remove, maybe
131 ticksPerDegreeRecord
= &ticksPerDegree1
; // TODO temp, remove, maybe
132 ADCBuffers
= &ADCBuffers0
;
133 ADCBuffersRecord
= &ADCBuffers1
;
135 ticksPerDegree
= &ticksPerDegree1
; // TODO temp, remove, maybe
136 ticksPerDegreeRecord
= &ticksPerDegree0
; // TODO temp, remove, maybe
137 ADCBuffers
= &ADCBuffers1
;
138 ADCBuffersRecord
= &ADCBuffers0
;
141 /* Clear the calc required flag */
142 coreStatusA
&= CLEAR_CALC_FUEL_IGN
;
144 ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
146 // TODO DEBUG/TUNING MACRO HERE!
147 /* Keep track of how many calcs we are managing per second... */
148 Counters
.calculationsPerformed
++;
150 /* Generate the core variables from sensor input and recorded tooth timings */
152 // TODO DEBUG/TUNING MACRO HERE!
154 /* Generate the derived variables from the core variables based on settings */
155 generateDerivedVars();
156 // TODO DEBUG/TUNING MACRO HERE!
158 /* Perform the calculations TODO possibly move this to the software interrupt if it makes sense to do so */
159 calculateFuelAndIgnition();
160 // TODO DEBUG/TUNING MACRO HERE!
162 /* Calculate the scheduling based on configuration and previously calculated variables */
164 // TODO DEBUG/TUNING MACRO HERE!
166 /* In the event that no calcs are required, sleep a little before returning to retry. */
167 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
168 /* Using 0.8 ticks as micros so it will run for a little longer than the math did */
172 if(!(TXBufferInUseFlags
)){
173 /* If the flag for com packet processing is set and the TX buffer is available process the data! */
174 if(RXStateFlags
& RX_READY_TO_PROCESS
){
176 RXStateFlags
&= RX_CLEAR_READY_TO_PROCESS
;
178 /* Handle the incoming packet */
179 decodePacketAndRespond();
180 }else{// if(lastCalcCount != Counters.calculationsPerformed){ // substitute true for full speed continuous stream test...
182 /* send asynchronous data log if required */
183 switch (TablesB
.SmallTablesB
.loggingSettings
.datalogStreamType
) {
184 case asyncDatalogOff
:
188 case asyncDatalogBasic
:
190 /* Flag that we are transmitting! */
191 TXBufferInUseFlags
|= COM_SET_SCI0_INTERFACE_ID
;
192 // SCI0 only for now...
194 // headers including length... *length = configuredBasicDatalogLength;
195 TXBufferCurrentPositionHandler
= (unsigned char*)&TXBuffer
;
197 /* Initialised here such that override is possible */
198 TXBufferCurrentPositionSCI0
= (unsigned char*)&TXBuffer
;
199 TXBufferCurrentPositionCAN0
= (unsigned char*)&TXBuffer
;
201 /* Set the flags : firmware, no ack, no addrs, has length */
202 *TXBufferCurrentPositionHandler
= HEADER_HAS_LENGTH
;
203 TXBufferCurrentPositionHandler
++;
205 /* Set the payload ID */
206 *((unsigned short*)TXBufferCurrentPositionHandler
) = responseBasicDatalog
;
207 TXBufferCurrentPositionHandler
+= 2;
210 unsigned short* localLength
= (unsigned short*)TXBufferCurrentPositionHandler
;
211 TXBufferCurrentPositionHandler
+= 2;
213 /* populate data log */
214 *localLength
= populateBasicDatalog();
218 case asyncDatalogScratchPad
:
222 case asyncDatalogStructs
:
226 case asyncDatalogPosition
:
230 case asyncDatalogBlockBytes
:
234 case asyncDatalogBlockWords
:
238 case asyncDatalogBlockLongs
:
242 case asyncDatalogStreamByte
:
244 /* Flag that we are transmitting! */
245 TXBufferInUseFlags
|= COM_SET_SCI0_INTERFACE_ID
;
246 // SCI0 only for now...
248 // headers including length... *length = configuredBasicDatalogLength;
249 TXBufferCurrentPositionHandler
= (unsigned char*)&TXBuffer
;
251 /* Initialised here such that override is possible */
252 TXBufferCurrentPositionSCI0
= (unsigned char*)&TXBuffer
;
253 TXBufferCurrentPositionCAN0
= (unsigned char*)&TXBuffer
;
255 /* Set the flags all zeros */
256 *TXBufferCurrentPositionHandler
= 0;
257 TXBufferCurrentPositionHandler
++;
259 /* Set the payload ID */
260 *((unsigned short*)TXBufferCurrentPositionHandler
) = responseByteLADatalog
;
261 TXBufferCurrentPositionHandler
+= 2;
263 /** Store PTIT for now, later make address of byte configurable TODO @todo */
264 *((unsigned char*)TXBufferCurrentPositionHandler
) = PTIT
;
265 TXBufferCurrentPositionHandler
++;
270 case asyncDatalogStreamWord
:
274 case asyncDatalogStreamLong
:
279 // mechanism to ensure we only send something if the data has been updated
280 lastCalcCount
= Counters
.calculationsPerformed
;
286 // PWM experimentation