espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.24k stars 7.19k forks source link

ESP32-S2 wrong ADC Threshold (monitor mode) (IDFGH-6611) #8253

Open urbanze opened 2 years ago

urbanze commented 2 years ago

Hello, I trying to use ADC Threshold with ESP32-S2 (monitor mode) but without examples, this is a little boring. Monitor mode is not working correctly.

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/temp_sensor.h"
#include "driver/adc.h"

void isr_adc(void *z)
{
    uint32_t st1 = adc_digi_intr_get_status(ADC_UNIT_1);
    uint32_t st2 = REG_READ(0x3F440000+0x50);
    adc_digi_intr_clear(ADC_UNIT_1, ADC_DIGI_INTR_MASK_MONITOR);

    ESP_EARLY_LOGI(__func__, "ISR_ADC [%u], [%u]", st1, st2);
}

void app_main(void)
{

    adc1_config_width(ADC_WIDTH_BIT_13);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
    adc1_get_raw(ADC1_CHANNEL_0);

    adc_digi_start();

    adc_digi_monitor_t cfg;
    cfg.adc_unit = ADC_UNIT_1;
    cfg.channel = ADC_CHANNEL_MAX;
    cfg.mode = ADC_DIGI_MONITOR_LOW;
    cfg.threshold = 1000;

    ESP_ERROR_CHECK(adc_digi_monitor_set_config(ADC_DIGI_MONITOR_IDX0, &cfg));
    ESP_ERROR_CHECK(adc_digi_monitor_enable(ADC_DIGI_MONITOR_IDX0, true));

    ESP_ERROR_CHECK(adc_digi_isr_register(isr_adc, NULL, 0));
    ESP_ERROR_CHECK(adc_digi_intr_enable(ADC_UNIT_1, ADC_DIGI_INTR_MASK_MONITOR));

    while (1)
    {
        int val = 0;
        //val = adc1_get_raw(ADC1_CHANNEL_0);
        ESP_LOGI(__func__, "%d", val);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

ISR_ADC() is only trigged when "adc1_get_raw()" is executed, if I comment "adc1_get_raw()", ISR_ADC() is never called. Monitor mode should generate interrupt by itself, without having to polling with get_raw().

I tested with IDF 4.3 and 4.4.

SuGlider commented 2 years ago

adc_digi_isr_register() has been removed in IDF 4.4. It can be found only with IDF 4.2 and 4.3.

My guess is that it was removed beause it doesn't work.

SuGlider commented 2 years ago

Reading the S2 TRM, on chapter 32.2 (SAR ADC) there are some hints about how this should work:

Page 808: Threshold monitoring. An interrupt will be triggered when the value is greater or less than the threshold.

On page 814, chapter 32.2.5.4 (Threshold Monitoring) it talks about how to trigger an ADC interrupt.

On page 836 there is a reference to APB_SARADC_ADC2_THRES_MODE 1: ADC_DATA > = threshold, generate interrupt. 0: ADC_DATA < threshold, generate interrupt. (R/W) APB_SARADC_ADC1_THRES_MODE 1: ADC_DATA > = threshold, generate interrupt. 0: ADC_DATA < threshold, generate interrupt. (R/W)

Although your code sets up the Thresholds with adc_digi_monitor_set_config(), it seems that it doesn't work.

SuGlider commented 2 years ago

I think that this line is causing the issue: adc1_config_width(ADC_WIDTH_BIT_13); Upon TRM, on page 806, DIG ADC: 12-bit sampling resolution at most.

You may try to set it to 12-bit instead and verify if it works after that.

urbanze commented 2 years ago

I think that this line is causing the issue: adc1_config_width(ADC_WIDTH_BIT_13); Upon TRM, on page 806, DIG ADC: 12-bit sampling resolution at most.

You may try to set it to 12-bit instead and verify if it works after that.

but s2 is 13-bit adc! Monitor mode change this feature???

SuGlider commented 2 years ago

but s2 is 13-bit adc! Monitor mode change this feature???

Yes, but 13-bit is only for the RTC ADC controller. In TRM, page 811, in chapter 32.2.5 (DIG ADC Controllers), it is clear: "Up to 12-bit sampling resolution". Try changing this parameter and verify if this new setting makes it work.

urbanze commented 2 years ago

but s2 is 13-bit adc! Monitor mode change this feature???

Yes, but 13-bit is only for the RTC ADC controller. In TRM, page 811, in chapter 32.2.5 (DIG ADC Controllers), it is clear: "Up to 12-bit sampling resolution". Try changing this parameter and verify if this new setting makes it work.

./main/main.c:38:23: error: 'ADC_WIDTH_BIT_12' undeclared (first use in this function); did you mean 'ADC_WIDTH_BIT_13'? adc1_config_width(ADC_WIDTH_BIT_12); ^~~~~~~~~~~~~~~~ ADC_WIDTH_BIT_13 ../main/main.c:38:23: note: each undeclared identifier is reported only once for each function it appears in ninja: build stopped: subcommand failed. ninja failed with exit code 1

Not defined, only works in esp32?!

SuGlider commented 2 years ago

https://github.com/espressif/esp-idf/blob/release/v4.3/components/hal/include/hal/adc_types.h#L86-L98 it sounds like for ESP32S2 there is only one option for this type 13 bits. but for IDF 4.2, it shall be there. https://github.com/espressif/esp-idf/blob/release/v4.2/components/soc/include/hal/adc_types.h#L73-L82

But, looking into https://github.com/espressif/esp-idf/blob/release/v4.2/components/driver/adc_common.c#L307-L320 it also seems that adc1_config_width() works for the RTC ADC controller.

I'd remove this line from the code and test it.

SuGlider commented 2 years ago

I think there is a step missing, which is using adc_digi_controller_config(...) to configure the controller. Also adc_digi_init()

Look in https://docs.espressif.com/projects/esp-idf/en/release-v4.2/esp32s2/api-reference/peripherals/adc.html

urbanze commented 2 years ago

I think there is a step missing, which is using adc_digi_controller_config(...) to configure the controller. Also adc_digi_init()

Look in https://docs.espressif.com/projects/esp-idf/en/release-v4.2/esp32s2/api-reference/peripherals/adc.html

After configure digital domain to ADC, works but not 100%. With code bellow, configured to "ADC_DIGI_MONITOR_LOW", ISR is called only when I put 3v3 in GPIO, inverse of expected, is correct?

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/temp_sensor.h"
#include "driver/adc.h"

void isr_adc(void *z)
{
    uint32_t st1 = adc_digi_intr_get_status(ADC_UNIT_1);
    uint32_t st2 = REG_READ(0x3F440000+0x50);
    adc_digi_intr_clear(ADC_UNIT_1, ADC_DIGI_INTR_MASK_MONITOR);

    ESP_EARLY_LOGI(__func__, "ISR_ADC [%u], [%u]", st1, st2);
}

void app_main(void)
{
    adc_digi_pattern_table_t a1p;
    a1p.atten = 3;
    a1p.channel = ADC_CHANNEL_0;

    adc_digi_clk_t adc_clk;
    adc_clk.use_apll = 0;
    adc_clk.div_num = 100;
    adc_clk.div_b = 1;
    adc_clk.div_a = 0;

    adc_digi_config_t dcfg;
    dcfg.conv_limit_en = 0;
    dcfg.conv_limit_num = 255;
    dcfg.adc1_pattern_len = 1;
    dcfg.adc2_pattern_len = 0;
    dcfg.adc1_pattern = &a1p;
    dcfg.adc2_pattern = 0;
    dcfg.conv_mode = ADC_CONV_SINGLE_UNIT_1;
    dcfg.format = ADC_DIGI_FORMAT_12BIT;
    dcfg.interval = 400;
    dcfg.dig_clk = adc_clk;
    dcfg.dma_eof_num = 1000;

    ESP_ERROR_CHECK(adc_digi_init());
    ESP_ERROR_CHECK(adc_digi_controller_config(&dcfg));

    adc_digi_monitor_t mcfg;
    mcfg.adc_unit = ADC_UNIT_1;
    mcfg.channel = ADC_CHANNEL_MAX;
    mcfg.mode = ADC_DIGI_MONITOR_LOW;
    mcfg.threshold = 4096;

    ESP_ERROR_CHECK(adc_digi_monitor_set_config(ADC_DIGI_MONITOR_IDX0, &mcfg));
    ESP_ERROR_CHECK(adc_digi_monitor_enable(ADC_DIGI_MONITOR_IDX0, true));

    ESP_ERROR_CHECK(adc_digi_isr_register(isr_adc, NULL, 0));
    ESP_ERROR_CHECK(adc_digi_intr_enable(ADC_UNIT_1, ADC_DIGI_INTR_MASK_MONITOR));

    ESP_ERROR_CHECK(adc_digi_start());
    while (1)
    {
        int val = 0;
        //val = adc1_get_raw(ADC1_CHANNEL_0);
        ESP_LOGI(__func__, "%d", val);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}
SuGlider commented 2 years ago

Yes. Correct.

ADC has actually an interrupt for detecting PWR 3.3Vdd. It works with RTC ADC mode.

There are 2 modes:

I think that maybe the code is configuring both at the same time and maybe some bit turns on the RTC mode and disable the DIGI mode.

Anyway, it seems that this Interrupt functionality has been disabled for IDF 4.4+, thus be careful, because it may not be compatible with newer IDF versions.

urbanze commented 2 years ago

Yes. Correct.

ADC has actually an interrupt for detecting PWR 3.3Vdd. It works with RTC ADC mode.

There are 2 modes:

  • RTC that can't generate Interrupt on Thresholds and
  • DMA/DIGI mode that can do it.

I think that maybe the code is configuring both at the same time and maybe some bit turns on the RTC mode and disable the DIGI mode.

Anyway, it seems that this Interrupt functionality has been disabled for IDF 4.4+, thus be careful, because it may not be compatible with newer IDF versions.

Sorry, I expressed myself wrong.

I commented that it only works when I put 3v3, because Im only testing with GND and 3v3. What I mean is that the interrupt is called (apparently correct) but the configuration is inverted.

When configured to low_trigger, only occurs when put 3v3. When configure to high_trigger, only occurs when put GND.

urbanze commented 2 years ago

Any one can help me? Im writing an article and I need clarification on this inverted trigger