From 517510c204bd188ac98a84e8c18965fedcbad0e6 Mon Sep 17 00:00:00 2001 From: dario Date: Sun, 11 Feb 2024 14:12:49 +0100 Subject: [PATCH] Updated implementation for driver --- include/sta/sensors/w25qxx.hpp | 178 ++++++++++++++++++++++++++-- include/sta/sensors/w25qxx_defs.hpp | 8 +- src/w25qxx.cpp | 142 +++++++++++++++++++--- 3 files changed, 296 insertions(+), 32 deletions(-) diff --git a/include/sta/sensors/w25qxx.hpp b/include/sta/sensors/w25qxx.hpp index a22e459..bda8496 100644 --- a/include/sta/sensors/w25qxx.hpp +++ b/include/sta/sensors/w25qxx.hpp @@ -9,11 +9,73 @@ namespace sta { class W25Qxx { + enum class BlockSize + { + _32KB, + _64KB + }; + + // TODO decide address size at compile time? Use sta/config.hpp? + + union Address32 + { + uint32_t value; + uint8_t buffer[4]; + uint8_t page[3]; + }; + + union Address24 + { + uint32_t value; + uint8_t dummy; + uint8_t buffer[3]; + uint8_t page[2]; + }; + + enum class ChipState + { + NORMAL, + POWERED_DOWN + }; public: W25Qxx(SPIDevice * device); uint8_t init(); + uint8_t getChipID(); + + uint8_t getManufacturerID(); + + uint64_t getUniqueID(); + + // TODO: SFDP register? + + /** + * @brief Puts the chip into power-down state which significantly reduces power consumption. + * + * @remarks Every instruction but the releasePowerDown instruction are ignored in this state, + * allowing for maximum write protection. + * + * @return uint8_t Returns 1 if the operation was successful, 0 otherwise. + */ + uint8_t powerDown(); + + /** + * @brief + * + * @return uint8_t + */ + uint8_t releasePowerDown(); + + // enable reset + + // reset device + + // Extended address register read / write? + + // Enter 4-Byte address mode + + // Exit 4-Byte address mode public: /* * Status registers. @@ -44,6 +106,104 @@ namespace sta * @return bool Returns true if the flash is busy, false otherwise. */ bool isBusy(); + public: + /* + * Read / Write operations + */ + + /** + * @brief + * + * @param addr + * @param buffer + * @param length + * @return uint8_t + */ + uint8_t readData(Address32 address, uint8_t * buffer, size_t length, bool fast = true); + + /** + * @brief + * + * @param addr + * @param buffer + * @param length + * @return uint8_t + */ + uint8_t pageProgram(Address32 address, uint8_t * buffer, size_t length); + + + public: + /* + * Erase operations + */ + + /** + * @brief Sets all memory within a specified sector (4 KByte) to 1s. + * + * @remarks Afterwards, the device won't accept any instructions for a duration T_SE. This can be checked + * by reading the busy bit. + * + * @param address The address of the sector to erase. + * @return bool Returns 1 if the operation was successful, 0 otherwise. + */ + uint8_t sectorErase(Address32 address); + + /** + * @brief Sets all memory within a specified block (32/64 KByte) to 1s. + * + * @remarks Afterwards, the device won't accept any instructions for a duration T_SE. This can be checked + * by reading the busy bit. + * + * @param address The address of the block to erase. + * @param blockSize The size of the block (32KByte vs 64KByte) + * @return uint8_t Returns 1 if the operation was successful, 0 otherwise. + */ + uint8_t blockErase(Address32 address, BlockSize blockSize); + + /** + * @brief Sets all memory on the chip to 1s. + * + * @return uint8_t Returns 1 if the operation was successful, 0 otherwise. + */ + uint8_t chipErase(); + + /** + * @brief Suspends an ongoing sector or block erase operation. + * + * @remarks Chip erase operations cannot be suspended. + * + * @return uin8t_t Returns 1 if the operation was successful, 0 otherwise. + */ + uint8_t suspendErase(); + + /** + * @brief Resumes a suspended sector or block erase operation. + * + * @return uint8_t Returns 1 if the operation was successful, 0 otherwise. + */ + uint8_t resumeErase(); + + // TODO: Read SUS bit + public: + // Erase security registers + + // Program security registers + + // Read security registers + + // Indiv Block / Sector lock + + // Indiv Block / Sector unlock + + // Indiv Block / Sector lock read + + // Global Block / Sector lock + + // Global Block / Sector unlock + private: + uint8_t busWrite(uint8_t instruction, const uint8_t * data = nullptr, size_t length = 0, uint8_t * arguments = nullptr, size_t arg_length = 0); + + uint8_t busRead(uint8_t instruction, uint8_t * data, size_t length, uint8_t * arguments = nullptr, size_t arg_length = 0); /** * @brief Checks if the flash is write enabled. @@ -51,24 +211,16 @@ namespace sta * @return bool Returns true if the flash is write enabled, false otherwise. */ bool isWriteEnabled(); - - public: - - uint8_t readData(const uint8_t * addr, uint8_t * buffer, size_t length); - - uint8_t pageProgram(const uint8_t * addr, uint8_t * buffer, size_t length); - private: - uint8_t busWrite(uint8_t instruction, const uint8_t * data = nullptr, size_t length = 0); - - uint8_t busRead(uint8_t instruction, uint8_t * data, size_t length); - + uint8_t writeEnable(); + + uint8_t writeDisable(); uint8_t writeVolatileEnable(); - uint8_t writeDisable(); - + private: SPIDevice * device_; + ChipState state_; }; } // namespace sta diff --git a/include/sta/sensors/w25qxx_defs.hpp b/include/sta/sensors/w25qxx_defs.hpp index b647c5c..ca74d61 100644 --- a/include/sta/sensors/w25qxx_defs.hpp +++ b/include/sta/sensors/w25qxx_defs.hpp @@ -38,10 +38,12 @@ #define W25QXX_PAGE_PROGAM 0x02 #define W25QXX_QUAD_PAGE_PROGAM 0x32 -#define W25QXX_SECTOR_ERASE 0x20 -#define W25QXX_BLOCK_ERASE 0xD8 +#define W25QXX_SECTOR_ERASE 0x21 +#define W25QXX_BLOCK_ERASE_32_KB 0x52 +#define W25QXX_BLOCK_ERASE_64_KB 0xD8 -#define W25QXX_FAST_READ 0x03 +#define W25QXX_READ 0x03 +#define W25QXX_FAST_READ 0x0B #define W25QXX_FAST_READ_DUAL_OUT 0x3B #define W25QXX_FAST_READ_QUAD_OUT 0x6B #define W25QXX_SFDP_REG 0x5A diff --git a/src/w25qxx.cpp b/src/w25qxx.cpp index 73949b3..b8ce0f2 100644 --- a/src/w25qxx.cpp +++ b/src/w25qxx.cpp @@ -1,12 +1,16 @@ #include +#include +#include + namespace sta { W25Qxx::W25Qxx(SPIDevice * device) - : device_{device} + : device_{device}, + state_{ChipState::NORMAL} { - + STA_ASSERT(device != nullptr); } uint8_t W25Qxx::init() @@ -16,6 +20,41 @@ namespace sta return 1; } + uint8_t W25Qxx::getChipID() + { + uint8_t buffer[4]; + busRead(W25QXX_RELEASE_POWER_DOWN, buffer, 3); + + return buffer[3]; + } + + uint8_t W25Qxx::getManufacturerID() + { + Address24 address = {0}; + + uint8_t id; + busRead(W25QXX_JEDEC_ID, &id, 1, address.buffer, 3); + + return id; + } + + uint64_t W25Qxx::getUniqueID() + { + uint8_t dummy[4]; + uint8_t id[8]; + + busRead(W25QXX_READ_UNIQUE_ID, id, 8, dummy, 4); + + uint64_t id_complete; + + for (size_t i; i < 8; i++) + { + id_complete |= id[i] << (7-i) * 8; + } + + return id_complete; + } + bool W25Qxx::isBusy() { uint8_t status = 0; @@ -24,26 +63,46 @@ namespace sta return (0x01 & status) == 0x01; } - uint8_t - - uint8_t W25Qxx::readData(const uint8_t * addr, uint8_t * buffer, size_t length) + uint8_t W25Qxx::readData(Address32 addr, uint8_t * buffer, size_t length, bool fast /* = true */) { device_->beginTransmission(); - const uint8_t instruction = W25QXX_FAST_READ; + uint8_t instruction = fast ? W25QXX_FAST_READ : W25QXX_FAST_READ; device_->transfer(&instruction, 1); - device_->transfer(addr, 3); + + // In fast mode we have to send a 8 dummy clock cycles first. + if (fast) + { + uint8_t dummy = 0; + device_->transfer(&dummy, 1); + } + + device_->transfer(addr.buffer, sizeof(addr)); device_->receive(buffer, length); + device_->endTransmission(); return 1; } - uint8_t W25Qxx::busWrite(uint8_t instruction, const uint8_t * data /* = nullptr */, size_t length /* = 0 */) + uint8_t W25Qxx::pageProgram(Address32 addr, uint8_t * buffer, size_t length) + { + if (!writeEnable()) + { + return 0; + } + + // TODO: sizeof(addr)-1 doesn't work for 24 bit addresses. + return busWrite(W25QXX_PAGE_PROGAM, buffer, length, addr.page, sizeof(addr)-1); + + // TODO: Does this reset the writeEnable bit? + } + + uint8_t W25Qxx::busWrite(uint8_t instruction, const uint8_t * data /* = nullptr */, size_t length /* = 0 */, uint8_t * arguments /* = nullptr */, size_t arg_length /* = 0 */) { return 1; } - uint8_t W25Qxx::busRead(uint8_t instruction, uint8_t * data, size_t length) + uint8_t W25Qxx::busRead(uint8_t instruction, uint8_t * data, size_t length, uint8_t * arguments /* = nullptr */, size_t arg_length /* = 0 */) { return 1; } @@ -118,19 +177,70 @@ namespace sta return 0; } - uint8_t W25Qxx::pageProgram(const uint8_t * addr, uint8_t * buffer, size_t length) + bool W25Qxx::isWriteEnabled() + { + + } + + uint8_t W25Qxx::sectorErase(Address32 address) { if (!writeEnable()) { return 0; } - device_->beginTransmission(); - uint8_t instruction = W25QXX_PAGE_PROGAM; - device_->transfer(&instruction, 1); - device_->transfer(buffer, length); - device_->endTransmission(); + return busWrite(W25QXX_SECTOR_ERASE, address.buffer, sizeof(address)); + } - return 1; + uint8_t W25Qxx::blockErase(Address32 address, BlockSize blockSize) + { + if (!writeEnable()) + { + return 0; + } + + if (blockSize == BlockSize::_32KB) + { + return busWrite(W25QXX_BLOCK_ERASE_32_KB, address.buffer, sizeof(address)); + } + else + { + return busWrite(W25QXX_BLOCK_ERASE_64_KB, address.buffer, sizeof(address)); + } + } + + uint8_t W25Qxx::chipErase() + { + if (!writeEnable()) + { + return 0; + } + + return busWrite(W25QXX_CHIP_ERASE); + } + + uint8_t W25Qxx::suspendErase() + { + return busWrite(W25QXX_ERASE_SUSPEND_PROG); + } + + uint8_t W25Qxx::resumeErase() + { + return busWrite(W25QXX_ERASE_RESUME_PROG); + } + + uint8_t W25Qxx::powerDown() + { + return busWrite(W25QXX_POWER_DOWN); + } + + uint8_t W25Qxx::releasePowerDown() + { + if (state_ == ChipState::POWERED_DOWN) + { + return busWrite(W25QXX_RELEASE_POWER_DOWN); + } + + return 0; } } // namespace sta \ No newline at end of file