Just4Trionic - CAN and BDM FLASH programmer for Saab cars
Fork of Just4Trionic by
Revision 0:e0b964252a05, committed 2010-05-19
- Comitter:
- Just4pLeisure
- Date:
- Wed May 19 12:39:18 2010 +0000
- Child:
- 1:d5452e398b76
- Commit message:
Changed in this revision
--- /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