mirror of
https://github.com/TomNisbet/TommyPROM.git
synced 2024-09-26 00:55:35 +00:00
212 lines
5.9 KiB
C++
212 lines
5.9 KiB
C++
#include "Configure.h"
|
|
#if defined(PROM_IS_SST39SF)
|
|
|
|
#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);}
|
|
|
|
|
|
PromDeviceSST39SF::PromDeviceSST39SF(uint32_t size, unsigned maxWriteTime, bool polling)
|
|
: PromDevice(size, 0, maxWriteTime, polling),
|
|
currentSector(0xffffffff)
|
|
{
|
|
}
|
|
|
|
|
|
void PromDeviceSST39SF::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();
|
|
}
|
|
|
|
|
|
// Erase all 4K byte sectors containing the specified address range.
|
|
ERET PromDeviceSST39SF::erase(uint32_t start, uint32_t end)
|
|
{
|
|
start >>= 12;
|
|
end >>= 12;
|
|
for (uint32_t sector = start; (sector <= end); sector++)
|
|
{
|
|
eraseSector(sector << 12);
|
|
}
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
// BEGIN PRIVATE METHODS
|
|
//
|
|
|
|
// Use the PromAddressDriver to set an address in the two address shift registers.
|
|
void PromDeviceSST39SF::setAddress(uint32_t address)
|
|
{
|
|
PromAddressDriver::setAddress(address);
|
|
}
|
|
|
|
|
|
// Read a byte from a given address
|
|
byte PromDeviceSST39SF::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 PromDeviceSST39SF::burnByte(byte value, uint32_t address)
|
|
{
|
|
bool status = false;
|
|
|
|
// Erase a sector before writing any new data to it. Note that multiple
|
|
// burnByte calls to the same sector will only do an erase on the first call.
|
|
// If multiple burn calls will be needed for the same address, it is up to the
|
|
// caller to erase the sector before writing.
|
|
if ((address & 0xfffff000) != currentSector)
|
|
{
|
|
eraseSector(address);
|
|
currentSector = address & 0xfffff000;
|
|
}
|
|
disableOutput();
|
|
disableWrite();
|
|
|
|
setDataBusMode(OUTPUT);
|
|
enableChip();
|
|
setByte(0xaa, 0x5555);
|
|
setByte(0x55, 0x2aaa);
|
|
setByte(0xa0, 0x5555);
|
|
|
|
setAddress(address);
|
|
setDataBusMode(OUTPUT);
|
|
writeDataBus(value);
|
|
|
|
delayMicroseconds(1);
|
|
enableWrite();
|
|
delayMicroseconds(1);
|
|
disableWrite();
|
|
|
|
status = waitForWriteCycleEnd(value);
|
|
|
|
disableChip();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
bool PromDeviceSST39SF::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 (int 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 sector erase. This is not a complete byte write function
|
|
// because it does not set the chip enable or the mode of the data bus.
|
|
void PromDeviceSST39SF::setByte(byte value, uint32_t address)
|
|
{
|
|
setAddress(address);
|
|
writeDataBus(value);
|
|
|
|
delayMicroseconds(1);
|
|
enableWrite();
|
|
delayMicroseconds(1);
|
|
disableWrite();
|
|
}
|
|
|
|
void PromDeviceSST39SF::eraseSector(uint32_t addr)
|
|
{
|
|
disableOutput();
|
|
disableWrite();
|
|
setDataBusMode(OUTPUT);
|
|
enableChip();
|
|
setByte(0xaa, 0x5555);
|
|
setByte(0x55, 0x2aaa);
|
|
setByte(0x80, 0x5555);
|
|
setByte(0xaa, 0x5555);
|
|
setByte(0x55, 0x2aaa);
|
|
setByte(0x30, addr & 0xfffff000);
|
|
delay(28);
|
|
disableChip();
|
|
}
|
|
|
|
#endif // #if defined(PROM_IS_SST39SF)
|