Refactor the Makefile into pieces. This commit inspired by AncientGeek, my hatred...
[freeems-vanilla.git] / src / main / commsCore.c
blob5685f8f35803c930532a1164c8653c458236ec58
1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2014 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
43 #define COMMSCORE_C
44 #include "inc/freeEMS.h"
45 #include "inc/flashWrite.h"
46 #include "inc/interrupts.h"
47 #include "inc/utils.h"
48 #include "inc/tableLookup.h"
49 #include "inc/locationIDs.h"
50 #include "inc/blockDetailsLookup.h"
51 #include "inc/decoderInterface.h"
52 #include "inc/commsCore.h"
53 #include "inc/init.h"
54 #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
55 #include "decoders/inc/BenchTest.h"
58 /** @brief Populate a basic datalog packet
60 * Copies various chunks of data to the transmission buffer and truncates to
61 * the configured length. If changing this, update the maxBasicDatalogLength.
63 unsigned short populateBasicDatalog(){
64 /// @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.
66 KeyUserDebugs.coreStatusA = coreStatusA;
67 KeyUserDebugs.clockIn8thsOfAMilli = Clocks.realTimeClockMain;
68 KeyUserDebugs.clockInMilliSeconds = Clocks.realTimeClockMillis;
70 unsigned short confSize = 0;
71 unsigned char chunkLimit = TablesB.SmallTablesB.loggingSettings.firstChunk + TablesB.SmallTablesB.loggingSettings.numberOfChunks;
72 unsigned char chunks;
73 for(chunks=TablesB.SmallTablesB.loggingSettings.firstChunk;chunks<chunkLimit;chunks++){
74 unsigned short localSize = TablesB.SmallTablesB.loggingSettings.logChunks[chunks].size;
75 confSize += localSize;
76 if(confSize > 2048){
77 confSize -= localSize;
78 break;
80 memcpy(TXBufferCurrentPositionHandler, TablesB.SmallTablesB.loggingSettings.logChunks[chunks].address, localSize);
81 TXBufferCurrentPositionHandler += localSize;
83 // After copying data, otherwise tempClock is NEVER zero and reset detection does NOT work
84 KeyUserDebugs.tempClock++;
85 return confSize;
89 // All of these require some range checking, eg only some registers, and all RAM, not flash, not other regs
90 // TODO pointer for one byte
91 // TODO pointer for one short
92 // TODO function to log generic memory region by location and size ? requires length!
93 // Ranges are :
94 // RAM window
95 // bss/data region
96 // IO registers etc that can't be altered simply by reading from.
97 // NOT :
98 // flash makes no sense
99 // some regs are sensitive
100 // some RAM is unused
101 // serial buffers make no sense
102 // eeprom makes no sense
104 // 2k of regs max - user beware for now
105 // 12k of RAM max
107 //init :
108 //logaddr = fixed.addr
109 //loglen = fixed.len
111 //len = loglen OR 1 OR 2
113 //check :
114 //if((addr < 0x0800) && (length < (0x0800 - addr))){
115 // // reg space is OK
116 //}else if(((0x1000 < addr) && (addr < 0x4000)) && (length < (0x4000 - addr))){
117 // // RAM space is OK
118 //}else{
119 // // send an error instead
122 //run check at init and set time, not run time or just not check?? maybe its silly to check at all
124 // /* Just dump the ADC channels as fast as possible */
125 //void populateScopeLogADCAll(){
126 // sampleBlockADC(TXBufferCurrentPositionHandler);
127 // TXBufferCurrentPositionHandler += sizeof(ADCBuffer);
131 // 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.
134 /** @brief Finalise a packet and send it
136 * This functions job is to finalise the main loop part of the packet sending
137 * process. It configures the pos/neg ack header bit, adds the code if neg,
138 * runs a checksum over the packet data and tags it to the end before
139 * configuring the various ISRs that need to send the data out.
141 void finaliseAndSend(unsigned short errorID){
143 if(errorID != 0){
144 *TXHeaderFlags |= HEADER_IS_NACK;
145 *((unsigned short*)TXBufferCurrentPositionHandler) = errorID;
146 TXBufferCurrentPositionHandler += 2;
149 /* Tag the checksum on the end */
150 *TXBufferCurrentPositionHandler = checksum((unsigned char*)&TXBuffer, ((unsigned short)TXBufferCurrentPositionHandler - (unsigned short)&TXBuffer));
152 /* Send it out on all the channels required. */
154 /* SCI0 - Main serial interface */
155 if(TXBufferInUseFlags & COM_SET_SCI0_INTERFACE_ID){
156 /* Initiate transmission */
157 SCI0DRL = START_BYTE;
159 /* Note : Order Is Important! */
160 /* TX empty flag is already set, so we must clear it by writing out before enabling the interrupt */
161 SCI0CR2 |= (SCICR2_TX_ENABLE | SCICR2_TX_ISR_ENABLE);
163 /* CAN0 - Main CAN interface */
164 if(TXBufferInUseFlags & COM_SET_CAN0_INTERFACE_ID){
165 // just clear up front for now
166 TXBufferInUseFlags &= COM_CLEAR_CAN0_INTERFACE_ID;
168 /* spare2 */
169 if(TXBufferInUseFlags & COM_SET_SPARE2_INTERFACE_ID){
170 // just clear up front for now
171 TXBufferInUseFlags &= COM_CLEAR_SPARE2_INTERFACE_ID;
173 /* spare3 */
174 if(TXBufferInUseFlags & COM_SET_SPARE3_INTERFACE_ID){
175 // just clear up front for now
176 TXBufferInUseFlags &= COM_CLEAR_SPARE3_INTERFACE_ID;
178 /* spare4 */
179 if(TXBufferInUseFlags & COM_SET_SPARE4_INTERFACE_ID){
180 // just clear up front for now
181 TXBufferInUseFlags &= COM_CLEAR_SPARE4_INTERFACE_ID;
183 /* spare5 */
184 if(TXBufferInUseFlags & COM_SET_SPARE5_INTERFACE_ID){
185 // just clear up front for now
186 TXBufferInUseFlags &= COM_CLEAR_SPARE5_INTERFACE_ID;
188 /* spare6 */
189 if(TXBufferInUseFlags & COM_SET_SPARE6_INTERFACE_ID){
190 // just clear up front for now
191 TXBufferInUseFlags &= COM_CLEAR_SPARE6_INTERFACE_ID;
193 /* spare7 */
194 if(TXBufferInUseFlags & COM_SET_SPARE7_INTERFACE_ID){
195 // just clear up front for now
196 TXBufferInUseFlags &= COM_CLEAR_SPARE7_INTERFACE_ID;
201 /** @brief Decode a packet and respond
203 * This is the core function that controls which functionality is run when a
204 * packet is received in full by the ISR code and control is passed back to the
205 * main loop code. The vast majority of communications action happens here.
207 void decodePacketAndRespond(){
208 /* Extract and build up the header fields */
209 TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;
211 /* Initialised here such that override is possible */
212 TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
213 TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;
215 // How big was the packet that we got back
216 unsigned short RXPacketLengthReceived = (unsigned short)RXBufferCurrentPosition - (unsigned short)&RXBuffer;
218 /* Check that the packet is big enough for header,ID,checksum */
219 if(RXPacketLengthReceived < 4){
220 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
221 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_UNDER_LENGTH_OFFSET);
222 KeyUserDebugs.serialAndCommsCodeErrors++;
223 return;
226 /* Pull out the received checksum and calculate the real one, then check */
227 unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1);
228 unsigned char RXCalculatedChecksum = checksum((unsigned char*)&RXBuffer, RXPacketLengthReceived - 1);
229 if(RXCalculatedChecksum != RXReceivedChecksum){
230 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
231 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_CHECKSUM_MISMATCHES_OFFSET);
232 KeyUserDebugs.serialAndCommsCodeErrors++;
233 return;
236 /* Start this off as full packet length and build down to the actual length */
237 RXCalculatedPayloadLength = RXPacketLengthReceived;
239 /* Grab the RX header flags out of the RX buffer */
240 RXBufferCurrentPosition = (unsigned char*)&RXBuffer;
241 RXHeaderFlags = *RXBufferCurrentPosition;
242 RXBufferCurrentPosition++;
243 RXCalculatedPayloadLength--;
245 /* Flag that we are transmitting! */
246 TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
247 // SCI0 only for now...
249 /* Load a blank header into the TX buffer ready for masking */
250 TXHeaderFlags = TXBufferCurrentPositionHandler;
251 *TXHeaderFlags = 0;
252 TXBufferCurrentPositionHandler++;
254 /* Grab the payload ID for processing and load the return ID */
255 RXHeaderPayloadID = *((unsigned short*)RXBufferCurrentPosition);
256 *((unsigned short*)TXBufferCurrentPositionHandler) = RXHeaderPayloadID + 1;
257 RXBufferCurrentPosition += 2;
258 TXBufferCurrentPositionHandler += 2;
259 RXCalculatedPayloadLength -= 2;
261 /* Check that the length is sufficient for the fields configured. Packets
262 * that are too long will be caught and rejected on an individual payload
263 * ID basis as the information required to handle that is not available at
264 * this point. Packets that are too short are rejected immediately!
266 if(((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 7))
267 || ((RXHeaderFlags & HEADER_HAS_LENGTH) && (RXPacketLengthReceived < 6))
268 || ((RXHeaderFlags & HEADER_HAS_SEQUENCE) && (RXPacketLengthReceived < 5))){
269 finaliseAndSend(packetTooShortForSpecifiedFields);
270 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
271 return;
274 /* Subtract checksum to get final length */
275 RXCalculatedPayloadLength--;
277 if(RXHeaderFlags & HEADER_HAS_SEQUENCE){
278 *TXBufferCurrentPositionHandler = *RXBufferCurrentPosition;
279 RXBufferCurrentPosition++;
280 TXBufferCurrentPositionHandler++;
281 RXCalculatedPayloadLength--;
282 *TXHeaderFlags |= HEADER_HAS_SEQUENCE;
285 if(RXHeaderFlags & HEADER_HAS_LENGTH){
286 RXHeaderPayloadLength = *((unsigned short*)RXBufferCurrentPosition);
287 RXBufferCurrentPosition += 2;
288 RXCalculatedPayloadLength -= 2;
289 /* Already subtracted one for checksum */
290 if(RXHeaderPayloadLength != RXCalculatedPayloadLength){
291 finaliseAndSend(payloadLengthHeaderMismatch);
292 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
293 return;
297 /* Calculate the position of the end of the stored packet for later use as a buffer */
298 void* leftOverBuffer = (void*)((unsigned short)&RXBuffer + RXPacketLengthReceived);
300 unsigned short errorID = 0;
301 /* This is where all the communication logic resides.
303 * Please Note: Length and its flag should be set by each return packet
304 * type handler if required or desired. If an ack has been requested,
305 * ensure the negative ack flag is set if the operation failed.
307 switch (RXHeaderPayloadID){
308 // FreeEMS Core Comms Interface cases
309 case requestInterfaceVersion:
311 if(RXCalculatedPayloadLength != 0){
312 errorID = payloadLengthTypeMismatch;
313 break;
316 /* This type must have a length field, set that up */
317 *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(interfaceVersion);
318 *TXHeaderFlags |= HEADER_HAS_LENGTH;
319 TXBufferCurrentPositionHandler += 2;
320 /* Load the body into place */
321 memcpy((void*)TXBufferCurrentPositionHandler, (void*)&interfaceVersion, sizeof(interfaceVersion));
322 TXBufferCurrentPositionHandler += sizeof(interfaceVersion);
323 break;
325 case requestFirmwareVersion:
327 if(RXCalculatedPayloadLength != 0){
328 errorID = payloadLengthTypeMismatch;
329 break;
331 /* This type must have a length field, set that up */
332 *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(firmwareVersion);
333 *TXHeaderFlags |= HEADER_HAS_LENGTH;
334 TXBufferCurrentPositionHandler += 2;
335 /* Load the body into place */
336 memcpy((void*)TXBufferCurrentPositionHandler, (void*)&firmwareVersion, sizeof(firmwareVersion));
337 TXBufferCurrentPositionHandler += sizeof(firmwareVersion);
338 break;
340 case requestMaxPacketSize:
342 if(RXCalculatedPayloadLength != 0){
343 errorID = payloadLengthTypeMismatch;
344 break;
346 /* Load the size into place */
347 *((unsigned short*)TXBufferCurrentPositionHandler) = RX_BUFFER_SIZE;
348 TXBufferCurrentPositionHandler += 2;
349 break;
351 case requestEchoPacketReturn:
353 /* This type must have a length field, set that up */
354 *((unsigned short*)TXBufferCurrentPositionHandler) = RXPacketLengthReceived;
355 *TXHeaderFlags |= HEADER_HAS_LENGTH;
356 TXBufferCurrentPositionHandler += 2;
357 /* Load the body into place */
358 memcpy((void*)TXBufferCurrentPositionHandler, (void*)&RXBuffer, RXPacketLengthReceived);
359 /* Note, there is no overflow check here because the TX buffer is slightly */
360 /* bigger than the RX buffer and there is overflow checking for receives anyway. */
361 TXBufferCurrentPositionHandler += RXPacketLengthReceived;
362 break;
364 case requestSoftSystemReset:
366 if(RXCalculatedPayloadLength != 0){
367 errorID = payloadLengthTypeMismatch;
368 }else{ // Perform soft system reset
369 _start();
371 break;
373 case requestHardSystemReset:
375 if(RXCalculatedPayloadLength != 0){
376 errorID = payloadLengthTypeMismatch;
377 }else{
378 /* This is how the serial monitor does it. */
379 COPCTL = 0x01; /* Arm with shortest time */
380 ARMCOP = 0xFF; /* Write bad value, should cause immediate reset */
381 /* Using _start() only resets the app ignoring the monitor switch. It does not work */
382 /* properly because the location of _start is not the master reset vector location. */
384 break;
386 case requestReInitOfSystem:
388 if(RXCalculatedPayloadLength != 0){
389 errorID = payloadLengthTypeMismatch;
390 }else{
391 init();
393 break;
395 // FreeEMS Vanilla Firmware Specific cases
396 case clearCountersAndFlagsToZero:
398 if(RXCalculatedPayloadLength != 0){
399 errorID = payloadLengthTypeMismatch;
400 break;
403 unsigned short zeroCounter;
404 unsigned char* counterPointer;
406 counterPointer = (unsigned char*) &Counters;
407 for(zeroCounter = 0;zeroCounter < sizeof(Counter);zeroCounter++){
408 *counterPointer = 0;
409 counterPointer++;
412 KeyUserDebugs.flaggableFlags = 0;
413 counterPointer = (unsigned char*) &Flaggables;
414 for(zeroCounter = 0;zeroCounter < sizeof(Flaggable);zeroCounter++){
415 *counterPointer = 0;
416 counterPointer++;
419 KeyUserDebugs.flaggableFlags2 = 0;
420 counterPointer = (unsigned char*) &Flaggables2;
421 for(zeroCounter = 0;zeroCounter < sizeof(Flaggable2);zeroCounter++){
422 *counterPointer = 0;
423 counterPointer++;
425 break;
427 case requestBuiltByName:
428 case requestSupportEmail:
429 case requestDecoderName:
430 case requestFirmwareBuildDate:
431 case requestCompilerVersion:
432 case requestOperatingSystem:
434 if(RXCalculatedPayloadLength != 0){
435 errorID = payloadLengthTypeMismatch;
436 break;
439 unsigned char* stringToSend = 0;
440 switch (RXHeaderPayloadID) {
441 case requestBuiltByName:
442 stringToSend = (unsigned char*)builtByName;
443 break;
444 case requestSupportEmail:
445 stringToSend = (unsigned char*)supportEmail;
446 break;
447 case requestDecoderName:
448 stringToSend = (unsigned char*)decoderName;
449 break;
450 case requestFirmwareBuildDate:
451 stringToSend = (unsigned char*)buildTimeAndDate;
452 break;
453 case requestCompilerVersion:
454 stringToSend = (unsigned char*)compilerVersion;
455 break;
456 case requestOperatingSystem:
457 stringToSend = (unsigned char*)operatingSystem;
458 break;
460 /* This type must have a length field, set that up and load the body into place at the same time */
461 *((unsigned short*)TXBufferCurrentPositionHandler) = stringCopy((TXBufferCurrentPositionHandler + 2), stringToSend);
462 *TXHeaderFlags |= HEADER_HAS_LENGTH;
463 // Update with length field and string length.
464 TXBufferCurrentPositionHandler += 2 + *((unsigned short*)TXBufferCurrentPositionHandler);
465 break;
467 case updateBlockInRAM:
469 // Subtract six to allow for the locationID, size, offset
470 if(RXCalculatedPayloadLength < 7){
471 errorID = payloadLengthTypeMismatch;
472 break;
475 // Extract the RAM location ID
476 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
477 RXBufferCurrentPosition += 2;
479 // Extract the offset to place the data at
480 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
481 RXBufferCurrentPosition += 2;
483 // Extract the size of the data to be stored
484 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
485 RXBufferCurrentPosition += 2;
487 // Look up the memory location details
488 blockDetails details;
489 lookupBlockDetails(locationID, &details);
491 // Don't let anyone write to running variables unless we are running BenchTest firmware!
492 if((details.flags & block_is_read_only) && compare((unsigned char*)&decoderName, (unsigned char*)BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME))){
493 errorID = attemptToWriteToReadOnlyBlock;
494 break;
497 // Subtract six to allow for the locationID, size, offset
498 if((RXCalculatedPayloadLength - 6) != size){
499 errorID = payloadNotEqualToSpecifiedValue;
500 break;
503 // If either of these is zero then this block is not in RAM!
504 if((details.RAMPage == 0) || (details.RAMAddress == 0)){
505 errorID = invalidMemoryActionForID;
506 break;
509 // Check that size and offset describe a region that is not out of bounds
510 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
511 errorID = invalidSizeOffsetCombination;
512 break;
515 // Don't allow sub region manipulation where it does not make sense or is unsafe.
516 if((size != details.size) && !(details.flags & block_is_indexable)){
517 errorID = uncheckedTableManipulationNotAllowed;
518 break;
521 // Save page values for restore
522 unsigned char oldRamPage = RPAGE;
523 // Set the viewable RAM page
524 RPAGE = details.RAMPage;
526 /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
528 if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
529 void* bufferToCheck;
531 // For sub regions, construct an image for verification
532 if(size != details.size){
533 // Copy data from destination location to buffer
534 memcpy(leftOverBuffer, details.RAMAddress, details.size);
536 // Copy data from rx buffer to buffer over writing old data
537 memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
539 bufferToCheck = leftOverBuffer;
540 }else{
541 bufferToCheck = RXBufferCurrentPosition;
544 // Verify all tables
545 if(details.flags & block_is_main_table){
546 errorID = validateMainTable((mainTable*)bufferToCheck);
547 }else if(details.flags & block_is_2dus_table){
548 errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
549 }// TODO add other table types here
551 // If the validation failed, report it
552 if(errorID != 0){
553 RPAGE = oldRamPage; // Restore the original RAM page, even when getting an error condition.
554 break;
558 // Copy from the RX buffer to the block of RAM
559 memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
561 // Check that the write was successful
562 unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
564 // Restore the original RAM and flash pages
565 RPAGE = oldRamPage;
567 if(index != 0){
568 errorID = MEMORY_WRITE_ERROR;
570 break;
572 case updateBlockInFlash:
574 // Subtract six to allow for the locationID, size, offset
575 if(RXCalculatedPayloadLength < 7){
576 errorID = payloadLengthTypeMismatch;
577 break;
580 // Extract the RAM location ID
581 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
582 RXBufferCurrentPosition += 2;
584 // Extract the offset to place the data at
585 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
586 RXBufferCurrentPosition += 2;
588 // Extract the size of the data to be stored
589 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
590 RXBufferCurrentPosition += 2;
592 // Look up the memory location details
593 blockDetails details;
594 lookupBlockDetails(locationID, &details);
596 // Subtract six to allow for the locationID, size, offset
597 if((RXCalculatedPayloadLength - 6) != size){
598 errorID = payloadNotEqualToSpecifiedValue;
599 break;
602 // If either of these is zero then this block is not in flash!
603 if((details.FlashPage == 0) || (details.FlashAddress == 0)){
604 errorID = invalidMemoryActionForID;
605 break;
608 // Check that size and offset describe a region that is not out of bounds
609 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
610 errorID = invalidSizeOffsetCombination;
611 break;
614 // Don't allow sub region manipulation where it does not make sense or is unsafe.
615 if((size != details.size) && !(details.flags & block_is_indexable)){
616 errorID = uncheckedTableManipulationNotAllowed;
617 break;
620 /// TODO @todo factor this out into validation delegation function once the number of types increases somewhat
622 if((details.flags & block_is_main_table) || (details.flags & block_is_2dus_table)){
623 void* bufferToCheck;
625 // For sub regions, construct an image for verification
626 if(size != details.size){
627 /* Save page value for restore and set the visible page */
628 unsigned char oldFlashPage = PPAGE;
629 PPAGE = details.FlashPage;
631 // Copy data from destination location to buffer
632 memcpy(leftOverBuffer, details.FlashAddress, details.size);
634 /* Restore the original flash page */
635 PPAGE = oldFlashPage;
637 // Copy data from rx buffer to buffer over writing old data
638 memcpy(leftOverBuffer + offset, RXBufferCurrentPosition, size);
640 bufferToCheck = leftOverBuffer;
641 }else{
642 bufferToCheck = RXBufferCurrentPosition;
645 // Verify all tables
646 if(details.flags & block_is_main_table){
647 errorID = validateMainTable((mainTable*)bufferToCheck);
648 }else if(details.flags & block_is_2dus_table){
649 errorID = validateTwoDTable((twoDTableUS*)bufferToCheck);
650 }// TODO add other table types here
652 // If the validation failed, report it
653 if(errorID != 0){
654 break;
658 /* Copy the flash details and populate the RAM details with the buffer location */
659 blockDetails burnDetails;
660 burnDetails.FlashPage = details.FlashPage;
661 burnDetails.FlashAddress = details.FlashAddress + offset;
662 burnDetails.RAMPage = RPAGE;
663 burnDetails.RAMAddress = RXBufferCurrentPosition;
664 burnDetails.size = size;
666 /* Copy from the RX buffer to the block of flash */
667 errorID = writeBlock(&burnDetails, leftOverBuffer);
668 if(errorID != 0){
669 break;
672 /* If present in RAM, update that too */
673 if((details.RAMPage != 0) && (details.RAMAddress != 0)){
674 /* Save page values for restore */
675 unsigned char oldRamPage = RPAGE;
676 /* Set the viewable RAM page */
677 RPAGE = details.RAMPage;
679 /* Copy from the RX buffer to the block of RAM */
680 memcpy((unsigned char*)(details.RAMAddress + offset), RXBufferCurrentPosition, size);
682 /* Check that the write was successful */
683 unsigned char index = compare(RXBufferCurrentPosition, (unsigned char*)(details.RAMAddress + offset), size);
685 /* Restore the original RAM and flash pages */
686 RPAGE = oldRamPage;
688 if(index != 0){
689 errorID = MEMORY_WRITE_ERROR;
693 break;
695 case retrieveBlockFromRAM:
697 if(RXCalculatedPayloadLength != 6){
698 errorID = payloadLengthTypeMismatch;
699 break;
702 // Extract the RAM location ID
703 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
704 RXBufferCurrentPosition += 2;
706 // Extract the offset to place the data at
707 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
708 RXBufferCurrentPosition += 2;
710 // Extract the size of the data to be stored
711 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
712 RXBufferCurrentPosition += 2;
714 /* Look up the memory location details */
715 blockDetails details;
716 lookupBlockDetails(locationID, &details);
718 if((details.RAMPage == 0) || (details.RAMAddress == 0)){
719 errorID = invalidMemoryActionForID;
720 break;
723 // Special behaviour for size of zero which returns the whole block
724 if((size == 0) && (offset == 0)){
725 size = details.size;
728 // Check that size and offset describe a region that is not out of bounds
729 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
730 errorID = invalidSizeOffsetCombination;
731 break;
734 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
735 if((size != details.size) && !(details.flags & block_is_indexable)){
736 errorID = doesNotMakeSenseToRetrievePartially;
737 break;
740 // This type must have a length field, set that up
741 *((unsigned short*)TXBufferCurrentPositionHandler) = size;
742 *TXHeaderFlags |= HEADER_HAS_LENGTH;
743 TXBufferCurrentPositionHandler += 2;
745 /* Save page value for restore and set the visible page */
746 unsigned char oldRamPage = RPAGE;
747 RPAGE = details.RAMPage;
749 /* Copy the block of RAM to the TX buffer */
750 memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.RAMAddress + offset), size);
751 TXBufferCurrentPositionHandler += size;
753 /* Restore the original RAM and flash pages */
754 RPAGE = oldRamPage;
756 break;
758 case retrieveBlockFromFlash:
760 if(RXCalculatedPayloadLength != 6){
761 errorID = payloadLengthTypeMismatch;
762 break;
765 // Extract the RAM location ID
766 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
767 RXBufferCurrentPosition += 2;
769 // Extract the offset to place the data at
770 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
771 RXBufferCurrentPosition += 2;
773 // Extract the size of the data to be stored
774 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
775 RXBufferCurrentPosition += 2;
777 /* Look up the memory location details */
778 blockDetails details;
779 lookupBlockDetails(locationID, &details);
781 if((details.FlashPage == 0) || (details.FlashAddress == 0)){
782 errorID = invalidMemoryActionForID;
783 break;
786 // Special behaviour for size of zero which returns the whole block
787 if((size == 0) && (offset == 0)){
788 size = details.size;
791 // Check that size and offset describe a region that is not out of bounds
792 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
793 errorID = invalidSizeOffsetCombination;
794 break;
797 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
798 if((size != details.size) && !(details.flags & block_is_indexable)){
799 errorID = doesNotMakeSenseToRetrievePartially;
800 break;
803 // This type must have a length field, set that up
804 *((unsigned short*)TXBufferCurrentPositionHandler) = size;
805 *TXHeaderFlags |= HEADER_HAS_LENGTH;
806 TXBufferCurrentPositionHandler += 2;
808 /* Save page value for restore and set the visible page */
809 unsigned char oldFlashPage = PPAGE;
810 PPAGE = details.FlashPage;
812 /* Copy the block of flash to the TX buffer */
813 memcpy(TXBufferCurrentPositionHandler, (unsigned char*)(details.FlashAddress + offset), size);
814 TXBufferCurrentPositionHandler += size;
816 /* Restore the original RAM and flash pages */
817 PPAGE = oldFlashPage;
819 break;
821 case burnBlockFromRamToFlash:
823 if(RXCalculatedPayloadLength != 6){
824 errorID = payloadLengthTypeMismatch;
825 break;
828 // Extract the RAM location ID
829 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
830 RXBufferCurrentPosition += 2;
832 // Extract the offset to place the data at
833 unsigned short offset = *((unsigned short*)RXBufferCurrentPosition);
834 RXBufferCurrentPosition += 2;
836 // Extract the size of the data to be stored
837 unsigned short size = *((unsigned short*)RXBufferCurrentPosition);
838 RXBufferCurrentPosition += 2;
840 /* Look up the memory location details */
841 blockDetails details;
842 lookupBlockDetails(locationID, &details);
844 /* Check that all data we need is present */
845 if((details.RAMPage == 0) || (details.RAMAddress == 0) || (details.FlashPage == 0) || (details.FlashAddress == 0)){
846 errorID = invalidMemoryActionForID;
847 break;
850 // Special behaviour for size of zero which burns the whole block
851 if((size == 0) && (offset == 0)){
852 size = details.size;
855 // Check that size and offset describe a region that is not out of bounds
856 if((size == 0) || (offset > (details.size - 1)) || (size > (details.size - offset))){
857 errorID = invalidSizeOffsetCombination;
858 break;
861 // Don't allow sub region retrieval where it does not make sense or is unsafe. (keep it symmetric for djandruczyk)
862 if((size != details.size) && !(details.flags & block_is_indexable)){
863 errorID = doesNotMakeSenseToRetrievePartially;
864 break;
868 // adjust details block to feed to represent the subsection of ram and flash that we want to burn down.
869 details.RAMAddress += offset;
870 details.FlashAddress += offset;
871 details.size = size;
873 /* Write the block down from RAM to Flash */
874 errorID = writeBlock(&details, leftOverBuffer);
875 break;
877 case requestDatalogPacket: // Set type through standard configuration methods
879 if(RXCalculatedPayloadLength != 0){
880 errorID = payloadLengthTypeMismatch;
881 break;
884 /* Set the length field up */
885 *TXHeaderFlags |= HEADER_HAS_LENGTH;
886 unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
887 TXBufferCurrentPositionHandler += 2;
889 /* Fill out the log and send */
890 *localLength = populateBasicDatalog();
891 break;
893 case setAsyncDatalogType:
895 if(RXCalculatedPayloadLength != 1){
896 errorID = payloadLengthTypeMismatch;
897 break;
900 unsigned char newDatalogType = *((unsigned char*)RXBufferCurrentPosition);
901 if(newDatalogType > asyncDatalogLastType){
902 errorID = noSuchAsyncDatalogType;
903 break;
906 TablesB.SmallTablesB.loggingSettings.datalogStreamType = newDatalogType;
907 break;
909 case retrieveArbitraryMemory:
911 if(RXCalculatedPayloadLength != 6){
912 errorID = payloadLengthTypeMismatch;
913 break;
916 unsigned short length = *((unsigned short*)RXBufferCurrentPosition);
917 RXBufferCurrentPosition += 2;
918 // Make sure the buffer can handle the block
919 if(length > TX_MAX_PAYLOAD_SIZE){
920 errorID = requestedLengthTooLarge;
921 break;
924 void* address = (void*) *((unsigned short*)RXBufferCurrentPosition);
925 RXBufferCurrentPosition += 2;
926 // Ensure we don't try to read past the end of the address space
927 if(((unsigned short)address) <= ((0xFFFF - length) + 1)){
928 // TODO Possibly check and limit ranges
929 errorID = requestedAddressDisallowed;
930 break;
933 unsigned char RAMPage = *((unsigned char*)RXBufferCurrentPosition);
934 RXBufferCurrentPosition++;
935 // Ensure RAM page is valid. Being too high is not possible.
936 if(RAMPage < RPAGE_MIN){
937 errorID = requestedRAMPageInvalid;
938 break;
941 unsigned char FlashPage = *((unsigned char*)RXBufferCurrentPosition);
942 RXBufferCurrentPosition++;
943 // Ensure Flash page is valid. Being too high is not possible.
944 if(FlashPage < PPAGE_MIN){
945 errorID = requestedFlashPageInvalid;
946 break;
949 /* This type must have a length field, set that up */
950 *((unsigned short*)TXBufferCurrentPositionHandler) = length + 6;
951 *TXHeaderFlags |= HEADER_HAS_LENGTH;
952 TXBufferCurrentPositionHandler += 2;
954 /* Put the request payload into the reply */
955 *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short) address;
956 TXBufferCurrentPositionHandler += 2;
957 *((unsigned short*)TXBufferCurrentPositionHandler) = length;
958 TXBufferCurrentPositionHandler += 2;
959 *((unsigned char*)TXBufferCurrentPositionHandler) = RAMPage;
960 TXBufferCurrentPositionHandler++;
961 *((unsigned char*)TXBufferCurrentPositionHandler) = FlashPage;
962 TXBufferCurrentPositionHandler++;
964 /* Load the body into place */
965 memcpy((void*)TXBufferCurrentPositionHandler, address, length);
966 TXBufferCurrentPositionHandler += length;
968 break;
970 case retrieveListOfLocationIDs:
972 if(RXCalculatedPayloadLength != 3){
973 errorID = payloadLengthTypeMismatch;
974 break;
977 // Extract the type of list that we want
978 unsigned char listType = *((unsigned char*)RXBufferCurrentPosition);
979 RXBufferCurrentPosition++;
981 // Extract the mask for the qualities that we want
982 unsigned short blockDetailsMask = *((unsigned short*)RXBufferCurrentPosition);
983 RXBufferCurrentPosition += 2;
985 // This type must have a length field, set that up
986 unsigned short * listLength = (unsigned short*)TXBufferCurrentPositionHandler;
987 *TXHeaderFlags |= HEADER_HAS_LENGTH;
988 TXBufferCurrentPositionHandler += 2;
990 // Zero the counter before we start, woops!
991 *listLength = 0;
993 unsigned long locationID;
994 blockDetails details;
995 for(locationID = 0;locationID < 65536;locationID++){
996 unsigned short locationIDDoesntExist;
997 locationIDDoesntExist = lookupBlockDetails((unsigned short)locationID, &details);
999 if(!locationIDDoesntExist){
1000 if((listType == 0x00) || // get all
1001 ((listType == 0x01) && (details.flags & blockDetailsMask)) || // get OR of bits
1002 ((listType == 0x02) && (!(~(details.flags) & blockDetailsMask)))){ // get AND of bits
1003 *((unsigned short*)TXBufferCurrentPositionHandler) = (unsigned short)locationID;
1004 TXBufferCurrentPositionHandler += 2;
1005 *listLength += 2;
1010 break;
1012 case retrieveLocationIDDetails:
1014 if(RXCalculatedPayloadLength != 2){
1015 errorID = payloadLengthTypeMismatch;
1016 break;
1019 // Extract the RAM location ID
1020 unsigned short locationID = *((unsigned short*)RXBufferCurrentPosition);
1021 RXBufferCurrentPosition += 2;
1023 // This type must have a length field, set that up
1024 *((unsigned short*)TXBufferCurrentPositionHandler) = sizeof(blockDetails);
1025 *TXHeaderFlags |= HEADER_HAS_LENGTH;
1026 TXBufferCurrentPositionHandler += 2;
1028 // Write straight to output buffer to save time/code
1029 errorID = lookupBlockDetails(locationID, (blockDetails*)TXBufferCurrentPositionHandler);
1031 if(errorID != 0){
1032 break;
1035 // Adjust TX buffer position if successful
1036 TXBufferCurrentPositionHandler += sizeof(blockDetails);
1038 break;
1040 case requestUnitTestOverSerial:
1043 * The idea here is to call this function with arguments, and data
1044 * and have the result sent back for comparison with an expected
1045 * result that isn't divulged to the firmware.
1047 * It is intended that all testable functions be callable through
1048 * this mechanism and that any number of test executions can be
1049 * performed by an external suite using different parameters and
1050 * data sets and matching expected results.
1052 * The usual error mechanism shall be used to indicate some sort of
1053 * either internal or test failure and returned errors shall be
1054 * suitably descriptive to allow diagnosis and fixing of issues.
1057 // Must at least have test ID
1058 if(RXCalculatedPayloadLength < 2){
1059 errorID = payloadLengthTypeMismatch;
1060 break;
1063 // grab unit test ID from payload
1064 unsigned short unitTestID = *((unsigned short*)RXBufferCurrentPosition);
1065 RXBufferCurrentPosition += 2;
1067 switch(unitTestID){
1068 case testEmptyTest:
1070 // Must be only the ID
1071 if(RXCalculatedPayloadLength != 2){
1072 errorID = payloadShorterThanRequiredForTest;
1073 break;
1076 *((unsigned short*)TXBufferCurrentPositionHandler) = unitTestID;
1077 TXBufferCurrentPositionHandler +=2;
1079 break;
1081 case testTwoDTableUSLookup:
1083 // ID + Value + Table
1084 if(RXCalculatedPayloadLength != (2 + 2 + sizeof(twoDTableUS))){
1085 errorID = payloadShorterThanRequiredForTest;
1086 break;
1089 unsigned short Value = *((unsigned short*)RXBufferCurrentPosition);
1090 RXBufferCurrentPosition += 2;
1092 twoDTableUS* Table = ((twoDTableUS*)RXBufferCurrentPosition);
1093 RXBufferCurrentPosition += sizeof(twoDTableUS);
1095 unsigned short result = lookupTwoDTableUS(Table, Value);
1097 *((unsigned short*)TXBufferCurrentPositionHandler) = result;
1098 TXBufferCurrentPositionHandler +=2;
1100 break;
1102 // http://issues.freeems.org/view.php?id=156
1104 /// TODO @todo test all things listed below:
1105 // lookupPagedMainTableCellValue - pass this RPAGE so that it remains unchanged
1106 // validateMainTable
1107 // validateTwoDTable
1108 // set table values - leave this till last, currently unused by mtx, likely to be removed anyway
1109 // generateDerivedVars - convert to pointers, remove headers, privatise a lot of data!
1110 // calculateFuelAndIgnition - ditto
1111 // scheduling algorithm - ditto
1112 // safeAdd
1113 // safeTrim
1114 // safeScale
1115 // sleep (milliseconds)
1116 // sleepMicro (microseconds)
1117 // checksum
1118 // stringCopy
1119 // compare
1120 // 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)
1121 // init code may be able to be partially checked
1122 // most other code at this stage is ISR code, flash writing code, or could interfere with the running of the engine
1123 // more testable code will appear with time, such as the HAL layer, and most accessory functions.
1124 default:
1126 errorID = noSuchUnitTestID;
1129 // each case:
1130 // checks length, fails if wrong
1131 // parses data into args
1132 // calls function on data/args
1133 // assembles response OR sets error
1134 // breaks
1136 break;
1138 case startBenchTestSequence:
1140 // see TODO on include at top and modify this line appropriately
1141 if(!(compare((unsigned char*)&decoderName, (unsigned char*)BENCH_TEST_NAME, sizeof(BENCH_TEST_NAME)))){
1142 if(RXCalculatedPayloadLength < 1){
1143 errorID = payloadLengthTypeMismatch;
1144 break;
1147 unsigned char localTestMode = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
1148 RXBufferCurrentPosition++;
1149 if(localTestMode > TEST_MODE_BUMP_UP_CYCLES){
1150 errorID = unimplementedTestMode;
1151 break;
1152 }else if((localTestMode == TEST_MODE_STOP) && (RXCalculatedPayloadLength == 1)){
1153 if(!(coreStatusA & BENCH_TEST_ON)){
1154 errorID = benchTestNotRunningToStop;
1155 break;
1158 // Ensure we succeed at stopping it as quickly as possible.
1159 ATOMIC_START();
1160 // Stash mid-test details for return
1161 *((unsigned short*)TXBufferCurrentPositionHandler) = testNumberOfCycles; // Save and return the remaining cycle count
1162 TXBufferCurrentPositionHandler +=2;
1163 *((unsigned char*)TXBufferCurrentPositionHandler) = KeyUserDebugs.currentEvent; // Save the current event for the ultra-fussy
1164 TXBufferCurrentPositionHandler++;
1165 // Setup the test to stop ASAP
1166 KeyUserDebugs.currentEvent = testEventsPerCycle - 1; // Gets incremented then compared with testEventsPerCycle
1167 testNumberOfCycles = 1; // Gets decremented then compared with zero
1168 ATOMIC_END();
1170 break;
1171 }else if((localTestMode == TEST_MODE_BUMP_UP_CYCLES) && (RXCalculatedPayloadLength == 2)){
1172 if(!(coreStatusA & BENCH_TEST_ON)){
1173 errorID = benchTestNotRunningToBump;
1174 break;
1177 // Get bump value from payload
1178 unsigned char bumpCycles = *((unsigned char*)RXBufferCurrentPosition); //1; // The only mode, for now.
1179 RXBufferCurrentPosition++;
1181 if(bumpCycles == 0){
1182 errorID = bumpingByZeroMakesNoSense;
1183 break;
1186 // Bump count by value from payload
1187 testNumberOfCycles += bumpCycles;
1188 // Given that this function is only for situations when A it's getting near to
1189 // zero and B the user is watching, not checking for overflow is reasonable.
1191 *((unsigned char*)TXBufferCurrentPositionHandler) = bumpCycles; // Return the bump size for achaelogical purposes
1192 TXBufferCurrentPositionHandler++;
1194 break;
1195 }else if((localTestMode == TEST_MODE_ITERATIONS) && (RXCalculatedPayloadLength == 24)){
1196 testMode = localTestMode;
1197 // do nothing to fall through, or move other code into here
1198 }else{
1199 errorID = packetSizeWrongForTestMode;
1200 break;
1203 if(coreStatusA & BENCH_TEST_ON){
1204 errorID = benchTestAlreadyRunning;
1205 break;
1208 // Parse the values and return all but the test packet type
1210 testEventsPerCycle = *((unsigned char*)RXBufferCurrentPosition); //100; // @ 10ms = 1s
1211 RXBufferCurrentPosition++;
1212 *((unsigned char*)TXBufferCurrentPositionHandler) = testEventsPerCycle;
1213 TXBufferCurrentPositionHandler++;
1214 if(testEventsPerCycle == 0){
1215 errorID = invalidEventsPerCycle;
1216 break;
1219 testNumberOfCycles = *((unsigned short*)RXBufferCurrentPosition); //20; // @ 1s = 20s
1220 RXBufferCurrentPosition += 2;
1221 *((unsigned short*)TXBufferCurrentPositionHandler) = testNumberOfCycles;
1222 TXBufferCurrentPositionHandler +=2;
1223 if(testNumberOfCycles == 0){
1224 errorID = invalidNumberOfCycles;
1225 break;
1228 testTicksPerEvent = *((unsigned short*)RXBufferCurrentPosition); //12500; // @ 0.8us = 10ms
1229 RXBufferCurrentPosition += 2;
1230 *((unsigned short*)TXBufferCurrentPositionHandler) = testTicksPerEvent;
1231 TXBufferCurrentPositionHandler +=2;
1232 if(testTicksPerEvent < decoderMaxCodeTime){
1233 errorID = tooShortOfAnEventPeriod;
1234 break;
1237 // Pluck the arrays out of the packet for the loop below
1238 unsigned char* testEventNumbers = RXBufferCurrentPosition;
1239 RXBufferCurrentPosition += 6;
1240 unsigned short* testPulseWidths = (unsigned short*)RXBufferCurrentPosition;
1241 RXBufferCurrentPosition += 12;
1242 memcpy((void*)TXBufferCurrentPositionHandler, (void*)testEventNumbers, 18);
1243 TXBufferCurrentPositionHandler += 18;
1245 // Reset the clock for reading timeout
1246 Clocks.timeoutADCreadingClock = 0; // make this optional, such that we can use real inputs to determine pw and/or dwell.
1248 // Validate and transfer the per-channel data
1249 unsigned char channel;
1250 unsigned char configuredChannels = 6;
1251 for(channel = 0;channel < 6;channel++){
1252 if(testPulseWidths[channel] > ectSwitchOnCodeTime){ // See next block for warning.
1253 // use as-is
1254 outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime;
1255 outputEventPulseWidthsMath[channel] = testPulseWidths[channel];
1256 outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1257 }else if(testPulseWidths[channel] > 3){
1258 // less than the code time, and not special, error!
1259 errorID = tooShortOfAPulseWidthToTest;
1260 // Warning, PWs close to this could be slightly longer than requested, that will change in later revisions.
1261 break;
1262 }else if(testPulseWidths[channel] == 3){
1263 testMode++; // Dirty hack to avoid dealing with Dave for the time being.
1264 testNumberOfMissing = channel;
1265 }else if(testPulseWidths[channel] == 2){
1266 // use the dwell from the core maths and input vars.
1267 outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime;
1268 outputEventPulseWidthsMath[channel] = DerivedVars->Dwell;
1269 outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1270 }else if(testPulseWidths[channel] == 1){
1271 // use the reference pulse width from the core maths and input vars.
1272 outputEventDelayFinalPeriod[channel] = decoderMaxCodeTime;
1273 outputEventPulseWidthsMath[channel] = DerivedVars->RefPW;
1274 outputEventInputEventNumbers[channel] = testEventNumbers[channel];
1275 }else{ // is zero
1276 // Set this channel to zero for and therefore off, don't set this channel.
1277 outputEventInputEventNumbers[channel] = 0xFF; // Off.
1278 configuredChannels--;
1282 if(configuredChannels == 0){
1283 errorID = noChannelsConfiguredToTest;
1284 break;
1287 if(errorID == 0){
1288 // Let the first iteration roll it over to zero.
1289 KeyUserDebugs.currentEvent = 0xFF; // Needs to be here in case of multiple runs, init is not sufficient
1291 if(testMode == TEST_MODE_DODGY_MISSING_TOOTH){
1292 if(testEventsPerCycle <= 127){
1293 testEventsPerCycle *= 2;
1294 }else{
1295 errorID = tooManyEventsPerCycleMissingTth;
1296 break;
1299 // Store the time per event in RPM such that it can be updated dynamically
1300 CoreVars->RPM = testTicksPerEvent;
1302 // The channels to use rely on the defaults from initialisers! Custom builds can break BenchTest mode!
1304 // Un-schedule anything that got scheduled
1305 outputEventInputEventNumbers[2] = 0xFF;
1306 outputEventInputEventNumbers[3] = 0xFF;
1307 outputEventInputEventNumbers[4] = 0xFF;
1308 outputEventInputEventNumbers[5] = 0xFF;
1309 }else if(testMode > TEST_MODE_DODGY_MISSING_TOOTH){
1310 errorID = unimplementedTestMode;
1311 break;
1314 // Trigger decoder interrupt to fire thus starting the loop!
1315 TIE = 0x01; // The ISR does the rest!
1317 // Nothing went wrong, now set flag.
1318 coreStatusA |= BENCH_TEST_ON;
1319 }else{
1320 break;
1324 /* http://issues.freeems.org/view.php?id=155
1326 * The following block has been left in, as I still do not know why it won't work as intended:
1328 * - It should fire all 6 output pins with a 52ms duration pulse, exactly once.
1329 * - 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
1330 * - The interrupts run, but the pin doesn't change state, despite the registers being configured correctly
1332 * I've tried quite a bit:
1334 * - Moving this code around
1335 * - Checking memory definitions
1336 * - Completely rewriting the output ISR
1337 * - Adding significant debug to output ISR
1338 * - Checking for register contents in output ISR
1339 * - Checking for key things modified in this file
1340 * - General head scratching and confused searching
1343 // outputEventPinNumbers[0] = 0; // 1 ign
1344 // outputEventPinNumbers[1] = 1; // 2 ign
1345 // outputEventPinNumbers[2] = 2; // 3 ign/1 fuel
1346 // outputEventPinNumbers[3] = 3; // 4 ign/2 fuel
1347 // outputEventPinNumbers[4] = 4; // 3 fuel
1348 // outputEventPinNumbers[5] = 5; // 4 fuel
1349 // outputEventDelayFinalPeriod[0] = decoderMaxCodeTime;
1350 // outputEventDelayFinalPeriod[1] = decoderMaxCodeTime;
1351 // outputEventDelayFinalPeriod[2] = decoderMaxCodeTime;
1352 // outputEventDelayFinalPeriod[3] = decoderMaxCodeTime;
1353 // outputEventDelayFinalPeriod[4] = decoderMaxCodeTime;
1354 // outputEventDelayFinalPeriod[5] = decoderMaxCodeTime;
1355 // outputEventPulseWidthsMath[0] = SHORTMAX;
1356 // outputEventPulseWidthsMath[1] = SHORTMAX;
1357 // outputEventPulseWidthsMath[2] = SHORTMAX;
1358 // outputEventPulseWidthsMath[3] = SHORTMAX;
1359 // outputEventPulseWidthsMath[4] = SHORTMAX;
1360 // outputEventPulseWidthsMath[5] = SHORTMAX;
1362 // unsigned short edgeTimeStamp = TCNT;
1363 // // call sched output with args
1364 // LongTime timeStamp;
1365 // /* Install the low word */
1366 // timeStamp.timeShorts[1] = edgeTimeStamp;
1367 // /* Find out what our timer value means and put it in the high word */
1368 // if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
1369 // timeStamp.timeShorts[0] = timerExtensionClock + 1;
1370 // }else{
1371 // timeStamp.timeShorts[0] = timerExtensionClock;
1372 // }
1374 // schedulePortTPin(0, timeStamp);
1375 // schedulePortTPin(1, timeStamp);
1376 // schedulePortTPin(2, timeStamp);
1377 // schedulePortTPin(3, timeStamp);
1378 // schedulePortTPin(4, timeStamp);
1379 // schedulePortTPin(5, timeStamp);
1381 // sleep(1000);
1382 }else{
1383 errorID = thisIsNotTheBenchTestDecoder;
1385 break;
1387 default:
1389 if((RXHeaderPayloadID % 2) == 1){
1390 errorID = invalidPayloadID;
1391 }else{
1392 errorID = unrecognisedPayloadID;
1394 break;
1398 // Always reply, if errorID is zero it's just an ack.
1399 finaliseAndSend(errorID);
1401 /* Switch reception back on now that we are done with the received data */
1402 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
1406 /* This function should be period limited to about 10 seconds internally (or by scheduler) */
1407 //void checkCountersAndSendErrors(){
1408 // compare time stamps with current time stamps and execute if old enough. (if no scheduler)
1410 // compare counters with counters cache (from last time) sending an error packet when they differ
1412 // copy counters to counters cache for next time
1414 // send errors with busy wait on the basis that all errors should be taken care of and not be sent in fairly short order?
1416 // or send with isr but just busy wait for it to finish before sending the next?
1418 // 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.
1420 // need to figure out how to queue received packets for processing when we are currently sending stuff out.
1422 // above notes don't belong here really.