Can I program the ESP32's I2S to use the adc continuous and dac continuous modes simultaneously?

168 views Asked by At

I'm trying to set a digital filter using the adc and dac at the same time, but I can't manage to initialize my dac since the I2S controller 0 is being used by the adc. Is there a way to switch between them? There's not much information about the usage of the dac and adc continuous library.

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_adc/adc_continuous.h"
#include <string.h>
#include <freertos/semphr.h>
#include <stdlib.h>
#include <driver/dac_continuous.h>

#define DEBUG_PIN GPIO_NUM_2
#define ADC_CHANNEL ADC_CHANNEL_5
#define SIGNAL_SAMPLE_FREQUENCY 44100
#define SAMPLE_SIZE 64

adc_continuous_handle_t adc_handle = NULL;
dac_continuous_handle_t dac_handle = NULL;

static SemaphoreHandle_t frame_ready;
static const char *TAG = "EXAMPLE";

#define EXAMPLE_ADC_GET_CHANNEL(p_data)     ((p_data)->type1.channel)
#define EXAMPLE_ADC_GET_DATA(p_data)        ((p_data)->type1.data)

void gpio_init(void);
void blink_led(void);
void adc_init(void);
void dac_init(void);

bool IRAM_ATTR Convertion_Task(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data);
static void dac_write_data_synchronously(uint8_t *data, size_t data_size);

void app_main(void) {

    gpio_init();
    adc_init();
    dac_init();
    
    adc_continuous_start(adc_handle);
    
    esp_err_t ret;
    uint32_t ret_num = 0;
    uint8_t result[SAMPLE_SIZE] = {0};
    memset(result, 0xcc, SAMPLE_SIZE);

    frame_ready = xSemaphoreCreateBinary();

    while (1)
    {
        if (xSemaphoreTake(frame_ready, portMAX_DELAY) == pdTRUE){
            while (1) {
                ret = adc_continuous_read(adc_handle, result, SAMPLE_SIZE, &ret_num, 0);
                if (ret == ESP_OK) {
                        for (int i = 0; i < ret_num; i += SOC_ADC_DIGI_RESULT_BYTES) {

                            adc_digi_output_data_t *p = (adc_digi_output_data_t*)&result[i];
                            uint32_t data = EXAMPLE_ADC_GET_DATA(p);

                            uint8_t data_8bit = (uint8_t)(data >> 4);

                            size_t data_8bit_size = sizeof(data_8bit);
                            adc_continuous_stop(adc_handle);
                            dac_write_data_synchronously(&data_8bit, data_8bit_size);
                            adc_continuous_start(adc_handle);


                            }
                } else if (ret == ESP_ERR_TIMEOUT) {
                    break;
                }
            }

        }

    }

    adc_continuous_stop(adc_handle);
    adc_continuous_deinit(adc_handle);
    vSemaphoreDelete(frame_ready);
}

void adc_init(void){
    adc_continuous_handle_cfg_t adc_handle_config = {
        .max_store_buf_size = 1024,                     // Buffer size
        .conv_frame_size = SAMPLE_SIZE                      // Conversion frame size SOC_ADC_DIGI_DATA_BYTES_PER_CONV, in bytes
    };

    adc_digi_pattern_config_t adc_pattern = {
        .atten = ADC_ATTEN_DB_11,                    // The input voltage of ADC will be attenuated extending the range
                                                    // of measurement by about 6 dB (2x)
        .channel = ADC_CHANNEL,                     // Channel these configurations are refering to
        .unit = ADC_UNIT_1,                         // Uses SAR ADC unit 2
        .bit_width = ADC_BITWIDTH_12,          // Default ADC output bits, max supported width will be selected
    };

    adc_continuous_config_t adc_config = {
        .pattern_num = 1,                           // Number of ADC pin being read
        .adc_pattern = &adc_pattern,                // List of configurations for every ADC pin  
        .sample_freq_hz = SIGNAL_SAMPLE_FREQUENCY,  // The expected ADC sampling frequency in Hz for audio capture
        .conv_mode = ADC_CONV_SINGLE_UNIT_1,            // Use ADC2 for conversion simultaneously
        .format = ADC_DIGI_OUTPUT_FORMAT_TYPE1     // 12 Bit Resolution
    };

    adc_continuous_evt_cbs_t event_callback = {
        .on_conv_done = Convertion_Task
    };

    adc_continuous_new_handle(&adc_handle_config, &adc_handle); // Initialize ADC continuous driver and get a handle to it

    adc_continuous_config(adc_handle, &adc_config); // Set ADC continuous mode required configurations

    adc_continuous_register_event_callbacks(adc_handle, &event_callback, NULL); // Register the callbacks
}

void gpio_init(void){
        const gpio_config_t debug_config = {
            .pin_bit_mask = (1ULL << DEBUG_PIN),
            .mode = GPIO_MODE_OUTPUT,
            .pull_up_en = GPIO_PULLUP_DISABLE,
            .pull_down_en = GPIO_PULLDOWN_DISABLE,
            .intr_type = GPIO_INTR_DISABLE
        };

        gpio_config(&debug_config);
}

void blink_led(void) {
    gpio_set_level(DEBUG_PIN, 1);
    vTaskDelay(100 / portTICK_PERIOD_MS);  // Blink for 100 ms
    gpio_set_level(DEBUG_PIN, 0);
}

bool Convertion_Task(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data){
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    xSemaphoreGiveFromISR(frame_ready, &xHigherPriorityTaskWoken);

    return (xHigherPriorityTaskWoken == pdTRUE);
}

void dac_init(void){
    
    dac_continuous_config_t dac_config = {
    .chan_mask = DAC_CHANNEL_MASK_CH0,          // DAC channels' mask for selecting GPIO25 on ESP32
    .desc_num = 4,                              // Typically, suggest setting the number bigger than 5, in case the DMA stopped while sending a short buffer
    .buf_size = 2048,                            // Buffer size
    .freq_hz = SIGNAL_SAMPLE_FREQUENCY,         // The expected DAC conversion frequency in Hz for audio capture
    .offset = 0,                                // Possible offset for the DAC digital data. Range -128~127
    .clk_src = DAC_DIGI_CLK_SRC_APLL,        // The default clock source of the digital controller: From 19.6 KHz to several MHz.
    .chan_mode = DAC_CHANNEL_MODE_SIMUL,        // The data in the DMA buffer is simultaneously output to the enable channel of the DAC.
    };

    dac_continuous_new_channels(&dac_config, &dac_handle);
    
    dac_continuous_enable(dac_handle);

    dac_continuous_handle_t dac_handle_temp = NULL;

    esp_err_t init_result = dac_continuous_new_channels(&dac_config, &dac_handle_temp);

    if (init_result == ESP_OK && dac_handle_temp != NULL) {
        // Initialization successful, update the global handle
        dac_handle = dac_handle_temp;

        // Enable DAC
        dac_continuous_enable(dac_handle);
    } else {
        ESP_LOGI(TAG, "DAC Initialization Error: %s", esp_err_to_name(init_result));

    }

}

static void dac_write_data_synchronously(uint8_t *data, size_t data_size)
{
    while (1) {
        dac_continuous_write(dac_handle, data, data_size, NULL, -1);
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

I tried multiple logic structures to stop and restart the adc during dac usage but I've not been successful

0

There are 0 answers