Just4Trionic - CAN and BDM FLASH programmer for Saab cars

Dependencies:   mbed

Revision:
1:d5452e398b76
Child:
3:92dae9083c83
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t5can.cpp	Tue Sep 14 21:02:04 2010 +0000
@@ -0,0 +1,1020 @@
+/*******************************************************************************
+
+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 %.1f 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':
+        case 'B':
+            return t5_can_send_boot_loader()
+                   ? 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()
+                   ? 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_get_adaption_data())
+                return TERM_ERR;
+            if (!t5_can_send_boot_loader())
+                return TERM_ERR;
+            if (!t5_can_get_start_and_chip_types(&flash_start)) {
+                t5_can_bootloader_reset();
+                return TERM_ERR;
+            }
+            return (t5_can_dump_flash(flash_start) && t5_can_bootloader_reset())
+                   ? 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)
+                   ? TERM_OK : TERM_ERR;
+        case 'F':
+            if (!t5_can_send_boot_loader())
+                return TERM_ERR;
+            if (!t5_can_get_start_and_chip_types(&flash_start)) {
+                t5_can_bootloader_reset();
+                return TERM_ERR;
+            }
+            if (!t5_can_get_checksum())
+                led4 = 1;
+            if (!t5_can_erase_flash()) {
+                t5_can_bootloader_reset();
+                return TERM_ERR;
+            }
+            return (t5_can_send_flash_bin_update(flash_start) && t5_can_get_checksum() && t5_can_bootloader_reset())
+                   ? 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 - Read SRAM adaption and DUMP T5 FLASH BIN file\r\n");
+    printf("F - FLASH the update file to the T5 (and write SRAM - not done!)\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 - Read SRAM adaption and DUMP T5 FLASH BIN file\r\n");
+    printf("F - FLASH the update file to the T5 (and write SRAM - not done!)\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.S19 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() {
+    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
+                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))
+                        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
+// 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;
+        default:
+            printf("UNKNOWN FLASH chips - check pin65 has enough volts!\r\n");
+    }
+    switch (type) {
+        case AMD28F512:
+        case INTEL28F512:
+            printf("28F512 FLASH chips.\r\n");
+            break;
+        case AMD28F010:
+        case INTEL28F010:
+            printf("28F010 FLASH chips.\r\n");
+            break;
+        case AMD29F010:
+            printf("29F010 FLASH chips.\r\n");
+            break;
+        default:
+            printf("UNKNOWN - check pin65 has enough volts!\r\n");
+            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];
+    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;
+        }
+    }
+    // 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;
+    }
+    fclose(fp);
+    return TRUE;
+}
+
+
+//
+// t5_can_send_flash_bin_update
+//
+// Sends a FLASH update file, modified.hex 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.hex", "r");    // Open "modified.s19" on the local file system for reading
+    if (!fp) {
+        printf("Error: I could not find the BIN file MODIFIED.HEX\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;
+
+    while (curr_addr <= TRIONICLASTADDR) {
+
+// send a bootloader address message
+        if (!t5_can_send_boot_address(curr_addr, 0x80)) {
+            fclose(fp);
+            printf("Error sending CAN message");
+            return FALSE;
+        }
+        curr_addr += 0x80;
+// 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("Error reading the BIN file MODIFIED.HEX");
+                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("Error sending CAN message");
+                    return FALSE;
+                }
+        }
+    }
+    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;
+}