Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Second call to i2s_adc_enable() always blocks (IDFGH-572) #2964

Closed
detly opened this issue Jan 18, 2019 · 7 comments
Closed

Second call to i2s_adc_enable() always blocks (IDFGH-572) #2964

detly opened this issue Jan 18, 2019 · 7 comments

Comments

@detly
Copy link

detly commented Jan 18, 2019

Environment

  • Development Kit: Wemos Lolin D32
  • Module or chip used: ESP32-WROOM-32
  • IDF version: v3.3-beta1-223-ga62cbfec9
  • Build System: Cmake
  • Compiler version: 1.22.0-80-g6c4433a
  • Operating System: Linux (Ubuntu 18.04)
  • Power Supply: USB

Problem Description

In a program that uses the I2S direct-from-ADC capability, I can only call i2s_adc_enable() once before it blocks ie. the second time I call it, it never returns. So I can't switch this capability on and off as my program moves through different states.

Expected Behavior

I would expect that if I call i2s_adc_disable() I would be able to call i2s_adc_enable() again later, or at the very least I would get an error I can handle.

Actual Behavior

A second call to i2s_adc_enable() results in the program not proceeding. There's no error or stack trace, just nothing else happens. I can't figure out from the docs if there are any other calls I need to make for this to work.

Steps to reproduce

  1. Check out the test code.

    git checkout https://gitlab.com/detly/i2s_test.git
    
  2. Build with idf.py build.

  3. Flash with idf.py flash monitor.

You do not need anything connected to the ADCs to reproduce.

Code to reproduce this issue

See the test project on Gitlab (no login is required, it's public). The basic architecture is:

  • I2S and ADC are setup
  • I2S is configured for direct ADC reading
  • the main task calls i2s_adc_enable()
  • the main task starts another task for reading from I2S with xTaskCreate()
  • this task runs, calling i2s_read() in a loop and printing a small amount of the data
  • the main task waits 2s and kills the task with vTaskDelete()
  • the main task calls i2s_adc_disable()
  • the main task waits 2s and loops again
  • on the next call to i2s_adc_enable(), "nothing" happens after this

By "nothing" I mean: nothing more is printed to the log, and there's no visible errors eg. a stack trace or error indicating that a FreeRTOS call timed out, no processor exceptions, etc.

My guess is that it's hanging on trying to acquire the lock for the ADC, but that's released in an earlier call (to i2s_adc_disable()) as far as I can tell.

The only example code I can find is in examples/peripherals/i2s_adc_dac, and that does a full reset of the chip in between connections. That's not an option in my project.

Debug Logs

I (0) cpu_start: App cpu up.
I (274) heap_init: Initializing. RAM available for dynamic allocation:
I (281) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (287) heap_init: At 3FFB2F10 len 0002D0F0 (180 KiB): DRAM
I (294) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (300) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (306) heap_init: At 400882BC len 00017D44 (95 KiB): IRAM
I (312) cpu_start: Pro cpu start user code
I (331) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (333) I2S: DMA Malloc info, datalen=blocksize=4092, dma_buf_count=2
I (333) I2S: PLL_D2: Req RATE: 24000, real rate: 1515.000, BITS: 16, CLKM: 55, BCK: 60, MCLK: 55.556, SCLK: 48480.000000, diva: 64, divb: 35
I (353) i2s-test: enabling I2S ADC
I (353) I2S: PLL_D2: Req RATE: 24000, real rate: 1515.000, BITS: 16, CLKM: 55, BCK: 60, MCLK: 55.556, SCLK: 48480.000000, diva: 64, divb: 35
I (363) i2s-test: starting I2S read task
I (413) i2s-test: data: 0x6C 0x68 0x60 0x68
I (413) i2s-test: data: 0x68 0x60 0x68 0x6A
I (413) i2s-test: data: 0x60 0x68 0x6A 0x68
I (413) i2s-test: data: 0x68 0x6A 0x68 0x6C
I (413) i2s-test: data: 0x6A 0x68 0x6C 0x68
I (423) i2s-test: data: 0x68 0x6C 0x68 0x63
I (423) i2s-test: data: 0x6C 0x68 0x63 0x68
I (433) i2s-test: data: 0x68 0x63 0x68 0x6A
I (433) i2s-test: data: 0x63 0x68 0x6A 0x68
I (443) i2s-test: data: 0x68 0x6A 0x68 0x70
I (443) i2s-test: data: 0x6A 0x68 0x70 0x68
I (453) i2s-test: data: 0x68 0x70 0x68 0x63
I (453) i2s-test: data: 0x70 0x68 0x63 0x68
I (463) i2s-test: data: 0x68 0x63 0x68 0x67
I (463) i2s-test: data: 0x63 0x68 0x67 0x68
I (473) i2s-test: data: 0x68 0x67 0x68 0x70
I (1373) i2s-test: stopping I2S read task
I (1373) i2s-test: disabling I2S ADC
I (2373) i2s-test: enabling I2S ADC

It just waits here until I do ctrl+]. Hours, if I leave it.

Other items if possible

@Alvin1Zhang Alvin1Zhang changed the title Second call to i2s_adc_enable() always blocks [TW#28456] Second call to i2s_adc_enable() always blocks Jan 21, 2019
@projectgus projectgus changed the title [TW#28456] Second call to i2s_adc_enable() always blocks Second call to i2s_adc_enable() always blocks (IDFGH-572) Mar 12, 2019
@vgonet
Copy link

vgonet commented Apr 9, 2019

Hello,

I've almost the same issue. I try to continuously acquire ADC data through the I2S but as soon as I activate the ADC (with i2s_adc_enable), the execution of my firmware is blocked (without errors or warnings in the monitor). My goal is to activate the ADC only once at initialization. I don't understand why I can't activate the ADC at startup and then let the DMA do its job on its side.

Thank you for your help.

@koobest
Copy link
Contributor

koobest commented Apr 12, 2019

Hi, @detly
I think some part of your code should be redesigned . In i2s_read_task, you have called i2s_read, before this function is finished, you should not disable the adc.

static void i2s_read_task(void * arg)
{
    main_data_t * data = arg;
    bool printed = false;
    ESP_LOGI(TAG, "-----------starting I2S read task");

    while (true)
    {
        size_t data_remaining = BUFFER_SIZE_BYTES;

        while (data_remaining > 0)
        {
            size_t data_recd = 0;
            printf("read\n");
            ESP_ERROR_CHECK(
                i2s_read(
                    I2S_NUM_0,
                    data->buffer, data_remaining,
                    &data_recd,
                    portMAX_DELAY)
            );
            printf("read done\n");
            data_remaining -= data_recd;
        }

        if (!printed) {
            show_some_data(data->buffer);
            printed = true;
        }
    }
}

@koobest
Copy link
Contributor

koobest commented Apr 12, 2019

Hi, @vgonet

Can you provide more info? I think you and detly have encountered the same issue.
before i2s_read is finished, disabling adc may case i2s_read blocking all the time.

thanks !!

@detly
Copy link
Author

detly commented May 1, 2019

@koobest I don't understand. I don't disable the ADC before subsequent calls to i2s_read(). I don't see what functional difference your code has from mine apart from some extra logging. Can you clarify what you mean?

@detly
Copy link
Author

detly commented May 2, 2019

@koobest Oh wait, I think I see what you're saying. Your logging statements show:

read done
read
I (1323) i2s-test: stopping I2S read task
I (1323) i2s-test: disabling I2S ADC
I (2323) i2s-test: enabling I2S ADC

So it seems the ADC is disabled before the read is complete. Perhaps the call to vTaskDelete() returns before the i2s_read_task() has stopped running. If that's the case, I don't think I can rely on the FreeRTOS API to deal with this kind of interaction.

@detly
Copy link
Author

detly commented May 2, 2019

Or maybe it's that it stops i2s_read_task() in the middle of a read, before the lock is released.

@detly
Copy link
Author

detly commented May 2, 2019

Alright, thanks to @koobest I figured it out. Of course FreeRTOS has all the tools needed to do this ie. a mutex around the i2s_read() call and a notification to the reading task to stop. I pushed a new version of my demo code to the Gitlab repo linked above which does this and works fine; here's just the read task:

static void i2s_read_task(void * arg)
{
    main_data_t * data = arg;
    bool printed = false;
    bool keep_going = true;

    do
    {
        size_t data_remaining = BUFFER_SIZE_BYTES;

        keep_going = (xTaskNotifyWait(0, 0, NULL, 0) == pdFAIL);

        (void) xSemaphoreTake(data->i2s_read_mutex, portMAX_DELAY);

        while (data_remaining > 0)
        {
            size_t data_recd = 0;

            ESP_ERROR_CHECK(
                i2s_read(
                    I2S_NUM_0,
                    data->buffer, data_remaining,
                    &data_recd,
                    portMAX_DELAY)
            );

            data_remaining -= data_recd;
        }

        xSemaphoreGive(data->i2s_read_mutex);

        if (!printed) {
            show_some_data(data->buffer);
            printed = true;
        }

    } while(keep_going);

    vTaskSuspend(NULL);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants