Programador EEPROM amb Arduino

De Wikijoan
Dreceres ràpides: navegació, cerca

Contingut

Introducció

Per fer el projecte Home-Built_Z-80_Computer tinc una EEPROM de 16K que he de programar amb el programa que hi vull ficar a dins. Per tant, necessito un programador de EEPROMs. N'hi ha de comercials, però també es pot fer amb Arduino. Es tracta de fer un programaor paral.lel, doncs aquestes memòries exposen tots els pins per direccionar les adreces (11 pins en el cas de 16Kb=2KBx8, 13 pins en el cas de 64Kb=8KBx8), i els 8 bits de les dades.

Intentaré reproduir aquest projecte:

Altres enllaços interessants:

Desenvolupament

EEPROM 28C64 (hi ha un error)
EEPROM 28C16
Programador eeprom 28C16.jpg

NOTA: per fer l'esquema m'he instal.lat Fritzing, tot i que hi ha coses que no he vist com funcionen.

L'article de referència grava una EEPROM 28C64, i jo en el projecte del Z80 he de gravar una 28C16. Resulta que són quasi compatibles, però no del tot. Puc fer un gravador que sigui compatible per tots dos, o anar directe al gra i que només sigui compatible pel 28C16, que és el que tinc (de fet, els 28C64 són més fàcils de trobar i més baratos a eBay).

Aniré al gra. Per tant, la imatge del projecte original no és correcta, i l'he de refer per tal que reflexi el petit canvi que hi ha (a part de tenir dues línies menys d'adreces).

Petita aclaració: La EEPROM 28C16 són 16Kb (kilo bits) de memòria, que són 2KB (kilo bytes). Com que el bus de dades és de 8 bits, queda clar que hi ha 2K posicions de memòria. Això és direcciona amb 11 pins: A0...A10.

ERROR. Compte perquè l'esquemàtic original està malament. Sorry Dave, your schematic seems wrong. Please check pinout of LS04 chip... Yes you are correct, green wires should be pins 1, 3 and 5 and purple wires 2, 4 and 6. Well done for finding that but I'm sorry but there is no prize for "Spot the deliberate mistake." :D. Això ja ho he corregit en l'esquemàtic que ja he fet per al 28C16.

NOTA. He adaptat el codi original per al meu 28C16. El que veia era que començava a gravar no en la direcció 0, sinó en la direcció 1. No se si amb el 28C64 el codi original funciona, però amb el 28C16 no funciona. Cal llegir atentament el datasheet del 28C16, com és l'operació d'escriptura i de lectura.

READ:
The AT28C16 is accessed like a Static RAM.
When CE(-)  and OE(-) are low and WE(-)  is high, the data stored at the memory location determined by the address pins is
asserted on the outputs. The outputs are put in a high impedance state whenever CE(-)  or OE(-)  is high. This dual line
control gives designers increased flexibility in preventing bus contention. 

BYTE WRITE:
Writing data into the AT28C16 is similar to writing into a Static RAM. A low pulse on the WE(-)  or CE(-) input with OE(-)
 high and CE(-)  or WE(-) low (respectively) initiates a byte write. The address location is latched on the last falling edge of WE(-)
 (or CE(-)); the new data is latched on the first rising edge. Internally, the device performs a self-clear before write.
Once a byte write has been started, it will automatically time itself to completion. Once a programming operation has been
initiated and for the duration of tWC, a read operation will effectively be a polling operation.

Per aclarir, també s'adjunta un codi mínim per escriure i llegir directament en una posició concreta de la memòria, concretament la posició 0 (o la que es vulgui)

EEPROM writer

Com es comenta en l'article, hi ha un problema amb el buffer a l'hora d'enviar les dades. Parteixo d'un fitxer rom/bin a l'ordinador que he generat amb l'ensamblador, i que representa el programa que vull gravar a la EEPROM i que són les instruccions que executarà el Z80 (en el futur).

Aquests bytes els he d'enviar al Arduino Mega, i el programa que hi ha a l'Arduino Mega ha de ser capaç de llegir els bytes, i gravar-los a la EEPROM. El que s'explica ara és que el procés d'enviar els bytes a l'arduino s'ha de fer en blocs de 64 bytes, i fent petites esperes per assegurar-nos de què no hi ha problemes. Això ho fem amb aquest bash script.

script eeprom_send.sh:

#!/bin/bash
split -C 64 ${1} ${1}.part.
for part in `ls ${1}.part.*`
do
 cat ${part} >> ${2}
 sleep 1
done
rm -f ${1}.part.*

To use it type:

$ ./eeprom_send.sh zx81.rom /dev/ttyACM0

But first you just need to program the Arduino and then open the serial monitor window (dont ask me why).

Here is the Arduino program: (l'adapto per la 28LC16):

script EEPROM_28C16_writer.ino:

// http://z80dave.blogspot.com.es/2011/09/21-building-eeprom-programmer-part-2.html
/*
 EEPROM Programmer
*/
#define memsize 2048

int STS   = 13;  // Status Indicator

int AP[11] = {22,24,26,28,30,32,34,36,38,40,42};
int AD[11] = {0,0,0,0,0,0,0,0,0,0,0};
int DP[8]  = {23,25,27,29,31,33,35,37};
int DD[8]  = {0,0,0,0,0,0,0,0};

int CE  = 4;
int OE  = 3;
int WE  = 2;

int i;
int A;
int D;
int wait;

void setup() {
  
  Serial.begin(115200);
  Serial.flush();
  
  pinMode(STS,   OUTPUT);
  
  // Setup Address Pins 
  for (i=0;i<11;i++) {
    pinMode(AP[i],OUTPUT);
  }
  
  // Setup Data Pins
  for (i=0;i<8;i++) {
    pinMode(DP[i],OUTPUT);
  }
  
  // Setup Control Pins
  pinMode(CE, OUTPUT);
  pinMode(WE, OUTPUT);
  pinMode(OE, OUTPUT);
  
  // Setup Chip
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, HIGH);
  //digitalWrite(OE, LOW);
  
  Serial.println("Waiting for Data...");
  while(Serial.available()==0) {
    digitalWrite(STS,LOW);
    delay(100);
    digitalWrite(STS,HIGH);
    delay(100);
  }

  for (A=0;A<memsize;A++) {
     
    D=Serial.read();
    Serial.println(D);
    digitalWrite(STS,HIGH);  //Signal that we're writing.

    // Setup Address Pins
    for (i=0;i<11;i++) {
      if((A&bit(i))>0) {
        AD[i]=HIGH;
      } else {
        AD[i]=LOW;
      }      
      digitalWrite(AP[i],AD[i]);
    }
    delay(1);
    
  // Setup Chip
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, HIGH);
    
    // Setup Data Pins
    for (i=0;i<8;i++) {
      if((D&bit(i))>0) {
        DD[i]=HIGH;
      } else {
        DD[i]=LOW;
      }      
      digitalWrite(DP[i],DD[i]);
    }
    delay(1);

    digitalWrite(OE, LOW);
    delay(1);
    digitalWrite(WE,HIGH);
    delay(1);
    digitalWrite(CE,HIGH);
    delay(1);

   digitalWrite(STS,LOW); // Signal that we're waiting
    wait=0;
    while(Serial.available()==0) {
      delay(1);
      if (wait>2000) {
        A=memsize;
        break;
      }
      wait++;
    }
    
  }
  
}

void loop() {
  digitalWrite(STS, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(STS, LOW);    // set the LED off
  delay(1000);              // wait for a second
}

EEPROM reader

script EEPROM_28C16_reader.ino:

//http://z80dave.blogspot.com.es/2011/09/20-building-eeprom-programmer.html
/*
 EEPROM Chip Reader
*/

#define memsize 2048

int STS   = 13;  // Status Indicator

int AP[11] = {22,24,26,28,30,32,34,36,38,40,42};
int AD[11] = {0,0,0,0,0,0,0,0,0,0,0};
int DP[8]  = {23,25,27,29,31,33,35,37};
int DD[8]  = {0,0,0,0,0,0,0,0};

int CE  = 4;
int OE  = 3;
int WE  = 2;

int i;
int j;
int D;
int A;

void setup() {
      
  // Setup Control Pins
  pinMode(CE, OUTPUT);
  pinMode(WE, OUTPUT);
  pinMode(OE, OUTPUT);
  
  // Disable Chip, and disable read and write.
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, LOW);

  Serial.begin(115200);
  
  Serial.println("Reading EEPROM...");
  
  pinMode(STS,   OUTPUT);
  digitalWrite(STS,HIGH);
  
  // Setup Address Pins 
  for (i=0;i<11;i++) {
    pinMode(AP[i],OUTPUT);
  }
  
  // Setup Data Pins
  for (i=0;i<8;i++) {
    pinMode(DP[i],INPUT);
  }

  delay(1000);
  
  for (A=0;A<memsize;) {
    
    if (A<4096) Serial.print("0");
    if (A<256)  Serial.print("0");
    if (A<16)   Serial.print("0");
    Serial.print(A,HEX);
    Serial.print(" ");

    for (j=0;j<11;j++) {
    
      // Setup Address Pins
      for (i=0;i<11;i++) {
        if((A&bit(i))>0) {
          AD[i]=HIGH;
        } else {
          AD[i]=LOW;
        }      
        digitalWrite(AP[i],AD[i]);
      }
    
      digitalWrite(CE,HIGH);      // Chip Enabled
      digitalWrite(OE,HIGH);      // Read Enabled
    
      // Read Data Pins
      D=0;
      for (i=0;i<8;i++) {
        DD[i]=digitalRead(DP[i]);
        D=D+bit(i)*DD[i];
      
      }
    
      digitalWrite(OE,LOW);     // Read Disabled
      digitalWrite(CE,LOW);     // Chip Disabled
    
      if (D<16) Serial.print("0");
      Serial.print(D,HEX);
      Serial.print(" ");
      //Serial.println(D);
      
      A++;
    }
    
    Serial.println();
    
  }
  
}

void loop() {
  digitalWrite(STS, HIGH);   // set the LED on
  delay(500);              // wait for a second
  digitalWrite(STS, LOW);    // set the LED off
  delay(500);              // wait for a second
}

EEPROM clear

Es tracta de posar a 0 tots els bytes.

script EEPROM_28C16_clear.ino:

// http://z80dave.blogspot.com.es/2011/09/21-building-eeprom-programmer-part-2.html
/*
 EEPROM Programmer
*/
#define memsize 2048

int STS   = 13;  // Status Indicator

int AP[11] = {22,24,26,28,30,32,34,36,38,40,42};
int AD[11] = {0,0,0,0,0,0,0,0,0,0,0};
int DP[8]  = {23,25,27,29,31,33,35,37};
int DD[8]  = {0,0,0,0,0,0,0,0};

int CE  = 4;
int OE  = 3;
int WE  = 2;

int i;
int A;
int D;
int wait;

void setup() {
  
  Serial.begin(115200);
  Serial.flush();
  
  pinMode(STS,   OUTPUT);
  
  // Setup Address Pins 
  for (i=0;i<11;i++) {
    pinMode(AP[i],OUTPUT);
  }
  
  // Setup Data Pins
  for (i=0;i<8;i++) {
    pinMode(DP[i],OUTPUT);
  }
  
  // Setup Control Pins
  pinMode(CE, OUTPUT);
  pinMode(WE, OUTPUT);
  pinMode(OE, OUTPUT);
  
  // Setup Chip
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, HIGH);

  for (A=0;A<memsize;A++) {
     
    D=0; //clear memor
    Serial.println(D);
    digitalWrite(STS,HIGH);  //Signal that we're writing.

    // Setup Address Pins
    for (i=0;i<11;i++) {
      if((A&bit(i))>0) {
        AD[i]=HIGH;
      } else {
        AD[i]=LOW;
      }      
      digitalWrite(AP[i],AD[i]);
    }
    delay(1);
    
  // Setup Chip
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, HIGH);
    
    // Setup Data Pins
    for (i=0;i<8;i++) {
      if((D&bit(i))>0) {
        DD[i]=HIGH;
      } else {
        DD[i]=LOW;
      }      
      digitalWrite(DP[i],DD[i]);
    }
    delay(1);

    digitalWrite(OE, LOW);
    delay(1);
    digitalWrite(WE,HIGH);
    delay(1);
    digitalWrite(CE,HIGH);
    delay(1);

  }
  Serial.println("clear memory finished");
}

void loop() {
  digitalWrite(STS, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(STS, LOW);    // set the LED off
  delay(1000);              // wait for a second
}

Reading EEPROM...
0000 00 00 00 00 00 00 00 00 00 00 00 
000B 00 00 00 00 00 00 00 00 00 00 00 
...
07F3 00 00 00 00 00 00 00 00 00 00 00 
07FE 00 00 00 00 00 00 00 00 00 00 00 

Efectivament la última posició de memòria és la 07FF (i s'acaba la línia). Són 0800 posicions de memòria = 8*16^2 = 2048.

Checksum. Comprovació

Per veure el contingut d'un fitxer d'una forma amigable: (el * vol dir que es repeteix la línia)

$ od -Ax -t x1 la_vaca_cega.txt 
000000 4c 61 20 56 61 63 61 20 43 65 67 61 0a 2d 2d 2d
000010 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d
*
000030 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 0a 54
000040 6f 70 61 6e 74 20 64 65 20 63 61 70 20 65 6e 20
000050 75 6e 61 20 69 20 61 6c 74 72 61 20 73 6f 63 61
000060 2c 0a 61 76 61 6e c3 a7 61 6e 74 20 64 27 65 73
...
0003a0 6e 74 20 6c 6c c3 a0 6e 67 75 69 64 61 6d 65 6e
0003b0 74 20 6c 61 20 6c 6c 61 72 67 61 20 63 75 61 2e
0003c0 0a
0003c1

Gravo el fitxer la_vaca_cega.txt a la ROM, i comprovaré que està bé, fent un checksum bàsic, que consisteix en sumar el valor de tots els bytes.

#include <stdio.h>
main()
{
 int c,sum;
 sum=0;
 while ((c=getchar()) != EOF) {
  sum=sum+c;
 }
 printf("Checksum: %d\n",sum);
}
$ gcc -o checksum checksum.c
./checksum < la_vaca_cega.txt
Checksum: 84646

Ara faig la lectura de la rom, i modifico el codi per tal que en el monitor sèrie sortin el contingut de les adreces en decimal, una sota de l'altra. Comento unes quantes línies

...
  for (A=0;A<memsize;) {
    
    //if (A<4096) Serial.print("0"); //
    //if (A<256)  Serial.print("0"); //
    //if (A<16)   Serial.print("0"); //
    //Serial.print(A,HEX); //
    //Serial.print(" "); //

    for (j=0;j<11;j++) {
    
      // Setup Address Pins
      for (i=0;i<11;i++) {
        if((A&bit(i))>0) {
          AD[i]=HIGH;
        } else {
          AD[i]=LOW;
        }      
        digitalWrite(AP[i],AD[i]);
      }
    
      digitalWrite(CE,HIGH);      // Chip Enabled
      digitalWrite(OE,HIGH);      // Read Enabled
    
      // Read Data Pins
      D=0;
      for (i=0;i<8;i++) {
        DD[i]=digitalRead(DP[i]);
        D=D+bit(i)*DD[i];
      
      }
    
      digitalWrite(OE,LOW);     // Read Disabled
      digitalWrite(CE,LOW);     // Chip Disabled
    
      //if (D<16) Serial.print("0"); //
      //Serial.print(D,HEX); //
      //Serial.print(" "); //
      Serial.println(D);
      
      A++;
    }
    
    //Serial.println(); //
...

Copio tot el contingut, el porto a l'Excel, i faig la suma. Obtinc el mateix valor que abans: 84646

...
32
99
117
97
46
10
0
84646

Si els últims bytes que surten a la llista són diferents de 0 no preocupar-se, doncs la memòria s'acaba a 07FF, i els últims bytes queden fora d'aquest valor, ja no són de la memòria.

EEPROM write first address direction

script EEPROM_28C16_writer_firstbyte

// http://z80dave.blogspot.com.es/2011/09/21-building-eeprom-programmer-part-2.html
/*
 EEPROM Programmer. Prova
*/


int AP[11] = {22,24,26,28,30,32,34,36,38,40,42};
int AD[11] = {0,0,0,0,0,0,0,0,0,0,0};
int DP[8]  = {23,25,27,29,31,33,35,37};
int DD[8]  = {0,0,0,0,0,0,0,0};

int CE  = 4;
int OE  = 3;
int WE  = 2;

int i;
int A;
int D;
int wait;

void setup() {
  
  Serial.begin(115200);
  Serial.flush();
  
  // Setup Address Pins 
  for (i=0;i<11;i++) {
    pinMode(AP[i],OUTPUT);
  }
  
  // Setup Data Pins
  for (i=0;i<8;i++) {
    pinMode(DP[i],OUTPUT);
  }
  
  // Setup Control Pins
  pinMode(CE, OUTPUT);
  pinMode(WE, OUTPUT);
  pinMode(OE, OUTPUT);
  
  // Setup Chip
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, LOW);

    D=103;
    Serial.println(D);

    AD[0]=LOW;
    AD[1]=LOW;
    AD[2]=LOW;
    AD[3]=LOW;
    AD[4]=LOW;
    AD[5]=LOW;
    AD[6]=LOW;
    AD[7]=LOW;
    AD[8]=LOW;
    AD[9]=LOW;
    AD[10]=LOW;
    digitalWrite(AP[0],AD[0]);
    digitalWrite(AP[1],AD[1]);
    digitalWrite(AP[2],AD[2]);
    digitalWrite(AP[3],AD[3]);
    digitalWrite(AP[4],AD[4]);
    digitalWrite(AP[5],AD[5]);
    digitalWrite(AP[6],AD[6]);
    digitalWrite(AP[7],AD[7]);
    digitalWrite(AP[8],AD[8]);
    digitalWrite(AP[9],AD[9]);
    digitalWrite(AP[10],AD[10]);

    delay(1);
    

    //digitalWrite(WE,LOW);    // Write Enabled
    //delay(1);
    //digitalWrite(CE,LOW);    // Chip Enable
    //delay(1);

    
    // Setup Data Pins
    for (i=0;i<8;i++) {
      if((D&bit(i))>0) {
        DD[i]=HIGH;
      } else {
        DD[i]=LOW;
      }      
      digitalWrite(DP[i],DD[i]);
      Serial.print(DD[i]);
    }

    digitalWrite(CE,HIGH);
    delay(1);
    digitalWrite(WE,HIGH);
    delay(1);


  
}

void loop() {
}

EEPROM read first address direction

script EEPROM_28C16_reader_firstbyte

//http://z80dave.blogspot.com.es/2011/09/20-building-eeprom-programmer.html
/*
 EEPROM Chip Reader. Prova, volem llegir el primer byte
*/


int AP[11] = {22,24,26,28,30,32,34,36,38,40,42};
int AD[11] = {0,0,0,0,0,0,0,0,0,0,0};
int DP[8]  = {23,25,27,29,31,33,35,37};
int DD[8]  = {0,0,0,0,0,0,0,0};

int CE  = 4;
int OE  = 3;
int WE  = 2;

int i;
int j;
int D;
int A;

void setup() {
      
  // Setup Control Pins
  pinMode(CE, OUTPUT);
  pinMode(WE, OUTPUT);
  pinMode(OE, OUTPUT);
  
  // Disable Chip, and disable read and write.
  digitalWrite(CE, LOW);
  digitalWrite(WE, LOW);
  digitalWrite(OE, LOW);

  Serial.begin(115200);
  
  Serial.println("Reading EEPROM...");
    
  // Setup Address Pins 
  for (i=0;i<11;i++) {
    pinMode(AP[i],OUTPUT);
  }
  
  // Setup Data Pins
  for (i=0;i<8;i++) {
    pinMode(DP[i],INPUT);
  }

  delay(1000);
    
    A=0; //primera direccio de la memoria
    if (A<4096) Serial.print("0");
    if (A<256)  Serial.print("0");
    if (A<16)   Serial.print("0");
    Serial.print(A,HEX);
    Serial.print(" ");
    
    AD[0]=HIGH;
    AD[1]=LOW;
    AD[2]=LOW;
    AD[3]=LOW;
    AD[4]=LOW;
    AD[5]=LOW;
    AD[6]=LOW;
    AD[7]=LOW;
    AD[8]=LOW;
    AD[9]=LOW;
    AD[10]=LOW;
    digitalWrite(AP[0],AD[0]);
    digitalWrite(AP[1],AD[1]);
    digitalWrite(AP[2],AD[2]);
    digitalWrite(AP[3],AD[3]);
    digitalWrite(AP[4],AD[4]);
    digitalWrite(AP[5],AD[5]);
    digitalWrite(AP[6],AD[6]);
    digitalWrite(AP[7],AD[7]);
    digitalWrite(AP[8],AD[8]);
    digitalWrite(AP[9],AD[9]);
    digitalWrite(AP[10],AD[10]);
 
    delay(1);
    
    
      digitalWrite(CE,HIGH);      // Chip Enabled
          delay(1);
      digitalWrite(OE,HIGH);      // Read Enabled
        delay(1);
    
      // Read Data Pins
      D=0;
      for (i=0;i<8;i++) {
        DD[i]=digitalRead(DP[i]);
        D=D+bit(i)*DD[i];
      
      }
    
      digitalWrite(OE,LOW);     // Read Disabled
      digitalWrite(CE,LOW);     // Chip Disabled
    
      if (D<16) Serial.print("0");
      Serial.print(D,HEX);
      Serial.print(" ");
      //Serial.println(D);
      
      A++;
    
    Serial.println();
    
  
}

void loop() {
}

Programar l'exemple per al Z80 mínim

Primer de tot generem el codi màquina a partir del llenguatge ensamblador:

$ cd ~/projectes/Z80/z80asm-1.8
$ ./z80asm -I /headers/ -o examples/exemple2.bin -i examples/exemple2.asm
$ od -Ax -t x1 examples/exemple2.bin
000000 3e 01 d3 ff 07 fe 80 28 02 18 f7 d3 ff 0f fe 01
000010 28 f0 18 f7
000014

Si no ho hem fet, seria interessant primer esborrar la EEPROM (EEPROM_28C16_clear.ino).

I ara gravem aquests bytes a la EEPROM. Amb el programador EEPRIM + Arduino Mega, carreguem el EEPROM_28C16_writer.ino. Aquests script escolta els bytes que rep per USB, i amb el script eeprom_send.sh enviem els bytes:

$ cd ~/projectes/Z80
$ ./eeprom_send.sh z80asm-1.8/examples/exemple2.bin /dev/ttyUSB1

Podem comprovar amb el EEPROM_28C16_reader.ino que els bytes s'han gravat de forma correcta:

0000 3E 01 D3 FF 07 FE 80 28 02 18 F7 
000B D3 FF 0F FE 01 28 F0 18 F7 00 00 

creat per Joan Quintana Compte, juny 2017

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
IES Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines