I2S Input to SD card, Interrupts & DMA

Zopkins
Posts: 1
Joined: Sat Mar 10, 2018 3:52 pm

I2S Input to SD card, Interrupts & DMA

Postby Zopkins » Sun Mar 11, 2018 1:53 pm

Hi, I'm struggling developing code that allows to get the input form ADC(PCM1808) to ESP32 and write it to SD Card.
The step I'm puzzled with is implementing an interrupt that indicates that the recievie DMA buffer is full.

Here is my sample code.

Code: Select all


C Code

static intr_handle_t i2s_intr_handle;

int DMA_STATUS = 0;

static void IRAM_ATTR i2s_isr(void* arg)
{
	I2S0.int_clr.val = I2S0.int_raw.val;
	DMA_STATUS = 1;
	ESP_LOGE(TAG, "DMA IS FULL");
}

...

    // Technical Reference Manual pg 303-309
    // 0x3FF4F010 I2S_INT_ST_REG (0x0010) DMA Interrupt I2S_IN_SUC_EOF_INT: Triggered when all data have been received.
	int ivalue = esp_intr_alloc_intrstatus(ETS_I2S0_INTR_SOURCE
	,ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM
	,0x3FF4F010
	,0x0000200
	,&i2s_isr
	,NULL
	,&i2s_intr_handle);
	
	if(ivalue == ESP_ERR_INVALID_ARG)
	{
		 ESP_LOGI(TAG, ":( %d ", ivalue);
	}

    //int ivalue = esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3 | ESP_INTR_FLAG_IRAM, &i2s_isr, NULL, &i2s_intr_handle);
    ESP_LOGI(TAG, "INTERRUPT Allocation:  %d ", ivalue);
    ivalue = esp_intr_enable(i2s_intr_handle);
	ESP_LOGI(TAG, "INTERRUPT Enable:      %d ", ivalue);


	
FULL CODE
-------------------------------------------------------	
	
/*
 * main.c
 *
 *	Author: Kamil
 *      Version:
 *	Latest Modification: 11.03.2018
 *
 *	CHIP REVISION:1 - https://www.tme.eu/gb/details/esp32-devkitc/development-kits-others/espressif/
 *      esp-idf - eclipse
 */

#include <stdlib.h>
#include <stddef.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/time.h>

#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_intr.h"
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "driver/sdspi_host.h"
#include "sdmmc_cmd.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "driver/i2s.h"
#include "driver/rtc_io.h"
#include "esp_clk.h"
#include "esp_attr.h"

#include "soc/i2s_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/dport_reg.h"
#include "esp_intr_alloc.h"

#include "byteswap.h"
#include "endian.h"
#include "sdkconfig.h"
#include "soc/efuse_reg.h"

static intr_handle_t i2s_intr_handle;

static const char *TAG = "I2S INPUT TEST";

#define BLINK_GPIO GPIO_NUM_18
#define SAMPLE_RATE     (96000)
#define I2S_NUM (0)
#define BUTTON_GPIO GPIO_NUM_5

static const i2s_config_t i2s_config = {
     .mode = I2S_MODE_MASTER | I2S_MODE_RX,
     .sample_rate = SAMPLE_RATE,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_24BIT,
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format =  I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_PCM_LONG,
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
     .dma_buf_count = 2,
     .dma_buf_len = 128,
     .use_apll = 1
};

static const i2s_pin_config_t pin_config = {
    .bck_io_num = 26,
    .ws_io_num = 25,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = 23
};

// This example can use SDMMC and SPI peripherals to communicate with SD card.
// By default, SDMMC peripheral is used.
// To enable SPI mode, uncomment the following line:

#define USE_SPI_MODE

// When testing SD and SPI modes, keep in mind that once the card has been
// initialized in SPI mode, it can not be reinitialized in SD mode without
// toggling power to the card.

#ifdef USE_SPI_MODE
// Pin mapping when using SPI mode.
// With this mapping, SD card can be used both in SPI and 1-line SD mode.
// Note that a pull-up on CS line is required in SD mode.
#define PIN_NUM_MISO 2
#define PIN_NUM_MOSI 15
#define PIN_NUM_CLK  14
#define PIN_NUM_CS   13
#endif //USE_SPI_MODE

uint8_t getChipRevision()
{
  return (REG_READ(EFUSE_BLK0_RDATA3_REG) >> EFUSE_RD_CHIP_VER_RESERVE_S ) && EFUSE_RD_CHIP_VER_RESERVE_V;
}

int DMA_STATUS = 0;

static void IRAM_ATTR i2s_isr(void* arg)
{
	//I2S0.int_clr.val = I2S0.int_raw.val;
	DMA_STATUS = 1;
	ESP_LOGE(TAG, "DMA FULL");
}


void app_main(void)
{

	 ESP_LOGI(TAG, "Chip Revision = %d", getChipRevision());

	  //Configure BLINK LED
	  gpio_pad_select_gpio(BLINK_GPIO);               //Set pin as GPIO
	  gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);   //Set as Output
	  gpio_set_level(BLINK_GPIO, 0);

	ESP_LOGI(TAG, "Initializing SD card");

#ifndef USE_SPI_MODE
    ESP_LOGI(TAG, "Using SDMMC peripheral");
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();

    // To use 1-line SD mode, uncomment the following line:
    // host.flags = SDMMC_HOST_FLAG_1BIT;

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();

    // GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
    // Internal pull-ups are not sufficient. However, enabling internal pull-ups
    // does make a difference some boards, so we do that here.
    gpio_set_pull_mode(15, GPIO_PULLUP_ONLY);   // CMD, needed in 4- and 1- line modes
    gpio_set_pull_mode(2, GPIO_PULLUP_ONLY);    // D0, needed in 4- and 1-line modes
    gpio_set_pull_mode(4, GPIO_PULLUP_ONLY);    // D1, needed in 4-line mode only
    gpio_set_pull_mode(12, GPIO_PULLUP_ONLY);   // D2, needed in 4-line mode only
    gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);   // D3, needed in 4- and 1-line modes

#else
    ESP_LOGI(TAG, "Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
    slot_config.gpio_miso = PIN_NUM_MISO;
    slot_config.gpio_mosi = PIN_NUM_MOSI;
    slot_config.gpio_sck  = PIN_NUM_CLK;
    slot_config.gpio_cs   = PIN_NUM_CS;
    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
#endif //USE_SPI_MODE

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5
    };

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
    // Please check its source code and implement error recovery when developing
    // production applications.
    sdmmc_card_t* card;
    esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                "If you want the card to be formatted, set format_if_mount_failed = true.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%d). "
                "Make sure SD card lines have pull-up resistors in place.", ret);
        }
        return;
    }

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);

    // File Create.
    ESP_LOGI(TAG, "Opening file");
    FILE* f = fopen("/sdcard/hello.wav", "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }

	gpio_set_direction(GPIO_NUM_22,  GPIO_MODE_INPUT);   	//Set as Input
	gpio_set_direction(GPIO_NUM_0,  GPIO_MODE_OUTPUT);   	//Set as Output
	gpio_set_direction(GPIO_NUM_25, GPIO_MODE_OUTPUT);   	//Set as Output
	gpio_set_direction(GPIO_NUM_26, GPIO_MODE_OUTPUT);   	//Set as Output

	//install and start i2s driver
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
    ESP_LOGI(TAG, "I2S Driver Installed");

    //Set pins for i2s driver
    i2s_set_pin(I2S_NUM, &pin_config);
    ESP_LOGI(TAG, "I2S Pins Configured");

    //set sample rates
    i2s_set_sample_rates(I2S_NUM, 96000);
    ESP_LOGI(TAG, "Sample Rate Set");

	i2s_set_clk(I2S_NUM, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_24BIT, I2S_CHANNEL_STEREO);

	// Output I2S master clock to pin 0
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
	WRITE_PERI_REG(PIN_CTRL, READ_PERI_REG(PIN_CTRL) & 0xFFFFFFF0);

    int REC_LOOP = 1;
    long long unsigned int bytecount = 0;
    int bytes_read = 0;
    // unsigned long int SampleValue_L;
    long unsigned int Counter = 0;

    unsigned int buf_len = 4096;
    char *buf = calloc(buf_len, sizeof(char));

    // Technical Reference Manual pg 303-309
    //    0x3FF4F010   I2S_INT_ST_REG (0x0010) I2S_IN_SUC_EOF_INT: Triggered when all data have been received.
	int ivalue = esp_intr_alloc_intrstatus(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, 0x3FF4F010 ,0x0000200, &i2s_isr, NULL, &i2s_intr_handle);
	if(ivalue == ESP_ERR_INVALID_ARG)
	{
		 ESP_LOGI(TAG, ":( %d ", ivalue);
	}

    //int ivalue = esp_intr_alloc(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3 | ESP_INTR_FLAG_IRAM, &i2s_isr, NULL, &i2s_intr_handle);
    ESP_LOGI(TAG, "INTERRUPT Allocation:  %d ", ivalue);
    ivalue = esp_intr_enable(i2s_intr_handle);
	ESP_LOGI(TAG, "INTERRUPT Enable:      %d ", ivalue);

	gpio_set_level(BLINK_GPIO, 1);
    ESP_LOGI(TAG, "Recording Begins");
    while(REC_LOOP)
    {

        bytes_read = 0;
        while(bytes_read == 0)
        {

			if( DMA_STATUS == 1 )
			{
				DMA_STATUS = 0;
				bytes_read = i2s_read_bytes(I2S_NUM, buf, buf_len,portMAX_DELAY);
			}

			/*
			// ADC Value Test
			bytes_read = i2s_read_bytes(I2S_NUM, buf, buf_len,portMAX_DELAY);
			for(Counter = 0; Counter < buf_len; Counter++)
		        	{
				ESP_LOGI(TAG, "SAMPLE: %lu  VALUE: %d ", Counter, (int)buf[Counter]);
		        	}
			*/
        }

        if(bytes_read > 0)
        {
        	ESP_LOGE(TAG, "Writing Sample Data to file");
        	fwrite(&buf, buf_len,1,f);
        	bytecount += bytes_read;
        }

    	if(bytecount >= 1440000)
    	{
    		esp_intr_free(i2s_intr_handle);
    		REC_LOOP=0;
    		ESP_LOGE(TAG, "Enough Samples");
    		break;
    	}

     }

	 gpio_set_level(BLINK_GPIO, 0);
     ESP_LOGI(TAG, "Recording Completed");

     fclose(f);

     i2s_driver_uninstall(I2S_NUM); //stop & destroy i2s driver

    // All done, unmount partition and disable SDMMC or SPI peripheral
    esp_vfs_fat_sdmmc_unmount();
    ESP_LOGI(TAG, "Card unmounted");
}

So my questions are:

1) What is the difference between interrupts i.e esp_intr_alloc_intrstatus, esp_intr_alloc,
2) How to set up the interrupt that is triggered when the DMA is full(for both cases, recieving and transmiting) and another one that is triggered when fwrite to sd card finishes, example code would be very helpfull as I have diffuculties reading datasheets.
3) How to obtain the address of an interrupt status register, for example I2S_IN_SUC_EOF_INT
4) Do I need to reset the DMA Buffer after calling i2s_read_bytes if so how can I go about it?
5) Is there a way to write data to SD card without calling i2s_read_bytes() first?
6) Any resources worth studying?

I really appreciate any help. :)

Who is online

Users browsing this forum: No registered users and 113 guests