diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 832323a9..1ed58633 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -11,7 +11,7 @@ versionInfo = "S" ; Put this in the title bar. [TunerStudio] - iniSpecVersion = 3.24 + iniSpecVersion = 3.46 ;------------------------------------------------------------------------------- @@ -98,23 +98,35 @@ endianness = little nPages = 11 - burnCommand = "B" - pageSize = 288, 64, 288, 64, 288, 64, 64, 160, 192, 128, 192 -; pageIdentifier = "\$tsCanId\x01", "\$tsCanId\x02", "\$tsCanId\x03", "\$tsCanId\x04", "\$tsCanId\x05", "\$tsCanId\x06", "\$tsCanId\x07", "\$tsCanId\x08", "\$tsCanId\x09", "\$tsCanId\x0A" - pageActivationDelay = 10 - pageActivate = "P\001", "P\002", "P\003", "P\004", "P\005", "P\006", "P\007", "P\010", "P\011", "P\012", "P\013" - pageReadCommand = "V", "V", "V", "V", "V", "V", "V", "V", "V", "V", "V" - pageValueWrite = "W%2o%v", "W%o%v", "W%2o%v", "W%o%v", "W%2o%v", "W%o%v", "W%o%v", "W%o%v", "W%o%v", "W%o%v", "W%o%v" -; pageChunkWrite = "" ; No chunk write yet + ;pageSize = 288, 64, 288, 64, 288, 64, 64, 160, 192, 128, 192 + + ;burnCommand = "B" + ;pageActivate = "P\001", "P\002", "P\003", "P\004", "P\005", "P\006", "P\007", "P\010", "P\011", "P\012", "P\013" + ;pageReadCommand = "V", "V", "V", "V", "V", "V", "V", "V", "V", "V", "V" + ;pageValueWrite = "W%2o%v", "W%o%v", "W%2o%v", "W%o%v", "W%2o%v", "W%o%v", "W%o%v", "W%o%v", "W%o%v", "W%o%v", "W%o%v" + + + ;pageChunkWrite = "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v", "w%2o%2c%v" + ;pageChunkWrite = "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v", "X%o%c%v" + + ; New commands + pageSize = 288, 128, 288, 128, 288, 128, 128, 160, 192, 128, 192 + pageIdentifier = "\$tsCanId\x01", "\$tsCanId\x02", "\$tsCanId\x03", "\$tsCanId\x04", "\$tsCanId\x05", "\$tsCanId\x06", "\$tsCanId\x07", "\$tsCanId\x08", "\$tsCanId\x09", "\$tsCanId\x0A", "\$tsCanId\x0B" + burnCommand = "b%2i", "b%2i", "b%2i", "b%2i", "b%2i", "b%2i", "b%2i", "b%2i", "b%2i", "b%2i", "b%2i" + pageReadCommand = "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v", "p%2i%2o%2c%v" + pageValueWrite = "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v" + ;pageChunkWrite = "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v" + ;pageChunkWrite = "w%2i%2o%2c%v", "", "w%2i%2o%2c%v", "", "w%2i%2o%2c%v", "", "", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v", "w%2i%2o%2c%v" blockingFactor = 2048 tableBlockingFactor = 2048 delayAfterPortOpen=1000 ;validateArrayBounds = true blockReadTimeout = 2000 - ;tsWriteBlocks = off + ;tsWriteBlocks = on ;writeBlocks = off - interWriteDelay = 5 + interWriteDelay = 1 + pageActivationDelay = 10 ;New for TS 3.0.08ish upwards, define lists of standard I/O options diff --git a/speeduino/comms.h b/speeduino/comms.h index 132944e9..5f84a81e 100644 --- a/speeduino/comms.h +++ b/speeduino/comms.h @@ -20,6 +20,9 @@ bool isMap = true; unsigned long requestCount = 0; //The number of times the A command has been issued byte currentCommand; bool cmdPending = false; +bool chunkPending = false; +uint16_t chunkComplete = 0; +uint16_t chunkSize = 0; byte cmdGroup = 0; byte cmdValue = 0; int cmdCombined = 0; //the cmdgroup as high byte and cmdvalue as low byte diff --git a/speeduino/comms.ino b/speeduino/comms.ino index 2acddea8..d9da098b 100644 --- a/speeduino/comms.ino +++ b/speeduino/comms.ino @@ -15,7 +15,7 @@ A detailed description of each call can be found at: http://www.msextra.com/doc/ void command() { - if (!cmdPending) { currentCommand = Serial.read(); } + if (cmdPending == false) { currentCommand = Serial.read(); } switch (currentCommand) { @@ -26,7 +26,18 @@ void command() case 'B': // Burn current values to eeprom - writeConfig(); + writeAllConfig(); + break; + + case 'b': // New EEPROM burn command to only burn a single page at a time + cmdPending = true; + + if (Serial.available() >= 2) + { + Serial.read(); //Ignore the first table value, it's always 0 + writeConfig(Serial.read()); + cmdPending = false; + } break; case 'C': // test communications. This is used by Tunerstudio to see whether there is an ECU on a given serial port @@ -82,7 +93,7 @@ void command() case 'Q': // send code version Serial.print("speeduino 201709-dev"); - break; + break; case 'V': // send VE table and constants in binary sendPage(false); @@ -117,6 +128,76 @@ void command() break; + /* + * New method for sending page values + */ + case 'p': + cmdPending = true; + + //6 bytes required: + //2 - Page identifier + //2 - offset + //2 - Length + if(Serial.available() >= 6) + { + byte offset1, offset2, length1, length2; + int length; + byte tempPage; + + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + tempPage = Serial.read(); + //currentPage = 1; + offset1 = Serial.read(); + offset2 = Serial.read(); + valueOffset = word(offset2, offset1); + length1 = Serial.read(); + length2 = Serial.read(); + length = word(length2, length1); + for(int x = 0; x < length; x++) + { + Serial.write( getPageValue(tempPage, valueOffset + x) ); + } + + cmdPending = false; + } + break; + + case 'w': + cmdPending = true; + + if(chunkPending == false) + { + //This means it's a new request + //7 bytes required: + //2 - Page identifier + //2 - offset + //2 - Length (Should always be 1 until chunk write is setup) + //1 - New value + if(Serial.available() >= 7) + { + byte offset1, offset2, length1, length2; + + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + currentPage = Serial.read(); + //currentPage = 1; + offset1 = Serial.read(); + offset2 = Serial.read(); + valueOffset = word(offset2, offset1); + length1 = Serial.read(); // Length to be written (Should always be 1) + length2 = Serial.read(); // Length to be written (Should always be 1) + chunkSize = word(length2, length1); + + //chunkPending = true; + for(uint16_t x = 0; x < chunkSize; x++) + { + while(Serial.available() == 0) { } + receiveValue( (valueOffset + x), Serial.read()); + } + cmdPending = false; + } + } + break; + case 't': // receive new Calibration info. Command structure: "t", . This is an MS2/Extra command, NOT part of MS1 spec byte tableID; //byte canID; @@ -382,12 +463,16 @@ void receiveValue(int valueOffset, byte newValue) //X Axis fuelTable.axisX[(valueOffset - 256)] = ((int)(newValue) * TABLE_RPM_MULTIPLIER); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct (TABLE_RPM_MULTIPLIER) } - else + else if(valueOffset < 288) { //Y Axis int tempOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order (Due to us using (0,0) in the top left rather than bottom right fuelTable.axisY[tempOffset] = (int)(newValue) * TABLE_LOAD_MULTIPLIER; } + else + { + //This should never happen. It means there's an invalid offset value coming through + } } break; @@ -413,7 +498,7 @@ void receiveValue(int valueOffset, byte newValue) //X Axis ignitionTable.axisX[(valueOffset - 256)] = (int)(newValue) * TABLE_RPM_MULTIPLIER; //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct } - else + else if(valueOffset < 288) { //Y Axis int tempOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order @@ -544,6 +629,7 @@ void receiveValue(int valueOffset, byte newValue) default: break; } + //if(Serial.available() > 16) { command(); } } /* @@ -980,6 +1066,130 @@ void sendPage(bool useChar) } //sendComplete } +byte getPageValue(byte page, uint16_t valueAddress) +{ + void* pnt_configPage = &configPage1; //Default value is for safety only. Will be changed below if needed. + byte returnValue = 0; + + switch (page) + { + case veMapPage: + if( valueAddress < 256) { returnValue = fuelTable.values[15 - (valueAddress / 16)][valueAddress % 16]; } //This is slightly non-intuitive, but essentially just flips the table vertically (IE top line becomes the bottom line etc). Columns are unchanged. Every 16 loops, manually call loop() to avoid potential misses + else if(valueAddress < 272) { returnValue = byte(fuelTable.axisX[(valueAddress - 256)] / TABLE_RPM_MULTIPLIER); } //RPM Bins for VE table (Need to be dvidied by 100) + else if (valueAddress < 288) { returnValue = byte(fuelTable.axisY[15 - (valueAddress - 272)] / TABLE_LOAD_MULTIPLIER); } //MAP or TPS bins for VE table + break; + + case veSetPage: + pnt_configPage = &configPage1; //Create a pointer to Page 1 in memory + returnValue = *((byte *)pnt_configPage + valueAddress); + break; + + case ignMapPage: + if( valueAddress < 256) { returnValue = ignitionTable.values[15 - (valueAddress / 16)][valueAddress % 16]; } //This is slightly non-intuitive, but essentially just flips the table vertically (IE top line becomes the bottom line etc). Columns are unchanged. Every 16 loops, manually call loop() to avoid potential misses + else if(valueAddress < 272) { returnValue = byte(ignitionTable.axisX[(valueAddress - 256)] / TABLE_RPM_MULTIPLIER); } //RPM Bins for VE table (Need to be dvidied by 100) + else if (valueAddress < 288) { returnValue = byte(ignitionTable.axisY[15 - (valueAddress - 272)] / TABLE_LOAD_MULTIPLIER); } //MAP or TPS bins for VE table + break; + + case ignSetPage: + pnt_configPage = &configPage2; //Create a pointer to Page 2 in memory + returnValue = *((byte *)pnt_configPage + valueAddress); + break; + + case afrMapPage: + if( valueAddress < 256) { returnValue = afrTable.values[15 - (valueAddress / 16)][valueAddress % 16]; } //This is slightly non-intuitive, but essentially just flips the table vertically (IE top line becomes the bottom line etc). Columns are unchanged. Every 16 loops, manually call loop() to avoid potential misses + else if(valueAddress < 272) { returnValue = byte(afrTable.axisX[(valueAddress - 256)] / TABLE_RPM_MULTIPLIER); } //RPM Bins for VE table (Need to be dvidied by 100) + else if (valueAddress < 288) { returnValue = byte(afrTable.axisY[15 - (valueAddress - 272)] / TABLE_LOAD_MULTIPLIER); } //MAP or TPS bins for VE table + break; + + case afrSetPage: + pnt_configPage = &configPage3; //Create a pointer to Page 3 in memory + returnValue = *((byte *)pnt_configPage + valueAddress); + break; + + case iacPage: + pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory + returnValue = *((byte *)pnt_configPage + valueAddress); + break; + + case boostvvtPage: + + { + //Need to perform a translation of the values[MAP/TPS][RPM] into the MS expected format + if(valueAddress < 80) + { + //Boost table + if(valueAddress < 64) { returnValue = boostTable.values[7 - (valueAddress / 8)][valueAddress % 8]; } + else if(valueAddress < 72) { returnValue = byte(boostTable.axisX[(valueAddress - 64)] / TABLE_RPM_MULTIPLIER); } + else if(valueAddress < 80) { returnValue = byte(boostTable.axisY[7 - (valueAddress - 72)]); } + } + else + { + valueAddress -= 80; + //VVT table + if(valueAddress < 64) { returnValue = vvtTable.values[7 - (valueAddress / 8)][valueAddress % 8]; } + else if(valueAddress < 72) { returnValue = byte(vvtTable.axisX[(valueAddress - 64)] / TABLE_RPM_MULTIPLIER); } + else if(valueAddress < 80) { returnValue = byte(vvtTable.axisY[7 - (valueAddress - 72)]); } + } + } + break; + + case seqFuelPage: + + { + //Need to perform a translation of the values[MAP/TPS][RPM] into the TS expected format + if(valueAddress < 48) + { + //trim1 table + if(valueAddress < 36) { returnValue = trim1Table.values[5 - (valueAddress / 6)][valueAddress % 6]; } + else if(valueAddress < 42) { returnValue = byte(trim1Table.axisX[(valueAddress - 36)] / TABLE_RPM_MULTIPLIER); } + else if(valueAddress < 48) { returnValue = byte(trim1Table.axisY[5 - (valueAddress - 42)] / TABLE_LOAD_MULTIPLIER); } + } + else if(valueAddress < 96) + { + valueAddress -= 48; + //trim2 table + if(valueAddress < 36) { returnValue = trim2Table.values[5 - (valueAddress / 6)][valueAddress % 6]; } + else if(valueAddress < 42) { returnValue = byte(trim2Table.axisX[(valueAddress - 36)] / TABLE_RPM_MULTIPLIER); } + else if(valueAddress < 48) { returnValue = byte(trim2Table.axisY[5 - (valueAddress - 42)] / TABLE_LOAD_MULTIPLIER); } + } + else if(valueAddress < 144) + { + valueAddress -= 96; + //trim3 table + if(valueAddress < 36) { returnValue = trim3Table.values[5 - (valueAddress / 6)][valueAddress % 6]; } + else if(valueAddress < 42) { returnValue = byte(trim3Table.axisX[(valueAddress - 36)] / TABLE_RPM_MULTIPLIER); } + else if(valueAddress < 48) { returnValue = byte(trim3Table.axisY[5 - (valueAddress - 42)] / TABLE_LOAD_MULTIPLIER); } + } + else if(valueAddress < 192) + { + valueAddress -= 144; + //trim4 table + if(valueAddress < 36) { returnValue = trim4Table.values[5 - (valueAddress / 6)][valueAddress % 6]; } + else if(valueAddress < 42) { returnValue = byte(trim4Table.axisX[(valueAddress - 36)] / TABLE_RPM_MULTIPLIER); } + else if(valueAddress < 48) { returnValue = byte(trim4Table.axisY[5 - (valueAddress - 42)] / TABLE_LOAD_MULTIPLIER); } + } + } + break; + + case canbusPage: + pnt_configPage = &configPage10; //Create a pointer to Page 10 in memory + returnValue = *((byte *)pnt_configPage + valueAddress); + break; + + case warmupPage: + pnt_configPage = &configPage11; //Create a pointer to Page 11 in memory + returnValue = *((byte *)pnt_configPage + valueAddress); + break; + + default: + Serial.println(F("\nPage has not been implemented yet. Change to another page.")); + //Just set default Values to avoid warnings + pnt_configPage = &configPage11; + break; + } + return returnValue; +} + /* This function is used to store calibration data sent by Tuner Studio. */ diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 0e3606db..03c8378e 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -130,6 +130,7 @@ bool initialisationComplete = false; //Tracks whether the setup() functino has r void setup() { + initialiseTimers(); digitalWrite(LED_BUILTIN, LOW); //Setup the dummy fuel and ignition tables //dummyFuelTable(&fuelTable); @@ -237,10 +238,8 @@ void setup() //Set the tacho output default state digitalWrite(pinTachOut, HIGH); - //Perform all initialisations initialiseSchedulers(); - initialiseTimers(); //initialiseDisplay(); initialiseIdle(); initialiseFan(); @@ -300,6 +299,7 @@ void setup() currentStatus.launchingHard = false; currentStatus.crankRPM = ((unsigned int)configPage2.crankRPM * 100); //Crank RPM limit (Saves us calculating this over and over again. It's updated once per second in timers.ino) triggerFilterTime = 0; //Trigger filter time is the shortest possible time (in uS) that there can be between crank teeth (ie at max RPM). Any pulses that occur faster than this time will be disgarded as noise. This is simply a default value, the actual values are set in the setup() functinos of each decoder + dwellLimit_uS = (1000 * configPage2.dwellLimit); noInterrupts(); initialiseTriggers(); @@ -648,11 +648,15 @@ void loop() //Check for any requets from serial. Serial operations are checked under 2 scenarios: // 1) Every 64 loops (64 Is more than fast enough for TunerStudio). This function is equivalent to ((loopCount % 64) == 1) but is considerably faster due to not using the mod or division operations // 2) If the amount of data in the serial buffer is greater than a set threhold (See globals.h). This is to avoid serial buffer overflow when large amounts of data is being sent - if ( (BIT_CHECK(LOOP_TIMER, BIT_TIMER_15HZ)) || (Serial.available() > SERIAL_BUFFER_THRESHOLD) ) + //if ( (BIT_CHECK(TIMER_mask, BIT_TIMER_15HZ)) || (Serial.available() > SERIAL_BUFFER_THRESHOLD) ) + //if ( (timer15Hz == true) ) + if ( ((mainLoopCount & 31) == 1) or (Serial.available() > SERIAL_BUFFER_THRESHOLD) ) { if (Serial.available() > 0) { command(); } } + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //ATmega2561 does not have Serial3 //if serial3 interface is enabled then check for serial3 requests. if (configPage10.enable_canbus == 1)