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.
Die Firmware zum Lesen und Beschreiben des Flash-Speichers ist zwar lang, aber einfach zu verstehen. Sie besteht aus vier Teilen:
- Die einfachen Eingangs/Ausgangsfunktionen
- Die Schreibe/Lesebefehlsfunktionen
- Die speziellen Flash-Funktionen
- 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.
* 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();
}
}