nand2/tools/olimexino-32u4 firmware/iceprog/SPIFlash.cpp
Michael Schröder 971b323822 added v2.0
2023-01-11 23:04:57 +01:00

1961 lines
60 KiB
C++

/* Arduino SPIFlash Library v.2.2.0
* Copyright (C) 2015 by Prajwal Bhattaram
* Modified by Prajwal Bhattaram - 24/11/2015
*
* This file is part of the Arduino SPIFlash Library. This library is for
* Winbond NOR flash memory modules. In its current form it enables reading
* and writing individual data variables, structs and arrays from and to various locations;
* reading and writing pages; continuous read functions; sector, block and chip erase;
* suspending and resuming programming/erase and powering down for low power operation.
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License v3.0
* along with the Arduino SPIFlash Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "SPIFlash.h"
#if defined (__arm__) && defined (__SAM3X8E__)
#define _delay_us(us) delayMicroseconds(us)
#else
#include <util/delay.h>
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Uncomment the code below to run a diagnostic if your flash //
// does not respond //
// //
// Error codes will be generated and returned on functions //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//#define RUNDIAGNOSTIC //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Uncomment the code below to increase the speed of the library //
// by disabling _notPrevWritten() //
// //
// Make sure the sectors being written to have been erased beforehand //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#if defined (__SAM3X8E__)
//#define HIGHSPEED
#elif defined (__AVR__)
//#define HIGHSPEED
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#define _MANID 0xEF
#define PAGESIZE 0x100
#define MANID 0x90
#define PAGEPROG 0x02
#define READDATA 0x03
#define FASTREAD 0x0B
#define WRITEDISABLE 0x04
#define READSTAT1 0x05
#define READSTAT2 0x35
#define WRITEENABLE 0x06
#define SECTORERASE 0x20
#define BLOCK32ERASE 0x52
#define CHIPERASE 0x60
#define SUSPEND 0x75
#define ID 0x90
#define RESUME 0x7A
#define JEDECID 0x9f
#define RELEASE 0xAB
#define POWERDOWN 0xB9
#define BLOCK64ERASE 0xD8
#define BUSY 0x01
#define WRTEN 0x02
#define SUS 0x40
#define DUMMYBYTE 0xEE
#define arrayLen(x) (sizeof(x) / sizeof(*x))
#define lengthOf(x) (sizeof(x))/sizeof(byte)
#define maxAddress capacity
#define NO_CONTINUE 0x00
#define PASS 0x01
#define FAIL 0x00
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// List of Error codes //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#ifdef RUNDIAGNOSTIC
#define SUCCESS 0x00
#define CALLBEGIN 0x01
#define UNKNOWNCHIP 0x02
#define UNKNOWNCAP 0x03
#define CHIPBUSY 0x04
#define OUTOFBOUNDS 0x05
#define CANTENWRITE 0x06
#define PREVWRITTEN 0x07
#define UNKNOWNERROR 0xFF
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
#if defined (__arm__) && defined (__SAM3X8E__)
#include <SPI.h>
#elif defined (__AVR__)
#ifdef __AVR_ATtiny85__
#define CHIP_SELECT PORTB &= ~cs_mask;
#define CHIP_DESELECT PORTB |= cs_mask;
#define SPIBIT \
USICR = ((1<<USIWM0)|(1<<USITC)); \
USICR = ((1<<USIWM0)|(1<<USITC)|(1<<USICLK));
static uint8_t xfer(uint8_t n) {
USIDR = n;
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
return USIDR;
}
#else
#include <SPI.h>
#define CHIP_SELECT *cs_port &= ~cs_mask;
#define CHIP_DESELECT *cs_port |= cs_mask;
#define xfer(n) SPI.transfer(n)
#endif
#endif
// Constructor
#if defined (__arm__) && defined (__SAM3X8E__)
SPIFlash::SPIFlash(uint8_t cs, bool overflow) {
csPin = cs;
pageOverflow = overflow;
}
#elif defined (__AVR__)
SPIFlash::SPIFlash(uint8_t cs, bool overflow) {
csPin = cs;
#ifndef __AVR_ATtiny85__
cs_port = portOutputRegister(digitalPinToPort(csPin));
#endif
cs_mask = digitalPinToBitMask(csPin);
SPI.begin();
SPI.setDataMode(0);
SPI.setBitOrder(MSBFIRST);
//SPI.setClockDivider(SPI_CLOCK_DIV2);
pageOverflow = overflow;
pinMode(cs, OUTPUT);
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Private functions used by read, write and erase operations //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Select chip and issue command - data to follow
void SPIFlash::_cmd(uint8_t cmd, bool _continue, uint8_t cs) {
cs = csPin;
#if defined (__arm__) && defined (__SAM3X8E__)
if (!_continue)
SPI.transfer(cs, cmd);
else
SPI.transfer(cs, cmd, SPI_CONTINUE);
#else
CHIP_SELECT
(void)xfer(cmd);
#endif
}
// Checks if status register 1 can be accessed - used during powerdown and power up and for debugging
uint8_t SPIFlash::_readStat1(void) {
uint8_t stat1;
_cmd(READSTAT1);
#if defined (__SAM3X8E__)
stat1 = SPI.transfer(csPin, 0x00);
#else
stat1 = xfer(0);
CHIP_DESELECT
#endif
return stat1;
}
// Checks the erase/program suspend flag before enabling/disabling a program/erase suspend operation
bool SPIFlash::_noSuspend(void) {
uint8_t state;
_cmd(READSTAT2);
#if defined (__arm__) && defined (__SAM3X8E__)
state = SPI.transfer(csPin, 0x00);
#else
state = xfer(0);
CHIP_DESELECT
#endif
if(state & SUS)
return false;
return true;
}
// Polls the status register 1 until busy flag is cleared or timeout
bool SPIFlash::_notBusy(uint32_t timeout) {
uint8_t state;
uint32_t startTime = millis();
do {
_cmd(READSTAT1);
#if defined (__arm__) && defined (__SAM3X8E__)
state = SPI.transfer(csPin, 0x00);
#else
state = xfer(0);
CHIP_DESELECT
#endif
if((millis()-startTime) > timeout){
#ifdef RUNDIAGNOSTIC
errorcode = CHIPBUSY;
_troubleshoot(errorcode);
#endif
return false;
}
} while(state & BUSY);
return true;
}
//Enables writing to chip by setting the WRITEENABLE bit
bool SPIFlash::_writeEnable(void) {
uint8_t state;
#if defined (__arm__) && (__SAM3X8E__)
SPI.transfer(csPin, WRITEENABLE);
#else
_cmd(WRITEENABLE);
CHIP_DESELECT
#endif
//verifies that WRITE is enabled
#if defined (__arm__) && (__SAM3X8E__)
SPI.transfer(csPin, READSTAT1, SPI_CONTINUE);
state = SPI.transfer(csPin, 0x00);
#else
_cmd(READSTAT1);
state = xfer(0);
CHIP_DESELECT
#endif
if(state & WRTEN)
return true;
else {
#ifdef RUNDIAGNOSTIC
errorcode = CANTENWRITE;
_troubleshoot(errorcode);
#endif
return false;
}
}
//Disables writing to chip by setting the Write Enable Latch (WEL) bit in the Status Register to 0
//_writeDisable() is not required under the following conditions because the Write Enable Latch (WEL) flag is cleared to 0
// i.e. to write disable state:
// Power-up, Write Disable, Page Program, Quad Page Program, Sector Erase, Block Erase, Chip Erase, Write Status Register,
// Erase Security Register and Program Security register
bool SPIFlash::_writeDisable(void) {
_cmd(WRITEDISABLE);
#if defined (__AVR__)
CHIP_DESELECT
#endif
return true;
}
//Gets address from page number and offset. Takes two arguments:
// 1. page_number --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
uint32_t SPIFlash::_getAddress(uint16_t page_number, uint8_t offset) {
uint32_t address = page_number;
address = (address << 8) + offset;
if (!_addressCheck(address)){
#ifdef RUNDIAGNOSTIC
errorcode = OUTOFBOUNDS;
_troubleshoot(errorcode);
#endif
return false;
}
else
return address;
}
//Checks the device ID to establish storage parameters
bool SPIFlash::_getManId(uint8_t *b1, uint8_t *b2) {
if(!_notBusy())
return false;
_cmd(MANID);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, 0x00, SPI_CONTINUE);
SPI.transfer(csPin, 0x00, SPI_CONTINUE);
SPI.transfer(csPin, 0x00, SPI_CONTINUE);
*b1 = SPI.transfer(csPin, 0x00, SPI_CONTINUE);
*b2 = SPI.transfer(csPin, 0x00);
#else
xfer(0);
xfer(0);
xfer(0);
*b1 = xfer(0);
*b2 = xfer(0);
CHIP_DESELECT
#endif
return true;
}
//Checks for presence of chip by requesting JEDEC ID
bool SPIFlash::_getJedecId(uint8_t *b1, uint8_t *b2, uint8_t *b3) {
if(!_notBusy())
return false;
_cmd(JEDECID);
#if defined (__arm__) && defined (__SAM3X8E__)
*b1 = SPI.transfer(csPin, 0x00, SPI_CONTINUE); // manufacturer id
*b2 = SPI.transfer(csPin, 0x00, SPI_CONTINUE); // manufacturer id
*b3 = SPI.transfer(csPin, 0x00); // capacity
#else
*b1 = xfer(0); // manufacturer id
*b2 = xfer(0); // memory type
*b3 = xfer(0); // capacity
CHIP_DESELECT
#endif
return true;
}
//Identifies the chip
bool SPIFlash::_chipID(void) {
//Get Manfucturer/Device ID so the library can identify the chip
uint8_t manID, devID ;
_getManId(&manID, &devID);
if (manID != _MANID){ //If the chip is not a Winbond Chip
#ifdef RUNDIAGNOSTIC
errorcode = UNKNOWNCHIP; //Error code for unidentified chip
_troubleshoot(errorcode);
#endif
while(1);
}
//Check flash memory type and identify capacity
uint8_t i;
//capacity & chip name
for (i = 0; i < sizeof(devType); i++)
{
if (devID == devType[i]) {
capacity = memSize[i];
name = chipName[i];
}
}
if (capacity == 0) {
#ifdef RUNDIAGNOSTIC
errorcode = UNKNOWNCAP; //Error code for unidentified capacity
_troubleshoot(errorcode);
#endif
while(1);
}
maxPage = capacity/PAGESIZE;
/*#ifdef RUNDIAGNOSTIC
char buffer[64];
sprintf(buffer, "Manufacturer ID: %02xh\nMemory Type: %02xh\nCapacity: %lu\nmaxPage: %d", manID, devID, capacity, maxPage);
Serial.println(buffer);
#endif*/
return true;
}
//Checks to see if pageOverflow is permitted and assists with determining next address to read/write.
//Sets the global address variable
bool SPIFlash::_addressCheck(uint32_t address) {
#ifdef RUNDIAGNOSTIC
if (capacity == 0) {
errorcode = CALLBEGIN;
_troubleshoot(errorcode);
}
#endif
if (address >= capacity) {
if (!pageOverflow) {
#ifdef RUNDIAGNOSTIC
errorcode = OUTOFBOUNDS;
_troubleshoot(errorcode);
#endif
return false; // At end of memory - (!pageOverflow)
}
else {
address = 0x00; // At end of memory - (pageOverflow)
return true;
}
}
return true; // Not at end of memory if (address < capacity)
}
//Double checks all parameters before calling a Read
//Has two variants:
// A. Takes address and returns the address if true, else returns false. Throws an error if there is a problem.
// B. Takes page_number and offset and returns address. Throws an error if there is a problem
// Variant A
uint32_t SPIFlash::_prepRead(uint32_t address) {
if(!_notBusy())
return false;
if (!_addressCheck(address))
return false;
else
return address;
}
// Variant B
uint32_t SPIFlash::_prepRead(uint16_t page_number, uint8_t offset) {
uint32_t address = _getAddress(page_number, offset);
return _prepRead(address);
}
//Initiates read operation - but data is not read yet
void SPIFlash::_beginRead(uint32_t address) {
_cmd(READDATA);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, address >> 16, SPI_CONTINUE);
SPI.transfer(csPin, address >> 8, SPI_CONTINUE);
SPI.transfer(csPin, address, SPI_CONTINUE);
#else
(void)xfer(address >> 16);
(void)xfer(address >> 8);
(void)xfer(address);
#endif
//SPI data lines are left open until _endProcess() is called
}
//Initiates fast read operation (can operate at the highest possible freq of 104 MHz (at 3.0-3.6V) of 80 MHz (at 2.5-3.6V))
//- but data is not read yet
void SPIFlash::_beginFastRead(uint32_t address) {
_cmd(FASTREAD);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, address >> 16, SPI_CONTINUE);
SPI.transfer(csPin, address >> 8, SPI_CONTINUE);
SPI.transfer(csPin, address, SPI_CONTINUE);
#else
(void)xfer(address >> 16);
(void)xfer(address >> 8);
(void)xfer(address);
#endif
//SPI data lines are left open until _endProcess() is called
}
//Reads next byte. Call 'n' times to read 'n' number of bytes. Should be called after _beginRead()
uint8_t SPIFlash::_readNextByte(bool _continue) {
uint8_t result;
#if defined (__arm__) && defined (__SAM3X8E__)
if (!_continue)
result = SPI.transfer(csPin, 0x00);
else
result = SPI.transfer(csPin, 0x00, SPI_CONTINUE);
#else
result = xfer(0x00);
#endif
return result;
}
//Double checks all parameters before initiating a write
//Has two variants:
// A. Takes address and returns address if true or 'false' if false. Throws an error if there is a problem.
// B. Takes page_number and offset and returns address. Throws an error if there is a problem
// Variant A
uint32_t SPIFlash::_prepWrite(uint32_t address) {
if(!_notBusy()||!_writeEnable())
return false;
if (!_addressCheck(address)) {
#ifdef RUNDIAGNOSTIC
errorcode = OUTOFBOUNDS;
_troubleshoot(errorcode);
#endif
return false;
}
else
return address;
}
// Variant B
uint32_t SPIFlash::_prepWrite(uint16_t page_number, uint8_t offset) {
uint32_t address = _getAddress(page_number, offset);
return _prepWrite(address);
}
//Initiates write operation - but data is not written yet
bool SPIFlash::_beginWrite(uint32_t address) {
_cmd(PAGEPROG);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, address >> 16, SPI_CONTINUE);
SPI.transfer(csPin, address >> 8, SPI_CONTINUE);
SPI.transfer(csPin, address, SPI_CONTINUE);
#else
(void)xfer(address >> 16);
(void)xfer(address >> 8);
(void)xfer(address);
#endif
//SPI data lines are left open until _endProcess() is called
return true;
}
//Writes next byte. Call 'n' times to read 'n' number of bytes. Should be called after _beginWrite()
bool SPIFlash::_writeNextByte(uint8_t b, bool _continue) {
#if defined (__arm__) && defined (__SAM3X8E__)
if (!_continue)
SPI.transfer(csPin, b);
else
SPI.transfer(csPin, b, SPI_CONTINUE);
#else
xfer(b);
#endif
return true;
}
//Stops all operations. Should be called after all the required data is read/written from repeated _readNextByte()/_writeNextByte() calls
void SPIFlash::_endProcess(void) {
#if defined (__arm__) && defined (__SAM3X8E__)
_delay_us(3);
#else
CHIP_DESELECT
#endif
_delay_us(3);
}
#ifndef HIGHSPEED
bool SPIFlash::_notPrevWritten(uint32_t address, uint8_t size) {
uint32_t _size = size;
/*uint8_t databyte1, databyte2, databyte3;
databyte1 = readByte(address, true);
databyte2 = readByte((address + (size/2)), true);
databyte3 = readByte((address+size), true);
if (databyte1 != 255 || databyte2 != 255 || databyte3 != 255) {
#ifdef RUNDIAGNOSTIC
errorcode = PREVWRITTEN;
_troubleshoot(errorcode);
#endif
return false;
}
_delay_us(3);
return true;*/
uint32_t sampleSize;
if (_size <= 10) {
sampleSize = _size;
}
if (_size > 10) {
do {
sampleSize++;
_size/=10;
} while((_size/10) >= 1);
}
uint32_t addresses[sampleSize];
for (uint16_t i = 0; i < sampleSize; i++) {
addresses[i] = (rand() % size) + address;
}
for (uint16_t i = 0; i < sampleSize; i++) {
uint8_t databyte = readByte(addresses[i]);
if (databyte != 0xFF) {
#ifdef RUNDIAGNOSTIC
errorcode = PREVWRITTEN;
_troubleshoot(errorcode);
#endif
return false;
}
}
return true;
}
#endif
#ifdef RUNDIAGNOSTIC
//Troubleshooting function. Called when #ifdef RUNDIAGNOSTIC is uncommented at the top of this file.
void SPIFlash::_troubleshoot(uint8_t error) {
switch (error) {
case SUCCESS:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(SUCCESS, HEX);
#else
Serial.println("Action completed successfully");
#endif
break;
case CALLBEGIN:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(CALLBEGIN, HEX);
#else
Serial.println("*constructor_of_choice*.begin() was not called in void setup()");
#endif
break;
case UNKNOWNCHIP:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(UNKNOWNCHIP, HEX);
#else
Serial.println("Unable to identify chip. Are you shure this is a Winbond Flash chip");
Serial.println("Please raise an issue at http://www.github.com/Marzogh/SPIFlash/issues with your chip type and I will try to add support to your chip");
#endif
break;
case UNKNOWNCAP:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(UNKNOWNCAP, HEX);
#else
Serial.println("Unable to identify capacity.");
Serial.println("Please raise an issue at http://www.github.com/Marzogh/SPIFlash/issues with your chip type and I will work on adding support to your chip");
#endif
break;
case CHIPBUSY:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(CHIPBUSY, HEX);
#else
Serial.println("Chip is busy.");
Serial.println("Make sure all pins have been connected properly");
Serial.print("If it still doesn't work, ");
Serial.println("please raise an issue at http://www.github.com/Marzogh/SPIFlash/issues with the details of what your were doing when this error occured");
#endif
break;
case OUTOFBOUNDS:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(OUTOFBOUNDS, HEX);
#else
Serial.println("Page overflow has been disabled and the address called exceeds the memory");
#endif
break;
case CANTENWRITE:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(CANTENWRITE, HEX);
#else
Serial.println("Unable to Enable Writing to chip.");
Serial.println("Please make sure the HOLD & WRITEPROTECT pins are connected properly to VCC & GND respectively");
Serial.print("If you are still facing issues, ");
Serial.println("please raise an issue at http://www.github.com/Marzogh/SPIFlash/issues with the details of what your were doing when this error occured");
#endif
break;
case PREVWRITTEN:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x0");
Serial.println(PREVWRITTEN, HEX);
#else
Serial.println("This sector already contains data.");
Serial.println("Please make sure the sectors being written to are erased.");
Serial.print("If you are still facing issues, ");
Serial.println("please raise an issue at http://www.github.com/Marzogh/SPIFlash/issues with the details of what your were doing when this error occured");
#endif
break;
default:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega32U4__) || defined (__AVR_ATtiny85__)
Serial.print("Error code: 0x");
Serial.println(UNKNOWNERROR, HEX);
#else
Serial.println("Unknown error");
Serial.println("Please raise an issue at http://www.github.com/Marzogh/SPIFlash/issues with the details of what your were doing when this error occured");
#endif
break;
}
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Public functions used for read, write and erase operations //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//Identifies chip and establishes parameters
void SPIFlash::begin() {
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.begin(csPin);
SPI.setClockDivider(csPin, 21);
SPI.setDataMode(csPin, SPI_MODE0);
#endif
_chipID();
}
//Returns capacity of chip
uint32_t SPIFlash::getCapacity() {
return capacity;
}
//Returns maximum number of pages
uint32_t SPIFlash::getMaxPage() {
return maxPage;
}
//Returns identifying name of the chip
uint16_t SPIFlash::getChipName() {
return name;
}
//Checks for and initiates the chip by requesting JEDEC ID which is returned as a 32 bit int
uint16_t SPIFlash::getManID() {
uint8_t b1, b2;
_getManId(&b1, &b2);
uint32_t id = b1;
id = (id << 8)|(b2 << 0);
return id;
}
//Checks for and initiates the chip by requesting JEDEC ID which is returned as a 32 bit int
uint32_t SPIFlash::getJEDECID() {
uint8_t b1, b2, b3;
_getJedecId(&b1, &b2, &b3);
uint32_t id = b1;
id = (id << 8)|(b2 << 0);
id = (id << 8)|(b3 << 0);
return id;
}
//Gets the next available address for use. Has two variants:
// A. Takes the size of the data as an argument and returns a 32-bit address
// B. Takes a three variables, the size of the data and two other variables to return a page number value & an offset into.
// All addresses in the in the sketch must be obtained via this function or not at all.
// Variant A
uint32_t SPIFlash::getAddress(uint16_t size) {
if (!_addressCheck(currentAddress)){
#ifdef RUNDIAGNOSTIC
errorcode = OUTOFBOUNDS;
_troubleshoot(errorcode);
#endif
return false;
}
else {
uint32_t address = currentAddress;
/*Serial.print("Current Address: ");
Serial.println(currentAddress);*/
currentAddress+=size;
return address;
}
}
// Variant B
bool SPIFlash::getAddress(uint16_t size, uint16_t &page_number, uint8_t &offset) {
uint32_t address = getAddress(size);
offset = (address >> 0);
page_number = (address >> 8);
return true;
}
//Function for returning the size of the string (only to be used for the getAddress() function)
uint16_t SPIFlash::sizeofStr(String &inputStr) {
uint16_t inStrLen = inputStr.length() + 1;
uint16_t size;
//inputStr.toCharArray(inputChar, inStrLen);
size=(sizeof(char)*inStrLen);
size+=sizeof(inStrLen);
return size;
}
// Reads a byte of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
uint8_t SPIFlash::readByte(uint32_t address, bool fastRead) {
uint8_t data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
data = _readNextByte(NO_CONTINUE);
_endProcess();
return data;
}
}
// Variant B
uint8_t SPIFlash::readByte(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readByte(address, fastRead);
}
// Reads a char of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
int8_t SPIFlash::readChar(uint32_t address, bool fastRead) {
int8_t data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
data = _readNextByte(NO_CONTINUE);
_endProcess();
return data;
}
}
// Variant B
int8_t SPIFlash::readChar(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readChar(address, fastRead);
}
// Reads an array of bytes starting from a specific location in a page.// Has two variants:
// A. Takes three arguments
// 1. address --> Any address from 0 to maxAddress
// 2. data_buffer --> The array of bytes to be read from the flash memory - starting at the address indicated
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes four arguments
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data_buffer --> The array of bytes to be read from the flash memory - starting at the offset on the page indicated
// 4. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
uint8_t SPIFlash::readByteArray(uint32_t address, uint8_t *data_buffer, uint16_t bufferSize, bool fastRead) {
if (!_prepRead(address)) {
}
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t a = 0; a < bufferSize; a++) {
if (a == (bufferSize-1))
data_buffer[a] = _readNextByte(NO_CONTINUE);
else
data_buffer[a] = _readNextByte();
_addressCheck(address++);
}
_endProcess();
}
return true;
}
// Variant B
uint8_t SPIFlash::readByteArray(uint16_t page_number, uint8_t offset, uint8_t *data_buffer, uint16_t bufferSize, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readByteArray(address, data_buffer, bufferSize, fastRead);
}
// Reads an array of bytes starting from a specific location in a page.// Has two variants:
// A. Takes three arguments
// 1. address --> Any address from 0 to maxAddress
// 2. data_buffer --> The array of bytes to be read from the flash memory - starting at the address indicated
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes four arguments
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data_buffer --> The array of bytes to be read from the flash memory - starting at the offset on the page indicated
// 4. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
uint8_t SPIFlash::readCharArray(uint32_t address, char *data_buffer, uint16_t bufferSize, bool fastRead) {
if (_prepRead(address)) {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t a = 0; a < bufferSize; a++) {
_addressCheck(address+a);
if (a == (bufferSize-1))
data_buffer[a] = _readNextByte(NO_CONTINUE);
else
data_buffer[a] = _readNextByte();
}
_endProcess();
return true;
}
else
return false;
}
// Variant B
uint8_t SPIFlash::readCharArray(uint16_t page_number, uint8_t offset, char *data_buffer, uint16_t bufferSize, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readCharArray(address, data_buffer, bufferSize, fastRead);
}
// Reads an unsigned int of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
uint16_t SPIFlash::readWord(uint32_t address, bool fastRead) {
union
{
byte b[sizeof(uint16_t)];
uint16_t I;
} data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t i=0; i < (sizeof(int16_t)); i++) {
if (i == (sizeof(uint16_t)-1))
data.b[i] = _readNextByte(NO_CONTINUE);
else
data.b[i] = _readNextByte();
}
_endProcess();
return data.I;
}
}
// Variant B
uint16_t SPIFlash::readWord(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readWord(address, fastRead);
}
// Reads a signed int of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
int16_t SPIFlash::readShort(uint32_t address, bool fastRead) {
union
{
byte b[sizeof(int16_t)];
int16_t s;
} data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t i=0; i < (sizeof(int16_t)); i++) {
if (i == (sizeof(int16_t)-1))
data.b[i] = _readNextByte(NO_CONTINUE);
else
data.b[i] = _readNextByte();
}
_endProcess();
return data.s;
}
}
// Variant B
int16_t SPIFlash::readShort(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readShort(address, fastRead);
}
// Reads an unsigned long of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
uint32_t SPIFlash::readULong(uint32_t address, bool fastRead) {
union
{
byte b[sizeof(uint32_t)];
uint32_t l;
} data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t i=0; i < (sizeof(uint32_t)); i++) {
if (i == (sizeof(uint32_t)-1))
data.b[i] = _readNextByte(NO_CONTINUE);
else
data.b[i] = _readNextByte();
}
_endProcess();
return data.l;
}
}
// Variant B
uint32_t SPIFlash::readULong(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readULong(address, fastRead);
}
// Reads a signed long of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
int32_t SPIFlash::readLong(uint32_t address, bool fastRead) {
union
{
byte b[sizeof(int32_t)];
int32_t l;
} data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t i=0; i < (sizeof(int32_t)); i++) {
if (i == (sizeof(int32_t)-1))
data.b[i] = _readNextByte(NO_CONTINUE);
else
data.b[i] = _readNextByte();
}
_endProcess();
return data.l;
}
}
// Variant B
int32_t SPIFlash::readLong(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readLong(address, fastRead);
}
// Reads a signed long of data from a specific location in a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// Variant A
float SPIFlash::readFloat(uint32_t address, bool fastRead) {
union
{
byte b[(sizeof(float))];
float f;
} data;
if (!_prepRead(address))
return false;
else {
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (uint16_t i=0; i < (sizeof(float)); i++) {
if (i == (sizeof(float)-1))
data.b[i] = _readNextByte(NO_CONTINUE);
else
data.b[i] = _readNextByte();
}
_endProcess();
return data.f;
}
}
// Variant B
float SPIFlash::readFloat(uint16_t page_number, uint8_t offset, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readFloat(address, fastRead);
}
// Reads a string from a specific location on a page.
// Has two variants:
// A. Takes three arguments
// 1. address --> Any address from 0 to maxAddress
// 2. outputString --> String variable to write the output to
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
// B. Takes four arguments
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. outputString --> String variable to write the output to
// 4. fastRead --> defaults to false - executes _beginFastRead() if set to true
// This function first reads a short from the address to figure out the size of the String object stored and
// then reads the String object data
// Variant A
bool SPIFlash::readStr(uint32_t address, String &outStr, bool fastRead) {
if (!_prepRead(address))
return false;
uint16_t strLen;
strLen = readShort(address);
address+=(sizeof(strLen));
char outputChar[strLen];
readCharArray(address, outputChar, strLen, fastRead);
outStr = String(outputChar);
return true;
}
// Variant B
bool SPIFlash::readStr(uint16_t page_number, uint8_t offset, String &outStr, bool fastRead) {
uint32_t address = _getAddress(page_number, offset);
return readStr(address, outStr, fastRead);
}
// Reads a page of data into a page buffer. Takes three arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. data_buffer --> a data buffer to read the data into (This HAS to be an array of 256 bytes)
// 3. fastRead --> defaults to false - executes _beginFastRead() if set to true
bool SPIFlash::readPage(uint16_t page_number, uint8_t *data_buffer, bool fastRead) {
uint32_t address = _prepRead(page_number);
if(!fastRead)
_beginRead(address);
else
_beginFastRead(address);
for (int a = 0; a < 256; ++a) {
if (a == (255))
data_buffer[a] = _readNextByte(NO_CONTINUE);
else
data_buffer[a] = _readNextByte();
}
_endProcess();
return true;
}
// Writes a byte of data to a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One byte of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One byte of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeByte(uint32_t address, uint8_t data, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
_beginWrite(address);
_writeNextByte(data, NO_CONTINUE);
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeByte(uint16_t page_number, uint8_t offset, uint8_t data, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeByte(address, data, errorCheck);
}
// Writes a char of data to a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One char of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One char of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeChar(uint32_t address, int8_t data, bool errorCheck) {
if (!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
_beginWrite(address);
_writeNextByte(data, NO_CONTINUE);
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeChar(uint16_t page_number, uint8_t offset, int8_t data, bool errorCheck) {
uint32_t address = _prepWrite(page_number, offset);
return writeChar(address, data, errorCheck);
}
// Writes an array of bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> An array of bytes to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> An array of bytes to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeByteArray(uint32_t address, uint8_t *data_buffer, uint16_t bufferSize, bool errorCheck) {
if (!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data_buffer)))
return false;
#endif
_beginWrite(address);
for (int i = 0; i < bufferSize; i++) {
_addressCheck(address+i);
if (i == (bufferSize-1))
_writeNextByte(data_buffer[i], NO_CONTINUE);
else
_writeNextByte(data_buffer[i]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data_buffer);
}
// Variant B
bool SPIFlash::writeByteArray(uint16_t page_number, uint8_t offset, uint8_t *data_buffer, uint16_t bufferSize, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeByteArray(address, data_buffer, bufferSize, errorCheck);
}
// Writes an array of bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> An array of chars to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> An array of chars to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeCharArray(uint32_t address, char *data_buffer, uint16_t bufferSize, bool errorCheck) {
if (!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data_buffer)))
return false;
#endif
_beginWrite(address);
for (int i = 0; i < bufferSize; i++) {
_addressCheck(address+i);
if (i == (bufferSize-1))
_writeNextByte(data_buffer[i], NO_CONTINUE);
else
_writeNextByte(data_buffer[i]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data_buffer);
}
// Variant B
bool SPIFlash::writeCharArray(uint16_t page_number, uint8_t offset, char *data_buffer, uint16_t bufferSize, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeCharArray(address, data_buffer, bufferSize, errorCheck);
}
// Writes an unsigned int as two bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One unsigned int of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One unsigned int of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeWord(uint32_t address, uint16_t data, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
union
{
uint8_t b[sizeof(data)];
uint16_t w;
} var;
var.w = data;
_beginWrite(address);
for (uint16_t j = 0; j < sizeof(data); j++) {
if (j == (sizeof(data)-1))
_writeNextByte(var.b[j], NO_CONTINUE);
else
_writeNextByte(var.b[j]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeWord(uint16_t page_number, uint8_t offset, uint16_t data, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeWord(address, data, errorCheck);
}
// Writes a signed int as two bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One signed int of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One signed int of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeShort(uint32_t address, int16_t data, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
union
{
uint8_t b[sizeof(data)];
int16_t s;
} var;
var.s = data;
_beginWrite(address);
for (uint16_t j = 0; j < (sizeof(data)); j++) {
if (j == (sizeof(data)-1))
_writeNextByte(var.b[j], NO_CONTINUE);
else
_writeNextByte(var.b[j]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeShort(uint16_t page_number, uint8_t offset, int16_t data, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeShort(address, data, errorCheck);
}
// Writes an unsigned long as four bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One unsigned long of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One unsigned long of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeULong(uint32_t address, uint32_t data, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
union
{
uint8_t b[sizeof(data)];
uint32_t l;
} var;
var.l = data;
_beginWrite(address);
for (uint16_t j = 0; j < (sizeof(data)); j++) {
if (j == (sizeof(data)-1))
_writeNextByte(var.b[j], NO_CONTINUE);
else
_writeNextByte(var.b[j]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeULong(uint16_t page_number, uint8_t offset, uint32_t data, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeULong(address, data, errorCheck);
}
// Writes a signed long as four bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One signed long of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One signed long of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeLong(uint32_t address, int32_t data, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
union
{
uint8_t b[sizeof(data)];
int32_t l;
} var;
var.l = data;
_beginWrite(address);
for (uint16_t j = 0; j < (sizeof(data)); j++) {
if (j == (sizeof(data)-1))
_writeNextByte(var.b[j], NO_CONTINUE);
else
_writeNextByte(var.b[j]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeLong(uint16_t page_number, uint8_t offset, int32_t data, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeLong(address, data, errorCheck);
}
// Writes a float as four bytes starting from a specific location in a page.
// Has two variants:
// A. Takes three arguments -
// 1. address --> Any address - from 0 to maxAddress
// 2. data --> One float of data to be written to a particular location on a page
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. data --> One float of data to be written to a particular location on a page
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// Variant A
bool SPIFlash::writeFloat(uint32_t address, float data, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data)))
return false;
#endif
union
{
uint8_t b[sizeof(data)];
float f;
} var;
var.f = data;
_beginWrite(address);
for (uint16_t j = 0; j < (sizeof(data)); j++) {
if (j == (sizeof(data)-1))
_writeNextByte(var.b[j], NO_CONTINUE);
else
_writeNextByte(var.b[j]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, data);
}
// Variant B
bool SPIFlash::writeFloat(uint16_t page_number, uint8_t offset, float data, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeFloat(address, data, errorCheck);
}
// Reads a string from a specific location on a page.
// Has two variants:
// A. Takes two arguments -
// 1. address --> Any address from 0 to maxAddress
// 2. inputString --> String variable to write the data from
// 3. errorCheck --> Turned on by default. Checks for writing errors
// B. Takes four arguments -
// 1. page --> Any page number from 0 to maxPage
// 2. offset --> Any offset within the page - from 0 to 255
// 3. inputString --> String variable to write the data from
// 4. errorCheck --> Turned on by default. Checks for writing errors
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
// This function first writes the size of the string as an unsigned int to the address to figure out the size of the String object stored and
// then writes the String object data. Therefore it takes up two bytes more than the size of the String itself.
// Variant A
bool SPIFlash::writeStr(uint32_t address, String &inputStr, bool errorCheck) {
if(!_prepWrite(address))
return false;
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(inputStr)))
return false;
#endif
uint16_t inStrLen = inputStr.length() + 1;
char inputChar[inStrLen];
writeWord(address, inStrLen, errorCheck);
address+=sizeof(inStrLen);
inputStr.toCharArray(inputChar, inStrLen);
writeCharArray(address, inputChar, inStrLen, errorCheck);
return true;
}
// Variant B
bool SPIFlash::writeStr(uint16_t page_number, uint8_t offset, String &inputStr, bool errorCheck) {
uint32_t address = _getAddress(page_number, offset);
return writeStr(address, inputStr, errorCheck);
}
// Writes a page of data from a data_buffer array. Make sure the sizeOf(uint8_t data_buffer[]) == 256.
// errorCheck --> Turned on by default. Checks for writing errors.
// WARNING: You can only write to previously erased memory locations (see datasheet).
// Use the eraseSector()/eraseBlock32K/eraseBlock64K commands to first clear memory (write 0xFFs)
bool SPIFlash::writePage(uint16_t page_number, uint8_t *data_buffer, bool errorCheck) {
uint32_t address = _prepWrite(page_number);
#ifndef HIGHSPEED
if(!_notPrevWritten(address, sizeof(data_buffer)))
return false;
#endif
_beginWrite(address);
for (uint16_t i = 0; i < PAGESIZE; ++i){
if (i == (PAGESIZE-1))
_writeNextByte(data_buffer[i], NO_CONTINUE);
else
_writeNextByte(data_buffer[i]);
}
_endProcess();
if (!errorCheck)
return true;
else
return _writeErrorCheck(address, *data_buffer);
}
//Erases one 4k sector. Has two variants:
// A. Takes the address as the argument and erases the sector containing the address.
// B. Takes page to be erased as the argument and erases the sector containing the page.
// The sectors are numbered 0 - 255 containing 16 pages each.
// Page 0-15 --> Sector 0; Page 16-31 --> Sector 1;......Page 4080-4095 --> Sector 255
// Variant A
bool SPIFlash::eraseSector(uint32_t address) {
if(!_notBusy()||!_writeEnable())
return false;
_cmd(SECTORERASE);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, address >> 16, SPI_CONTINUE);
SPI.transfer(csPin, address >> 8, SPI_CONTINUE);
SPI.transfer(csPin, 0x00);
#else
(void)xfer(address >> 16);
(void)xfer(address >> 8);
(void)xfer(0);
CHIP_DESELECT
#endif
if(!_notBusy(410L))
return false; //Datasheet says erasing a sector takes 400ms max
//_writeDisable(); //_writeDisable() is not required because the Write Enable Latch (WEL) flag is cleared to 0
// i.e. to write disable state upon the following conditions:
// Power-up, Write Disable, Page Program, Quad Page Program, ``Sector Erase``, Block Erase, Chip Erase, Write Status Register,
// Erase Security Register and Program Security register
return true;
}
// Variant B
bool SPIFlash::eraseSector(uint16_t page_number, uint8_t offset) {
uint32_t address = _getAddress(page_number, offset);
return eraseSector(address);
}
//Erases one 32k block. Has two variants:
// A. Takes the address as the argument and erases the block containing the address.
// B. Takes page to be erased as the argument and erases the block containing the page.
// The blocks are numbered 0 - 31 containing 128 pages each.
// Page 0-127 --> Block 0; Page 128-255 --> Block 1;......Page 3968-4095 --> Block 31
// Variant A
bool SPIFlash::eraseBlock32K(uint32_t address) {
if(!_notBusy()||!_writeEnable())
return false;
_cmd(BLOCK32ERASE);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, address >> 16, SPI_CONTINUE);
SPI.transfer(csPin, address >> 8, SPI_CONTINUE);
SPI.transfer(csPin, 0x00);
#else
(void)xfer(address >> 16);
(void)xfer(address >> 8);
(void)xfer(0);
CHIP_DESELECT
#endif
if(!_notBusy(410L))
return false; //Datasheet says erasing a sector takes 400ms max
//_writeDisable(); //_writeDisable() is not required because the Write Enable Latch (WEL) flag is cleared to 0
// i.e. to write disable state upon the following conditions:
// Power-up, Write Disable, Page Program, Quad Page Program, Sector Erase, ``Block Erase``, Chip Erase, Write Status Register,
// Erase Security Register and Program Security register
return true;
}
// Variant B
bool SPIFlash::eraseBlock32K(uint16_t page_number, uint8_t offset) {
uint32_t address = _getAddress(page_number, offset);
return eraseBlock32K(address);
}
//Erases one 64k block. Has two variants:
// A. Takes the address as the argument and erases the block containing the address.
// B. Takes page to be erased as the argument and erases the block containing the page.
// The blocks are numbered 0 - 15 containing 256 pages each.
// Page 0-255 --> Block 0; Page 256-511 --> Block 1;......Page 3840-4095 --> Block 15
// Variant A
bool SPIFlash::eraseBlock64K(uint32_t address) {
if(!_notBusy()||!_writeEnable())
return false;
_cmd(BLOCK64ERASE);
#if defined (__arm__) && defined (__SAM3X8E__)
SPI.transfer(csPin, address >> 16, SPI_CONTINUE);
SPI.transfer(csPin, address >> 8, SPI_CONTINUE);
SPI.transfer(csPin, 0x00);
#else
(void)xfer(address >> 16);
(void)xfer(address >> 8);
(void)xfer(0);
CHIP_DESELECT
#endif
if(!_notBusy(410L))
return false; //Datasheet says erasing a sector takes 400ms max
//_writeDisable(); //_writeDisable() is not required because the Write Enable Latch (WEL) flag is cleared to 0
// i.e. to write disable state upon the following conditions:
// Power-up, Write Disable, Page Program, Quad Page Program, Sector Erase, ``Block Erase``, Chip Erase, Write Status Register,
// Erase Security Register and Program Security register
return true;
}
// Variant B
bool SPIFlash::eraseBlock64K(uint16_t page_number, uint8_t offset) {
uint32_t address = _getAddress(page_number, offset);
return eraseBlock64K(address);
}
//Erases whole chip. Think twice before using.
bool SPIFlash::eraseChip(void) {
if(!_notBusy()||!_writeEnable())
return false;
_cmd(CHIPERASE, NO_CONTINUE);
#if defined (__AVR__)
CHIP_DESELECT
#endif
if(!_notBusy(7000L))
return false; //Datasheet says erasing chip takes 6s max
//_writeDisable(); //_writeDisable() is not required because the Write Enable Latch (WEL) flag is cleared to 0
// i.e. to write disable state upon the following conditions:
// Power-up, Write Disable, Page Program, Quad Page Program, Sector Erase, Block Erase, ``Chip Erase``, Write Status Register,
// Erase Security Register and Program Security register
return true;
}
//Suspends current Block Erase/Sector Erase/Page Program. Does not suspend chipErase().
//Page Program, Write Status Register, Erase instructions are not allowed.
//Erase suspend is only allowed during Block/Sector erase.
//Program suspend is only allowed during Page/Quad Page Program
bool SPIFlash::suspendProg(void) {
if(_notBusy() || !_noSuspend())
return false;
_cmd(SUSPEND, NO_CONTINUE);
#if defined (__AVR__)
CHIP_DESELECT
#endif
_delay_us(20);
if(!_notBusy() || _noSuspend()) //Max suspend Enable time according to datasheet
return false;
return true;
}
//Resumes previously suspended Block Erase/Sector Erase/Page Program.
bool SPIFlash::resumeProg(void) {
if(!_notBusy() || _noSuspend())
return false;
_cmd(RESUME, NO_CONTINUE);
#if defined (__AVR__)
CHIP_DESELECT
#endif
_delay_us(20);
if(_notBusy() || !_noSuspend())
return false;
return true;
}
//Puts device in low power state. Good for battery powered operations.
//Typical current consumption during power-down is 1mA with a maximum of 5mA. (Datasheet 7.4)
//In powerDown() the chip will only respond to powerUp()
bool SPIFlash::powerDown(void) {
if(!_notBusy())
return false;
_cmd(POWERDOWN, NO_CONTINUE);
#if defined (__AVR__)
CHIP_DESELECT
#endif
_delay_us(3); //Max powerDown enable time according to the Datasheet
uint8_t status1 = _readStat1();
uint8_t status2 = _readStat1();
status1 = _readStat1();
if (status1 != 255 && status2 != 255) {
if (status1 == status2 || status1 == 0 || status2 == 0) {
status1 = _readStat1();
status2 = _readStat1();
}
else if (status1 != status2)
return true;
}
else if (status1 == 255 && status2 == 255)
return true;
else if (status1 == 0 && status2 == 0)
return false;
return true;
}
//Wakes chip from low power state.
bool SPIFlash::powerUp(void) {
_cmd(RELEASE, NO_CONTINUE);
#if defined (__AVR__)
CHIP_DESELECT
#endif
_delay_us(3); //Max release enable time according to the Datasheet
if (_readStat1() == 255)
return false;
return true;
}
/************************************************************************************************************************
These functions are deprecated to enable compatibility with various AVR chips. They can be used by uncommenting
this block of code. However, be warned, this particular block of code has only been tested with the Arduino
IDE (1.6.5) and only with 8-bit AVR based Arduino boards and is not supported from library version 2.0 onward
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// These functions work with data to/from Serial stream. //
// Declares Serial.begin() if not previously declared. //
// Initiates Serial at 115200 baud. //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
This function is deprecated to enable compatibility with various AVR chips. It can be used by uncommenting
this block of code. However, be warned, this particular block of code has only been tested with the Arduino
IDE and only with 8-bit AVR based Arduino boards.
//Prints hex/dec formatted data from page reads - for debugging
void SPIFlash::_printPageBytes(uint8_t *data_buffer, uint8_t outputType) {
char buffer[10];
for (int a = 0; a < 16; ++a) {
for (int b = 0; b < 16; ++b) {
if (outputType == 1) {
sprintf(buffer, "%02x", data_buffer[a * 16 + b]);
Serial.print(buffer);
}
else if (outputType == 2) {
uint8_t x = data_buffer[a * 16 + b];
if (x < 10) Serial.print("0");
if (x < 100) Serial.print("0");
Serial.print(x);
Serial.print(',');
}
}
Serial.println();
}
}
//Reads a string from Serial
bool SPIFlash::readSerialStr(String &inputStr) {
if(!Serial)
Serial.begin(115200);
while (Serial.available()) {
inputStr = Serial.readStringUntil('\n');
return true;
}
return false;
}
//Reads a page of data and prints it to Serial stream. Make sure the sizeOf(uint8_t data_buffer[]) == 256.
void SPIFlash::printPage(uint16_t page_number, uint8_t outputType, bool fastRead) {
if(!Serial)
Serial.begin(115200);
char buffer[24];
sprintf(buffer, "Reading page (%04x)", page_number);
Serial.println(buffer);
uint8_t data_buffer[256];
readPage(page_number, data_buffer, fastRead);
_printPageBytes(data_buffer, outputType);
}
//Reads all pages on Flash chip and dumps it to Serial stream.
//This function is useful when extracting data from a flash chip onto a computer as a text file.
void SPIFlash::printAllPages(uint8_t outputType, bool fastRead) {
if(!Serial)
Serial.begin(115200);
Serial.println("Reading all pages");
uint8_t data_buffer[256];
for (int a = 0; a < maxPage; a++) {
readPage(a, data_buffer, fastRead);
_printPageBytes(data_buffer, outputType);
}
}
************************************************************************************************************************/