Sophie Dexter
/
Just4Trionic
Just4Trionic - CAN and BDM FLASH programmer for Saab cars
bdmtrionic.cpp
- Committer:
- Just4pLeisure
- Date:
- 2010-09-14
- Revision:
- 1:d5452e398b76
- Child:
- 2:bf3a2b29259a
File content as of revision 1:d5452e398b76:
/******************************************************************************* bdmtrionic.cpp (c) 2010 by Sophie Dexter General purpose BDM functions for Just4Trionic by Just4pLeisure A derivative work based on: //----------------------------------------------------------------------------- // CAN/BDM adapter firmware // (C) Janis Silins, 2010 // $id$ //----------------------------------------------------------------------------- ******************************************************************************** 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 "bdmtrionic.h" // structure for command address/value pairs struct mempair_t { uint32_t addr; ///< target address uint16_t val; ///< word value }; // word write algorithm (29Fxxx) static const struct mempair_t am29_write [] = { {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0xa0a0}, }; // chip erase algorithms static const struct mempair_t am29_erase [] = { {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0x8080}, {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0x1010} }; // reset algorithms //static const struct mempair_t am29_reset = {0xfffe, 0xf0f0}; static const struct mempair_t am29_reset [] = { {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0xf0f0}, }; // chip id algorithms static const struct mempair_t am29_id [] = { {0xaaaa, 0xaaaa}, {0x5554, 0x5555}, {0xaaaa, 0x9090}, }; // ;-) static const struct mempair_t flash_tag [] = { {0x7fe00, 0xFF4A}, {0x7fe02, 0x7573}, {0x7fe04, 0x7434}, {0x7fe06, 0x704C}, {0x7fe08, 0x6569}, {0x7fe0a, 0x7375}, {0x7fe0c, 0x7265}, {0x7fe0e, 0x3B29}, }; // local functions bool reset_am29(void); bool erase_am29(); bool flash_am29(const uint32_t* addr, uint16_t value); bool reset_am28(void); bool erase_am28(const uint32_t* start_addr, const uint32_t* end_addr); bool flash_am28(const uint32_t* addr, uint16_t value); bool get_flash_id(uint8_t* make, uint8_t* type); //----------------------------------------------------------------------------- /** Dumps contents of a memory block from [start_addr] up to, but not including, the [end_addr] as long words (word-aligned addresses). MCU must be in background mode. The operation interrupts if the break character is received. @param start_addr block start address @param end_addr block end address @return status flag */ uint8_t dump_flash(const uint32_t* start_addr, const uint32_t* end_addr) { // check parametres if (*start_addr > *end_addr) { return TERM_ERR; } // dump memory contents uint32_t curr_addr = *start_addr; uint32_t value; while ((curr_addr < *end_addr) && (pc.getc() != TERM_BREAK)) { // read long word if (curr_addr > *start_addr) { if (memdump_long(&value) != TERM_OK) { return TERM_ERR; } } else { if (memread_long(&value, &curr_addr) != TERM_OK) { return TERM_ERR; } } // send memory value to host printf("%08X", value); // add the terminating character if (curr_addr < *end_addr - 4) { pc.putc(TERM_OK); // light up the activity LED // led_on(LED_ACT); led3 = 1; } curr_addr += 4; } return TERM_OK; } //----------------------------------------------------------------------------- /** Dumps the contents of a T5 ECU to a BIN file on the mbed 'disk' from [start_addr] up to, but not including, the [end_addr]. MCU must be in background mode. @param start_addr block start address @param end_addr block end address @return status flag */ uint8_t dump_trionic() { // Configure the MC68332 register values to prepare for flashing printf("I am trying to discover what type of Trionic ECU I am connected to...\r\n"); prep_t5_do(); // Work out what type of FLASH chips we want to make a dump file for uint8_t make; uint8_t type; get_flash_id(&make, &type); // set up chip-specific functions bool (*reset_func)(); uint32_t flash_size; switch (type) { case AMD29F400B: case AMD29F400T: printf("I have found AMD29F400 type FLASH chips; I must be connected to a T7 ECU :-)\r\n"); reset_func = &reset_am29; flash_size = T7FLASHSIZE; break; case AMD29F010: printf("I have found AMD29F010 type FLASH chips; I must be connected to a repaired T5.5 ECU :-)\r\n"); reset_func = &reset_am29; flash_size = T55FLASHSIZE; break; case AMD28F010: case INTEL28F010: printf("I have found 28F010 type FLASH chips; I must be connected to a T5.5 ECU :-)\r\n"); reset_func = &reset_am28; flash_size = T55FLASHSIZE; break; case AMD28F512: case INTEL28F512: printf("I have found 28F512 type FLASH chips; I must be connected to a T5.2 ECU :-)\r\n"); reset_func = &reset_am28; flash_size = T52FLASHSIZE; break; default: // unknown flash type printf("I could not work out what FLASH chips or TRIONIC ECU I am connected to :-(\r\n"); return TERM_ERR; } // reset the FLASH chips if (!reset_func()) return TERM_ERR; printf("Creating FLASH dump file...\r\n"); FILE *fp = fopen("/local/original.bin", "w"); // Open "original.bin" on the local file system for writing if (!fp) { perror ("The following error occured"); return TERM_ERR; } // dump memory contents uint32_t addr = 0x00; uint32_t long_value; // setup start address to dump from if (memread_long_cmd(&addr) != TERM_OK) return TERM_ERR; timer.reset(); timer.start(); while (addr < flash_size) { uint16_t byte_count = 0; while (byte_count < FILE_BUF_LENGTH) { // get long word if (memget_long(&long_value) != TERM_OK) return TERM_ERR; addr += 4; // send memory value to file_buffer before saving to mbed 'disk' file_buffer[byte_count] = ((uint8_t)(long_value >> 24)); file_buffer[byte_count+1] = ((uint8_t)(long_value >> 16)); file_buffer[byte_count+2] = ((uint8_t)(long_value >> 8)); file_buffer[byte_count+3] = ((uint8_t)long_value); byte_count +=4; } // make the activity led twinkle led3 = 1; fwrite(file_buffer, 1, FILE_BUF_LENGTH, fp); if (ferror (fp)) { fclose (fp); printf ("Error writing to the FLASH BIN file.\r\n"); return TERM_ERR; } } // should 'clear' the BDM connection here but bdm_clear won't compile from here // instead do a memread (or anything really) but ignore the result because it's not needed for anything memread_long(&long_value, &addr); timer.stop(); printf("Getting the FLASH dump took %#.1f seconds.\r\n",timer.read()); fclose(fp); return TERM_OK; } //----------------------------------------------------------------------------- /** Erases the flash memory chip starting from [start_addr] up to, but not including [end_addr] and optionally verifies the result; MCU must be in background mode. @param flash_type type of flash chip @param start_addr flash start address @param end_addr flash end address @return status flag */ uint8_t erase_flash(const char* flash_type, const uint32_t* start_addr, const uint32_t* end_addr) { // AM29Fxxx chips (retrofitted to Trionic 5.x; original to T7) if (strncmp(flash_type, "29f010", 6) == 0 || strncmp(flash_type, "29f400", 6) == 0) { return erase_am29() ? TERM_OK : TERM_ERR; } // AM28F010 chip (Trionic 5.x original) if (strncmp(flash_type, "28f010", 6) == 0) { return erase_am28(start_addr, end_addr) ? TERM_OK : TERM_ERR; } return TERM_ERR; } //----------------------------------------------------------------------------- /** Writes a batch of long words to the flash starting from [start_addr]. The operation interrupts if a break character is received. MCU must be in background mode. @param flash_type type of flash chip @param start_addr block start address @return status flag */ uint8_t write_flash(const char* flash_type, const uint32_t* start_addr) { // set up chip-specific functions bool (*reset_func)(void); bool (*flash_func)(const uint32_t*, uint16_t); // AM29Fxxx chips (retrofitted to Trionic 5.x, original to T7) if (strncmp(flash_type, "29f010", 6) == 0 || strncmp(flash_type, "29f400", 6) == 0) { reset_func = &reset_am29; flash_func = &flash_am29; } else if (strncmp(flash_type, "28f010", 6) == 0) { // AM28F010 chip (Trionic 5.x original) reset_func = &reset_am28; flash_func = &flash_am28; } else { // unknown flash type return TERM_ERR; } // reset the flash if (!reset_func()) { return TERM_ERR; } uint32_t curr_addr = *start_addr; if (strncmp(flash_type, "29f010", 6) == 0) { curr_addr = 0; } int rx_char = 0; char rx_buf[8]; char* rx_ptr; uint32_t long_value; bool ret = true; // ready to receive data pc.putc(TERM_OK); while (true) { // receive long words from USB printf("receive long words from USB\r\n"); rx_ptr = rx_buf; do { rx_char = pc.getc(); if (rx_char != EOF) { // have got all characters for one long word if (rx_ptr > &rx_buf[7]) { ret = (rx_char == TERM_OK); break; } // save the character *rx_ptr++ = (char)rx_char; } } while (rx_char != TERM_OK && rx_char != TERM_BREAK); // end writing printf("end writing\r\n"); if (!ret || rx_char == TERM_BREAK) { break; } // convert value to long word printf("convert value to long word\r\n"); if (!ascii2int(&long_value, rx_buf, 8)) { ret = false; break; } printf("long value %08x \r\n", long_value); // write the first word printf("write the first word\r\n"); if (!flash_func(&curr_addr, (uint16_t)(long_value >> 16))) { ret = false; break; } curr_addr += 2; // write the second word printf("write the second word\r\n"); if (!flash_func(&curr_addr, (uint16_t)long_value)) { ret = false; break; } curr_addr += 2; // light up the activity LED // led_on(LED_ACT); led3 = 1; } // reset flash return (reset_func() && ret) ? TERM_OK : TERM_ERR; } //----------------------------------------------------------------------------- /** Writes a BIN file to the flash starting from [start_addr]. The operation ends when no more bytes can be read from the BIN file. MCU must be in background mode. @param flash_type type of flash chip @param start_addr block start address @return status flag */ uint8_t flash_trionic() { // Configure the MC68332 register values to prepare for flashing printf("I am trying to discover what type of Trionic ECU I am connected to...\r\n"); prep_t5_do(); // Work out what type of FLASH chips we want to program uint8_t make; uint8_t type; get_flash_id(&make, &type); // set up chip-specific functions bool (*reset_func)(); bool (*flash_func)(const uint32_t*, uint16_t); uint32_t flash_size; switch (type) { case AMD29F400B: case AMD29F400T: printf("I have found AMD29F400 type FLASH chips; I must be connected to a T7 ECU :-)\r\n"); reset_func = &reset_am29; flash_func = &flash_am29; flash_size = T7FLASHSIZE; break; case AMD29F010: printf("I have found AMD29F010 type FLASH chips; I must be connected to a repaired T5.5 ECU :-)\r\n"); reset_func = &reset_am29; flash_func = &flash_am29; flash_size = T55FLASHSIZE; break; case AMD28F010: case INTEL28F010: printf("I have found 28F010 type FLASH chips; I must be connected to a T5.5 ECU :-)\r\n"); reset_func = &reset_am28; flash_func = &flash_am28; flash_size = T55FLASHSIZE; break; case AMD28F512: case INTEL28F512: printf("I have found 28F512 type FLASH chips; I must be connected to a T5.2 ECU :-)\r\n"); reset_func = &reset_am28; flash_func = &flash_am28; flash_size = T52FLASHSIZE; break; default: // unknown flash type printf("I could not work out what FLASH chips or TRIONIC ECU I am connected to :-(\r\n"); return TERM_ERR; } // reset the FLASH chips if (!reset_func()) return TERM_ERR; printf("Checking the FLASH BIN file...\r\n"); FILE *fp = fopen("/local/modified.hex", "r"); // Open "modified.hex" on the local file system for reading if (!fp) { printf("Error: I could not find the BIN file MODIFIED.HEX\r\n");; return TERM_ERR; } // obtain file size - it should match the size of the FLASH chips: fseek (fp , 0 , SEEK_END); uint32_t file_size = ftell (fp); rewind (fp); // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU uint8_t stack_byte = 0; uint32_t stack_long = 0; if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= (stack_byte << 24); if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= (stack_byte << 16); if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= (stack_byte << 8); if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; stack_long |= stack_byte; rewind (fp); if (flash_size == T52FLASHSIZE && (file_size != T52FLASHSIZE || stack_long != T5POINTER)) { fclose(fp); printf("The BIN file does not appear to be for a T5.2 ECU :-(\r\n"); printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, flash_size, stack_long); return TERM_ERR; } if (flash_size == T55FLASHSIZE && (file_size != T55FLASHSIZE || stack_long != T5POINTER)) { fclose(fp); printf("The BIN file does not appear to be for a T5.5 ECU :-(\r\n"); printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, flash_size, stack_long); return TERM_ERR; } if (flash_size == T7FLASHSIZE && (file_size != T7FLASHSIZE || stack_long != T7POINTER)) { fclose(fp); printf("The BIN file does not appear to be for a T7 ECU :-(\r\n"); printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, flash_size, stack_long); return TERM_ERR; } timer.reset(); timer.start(); uint32_t curr_addr = 0; switch (type) { // AM29Fxxx chips (retrofitted to Trionic 5.x; original to T7) case AMD29F400B: case AMD29F400T: case AMD29F010: printf("Erasing 29F400/010 type FLASH chips...\r\n"); if (erase_am29() == TERM_ERR) { printf("WARNING: An error occured when I tried to erase the FLASH chips :-(\r\n"); return TERM_ERR; } break; // AM28F010 chip (Trionic 5.x original) case AMD28F010: case INTEL28F010: case AMD28F512: case INTEL28F512: printf("Erasing 28F010/512 type FLASH chips...\r\n"); if (erase_am28(&curr_addr, &flash_size) == TERM_ERR) { printf("WARNING: An error occured when I tried to erase the FLASH chips :-(\r\n"); return TERM_ERR; } break; default: // unknown flash type - shouldn't get here hence "Starange!" printf("Strange! I couldn't work out how to erase the FLASH chips in the TRIONIC ECU that I am connected to :-(\r\n"); return TERM_ERR; } timer.stop(); printf("Erasing took %#.1f seconds.\r\n",timer.read()); printf("Programming the FLASH chips...\r\n"); timer.reset(); timer.start(); uint16_t word_value = 0; uint8_t byte_value = 0; // bool ret = true; // ready to receive data while (curr_addr < flash_size) { // receive bytes from BIN file //Get a byte - break if no more bytes to get if (!fread(&byte_value,1,1,fp)) { fclose(fp); printf("Error reading the BIN file MODIFIED.HEX"); break; } word_value = (byte_value << 8); if (!fread(&byte_value,1,1,fp)) { fclose(fp); printf("Error reading the BIN file MODIFIED.HEX"); break; } word_value |= byte_value; // write the word if it is not 0xffff if (word_value != 0xffff) { if (!flash_func(&curr_addr, word_value)) break; } curr_addr += 2; // make the activity LED twinkle led3 = 1; } timer.stop(); fclose(fp); if (curr_addr == flash_size) { printf("Programming took %#.1f seconds.\r\n",timer.read()); reset_func(); for (uint8_t i = 0; i < 8; ++i) { memread_word(&word_value, &flash_tag[i].addr); flash_func(&flash_tag[i].addr, (flash_tag[i].val & word_value)); } } else { printf("WARNING: Oh dear, I couldn't program the FLASH at address 0x%8x.\r\n", curr_addr); } // reset flash return (reset_func() && (curr_addr == flash_size)) ? TERM_OK : TERM_ERR; } //----------------------------------------------------------------------------- /** Resets an AM29Fxxx flash memory chip. MCU must be in background mode. @param none @return succ / fail */ bool reset_am29(void) { // execute the reset command // uint32_t addr = 0xfffe; // return (memwrite_word(&addr, 0xf0f0) == TERM_OK); // execute the algorithm for (uint8_t i = 0; i < 3; ++i) { if (memwrite_word(&am29_reset[i].addr, am29_reset[i].val) != TERM_OK) return false; } return true; } //----------------------------------------------------------------------------- /** Erases an AM29Fxxx flash memory chip and verifies the result; MCU must be in background mode. @return succ / fail */ bool erase_am29() { // reset flash if (!reset_am29()) { return false; } // execute the algorithm for (uint8_t i = 0; i < 6; ++i) { if (memwrite_word(&am29_erase[i].addr, am29_erase[i].val) != TERM_OK) { reset_am29(); return false; } } // verify the result uint32_t addr = 0x0; uint16_t verify_value; uint8_t err_cnt = ERR_COUNT; while (--err_cnt) { // typical erase time = 1s // Allow up to 25.5 seconds erase time wait_ms(100); if (memread_word(&verify_value, &addr) == TERM_OK && verify_value == 0xffff) { // erase completed normally reset_am29(); return true; } } // erase failed reset_am29(); return false; } //----------------------------------------------------------------------------- /** Writes a word to AM29Fxxx flash memory chip and optionally verifies the result; MCU must be in background mode. @param addr destination address @param val value @return succ / fail */ bool flash_am29(const uint32_t* addr, uint16_t value) { // execute the algorithm for (uint8_t i = 0; i < 3; ++i) { if (memwrite_word(&am29_write[i].addr, am29_write[i].val) != TERM_OK) { reset_am29(); return false; } } // write the value if (memwrite_word(addr, value) != TERM_OK) { reset_am29(); return false; } // verify the result uint8_t err_cnt = ERR_COUNT; while (--err_cnt) { // Allow up to approx 2.55 milliseconds program time (255 * ~10us BDM memread time) // wait_ms(10); uint16_t verify_value; if ((memread_word(&verify_value, addr) == TERM_OK) && (verify_value == value)) { // flashing successful return true; } } // writing failed reset_am29(); return false; } //----------------------------------------------------------------------------- /** Resets a AM28Fxxx flash memory chip. MCU must be in background mode. @param start_addr flash start address @return succ / fail */ bool reset_am28(void) { uint32_t start_addr = 0x0; return (memwrite_word(&start_addr, 0xffff) == TERM_OK && memwrite_word(&start_addr, 0xffff) == TERM_OK); } //----------------------------------------------------------------------------- /** Erases an AM28Fxxx flash memory chip and verifies the result; MCU must be in background mode. @param start_addr flash start address @param end_addr flash end address @return succ / fail */ bool erase_am28(const uint32_t* start_addr, const uint32_t* end_addr) { // check the addresses if (!start_addr || !end_addr) return false; // reset flash if (!reset_am28()) return false; // write zeroes over entire flash space uint32_t addr = *start_addr; while (addr < *end_addr) { if (!flash_am28(&addr, 0x0000)) return false; addr += 2; // // feedback to host computer // pc.putc(TERM_OK); // make the activity LED twinkle // led3 = (addr & 0x400); led3 = 1; } // erase flash addr = *start_addr; uint8_t verify_value; uint16_t pulse_cnt = 0; if (memwrite_byte_cmd(NULL) != TERM_OK) { reset_am28(); return false; } while ((++pulse_cnt < 1000) && (addr < *end_addr)) { // issue the erase command if (memwrite_write_byte(&addr, 0x20) != TERM_OK || memwrite_write_byte(&addr, 0x20) != TERM_OK) break; wait_ms(10); while (addr < *end_addr) { // issue the verify command if (memwrite_read_byte(&addr, 0xa0) != TERM_OK) break; // wait_us(6); // check the written value if (memread_write_byte(&verify_value, &addr) != TERM_OK) break; if (verify_value != 0xff) break; // succeeded need to check next address addr++; // make the activity LED twinkle led3 = 1; } } // the erase process ends with a BDM_WRITE + BDM_BYTESIZE command left in the BDM // it is safe to use it to put one of the FLASH chips into read mode and thereby // leave the BDM ready for the next command memwrite_nop_byte(start_addr, 0x00); reset_am28(); // check for success return (addr == *end_addr) ? true : false; } //----------------------------------------------------------------------------- /** Writes a byte to AM28Fxxx flash memory chip and verifies the result A so called 'mask' method checks the FLASH contents and only tries to program bytes that need to be programmed. MCU must be in background mode. @param addr destination address @param val value @return succ / fail */ bool flash_am28(const uint32_t* addr, uint16_t value) { if (!addr) return false; uint8_t pulse_cnt = 0; uint16_t verify_value = 0; uint16_t mask_value = 0xffff; // put flash into read mode and read address if (memwrite_word_read_word(&verify_value, addr, 0x0000) != TERM_OK) return false; // return if FLASH already has the correct value - e.g. not all of the FLASH is used and is 0xff if (verify_value == value) return true; while (++pulse_cnt < 25) { // set a mask if ((uint8_t)verify_value == (uint8_t)value) mask_value &= 0xff00; if ((uint8_t)(verify_value >> 8) == (uint8_t)(value >> 8)) mask_value &= 0x00ff; // write the new value if (memwrite_word_write_word(addr, (0x4040 & mask_value), value) != TERM_OK) break; // NOTE the BDM interface is slow enough that there is no need for a 10us delay before verifying // wait_us(10); // issue the verification command // NOTE the BDM interface is slow enough that there is no need for a 6us delay before reading back if (memwrite_word_read_word(&verify_value, addr, (0xc0c0 & mask_value)) != TERM_OK) break; // check if flashing was successful; if (verify_value == value) return true; } // something went wrong; reset the flash chip and return failed reset_am28(); return false; } //----------------------------------------------------------------------------- /** Does the equivalent of do prept5.do in BD32 Sets up all of the control registers in the MC68332 so that we can program the FLASH chips @param none @return succ / fail */ uint8_t prep_t5_do(void) { // reset and freeze the MC68332 chip if (restart_chip() != TERM_OK) return TERM_ERR; // set the 'fc' registers to allow supervisor mode access uint32_t long_value = 0x05; if (sysreg_write(0x0e, &long_value) != TERM_OK) return TERM_ERR; if (sysreg_write(0x0f, &long_value) != TERM_OK) return TERM_ERR; // Set MC68332 to 16 MHz (actually 16.78 MHz) long_value = 0x00fffa04; if (memwrite_word(&long_value, 0x7f00) != TERM_OK) return TERM_ERR; // Disable watchdog and monitors long_value = 0x00fffa21; if (memwrite_byte(&long_value, 0x00) != TERM_OK) return TERM_ERR; // Chip select pin assignments long_value = 0x00fffa44; if (memwrite_word(&long_value, 0x3fff) != TERM_OK) return TERM_ERR; // Boot Chip select read only, one wait state long_value = 0x00fffa48; if (memwrite_word(&long_value, 0x0007) != TERM_OK) return TERM_ERR; if (memfill_word(0x6870) != TERM_OK) return TERM_ERR; // Chip select 1 and 2 upper lower bytes, zero wait states long_value = 0x00fffa50; if (memwrite_word(&long_value, 0x0007) != TERM_OK) return TERM_ERR; if (memfill_word(0x3030) != TERM_OK) return TERM_ERR; if (memfill_word(0x0007) != TERM_OK) return TERM_ERR; if (memfill_word(0x5030) != TERM_OK) return TERM_ERR; // PQS Data - turn on VPPH long_value = 0x00fffc14; if (memwrite_word(&long_value, 0x0040) != TERM_OK) return TERM_ERR; // PQS Data Direction output long_value = 0x00fffc17; if (memwrite_byte(&long_value, 0x40) != TERM_OK) return TERM_ERR; // wait for programming voltage to be ready wait_ms(10); // // Enable internal 2kByte RAM of 68332 at address 0x00100000 // long_value = 0x00fffb04; // if (memwrite_word(&long_value, 0x1000) != TERM_OK) return TERM_ERR; return TERM_OK; } //----------------------------------------------------------------------------- /** Works out what type of flash chip is fitted in the ECU by reading the manufacturer byte codes. It is enough to use the 29Fxxx flash id algorithm because 28Fxxx FLASH chips ignore the first few writes needed by the 29Fxxx chips MCU must be in background mode. @param make (out) type (out) @return succ / fail */ bool get_flash_id(uint8_t* make, uint8_t* type) { uint32_t addr = 0x0; uint32_t value; bool ret; // read id bytes algorithm for 29F010/400 FLASH chips for (uint8_t i = 0; i < 3; ++i) { if (memwrite_word(&am29_id[i].addr, am29_id[i].val) != TERM_OK) return false; } if (memread_long(&value, &addr) != TERM_OK) return false; *make = (uint8_t)(value >> 24); *type = (uint8_t)(value >> 8); printf("FLASH id bytes: %08x, make: %02x, type: %02x\r\n", value, *make, *type); switch (*type) { case AMD29F400B: case AMD29F400T: case AMD29F010: case AMD28F010: case INTEL28F010: case AMD28F512: case INTEL28F512: ret = true; default: ret = false; } return ret; } //----------------------------------------------------------------------------- // EOF //-----------------------------------------------------------------------------