mirror of
https://github.com/pellepl/spiffs.git
synced 2025-08-06 14:50:17 +08:00
restructured files
This commit is contained in:
232
docs/INTEGRATION
Normal file
232
docs/INTEGRATION
Normal file
@ -0,0 +1,232 @@
|
||||
* INTEGRATING SPIFFS
|
||||
|
||||
In order to integrate spiffs to your embedded target, you will basically need:
|
||||
- A SPI flash device which your processor can communicate with
|
||||
- An implementation for reading, writing and erasing the flash
|
||||
- Memory (flash or ram) for the code
|
||||
- Memory (ram) for the stack
|
||||
|
||||
Other stuff may be needed, threaded systems might need mutexes and so on.
|
||||
|
||||
** Logical structure
|
||||
|
||||
First and foremost, one must decide how to divide up the SPI flash for spiffs.
|
||||
Having the datasheet for the actual SPI flash in hand will help. Spiffs can be
|
||||
defined to use all or only parts of the SPI flash.
|
||||
|
||||
If following seems arcane, read the "DESIGN" chapter first.
|
||||
|
||||
- Decide the logical size of blocks. This must be a multiple of the biggest
|
||||
physical SPI flash block size. To go safe, use the physical block size -
|
||||
which in many cases is 65536 bytes.
|
||||
- Decide the logical size of pages. This must be a 2nd logarithm part of the
|
||||
logical block size. To go safe, use 256 bytes to start with.
|
||||
- Decide how much of the SPI flash memory to be used for spiffs. This must be
|
||||
on logical block boundary. If unsafe, use 1 megabyte to start with.
|
||||
- Decide where on the SPI flash memory the spiffs area should start. This must
|
||||
be on physical block/sector boundary. If unsafe, use address 0.
|
||||
|
||||
** SPI flash API
|
||||
|
||||
The target must provide three functions to spiffs:
|
||||
|
||||
- s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst)
|
||||
- s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src)
|
||||
- s32_t (*spiffs_erase)(u32_t addr, u32_t size)
|
||||
|
||||
These functions define the only communication between the SPI flash and the
|
||||
spiffs stack.
|
||||
|
||||
On success these must return 0 (or SPIFFS_OK). Anything else will be considered
|
||||
an error.
|
||||
|
||||
The size for read and write requests will never exceed the logical page size,
|
||||
but it may be less.
|
||||
|
||||
The address and size on erase requests will always be on physical block size
|
||||
boundaries.
|
||||
|
||||
** Mount specification
|
||||
|
||||
In spiffs.h, there is a SPIFFS_mount function defined, used to mount spiffs on
|
||||
the SPI flash.
|
||||
|
||||
s32_t SPIFFS_mount(
|
||||
spiffs *fs,
|
||||
spiffs_config *config,
|
||||
u8_t *work,
|
||||
u8_t *fd_space,
|
||||
u32_t fd_space_size,
|
||||
void *cache,
|
||||
u32_t cache_size,
|
||||
spiffs_check_callback check_cb_f)
|
||||
|
||||
- fs Points to a spiffs struct. This may be totally uninitialized.
|
||||
- config Points to a spiffs_config struct. This struct must be
|
||||
initialized when mounting. See below.
|
||||
- work A ram memory buffer being double the size of the logical page
|
||||
size. This buffer is used excessively by the spiffs stack. If
|
||||
logical page size is 256, this buffer must be 512 bytes.
|
||||
- fd_space A ram memory buffer used for file descriptors.
|
||||
- fd_space_size The size of the file descriptor buffer. A file descriptor
|
||||
normally is around 32 bytes depending on the build config -
|
||||
the bigger the buffer, the more file descriptors are
|
||||
available.
|
||||
- cache A ram memory buffer used for cache. Ignored if cache is
|
||||
disabled in build config.
|
||||
- cache_size The size of the cache buffer. Ignored if cache is disabled in
|
||||
build config. One cache page will be slightly larger than the
|
||||
logical page size. The more ram, the more cache pages, the
|
||||
quicker the system.
|
||||
- check_cb_f Callback function for monitoring spiffs consistency checks and
|
||||
mending operations. May be null.
|
||||
|
||||
The config struct must be initialized prior to mounting. One must always
|
||||
define the SPI flash access functions:
|
||||
|
||||
spiffs_config.hal_read_f - pointing to the function reading the SPI flash
|
||||
spiffs_config.hal_write_f - pointing to the function writing the SPI flash
|
||||
spiffs_config.hal_erase_f - pointing to the function erasing the SPI flash
|
||||
|
||||
|
||||
|
||||
spiffs_config.phys_size - the physical number of bytes accounted for
|
||||
spiffs on the SPI flash
|
||||
spiffs_config.phys_addr - the physical starting address on the SPI flash
|
||||
|
||||
TODO
|
||||
|
||||
** Build config
|
||||
|
||||
makefile: The files needed to be compiled to your target resides in files.mk to
|
||||
be included in your makefile, either by cut and paste or by inclusion.
|
||||
|
||||
Types: spiffs uses the types u8_t, s8_t, u16_t, s16_t, u32_t, s32_t; these must
|
||||
be typedeffed.
|
||||
|
||||
spiffs_config.h: you also need to define a spiffs_config.h header. Example of
|
||||
this is found in the default/ directory.
|
||||
|
||||
|
||||
* QUICK AND DIRTY INTEGRATION EXAMPLE
|
||||
|
||||
So, assume you're running a Cortex-M3 board with a 2 MB SPI flash on it. The
|
||||
SPI flash has 64kB blocks. Your project is built using gnumake, and now you
|
||||
want to try things out.
|
||||
|
||||
First, you simply copy the files named in files.mk to your own source folder. Then
|
||||
you point out these files in your make script for compilation.
|
||||
|
||||
Also copy the spiffs_config.h over from the default/ folder.
|
||||
|
||||
Build fails, nagging about inclusions and u32_t and whatnot. Open the
|
||||
spiffs_config.h and delete the bad inclusions. Also, add following typedefs:
|
||||
|
||||
typedef signed int s32_t;
|
||||
typedef unsigned int u32_t;
|
||||
typedef signed short s16_t;
|
||||
typedef unsigned short u16_t;
|
||||
typedef signed char s8_t;
|
||||
typedef unsigned char u8_t;
|
||||
|
||||
Now it should builds. Over to the mounting business. Assume you already
|
||||
implemented the read, write and erase functions to your SPI flash:
|
||||
|
||||
void my_spi_read(int addr, int size, char *buf)
|
||||
void my_spi_write(int addr, int size, char *buf)
|
||||
void my_spi_erase(int addr, int size)
|
||||
|
||||
In your main.c or similar, include the spiffs.h and do that spiffs struct:
|
||||
|
||||
#include <spiffs.h>
|
||||
|
||||
static spiffs fs;
|
||||
|
||||
Also, toss up some of the needed buffers:
|
||||
|
||||
#define LOG_PAGE_SIZE 256
|
||||
|
||||
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
|
||||
static u8_t spiffs_fds[32*4];
|
||||
static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4];
|
||||
|
||||
Now, write the my_spiffs_mount function:
|
||||
|
||||
void my_spiffs_mount() {
|
||||
spiffs_config cfg;
|
||||
cfg.phys_size = 2*1024*1024; // use all spi flash
|
||||
cfg.phys_addr = 0; // start spiffs at start of spi flash
|
||||
cfg.phys_erase_block = 65536; // well, this is what the datasheet says
|
||||
cfg.log_block_size = 65536; // seems sensible
|
||||
cfg.log_block_size = LOG_PAGE_SIZE; // seems sensible
|
||||
|
||||
cfg.hal_read_f = my_spi_read;
|
||||
cfg.hal_write_f = my_spi_write;
|
||||
cfg.hal_erase_f = my_spi_erase;
|
||||
|
||||
int res = SPIFFS_mount(&fs,
|
||||
&cfg,
|
||||
spiffs_work_buf,
|
||||
spiffs_fds,
|
||||
sizeof(spiffs_fds),
|
||||
spiffs_cache,
|
||||
sizeof(spiffs_cache),
|
||||
0);
|
||||
printf("mount res: %i\n", res);
|
||||
}
|
||||
|
||||
Now, build warns about the my_spi_read, write and erase functions. Wrong
|
||||
signatures, so go wrap them:
|
||||
|
||||
static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
|
||||
my_spi_read(addr, size, dst);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) {
|
||||
my_spi_write(addr, size, dst);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t my_spiffs_erase(u32_t addr, u32_t size) {
|
||||
my_spi_erase(addr, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
Redirect the config in my_spiffs_mount to the wrappers instead:
|
||||
|
||||
cfg.hal_read_f = my_spiffs_read;
|
||||
cfg.hal_write_f = my_spiffs_write;
|
||||
cfg.hal_erase_f = my_spiffs_erase;
|
||||
|
||||
Ok, now you should be able to build and run. However, you get this output:
|
||||
|
||||
mount res: -1
|
||||
|
||||
but you wanted
|
||||
|
||||
mount res: 0
|
||||
|
||||
This is probably due to you having being experimenting with your SPI flash, so
|
||||
it contains garbage from spiffs's point of view. Do a mass erase and run again.
|
||||
|
||||
If all is ok now, you're good to go. Try creating a file and read it back:
|
||||
|
||||
static void test_spiffs() {
|
||||
char buf[12];
|
||||
|
||||
spiffs_file fd = SPIFFS_open(&fs, "my_file", 0, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR);
|
||||
if (SPIFFS_write(&fs, fd, "Hello world", 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
|
||||
SPIFFS_close(&fs, fd);
|
||||
|
||||
fd = SPIFFS_open(&fs, "my_file", 0, SPIFFS_RDWR);
|
||||
if (SPIFFS_read(&fs, fd, buf, 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
|
||||
SPIFFS_close(&fs, fd);
|
||||
|
||||
printf("--> %s <--\n", buf);
|
||||
}
|
||||
|
||||
And, crossing fingers hard, you'll get the output:
|
||||
|
||||
--> Hello world <--
|
||||
|
30
docs/TECH_SPEC
Normal file
30
docs/TECH_SPEC
Normal file
@ -0,0 +1,30 @@
|
||||
* USING SPIFFS
|
||||
|
||||
TODO
|
||||
|
||||
* DESIGN
|
||||
|
||||
** SPI flash devices
|
||||
|
||||
Below is a small description of how SPI flashes work internally.
|
||||
|
||||
SPI flash devices are physically divided in blocks. On some SPI flash devices,
|
||||
blocks are further divided into sectors. Datasheets sometimes name blocks as
|
||||
sectors and vice versa.
|
||||
|
||||
Common memory capacaties for SPI flashes are 512kB up to 8 MB of data, with
|
||||
blocks of 64K. Sectors normally are 4K, if supported. The entire memory is
|
||||
linear and can be written in random access, but erasing can only be done block-
|
||||
or sectorwise; or by mass erase.
|
||||
|
||||
SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
|
||||
they fail erasing.
|
||||
|
||||
A clean SPI flash from factory have all bits in entire memory set to one. A
|
||||
mass erase will reset the device to this state. Block or sector erasing will
|
||||
put the area given by the sector or block to ones. Writing to a SPI flash
|
||||
pulls ones to zeroes. Writing 0xFF to an address is simply a no-op. This way
|
||||
of "nand-writing" is used considerably in spiffs.
|
||||
|
||||
** TODO
|
||||
|
Reference in New Issue
Block a user