FreeEMS  0.2.0-SNAPSHOT-285-g028e24c
main.c
Go to the documentation of this file.
1 /* FreeEMS - the open source engine management system
2  *
3  * Copyright 2008-2012 Fred Cooke
4  *
5  * This file is part of the FreeEMS project.
6  *
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.
11  *
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.
16  *
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/
19  *
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!
22  *
23  * Thank you for choosing FreeEMS to run your engine!
24  */
25 
26 
27 /** @file
28  *
29  * @brief The main function!
30  *
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.
37  */
38 
39 
40 #include "inc/main.h"
41 
42 
43 /** @brief The main function!
44  *
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.
56  */
57 int main(){ /// @todo TODO maybe move this to paged flash ?
58  // Set everything up.
59  init();
60 
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.
62 
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++){
69  activeFuelChannels[fixedConfigs1.schedulingSettings.outputEventPinNumbers[outputEvent]] = outputEvent;
70  }
71  }
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;
85  }else{
86  timeStamp.timeShorts[0] = timerExtensionClock;
87  }
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);
94  }
95  }
96 
97  // Run forever repeating.
98  while(TRUE){
99  //unsigned short start = realTimeClockMillis;
100  /* If ADCs require forced sampling, sample now */
102  ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
103  /* Atomic block to ensure a full set of readings are taken together */
104 
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 ?
107 
108  sampleEachADC(ADCBuffersRecord); // TODO still need to do a pair of loops and clock these two functions for performance.
109  //sampleLoopADC(&ADCBuffers);
112 
113  /* Set flag to say calc required */
115 
116  /* Clear force reading flag */
118  }
119 
120  ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
121  }
122 
123  /* If required, do main fuel and ignition calcs first */
125  ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
126  /* Atomic block to ensure that we don't clear the flag for the next data set when things are tight */
127 
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
134  }else{
135  ticksPerDegree = &ticksPerDegree1; // TODO temp, remove, maybe
136  ticksPerDegreeRecord = &ticksPerDegree0; // TODO temp, remove, maybe
139  }
140 
141  /* Clear the calc required flag */
143 
144  ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
145 
146  // TODO DEBUG/TUNING MACRO HERE!
147  /* Keep track of how many calcs we are managing per second... */
149 
150  /* Generate the core variables from sensor input and recorded tooth timings */
152  // TODO DEBUG/TUNING MACRO HERE!
153 
154  /* Generate the derived variables from the core variables based on settings */
156  // TODO DEBUG/TUNING MACRO HERE!
157 
158  /* Perform the calculations TODO possibly move this to the software interrupt if it makes sense to do so */
160  // TODO DEBUG/TUNING MACRO HERE!
161 
162  /* Calculate the scheduling based on configuration and previously calculated variables */
163  scheduleOutputs();
164  // TODO DEBUG/TUNING MACRO HERE!
165  }else{
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 */
169  }
170 
171 
172  if(!(TXBufferInUseFlags)){
173  /* If the flag for com packet processing is set and the TX buffer is available process the data! */
175  /* Clear the flag */
177 
178  /* Handle the incoming packet */
180  }else{// if(lastCalcCount != Counters.calculationsPerformed){ // substitute true for full speed continuous stream test...
181 
182  /* send asynchronous data log if required */
183  switch (TablesB.SmallTablesB.loggingSettings.datalogStreamType) {
184  case asyncDatalogOff:
185  {
186  break;
187  }
188  case asyncDatalogBasic:
189  {
190  /* Flag that we are transmitting! */
192  // SCI0 only for now...
193 
194  // headers including length... *length = configuredBasicDatalogLength;
195  TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
196 
197  /* Initialised here such that override is possible */
198  TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
199  TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;
200 
201  /* Set the flags : firmware, no ack, no addrs, has length */
204 
205  /* Set the payload ID */
208 
209  /* Set the length */
210  unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
212 
213  /* populate data log */
214  *localLength = populateBasicDatalog();
215  finaliseAndSend(0);
216  break;
217  }
219  {
220  break;
221  }
222  case asyncDatalogStructs:
223  {
224  break;
225  }
227  {
228  break;
229  }
231  {
232  break;
233  }
235  {
236  break;
237  }
239  {
240  break;
241  }
243  {
244  /* Flag that we are transmitting! */
246  // SCI0 only for now...
247 
248  // headers including length... *length = configuredBasicDatalogLength;
249  TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
250 
251  /* Initialised here such that override is possible */
252  TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
253  TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;
254 
255  /* Set the flags all zeros */
258 
259  /* Set the payload ID */
262 
263  /** Store PTIT for now, later make address of byte configurable TODO @todo */
264  *((unsigned char*)TXBufferCurrentPositionHandler) = PTIT;
266 
267  finaliseAndSend(0);
268  break;
269  }
271  {
272  break;
273  }
275  {
276  break;
277  }
278  }
279  // mechanism to ensure we only send something if the data has been updated
281  }
282  }
283 
285 
286  // PWM experimentation
287  adjustPWM();
288  }
289 }