383 lines
8.3 KiB
C++
383 lines
8.3 KiB
C++
|
// Maxim/Dallas 1-Wire EPROM & EEPROM library for Arduino
|
||
|
// Copyright (C) 2011-2014 Eric Hokanson
|
||
|
// https://github.com/pceric
|
||
|
|
||
|
// This library is free software; you can redistribute it and/or
|
||
|
// modify it under the terms of the GNU Lesser General Public
|
||
|
// License as published by the Free Software Foundation; either
|
||
|
// version 2.1 of the License, or (at your option) any later version.
|
||
|
|
||
|
// This library is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
// Lesser General Public License for more details.
|
||
|
|
||
|
#include "DallasEPROM.h"
|
||
|
|
||
|
/** Supported chips. */
|
||
|
model_type _chip_model_list[] = {
|
||
|
// EPROMs
|
||
|
{ 0x09, "DS2502", 4, true },
|
||
|
{ 0x0B, "DS2505", 64, true },
|
||
|
// EEPROMs
|
||
|
{ 0x14, "DS2430", 1, false },
|
||
|
{ 0x2D, "DS2431", 4, false },
|
||
|
{ 0x23, "DS2433", 16, false },
|
||
|
{ 0, 0, 0, 0 }
|
||
|
};
|
||
|
|
||
|
DallasEPROM::DallasEPROM(OneWire* rWire) {
|
||
|
_wire = rWire;
|
||
|
_progPin = -1;
|
||
|
}
|
||
|
|
||
|
DallasEPROM::DallasEPROM(OneWire* rWire, int progPin) {
|
||
|
_wire = rWire;
|
||
|
_progPin = progPin;
|
||
|
pinMode(progPin, OUTPUT);
|
||
|
digitalWrite(progPin, LOW);
|
||
|
}
|
||
|
|
||
|
/*******************
|
||
|
* Static methods
|
||
|
*******************/
|
||
|
|
||
|
bool DallasEPROM::validAddress(uint8_t* deviceAddress) {
|
||
|
return (OneWire::crc8(deviceAddress, 7) == deviceAddress[7]);
|
||
|
}
|
||
|
|
||
|
bool DallasEPROM::isSupported(uint8_t* deviceAddress) {
|
||
|
int i = 0;
|
||
|
while (_chip_model_list[i].id) {
|
||
|
if (deviceAddress[0] == _chip_model_list[i].id)
|
||
|
return true;
|
||
|
++i;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*******************
|
||
|
* Public methods
|
||
|
*******************/
|
||
|
|
||
|
bool DallasEPROM::search() {
|
||
|
int i;
|
||
|
_curModelIndex = -1;
|
||
|
if (!_wire->reset())
|
||
|
return false;
|
||
|
_wire->reset_search();
|
||
|
while (_wire->search(_addr)) {
|
||
|
i = 0;
|
||
|
while (_chip_model_list[i].id) {
|
||
|
if (_addr[0] == _chip_model_list[i].id) {
|
||
|
_curModelIndex = i;
|
||
|
return true;
|
||
|
}
|
||
|
++i;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
uint8_t* DallasEPROM::getAddress() {
|
||
|
return _addr;
|
||
|
}
|
||
|
|
||
|
void DallasEPROM::setAddress(uint8_t* pAddress) {
|
||
|
int i = 0;
|
||
|
_curModelIndex = -1;
|
||
|
|
||
|
memcpy(_addr, pAddress, 8);
|
||
|
|
||
|
while (_chip_model_list[i].id) {
|
||
|
if (_addr[0] == _chip_model_list[i].id) {
|
||
|
_curModelIndex = i;
|
||
|
return;
|
||
|
}
|
||
|
++i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char* DallasEPROM::getDeviceName() {
|
||
|
if (_curModelIndex >= 0)
|
||
|
return _chip_model_list[_curModelIndex].name;
|
||
|
else
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
bool DallasEPROM::isConnected() {
|
||
|
uint8_t tmpAddress[8];
|
||
|
|
||
|
if (!_wire->reset())
|
||
|
return false;
|
||
|
_wire->reset_search();
|
||
|
while (_wire->search(tmpAddress)) {
|
||
|
if (memcmp(_addr, tmpAddress, 8)==0)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int DallasEPROM::readPage(uint8_t* data, int page) {
|
||
|
unsigned int address = page * 32;
|
||
|
|
||
|
if (!isPageValid(page))
|
||
|
return INVALID_PAGE;
|
||
|
if (!isConnected())
|
||
|
return DEVICE_DISCONNECTED;
|
||
|
|
||
|
// check for page redirection
|
||
|
if (isEPROMDevice()) {
|
||
|
byte command[] = { READSTATUS, (byte)(page+1), 0x00 };
|
||
|
byte new_addr;
|
||
|
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
_wire->write(command[0]);
|
||
|
_wire->write(command[1]);
|
||
|
_wire->write(command[2]);
|
||
|
|
||
|
if (OneWire::crc8(command, 3) != _wire->read())
|
||
|
return CRC_MISMATCH;
|
||
|
|
||
|
if ((new_addr = _wire->read()) != 0xFF)
|
||
|
address = new_addr;
|
||
|
}
|
||
|
|
||
|
byte command[] = { READMEMORY, (byte) address, (byte)(address >> 8) };
|
||
|
|
||
|
// send the command and starting address
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
_wire->write(command[0]);
|
||
|
_wire->write(command[1]);
|
||
|
_wire->write(command[2]);
|
||
|
|
||
|
// Check CRC on EPROM devices
|
||
|
if (isEPROMDevice() && OneWire::crc8(command, 3) != _wire->read())
|
||
|
return CRC_MISMATCH;
|
||
|
|
||
|
// Read the entire page
|
||
|
for (int i = 0; i < 32; i++) {
|
||
|
data[i] = _wire->read();
|
||
|
}
|
||
|
|
||
|
// TODO: On EPROM device you can check the CRC post read
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int DallasEPROM::writePage(uint8_t* data, int page) {
|
||
|
unsigned int address = page * 32;
|
||
|
|
||
|
if (!isPageValid(page))
|
||
|
return INVALID_PAGE;
|
||
|
if (!isConnected())
|
||
|
return DEVICE_DISCONNECTED;
|
||
|
|
||
|
// EEPROMS have a difference write method than EPROMS
|
||
|
if (!isEPROMDevice()) {
|
||
|
int status;
|
||
|
|
||
|
// a page is 4 8-byte scratch writes
|
||
|
for (int i = 0; i < 32; i += 8) {
|
||
|
if ((status = scratchWrite(&data[i], 8, address + i)))
|
||
|
return status;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
byte command[] = { WRITEMEMORY, (byte) address, (byte)(address >> 8),
|
||
|
data[0] };
|
||
|
|
||
|
// send the command, address, and the first byte
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
_wire->write(command[0]);
|
||
|
_wire->write(command[1]);
|
||
|
_wire->write(command[2]);
|
||
|
_wire->write(command[3]);
|
||
|
|
||
|
// Check CRC
|
||
|
if (OneWire::crc8(command, 4) != _wire->read())
|
||
|
return CRC_MISMATCH;
|
||
|
|
||
|
// Issue programming pulse for the first byte
|
||
|
if (_progPin >= 0) {
|
||
|
digitalWrite(_progPin, HIGH);
|
||
|
delayMicroseconds(500);
|
||
|
digitalWrite(_progPin, LOW);
|
||
|
}
|
||
|
delayMicroseconds(500);
|
||
|
|
||
|
// Check the first byte for proper burn
|
||
|
if (command[3] != _wire->read())
|
||
|
return COPY_FAILURE;
|
||
|
|
||
|
// write out the rest of the page
|
||
|
for (int i = 1; i < 32; i++) {
|
||
|
// Write byte
|
||
|
_wire->write(data[i]);
|
||
|
// Check CRC
|
||
|
_wire->read(); // FIXME: The EPROM calculates a CRC based on 9 bits, we can't do that with OneWire
|
||
|
//byte crc[] = { (byte)((address+i) & 0x01), data[i] };
|
||
|
//if (OneWire::crc8(crc, 2) != _wire->read())
|
||
|
// return CRC_MISMATCH;
|
||
|
// Issue programming pulse
|
||
|
if (_progPin >= 0) {
|
||
|
digitalWrite(_progPin, HIGH);
|
||
|
delayMicroseconds(500);
|
||
|
digitalWrite(_progPin, LOW);
|
||
|
}
|
||
|
delayMicroseconds(500);
|
||
|
// Check for proper burn
|
||
|
if (data[i] != _wire->read())
|
||
|
return COPY_FAILURE;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int DallasEPROM::lockPage(int page) {
|
||
|
if (!isPageValid(page))
|
||
|
return INVALID_PAGE;
|
||
|
if (!isConnected())
|
||
|
return DEVICE_DISCONNECTED;
|
||
|
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
|
||
|
if (isEPROMDevice()) {
|
||
|
byte command[] = { WRITESTATUS, 0x00, 0x00, (1 << page) };
|
||
|
|
||
|
_wire->write(command[0]);
|
||
|
_wire->write(command[1]);
|
||
|
_wire->write(command[2]);
|
||
|
_wire->write(command[3]);
|
||
|
|
||
|
// Check CRC
|
||
|
if (OneWire::crc8(command, 4) != _wire->read())
|
||
|
return CRC_MISMATCH;
|
||
|
|
||
|
// Issue programming pulse
|
||
|
if (_progPin >= 0) {
|
||
|
digitalWrite(_progPin, HIGH);
|
||
|
delayMicroseconds(500);
|
||
|
digitalWrite(_progPin, LOW);
|
||
|
}
|
||
|
delayMicroseconds(500);
|
||
|
|
||
|
// TODO: Verify data
|
||
|
} else {
|
||
|
unsigned int start;
|
||
|
byte data[] = { 0x55 }; // write protect
|
||
|
|
||
|
start = _chip_model_list[_curModelIndex].pages * 32 + page;
|
||
|
scratchWrite(data, 1, start);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool DallasEPROM::isPageLocked(int page) {
|
||
|
byte status;
|
||
|
|
||
|
if (!isPageValid(page))
|
||
|
return INVALID_PAGE;
|
||
|
if (!isConnected())
|
||
|
return DEVICE_DISCONNECTED;
|
||
|
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
|
||
|
if (isEPROMDevice()) {
|
||
|
byte command[] = { READSTATUS, 0x00, 0x00 };
|
||
|
_wire->write(command[0]);
|
||
|
_wire->write(command[1]);
|
||
|
_wire->write(command[2]);
|
||
|
|
||
|
// Check CRC on EPROM devices
|
||
|
if (OneWire::crc8(command, 3) != _wire->read())
|
||
|
return CRC_MISMATCH;
|
||
|
|
||
|
status = _wire->read();
|
||
|
|
||
|
_wire->reset();
|
||
|
|
||
|
return 1 & (status >> page);
|
||
|
} else {
|
||
|
unsigned int start;
|
||
|
|
||
|
start = _chip_model_list[_curModelIndex].pages * 32 + page;
|
||
|
|
||
|
_wire->write(READMEMORY);
|
||
|
_wire->write((byte)start);
|
||
|
_wire->write((byte)(start >> 8));
|
||
|
|
||
|
if (_wire->read() == 0x55)
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*******************
|
||
|
* Private methods
|
||
|
*******************/
|
||
|
|
||
|
int DallasEPROM::scratchWrite(uint8_t* data, int length, unsigned int address) {
|
||
|
byte auth[3];
|
||
|
|
||
|
// send the command and address
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
_wire->write(WRITEMEMORY);
|
||
|
_wire->write((byte) address);
|
||
|
_wire->write((byte)(address >> 8));
|
||
|
|
||
|
// write "length" bytes to the scratchpad
|
||
|
for (int i = 0; i < length; i++)
|
||
|
_wire->write(data[i]);
|
||
|
|
||
|
// Read the auth code from the scratchpad and verify integrity
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
_wire->write(READSTATUS);
|
||
|
_wire->read_bytes(auth, 3);
|
||
|
for (int i = 0; i < length; i++) {
|
||
|
if (_wire->read() != data[i])
|
||
|
return BAD_INTEGRITY;
|
||
|
}
|
||
|
|
||
|
// Issue copy scratchpad with auth bytes
|
||
|
_wire->reset();
|
||
|
_wire->select(_addr);
|
||
|
_wire->write(WRITESTATUS);
|
||
|
_wire->write(auth[0]);
|
||
|
_wire->write(auth[1]);
|
||
|
_wire->write(auth[2], 1);
|
||
|
|
||
|
// Need 10ms prog delay
|
||
|
delay(10);
|
||
|
_wire->depower();
|
||
|
|
||
|
// Check for success
|
||
|
if (_wire->read() != 0xAA)
|
||
|
return COPY_FAILURE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool DallasEPROM::isPageValid(int page) {
|
||
|
if (_curModelIndex >= 0 && page < _chip_model_list[_curModelIndex].pages)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool DallasEPROM::isEPROMDevice() {
|
||
|
if (_curModelIndex >= 0 && _chip_model_list[_curModelIndex].isEPROM == true)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/** @file */
|