NCS: Writing I2S data to SD card

Hi everybody,

I'm using the nRF Connect SDK v2.4.99 and an nRF5340 DK to write data received via I2S to the SD Card. 

My application is based on the SDK's I2S echo sample. Both, the I2S sample and the code for writing to the SD Card are working independently. However, when I combine both into one application, my I2S code fails after very short time with the error message: "Failed to allocate next RX buffer: -12" ("i2s_nrfx.c" in line 205).

I was doing some debugging but I couldn't figure out the problem. I'm posting my main.c below. Since I do not want to use any I2S TX device or buffer, I removed all corresponding code from the I2S echo sample. I would appreciate if anybody could point me towards the bug in my code.

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/drivers/gpio.h>
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/storage/disk_access.h>
#include <zephyr/logging/log.h>
#include <zephyr/fs/fs.h>
#include <ff.h>
#include <stdio.h>

LOG_MODULE_REGISTER(main);

/*------------------------------------
 *------------I2S SETTINGS------------
 *------------------------------------
 */ 
#if DT_NODE_EXISTS(DT_NODELABEL(i2s_rxtx))
#define I2S_RX_NODE  DT_NODELABEL(i2s_rxtx)
#define I2S_TX_NODE  I2S_RX_NODE
#else
#define I2S_RX_NODE  DT_NODELABEL(i2s_rx)
// #define I2S_TX_NODE  DT_NODELABEL(i2s_tx)
#endif

#define SAMPLE_FREQUENCY    44100
#define SAMPLE_BIT_WIDTH    32
#define BYTES_PER_SAMPLE    sizeof(int32_t) //4bytes
#define NUMBER_OF_CHANNELS  1
#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 4) * NUMBER_OF_CHANNELS) 
#define INITIAL_BLOCKS      2
#define TIMEOUT 1

#define BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
#define BLOCK_COUNT (INITIAL_BLOCKS + 2)
K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);

/*----------------------------------------
 *----------BUTTONS/ISR CONFIG------------
 *----------------------------------------
 */ 
#define SW1_NODE        DT_ALIAS(sw1)
#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
static struct gpio_dt_spec sw1_spec = GPIO_DT_SPEC_GET(SW1_NODE, gpios);
#endif


static K_SEM_DEFINE(toggle_transfer, 1, 1); //Sera la variable que activara o no el semaforo, segun el boton sw1


#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
static void sw1_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	k_sem_give(&toggle_transfer); //Si se apreta el boton, la variable "se activa"
}
#endif

static bool init_interruptions(void)
{
	int ret;

#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
	static struct gpio_callback sw1_cb_data;

	if (!device_is_ready(sw1_spec.port)) {
		printk("%s is not ready\n", sw1_spec.port->name);
		return false;
	}

	ret = gpio_pin_configure_dt(&sw1_spec, GPIO_INPUT);
	if (ret < 0) {
		printk("Failed to configure %s pin %d: %d\n",
		       sw1_spec.port->name, sw1_spec.pin, ret);
		return false;
	}

	ret = gpio_pin_interrupt_configure_dt(&sw1_spec, GPIO_INT_EDGE_TO_ACTIVE);
	if (ret < 0) {
		printk("Failed to configure interrupt on %s pin %d: %d\n",
		       sw1_spec.port->name, sw1_spec.pin, ret);
		return false;
	}

	gpio_init_callback(&sw1_cb_data, sw1_handler, BIT(sw1_spec.pin));
	gpio_add_callback(sw1_spec.port, &sw1_cb_data);
	printk("Press \"%s\" to stop/restart I2S streams\n", sw1_spec.port->name);
#endif
	(void)ret;
	return true;
}

/* ----------------------------------------------------------------------------
* ----------------------------------- SD CARD ---------------------------------
* -----------------------------------------------------------------------------
*/
#define DISK_DRIVE_NAME "SD"
#define DISK_MOUNT_PT "/"DISK_DRIVE_NAME":"
char file_name[16];
static const char *disk_mount_pt = DISK_MOUNT_PT;

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

bool SD_iniciar(void){
	static const char *disk_pdrv = DISK_DRIVE_NAME;
		uint64_t memory_size_mb;
		uint32_t block_count;
		uint32_t block_size;

		if (disk_access_init(disk_pdrv) != 0) {
			LOG_ERR("Storage init ERROR!");
			return false;
		}

		if (disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_COUNT, &block_count)) {
			LOG_ERR("Unable to get sector count");
			return false;
		}
		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 false;
		}
		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));
		return true;
}

int SD_lsdir(const char *path){
//Pre: Absolute path to list
//Post: -5 on error, listed entries on success. Retorna int num de wavs

	int ret;
	struct fs_dir_t dir;
	static struct fs_dirent entry;
	int count = 0;
	uint8_t exst_wavs = 0;

	fs_dir_t_init(&dir);

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

	printk("\nListing dir %s ...\n", path);
	while(true) {
		/* Verify fs_readdir() */
		ret = fs_readdir(&dir, &entry);
		/* entry.name[0] == 0 means end-of-dir */
		if (ret || 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);
		}
		if(strstr(entry.name,".WAV") != NULL) exst_wavs++;
		count++;
	}
	printk("Hay %d archivos .WAV", exst_wavs);
	printk(" en un total de %d archivos.\n", count);
	/* Verify fs_closedir() */
	fs_closedir(&dir);
	if (ret == 0) {
		ret = exst_wavs;
	}
	return ret;
}

int SD_WriteTest(void)
{
	//#define BUF_SIZE 16128
	#define BUF_SIZE 100 //Para ASCII
	int16_t writebuff[BUF_SIZE];
	int ret;
	struct fs_file_t file;
	fs_file_t_init(&file);
    for (int i = 0; i < BUF_SIZE - 1; i++)
    {
        writebuff[i] = 'A' + (i % 26); // Escribe el abecedario
    }
	writebuff[BUF_SIZE - 1] = '\0'; // El texto debe tener terminación nula
	LOG_INF("Opening file path");
	ret = fs_open(&file, "/SD:/HOLA.txt", FS_O_CREATE | FS_O_WRITE);
	if (ret) {
		LOG_ERR("Error opening file [%d]", ret);
		return 0;
	}
	LOG_INF("Done opening file path");
	if (fs_write(&file, writebuff, BUF_SIZE*sizeof(int16_t)) != BUF_SIZE*sizeof(int16_t))
	{
		LOG_ERR("failed to write buff");
		fs_close(&file);
		return 0;
	}
	LOG_INF("Done writing");
	fs_close(&file);
	return 1;
}

static void SD_ejecutar(void){
		if(SD_iniciar()){
			mp.mnt_point = disk_mount_pt;
			int ret = fs_mount(&mp);
			if (ret == FR_OK) {
				printk("Disk mounted.\n");
				if (SD_lsdir(disk_mount_pt) == 0) {
					printk("Creando test files.\n");
					ret = SD_WriteTest();
					if (!ret){
						LOG_ERR("failed to write");
					}
					printk("Showing again...\n");
					SD_lsdir(disk_mount_pt);
				}
			}	
		else {
			printk("Error mounting disk.\n");
		}
	}
}


/* ----------------------------------------------------------------------------
* --------------------------------- I2S AUDIO ---------------------------------
* -----------------------------------------------------------------------------
*/
static bool configure_streams(const struct device *i2s_dev_rx,
			    //   const struct device *i2s_dev_tx,
			      const struct i2s_config *config)
{
	int ret;

	ret = i2s_configure(i2s_dev_rx, I2S_DIR_RX, config);
	if (ret < 0) {
		printk("Failed to configure RX stream: %d\n", ret);
		return false;
	}

	return true;
}

static bool prepare_transfer(const struct device *i2s_dev_rx, const struct device *i2s_dev_tx)
{
	int ret;

	for (int i = 0; i < INITIAL_BLOCKS; ++i) {
		void *mem_block;

		ret = k_mem_slab_alloc(&mem_slab, &mem_block, K_NO_WAIT);
		if (ret < 0) {
			printk("Failed to allocate TX block %d: %d\n", i, ret);
			return false;
		}
		memset(mem_block, 0, BLOCK_SIZE);
		ret = i2s_write(i2s_dev_tx, mem_block, BLOCK_SIZE);
		if (ret < 0) {
			printk("Failed to write block %d: %d\n", i, ret);
			return false;
		}
	}

	return true;
}

static bool trigger_command(const struct device *i2s_dev_rx, enum i2s_trigger_cmd cmd)
{
	int ret;

	ret = i2s_trigger(i2s_dev_rx, I2S_DIR_RX, cmd);
	if (ret < 0) {
		printk("Failed to trigger command %d on RX: %d\n", cmd, ret);
		return false;
	}

	return true;
}

/* ----------------------------------------------------------------------------
* ----------------------------------- MAIN ------------------------------------
* -----------------------------------------------------------------------------
*/
int main(void)
{
	const struct device *const i2s_dev_rx = DEVICE_DT_GET(I2S_RX_NODE);
	// const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
	struct i2s_config config;
	printk("SD_I2S program\n");
	SD_ejecutar();
	if (!device_is_ready(i2s_dev_rx)) {
		printk("%s is not ready\n", i2s_dev_rx->name);
		return 0;
	}

	config.word_size = SAMPLE_BIT_WIDTH;
	config.channels = NUMBER_OF_CHANNELS;
	config.format = I2S_FMT_DATA_FORMAT_I2S;
	config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
	config.frame_clk_freq = SAMPLE_FREQUENCY;
	config.mem_slab = &mem_slab;
	config.block_size = BLOCK_SIZE;
	config.timeout = TIMEOUT; 
	if (!configure_streams(i2s_dev_rx, &config)) {
		return 0;
	}
	int i = 1; //Para la generacion de archivos
	struct fs_file_t file;
	for (;;) {
		k_sem_take(&toggle_transfer, K_FOREVER); //Solo avanza si la variable toggle se activa (cuando se toca el boton)
		fs_file_t_init(&file);
		LOG_INF("Opening file path");
		char filename[30];
		sprintf(&filename, "/SD:/audio%d.txt", i); //Para que sea nombre archivo iterativo
		int ret = fs_open(&file, filename, FS_O_CREATE | FS_O_WRITE);
		if (ret) {
			LOG_ERR("Error opening file [%d]", ret);
			return 0;
		}

		printk("Streams started\n");
		if (!init_interruptions()) {
			return 0;
		}

		if (!trigger_command(i2s_dev_rx, I2S_TRIGGER_START)) {
			return 0;
		}
		while (k_sem_take(&toggle_transfer, K_NO_WAIT) != 0) { //Solo en el caso que se active la variable sale del bucle, PERO NO ESPERA a que sea activado
			void *mem_block;
			size_t block_size;
			ret = i2s_read(i2s_dev_rx, &mem_block, &block_size);
			if (ret < 0) {
				printk("Failed to read data: %d\n", ret);
				return 0;
			}

			ret = fs_write(&file, mem_block, block_size);
			if (ret < 0) {
				printk("Failed to write data in sd card: %d\n", ret);
				break;
			}
		}
		if (!trigger_command(i2s_dev_rx, I2S_TRIGGER_DROP)) {
			return 0;
		}
		printk("Stream stopped\n");

		printk("File named \"audio%d.txt\" successfully created\n", i);		
		fs_close(&file);
		i++;//contador
	}
}

Parents
  • I am not able to replicate the issue. Seems like the application is merged OK but I cannot test the functionality of the I2S. Can you give me more details of the SD you are using and the pin connections you have to it?

  • Hi, I am working with him.

    The SD card we are using is SanDisk EDGE 16GB class 10. The connections are:
    SCK->  P1.15
    MISO->P1.14
    MOSI->P1.13
    CS->    P1.12

    Although the problem doesn't appear to be the SD card, since in another code (same card, same pin connections) we could save data in the SD card without any problem.

    Here is the overlay file:

    //I2S
    &pinctrl {
    	i2s0_default_alt: i2s0_default_alt {
    		group1 {
    			psels = <NRF_PSEL(I2S_SCK_M, 0, 7)>,
    				<NRF_PSEL(I2S_LRCK_M, 0, 12)>,
    				<NRF_PSEL(I2S_SDIN, 1, 06)>;
    				//<NRF_PSEL(I2S_SDOUT, 1, 07)>;
    		};
    	};
    };
    
    
    &clock {
    	hfclkaudio-frequency = <11289600>;
    };
    
    i2s_rx: &i2s0 {
    	status = "okay";
    	pinctrl-0 = <&i2s0_default_alt>;
    	pinctrl-names = "default";
    	clock-source = "ACLK";
    };
    
    //SD CARD
    &spi4 {
    	status = "okay";
    	cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
    //     sck-pin = <17>;
    //     mosi-pin = <13>;
    //     miso-pin = <14>;
    	sdhc0: sdhc@0 {
    			compatible = "zephyr,sdhc-spi-slot";
    			reg = <0>;
    			status = "okay";
    			mmc{
    				compatible = "zephyr,sdmmc-disk";
    				status = "okay";
    			};
    			label = "SDHC0";
    			spi-max-frequency = <25000000>;
    	};
    };
    /* Because FAT FS needs at least 64kiB partition and default
    * storage_partition is 32kiB for that board, we need to reorgatnize
    * partitions to get at least 64KiB.
    * This overlay removes image slot partitions and strips each of 64kiB,
    * and removes the storage partition to add the additional 2*64kiB to
    * it.
    */
    /delete-node/ &slot0_partition;
    /delete-node/ &slot1_partition;
    /delete-node/ &storage_partition;
    
    &flash0 {
    
    	partitions {
    		compatible = "fixed-partitions";
    		#address-cells = <1>;
    		#size-cells = <1>;
    
    		slot0_partition: partition@c000 {
    			reg = <0x0000C000 0x00066000>;
    		};
    		slot1_partition: partition@72000 {
    			reg = <0x00072000 0x00066000>;
    		};
    
    		storage_partition: partition@d8000 {
    			label = "storage";
    			reg = <0x000d8000 0x00028000>;
    		};
    	};
    };
    
    / {
    	msc_disk0 {
    		status="okay";
    		compatible = "zephyr,flash-disk";
    		partition = <&storage_partition>;
    		disk-name = "SD";
    		/* cache-size == page erase size */
    		cache-size = <4096>;
    	};
    };

  • Hi Susheel, my colleague Agus uploaded a zip-file of the complete project above. This should include all the files you are asking.

  • I managed to get the setup ready using your project, Mortiz, but unable to initialize the SPI for some reason. 

    [00:00:00.397,613] <err> sd: Card error on CMD0
    [00:00:00.397,644] <err> main: Storage init ERROR!

    Ill investigate this further on Monday.

  • Moritz, 

    I connected an SD card controller with spim4 connected to 

    SCK-13
    MOSI-14
    MISO-15
    CSN - 16
    Do you have the same setup? I still do not see initialization go through with this setup. Maybe I am not using the right SPIM instance?

  • Hi,
    No my SPI setup is different in the nrf5340.

    SCK-1.15

    MISO-1.14

    MOSI-1.13

    CS-1.12

    I have only defined CS in the overlay file, since the rest were already predefined in the nrf5340_cpuapp_common-pinctrl.dtsi file. Here is how my pinout looks like:

    -

    And this is how I defined the SPI4 in the overlay file.

    &spi4 {
    	status = "okay";
    	cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
    	sdhc0: sdhc@0 {
    			compatible = "zephyr,sdhc-spi-slot";
    			reg = <0>;
    			status = "okay";
    			mmc{
    				compatible = "zephyr,sdmmc-disk";
    				status = "okay";
    			};
    			label = "SDHC0";
    			spi-max-frequency = <25000000>;
    	};
    };

    I hope I answered your question. 

    Agus

  • Agus, I used your project with the pin numbers you mentioned. Now I have the same devicetree

    And I am able to run your project without any issue when I connect it to a normal SD card.

    Its been running for 30 minutes now without any error.

Reply Children
Related