diff --git a/include/sta/sensors/w25qxx.hpp b/include/sta/sensors/w25qxx.hpp index bda8496..be7467e 100644 --- a/include/sta/sensors/w25qxx.hpp +++ b/include/sta/sensors/w25qxx.hpp @@ -7,41 +7,59 @@ namespace sta { + /** + * @brief + * + */ + enum class BlockSize + { + _32KB, + _64KB + }; + + /** + * @brief + * + */ + enum class ChipState + { + NORMAL, + POWERED_DOWN + }; + + /** + * @brief + * + */ + enum class AddressMode + { + _24BIT, + _32BIT + }; + 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); + /** + * @brief Construct a new W25Qxx object + * + * @param device + * @param addrMode + */ + W25Qxx(SPIDevice * device, AddressMode addrMode = AddressMode::_24BIT); uint8_t init(); + /** + * @brief Set the Address Mode object + * + * @param addrMode + * @return uint8_t + */ + uint8_t setAddressMode(AddressMode addrMode); + + AddressMode getAddressMode(); + uint8_t getChipID(); uint8_t getManufacturerID(); @@ -119,7 +137,7 @@ namespace sta * @param length * @return uint8_t */ - uint8_t readData(Address32 address, uint8_t * buffer, size_t length, bool fast = true); + uint8_t readData(uint32_t address, uint8_t * buffer, size_t length, bool fast = true); /** * @brief @@ -129,7 +147,7 @@ namespace sta * @param length * @return uint8_t */ - uint8_t pageProgram(Address32 address, uint8_t * buffer, size_t length); + uint8_t pageProgram(uint32_t address, uint8_t * buffer, size_t length); public: @@ -146,7 +164,7 @@ namespace sta * @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); + uint8_t sectorErase(uint32_t address); /** * @brief Sets all memory within a specified block (32/64 KByte) to 1s. @@ -158,7 +176,7 @@ namespace sta * @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); + uint8_t blockErase(uint32_t address, BlockSize blockSize); /** * @brief Sets all memory on the chip to 1s. @@ -201,8 +219,28 @@ namespace sta // Global Block / Sector unlock private: + /** + * @brief + * + * @param instruction + * @param data + * @param length + * @param arguments + * @param arg_length + * @return uint8_t + */ uint8_t busWrite(uint8_t instruction, const uint8_t * data = nullptr, size_t length = 0, uint8_t * arguments = nullptr, size_t arg_length = 0); + /** + * @brief + * + * @param instruction + * @param data + * @param length + * @param arguments + * @param arg_length + * @return uint8_t + */ uint8_t busRead(uint8_t instruction, uint8_t * data, size_t length, uint8_t * arguments = nullptr, size_t arg_length = 0); /** @@ -221,6 +259,7 @@ namespace sta private: SPIDevice * device_; ChipState state_; + AddressMode addrMode_; }; } // namespace sta diff --git a/include/sta/sensors/w25qxx_defs.hpp b/include/sta/sensors/w25qxx_defs.hpp index ca74d61..9557178 100644 --- a/include/sta/sensors/w25qxx_defs.hpp +++ b/include/sta/sensors/w25qxx_defs.hpp @@ -1,6 +1,8 @@ #ifndef STA_SENSORS_W25QXX_DEFS_HPP #define STA_SENSORS_W25QXX_DEFS_HPP +// Enable 4 Byte address mode (for W25Q512) +#define W25QXX_4_BYTE_ADDR_ENABLE 0xB7 // W25Qxx-specific instructions #define W25QXX_WRITE_ENABLE 0x06 @@ -26,7 +28,6 @@ // ID reading #define W25QXX_DEVICE_ID 0x90 -#define W25QXX_JEDEC_ID 0x9F #define W25QXX_READ_UNIQUE_ID 0x4B #define W25QXX_GLOBAL_BLOCK_LOCK 0x7E @@ -56,7 +57,8 @@ #define W25QXX_INDIV_BLOCK_UNLOCK 0x39 #define W25QXX_READ_BLOCK_LOCK 0x3D -#define W25QXX_FAST_READ_DUAL + +#define W25QXX_DEVICE_ID_RESULT 0xEF -#endif // STA_SENSORS_W25QXX_DEFS_HPP \ No newline at end of file +#endif // STA_SENSORS_W25QXX_DEFS_HPP diff --git a/src/w25qxx.cpp b/src/w25qxx.cpp index 5483c4e..bded378 100644 --- a/src/w25qxx.cpp +++ b/src/w25qxx.cpp @@ -8,20 +8,56 @@ namespace sta { - W25Qxx::W25Qxx(SPIDevice * device) + W25Qxx::W25Qxx(SPIDevice * device, AddressMode addrMode /* = AddressMode::_24BIT */) : device_{device}, - state_{ChipState::NORMAL} + state_{ChipState::POWERED_DOWN}, + addrMode_{addrMode} { STA_ASSERT(device != nullptr); } uint8_t W25Qxx::init() { + // Check if the chip returns the correct device id. + if (getManufacturerID() != W25QXX_DEVICE_ID_RESULT) + { + return 0; + } + // If requested, tell the flash chip to use 32 bit addresses. + if (addrMode_ == AddressMode::_32BIT) + { + if (!setAddressMode(AddressMode::_32BIT)) + { + return 0; + } + } + + if (!releasePowerDown()) + { + return 0; + } return 1; } + uint8_t W25Qxx::setAddressMode(AddressMode addrMode) + { + busWrite(W25QXX_4_BYTE_ADDR_ENABLE); + + while (isBusy()) {} + + return getAddressMode() == addrMode; + } + + AddressMode W25Qxx::getAddressMode() + { + uint8_t status; + readStatusRegister(3, &status); + + return status && 0x01 == 0x01 ? AddressMode::_32BIT : AddressMode::_24BIT; + } + uint8_t W25Qxx::getChipID() { uint8_t buffer[4]; @@ -35,7 +71,7 @@ namespace sta uint8_t dummy[3] = {0, 0, 0}; uint8_t id; - busRead(W25QXX_JEDEC_ID, &id, 1, dummy, 3); + busRead(W25QXX_DEVICE_ID, &id, 1, dummy, 3); return id; } @@ -45,8 +81,6 @@ namespace sta uint8_t dummy[4]; uint8_t id[8]; - STA_DEBUG_PRINTLN("Before crash?"); - busRead(W25QXX_READ_UNIQUE_ID, id, 8, dummy, 4); uint64_t id_complete = 0; @@ -64,41 +98,75 @@ namespace sta uint8_t status = 0; readStatusRegister(1, &status); - return (0x01 & status) == 0x01; + return (0x01 && status) == 0x01; } - uint8_t W25Qxx::readData(Address32 addr, uint8_t * buffer, size_t length, bool fast /* = true */) + uint8_t W25Qxx::readData(uint32_t address, uint8_t * buffer, size_t length, bool fast /* = true */) { - device_->beginTransmission(); - - uint8_t instruction = fast ? W25QXX_FAST_READ : W25QXX_FAST_READ; - device_->transfer(&instruction, 1); + uint8_t instruction = fast ? W25QXX_FAST_READ : W25QXX_READ; // In fast mode we have to send a 8 dummy clock cycles first. if (fast) { - uint8_t dummy = 0; - device_->transfer(&dummy, 1); + // TODO } - device_->transfer(addr.buffer, sizeof(addr)); - device_->receive(buffer, length); - device_->endTransmission(); + while (isBusy()) {} - return 1; + // Depending on address mode, send 3 bytes or 4 bytes. + if (addrMode_ == AddressMode::_32BIT) + { + uint8_t addrBuffer[4] = { + (uint8_t) (address << 24), + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; + + return busRead(instruction, buffer, length, addrBuffer, 4); + } + else + { + uint8_t addrBuffer[3] = { + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; + + return busRead(instruction, buffer, length, addrBuffer, 4); + } } - uint8_t W25Qxx::pageProgram(Address32 addr, uint8_t * buffer, size_t length) + uint8_t W25Qxx::pageProgram(uint32_t address, uint8_t * buffer, size_t length) { + while (isBusy()) {} + 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); + if (addrMode_ == AddressMode::_32BIT) + { + uint8_t addrBuffer[4] = { + (uint8_t) (address << 24), + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; - // TODO: Does this reset the writeEnable bit? + return busWrite(W25QXX_PAGE_PROGAM, buffer, length, addrBuffer, 4); + } + else + { + uint8_t addrBuffer[3] = { + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; + + return busWrite(W25QXX_PAGE_PROGAM, buffer, length, addrBuffer, 4); + } } 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 */) @@ -114,8 +182,11 @@ namespace sta device_->transfer(arguments, arg_length); } - // Send the actual data bytes. - device_->transfer(data, length); + // If necessary, send the actual data bytes. + if (data != nullptr && length != 0) + { + device_->transfer(data, length); + } device_->endTransmission(); @@ -126,13 +197,9 @@ namespace sta { device_->beginTransmission(); - STA_DEBUG_PRINTLN("MAYBE HERE?"); - // Send the instruction. device_->transfer(instruction); - STA_DEBUG_PRINTLN("OR HERE?"); - // If requested, send argument bytes before receiving the actual data. if (arguments != nullptr && arg_length != 0) { @@ -144,8 +211,6 @@ namespace sta device_->endTransmission(); - STA_DEBUG_PRINTLN("STILL ALIVE?"); - return 1; } @@ -221,34 +286,73 @@ namespace sta bool W25Qxx::isWriteEnabled() { - + return true; // TODO } - uint8_t W25Qxx::sectorErase(Address32 address) + uint8_t W25Qxx::sectorErase(uint32_t address) { if (!writeEnable()) { return 0; } - return busWrite(W25QXX_SECTOR_ERASE, address.buffer, sizeof(address)); - } - - uint8_t W25Qxx::blockErase(Address32 address, BlockSize blockSize) - { - if (!writeEnable()) + if (addrMode_ == AddressMode::_32BIT) { - return 0; - } + uint8_t addrBuffer[4] = { + (uint8_t) (address << 24), + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; - if (blockSize == BlockSize::_32KB) - { - return busWrite(W25QXX_BLOCK_ERASE_32_KB, address.buffer, sizeof(address)); + return busWrite(W25QXX_SECTOR_ERASE, addrBuffer, 4); } else { - return busWrite(W25QXX_BLOCK_ERASE_64_KB, address.buffer, sizeof(address)); + uint8_t addrBuffer[3] = { + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; + + return busWrite(W25QXX_SECTOR_ERASE, addrBuffer, 3); } + + return 1; + } + + uint8_t W25Qxx::blockErase(uint32_t address, BlockSize blockSize) + { + if (!writeEnable()) + { + return 0; + } + + uint8_t instruction = blockSize == BlockSize::_32KB ? W25QXX_BLOCK_ERASE_32_KB : W25QXX_BLOCK_ERASE_64_KB; + + if (addrMode_ == AddressMode::_32BIT) + { + uint8_t addrBuffer[4] = { + (uint8_t) (address << 24), + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; + + return busWrite(instruction, addrBuffer, 4); + } + else + { + uint8_t addrBuffer[3] = { + (uint8_t) (address << 16), + (uint8_t) (address << 8), + (uint8_t) (address) + }; + + return busWrite(instruction, addrBuffer, 3); + } + + return 1; } uint8_t W25Qxx::chipErase() @@ -285,4 +389,4 @@ namespace sta return 0; } -} // namespace sta \ No newline at end of file +} // namespace sta