Sophie Dexter
/
Just4Trionic
Just4Trionic - CAN and BDM FLASH programmer for Saab cars
t5can.cpp
- Committer:
- Just4pLeisure
- Date:
- 2015-04-25
- Revision:
- 5:1775b4b13232
- Parent:
- 3:92dae9083c83
File content as of revision 5:1775b4b13232:
/******************************************************************************* trionic5.cpp - CAN Bus functions for Just4Trionic by Just4pLeisure (c) 2010 by Sophie Dexter This C++ module provides functions for reading and writing the FLASH chips and SRAM in Trionic5 ECUs. (Writing the adaption data back to SRAM not done yet). Some functions need an additional 'bootloader' program to be sent to the T5 ECU before they can be used. These functions are: Identifying the T5 ECU type and FLASH chips, dumping the FLASH chips, erasing the FLASH chips, writing to the FLASH chips and calculating the FLASH chips' checksum. My version of the bootloader, BOOTY.S19, includes some features not in other bootloaders; identifying the ECU and FLASH chip types, a 'safer' way of dumping the FLASH chips and the ability to program AMD 29F010 type FLASH chips ******************************************************************************** WARNING: Use at your own risk, sadly this software comes with no guarantees. This software is provided 'free' and in good faith, but the author does not accept liability for any damage arising from its use. *******************************************************************************/ #include "t5can.h" // constants #define CMD_BUF_LENGTH 32 ///< command buffer size // static variables static char cmd_buffer[CMD_BUF_LENGTH]; ///< command string buffer //static uint32_t cmd_addr; ///< address (optional) //static uint32_t cmd_value; ///< value (optional) //static uint32_t cmd_result; ///< result static uint32_t flash_start = 0; // private functions uint8_t execute_t5_cmd(); void t5_can_show_help(); void t5_can_show_full_help(); void t5_can() { // Start the CAN bus system // Note that at the moment this is only for T5 ECUs at 615 kbits can_open(); can_set_speed(615000); t5_can_show_help(); // main loop *cmd_buffer = '\0'; char ret; char rx_char; while (true) { // read chars from USB // send received messages to the pc over USB connection // This function displays any CAN messages that are 'missed' by the other functions // Can messages might be 'missed' because they are received after a 'timeout' period // or because they weren't expected, e.g. if the T5 ECU resets for some reason t5_can_show_can_message(); if (pc.readable()) { // turn Error LED off for next command led4 = 0; rx_char = pc.getc(); switch (rx_char) { // 'ESC' key to go back to mbed Just4Trionic 'home' menu case '\e': can_close(); return; // end-of-command reached case TERM_OK : // execute command and return flag via USB timer.reset(); timer.start(); ret = execute_t5_cmd(); pc.putc(ret); printf("Completed in %.3f seconds.\r\n", timer.read()); // reset command buffer *cmd_buffer = '\0'; // light up LED // ret == TERM_OK ? led_on(LED_ACT) : led_on(LED_ERR); ret == TERM_OK ? led3 = 1 : led4 = 1; break; // another command char default: // store in buffer if space permits if (StrLen(cmd_buffer) < CMD_BUF_LENGTH - 1) { StrAddc(cmd_buffer, rx_char); } break; } } } } //----------------------------------------------------------------------------- /** Executes a command and returns result flag (does not transmit the flag itself). @return command flag (success / failure) */ uint8_t execute_t5_cmd() { // uint8_t cmd_length = strlen(cmd_buffer); // command groups switch (*cmd_buffer) { // CHECK_ARGLENGTH(0); // Get the Symbol Table case 's': return t5_can_get_symbol_table() ? TERM_OK : TERM_ERR; case 'S': return T5ReadCmnd(T5SYMBOLS) ? TERM_OK : TERM_ERR; // Get the Trionic5 software version string case 'v': return t5_can_get_version() ? TERM_OK : TERM_ERR; case 'V': return T5ReadCmnd(T5VERSION) ? TERM_OK : TERM_ERR; // Read Adaption Data from RAM and write it to a file case 'r': case 'R': return t5_can_get_adaption_data() ? TERM_OK : TERM_ERR; // CR - send CR type message case '\0': return T5ReadCmnd(CR) ? TERM_OK : TERM_ERR; // Get a single symbol from the Symbol Table case 'a': char symbol[40]; T5GetSymbol(symbol); printf("%s",symbol); return TERM_OK; // Just send an 'ACK' message case 'A': return T5Ack() ? TERM_OK : TERM_ERR; // Send a Bootloader file to the T5 ECU case 'b': return (t5_can_send_boot_loader() && can_set_speed(1000000)) ? TERM_OK : TERM_ERR; case 'B': return (t5_can_send_boot_loader_S19() && can_set_speed(1000000)) ? TERM_OK : TERM_ERR; // Get Checksum from ECU (Bootloader must be uploaded first) case 'c': case 'C': return t5_can_get_checksum() ? TERM_OK : TERM_ERR; // Exit the BootLoader and restart the T5 ECU case 'q': case 'Q': return (t5_can_bootloader_reset() && can_set_speed(615000)) ? TERM_OK : TERM_ERR; // Erase the FLASH chips case 'e': case 'E': return t5_can_erase_flash() ? TERM_OK : TERM_ERR; // Read back the FLASH chip types case 't': case 'T': return t5_can_get_start_and_chip_types(&flash_start) ? TERM_OK : TERM_ERR; // DUMP the T5 ECU BIN file stored in the FLASH chips case 'd': // NOTE 'd' command Just4TESTING! only dumps T5.5 ECU return t5_can_dump_flash(T55FLASHSTART) ? TERM_OK : TERM_ERR; case 'D': // if (!t5_can_send_boot_loader_S19()) if (!t5_can_send_boot_loader()) return TERM_ERR; can_set_speed(1000000); if (!t5_can_get_start_and_chip_types(&flash_start)) { t5_can_bootloader_reset(); can_set_speed(615000); return TERM_ERR; } return (t5_can_dump_flash(flash_start) && t5_can_bootloader_reset() && can_set_speed(615000)) ? TERM_OK : TERM_ERR; // Send a FLASH update file to the T5 ECU case 'f': // NOTE 'f' command Just4TESTING! only FLASHes T5.5 ECU (with S19 type file) //return t5_can_send_flash_s19_update(T55FLASHSTART) return t5_can_send_flash_bin_update(T55FLASHSTART) ? TERM_OK : TERM_ERR; case 'F': // if (!t5_can_send_boot_loader_S19()) if (!t5_can_send_boot_loader()) return TERM_ERR; can_set_speed(1000000); if (!t5_can_get_start_and_chip_types(&flash_start)) { t5_can_bootloader_reset(); can_set_speed(615000); return TERM_ERR; } if (!t5_can_get_checksum()) led4 = 1; if (!t5_can_erase_flash()) { t5_can_bootloader_reset(); can_set_speed(615000); return TERM_ERR; } return (t5_can_send_flash_bin_update(flash_start) && t5_can_get_checksum() && t5_can_bootloader_reset() && can_set_speed(615000)) ? TERM_OK : TERM_ERR; // Send the C3 message - should get last used address 0x7FFFF case '3': return t5_can_get_last_address() ? TERM_OK : TERM_ERR; // Print help case 'h': t5_can_show_help(); return TERM_OK; case 'H': t5_can_show_full_help(); return TERM_OK; default: t5_can_show_help(); break; } // unknown command return TERM_ERR; } // // Trionic5ShowHelp // // Displays a list of things that can be done with the T5 ECU. // // inputs: none // return: none // void t5_can_show_help() { printf("Trionic 5 Command Menu\r\n"); printf("======================\r\n"); printf("D - DUMP the T5.x ECU FLASH to a file 'ORIGINAL.BIN'\r\n"); printf("F - FLASH the update file 'MODIFIED.BIN' to the T5.x\r\n"); printf("\r\n"); printf("r - read SRAM and write it to ADAPTION.RAM file\r\n"); printf("s - read Symbol Table and write it to SYMBOLS.TXT\r\n"); printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n"); printf("\r\n"); printf("'ESC' - Return to Just4Trionic Main Menu\r\n"); printf("\r\n"); printf("h - Show this help menu\r\n"); printf("\r\n"); return; } // // t5_can_show_full_help // // Displays a complete list of things that can be done with the T5 ECU. // // inputs: none // return: none // void t5_can_show_full_help() { printf("Trionic 5 Command Menu\r\n"); printf("======================\r\n"); printf("D - DUMP the T5.x ECU FLASH to a file 'ORIGINAL.BIN'\r\n"); printf("F - FLASH the update file 'MODIFIED.BIN' to the T5.x\r\n"); printf("\r\n"); printf("b - upload and start MyBooty.S19 bootloader\r\n"); printf("c - get T5 ECU FLASH checksum (need to upload BOOTY.S19 before using this command)\r\n"); printf("d - dump the T5 FLASH BIN file and write it to ORIGINAL.BIN\r\n"); printf("e - erase the FLASH chips in the T5 ECU\r\n"); printf("f - FLASH the update file MODIFIED.BIN to the T5 ECU\r\n"); printf("r - read SRAM and write it to ADAPTION.RAM file\r\n"); printf("s - read Symbol Table, display it and write it to SYMBOLS.TXT\r\n"); printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n"); printf("q - exit the bootloader and reset the T5 ECU\r\n"); printf("t - read the FLASH chip type in the T5 ECU\r\n"); printf("3 - read the last used FLASH address in the T5 ECU\r\n"); printf("S - send 's' message (to get symbol table)\r\n"); printf("V - send 'S' message (to get software version)\r\n"); printf("'Enter' Key - send an CR message\r\n"); printf("a - send an ACK\r\n"); printf("A - read a single symbol from the symbol table\r\n"); printf("\r\n"); printf("'ESC' - Return to Just4Trionic Main Menu\r\n"); printf("\r\n"); printf("H - Show this help menu\r\n"); printf("\r\n"); return; } // // t5_can_show_can_message // // Displays a CAN message in the RX buffer if there is one. // // inputs: none // return: bool TRUE if there was a message, FALSE if no message. // bool t5_can_show_can_message() { CANMessage can_MsgRx; if (can.read(can_MsgRx)) { printf("w%03x%d", can_MsgRx.id, can_MsgRx.len); for (char i=0; i<can_MsgRx.len; i++) { printf("%02x", can_MsgRx.data[i]); } printf(" %c ", can_MsgRx.data[2]); printf("\r\n"); return TRUE; } return FALSE; } // // t5_can_get_symbol_table // // Gets the T5 ECU symbol table. // The Symbol Table is saved to a file, symbols.txt, on the mbed 'local' file system 'disk'. // // inputs: none // return: bool TRUE if there all went OK, FALSE if there was an error // bool t5_can_get_symbol_table() { printf("Saving the symbol table file\r\n"); FILE *fp = fopen("/local/symbols.txt", "w"); // Open "symbols.txt" on the local file system for writing if (!fp) { perror ("The following error occured"); return FALSE; } char symbol[40]; T5ReadCmnd(T5SYMBOLS); if (T5WaitResponse() != '>') { fclose(fp); return FALSE; } T5ReadCmnd(CR); if (T5WaitResponse() != '>') { fclose(fp); return FALSE; } do { T5GetSymbol(symbol); // printf("%s",symbol); if (fprintf(fp,"%s",symbol) < 0) { fclose (fp); printf ("ERROR Writing to the symbols.txt file!\r\n"); return FALSE; }; } while (!StrCmp(symbol,"END\r\n")); fclose(fp); return TRUE; } // // t5_can_get_version // // Gets the T5 software version string. // The software version is is sent to the PC and saved to a file, version.txt, on the mbed 'local' file system 'disk'. // // inputs: none // return: bool TRUE if there all went OK, FALSE if there was an error // bool t5_can_get_version() { FILE *fp = fopen("/local/version.txt", "w"); // Open "version.txt" on the local file system for writing if (!fp) { perror ("The following error occured"); return FALSE; } char symbol[40]; T5ReadCmnd(T5VERSION); if (T5WaitResponse() != '>') { fclose(fp); return FALSE; } T5ReadCmnd(CR); if (T5WaitResponse() != '>') { fclose(fp); return FALSE; } T5GetSymbol(symbol); printf("%s",symbol); if (fprintf(fp,"%s",symbol) < 0) { fclose (fp); printf ("ERROR Writing to the version.txt file!\r\n"); return FALSE; }; fclose(fp); printf("The version.txt file has been saved.\r\n"); return TRUE; } // // Trionic5GetAdaptionData // // Gets the adaption data from the T5's SRAM. // The adaption data is stored in a hex file, adaption.RAM, on the mbed 'local' file system 'disk'. // // Reading the Adaption data from SRAM takes about 6.5 seconds. // // inputs: none // return: bool TRUE if all went OK, FALSE if there was an error. // bool t5_can_get_adaption_data() { printf("Saving the SRAM adaption data.\r\n"); FILE *fp = fopen("/local/adaption.RAM", "w"); // Open "adaption.RAM" on the local file system for writing if (!fp) { printf("ERROR: Unable to open a file for the adaption data!\r\n"); return FALSE; } unsigned int address = 5; // Mysterious reason for starting at 5 !!! char RAMdata[6]; while (address < T5RAMSIZE) { if (!t5_can_read_data(RAMdata, address)) { fclose (fp); printf ("Error reading from the CAN bus.\r\n"); return FALSE; } address += 6; if (fwrite(RAMdata, 1, 6, fp) != 6) { fclose (fp); printf ("Error writing to the SRAM adaption file.\r\n"); return FALSE; } } // There are a few more bytes to get because because the SRAM file is not an exact multiple of 6 bytes! // the % (modulo) mathematics function tells us how many bytes there are still to get if (!t5_can_read_data(RAMdata, (T5RAMSIZE - 1))) { fclose (fp); printf ("Error reading from the CAN bus.\r\n"); return FALSE; } if (fwrite((RAMdata + 6 - (T5RAMSIZE % 6)), 1, (T5RAMSIZE % 6), fp) != (T5RAMSIZE % 6)) { fclose (fp); printf ("Error writing to the SRAM adaption file.\r\n"); return FALSE; } fclose(fp); return TRUE; } // // t5_can_send_boot_loader // // Sends a 'bootloader' file, booty.s19 to the T5 ECU. // The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format. // // The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-) // // Sending the 'bootloader' to the T5 ECU takes just over 1 second. // // inputs: none // return: bool TRUE if all went OK, // FALSE if the 'bootloader' wasn't sent for some reason. // bool t5_can_send_boot_loader() { uint32_t BootloaderSize = sizeof(T5BootLoader); uint32_t address = MYBOOTY_START; // Start and execute from address of T5Bootloader uint32_t count = 0; // progress count of bootloader bytes transferred char msg[8]; // Construct the bootloader frame for uploading printf("Starting the bootloader.\r\n"); while (count < BootloaderSize) { // send a bootloader address message if (!t5_can_send_boot_address((address+count), MYBOOTY_CHUNK)) return FALSE; // send bootloader frames // 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 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes // is sent with the upload address) and ignores any extra bytes in the last frame. for (uint8_t i=0; i<MYBOOTY_CHUNK; i++) { msg[1+(i%7)] = T5BootLoader[count+i]; if (i%7 == 0) msg[0]=i; // set the index number if ((i%7 == 6) || (i == MYBOOTY_CHUNK-1 )) { if (!t5_can_send_boot_frame(msg)) return FALSE; } } count += MYBOOTY_CHUNK; } // These two lines really shouldn't be necessary but for some reason the first start // command is ignored and a short delay is required before repeating. Using only a // delay, even a very long one, doesn't work. // // NOTE: This measure isn't required when uploading an external S19 bootloader file! // T5StartBootLoader(address); wait_ms(1); // return T5StartBootLoader(address); } // // t5_can_send_boot_loader_S19 // // Sends a 'bootloader' file, booty.s19 to the T5 ECU. // The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format. // // The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-) // // Sending the 'bootloader' to the T5 ECU takes just over 1 second. // // inputs: none // return: bool TRUE if all went OK, // FALSE if the 'bootloader' wasn't sent for some reason. // bool t5_can_send_boot_loader_S19() { printf("Starting the bootloader.\r\n"); FILE *fp = fopen("/local/MyBooty.S19", "r"); // Open "booty.s19" on the local file system for reading if (!fp) { printf("Error: I could not find the bootloader file MyBooty.S19\r\n"); return FALSE; } int c = 0; // for some reason fgetc returns an int instead of a char uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal uint8_t asize = 0; // 2,3 or 4 bytes in address uint32_t address = 0; // address to put S-record uint8_t checksum = 0; // checksum check at the end of each S-record line char msg[8]; // Construct the bootloader frame for uploading bool sent = FALSE; while (!sent) { // get characters until we get an 'S' (throws away \n,\r characters and other junk) do c = fgetc (fp); while (c != 'S' && c != EOF); // if (c == EOF) return '\a'; c = fgetc(fp); // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) // if ((c = fgetc(fp)) == EOF) return '\a'; // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) switch (c) { case '0': break; // Skip over S0 header record case '1': case '2': case '3': asize = 1 + c - '0'; // 2, 3 or 4 bytes for address address = 0; // get the number of bytes (in ascii format) in this S-record line // there must be at least the address and the checksum so return with an error if less than this! if ((c = SRecGetByte(fp)) < (asize + 1)) break; // if ((c = SRecGetByte(fp)) < 3) return '\a'; count = c; checksum = c; // get the address for (uint8_t i=0; i<asize; i++) { c = SRecGetByte(fp); checksum += c; address <<= 8; address |= c; count--; } // send a bootloader address message //// printf("address %x count %x\r\n",address ,count-1 ); if (!t5_can_send_boot_address(address, (count-1))) return FALSE; // get and send the bootloader frames for this S-record // 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 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame. for (uint8_t i=0; i<count-1; i++) { c = SRecGetByte(fp); checksum += c; msg[1+(i%7)] = c; if (i%7 == 0) msg[0]=i; // set the index number if ((i%7 == 6) || (i == count - 2)) { //// 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] ); if (!t5_can_send_boot_frame(msg)) { fclose(fp); return FALSE; } } } // get the checksum if ((checksum += SRecGetByte(fp)) != 0xFF) { printf("Error in S-record, checksum = %2x\r\n", checksum); fclose(fp); return FALSE; } break; case '5': break; // Skip over S5 record types case '7': case '8': case '9': asize = 11 - (c - '0'); // 2, 3 or 4 bytes for address address = 0; // get the number of bytes (in ascii format) in this S-record line there must be just the address and the checksum // so return with an error if other than this! if ((c = SRecGetByte(fp)) != (asize + 1)) break; // if ((c = SRecGetByte(fp)) < 3) return '\a'; checksum = c; // get the address for (uint8_t i=0; i<asize; i++) { c = SRecGetByte(fp); checksum += c; address <<= 8; address |= c; } // get the checksum if ((checksum += SRecGetByte(fp)) != 0xFF) { fclose(fp); printf("Error in S-record, checksum = %2x\r\n", checksum); return FALSE; } T5StartBootLoader(address); // T5WaitResponsePrint(); sent = TRUE; break; // Some kind of invalid S-record type so break default: fclose(fp); printf("oops - didn't recognise that S-Record \r\n"); return FALSE; } } fclose(fp); return TRUE; } // // t5_can_get_checksum // // Calculates the checksum of the FLASH in the T5 ECU. // The 'bootloader', booty.s19, must be loaded before this function can be used. // The 'bootloader' actually calculates the checksum and compares it with the // value stored in the 'header' region at the end of the FLASH. // // The bootloader sends a single CAN message with the result e.g. // w00C8C800CAFEBABE0808 // 00C - T5 response messages have an CAN id of 00C // 8 - All T5 messages have a message length of 8 bytes // C8 - This is the checksum message type // 00 - 00 means OK, the checksum calculation matches the stored value which in this case is: // CAFEBABE - lol :-) // // w00C8C801FFFFFFFF0808 // 01 - 01 means calculated value doesn't matched the stored value // FFFFFFFF - in this case the stored value is FFFFFFFF - the chips might be erased // // Calculating the checksum takes a little under 2 seconds. // // inputs: none // return: bool TRUE if all went OK, // bool t5_can_get_checksum() { uint32_t checksum = 0; if (!t5_boot_checksum_command(&checksum)) { printf("Error The ECU's checksum is wrong!\r\n"); return FALSE; } printf("The FLASH checksum value is %#010x.\r\n", checksum); return TRUE; } // // t5_can_bootloader_reset // // Exit the Bootloader and restart the T5 ECU // // inputs: none // outputs: bool TRUE if all went OK. // bool t5_can_bootloader_reset() { printf("Exiting the bootloader and restarting the T5 ECU.\r\n"); if (!t5_boot_reset_command()) { printf("Error trying to reset the T5 ECU!\r\n"); return FALSE; } return TRUE; } // // t5_can_get_start_and_chip_types // // Gets the FLASH chip type fitted. // // NOTE the bootloader must be loaded in order to use this function. // // CAN messages from the T5 ECU with FLASH data look like this: // // C9,00,aa,aa,aa,aa,mm,dd, // // C9 lets us know its a FLASH id message // // 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 // 0x00020000 - T5.2 // 0x00040000 - T5.5 // // mm = Manufacturer id. These can be: // 0x89 - Intel // 0x31 - CSI/CAT // 0x01 - AMD // 0x1F - Atmel // // dd = Device id. These can be: // 0xB8 - Intel _or_ CSI 28F512 (Fiited by Saab in T5.2) // 0xB4 - Intel _or_ CSI 28F010 (Fitted by Saab in T5.5) // 0x25 - AMD 28F512 (Fiited by Saab in T5.2) // 0xA7 - AMD 28F010 (Fitted by Saab in T5.5) // 0x20 - AMD 29F010 (Some people have put these in their T5.5) // 0x5D - Atmel 29C512 (Some people mave have put these in their T5.2) // 0xD5 - Atmel 29C010 (Some people have put these in their T5.5) // // mm = 0xFF, dd == 0xF7 probably means that the programming voltage isn't right. // // Finding out which ECU type and FLASH chips are fitted takes under a second. // // inputs: start T5 ecu start address // return: bool TRUE if all went OK. // bool t5_can_get_start_and_chip_types(uint32_t* start) { *start = 0; uint8_t make = 0; uint8_t type = 0; if (!t5_boot_get_flash_type(start, &make, &type)) { printf("Error trying to find out the ECU's first address and FLASH chip type!\r\n"); return FALSE; } printf("This is a T5.%s ECU with ", ((*start == T52FLASHSTART) ? "2" : "5")); switch (make) { case AMD: printf("AMD "); break; case CSI: printf("CSI "); break; case INTEL: printf("INTEL "); break; case ATMEL: printf("ATMEL "); break; case SST: printf("SST "); break; case ST: printf("ST "); break; case AMIC: printf("AMIC "); break; default: printf("\r\nUNKNOWN Manufacturer Id: %02x - Also check pin65 has enough volts!\r\n", make); } switch (type) { case AMD28F512: case INTEL28F512: printf("28F512 FLASH chips.\r\n"); break; case ATMEL29C512: printf("29C512 FLASH chips.\r\n"); break; case ATMEL29C010: printf("29C010 FLASH chips.\r\n"); break; case AMD28F010: case INTEL28F010: printf("28F010 FLASH chips.\r\n"); break; case AMD29F010: case SST39SF010: // case ST29F010: // Same as AMD29F010 case AMICA29010L: printf("29F010 FLASH chips.\r\n"); break; default: printf("UNKNOWN Device Id: %02x - Also check pin65 has enough volts!\r\n", type); return FALSE; } return TRUE; } // // t5_can_erase_flash // // Erases the FLASH Chips. // // NOTE the bootloader must be loaded in order to use this function. // // CAN messages from the T5 ECU with FLASH erase command look like this: // // C0,cc,08,08,08,08,08,08 // // C0 tells us this is a response to the FLASH erase command. // // cc is a code that tells us what happened: // 00 - FLASH was erased OK // 01 - Could not erase FLASH chips to 0xFF // 02 - Could not write 0x00 to 28F FLASH chips // 03 - Unrecognised FLASH chip type (or maybe programming voltage isn't right) // 04 - Intel chips found, but unrecognised type // 05 - AMD chips found, but unrecognised type // 06 - CSI/Catalyst chips found, but unrecognised type // 07 - Atmel chips found - Atmel chips don't need to be erased // // Erasing 28F type FLASH chips takes around 22 seconds. 29F chips take around 4 seconds. // // inputs: none // return: bool TRUE if all went OK. // bool t5_can_erase_flash() { printf("Erasing the FLASH chips.\r\n"); if (!t5_boot_erase_command()) { printf("Error The ECU's FLASH has not been erased!\r\n"); return FALSE; } return TRUE; } // // t5_can_dump_flash // // Dumps the FLASH chip BIN file, original.bin to the mbed 'disk' // // NOTE the bootloader must be loaded in order to use this function. // // Dumping FLASH chips in a T5.5 ECU takes around 35 seconds. // Dumping T5.2 ECUs should take about half of this time // // inputs: start start address of the T5.x FLASH // return: bool TRUE if all went OK, FALSE if there was an error. // bool t5_can_dump_flash(uint32_t start) { printf("Saving the original FLASH BIN file.\r\n"); FILE *fp = fopen("/local/original.bin", "w"); // Open "original.bin" on the local file system for writing if (!fp) { perror ("The following error occured"); return FALSE; } uint32_t address = start + 5; // Mysterious reason for starting at 5 !!! char FLASHdata[6]; printf(" 0.00 %% complete.\r"); while (address < TRIONICLASTADDR) { if (!t5_can_read_data(FLASHdata, address)) { fclose (fp); printf ("Error reading from the CAN bus.\r\n"); return FALSE; } address += 6; if (fwrite(FLASHdata, 1, 6, fp) != 6) { fclose (fp); printf ("Error writing to the FLASH BIN file.\r\n"); return FALSE; } printf("%6.2f\r", 100*(float)(address-start)/(float)(TRIONICLASTADDR - start) ); } // There are a few more bytes to get because because the bin file is not an exact multiple of 6 bytes! // the % (modulo) mathematics function tells us how many bytes there are still to get if (!t5_can_read_data(FLASHdata, (TRIONICLASTADDR))) { fclose (fp); printf ("Error reading from the CAN bus.\r\n"); return FALSE; } if (fwrite((FLASHdata + 6 - ((TRIONICLASTADDR - start +1) % 6)), 1, ((TRIONICLASTADDR - start +1) % 6), fp) != ((TRIONICLASTADDR - start +1) % 6)) { fclose (fp); printf ("Error writing to the FLASH BIN file.\r\n"); return FALSE; } printf("100.00 %% complete.\r\n"); fclose(fp); return TRUE; } // // t5_can_send_flash_bin_update // // Sends a FLASH update file, modified.bin to the T5 ECU. // The FLASH update file is stored on the local file system and must be in hex format. // // FLASHing a T5.5 ECU takes around 40 seconds. FLASHing T5.2 ECUs should take about half of this time // // inputs: start start address of the T5.x FLASH // return: bool TRUE if all went OK, // FALSE if the FLASH update failed for some reason. // bool t5_can_send_flash_bin_update(uint32_t start) { printf("Programming the FLASH chips.\r\n"); FILE *fp = fopen("/local/modified.bin", "r"); // Open "modified.bin" on the local file system for reading if (!fp) { printf("Error: I could not find the BIN file MODIFIED.BIN\r\n"); return FALSE; } // obtain file size - it should match the size of the FLASH chips: fseek (fp , 0 , SEEK_END); uint32_t file_size = ftell (fp); rewind (fp); // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU uint8_t stack_byte = 0; uint32_t stack_long = 0; if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= (stack_byte << 24); if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= (stack_byte << 16); if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= (stack_byte << 8); if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= stack_byte; rewind (fp); if (start == T52FLASHSTART && (file_size != T52FLASHSIZE || stack_long != T5POINTER)) { fclose(fp); printf("The BIN file does not appear to be for a T5.2 ECU :-(\r\n"); printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T52FLASHSIZE, stack_long); return TERM_ERR; } if (start == T55FLASHSTART && (file_size != T55FLASHSIZE || stack_long != T5POINTER)) { fclose(fp); printf("The BIN file does not appear to be for a T5.5 ECU :-(\r\n"); printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T55FLASHSIZE, stack_long); return TERM_ERR; } char msg[8]; // Construct the bootloader frame for uploading uint32_t curr_addr = start; // address to put FLASH data uint8_t byte_value = 0; printf(" 0.00 %% complete.\r"); while (curr_addr <= TRIONICLASTADDR) { // send a bootloader address message if (!t5_can_send_boot_address(curr_addr, 0x80)) { fclose(fp); printf("\r\nError sending Block Start message. Address: %08x\r\n",curr_addr); return FALSE; } // Construct and send the bootloader frames // 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 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame. for (uint8_t i=0; i<0x80; i++) { if (!fread(&byte_value,1,1,fp)) { fclose(fp); printf("\r\nError reading the BIN file MODIFIED.BIN\r\n"); return FALSE; } msg[1+(i%7)] = byte_value; if (i%7 == 0) msg[0]=i; // set the index number if ((i%7 == 6) || (i == 0x80 - 1)) if (!t5_can_send_boot_frame(msg)) { fclose(fp); printf("\r\nError sending a Block data message. Address: %08x, Index: %02x\r\n",curr_addr, i); return FALSE; } } curr_addr += 0x80; printf("%6.2f\r", 100*(float)(curr_addr - start)/(float)(TRIONICLASTADDR - start) ); } printf("100.00 %% complete.\r\n"); fclose(fp); return TRUE; } // // t5_can_send_flash_s19_update // // Sends a FLASH update file, modified.s19 to the T5 ECU. // The FLASH update file is stored on the local file system and must be in S19 format. // // FLASHing a T5.5 ECU takes around 60 seconds. FLASHing T5.2 ECUs should take about half of this time // // inputs: start start address of the T5.x FLASH // return: bool TRUE if all went OK, // FALSE if the FLASH update failed for some reason. // bool t5_can_send_flash_s19_update(uint32_t start) { printf("Programming the FLASH chips.\r\n"); FILE *fp = fopen("/local/modified.s19", "r"); // Open "modified.s19" on the local file system for reading if (!fp) { printf("Error: I could not find the BIN file MODIFIED.S19\r\n"); return FALSE; } int c = 0; // for some reason fgetc returns an int instead of a char uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal uint8_t asize = 0; // 2,3 or 4 bytes in address uint32_t address = 0; // address to put S-record uint8_t checksum = 0; // checksum check at the end char msg[8]; // Construct the bootloader frame for uploading bool sent = FALSE; while (!sent) { do c = fgetc (fp); // get characters until we get an 'S' (throws away \n,\r characters and other junk) while (c != 'S' && c != EOF); // if (c == EOF) return '\a'; c = fgetc(fp); // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) // if ((c = fgetc(fp)) == EOF) return '\a'; // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached) switch (c) { case '0': break; // Skip over S0 header record case '1': case '2': case '3': asize = 1 + c - '0'; // 2, 3 or 4 bytes for address address = 0; // get the number of bytes (in ascii format) in this S-record line // there must be at least the address and the checksum so return with an error if less than this! if ((c = SRecGetByte(fp)) < (asize + 1)) { fclose(fp); printf("Error in S-record address"); return FALSE; } count = c; checksum = c; // get the address for (uint8_t i=0; i<asize; i++) { c = SRecGetByte(fp); checksum += c; address <<= 8; address |= c; count--; } // send a bootloader address message - Adding the start address that was supplied (for T5.2/T5.5) if (!t5_can_send_boot_address((address + start), (count - 1))) { fclose(fp); printf("Error sending CAN message"); return FALSE; } // get and send the bootloader frames for this S-record // 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 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame. for (uint8_t i=0; i<count-1; i++) { c = SRecGetByte(fp); checksum += c; msg[1+(i%7)] = c; if (i%7 == 0) msg[0]=i; // set the index number if ((i%7 == 6) || (i == count - 2)) if (!t5_can_send_boot_frame(msg)) { fclose(fp); printf("Error sending CAN message"); return FALSE; } } // get the checksum if ((checksum += SRecGetByte(fp)) != 0xFF) { fclose(fp); printf("Error in S-record, checksum = %2x\r\n", checksum); return FALSE; } break; case '5': break; // Skip over S5 record types case '7': case '8': case '9': sent = TRUE; break; // Some kind of invalid S-record type so break default: printf("oops - didn't recognise that S-Record\r\n"); fclose(fp); return FALSE; } } fclose(fp); return TRUE; } // // t5_can_get_last_address // // Sends a C3 Message to the T5 ECU. The reply should contain the last used FLASH address // which is always 0x0007FFFF // // The last 2 bytes of the message might be useful to work out whether or not the bootloader // has been loaded. The 'mode' value will be 0x0808 when the bootloader is running or 0x89b8 // when it isn't. // // inputs: none // return: bool TRUE if all went OK, // bool t5_can_get_last_address() { uint32_t last_address = 0; uint16_t mode = 0; if (!t5_boot_c3_command(&last_address, &mode)) { printf("Error trying to find out the ECU's last address and mode!\r\n"); return FALSE; } printf("The Last used FLASH address is: %#010x, mode %#06x\r\n", last_address, mode); return TRUE; }