This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Writing/reading data to external flash on the nRF9160DK

Hello Nordic!

I have connected a sensor to the DK via SPI. It outputs a lot of data, a lot... for 1 second of data I get 4096 entries. Each entry is 10 bytes in length.


I want to store 20 seconds of sensor data on the flash memory, that is a total of about 0,8 MB if my calculations are correct.

The sensor has a FIFO-queue so if the write operation is quick enough there shouldn't be any data loss.
Once 20 seconds worth of data is recorded I then want to send this via LTE to server, I'm guessing in chunks.

Questions:
* How do I go about this? Is there a binary write example available?

* What size should the chunks be for this example?

* Once a file is written and done, before going in to the LTE-part of the design, is there any way to extract the bin file for data validation from the DK?


Thank you!

Parents
  • Hi Sebastiaan! Thank you for answering, truly appreciate it! I was to understand that the nRF9160DK has a 64 Mb external flash memory described here:
    https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrf91_dk%2FUG%2Fnrf91_DK%2Fexternal_memory.html

    This is indeed done via SPI.

    I'm thinking http communication but that is mostly because it's the one I know and have used, does it make much of a difference?

    That's a clever solution for getting the data, I will look into that solution.

    Many thanks!

  • Since you mention using the nRF9160DK's external flash, it is important that you switch in the lines for the external flash. That is done by re-programming the board controller, with the sequence explained here:

    https://devzone.nordicsemi.com/f/nordic-q-a/80836/extend-flash-size-of-nrf9160---single-slot-application/335345#335345

     

    create the file my_project/pm_static.yml, which holds something like this (alter sizes to your needs):

    settings_storage:
      address: 0x0
      region: external_flash
      size: 0x40000

     

    Kind regards,

    Håkon

  • It is a rare treat for us to get snow down here and even though the entire community and infrastructure shuts down, I really enjoy it. I miss living in the north.

    Ontopic: Yes, I realized that too, so I've written my own drivers by studying the datasheet of the respective component. They are included in my main.c-file. The compatible part is really just a copy paste of the adxl362 and doesn't contain any functionality. The drivers are working flawlessly for 2 of the 3 sensors. I am yet to figure out what causes problem with the third one. I handle the cs-pins manually, putting them high or low before reads/write. On these sensors, a high cs pin blocks data. So reading from 1 sensor, keeping the other two cs pins high, should only give me data stream from that sensor, however, there is a strange interference when doing this. Sorry for rambling, I understand this is impossible for you to help with.

    Off to spend the weekend in the woods in a tent, with a stove and multiple beers.
    Take care Håkon, I'll return with more rambling questions about writing to external memory in a bit

  • Hi,

     

    nWre said:
    It is a rare treat for us to get snow down here and even though the entire community and infrastructure shuts down, I really enjoy it. I miss living in the north.

    Snow is wonderful, I agree. It brightens the day during these darker winter days.

     

    nWre said:
    Ontopic: Yes, I realized that too, so I've written my own drivers by studying the datasheet of the respective component. They are included in my main.c-file. The compatible part is really just a copy paste of the adxl362 and doesn't contain any functionality. The drivers are working flawlessly for 2 of the 3 sensors. I am yet to figure out what causes problem with the third one. I handle the cs-pins manually, putting them high or low before reads/write. On these sensors, a high cs pin blocks data. So reading from 1 sensor, keeping the other two cs pins high, should only give me data stream from that sensor, however, there is a strange interference when doing this. Sorry for rambling, I understand this is impossible for you to help with.

    I'd recommend using a logic analyzer (saleae or similar if you have available) to decode the SPI traffic (MOSI, MISO, CLK, CSN) to see if there's anything out-of-the-ordinary.

    If there's a interference, you could try other GPIOs to see if there might be a hardware related issue on specific GPIOs (soldering or similar?).

    nWre said:
    Off to spend the weekend in the woods in a tent, with a stove and multiple beers.
    Take care Håkon, I'll return with more rambling questions about writing to external memory in a bit

    Take care, hope you have a wonderful weekend!

     

    Kind regards,

    Håkon

  • Hi again Håkon! Hoppas allt är bra med dig!

    I've flashed the sequence you linked earlier with the prog/debug-switch on nrf52:

    / {
            board-control {
                    external_flash_pins_routing: switch-ext-mem-ctrl {
                            compatible = "nordic,nrf9160dk-optional-routing";
                            control-gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>;
                            status = "okay";
                    };
            };
    };


    I've also added the SPI-code that you linked:

    &spi3 {
    	status = "okay";
    	sck-pin = <13>;
    	mosi-pin = <11>;
    	miso-pin = <12>;
    	cs-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
    	mx25r64: mx25r6435f@0 {
    		compatible = "jedec,spi-nor";
    		reg = <0>;
    		spi-max-frequency = <8000000>;
    		label = "MX25R64";
    		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>;
    	};
    };


    Should the CS-GPIOS match? Now that this config is done, should I be able to use say, LittleFS to write to the external memory by pointing it towards the MX25R64 in the device tree? Is there any other config I should do?

    Kind regards
    nWre

  • Hi nWre!

     

    nWre said:
    Hi again Håkon! Hoppas allt är bra med dig!

    All good here, thanks! Hope you had a wonderful trip!

     

    nWre said:
    I've flashed the sequence you linked earlier with the prog/debug-switch on nrf52:
    nWre said:
    I've also added the SPI-code that you linked:

     

    The "board-control" is setting a pin on the nRF52840 on the DK to route the ext-flash to the nRF91, while the next is a direct setting of which nRF9160 GPIO on the is connected to the /CS pin on the external-flash itself.

     

    They should match the hardware layout of the nRF9160-DK, which they do by default.

     

    nWre said:
    Now that this config is done, should I be able to use say, LittleFS to write to the external memory by pointing it towards the MX25R64 in the device tree? Is there any other config I should do?

    Define it in your pm_static.yml, here from addr offset 0, size 256kB in region "external_flash":

    littlefs_storage:
      address: 0x0
      device: MX25R64
      end_address: 0x40000
      region: external_flash
      size: 0x40000

     

    Remember to re-generate your build after altering the pm_static.yml!

     

    Kind regards,

    Håkon

  • Seems like the namespace littlefs_storage is not accepted when I try to build the project. I get this:

    'PM_littlefs_storage_ID' undeclared here (not in a function); did you mean 'littlefs_storage'?

    I'm trying to use the example from Zephyr

    /* Matches LFS_NAME_MAX */
    #define MAX_PATH_LEN 255
    
    #define PARTITION_NODE DT_NODELABEL(mx25r6435f)
    
    #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 *)FLASH_AREA_ID(storage),
    	.mnt_point = "/lfs",
    };
    #endif /* PARTITION_NODE */
    
    void memtest(void)
    {
    	struct fs_mount_t *mp =
    #if DT_NODE_EXISTS(PARTITION_NODE)
    		&FS_FSTAB_ENTRY(PARTITION_NODE)
    #else
    		&lfs_storage_mnt
    #endif
    		;
    	unsigned int id = (uintptr_t)mp->storage_dev;
    	char fname[MAX_PATH_LEN];
    	struct fs_statvfs sbuf;
    	const struct flash_area *pfa;
    	int rc;
    
    	snprintf(fname, sizeof(fname), "%s/boot_count", mp->mnt_point);
    
    	rc = flash_area_open(id, &pfa);
    	if (rc < 0)
    	{
    		printk("FAIL: unable to find flash area %u: %d\n",
    			   id, rc);
    		return;
    	}
    
    	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))
    	{
    		printk("Erasing flash area ... ");
    		rc = flash_area_erase(pfa, 0, pfa->fa_size);
    		printk("%d\n", rc);
    	}
    
    	flash_area_close(pfa);
    
    	/* 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)
    	{
    		printk("FAIL: mount id %u at %s: %d\n",
    			   (unsigned int)mp->storage_dev, mp->mnt_point,
    			   rc);
    		return;
    	}
    	printk("%s mount: %d\n", mp->mnt_point, rc);
    #else
    	printk("%s automounted\n", mp->mnt_point);
    #endif
    
    	rc = fs_statvfs(mp->mnt_point, &sbuf);
    	if (rc < 0)
    	{
    		printk("FAIL: statvfs: %d\n", rc);
    		goto out;
    	}
    
    	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);
    
    	struct fs_dirent dirent;
    
    	rc = fs_stat(fname, &dirent);
    	printk("%s stat: %d\n", fname, rc);
    	if (rc >= 0)
    	{
    		printk("\tfn '%s' siz %u\n", dirent.name, dirent.size);
    	}
    
    	struct fs_file_t file;
    
    	fs_file_t_init(&file);
    
    	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
    	if (rc < 0)
    	{
    		printk("FAIL: open %s: %d\n", fname, rc);
    		goto out;
    	}
    
    	uint32_t boot_count = 0;
    
    	if (rc >= 0)
    	{
    		rc = fs_read(&file, &boot_count, sizeof(boot_count));
    		printk("%s read count %u: %d\n", fname, boot_count, rc);
    		rc = fs_seek(&file, 0, FS_SEEK_SET);
    		printk("%s seek start: %d\n", fname, rc);
    	}
    
    	boot_count += 1;
    	rc = fs_write(&file, &boot_count, sizeof(boot_count));
    	printk("%s write new boot count %u: %d\n", fname,
    		   boot_count, rc);
    
    	rc = fs_close(&file);
    	printk("%s close: %d\n", fname, rc);
    
    	struct fs_dir_t dir;
    
    	fs_dir_t_init(&dir);
    
    	rc = fs_opendir(&dir, mp->mnt_point);
    	printk("%s opendir: %d\n", mp->mnt_point, rc);
    
    	while (rc >= 0)
    	{
    		struct fs_dirent ent = {0};
    
    		rc = fs_readdir(&dir, &ent);
    		if (rc < 0)
    		{
    			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);
    
    out:
    	rc = fs_unmount(mp);
    	printk("%s unmount: %d\n", mp->mnt_point, rc);
    }


    Önsker deg en nydelig helg!

Reply
  • Seems like the namespace littlefs_storage is not accepted when I try to build the project. I get this:

    'PM_littlefs_storage_ID' undeclared here (not in a function); did you mean 'littlefs_storage'?

    I'm trying to use the example from Zephyr

    /* Matches LFS_NAME_MAX */
    #define MAX_PATH_LEN 255
    
    #define PARTITION_NODE DT_NODELABEL(mx25r6435f)
    
    #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 *)FLASH_AREA_ID(storage),
    	.mnt_point = "/lfs",
    };
    #endif /* PARTITION_NODE */
    
    void memtest(void)
    {
    	struct fs_mount_t *mp =
    #if DT_NODE_EXISTS(PARTITION_NODE)
    		&FS_FSTAB_ENTRY(PARTITION_NODE)
    #else
    		&lfs_storage_mnt
    #endif
    		;
    	unsigned int id = (uintptr_t)mp->storage_dev;
    	char fname[MAX_PATH_LEN];
    	struct fs_statvfs sbuf;
    	const struct flash_area *pfa;
    	int rc;
    
    	snprintf(fname, sizeof(fname), "%s/boot_count", mp->mnt_point);
    
    	rc = flash_area_open(id, &pfa);
    	if (rc < 0)
    	{
    		printk("FAIL: unable to find flash area %u: %d\n",
    			   id, rc);
    		return;
    	}
    
    	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))
    	{
    		printk("Erasing flash area ... ");
    		rc = flash_area_erase(pfa, 0, pfa->fa_size);
    		printk("%d\n", rc);
    	}
    
    	flash_area_close(pfa);
    
    	/* 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)
    	{
    		printk("FAIL: mount id %u at %s: %d\n",
    			   (unsigned int)mp->storage_dev, mp->mnt_point,
    			   rc);
    		return;
    	}
    	printk("%s mount: %d\n", mp->mnt_point, rc);
    #else
    	printk("%s automounted\n", mp->mnt_point);
    #endif
    
    	rc = fs_statvfs(mp->mnt_point, &sbuf);
    	if (rc < 0)
    	{
    		printk("FAIL: statvfs: %d\n", rc);
    		goto out;
    	}
    
    	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);
    
    	struct fs_dirent dirent;
    
    	rc = fs_stat(fname, &dirent);
    	printk("%s stat: %d\n", fname, rc);
    	if (rc >= 0)
    	{
    		printk("\tfn '%s' siz %u\n", dirent.name, dirent.size);
    	}
    
    	struct fs_file_t file;
    
    	fs_file_t_init(&file);
    
    	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
    	if (rc < 0)
    	{
    		printk("FAIL: open %s: %d\n", fname, rc);
    		goto out;
    	}
    
    	uint32_t boot_count = 0;
    
    	if (rc >= 0)
    	{
    		rc = fs_read(&file, &boot_count, sizeof(boot_count));
    		printk("%s read count %u: %d\n", fname, boot_count, rc);
    		rc = fs_seek(&file, 0, FS_SEEK_SET);
    		printk("%s seek start: %d\n", fname, rc);
    	}
    
    	boot_count += 1;
    	rc = fs_write(&file, &boot_count, sizeof(boot_count));
    	printk("%s write new boot count %u: %d\n", fname,
    		   boot_count, rc);
    
    	rc = fs_close(&file);
    	printk("%s close: %d\n", fname, rc);
    
    	struct fs_dir_t dir;
    
    	fs_dir_t_init(&dir);
    
    	rc = fs_opendir(&dir, mp->mnt_point);
    	printk("%s opendir: %d\n", mp->mnt_point, rc);
    
    	while (rc >= 0)
    	{
    		struct fs_dirent ent = {0};
    
    		rc = fs_readdir(&dir, &ent);
    		if (rc < 0)
    		{
    			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);
    
    out:
    	rc = fs_unmount(mp);
    	printk("%s unmount: %d\n", mp->mnt_point, rc);
    }


    Önsker deg en nydelig helg!

Children
Related