Just4Trionic - CAN and BDM FLASH programmer for Saab cars

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
Just4pLeisure
Date:
Wed May 19 12:39:18 2010 +0000
Child:
1:d5452e398b76
Commit message:

Changed in this revision

BDM.cpp Show annotated file Show diff for this revision Revisions of this file
BDM.h Show annotated file Show diff for this revision Revisions of this file
CAN232.cpp Show annotated file Show diff for this revision Revisions of this file
CAN232.h Show annotated file Show diff for this revision Revisions of this file
CANUtils.cpp Show annotated file Show diff for this revision Revisions of this file
CANUtils.h Show annotated file Show diff for this revision Revisions of this file
SRecUtils.cpp Show annotated file Show diff for this revision Revisions of this file
SRecUtils.h Show annotated file Show diff for this revision Revisions of this file
T5Utils.cpp Show annotated file Show diff for this revision Revisions of this file
T5Utils.h Show annotated file Show diff for this revision Revisions of this file
Trionic5.cpp Show annotated file Show diff for this revision Revisions of this file
Trionic5.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
strings.cpp Show annotated file Show diff for this revision Revisions of this file
strings.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BDM.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,5 @@
+
+#include "BDM.h"
+
+void BDM() {
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BDM.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,3 @@
+// BDM.h
+
+extern void BDM();
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CAN232.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,5 @@
+
+#include "CAN232.h"
+
+void CAN232() {
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CAN232.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,3 @@
+// CAN232.h
+
+extern void CAN232();
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CANUtils.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,97 @@
+// CANUtils.cpp - functions that work with the CAN bus directly
+// Anything to do with the CAN bus must (should anyway) be done by one of these functions.
+
+#include "CANUtils.h"
+
+// CAN_RS pin at Philips PCA82C250 can bus controller.
+// activate transceiver by pulling this pin to GND.
+// (Rise and fall slope controlled by resistor R_s)
+// (+5V result in tranceiver standby mode)
+// For further information see datasheet page 4
+DigitalOut can_Pca82c250SlopePin(p28);// second can controller on these pins. Not used here.
+// CAN can1(p9, p10);
+// We use can on mbed pins 29(CAN_TXD) and 30(CAN_RXD).
+CAN can2(p30, p29);
+// Use a timer to see if things take too long
+Timer CANTimer;
+
+// Use the LEDs to if anything is happening on the CAN bus
+// LED1 CAN send
+DigitalOut led1(LED1);
+// LED2 CAN receive
+DigitalOut led2(LED2);
+
+
+void CANOpen() {
+    // 600kbit/s first - basically sets up CAN interface, but to wrong speed - not sure what else it does
+    can2.frequency(600000);
+    // 615kbit/s direct write of 615 kbit/s speed setting
+    LPC_CAN2->BTR = 0x370002;
+    // activate external can transceiver
+    can_Pca82c250SlopePin = 0;
+}
+
+void CANClose() {
+    // disable external can transceiver
+    can_Pca82c250SlopePin = 1;
+}
+
+//
+// Sends a CAN Message, returns FALSE if the message wasn't sent in time
+//
+// inputs:  integer CAN message 'id', pointer to 'frame', integer message length and integer timeout
+// return:     TRUE if the CAN message was sent before the 'timeout' expires
+//          FALSE if 'timeout' expires or the message length is wrong
+//
+extern bool CANSendTimeout (int id, char *frame, int len, int timeout) {
+    CANTimer.reset();
+    CANTimer.start();
+    while (CANTimer.read_ms() < timeout) {
+        if (can2.write(CANMessage(id, frame, len))) {
+            CANTimer.stop();
+            led1 = !led1;
+            return TRUE;
+        }
+    }
+    CANTimer.stop();
+    return FALSE;
+}
+
+//
+// Waits for a CAN Message with the specified 'id' for a time specified by the 'timeout'
+// All other messages are ignored
+// The CAN message frame is returned using the pointer to 'frame'
+//
+// inputs:    integer CAN message 'id', pointer to 'frame' for returning the data
+//          integer expected length of message, len and integer for the waiting time 'timeout'
+//
+// return:    TRUE if a qualifying message was received
+//          FALSE if 'timeout' expires or the message length is wrong
+//
+extern bool CANWaitTimeout (int id, char *frame, int len, int timeout) {
+    CANMessage CANMsgRx;
+    CANTimer.reset();
+    CANTimer.start();
+    while (CANTimer.read_ms() < timeout) {
+        if (can2.read(CANMsgRx)) {
+/*
+            printf("w%03x8", CANMsgRx.id);
+            for (char i=0; i<len; i++) {
+                printf("%02x", CANMsgRx.data[i]);
+            }
+            printf("\n\r");
+//            */
+            led2 = !led2;
+            if (CANMsgRx.id == id) {
+                CANTimer.stop();
+//                if (T5MsgRx.len != len)
+//                    return FALSE;
+                for (int i=0; i<len; i++)
+                    frame[i] = CANMsgRx.data[i];
+                return TRUE;
+            }
+        }
+    }
+    CANTimer.stop();
+    return FALSE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CANUtils.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,14 @@
+
+// CANUtils.h - information and definitions needed for the CAN bus
+
+#include "CAN.h"
+#include "mbed.h"
+
+#define TRUE 1
+#define FALSE 0
+
+extern void CANOpen();
+extern void CANClose();
+
+extern bool CANSendTimeout (int id, char *frame, int len, int timeout);
+extern bool CANWaitTimeout (int id, char *frame, int len, int timeout);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SRecUtils.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,40 @@
+
+#include "SRecUtils.h"
+
+// SRecGetByte
+//
+// Returns an int which is a single byte made up from two ascii characters read from an S-record file
+//
+// inputs:	a file pointer for the S-record file
+// return:	an integer which is the byte in hex format
+
+int SRecGetByte(FILE *fp) {
+    int c = 0;
+    int retbyte = 0;
+
+    for(int i=0; i<2; i++) {
+        if ((c = fgetc(fp)) == EOF) return -1;
+        c -= (c > '9') ? ('A' - 10) : '0';
+        retbyte = (retbyte << 4) + c;
+    }
+    return retbyte;
+}
+
+// SRecGetAddress
+//
+// Returns an int which is the address part of the S-record line
+// The S-record type 1/2/3 or 9/8/7 determines if there are 2, 3 or 4 bytes in the address
+//
+// inputs:	an integer which is the number of bytes that make up the address; 2, 3 or 4 bytes
+//			a file pointer for the S-record file
+// return:	an integer which is the load address for the S-record
+
+int SRecGetAddress(int size, FILE *fp) {
+    int address = 0;
+    for (int i = 0; i<size; i++)
+    {
+        address <<= 8;
+        address |= SRecGetByte (fp);
+    }
+    return address;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SRecUtils.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,10 @@
+
+// SRecUtils.h - information and definitions needed for working with S-Records
+
+#include "mbed.h"
+
+#define TRUE 1
+#define FALSE 0
+
+extern int SRecGetByte(FILE *fp);
+extern int SRecGetAddress(int size, FILE *fp);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/T5Utils.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,389 @@
+/*******************************************************************************
+
+T5Utils.cpp - By Sophie Dexter, April 2010
+
+This C++ module provides functions for communicating simple messages to and from
+the T5 ECU
+
+********************************************************************************
+
+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 "T5Utils.h"
+
+//
+// T5Open enables the CAN chip and sets the CAN speed to 615 kbits.
+//
+// This function is a 'quick fix' for now.
+//
+// inputs:    none
+// return:    bool TRUE if all went OK, 
+//
+bool T5Open() {
+    CANOpen ();
+    return TRUE;
+}
+
+//
+// T5Close disables the CAN chip.
+//
+// This function is a 'quick fix' for now.
+//
+// inputs:    none
+// return:    bool TRUE if all went OK, 
+//
+bool T5Close() {
+    CANClose ();
+    return TRUE;
+}
+
+//
+// T5WaitResponse
+//
+// Waits for a response message with the correct message id from the T5 ECU.
+// The response is a single ascii character from the symboltable.
+//
+// Returns an error ('BELL' character) if:
+//      a response is not received before the timeout
+//      the message length is less than 8 bytes (all messages should be 8 bytes)
+//      the message type is incorrect
+//
+// inputs:    none
+// return:    a single char that is the response from the T5 ECU
+//
+char T5WaitResponse() {
+    char T5RxMsg[8];
+    if (!CANWaitTimeout(RESPID, T5RxMsg, 8, T5MESSAGETIMEOUT)) return '\a';
+    if (T5RxMsg[0] != 0xC6) return '\a';
+    return T5RxMsg[2];
+}
+
+//
+//
+// T5WaitResponsePrint
+//
+// Waits for a response message with the correct message id from the T5 ECU and displays the whole message:
+//
+// wiiildddddddddddddddd
+//  iii - is the CAN message id
+//     l - is the message length, should always be 8 for T5 messages
+//      dd,dd,dd,dd,dd,dd,dd,dd are the CAN message data bytes
+//
+// Returns an error if:
+//      a response is not received before the timeout
+//      the message length is less than 8 bytes (all messages should be 8 bytes)
+//      the message type is incorrect
+//
+// Inputs:    none
+// return:    bool TRUE if a qualifying message was received
+//                 FALSE if a message wasn't received in time, had the wrong id etc...
+//
+bool T5WaitResponsePrint() {
+    char T5RxMsg[8];
+    if (!CANWaitTimeout(RESPID, T5RxMsg, 8, T5CHECKSUMTIMEOUT)) return FALSE;
+    printf("w%03x8", RESPID);
+    for (char i=0; i<8; i++) {
+        printf("%02x", T5RxMsg[i]);
+    }
+    printf("\n\r");
+    return TRUE;
+}
+
+// T5GetSymbol
+//
+// Reads a single symbol name (in the symbol table) from the T5 ECU.
+// T5GetSymbol sends ACK messages to the T5 ECU and stores the
+// reply characters in a string until a '\n' character is received
+// when the function returns with the pointer to that string.
+//
+// inputs:    a pointer to the string used to store the symbol name.
+// return:    a pointer to the string used to store the symbol name.
+//
+char *T5GetSymbol(char *s) {
+    do {
+        if (T5Ack()) {
+        }
+        *s = T5WaitResponse();
+    } while (*s++ != '\n');
+    *s = '\0';
+    return s;
+}
+
+// T5ReadCmnd
+//
+// Sends a 'read' type command to the T5 ECU. 'read' commands are used to read something from the T5 ECU.
+//
+// inputs:    a 'char' which is the type of 'read' requested (e.g. symbol table, version etc).
+// return:    bool TRUE if the message was sent within the timeout period,
+//                 FALSE if the message couldn't be sent.
+//
+bool T5ReadCmnd(char c) {
+    char T5TxMsg[] = T5_RDCMND;
+    T5TxMsg[1] = c;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5WriteCmnd is unfinished
+//
+bool T5WriteCmnd(unsigned int addr, char data) {
+    return FALSE;
+}
+
+// T5ReadRAM
+//
+// Reads 6 bytes of the adaption data from the SRAM in the T5 ECU.
+//
+// inputs:    a pointer to an array of 6 bytes for storing the data read from the T5 ECU.
+//            the address to read SRAM data from in the T5 ECU
+// return:    a pointer to an array of 6 bytes for storing the data read from the T5 ECU.
+//
+char *T5ReadRAM(char *data, unsigned int addr) {
+    T5RAMCmnd(addr);
+    T5WaitRAM(data);
+    return data;
+}
+
+// T5ReadFLASH
+//
+// Reads 6 bytes of the FLASH in the T5 ECU.
+//
+// inputs:    a pointer to an array of 6 bytes for storing the data read from the T5 ECU.
+//            the address to read SRAM data from in the T5 ECU
+// return:    a pointer to an array of 6 bytes for storing the data read from the T5 ECU.
+//
+char *T5ReadFLASH(char *data, unsigned int addr) {
+    T5RAMCmnd(addr);
+    T5WaitRAM(data);
+    return data;
+}
+
+//
+// T5RAMCmnd
+//
+// Sends a command to the ECU asking the T5 ECU to send some SRAM data
+//
+// inputs:    the address to read SRAM data from in the T5 ECU
+// return:    bool TRUE if the message was sent within the timeout period,
+//                 FALSE if the message couldn't be sent.
+//
+bool T5RAMCmnd(unsigned int addr) {
+    char T5TxMsg[] = T5RAMCMND;
+    T5TxMsg[1] = ((char)(addr >> 24));         // high high byte of address
+    T5TxMsg[2] = ((char)(addr >> 16));         // high low byte of address
+    T5TxMsg[3] = ((char)(addr >> 8));          // low high byte of address
+    T5TxMsg[4] = ((char)(addr));               // low low byte of address
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5WaitRAM
+//
+// Waits for the T5 ECU to send some SRAM data
+//
+// There is something back-to-front about the way this works, but it does :-)
+//
+// inputs:    a pointer to an array of 6 bytes for storing the data read from the T5 ECU.
+// return:    a pointer to an array of 6 bytes for storing the data read from the T5 ECU.
+//
+char *T5WaitRAM(char *data) {
+    char T5RxMsg[8];
+    CANWaitTimeout(RESPID, T5RxMsg, 8, T5MESSAGETIMEOUT);
+//    if (T5RxMsg[0] != 0xC6) return FALSE;
+//    if (T5RxMsg[0] != 0x0C) return FALSE;
+    for (int i=2; i<8; i++)
+        data[7-i] = T5RxMsg[i];
+    return data;
+}
+
+// T5Ack
+//
+// Sends an 'ACK' message to the T5 ECU to prompt the T5 to send the next
+// ascii character from the symboltable
+//
+// Returns an error if:
+//      the 'ACK' message cannot be sent before the timeout
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+
+bool T5Ack() {
+    char T5TxMsg[] = T5ACKCMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5SendBootAddress
+//
+// Send an address where 'bootloader' or FLASH CAN messages are uploaded to.
+// The number of bytes (up to 0x7F) that will be sent in the following CAN messages is also sent.
+//
+// inputs:    an unsigned int, the address where messages should be sent.
+//            an int, the number of bytes (up to 0x7F) that will be sent in the following CAN messages.
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5SendBootAddress(unsigned int addr, int len) {
+    char T5TxMsg[] = T5_BTCMND;
+    T5TxMsg[1] = ((char)(addr >> 24));         // high high byte of address
+    T5TxMsg[2] = ((char)(addr >> 16));         // high low byte of address
+    T5TxMsg[3] = ((char)(addr >> 8));          // low high byte of address
+    T5TxMsg[4] = ((char)(addr));               // low low byte of address
+    T5TxMsg[5] = ((char)(len));                // number of bytes to upload (sent in following messages)
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+// Upload 'bootloader' or new FLASH byts to T5 ECU in CAN messages
+//
+// The CAN messages are made up of 1 byte which is added to the address given in the T5SendBootAddress meaassge
+// followed by 7 bytes of data.
+// The first byte normally starts at 0x00 and increases by 7 in following messages until another T5SendBootAddress message is sent e.g.
+// 00,dd,dd,dd,dd,dd,dd,dd,dd 'dd' bytes are the data bytes, 7 in each CAN message except the last one which may have less.
+// 07,dd,dd,dd,dd,dd,dd,dd,dd
+// 0E,dd,dd,dd,dd,dd,dd,dd,dd (0x0E is 14)
+// 15,dd,dd,dd,dd,dd,dd,dd,dd (0x15 is 21)
+// etc.
+//
+// inputs:    a pointer to an array of 8 bytes to be sent to the T5 ECU SRAM or FLASH.
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5SendBootFrame (char *frame) {
+    return (CANSendTimeout (CMNDID, frame, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5StartBootLoader
+//
+// Send the jump to address to the T5 ECU for starting the 'bootloader'.
+// The jump address must be somewhere in RAM (addresses 0x0000 to 0x8000).
+// (The start address comes from the S7/8/9 line in the S19 file.)
+//
+// inputs:    an unsigned int, the address to jump to start of 'bootloader'.
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5StartBootLoader (unsigned int addr) {
+    char T5TxMsg[] = T5JMPCMND;
+    T5TxMsg[3] = ((char)(addr >> 8));          // low byte of address
+    T5TxMsg[4] = ((char)(addr));               // high byte of address
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5BootCSumCmnd
+//
+// Send the checksum command to Bootloader
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5BootCSumCmnd() {
+    char T5TxMsg[] = T5SUMCMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5BootResetCmnd
+//
+// Send 'exit and restart T5' command to the Bootloader
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5BootResetCmnd() {
+    char T5TxMsg[] = T5RSTCMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5BootFLASHType
+//
+// Send 'get FLASH chip types' command to the Bootloader
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5BootFLASHType() {
+    char T5TxMsg[] = T5TYPCMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5BootEraseCmnd
+//
+// Send 'erase FLASH chip types' command to the Bootloader
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5BootEraseCmnd() {
+    char T5TxMsg[] = T5ERACMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5BootDumpFLASHCmnd
+//
+// Send 'Dump FLASH BIN file' command
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+extern bool T5BootDumpFLASHCmnd() {
+    char T5TxMsg[] = T5DMPCMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
+
+//
+// T5WaitFLASH
+//
+// Waits for the T5 ECU to send 4 bytes of FLASH data when dumping the FLASH
+//
+// CAN messages look like this:
+// A6,04,dd,dd,dd,dd,08,08
+// A6 - means FLASH dump type message
+//    04 - means (at least) 4 more bytes still to come
+//       dd,dd,dd,dd are 4 data bytes
+// The last message looks like this:
+// A6,00,dd,dd,dd,dd,08,08
+// A6 - means FLASH dump type message
+//    00 - means no more bytes after this CAN message
+//       dd,dd,dd,dd are the last 4 data bytes
+//
+// inputs:    a pointer to an array of 8 bytes for the CAN message containing FLASH data.
+// return:    a pointer to an array of 8 bytes for the CAN message containing FLASH data.
+//
+char *T5WaitFLASH(char *data) {
+    char T5RxMsg[8];
+    CANWaitTimeout(RESPID, T5RxMsg, 8, T5MESSAGETIMEOUT);
+//    if (T5RxMsg[0] != 0xC6) return FALSE;
+//    if (T5RxMsg[0] != 0x0C) return FALSE;
+    for (int i=0; i<8; i++)
+        data[i] = T5RxMsg[i];
+    return data;
+}
+
+//
+// T5BootResetCmnd
+//
+// Send 'C3 - Get last address' command to the Bootloader
+//
+// inputs:    none
+// return:    bool TRUE if message was sent OK,
+//                 FALSE if message could not be sent.
+//
+bool T5BootC3Command() {
+    char T5TxMsg[] = T5EOFCMND;
+    return (CANSendTimeout (CMNDID, T5TxMsg, 8, T5MESSAGETIMEOUT));
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/T5Utils.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,59 @@
+
+// T5Utils.h - information and definitions needed for communicating with the T5 ECU
+
+#include "mbed.h"
+#include "CANUtils.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define CMNDID 0x05
+#define ACK_ID 0x06
+#define RESPID 0x0C
+
+#define T5_RDCMND {0xC4,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}     // C4 Command template for reading something from the T5 ECU
+#define T5_WRCMND {0xC4,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C4 Command template for writing something to the T5 ECU
+#define T5ACKCMND {0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C6 Command for acknowledging receipt of last message from T5 ECU or
+                                                                // asking it to send some more data (e.g. symbol table) I'm not sure which :lol:
+#define T5_BTCMND {0xA5,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // A5 Command template for sending a bootloader to the T5 ECU
+#define T5JMPCMND {0xC1,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C1 Command template for starting a bootloader that has been sent to the T5 ECU
+#define T5EOFCMND {0xC3,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C3 Command for reading back the last address of FLASH in the T5 ECU
+#define T5A_DCMND {0xC5,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C5 Command for setting analogue to digital registers (don't know what this is for)
+//#define T5RAMCMND {0xC7,0x00,0x00,0x00,0x00,0xFC,0xC4,0x77}     // C7 Command for reading T5 RAM (all the symbols and adaption data is in RAM)
+#define T5RAMCMND {0xC7,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C7 Command for reading T5 RAM (all the symbols and adaption data is in RAM)
+
+
+#define T5DMPCMND {0xA6,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // A6 Command for dumping the T5 FLASH BIN file (Bootloader must be loaded before using)
+#define T5ERACMND {0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C0 Command for erasing T5 FLASH Chips !!! (Bootloader must be loaded before using)
+#define T5RSTCMND {0xC2,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C2 Command for exiting the Bootloader and restarting the T5 ECU (Bootloader must be loaded before using)
+#define T5SUMCMND {0xC8,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C8 Command for reading T5 FLASH Checksum (Bootloader must be loaded before using)
+#define T5TYPCMND {0xC9,0x00,0x00,0x00,0x00,0x00,0x00,0x00}     // C9 Command for reading T5 FLASH Chip types (Bootloader must be loaded before using)
+
+
+#define T5MESSAGETIMEOUT 500    // 500 milliseconds (half a second) - Seems to be plenty of time to wait for messages on the CAN bus
+#define T5CHECKSUMTIMEOUT 60000 // 60 seconds (60,000 milliseconds) - Usually takes less than 2 seconds so allowing 10 is plenty
+//                                                                    - Increased to 60 seconds for now because using it as erase timeout too
+
+extern bool T5Open();
+extern bool T5Close();
+extern char T5WaitResponse();
+extern bool T5WaitResponsePrint();
+extern char *T5GetSymbol(char *a_symbol);
+extern bool T5ReadCmnd(char c);
+extern bool T5WriteCmnd(unsigned int addr, char data);
+extern char *T5ReadRAM(char *data, unsigned int addr);
+extern char *T5ReadFLASH(char *data, unsigned int addr);
+extern bool T5RAMCmnd(unsigned int addr);
+extern char *T5WaitRAM(char *data);
+extern bool T5Ack();
+extern bool T5SendBootAddress(unsigned int addr, int len);
+extern bool T5SendBootFrame (char *frame);
+extern bool T5StartBootLoader (unsigned int addr);
+
+extern bool T5BootCSumCmnd();
+extern bool T5BootResetCmnd();
+extern bool T5BootFLASHType();
+extern bool T5BootEraseCmnd();
+extern bool T5BootDumpFLASHCmnd();
+extern char *T5WaitFLASH(char *data);
+extern bool T5BootC3Command();
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Trionic5.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,728 @@
+/*******************************************************************************
+
+Trionic5.cpp - By Sophie Dexter, April 2010
+
+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 (NOTE
+that AMD 29F010 FLASH programming is experimental at this stage to say the least)
+
+********************************************************************************
+
+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 "Trionic5.h"
+
+// RS232 on USB connection
+Serial pc2(USBTX, USBRX); // tx, rx
+
+// A timer for timing how long things take to happen
+Timer timer;
+
+// We use CAN on mbed pins 29(CAN_TXD) and 30(CAN_RXD).
+CAN can_temp(p30, p29);
+
+// Need to create this to be able to read and write files on the mbed 'disk'
+LocalFileSystem local("local");
+
+void Trionic5() {
+    // Start the CAN bus system
+    // Note that at the moment this is only for T5 ECUs at 615 kbits
+    CANOpen();
+
+    int counter = 0;        //counter used for testing things !?!
+
+    Trionic5ShowHelp();
+
+    while (1) {
+        // 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
+        Trionic5ShowCANMessage();
+
+//        Wait for a single command character from the PC
+        if (pc2.readable()) {
+//            char command[30] = "w0068c600000000000000";
+//            command = pc2.gets();
+            char c = (pc2.getc());
+            if ((c=='h') || (c=='H')) Trionic5ShowHelp();            // Print help
+            if (c=='\e') return;                                    // 'ESC' key to go back to mbed CAN tool menu
+//            if ((c=='s') or (c=='S')) {
+            if (c=='s') Trionic5GetSymbolTable();                    // Get the Symbol Table
+            if (c=='S') T5ReadCmnd(T5SYMBOLS);
+//            if ((c=='v') or (c=='V')) {
+            if (c=='v') Trionic5GetVersion();                        // Get the Trionic5 software version string
+            if (c=='V') T5ReadCmnd(T5VERSION);
+//            if ((c=='r') or (c=='R')) {
+            if (c=='r') Trionic5GetAdaptionData();                    // Read Adaption Data from RAM and write it to a file
+            if (c=='R') T5RAMCmnd(counter++);
+            if (c=='\r') T5ReadCmnd(CR);                            // CR - send CR type message
+
+//            if ((c=='a') or (c=='A')) {
+            if (c=='a') {                                            //  Get a single symbol from the Symbol Table
+                char symbol[40];
+                T5GetSymbol(symbol);
+                printf("%s",symbol);
+            }
+            if (c=='A') T5Ack();                                    // Just send an 'ACK' message
+            if ((c=='b') or (c=='B')) Trionic5SendBootLoader();        // Send a Bootloader file to the T5 ECU
+            if ((c=='c') or (c=='C')) Trionic5GetChecksum();        // Get Checksum from ECU (Bootloader must be uploaded first)
+            if ((c=='q') or (c=='Q')) Trionic5BootloaderReset();    // Exit the BootLoader and restart the T5 ECU
+            if ((c=='e') or (c=='E')) Trionic5EraseFLASH();            // Erase the FLASH chips
+            if ((c=='t') or (c=='T')) Trionic5GetChipTypes();        // Read back the FLASH chip types
+//            if ((c=='d') or (c=='D')) Trionic5DumpFLASH();            // DUMP the T5 ECU BIN file stored in the FLASH chips
+//            if (c=='d') Trionic5DumpFLASH();            // DUMP the T5 ECU BIN file stored in the FLASH chips
+//            if (c=='D') Trionic5DumpFLASH2();            // DUMP the T5 ECU BIN file stored in the FLASH chips
+            if ((c=='d') or (c=='D')) Trionic5DumpFLASH2();            // DUMP the T5 ECU BIN file stored in the FLASH chips
+            if ((c=='f') or (c=='F')) Trionic5SendFLASHUpdate();    // Send a FLASH update file to the T5 ECU
+            if (c=='3') Trionic5SendC3Message();            // Send the C3 message - should get last used address 0x7FFFF
+        }
+    }
+}
+
+//
+// Trionic5ShowHelp
+//
+// Displays a list of things that can be done with the T5 ECU.
+//
+// inputs:    none
+// return:    none
+//
+void Trionic5ShowHelp() {
+    printf("Trionic 5 Command Menu\r\n");
+    printf("======================\r\n");
+    printf("b - upload and start MyBooty.S19 bootloader\r\n");
+    printf("c - get T5 ECU FLASH checksum (need toupload BOOTY.S19 before using this command)\r\n");
+    printf("d - dump the T5 FLASH BIN file and write it to ORIGINAL.HEX\r\n");
+    printf("e - erase the FLASH chips in the T5 ECU\r\n");
+    printf("f - FLASH the upate file MODIFIED.S19 to the T5 ECU\r\n");
+    printf("e - erase the FLASH chips in the T5 ECU\r\n");
+    printf("r - read SRAM and write it to ADAPTION.HEX 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 mbed CAN tool Menu\r\n");
+    printf("\r\n");
+    printf("h/H - show this help menu\r\n");
+    return;
+}
+
+//
+// Trionic5ShowCANMessage
+//
+// 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 Trionic5ShowCANMessage() {
+    CANMessage can_MsgRx;
+    if (can_temp.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;
+}
+
+//
+// Trionic5GetSymbolTable
+//
+// Gets the T5 ECU symbol table.
+// The Symbol Table is sent to the PC and 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 Trionic5GetSymbolTable() {
+    FILE *fp = fopen("/local/symbols.txt", "w");  // Open "symbols.txt" on the local file system for writing
+    if (!fp) return FALSE;
+    char symbol[40];
+    char response = '\0';
+    T5ReadCmnd(T5SYMBOLS);
+    response = T5WaitResponse();
+    if (response != '>')
+        return FALSE;
+//                printf("%c",response);
+    T5ReadCmnd(CR);
+    response = T5WaitResponse();
+    if (response != '>')
+        return FALSE;
+//                printf("%c",response);
+    timer.reset();
+    timer.start();
+    do {
+        T5GetSymbol(symbol);
+        printf("%s",symbol);
+        fprintf(fp,"%s",symbol);
+    } while (!StrCmp(symbol,"END\r\n"));
+    timer.stop();
+    printf("Getting the Symbol Table took %f seconds.\r\n",timer.read());
+    fclose(fp);
+    return TRUE;
+}
+
+//
+// Trionic5GetVersion
+//
+// 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 Trionic5GetVersion() {
+    FILE *fp = fopen("/local/version.txt", "w");  // Open "version.txt" on the local file system for writing
+    if (!fp) return FALSE;
+    char symbol[40];
+    char response = '\0';
+    T5ReadCmnd(T5VERSION);
+    response = T5WaitResponse();
+    if (response != '>')
+        return FALSE;
+//                printf("%c",response);
+    T5ReadCmnd(CR);
+    response = T5WaitResponse();
+    if (response != '>')
+        return FALSE;
+//                printf("%c",response);
+    T5GetSymbol(symbol);
+    printf("%s",symbol);
+    if (fp)
+        fprintf(fp,"%s",symbol);
+    else
+        printf("ERROR Could not open version.txt for saving\r\n");
+    fclose(fp);
+    return TRUE;
+}
+
+//
+// Trionic5GetAdaptionData
+//
+// Gets the adaption data from the T5's SRAM. 
+// The adaption data is stored in a hex file, adaption.hex, on the mbed 'local' file system 'disk'.
+//
+// Reading the Adaption data from SRAM takes about 16 seconds.
+//
+// inputs:    none
+// return:    bool TRUE if all went OK, FALSE if there was an error.
+//
+bool Trionic5GetAdaptionData() {
+    printf("getting adaption data\r\n");
+    FILE *fp = fopen("/local/adaption.hex", "w");    // Open "adaption.hex" on the local file system for writing
+    if (!fp) return FALSE;
+    printf("opened adaption data file\r\n");
+    unsigned int address = 5;                       // Mysterious reason for starting at 5 !!!
+    char RAMdata[6];
+    timer.reset();
+    timer.start();
+    for (int i=0; i<5462; i++) {                    // Mysterious number 5462 is 0x8000 / 6 - rounded up !!!
+        T5ReadRAM(RAMdata, address);
+        address += 6;
+        for (int j=0; j<6; j++) {
+            if (((i*6) + j) < T5RAMSIZE)
+                fputc(RAMdata[j],fp);
+        }
+    }
+    timer.stop();
+    printf("Getting the RAM Data took %f seconds.\r\n",timer.read());
+    fclose(fp);
+    return TRUE;
+}
+
+//
+// Trionic5SendBootLoader
+//
+// 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 Trionic5SendBootLoader() {
+    FILE *fp = fopen("/local/MyBooty.S19", "r");    // Open "booty.s19" on the local file system for reading
+    if (!fp) return FALSE;
+    int c = 0; // for some reason fgetc returns an int instead of a char
+    int count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal
+    int asize = 0; // 2,3 or 4 bytes in address
+    unsigned int address = 0; // address to put S-record
+    int checksum = 0; // checksum check at the end of each S-record line
+    char msg[8]; // Construct the bootloader frame for uploading
+    bool sent = FALSE;
+
+    timer.reset();
+    timer.start();
+    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)) break;
+//                        if ((c = SRecGetByte(fp)) < 3) return '\a';
+                count = c;
+                checksum = c;
+// get the address
+                for (int i=0; i<asize; i++) {
+                    c = SRecGetByte(fp);
+                    checksum += c;
+                    address <<= 8;
+                    address |= c;
+                    count--;
+                }
+// send a bootloader address message
+                T5SendBootAddress(address, (count-1));
+//                T5WaitResponsePrint();
+                T5WaitResponse();
+// 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 (int 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)) {
+                        T5SendBootFrame(msg);
+//                        T5WaitResponsePrint();
+                        T5WaitResponse();
+                    }
+                }
+// get the checksum
+                if (((checksum += SRecGetByte(fp)) | 0xFF) != 0xFF) break;
+//                if ((checksum += SRecGetByte(fp)) != 0xff) return '\a';
+                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!
+                printf("Starting the bootloader, ");
+                if ((c = SRecGetByte(fp)) != (asize + 1)) break;
+//                if ((c = SRecGetByte(fp)) < 3) return '\a';
+                printf("so far so good");
+                checksum = c;
+// get the address
+                for (int i=0; i<asize; i++) {
+                    c = SRecGetByte(fp);
+                    checksum += c;
+                    address <<= 8;
+                    address |= c;
+                }
+                printf("calculated the address, ");
+// get the checksum
+//                if (((checksum += SRecGetByte(fp)) | 0xFF) != 0xFF) break;
+//                if ((checksum += SRecGetByte(fp)) != 0xff) return '\a';
+                printf("checksum is OK, ");
+                T5StartBootLoader(address);
+//                T5WaitResponsePrint();
+                sent = TRUE;
+                printf("and we're done :-) \r\n");
+                break;
+
+// Some kind of invalid S-record type so break
+            default:
+                printf("oops - didn't recognise that S-Record \r\n");
+                return FALSE;
+//                            return SREC_FORMAT;
+        }
+    }
+    timer.stop();
+    printf("Uploading and starting the bootloader took %f seconds. \r\n",timer.read());
+    fclose(fp);
+    return TRUE;
+}
+
+//
+// Trionic5GetChecksum
+//
+// 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 Trionic5GetChecksum() {
+    timer.reset();
+    timer.start();
+    T5BootCSumCmnd();
+    T5WaitResponsePrint();
+    timer.stop();
+    printf("T5 ECU took took %f seconds to calculate it's checksum.\r\n",timer.read());
+    return TRUE;
+}
+
+//
+// Trionic5BootloaderReset
+//
+// Exits the Bootloader and restart the T5 ECU
+//
+// inputs:    none
+// outputs:    bool TRUE if all went OK. 
+//
+bool Trionic5BootloaderReset() {
+    T5BootResetCmnd();
+    T5WaitResponsePrint();
+    return TRUE;
+}
+
+//
+// Trionic5GetChipTypes
+//
+// 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:    none
+// return:    bool TRUE if all went OK.
+//
+bool Trionic5GetChipTypes() {
+    timer.reset();
+    timer.start();
+    T5BootFLASHType();
+    T5WaitResponsePrint();
+    timer.stop();
+    printf("Getting the FLASH chip id took %f seconds.\r\n",timer.read());
+    return TRUE;
+}
+
+//
+// Trionic5EraseFLASH
+//
+// 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 should be erased much more quickly.
+//
+// inputs:    none
+// return:    bool TRUE if all went OK.
+//
+bool Trionic5EraseFLASH() {
+    timer.reset();
+    timer.start();
+    T5BootEraseCmnd();
+    T5WaitResponsePrint();
+    timer.stop();
+    printf("Erasing the FLASH took %f seconds.\r\n",timer.read());
+    return TRUE;
+}
+
+//
+// Trionic5DumpFLASH
+//
+// Dumps the FLASH chip BIN file, original.hex to the mbed 'disk'
+//
+// NOTE the bootloader must be loaded in order to use this function.
+//
+// CAN messages from the T5 ECU with FLASH data look like this:
+//
+// A6,mm,dd,dd,dd,dd,08,08
+//
+// A6 lets us know its a FLASH data message
+// mm tells us if there is any more data
+//      mm == 04 means at least 4 more bytes to come
+//      mm == 00 means that these are the last FLASH data bytes
+// dd,dd,dd,dd are 4 FLASH data bytes 
+// The last 2 bytes are 08,08 they aren't and don't meean anything
+// (It would be more efficient to dump 6 bytes at a time because less
+// CAN messages would be needed but it was easier to write the
+// bootloader to send just 4 bytes so that's what I did)
+//
+// Dumping FLASH chips in a T5.5 ECU takes around 88 seconds. Dumping T5.2 ECUs should take about half of this time
+//
+// inputs:    none
+// return:    bool TRUE if all went OK.
+//
+bool Trionic5DumpFLASH() {
+    printf("getting FLASH BIN file\r\n");
+    FILE *fp = fopen("/local/original.hex", "w");    // Open "original.hex" on the local file system for writing
+    if (!fp) return FALSE;
+    printf("opened FLASH BIN file\r\n");
+    char FLASHdata[8];
+    timer.reset();
+    timer.start();
+//    int count = 0;
+    do {
+        T5BootDumpFLASHCmnd();
+//        T5WaitResponsePrint();
+        T5WaitFLASH(FLASHdata);
+        for (int j=0; j<4; j++) {
+            fputc(FLASHdata[j+2],fp);
+//        count++;
+        }
+    } while (FLASHdata[1] != 0);
+    timer.stop();
+//    printf("count = 0x%x\r\n", count);
+    printf("Getting the FLASH BIN took %f seconds.\r\n",timer.read());
+    fclose(fp);
+    return TRUE;
+}
+
+
+//
+// Trionic5DumpFLASH2
+//
+// Gets the adaption data from the T5's SRAM. 
+// The adaption data is stored in a hex file, adaption.hex, on the mbed 'local' file system 'disk'.
+//
+// Reading the Adaption data from SRAM takes about 16 seconds.
+//
+// inputs:    none
+// return:    bool TRUE if all went OK, FALSE if there was an error.
+//
+bool Trionic5DumpFLASH2() {
+    printf("getting FLASH BIN file\r\n");
+    FILE *fp = fopen("/local/original.hex", "w");    // Open "adaption.hex" on the local file system for writing
+    if (!fp) return FALSE;
+    printf("opened FLASH BIN file\r\n");
+    unsigned int address = 0x40005;                  // Mysterious reason for starting at 5 !!!
+    char FLASHdata[6];
+    timer.reset();
+    timer.start();
+    for (int i=0; i<43690; i++) {                    // Mysterious number 43690 is 0x40000 / 6
+        T5ReadFLASH(FLASHdata, address);
+        address += 6;
+        for (int j=0; j<6; j++) {
+            fputc(FLASHdata[j],fp);
+        }
+    }
+    address = 0x7FFFF;
+    T5ReadFLASH(FLASHdata, address);
+    for (int j=0; j<4; j++) {
+        fputc(FLASHdata[j+2],fp);
+    }
+    timer.stop();
+    printf("Getting the FLASH BIN took %f seconds.\r\n",timer.read());
+    fclose(fp);
+    return TRUE;
+}
+
+//
+// Trionic5SendFLASHUpdate
+//
+// 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 90 seconds. FLASHing T5.2 ECUs should take about half of this time
+//
+// inputs:    none
+// return:    bool TRUE if all went OK,
+//                 FALSE if the FLASH update failed for some reason.
+//
+bool Trionic5SendFLASHUpdate() {
+    FILE *fp = fopen("/local/modified.s19", "r");    // Open "modified.s19" on the local file system for reading
+    if (!fp) return FALSE;
+    printf("Opened MODIFIED.S19 ");
+    int c = 0; // for some reason fgetc returns an int instead of a char
+    int count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal
+    int asize = 0; // 2,3 or 4 bytes in address
+    unsigned int address = 0; // address to put S-record
+    int checksum = 0; // checksum check at the end
+    char msg[8]; // Construct the bootloader frame for uploading
+    bool sent = FALSE;
+
+    timer.reset();
+    timer.start();
+    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)) break;
+//                        if ((c = SRecGetByte(fp)) < 3) return '\a';
+                count = c;
+                checksum = c;
+// get the address
+                for (int i=0; i<asize; i++) {
+                    c = SRecGetByte(fp);
+                    checksum += c;
+                    address <<= 8;
+                    address |= c;
+                    count--;
+                }
+                address += 0x40000;         // Only for T5.5 at the moment !!!
+// send a bootloader address message
+                T5SendBootAddress(address, (count-1));
+//                T5WaitResponsePrint();
+                T5WaitResponse();
+// 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 (int 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)) {
+                        T5SendBootFrame(msg);
+//                        T5WaitResponsePrint();
+                        T5WaitResponse();
+                    }
+                }
+// get the checksum
+                if (((checksum += SRecGetByte(fp)) | 0xFF) != 0xFF) break;
+//                if ((checksum += SRecGetByte(fp)) != 0xff) return '\a';
+                break;
+
+            case '5':
+                break;                  // Skip over S5 record types
+
+            case '7':
+            case '8':
+            case '9':
+                sent = TRUE;
+                printf("and we're done :-) \r\n");
+                break;
+
+// Some kind of invalid S-record type so break
+            default:
+                printf("oops - didn't recognise that S-Record \r\n");
+                return FALSE;
+//                            return SREC_FORMAT;
+        }
+    }
+    timer.stop();
+    printf("Uploading the FLASH update file took %f seconds. \r\n",timer.read());
+    fclose(fp);
+    return TRUE;
+}
+
+//
+// Trionic5SendC3Message
+//
+// Sends a C3 Message to the T5 ECU. The reply should contain the last used FLASH address
+// which is always 0x0007FFFF
+//
+// inputs:    none
+// return:    bool TRUE if all went OK,
+//
+bool Trionic5SendC3Message() {
+    T5BootC3Command();
+    T5WaitResponsePrint();
+    printf("The Last used FLASH address message is:\r\n");
+    return TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Trionic5.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,36 @@
+
+// Trionic5.h - information and definitions needed for doing things with the T5 ECU
+
+#include "mbed.h"
+#include "CAN.h"
+#include "strings.h"
+#include "T5Utils.h"
+#include "SRecUtils.h"
+
+#define CR 0x0D
+#define NL 0x0A
+
+#define T5SYMBOLS 'S'
+#define T5VERSION 's'
+#define T5WRITE 'W'
+
+#define T5RAMSIZE 0x8000   // T5 ECU has 32 kbytes of RAM 0x8000 = 32,768 (same as MC68332 clock speed!?!) 
+#define T55FLASHSIZE 0x40000   // T5 ECU has 32 kbytes of RAM 0x8000 = 32,768 (same as MC68332 clock speed!?!) 
+#define T52FLASHSIZE 0x20000   // T5 ECU has 32 kbytes of RAM 0x8000 = 32,768 (same as MC68332 clock speed!?!) 
+
+extern void Trionic5();
+
+void Trionic5ShowHelp();
+bool Trionic5ShowCANMessage();
+bool Trionic5GetSymbolTable();
+bool Trionic5GetVersion();
+bool Trionic5GetAdaptionData();
+bool Trionic5SendBootLoader();
+bool Trionic5GetChecksum();
+bool Trionic5BootloaderReset();
+bool Trionic5GetChipTypes();
+bool Trionic5EraseFLASH();
+bool Trionic5DumpFLASH();
+bool Trionic5DumpFLASH2();
+bool Trionic5SendFLASHUpdate();
+bool Trionic5SendC3Message();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,113 @@
+/*******************************************************************************
+
+Just4Trionic by Just4pLeisure    
+*****************************
+
+Whilst I have written this program myself I could not have done it without
+a lot of help and the original ideas and programs written by:
+Dilemma - Author of the Trionic Suite software programs and general Guru
+http://trionic.mobixs.eu/ and http://www.ecuproject.com.
+General Failure - Author of the T5CANlib software and regular contributor at
+http://www.ecuproject.com.
+Tomi Liljemark - Lots of information and programs about the Saab CAN bus
+http://pikkupossu.1g.fi/tomi/projects/projects.html.
+Scott Howard - Author of the BDM software.
+Plus inspiration and ideas from many others, including johnc.
+
+Sophie x
+
+********************************************************************************
+
+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.
+
+********************************************************************************
+
+Version 1 (04/2010)- The basic functions are working, but not very easy to use
+
+I have decided to 'release' this software somewhat prematurely because the FLASH
+chips in my spare ECU have 'died' and I don't know when I will be able to do
+carry on improving and 'polishing' it. This way others will be able to use and
+enhance it without having to wait for me.
+
+For now, only option '5' Trionic ECU CAN interface is working. BDM and Lawicell
+CAN232 functions are dummies. The intention is to build a complete suite of CAN
+software for Trionic5, 7 and 8 ECU types as well as adding a BDM interface to
+make an 'all-in-one' USB programming tool.
+
+To make this you will need an mbed system and the CAN circuit from this page:
+http://mbed.org/projects/cookbook/wiki/CanBusExample1
+
+Some ideas for the truly creative and adventurous of you is to make a gizmo that
+doesn't even need to be connected to a laptop or PC to use, maybe even a self-
+contained vesion of Dilemma's CarPC using ideas from this pages:
+
+http://mbed.org/projects/cookbook/wiki/PS2Keyboard
+http://mbed.org/projects/cookbook/wiki/PS2Mouse
+http://mbed.org/projects/cookbook/wiki/MobileLCD
+http://mbed.org/projects/cookbook/wiki/SDCard
+
+*******************************************************************************/
+
+#include "mbed.h"
+
+#include "strings.h"
+#include "BDM.h"
+#include "CAN232.h"
+#include "Trionic5.h"
+
+Serial pc(USBTX, USBRX); // tx, rx
+
+#define CR 0x0D
+#define NL 0x0A
+#define BELL 0x07
+
+#define TRUE 1
+#define FALSE 0
+
+void ShowHelp();
+
+int main() {
+    // fast serial speed
+//    pc.baud(921600);
+    pc.baud(115200);
+    // make plenty of space for command from RS232
+    char command[5];
+    ShowHelp();
+    while (1) {
+// Only get 2 characters, the command and \r
+// For now I cannot work out how to use gets() so only single characters
+// can be used for commands :-(
+//
+// At the moment only option '5' does anything - the Trionic5 functions
+//
+        pc.gets(command,2);
+        int len = strlen(command);
+        if (len != 1) 
+            printf ("\a");
+        else if (ToUpper(command[0]) == 'B')
+            BDM();
+        else if (ToUpper(command[0]) == 'O')
+            CAN232();
+        else if (ToUpper(command[0]) == '5')
+            Trionic5();
+        else if (ToUpper(command[0]) == 'H')
+            ShowHelp();
+//  Unrecognised so ring the BELL :(
+        else
+            printf ("\a");
+    }
+}
+
+void ShowHelp() {
+    printf("Just4Trionic Command Menu\r\n");
+    printf("=========================\r\n");
+    printf("b/B - Start BDM interface (NOT DONE)\r\n");
+    printf("o/O - Open Lawicel CAN232 type interface (NOT DONE)\r\n");
+    printf("5   - Start Trionic5 ECU CAN interface\r\n");
+    printf("\r\n");
+    printf("h/H - show this help menu\r\n");
+    return;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/49a220cc26e0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/strings.cpp	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,84 @@
+/*******************************************************************************
+
+Strings.cpp - By Sophie Dexter, April 2010
+
+This C++ module provides functions for working with 'strings' of ascii 'char's
+
+C++ should have functions like these, but it doesn't seem to so these are my
+own ones. They are very simple and do not check for any errors e.g. StrCpy
+does not check that s1 is big enough to store all of s2.
+
+********************************************************************************
+
+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 "strings.h"
+
+// copies a string, s2, (array of chars) to another string, s1.
+char *StrCpy(char *s1, char *s2) {
+//    while (*s1++ = *s2++);
+    while (*s2)
+        *s1++ = *s2++;
+    return s1;
+}
+
+// returns the number of chars in a string
+int StrLen(char *s) {
+    int x = 0;
+    while (*s++)
+        x++;
+    return (x);
+}
+
+// checks s1 to see if it the same as s2
+// returns TRUE if there is a match
+// WARNING actually only checks that s1 starts with s2!
+bool StrCmp(char *s1, char *s2) {
+//    while (*s2 != '\0') {
+    while (*s2) {
+        if (*s1++ != *s2++) {
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+// Converts lower case ascii letters a-z to upper case
+// leaves other characters unchanged
+char ToUpper(char c) {
+    if (c >= 'a' && c <= 'z')
+        c -= 32;
+    return c;
+}
+
+// Converts upper case ascii letters A-Z to lower case
+// leaves other characters unchanged
+char ToLower(char c) {
+    if (c >= 'A' && c <= 'Z')
+        c += 32;
+    return c;
+}
+
+// Converts ASCII numbers 0-9 and letters a-f (and A-F) to hex values 0x00-0x0F
+// leaves other characters unchanged
+// ASCII '0' is worth 0x30 so subtract 0x30 if ascii character is a number
+// Lower case ASCII letter 'a' is worth 0x61, but we want this to be worth 0x0A
+// Subtract 0x57 (0x61 + 0x0A = 0x57) from lower case letters
+// Upper case ASCII letter 'A' is worth 0x41, but we want this to be worth 0x0A
+// Subtract 0x37 (0x41 + 0x0A = 0x37) from upper case letters
+
+char *aToh(char *s) {
+    while (*s) {
+        if ((*s >= '0') && (*s <='9'))
+            *s -= '0';
+        else if ((*s >= 'a') && (*s <='f'))
+            *s -= ('a' - 0x0A);
+        else if ((*s >= 'A') && (*s <='F'))
+            *s -= ('A' - 0x0A);
+        s++;
+    }
+    return s;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/strings.h	Wed May 19 12:39:18 2010 +0000
@@ -0,0 +1,12 @@
+
+// strings.h - information and definitions needed for working with strings of characters (ascii 'char')
+
+#define TRUE 1
+#define FALSE 0
+
+extern char *StrCpy(char *s1, char *s2);
+extern int StrLen(char *s);
+extern bool StrCmp(char *s1, char *s2);
+extern char ToUpper(char c);
+extern char ToLower(char c);
+extern char *aToh(char *s);
\ No newline at end of file