Standard hard coded datalog replaced with a totally configurable log setup. Up to...
[freeems-vanilla.git] / src / commsCore.c
blobee0b1c8741391d72e6381ac5f8af5c53fa9fbabc
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!
27 /** @file
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
41 * @author Fred Cooke
45 #define COMMSCORE_C
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"
55 #include "inc/init.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.
66 unsigned short populateBasicDatalog(){
67 /// @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.
69 // By default, default values are populated, but if you drop code into the custom directory, that replaces the defaults.
70 populateCustomDatalog();
72 // Done here to overwrite cheeky custom users data:
73 KeyUserDebugs.coreStatusA = coreStatusA;
74 KeyUserDebugs.tempClock++;
75 KeyUserDebugs.clockIn8thsOfAMilli = Clocks.realTimeClockMain;
76 KeyUserDebugs.clockInMilliSeconds = Clocks.realTimeClockMillis;
78 unsigned short confSize = 0;
79 unsigned char chunkLimit = TablesB.SmallTablesB.loggingSettings.firstChunk + TablesB.SmallTablesB.loggingSettings.numberOfChunks;
80 unsigned char chunks;
81 for(chunks=TablesB.SmallTablesB.loggingSettings.firstChunk;chunks<chunkLimit;chunks++){
82 unsigned short localSize = TablesB.SmallTablesB.loggingSettings.logChunks[chunks].size;
83 confSize += localSize;
84 if(confSize > 2048){
85 confSize -= localSize;
86 break;
88 memcpy(TXBufferCurrentPositionHandler, TablesB.SmallTablesB.loggingSettings.logChunks[chunks].address, localSize);
89 TXBufferCurrentPositionHandler += localSize;
91 return confSize;
95 // All of these require some range checking, eg only some registers, and all RAM, not flash, not other regs
96 // TODO pointer for one byte
97 // TODO pointer for one short
98 // TODO function to log generic memory region by location and size ? requires length!
99 // Ranges are :
100 // RAM window
101 // bss/data region
102 // IO registers etc that can't be altered simply by reading from.
103 // NOT :
104 // flash makes no sense
105 // some regs are sensitive
106 // some RAM is unused
107 // serial buffers make no sense
108 // eeprom makes no sense
110 // 2k of regs max - user beware for now
111 // 12k of RAM max
113 //init :
114 //logaddr = fixed.addr
115 //loglen = fixed.len
117 //len = loglen OR 1 OR 2
119 //check :
120 //if((addr < 0x0800) && (length < (0x0800 - addr))){
121 // // reg space is OK
122 //}else if(((0x1000 < addr) && (addr < 0x4000)) && (length < (0x4000 - addr))){
123 // // RAM space is OK
124 //}else{
125 // // send an error instead
128 //run check at init and set time, not run time or just not check?? maybe its silly to check at all
130 // /* Just dump the ADC channels as fast as possible */
131 //void populateScopeLogADCAll(){
132 // sampleBlockADC(TXBufferCurrentPositionHandler);
133 // TXBufferCurrentPositionHandler += sizeof(ADCBuffer);
137 // 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.
140 /** @brief Finalise a packet and send it
142 * This functions job is to finalise the main loop part of the packet sending
143 * process. It configures the pos/neg ack header bit, adds the code if neg,
144 * runs a checksum over the packet data and tags it to the end before
145 * configuring the various ISRs that need to send the data out.
147 * @author Fred Cooke
149 * @bug http://issues.freeems.org/view.php?id=81
150 * @todo TODO fix the double/none start byte bug and remove the hack!
152 void finaliseAndSend(unsigned short errorID){
154 if(errorID != 0){
155 *TXHeaderFlags |= HEADER_IS_NACK;
156 *((unsigned short*)TXBufferCurrentPositionHandler) = errorID;
157 TXBufferCurrentPositionHandler += 2;
160 /* Tag the checksum on the end */
161 *TXBufferCurrentPositionHandler = checksum((unsigned char*)&TXBuffer, ((unsigned short)TXBufferCurrentPositionHandler - (unsigned short)&TXBuffer));
163 /* Send it out on all the channels required. */
165 /* SCI0 - Main serial interface */
166 if(TXBufferInUseFlags & COM_SET_SCI0_INTERFACE_ID){
167 /* Initiate transmission */
168 SCI0DRL = START_BYTE;
170 /* Note : Order Is Important! */
171 /* TX empty flag is already set, so we must clear it by writing out before enabling the interrupt */
172 SCI0CR2 |= (SCICR2_TX_ENABLE | SCICR2_TX_ISR_ENABLE);
174 /* CAN0 - Main CAN interface */
175 if(TXBufferInUseFlags & COM_SET_CAN0_INTERFACE_ID){
176 // just clear up front for now
177 TXBufferInUseFlags &= COM_CLEAR_CAN0_INTERFACE_ID;
179 /* spare2 */
180 if(TXBufferInUseFlags & COM_SET_SPARE2_INTERFACE_ID){
181 // just clear up front for now
182 TXBufferInUseFlags &= COM_CLEAR_SPARE2_INTERFACE_ID;
184 /* spare3 */
185 if(TXBufferInUseFlags & COM_SET_SPARE3_INTERFACE_ID){
186 // just clear up front for now
187 TXBufferInUseFlags &= COM_CLEAR_SPARE3_INTERFACE_ID;
189 /* spare4 */
190 if(TXBufferInUseFlags & COM_SET_SPARE4_INTERFACE_ID){
191 // just clear up front for now
192 TXBufferInUseFlags &= COM_CLEAR_SPARE4_INTERFACE_ID;
194 /* spare5 */
195 if(TXBufferInUseFlags & COM_SET_SPARE5_INTERFACE_ID){
196 // just clear up front for now
197 TXBufferInUseFlags &= COM_CLEAR_SPARE5_INTERFACE_ID;
199 /* spare6 */
200 if(TXBufferInUseFlags & COM_SET_SPARE6_INTERFACE_ID){
201 // just clear up front for now
202 TXBufferInUseFlags &= COM_CLEAR_SPARE6_INTERFACE_ID;
204 /* spare7 */
205 if(TXBufferInUseFlags & COM_SET_SPARE7_INTERFACE_ID){
206 // just clear up front for now
207 TXBufferInUseFlags &= COM_CLEAR_SPARE7_INTERFACE_ID;
212 /** @brief Decode a packet and respond
214 * This is the core function that controls which functionality is run when a
215 * packet is received in full by the ISR code and control is passed back to the
216 * main loop code. The vast majority of communications action happens here.
218 * @author Fred Cooke
220 void decodePacketAndRespond(){
221 /* Extract and build up the header fields */
222 TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
224 /* Initialised here such that override is possible */
225 TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
226 TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;
228 // How big was the packet that we got back
229 unsigned short RXPacketLengthReceived = (unsigned short)RXBufferCurrentPosition - (unsigned short)&RXBuffer;
231 /* Check that the packet is big enough for header,ID,checksum */
232 if(RXPacketLengthReceived < 4){
233 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
234 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_UNDER_LENGTH_OFFSET);
235 KeyUserDebugs.serialAndCommsCodeErrors++;
236 return;
239 /* Pull out the received checksum and calculate the real one, then check */
240 unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1);
241 unsigned char RXCalculatedChecksum = checksum((unsigned char*)&RXBuffer, RXPacketLengthReceived - 1);
242 if(RXCalculatedChecksum != RXReceivedChecksum){
243 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
244 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_CHECKSUM_MISMATCHES_OFFSET);
245 KeyUserDebugs.serialAndCommsCodeErrors++;
246 return;
249 /* Start this off as full packet length and build down to the actual length */
250 RXCalculatedPayloadLength = RXPacketLengthReceived;
252 /* Grab the RX header flags out of the RX buffer */
253 RXBufferCurrentPosition = (unsigned char*)&RXBuffer;
254 RXHeaderFlags = *RXBufferCurrentPosition;
255 RXBufferCurrentPosition++;
256 RXCalculatedPayloadLength--;
258 /* Flag that we are transmitting! */
259 TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
260 // SCI0 only for now...
262 /* Load a blank header into the TX buffer ready for masking */
263 TXHeaderFlags = TXBufferCurrentPositionHandler;
264 *TXHeaderFlags = 0;
265 TXBufferCurrentPositionHandler++;
267 /* Grab the payload ID for processing and load the return ID */
268 RXHeaderPayloadID = *((unsigned short*)RXBufferCurrentPosition);
269 *((unsigned short*)TXBufferCurrentPositionHandler) = RXHeaderPayloadID + 1;
270 RXBufferCurrentPosition += 2;
271 TXBufferCurrentPositionHandler += 2;
272 RXCalculatedPayloadLength -= 2;
274 /* Check that the length is sufficient for the fields configured. Packets
275 * that are too long will be caught and rejected on an individual payload
276 * ID basis as the information required to handle that is not available at
277 * this point. Packets that are too short are rejected immediately!
279 if(((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 7))
280 || ((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXPacketLengthReceived < 6))
281 || ((RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 5))){
282 finaliseAndSend(packetTooShortForSpecifiedFields);
283 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
284 return;
287 /* Subtract checksum to get final length */
288 RXCalculatedPayloadLength--;
290 if(RXHeaderFlags & HEADER_HAS_SEQUENCE){
291 *TXBufferCurrentPositionHandler = *RXBufferCurrentPosition;
292 RXBufferCurrentPosition++;
293 TXBufferCurrentPositionHandler++;
294 RXCalculatedPayloadLength--;
295 *TXHeaderFlags |= HEADER_HAS_SEQUENCE;
298 if(RXHeaderFlags & HEADER_HAS_LENGTH){
299 RXHeaderPayloadLength = *((unsigned short*)RXBufferCurrentPosition);
300 RXBufferCurrentPosition += 2;
301 RXCalculatedPayloadLength -= 2;
302 /* Already subtracted one for checksum */
303 if(RXHeaderPayloadLength != RXCalculatedPayloadLength){
304 finaliseAndSend(payloadLengthHeaderMismatch);
305 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
306 return;
310 /* Calculate the position of the end of the stored packet for later use as a buffer */
311 void* leftOverBuffer = (void*)((unsigned short)&RXBuffer + RXPacketLengthReceived);
313 unsigned short errorID = 0;
314 /* This is where all the communication logic resides.
316 * Please Note: Length and it's flag should be set by each return packet
317 * type handler if required or desired. If an ack has been requested,
318 * ensure the negative ack flag is set if the operation failed.
320 switch (RXHeaderPayloadID){
321 // FreeEMS Core Comms Interface cases
322 case requestInterfaceVersion:
324 if(RXCalculatedPayloadLength != 0){
325 errorID = payloadLengthTypeMismatch;
326 break;
329 /* This type must have a length field, set that up */
330 *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(interfaceVersion);
331 *TXHeaderFlags |= HEADER_HAS_LENGTH;
332 TXBufferCurrentPositionHandler += 2;
333 /* Load the body into place */
334 memcpy((void*)TXBufferCurrentPositionHandler, (void*)&interfaceVersion, sizeof(interfaceVersion));
335 TXBufferCurrentPositionHandler += sizeof(interfaceVersion);
336 break;
338 case requestFirmwareVersion:
340 if(RXCalculatedPayloadLength != 0){
341 errorID = payloadLengthTypeMismatch;
342 break;
344 /* This type must have a length field, set that up */
345 *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(firmwareVersion);
346 *TXHeaderFlags |= HEADER_HAS_LENGTH;
347 TXBufferCurrentPositionHandler += 2;
348 /* Load the body into place */
349 memcpy((void*)TXBufferCurrentPositionHandler, (void*)&firmwareVersion, sizeof(firmwareVersion));
350 TXBufferCurrentPositionHandler += sizeof(firmwareVersion);
351 break;
353 case requestMaxPacketSize:
355 if(RXCalculatedPayloadLength != 0){
356 errorID = payloadLengthTypeMismatch;
357 break;
359 /* Load the size into place */
360 *((unsigned short*)TXBufferCurrentPositionHandler) = RX_BUFFER_SIZE;
361 TXBufferCurrentPositionHandler += 2;
362 break;
364 case requestEchoPacketReturn:
366 /* This type must have a length field, set that up */
367 *((unsigned short*)TXBufferCurrentPositionHandler) = RXPacketLengthReceived;
368 *TXHeaderFlags |= HEADER_HAS_LENGTH;
369 TXBufferCurrentPositionHandler += 2;
370 /* Load the body into place */
371 memcpy((void*)TXBufferCurrentPositionHandler, (void*)&RXBuffer, RXPacketLengthReceived);
372 /* Note, there is no overflow check here because the TX buffer is slightly */
373 /* bigger than the RX buffer and there is overflow checking for receives anyway. */
374 TXBufferCurrentPositionHandler += RXPacketLengthReceived;
375 break;
377 case requestSoftSystemReset:
379 if(RXCalculatedPayloadLength != 0){
380 errorID = payloadLengthTypeMismatch;
381 }else{ // Perform soft system reset
382 _start();
384 break;
386 case requestHardSystemReset:
388 if(RXCalculatedPayloadLength != 0){
389 errorID = payloadLengthTypeMismatch;
390 }else{
391 /* This is how the serial monitor does it. */
392 COPCTL = 0x01; /* Arm with shortest time */
393 ARMCOP = 0xFF; /* Write bad value, should cause immediate reset */
394 /* Using _start() only resets the app ignoring the monitor switch. It does not work */
395 /* properly because the location of _start is not the master reset vector location. */
397 break;
399 case requestReInitOfSystem:
401 if(RXCalculatedPayloadLength != 0){
402 errorID = payloadLengthTypeMismatch;
403 }else{
404 init();
406 break;
408 // FreeEMS Vanilla Firmware Specific cases
409 case clearCountersAndFlagsToZero:
411 if(RXCalculatedPayloadLength != 0){
412 errorID = payloadLengthTypeMismatch;
413 break;
416 unsigned short zeroCounter;
417 unsigned char* counterPointer = (char*) &Counters;
418 for(zeroCounter = 0;zeroCounter < sizeof(Counter);zeroCounter++){
419 *counterPointer = 0;
420 counterPointer++;
422 KeyUserDebugs.flaggableFlags = 0;
423 unsigned char* flaggablePointer = (char*) &Flaggables;
424 for(zeroCounter = 0;zeroCounter < sizeof(Flaggable);zeroCounter++){
425 *flaggablePointer = 0;
426 flaggablePointer++;
428 break;
430 case requestDecoderName:
431 case requestFirmwareBuildDate:
432 case requestCompilerVersion:
433 case requestOperatingSystem:
435 if(RXCalculatedPayloadLength != 0){
436 errorID = payloadLengthTypeMismatch;
437 break;
440 unsigned char* stringToSend = 0;
441 switch (RXHeaderPayloadID) {
442 case requestDecoderName:
443 stringToSend = (unsigned char*)decoderName;
444 break;
445 case requestFirmwareBuildDate:
446 stringToSend = (unsigned char*)buildTimeAndDate;
447 break;
448 case requestCompilerVersion:
449 stringToSend = (unsigned char*)compilerVersion;
450 break;
451 case requestOperatingSystem:
452 stringToSend = (unsigned char*)operatingSystem;
453 break;
455 /* This type must have a length field, set that up and load the body into place at the same time */
456 *((unsigned short*)TXBufferCurrentPositionHandler) = stringCopy((TXBufferCurrentPositionHandler + 2), stringToSend);
457 *TXHeaderFlags |= HEADER_HAS_LENGTH;
458 // Update with length field and string length.
459 TXBufferCurrentPositionHandler += 2 + *((unsigned short*)TXBufferCurrentPositionHandler);
460 break;
462 case updateBlockInRAM:
464 // Subtract six to allow for the locationID, size, offset
465 if(RXCalculatedPayloadLength < 7){
466 errorID = payloadLengthTypeMismatch;
467 break;
470 // Extract the RAM location ID
471 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
472 RXBufferCurrentPosition += 2;
474 // Extract the offset to place the data at
475 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
476 RXBufferCurrentPosition += 2;
478 // Extract the size of the data to be stored
479 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
480 RXBufferCurrentPosition += 2;
482 // Look up the memory location details
483 blockDetails details;
484 lookupBlockDetails(locationID, &details);
486 // Don't let anyone write to running variables unless we are running BenchTest firmware!
487 if((details.flags & block_is_read_only) && compare((char*)&decoderName, BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME))){
488 errorID = attemptToWriteToReadOnlyBlock;
489 break;
492 // Subtract six to allow for the locationID, size, offset
493 if((RXCalculatedPayloadLength - 6) != size){
494 errorID = payloadNotEqualToSpecifiedValue;
495 break;
498 // If either of these is zero then this block is not in RAM!
499 if((details.RAMPage == 0) || (details.RAMAddress == 0)){
500 errorID = invalidMemoryActionForID;
501 break;
504 // Check that size and offset describe a region that is not out of bounds
505 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
506 errorID = invalidSizeOffsetCombination;
507 break;
510 // Don't allow sub region manipulation where it does not make sense or is unsafe.
511 if((size != details.size) && !(details.flags & block_is_indexable)){
512 errorID = uncheckedTableManipulationNotAllowed;
513 break;
516 // Save page values for restore
517 unsigned char oldRamPage = RPAGE;
518 // Set the viewable RAM page
519 RPAGE = details.RAMPage;
521 /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
523 if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
524 void* bufferToCheck;
526 // For sub regions, construct an image for verification
527 if(size != details.size){
528 // Copy data from destination location to buffer
529 memcpy(leftOverBuffer, details.RAMAddress, details.size);
531 // Copy data from rx buffer to buffer over writing old data
532 memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
534 bufferToCheck = leftOverBuffer;
535 }else{
536 bufferToCheck = RXBufferCurrentPosition;
539 // Verify all tables
540 if(details.flags & block_is_main_table){
541 errorID = validateMainTable((mainTable*)bufferToCheck);
542 }else if(details.flags & block_is_2dus_table){
543 errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
544 }// TODO add other table types here
546 // If the validation failed, report it
547 if(errorID != 0){
548 RPAGE = oldRamPage; // Restore the original RAM page, even when getting an error condition.
549 break;
553 // Copy from the RX buffer to the block of RAM
554 memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
556 // Check that the write was successful
557 unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
559 // Restore the original RAM and flash pages
560 RPAGE = oldRamPage;
562 if(index != 0){
563 errorID = MEMORY_WRITE_ERROR;
565 break;
567 case updateBlockInFlash:
569 // Subtract six to allow for the locationID, size, offset
570 if(RXCalculatedPayloadLength < 7){
571 errorID = payloadLengthTypeMismatch;
572 break;
575 // Extract the RAM location ID
576 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
577 RXBufferCurrentPosition += 2;
579 // Extract the offset to place the data at
580 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
581 RXBufferCurrentPosition += 2;
583 // Extract the size of the data to be stored
584 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
585 RXBufferCurrentPosition += 2;
587 // Look up the memory location details
588 blockDetails details;
589 lookupBlockDetails(locationID, &details);
591 // Subtract six to allow for the locationID, size, offset
592 if((RXCalculatedPayloadLength - 6) != size){
593 errorID = payloadNotEqualToSpecifiedValue;
594 break;
597 // If either of these is zero then this block is not in flash!
598 if((details.FlashPage == 0) || (details.FlashAddress == 0)){
599 errorID = invalidMemoryActionForID;
600 break;
603 // Check that size and offset describe a region that is not out of bounds
604 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
605 errorID = invalidSizeOffsetCombination;
606 break;
609 // Don't allow sub region manipulation where it does not make sense or is unsafe.
610 if((size != details.size) && !(details.flags & block_is_indexable)){
611 errorID = uncheckedTableManipulationNotAllowed;
612 break;
615 /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
617 if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
618 void* bufferToCheck;
620 // For sub regions, construct an image for verification
621 if(size != details.size){
622 /* Save page value for restore and set the visible page */
623 unsigned char oldFlashPage = PPAGE;
624 PPAGE = details.FlashPage;
626 // Copy data from destination location to buffer
627 memcpy(leftOverBuffer, details.FlashAddress, details.size);
629 /* Restore the original flash page */
630 PPAGE = oldFlashPage;
632 // Copy data from rx buffer to buffer over writing old data
633 memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
635 bufferToCheck = leftOverBuffer;
636 }else{
637 bufferToCheck = RXBufferCurrentPosition;
640 // Verify all tables
641 if(details.flags & block_is_main_table){
642 errorID = validateMainTable((mainTable*)bufferToCheck);
643 }else if(details.flags & block_is_2dus_table){
644 errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
645 }// TODO add other table types here
647 // If the validation failed, report it
648 if(errorID != 0){
649 break;
653 /* Copy the flash details and populate the RAM details with the buffer location */
654 blockDetails burnDetails;
655 burnDetails.FlashPage = details.FlashPage;
656 burnDetails.FlashAddress = details.FlashAddress + offset;
657 burnDetails.RAMPage = RPAGE;
658 burnDetails.RAMAddress = RXBufferCurrentPosition;
659 burnDetails.size = size;
661 /* Copy from the RX buffer to the block of flash */
662 errorID = writeBlock(&burnDetails, leftOverBuffer);
663 if(errorID != 0){
664 break;
667 /* If present in RAM, update that too */
668 if((details.RAMPage != 0) && (details.RAMAddress != 0)){
669 /* Save page values for restore */
670 unsigned char oldRamPage = RPAGE;
671 /* Set the viewable RAM page */
672 RPAGE = details.RAMPage;
674 /* Copy from the RX buffer to the block of RAM */
675 memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
677 /* Check that the write was successful */
678 unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
680 /* Restore the original RAM and flash pages */
681 RPAGE = oldRamPage;
683 if(index != 0){
684 errorID = MEMORY_WRITE_ERROR;
688 break;
690 case retrieveBlockFromRAM:
692 if(RXCalculatedPayloadLength != 6){
693 errorID = payloadLengthTypeMismatch;
694 break;
697 // Extract the RAM location ID
698 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
699 RXBufferCurrentPosition += 2;
701 // Extract the offset to place the data at
702 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
703 RXBufferCurrentPosition += 2;
705 // Extract the size of the data to be stored
706 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
707 RXBufferCurrentPosition += 2;
709 /* Look up the memory location details */
710 blockDetails details;
711 lookupBlockDetails(locationID, &details);
713 if((details.RAMPage == 0) || (details.RAMAddress == 0)){
714 errorID = invalidMemoryActionForID;
715 break;
718 // Special behaviour for size of zero which returns the whole block
719 if((size == 0) && (offset == 0)){
720 size = details.size;
723 // Check that size and offset describe a region that is not out of bounds
724 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
725 errorID = invalidSizeOffsetCombination;
726 break;
729 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
730 if((size != details.size) && !(details.flags & block_is_indexable)){
731 errorID = doesNotMakeSenseToRetrievePartially;
732 break;
735 // This type must have a length field, set that up
736 *((unsigned short*)TXBufferCurrentPositionHandler) = size;
737 *TXHeaderFlags |= HEADER_HAS_LENGTH;
738 TXBufferCurrentPositionHandler += 2;
740 /* Save page value for restore and set the visible page */
741 unsigned char oldRamPage = RPAGE;
742 RPAGE = details.RAMPage;
744 /* Copy the block of RAM to the TX buffer */
745 memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.RAMAddress + offset), size);
746 TXBufferCurrentPositionHandler += size;
748 /* Restore the original RAM and flash pages */
749 RPAGE = oldRamPage;
751 break;
753 case retrieveBlockFromFlash:
755 if(RXCalculatedPayloadLength != 6){
756 errorID = payloadLengthTypeMismatch;
757 break;
760 // Extract the RAM location ID
761 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
762 RXBufferCurrentPosition += 2;
764 // Extract the offset to place the data at
765 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
766 RXBufferCurrentPosition += 2;
768 // Extract the size of the data to be stored
769 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
770 RXBufferCurrentPosition += 2;
772 /* Look up the memory location details */
773 blockDetails details;
774 lookupBlockDetails(locationID, &details);
776 if((details.FlashPage == 0) || (details.FlashAddress == 0)){
777 errorID = invalidMemoryActionForID;
778 break;
781 // Special behaviour for size of zero which returns the whole block
782 if((size == 0) && (offset == 0)){
783 size = details.size;
786 // Check that size and offset describe a region that is not out of bounds
787 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
788 errorID = invalidSizeOffsetCombination;
789 break;
792 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
793 if((size != details.size) && !(details.flags & block_is_indexable)){
794 errorID = doesNotMakeSenseToRetrievePartially;
795 break;
798 // This type must have a length field, set that up
799 *((unsigned short*)TXBufferCurrentPositionHandler) = size;
800 *TXHeaderFlags |= HEADER_HAS_LENGTH;
801 TXBufferCurrentPositionHandler += 2;
803 /* Save page value for restore and set the visible page */
804 unsigned char oldFlashPage = PPAGE;
805 PPAGE = details.FlashPage;
807 /* Copy the block of flash to the TX buffer */
808 memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.FlashAddress + offset), size);
809 TXBufferCurrentPositionHandler += size;
811 /* Restore the original RAM and flash pages */
812 PPAGE = oldFlashPage;
814 break;
816 case burnBlockFromRamToFlash:
818 if(RXCalculatedPayloadLength != 6){
819 errorID = payloadLengthTypeMismatch;
820 break;
823 // Extract the RAM location ID
824 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
825 RXBufferCurrentPosition += 2;
827 // Extract the offset to place the data at
828 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
829 RXBufferCurrentPosition += 2;
831 // Extract the size of the data to be stored
832 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
833 RXBufferCurrentPosition += 2;
835 /* Look up the memory location details */
836 blockDetails details;
837 lookupBlockDetails(locationID, &details);
839 /* Check that all data we need is present */
840 if((details.RAMPage == 0) || (details.RAMAddress == 0) || (details.FlashPage == 0) || (details.FlashAddress == 0)){
841 errorID = invalidMemoryActionForID;
842 break;
845 // Special behaviour for size of zero which burns the whole block
846 if((size == 0) && (offset == 0)){
847 size = details.size;
850 // Check that size and offset describe a region that is not out of bounds
851 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
852 errorID = invalidSizeOffsetCombination;
853 break;
856 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
857 if((size != details.size) && !(details.flags & block_is_indexable)){
858 errorID = doesNotMakeSenseToRetrievePartially;
859 break;
863 // adjust details block to feed to represent the subsection of ram and flash that we want to burn down.
864 details.RAMAddress += offset;
865 details.FlashAddress += offset;
866 details.size = size;
868 /* Write the block down from RAM to Flash */
869 errorID = writeBlock(&details, leftOverBuffer);
870 break;
872 case requestDatalogPacket: // Set type through standard configuration methods
874 if(RXCalculatedPayloadLength != 0){
875 errorID = payloadLengthTypeMismatch;
876 break;
879 /* Set the length field up */
880 *TXHeaderFlags |= HEADER_HAS_LENGTH;
881 unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
882 TXBufferCurrentPositionHandler += 2;
884 /* Fill out the log and send */
885 *localLength = populateBasicDatalog();
886 break;
888 case setAsyncDatalogType:
890 if(RXCalculatedPayloadLength != 1){
891 errorID = payloadLengthTypeMismatch;
892 break;
895 unsigned char newDatalogType = *((unsigned char*)RXBufferCurrentPosition);
896 if(newDatalogType > asyncDatalogLastType){
897 errorID = noSuchAsyncDatalogType;
898 break;
901 TablesB.SmallTablesB.loggingSettings.datalogStreamType = newDatalogType;
902 break;
904 case retrieveArbitraryMemory:
906 if(RXCalculatedPayloadLength != 6){
907 errorID = payloadLengthTypeMismatch;
908 break;
911 unsigned short length = *((unsigned short*)RXBufferCurrentPosition);
912 RXBufferCurrentPosition += 2;
913 // Make sure the buffer can handle the block
914 if(length > TX_MAX_PAYLOAD_SIZE){
915 errorID = requestedLengthTooLarge;
916 break;
919 void* address = (void*) *((unsigned short*)RXBufferCurrentPosition);
920 RXBufferCurrentPosition += 2;
921 // Ensure we don't try to read past the end of the address space
922 if(((unsigned short)address) <= ((0xFFFF - length) + 1)){
923 // TODO Possibly check and limit ranges
924 errorID = requestedAddressDisallowed;
925 break;
928 unsigned char RAMPage = *((unsigned char*)RXBufferCurrentPosition);
929 RXBufferCurrentPosition++;
930 // Ensure RAM page is valid. Being too high is not possible.
931 if(RAMPage < RPAGE_MIN){
932 errorID = requestedRAMPageInvalid;
933 break;
936 unsigned char FlashPage = *((unsigned char*)RXBufferCurrentPosition);
937 RXBufferCurrentPosition++;
938 // Ensure Flash page is valid. Being too high is not possible.
939 if(FlashPage < PPAGE_MIN){
940 errorID = requestedFlashPageInvalid;
941 break;
944 /* This type must have a length field, set that up */
945 *((unsigned short*)TXBufferCurrentPositionHandler) = length + 6;
946 *TXHeaderFlags |= HEADER_HAS_LENGTH;
947 TXBufferCurrentPositionHandler += 2;
949 /* Put the request payload into the reply */
950 *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short) address;
951 TXBufferCurrentPositionHandler += 2;
952 *((unsigned short*)TXBufferCurrentPositionHandler) = length;
953 TXBufferCurrentPositionHandler += 2;
954 *((unsigned char*)TXBufferCurrentPositionHandler) = RAMPage;
955 TXBufferCurrentPositionHandler++;
956 *((unsigned char*)TXBufferCurrentPositionHandler) = FlashPage;
957 TXBufferCurrentPositionHandler++;
959 /* Load the body into place */
960 memcpy((void*)TXBufferCurrentPositionHandler, address, length);
961 TXBufferCurrentPositionHandler += length;
963 break;
965 case retrieveListOfLocationIDs:
967 if(RXCalculatedPayloadLength != 3){
968 errorID = payloadLengthTypeMismatch;
969 break;
972 // Extract the type of list that we want
973 unsigned char listType = *((unsigned char*)RXBufferCurrentPosition);
974 RXBufferCurrentPosition++;
976 // Extract the mask for the qualities that we want
977 unsigned short blockDetailsMask = *((unsigned short*)RXBufferCurrentPosition);
978 RXBufferCurrentPosition += 2;
980 // This type must have a length field, set that up
981 unsigned short * listLength = (unsigned short*)TXBufferCurrentPositionHandler;
982 *TXHeaderFlags |= HEADER_HAS_LENGTH;
983 TXBufferCurrentPositionHandler += 2;
985 // Zero the counter before we start, woops!
986 *listLength = 0;
988 unsigned long locationID;
989 blockDetails details;
990 for(locationID = 0;locationID < 65536;locationID++){
991 unsigned short locationIDDoesntExist;
992 locationIDDoesntExist = lookupBlockDetails((unsigned short)locationID, &details);
994 if(!locationIDDoesntExist){
995 if((listType == 0x00) || // get all
996 ((listType == 0x01) && (details.flags & blockDetailsMask)) || // get OR of bits
997 ((listType == 0x02) && (!(~(details.flags) & blockDetailsMask)))){ // get AND of bits
998 *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short)locationID;
999 TXBufferCurrentPositionHandler += 2;
1000 *listLength += 2;
1005 break;
1007 case retrieveLocationIDDetails:
1009 if(RXCalculatedPayloadLength != 2){
1010 errorID = payloadLengthTypeMismatch;
1011 break;
1014 // Extract the RAM location ID
1015 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
1016 RXBufferCurrentPosition += 2;
1018 // This type must have a length field, set that up
1019 *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(blockDetails);
1020 *TXHeaderFlags |= HEADER_HAS_LENGTH;
1021 TXBufferCurrentPositionHandler += 2;
1023 // Write straight to output buffer to save time/code
1024 errorID = lookupBlockDetails(locationID, (blockDetails*)TXBufferCurrentPositionHandler);
1026 if(errorID != 0){
1027 break;
1030 // Adjust TX buffer position if successful
1031 TXBufferCurrentPositionHandler += sizeof(blockDetails);
1033 break;
1035 case requestUnitTestOverSerial:
1038 * The idea here is to call this function with arguments, and data
1039 * and have the result sent back for comparison with an expected
1040 * result that isn't divulged to the firmware.
1042 * It is intended that all testable functions be callable through
1043 * this mechanism and that any number of test executions can be
1044 * performed by an external suite using different parameters and
1045 * data sets and matching expected results.
1047 * The usual error mechanism shall be used to indicate some sort of
1048 * either internal or test failure and returned errors shall be
1049 * suitably descriptive to allow diagnosis and fixing of issues.
1052 // Must at least have test ID
1053 if(RXCalculatedPayloadLength < 2){
1054 errorID = payloadLengthTypeMismatch;
1055 break;
1058 // grab unit test ID from payload
1059 unsigned short unitTestID = *((unsigned short*)RXBufferCurrentPosition);
1060 RXBufferCurrentPosition += 2;
1062 switch(unitTestID){
1063 case testEmptyTest:
1065 // Must be only the ID
1066 if(RXCalculatedPayloadLength != 2){
1067 errorID = payloadShorterThanRequiredForTest;
1068 break;
1071 *((unsigned short*)TXBufferCurrentPositionHandler) = unitTestID;
1072 TXBufferCurrentPositionHandler +=2;
1074 break;
1076 case testTwoDTableUSLookup:
1078 // ID + Value + Table
1079 if(RXCalculatedPayloadLength != (2 + 2 + sizeof(twoDTableUS))){
1080 errorID = payloadShorterThanRequiredForTest;
1081 break;
1084 unsigned short Value = *((unsigned short*)RXBufferCurrentPosition);
1085 RXBufferCurrentPosition += 2;
1087 twoDTableUS* Table = ((twoDTableUS*)RXBufferCurrentPosition);
1088 RXBufferCurrentPosition += sizeof(twoDTableUS);
1090 unsigned short result = lookupTwoDTableUS(Table, Value);
1092 *((unsigned short*)TXBufferCurrentPositionHandler) = result;
1093 TXBufferCurrentPositionHandler +=2;
1095 break;
1097 // http://issues.freeems.org/view.php?id=156
1099 /// TODO @todo test all things listed below:
1100 // lookupPagedMainTableCellValue - pass this RPAGE so that it remains unchanged
1101 // validateMainTable
1102 // validateTwoDTable
1103 // set table values - leave this till last, currently unused by mtx, likely to be removed anyway
1104 // generateDerivedVars - convert to pointers, remove headers, privatise a lot of data!
1105 // calculateFuelAndIgnition - ditto
1106 // scheduling algorithm - ditto
1107 // safeAdd
1108 // safeTrim
1109 // safeScale
1110 // sleep (milliseconds)
1111 // sleepMicro (microseconds)
1112 // checksum
1113 // stringCopy
1114 // compare
1115 // 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)
1116 // init code may be able to be partially checked
1117 // most other code at this stage is ISR code, flash writing code, or could interfere with the running of the engine
1118 // more testable code will appear with time, such as the HAL layer, and most accessory functions.
1119 default:
1121 errorID = noSuchUnitTestID;
1124 // each case:
1125 // checks length, fails if wrong
1126 // parses data into args
1127 // calls function on data/args
1128 // assembles response OR sets error
1129 // breaks
1131 break;
1133 case startBenchTestSequence:
1135 // see TODO on include at top and modify this line appropriately
1136 if(!(compare((char*)&decoderName, BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME)))){
1137 if(RXCalculatedPayloadLength < 1){
1138 errorID = payloadLengthTypeMismatch;
1139 break;
1142 unsigned char localTestMode = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
1143 RXBufferCurrentPosition++;
1144 if(localTestMode > TEST_MODE_BUMP_UP_CYCLES){
1145 errorID = unimplementedTestMode;
1146 break;
1147 }else if((localTestMode == TEST_MODE_STOP) && (RXCalculatedPayloadLength == 1)){
1148 if(!(coreStatusA & BENCH_TEST_ON)){
1149 errorID = benchTestNotRunningToStop;
1150 break;
1153 // Ensure we succeed at stopping it as quickly as possible.
1154 ATOMIC_START();
1155 KeyUserDebugs.currentEvent = testEventsPerCycle - 1; // Gets incremented then compared with testEventsPerCycle
1156 testNumberOfCycles = 1; // Gets decremented then compared with zero
1157 ATOMIC_END();
1159 // eventually save and return where it got to
1160 break;
1161 }else if((localTestMode == TEST_MODE_BUMP_UP_CYCLES) && (RXCalculatedPayloadLength == 2)){
1162 if(!(coreStatusA & BENCH_TEST_ON)){
1163 errorID = benchTestNotRunningToBump;
1164 break;
1167 // Get bump value from payload
1168 unsigned char bumpCycles = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
1169 RXBufferCurrentPosition++;
1171 if(bumpCycles == 0){
1172 errorID = bumpingByZeroMakesNoSense;
1173 break;
1176 // Bump count by value from payload
1177 testNumberOfCycles += bumpCycles;
1178 // Given that this function is only for situations when A it's getting near to
1179 // zero and B the user is watching, not checking for overflow is reasonable.
1180 break;
1181 }else if((localTestMode == TEST_MODE_ITERATIONS) && (RXCalculatedPayloadLength == 24)){
1182 testMode = localTestMode;
1183 // do nothing to fall through, or move other code into here
1184 }else{
1185 errorID = packetSizeWrongForTestMode;
1186 break;
1189 if(coreStatusA & BENCH_TEST_ON){
1190 errorID = benchTestAlreadyRunning;
1191 break;
1194 testEventsPerCycle = *((unsigned char*)RXBufferCurrentPosition); //100; // @ 10ms = 1s
1195 RXBufferCurrentPosition++;
1196 if(testEventsPerCycle == 0){
1197 errorID = invalidEventsPerCycle;
1198 break;
1201 testNumberOfCycles = *((unsigned short*)RXBufferCurrentPosition); //20; // @ 1s = 20s
1202 RXBufferCurrentPosition += 2;
1203 if(testNumberOfCycles == 0){
1204 errorID = invalidNumberOfCycles;
1205 break;
1208 testTicksPerEvent = *((unsigned short*)RXBufferCurrentPosition); //12500; // @ 0.8us = 10ms
1209 RXBufferCurrentPosition += 2;
1210 if(testTicksPerEvent < decoderMaxCodeTime){
1211 errorID = tooShortOfAnEventPeriod;
1212 break;
1215 // Pluck the arrays out of the packet for the loop below
1216 unsigned char* testEventNumbers = RXBufferCurrentPosition;
1217 RXBufferCurrentPosition += 6;
1218 unsigned short* testPulseWidths = (unsigned short*)RXBufferCurrentPosition;
1219 RXBufferCurrentPosition += 12;
1221 // Reset the clock for reading timeout
1222 Clocks.timeoutADCreadingClock = 0; // make this optional, such that we can use real inputs to determine pw and/or dwell.
1224 // Validate and transfer the per-channel data
1225 unsigned char channel;
1226 unsigned char configuredChannels = 6;
1227 for(channel = 0;channel < 6;channel++){
1228 if(testPulseWidths[channel] > injectorSwitchOnCodeTime){ // See next block for warning.
1229 // use as-is
1230 outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime;
1231 outputEventPulseWidthsMath[channel] = testPulseWidths[channel];
1232 outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1233 }else if(testPulseWidths[channel] > 3){
1234 // less than the code time, and not special, error!
1235 errorID = tooShortOfAPulseWidthToTest;
1236 // Warning, PWs close to this could be slightly longer than requested, that will change in later revisions.
1237 break;
1238 }else if(testPulseWidths[channel] == 3){
1239 testMode++; // Dirty hack to avoid dealing with Dave for the time being.
1240 testNumberOfMissing = channel;
1241 }else if(testPulseWidths[channel] == 2){
1242 // use the dwell from the core maths and input vars.
1243 outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime;
1244 outputEventPulseWidthsMath[channel] = DerivedVars->Dwell;
1245 outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1246 }else if(testPulseWidths[channel] == 1){
1247 // use the reference pulse width from the core maths and input vars.
1248 outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime;
1249 outputEventPulseWidthsMath[channel] = DerivedVars->RefPW;
1250 outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1251 }else{ // is zero
1252 // Set this channel to zero for and therefore off, don't set this channel.
1253 outputEventInputEventNumbers[channel] = 0xFF; // Off.
1254 configuredChannels--;
1258 if(configuredChannels == 0){
1259 errorID = noChannelsConfiguredToTest;
1260 break;
1263 if(errorID == 0){
1264 // Let the first iteration roll it over to zero.
1265 KeyUserDebugs.currentEvent = 0xFF; // Needs to be here in case of multiple runs, init is not sufficient
1267 if(testMode == TEST_MODE_DODGY_MISSING_TOOTH){
1268 if(testEventsPerCycle <= 127){
1269 testEventsPerCycle *= 2;
1270 }else{
1271 errorID = tooManyEventsPerCycleMissingTth;
1272 break;
1275 // Store the time per event in RPM such that it can be updated dynamically
1276 CoreVars->RPM = testTicksPerEvent;
1278 // The channels to use rely on the defaults from initialisers! Custom builds can break BenchTest mode!
1280 // Un-schedule anything that got scheduled
1281 outputEventInputEventNumbers[2] = 0xFF;
1282 outputEventInputEventNumbers[3] = 0xFF;
1283 outputEventInputEventNumbers[4] = 0xFF;
1284 outputEventInputEventNumbers[5] = 0xFF;
1285 }else if(testMode > TEST_MODE_DODGY_MISSING_TOOTH){
1286 errorID = unimplementedTestMode;
1287 break;
1290 // Trigger decoder interrupt to fire thus starting the loop!
1291 TIE = 0x01; // The ISR does the rest!
1293 // Nothing went wrong, now set flag.
1294 coreStatusA |= BENCH_TEST_ON;
1295 }else{
1296 break;
1300 /* http://issues.freeems.org/view.php?id=155
1302 * The following block has been left in, as I still do not know why it won't work as intended:
1304 * - It should fire all 6 output pins with a 52ms duration pulse, exactly once.
1305 * - 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
1306 * - The interrupts run, but the pin doesn't change state, despite the registers being configured correctly
1308 * I've tried quite a bit:
1310 * - Moving this code around
1311 * - Checking memory definitions
1312 * - Completely rewriting the output ISR
1313 * - Adding significant debug to output ISR
1314 * - Checking for register contents in output ISR
1315 * - Checking for key things modified in this file
1316 * - General head scratching and confused searching
1319 // outputEventPinNumbers[0] = 0; // 1 ign
1320 // outputEventPinNumbers[1] = 1; // 2 ign
1321 // outputEventPinNumbers[2] = 2; // 3 ign/1 fuel
1322 // outputEventPinNumbers[3] = 3; // 4 ign/2 fuel
1323 // outputEventPinNumbers[4] = 4; // 3 fuel
1324 // outputEventPinNumbers[5] = 5; // 4 fuel
1325 // outputEventDelayFinalPeriod[0] = decoderMaxCodeTime;
1326 // outputEventDelayFinalPeriod[1] = decoderMaxCodeTime;
1327 // outputEventDelayFinalPeriod[2] = decoderMaxCodeTime;
1328 // outputEventDelayFinalPeriod[3] = decoderMaxCodeTime;
1329 // outputEventDelayFinalPeriod[4] = decoderMaxCodeTime;
1330 // outputEventDelayFinalPeriod[5] = decoderMaxCodeTime;
1331 // outputEventPulseWidthsMath[0] = SHORTMAX;
1332 // outputEventPulseWidthsMath[1] = SHORTMAX;
1333 // outputEventPulseWidthsMath[2] = SHORTMAX;
1334 // outputEventPulseWidthsMath[3] = SHORTMAX;
1335 // outputEventPulseWidthsMath[4] = SHORTMAX;
1336 // outputEventPulseWidthsMath[5] = SHORTMAX;
1338 // unsigned short edgeTimeStamp = TCNT;
1339 // // call sched output with args
1340 // LongTime timeStamp;
1341 // /* Install the low word */
1342 // timeStamp.timeShorts[1] = edgeTimeStamp;
1343 // /* Find out what our timer value means and put it in the high word */
1344 // if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
1345 // timeStamp.timeShorts[0] = timerExtensionClock + 1;
1346 // }else{
1347 // timeStamp.timeShorts[0] = timerExtensionClock;
1348 // }
1350 // schedulePortTPin(0, timeStamp);
1351 // schedulePortTPin(1, timeStamp);
1352 // schedulePortTPin(2, timeStamp);
1353 // schedulePortTPin(3, timeStamp);
1354 // schedulePortTPin(4, timeStamp);
1355 // schedulePortTPin(5, timeStamp);
1357 // sleep(1000);
1358 }else{
1359 errorID = thisIsNotTheBenchTestDecoder;
1361 break;
1363 default:
1365 if((RXHeaderPayloadID % 2) == 1){
1366 errorID = invalidPayloadID;
1367 }else{
1368 errorID = unrecognisedPayloadID;
1370 break;
1374 // Always reply, if errorID is zero it's just an ack.
1375 finaliseAndSend(errorID);
1377 /* Switch reception back on now that we are done with the received data */
1378 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
1382 /** @brief Send an error if buffer free
1384 * 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.
1386 * @author Fred Cooke
1388 * @warning Use of this function signifies that the error you are trying to propagate is not urgent and can be forgotten.
1390 * @note Consider not throwing an error if it seems appropriate to use this.
1392 * @todo TODO this is only used in coreVarGen, such errors should be caught at init time, NOT runtime, fix that...
1394 * @param errorID is the error ID to be passed out to listening devices.
1396 void sendErrorIfClear(unsigned short errorID){
1397 if(!TXBufferInUseFlags){
1398 TXBufferInUseFlags = ONES;
1399 sendErrorInternal(errorID);
1400 }else{
1401 FLAG_AND_INC_FLAGGABLE(FLAG_COMMS_ERROR_MESSAGES_NOT_SENT_OFFSET);
1406 /* not currently used...
1407 * @brief Send an error even if we must wait
1409 * This is a wrapper for use outside the communication handler function. This
1410 * function will block until the error is able to be sent. This behaviour is
1411 * not recommended as it will interfere with engine operation somewhat.
1413 * @author Fred Cooke
1415 * @warning Use of this function signifies that the error you are trying to propagate is extremely urgent and can not be forgotten.
1417 * @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.
1419 * @param errorID is the error ID to be passed out to listening devices.
1421 //void sendErrorBusyWait(unsigned short errorID){
1422 // while(TXBufferInUseFlags){} /* Wait till clear to send */
1423 // TXBufferInUseFlags = ONES;
1424 // sendErrorInternal(errorID);
1428 /** @brief Send an error
1430 * This function is only for use inside the communication handling function.
1431 * Use of it outside this environment is not supported and behaviour when used
1432 * as such is undefined.
1434 * @author Fred Cooke
1436 * @warning ONLY use this function from within the communication handler.
1438 * @see sendErrorIfClear()
1439 * @see sendErrorBusyWait()
1441 * @todo TODO clean up the mess of commented out crap in here!
1442 * @todo TODO decide on errorCode or errorID and consistencise it everywhere.
1444 * @param errorID is the error ID to be passed out to listening devices.
1446 void sendErrorInternal(unsigned short errorID){
1447 // set buffer in use, consider blocking interrupts to do this cleanly
1450 // TXBufferInUseFlags = 0;
1451 /* No need for atomic block here as one of two conditions will always be */
1452 /* true when calling this. Either we have been flagged to receive and */
1453 /* decode a packet, or we are in an ISR. In either case it is safe to */
1454 /* check the flags and initiate the sequence if they are clear. */
1455 // if(RXTXSerialStateFlags & TX_IN_PROGRESS){
1456 /* It's OK to return without resetting as it will be done by */
1457 /* either of those processes if they are underway. The other */
1458 /* processes are not overridden because they have priority. */
1459 // return;
1460 // }else{ /* Turn off reception */
1461 /* It's OK to turn this off if nothing was currently being received */
1462 // SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
1463 // SCI0CR2 &= SCICR2_RX_DISABLE;
1465 /* Build up the packet */
1466 /* Set the pointer to the start */
1467 TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
1468 /* Set the length */
1469 // TXPacketLengthToSend = 5; /* Flags + Payload ID + Error Code */
1470 // Flags = empty
1471 *TXBufferCurrentPositionHandler = 0x00;
1472 TXBufferCurrentPositionHandler++;
1473 /* Set the payload ID */
1474 *((unsigned short*)TXBufferCurrentPositionHandler) = asyncErrorCodePacket;
1475 TXBufferCurrentPositionHandler += 2;
1477 finaliseAndSend(errorID);
1478 // }
1482 /** @brief Send a debug message if buffer free
1484 * 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.
1486 * @author Fred Cooke
1488 * @note This function exists as a convenience to developers, do not publish code that calls this function.
1490 * @param message is a pointer to the null terminated debug message string.
1492 void sendDebugIfClear(unsigned char* message){
1493 if(!TXBufferInUseFlags){
1494 TXBufferInUseFlags = ONES;
1495 sendDebugInternal(message);
1496 }else{
1497 FLAG_AND_INC_FLAGGABLE(FLAG_COMMS_DEBUG_MESSAGES_NOT_SENT_OFFSET);
1502 /** Send a debug message even if we must wait
1504 * This is a wrapper for use outside the communication handler function. This
1505 * function will block until the debug message is able to be sent.
1507 * @author Fred Cooke
1509 * @note This function exists as a convenience to developers, do not publish code that calls this function.
1511 * @param message is a pointer to the null terminated debug message string.
1513 //void sendDebugBusyWait(unsigned char* message){
1514 // while(TXBufferInUseFlags){} /* Wait till clear to send */
1515 // TXBufferInUseFlags = ONES;
1516 // sendDebugInternal(message);
1520 /** @brief Send a debug message
1522 * Sends a null terminated debug message out on the broadcast address of all available interfaces.
1524 * @author Fred Cooke
1526 * @warning ONLY use this function from within the communication handler.
1528 * @see sendDebugIfClear()
1529 * @see sendDebugBusyWait()
1531 * @note This function exists as a convenience to developers, do not publish code that calls this function.
1533 * @todo TODO clean up the mess of commented out crap in here!
1535 * @param message is a pointer to the null terminated debug message string.
1537 void sendDebugInternal(unsigned char* message){
1539 // set buffer in use, consider blocking interrupts to do this cleanly
1541 // if(TRUE){
1542 // Counters.serialDebugUnsentCounter++;
1543 // return;
1544 // }
1545 // wrong :
1546 /* No need for atomic block here as one of two conditions will always be */
1547 /* true when calling this. Either we have been flagged to receive and */
1548 /* decode a packet, or we are in an ISR. In either case it is safe to */
1549 /* check the flags and initiate the sequence if they are clear. */
1550 //if(RXTXSerialStateFlags & TX_IN_PROGRESS){
1551 // wrong :
1552 /* It's OK to return without resetting as it will be done by */
1553 /* either of those processes if they are underway. The other */
1554 /* processes are not overridden because they have priority. */
1555 //TXBufferInUseFlags = 0;
1556 //return;
1557 // }else{ /* Turn off reception */
1558 /* It's OK to turn this off if nothing was currently being received */
1559 // SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
1560 // SCI0CR2 &= SCICR2_RX_DISABLE;
1562 /* Build up the packet */
1563 /* Set the pointer to the start and init the length */
1564 TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
1566 /* Load a protocol with length header into the TX buffer ready for masking */
1567 *TXBufferCurrentPositionHandler = 0x11;
1568 TXBufferCurrentPositionHandler++;
1570 /* Set the payload ID */
1571 *((unsigned short*)TXBufferCurrentPositionHandler) = asyncDebugInfoPacket;
1572 TXBufferCurrentPositionHandler += 2;
1574 /* Store the length location */
1575 unsigned short* TXLength = (unsigned short*)TXBufferCurrentPositionHandler;
1576 TXBufferCurrentPositionHandler += 2;
1578 /* Copy the string into place and record the length copied */
1579 unsigned short messageLength = stringCopy(TXBufferCurrentPositionHandler, message);
1580 *TXLength = messageLength;
1581 TXBufferCurrentPositionHandler += messageLength;
1583 finaliseAndSend(0);
1588 /* This function should be period limited to about 10 seconds internally (or by scheduler) */
1589 //void checkCountersAndSendErrors(){
1590 // compare time stamps with current time stamps and execute if old enough. (if no scheduler)
1592 // compare counters with counters cache (from last time) sending an error packet when they differ
1594 // copy counters to counters cache for next time
1596 // send errors with busy wait on the basis that all errors should be taken care of and not be sent in fairly short order?
1598 // or send with isr but just busy wait for it to finish before sending the next?
1600 // 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.
1602 // need to figure out how to queue received packets for processing when we are currently sending stuff out.
1604 // above notes don't belong here really.
1608 //void prepareDatalog(){
1609 // // send data log by default otherwise
1610 // unsigned char chunksExpected = 8; // based on configuration, yet to determine how to calculate this number
1611 // unsigned char chunksLoaded = 0;
1612 // if ((!receiving) && (datalogMask & rawVarsMask)) {
1613 // //
1614 // chunksLoaded++;
1615 // }
1616 // if ((!receiving) && (datalogMask & Mask)) {
1617 // //
1618 // chunksLoaded++;
1619 // }
1620 // if ((!receiving) && (datalogMask & Mask)) {
1621 // //
1622 // chunksLoaded++;
1623 // }
1624 // if ((!receiving) && (datalogMask & Mask)) {
1625 // //
1626 // chunksLoaded++;
1627 // }
1628 // if ((!receiving) && (datalogMask & Mask)) {
1629 // //
1630 // chunksLoaded++;
1631 // }
1632 // if ((!receiving) && (datalogMask & Mask)) {
1633 // //
1634 // chunksLoaded++;
1635 // }
1636 // if ((!receiving) && (datalogMask & Mask)) {
1637 // //
1638 // chunksLoaded++;
1639 // }
1640 // if ((!receiving) && (datalogMask & Mask)) {
1641 // //
1642 // chunksLoaded++;
1643 // }
1644 // // set the length
1645 // // the pointer should be correct already