From 4529343228719f78f647553546f6448ccf179723 Mon Sep 17 00:00:00 2001 From: Tom Nisbet Date: Sun, 9 Jun 2019 16:13:39 -0400 Subject: [PATCH] Add Hardware Verify sketch --- HardwareVerify/Configure.h | 9 + HardwareVerify/HardwareVerify.ino | 262 +++++++++++++++++++++++++++ HardwareVerify/PromAddressDriver.cpp | 70 +++++++ HardwareVerify/PromAddressDriver.h | 17 ++ HardwareVerify/PromDevice.cpp | 155 ++++++++++++++++ HardwareVerify/PromDevice.h | 48 +++++ HardwareVerify/PromDevice28C.cpp | 211 +++++++++++++++++++++ HardwareVerify/PromDevice28C.h | 53 ++++++ HardwareVerify/README.md | 61 +++++++ 9 files changed, 886 insertions(+) create mode 100644 HardwareVerify/Configure.h create mode 100644 HardwareVerify/HardwareVerify.ino create mode 100644 HardwareVerify/PromAddressDriver.cpp create mode 100644 HardwareVerify/PromAddressDriver.h create mode 100644 HardwareVerify/PromDevice.cpp create mode 100644 HardwareVerify/PromDevice.h create mode 100644 HardwareVerify/PromDevice28C.cpp create mode 100644 HardwareVerify/PromDevice28C.h create mode 100644 HardwareVerify/README.md diff --git a/HardwareVerify/Configure.h b/HardwareVerify/Configure.h new file mode 100644 index 0000000..d346fdf --- /dev/null +++ b/HardwareVerify/Configure.h @@ -0,0 +1,9 @@ +// Uncomment only one of the ARDUINO_IS_ lines to use the fast I/O code for +// the data bus, or comment them all out to use the slower bit-at-a-time code. + +//#define ARDUINO_IS_MICRO +#define ARDUINO_IS_UNO +//#define ARDUINO_IS_NANO + + +#include "PromDevice28C.h" diff --git a/HardwareVerify/HardwareVerify.ino b/HardwareVerify/HardwareVerify.ino new file mode 100644 index 0000000..6b59100 --- /dev/null +++ b/HardwareVerify/HardwareVerify.ino @@ -0,0 +1,262 @@ +/** +* Test hardware for ATMEL 28C series EEPROMs. +* +* The hardware uses two 74LS164 shift registers as the low and +* high address registers. +**/ + +#include "Configure.h" + +#define LED 13 + +PromDevice28C prom(32 * 1024L, 64, 10, true); + + +/*****************************************************************************/ +/*****************************************************************************/ + +/** + * CLI parse functions + */ +const char hex[] = "0123456789abcdef"; + + +// Read a line of data from the serial connection. +char * readLine(char * buffer, int len) +{ + for (int ix = 0; (ix < len); ix++) + { + buffer[ix] = 0; + } + + // read serial data until linebreak or buffer is full + char c = ' '; + int ix = 0; + do { + if (Serial.available()) + { + c = Serial.read(); + if ((c == '\b') && (ix > 0)) + { + // Backspace, forget last character + --ix; + } + buffer[ix++] = c; + Serial.write(c); + } + } while ((c != '\n') && (ix < len)); + + buffer[ix - 1] = 0; + return buffer; +} + + + + +/************************************************************ +* convert a single hex character [0-9a-fA-F] to its value +* @param char c single character (digit) +* @return byte value of the digit (0-15) +************************************************************/ +byte hexDigit(char c) +{ + if ((c >= '0') && (c <= '9')) + { + return c - '0'; + } + else if ((c >= 'a') && (c <= 'f')) + { + return c - 'a' + 10; + } + else if ((c >= 'A') && (c <= 'F')) + { + return c - 'A' + 10; + } + else + { + return 0xff; + } +} + + +/************************************************************ +* convert a hex byte (00 - ff) to byte +* @param c-string with the hex value of the byte +* @return byte represented by the digits +************************************************************/ +byte hexByte(char * a) +{ + return (hexDigit(a[0]) << 4) | hexDigit(a[1]); +} + + +/************************************************************ +* convert a hex word (0000 - ffff) to unsigned int +* @param c-string with the hex value of the word +* @return unsigned int represented by the digits +************************************************************/ +unsigned int hexWord(char * data) +{ + return (hexDigit(data[0]) << 12) | + (hexDigit(data[1]) << 8) | + (hexDigit(data[2]) << 4) | + (hexDigit(data[3])); +} + + +void printByte(byte b) +{ + char line[3]; + + line[0] = hex[b >> 4]; + line[1] = hex[b & 0x0f]; + line[2] = '\0'; + + Serial.print(line); +} + + +void printWord(word w) +{ + char line[5]; + + line[0] = hex[(w >> 12) & 0x0f]; + line[1] = hex[(w >> 8) & 0x0f]; + line[2] = hex[(w >> 4) & 0x0f]; + line[3] = hex[(w) & 0x0f]; + line[4] = '\0'; + + Serial.print(line); +} + + +/************************************************ +* MAIN +*************************************************/ +word addr = 0; + +void setup() +{ + // Do this first so that it initializes all of the hardware pins into a + // non-harmful state. The Arduino or the target EEPROM could be damaged + // if both writing to the data bus at the same time. + prom.begin(); + + Serial.begin(115200); +} + + + +word start = 0; +word end = 0xff; +byte val = 0xff; + +void loop() +{ + byte b; + word w; + bool error = false; + char line[20]; + uint32_t numBytes; + + Serial.print("\n#"); + Serial.flush(); + readLine(line, sizeof(line)); + byte c = tolower(line[0]); + if ((c >= 'A') && (c <= 'Z')) { + c |= 0x20; + } + + /* + * Note that the comamnds here allow for direct writing of the 28C control lines with some exceptions to + * protect the chip and the host arduino: + * 1) When the O command is used to enable chip output, the arduino data bus us set to INPUT + * 2) When the D command is used to write data from the arduino, the chip output is disabled + * 3) The R command sets to output enable (OE) on the chip (but not the chip enable (CE)) */ + switch (c) + { + case 'a': + if (hexDigit(line[1]) <= 15) + { + w = hexWord(line + 1); + prom.setAddress(w); + } + else + error = true; + break; + case 'd': + if (hexDigit(line[1]) <= 15) + { + prom.disableOutput(); + prom.setDataBusMode(OUTPUT); + b = hexByte(line + 1); + prom.writeDataBus(b); + } + else + error = true; + break; + case 'c': + case 'o': + case 'w': + if ((line[1] == 'd') || (line[1] == 'e')) { + bool enable = line[1] == 'e'; + if (c == 'c') + if (enable) prom.enableChip(); else prom.disableChip(); + else if (c == 'w') + if (enable) prom.enableWrite(); else prom.disableWrite(); + else { // c == 'o' + if (enable) + { + // Don't allow the prom and the data bus to output at the same time + prom.setDataBusMode(INPUT); + prom.enableOutput(); + } + else prom.disableOutput(); + } + } + else + { + error = true; + } + break; + + case 'r': + prom.setDataBusMode(INPUT); + prom.enableOutput(); + b = prom.readDataBus(); + printByte(b); + Serial.println(); + prom.disableOutput(); + break; + + case 'l': + Serial.println(F("Writing the lock code to enable Software Write Protect mode.")); + prom.enableSoftwareWriteProtect(); + break; + + case 'u': + Serial.println(F("Writing the unlock code to disable Software Write Protect mode.")); + prom.disableSoftwareWriteProtect(); + break; + + default: + error = true; + break; + } + + if (error) { + Serial.print(F("Hardware Verifier - ")); + Serial.println(prom.getName()); + Serial.println(); + Serial.println(F("Valid commands are:")); + Serial.println(F(" Axxxx - Set address bus to xxxx")); + Serial.println(F(" Dxx - Set Data bus to xx")); + Serial.println(F(" Cs - Set Chip enable to state (e=enable, d=disable)")); + Serial.println(F(" Os - Set Output enable to state (e=enable, d=disable)")); + Serial.println(F(" Ws - Set Write enable to state (e=enable, d=disable)")); + Serial.println(F(" R - Read and print the value on the data bus")); + Serial.println(F(" L - Send Lock sequence to enable device Software Data Protection")); + Serial.println(F(" U - Send Unlock sequence to disable device Software Data Protection")); + } +} + diff --git a/HardwareVerify/PromAddressDriver.cpp b/HardwareVerify/PromAddressDriver.cpp new file mode 100644 index 0000000..048c190 --- /dev/null +++ b/HardwareVerify/PromAddressDriver.cpp @@ -0,0 +1,70 @@ +#include "PromAddressDriver.h" + +#define ADDR_CLK_HI A3 +#define ADDR_CLK_LO A4 +#define ADDR_DATA A5 + + +void PromAddressDriver::begin() +{ + // The address control pins are always outputs. + pinMode(ADDR_DATA, OUTPUT); + pinMode(ADDR_CLK_LO, OUTPUT); + pinMode(ADDR_CLK_HI, OUTPUT); + digitalWrite(ADDR_DATA, LOW); + digitalWrite(ADDR_CLK_LO, LOW); + digitalWrite(ADDR_CLK_HI, LOW); + + + // To save time, the setAddress only writes the hi byte if it has changed. + // The value used to detect the change is initialized to a non-zero value, + // so set an initial address to avoid the the case where the first address + // written is the 'magic' initial address. + setAddress(0x0000); +} + + +// Set a 16 bit address in the two address shift registers. +void PromAddressDriver::setAddress(word address) +{ + static byte lastHi = 0xca; + byte hi = address >> 8; + byte lo = address & 0xff; + + if (hi != lastHi) + { + setAddressRegister(ADDR_CLK_HI, hi); + lastHi = hi; + } + setAddressRegister(ADDR_CLK_LO, lo); +} + + +// Shift an 8-bit value into one of the address shift registers. Note that +// the data pins are tied together, selecting the high or low address register +// is a matter of using the correct clock pin to shift the data in. +void PromAddressDriver::setAddressRegister(uint8_t clkPin, byte addr) +{ + // Make sure the clock is low to start. + digitalWrite(clkPin, LOW); + + // Shift 8 bits in, starting with the MSB. + for (int ix = 0; (ix < 8); ix++) + { + // Set the data bit + if (addr & 0x80) + { + digitalWrite(ADDR_DATA, HIGH); + } + else + { + digitalWrite(ADDR_DATA, LOW); + } + + digitalWrite(clkPin, HIGH); // Clock in a bit + digitalWrite(clkPin, LOW); // Reset the clock pin + addr <<= 1; + } +} + + diff --git a/HardwareVerify/PromAddressDriver.h b/HardwareVerify/PromAddressDriver.h new file mode 100644 index 0000000..918f224 --- /dev/null +++ b/HardwareVerify/PromAddressDriver.h @@ -0,0 +1,17 @@ +#ifndef INCLUDE_PROM_ADDRESS_DRIVER_H +#define INCLUDE_PROM_ADDRESS_DRIVER_H + +#include "Arduino.h" + +class PromAddressDriver { + public: + static void begin(); + static void setAddress(word address); + + private: + static void setAddressRegister(uint8_t clkPin, byte addr); +}; + + +#endif // #define INCLUDE_PROM_ADDRESS_DRIVER_H + diff --git a/HardwareVerify/PromDevice.cpp b/HardwareVerify/PromDevice.cpp new file mode 100644 index 0000000..e85ae31 --- /dev/null +++ b/HardwareVerify/PromDevice.cpp @@ -0,0 +1,155 @@ +#include "Configure.h" +#include "PromDevice.h" + + +PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling) + : mSize(size), + mBlockSize(blockSize), + mMaxWriteTime(maxWriteTime), + mSupportsDataPoll(polling) +{ +} + + +// Write a block of data to the device. If the device supports block writes, +// the data will be broken into chunks and written using the block mode. +// Otherwise, each byte will be individually written and verified. +bool PromDevice::writeData(byte data[], word len, word address) +{ + bool status = true; + + if (mBlockSize == 0) + { + // Device does not support block writes. + for (word ix = 0; (ix < len); ix++) + { + if (burnByte(data[ix], address + ix) == false) + { + status = false; + break; + } + } + } + else + { + word offset = 0; + word chunkSize; + if (address & (mBlockSize - 1)) + { + // Address does not start on a block boundary. Adjust the size of + // the first block to fit within a single block. + chunkSize = mBlockSize - (address & (mBlockSize - 1)); + chunkSize = (chunkSize > len) ? len : chunkSize; + if (burnBlock(data, chunkSize, address) == false) + { + return false; + } + offset += chunkSize; + len -= chunkSize; + } + + // All writes are now aligned to block boundaries, so write full blocks + // or remaining length, whichever is smaller. + while (len > 0) + { + chunkSize = (len > mBlockSize) ? mBlockSize : len; + if (burnBlock(data + offset, chunkSize, address + offset) == false) + { + status = false; + break; + } + offset += chunkSize; + len -= chunkSize; + } + } + + return status; +} + + +// BEGIN PRIVATE METHODS +// + +// Set the I/O state of the data bus. +// The first two bits of port D are used for serial, so the 8 bits data bus are +// on pins D2..D9. +void PromDevice::setDataBusMode(uint8_t mode) +{ +#if defined(ARDUINO_IS_UNO) || defined(ARDUINO_IS_NANO) + // On the Uno and Nano, D2..D9 maps to the upper 6 bits of port D and the + // lower 2 bits of port B. + if (mode == OUTPUT) + { + DDRB |= 0x03; + DDRD |= 0xfc; + } + else + { + DDRB &= 0xfc; + DDRD &= 0x03; + } +#elif defined(ARDUINO_IS_MICRO) + // On the Micro, D2..D9 maps to the upper 7 bits of port B and the + // lower bit of port D. + if (mode == OUTPUT) + { + DDRB |= 0xfe; + DDRD |= 0x01; + } + else + { + DDRB &= 0x01; + DDRD &= 0xfe; + } +#else + byte bit = 0x01; + for (int pin = 2; (pin <= 9); pin++) { + pinMode(pin, mode); + bit <<= 1; + } +#endif +} + + +// Read a byte from the data bus. The caller must set the bus to input_mode +// before calling this or no useful data will be returned. +byte PromDevice::readDataBus() +{ +#if defined(ARDUINO_IS_UNO) || defined(ARDUINO_IS_NANO) + return (PINB << 6) | (PIND >> 2); +#elif defined(ARDUINO_IS_MICRO) + return (PINB & 0xfe) | (PIND & 0x01); +#else + byte data = 0; + byte bit = 0x01; + for (int pin = 2; (pin <= 9); pin++) { + if (digitalRead(pin) == HIGH) { + data |= bit; + } + bit <<= 1; + } + return data; +#endif +} + + +// Write a byte to the data bus. The caller must set the bus to output_mode +// before calling this or no data will be written. +void PromDevice::writeDataBus(byte data) +{ +#if defined(ARDUINO_IS_UNO) || defined(ARDUINO_IS_NANO) + PORTB = (PORTB & 0xfc) | (data >> 6); + PORTD = (PORTD & 0x03) | (data << 2); +#elif defined(ARDUINO_IS_MICRO) + PORTB = (PORTB & 0x01) | (data & 0xfe); + PORTD = (PORTD & 0xfe) | (data & 0x01); +#else + byte bit = 0x01; + for (int pin = 2; (pin <= 9); pin++) { + digitalWrite(pin, (data & bit) ? HIGH : LOW); + bit <<= 1; + } +#endif +} + + diff --git a/HardwareVerify/PromDevice.h b/HardwareVerify/PromDevice.h new file mode 100644 index 0000000..324d40f --- /dev/null +++ b/HardwareVerify/PromDevice.h @@ -0,0 +1,48 @@ +#ifndef INCLUDE_PROM_DEVICE_H +#define INCLUDE_PROM_DEVICE_H + +#include "Arduino.h" + +/*****************************************************************************/ +/*****************************************************************************/ +/** + * PromDevice class + * + * Provides the interface to read and write data from a parallel PROM using the + * Arduino. + * + * Block writes are supported on compatible devices by specifying a blockSize + * in the constructor. Use zero for devices that only support byte writes. + */ +class PromDevice +{ + public: + PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling); + bool writeData(byte data[], word len, word address); + byte readData(word address) { return readByte(address); } + + virtual void begin() = 0; + virtual const char * getName() = 0; + virtual void disableSoftwareWriteProtect() {} + virtual void enableSoftwareWriteProtect() {} + + //protected: + unsigned int mSize; // Size of the device, in bytes + unsigned int mBlockSize; // Block size for page writes, zero if N/A + unsigned int mMaxWriteTime; // Max time (in ms) to wait for write cycle to complete + bool mSupportsDataPoll; // End of write detected by data polling + + void setDataBusMode(uint8_t mode); + byte readDataBus(); + void writeDataBus(byte data); + + virtual void setAddress(word address) = 0; + virtual byte readByte(word address) = 0; + virtual bool burnByte(byte value, word address) = 0; + virtual bool burnBlock(byte data[], word len, word address) { return false; } + +}; + + +#endif // #define INCLUDE_PROM_DEVICE_H + diff --git a/HardwareVerify/PromDevice28C.cpp b/HardwareVerify/PromDevice28C.cpp new file mode 100644 index 0000000..737b88c --- /dev/null +++ b/HardwareVerify/PromDevice28C.cpp @@ -0,0 +1,211 @@ +#include "Configure.h" + +#include "PromAddressDriver.h" + + + +PromDevice28C::PromDevice28C(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling) + : PromDevice(size, blockSize, maxWriteTime, polling) +{ +} + +void PromDevice28C::begin() +{ + // Define the data bus as input initially so that it does not put out a + // signal that could collide with output on the data pins of the EEPROM. + setDataBusMode(INPUT); + + // Define the EEPROM control pins as output, making sure they are all + // in the disabled state. + digitalWrite(OE, HIGH); + pinMode(OE, OUTPUT); + digitalWrite(CE, HIGH); + pinMode(CE, OUTPUT); + digitalWrite(WE, HIGH); + pinMode(WE, OUTPUT); + + // This chip uses the shift register hardware for addresses, so initialize that. + PromAddressDriver::begin(); +} + + +// Write the special six-byte code to turn off Software Data Protection. +void PromDevice28C::disableSoftwareWriteProtect() +{ + disableOutput(); + disableWrite(); + setDataBusMode(OUTPUT); + + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0x80, 0x5555); + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0x20, 0x5555); + + setDataBusMode(INPUT); + disableChip(); +} + + +// Write the special three-byte code to turn on Software Data Protection. +void PromDevice28C::enableSoftwareWriteProtect() +{ + disableOutput(); + disableWrite(); + setDataBusMode(OUTPUT); + + setByte(0xaa, 0x5555); + setByte(0x55, 0x2aaa); + setByte(0xa0, 0x5555); + + setDataBusMode(INPUT); + disableChip(); +} + + +// BEGIN PRIVATE METHODS +// + +// Use the PromAddressDriver to set a 16 bit address in the two address shift registers. +void PromDevice28C::setAddress(word address) +{ + PromAddressDriver::setAddress(address); +} + + +// Read a byte from a given address +byte PromDevice28C::readByte(word address) +{ + byte data = 0; + setAddress(address); + setDataBusMode(INPUT); + disableOutput(); + disableWrite(); + enableChip(); + enableOutput(); + data = readDataBus(); + disableOutput(); + disableChip(); + + return data; +} + + +// Burn a byte to the chip and verify that it was written. +bool PromDevice28C::burnByte(byte value, word address) +{ + bool status = false; + + disableOutput(); + disableWrite(); + + setAddress(address); + setDataBusMode(OUTPUT); + writeDataBus(value); + + enableChip(); + delayMicroseconds(1); + enableWrite(); + delayMicroseconds(1); + disableWrite(); + + status = waitForWriteCycleEnd(value); + + disableChip(); + + return status; +} + + +bool PromDevice28C::burnBlock(byte data[], word len, word address) +{ + bool status = false; + + if (len == 0) return true; + + disableOutput(); + disableWrite(); + enableChip(); + + // Write all of the bytes in the block out to the chip. The chip will + // program them all at once as long as they are written fast enough. + setDataBusMode(OUTPUT); + for (word ix = 0; (ix < len); ix++) + { + setAddress(address + ix); + writeDataBus(data[ix]); + + delayMicroseconds(1); + enableWrite(); + delayMicroseconds(1); + disableWrite(); + } + + status = waitForWriteCycleEnd(data[len - 1]); + disableChip(); + + return status; +} + + +bool PromDevice28C::waitForWriteCycleEnd(byte lastValue) +{ + if (mSupportsDataPoll) + { + // Verify programming complete by reading the last value back until it matches the + // value written twice in a row. The D7 bit will read the inverse of last written + // data and the D6 bit will toggle on each read while in programming mode. + // + // Note that the max readcount is set to the device's maxReadTime (in uSecs) + // divided by two because there are two 1 uSec delays in the loop. In reality, + // the loop could run for longer because this does not account for the time needed + // to run all of the loop code. In actual practice, the loop will terminate much + // earlier because it will detect the end of the write well before the max time. + setDataBusMode(INPUT); + delayMicroseconds(1); + for (int readCount = mMaxWriteTime * 1000 / 2; (readCount > 0); readCount--) + { + enableOutput(); + delayMicroseconds(1); + byte b1 = readDataBus(); + disableOutput(); + enableOutput(); + delayMicroseconds(1); + byte b2 = readDataBus(); + disableOutput(); + if ((b1 == b2) && (b1 == lastValue)) + { + return true; + } + } + + return false; + } + else + { + // No way to detect success. Just wait the max write time. + delayMicroseconds(mMaxWriteTime * 1000L); + return true; + } +} + + +// Set an address and data value and toggle the write control. This is used +// to write control sequences, like the software write protect. This is not a +// complete byte write function because it does not set the chip enable or the +// mode of the data bus. +void PromDevice28C::setByte(byte value, word address) +{ + setAddress(address); + writeDataBus(value); + + delayMicroseconds(1); + enableChip(); + enableWrite(); + delayMicroseconds(1); + disableChip(); + disableWrite(); +} + + diff --git a/HardwareVerify/PromDevice28C.h b/HardwareVerify/PromDevice28C.h new file mode 100644 index 0000000..6c4d06c --- /dev/null +++ b/HardwareVerify/PromDevice28C.h @@ -0,0 +1,53 @@ +#ifndef INCLUDE_PROM_DEVICE_28C_H +#define INCLUDE_PROM_DEVICE_28C_H + +#include "Arduino.h" +#include "PromDevice.h" + +/*****************************************************************************/ +/*****************************************************************************/ +/** + * PromDevice28C class + * + * Provides the device-specific interface to read and write data from a + * 28C series parallel EEPROM using the Arduino. + * + * Block writes are supported on compatible devices by specifying a blockSize + * in the constructor. Use zero for byte writes. + */ + +// IO lines for the EEPROM device control +// Pins D2..D9 are used for the data bus. +#define WE A0 +#define CE A1 +#define OE A2 + +class PromDevice28C : public PromDevice +{ + public: + PromDevice28C(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling); + void begin(); + const char * getName() { return "28C series EEPROM"; } + void disableSoftwareWriteProtect(); + void enableSoftwareWriteProtect(); + + //protected: + void setAddress(word address); + byte readByte(word address); + bool burnByte(byte value, word address); + bool burnBlock(byte data[], word len, word address); + bool waitForWriteCycleEnd(byte lastValue); + void setByte(byte value, word address); + + + // Set the status of the device control pins + void enableChip() { digitalWrite(CE, LOW); } + void disableChip() { digitalWrite(CE, HIGH);} + void enableOutput() { digitalWrite(OE, LOW); } + void disableOutput() { digitalWrite(OE, HIGH);} + void enableWrite() { digitalWrite(WE, LOW);} + void disableWrite() { digitalWrite(WE, HIGH);} +}; + +#endif // #define INCLUDE_PROM_DEVICE_28C_H + diff --git a/HardwareVerify/README.md b/HardwareVerify/README.md new file mode 100644 index 0000000..fcaa84d --- /dev/null +++ b/HardwareVerify/README.md @@ -0,0 +1,61 @@ +## hardware Verifier Tool +This tools allows access to individual control lines from the Arduino to verify that the hardware was assembled correctly. + +It can be used without a chip installed to scope out address and data lines. It also offers low-level control when the chip +is installed. + +Note that the comamnds allow for direct writing of the 28C control lines with some exceptions to protect the chip and the host arduino: + +* When the O command is used to enable chip output, the arduino data bus is set to INPUT +* When the D command is used to write data from the arduino, the chip output is disabled +* The R command sets the output enable (OE) on the chip, but not the chip enable (CE) + +The session below shows how a write fails to a locked chip and then succeeds once the chip is unlocked. + +``` +Hardware Verifier - 28C series EEPROM + +Valid commands are: + Axxxx - Set address bus to xxxx + Dxx - Set Data bus to xx + Cs - Set Chip enable to state (e=enable, d=disable) + Os - Set Output enable to state (e=enable, d=disable) + Ws - Set Write enable to state (e=enable, d=disable) + R - Read and print the value on the data bus + L - Send Lock sequence to enable device Software Data Protection + U - Send Unlock sequence to disable device Software Data Protection + +#l +Writing the lock code to enable Software Write Protect mode. + +#a0000 + +#ce + +#r +22 + +#d55 + +#we + +#wd + +#r +22 + +#u +Writing the unlock code to disable Software Write Protect mode. + +#d33 + +#we + +#wd + +#r +33 + +#cd + +``` \ No newline at end of file