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
}
}