Merge pull request #219 from lancaster-university/microbit-mbfs
Microbit mbfs
This commit is contained in:
commit
4569369a95
|
@ -72,6 +72,35 @@ DEALINGS IN THE SOFTWARE.
|
|||
#define MICROBIT_HEAP_END (CORTEX_M0_STACK_BASE - MICROBIT_STACK_SIZE)
|
||||
#endif
|
||||
|
||||
// Defines the size of a physical FLASH page in RAM.
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 1024
|
||||
#endif
|
||||
|
||||
// Defines where in memory persistent data is stored.
|
||||
#ifndef KEY_VALUE_STORE_PAGE
|
||||
#define KEY_VALUE_STORE_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 17))
|
||||
#endif
|
||||
|
||||
#ifndef BLE_BOND_DATA_PAGE
|
||||
#define BLE_BOND_DATA_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 18))
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_SCRATCH_PAGE
|
||||
#define DEFAULT_SCRATCH_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 19))
|
||||
#endif
|
||||
|
||||
// Address of the end of the current program in FLASH memory.
|
||||
// This is recorded by the C/C++ linker, but the symbol name varies depending on which compiler is used.
|
||||
#if defined(__arm)
|
||||
extern uint32_t Image$$ER_IROM1$$RO$$Limit;
|
||||
#define FLASH_PROGRAM_END (uint32_t) (&Image$$ER_IROM1$$RO$$Limit)
|
||||
#else
|
||||
extern uint32_t __etext;
|
||||
#define FLASH_PROGRAM_END (uint32_t) (&__etext)
|
||||
#endif
|
||||
|
||||
|
||||
// Enables or disables the MicroBitHeapllocator. Note that if disabled, no reuse of the SRAM normally
|
||||
// reserved for SoftDevice is possible, and out of memory condition will no longer be trapped...
|
||||
// i.e. panic() will no longer be triggered on memory full conditions.
|
||||
|
@ -325,6 +354,26 @@ DEALINGS IN THE SOFTWARE.
|
|||
#define MICROBIT_DEFAULT_SERIAL_MODE SYNC_SLEEP
|
||||
#endif
|
||||
|
||||
//
|
||||
// File System configuration defaults
|
||||
//
|
||||
|
||||
//
|
||||
// Defines the logical block size for the file system.
|
||||
// Must be a factor of the physical PAGE_SIZE (ideally a power of two less).
|
||||
//
|
||||
#ifndef MBFS_BLOCK_SIZE
|
||||
#define MBFS_BLOCK_SIZE 256
|
||||
#endif
|
||||
|
||||
//
|
||||
// FileSystem writeback cache size, in bytes. Defines how many bytes will be stored
|
||||
// in RAM before being written back to FLASH. Set to zero to disable this feature.
|
||||
// Should be <= MBFS_BLOCK_SIZE.
|
||||
//
|
||||
#ifndef MBFS_CACHE_SIZE
|
||||
#define MBFS_CACHE_SIZE 16
|
||||
#endif
|
||||
|
||||
//
|
||||
// I/O Options
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
#ifndef MICROBIT_FILE_H
|
||||
#define MICROBIT_FILE_H
|
||||
|
||||
#include "mbed.h"
|
||||
#include "MicroBitConfig.h"
|
||||
#include "MicroBitFileSystem.h"
|
||||
#include "ManagedString.h"
|
||||
|
||||
#define READ MB_READ
|
||||
#define WRITE MB_WRITE
|
||||
#define READ_AND_WRITE READ | WRITE
|
||||
#define CREATE MB_CREAT
|
||||
|
||||
class MicroBitFile
|
||||
{
|
||||
|
||||
int fileHandle;
|
||||
ManagedString fileName;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates an instance of a MicroBitFile and creates a new file if required.
|
||||
*
|
||||
* @param fileName the name of the file to create/open.
|
||||
*
|
||||
* @param mode One of: READ, WRITE, READ_AND_WRITE. Defaults to READ_AND_WRITE.
|
||||
*/
|
||||
MicroBitFile(ManagedString fileName, int mode = READ | WRITE | CREATE);
|
||||
|
||||
/**
|
||||
* Seeks to a position in this MicroBitFile instance from the beginning of the file.
|
||||
*
|
||||
* @param offset the number of bytes to seek, relative to the beginning of the file.
|
||||
*
|
||||
* @return the new seek position, MICROBIT_NOT_SUPPORTED if the current file handle is invalid,
|
||||
* MICROBIT_INVALID_PARAMETER if the given offset is negative.
|
||||
*/
|
||||
int setPosition(int position);
|
||||
|
||||
/**
|
||||
* Returns the current position of the seek head for the current file.
|
||||
*
|
||||
* @return the new seek position, MICROBIT_NOT_SUPPORTED if the current file handle is invalid.
|
||||
*/
|
||||
int getPosition();
|
||||
|
||||
/**
|
||||
* Writes the given bytes to this MicroBitFile instance at the current position.
|
||||
*
|
||||
* @param bytes a pointer to the bytes to write to this file.
|
||||
*
|
||||
* @param len the number of bytes to write.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid, MICROBIT_INVALID_PARAMETER if bytes is invalid, or
|
||||
* len is negative.
|
||||
*/
|
||||
int write(const char *bytes, int len);
|
||||
|
||||
/**
|
||||
* Writes the given ManagedString to this MicroBitFile instance at the current position.
|
||||
*
|
||||
* @param s The ManagedString to write to this file.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid, MICROBIT_INVALID_PARAMETER if bytes is invalid, or
|
||||
* len is negative.
|
||||
*/
|
||||
int write(ManagedString s);
|
||||
|
||||
/**
|
||||
* Reads a single character from the file at the current position.
|
||||
*
|
||||
* @return the character, or MICROBIT_NOT_SUPPORTED if the current file handle
|
||||
* is invalid.
|
||||
*/
|
||||
int read();
|
||||
|
||||
/**
|
||||
* Reads from the file into a given buffer.
|
||||
*
|
||||
* @param buffer a pointer to the buffer where data will be stored.
|
||||
*
|
||||
* @param size the number of bytes that can be safely stored in the buffer.
|
||||
*
|
||||
* @return the number of bytes read, or MICROBIT_INVALID_PARAMETER if buffer is
|
||||
* invalid, or the size given is less than 0.
|
||||
*/
|
||||
int read(char *buffer, int size);
|
||||
|
||||
/**
|
||||
* Reads from the current MicroBitFile into a given buffer.
|
||||
*
|
||||
* @param size the number of bytes to be read from the file.
|
||||
*
|
||||
* @return a ManagedString containing the requested bytes, oran empty ManagedString
|
||||
* on error.
|
||||
*/
|
||||
ManagedString read(int size);
|
||||
|
||||
/**
|
||||
* Removes this MicroBitFile from the MicroBitFileSystem.
|
||||
*
|
||||
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if the given filename
|
||||
* does not exist, MICROBIT_CANCELLED if something went wrong.
|
||||
*/
|
||||
int remove();
|
||||
|
||||
/**
|
||||
* Seeks to the end of the file, and appends the given ManagedString to this MicroBitFile instance.
|
||||
*
|
||||
* @param bytes a pointer to the bytes to write to this file.
|
||||
*
|
||||
* @param len the number of bytes to write.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid or this file was not opened in WRITE mode.
|
||||
*/
|
||||
int append(const char *bytes, int len);
|
||||
|
||||
/**
|
||||
* Seeks to the end of the file, and appends the given ManagedString to this MicroBitFile instance.
|
||||
*
|
||||
* @param s The ManagedString to write to this file.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid or this file was not opened in WRITE mode.
|
||||
*/
|
||||
int append(ManagedString s);
|
||||
|
||||
/**
|
||||
* Determines if this MicroBitFile instance refers to a valid, open file.
|
||||
*
|
||||
* @return true if this file is valid, false otherwise.
|
||||
*
|
||||
*/
|
||||
bool isValid();
|
||||
|
||||
/**
|
||||
* Returns the handle used by this MicroBitFile instance.
|
||||
*
|
||||
* @note This member function will also inform the user of any errors encountered
|
||||
* during the opening of this MicroBitFile. At open, the handle is set
|
||||
* to the return value from MicroBitFileSystem.open().
|
||||
*/
|
||||
int getHandle();
|
||||
|
||||
/**
|
||||
* Closes this MicroBitFile instance
|
||||
*
|
||||
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the file handle
|
||||
* is invalid.
|
||||
*
|
||||
* @note MicroBitFiles are opened at construction and are implicitly closed at
|
||||
* destruction. They can be closed explicitly using this member function.
|
||||
*/
|
||||
int close();
|
||||
|
||||
/**
|
||||
* Writes back all state associated with the given file to FLASH memory,
|
||||
* leaving the file open.
|
||||
*
|
||||
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the file system has not
|
||||
* been initialised or if this file is invalid.
|
||||
*/
|
||||
int flush();
|
||||
|
||||
/**
|
||||
* Destructor for MicroBitFile. Implicitly closes the current file.
|
||||
*/
|
||||
~MicroBitFile();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,499 @@
|
|||
#ifndef MICROBIT_FILE_SYSTEM_H
|
||||
#define MICROBIT_FILE_SYSTEM_H
|
||||
|
||||
#include "MicroBitConfig.h"
|
||||
#include "MicroBitFlash.h"
|
||||
|
||||
|
||||
// Configuration options.
|
||||
#define MBFS_FILENAME_LENGTH 16
|
||||
#define MBFS_MAGIC "MICROBIT_FS_1_0"
|
||||
|
||||
// open() flags.
|
||||
#define MB_READ 0x01
|
||||
#define MB_WRITE 0x02
|
||||
#define MB_CREAT 0x04
|
||||
#define MB_APPEND 0x08
|
||||
|
||||
// seek() flags.
|
||||
#define MB_SEEK_SET 0x01
|
||||
#define MB_SEEK_END 0x02
|
||||
#define MB_SEEK_CUR 0x04
|
||||
|
||||
// Status flags
|
||||
#define MBFS_STATUS_INITIALISED 0x01
|
||||
|
||||
// FileTable codes
|
||||
#define MBFS_UNUSED 0xFFFF
|
||||
#define MBFS_EOF 0xEFFF
|
||||
#define MBFS_DELETED 0x0000
|
||||
|
||||
// DirectorEntry flags
|
||||
#define MBFS_DIRECTORY_ENTRY_FREE 0x8000
|
||||
#define MBFS_DIRECTORY_ENTRY_VALID 0x4000
|
||||
#define MBFS_DIRECTORY_ENTRY_DIRECTORY 0x2000
|
||||
#define MBFS_DIRECTORY_ENTRY_NEW 0xffff
|
||||
#define MBFS_DIRECTORY_ENTRY_DELETED 0x0000
|
||||
|
||||
// Enumeration of BLOCK YPES
|
||||
#define MBFS_BLOCK_TYPE_FILE 1
|
||||
#define MBFS_BLOCK_TYPE_DIRECTORY 2
|
||||
#define MBFS_BLOCK_TYPE_FILETABLE 3
|
||||
|
||||
|
||||
//
|
||||
// Every file in the file system has a file descriptor.
|
||||
// These are held in directory entries, using the following
|
||||
// structure.
|
||||
//
|
||||
struct DirectoryEntry
|
||||
{
|
||||
char file_name[MBFS_FILENAME_LENGTH]; // Name of the file.
|
||||
uint16_t first_block; // Logical block address of the start of the file.
|
||||
uint16_t flags; // Status of the file.
|
||||
uint32_t length; // Length of the file in bytes.
|
||||
};
|
||||
|
||||
//
|
||||
// A directory is a list of DirectoryEntry structures:
|
||||
//
|
||||
struct Directory
|
||||
{
|
||||
DirectoryEntry entry[0];
|
||||
};
|
||||
|
||||
//
|
||||
// A FileDescriptor holds contextual information needed for each OPEN file.
|
||||
//
|
||||
struct FileDescriptor
|
||||
{
|
||||
// read/write/creat flags.
|
||||
uint16_t flags;
|
||||
|
||||
// FileDescriptor id
|
||||
uint16_t id;
|
||||
|
||||
// current file position, in bytes.
|
||||
uint32_t seek;
|
||||
|
||||
// the current file size. n.b. this may be different to that stored in the DirectoryEntry.
|
||||
uint32_t length;
|
||||
|
||||
// the directory entry of this file.
|
||||
DirectoryEntry *dirent;
|
||||
|
||||
// the directory entry of our parent directory.
|
||||
DirectoryEntry *directory;
|
||||
|
||||
// We maintain a chain of open file descriptors. Reference to the next FileDescriptor in the chain.
|
||||
FileDescriptor *next;
|
||||
|
||||
// Optional writeback cache, to minimise FLASH write operations at the expense of RAM.
|
||||
uint16_t cacheLength;
|
||||
uint8_t cache[MBFS_CACHE_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class definition for the MicroBit File system
|
||||
*
|
||||
* Microbit file system class. Presents a POSIX-like interface consisting of:
|
||||
* - open()
|
||||
* - close()
|
||||
* - read()
|
||||
* - write()
|
||||
* - seek()
|
||||
* - remove()
|
||||
*
|
||||
* Only a single instance shoud exist at any given time.
|
||||
*/
|
||||
class MicroBitFileSystem
|
||||
{
|
||||
private:
|
||||
|
||||
// Status flags
|
||||
uint32_t status;
|
||||
|
||||
// The instance of MicroBitFlash - the interface used for all flash writes/erasures
|
||||
MicroBitFlash flash;
|
||||
|
||||
// Total Number of logical pages available for file data (including the file table)
|
||||
int fileSystemSize;
|
||||
|
||||
// Memory address of the start of the file system.
|
||||
uint16_t* fileSystemTable;
|
||||
|
||||
// Size of the file table (blocks)
|
||||
uint16_t fileSystemTableSize;
|
||||
|
||||
// Cache of the last block allocated. Used to enable round robin use of blocks.
|
||||
uint16_t lastBlockAllocated;
|
||||
|
||||
// Reference to the root directory of the file system.
|
||||
DirectoryEntry *rootDirectory;
|
||||
|
||||
// Chain of open files.
|
||||
FileDescriptor *openFiles;
|
||||
|
||||
/**
|
||||
* Initialize the flash storage system
|
||||
*
|
||||
* The file system is located dynamically, based on where the program code
|
||||
* and code data finishes. This avoids having to allocate a fixed flash
|
||||
* region for builds even without MicroBitFileSystem.
|
||||
*
|
||||
* This method checks if the file system already exists, and loads it.
|
||||
* If not, it will determines the optimal size of the file system, if necessary, and format the space
|
||||
*
|
||||
* @return MICROBIT_OK on success, or an error code.
|
||||
*/
|
||||
int init(uint32_t flashStart, int flashPages);
|
||||
|
||||
/**
|
||||
* Attempts to detect and load an existing file system.
|
||||
*
|
||||
* @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the file system could not be found.
|
||||
*/
|
||||
int load();
|
||||
|
||||
/**
|
||||
* Allocate a free logical block.
|
||||
* A round robin algorithm is used to even out the wear on the physical device.
|
||||
* @return NULL on error, page address on success
|
||||
*/
|
||||
uint16_t getFreeBlock();
|
||||
|
||||
/**
|
||||
* Allocates a free physical block.
|
||||
* A round robin algorithm is used to even out the wear on the physical device.
|
||||
* @return NULL on error, page address on success
|
||||
*/
|
||||
uint32_t* getFreePage();
|
||||
|
||||
/**
|
||||
* Retrieve the DirectoryEntry assoiated with the given file's DIRECTORY (not the file itself).
|
||||
*
|
||||
* @param filename A fully qualified filename, from the root.
|
||||
* @return A pointer to the DirectoryEntry for the given file's directory, or NULL if no entry is found.
|
||||
*/
|
||||
DirectoryEntry* getDirectoryOf(char const * filename);
|
||||
|
||||
/**
|
||||
* Retrieve the DirectoryEntry for the given filename.
|
||||
*
|
||||
* @param filename A fully or partially qualified filename.
|
||||
* @param directory The directory to search. If ommitted, the root directory will be used.
|
||||
* @return A pointer to the DirectoryEntry for the given file, or NULL if no entry is found.
|
||||
*/
|
||||
DirectoryEntry* getDirectoryEntry(char const * filename, const DirectoryEntry *directory = NULL);
|
||||
|
||||
/**
|
||||
* Create a new DirectoryEntry with the given filename and flags.
|
||||
*
|
||||
* @param filename A fully or partially qualified filename.
|
||||
* @param directory The directory in which to create the entry
|
||||
* @param isDirectory true if the entry being created is itself a directory
|
||||
*
|
||||
* @return A pointer to the new DirectoryEntry for the given file, or NULL if it was not possible to allocated resources.
|
||||
*/
|
||||
DirectoryEntry* createFile(char const * filename, DirectoryEntry *directory, bool isDirectory);
|
||||
|
||||
/**
|
||||
* Allocate a free DiretoryEntry in the given directory, extending and refreshing the directory block if necessary.
|
||||
*
|
||||
* @param directory The directory to add a DirectoryEntry to
|
||||
* @return A pointer to the new DirectoryEntry for the given file, or NULL if it was not possible to allocated resources.
|
||||
*/
|
||||
DirectoryEntry* createDirectoryEntry(DirectoryEntry *directory);
|
||||
|
||||
/**
|
||||
* Refresh the physical page associated with the given block.
|
||||
* Any logical blocks marked for deletion on that page are recycled.
|
||||
*
|
||||
* @param block the block to recycle.
|
||||
* @param type One of MBFS_BLOCK_TYPE_FILE, MBFS_BLOCK_TYPE_DIRECTORY, MBFS_BLOCK_TYPE_FILETABLE.
|
||||
* Erases and regenerates the given block, recycling any data marked for deletion.
|
||||
* @return MICROBIT_OK on success.
|
||||
*/
|
||||
int recycleBlock(uint16_t block, int type = MBFS_BLOCK_TYPE_FILE);
|
||||
|
||||
/**
|
||||
* Refresh the physical pages associated with the file table.
|
||||
* Any logical blocks marked for deletion on those pages are recycled back to UNUSED.
|
||||
*
|
||||
* @return MICROBIT_OK on success.
|
||||
*/
|
||||
int recycleFileTable();
|
||||
|
||||
/**
|
||||
* Retrieve a memory pointer for the start of the physical memory page containing the given block.
|
||||
*
|
||||
* @param block A valid block number.
|
||||
*
|
||||
* @return A pointer to the physical page in FLASH memory holding the given block.
|
||||
*/
|
||||
uint32_t *getPage(uint16_t block);
|
||||
|
||||
/**
|
||||
* Retrieve a memory pointer for the start of the given block.
|
||||
*
|
||||
* @param block A valid block number.
|
||||
*
|
||||
* @return A pointer to the FLASH memory associated with the given block.
|
||||
*/
|
||||
uint32_t *getBlock(uint16_t block);
|
||||
|
||||
/**
|
||||
* Retrieve the next block in a chain.
|
||||
*
|
||||
* @param block A valid block number.
|
||||
*
|
||||
* @return The block number of the next block in the file.
|
||||
*/
|
||||
uint16_t getNextFileBlock(uint16_t block);
|
||||
|
||||
/**
|
||||
* Determine the logical block that contains the given address.
|
||||
*
|
||||
* @param address A valid memory location within the file system space.
|
||||
*
|
||||
* @return The block number containing the given address.
|
||||
*/
|
||||
uint16_t getBlockNumber(void *address);
|
||||
|
||||
/**
|
||||
* Determine the number of logical blocks required to hold the file table.
|
||||
*
|
||||
* @return The number of logical blocks required to hold the file table.
|
||||
*/
|
||||
uint16_t calculateFileTableSize();
|
||||
|
||||
/*
|
||||
* Update a file table entry to a given value.
|
||||
*
|
||||
* @param block The block to update.
|
||||
* @param value The value to store in the file table.
|
||||
* @return MICROBIT_OK on success.
|
||||
*/
|
||||
int fileTableWrite(uint16_t block, uint16_t value);
|
||||
|
||||
/**
|
||||
* Searches the list of open files for one with the given identifier.
|
||||
*
|
||||
* @param fd A previsouly opened file identifier, as returned by open().
|
||||
* @param remove Remove the file descriptor from the list if true.
|
||||
* @return A FileDescriptor matching the given ID, or NULL if the file is not open.
|
||||
*/
|
||||
FileDescriptor* getFileDescriptor(int fd, bool remove = false);
|
||||
|
||||
/**
|
||||
* Initialises a new file system
|
||||
*
|
||||
* @return MICROBIT_OK on success, or an error code.
|
||||
*/
|
||||
int format();
|
||||
|
||||
/**
|
||||
* Flush a given file's cache back to FLASH memory.
|
||||
*
|
||||
* @param file File descriptor to flush.
|
||||
* @return The number of bytes written.
|
||||
*
|
||||
*/
|
||||
int writeBack(FileDescriptor *file);
|
||||
|
||||
/**
|
||||
* Write a given buffer to the file provided.
|
||||
*
|
||||
* @param file FileDescriptor of the file to write
|
||||
* @param buffer The start of the buffer to write
|
||||
* @param length The number of bytes to write
|
||||
* @return The number of bytes written.
|
||||
*/
|
||||
int writeBuffer(FileDescriptor *file, uint8_t* buffer, int length);
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the given filename is a valid filename for use in MicroBitFileSystem.
|
||||
* valid filenames must be >0 characters in lenght, NULL temrinated and contain
|
||||
* only printable characters.
|
||||
*
|
||||
* @param name The name of the file to test.
|
||||
* @return true if the filename is valid, false otherwsie.
|
||||
*/
|
||||
bool isValidFilename(const char *name);
|
||||
|
||||
public:
|
||||
|
||||
static MicroBitFileSystem *defaultFileSystem;
|
||||
|
||||
/**
|
||||
* Constructor. Creates an instance of a MicroBitFileSystem.
|
||||
*/
|
||||
MicroBitFileSystem(uint32_t flashStart = 0, int flashPages = 0);
|
||||
|
||||
/**
|
||||
* Open a new file, and obtain a new file handle (int) to
|
||||
* read/write/seek the file. The flags are:
|
||||
* - MB_READ : read from the file.
|
||||
* - MB_WRITE : write to the file.
|
||||
* - MB_CREAT : create a new file, if it doesn't already exist.
|
||||
*
|
||||
* If a file is opened that doesn't exist, and MB_CREAT isn't passed,
|
||||
* an error is returned, otherwise the file is created.
|
||||
*
|
||||
* @param filename name of the file to open, must contain only printable characters.
|
||||
* @param flags One or more of MB_READ, MB_WRITE or MB_CREAT.
|
||||
* @return return the file handle,MICROBIT_NOT_SUPPORTED if the file system has
|
||||
* not been initialised MICROBIT_INVALID_PARAMETER if the filename is
|
||||
* too large, MICROBIT_NO_RESOURCES if the file system is full.
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f();
|
||||
* int fd = f.open("test.txt", MB_WRITE|MB_CREAT);
|
||||
* if(fd<0)
|
||||
* print("file open error");
|
||||
* @endcode
|
||||
*/
|
||||
int open(char const * filename, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Writes back all state associated with the given file to FLASH memory,
|
||||
* leaving the file open.
|
||||
*
|
||||
* @param fd file descriptor - obtained with open().
|
||||
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the file system has not
|
||||
* been initialised, MICROBIT_INVALID_PARAMETER if the given file handle
|
||||
* is invalid.
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f();
|
||||
* int fd = f.open("test.txt", MB_READ);
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* f.flush(fd);
|
||||
* @endcode
|
||||
*/
|
||||
int flush(int fd);
|
||||
|
||||
/**
|
||||
* Close the specified file handle.
|
||||
* File handle resources are then made available for future open() calls.
|
||||
*
|
||||
* close() must be called to ensure all pending data is written back to FLASH memory.
|
||||
*
|
||||
* @param fd file descriptor - obtained with open().
|
||||
* @return non-zero on success, MICROBIT_NOT_SUPPORTED if the file system has not
|
||||
* been initialised, MICROBIT_INVALID_PARAMETER if the given file handle
|
||||
* is invalid.
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f();
|
||||
* int fd = f.open("test.txt", MB_READ);
|
||||
* if(!f.close(fd))
|
||||
* print("error closing file.");
|
||||
* @endcode
|
||||
*/
|
||||
int close(int fd);
|
||||
|
||||
/**
|
||||
* Move the current position of a file handle, to be used for
|
||||
* subsequent read/write calls.
|
||||
*
|
||||
* The offset modifier can be:
|
||||
* - MB_SEEK_SET set the absolute seek position.
|
||||
* - MB_SEEK_CUR set the seek position based on the current offset.
|
||||
* - MB_SEEK_END set the seek position from the end of the file.
|
||||
* E.g. to seek to 2nd-to-last byte, use offset=-1.
|
||||
*
|
||||
* @param fd file handle, obtained with open()
|
||||
* @param offset new offset, can be positive/negative.
|
||||
* @param flags
|
||||
* @return new offset position on success, MICROBIT_NOT_SUPPORTED if the file system
|
||||
* is not intiialised, MICROBIT_INVALID_PARAMETER if the flag given is invalid
|
||||
* or the file handle is invalid.
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f;
|
||||
* int fd = f.open("test.txt", MB_READ);
|
||||
* f.seek(fd, -100, MB_SEEK_END); //100 bytes before end of file.
|
||||
* @endcode
|
||||
*/
|
||||
int seek(int fd, int offset, uint8_t flags);
|
||||
|
||||
/**
|
||||
* Write data to the file.
|
||||
*
|
||||
* Write from buffer, length bytes to the current seek position.
|
||||
* On each invocation to write, the seek position of the file handle
|
||||
* is incremented atomically, by the number of bytes returned.
|
||||
*
|
||||
* @param fd File handle
|
||||
* @param buffer the buffer from which to write data
|
||||
* @param size number of bytes to write
|
||||
* @return number of bytes written on success, MICROBIT_NO_RESOURCES if data did
|
||||
* not get written to flash or the file system has not been initialised,
|
||||
* or this file was not opened with the MB_WRITE flag set, MICROBIT_INVALID_PARAMETER
|
||||
* if the given file handle is invalid.
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f();
|
||||
* int fd = f.open("test.txt", MB_WRITE);
|
||||
* if(f.write(fd, "hello!", 7) != 7)
|
||||
* print("error writing");
|
||||
* @endcode
|
||||
*/
|
||||
int write(int fd, uint8_t* buffer, int size);
|
||||
|
||||
/**
|
||||
* Read data from the file.
|
||||
*
|
||||
* Read len bytes from the current seek position in the file, into the
|
||||
* buffer. On each invocation to read, the seek position of the file
|
||||
* handle is incremented atomically, by the number of bytes returned.
|
||||
*
|
||||
* @param fd File handle, obtained with open()
|
||||
* @param buffer to store data
|
||||
* @param size number of bytes to read
|
||||
* @return number of bytes read on success, MICROBIT_NOT_SUPPORTED if the file
|
||||
* system is not initialised, or this file was not opened with the
|
||||
* MB_READ flag set, MICROBIT_INVALID_PARAMETER if the given file handle
|
||||
* is invalid.
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f;
|
||||
* int fd = f.open("read.txt", MB_READ);
|
||||
* if(f.read(fd, buffer, 100) != 100)
|
||||
* print("read error");
|
||||
* @endcode
|
||||
*/
|
||||
int read(int fd, uint8_t* buffer, int size);
|
||||
|
||||
/**
|
||||
* Remove a file from the system, and free allocated assets
|
||||
* (including assigned blocks which are returned for use by other files).
|
||||
*
|
||||
* @todo the file must not already have an open file handle.
|
||||
*
|
||||
* @param filename name of the file to remove.
|
||||
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if the given filename
|
||||
* does not exist, MICROBIT_CANCELLED if something went wrong
|
||||
*
|
||||
* @code
|
||||
* MicroBitFileSystem f;
|
||||
* if(!f.remove("file.txt"))
|
||||
* printf("file could not be removed");
|
||||
* @endcode
|
||||
*/
|
||||
int remove(char const * filename);
|
||||
|
||||
/**
|
||||
* Creates a new directory with the given name and location
|
||||
*
|
||||
* @param name The fully qualified name of the new directory.
|
||||
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if the path is invalid, or MICROBT_NO_RESOURCES if the FileSystem is full.
|
||||
*/
|
||||
int createDirectory(char const *name);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef MICROBIT_FLASH_H_
|
||||
#define MICROBIT_FLASH_H_
|
||||
|
||||
#include <mbed.h>
|
||||
|
||||
#define PAGE_SIZE 1024
|
||||
|
||||
class MicroBitFlash
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
/**
|
||||
* Write to flash memory, assuming that a write is valid
|
||||
* (using need_erase).
|
||||
*
|
||||
* @param page_address address of memory to write to.
|
||||
* Must be word aligned.
|
||||
* @param buffer address to write from, must be word-aligned.
|
||||
* @param len number of uint32_t words to write.
|
||||
*/
|
||||
void flash_burn(uint32_t* page_address, uint32_t* buffer, int len);
|
||||
|
||||
|
||||
/**
|
||||
* Check if an erase is required to write to a region in flash memory.
|
||||
* This is determined if, for any byte:
|
||||
* ~O & N != 0x00
|
||||
* Where O=Original byte, N = New byte.
|
||||
*
|
||||
* @param source to write from
|
||||
* @param flash_address to write to
|
||||
* @param len number of uint8_t to check.
|
||||
* @return non-zero if erase required, zero otherwise.
|
||||
*/
|
||||
int need_erase(uint8_t* source, uint8_t* flash_addr, int len);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
MicroBitFlash();
|
||||
|
||||
/**
|
||||
* Writes the given number of bytes to the address in flash specified.
|
||||
* Neither address nor buffer need be word-aligned.
|
||||
* @param address location in flash to write to.
|
||||
* @param buffer location in memory to write from.
|
||||
* @length number of bytes to burn
|
||||
* @param scratch_addr if specified, scratch page to use. Use default
|
||||
* otherwise.
|
||||
* @return non-zero on sucess, zero on error.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* MicroBitFlash flash();
|
||||
* uint32_t word = 0x01;
|
||||
* flash.flash_write((uint8_t*)0x38000, &word, sizeof(word))
|
||||
* @endcode
|
||||
*/
|
||||
int flash_write(void* address, void* buffer, int length,
|
||||
void* scratch_addr = NULL);
|
||||
|
||||
/**
|
||||
* Erase an entire page.
|
||||
* @param page_address address of first word of page.
|
||||
*/
|
||||
void erase_page(uint32_t* page_address);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -41,6 +41,9 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
|
|||
"drivers/MicroBitStorage.cpp"
|
||||
"drivers/MicroBitThermometer.cpp"
|
||||
"drivers/TimedInterruptIn.cpp"
|
||||
"drivers/MicroBitFlash.cpp"
|
||||
"drivers/MicroBitFile.cpp"
|
||||
"drivers/MicroBitFileSystem.cpp"
|
||||
|
||||
"bluetooth/MicroBitAccelerometerService.cpp"
|
||||
"bluetooth/MicroBitBLEManager.cpp"
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
#include "MicroBitFile.h"
|
||||
#include "ErrorNo.h"
|
||||
|
||||
/**
|
||||
* Creates an instance of a MicroBitFile and creates a new file if required.
|
||||
*
|
||||
* @param fileName the name of the file to create/open.
|
||||
*
|
||||
* @param mode One of: READ, WRITE, READ_AND_WRITE. Defaults to READ_AND_WRITE.
|
||||
*/
|
||||
MicroBitFile::MicroBitFile(ManagedString fileName, int mode)
|
||||
{
|
||||
this->fileName = fileName;
|
||||
|
||||
MicroBitFileSystem* fs;
|
||||
|
||||
if(MicroBitFileSystem::defaultFileSystem == NULL)
|
||||
fs = new MicroBitFileSystem();
|
||||
else
|
||||
fs = MicroBitFileSystem::defaultFileSystem;
|
||||
|
||||
fileHandle = fs->open(fileName.toCharArray(), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to a position in this MicroBitFile instance from the beginning of the file.
|
||||
*
|
||||
* @param offset the number of bytes to seek, relative to the beginning of the file.
|
||||
*
|
||||
* @return the new seek position, MICROBIT_NOT_SUPPORTED if the current file handle is invalid,
|
||||
* MICROBIT_INVALID_PARAMETER if the given offset is negative.
|
||||
*/
|
||||
int MicroBitFile::setPosition(int offset)
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
if(offset < 0)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
return MicroBitFileSystem::defaultFileSystem->seek(fileHandle, offset, MB_SEEK_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current position of the seek head for the current file.
|
||||
*
|
||||
* @return the new seek position, MICROBIT_NOT_SUPPORTED if the current file handle is invalid.
|
||||
*/
|
||||
int MicroBitFile::getPosition()
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
return MicroBitFileSystem::defaultFileSystem->seek(fileHandle, 0, MB_SEEK_CUR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given bytes to this MicroBitFile instance at the current position.
|
||||
*
|
||||
* @param bytes a pointer to the bytes to write to this file.
|
||||
*
|
||||
* @param len the number of bytes to write.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid, MICROBIT_INVALID_PARAMETER if bytes is invalid, or
|
||||
* len is negative.
|
||||
*/
|
||||
int MicroBitFile::write(const char *bytes, int len)
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
return MicroBitFileSystem::defaultFileSystem->write(fileHandle, (uint8_t*)bytes, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given ManagedString to this MicroBitFile instance at the current position.
|
||||
*
|
||||
* @param s The ManagedString to write to this file.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid, MICROBIT_INVALID_PARAMETER if bytes is invalid, or
|
||||
* len is negative.
|
||||
*/
|
||||
int MicroBitFile::write(ManagedString s)
|
||||
{
|
||||
return write(s.toCharArray(), s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single character from the file at the current position.
|
||||
*
|
||||
* @return the character, or MICROBIT_NOT_SUPPORTED if the current file handle
|
||||
* is invalid.
|
||||
*/
|
||||
int MicroBitFile::read()
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
char c[1];
|
||||
|
||||
int ret = read(c,1);
|
||||
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
return c[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the file into a given buffer.
|
||||
*
|
||||
* @param buffer a pointer to the buffer where data will be stored.
|
||||
*
|
||||
* @param size the number of bytes that can be safely stored in the buffer.
|
||||
*
|
||||
* @return the number of bytes read, or MICROBIT_INVALID_PARAMETER if buffer is
|
||||
* invalid, or the size given is less than 0.
|
||||
*/
|
||||
int MicroBitFile::read(char *buffer, int size)
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
if(size < 0 || buffer == NULL)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
return MicroBitFileSystem::defaultFileSystem->read(fileHandle, (uint8_t*)buffer, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the current MicroBitFile into a given buffer.
|
||||
*
|
||||
* @param size the number of bytes to be read from the file.
|
||||
*
|
||||
* @return a ManagedString containing the requested bytes, oran empty ManagedString
|
||||
* on error.
|
||||
*/
|
||||
ManagedString MicroBitFile::read(int size)
|
||||
{
|
||||
char buff[size + 1];
|
||||
|
||||
buff[size] = 0;
|
||||
|
||||
int ret = read(buff, size);
|
||||
|
||||
if(ret < 0)
|
||||
return ManagedString();
|
||||
|
||||
return ManagedString(buff,ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this MicroBitFile from the MicroBitFileSystem.
|
||||
*
|
||||
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if the given filename
|
||||
* does not exist, MICROBIT_CANCELLED if something went wrong.
|
||||
*/
|
||||
int MicroBitFile::remove()
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
int ret = MicroBitFileSystem::defaultFileSystem->remove(fileName.toCharArray());
|
||||
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
fileHandle = MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to the end of the file, and appends the given ManagedString to this MicroBitFile instance.
|
||||
*
|
||||
* @param bytes a pointer to the bytes to write to this file.
|
||||
*
|
||||
* @param len the number of bytes to write.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid or this file was not opened in WRITE mode.
|
||||
*/
|
||||
int MicroBitFile::append(const char *bytes, int len)
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
int ret = MicroBitFileSystem::defaultFileSystem->seek(fileHandle, 0, MB_SEEK_END);
|
||||
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
return write(bytes,len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to the end of the file, and appends the given ManagedString to this MicroBitFile instance.
|
||||
*
|
||||
* @param s The ManagedString to write to this file.
|
||||
*
|
||||
* @return The number of bytes written, MICROBIT_NOT_SUPPORTED if the current file
|
||||
* handle is invalid or this file was not opened in WRITE mode.
|
||||
*/
|
||||
int MicroBitFile::append(ManagedString s)
|
||||
{
|
||||
return append(s.toCharArray(), s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this MicroBitFile instance refers to a valid, open file.
|
||||
*
|
||||
* @return true if this file is valid, false otherwise.
|
||||
*
|
||||
*/
|
||||
bool MicroBitFile::isValid()
|
||||
{
|
||||
return fileHandle >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handle used by this MicroBitFile instance.
|
||||
*
|
||||
* @note This member function will also inform the user of any errors encountered
|
||||
* during the opening of this MicroBitFile. At open, the handle is set
|
||||
* to the return value from MicroBitFileSystem.open().
|
||||
*/
|
||||
int MicroBitFile::getHandle()
|
||||
{
|
||||
return fileHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this MicroBitFile instance
|
||||
*
|
||||
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the file handle
|
||||
* is invalid.
|
||||
*
|
||||
* @note MicroBitFiles are opened at construction and are implicitly closed at
|
||||
* destruction. They can be closed explicitly using this member function.
|
||||
*/
|
||||
int MicroBitFile::close()
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
int ret = MicroBitFileSystem::defaultFileSystem->close(fileHandle);
|
||||
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
fileHandle = MICROBIT_NO_RESOURCES;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes back all state associated with the given file to FLASH memory,
|
||||
* leaving the file open.
|
||||
*
|
||||
* @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the file system has not
|
||||
* been initialised or if this file is invalid.
|
||||
*/
|
||||
int MicroBitFile::flush()
|
||||
{
|
||||
if(fileHandle < 0)
|
||||
return MICROBIT_NOT_SUPPORTED;
|
||||
|
||||
return MicroBitFileSystem::defaultFileSystem->flush(fileHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor for MicroBitFile. Implicitly closes the current file.
|
||||
*/
|
||||
MicroBitFile::~MicroBitFile()
|
||||
{
|
||||
close();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,162 @@
|
|||
#include "MicroBitConfig.h"
|
||||
#include "MicroBitFlash.h"
|
||||
#include "ErrorNo.h"
|
||||
#include "mbed.h" // NVIC
|
||||
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
|
||||
#define WORD_ADDR(x) (((uint32_t)x) & 0xFFFFFFFC)
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
MicroBitFlash::MicroBitFlash()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an erase is required to write to a region in flash memory.
|
||||
* This is determined if, for any byte:
|
||||
* ~O & N != 0x00
|
||||
* Where O=Original byte, N = New byte.
|
||||
*
|
||||
* @param source to write from
|
||||
* @param flash_address to write to
|
||||
* @param len number of uint8_t to check.
|
||||
* @return non-zero if erase required, zero otherwise.
|
||||
*/
|
||||
int MicroBitFlash::need_erase(uint8_t* source, uint8_t* flash_addr, int len)
|
||||
{
|
||||
// Erase is necessary if for any byte:
|
||||
// O & ~N != 0
|
||||
// Where O = original, and N = new byte.
|
||||
|
||||
for(;len>0;len--)
|
||||
{
|
||||
if((~*(flash_addr++) & *(source++)) != 0x00) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase an entire page
|
||||
* @param page_address address of first word of page
|
||||
*/
|
||||
void MicroBitFlash::erase_page(uint32_t* pg_addr)
|
||||
{
|
||||
// Turn on flash erase enable and wait until the NVMC is ready:
|
||||
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een);
|
||||
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
|
||||
|
||||
// Erase page:
|
||||
NRF_NVMC->ERASEPAGE = (uint32_t)pg_addr;
|
||||
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
|
||||
|
||||
// Turn off flash erase enable and wait until the NVMC is ready:
|
||||
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
||||
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to flash memory, assuming that a write is valid
|
||||
* (using need_erase).
|
||||
*
|
||||
* @param page_address address of memory to write to.
|
||||
* Must be word aligned.
|
||||
* @param buffer address to write from, must be word-aligned.
|
||||
* @param len number of uint32_t words to write.
|
||||
*/
|
||||
void MicroBitFlash::flash_burn(uint32_t* addr, uint32_t* buffer, int size)
|
||||
{
|
||||
|
||||
// Turn on flash write enable and wait until the NVMC is ready:
|
||||
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
|
||||
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
|
||||
|
||||
for(int i=0;i<size;i++)
|
||||
{
|
||||
*(addr+i) = *(buffer+i);
|
||||
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
|
||||
}
|
||||
|
||||
// Turn off flash write enable and wait until the NVMC is ready:
|
||||
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
|
||||
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given number of bytes to the address in flash specified.
|
||||
* Neither address nor buffer need be word-aligned.
|
||||
* @param address location in flash to write to.
|
||||
* @param buffer location in memory to write from.
|
||||
* @length number of bytes to burn
|
||||
* @param scratch_addr if specified, scratch page to use. Use default
|
||||
* otherwise.
|
||||
* @return non-zero on sucess, zero on error.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* MicroBitFlash flash();
|
||||
* uint32_t word = 0x01;
|
||||
* flash.flash_write((uint8_t*)0x38000, &word, sizeof(word))
|
||||
* @endcode
|
||||
*/
|
||||
int MicroBitFlash::flash_write(void* address, void* from_buffer,
|
||||
int length, void* scratch_addr)
|
||||
{
|
||||
|
||||
// Ensure that scratch_addr is aligned on a page boundary.
|
||||
if((uint32_t)scratch_addr & 0x3FF)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
// Locate the hardware FLASH page used by this operation.
|
||||
int page = (uint32_t)address / PAGE_SIZE;
|
||||
uint32_t* pgAddr = (uint32_t*)(page * PAGE_SIZE);
|
||||
|
||||
// offset to write from within page.
|
||||
int offset = (uint32_t)address % PAGE_SIZE;
|
||||
|
||||
uint8_t* writeFrom = (uint8_t*)pgAddr;
|
||||
int start = WORD_ADDR(offset);
|
||||
int end = WORD_ADDR((offset+length+4));
|
||||
int erase = need_erase((uint8_t *)from_buffer, (uint8_t *)address, length);
|
||||
|
||||
// Preserve the data by writing to the scratch page.
|
||||
if(erase)
|
||||
{
|
||||
if (!scratch_addr)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
this->flash_burn((uint32_t*)scratch_addr, pgAddr, PAGE_SIZE/4);
|
||||
this->erase_page(pgAddr);
|
||||
writeFrom = (uint8_t*)scratch_addr;
|
||||
start = 0;
|
||||
end = PAGE_SIZE;
|
||||
}
|
||||
|
||||
uint32_t writeWord = 0;
|
||||
|
||||
for(int i=start;i<end;i++)
|
||||
{
|
||||
int byteOffset = i%4;
|
||||
|
||||
if(i >= offset && i < (offset + length))
|
||||
{
|
||||
// Write from buffer.
|
||||
writeWord |= (((uint8_t *)from_buffer)[i-offset] << ((byteOffset)*8));
|
||||
}
|
||||
else
|
||||
{
|
||||
writeWord |= (writeFrom[i] << ((byteOffset)*8));
|
||||
}
|
||||
|
||||
if( ((i+1)%4) == 0)
|
||||
{
|
||||
this->flash_burn(pgAddr + (i/4), &writeWord, 1);
|
||||
writeWord = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return MICROBIT_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue