TommyPROM/TommyPROM/PromDevice28C.cpp

241 lines
6.3 KiB
C++

#include "Configure.h"
#if defined(PROM_IS_28C)
#include "PromAddressDriver.h"
// 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
// Set the status of the device control pins
static void enableChip() { digitalWrite(CE, LOW); }
static void disableChip() { digitalWrite(CE, HIGH);}
static void enableOutput() { digitalWrite(OE, LOW); }
static void disableOutput() { digitalWrite(OE, HIGH);}
static void enableWrite() { digitalWrite(WE, LOW); }
static void disableWrite() { digitalWrite(WE, HIGH);}
PromDevice28C::PromDevice28C(uint32_t 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.
ERET PromDevice28C::disableSoftwareWriteProtect()
{
disableOutput();
disableWrite();
enableChip();
setDataBusMode(OUTPUT);
setByte(0xaa, 0x5555);
setByte(0x55, 0x2aaa);
setByte(0x80, 0x5555);
setByte(0xaa, 0x5555);
setByte(0x55, 0x2aaa);
setByte(0x20, 0x5555);
setDataBusMode(INPUT);
disableChip();
return RET_OK;
}
// Write the special three-byte code to turn on Software Data Protection.
ERET PromDevice28C::enableSoftwareWriteProtect()
{
disableOutput();
disableWrite();
enableChip();
setDataBusMode(OUTPUT);
setByte(0xaa, 0x5555);
setByte(0x55, 0x2aaa);
setByte(0xa0, 0x5555);
setDataBusMode(INPUT);
disableChip();
return RET_OK;
}
// BEGIN PRIVATE METHODS
//
// Use the PromAddressDriver to set an address in the two address shift registers.
void PromDevice28C::setAddress(uint32_t address)
{
PromAddressDriver::setAddress(address);
}
// Read a byte from a given address
byte PromDevice28C::readByte(uint32_t 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, uint32_t 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[], uint32_t len, uint32_t address)
{
bool status = false;
if (len == 0) return true;
++debugBlockWrites;
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 (uint32_t ix = 0; (ix < len); ix++)
{
setAddress(address + ix);
writeDataBus(data[ix]);
delayMicroseconds(1);
enableWrite();
delayMicroseconds(1);
disableWrite();
}
status = waitForWriteCycleEnd(data[len - 1]);
disableChip();
if (!status) {
debugLastAddress = address + len - 1;
}
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.
//
// This loop code takes about 18uSec to execute. The max readcount is set to the
// device's maxReadTime (in uSecs) divided by ten rather than eighteen to ensure
// that it runs at least as long as the chip's timeout value, even if some code
// optimizations are made later. In actual practice, the loop will terminate much
// earlier because it will detect the end of the write well before the max time.
byte b1=0, b2=0;
setDataBusMode(INPUT);
delayMicroseconds(1);
for (unsigned readCount = 1; (readCount < (mMaxWriteTime * 100)); readCount++)
{
enableChip();
enableOutput();
delayMicroseconds(1);
b1 = readDataBus();
disableOutput();
disableChip();
enableChip();
enableOutput();
delayMicroseconds(1);
b2 = readDataBus();
disableOutput();
disableChip();
if ((b1 == b2) && (b1 == lastValue))
{
return true;
}
}
debugLastExpected = lastValue;
debugLastReadback = b2;
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, uint32_t address)
{
setAddress(address);
writeDataBus(value);
delayMicroseconds(1);
enableWrite();
delayMicroseconds(1);
disableWrite();
}
#endif // #if defined(PROM_IS_28C)