mirror of
https://git.intern.spaceteamaachen.de/ALPAKA/driver-w25qxxx.git
synced 2025-06-10 18:45:59 +00:00
Merge pull request 'Added simple logger' (#1) from feature/logger into main
Reviewed-on: https://git.intern.spaceteamaachen.de/ALPAKA/driver-w25qxxx/pulls/1 Reviewed-by: carlwachter <carlwachter@noreply.git.intern.spaceteamaachen.de>
This commit is contained in:
commit
82e6dd388c
@ -2,6 +2,7 @@
|
|||||||
#define STA_SENSORS_W25Q128_HPP
|
#define STA_SENSORS_W25Q128_HPP
|
||||||
|
|
||||||
#include <sta/bus/spi/device.hpp>
|
#include <sta/bus/spi/device.hpp>
|
||||||
|
#include <sta/mutex.hpp>
|
||||||
#include <sta/drivers/w25qxx_defs.hpp>
|
#include <sta/drivers/w25qxx_defs.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -60,12 +61,14 @@ namespace sta
|
|||||||
using DelayUsFunc = void (*)(uint32_t);
|
using DelayUsFunc = void (*)(uint32_t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new W25Qxx object
|
* @brief Driver class for the W25QXX flash storage series.
|
||||||
*
|
*
|
||||||
* @param device
|
* @param device A SPI device handle from sta-core.
|
||||||
* @param addrMode
|
* @param delay A microsecond delay function.
|
||||||
|
* @param addrMode Choose between 24 Bit and 32 Bit addressing.
|
||||||
|
* @param mutex A mutex for thread safety if the flash chip is used by multiple threads. Defaults to a always free mutex.
|
||||||
*/
|
*/
|
||||||
W25Qxx(SPIDevice * device, DelayUsFunc delay, AddressMode addrMode = AddressMode::_24BIT);
|
W25Qxx(SPIDevice * device, DelayUsFunc delay, AddressMode addrMode = AddressMode::_24BIT, Mutex * mutex = Mutex::ALWAYS_FREE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize the flash chip.
|
* @brief Initialize the flash chip.
|
||||||
@ -74,18 +77,12 @@ namespace sta
|
|||||||
*/
|
*/
|
||||||
uint8_t init();
|
uint8_t init();
|
||||||
|
|
||||||
uint32_t getChunkBytes(ChunkSize size);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Find the first memory section not satisfying a given criterion using a binary search.
|
* @brief Checks if the flash is busy writing or erasing.
|
||||||
*
|
*
|
||||||
* @note This function assume that there is a page n such that every page before n satisfies the criterion, while every page after that doesn't
|
* @return bool Returns true if the flash is busy, false otherwise.
|
||||||
*
|
|
||||||
* @param criterion A function evaluating the criterion on a page.
|
|
||||||
* @param size The size of the memory section. Has to be one of the predefined sizes.
|
|
||||||
* @return uint32_t The last address such that the criterion is satisfied.
|
|
||||||
*/
|
*/
|
||||||
uint32_t findLast(std::function<bool(uint8_t*)> criterion, ChunkSize size);
|
bool isBusy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the Address Mode object
|
* @brief Set the Address Mode object
|
||||||
@ -146,41 +143,7 @@ namespace sta
|
|||||||
|
|
||||||
// reset device
|
// reset device
|
||||||
|
|
||||||
// Extended address register read / write?
|
// Extended address register read / write?
|
||||||
|
|
||||||
// Enter 4-Byte address mode
|
|
||||||
|
|
||||||
// Exit 4-Byte address mode
|
|
||||||
public:
|
|
||||||
/*
|
|
||||||
* Status registers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read one of the flash's status registers.
|
|
||||||
*
|
|
||||||
* @param regID The ID of the status register. Can only be 1, 2 or 3.
|
|
||||||
* @param status_byte A pointer to the variable to write the state into.
|
|
||||||
* @return uint8_t Returns 1 if successful, 0 otherwise.
|
|
||||||
*/
|
|
||||||
uint8_t readStatusRegister(uint8_t regID, uint8_t * status_byte);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Write into one of the chip's status registers.
|
|
||||||
*
|
|
||||||
* @param regID The ID of the status register. Can only be 1, 2 or 3.
|
|
||||||
* @param status_byte The byte to write into the status register.
|
|
||||||
* @param nonvolatile If set to true, this setting will be restored after power off.
|
|
||||||
* @return uint8_t Returns 1 if successful, 0 otherwise.
|
|
||||||
*/
|
|
||||||
uint8_t writeStatusRegister(uint8_t regID, uint8_t * status_byte, bool nonvolatile = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if the flash is busy writing or erasing.
|
|
||||||
*
|
|
||||||
* @return bool Returns true if the flash is busy, false otherwise.
|
|
||||||
*/
|
|
||||||
bool isBusy();
|
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* Read / Write operations
|
* Read / Write operations
|
||||||
@ -218,10 +181,10 @@ namespace sta
|
|||||||
* @remarks Afterwards, the device won't accept any instructions for a duration T_SE. This can be checked
|
* @remarks Afterwards, the device won't accept any instructions for a duration T_SE. This can be checked
|
||||||
* by reading the busy bit.
|
* by reading the busy bit.
|
||||||
*
|
*
|
||||||
* @param address The address of the sector to erase.
|
* @param address The number of the sector to erase. Here, 0 is the first sector, 1 the second and so on.
|
||||||
* @return bool Returns 1 if the operation was successful, 0 otherwise.
|
* @return bool Returns 1 if the operation was successful, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
uint8_t sectorErase(uint32_t address);
|
uint8_t sectorErase(uint32_t address, bool blocking = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets all memory within a specified block (32/64 KByte) to 1s.
|
* @brief Sets all memory within a specified block (32/64 KByte) to 1s.
|
||||||
@ -315,13 +278,32 @@ namespace sta
|
|||||||
|
|
||||||
uint8_t writeVolatileEnable();
|
uint8_t writeVolatileEnable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read one of the flash's status registers.
|
||||||
|
*
|
||||||
|
* @param regID The ID of the status register. Can only be 1, 2 or 3.
|
||||||
|
* @param status_byte A pointer to the variable to write the state into.
|
||||||
|
* @return uint8_t Returns 1 if successful, 0 otherwise.
|
||||||
|
*/
|
||||||
|
uint8_t readStatusRegister(uint8_t regID, uint8_t * status_byte);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write into one of the chip's status registers.
|
||||||
|
*
|
||||||
|
* @param regID The ID of the status register. Can only be 1, 2 or 3.
|
||||||
|
* @param status_byte The byte to write into the status register.
|
||||||
|
* @param nonvolatile If set to true, this setting will be restored after power off.
|
||||||
|
* @return uint8_t Returns 1 if successful, 0 otherwise.
|
||||||
|
*/
|
||||||
|
uint8_t writeStatusRegister(uint8_t regID, uint8_t * status_byte, bool nonvolatile = false);
|
||||||
private:
|
private:
|
||||||
SPIDevice * device_;
|
SPIDevice * device_;
|
||||||
DelayUsFunc delay_;
|
DelayUsFunc delay_;
|
||||||
|
Mutex * mutex_;
|
||||||
ChipState state_;
|
ChipState state_;
|
||||||
AddressMode addrMode_;
|
AddressMode addrMode_;
|
||||||
};
|
};
|
||||||
} // namespace sta
|
} // namespace sta
|
||||||
|
|
||||||
|
|
||||||
#endif // STA_SENSORS_W25Q128_HPP
|
#endif // STA_SENSORS_W25Q128_HPP
|
||||||
|
@ -39,12 +39,14 @@
|
|||||||
#define W25QXX_PAGE_PROGAM 0x02
|
#define W25QXX_PAGE_PROGAM 0x02
|
||||||
#define W25QXX_QUAD_PAGE_PROGAM 0x32
|
#define W25QXX_QUAD_PAGE_PROGAM 0x32
|
||||||
|
|
||||||
#define W25QXX_SECTOR_ERASE 0x21
|
#define W25QXX_SECTOR_ERASE 0x20
|
||||||
#define W25QXX_BLOCK_ERASE_32_KB 0x52
|
#define W25QXX_BLOCK_ERASE_32_KB 0x52
|
||||||
#define W25QXX_BLOCK_ERASE_64_KB 0xD8
|
#define W25QXX_BLOCK_ERASE_64_KB 0xD8
|
||||||
|
|
||||||
#define W25QXX_READ 0x03
|
#define W25QXX_READ 0x03
|
||||||
|
#define W25QXX_READ_32_BIT 0x13
|
||||||
#define W25QXX_FAST_READ 0x0B
|
#define W25QXX_FAST_READ 0x0B
|
||||||
|
#define W25QXX_FAST_READ_32_BIT 0x0C
|
||||||
#define W25QXX_FAST_READ_DUAL_OUT 0x3B
|
#define W25QXX_FAST_READ_DUAL_OUT 0x3B
|
||||||
#define W25QXX_FAST_READ_QUAD_OUT 0x6B
|
#define W25QXX_FAST_READ_QUAD_OUT 0x6B
|
||||||
#define W25QXX_SFDP_REG 0x5A
|
#define W25QXX_SFDP_REG 0x5A
|
||||||
@ -61,6 +63,7 @@
|
|||||||
|
|
||||||
#define W25QXX_PAGE_SIZE 0x100
|
#define W25QXX_PAGE_SIZE 0x100
|
||||||
#define W25QXX_SECTOR_SIZE 0x1000
|
#define W25QXX_SECTOR_SIZE 0x1000
|
||||||
|
#define W25QXX_PAGE_PER_SECTOR 0x10
|
||||||
#define W25QXX_BLOCK_32_KB_SIZE 0x8000
|
#define W25QXX_BLOCK_32_KB_SIZE 0x8000
|
||||||
#define W25QXX_BLOCK_64_KB_SIZE 0xF000
|
#define W25QXX_BLOCK_64_KB_SIZE 0xF000
|
||||||
|
|
||||||
|
103
include/sta/utils/logger.hpp
Normal file
103
include/sta/utils/logger.hpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#ifndef STA_UTILS_LOGGER_HPP
|
||||||
|
#define STA_UTILS_LOGGER_HPP
|
||||||
|
|
||||||
|
#include <sta/drivers/w25qxx.hpp>
|
||||||
|
#include <sta/debug/assert.hpp>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace sta
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a logger object which manages reading and writing data to a segment of a flash chip.
|
||||||
|
*
|
||||||
|
* @param flash The flash chip to use for logging.
|
||||||
|
* @param startSector The index of the start sector (1 LSB = 4096 bytes).
|
||||||
|
* @param endSector The index of the end sector (1 LSB = 4096 bytes).
|
||||||
|
*/
|
||||||
|
Logger(W25Qxx * flash, uint32_t startSector, uint32_t endSector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write a new data point to the flash chip as long as the segment's limit wasn't reached.
|
||||||
|
*
|
||||||
|
* @param data A pointer to the data to write to the flash chip.
|
||||||
|
* @return true if successful, false if the segment end was reached.
|
||||||
|
*/
|
||||||
|
bool write(T* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write a new data point to the flash chip as long as the segment's limit wasn't reached.
|
||||||
|
*
|
||||||
|
* @param data The data to write to the flash chip.
|
||||||
|
* @return true if successful, false if the segment end was reached.
|
||||||
|
*/
|
||||||
|
bool write(T data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the flash memory used by the logger.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of data points currently written to the memory segment.
|
||||||
|
*
|
||||||
|
* @return size_t The number of data points.
|
||||||
|
*/
|
||||||
|
size_t count();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of data points that can be written to the logger before an overflow occurs.
|
||||||
|
*
|
||||||
|
* @return size_t The number of data points.
|
||||||
|
*/
|
||||||
|
size_t remaining();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the total number of data points that fit into the logger.
|
||||||
|
*
|
||||||
|
* @return size_t The number of data points.
|
||||||
|
*/
|
||||||
|
size_t capacity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the ith element stored in the flash storage.
|
||||||
|
*
|
||||||
|
* @param i The index of the element to read.
|
||||||
|
* @return T The ith element stored in the flash storage.
|
||||||
|
*/
|
||||||
|
T get(std::size_t i);
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Find the first sector in the segment that was not written yet.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void findLast();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A method that checks if a page is empty
|
||||||
|
*
|
||||||
|
* @param buffer A buffer of size 256 containing data from a page.
|
||||||
|
* @return true if the page is empty, false otherwise.
|
||||||
|
*/
|
||||||
|
bool searchCriterion(uint8_t * buffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
W25Qxx * flash_;
|
||||||
|
uint32_t start_;
|
||||||
|
uint32_t end_;
|
||||||
|
uint32_t address_;
|
||||||
|
|
||||||
|
uint8_t buffer_[W25QXX_PAGE_SIZE];
|
||||||
|
bool flushed_;
|
||||||
|
uint32_t ptr_;
|
||||||
|
};
|
||||||
|
} // namespace sta
|
||||||
|
|
||||||
|
#include <sta/utils/logger.tpp>
|
||||||
|
|
||||||
|
#endif // STA_UTILS_LOGGER_HPP
|
205
include/sta/utils/logger.tpp
Normal file
205
include/sta/utils/logger.tpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#ifndef STA_UTILS_LOGGER_TPP
|
||||||
|
#define STA_UTILS_LOGGER_TPP
|
||||||
|
|
||||||
|
#include <sta/debug/debug.hpp>
|
||||||
|
#include <sta/drivers/w25qxx.hpp>
|
||||||
|
|
||||||
|
namespace sta
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
Logger<T>::Logger(W25Qxx * flash, uint32_t startSec, uint32_t endSec)
|
||||||
|
: flash_{flash},
|
||||||
|
start_{startSec},
|
||||||
|
end_{endSec},
|
||||||
|
address_{start_ * W25QXX_SECTOR_SIZE},
|
||||||
|
buffer_{0x00, },
|
||||||
|
flushed_{false},
|
||||||
|
ptr_ {0}
|
||||||
|
{
|
||||||
|
STA_ASSERT(flash != nullptr);
|
||||||
|
STA_ASSERT(endSec > startSec);
|
||||||
|
|
||||||
|
// Jump to the last written page.
|
||||||
|
findLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Logger<T>::searchCriterion(uint8_t * buffer)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < W25QXX_PAGE_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (buffer[i] != 0xFF)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Logger<T>::findLast()
|
||||||
|
{
|
||||||
|
uint32_t left = start_;
|
||||||
|
uint32_t right = end_+1;
|
||||||
|
uint32_t middle;
|
||||||
|
|
||||||
|
uint8_t * buffer = new uint8_t[256];
|
||||||
|
|
||||||
|
while (left <= right)
|
||||||
|
{
|
||||||
|
middle = (left + right) / 2;
|
||||||
|
flash_->readData(middle * W25QXX_SECTOR_SIZE, buffer, W25QXX_PAGE_SIZE);
|
||||||
|
|
||||||
|
if (middle == 0 && searchCriterion(buffer))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchCriterion(buffer))
|
||||||
|
{
|
||||||
|
if (middle == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_->readData((middle-1) * W25QXX_SECTOR_SIZE, buffer, W25QXX_PAGE_SIZE);
|
||||||
|
if (!searchCriterion(buffer))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
right = middle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
left = middle + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
middle = (left + right) / 2;
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
address_ = middle * W25QXX_SECTOR_SIZE;
|
||||||
|
ptr_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Logger<T>::write(T* data)
|
||||||
|
{
|
||||||
|
// If writing the data would exceed the segment length, return false and don't do anything.
|
||||||
|
if ((address_ + ptr_ + sizeof(T)) / W25QXX_SECTOR_SIZE >= end_)
|
||||||
|
{
|
||||||
|
// If the current page hasn't been uploaded yet, do it now.
|
||||||
|
if (!flushed_)
|
||||||
|
{
|
||||||
|
flash_->pageProgram(address_, buffer_, W25QXX_PAGE_SIZE);
|
||||||
|
flushed_ = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the data to a byte array.
|
||||||
|
uint8_t * bytes = reinterpret_cast<uint8_t*>(data);
|
||||||
|
uint32_t length = sizeof(T);
|
||||||
|
|
||||||
|
// If the written data exceeds the remaining bytes in the page.
|
||||||
|
while (ptr_ + length >= W25QXX_PAGE_SIZE)
|
||||||
|
{
|
||||||
|
// Bytes remaining until the page is full.
|
||||||
|
uint32_t remaining = W25QXX_PAGE_SIZE - ptr_;
|
||||||
|
|
||||||
|
// If the page to written is in a new sector, erase the new sector before writing to it.
|
||||||
|
if (address_ % W25QXX_SECTOR_SIZE == 0)
|
||||||
|
{
|
||||||
|
flash_->sectorErase(address_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(buffer_ + ptr_, bytes, remaining);
|
||||||
|
flash_->pageProgram(address_, buffer_, W25QXX_PAGE_SIZE);
|
||||||
|
|
||||||
|
bytes += remaining;
|
||||||
|
length -= remaining;
|
||||||
|
ptr_ = 0;
|
||||||
|
address_ += W25QXX_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(buffer_ + ptr_, bytes, length);
|
||||||
|
ptr_ += length;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Logger<T>::write(T data)
|
||||||
|
{
|
||||||
|
return write(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Logger<T>::clear()
|
||||||
|
{
|
||||||
|
uint32_t left = start_;
|
||||||
|
uint32_t right = end_+1;
|
||||||
|
uint32_t middle;
|
||||||
|
|
||||||
|
// Erase all sectors the binary search would check when trying to find the last written page.
|
||||||
|
while (left <= right)
|
||||||
|
{
|
||||||
|
middle = (left + right) / 2;
|
||||||
|
flash_->sectorErase(middle * W25QXX_SECTOR_SIZE, true);
|
||||||
|
right = middle;
|
||||||
|
|
||||||
|
if (middle == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
flash_->sectorErase((middle-1) * W25QXX_SECTOR_SIZE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
middle = (left + right) / 2;
|
||||||
|
flash_->sectorErase(middle * W25QXX_SECTOR_SIZE, true);
|
||||||
|
|
||||||
|
address_ = start_ * W25QXX_SECTOR_SIZE;
|
||||||
|
ptr_ = 0;
|
||||||
|
flushed_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t Logger<T>::count()
|
||||||
|
{
|
||||||
|
return ((address_ + ptr_) - start_ * W25QXX_SECTOR_SIZE) / sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t Logger<T>::remaining()
|
||||||
|
{
|
||||||
|
return (end_ * W25QXX_SECTOR_SIZE - (address_ + ptr_)) / sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t Logger<T>::capacity()
|
||||||
|
{
|
||||||
|
return (end_ - start_) * W25QXX_SECTOR_SIZE / sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T Logger<T>::get(std::size_t i)
|
||||||
|
{
|
||||||
|
uint32_t address = start_ * W25QXX_SECTOR_SIZE + i * sizeof(T);
|
||||||
|
|
||||||
|
// If the requested element is in the cache, read it from there.
|
||||||
|
if (address / W25QXX_PAGE_SIZE == address_ / W25QXX_PAGE_SIZE)
|
||||||
|
{
|
||||||
|
uint8_t * ptr = buffer_ + address % W25QXX_PAGE_SIZE;
|
||||||
|
return *reinterpret_cast<T*>(ptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8_t buffer[sizeof(T)];
|
||||||
|
flash_->readData(address, buffer, sizeof(T));
|
||||||
|
return *reinterpret_cast<T*>(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace sta
|
||||||
|
|
||||||
|
#endif // STA_UTILS_LOGGER_TPP
|
161
src/w25qxx.cpp
161
src/w25qxx.cpp
@ -9,17 +9,21 @@
|
|||||||
|
|
||||||
namespace sta
|
namespace sta
|
||||||
{
|
{
|
||||||
W25Qxx::W25Qxx(SPIDevice * device, DelayUsFunc delay, AddressMode addrMode /* = AddressMode::_24BIT */)
|
W25Qxx::W25Qxx(SPIDevice * device, DelayUsFunc delay, AddressMode addrMode /* = AddressMode::_24BIT */, Mutex * mutex /* = Mutex::ALWAYS_FREE */)
|
||||||
: device_{device},
|
: device_{device},
|
||||||
delay_{delay},
|
delay_{delay},
|
||||||
|
mutex_{mutex},
|
||||||
state_{ChipState::POWERED_DOWN},
|
state_{ChipState::POWERED_DOWN},
|
||||||
addrMode_{addrMode}
|
addrMode_{addrMode}
|
||||||
{
|
{
|
||||||
STA_ASSERT(device != nullptr);
|
STA_ASSERT(device != nullptr);
|
||||||
|
STA_ASSERT(mutex != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t W25Qxx::init()
|
uint8_t W25Qxx::init()
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
powerDown();
|
powerDown();
|
||||||
delay_(5);
|
delay_(5);
|
||||||
|
|
||||||
@ -46,69 +50,9 @@ namespace sta
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t W25Qxx::getChunkBytes(ChunkSize size)
|
|
||||||
{
|
|
||||||
switch (size)
|
|
||||||
{
|
|
||||||
case ChunkSize::PAGE:
|
|
||||||
return W25QXX_PAGE_SIZE;
|
|
||||||
|
|
||||||
case ChunkSize::SECTOR:
|
|
||||||
return W25QXX_SECTOR_SIZE;
|
|
||||||
|
|
||||||
case ChunkSize::BLOCK_32KB:
|
|
||||||
return W25QXX_BLOCK_32_KB_SIZE;
|
|
||||||
|
|
||||||
case ChunkSize::BLOCK_64KB:
|
|
||||||
return W25QXX_BLOCK_64_KB_SIZE;
|
|
||||||
|
|
||||||
default:
|
|
||||||
STA_UNREACHABLE();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t W25Qxx::findLast(std::function<bool(uint8_t*)> criterion, ChunkSize size)
|
|
||||||
{
|
|
||||||
uint32_t bytes = getChunkBytes(size);
|
|
||||||
|
|
||||||
uint32_t left = 0;
|
|
||||||
uint32_t right = (addrMode_ == AddressMode::_32BIT ? W25QXX_32B_MEM_SIZE : W25QXX_24B_MEM_SIZE) / bytes;
|
|
||||||
uint32_t middle;
|
|
||||||
|
|
||||||
uint8_t * buffer = new uint8_t[bytes];
|
|
||||||
|
|
||||||
while (left < right)
|
|
||||||
{
|
|
||||||
middle = (left + right) / 2;
|
|
||||||
STA_DEBUG_PRINTF("left=%d, middle=%d, right=%d", left, middle, right);
|
|
||||||
readData(middle * bytes, buffer, bytes);
|
|
||||||
|
|
||||||
if (criterion(buffer))
|
|
||||||
{
|
|
||||||
left = middle + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
right = middle - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
middle = (left + right) / 2;
|
|
||||||
|
|
||||||
readData(middle * bytes, buffer, bytes);
|
|
||||||
if (criterion(buffer))
|
|
||||||
{
|
|
||||||
middle += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] buffer;
|
|
||||||
|
|
||||||
return middle * bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t W25Qxx::setAddressMode(AddressMode addrMode)
|
uint8_t W25Qxx::setAddressMode(AddressMode addrMode)
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
busWrite(W25QXX_4_BYTE_ADDR_ENABLE);
|
busWrite(W25QXX_4_BYTE_ADDR_ENABLE);
|
||||||
|
|
||||||
while (isBusy()) {}
|
while (isBusy()) {}
|
||||||
@ -118,6 +62,8 @@ namespace sta
|
|||||||
|
|
||||||
AddressMode W25Qxx::getAddressMode()
|
AddressMode W25Qxx::getAddressMode()
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
readStatusRegister(3, &status);
|
readStatusRegister(3, &status);
|
||||||
|
|
||||||
@ -126,6 +72,8 @@ namespace sta
|
|||||||
|
|
||||||
uint8_t W25Qxx::getChipID()
|
uint8_t W25Qxx::getChipID()
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
uint8_t buffer[4];
|
uint8_t buffer[4];
|
||||||
busRead(W25QXX_RELEASE_POWER_DOWN, buffer, 3);
|
busRead(W25QXX_RELEASE_POWER_DOWN, buffer, 3);
|
||||||
|
|
||||||
@ -134,6 +82,8 @@ namespace sta
|
|||||||
|
|
||||||
uint8_t W25Qxx::getManufacturerID()
|
uint8_t W25Qxx::getManufacturerID()
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
uint8_t dummy[3] = {0, 0, 0};
|
uint8_t dummy[3] = {0, 0, 0};
|
||||||
uint8_t id[2] = {0, 0};
|
uint8_t id[2] = {0, 0};
|
||||||
|
|
||||||
@ -144,6 +94,8 @@ namespace sta
|
|||||||
|
|
||||||
uint64_t W25Qxx::getUniqueID()
|
uint64_t W25Qxx::getUniqueID()
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
uint8_t dummy[4];
|
uint8_t dummy[4];
|
||||||
uint8_t id[8];
|
uint8_t id[8];
|
||||||
|
|
||||||
@ -151,7 +103,7 @@ namespace sta
|
|||||||
|
|
||||||
uint64_t id_complete = 0;
|
uint64_t id_complete = 0;
|
||||||
|
|
||||||
for (size_t i; i < 8; i++)
|
for (size_t i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
id_complete |= id[i] << (7-i) * 8;
|
id_complete |= id[i] << (7-i) * 8;
|
||||||
}
|
}
|
||||||
@ -161,6 +113,8 @@ namespace sta
|
|||||||
|
|
||||||
bool W25Qxx::isBusy()
|
bool W25Qxx::isBusy()
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
readStatusRegister(1, &status);
|
readStatusRegister(1, &status);
|
||||||
|
|
||||||
@ -169,27 +123,38 @@ namespace sta
|
|||||||
|
|
||||||
uint8_t W25Qxx::readData(uint32_t address, 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 */)
|
||||||
{
|
{
|
||||||
uint8_t instruction = fast ? W25QXX_FAST_READ : W25QXX_READ;
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
// In fast mode we have to send a 8 dummy clock cycles first.
|
uint8_t instruction = fast ? W25QXX_FAST_READ : W25QXX_READ;
|
||||||
if (fast)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
while (isBusy()) {}
|
while (isBusy()) {}
|
||||||
|
|
||||||
// Depending on address mode, send 3 bytes or 4 bytes.
|
// Depending on address mode, send 3 bytes or 4 bytes.
|
||||||
if (addrMode_ == AddressMode::_32BIT)
|
if (addrMode_ == AddressMode::_32BIT)
|
||||||
{
|
{
|
||||||
uint8_t addrBuffer[4] = {
|
if (fast)
|
||||||
(uint8_t) (address >> 24),
|
{
|
||||||
(uint8_t) (address >> 16),
|
uint8_t addrBuffer[5] = {
|
||||||
(uint8_t) (address >> 8),
|
(uint8_t) (address >> 24),
|
||||||
(uint8_t) (address)
|
(uint8_t) (address >> 16),
|
||||||
};
|
(uint8_t) (address >> 8),
|
||||||
|
(uint8_t) (address),
|
||||||
|
0x00 // Dummy byte for fast mode
|
||||||
|
};
|
||||||
|
|
||||||
return busRead(instruction, buffer, length, addrBuffer, 4);
|
return busRead(instruction, buffer, length, addrBuffer, 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8_t addrBuffer[5] = {
|
||||||
|
(uint8_t) (address >> 24),
|
||||||
|
(uint8_t) (address >> 16),
|
||||||
|
(uint8_t) (address >> 8),
|
||||||
|
(uint8_t) (address)
|
||||||
|
};
|
||||||
|
|
||||||
|
return busRead(instruction, buffer, length, addrBuffer, 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -219,6 +184,8 @@ namespace sta
|
|||||||
|
|
||||||
uint8_t W25Qxx::pageProgram(uint32_t address, uint8_t * buffer, size_t length)
|
uint8_t W25Qxx::pageProgram(uint32_t address, uint8_t * buffer, size_t length)
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
STA_ASSERT(length <= W25QXX_PAGE_SIZE);
|
STA_ASSERT(length <= W25QXX_PAGE_SIZE);
|
||||||
while (isBusy()) {}
|
while (isBusy()) {}
|
||||||
|
|
||||||
@ -254,6 +221,8 @@ namespace sta
|
|||||||
|
|
||||||
uint8_t W25Qxx::sectorProgram(uint32_t address, uint8_t * buffer, size_t length)
|
uint8_t W25Qxx::sectorProgram(uint32_t address, uint8_t * buffer, size_t length)
|
||||||
{
|
{
|
||||||
|
lock_guard<Mutex>lock(*mutex_);
|
||||||
|
|
||||||
STA_ASSERT(length <= W25QXX_SECTOR_SIZE);
|
STA_ASSERT(length <= W25QXX_SECTOR_SIZE);
|
||||||
|
|
||||||
uint32_t nPages = length / W25QXX_PAGE_SIZE;
|
uint32_t nPages = length / W25QXX_PAGE_SIZE;
|
||||||
@ -399,8 +368,15 @@ namespace sta
|
|||||||
return (0x02 & status) == 0x02;
|
return (0x02 & status) == 0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t W25Qxx::sectorErase(uint32_t address)
|
uint8_t W25Qxx::sectorErase(uint32_t address, bool blocking /* = false */)
|
||||||
{
|
{
|
||||||
|
if (address % W25QXX_SECTOR_SIZE != 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isBusy()) {}
|
||||||
|
|
||||||
if (!writeEnable())
|
if (!writeEnable())
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
@ -410,31 +386,36 @@ namespace sta
|
|||||||
|
|
||||||
if (addrMode_ == AddressMode::_32BIT)
|
if (addrMode_ == AddressMode::_32BIT)
|
||||||
{
|
{
|
||||||
uint8_t addrBuffer[4] = {
|
uint8_t addrBuffer[4] = {
|
||||||
(uint8_t) (address >> 24),
|
(uint8_t) (address >> 24),
|
||||||
(uint8_t) (address >> 16),
|
(uint8_t) (address >> 16),
|
||||||
(uint8_t) (address >> 8),
|
(uint8_t) (address >> 8),
|
||||||
(uint8_t) (address)
|
(uint8_t) (address)
|
||||||
};
|
};
|
||||||
|
|
||||||
return busWrite(W25QXX_SECTOR_ERASE, addrBuffer, 4);
|
return busWrite(W25QXX_SECTOR_ERASE, addrBuffer, 4);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t addrBuffer[3] = {
|
uint8_t addrBuffer[3] = {
|
||||||
(uint8_t) (address >> 16),
|
(uint8_t) (address >> 16),
|
||||||
(uint8_t) (address >> 8),
|
(uint8_t) (address >> 8),
|
||||||
(uint8_t) (address)
|
(uint8_t) (address)
|
||||||
};
|
};
|
||||||
|
|
||||||
return busWrite(W25QXX_SECTOR_ERASE, addrBuffer, 3);
|
return busWrite(W25QXX_SECTOR_ERASE, addrBuffer, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blocking)
|
||||||
|
delay_(200*1000);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t W25Qxx::blockErase(uint32_t address, BlockSize blockSize)
|
uint8_t W25Qxx::blockErase(uint32_t address, BlockSize blockSize)
|
||||||
{
|
{
|
||||||
|
while (isBusy()) {}
|
||||||
|
|
||||||
if (!writeEnable())
|
if (!writeEnable())
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user