I2S Input to SD card, Interrupts & DMA
Posted: 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.
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.
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");
}
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.