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.