内容简介:There's one thing truly remarkable about the
Configure Mynewt for SPI Flash on PineTime Smart Watch (nRF52)
There's one thing truly remarkable about the Apache Mynewt embedded operating system... Almost any feature may be switched on by editing a configuration file!
Today we'll learn to configure Mynewt OS to enable access to SPI Flash Memory on PineTime Smart Watch ... Just by editing two configuration files ( syscfg.yml
and pkg.yml
), and making some minor code changes ( hal_bsp.c
).
These steps will work for any Nordic nRF52 device, and probably STM32 devices too (see diagram above)...
-
Configure pins for SPI Portin
syscfg.yml
-
Configure flash interfacein
syscfg.yml
-
Configure flash timingsin
syscfg.yml
-
Include
spiflash.h
inhal_bsp.c
-
Define internal and external flash devicesin
hal_bsp.c
-
Access flash devices by IDin
hal_bsp.c
-
Add
spiflash
driver topkg.yml
Read on for the details...
syscfg.yml
: Configuration for Board Support Package
1 syscfg.yml:
Configure Pins for SPI Port
The first configuration file we'll edit is syscfg.yml
from Mynewt's Board Support Package. For PineTime, this file is located at...
Let's add the PineTime SPI settings according to the PineTime Wiki : PineTime Port Assignment ...
nRF52 Pin | Function | Description |
---|---|---|
P0.02 |
SPI-SCK, LCD_SCK |
SPI Clock |
P0.03 |
SPI-MOSI, LCD_SDI |
SPI MOSI (master to slave) |
P0.04 |
SPI-MISO |
SPI MISO (slave to master) |
P0.05 |
SPI-CE# (SPI-NOR) |
SPI Chip Select |
Here's how it looks in syscfg.yml
...
syscfg.vals: ... # Default Pins for Peripherals # Defined in http://files.pine64.org/doc/PineTime/PineTime%20Port%20Assignment%20rev1.0.pdf # SPI port 0 connected to ST7789 display and XT25F32B flash SPI_0_MASTER_PIN_SCK: 2 # P0.02/AIN0: SPI-SCK, LCD_SCK SPI clock for display and flash SPI_0_MASTER_PIN_MOSI: 3 # P0.03/AIN1: SPI-MOSI, LCD_SDI SPI MOSI for display and flash SPI_0_MASTER_PIN_MISO: 4 # P0.04/AIN2: SPI-MISO SPI MISO for flash only ...
For pin numbers on nRF52, we may drop the P0
prefix and write P02.02
as 2
.
We'll add SPI Chip Select ( Pin 5
) in a while.
2 syscfg.yml:
Configure flash interface
Now we'll edit syscfg.yml
to tell Mynewt how to access our Flash Memory. From the PineTime Wiki ...
SPI Flash information: XTX XT25F32B 32 Mb (4 MB) SPI NOR Flash
Data sheets for this part are hard to find but it acts similar to other QuadSPI SPI NOR Flash such as Macronix 32 Mb (4 MB) SPI NOR Flash
IDs for XT25F32B are: Manufacturer ( 0x0b
) , Device ( 0x15
), Memory Type ( 0x40
) , Density ( 0x16
)
Confused about MB
and Mb
? MB
stands for Mega Byte (roughly a million bytes), whereas Mb
stands for Mega Bit (roughly a million bits). Divide Mb
by 8 to get MB
.
We don't have the datasheet for XT25F32B Flash Memory... But fortunately the JEDEC IDs for Manufacturer, Memory Type and Density (Capacity) are all that we need for syscfg.yml
...
syscfg.vals: ... # SPI Flash # XTX XT25F32B 32 Mb (4 MB) SPI NOR Flash (similar to QuadSPI SPI NOR Flash like Macronix 32 Mb (4 MB) MX25L3233F) # manufacturer (0x0b), device (0x15), memory type (0x40), density (0x16) # Settings below are documented at https://github.com/apache/mynewt-core/blob/master/hw/drivers/flash/spiflash/syscfg.yml SPIFLASH: 1 # Enable SPI Flash SPIFLASH_SPI_NUM: 0 # SPI Interface 0 SPIFLASH_SPI_CS_PIN: 5 # SPI interface CS pin: P0.05/AIN3, SPI-CE# (SPI-NOR) SPIFLASH_BAUDRATE: 8000 # Requested baudrate, 8000 is the fastest baudrate supported by nRF52832 SPIFLASH_MANUFACTURER: 0x0B # Expected SpiFlash manufacturer as read by Read JEDEC ID command 9FH SPIFLASH_MEMORY_TYPE: 0x40 # Expected SpiFlash memory type as read by Read JEDEC ID command 9FH SPIFLASH_MEMORY_CAPACITY: 0x16 # Expected SpiFlash memory capactity as read by Read JEDEC ID command 9FH (2 ^ 0x16 = 32 Mb) SPIFLASH_SECTOR_COUNT: 1024 # Number of sectors: 1024 sectors of 4 KB each SPIFLASH_SECTOR_SIZE: 4096 # Number of bytes that can be erased at a time: 4 KB sector size SPIFLASH_PAGE_SIZE: 256 # TODO Number of bytes that can be written at a time ...
The JEDEC Device ID ( 0x15
) is not used.
SPIFLASH_SECTOR_COUNT
and SPIFLASH_SECTOR_SIZE
were copied from Macronix MX25L3233F since it's similar to our XT25F32B.
SPIFLASH_PAGE_SIZE
was copied from another Mynewt configuration: black_vet6
3 syscfg.yml:
Configure Flash Timings
Finally we edit syscfg.yml
to tell Mynewt the timing characteristics of our Flash Memory...
syscfg.vals: ... # Copied from https://github.com/apache/mynewt-core/blob/master/hw/bsp/black_vet6/syscfg.yml SPIFLASH_TBP1_TYPICAL: 20 # Byte program time (first byte) (us) SPIFLASH_TBP1_MAXIMUM: 50 # Maximum byte program time (first byte) (us) SPIFLASH_TPP_TYPICAL: 700 # Page program time (us) SPIFLASH_TPP_MAXIMUM: 3000 # Maximum page program time (us) SPIFLASH_TSE_TYPICAL: 30000 # Sector erase time (4KB) (us) SPIFLASH_TSE_MAXIMUM: 400000 # Maximum sector erase time (us) SPIFLASH_TBE1_TYPICAL: 120000 # Block erase time (32KB) (us) SPIFLASH_TBE1_MAXIMUM: 800000 # Maximum block erase time (32KB) (us) SPIFLASH_TBE2_TYPICAL: 150000 # Block erase time (64KB) (us) SPIFLASH_TBE2_MAXIMUM: 1000000 # Maximum block erase time (64KB) (us) SPIFLASH_TCE_TYPICAL: 3000000 # Chip erase time (us) SPIFLASH_TCE_MAXIMUM: 10000000 # Maximum chip erase time (us) ...
If we have the Flash Memory Datasheet, fill in the numbers from the datasheet.
The above settings were copied from another Mynewt configuration: black_vet6
hal_bsp.c
: Code for Board Support Package
4 hal_bsp.c:
Include spiflash.h
After editing syscfg.yml
in the Board Support Package, let's make some minor tweaks to the source code ( hal_bsp.c
) of the Board Support Package.
For PineTime, the source file is located at...
First we insert the header file for the SPI Flash Driver into hal_bsp.c
like this...
#if MYNEWT_VAL(SPIFLASH) // If External SPI Flash exists... #include <spiflash/spiflash.h> #endif // MYNEWT_VAL(SPIFLASH)
5 hal_bsp.c:
Define Internal and External Flash Devices
Next we define two Mynewt Flash Devices...
- Internal Flash ROM with Flash Device ID 0
- External SPI Flash with Flash Device ID 1
Edit hal_bsp.c
and insert this block of code...
/// Array of Flash Devices static const struct hal_flash *flash_devs[] = { [0] = &nrf52k_flash_dev, // Internal Flash ROM #if MYNEWT_VAL(SPIFLASH) // If External SPI Flash exists... [1] = &spiflash_dev.hal, // External SPI Flash #endif // MYNEWT_VAL(SPIFLASH) };
Later we'll use Flash Device ID 1 when accessing SPI Flash.
6 hal_bsp.c:
Access Flash Devices by ID
Finally we edit the code to fetch the Flash Devices by the Flash Device ID.
Edit hal_bsp.c
. Look for the function hal_bsp_flash_dev()
and replace the function by this code...
/// Return the Flash Device for the ID. 0 for Internal Flash ROM, 1 for External SPI Flash const struct hal_flash * hal_bsp_flash_dev(uint8_t id) { if (id >= ARRAY_SIZE(flash_devs)) { return NULL; } return flash_devs[id]; }
This function returns the Internal Flash ROM for ID 0, and External SPI Flash for ID 1.
pkg.yml
: Drivers for Board Support Package
7 pkg.yml:
Add spiflash
driver
The last file we'll edit is pkg.yml
from the Board Support Package. For PineTime this file is located at...
Edit pkg.yml
. Look for the pkg.deps
section and add spiflash
like this...
pkg.deps: ... - "@apache-mynewt-core/hw/drivers/flash/spiflash" # SPI Flash Driver
This starts up the SPI Flash Driver whenever Mynewt boots. And we're done!
Now let's write a simple program to read, write and erase the SPI Flash.
Here's the C code to test reading, writing and erasing SPI Flash on Mynewt: flash_test.c
. The code was derived from Mynewt's test code for Flash Devices .
The test code calls Mynewt's Flash HAL (Hardware Adaptation Layer) to access the flash memory...
-
Erase Flash
hal_flash_erase(id, offset, size)
Erase internal / external flash memory at the
offset
address, forsize
bytes. Seehal_flash_erase
-
Read Flash
hal_flash_read(id, offset, buf, sector_count)
Read internal / external flash memory from the
offset
address into thebuf
buffer, forsector_count
sectors. On PineTime, one sector contains 4 KB. Seehal_flash_read
-
Write Flash
hal_flash_write(id, offset, buf, sector_count)
Write internal / external flash memory from the
buf
buffer to theoffset
address, forsector_count
sectors. On PineTime, one sector contains 4 KB. Seehal_flash_write
For the above functions, id
is 0 for Internal Flash ROM, 1 for External SPI Flash.
For easier testing, the above functions are wrapped inside the flash_cmd()
function, which is also defined in flash_test.c
Let's call flash_cmd()
to test the SPI Flash functions...
Here's the test code in flash_test.c
to read Internal Flash ROM and External SPI Flash...
/// Test internal flash ROM and external SPI flash int test_flash() { // Keep running tests until a test returns an error (non-zero result) if ( //////////////////////////////// // Read Flash // <flash-id> <offset> <size> // Read internal flash ROM flash_cmd(READ_COMMAND, 0, 0x0, 32) || // Read external SPI flash flash_cmd(READ_COMMAND, 1, 0x0, 32) || ... 0 ) { return -1; } // Tests failed return 0; // Tests OK }
This code reads 32 bytes, starting at offset 0, from both Internal Flash ROM and External SPI Flash. As expected, the flash memory contents are different for Internal Flash ROM and External SPI Flash...
Testing flash... Read Internal Flash ROM... Read 0x0 + 20 0x0000: 0x00 0x00 0x01 0x20 0xd9 0x00 0x00 0x00 0x0008: 0x35 0x01 0x00 0x00 0x37 0x01 0x00 0x00 0x0010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 Read External SPI Flash... Read 0x0 + 20 0x0000: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0008: 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x0010: 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x0018: 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f 0x20
Next in flash_test.c
we erase the External SPI Flash...
///////////////////////////////////// // Erase Flash: Set all bits to 1 // <flash-id> <offset> <size> // Erase external SPI flash flash_cmd(ERASE_COMMAND, 1, 0x0, 32) ||
Erase External SPI Flash... Erase 0x0 + 20 Done!
After erasing, let's read both Internal Flash ROM and External SPI Flash ( flash_test.c
)...
//////////////////////////////////////// // Read Flash // <flash-id> read <offset> <size> // Read internal flash ROM flash_cmd(READ_COMMAND, 0, 0x0, 32) || // Read external SPI flash flash_cmd(READ_COMMAND, 1, 0x0, 32) ||
Here are the contents...
Read Internal Flash ROM... Read 0x0 + 20 0x0000: 0x00 0x00 0x01 0x20 0xd9 0x00 0x00 0x00 0x0008: 0x35 0x01 0x00 0x00 0x37 0x01 0x00 0x00 0x0010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 Read External SPI Flash... Read 0x0 + 20 0x0000: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x0008: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x0010: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x0018: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
Why was SPI Flash wiped out with 0xff
?
That's expected when we erase NOR Flash Memory ... All bits will get set to 1
.
When we write to NOR Flash Memory, we may only flip 1
bits to 0
, not 0
to 1
.
Hence before writing any data into NOR Flash Memory, we need to erase all bits to 1
. Then we write the data to flash, flipping some 1
bits to 0
.
Now that SPI Flash has been flipped to 1
, let's write some data ( flash_test.c
)...
////////////////////////////////////////////// // Write Flash: Write 0x01, 0x02, 0x03, ... // (Must erase before writing) // <flash-id> <offset> <size> // Write external SPI flash flash_cmd(WRITE_COMMAND, 1, 0x0, 32) ||
This shows...
Write External SPI Flash... Write 0x0 + 20 Done!
flash_cmd()
writes to SPI Flash the bytes 0x01
, 0x02
, 0x03
, ...
Let's read SPI Flash and check ( flash_test.c
)...
//////////////////////////////////////////// // Read Flash // <flash-id> <offset> <size> // Read internal flash ROM flash_cmd(READ_COMMAND, 0, 0x0, 32) || // Read external SPI flash flash_cmd(READ_COMMAND, 1, 0x0, 32) ||
Here's the result...
Read Internal Flash ROM... Read 0x0 + 20 0x0000: 0x00 0x00 0x01 0x20 0xd9 0x00 0x00 0x00 0x0008: 0x35 0x01 0x00 0x00 0x37 0x01 0x00 0x00 0x0010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 Read External SPI Flash... Read 0x0 + 20 0x0000: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0008: 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x0010: 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x0018: 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f 0x20
Yep the flipping of bits from 1
to 0
worked!
8.4 SPI Flash Sector Map
To check whether SPI Flash is correctly configured, we may dump the Flash Sector Map like this ( flash_test.c
)...
//////////////////// // Dump Sector Map // Dump sector map for internal flash ROM map_cmd(0) || // Dump sector map for external SPI flash map_cmd(1) ||
Here's the output...
Sector Map for Internal Flash ROM... Flash 0 at 0x0 size 0x80000 with 128 sectors, alignment req 1 bytes 0: 1000 1: 1000 2: 1000 ... 127: 1000 Sector Map for External SPI Flash... Flash 1 at 0x0 size 0x400000 with 1024 sectors, alignment req 1 bytes 0: 1000 1: 1000 2: 1000 ... 1023: 1000
This says that SPI Flash has been configured with 1024 sectors, each sector 4 KB in size ( 0x1000
).
9 SPI Flash Benchmark
How fast can we read SPI Flash? Compared with Internal Flash ROM?
Let's run the flash speed tests found in flash_test.c
...
////////////////////// // Test Flash Speed // <flash_id> <addr> <rd_sz>|range [move] // range=0 for size mode, range=1 for range mode, move=1 for move // Internal flash ROM, size mode, no move speed_cmd(0, 0x0, 32, 0, 0) || // External SPI flash, size mode, no move speed_cmd(1, 0x0, 32, 0, 0) ||
This code repeatedly reads 32 bytes from Internal Flash ROM and External SPI Flash... And counts how many Read Operations were completed in 2 seconds.
Here's the output...
Speed Test for Internal Flash ROM... Speed test, hal_flash_read(0, 0x0, 32) 207503 Speed Test for External SPI Flash... Speed test, hal_flash_read(1, 0x0, 32) 16107
That's 207,503 Read Operations Per 2 Seconds for Internal Flash ROM, and 16,107 Read Operations Per 2 Seconds for External SPI Flash.
When we divide the numbers by 2, we get the Read Operations Per Second...
Size (Bytes) | Flash ROM Reads/Sec |
SPI Flash Reads/Sec |
---|---|---|
32 | 103,751 | 8,053 |
As expected, Internal Flash ROM is faster than External SPI Flash, roughly 13 times faster.
Let's run the test speed on a range of data sizes, from 1 byte to 256 bytes ( flash_test.c
)...
// Internal flash ROM, range mode, no move speed_cmd(0, 0x0, 0, 1, 0) || // External SPI flash, range mode, no move speed_cmd(1, 0x0, 0, 1, 0) ||
Now we get a table of Read Operations Per 2 Seconds (second column), for data sizes ranging from 1 byte to 256 bytes (first column)...
Speed Test for Internal Flash ROM... Speed test, hal_flash_read(0, 0x0, X) 1 271962 2 261931 4 271962 8 260862 16 241174 24 221913 32 207503 48 182082 64 162210 96 133148 128 112917 192 86600 256 70232 Speed Test for External SPI Flash... Speed test, hal_flash_read(1, 0x0, X) 1 44139 2 42048 4 37684 8 31639 16 23955 24 19250 32 16107 48 12132 64 9731 96 6971 128 5431 192 3766 256 2883
Divide the numbers by 2 to get Read Operations Per Second for various data sizes...
Size (Bytes) | Flash ROM Reads/Sec |
SPI Flash Reads/Sec |
---|---|---|
1 | 135,981 | 22,069 |
2 | 130,965 | 21,024 |
4 | 135,981 | 18,842 |
8 | 130,431 | 15,819 |
16 | 120,587 | 11,977 |
24 | 110,956 | 9,625 |
32 | 103,751 | 8,053 |
48 | 91,041 | 6,066 |
64 | 81,105 | 4,865 |
96 | 66,574 | 3,485 |
128 | 56,458 | 2,715 |
192 | 43,300 | 1,883 |
256 | 35,116 | 1,441 |
Internal Flash ROM is still faster than External SPI Flash. For reads of 1 byte, Internal Flash ROM is faster by 6 times. But for reads of 256 bytes, Internal Flash ROM is faster by 24 times!
10 MCUBoot Bootloader with SPI Flash
MCUBoot is an open-source Bootloader that supports Mynewt, RIOT and Zephyr operating systems... It's the first thing that will run on PineTime when it boots .
During firmware updates, MCUBoot stores the previous version of the firmware into Internal Flash ROM, and rolls back to the previous firmware if the new firmware doesn't start properly.
But on constrained devices like PineTime, this robustness will cost us... We might run out of space in Internal Flash ROM to store the old firmware!
Fortunately MCUBoot on Mynewt works seamlessly with SPI Flash... Watch how the new firmware FLASH_AREA_IMAGE_0
coexists with the old firmware FLASH_AREA_IMAGE_1
in this Flash Memory Map for MCUBoot for Mynewt: hw/bsp/black_vet6/bsp.yml
bsp.flash_map: areas: # System areas. FLASH_AREA_BOOTLOADER: device: 0 offset: 0x08000000 size: 32kB FLASH_AREA_IMAGE_0: device: 0 offset: 0x08020000 size: 256kB FLASH_AREA_IMAGE_SCRATCH: device: 0 offset: 0x08060000 size: 128kB # User areas. FLASH_AREA_REBOOT_LOG: user_id: 0 device: 0 offset: 0x08008000 size: 32kB FLASH_AREA_IMAGE_1: device: 1 offset: 0x00000000 size: 256kB FLASH_AREA_NFFS: user_id: 1 device: 1 offset: 0x00040000 size: 32kB
Note that the new firmware FLASH_AREA_IMAGE_0
resides on Flash Device 0 (Internal Flash ROM) , while the old firmware FLASH_AREA_IMAGE_1
resides on Flash Device 1 (External SPI Flash) .
This means that we won't waste any previous space in Internal Flash ROM for storing the old firmware... MCUBoot automatically swaps the old firmware into External SPI Flash! Using MCUBoot Bootloader with SPI Flash is really that easy!
11 Debug SPI Flash with MCUBoot Bootloader
If we're using the MCUBoot Bootloader (like on PineTime), debugging and testing SPI Flash can be somewhat challenging.
Remember that we added the SPI Flash Driver to the Board Support Package?
The Board Support Package is used by both the MCUBoot Bootloader as well as the Application Firmware. Which means that the SPI Flash Driver is loaded when MCUBoot starts.
What happens if the SPI Flash Driver is configured incorrectly?
MCUBoot may crash... Before starting the Application Firmware! (This happened to me)
Hence for debugging and testing SPI Flash, I strongly recommend switching the Bootloader to a simpler one that doesn't require any drivers: the Stub Bootloader .
The Stub Bootloader doesn't do anything... It simply jumps to the Application Firmware.
This will enable us to debug and test SPI Flash with our Application Firmware, before using it with MCUBoot.
Also MCUBoot expects the Application Firmware Image to start with the MCUBoot Image Header. When the GDB debugger flashes the Firmware ELF File into ROM, the Image Header is empty. So MCUBoot won't work start the Application Firmware properly when the debugger is running. Switching MCUBoot to the Stub Bootloader will solve this.
Source code for Stub Bootloader
12 Switch MCUBoot to Stub Bootloader
To switch the PineTime Bootloader from MCUBoot to the Stub Bootloader, edit the Bootloader Target Settings targets/nrf52_boot/target.yml
.
Comment out this line (insert #
at the beginning of the line)...
# target.app: "@mcuboot/boot/mynewt" # Use MCUBoot, which doesn't support debugging
And uncomment this line (remove #
from the beginning of the line)...
target.app: "apps/boot_stub" # Use Stub Bootloader, which supports debugging
Then edit the Bootloader OpenOCD Script scripts/nrf52/flash-boot.ocd
Comment out the program
line below (insert #
at the beginning of the line)...
And uncomment the program
line below (remove #
from the beginning of the line)...
program bin/targets/nrf52_boot/app/apps/boot_stub/boot_stub.elf.bin verify 0x00000000
Build the Stub Bootloader by clicking Terminal -> Run Task -> Build Bootloader
. Or run the Bootloader Build Script: scripts/nrf52/build-boot.sh
Flash the Stub Bootloader to PineTime by clicking Terminal -> Run Task -> Flash Bootloader
. Or run the Bootloader Flash Script: scripts/nrf52/flash-boot.sh
Our PineTime now boots with the Stub Bootloader... Ready for debugging!
13 Inside the SPI Flash Driver
We have been using Mynewt's SPI Flash Driver: spiflash.h
and spiflash.c
Remember the configuration we have set in syscfg.yml
like SPIFLASH_MANUFACTURER
, SPIFLASH_MEMORY_TYPE
, SPIFLASH_MEMORY_CAPACITY
?
The flash driver uses these configuration settings to access our SPI flash memory. Everything in Mynewt's SPI Flash Driver is configured via syscfg.yml
... Amazing!
Mynewt's SPI Flash Driver seemed very mysterious... Until I stumbled upon this Board Support Package in Mynewt: black_vet6/syscfg.yml
The configuration and code in this article were derived from that Board Support Package.
14 SPI Flash File System
We have seen Mynewt's Flash HAL functions for reading, writing and erasing SPI Flash at the byte and sector level. That's too low-level for us firmware programmers.
Can we have files and directories in SPI Flash?
Yes we can, with a Flash File System like littlefs .
Earlier we saw this Flash Memory Map: hw/bsp/black_vet6/bsp.yml
bsp.flash_map: areas: ... FLASH_AREA_NFFS: user_id: 1 device: 1 offset: 0x00040000 size: 32kB
FLASH_AREA_NFFS
is a Flash Area that's available for use by the Application Firmware for storing application data. Note that it's located on Flash Device 1, which is the External SPI Flash.
Mynewt will let us install a File System into the FLASH_AREA_NFFS
Flash Area for storing our files and directories.
Here's a simple example from littlefs/README.md
that updates a file named boot_count
every time the program runs. The program can be interrupted at any time without losing track of how many times it has been booted and without corrupting the filesystem...
#include "lfs.h" // variables used by the filesystem lfs_t lfs; lfs_file_t file; // configuration of the filesystem is provided by this struct const struct lfs_config cfg = { // block device operations .read = flash_read, // TODO: Implement with hal_flash_read() .prog = flash_prog, // TODO: Implement with hal_flash_write() .erase = flash_erase, // TODO: Implement with hal_flash_erase() .sync = flash_sync, // TODO: Provide a sync function // block device configuration .read_size = 16, .prog_size = 16, .block_size = 4096, .block_count = 128, .cache_size = 16, .lookahead_size = 16, .block_cycles = 500, }; // entry point int test_littlefs(void) { // mount the filesystem int err = lfs_mount(&lfs, &cfg); // reformat if we can't mount the filesystem // this should only happen on the first boot if (err) { lfs_format(&lfs, &cfg); lfs_mount(&lfs, &cfg); } // read current count uint32_t boot_count = 0; lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); // update boot count boot_count += 1; lfs_file_rewind(&lfs, &file); lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count)); // remember the storage is not updated until the file is closed successfully lfs_file_close(&lfs, &file); // release any resources we were using lfs_unmount(&lfs); // print the boot count console_printf("boot_count: %d\n", boot_count); console_flush(); }
The lfs_config
struct points to functions that will be used by littlefs for reading, writing and erasing the flash device: flash_read
, flash_prog
, flash_erase
.
These functions may be implemented with similar functions defined in Mynewt's Flash HAL : hal_flash_read
, hal_flash_write
, hal_flash_erase
.
lfs_config
is documented here: lfs.h
Note that Mynewt's Flash HAL uses absolute addresses (instead of addresses relative to the Flash Area) when accessing SPI Flash. We need to map Flash Area addresses in the Flash Map to absolute addresses using Mynewt's Flash Map functions: flash_map.h
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。