diff --git a/.vscode/settings.json b/.vscode/settings.json index 3a12360..5983f7c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,9 @@ "tuple": "cpp", "type_traits": "cpp", "vector": "cpp", - "string": "cpp" + "string": "cpp", + "__string": "cpp", + "string_view": "cpp", + "__split_buffer": "cpp" } } \ No newline at end of file diff --git a/Makefile b/Makefile index 5af8cfb..0b940c6 100644 --- a/Makefile +++ b/Makefile @@ -645,6 +645,14 @@ OBJECTS += ./src/drivers/MPU6000.o OBJECTS += ./src/drivers/MS5611.o OBJECTS += ./src/drivers/stepper.o OBJECTS += ./src/drivers/servo.o +OBJECTS += ./src/drivers/SerialFlash/M25PDevice.o +OBJECTS += ./src/drivers/SerialFlash/M25PDeviceImpl.o +OBJECTS += ./src/drivers/SerialFlash/MbedSPICommand.o +OBJECTS += ./src/drivers/SerialFlash/SerialFlashDeviceCreator.o +OBJECTS += ./src/drivers/SerialFlash/SST25Device.o +OBJECTS += ./src/drivers/SerialFlash/SST25DeviceImpl.o +OBJECTS += ./src/drivers/M25P16.o +OBJECTS += ./src/drivers/SpiFlash25.o OBJECTS += ./src/control/ImuFusion.o OBJECTS += ./src/control/PID.o OBJECTS += ./src/math/Utilities.o diff --git a/img/Revolution Schematic.pdf b/img/Revolution Schematic.pdf index 63be396..2be7e15 100644 Binary files a/img/Revolution Schematic.pdf and b/img/Revolution Schematic.pdf differ diff --git a/main.cpp b/main.cpp index c4cd8fa..95565d8 100644 --- a/main.cpp +++ b/main.cpp @@ -14,10 +14,15 @@ #include "src/drivers/MPU6000.h" #include "src/drivers/stepper.h" #include "src/drivers/servo.h" +#include "src/drivers/M25P16.h" // Control #include "src/control/lpf.h" #include "src/control/PID.h" #include "src/control/ImuFusion.h" +// Flash Memory - TODO: This component is not correctly implemented yet +#include "src/drivers/SerialFlash/MbedSPICommand.h" +#include "src/drivers/SerialFlash/SerialFlashDeviceCreator.h" +#include "src/drivers/SpiFlash25.h" #define WHEEL_SIZE 0.09f @@ -32,8 +37,14 @@ EventQueue queue; Serial serialMotorOutputs(PA_2, PA_3, 115200); // MPU setup -SPI spi(PA_7, PA_6, PA_5); //define the SPI (mosi, miso, sclk). Default frequency is 1Mhz -mpu6000_spi imu(spi,PA_4); //define the mpu6000 object +SPI spiImu(PA_7, PA_6, PA_5); //define the SPI (mosi, miso, sclk). Default frequency is 1Mhz +mpu6000_spi imu(spiImu,PA_4); //define the mpu6000 object + +// Flash setup +//SPI spiFlash(PC_12, PC_11, PC_10, PA_15); +//SPI spiFlash(PC_12, PC_11, PC_10, PB_3); +//M25P16 memory(spiFlash, PB_3); +//DigitalOut radioCS(PA_15); PwmOut ledBlue(D4); DigitalOut ledOrg(D5); @@ -48,7 +59,7 @@ InterruptIn gyroINT(PC_4); Timer timer; -// TODO: Figure out some good values +// Throttle PI Controller with some acceptable values controllerPI throttleControl(0.0025, 0.01, 5, 0); // 0.065, 0.05, 12, 40 // TODO: Figure out some good values @@ -174,6 +185,74 @@ void pulseLedContext() } } +void testFlash() +{ + ISPICommand* pSPICommand = new MbedSPICommand(PC_12, PC_11, PC_10, PB_3); // MOSI, MISO, SCK, CS + ISerialFlashDevice* pDevice = SerialFlashDeviceCreator::Create(pSPICommand); + + if (pDevice == NULL) + { + printf("Unsupported device"); + return; + } + + // std::string deviceName = pDevice->GetDeviceName(); + // printf("%s\n", deviceName); + int capacity = pDevice->GetCapacity() / 1024; + printf("%dK x 8\n", capacity); + + // int *a[3]; + // *a[0] = 1; + // *a[1] = 2; + // *a[2] = 3; + + // int *b[3]; + + int result; + + // result = pDevice->Write(100, a, sizeof(a)); + + // result = pDevice->Read(100, b, sizeof(b)); + + uint8_t c(-1), d(0); + + result = pDevice->Read(200, &c, sizeof(c)); + + wait(1); + + c += 4; + + result = pDevice->Write(200, &c, sizeof(c)); + + wait(1); + + result = pDevice->Read(200, &d, sizeof(d)); + +} + +void testFlash2() +{ + SpiFlash25 flash(PC_12, PC_11, PC_10, PB_3); + + char* k = flash.read_id(); + + char f = flash.read_status(); + + char a[] = "Hej och ha"; + + bool result; + + result = flash.write(0, strlen(a), a); + + char b[20]; + + result = flash.read(0, strlen(a), b); + + printf("result"); + + +} + // main() runs in its own thread int main() { @@ -182,6 +261,49 @@ int main() { Thread serialThread; serialThread.start(callback(&readSerialContext)); + // Init memory + DigitalOut radioCS(PA_15); + radioCS = 1; // Disable radio chip. May not be necessary + //SPI spiFlash(PC_12, PC_11, PC_10); + + testFlash2(); + + // M25P16 memory(spiFlash, PB_3); + // bool memoryUp = memory.init(); + + //printf("Initialization of onboard memory " + memoryUp ? "succeded" : "failed"); + + //memory.chipErase(); + int start = 0; + + int values[3]; + //memory.read(start, &values, sizeof(values)); + + + values[0]++; + //values[1]++; + values[2]++; + + //memory.write(start, values, sizeof(values)); + + + // int values2[3]; + // memory.read(start, values2, sizeof(values2)); + + // memory.read(0, values2, sizeof(values2)); + + // values[0]++; + // values[1]++; + // values[2]++; + + // start += sizeof(values); + // memory.write(0, values, sizeof(values)); + + // memory.read(0, values2, sizeof(values2)); + + + + // MPU startup at 100hz if(imu.init(10,BITS_DLPF_CFG_188HZ)){ printf("\nCouldn't initialize MPU6000 via SPI!"); diff --git a/src/drivers/M25P16.cpp b/src/drivers/M25P16.cpp new file mode 100644 index 0000000..36ceaa7 --- /dev/null +++ b/src/drivers/M25P16.cpp @@ -0,0 +1,259 @@ +#include "M25P16.h" + +namespace drivers { + +M25P16::M25P16(SPI& spi, PinName cs) +: m_spi(spi) +, m_cs(cs) +, m_property() +{ + m_property.deviceName = "M25P16"; + m_property.capacity = 16384 * 1024 / 8; + m_property.blockProtectionMask = 0xe3; +} + +bool M25P16::init() +{ + //clearBlockProtection(); + + int manufacturerId; + int memoryType; + int memoryCapacity; + + readId(manufacturerId, memoryType, memoryCapacity); + + clearBlockProtection(); + + // If we get the expected values from the bus then we are good + return manufacturerId == 0x20 + && memoryType == 0x20 + && memoryCapacity == 0x15; +} + +void M25P16::readId(int& manufacturerId, int& memoryType, int& memoryCapacity) +{ + m_spi.frequency(46000000); + + unsigned char id[3]; + readSPI(0x9f, id, sizeof(id)); + manufacturerId = id[0]; + memoryType = id[1]; + memoryCapacity = id[2]; +} + +int M25P16::read(int address, void* buffer, int length) +{ + if (address >= getCapacity()) + { + return 0; + } + if (address + length > getCapacity()) + { + length = getCapacity() - address; + } + if (length == 0) + { + return 0; + } + + char param[] = {0, 0, 0, 0xff}; + fillAddress(param, address); + readSPI(0x0b, param, sizeof(param), buffer, length); + return length; +} + +void M25P16::fillAddress(char* pBuffer, int address) +{ + *(pBuffer + 0) = (address & 0xff0000) >> 16; + *(pBuffer + 1) = (address & 0x00ff00) >> 8; + *(pBuffer + 2) = (address & 0x0000ff); +} + +int M25P16::write(int address, const void* buffer, int length) +{ + if (address >= getCapacity()) + { + return 0; + } + if (address + length > getCapacity()) + { + length = getCapacity() - address; + } + if (length == 0) + { + return 0; + } + + int result = length; + + const int pageSize = 256; + const int pageSizeMask = pageSize - 1; + const int pageAddressMask = ~pageSizeMask; + + const unsigned char* p = static_cast(buffer); + if ((address & pageSizeMask) != 0) + { + int readLen = address & pageSizeMask; + int copyLen = pageSize - readLen; + if (copyLen > length) + { + copyLen = length; + } + char buf[pageSize]; + int writeAddress = address & pageAddressMask; + read(writeAddress, buf, readLen); + memcpy(&buf[address & pageSizeMask], buffer, copyLen); + pageProgram(writeAddress, buf); + p += readLen; + address += pageSize - readLen; + length -= copyLen; + } + while (length >= pageSize) + { + pageProgram(address, p); + address += pageSize; + p += pageSize; + length -= pageSize; + } + if (length != 0) + { + char buf[pageSize]; + memcpy(buf, p, length); + memset(&buf[length], 0xff, pageSize - length); + pageProgram(address, buf); + } + return result; +} + +void M25P16::select() +{ + m_cs = 0; +} + +void M25P16::deselect() +{ + m_cs = 1; +} + +unsigned int M25P16::whoami() +{ + return 0; +} + +int M25P16::getCapacity() const +{ + return m_property.capacity; +} + +std::string M25P16::getDeviceName() const +{ + return m_property.deviceName; +} + +int M25P16::readStatusRegister(void) +{ + unsigned char status; + readSPI(0x05, &status, 1); + return status; +} + +void M25P16::writeStatusRegister(int value) +{ + char vb = static_cast(value); + writeSPI(0x01, &vb, 1); +} + +void M25P16::writeEnable() +{ + writeSPI(0x06); +} + +void M25P16::writeDisable() +{ + writeSPI(0x04); +} + +void M25P16::clearBlockProtection() +{ + writeEnable(); + + int status = readStatusRegister(); + status &= m_property.blockProtectionMask; + writeStatusRegister(status); +} + +void M25P16::writeSPI(int command) +{ + m_cs = 0; + m_spi.write(command); + m_cs = 1; +} + +void M25P16::writeSPI(int command, const void* pParam, int length) +{ + m_cs = 0; + m_spi.write(command); + + const char*p = static_cast(pParam); + for (int i = 0; i < length; ++i) + { + m_spi.write(*p++); + } + m_cs = 1; +} + +void M25P16::readSPI(int command, void* pRead, int readLength) +{ + m_cs = 0; + m_spi.write(command); + + char* pReadByte = static_cast(pRead); + for (int i = 0; i < readLength; ++i) + { + *pReadByte++ = m_spi.write(0xff); + } + m_cs = 1; +} + +void M25P16::readSPI(int command, const void* pParam, int paramLength, void* pReaded, int readLength) +{ + m_cs = 0; + m_spi.write(command); + + const char* pParamByte = static_cast(pParam); + for (int i = 0; i < paramLength; ++i) + { + m_spi.write(*pParamByte++); + } + char* pReadedByte = static_cast(pReaded); + for (int i = 0; i < readLength; ++i) + { + *pReadedByte++ = m_spi.write(0xff); + } + m_cs = 1; +} + +void M25P16::chipErase() +{ + writeEnable(); + + writeSPI(0xc7); + + while (readStatusRegister() & 0x01); + + clearBlockProtection(); +} + +void M25P16::pageProgram(int address, const void* buffer) +{ + writeEnable(); + + char param[256 + 3]; + fillAddress(param, address); + memcpy(param + 3, buffer, 256); + writeSPI(0x02, param, sizeof(param)); + + while (readStatusRegister() & 0x01); +} + +} // namespace drivers \ No newline at end of file diff --git a/src/drivers/M25P16.h b/src/drivers/M25P16.h new file mode 100644 index 0000000..c06a5be --- /dev/null +++ b/src/drivers/M25P16.h @@ -0,0 +1,74 @@ +#include "mbed.h" + +#include + +#pragma once + +namespace drivers { + +class M25P16 { + +public: + + M25P16(SPI& spi, PinName cs); + + bool init(); + + int read(int address, void* buffer, int length); + + int write(int address, const void* buffer, int length); + + void chipErase(); + + unsigned int whoami(); + +private: + + SPI& m_spi; + DigitalOut m_cs; + + struct DeviceProperty + { + std::string deviceName; + int capacity; + int blockProtectionMask; + } m_property; + + // Returns the size of the memory + int getCapacity() const; + + void readId(int& manufacturerId, int& memoryType, int& memoryCapacity); + + std::string getDeviceName() const; + + void fillAddress(char* pBuffer, int address); + + int readStatusRegister(); + + void writeStatusRegister(int value); + + void writeEnable(); + + void writeDisable(); + + void clearBlockProtection(); + + void select(); + + void deselect(); + + void writeSPI(int command); + + void writeSPI(int command, const void* pParam, int length); + + void readSPI(int command, void* pRead, int readLength); + + void readSPI(int command, const void* pParam, int paramLength, void* pReaded, int readLength); + + void pageProgram(int address, const void* buffer); + +}; + +#define MYBIT 0x00 + +} // namespace drivers \ No newline at end of file diff --git a/src/drivers/SPICommand/main.cpp b/src/drivers/SPICommand/main.cpp new file mode 100644 index 0000000..84e054f --- /dev/null +++ b/src/drivers/SPICommand/main.cpp @@ -0,0 +1,26 @@ +#include "mbed.h" +#include "MbedSPICommand.h" +#include "SerialFlashDeviceCreator.h" + +DigitalOut myled(LED1); + +int main() +{ + ISPICommand* pSPICommand = new MbedSPICommand(p11, p12, p13, p14); // MOSI, MISO, SCK, CS + + ISerialFlashDevice* pDevice = SerialFlashDeviceCreator::Create(pSPICommand); + if (pDevice == NULL) + { + printf("Unsupported device"); + return; + } + printf("%s\n", pDevice->GetDeviceName()); + printf("%dK x 8\n", pDevice->GetCapacity() / 1024); + + while(1) { + myled = 1; + wait(0.2); + myled = 0; + wait(0.2); + } +} diff --git a/src/drivers/SPICommand/mbed.bld b/src/drivers/SPICommand/mbed.bld new file mode 100644 index 0000000..e5e55f2 --- /dev/null +++ b/src/drivers/SPICommand/mbed.bld @@ -0,0 +1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/6473597d706e \ No newline at end of file diff --git a/src/drivers/SerialFlash/ISPICommand.h b/src/drivers/SerialFlash/ISPICommand.h new file mode 100644 index 0000000..86225e1 --- /dev/null +++ b/src/drivers/SerialFlash/ISPICommand.h @@ -0,0 +1,14 @@ +#pragma once + +class ISPICommand +{ +public: + + virtual ~ISPICommand() {} + virtual void SetMaxFrequency(int frequency) = 0; + + virtual void Write(int command) = 0; + virtual void Write(int command, const void* pParam, int length) = 0; + virtual void Read(int command, void* pReaded, int readLength) = 0; + virtual void Read(int command, const void* pParam, int paramLength, void* pReaded, int readLength) = 0; +}; diff --git a/src/drivers/SerialFlash/ISerialFlashDevice.h b/src/drivers/SerialFlash/ISerialFlashDevice.h new file mode 100644 index 0000000..34a5b6b --- /dev/null +++ b/src/drivers/SerialFlash/ISerialFlashDevice.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class ISerialFlashDevice +{ +public: + virtual ~ISerialFlashDevice(void) { } + virtual std::string GetDeviceName(void) const = 0; + virtual int GetCapacity(void) const = 0; + virtual void ChipErase(void) = 0; + virtual int Read(int address, void* buffer, int length) = 0; + virtual int Write(int address, const void* buffer, int length) = 0; +}; diff --git a/src/drivers/SerialFlash/M25PDevice.cpp b/src/drivers/SerialFlash/M25PDevice.cpp new file mode 100644 index 0000000..9a39c87 --- /dev/null +++ b/src/drivers/SerialFlash/M25PDevice.cpp @@ -0,0 +1,50 @@ +#include "mbed.h" +#include "M25PDeviceImpl.h" +#include "M25PDevice.h" + +bool M25PDevice::IsSupported(ISPICommand* pSPICommand) +{ + return M25PDeviceImpl::IsSupported(pSPICommand); +} + +M25PDevice* M25PDevice::Create(ISPICommand* pSPICommand) +{ + M25PDeviceImpl* pImpl = M25PDeviceImpl::Create(pSPICommand); + if (pImpl == NULL) + { + return NULL; + } + return new M25PDevice(pImpl); +} + + +M25PDevice::M25PDevice(M25PDeviceImpl* pImpl) + : _pImpl(pImpl) +{ + +} + +string M25PDevice::GetDeviceName(void) const +{ + return _pImpl->GetDeviceName(); +} + +int M25PDevice::GetCapacity(void) const +{ + return _pImpl->GetCapacity(); +} + +void M25PDevice::ChipErase(void) +{ + _pImpl->BulkErase(); +} + +int M25PDevice::Read(int address, void* buffer, int length) +{ + return _pImpl->Read(address, buffer, length); +} + +int M25PDevice::Write(int address, const void* buffer, int length) +{ + return _pImpl->Write(address, buffer, length); +} diff --git a/src/drivers/SerialFlash/M25PDevice.h b/src/drivers/SerialFlash/M25PDevice.h new file mode 100644 index 0000000..8629a53 --- /dev/null +++ b/src/drivers/SerialFlash/M25PDevice.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "ISerialFlashDevice.h" + +class ISPICommand; +class M25PDeviceImpl; + +class M25PDevice : public ISerialFlashDevice +{ +public: + static M25PDevice* Create(ISPICommand* pSPICommand); + static bool IsSupported(ISPICommand* pSPICommand); + + virtual std::string GetDeviceName(void) const; + virtual int GetCapacity(void) const; + virtual void ChipErase(void); + virtual int Read(int address, void* buffer, int length); + virtual int Write(int address, const void* buffer, int length); + +private: + std::auto_ptr _pImpl; + M25PDevice(M25PDeviceImpl* pImpl); +}; diff --git a/src/drivers/SerialFlash/M25PDeviceImpl.cpp b/src/drivers/SerialFlash/M25PDeviceImpl.cpp new file mode 100644 index 0000000..40f3a3b --- /dev/null +++ b/src/drivers/SerialFlash/M25PDeviceImpl.cpp @@ -0,0 +1,235 @@ +#include "ISPICommand.h" +#include +#include "M25PDeviceImpl.h" + +const M25PDeviceImpl::DeviceProperty* M25PDeviceImpl::findMatchDevice(ISPICommand* pSPICommand) +{ + int manufacturerId; + int memoryType; + int memoryCapacity; + readId(pSPICommand, manufacturerId, memoryType, memoryCapacity); + + struct SupportedDevice + { + int manufacturerId; + int memoryType; + int memoryCapacity; + DeviceProperty property; + } static const supportedDevices[] = + { + //M25P16 16MBit (2MB) + { 0x20, 0x20, 0x15, + { + "M25P16", + 16384 * 1024 / 8, + 0xe3, + }, + }, + //M25P80 8MBit (1MB) + { 0x20, 0x20, 0x14, + { + "M25P80", + 8192 * 1024 / 8, + 0xe3, + }, + }, + }; + int count = sizeof(supportedDevices) / sizeof(supportedDevices[0]); + for (int i = 0; i < count; ++i) + { + const SupportedDevice& device = supportedDevices[i]; + if (device.manufacturerId == manufacturerId && device.memoryType == memoryType && device.memoryCapacity == memoryCapacity) + { + return &device.property; + } + } + return NULL; +} + +bool M25PDeviceImpl::IsSupported(ISPICommand* pSPICommand) +{ + return findMatchDevice(pSPICommand) != NULL; +} + +M25PDeviceImpl* M25PDeviceImpl::Create(ISPICommand* pSPICommand) +{ + const DeviceProperty* property = findMatchDevice(pSPICommand); + if (property == NULL) + { + return NULL; + } + return new M25PDeviceImpl(pSPICommand, *property); +} + +M25PDeviceImpl::M25PDeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property) + : _pSPICommand(pSPICommand) + , _property(property) +{ + clearBlockProtection(); +} + +M25PDeviceImpl::~M25PDeviceImpl(void) +{ + +} + +void M25PDeviceImpl::readId(ISPICommand* pSPICommand, int& manufacturerId, int& memoryType, int& memoryCapacity) +{ + const static int DefaultOperationFrequency = 20000000; + pSPICommand->SetMaxFrequency(DefaultOperationFrequency); + + unsigned char id[3]; + pSPICommand->Read(0x9f, id, sizeof(id)); + manufacturerId = id[0]; + memoryType = id[1]; + memoryCapacity = id[2]; +} + +int M25PDeviceImpl::readStatusRegister(void) +{ + unsigned char status; + _pSPICommand->Read(0x05, &status, 1); + return status; +} + +void M25PDeviceImpl::writeStatusRegister(int value) +{ + char vb = static_cast(value); + _pSPICommand->Write(0x01, &vb, 1); +} + +void M25PDeviceImpl::writeEnable() +{ + _pSPICommand->Write(0x06); +} + +void M25PDeviceImpl::writeDisable() +{ + _pSPICommand->Write(0x04); +} + +void M25PDeviceImpl::clearBlockProtection(void) +{ + writeEnable(); + + int status = readStatusRegister(); + status &= _property.blockProtectionMask; + writeStatusRegister(status); +} + +std::string M25PDeviceImpl::GetDeviceName(void) const +{ + return _property.deviceName; +} + +int M25PDeviceImpl::GetCapacity(void) const +{ + return _property.capacity; +} + +int M25PDeviceImpl::Read(int address, void* buffer, int length) +{ + if (address >= GetCapacity()) + { + return 0; + } + if (address + length > GetCapacity()) + { + length = GetCapacity() - address; + } + if (length == 0) + { + return 0; + } + + char param[] = { 0, 0, 0, 0xff }; + fillAddress(param, address); + _pSPICommand->Read(0x0b, param, sizeof(param), buffer, length); + return length; +} + +void M25PDeviceImpl::fillAddress(char* pBuffer, int address) +{ + *(pBuffer + 0) = (address & 0xff0000) >> 16; + *(pBuffer + 1) = (address & 0x00ff00) >> 8; + *(pBuffer + 2) = (address & 0x0000ff); +} + +int M25PDeviceImpl::Write(int address, const void* buffer, int length) +{ + if (address >= GetCapacity()) + { + return 0; + } + if (address + length > GetCapacity()) + { + length = GetCapacity() - address; + } + if (length == 0) + { + return 0; + } + + int result = length; + + const int pageSize = 256; + const int pageSizeMask = pageSize - 1; + const int pageAddressMask = ~pageSizeMask; + + const unsigned char* p = static_cast(buffer); + if ((address & pageSizeMask) != 0) + { + int readLen = address & pageSizeMask; + int copyLen = pageSize - readLen; + if (copyLen > length) + { + copyLen = length; + } + char buf[pageSize]; + int writeAddress = address & pageAddressMask; + Read(writeAddress, buf, readLen); + memcpy(&buf[address & pageSizeMask], buffer, copyLen); + pageProgram(writeAddress, buf); + p += readLen; + address += pageSize - readLen; + length -= copyLen; + } + while (length >= pageSize) + { + pageProgram(address, p); + address += pageSize; + p += pageSize; + length -= pageSize; + } + if (length != 0) + { + char buf[pageSize]; + memcpy(buf, p, length); + memset(&buf[length], 0xff, pageSize - length); + pageProgram(address, buf); + } + return result; +} + +void M25PDeviceImpl::BulkErase() +{ + writeEnable(); + + _pSPICommand->Write(0xc7); + + while (readStatusRegister() & 0x01); + + clearBlockProtection(); +} + +void M25PDeviceImpl::pageProgram(int address, const void* buffer) +{ + writeEnable(); + + char param[256 + 3]; + fillAddress(param, address); + memcpy(param + 3, buffer, 256); + _pSPICommand->Write(0x02, param, sizeof(param)); + + while (readStatusRegister() & 0x01); +} diff --git a/src/drivers/SerialFlash/M25PDeviceImpl.h b/src/drivers/SerialFlash/M25PDeviceImpl.h new file mode 100644 index 0000000..e184f5b --- /dev/null +++ b/src/drivers/SerialFlash/M25PDeviceImpl.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +class ISPICommand; + +class M25PDeviceImpl +{ +public: + static bool IsSupported(ISPICommand* pSPICommand); + static M25PDeviceImpl* Create(ISPICommand* pSPICommand); + ~M25PDeviceImpl(void); + + std::string GetDeviceName() const; + int GetCapacity() const; + int Read(int address, void* buffer, int length); + int Write(int address, const void* buffer, int length); + void BulkErase(void); + +private: + ISPICommand* _pSPICommand; + int _operationFrequency; + struct DeviceProperty + { + std::string deviceName; + int capacity; + int blockProtectionMask; + } const& _property; + static const DeviceProperty* findMatchDevice(ISPICommand* pSPI); + + M25PDeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property); + static void readId(ISPICommand* pSPICommand, int& manufacturerId, int& memoryType, int& memoryCapacity); + static void fillAddress(char* pBuffer, int address); + + int readStatusRegister(void); + void clearBlockProtection(void); + void writeStatusRegister(int value); + void writeEnable(void); + void writeDisable(void); + void pageProgram(int address, const void* buffer); +}; diff --git a/src/drivers/SerialFlash/MbedSPICommand.cpp b/src/drivers/SerialFlash/MbedSPICommand.cpp new file mode 100644 index 0000000..da7acee --- /dev/null +++ b/src/drivers/SerialFlash/MbedSPICommand.cpp @@ -0,0 +1,71 @@ +#include "MbedSPICommand.h" + +MbedSPICommand::MbedSPICommand(PinName mosi, PinName miso, PinName sck, PinName cs) + : _spi(mosi, miso, sck) + , _cs(cs) +{ + //CS inactive + _cs = 1; +} + +void MbedSPICommand::SetMaxFrequency(int frequency) +{ + static const int LPC1768MaxFrequency = 46000000; + + if (frequency > LPC1768MaxFrequency) + { + frequency = LPC1768MaxFrequency; + } + _spi.frequency(frequency); +} + +void MbedSPICommand::Write(int command) +{ + _cs = 0; + _spi.write(command); + _cs = 1; +} + +void MbedSPICommand::Write(int command, const void* pParam, int length) +{ + _cs = 0; + _spi.write(command); + + const char*p = static_cast(pParam); + for (int i = 0; i < length; ++i) + { + _spi.write(*p++); + } + _cs = 1; +} + +void MbedSPICommand::Read(int command, void* pReaded, int readLength) +{ + _cs = 0; + _spi.write(command); + + char* pReadedByte = static_cast(pReaded); + for (int i = 0; i < readLength; ++i) + { + *pReadedByte++ = _spi.write(0xff); + } + _cs = 1; +} + +void MbedSPICommand::Read(int command, const void* pParam, int paramLength, void* pReaded, int readLength) +{ + _cs = 0; + _spi.write(command); + + const char* pParamByte = static_cast(pParam); + for (int i = 0; i < paramLength; ++i) + { + _spi.write(*pParamByte++); + } + char* pReadedByte = static_cast(pReaded); + for (int i = 0; i < readLength; ++i) + { + *pReadedByte++ = _spi.write(0xff); + } + _cs = 1; +} diff --git a/src/drivers/SerialFlash/MbedSPICommand.h b/src/drivers/SerialFlash/MbedSPICommand.h new file mode 100644 index 0000000..36e7db8 --- /dev/null +++ b/src/drivers/SerialFlash/MbedSPICommand.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ISPICommand.h" +#include "mbed.h" + +class MbedSPICommand : public ISPICommand +{ +public: + MbedSPICommand(PinName mosi, PinName miso, PinName sck, PinName cs); + + virtual void SetMaxFrequency(int frequency); + + virtual void Write(int command); + virtual void Write(int command, const void* pParam, int length); + virtual void Read(int command, void* pReaded, int readLength); + virtual void Read(int command, const void* pParam, int paramLength, void* pReaded, int readLength); + +private: + SPI _spi; + DigitalOut _cs; +}; + diff --git a/src/drivers/SerialFlash/SST25Device.cpp b/src/drivers/SerialFlash/SST25Device.cpp new file mode 100644 index 0000000..5480efb --- /dev/null +++ b/src/drivers/SerialFlash/SST25Device.cpp @@ -0,0 +1,50 @@ +#include "mbed.h" +#include "SST25DeviceImpl.h" +#include "SST25Device.h" + +bool SST25Device::IsSupported(ISPICommand* pSPICommand) +{ + return SST25DeviceImpl::IsSupported(pSPICommand); +} + +SST25Device* SST25Device::Create(ISPICommand* pSPICommand) +{ + SST25DeviceImpl* pImpl = SST25DeviceImpl::Create(pSPICommand); + if (pImpl == NULL) + { + return NULL; + } + return new SST25Device(pImpl); +} + + +SST25Device::SST25Device(SST25DeviceImpl* pImpl) + : _pImpl(pImpl) +{ + +} + +string SST25Device::GetDeviceName(void) const +{ + return _pImpl->GetDeviceName(); +} + +int SST25Device::GetCapacity(void) const +{ + return _pImpl->GetCapacity(); +} + +void SST25Device::ChipErase(void) +{ + _pImpl->ChipErase(); +} + +int SST25Device::Read(int address, void* buffer, int length) +{ + return _pImpl->Read(address, buffer, length); +} + +int SST25Device::Write(int address, const void* buffer, int length) +{ + return _pImpl->Write(address, buffer, length); +} diff --git a/src/drivers/SerialFlash/SST25Device.h b/src/drivers/SerialFlash/SST25Device.h new file mode 100644 index 0000000..8f436f7 --- /dev/null +++ b/src/drivers/SerialFlash/SST25Device.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "ISerialFlashDevice.h" + +class ISPICommand; +class SST25DeviceImpl; + +class SST25Device : public ISerialFlashDevice +{ +public: + static SST25Device* Create(ISPICommand* pSPICommand); + static bool IsSupported(ISPICommand* pSPICommand); + + virtual std::string GetDeviceName(void) const; + virtual int GetCapacity(void) const; + virtual void ChipErase(void); + virtual int Read(int address, void* buffer, int length); + virtual int Write(int address, const void* buffer, int length); + +private: + std::auto_ptr _pImpl; + SST25Device(SST25DeviceImpl* pImpl); +}; diff --git a/src/drivers/SerialFlash/SST25DeviceImpl.cpp b/src/drivers/SerialFlash/SST25DeviceImpl.cpp new file mode 100644 index 0000000..27bc16a --- /dev/null +++ b/src/drivers/SerialFlash/SST25DeviceImpl.cpp @@ -0,0 +1,444 @@ +#include "ISPICommand.h" +#include +#include "SST25DeviceImpl.h" + +const SST25DeviceImpl::DeviceProperty* SST25DeviceImpl::findMatchDevice(ISPICommand* pSPICommand) +{ + int manufacturersId; + int deviceId; + readId(pSPICommand, manufacturersId, deviceId); + + struct SupportedDevice + { + int manufacturersId; + int deviceId; + DeviceProperty property; + } static const supportedDevices[] = + { + //SST25xF512A 512KBit (64KByte) + { 0xbf, 0x48, + { + "SST25xF512A", + 512 * 1024 / 8, + 0xf3, + 1, + &SST25DeviceImpl::writeAAI, + 33000000 + }, + }, + //SST25xF010A 1MBit (128KByte) + { 0xbf, 0x49, + { + "SST25xF010A", + 1024 * 1024 / 8, + 0xf3, + 1, + &SST25DeviceImpl::writeAAI, + 33000000 + }, + }, + //SST25xF020A 2MBit (256KByte) + { 0xbf, 0x43, + { + "SST25xF020A", + 2048 * 1024 / 8, + 0xf3, + 1, + &SST25DeviceImpl::writeAAI, + 33000000 + }, + }, + //SST25xF040B 4MBit (512KByte) + { 0xbf, 0x8d, + { + "SST25xF040B", + 4096 * 1024 / 8, + 0xe3, + 1, + &SST25DeviceImpl::writeAAIWord, + 50000000 + }, + }, + //SST25xF080B 8MBit (1MByte) + { 0xbf, 0x8e, + { + "SST25xF080B", + 8192 * 1024 / 8, + 0xe3, + 1, + &SST25DeviceImpl::writeAAIWord, + 50000000 + }, + }, + //SST25xF016B 16MBit (2MByte) + { 0xbf, 0x41, + { + "SST25xF016B", + 16384 * 1024 / 8, + 0xe3, + 1, + &SST25DeviceImpl::writeAAIWord, + 50000000 + }, + }, + //SST25xF032B 32MBit (4MByte) + { 0xbf, 0x4a, + { + "SST25xF032B", + 32768 * 1024 / 8, + 0xc3, + 1, + &SST25DeviceImpl::writeAAIWord, + 50000000 + }, + }, + //SST25xF064C 64MBit (8MByte) + { 0xbf, 0x4b, + { + "SST25xF064C", + 65536 * 1024 / 8, + 0xc3, + 256, + &SST25DeviceImpl::writePage, + 50000000 + }, + } + }; + int count = sizeof(supportedDevices) / sizeof(supportedDevices[0]); + for (int i = 0; i < count; ++i) + { + const SupportedDevice& device = supportedDevices[i]; + if (device.manufacturersId == manufacturersId && device.deviceId == deviceId) + { + return &device.property; + } + } + return NULL; +} + +bool SST25DeviceImpl::IsSupported(ISPICommand* pSPICommand) +{ + return findMatchDevice(pSPICommand) != NULL; +} + +SST25DeviceImpl* SST25DeviceImpl::Create(ISPICommand* pSPICommand) +{ + const DeviceProperty* property = findMatchDevice(pSPICommand); + if (property == NULL) + { + return NULL; + } + return new SST25DeviceImpl(pSPICommand, *property); +} + +SST25DeviceImpl::SST25DeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property) + : _pSPICommand(pSPICommand) + , _property(property) +{ + _pSPICommand->SetMaxFrequency(_property.operationClkHz); +} + +SST25DeviceImpl::~SST25DeviceImpl(void) +{ + +} + +void SST25DeviceImpl::readId(ISPICommand* pSPICommand, int& manufacturersId, int& deviceId) +{ + const static int DefaultOperationFrequency = 20000000; + pSPICommand->SetMaxFrequency(DefaultOperationFrequency); + + unsigned char read[2]; + char param[3]; + fillAddress(param, 0x000000); + pSPICommand->Read(0x90, param, sizeof(param), read, sizeof(read)); + manufacturersId = read[0]; + deviceId = read[1]; +} + +int SST25DeviceImpl::readStatusRegister(void) +{ + unsigned char read; + _pSPICommand->Read(0x05, &read, 1); + return read; +} + +void SST25DeviceImpl::writeStatusRegister(int value) +{ + _pSPICommand->Write(0x50); + + char vb = static_cast(value); + _pSPICommand->Write(0x01, &vb, sizeof(vb)); +} + +void SST25DeviceImpl::writeEnable() +{ + int status = readStatusRegister(); + status &= _property.blockProtectionMask; + writeStatusRegister(status); + _pSPICommand->Write(0x06); +} + +void SST25DeviceImpl::writeDisable() +{ + _pSPICommand->Write(0x04); +} + +void SST25DeviceImpl::waitForReady() +{ + while (readStatusRegister() & 0x01) + ; +} + +std::string SST25DeviceImpl::GetDeviceName(void) const +{ + return _property.deviceName; +} + +int SST25DeviceImpl::GetCapacity(void) const +{ + return _property.capacity; +} + +int SST25DeviceImpl::Read(int address, void* buffer, int length) +{ + if (address >= GetCapacity()) + { + return 0; + } + if (address + length > GetCapacity()) + { + length = GetCapacity() - address; + } + if (length == 0) + { + return 0; + } + + _pSPICommand->SetMaxFrequency(_property.operationClkHz); + + char param[] = { 0, 0, 0, 0xff }; + fillAddress(param, address); + _pSPICommand->Read(0x0b, param, sizeof(param), buffer, length); + return length; +} + +int SST25DeviceImpl::Write(int address, const void* buffer, int length) +{ + if (address >= GetCapacity()) + { + return 0; + } + if (address + length > GetCapacity()) + { + length = GetCapacity() - address; + } + if (length == 0) + { + return 0; + } + (this->*_property.pfnWriter)(address, buffer, length); + return length; +} + +void SST25DeviceImpl::ChipErase() +{ + _pSPICommand->SetMaxFrequency(_property.operationClkHz); + + writeEnable(); + _pSPICommand->Write(0x60); + waitForReady(); +} + +void SST25DeviceImpl::byteProgram(int address, int value) +{ + _pSPICommand->SetMaxFrequency(_property.operationClkHz); + + writeEnable(); + + char param[] = { 0, 0, 0, value }; + fillAddress(param, address); + _pSPICommand->Write(0x02, param, sizeof(param)); + + waitForReady(); +} + +void SST25DeviceImpl::beginAAIProgram(int address, int data) +{ + _pSPICommand->SetMaxFrequency(_property.operationClkHz); + + writeEnable(); + + char param[] = { 0, 0, 0, data }; + fillAddress(param, address); + _pSPICommand->Write(0xaf, param, sizeof(param)); + + waitForReady(); +} + +void SST25DeviceImpl::nextAAIProgram(int data) +{ + char param = static_cast(data); + _pSPICommand->Write(0xaf, ¶m, 1); + + waitForReady(); +} + +void SST25DeviceImpl::endAAIProgram(void) +{ + _pSPICommand->Write(0x04); + waitForReady(); +} + +void SST25DeviceImpl::beginAAIWordProgram(int address, int data) +{ + _pSPICommand->SetMaxFrequency(_property.operationClkHz); + + writeEnable(); + char param[] = { 0, 0, 0, (data & 0xff00) >> 8, data & 0xff }; + fillAddress(param, address); + _pSPICommand->Write(0xad, param, sizeof(param)); + waitForReady(); +} + +void SST25DeviceImpl::nextAAIWordProgram(int data) +{ + char param[] = { (data & 0xff00) >> 8, data & 0xff }; + _pSPICommand->Write(0xad, param, sizeof(param)); + waitForReady(); +} + +void SST25DeviceImpl::fillAddress(char* pBuffer, int address) +{ + *(pBuffer + 0) = (address & 0xff0000) >> 16; + *(pBuffer + 1) = (address & 0x00ff00) >> 8; + *(pBuffer + 2) = (address & 0x0000ff); +} + +void SST25DeviceImpl::pageProgram(int address, const void* buffer) +{ + _pSPICommand->SetMaxFrequency(_property.operationClkHz); + + writeEnable(); + + char param[256 + 3]; + fillAddress(param, address); + memcpy(param + 3, buffer, 256); + _pSPICommand->Write(0x02, param, sizeof(param)); + + waitForReady(); +} + +void SST25DeviceImpl::writePage(int address, const void* buffer, int length) +{ + int pageSize = _property.pageSize; + int pageSizeMask = pageSize - 1; + int pageAddressMask = ~pageSizeMask; + + if (length <= 0) + { + return; + } + const unsigned char* p = static_cast(buffer); + if ((address & pageSizeMask) != 0) + { + int readLen = address & pageSizeMask; + int copyLen = pageSize - readLen; + if (copyLen > length) + { + copyLen = length; + } + char buf[pageSize]; + int writeAddress = address & pageAddressMask; + Read(writeAddress, buf, readLen); + memcpy(&buf[address & pageSizeMask], buffer, copyLen); + pageProgram(writeAddress, buf); + p += readLen; + address += pageSize - readLen; + length -= copyLen; + } + while (length >= pageSize) + { + pageProgram(address, p); + address += pageSize; + p += pageSize; + length -= pageSize; + } + if (length != 0) + { + char buf[pageSize]; + memcpy(buf, p, length); + memset(&buf[length], 0xff, pageSize - length); + pageProgram(address, buf); + } +} + +void SST25DeviceImpl::writeBytes(int address, const void* buffer, int length) +{ + const unsigned char* p = static_cast(buffer); + while (length-- >= 0) + { + byteProgram(address++, *p++); + } +} + +void SST25DeviceImpl::writeAAI(int address, const void* buffer, int length) +{ + if (length <= 0) + { + return; + } + + const unsigned char* p = static_cast(buffer); + if (length < 2) + { + byteProgram(address, *p); + return; + } + + beginAAIProgram(address, *p++); + while (--length != 0) + { + nextAAIProgram(*p++); + } + endAAIProgram(); +} + +void SST25DeviceImpl::writeAAIWord(int address, const void* buffer, int length) +{ + if (length <= 0) + { + return; + } + + const unsigned char* p = static_cast(buffer); + if ((address & 0x1) != 0) + { + byteProgram(address++, *p++); + length--; + } + + if (length < 4) + { + writeBytes(address, p, length); + return; + } + + beginAAIWordProgram(address, (*p << 8) | *(p + 1)); + address += length & ~0x1; + p += 2; + length -= 2; + + do + { + nextAAIWordProgram((*p << 8) | *(p + 1)); + p += 2; + length -= 2; + } while (length >= 2); + endAAIProgram(); + + if (length != 0) + { + byteProgram(address, *p); + } +} diff --git a/src/drivers/SerialFlash/SST25DeviceImpl.h b/src/drivers/SerialFlash/SST25DeviceImpl.h new file mode 100644 index 0000000..99b03ee --- /dev/null +++ b/src/drivers/SerialFlash/SST25DeviceImpl.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +class ISPICommand; + +class SST25DeviceImpl +{ +public: + static bool IsSupported(ISPICommand* pSPICommand); + static SST25DeviceImpl* Create(ISPICommand* pSPICommand); + ~SST25DeviceImpl(void); + + std::string GetDeviceName() const; + int GetCapacity() const; + int Read(int address, void* buffer, int length); + int Write(int address, const void* buffer, int length); + void ChipErase(void); + +private: + ISPICommand* const _pSPICommand; + int _operationFrequency; + struct DeviceProperty + { + std::string deviceName; + int capacity; + int blockProtectionMask; + int pageSize; + void (SST25DeviceImpl::*pfnWriter)(int address, const void* buffer, int length); + + int operationClkHz; + } const& _property; + static const DeviceProperty* findMatchDevice(ISPICommand* pSPICommand); + + SST25DeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property); + static void readId(ISPICommand* pSPICommand, int& manufacturersId, int& deviceId); + static void fillAddress(char* pBuffer, int address); + + int readStatusRegister(void); + void writeStatusRegister(int value); + void writeEnable(void); + void writeDisable(void); + void waitForReady(void); + + void byteProgram(int address, int value); + void pageProgram(int address, const void* buffer); + void beginAAIProgram(int address, int data); + void nextAAIProgram(int data); + void endAAIProgram(void); + void beginAAIWordProgram(int address, int data); + void nextAAIWordProgram(int data); + + void writeBytes(int address, const void* buffer, int length); + void writeAAI(int address, const void* buffer, int length); + void writeAAIWord(int address, const void* buffer, int length); + void writePage(int address, const void* buffer, int length); +}; diff --git a/src/drivers/SerialFlash/SerialFlashDeviceCreator.cpp b/src/drivers/SerialFlash/SerialFlashDeviceCreator.cpp new file mode 100644 index 0000000..d398526 --- /dev/null +++ b/src/drivers/SerialFlash/SerialFlashDeviceCreator.cpp @@ -0,0 +1,17 @@ +#include "ISerialFlashDevice.h" +#include "SST25Device.h" +#include "M25PDevice.h" +#include "SerialFlashDeviceCreator.h" + +ISerialFlashDevice* SerialFlashDeviceCreator::Create(ISPICommand* pSPICommand) +{ + if (M25PDevice::IsSupported(pSPICommand)) + { + return M25PDevice::Create(pSPICommand); + } + if (SST25Device::IsSupported(pSPICommand)) + { + return SST25Device::Create(pSPICommand); + } + return NULL; +} diff --git a/src/drivers/SerialFlash/SerialFlashDeviceCreator.h b/src/drivers/SerialFlash/SerialFlashDeviceCreator.h new file mode 100644 index 0000000..d341a56 --- /dev/null +++ b/src/drivers/SerialFlash/SerialFlashDeviceCreator.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ISerialFlashDevice.h" + +class ISPICommand; + +class SerialFlashDeviceCreator +{ +public: + static ISerialFlashDevice* Create(ISPICommand* pSPICommand); +}; diff --git a/src/drivers/SpiFlash25.cpp b/src/drivers/SpiFlash25.cpp new file mode 100644 index 0000000..4fb448b --- /dev/null +++ b/src/drivers/SpiFlash25.cpp @@ -0,0 +1,189 @@ +/* Library for SPI flash 25* devices. + * Copyright (c) 2014 Multi-Tech Systems + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "SpiFlash25.h" + +SpiFlash25::SpiFlash25(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName W, PinName HOLD, int page_size) +: _spi(mosi, miso, sclk), + _cs(cs), + _page_size(page_size) +{ + + wakeup(); + + _cs.write(1); + _spi.format(8, 3); + _spi.frequency(75000000); + + if (W != NC) { + _w = new DigitalOut(W); + _w->write(1); + } + if (HOLD != NC) { + _hold = new DigitalOut(HOLD); + _hold->write(1); + } + + _cs.write(0); + _spi.write(READ_IDENTIFICATION); + _id[ID_MANUFACTURER] = _spi.write(0x00); + _id[ID_MEM_TYPE] = _spi.write(0x00); + _id[ID_MEM_SIZE] = _spi.write(0x00); + _cs.write(1); + + _mem_size = 1 << _id[ID_MEM_SIZE]; +} + +void SpiFlash25::format(int bits, int mode) { + _spi.format(bits, mode); +} + +void SpiFlash25::frequency(int hz) { + _spi.frequency(hz); +} + +bool SpiFlash25::read(int addr, int len, char* data) { + if (addr + len > _mem_size) { + return false; + } + + enable_write(); + + _cs.write(0); + _spi.write(READ_DATA); + _spi.write(high_byte(addr)); + _spi.write(mid_byte(addr)); + _spi.write(low_byte(addr)); + + for (int i = 0; i < len; i++) { + data[i] = _spi.write(0x00); + } + + _cs.write(1); + + return true; +} + +bool SpiFlash25::write(int addr, int len, const char* data) { + if (addr + len > _mem_size) { + return false; + } + + int written = 0; + int write_size = 0; + + while (written < len) { + write_size = _page_size - ((addr + written) % _page_size); + if (written + write_size > len) { + write_size = len - written; + } + + if (! write_page(addr + written, write_size, data + written)) { + return false; + } + + written += write_size; + } + + return true; +} + +char* SpiFlash25::read_id() { + return _id; +} + +char SpiFlash25::read_status() { + char status; + + _cs.write(0); + _spi.write(READ_STATUS); + status = _spi.write(0x00); + _cs.write(1); + + return status; +} + +void SpiFlash25::clear_sector(int addr) { + enable_write(); + + _cs.write(0); + _spi.write(SECTOR_ERASE); + _spi.write(high_byte(addr)); + _spi.write(mid_byte(addr)); + _spi.write(low_byte(addr)); + _cs.write(1); + + wait_for_write(); +} + +void SpiFlash25::clear_mem() { + enable_write(); + + _cs.write(0); + _spi.write(BULK_ERASE); + _cs.write(1); + + wait_for_write(); +} + +bool SpiFlash25::write_page(int addr, int len, const char* data) { + enable_write(); + + _cs.write(0); + _spi.write(PAGE_PROGRAM); + _spi.write(high_byte(addr)); + _spi.write(mid_byte(addr)); + _spi.write(low_byte(addr)); + + for (int i = 0; i < len; i++) { + _spi.write(data[i]); + } + + _cs.write(1); + wait_for_write(); + + return true; +} + +void SpiFlash25::enable_write() { + _cs.write(0); + _spi.write(WRITE_ENABLE); + _cs.write(1); +} + +void SpiFlash25::wait_for_write() { + while (read_status() & STATUS_WIP) { + wait_us(10); + } +} + +void SpiFlash25::deep_power_down() { + _cs.write(0); + _spi.write(DEEP_POWER_DOWN); + _cs.write(1); +} + +void SpiFlash25::wakeup() { + _cs.write(0); + _spi.write(DEEP_POWER_DOWN_RELEASE); + _cs.write(1); +} \ No newline at end of file diff --git a/src/drivers/SpiFlash25.h b/src/drivers/SpiFlash25.h new file mode 100644 index 0000000..6203544 --- /dev/null +++ b/src/drivers/SpiFlash25.h @@ -0,0 +1,107 @@ +/* Library for SPI flash 25* devices. + * Copyright (c) 2014 Multi-Tech Systems + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SPIFLASH25_H +#define SPIFLASH25_H + +#include "mbed.h" + +class SpiFlash25 { + public: + SpiFlash25(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName W = NC, PinName HOLD = NC, int page_size = 256); + + /* Set the page size (default 256) */ + void set_page_size(int size); + + /* Set up the internal SPI object */ + void format(int bits, int mode); + void frequency(int hz); + + /* Reads and writes can be across page boundaries */ + bool read(int addr, int len, char* data); + bool write(int addr, int len, const char* data); + + /* Read ID and status registers */ + char* read_id(); + char read_status(); + + /* Erase methods */ + void clear_sector(int addr); + void clear_mem(); + + void deep_power_down(); + void wakeup(); + + private: + enum { + WRITE_ENABLE = 0x06, + WRITE_DISABLE = 0x04, + READ_IDENTIFICATION = 0x9F, + READ_STATUS = 0x05, + WRITE_STATUS = 0x01, + READ_DATA = 0x03, + READ_DATA_FAST = 0x0B, + PAGE_PROGRAM = 0x02, + SECTOR_ERASE = 0xD8, + BULK_ERASE = 0xC7, + DEEP_POWER_DOWN = 0xB9, + DEEP_POWER_DOWN_RELEASE = 0xAB, + }; + + enum { + STATUS_SRWD = 0x80, // 0b 1000 0000 + STATUS_BP2 = 0x10, // 0b 0001 0000 + STATUS_BP1 = 0x08, // 0b 0000 1000 + STATUS_BP0 = 0x04, // 0b 0000 0100 + STATUS_WEL = 0x02, // 0b 0000 0010 + STATUS_WIP = 0x01, // 0b 0000 0001 + }; + + enum { + ID_MANUFACTURER = 0, + ID_MEM_TYPE = 1, + ID_MEM_SIZE = 2, + }; + + bool write_page(int addr, int len, const char* data); + void enable_write(); + void wait_for_write(); + + static inline char high_byte(int addr) { + return ((addr & 0xff0000) >> 16); + } + static inline char mid_byte(int addr) { + return ((addr & 0xff00) >> 8); + } + static inline char low_byte(int addr) { + return (addr & 0xff); + } + + SPI _spi; + DigitalOut _cs; + DigitalOut* _w; + DigitalOut* _hold; + int _mem_size; + int _page_size; + char _id[3]; +}; +#endif \ No newline at end of file