Arduino Flash Programmierer

Auf dieser Seite beschreibe ich meinen Flash-Speicher Programmer mit hilfe eines Arduinos. Der Programmer unterstützt die folgenden Flash-Speicher von Microchip im DIP32-Gehäuse:

  • 39F040 (512Kx8)
  • 39F020 (256Kx8)
  • 39F010 (128Kx8)

Die benötigten Bauteile sind:

  • 4x 74HC595
  • 1x 73HC165
  • 1x Arduino Uno
  • 1x a 40 or 32 pin Sockel (ZIF ist hier eine gute Wahl)
  • 6x 100nf Kondensatoreb
  • 1x 22 pin steckerleiste
  • 1x 6 pin buchsenleiste
  • 3x 1KΩ Widerstände
  • 3x leds (Farbe nicht wichtig)

Der Programmer kann auf einem Steckbrett oder auf Experimentierplatine aufgebaut werden, was dann deutluch robuster und zuverlässiger ist. Ich habe mich dafür entschieden, den Programmer mit der zweiten Methode aufzubauen.

Der Schaltplan des Programmers

Die Firmware zum Lesen und Beschreiben des Flash-Speichers ist zwar lang, aber einfach zu verstehen. Sie besteht aus vier Teilen:

  1. Die einfachen Eingangs/Ausgangsfunktionen
  2. Die Schreibe/Lesebefehlsfunktionen
  3. Die speziellen Flash-Funktionen
  4. Der Befehlsverarbeiter.

Der Befehlsverarbeiter nimmt 9 Befehle über die serielle Schnittstelle beio 115200 Baud entgegen. Die Befehle sind in der Firmware unten aufgelistet. Kurze Zeit später habe ich ein Programm geschrieben, dass es erlaubt, Dateien auf einem PC komfortabel in den Flash-Speicher zu brennen.

/* Title:       Flash Programmer
 * Author:      R.Lux
 * Last Edited: 05.10.17
 * Flash Programmer
 * Commands:
 * 00                     -> 00                 :NOP
 * 01                     -> 01 X ... X         :FIRMWARE PING
 * 02                     -> 02 ID              :Get chip ID
 * 03 55                  -> 03 R               :Erase entire chip
 * 04 55 HA MA LA         -> 04 R               :Erase Sector starting at HA MA LA
 * 05 HA MA LA            -> 05 X               :Get Byte X at HA MA LA
 * 06 55 HA MA LA X       -> 06 R               :Write Byte X to HA MA LA
 * 07 HA MA               -> 07 X ... XX C      :Get 256 Bytes from HA MA LA. C = 16 bit Checksum  X ... XX = 256 Bytes
 * 08 55 HA MA X ... XX C -> 08 R               :Write 256 Bytes to HA MA  C = 16 Checksum
 *
 * R = Return Code:
 * 00 = OK, 01 = didnt receive 0x55 confirmatioin, 02 = Checksum wasnt valid
 * C = 16 bit Checksum (MSB ... LSB)
 * Note: Serial buffer needs to increased to 300 bytes
 */


#define SHIFT_IN_LOAD 6
#define SHIFT_IN_CLOCK A0
#define SHIFT_IN_DATA 7
#define SHIFT_OUT_DATA 3
#define SHIFT_OUT_CLOCK 2
#define SHIFT_OUT_EN 5
#define SHIFT_OUT_SAVE 4
#define FLASH_RD A2
#define FLASH_WR A1

byte addresslow = 0;
byte addressmid = 0;
byte addresshigh = 0;
byte data = 0;
byte data_buffer[256] = {};
byte buffer_addresslow = 0;
byte buffer_addressmid = 0;
byte buffer_addresshigh = 0;
byte firmware_ping_response[16] = {0x46, 0x4c, 0x41, 0x53, 0x48, 0x50, 0x52, 0x4f, 0x47, 0x2e, 0x20, 0x56, 0x31, 0x2e, 0x30, 0x30};
unsigned int crc = 0;
unsigned int given_crc = 0;
byte crc_low = 0;
byte crc_high = 0;
byte given_crc_low = 0;
byte given_crc_high = 0;


void setup() {
  // Setup serial interface
  Serial.begin(115200);

  // Setup pin modes
  pinMode(SHIFT_IN_LOAD, OUTPUT);
  pinMode(SHIFT_IN_CLOCK, OUTPUT);
  pinMode(SHIFT_IN_DATA, INPUT);
  pinMode(SHIFT_OUT_DATA, OUTPUT);
  pinMode(SHIFT_OUT_CLOCK, OUTPUT);
  pinMode(SHIFT_OUT_EN, OUTPUT);
  pinMode(SHIFT_OUT_SAVE, OUTPUT);
  pinMode(FLASH_RD, OUTPUT);
  pinMode(FLASH_WR, OUTPUT);

  // Setup pin states
  digitalWrite(SHIFT_IN_LOAD, HIGH);
  digitalWrite(SHIFT_IN_CLOCK, LOW);
  digitalWrite(SHIFT_IN_DATA, LOW);
  digitalWrite(SHIFT_OUT_DATA, LOW);
  digitalWrite(SHIFT_OUT_CLOCK, LOW);
  digitalWrite(SHIFT_OUT_EN, HIGH);
  digitalWrite(SHIFT_OUT_SAVE, LOW);
  digitalWrite(FLASH_RD, HIGH);
  digitalWrite(FLASH_WR, HIGH);
}


void shiftoutbyte(byte data){
  // Loop through all bit from bit 7 to 0
  for(int index = 7; index > -1; index--){
    // Set Pin
    digitalWrite(SHIFT_OUT_DATA, bitRead(data, index));

    // Pulse Clock line
    digitalWrite(SHIFT_OUT_CLOCK, HIGH);
    digitalWrite(SHIFT_OUT_CLOCK, LOW);
  }
}

void dowritepulse(){
  digitalWrite(FLASH_WR, LOW);
  digitalWrite(FLASH_WR, HIGH);
}

void setAddressData(){

  //Shift out address and data
  shiftoutbyte(addresshigh);
  shiftoutbyte(addressmid);
  shiftoutbyte(addresslow);
  shiftoutbyte(data);

  //Save output data
  digitalWrite(SHIFT_OUT_SAVE, HIGH);
  delayMicroseconds(1);
  digitalWrite(SHIFT_OUT_SAVE, LOW);
 
}


byte readData(){
  // Set address
  setAddressData();
 
  // Tell flash to output data
  digitalWrite(FLASH_RD, LOW);
 
  // Save data into shift register
  digitalWrite(SHIFT_IN_LOAD, LOW);
  digitalWrite(SHIFT_IN_LOAD, HIGH);

  digitalWrite(FLASH_RD, HIGH);

  // Shift in data
  for(int index=7; index > -1; index--){
    // Write bit into variable
    bitWrite(data, index, digitalRead(SHIFT_IN_DATA));

    // Pulse clock
    digitalWrite(SHIFT_IN_CLOCK, HIGH);
    digitalWrite(SHIFT_IN_CLOCK, LOW);

  }
  return data;
}

void writeData(){
  // Transfer address and data into shift registers
  setAddressData();
  // Enable shift register output
  digitalWrite(SHIFT_OUT_EN, LOW);
  // Write data at address
  dowritepulse();
  // Disable shift register output again
  digitalWrite(SHIFT_OUT_EN, HIGH);
}

void writeByte(){
  // Save data and address which is to be written
  byte temp_data = data;
  byte temp_addr_h = addresshigh;
  byte temp_addr_m = addressmid;
  byte temp_addr_l = addresslow;

  // Write magic sequence to flash
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xAA;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x2A;
  addresslow = 0xAA;
  data = 0x55;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xA0;
  writeData();

  // Write the actual data to the address now!
  addresshigh = temp_addr_h;
  addressmid = temp_addr_m;
  addresslow = temp_addr_l;
  data = temp_data;
  writeData();
}

byte eraseSector(){
  // Save data and address which is to be written
  byte temp_data = data;
  byte temp_addr_h = addresshigh;
  byte temp_addr_m = addressmid;
  byte temp_addr_l = addresslow;

  // Write magic sequence to flash
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xAA;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x2A;
  addresslow = 0xAA;
  data = 0x55;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0x80;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xAA;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x2A;
  addresslow = 0xAA;
  data = 0x55;
  writeData();

  // Now give the flash the sector start address
  // Restore address
  addresshigh = temp_addr_h;
  addressmid = temp_addr_m;
  addresslow = temp_addr_l;
  data = 0x30;
  writeData();

  //Mandatory  delay
  delay(25);
}

void eraseChip(){
  // Write magic sequence to flash
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xAA;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x2A;
  addresslow = 0xAA;
  data = 0x55;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0x80;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xAA;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x2A;
  addresslow = 0xAA;
  data = 0x55;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0x10;
  writeData();

  delay(100);
}

byte getChipId(){
  // Write magic sequence to flash
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0xAA;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x2A;
  addresslow = 0xAA;
  data = 0x55;
  writeData();
  addresshigh = 0x00;
  addressmid = 0x55;
  addresslow = 0x55;
  data = 0x90;
  writeData();
 
  // Fetch ID now
  addresshigh = 0x00;
  addressmid = 0x00;
  addresslow = 0x01;
  byte ID = readData();

  // Exit ID Mode
  data = 0xF0;
  writeData();
 
  return ID;
}

void databuffercrc(){
  // Add all bytes of the data buffer together
  crc = 0;
  for(int index = 0; index != 256; index++){
    crc = crc + data_buffer[index];
  }
  crc_low = lowByte(crc);
  crc_high = highByte(crc);
}



void loop() {
  // Check if bytes are available
  if(Serial.available() > 0){
    // Main interpreter
    byte command = Serial.read();
    Serial.write(command); // Send back command
    delay(1);
    switch (command){
      case 0: // NOP
        Serial.write(0);
        break;
      case 1: // Firmware Ping
        Serial.write(firmware_ping_response, 16);
        break;
      case 2: // Get chip Id
        Serial.write(getChipId());
        break;
      case 3: // Erase entire chip
        // check if second byte is 0x55 (dont erase flash by accident)
        if(Serial.read() == 0x55){
          eraseChip();
          Serial.write(0); // Erase succesfull
        } else {
          Serial.write(1); // There was an error
        }
        break;
      case 4: // Erase sector starting at given address
        // check if second byte is 0x55 (dont erase flash by accident)
        if(Serial.read() == 0x55){
          addresshigh = Serial.read();
          addressmid = Serial.read();
          addresslow = Serial.read();
          eraseSector();
          Serial.write(0); // Erase succesfull
        } else {
          Serial.write(1); // There was an error
        }
        break;
      case 5: // Read single byte
          addresshigh = Serial.read();
          addressmid = Serial.read();
          addresslow = Serial.read();
          Serial.write(readData());
          break;
      case 6: // Write single byte
          // check if second byte is 0x55 (dont write flash by accident)
          if(Serial.read() == 0x55){
             addresshigh = Serial.read();
             addressmid = Serial.read();
             addresslow = Serial.read();
             data = Serial.read();
             writeByte();
             Serial.write(0); // Write succesfull
          } else {
            // remove all data bytes
            for(int index = 0; index < 4; index++){
              byte temp = Serial.read();
            }
             Serial.write(1); // There was an error
          }
          break;
      case 7: // Read 256 bytes starting at address into data buffer and send over serial
          addresshigh = Serial.read();
          addressmid = Serial.read();
          addresslow = 0;
          // Fill buffer
          for(addresslow; addresslow != 0xFF; addresslow++){
            data_buffer[addresslow] = readData();
          }
          // Transfer last Byte
          addresslow = 0xFF;
          data_buffer[addresslow] = readData();
         
         
          // Calculate CRC
          databuffercrc();

          // Write Buffer to serial
          Serial.write(data_buffer, 256);
          // Send CRC
          Serial.write(crc_high);
          Serial.write(crc_low);
          break;
     
       case 8: // Read 256 bytes from the serial port, check the crc and then write to flash
          // Check for the confirmation byte
          if(Serial.read() == 0x55){
            addresshigh = Serial.read();
            addressmid = Serial.read();
            addresslow = 0;
            for(addresslow; addresslow != 255; addresslow++){
              while(Serial.available() < 1)delayMicroseconds(100);
              data_buffer[addresslow] = Serial.read();
            }
            addresslow = 0xFF;
            while(Serial.available() < 1)delayMicroseconds(100);
            data_buffer[addresslow] = Serial.read();

            // Wait for bytes
            // Read given CRC from Serial
            while(Serial.available() < 1)delayMicroseconds(100);
            given_crc_high = Serial.read();
            while(Serial.available() < 1)delayMicroseconds(100);
            given_crc_low = Serial.read();

            // Calculate CRC for data buffer
            databuffercrc();
         
            // Compare given CRC with the calculated one
            if((given_crc_low != crc_low) or (given_crc_high != crc_high)){
              // CRCs dont match!
              Serial.write(2);
              break;
            }
            // CRCs match, so we write the buffer to the flash now
            for(addresslow = 0; addresslow != 255; addresslow++){
              data = data_buffer[addresslow];
              writeByte();
            }
            addresslow = 0xFF;
            data = data_buffer[addresslow];
            writeByte();
            Serial.write(0);
          } else {
            // Remove data bytes
            for(int index = 0; index < 258; index++){
              byte dump = Serial.read();
            }
            // Tell computer that the confirmation byte wasnt received
            Serial.write(1);
           
          }
          break;

    }
    Serial.flush();
     
  }
 

}