mirror of https://github.com/pellepl/spiffs
Peter Andersson
11 years ago
15 changed files with 275 additions and 277 deletions
@ -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 <-- |
||||
|
|
@ -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 |
||||
|
|
Loading…
Reference in new issue