/* 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);
  }
}
************************************************************************************************************************/
