Hi,
I am new to nrf9160 and zephyr. I was using the SD card on SPI bus to write the raw data coming from the PDM interface on nrf9160 Feather. Before that I want to do a SD write test to see how the speed is, so I wrote a simple test as attached.
The problem I am facing is:

  • I am able to mount, write and read the file on the SD card, but only on a limit speed of ~ 29KBytes/s, which according to this thread , is using a SPI speed of 4MHz. On my overlay file, I set spi-max-frequency = <8000000> . So I expect it should be at least faster than 29KBytes/s on SD write but not. Then I try changing the spi-max-frequency as different value, like 24MHz, 4MHz, even 800KHz. None of them change the speed and still keep the SD write speed as 29KBytes/s. I wonder if I have done in the wrong way of setting the SPI frequency?
  • Second thing I’ve found from the ‘disk_access_spi_sdhc.c’ under ‘zephyr/subsys/disk/’ path that it #define SDHC_SPI_SPEED 4000000, it kind of make sense that why it is running on 4MHz SPI speed, but again, it should be able to change the SPI frequency in some way, which back to my first question if I have done in the wrong way of setting the SPI frequency?
  • Third thing I want to ask, as I checked the data sheet of nrf9160, it doesn’t said anything on SPI frequency but from the register, it only list from 125kbps to 8Mbps. I wonder if it is correct to say SPI only run at a maximum speed of 8MHz on nrf9160?

Can anyone help with the issue please, thanks in advanced!!

Zirun

Attachment:
my .overlay:

&spi2 {   
	compatible = "nordic,nrf-spim";
	status = "okay";
        sck-pin = <1>;
        mosi-pin = <30>;
        miso-pin = <0>;
	cs-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
	sdhc0: sdhc@0 {
                compatible = "zephyr,mmc-spi-slot";
                reg = <0>;
                status = "okay";
                label = "SDHC0";
                spi-max-frequency = <8000000>;
        };
};

my prj.conf:

#SPI
CONFIG_GPIO=y
CONFIG_SPI=y
CONFIG_SPI_2=y
CONFIG_NRFX_SPIM=y

#Disk access
CONFIG_DISK_ACCESS=y
CONFIG_DISK_ACCESS_SDHC=y
CONFIG_DISK_ACCESS_SPI_SDHC=y

#File system
CONFIG_LOG=y
CONFIG_FILE_SYSTEM=y
CONFIG_FAT_FILESYSTEM_ELM=y
CONFIG_FS_FATFS_LFN=y
CONFIG_PRINTK=y
CONFIG_PRINTK64=y
CONFIG_SPI_NRFX_RAM_BUFFER_SIZE=8

# Stacks and heaps
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_HEAP_MEM_POOL_SIZE=16384

CONFIG_NEWLIB_LIBC=y
#CONFIG_DEBUG=y
CONFIG_BOOTLOADER_MCUBOOT=y

My code:

#include <zephyr.h>
#include <device.h>
#include <disk/disk_access.h>
#include <logging/log.h>
#include <fs/fs.h>
#include <ff.h>

#include <nrf.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> 

#define BUF_SIZE 16128
int16_t writebuff[BUF_SIZE];

LOG_MODULE_REGISTER(main);

char testbuff[] = "this is a test buffer";

struct fs_file_t filep;

static FATFS fat_fs;
/* mounting info */
static struct fs_mount_t mp = {
	.type = FS_FATFS,
	.fs_data = &fat_fs,
};

static const char *disk_mount_pt = "/SD:"; 

static int lsdir(const char *path)
{
	int res;
	struct fs_dir_t dirp;
	static struct fs_dirent entry;                                

	/* Verify fs_opendir() */
	res = fs_opendir(&dirp, path);
	if (res) {
		printk("Error opening dir %s [%d]\n", path, res);
		return res;
	}

	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) {
			break;
		}

		if (entry.type == FS_DIR_ENTRY_DIR) {
			printk("[DIR ] %s\n", entry.name);
		} else {
			printk("[FILE] %s (size = %zu)\n",
				entry.name, entry.size);
		}
	}

	/* Verify fs_closedir() */
	fs_closedir(&dirp);

	return res;
}

int FSInit(void)
{
    int res;
	static const char *disk_pdrv = "SD";
	uint64_t memory_size_mb;
	uint32_t block_count;
	uint32_t block_size;

	printk("Filesystem init...\n");
	if (disk_access_init(disk_pdrv) != 0) {
		LOG_ERR("Storage init ERROR!");
		return 0;
	}

	if (disk_access_ioctl(disk_pdrv,
			DISK_IOCTL_GET_SECTOR_COUNT, &block_count)) {
		LOG_ERR("Unable to get sector count");
		return 0;
	}
	LOG_INF("Block count %u", block_count);

	if (disk_access_ioctl(disk_pdrv,
			DISK_IOCTL_GET_SECTOR_SIZE, &block_size)) {
		LOG_ERR("Unable to get sector size");
		return 0;
	}
	printk("Sector size %u\n", block_size);

	memory_size_mb = (uint64_t)block_count * block_size;
	printk("Memory Size(MB) %u\n", (uint32_t)(memory_size_mb >> 20));
	
	mp.mnt_point = disk_mount_pt;
	res = fs_mount(&mp);
	if (res == FR_OK) {
		printk("Disk mounted.\n");
		lsdir(disk_mount_pt);
	} else {
		printk("Error mounting disk.\n");
		return 0;
	}
    return 1;
}

static int SDWriteSpeedTest(void)
{
	int res;
	for (int32_t i = 0; i < BUF_SIZE; i++)
	{
		//Gernate random number into buffer
		writebuff[i] = rand() % 32768;
	}
	LOG_INF("Opening file path");
	res = fs_open(&filep, "/SD:/test.bin", FS_O_CREATE | 
							FS_O_WRITE);
	if (res) {
		LOG_ERR("Error opening file [%d]", res);
		return 0;
	}
	LOG_INF("Done opening file path");
	if (fs_write(&filep, writebuff, BUF_SIZE*sizeof(int16_t)) 
					!= BUF_SIZE*sizeof(int16_t))
	{
		LOG_ERR("failed to wirte buff");
		fs_close(&filep);
		return 0;
	}
	LOG_INF("Done writing");
	fs_close(&filep);
	return 1;
}

void main(void)
{
	int res;
	/* raw disk i/o */
	res = FSInit();
	if (!res)
	{
		LOG_ERR("failed to init filesystem");
	}

	res = SDWriteSpeedTest();
	if (!res)
	{
		LOG_ERR("failed to write");
	}
}

Hers is the result from the terminal:
[00:00:09.860,168] <inf> sdhc_spi: Found a 29664 MiB SDHC card.
[00:00:09.861,450] <inf> sdhc_spi: Manufacturer ID=39 OEM=‘PH’ Name=‘SD32G’ Revision=0×60 Serial=0xda44af6e
[00:00:09.861,480] <inf> main: Block count 60751872
[00:00:09.891,082] <inf> sdhc_spi: Found a 29664 MiB SDHC card.
[00:00:09.892,364] <inf> sdhc_spi: Manufacturer ID=39 OEM=‘PH’ Name=‘SD32G’ Revision=0×60 Serial=0xda44af6e
[00:00:10.551,940] <inf> main: Opening file path
[00:00:10.573,150] <inf> main: Done opening file path
[00:00:11.652,130] <inf> main: Done writing

In this way, speed is 32256 Bytes / (11.652130 - 10.573150) sec ~= 29.19KB/s

    zirunhong I wonder if I have done in the wrong way of setting the SPI frequency?

    The frequency is a function of both the nRF9160 and the SD card you’re using. What’s the rated max speed of the card?

    Seems like you’re setting the frequency correctly in your overlay

    zirunhong I wonder if it is correct to say SPI only run at a maximum speed of 8MHz on nrf9160?

    From a brief glance, the test code you have looks like it should work.

    Have you read back the data to see what was the result? I’d be curious if the data you’re writing is corrupt or not.

    Have you also, for sanity, probed the GPIO pins you’re using for SPI? (Also which pins are you using?) It’s always good to double check hardware fundamentals even if the problem doesn’t seem to manifest that way. It also doesn’t hurt to have a probe on the SPI CLK line to see if you can, indeed, modify the speed.

    zirunhong I wonder if it is correct to say SPI only run at a maximum speed of 8MHz on nrf9160?

    I’m running the onboard flash (supposedly) at 40MHz. I did see the 8bps though. I’ve yet to probe these signals as they run on inner layers. Now I’m curious to see if it truly is 40MHz! I know that 8bps doesn’t translate to 8MHz as there’s overhead, delays, finite buffers for transferring, etc.

    One thing that you may want to turn on if you’re using logs as measurements is the CONFIG_LOG_IMMEDIATE option. Otherwise the log thread will display the messages when the system is not busy processing higher priority tasks.

    Similarly, back to the probing part, you could probe the CLK line to check for the start and end of transfer and make sure you’re hardware measurements agree with the software output.

    Hope that helps!

      jaredwolff Hi thanks for reply so fast!

      What’s the rated max speed of the card?

      Have you read back the data to see what was the result?

      • I haven’t look back on this test data for accuracy, but it is show the exact same size as written(32256 Bytes). Additionally, I was trying to writing some PDM data into a wav file on sd card and it playback with clear recording voice so I assume the data is correct.

      Have you also, for sanity, probed the GPIO pins you’re using for SPI? (Also which pins are you using?)

      • CLK-P0.01(D5); MOSI-P0.30(D3); MISO-P0.0(D4); CS-P0.29(D2).
      • I try probe the CLK line on my scope and wired thing happens. The file system is not working (it can’t mount the SD card, read or write), when I remove the probe, it works as normal. When probing other line, it works perfect as well. I thought it might because of my scope goes wrong but I try with another scope and still the same. Still debugging…

      if you’re using logs as measurements is the CONFIG_LOG_IMMEDIATE option.

      • Thanks for the suggestion. I will add it on configuration

        zirunhong I try probe the CLK line on my scope and wired thing happens. The file system is not working (it can’t mount the SD card, read or write), when I remove the probe, it works as normal. When probing other line, it works perfect as well. I thought it might because of my scope goes wrong but I try with another scope and still the same. Still debugging…

        There’s also a mention of changing the drive strength of the GPIO when using higher speeds. I’m assuming it’s done in Nordic’s driver but unsure.

        Also make sure that you’re using high impedance mode on your probes. It should still drive the signal no matter what but I guess it depends on the probe you’re using.

          zirunhong I’ve also noticed that the SD breakout you’re using has “3V Only” Witten all over it. The nRF9160 Feather is a 3.3V board so that could also be related to your speed issues. You can look at the data sheet for your card to confirm it’s operating range. Though, in this case, I’m not sure this is the main issue.

            jaredwolff Thanks for your reply.

            There’s also a mention of changing the drive strength of the GPIO when using higher speeds. I’m assuming it’s done in Nordic’s driver but unsure.

            • I see, so can I change the drive strength manually? If so, not sure if it is the correct, but I added the following after initial the file system:
                      spim = device_get_binding(GPIO0);
              	if (spim == NULL) {
              		LOG_ERR("Error: device_get_binding()");
              		return 0;
              	}
              	res = gpio_pin_configure(spim, SCK_PIN, GPIO_OUTPUT | GPIO_DS_ALT_HIGH | GPIO_DS_ALT_LOW);
              	if (res < 0) {
              		LOG_ERR("set pin mode error\n");
              		return 0;
              	}

            Also make sure that you’re using high impedance mode on your probes. It should still drive the signal no matter what but I guess it depends on the probe you’re using.

            • The input impedance of the channel is marked as 1MOhms/15pF, so I think that should be in high impedance mode

            jaredwolff

            I’ve also noticed that the SD breakout you’re using has “3V Only” Witten all over it. The nRF9160 Feather is a 3.3V board so that could also be related to your speed issues. You can look at the data sheet for your card to confirm it’s operating range. Though, in this case, I’m not sure this is the main issue.

            • I checked the data sheet and it says: MicroSD cards must use 3.3V, so take care to only hook 3.3V to this pin. Hooking 5V or VBAT to this pin will damage your microSD card.

            @jaredwolff Zirun and I are working on this together. It would be amazing if you could confirm if we can get max SPI speed on this board or not. We need to be able to stream a number of seconds of audio data to the SD card or, possibly an external RAM chip but that would also be on the SPI bus. I spent a little bit of time troubleshooting this also but nothing stood out as a root cause. Perhaps it’s a limitation of the nRF9160?

            Thanks,
            Steven

              imoon Got it. I’ll reach out on Devzone to see if I get an answer from Nordic.

              jaredwolff
              Hey, I got the probe working and the frequency show on the scope is 250kHz not matter what frequency I was set

                zirunhong I saw your comment in the Nordic thread. Best to keep it going with them. I won’t be able to help you much more here.

                  13 days later

                  Hi,

                  I finally figure out how to change the speed, not 100% sure if it is the best way to do so. I am still waiting for someone from Nodic DevZone to confirm on this thread.

                  So what I have done is changing the SDHC_SPI_INITIAL_SPEED under zephyr\subsys\disk\disk_access_spi_sdhc.c to any frequency you want, in my case 4000000 (4MHz).

                  Theoretically, I should change SDHC_SPI_SPEED instead, since it is the SPI speed after initialization, but somehow it is not working according to my observation. I guess that is because the SPI hardware register is not updated with the new speed according to the code, even though it is assigned to the data struct, so it keep using the initial speed all the time.

                  Here is the result with the same code I posted:

                  From the log:
                  [00:00:00.480,438] <inf> main: Opening file path
                  [00:00:00.569,824] <inf> main: Done opening file path
                  [00:00:00.688,964] <inf> main: Done writing

                  Speed is 32256 Bytes / (0.688964 - 0.569824) sec ~= 270 KB/s, almost ten times faster!

                  From the scope:
                  scope

                  Hope that helps someone facing the similar issue!! Also thanks for your help @jaredwolff !

                  Zirun

                    zirunhong Thanks for sharing. Glad you’re making some progress. Sorry it’s not exactly what you wanted but it’s nice to see that you’re getting some improvement!

                    Terms and Conditions | Privacy Policy