2014-12-22 12:35:05 +01:00
|
|
|
* 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 in src/ to your own source folder. Exclude
|
|
|
|
all files in test folder. Then you point out these files in your make script
|
|
|
|
for compilation.
|
|
|
|
|
|
|
|
Also copy the spiffs_config.h over from the src/default/ folder.
|
|
|
|
|
|
|
|
Try building. This 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 build. 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];
|
2015-02-11 14:20:54 +01:00
|
|
|
static u8_t spiffs_cache_buf[(LOG_PAGE_SIZE+32)*4];
|
2014-12-22 12:35:05 +01:00
|
|
|
|
|
|
|
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; // according to datasheet
|
|
|
|
cfg.log_block_size = 65536; // let us not complicate things
|
2015-02-11 14:20:54 +01:00
|
|
|
cfg.log_page_size = LOG_PAGE_SIZE; // as we said
|
2014-12-22 12:35:05 +01:00
|
|
|
|
|
|
|
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),
|
2015-02-11 14:20:54 +01:00
|
|
|
spiffs_cache_buf,
|
|
|
|
sizeof(spiffs_cache_buf),
|
2014-12-22 12:35:05 +01:00
|
|
|
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 experimented with your SPI flash, so it
|
|
|
|
contains rubbish 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];
|
|
|
|
|
|
|
|
// Surely, I've mounted spiffs before entering here
|
|
|
|
|
|
|
|
spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
|
|
|
|
if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
|
|
|
|
SPIFFS_close(&fs, fd);
|
|
|
|
|
|
|
|
fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0);
|
|
|
|
if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
|
|
|
|
SPIFFS_close(&fs, fd);
|
|
|
|
|
|
|
|
printf("--> %s <--\n", buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
Compile, run, cross fingers hard, and you'll get the output:
|
|
|
|
|
|
|
|
--> Hello world <--
|
|
|
|
|
|
|
|
Got errors? Check spiffs.h for error definitions to get a clue what went voodoo.
|
|
|
|
|
|
|
|
|
2015-02-11 14:20:54 +01:00
|
|
|
* THINGS TO CHECK
|
|
|
|
|
|
|
|
When you alter the spiffs_config values, make sure you also check the typedefs
|
|
|
|
in spiffs_config.h:
|
|
|
|
|
|
|
|
- spiffs_block_ix
|
|
|
|
- spiffs_page_ix
|
|
|
|
- spiffs_obj_id
|
|
|
|
- spiffs_span_ix
|
|
|
|
|
|
|
|
The sizes of these typedefs must not underflow, else spiffs might end up in
|
|
|
|
eternal loops. Each typedef is commented what check for.
|
|
|
|
|
|
|
|
Also, if you alter the code or just want to verify your configuration, you can
|
|
|
|
run
|
|
|
|
|
|
|
|
> make test
|
|
|
|
|
|
|
|
in the spiffs folder. This will run all testcases using the configuration in
|
|
|
|
default/spiffs_config.h and test/params_test.h. The tests are written for linux
|
|
|
|
but should run under cygwin also.
|
|
|
|
|
|
|
|
|
2014-12-22 12:35:05 +01:00
|
|
|
* 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 "HOW TO CONFIG" 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
|
|
|
|
|
|
|
|
Depending on the build config - if SPIFFS_SINGLETON is set to zero - following
|
|
|
|
parameters must be defined:
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
spiffs_config.phys_erase_block - the physical size of the largest block/sector
|
|
|
|
on the SPI flash found within the spiffs
|
|
|
|
usage address space
|
|
|
|
|
|
|
|
spiffs_config.log_block_size - the logical size of a spiffs block
|
|
|
|
|
|
|
|
spiffs_config.log_page_size - the logical size of a spiffs page
|
|
|
|
|
|
|
|
If SPIFFS_SINGLETON is set to one, above parameters must be set ny defines in
|
|
|
|
the config header file, spiffs_config.h.
|
|
|
|
|
|
|
|
|
|
|
|
** 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.
|
|
|
|
|
|
|
|
|
|
|
|
** RAM
|
|
|
|
|
|
|
|
Spiffs needs ram. It needs a working buffer being double the size of the
|
|
|
|
logical page size. It also needs at least one file descriptor. If cache is
|
|
|
|
enabled (highly recommended), it will also need a bunch of cache pages.
|
|
|
|
|
|
|
|
Say you have a logical page size of 256 bytes. You want to be able to have four
|
|
|
|
files open simultaneously, and you can give spiffs four cache pages. This
|
|
|
|
roughly sums up to:
|
|
|
|
|
|
|
|
256*2 (work buffer) +
|
|
|
|
32*4 (file descriptors) +
|
|
|
|
(256+32)*4 (cache pages) + 40 (cache metadata)
|
|
|
|
|
|
|
|
i.e. 1832 bytes.
|
|
|
|
|
|
|
|
This is apart from call stack usage.
|
|
|
|
|
|
|
|
To get the exact amount of bytes needed on your specific target, enable
|
|
|
|
SPIFFS_BUFFER_HELP in spiffs_config.h, rebuild and call:
|
|
|
|
|
|
|
|
SPIFFS_buffer_bytes_for_filedescs
|
|
|
|
SPIFFS_buffer_bytes_for_cache
|
|
|
|
|
|
|
|
Having these figures you can disable SPIFFS_BUFFER_HELP again to save flash.
|
|
|
|
|
|
|
|
|
|
|
|
* HOW TO CONFIG
|
|
|
|
|
2015-05-29 08:54:07 +02:00
|
|
|
TODO
|