Sophie Dexter
/
Just4Trionic
Just4Trionic - CAN and BDM FLASH programmer for Saab cars
Embed:
(wiki syntax)
Show/hide line numbers
t5can.cpp
00001 /******************************************************************************* 00002 00003 trionic5.cpp - CAN Bus functions for Just4Trionic by Just4pLeisure 00004 (c) 2010 by Sophie Dexter 00005 00006 This C++ module provides functions for reading and writing the FLASH chips and 00007 SRAM in Trionic5 ECUs. (Writing the adaption data back to SRAM not done yet). 00008 00009 Some functions need an additional 'bootloader' program to be sent to the T5 ECU 00010 before they can be used. These functions are: Identifying the T5 ECU type and 00011 FLASH chips, dumping the FLASH chips, erasing the FLASH chips, writing to the 00012 FLASH chips and calculating the FLASH chips' checksum. 00013 00014 My version of the bootloader, BOOTY.S19, includes some features not in other 00015 bootloaders; identifying the ECU and FLASH chip types, a 'safer' way of dumping 00016 the FLASH chips and the ability to program AMD 29F010 type FLASH chips 00017 00018 ******************************************************************************** 00019 00020 WARNING: Use at your own risk, sadly this software comes with no guarantees. 00021 This software is provided 'free' and in good faith, but the author does not 00022 accept liability for any damage arising from its use. 00023 00024 *******************************************************************************/ 00025 00026 #include "t5can.h" 00027 00028 // constants 00029 #define CMD_BUF_LENGTH 32 ///< command buffer size 00030 00031 // static variables 00032 static char cmd_buffer[CMD_BUF_LENGTH]; ///< command string buffer 00033 00034 //static uint32_t cmd_addr; ///< address (optional) 00035 //static uint32_t cmd_value; ///< value (optional) 00036 //static uint32_t cmd_result; ///< result 00037 00038 static uint32_t flash_start = 0; 00039 00040 // private functions 00041 uint8_t execute_t5_cmd(); 00042 void t5_can_show_help(); 00043 void t5_can_show_full_help(); 00044 00045 void t5_can() 00046 { 00047 // Start the CAN bus system 00048 // Note that at the moment this is only for T5 ECUs at 615 kbits 00049 can_open(); 00050 can_set_speed(615000); 00051 00052 t5_can_show_help(); 00053 00054 // main loop 00055 *cmd_buffer = '\0'; 00056 char ret; 00057 char rx_char; 00058 while (true) { 00059 // read chars from USB 00060 // send received messages to the pc over USB connection 00061 // This function displays any CAN messages that are 'missed' by the other functions 00062 // Can messages might be 'missed' because they are received after a 'timeout' period 00063 // or because they weren't expected, e.g. if the T5 ECU resets for some reason 00064 t5_can_show_can_message(); 00065 if (pc.readable()) { 00066 // turn Error LED off for next command 00067 led4 = 0; 00068 rx_char = pc.getc(); 00069 switch (rx_char) { 00070 // 'ESC' key to go back to mbed Just4Trionic 'home' menu 00071 case '\e': 00072 can_close(); 00073 return; 00074 // end-of-command reached 00075 case TERM_OK : 00076 // execute command and return flag via USB 00077 timer.reset(); 00078 timer.start(); 00079 ret = execute_t5_cmd(); 00080 pc.putc(ret); 00081 printf("Completed in %.3f seconds.\r\n", timer.read()); 00082 // reset command buffer 00083 *cmd_buffer = '\0'; 00084 // light up LED 00085 // ret == TERM_OK ? led_on(LED_ACT) : led_on(LED_ERR); 00086 ret == TERM_OK ? led3 = 1 : led4 = 1; 00087 break; 00088 // another command char 00089 default: 00090 // store in buffer if space permits 00091 if (StrLen(cmd_buffer) < CMD_BUF_LENGTH - 1) { 00092 StrAddc(cmd_buffer, rx_char); 00093 } 00094 break; 00095 } 00096 } 00097 } 00098 } 00099 00100 //----------------------------------------------------------------------------- 00101 /** 00102 Executes a command and returns result flag (does not transmit the flag 00103 itself). 00104 00105 @return command flag (success / failure) 00106 */ 00107 uint8_t execute_t5_cmd() 00108 { 00109 00110 00111 // uint8_t cmd_length = strlen(cmd_buffer); 00112 // command groups 00113 switch (*cmd_buffer) { 00114 // CHECK_ARGLENGTH(0); 00115 // Get the Symbol Table 00116 case 's': 00117 return t5_can_get_symbol_table() 00118 ? TERM_OK : TERM_ERR; 00119 case 'S': 00120 return T5ReadCmnd(T5SYMBOLS) 00121 ? TERM_OK : TERM_ERR; 00122 00123 // Get the Trionic5 software version string 00124 case 'v': 00125 return t5_can_get_version() 00126 ? TERM_OK : TERM_ERR; 00127 case 'V': 00128 return T5ReadCmnd(T5VERSION) 00129 ? TERM_OK : TERM_ERR; 00130 00131 // Read Adaption Data from RAM and write it to a file 00132 case 'r': 00133 case 'R': 00134 return t5_can_get_adaption_data() 00135 ? TERM_OK : TERM_ERR; 00136 00137 // CR - send CR type message 00138 case '\0': 00139 return T5ReadCmnd(CR) 00140 ? TERM_OK : TERM_ERR; 00141 00142 // Get a single symbol from the Symbol Table 00143 case 'a': 00144 char symbol[40]; 00145 T5GetSymbol(symbol); 00146 printf("%s",symbol); 00147 return TERM_OK; 00148 00149 // Just send an 'ACK' message 00150 case 'A': 00151 return T5Ack() 00152 ? TERM_OK : TERM_ERR; 00153 00154 // Send a Bootloader file to the T5 ECU 00155 case 'b': 00156 return (t5_can_send_boot_loader() && can_set_speed(1000000)) 00157 ? TERM_OK : TERM_ERR; 00158 case 'B': 00159 return (t5_can_send_boot_loader_S19() && can_set_speed(1000000)) 00160 ? TERM_OK : TERM_ERR; 00161 00162 // Get Checksum from ECU (Bootloader must be uploaded first) 00163 case 'c': 00164 case 'C': 00165 return t5_can_get_checksum() 00166 ? TERM_OK : TERM_ERR; 00167 00168 // Exit the BootLoader and restart the T5 ECU 00169 case 'q': 00170 case 'Q': 00171 return (t5_can_bootloader_reset() && can_set_speed(615000)) 00172 ? TERM_OK : TERM_ERR; 00173 00174 // Erase the FLASH chips 00175 case 'e': 00176 case 'E': 00177 return t5_can_erase_flash() 00178 ? TERM_OK : TERM_ERR; 00179 00180 // Read back the FLASH chip types 00181 case 't': 00182 case 'T': 00183 return t5_can_get_start_and_chip_types(&flash_start) 00184 ? TERM_OK : TERM_ERR; 00185 00186 // DUMP the T5 ECU BIN file stored in the FLASH chips 00187 case 'd': 00188 // NOTE 'd' command Just4TESTING! only dumps T5.5 ECU 00189 return t5_can_dump_flash(T55FLASHSTART) 00190 ? TERM_OK : TERM_ERR; 00191 case 'D': 00192 // if (!t5_can_send_boot_loader_S19()) 00193 if (!t5_can_send_boot_loader()) 00194 return TERM_ERR; 00195 can_set_speed(1000000); 00196 if (!t5_can_get_start_and_chip_types(&flash_start)) { 00197 t5_can_bootloader_reset(); 00198 can_set_speed(615000); 00199 return TERM_ERR; 00200 } 00201 return (t5_can_dump_flash(flash_start) && t5_can_bootloader_reset() && can_set_speed(615000)) 00202 ? TERM_OK : TERM_ERR; 00203 00204 // Send a FLASH update file to the T5 ECU 00205 case 'f': 00206 // NOTE 'f' command Just4TESTING! only FLASHes T5.5 ECU (with S19 type file) 00207 //return t5_can_send_flash_s19_update(T55FLASHSTART) 00208 return t5_can_send_flash_bin_update(T55FLASHSTART) 00209 ? TERM_OK : TERM_ERR; 00210 case 'F': 00211 // if (!t5_can_send_boot_loader_S19()) 00212 if (!t5_can_send_boot_loader()) 00213 return TERM_ERR; 00214 can_set_speed(1000000); 00215 if (!t5_can_get_start_and_chip_types(&flash_start)) { 00216 t5_can_bootloader_reset(); 00217 can_set_speed(615000); 00218 return TERM_ERR; 00219 } 00220 if (!t5_can_get_checksum()) 00221 led4 = 1; 00222 if (!t5_can_erase_flash()) { 00223 t5_can_bootloader_reset(); 00224 can_set_speed(615000); 00225 return TERM_ERR; 00226 } 00227 return (t5_can_send_flash_bin_update(flash_start) && t5_can_get_checksum() && t5_can_bootloader_reset() && can_set_speed(615000)) 00228 ? TERM_OK : TERM_ERR; 00229 00230 // Send the C3 message - should get last used address 0x7FFFF 00231 case '3': 00232 return t5_can_get_last_address() 00233 ? TERM_OK : TERM_ERR; 00234 00235 // Print help 00236 case 'h': 00237 t5_can_show_help(); 00238 return TERM_OK; 00239 case 'H': 00240 t5_can_show_full_help(); 00241 return TERM_OK; 00242 default: 00243 t5_can_show_help(); 00244 break; 00245 } 00246 // unknown command 00247 return TERM_ERR; 00248 } 00249 00250 // 00251 // Trionic5ShowHelp 00252 // 00253 // Displays a list of things that can be done with the T5 ECU. 00254 // 00255 // inputs: none 00256 // return: none 00257 // 00258 void t5_can_show_help() 00259 { 00260 printf("Trionic 5 Command Menu\r\n"); 00261 printf("======================\r\n"); 00262 printf("D - DUMP the T5.x ECU FLASH to a file 'ORIGINAL.BIN'\r\n"); 00263 printf("F - FLASH the update file 'MODIFIED.BIN' to the T5.x\r\n"); 00264 printf("\r\n"); 00265 printf("r - read SRAM and write it to ADAPTION.RAM file\r\n"); 00266 printf("s - read Symbol Table and write it to SYMBOLS.TXT\r\n"); 00267 printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n"); 00268 printf("\r\n"); 00269 printf("'ESC' - Return to Just4Trionic Main Menu\r\n"); 00270 printf("\r\n"); 00271 printf("h - Show this help menu\r\n"); 00272 printf("\r\n"); 00273 return; 00274 } 00275 // 00276 // t5_can_show_full_help 00277 // 00278 // Displays a complete list of things that can be done with the T5 ECU. 00279 // 00280 // inputs: none 00281 // return: none 00282 // 00283 void t5_can_show_full_help() 00284 { 00285 printf("Trionic 5 Command Menu\r\n"); 00286 printf("======================\r\n"); 00287 printf("D - DUMP the T5.x ECU FLASH to a file 'ORIGINAL.BIN'\r\n"); 00288 printf("F - FLASH the update file 'MODIFIED.BIN' to the T5.x\r\n"); 00289 printf("\r\n"); 00290 printf("b - upload and start MyBooty.S19 bootloader\r\n"); 00291 printf("c - get T5 ECU FLASH checksum (need to upload BOOTY.S19 before using this command)\r\n"); 00292 printf("d - dump the T5 FLASH BIN file and write it to ORIGINAL.BIN\r\n"); 00293 printf("e - erase the FLASH chips in the T5 ECU\r\n"); 00294 printf("f - FLASH the update file MODIFIED.BIN to the T5 ECU\r\n"); 00295 printf("r - read SRAM and write it to ADAPTION.RAM file\r\n"); 00296 printf("s - read Symbol Table, display it and write it to SYMBOLS.TXT\r\n"); 00297 printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n"); 00298 printf("q - exit the bootloader and reset the T5 ECU\r\n"); 00299 printf("t - read the FLASH chip type in the T5 ECU\r\n"); 00300 printf("3 - read the last used FLASH address in the T5 ECU\r\n"); 00301 printf("S - send 's' message (to get symbol table)\r\n"); 00302 printf("V - send 'S' message (to get software version)\r\n"); 00303 printf("'Enter' Key - send an CR message\r\n"); 00304 printf("a - send an ACK\r\n"); 00305 printf("A - read a single symbol from the symbol table\r\n"); 00306 printf("\r\n"); 00307 printf("'ESC' - Return to Just4Trionic Main Menu\r\n"); 00308 printf("\r\n"); 00309 printf("H - Show this help menu\r\n"); 00310 printf("\r\n"); 00311 return; 00312 } 00313 00314 // 00315 // t5_can_show_can_message 00316 // 00317 // Displays a CAN message in the RX buffer if there is one. 00318 // 00319 // inputs: none 00320 // return: bool TRUE if there was a message, FALSE if no message. 00321 // 00322 bool t5_can_show_can_message() 00323 { 00324 CANMessage can_MsgRx; 00325 if (can.read(can_MsgRx)) { 00326 printf("w%03x%d", can_MsgRx.id, can_MsgRx.len); 00327 for (char i=0; i<can_MsgRx.len; i++) { 00328 printf("%02x", can_MsgRx.data[i]); 00329 } 00330 printf(" %c ", can_MsgRx.data[2]); 00331 printf("\r\n"); 00332 return TRUE; 00333 } 00334 return FALSE; 00335 } 00336 00337 // 00338 // t5_can_get_symbol_table 00339 // 00340 // Gets the T5 ECU symbol table. 00341 // The Symbol Table is saved to a file, symbols.txt, on the mbed 'local' file system 'disk'. 00342 // 00343 // inputs: none 00344 // return: bool TRUE if there all went OK, FALSE if there was an error 00345 // 00346 bool t5_can_get_symbol_table() 00347 { 00348 printf("Saving the symbol table file\r\n"); 00349 FILE *fp = fopen("/local/symbols.txt", "w"); // Open "symbols.txt" on the local file system for writing 00350 if (!fp) { 00351 perror ("The following error occured"); 00352 return FALSE; 00353 } 00354 char symbol[40]; 00355 T5ReadCmnd(T5SYMBOLS); 00356 if (T5WaitResponse() != '>') { 00357 fclose(fp); 00358 return FALSE; 00359 } 00360 T5ReadCmnd(CR); 00361 if (T5WaitResponse() != '>') { 00362 fclose(fp); 00363 return FALSE; 00364 } 00365 do { 00366 T5GetSymbol(symbol); 00367 // printf("%s",symbol); 00368 if (fprintf(fp,"%s",symbol) < 0) { 00369 fclose (fp); 00370 printf ("ERROR Writing to the symbols.txt file!\r\n"); 00371 return FALSE; 00372 }; 00373 } while (!StrCmp(symbol,"END\r\n")); 00374 fclose(fp); 00375 return TRUE; 00376 } 00377 00378 // 00379 // t5_can_get_version 00380 // 00381 // Gets the T5 software version string. 00382 // The software version is is sent to the PC and saved to a file, version.txt, on the mbed 'local' file system 'disk'. 00383 // 00384 // inputs: none 00385 // return: bool TRUE if there all went OK, FALSE if there was an error 00386 // 00387 bool t5_can_get_version() 00388 { 00389 FILE *fp = fopen("/local/version.txt", "w"); // Open "version.txt" on the local file system for writing 00390 if (!fp) { 00391 perror ("The following error occured"); 00392 return FALSE; 00393 } 00394 char symbol[40]; 00395 T5ReadCmnd(T5VERSION); 00396 if (T5WaitResponse() != '>') { 00397 fclose(fp); 00398 return FALSE; 00399 } 00400 T5ReadCmnd(CR); 00401 if (T5WaitResponse() != '>') { 00402 fclose(fp); 00403 return FALSE; 00404 } 00405 T5GetSymbol(symbol); 00406 printf("%s",symbol); 00407 if (fprintf(fp,"%s",symbol) < 0) { 00408 fclose (fp); 00409 printf ("ERROR Writing to the version.txt file!\r\n"); 00410 return FALSE; 00411 }; 00412 fclose(fp); 00413 printf("The version.txt file has been saved.\r\n"); 00414 return TRUE; 00415 } 00416 00417 // 00418 // Trionic5GetAdaptionData 00419 // 00420 // Gets the adaption data from the T5's SRAM. 00421 // The adaption data is stored in a hex file, adaption.RAM, on the mbed 'local' file system 'disk'. 00422 // 00423 // Reading the Adaption data from SRAM takes about 6.5 seconds. 00424 // 00425 // inputs: none 00426 // return: bool TRUE if all went OK, FALSE if there was an error. 00427 // 00428 bool t5_can_get_adaption_data() 00429 { 00430 printf("Saving the SRAM adaption data.\r\n"); 00431 FILE *fp = fopen("/local/adaption.RAM", "w"); // Open "adaption.RAM" on the local file system for writing 00432 if (!fp) { 00433 printf("ERROR: Unable to open a file for the adaption data!\r\n"); 00434 return FALSE; 00435 } 00436 unsigned int address = 5; // Mysterious reason for starting at 5 !!! 00437 char RAMdata[6]; 00438 while (address < T5RAMSIZE) { 00439 if (!t5_can_read_data(RAMdata, address)) { 00440 fclose (fp); 00441 printf ("Error reading from the CAN bus.\r\n"); 00442 return FALSE; 00443 } 00444 address += 6; 00445 if (fwrite(RAMdata, 1, 6, fp) != 6) { 00446 fclose (fp); 00447 printf ("Error writing to the SRAM adaption file.\r\n"); 00448 return FALSE; 00449 } 00450 } 00451 // There are a few more bytes to get because because the SRAM file is not an exact multiple of 6 bytes! 00452 // the % (modulo) mathematics function tells us how many bytes there are still to get 00453 if (!t5_can_read_data(RAMdata, (T5RAMSIZE - 1))) { 00454 fclose (fp); 00455 printf ("Error reading from the CAN bus.\r\n"); 00456 return FALSE; 00457 } 00458 if (fwrite((RAMdata + 6 - (T5RAMSIZE % 6)), 1, (T5RAMSIZE % 6), fp) != (T5RAMSIZE % 6)) { 00459 fclose (fp); 00460 printf ("Error writing to the SRAM adaption file.\r\n"); 00461 return FALSE; 00462 } 00463 fclose(fp); 00464 return TRUE; 00465 } 00466 00467 // 00468 // t5_can_send_boot_loader 00469 // 00470 // Sends a 'bootloader' file, booty.s19 to the T5 ECU. 00471 // The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format. 00472 // 00473 // The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-) 00474 // 00475 // Sending the 'bootloader' to the T5 ECU takes just over 1 second. 00476 // 00477 // inputs: none 00478 // return: bool TRUE if all went OK, 00479 // FALSE if the 'bootloader' wasn't sent for some reason. 00480 // 00481 bool t5_can_send_boot_loader() 00482 { 00483 uint32_t BootloaderSize = sizeof(T5BootLoader); 00484 uint32_t address = MYBOOTY_START; // Start and execute from address of T5Bootloader 00485 uint32_t count = 0; // progress count of bootloader bytes transferred 00486 char msg[8]; // Construct the bootloader frame for uploading 00487 00488 printf("Starting the bootloader.\r\n"); 00489 while (count < BootloaderSize) { 00490 // send a bootloader address message 00491 if (!t5_can_send_boot_address((address+count), MYBOOTY_CHUNK)) return FALSE; 00492 // send bootloader frames 00493 // NOTE the last frame sent may have less than 7 real data bytes but 7 bytes are always sent. In this case the unnecessary bytes 00494 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes 00495 // is sent with the upload address) and ignores any extra bytes in the last frame. 00496 for (uint8_t i=0; i<MYBOOTY_CHUNK; i++) { 00497 msg[1+(i%7)] = T5BootLoader[count+i]; 00498 if (i%7 == 0) msg[0]=i; // set the index number 00499 if ((i%7 == 6) || (i == MYBOOTY_CHUNK-1 )) { 00500 if (!t5_can_send_boot_frame(msg)) return FALSE; 00501 } 00502 } 00503 count += MYBOOTY_CHUNK; 00504 } 00505 // These two lines really shouldn't be necessary but for some reason the first start 00506 // command is ignored and a short delay is required before repeating. Using only a 00507 // delay, even a very long one, doesn't work. 00508 // 00509 // NOTE: This measure isn't required when uploading an external S19 bootloader file! 00510 // 00511 T5StartBootLoader(address); 00512 wait_ms(1); 00513 // 00514 return T5StartBootLoader(address); 00515 } 00516 00517 00518 // 00519 // t5_can_send_boot_loader_S19 00520 // 00521 // Sends a 'bootloader' file, booty.s19 to the T5 ECU. 00522 // The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format. 00523 // 00524 // The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-) 00525 // 00526 // Sending the 'bootloader' to the T5 ECU takes just over 1 second. 00527 // 00528 // inputs: none 00529 // return: bool TRUE if all went OK, 00530 // FALSE if the 'bootloader' wasn't sent for some reason. 00531 // 00532 bool t5_can_send_boot_loader_S19() 00533 { 00534 printf("Starting the bootloader.\r\n"); 00535 FILE *fp = fopen("/local/MyBooty.S19", "r"); // Open "booty.s19" on the local file system for reading 00536 if (!fp) { 00537 printf("Error: I could not find the bootloader file MyBooty.S19\r\n"); 00538 return FALSE; 00539 } 00540 int c = 0; // for some reason fgetc returns an int instead of a char 00541 uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal 00542 uint8_t asize = 0; // 2,3 or 4 bytes in address 00543 uint32_t address = 0; // address to put S-record 00544 uint8_t checksum = 0; // checksum check at the end of each S-record line 00545 char msg[8]; // Construct the bootloader frame for uploading 00546 bool sent = FALSE; 00547 00548 while (!sent) { 00549 // get characters until we get an 'S' (throws away \n,\r characters and other junk) 00550 do c = fgetc (fp); 00551 while (c != 'S' && c != EOF); 00552 // if (c == EOF) return '\a'; 00553 c = fgetc(fp); // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) 00554 // if ((c = fgetc(fp)) == EOF) return '\a'; // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) 00555 switch (c) { 00556 case '0': 00557 break; // Skip over S0 header record 00558 00559 case '1': 00560 case '2': 00561 case '3': 00562 asize = 1 + c - '0'; // 2, 3 or 4 bytes for address 00563 address = 0; 00564 // get the number of bytes (in ascii format) in this S-record line 00565 // there must be at least the address and the checksum so return with an error if less than this! 00566 if ((c = SRecGetByte(fp)) < (asize + 1)) break; 00567 // if ((c = SRecGetByte(fp)) < 3) return '\a'; 00568 count = c; 00569 checksum = c; 00570 // get the address 00571 for (uint8_t i=0; i<asize; i++) { 00572 c = SRecGetByte(fp); 00573 checksum += c; 00574 address <<= 8; 00575 address |= c; 00576 count--; 00577 } 00578 // send a bootloader address message 00579 //// printf("address %x count %x\r\n",address ,count-1 ); 00580 if (!t5_can_send_boot_address(address, (count-1))) return FALSE; 00581 // get and send the bootloader frames for this S-record 00582 // NOTE the last frame sent may have less than 7 real data bytes but 7 bytes are always sent. In this case the unnecessary bytes 00583 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes 00584 // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame. 00585 for (uint8_t i=0; i<count-1; i++) { 00586 c = SRecGetByte(fp); 00587 checksum += c; 00588 msg[1+(i%7)] = c; 00589 if (i%7 == 0) msg[0]=i; // set the index number 00590 if ((i%7 == 6) || (i == count - 2)) { 00591 //// printf("Sending %2x %2x %2x %2x %2x %2x %2x %2x \r\n", msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6], msg[7] ); 00592 if (!t5_can_send_boot_frame(msg)) { 00593 fclose(fp); 00594 return FALSE; 00595 } 00596 } 00597 } 00598 // get the checksum 00599 if ((checksum += SRecGetByte(fp)) != 0xFF) { 00600 printf("Error in S-record, checksum = %2x\r\n", checksum); 00601 fclose(fp); 00602 return FALSE; 00603 } 00604 break; 00605 00606 case '5': 00607 break; // Skip over S5 record types 00608 00609 case '7': 00610 case '8': 00611 case '9': 00612 asize = 11 - (c - '0'); // 2, 3 or 4 bytes for address 00613 address = 0; 00614 // get the number of bytes (in ascii format) in this S-record line there must be just the address and the checksum 00615 // so return with an error if other than this! 00616 if ((c = SRecGetByte(fp)) != (asize + 1)) break; 00617 // if ((c = SRecGetByte(fp)) < 3) return '\a'; 00618 checksum = c; 00619 // get the address 00620 for (uint8_t i=0; i<asize; i++) { 00621 c = SRecGetByte(fp); 00622 checksum += c; 00623 address <<= 8; 00624 address |= c; 00625 } 00626 // get the checksum 00627 if ((checksum += SRecGetByte(fp)) != 0xFF) { 00628 fclose(fp); 00629 printf("Error in S-record, checksum = %2x\r\n", checksum); 00630 return FALSE; 00631 } 00632 T5StartBootLoader(address); 00633 // T5WaitResponsePrint(); 00634 sent = TRUE; 00635 break; 00636 00637 // Some kind of invalid S-record type so break 00638 default: 00639 fclose(fp); 00640 printf("oops - didn't recognise that S-Record \r\n"); 00641 return FALSE; 00642 } 00643 } 00644 fclose(fp); 00645 return TRUE; 00646 } 00647 00648 // 00649 // t5_can_get_checksum 00650 // 00651 // Calculates the checksum of the FLASH in the T5 ECU. 00652 // The 'bootloader', booty.s19, must be loaded before this function can be used. 00653 // The 'bootloader' actually calculates the checksum and compares it with the 00654 // value stored in the 'header' region at the end of the FLASH. 00655 // 00656 // The bootloader sends a single CAN message with the result e.g. 00657 // w00C8C800CAFEBABE0808 00658 // 00C - T5 response messages have an CAN id of 00C 00659 // 8 - All T5 messages have a message length of 8 bytes 00660 // C8 - This is the checksum message type 00661 // 00 - 00 means OK, the checksum calculation matches the stored value which in this case is: 00662 // CAFEBABE - lol :-) 00663 // 00664 // w00C8C801FFFFFFFF0808 00665 // 01 - 01 means calculated value doesn't matched the stored value 00666 // FFFFFFFF - in this case the stored value is FFFFFFFF - the chips might be erased 00667 // 00668 // Calculating the checksum takes a little under 2 seconds. 00669 // 00670 // inputs: none 00671 // return: bool TRUE if all went OK, 00672 // 00673 bool t5_can_get_checksum() 00674 { 00675 uint32_t checksum = 0; 00676 if (!t5_boot_checksum_command(&checksum)) { 00677 printf("Error The ECU's checksum is wrong!\r\n"); 00678 return FALSE; 00679 } 00680 printf("The FLASH checksum value is %#010x.\r\n", checksum); 00681 return TRUE; 00682 } 00683 00684 // 00685 // t5_can_bootloader_reset 00686 // 00687 // Exit the Bootloader and restart the T5 ECU 00688 // 00689 // inputs: none 00690 // outputs: bool TRUE if all went OK. 00691 // 00692 bool t5_can_bootloader_reset() 00693 { 00694 printf("Exiting the bootloader and restarting the T5 ECU.\r\n"); 00695 if (!t5_boot_reset_command()) { 00696 printf("Error trying to reset the T5 ECU!\r\n"); 00697 return FALSE; 00698 } 00699 return TRUE; 00700 } 00701 00702 // 00703 // t5_can_get_start_and_chip_types 00704 // 00705 // Gets the FLASH chip type fitted. 00706 // 00707 // NOTE the bootloader must be loaded in order to use this function. 00708 // 00709 // CAN messages from the T5 ECU with FLASH data look like this: 00710 // 00711 // C9,00,aa,aa,aa,aa,mm,dd, 00712 // 00713 // C9 lets us know its a FLASH id message 00714 // 00715 // aa,aa,aa,aa is the FLASH start address which we can use to work out if this is a T5.2 or T5.5 ECU 00716 // 0x00020000 - T5.2 00717 // 0x00040000 - T5.5 00718 // 00719 // mm = Manufacturer id. These can be: 00720 // 0x89 - Intel 00721 // 0x31 - CSI/CAT 00722 // 0x01 - AMD 00723 // 0x1F - Atmel 00724 // 00725 // dd = Device id. These can be: 00726 // 0xB8 - Intel _or_ CSI 28F512 (Fiited by Saab in T5.2) 00727 // 0xB4 - Intel _or_ CSI 28F010 (Fitted by Saab in T5.5) 00728 // 0x25 - AMD 28F512 (Fiited by Saab in T5.2) 00729 // 0xA7 - AMD 28F010 (Fitted by Saab in T5.5) 00730 // 0x20 - AMD 29F010 (Some people have put these in their T5.5) 00731 // 0x5D - Atmel 29C512 (Some people mave have put these in their T5.2) 00732 // 0xD5 - Atmel 29C010 (Some people have put these in their T5.5) 00733 // 00734 // mm = 0xFF, dd == 0xF7 probably means that the programming voltage isn't right. 00735 // 00736 // Finding out which ECU type and FLASH chips are fitted takes under a second. 00737 // 00738 // inputs: start T5 ecu start address 00739 // return: bool TRUE if all went OK. 00740 // 00741 bool t5_can_get_start_and_chip_types(uint32_t* start) 00742 { 00743 *start = 0; 00744 uint8_t make = 0; 00745 uint8_t type = 0; 00746 if (!t5_boot_get_flash_type(start, &make, &type)) { 00747 printf("Error trying to find out the ECU's first address and FLASH chip type!\r\n"); 00748 return FALSE; 00749 } 00750 printf("This is a T5.%s ECU with ", ((*start == T52FLASHSTART) ? "2" : "5")); 00751 switch (make) { 00752 case AMD: 00753 printf("AMD "); 00754 break; 00755 case CSI: 00756 printf("CSI "); 00757 break; 00758 case INTEL: 00759 printf("INTEL "); 00760 break; 00761 case ATMEL: 00762 printf("ATMEL "); 00763 break; 00764 case SST: 00765 printf("SST "); 00766 break; 00767 case ST: 00768 printf("ST "); 00769 break; 00770 case AMIC: 00771 printf("AMIC "); 00772 break; 00773 default: 00774 printf("\r\nUNKNOWN Manufacturer Id: %02x - Also check pin65 has enough volts!\r\n", make); 00775 } 00776 switch (type) { 00777 case AMD28F512: 00778 case INTEL28F512: 00779 printf("28F512 FLASH chips.\r\n"); 00780 break; 00781 case ATMEL29C512: 00782 printf("29C512 FLASH chips.\r\n"); 00783 break; 00784 case ATMEL29C010: 00785 printf("29C010 FLASH chips.\r\n"); 00786 break; 00787 case AMD28F010: 00788 case INTEL28F010: 00789 printf("28F010 FLASH chips.\r\n"); 00790 break; 00791 case AMD29F010: 00792 case SST39SF010: 00793 // case ST29F010: // Same as AMD29F010 00794 case AMICA29010L: 00795 printf("29F010 FLASH chips.\r\n"); 00796 break; 00797 default: 00798 printf("UNKNOWN Device Id: %02x - Also check pin65 has enough volts!\r\n", type); 00799 return FALSE; 00800 } 00801 return TRUE; 00802 } 00803 00804 // 00805 // t5_can_erase_flash 00806 // 00807 // Erases the FLASH Chips. 00808 // 00809 // NOTE the bootloader must be loaded in order to use this function. 00810 // 00811 // CAN messages from the T5 ECU with FLASH erase command look like this: 00812 // 00813 // C0,cc,08,08,08,08,08,08 00814 // 00815 // C0 tells us this is a response to the FLASH erase command. 00816 // 00817 // cc is a code that tells us what happened: 00818 // 00 - FLASH was erased OK 00819 // 01 - Could not erase FLASH chips to 0xFF 00820 // 02 - Could not write 0x00 to 28F FLASH chips 00821 // 03 - Unrecognised FLASH chip type (or maybe programming voltage isn't right) 00822 // 04 - Intel chips found, but unrecognised type 00823 // 05 - AMD chips found, but unrecognised type 00824 // 06 - CSI/Catalyst chips found, but unrecognised type 00825 // 07 - Atmel chips found - Atmel chips don't need to be erased 00826 // 00827 // Erasing 28F type FLASH chips takes around 22 seconds. 29F chips take around 4 seconds. 00828 // 00829 // inputs: none 00830 // return: bool TRUE if all went OK. 00831 // 00832 bool t5_can_erase_flash() 00833 { 00834 printf("Erasing the FLASH chips.\r\n"); 00835 if (!t5_boot_erase_command()) { 00836 printf("Error The ECU's FLASH has not been erased!\r\n"); 00837 return FALSE; 00838 } 00839 return TRUE; 00840 } 00841 00842 // 00843 // t5_can_dump_flash 00844 // 00845 // Dumps the FLASH chip BIN file, original.bin to the mbed 'disk' 00846 // 00847 // NOTE the bootloader must be loaded in order to use this function. 00848 // 00849 // Dumping FLASH chips in a T5.5 ECU takes around 35 seconds. 00850 // Dumping T5.2 ECUs should take about half of this time 00851 // 00852 // inputs: start start address of the T5.x FLASH 00853 00854 // return: bool TRUE if all went OK, FALSE if there was an error. 00855 // 00856 bool t5_can_dump_flash(uint32_t start) 00857 { 00858 printf("Saving the original FLASH BIN file.\r\n"); 00859 FILE *fp = fopen("/local/original.bin", "w"); // Open "original.bin" on the local file system for writing 00860 if (!fp) { 00861 perror ("The following error occured"); 00862 return FALSE; 00863 } 00864 uint32_t address = start + 5; // Mysterious reason for starting at 5 !!! 00865 char FLASHdata[6]; 00866 printf(" 0.00 %% complete.\r"); 00867 while (address < TRIONICLASTADDR) { 00868 if (!t5_can_read_data(FLASHdata, address)) { 00869 fclose (fp); 00870 printf ("Error reading from the CAN bus.\r\n"); 00871 return FALSE; 00872 } 00873 address += 6; 00874 if (fwrite(FLASHdata, 1, 6, fp) != 6) { 00875 fclose (fp); 00876 printf ("Error writing to the FLASH BIN file.\r\n"); 00877 return FALSE; 00878 } 00879 printf("%6.2f\r", 100*(float)(address-start)/(float)(TRIONICLASTADDR - start) ); 00880 } 00881 // There are a few more bytes to get because because the bin file is not an exact multiple of 6 bytes! 00882 // the % (modulo) mathematics function tells us how many bytes there are still to get 00883 if (!t5_can_read_data(FLASHdata, (TRIONICLASTADDR))) { 00884 fclose (fp); 00885 printf ("Error reading from the CAN bus.\r\n"); 00886 return FALSE; 00887 } 00888 if (fwrite((FLASHdata + 6 - ((TRIONICLASTADDR - start +1) % 6)), 1, ((TRIONICLASTADDR - start +1) % 6), fp) != ((TRIONICLASTADDR - start +1) % 6)) { 00889 fclose (fp); 00890 printf ("Error writing to the FLASH BIN file.\r\n"); 00891 return FALSE; 00892 } 00893 printf("100.00 %% complete.\r\n"); 00894 fclose(fp); 00895 return TRUE; 00896 } 00897 00898 00899 // 00900 // t5_can_send_flash_bin_update 00901 // 00902 // Sends a FLASH update file, modified.bin to the T5 ECU. 00903 // The FLASH update file is stored on the local file system and must be in hex format. 00904 // 00905 // FLASHing a T5.5 ECU takes around 40 seconds. FLASHing T5.2 ECUs should take about half of this time 00906 // 00907 // inputs: start start address of the T5.x FLASH 00908 00909 // return: bool TRUE if all went OK, 00910 // FALSE if the FLASH update failed for some reason. 00911 // 00912 bool t5_can_send_flash_bin_update(uint32_t start) 00913 { 00914 printf("Programming the FLASH chips.\r\n"); 00915 FILE *fp = fopen("/local/modified.bin", "r"); // Open "modified.bin" on the local file system for reading 00916 if (!fp) { 00917 printf("Error: I could not find the BIN file MODIFIED.BIN\r\n"); 00918 return FALSE; 00919 } 00920 00921 // obtain file size - it should match the size of the FLASH chips: 00922 fseek (fp , 0 , SEEK_END); 00923 uint32_t file_size = ftell (fp); 00924 rewind (fp); 00925 00926 // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU 00927 uint8_t stack_byte = 0; 00928 uint32_t stack_long = 0; 00929 if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; 00930 stack_long |= (stack_byte << 24); 00931 if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; 00932 stack_long |= (stack_byte << 16); 00933 if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; 00934 stack_long |= (stack_byte << 8); 00935 if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; 00936 stack_long |= stack_byte; 00937 rewind (fp); 00938 00939 if (start == T52FLASHSTART && (file_size != T52FLASHSIZE || stack_long != T5POINTER)) { 00940 fclose(fp); 00941 printf("The BIN file does not appear to be for a T5.2 ECU :-(\r\n"); 00942 printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T52FLASHSIZE, stack_long); 00943 return TERM_ERR; 00944 } 00945 if (start == T55FLASHSTART && (file_size != T55FLASHSIZE || stack_long != T5POINTER)) { 00946 fclose(fp); 00947 printf("The BIN file does not appear to be for a T5.5 ECU :-(\r\n"); 00948 printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T55FLASHSIZE, stack_long); 00949 return TERM_ERR; 00950 } 00951 00952 char msg[8]; // Construct the bootloader frame for uploading 00953 uint32_t curr_addr = start; // address to put FLASH data 00954 uint8_t byte_value = 0; 00955 00956 printf(" 0.00 %% complete.\r"); 00957 while (curr_addr <= TRIONICLASTADDR) { 00958 00959 // send a bootloader address message 00960 if (!t5_can_send_boot_address(curr_addr, 0x80)) { 00961 fclose(fp); 00962 printf("\r\nError sending Block Start message. Address: %08x\r\n",curr_addr); 00963 return FALSE; 00964 } 00965 // Construct and send the bootloader frames 00966 // NOTE the last frame sent may have less than 7 real data bytes but 7 bytes are always sent. In this case the unnecessary bytes 00967 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes 00968 // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame. 00969 for (uint8_t i=0; i<0x80; i++) { 00970 if (!fread(&byte_value,1,1,fp)) { 00971 fclose(fp); 00972 printf("\r\nError reading the BIN file MODIFIED.BIN\r\n"); 00973 return FALSE; 00974 } 00975 msg[1+(i%7)] = byte_value; 00976 if (i%7 == 0) msg[0]=i; // set the index number 00977 if ((i%7 == 6) || (i == 0x80 - 1)) 00978 if (!t5_can_send_boot_frame(msg)) { 00979 fclose(fp); 00980 printf("\r\nError sending a Block data message. Address: %08x, Index: %02x\r\n",curr_addr, i); 00981 return FALSE; 00982 } 00983 00984 } 00985 curr_addr += 0x80; 00986 printf("%6.2f\r", 100*(float)(curr_addr - start)/(float)(TRIONICLASTADDR - start) ); 00987 } 00988 printf("100.00 %% complete.\r\n"); 00989 fclose(fp); 00990 return TRUE; 00991 } 00992 00993 // 00994 // t5_can_send_flash_s19_update 00995 // 00996 // Sends a FLASH update file, modified.s19 to the T5 ECU. 00997 // The FLASH update file is stored on the local file system and must be in S19 format. 00998 // 00999 // FLASHing a T5.5 ECU takes around 60 seconds. FLASHing T5.2 ECUs should take about half of this time 01000 // 01001 // inputs: start start address of the T5.x FLASH 01002 01003 // return: bool TRUE if all went OK, 01004 // FALSE if the FLASH update failed for some reason. 01005 // 01006 bool t5_can_send_flash_s19_update(uint32_t start) 01007 { 01008 printf("Programming the FLASH chips.\r\n"); 01009 FILE *fp = fopen("/local/modified.s19", "r"); // Open "modified.s19" on the local file system for reading 01010 if (!fp) { 01011 printf("Error: I could not find the BIN file MODIFIED.S19\r\n"); 01012 return FALSE; 01013 } 01014 int c = 0; // for some reason fgetc returns an int instead of a char 01015 uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal 01016 uint8_t asize = 0; // 2,3 or 4 bytes in address 01017 uint32_t address = 0; // address to put S-record 01018 uint8_t checksum = 0; // checksum check at the end 01019 char msg[8]; // Construct the bootloader frame for uploading 01020 bool sent = FALSE; 01021 01022 while (!sent) { 01023 01024 do c = fgetc (fp); // get characters until we get an 'S' (throws away \n,\r characters and other junk) 01025 while (c != 'S' && c != EOF); 01026 // if (c == EOF) return '\a'; 01027 c = fgetc(fp); // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) 01028 // if ((c = fgetc(fp)) == EOF) return '\a'; // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) 01029 switch (c) { 01030 case '0': 01031 break; // Skip over S0 header record 01032 01033 case '1': 01034 case '2': 01035 case '3': 01036 asize = 1 + c - '0'; // 2, 3 or 4 bytes for address 01037 address = 0; 01038 // get the number of bytes (in ascii format) in this S-record line 01039 // there must be at least the address and the checksum so return with an error if less than this! 01040 if ((c = SRecGetByte(fp)) < (asize + 1)) { 01041 fclose(fp); 01042 printf("Error in S-record address"); 01043 return FALSE; 01044 } 01045 count = c; 01046 checksum = c; 01047 // get the address 01048 for (uint8_t i=0; i<asize; i++) { 01049 c = SRecGetByte(fp); 01050 checksum += c; 01051 address <<= 8; 01052 address |= c; 01053 count--; 01054 } 01055 // send a bootloader address message - Adding the start address that was supplied (for T5.2/T5.5) 01056 if (!t5_can_send_boot_address((address + start), (count - 1))) { 01057 fclose(fp); 01058 printf("Error sending CAN message"); 01059 return FALSE; 01060 } 01061 // get and send the bootloader frames for this S-record 01062 // NOTE the last frame sent may have less than 7 real data bytes but 7 bytes are always sent. In this case the unnecessary bytes 01063 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes 01064 // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame. 01065 for (uint8_t i=0; i<count-1; i++) { 01066 c = SRecGetByte(fp); 01067 checksum += c; 01068 msg[1+(i%7)] = c; 01069 if (i%7 == 0) msg[0]=i; // set the index number 01070 if ((i%7 == 6) || (i == count - 2)) 01071 if (!t5_can_send_boot_frame(msg)) { 01072 fclose(fp); 01073 printf("Error sending CAN message"); 01074 return FALSE; 01075 } 01076 } 01077 // get the checksum 01078 if ((checksum += SRecGetByte(fp)) != 0xFF) { 01079 fclose(fp); 01080 printf("Error in S-record, checksum = %2x\r\n", checksum); 01081 return FALSE; 01082 } 01083 break; 01084 01085 case '5': 01086 break; // Skip over S5 record types 01087 01088 case '7': 01089 case '8': 01090 case '9': 01091 sent = TRUE; 01092 break; 01093 01094 // Some kind of invalid S-record type so break 01095 default: 01096 printf("oops - didn't recognise that S-Record\r\n"); 01097 fclose(fp); 01098 return FALSE; 01099 } 01100 } 01101 fclose(fp); 01102 return TRUE; 01103 } 01104 01105 // 01106 // t5_can_get_last_address 01107 // 01108 // Sends a C3 Message to the T5 ECU. The reply should contain the last used FLASH address 01109 // which is always 0x0007FFFF 01110 // 01111 // The last 2 bytes of the message might be useful to work out whether or not the bootloader 01112 // has been loaded. The 'mode' value will be 0x0808 when the bootloader is running or 0x89b8 01113 // when it isn't. 01114 // 01115 // inputs: none 01116 // return: bool TRUE if all went OK, 01117 // 01118 bool t5_can_get_last_address() 01119 { 01120 uint32_t last_address = 0; 01121 uint16_t mode = 0; 01122 if (!t5_boot_c3_command(&last_address, &mode)) { 01123 printf("Error trying to find out the ECU's last address and mode!\r\n"); 01124 return FALSE; 01125 } 01126 printf("The Last used FLASH address is: %#010x, mode %#06x\r\n", last_address, mode); 01127 return TRUE; 01128 }
Generated on Fri Jul 15 2022 00:43:05 by 1.7.2