Hello
I'm new with zephyr environment and I want to use littlefs in order to partition an external flash over spi, and to be able to upload file over BLE by using a SMP server.
I know this topic comes up regularly. I downloaded the littlefs sample project for inspiration. Here's the error I'm getting when building :
main.c:111: undefined reference to `z_fsmp_DT_N_S_soc_S_qspi_40029000_S_mx25r6435f_0_S_partitions_S_partition_0'
I guess I misconfigured something but I can't figure out what.
My environment :
- board : nrf52840dk
- SDK v2.9.0
- Toolchain : v2.9.1
Here is my app.overlay :
/ { chosen { nordic,pm-ext-flash = &mx25r64; }; fstab { compatible = "zephyr,fstab"; lfs1: lfs1 { compatible = "zephyr,fstab,littlefs"; mount-point = "/lfs1"; partition = <&lfs1_partition>; /*automount;*/ read-size = <16>; prog-size = <16>; cache-size = <64>; lookahead-size = <32>; block-cycles = <512>; }; }; }; &mx25r64 { partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; lfs1_partition: partition@0 { label = "lfs1"; reg = <0x00000000 0x00010000>; }; }; };
Here is my custom dts file :
/dts-v1/;
#include <nordic/nrf52840_qfaa.dtsi>
#include "ngtble-pinctrl.dtsi"
/ {
model = "Nordic NGT BLE NRF52840";
compatible = "teledyne,ngtble";
chosen {
/*zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,uart-mcumgr = &uart0;
zephyr,bt-mon-uart = &uart0;
zephyr,bt-c2h-uart = &uart0;*/
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,code-partition = &slot0_partition;
zephyr,ieee802154 = &ieee802154;
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
};
};
&gpiote {
status = "okay";
};
&gpio0 {
status = "okay";
};
&gpio1 {
status = "okay";
};
&uart1 {
compatible = "nordic,nrf-uarte";
status = "okay";
current-speed = <9600>;
pinctrl-0 = <&uart1_default>;
pinctrl-1 = <&uart1_sleep>;
pinctrl-names = "default", "sleep";
modbus0 {
compatible = "zephyr,modbus-serial";
status = "okay";
};
};
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 DT_SIZE_K(48)>;
};
slot0_partition: partition@c000 {
label = "image-0";
reg = <0x0000c000 DT_SIZE_K(472)>;
};
slot1_partition: partition@82000 {
label = "image-1";
reg = <0x00082000 DT_SIZE_K(472)>;
};
storage_partition: partition@f8000 {
label = "storage";
reg = <0x000f8000 DT_SIZE_K(32)>;
};
};
};
&spi0 {
compatible = "nordic,nrf-spi";
/* Cannot be used together with i2c0. */
/* status = "okay"; */
pinctrl-0 = <&spi0_default>;
pinctrl-1 = <&spi0_sleep>;
pinctrl-names = "default", "sleep";
};
&spi1 {
compatible = "nordic,nrf-spi";
status = "okay";
pinctrl-0 = <&spi1_default>;
pinctrl-1 = <&spi1_sleep>;
pinctrl-names = "default", "sleep";
};
&spi2 {
compatible = "nordic,nrf-spi";
status = "disabled";
pinctrl-0 = <&spi2_default>;
pinctrl-1 = <&spi2_sleep>;
pinctrl-names = "default", "sleep";
};
&qspi {
status = "okay";
pinctrl-0 = <&qspi_default>;
pinctrl-1 = <&qspi_sleep>;
pinctrl-names = "default", "sleep";
mx25r64: mx25r6435f@0 {
compatible = "nordic,qspi-nor";
reg = <0>;
/* MX25R64 supports only pp and pp4io */
writeoc = "pp4io";
/* MX25R64 supports all readoc options */
readoc = "read4io";
sck-frequency = <8000000>;
jedec-id = [c2 28 17];
sfdp-bfp = [
e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb
ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52
10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44
30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff
];
size = <67108864>;
has-dpd;
t-enter-dpd = <10000>;
t-exit-dpd = <35000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
lfs1_partition: partition@0 {
label = "lfs1";
reg = <0x00000000 0x00010000>;
};
};
};
};
my pm_static.yml :
app:
address: 0xc200
end_address: 0x85000
region: flash_primary
size: 0x78e00
mcuboot:
address: 0x0
end_address: 0xc000
placement:
align:
end: 0x1000
before:
- mcuboot_primary
region: flash_primary
size: 0xc000
mcuboot_pad:
address: 0xc000
end_address: 0xc200
placement:
align:
start: 0x1000
before:
- mcuboot_primary_app
region: flash_primary
size: 0x200
mcuboot_primary:
address: 0xc000
end_address: 0x85000
orig_span: &id001
- mcuboot_pad
- app
region: flash_primary
sharers: 0x1
size: 0x79000
span: *id001
mcuboot_primary_app:
address: 0xc200
end_address: 0x85000
orig_span: &id002
- app
region: flash_primary
size: 0x78e00
span: *id002
mcuboot_secondary:
address: 0x85000
end_address: 0xfe000
placement:
after:
- mcuboot_primary
align:
start: 0x1000
region: flash_primary
share_size:
- mcuboot_primary
size: 0x79000
settings_storage:
address: 0xfe000
end_address: 0x100000
placement:
align:
start: 0x1000
before:
- end
region: flash_primary
size: 0x2000
sram_primary:
address: 0x20000000
end_address: 0x20040000
region: sram_primary
size: 0x40000
littlefs_storage:
address: 0x0
device: mx25r6435f
end_address: 0x800000
region: external_flash
size: 0x800000
my prj.conf file :
#
# Copyright 2024 DPEdesign
#
# All rights reserved
#
CONFIG_NCS_SAMPLES_DEFAULTS=y
CONFIG_DK_LIBRARY=y
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="FREEDOM"
CONFIG_BT_MAX_CONN=2
CONFIG_BT_SMP=y
CONFIG_BT_MAX_PAIRED=8
CONFIG_BT_GATT_AUTO_SEC_REQ=y
CONFIG_BT_FIXED_PASSKEY=y
CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y
# Enable bonding
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_RUNTIME=y
#Enable MCUBOOT bootloader build in the application
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="priv.pem"
#Include MCUMGR and the dependencies in the build
CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y
#CONFIG_MCUMGR_TRANSPORT_BT_AUTHEN=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=100
CONFIG_MODBUS=y
CONFIG_MODBUS_ROLE_CLIENT=y
CONFIG_PARTITION_MANAGER_ENABLED=y
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
# Enable binary descriptors
#CONFIG_BINDESC=y
# Enable definition of binary descriptors
#CONFIG_BINDESC_DEFINE=y
# Enable default build time binary descriptors
#CONFIG_BINDESC_DEFINE_BUILD_TIME=y
#CONFIG_BINDESC_BUILD_DATE_TIME_STRING=y
#############################################
# Enable file system commands
CONFIG_MCUMGR_GRP_FS=y
CONFIG_MCUMGR_GRP_FS_DL_CHUNK_SIZE=200
# Enable the storage erase command.
CONFIG_MCUMGR_GRP_ZBASIC=y
CONFIG_MCUMGR_GRP_ZBASIC_STORAGE_ERASE=y
# Optionally force the file system to be recreated
#CONFIG_APP_WIPE_STORAGE=y
# fs_dirent structures are big.
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_BT_RX_STACK_SIZE=4096
CONFIG_BT_HCI_TX_STACK_SIZE=4096
# Let __ASSERT do its job
CONFIG_DEBUG=y
CONFIG_INIT_STACKS=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_STACK_SENTINEL=y
CONFIG_THREAD_MONITOR=y
CONFIG_PM_SINGLE_IMAGE=y # ou CONFIG_PM_MULTI_IMAGE si tu utilises MCUboot
CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y
CONFIG_PM_PARTITION_REGION_LITTLEFS_EXTERNAL=y
#CONFIG_PM_PARTITION_REGION_SETTINGS_STORAGE_EXTERNAL=y
#CONFIG_PM_PARTITION_REGION_NVS_STORAGE_EXTERNAL=y
CONFIG_SPI=n
#CONFIG_SPI_NOR=y
#CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
#############################################
and here is the main.c file :
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/logging/log.h>
#include <zephyr/storage/flash_map.h>
#include "ble_mgt.h"
#define CONFIG_APP_LITTLEFS_STORAGE_FLASH
LOG_MODULE_REGISTER(main);
/* Matches LFS_NAME_MAX */
#define MAX_PATH_LEN 255
#define TEST_FILE_SIZE 547
static int lsdir(const char *path)
{
int res;
struct fs_dir_t dirp;
static struct fs_dirent entry;
fs_dir_t_init(&dirp);
/* Verify fs_opendir() */
res = fs_opendir(&dirp, path);
if (res) {
LOG_ERR("Error opening dir %s [%d]\n", path, res);
return res;
}
LOG_PRINTK("\nListing dir %s ...\n", path);
for (;;) {
/* Verify fs_readdir() */
res = fs_readdir(&dirp, &entry);
/* entry.name[0] == 0 means end-of-dir */
if (res || entry.name[0] == 0) {
if (res < 0) {
LOG_ERR("Error reading dir [%d]\n", res);
}
break;
}
if (entry.type == FS_DIR_ENTRY_DIR) {
LOG_PRINTK("[DIR ] %s\n", entry.name);
} else {
LOG_PRINTK("[FILE] %s (size = %zu)\n",
entry.name, entry.size);
}
}
/* Verify fs_closedir() */
fs_closedir(&dirp);
return res;
}
#ifdef CONFIG_APP_LITTLEFS_STORAGE_FLASH
static int littlefs_flash_erase(unsigned int id)
{
const struct flash_area *pfa;
int rc;
rc = flash_area_open(id, &pfa);
if (rc < 0) {
LOG_ERR("FAIL: unable to find flash area %u: %d\n",
id, rc);
return rc;
}
LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n",
id, (unsigned int)pfa->fa_off, pfa->fa_dev->name,
(unsigned int)pfa->fa_size);
/* Optional wipe flash contents */
if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
rc = flash_area_flatten(pfa, 0, pfa->fa_size);
LOG_ERR("Erasing flash area ... %d", rc);
}
flash_area_close(pfa);
return rc;
}
#define PARTITION_NODE DT_NODELABEL(lfs1)
#if DT_NODE_EXISTS(PARTITION_NODE)
FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
#else /* PARTITION_NODE */
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t lfs_storage_mnt = {
.type = FS_LITTLEFS,
.fs_data = &storage,
.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
.mnt_point = "/lfs1",
};
#endif /* PARTITION_NODE */
struct fs_mount_t *mountpoint =
#if DT_NODE_EXISTS(PARTITION_NODE)
&FS_FSTAB_ENTRY(PARTITION_NODE)
#else
&lfs_storage_mnt
#endif
;
static int littlefs_mount(struct fs_mount_t *mp)
{
int rc;
rc = littlefs_flash_erase((uintptr_t)mp->storage_dev);
if (rc < 0) {
return rc;
}
/* Do not mount if auto-mount has been enabled */
#if !DT_NODE_EXISTS(PARTITION_NODE) || \
!(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT)
rc = fs_mount(mp);
if (rc < 0) {
LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n",
(uintptr_t)mp->storage_dev, mp->mnt_point, rc);
return rc;
}
LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc);
#else
LOG_PRINTK("%s automounted\n", mp->mnt_point);
#endif
return 0;
}
#endif /* CONFIG_APP_LITTLEFS_STORAGE_FLASH */
void show_stack_usage(struct k_thread *thread, void *user_data) {
size_t unused = 0;
if (k_thread_stack_space_get(thread, &unused) == 0) {
printk("Thread %s → stack libre : %zu octets\n", k_thread_name_get(thread), unused);
}
}
int main(void)
{
struct fs_statvfs sbuf;
int rc;
LOG_PRINTK("Sample program to r/w files on littlefs\n");
ble_init();
rc = littlefs_mount(mountpoint);
if (rc < 0) {
return 0;
}
rc = fs_statvfs(mountpoint->mnt_point, &sbuf);
if (rc < 0) {
LOG_PRINTK("FAIL: statvfs: %d\n", rc);
goto out;
}
LOG_PRINTK("%s: bsize = %lu ; frsize = %lu ;"
" blocks = %lu ; bfree = %lu\n",
mountpoint->mnt_point,
sbuf.f_bsize, sbuf.f_frsize,
sbuf.f_blocks, sbuf.f_bfree);
rc = lsdir(mountpoint->mnt_point);
if (rc < 0) {
LOG_PRINTK("FAIL: lsdir %s: %d\n", mountpoint->mnt_point, rc);
goto out;
}
out:
//rc = fs_unmount(mountpoint);
//LOG_PRINTK("%s unmount: %d\n", mountpoint->mnt_point, rc);
//return 0;
for (;;) {
//dk_set_led(CON_STATUS_LED, (++blink_status) % 2);
k_sleep(K_MSEC(1000));
//k_thread_foreach(show_stack_usage, NULL);
//printk("\r\n");
}
}
If you have any suggestion, feel free to answer.
Thank you in advance,
Best regards,
Aurele