Sa. Mrz 2nd, 2024

Auslesen eines EPROMs bzw. eines EEPROMS ist eine zentrale Aufgabe, wenn man mit Mikroprozessoren wie dem 6502 oder dem Z80 experimentiert. Hierzu ist ein entsprechendes Programmiergerät natürlich sehr hilfreich, wenn man den Baustein nicht gänzlich von „Hand“ mit Daten füllen möchte. Im vorliegenden Beispiel gehen wir davon aus, dass ein programmiertes EEPROM vom Typ AT28C256 vorliegt, dessen Daten mit dem Arduino Mega ausgelesen werden sollen. Der Versuchsaufbau sieht wie folgt aus:

Wie man erkennt ist der Anschluss relativ einfach: Daten-Pins kommen an den Port L des Arduino Mega und die Adress-Pins ebenso an die oben im Beitrag schon beschriebenen Ports A und Port C. Damit wird die Programmierung auf die Port-Manipulations-Funktionen des Arduino ermöglicht, die das Ein- und Ausgeben der Daten sehr komfortabel umsetzen lassen.

Lesevorgang

Auf den AT28C256 Baustein kann wie auf ein statisches RAM zugegriffen werden. Daten können ausgelesen werden, wenn die Anschlüsse CE‘ und OE‘ auf LOW liegen und WE‘ auf HIGH gesetzt ist. Dann werden Daten an die durch die Adresspins bestimmten Speicherstelle an den Ausgängen IO0-IO7 ausgegeben.

Aus diesem Grund werden in unserem Versuchsaufbau – wie im obigen Bild zu sehen ist – die Anschlüsse OE‘ und CE‘ auf Masse gelegt werden, d.h. haben einen High Pegel. Ebenso muss der WE#-Pin auf +5V gelegt werden.

Damit ist der Baustein bereit. Legt man nun eine Adresse an die Pins A0-A14 so kann man die im EEPROM gespeicherten Daten auslesen.

Dazu wird im Arduino Programm eine 16bit Variable mit dem Namen address initialisiert. Der Wert dieser Variable muss in einen Low-Byte und einen High-Byte Bereich aufgeteilt werden. Das geht beispielsweise so:

// wandle address in 16 bit word
      byte LoByte = (address  & 0x00FF);
      byte HiByte = ((address  & 0xFF00) >>8);

Man erhält das Low-Byte in dem man den 16bit Wert mit 0x00FF UND verknüpft. Dadurch werden die oberen 8 Bit quasi ausgeblendet. Analog verfährt man mit dem High-Byte. Nur dass man hier mit 0xFF00 UND verknüpft, wodurch die unteren 8 Bit der Variable address ausgeblendet werden. Diese werden dann noch um 8 Stellen nach rechts verschoben, damit man einen 8-Bit Wert erhält.

Die so erzeugten 8-Bit Werte kann man nun den Ports A und C zuweisen, somit steht dann eine 16 Bit Wort zur Steuerung bereit. Da man nur 15-Bit für den Baustein benötigt, bleibt der Anschluss A15 einfach offen.

Wichtig ist sich dabei die Grundlagen der Port-Programmierung nochmals zu vergegenwärtigen. Zusammengefasst funktioniert das wie folgt: Die Ports werden von drei Registern gesteuert, die mit „DDRx“, „PORTx“ und „PINx“ bezeichnet werden. Diese drei Register existieren für jeden Port, es gibt also zum Beispiel für Port B ein DDRB-, ein PORTB- und ein PINB-Register. Das jeweilige DDRx-Register steuert, ob die Pins als Input oder Output konfiguriert sein sollen (DDR = Data Direction Register), das PORT-Register ist ein Ausgang, es legt fest, ob die Pins HIGH oder LOW sind und das PIN-Register gibt den Zustand der Pins an, die im DDR-Register auf Input gesetzt wurden.

Das Programm kann dann so aussehen:

    /*EEPROM Ansteuerung
     LESE Daten aus EEPROM 
     Alternativ als hexdump oder Step by step 
     */
    
    #define CLOCK 2
    unsigned int data = 0;
    uint16_t address = 0x0000;
    int staADR = 0x00;
    int endADR = 0xff;
    
    void setup() {
      Serial.begin(115200);
      pinMode(CLOCK, INPUT);
      DDRA = 0b11111111;    // Address = Output
      DDRC = 0b11111111;    // Address = Output
      DDRL = 0x00;          // Data = Input
      
     // hexDump(staADR, endADR);
      attachInterrupt(digitalPinToInterrupt(CLOCK), onClock, RISING);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    }
    
    void onClock() {
      char output[15];
      
      // wandle address in 16 bit word
      byte LoByte = (address  & 0x00FF);
      byte HiByte = ((address  & 0xFF00) >>8);
      
      // Schreibe Adresse an PortPins
      PORTA = LoByte; 
      PORTC = HiByte;

      // lese Daten von portPins
 
      data = PINL;
      Serial.println("-------------------------------------------------");
      sprintf(output, "   %04x  %02x", address, data);
      Serial.println(output); 
      //Serial.println("-----------------------------------------------");

      // next address
      address = address+1;
      
   }// end onClock
    
    void hexDump(int startAdr, int endAdr) {
        Serial.println();
        Serial.println("------------------------------------------------------------------------");
        Serial.println(" * HEX-Dump  * " );
        Serial.println("------------------------------------------------------------------------");
        
        for (unsigned int adr = startAdr; adr <= endAdr; adr++) {
           int a = adr; 

            // wandle addresse adr in 16 bit word
            byte LoByte = (adr  & 0x00FF);
            byte HiByte = ((adr  & 0xFF00) >>8);
            
            // Schreibe Adresse an PortPins
            PORTA = LoByte; 
            PORTC = HiByte;
      
            // lese Daten von portPins
            data = PINL;

           if ( (adr == 0) || (adr % 16 == 0))     
               Serial.print ( "  " + int2hex(adr, 4) + " : ");  // Zeige Speicheradresse an
               
           Serial.print (" " + int2hex(data, 2) + " ");         // Zeige Daten-Byte an
           
           if( (adr + 1) % 16 == 0)
             Serial.println();                 // Zeilenvorschub
           
        }// end for
       
   }//hexDump
    
         
   String int2hex(int wert, int stellen) {   // int -› hex-String Konvertierung mit der Angabe der Stellen
      String temp = String(wert, HEX);
      String prae = "";
      int len = temp.length(); // Die Länge der Zeichenkette ermitteln
      int diff = stellen - len;
      for(int i = 0; i < diff; i++)
          prae = prae + "O"; // Führende Nullen erzeugen
         return prae + temp; // Führende Nullen + Ergebnis zurückliefern
      } // end int2he
      
   
    

Das Programm kann entweder über einen Taster gesteuert werden…in diesem Fall wird die Interrupt-Routine verwendet und der Eingangs-Impuls an Pin2 des Arduino Mega geleitet. So kann man im Einzelschritt sehen, welche Adresse an den EEPROM angelegt wird und welche Daten ausgelesen werden.

Alternativ habe ich die Möglichkeit vorgesehen, einen ganzen Speicherbereich über die entwickelte Hexdump-Funktion auszugeben. Hierzu ist der Interrupt zu deaktivieren und die Start- und Endadresse anzugeben.