I'm developing a BLE device wich includes the USB Mass Storage feature with microSD card throught SPI.
My developmetn environment:
- nRF52833 DK
- nRF Connect SDK v1.9.1
- nRF Connect for VS Code v2022.4.219
I'm new with nRF devices and I'm happily struggling with new concepts like zephyr, devicetree and so on. For this reason I have a lot of doubts and questions but here I'll try to be short and sweet as much as possible. Sorry in advance if I'm asking for trivial concepts.
For my purpose above, I loaded in VS Code the "ncs\v1.9.1\zephyr\samples\subsys\usb\mass" example wich shows how to create an USB Mass Storage Application. The application's code is the following:
/* * Copyright (c) 2016 Intel Corporation. * Copyright (c) 2019-2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr.h> #include <logging/log.h> #include <usb/usb_device.h> #include <fs/fs.h> #include <stdio.h> LOG_MODULE_REGISTER(main); #if CONFIG_DISK_DRIVER_FLASH #include <storage/flash_map.h> #endif #if CONFIG_FAT_FILESYSTEM_ELM #include <ff.h> #endif #if CONFIG_FILE_SYSTEM_LITTLEFS #include <fs/littlefs.h> FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage); #endif static struct fs_mount_t fs_mnt; static int setup_flash(struct fs_mount_t *mnt) { int rc = 0; #if CONFIG_DISK_DRIVER_FLASH unsigned int id; const struct flash_area *pfa; mnt->storage_dev = (void *)FLASH_AREA_ID(storage); id = (uintptr_t)mnt->storage_dev; rc = flash_area_open(id, &pfa); 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); if (rc < 0 && IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) { printk("Erasing flash area ... "); rc = flash_area_erase(pfa, 0, pfa->fa_size); printk("%d\n", rc); } if (rc < 0) { flash_area_close(pfa); } #endif return rc; } static int mount_app_fs(struct fs_mount_t *mnt) { int rc; #if CONFIG_FAT_FILESYSTEM_ELM static FATFS fat_fs; mnt->type = FS_FATFS; mnt->fs_data = &fat_fs; if (IS_ENABLED(CONFIG_DISK_DRIVER_RAM)) { mnt->mnt_point = "/RAM:"; } else if (IS_ENABLED(CONFIG_DISK_DRIVER_SDMMC)) { mnt->mnt_point = "/SD:"; } else { mnt->mnt_point = "/NAND:"; } #elif CONFIG_FILE_SYSTEM_LITTLEFS mnt->type = FS_LITTLEFS; mnt->mnt_point = "/lfs"; mnt->fs_data = &storage; #endif rc = fs_mount(mnt); return rc; } static void setup_disk(void) { struct fs_mount_t *mp = &fs_mnt; struct fs_dir_t dir; struct fs_statvfs sbuf; int rc; fs_dir_t_init(&dir); if (IS_ENABLED(CONFIG_DISK_DRIVER_FLASH)) { rc = setup_flash(mp); if (rc < 0) { LOG_ERR("Failed to setup flash area"); return; } } if (!IS_ENABLED(CONFIG_FILE_SYSTEM_LITTLEFS) && !IS_ENABLED(CONFIG_FAT_FILESYSTEM_ELM)) { LOG_INF("No file system selected"); return; } rc = mount_app_fs(mp); if (rc < 0) { LOG_ERR("Failed to mount filesystem"); return; } /* Allow log messages to flush to avoid interleaved output */ k_sleep(K_MSEC(50)); printk("Mount %s: %d\n", fs_mnt.mnt_point, rc); rc = fs_statvfs(mp->mnt_point, &sbuf); if (rc < 0) { printk("FAIL: statvfs: %d\n", rc); return; } printk("%s: bsize = %lu ; frsize = %lu ;" " blocks = %lu ; bfree = %lu\n", mp->mnt_point, sbuf.f_bsize, sbuf.f_frsize, sbuf.f_blocks, sbuf.f_bfree); rc = fs_opendir(&dir, mp->mnt_point); printk("%s opendir: %d\n", mp->mnt_point, rc); if (rc < 0) { LOG_ERR("Failed to open directory"); } while (rc >= 0) { struct fs_dirent ent = { 0 }; rc = fs_readdir(&dir, &ent); if (rc < 0) { LOG_ERR("Failed to read directory entries"); break; } if (ent.name[0] == 0) { printk("End of files\n"); break; } printk(" %c %u %s\n", (ent.type == FS_DIR_ENTRY_FILE) ? 'F' : 'D', ent.size, ent.name); } (void)fs_closedir(&dir); return; } void main(void) { int ret; setup_disk(); ret = usb_enable(NULL); if (ret != 0) { LOG_ERR("Failed to enable USB"); return; } LOG_INF("The device is put in USB mass storage mode.\n"); }
The application starts with the configuration "RAM disk as block device" so it's not intended to be quickly used with a microSD card. To activate it, I added in the prj.conf the following lines:
CONFIG_DISK_DRIVER_SDMMC=y CONFIG_FILE_SYSTEM=y CONFIG_FAT_FILESYSTEM_ELM=y CONFIG_SPI=y
Is it enough? I got the first three lines from the Kconfig file related to the configuration menù in the nRF Connect extension of VS Code and the last line from an example on web (maybe outdated). And here comes my first question:
1) If I want to add a feature in my custom application where I can find all the related necessary configurations to add in prj.conf? In the nRF Connect SDK documentation I can only find a huge list of all configuration and not a section that tell me "Do you want to use SPI? Ok, so you should use these configurations: ..."
After that, I created an overlay file for the nRF52833 DK board for the SPI peripheral:
&spi1 { cs-gpios = <&gpio0 27 GPIO_ACTIVE_LOW>; sdhc0: sdhc@0 { compatible = "zephyr,mmc-spi-slot"; reg = <0>; status = "okay"; label = "SDHC0"; spi-max-frequency = <24000000>; }; };
I got the overlay above from this example.
Now my last questions:
2) The USB Mass Storage example uses the File System driver. How the FS and the SPI peripheral are linked? If I want use the spi2 instead? I suppose that the only link between the driver and the hardware is the label SDHC0 properties in the devicetree node. Am I worng?
3) I see that there is a lot of documentation around the web, but sometime I lost myself between the outdated information about old SDKs and the new suggested nRF Connect SDK. Do you have some suggestions as compass for orientation?
Thanks in advance.