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 * @ingroup communicationsFiles
31 * @brief Core communications functions.
33 * This file contains most of the core comms functionality. Currently that is
34 * only for UART serial style communication. It is already too big and needs
35 * to be split up somewhat. This will happen fairly soon during the serial
36 * refactoring and protocol fine tuning.
38 * @todo TODO function to setup a packet and send it fn(populateBodyFunctionPointer(), header, other, fields, here, and, use, or, not, within){}
39 * @todo TODO factor many things into functions and move the receive delegator to its own file
46 #include "inc/freeEMS.h"
47 #include "inc/flashWrite.h"
48 #include "inc/interrupts.h"
49 #include "inc/utils.h"
50 #include "inc/tableLookup.h"
51 #include "inc/locationIDs.h"
52 #include "inc/blockDetailsLookup.h"
53 #include "inc/decoderInterface.h"
54 #include "inc/commsCore.h"
56 #include <string.h> /// @todo TODO this is pulling in the system string.h not the m68hc1x version, and functions other than memcpy do not work because they are not in crt1.o or other included-by-default libs
57 #include <datalogPopulator.c>
58 #include "decoders/inc/BenchTest.h"
61 /** @brief Populate a basic datalog packet
63 * Copies various chunks of data to the transmission buffer and truncates to
64 * the configured length. If changing this, update the maxBasicDatalogLength.
68 * @warning This function is only a skeleton at this time.
70 void populateBasicDatalog(){
71 /* Save the current position */
72 unsigned char* position
= TXBufferCurrentPositionHandler
;
74 /// @todo TODO setup proper sequence and clock with some sort of differential measurement log to log. insert in front of actual data because these are part of the log itself.
76 // By default, default values are populated, but if you drop code into the custom directory, that replaces the defaults.
77 populateCustomDatalog();
78 // Done here to overwrite cheeky custom users data:
79 KeyUserDebugs
.coreStatusA
= coreStatusA
;
80 KeyUserDebugs
.tempClock
++;
81 KeyUserDebugs
.clockIn8thsOfAMilli
= Clocks
.realTimeClockMain
;
82 KeyUserDebugs
.clockInMilliSeconds
= Clocks
.realTimeClockMillis
;
85 memcpy(TXBufferCurrentPositionHandler
, CoreVars
, sizeof(CoreVar
));
86 TXBufferCurrentPositionHandler
+= sizeof(CoreVar
);
87 /* Get derived vars */
88 memcpy(TXBufferCurrentPositionHandler
, DerivedVars
, sizeof(DerivedVar
));
89 TXBufferCurrentPositionHandler
+= sizeof(DerivedVar
);
90 /* Get raw adc counts */
91 memcpy(TXBufferCurrentPositionHandler
, &KeyUserDebugs
, sizeof(KeyUserDebug
));
92 TXBufferCurrentPositionHandler
+= sizeof(KeyUserDebug
);
94 /* Set/Truncate the log to the specified length */
95 TXBufferCurrentPositionHandler
= position
+ TablesB
.SmallTablesB
.loggingSettings
.basicDatalogLength
;
99 // All of these require some range checking, eg only some registers, and all RAM, not flash, not other regs
100 // TODO pointer for one byte
101 // TODO pointer for one short
102 // TODO function to log generic memory region by location and size ? requires length!
106 // IO registers etc that can't be altered simply by reading from.
108 // flash makes no sense
109 // some regs are sensitive
110 // some RAM is unused
111 // serial buffers make no sense
112 // eeprom makes no sense
114 // 2k of regs max - user beware for now
118 //logaddr = fixed.addr
121 //len = loglen OR 1 OR 2
124 //if((addr < 0x0800) && (length < (0x0800 - addr))){
125 // // reg space is OK
126 //}else if(((0x1000 < addr) && (addr < 0x4000)) && (length < (0x4000 - addr))){
127 // // RAM space is OK
129 // // send an error instead
132 //run check at init and set time, not run time or just not check?? maybe its silly to check at all
134 // /* Just dump the ADC channels as fast as possible */
135 //void populateScopeLogADCAll(){
136 // sampleBlockADC(TXBufferCurrentPositionHandler);
137 // TXBufferCurrentPositionHandler += sizeof(ADCBuffer);
141 // what does this mean >> ??? TODO Look at the time stamps and where to write them, also whether to function call these simple blocks or write one function that handles all the logic.
144 /** @brief Finalise a packet and send it
146 * This functions job is to finalise the main loop part of the packet sending
147 * process. It configures the pos/neg ack header bit, adds the code if neg,
148 * runs a checksum over the packet data and tags it to the end before
149 * configuring the various ISRs that need to send the data out.
153 * @bug http://issues.freeems.org/view.php?id=81
154 * @todo TODO fix the double/none start byte bug and remove the hack!
156 void finaliseAndSend(unsigned short errorID
){
159 *TXHeaderFlags
|= HEADER_IS_NACK
;
160 *((unsigned short*)TXBufferCurrentPositionHandler
) = errorID
;
161 TXBufferCurrentPositionHandler
+= 2;
164 /* Tag the checksum on the end */
165 *TXBufferCurrentPositionHandler
= checksum((unsigned char*)&TXBuffer
, ((unsigned short)TXBufferCurrentPositionHandler
- (unsigned short)&TXBuffer
));
167 /* Send it out on all the channels required. */
169 /* SCI0 - Main serial interface */
170 if(TXBufferInUseFlags
& COM_SET_SCI0_INTERFACE_ID
){
171 /* Initiate transmission */
172 SCI0DRL
= START_BYTE
;
174 /* Note : Order Is Important! */
175 /* TX empty flag is already set, so we must clear it by writing out before enabling the interrupt */
176 SCI0CR2
|= (SCICR2_TX_ENABLE
| SCICR2_TX_ISR_ENABLE
);
178 /* CAN0 - Main CAN interface */
179 if(TXBufferInUseFlags
& COM_SET_CAN0_INTERFACE_ID
){
180 // just clear up front for now
181 TXBufferInUseFlags
&= COM_CLEAR_CAN0_INTERFACE_ID
;
184 if(TXBufferInUseFlags
& COM_SET_SPARE2_INTERFACE_ID
){
185 // just clear up front for now
186 TXBufferInUseFlags
&= COM_CLEAR_SPARE2_INTERFACE_ID
;
189 if(TXBufferInUseFlags
& COM_SET_SPARE3_INTERFACE_ID
){
190 // just clear up front for now
191 TXBufferInUseFlags
&= COM_CLEAR_SPARE3_INTERFACE_ID
;
194 if(TXBufferInUseFlags
& COM_SET_SPARE4_INTERFACE_ID
){
195 // just clear up front for now
196 TXBufferInUseFlags
&= COM_CLEAR_SPARE4_INTERFACE_ID
;
199 if(TXBufferInUseFlags
& COM_SET_SPARE5_INTERFACE_ID
){
200 // just clear up front for now
201 TXBufferInUseFlags
&= COM_CLEAR_SPARE5_INTERFACE_ID
;
204 if(TXBufferInUseFlags
& COM_SET_SPARE6_INTERFACE_ID
){
205 // just clear up front for now
206 TXBufferInUseFlags
&= COM_CLEAR_SPARE6_INTERFACE_ID
;
209 if(TXBufferInUseFlags
& COM_SET_SPARE7_INTERFACE_ID
){
210 // just clear up front for now
211 TXBufferInUseFlags
&= COM_CLEAR_SPARE7_INTERFACE_ID
;
216 /** @brief Decode a packet and respond
218 * This is the core function that controls which functionality is run when a
219 * packet is received in full by the ISR code and control is passed back to the
220 * main loop code. The vast majority of communications action happens here.
224 void decodePacketAndRespond(){
225 /* Extract and build up the header fields */
226 TXBufferCurrentPositionHandler
= (unsigned char*)&TXBuffer
;
228 /* Initialised here such that override is possible */
229 TXBufferCurrentPositionSCI0
= (unsigned char*)&TXBuffer
;
230 TXBufferCurrentPositionCAN0
= (unsigned char*)&TXBuffer
;
232 // How big was the packet that we got back
233 unsigned short RXPacketLengthReceived
= (unsigned short)RXBufferCurrentPosition
- (unsigned short)&RXBuffer
;
235 /* Check that the packet is big enough for header,ID,checksum */
236 if(RXPacketLengthReceived
< 4){
237 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
238 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_UNDER_LENGTH_OFFSET
);
239 KeyUserDebugs
.serialAndCommsCodeErrors
++;
243 /* Pull out the received checksum and calculate the real one, then check */
244 unsigned char RXReceivedChecksum
= (unsigned char)*(RXBufferCurrentPosition
- 1);
245 unsigned char RXCalculatedChecksum
= checksum((unsigned char*)&RXBuffer
, RXPacketLengthReceived
- 1);
246 if(RXCalculatedChecksum
!= RXReceivedChecksum
){
247 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
248 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_CHECKSUM_MISMATCHES_OFFSET
);
249 KeyUserDebugs
.serialAndCommsCodeErrors
++;
253 /* Start this off as full packet length and build down to the actual length */
254 RXCalculatedPayloadLength
= RXPacketLengthReceived
;
256 /* Grab the RX header flags out of the RX buffer */
257 RXBufferCurrentPosition
= (unsigned char*)&RXBuffer
;
258 RXHeaderFlags
= *RXBufferCurrentPosition
;
259 RXBufferCurrentPosition
++;
260 RXCalculatedPayloadLength
--;
262 /* Flag that we are transmitting! */
263 TXBufferInUseFlags
|= COM_SET_SCI0_INTERFACE_ID
;
264 // SCI0 only for now...
266 /* Load a blank header into the TX buffer ready for masking */
267 TXHeaderFlags
= TXBufferCurrentPositionHandler
;
269 TXBufferCurrentPositionHandler
++;
271 /* Grab the payload ID for processing and load the return ID */
272 RXHeaderPayloadID
= *((unsigned short*)RXBufferCurrentPosition
);
273 *((unsigned short*)TXBufferCurrentPositionHandler
) = RXHeaderPayloadID
+ 1;
274 RXBufferCurrentPosition
+= 2;
275 TXBufferCurrentPositionHandler
+= 2;
276 RXCalculatedPayloadLength
-= 2;
278 /* Check that the length is sufficient for the fields configured. Packets
279 * that are too long will be caught and rejected on an individual payload
280 * ID basis as the information required to handle that is not available at
281 * this point. Packets that are too short are rejected immediately!
283 if(((RXHeaderFlags
& HEADER_HAS_LENGTH
) && (RXHeaderFlags
& HEADER_HAS_SEQUENCE
) && (RXPacketLengthReceived
< 7))
284 || ((RXHeaderFlags
& HEADER_HAS_LENGTH
) && (RXPacketLengthReceived
< 6))
285 || ((RXHeaderFlags
& HEADER_HAS_SEQUENCE
) && (RXPacketLengthReceived
< 5))){
286 finaliseAndSend(packetTooShortForSpecifiedFields
);
287 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
291 /* Subtract checksum to get final length */
292 RXCalculatedPayloadLength
--;
294 if(RXHeaderFlags
& HEADER_HAS_SEQUENCE
){
295 *TXBufferCurrentPositionHandler
= *RXBufferCurrentPosition
;
296 RXBufferCurrentPosition
++;
297 TXBufferCurrentPositionHandler
++;
298 RXCalculatedPayloadLength
--;
299 *TXHeaderFlags
|= HEADER_HAS_SEQUENCE
;
302 if(RXHeaderFlags
& HEADER_HAS_LENGTH
){
303 RXHeaderPayloadLength
= *((unsigned short*)RXBufferCurrentPosition
);
304 RXBufferCurrentPosition
+= 2;
305 RXCalculatedPayloadLength
-= 2;
306 /* Already subtracted one for checksum */
307 if(RXHeaderPayloadLength
!= RXCalculatedPayloadLength
){
308 finaliseAndSend(payloadLengthHeaderMismatch
);
309 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
314 /* Calculate the position of the end of the stored packet for later use as a buffer */
315 void* leftOverBuffer
= (void*)((unsigned short)&RXBuffer
+ RXPacketLengthReceived
);
317 unsigned short errorID
= 0;
318 /* This is where all the communication logic resides.
320 * Please Note: Length and it's flag should be set by each return packet
321 * type handler if required or desired. If an ack has been requested,
322 * ensure the negative ack flag is set if the operation failed.
324 switch (RXHeaderPayloadID
){
325 // FreeEMS Core Comms Interface cases
326 case requestInterfaceVersion
:
328 if(RXCalculatedPayloadLength
!= 0){
329 errorID
= payloadLengthTypeMismatch
;
333 /* This type must have a length field, set that up */
334 *((unsigned short*)TXBufferCurrentPositionHandler
) = sizeof(interfaceVersion
);
335 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
336 TXBufferCurrentPositionHandler
+= 2;
337 /* Load the body into place */
338 memcpy((void*)TXBufferCurrentPositionHandler
, (void*)&interfaceVersion
, sizeof(interfaceVersion
));
339 TXBufferCurrentPositionHandler
+= sizeof(interfaceVersion
);
342 case requestFirmwareVersion
:
344 if(RXCalculatedPayloadLength
!= 0){
345 errorID
= payloadLengthTypeMismatch
;
348 /* This type must have a length field, set that up */
349 *((unsigned short*)TXBufferCurrentPositionHandler
) = sizeof(firmwareVersion
);
350 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
351 TXBufferCurrentPositionHandler
+= 2;
352 /* Load the body into place */
353 memcpy((void*)TXBufferCurrentPositionHandler
, (void*)&firmwareVersion
, sizeof(firmwareVersion
));
354 TXBufferCurrentPositionHandler
+= sizeof(firmwareVersion
);
357 case requestMaxPacketSize
:
359 if(RXCalculatedPayloadLength
!= 0){
360 errorID
= payloadLengthTypeMismatch
;
363 /* Load the size into place */
364 *((unsigned short*)TXBufferCurrentPositionHandler
) = RX_BUFFER_SIZE
;
365 TXBufferCurrentPositionHandler
+= 2;
368 case requestEchoPacketReturn
:
370 /* This type must have a length field, set that up */
371 *((unsigned short*)TXBufferCurrentPositionHandler
) = RXPacketLengthReceived
;
372 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
373 TXBufferCurrentPositionHandler
+= 2;
374 /* Load the body into place */
375 memcpy((void*)TXBufferCurrentPositionHandler
, (void*)&RXBuffer
, RXPacketLengthReceived
);
376 /* Note, there is no overflow check here because the TX buffer is slightly */
377 /* bigger than the RX buffer and there is overflow checking for receives anyway. */
378 TXBufferCurrentPositionHandler
+= RXPacketLengthReceived
;
381 case requestSoftSystemReset
:
383 if(RXCalculatedPayloadLength
!= 0){
384 errorID
= payloadLengthTypeMismatch
;
385 }else{ // Perform soft system reset
390 case requestHardSystemReset
:
392 if(RXCalculatedPayloadLength
!= 0){
393 errorID
= payloadLengthTypeMismatch
;
395 /* This is how the serial monitor does it. */
396 COPCTL
= 0x01; /* Arm with shortest time */
397 ARMCOP
= 0xFF; /* Write bad value, should cause immediate reset */
398 /* Using _start() only resets the app ignoring the monitor switch. It does not work */
399 /* properly because the location of _start is not the master reset vector location. */
403 case requestReInitOfSystem
:
405 if(RXCalculatedPayloadLength
!= 0){
406 errorID
= payloadLengthTypeMismatch
;
412 // FreeEMS Vanilla Firmware Specific cases
413 case clearCountersAndFlagsToZero
:
415 if(RXCalculatedPayloadLength
!= 0){
416 errorID
= payloadLengthTypeMismatch
;
420 unsigned short zeroCounter
;
421 unsigned char* counterPointer
= (char*) &Counters
;
422 for(zeroCounter
= 0;zeroCounter
< sizeof(Counter
);zeroCounter
++){
426 KeyUserDebugs
.flaggableFlags
= 0;
427 unsigned char* flaggablePointer
= (char*) &Flaggables
;
428 for(zeroCounter
= 0;zeroCounter
< sizeof(Flaggable
);zeroCounter
++){
429 *flaggablePointer
= 0;
434 case requestDecoderName
:
435 case requestFirmwareBuildDate
:
436 case requestCompilerVersion
:
437 case requestOperatingSystem
:
439 if(RXCalculatedPayloadLength
!= 0){
440 errorID
= payloadLengthTypeMismatch
;
444 unsigned char* stringToSend
= 0;
445 switch (RXHeaderPayloadID
) {
446 case requestDecoderName
:
447 stringToSend
= (unsigned char*)decoderName
;
449 case requestFirmwareBuildDate
:
450 stringToSend
= (unsigned char*)buildTimeAndDate
;
452 case requestCompilerVersion
:
453 stringToSend
= (unsigned char*)compilerVersion
;
455 case requestOperatingSystem
:
456 stringToSend
= (unsigned char*)operatingSystem
;
459 /* This type must have a length field, set that up and load the body into place at the same time */
460 *((unsigned short*)TXBufferCurrentPositionHandler
) = stringCopy((TXBufferCurrentPositionHandler
+ 2), stringToSend
);
461 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
462 // Update with length field and string length.
463 TXBufferCurrentPositionHandler
+= 2 + *((unsigned short*)TXBufferCurrentPositionHandler
);
466 case updateBlockInRAM
:
468 // Subtract six to allow for the locationID, size, offset
469 if(RXCalculatedPayloadLength
< 7){
470 errorID
= payloadLengthTypeMismatch
;
474 // Extract the RAM location ID
475 unsigned short locationID
= *((unsigned short*)RXBufferCurrentPosition
);
476 RXBufferCurrentPosition
+= 2;
478 // Extract the offset to place the data at
479 unsigned short offset
= *((unsigned short*)RXBufferCurrentPosition
);
480 RXBufferCurrentPosition
+= 2;
482 // Extract the size of the data to be stored
483 unsigned short size
= *((unsigned short*)RXBufferCurrentPosition
);
484 RXBufferCurrentPosition
+= 2;
486 // Look up the memory location details
487 blockDetails details
;
488 lookupBlockDetails(locationID
, &details
);
490 // Don't let anyone write to running variables unless we are running BenchTest firmware!
491 if((details
.flags
& block_is_read_only
) && compare((char*)&decoderName
, BENCH_TEST_NAME
, sizeof(BENCH_TEST_NAME
))){
492 errorID
= attemptToWriteToReadOnlyBlock
;
496 // Subtract six to allow for the locationID, size, offset
497 if((RXCalculatedPayloadLength
- 6) != size
){
498 errorID
= payloadNotEqualToSpecifiedValue
;
502 // If either of these is zero then this block is not in RAM!
503 if((details
.RAMPage
== 0) || (details
.RAMAddress
== 0)){
504 errorID
= invalidMemoryActionForID
;
508 // Check that size and offset describe a region that is not out of bounds
509 if((size
== 0) || (offset
> (details
.size
- 1)) || (size
> (details
.size
- offset
))){
510 errorID
= invalidSizeOffsetCombination
;
514 // Don't allow sub region manipulation where it does not make sense or is unsafe.
515 if((size
!= details
.size
) && !(details
.flags
& block_is_indexable
)){
516 errorID
= uncheckedTableManipulationNotAllowed
;
520 // Save page values for restore
521 unsigned char oldRamPage
= RPAGE
;
522 // Set the viewable RAM page
523 RPAGE
= details
.RAMPage
;
525 /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
527 if((details
.flags
& block_is_main_table
) || (details
.flags
& block_is_2dus_table
)){
530 // For sub regions, construct an image for verification
531 if(size
!= details
.size
){
532 // Copy data from destination location to buffer
533 memcpy(leftOverBuffer
, details
.RAMAddress
, details
.size
);
535 // Copy data from rx buffer to buffer over writing old data
536 memcpy(leftOverBuffer
+ offset
, RXBufferCurrentPosition
, size
);
538 bufferToCheck
= leftOverBuffer
;
540 bufferToCheck
= RXBufferCurrentPosition
;
544 if(details
.flags
& block_is_main_table
){
545 errorID
= validateMainTable((mainTable
*)bufferToCheck
);
546 }else if(details
.flags
& block_is_2dus_table
){
547 errorID
= validateTwoDTable((twoDTableUS
*)bufferToCheck
);
548 }// TODO add other table types here
550 // If the validation failed, report it
552 RPAGE
= oldRamPage
; // Restore the original RAM page, even when getting an error condition.
557 // Copy from the RX buffer to the block of RAM
558 memcpy((unsigned char*)(details
.RAMAddress
+ offset
), RXBufferCurrentPosition
, size
);
560 // Check that the write was successful
561 unsigned char index
= compare(RXBufferCurrentPosition
, (unsigned char*)(details
.RAMAddress
+ offset
), size
);
563 // Restore the original RAM and flash pages
567 errorID
= MEMORY_WRITE_ERROR
;
571 case updateBlockInFlash
:
573 // Subtract six to allow for the locationID, size, offset
574 if(RXCalculatedPayloadLength
< 7){
575 errorID
= payloadLengthTypeMismatch
;
579 // Extract the RAM location ID
580 unsigned short locationID
= *((unsigned short*)RXBufferCurrentPosition
);
581 RXBufferCurrentPosition
+= 2;
583 // Extract the offset to place the data at
584 unsigned short offset
= *((unsigned short*)RXBufferCurrentPosition
);
585 RXBufferCurrentPosition
+= 2;
587 // Extract the size of the data to be stored
588 unsigned short size
= *((unsigned short*)RXBufferCurrentPosition
);
589 RXBufferCurrentPosition
+= 2;
591 // Look up the memory location details
592 blockDetails details
;
593 lookupBlockDetails(locationID
, &details
);
595 // Subtract six to allow for the locationID, size, offset
596 if((RXCalculatedPayloadLength
- 6) != size
){
597 errorID
= payloadNotEqualToSpecifiedValue
;
601 // If either of these is zero then this block is not in flash!
602 if((details
.FlashPage
== 0) || (details
.FlashAddress
== 0)){
603 errorID
= invalidMemoryActionForID
;
607 // Check that size and offset describe a region that is not out of bounds
608 if((size
== 0) || (offset
> (details
.size
- 1)) || (size
> (details
.size
- offset
))){
609 errorID
= invalidSizeOffsetCombination
;
613 // Don't allow sub region manipulation where it does not make sense or is unsafe.
614 if((size
!= details
.size
) && !(details
.flags
& block_is_indexable
)){
615 errorID
= uncheckedTableManipulationNotAllowed
;
619 /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
621 if((details
.flags
& block_is_main_table
) || (details
.flags
& block_is_2dus_table
)){
624 // For sub regions, construct an image for verification
625 if(size
!= details
.size
){
626 /* Save page value for restore and set the visible page */
627 unsigned char oldFlashPage
= PPAGE
;
628 PPAGE
= details
.FlashPage
;
630 // Copy data from destination location to buffer
631 memcpy(leftOverBuffer
, details
.FlashAddress
, details
.size
);
633 /* Restore the original flash page */
634 PPAGE
= oldFlashPage
;
636 // Copy data from rx buffer to buffer over writing old data
637 memcpy(leftOverBuffer
+ offset
, RXBufferCurrentPosition
, size
);
639 bufferToCheck
= leftOverBuffer
;
641 bufferToCheck
= RXBufferCurrentPosition
;
645 if(details
.flags
& block_is_main_table
){
646 errorID
= validateMainTable((mainTable
*)bufferToCheck
);
647 }else if(details
.flags
& block_is_2dus_table
){
648 errorID
= validateTwoDTable((twoDTableUS
*)bufferToCheck
);
649 }// TODO add other table types here
651 // If the validation failed, report it
657 /* Copy the flash details and populate the RAM details with the buffer location */
658 blockDetails burnDetails
;
659 burnDetails
.FlashPage
= details
.FlashPage
;
660 burnDetails
.FlashAddress
= details
.FlashAddress
+ offset
;
661 burnDetails
.RAMPage
= RPAGE
;
662 burnDetails
.RAMAddress
= RXBufferCurrentPosition
;
663 burnDetails
.size
= size
;
665 /* Copy from the RX buffer to the block of flash */
666 errorID
= writeBlock(&burnDetails
, leftOverBuffer
);
671 /* If present in RAM, update that too */
672 if((details
.RAMPage
!= 0) && (details
.RAMAddress
!= 0)){
673 /* Save page values for restore */
674 unsigned char oldRamPage
= RPAGE
;
675 /* Set the viewable RAM page */
676 RPAGE
= details
.RAMPage
;
678 /* Copy from the RX buffer to the block of RAM */
679 memcpy((unsigned char*)(details
.RAMAddress
+ offset
), RXBufferCurrentPosition
, size
);
681 /* Check that the write was successful */
682 unsigned char index
= compare(RXBufferCurrentPosition
, (unsigned char*)(details
.RAMAddress
+ offset
), size
);
684 /* Restore the original RAM and flash pages */
688 errorID
= MEMORY_WRITE_ERROR
;
694 case retrieveBlockFromRAM
:
696 if(RXCalculatedPayloadLength
!= 6){
697 errorID
= payloadLengthTypeMismatch
;
701 // Extract the RAM location ID
702 unsigned short locationID
= *((unsigned short*)RXBufferCurrentPosition
);
703 RXBufferCurrentPosition
+= 2;
705 // Extract the offset to place the data at
706 unsigned short offset
= *((unsigned short*)RXBufferCurrentPosition
);
707 RXBufferCurrentPosition
+= 2;
709 // Extract the size of the data to be stored
710 unsigned short size
= *((unsigned short*)RXBufferCurrentPosition
);
711 RXBufferCurrentPosition
+= 2;
713 /* Look up the memory location details */
714 blockDetails details
;
715 lookupBlockDetails(locationID
, &details
);
717 if((details
.RAMPage
== 0) || (details
.RAMAddress
== 0)){
718 errorID
= invalidMemoryActionForID
;
722 // Special behaviour for size of zero which returns the whole block
723 if((size
== 0) && (offset
== 0)){
727 // Check that size and offset describe a region that is not out of bounds
728 if((size
== 0) || (offset
> (details
.size
- 1)) || (size
> (details
.size
- offset
))){
729 errorID
= invalidSizeOffsetCombination
;
733 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
734 if((size
!= details
.size
) && !(details
.flags
& block_is_indexable
)){
735 errorID
= doesNotMakeSenseToRetrievePartially
;
739 // This type must have a length field, set that up
740 *((unsigned short*)TXBufferCurrentPositionHandler
) = size
;
741 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
742 TXBufferCurrentPositionHandler
+= 2;
744 /* Save page value for restore and set the visible page */
745 unsigned char oldRamPage
= RPAGE
;
746 RPAGE
= details
.RAMPage
;
748 /* Copy the block of RAM to the TX buffer */
749 memcpy(TXBufferCurrentPositionHandler
, (unsigned char*)(details
.RAMAddress
+ offset
), size
);
750 TXBufferCurrentPositionHandler
+= size
;
752 /* Restore the original RAM and flash pages */
757 case retrieveBlockFromFlash
:
759 if(RXCalculatedPayloadLength
!= 6){
760 errorID
= payloadLengthTypeMismatch
;
764 // Extract the RAM location ID
765 unsigned short locationID
= *((unsigned short*)RXBufferCurrentPosition
);
766 RXBufferCurrentPosition
+= 2;
768 // Extract the offset to place the data at
769 unsigned short offset
= *((unsigned short*)RXBufferCurrentPosition
);
770 RXBufferCurrentPosition
+= 2;
772 // Extract the size of the data to be stored
773 unsigned short size
= *((unsigned short*)RXBufferCurrentPosition
);
774 RXBufferCurrentPosition
+= 2;
776 /* Look up the memory location details */
777 blockDetails details
;
778 lookupBlockDetails(locationID
, &details
);
780 if((details
.FlashPage
== 0) || (details
.FlashAddress
== 0)){
781 errorID
= invalidMemoryActionForID
;
785 // Special behaviour for size of zero which returns the whole block
786 if((size
== 0) && (offset
== 0)){
790 // Check that size and offset describe a region that is not out of bounds
791 if((size
== 0) || (offset
> (details
.size
- 1)) || (size
> (details
.size
- offset
))){
792 errorID
= invalidSizeOffsetCombination
;
796 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
797 if((size
!= details
.size
) && !(details
.flags
& block_is_indexable
)){
798 errorID
= doesNotMakeSenseToRetrievePartially
;
802 // This type must have a length field, set that up
803 *((unsigned short*)TXBufferCurrentPositionHandler
) = size
;
804 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
805 TXBufferCurrentPositionHandler
+= 2;
807 /* Save page value for restore and set the visible page */
808 unsigned char oldFlashPage
= PPAGE
;
809 PPAGE
= details
.FlashPage
;
811 /* Copy the block of flash to the TX buffer */
812 memcpy(TXBufferCurrentPositionHandler
, (unsigned char*)(details
.FlashAddress
+ offset
), size
);
813 TXBufferCurrentPositionHandler
+= size
;
815 /* Restore the original RAM and flash pages */
816 PPAGE
= oldFlashPage
;
820 case burnBlockFromRamToFlash
:
822 if(RXCalculatedPayloadLength
!= 6){
823 errorID
= payloadLengthTypeMismatch
;
827 // Extract the RAM location ID
828 unsigned short locationID
= *((unsigned short*)RXBufferCurrentPosition
);
829 RXBufferCurrentPosition
+= 2;
831 // Extract the offset to place the data at
832 unsigned short offset
= *((unsigned short*)RXBufferCurrentPosition
);
833 RXBufferCurrentPosition
+= 2;
835 // Extract the size of the data to be stored
836 unsigned short size
= *((unsigned short*)RXBufferCurrentPosition
);
837 RXBufferCurrentPosition
+= 2;
839 /* Look up the memory location details */
840 blockDetails details
;
841 lookupBlockDetails(locationID
, &details
);
843 /* Check that all data we need is present */
844 if((details
.RAMPage
== 0) || (details
.RAMAddress
== 0) || (details
.FlashPage
== 0) || (details
.FlashAddress
== 0)){
845 errorID
= invalidMemoryActionForID
;
849 // Check that size and offset describe a region that is not out of bounds
850 if((size
== 0) || (offset
> (details
.size
- 1)) || (size
> (details
.size
- offset
))){
851 errorID
= invalidSizeOffsetCombination
;
855 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
856 if((size
!= details
.size
) && !(details
.flags
& block_is_indexable
)){
857 errorID
= doesNotMakeSenseToRetrievePartially
;
862 // adjust details block to feed to represent the subsection of ram and flash that we want to burn down.
863 details
.RAMAddress
+= offset
;
864 details
.FlashAddress
+= offset
;
867 /* Write the block down from RAM to Flash */
868 errorID
= writeBlock(&details
, leftOverBuffer
);
871 case requestDatalogPacket
: // Set type through standard configuration methods
873 if((RXCalculatedPayloadLength
> 2) || (RXCalculatedPayloadLength
== 1)){
874 errorID
= payloadLengthTypeMismatch
;
876 }else if(RXCalculatedPayloadLength
== 2){
877 unsigned short newConfiguredLength
= *((unsigned short*)RXBufferCurrentPosition
);
878 if(newConfiguredLength
> maxBasicDatalogLength
){
879 errorID
= datalogLengthExceedsMax
;
882 TablesB
.SmallTablesB
.loggingSettings
.basicDatalogLength
= newConfiguredLength
;
884 }// fall through to use existing configured length
886 /* Set the length field up */
887 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
888 *(unsigned short*)TXBufferCurrentPositionHandler
= TablesB
.SmallTablesB
.loggingSettings
.basicDatalogLength
;
889 TXBufferCurrentPositionHandler
+= 2;
891 /* Fill out the log and send */
892 populateBasicDatalog(); // TODO change this to pull type from settings and call generic populator which populates with passed in type
895 case setAsyncDatalogType
:
897 if(RXCalculatedPayloadLength
!= 1){
898 errorID
= payloadLengthTypeMismatch
;
902 unsigned char newDatalogType
= *((unsigned char*)RXBufferCurrentPosition
);
903 if(newDatalogType
> asyncDatalogLastType
){
904 errorID
= noSuchAsyncDatalogType
;
908 TablesB
.SmallTablesB
.loggingSettings
.datalogStreamType
= newDatalogType
;
911 case retrieveArbitraryMemory
:
913 if(RXCalculatedPayloadLength
!= 6){
914 errorID
= payloadLengthTypeMismatch
;
918 unsigned short length
= *((unsigned short*)RXBufferCurrentPosition
);
919 RXBufferCurrentPosition
+= 2;
920 // Make sure the buffer can handle the block
921 if(length
> TX_MAX_PAYLOAD_SIZE
){
922 errorID
= requestedLengthTooLarge
;
926 void* address
= (void*) *((unsigned short*)RXBufferCurrentPosition
);
927 RXBufferCurrentPosition
+= 2;
928 // Ensure we don't try to read past the end of the address space
929 if(((unsigned short)address
) <= ((0xFFFF - length
) + 1)){
930 // TODO Possibly check and limit ranges
931 errorID
= requestedAddressDisallowed
;
935 unsigned char RAMPage
= *((unsigned char*)RXBufferCurrentPosition
);
936 RXBufferCurrentPosition
++;
937 // Ensure RAM page is valid. Being too high is not possible.
938 if(RAMPage
< RPAGE_MIN
){
939 errorID
= requestedRAMPageInvalid
;
943 unsigned char FlashPage
= *((unsigned char*)RXBufferCurrentPosition
);
944 RXBufferCurrentPosition
++;
945 // Ensure Flash page is valid. Being too high is not possible.
946 if(FlashPage
< PPAGE_MIN
){
947 errorID
= requestedFlashPageInvalid
;
951 /* This type must have a length field, set that up */
952 *((unsigned short*)TXBufferCurrentPositionHandler
) = length
+ 6;
953 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
954 TXBufferCurrentPositionHandler
+= 2;
956 /* Put the request payload into the reply */
957 *((unsigned short*)TXBufferCurrentPositionHandler
) = (unsigned short) address
;
958 TXBufferCurrentPositionHandler
+= 2;
959 *((unsigned short*)TXBufferCurrentPositionHandler
) = length
;
960 TXBufferCurrentPositionHandler
+= 2;
961 *((unsigned char*)TXBufferCurrentPositionHandler
) = RAMPage
;
962 TXBufferCurrentPositionHandler
++;
963 *((unsigned char*)TXBufferCurrentPositionHandler
) = FlashPage
;
964 TXBufferCurrentPositionHandler
++;
966 /* Load the body into place */
967 memcpy((void*)TXBufferCurrentPositionHandler
, address
, length
);
968 TXBufferCurrentPositionHandler
+= length
;
972 case retrieveListOfLocationIDs
:
974 if(RXCalculatedPayloadLength
!= 3){
975 errorID
= payloadLengthTypeMismatch
;
979 // Extract the type of list that we want
980 unsigned char listType
= *((unsigned char*)RXBufferCurrentPosition
);
981 RXBufferCurrentPosition
++;
983 // Extract the mask for the qualities that we want
984 unsigned short blockDetailsMask
= *((unsigned short*)RXBufferCurrentPosition
);
985 RXBufferCurrentPosition
+= 2;
987 // This type must have a length field, set that up
988 unsigned short * listLength
= (unsigned short*)TXBufferCurrentPositionHandler
;
989 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
990 TXBufferCurrentPositionHandler
+= 2;
992 // Zero the counter before we start, woops!
995 unsigned long locationID
;
996 blockDetails details
;
997 for(locationID
= 0;locationID
< 65536;locationID
++){
998 unsigned short locationIDDoesntExist
;
999 locationIDDoesntExist
= lookupBlockDetails((unsigned short)locationID
, &details
);
1001 if(!locationIDDoesntExist
){
1002 if((listType
== 0x00) || // get all
1003 ((listType
== 0x01) && (details
.flags
& blockDetailsMask
)) || // get OR of bits
1004 ((listType
== 0x02) && (!(~(details
.flags
) & blockDetailsMask
)))){ // get AND of bits
1005 *((unsigned short*)TXBufferCurrentPositionHandler
) = (unsigned short)locationID
;
1006 TXBufferCurrentPositionHandler
+= 2;
1014 case retrieveLocationIDDetails
:
1016 if(RXCalculatedPayloadLength
!= 2){
1017 errorID
= payloadLengthTypeMismatch
;
1021 // Extract the RAM location ID
1022 unsigned short locationID
= *((unsigned short*)RXBufferCurrentPosition
);
1023 RXBufferCurrentPosition
+= 2;
1025 // This type must have a length field, set that up
1026 *((unsigned short*)TXBufferCurrentPositionHandler
) = sizeof(blockDetails
);
1027 *TXHeaderFlags
|= HEADER_HAS_LENGTH
;
1028 TXBufferCurrentPositionHandler
+= 2;
1030 // Write straight to output buffer to save time/code
1031 errorID
= lookupBlockDetails(locationID
, (blockDetails
*)TXBufferCurrentPositionHandler
);
1037 // Adjust TX buffer position if successful
1038 TXBufferCurrentPositionHandler
+= sizeof(blockDetails
);
1042 case requestUnitTestOverSerial
:
1045 * The idea here is to call this function with arguments, and data
1046 * and have the result sent back for comparison with an expected
1047 * result that isn't divulged to the firmware.
1049 * It is intended that all testable functions be callable through
1050 * this mechanism and that any number of test executions can be
1051 * performed by an external suite using different parameters and
1052 * data sets and matching expected results.
1054 * The usual error mechanism shall be used to indicate some sort of
1055 * either internal or test failure and returned errors shall be
1056 * suitably descriptive to allow diagnosis and fixing of issues.
1059 // Must at least have test ID
1060 if(RXCalculatedPayloadLength
< 2){
1061 errorID
= payloadLengthTypeMismatch
;
1065 // grab unit test ID from payload
1066 unsigned short unitTestID
= *((unsigned short*)RXBufferCurrentPosition
);
1067 RXBufferCurrentPosition
+= 2;
1072 // Must be only the ID
1073 if(RXCalculatedPayloadLength
!= 2){
1074 errorID
= payloadShorterThanRequiredForTest
;
1078 *((unsigned short*)TXBufferCurrentPositionHandler
) = unitTestID
;
1079 TXBufferCurrentPositionHandler
+=2;
1083 case testTwoDTableUSLookup
:
1085 // ID + Value + Table
1086 if(RXCalculatedPayloadLength
!= (2 + 2 + sizeof(twoDTableUS
))){
1087 errorID
= payloadShorterThanRequiredForTest
;
1091 unsigned short Value
= *((unsigned short*)RXBufferCurrentPosition
);
1092 RXBufferCurrentPosition
+= 2;
1094 twoDTableUS
* Table
= ((twoDTableUS
*)RXBufferCurrentPosition
);
1095 RXBufferCurrentPosition
+= sizeof(twoDTableUS
);
1097 unsigned short result
= lookupTwoDTableUS(Table
, Value
);
1099 *((unsigned short*)TXBufferCurrentPositionHandler
) = result
;
1100 TXBufferCurrentPositionHandler
+=2;
1104 // http://issues.freeems.org/view.php?id=156
1106 /// TODO @todo test all things listed below:
1107 // lookupPagedMainTableCellValue - pass this RPAGE so that it remains unchanged
1108 // validateMainTable
1109 // validateTwoDTable
1110 // set table values - leave this till last, currently unused by mtx, likely to be removed anyway
1111 // generateDerivedVars - convert to pointers, remove headers, privatise a lot of data!
1112 // calculateFuelAndIgnition - ditto
1113 // scheduling algorithm - ditto
1117 // sleep (milliseconds)
1118 // sleepMicro (microseconds)
1122 // utils that can't be checked: sampleLoopADC sampleBlockADC sampleEachADC - can check for how long each takes! adjustPWM (test only anyway), resetToNonRunningState and setupPagedRAM (would interfere with functioning of device)
1123 // init code may be able to be partially checked
1124 // most other code at this stage is ISR code, flash writing code, or could interfere with the running of the engine
1125 // more testable code will appear with time, such as the HAL layer, and most accessory functions.
1128 errorID
= noSuchUnitTestID
;
1132 // checks length, fails if wrong
1133 // parses data into args
1134 // calls function on data/args
1135 // assembles response OR sets error
1140 case startBenchTestSequence
:
1142 // see TODO on include at top and modify this line appropriately
1143 if(!(compare((char*)&decoderName
, BENCH_TEST_NAME
, sizeof(BENCH_TEST_NAME
)))){
1144 if(RXCalculatedPayloadLength
< 1){
1145 errorID
= payloadLengthTypeMismatch
;
1149 unsigned char localTestMode
= *((unsigned char*)RXBufferCurrentPosition
); //1; // The only mode, for now.
1150 RXBufferCurrentPosition
++;
1151 if(localTestMode
> TEST_MODE_BUMP_UP_CYCLES
){
1152 errorID
= unimplementedTestMode
;
1154 }else if((localTestMode
== TEST_MODE_STOP
) && (RXCalculatedPayloadLength
== 1)){
1155 if(!(coreStatusA
& BENCH_TEST_ON
)){
1156 errorID
= benchTestNotRunningToStop
;
1160 // Ensure we succeed at stopping it as quickly as possible.
1162 KeyUserDebugs
.currentEvent
= testEventsPerCycle
- 1; // Gets incremented then compared with testEventsPerCycle
1163 testNumberOfCycles
= 1; // Gets decremented then compared with zero
1166 // eventually save and return where it got to
1168 }else if((localTestMode
== TEST_MODE_BUMP_UP_CYCLES
) && (RXCalculatedPayloadLength
== 2)){
1169 if(!(coreStatusA
& BENCH_TEST_ON
)){
1170 errorID
= benchTestNotRunningToBump
;
1174 // Get bump value from payload
1175 unsigned char bumpCycles
= *((unsigned char*)RXBufferCurrentPosition
); //1; // The only mode, for now.
1176 RXBufferCurrentPosition
++;
1178 if(bumpCycles
== 0){
1179 errorID
= bumpingByZeroMakesNoSense
;
1183 // Bump count by value from payload
1184 testNumberOfCycles
+= bumpCycles
;
1185 // Given that this function is only for situations when A it's getting near to
1186 // zero and B the user is watching, not checking for overflow is reasonable.
1188 }else if((localTestMode
== TEST_MODE_ITERATIONS
) && (RXCalculatedPayloadLength
== 24)){
1189 testMode
= localTestMode
;
1190 // do nothing to fall through, or move other code into here
1192 errorID
= packetSizeWrongForTestMode
;
1196 if(coreStatusA
& BENCH_TEST_ON
){
1197 errorID
= benchTestAlreadyRunning
;
1201 testEventsPerCycle
= *((unsigned char*)RXBufferCurrentPosition
); //100; // @ 10ms = 1s
1202 RXBufferCurrentPosition
++;
1203 if(testEventsPerCycle
== 0){
1204 errorID
= invalidEventsPerCycle
;
1208 testNumberOfCycles
= *((unsigned short*)RXBufferCurrentPosition
); //20; // @ 1s = 20s
1209 RXBufferCurrentPosition
+= 2;
1210 if(testNumberOfCycles
== 0){
1211 errorID
= invalidNumberOfCycles
;
1215 testTicksPerEvent
= *((unsigned short*)RXBufferCurrentPosition
); //12500; // @ 0.8us = 10ms
1216 RXBufferCurrentPosition
+= 2;
1217 if(testTicksPerEvent
< decoderMaxCodeTime
){
1218 errorID
= tooShortOfAnEventPeriod
;
1222 // Pluck the arrays out of the packet for the loop below
1223 unsigned char* testEventNumbers
= RXBufferCurrentPosition
;
1224 RXBufferCurrentPosition
+= 6;
1225 unsigned short* testPulseWidths
= (unsigned short*)RXBufferCurrentPosition
;
1226 RXBufferCurrentPosition
+= 12;
1228 // Reset the clock for reading timeout
1229 Clocks
.timeoutADCreadingClock
= 0; // make this optional, such that we can use real inputs to determine pw and/or dwell.
1231 // Validate and transfer the per-channel data
1232 unsigned char channel
;
1233 unsigned char configuredChannels
= 6;
1234 for(channel
= 0;channel
< 6;channel
++){
1235 if(testPulseWidths
[channel
] > injectorSwitchOnCodeTime
){ // See next block for warning.
1237 outputEventDelayFinalPeriod
[channel
] = decoderMaxCodeTime
;
1238 outputEventPulseWidthsMath
[channel
] = testPulseWidths
[channel
];
1239 outputEventInputEventNumbers
[channel
] = testEventNumbers
[channel
];
1240 }else if(testPulseWidths
[channel
] > 3){
1241 // less than the code time, and not special, error!
1242 errorID
= tooShortOfAPulseWidthToTest
;
1243 // Warning, PWs close to this could be slightly longer than requested, that will change in later revisions.
1245 }else if(testPulseWidths
[channel
] == 3){
1246 testMode
++; // Dirty hack to avoid dealing with Dave for the time being.
1247 testNumberOfMissing
= channel
;
1248 }else if(testPulseWidths
[channel
] == 2){
1249 // use the dwell from the core maths and input vars.
1250 outputEventDelayFinalPeriod
[channel
] = decoderMaxCodeTime
;
1251 outputEventPulseWidthsMath
[channel
] = DerivedVars
->Dwell
;
1252 outputEventInputEventNumbers
[channel
] = testEventNumbers
[channel
];
1253 }else if(testPulseWidths
[channel
] == 1){
1254 // use the reference pulse width from the core maths and input vars.
1255 outputEventDelayFinalPeriod
[channel
] = decoderMaxCodeTime
;
1256 outputEventPulseWidthsMath
[channel
] = DerivedVars
->RefPW
;
1257 outputEventInputEventNumbers
[channel
] = testEventNumbers
[channel
];
1259 // Set this channel to zero for and therefore off, don't set this channel.
1260 outputEventInputEventNumbers
[channel
] = 0xFF; // Off.
1261 configuredChannels
--;
1265 if(configuredChannels
== 0){
1266 errorID
= noChannelsConfiguredToTest
;
1271 // Let the first iteration roll it over to zero.
1272 KeyUserDebugs
.currentEvent
= 0xFF; // Needs to be here in case of multiple runs, init is not sufficient
1274 if(testMode
== TEST_MODE_DODGY_MISSING_TOOTH
){
1275 if(testEventsPerCycle
<= 127){
1276 testEventsPerCycle
*= 2;
1278 errorID
= tooManyEventsPerCycleMissingTth
;
1282 // Store the time per event in RPM such that it can be updated dynamically
1283 CoreVars
->RPM
= testTicksPerEvent
;
1285 // The channels to use rely on the defaults from initialisers! Custom builds can break BenchTest mode!
1287 // Un-schedule anything that got scheduled
1288 outputEventInputEventNumbers
[2] = 0xFF;
1289 outputEventInputEventNumbers
[3] = 0xFF;
1290 outputEventInputEventNumbers
[4] = 0xFF;
1291 outputEventInputEventNumbers
[5] = 0xFF;
1292 }else if(testMode
> TEST_MODE_DODGY_MISSING_TOOTH
){
1293 errorID
= unimplementedTestMode
;
1297 // Trigger decoder interrupt to fire thus starting the loop!
1298 TIE
= 0x01; // The ISR does the rest!
1300 // Nothing went wrong, now set flag.
1301 coreStatusA
|= BENCH_TEST_ON
;
1307 /* http://issues.freeems.org/view.php?id=155
1309 * The following block has been left in, as I still do not know why it won't work as intended:
1311 * - It should fire all 6 output pins with a 52ms duration pulse, exactly once.
1312 * - The SAME code run from anywhere else (pre main loop, in main loop, in rtc, in decoder) works fine, just not here in commsCore.c
1313 * - The interrupts run, but the pin doesn't change state, despite the registers being configured correctly
1315 * I've tried quite a bit:
1317 * - Moving this code around
1318 * - Checking memory definitions
1319 * - Completely rewriting the output ISR
1320 * - Adding significant debug to output ISR
1321 * - Checking for register contents in output ISR
1322 * - Checking for key things modified in this file
1323 * - General head scratching and confused searching
1326 // outputEventPinNumbers[0] = 0; // 1 ign
1327 // outputEventPinNumbers[1] = 1; // 2 ign
1328 // outputEventPinNumbers[2] = 2; // 3 ign/1 fuel
1329 // outputEventPinNumbers[3] = 3; // 4 ign/2 fuel
1330 // outputEventPinNumbers[4] = 4; // 3 fuel
1331 // outputEventPinNumbers[5] = 5; // 4 fuel
1332 // outputEventDelayFinalPeriod[0] = decoderMaxCodeTime;
1333 // outputEventDelayFinalPeriod[1] = decoderMaxCodeTime;
1334 // outputEventDelayFinalPeriod[2] = decoderMaxCodeTime;
1335 // outputEventDelayFinalPeriod[3] = decoderMaxCodeTime;
1336 // outputEventDelayFinalPeriod[4] = decoderMaxCodeTime;
1337 // outputEventDelayFinalPeriod[5] = decoderMaxCodeTime;
1338 // outputEventPulseWidthsMath[0] = SHORTMAX;
1339 // outputEventPulseWidthsMath[1] = SHORTMAX;
1340 // outputEventPulseWidthsMath[2] = SHORTMAX;
1341 // outputEventPulseWidthsMath[3] = SHORTMAX;
1342 // outputEventPulseWidthsMath[4] = SHORTMAX;
1343 // outputEventPulseWidthsMath[5] = SHORTMAX;
1345 // unsigned short edgeTimeStamp = TCNT;
1346 // // call sched output with args
1347 // LongTime timeStamp;
1348 // /* Install the low word */
1349 // timeStamp.timeShorts[1] = edgeTimeStamp;
1350 // /* Find out what our timer value means and put it in the high word */
1351 // if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
1352 // timeStamp.timeShorts[0] = timerExtensionClock + 1;
1354 // timeStamp.timeShorts[0] = timerExtensionClock;
1357 // schedulePortTPin(0, timeStamp);
1358 // schedulePortTPin(1, timeStamp);
1359 // schedulePortTPin(2, timeStamp);
1360 // schedulePortTPin(3, timeStamp);
1361 // schedulePortTPin(4, timeStamp);
1362 // schedulePortTPin(5, timeStamp);
1366 errorID
= thisIsNotTheBenchTestDecoder
;
1372 if((RXHeaderPayloadID
% 2) == 1){
1373 errorID
= invalidPayloadID
;
1375 errorID
= unrecognisedPayloadID
;
1381 // Always reply, if errorID is zero it's just an ack.
1382 finaliseAndSend(errorID
);
1384 /* Switch reception back on now that we are done with the received data */
1385 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
1389 /** @brief Send an error if buffer free
1391 * This is a wrapper for use outside the communication handler function. The error will only be sent if the buffer is empty and available, if not, it will be discarded.
1393 * @author Fred Cooke
1395 * @warning Use of this function signifies that the error you are trying to propagate is not urgent and can be forgotten.
1397 * @note Consider not throwing an error if it seems appropriate to use this.
1399 * @todo TODO this is only used in coreVarGen, such errors should be caught at init time, NOT runtime, fix that...
1401 * @param errorID is the error ID to be passed out to listening devices.
1403 void sendErrorIfClear(unsigned short errorID
){
1404 if(!TXBufferInUseFlags
){
1405 TXBufferInUseFlags
= ONES
;
1406 sendErrorInternal(errorID
);
1408 FLAG_AND_INC_FLAGGABLE(FLAG_COMMS_ERROR_MESSAGES_NOT_SENT_OFFSET
);
1413 /* not currently used...
1414 * @brief Send an error even if we must wait
1416 * This is a wrapper for use outside the communication handler function. This
1417 * function will block until the error is able to be sent. This behaviour is
1418 * not recommended as it will interfere with engine operation somewhat.
1420 * @author Fred Cooke
1422 * @warning Use of this function signifies that the error you are trying to propagate is extremely urgent and can not be forgotten.
1424 * @note Using this function blocks other main loop code from execution. Consider handling the error in another way if it seems appropriate to use this.
1426 * @param errorID is the error ID to be passed out to listening devices.
1428 //void sendErrorBusyWait(unsigned short errorID){
1429 // while(TXBufferInUseFlags){} /* Wait till clear to send */
1430 // TXBufferInUseFlags = ONES;
1431 // sendErrorInternal(errorID);
1435 /** @brief Send an error
1437 * This function is only for use inside the communication handling function.
1438 * Use of it outside this environment is not supported and behaviour when used
1439 * as such is undefined.
1441 * @author Fred Cooke
1443 * @warning ONLY use this function from within the communication handler.
1445 * @see sendErrorIfClear()
1446 * @see sendErrorBusyWait()
1448 * @todo TODO clean up the mess of commented out crap in here!
1449 * @todo TODO decide on errorCode or errorID and consistencise it everywhere.
1451 * @param errorID is the error ID to be passed out to listening devices.
1453 void sendErrorInternal(unsigned short errorID
){
1454 // set buffer in use, consider blocking interrupts to do this cleanly
1457 // TXBufferInUseFlags = 0;
1458 /* No need for atomic block here as one of two conditions will always be */
1459 /* true when calling this. Either we have been flagged to receive and */
1460 /* decode a packet, or we are in an ISR. In either case it is safe to */
1461 /* check the flags and initiate the sequence if they are clear. */
1462 // if(RXTXSerialStateFlags & TX_IN_PROGRESS){
1463 /* It's OK to return without resetting as it will be done by */
1464 /* either of those processes if they are underway. The other */
1465 /* processes are not overridden because they have priority. */
1467 // }else{ /* Turn off reception */
1468 /* It's OK to turn this off if nothing was currently being received */
1469 // SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
1470 // SCI0CR2 &= SCICR2_RX_DISABLE;
1472 /* Build up the packet */
1473 /* Set the pointer to the start */
1474 TXBufferCurrentPositionHandler
= (unsigned char*)&TXBuffer
;
1475 /* Set the length */
1476 // TXPacketLengthToSend = 5; /* Flags + Payload ID + Error Code */
1478 *TXBufferCurrentPositionHandler
= 0x00;
1479 TXBufferCurrentPositionHandler
++;
1480 /* Set the payload ID */
1481 *((unsigned short*)TXBufferCurrentPositionHandler
) = asyncErrorCodePacket
;
1482 TXBufferCurrentPositionHandler
+= 2;
1484 finaliseAndSend(errorID
);
1489 /** @brief Send a debug message if buffer free
1491 * This is a wrapper for use outside the communication handler function. The debug message will only be sent if the buffer is empty and available, if not, it will be discarded.
1493 * @author Fred Cooke
1495 * @note This function exists as a convenience to developers, do not publish code that calls this function.
1497 * @param message is a pointer to the null terminated debug message string.
1499 void sendDebugIfClear(unsigned char* message
){
1500 if(!TXBufferInUseFlags
){
1501 TXBufferInUseFlags
= ONES
;
1502 sendDebugInternal(message
);
1504 FLAG_AND_INC_FLAGGABLE(FLAG_COMMS_DEBUG_MESSAGES_NOT_SENT_OFFSET
);
1509 /** Send a debug message even if we must wait
1511 * This is a wrapper for use outside the communication handler function. This
1512 * function will block until the debug message is able to be sent.
1514 * @author Fred Cooke
1516 * @note This function exists as a convenience to developers, do not publish code that calls this function.
1518 * @param message is a pointer to the null terminated debug message string.
1520 //void sendDebugBusyWait(unsigned char* message){
1521 // while(TXBufferInUseFlags){} /* Wait till clear to send */
1522 // TXBufferInUseFlags = ONES;
1523 // sendDebugInternal(message);
1527 /** @brief Send a debug message
1529 * Sends a null terminated debug message out on the broadcast address of all available interfaces.
1531 * @author Fred Cooke
1533 * @warning ONLY use this function from within the communication handler.
1535 * @see sendDebugIfClear()
1536 * @see sendDebugBusyWait()
1538 * @note This function exists as a convenience to developers, do not publish code that calls this function.
1540 * @todo TODO clean up the mess of commented out crap in here!
1542 * @param message is a pointer to the null terminated debug message string.
1544 void sendDebugInternal(unsigned char* message
){
1546 // set buffer in use, consider blocking interrupts to do this cleanly
1549 // Counters.serialDebugUnsentCounter++;
1553 /* No need for atomic block here as one of two conditions will always be */
1554 /* true when calling this. Either we have been flagged to receive and */
1555 /* decode a packet, or we are in an ISR. In either case it is safe to */
1556 /* check the flags and initiate the sequence if they are clear. */
1557 //if(RXTXSerialStateFlags & TX_IN_PROGRESS){
1559 /* It's OK to return without resetting as it will be done by */
1560 /* either of those processes if they are underway. The other */
1561 /* processes are not overridden because they have priority. */
1562 //TXBufferInUseFlags = 0;
1564 // }else{ /* Turn off reception */
1565 /* It's OK to turn this off if nothing was currently being received */
1566 // SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
1567 // SCI0CR2 &= SCICR2_RX_DISABLE;
1569 /* Build up the packet */
1570 /* Set the pointer to the start and init the length */
1571 TXBufferCurrentPositionHandler
= (unsigned char*)&TXBuffer
;
1573 /* Load a protocol with length header into the TX buffer ready for masking */
1574 *TXBufferCurrentPositionHandler
= 0x11;
1575 TXBufferCurrentPositionHandler
++;
1577 /* Set the payload ID */
1578 *((unsigned short*)TXBufferCurrentPositionHandler
) = asyncDebugInfoPacket
;
1579 TXBufferCurrentPositionHandler
+= 2;
1581 /* Store the length location */
1582 unsigned short* TXLength
= (unsigned short*)TXBufferCurrentPositionHandler
;
1583 TXBufferCurrentPositionHandler
+= 2;
1585 /* Copy the string into place and record the length copied */
1586 unsigned short messageLength
= stringCopy(TXBufferCurrentPositionHandler
, message
);
1587 *TXLength
= messageLength
;
1588 TXBufferCurrentPositionHandler
+= messageLength
;
1595 /* This function should be period limited to about 10 seconds internally (or by scheduler) */
1596 //void checkCountersAndSendErrors(){
1597 // compare time stamps with current time stamps and execute if old enough. (if no scheduler)
1599 // compare counters with counters cache (from last time) sending an error packet when they differ
1601 // copy counters to counters cache for next time
1603 // send errors with busy wait on the basis that all errors should be taken care of and not be sent in fairly short order?
1605 // or send with isr but just busy wait for it to finish before sending the next?
1607 // debug messages, busy wait or isr or both, perhaps busy wait till able to send, lock sending (need semaphore for this as well as sending one?) and initiate send, then carry on? investigate timeframes for sends of smallish 100byte packets.
1609 // need to figure out how to queue received packets for processing when we are currently sending stuff out.
1611 // above notes don't belong here really.
1615 //void prepareDatalog(){
1616 // // send data log by default otherwise
1617 // unsigned char chunksExpected = 8; // based on configuration, yet to determine how to calculate this number
1618 // unsigned char chunksLoaded = 0;
1619 // if ((!receiving) && (datalogMask & rawVarsMask)) {
1623 // if ((!receiving) && (datalogMask & Mask)) {
1627 // if ((!receiving) && (datalogMask & Mask)) {
1631 // if ((!receiving) && (datalogMask & Mask)) {
1635 // if ((!receiving) && (datalogMask & Mask)) {
1639 // if ((!receiving) && (datalogMask & Mask)) {
1643 // if ((!receiving) && (datalogMask & Mask)) {
1647 // if ((!receiving) && (datalogMask & Mask)) {
1651 // // set the length
1652 // // the pointer should be correct already