ULP WAKE function and Main CPU interruptions

Palonso
Posts: 58
Joined: Tue Sep 24, 2019 8:43 pm

ULP WAKE function and Main CPU interruptions

Postby Palonso » Tue Sep 15, 2020 9:32 pm

Hi,

I'm looking forward to use the RTC_CNTL_ULP_CP_INT_ENA register bit in order to notify the main CPU (interruption) that "a reading" with the ULP was successfully made.

As far as I understood is that the mentioned flag goes HIGH when the WAKE function is called in the ULP core, in the pulse counter example that worked perfectly when the module is asleep but when I tried to configure it to work while active I wasn't able to even "get" the interruption.

Since the WAKE command worked fine asleep I guess the problem is in the main program, here it is:

Code: Select all

#include <stdio.h>
#include "esp_sleep.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc_periph.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "esp32/ulp.h"
#include "ulp_main.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rtc_cntl.h"

// GPIO used for pulse counting.
#define IN0 GPIO_NUM_36
#define IN1 GPIO_NUM_39

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

static void init_ulp_program(void);
static void update_pulse_count(void);

static void ulp_isr_handler();
static void ulp_isr_install();

void app_main(void)
{
    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    if (cause == ESP_SLEEP_WAKEUP_TIMER)
    {
        printf("TIMER wakeup, saving pulse count\n");
        update_pulse_count();
    } 
    else if (cause == ESP_SLEEP_WAKEUP_ULP) 
    {
        printf("ULP wakeup, saving pulse count\n");
        update_pulse_count();
    } 
    else 
    {
        printf("Not ULP wakeup, initializing ULP\n");
        init_ulp_program();
    }

    ulp_isr_install();

    //for (int i = 0; i<5; i++)
    while(1)
    {
        update_pulse_count();
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }

    printf("Entering deep sleep\n\n");
    //ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );
    esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ULP);
    esp_sleep_enable_timer_wakeup(3000000);
    esp_deep_sleep_start();
}

static void init_ulp_program(void)
{

    printf("Loadin ULP program\n");
    esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,
            (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
    ESP_ERROR_CHECK(err);

    printf("Setting ESP32 variables\n");

    /* Initialize some variables used by ULP program.
     * Each 'ulp_xyz' variable corresponds to 'xyz' variable in the ULP program.
     * These variables are declared in an auto generated header file,
     * 'ulp_main.h', name of this file is defined in component.mk as ULP_APP_NAME.
     * These variables are located in RTC_SLOW_MEM and can be accessed both by the
     * ULP and the main CPUs.
     *
     * Note that the ULP reads only the lower 16 bits of these variables.
     */

    printf("Setting ULP variables\n");
    uint16_t *ulp_variable;
    uint32_t timer = 140;

    uint16_t debounce_0 = 3;
    uint16_t debounce_1 = 100;

    ulp_variable = (&ulp_debounce_counter); //ulp_debounce_counter[0]
    *ulp_variable = 3;                      //ulp_debounce_counter[0] = 3
    ulp_variable+=2;                        //cada registro del ULP es de 16-Bits en espacios de 32-Bits, 
                                            //por eso se avanza dos espacios al "siguiente"
    *ulp_variable = 3;                      //ulp_debounce_counter[1] = 3

    ulp_variable = (&ulp_debounce_max_count);
    *ulp_variable = debounce_0;                      //(&ulp_debounce_max_count)[0] = 3;
    ulp_variable+=2;
    *ulp_variable = debounce_1;                      //(&ulp_debounce_max_count)[1] = 3;

    ulp_variable = (&ulp_next_edge);
    *ulp_variable = 0;                      //(&ulp_next_edge)[0] = 0;
    ulp_variable+=2;
    *ulp_variable = 0;                      //(&ulp_next_edge)[1] = 0;

    ulp_variable = (&ulp_pulse_cnt_pin);
    *ulp_variable = 2;                     //ulp_pulse_cnt_pin = {0:IN0, 1:IN1, [2-3]:Ambos}

    // Initialize selected GPIO as RTC IO, enable input.
    //Input-Only pin has no pull-up/down resistor
    rtc_gpio_init(IN0);
    rtc_gpio_set_direction(IN0, RTC_GPIO_MODE_INPUT_ONLY);
    rtc_gpio_hold_en(IN0);

    // Initialize selected GPIO as RTC IO, enable input.
    //Input-Only pin has no pull-up/down resistor
    rtc_gpio_init(IN1);
    rtc_gpio_set_direction(IN1, RTC_GPIO_MODE_INPUT_ONLY);
    rtc_gpio_hold_en(IN1);

    /* Disconnect GPIO12 and GPIO15 to remove current drain through
     * pullup/pulldown resistors.
     * GPIO12 may be pulled high to select flash voltage.
     */
    rtc_gpio_isolate(GPIO_NUM_12);
    rtc_gpio_isolate(GPIO_NUM_15);
    esp_deep_sleep_disable_rom_logging(); // suppress boot messages

    /* Set ULP wake up period to T = 140us.
     * Minimum pulse width has to be T * (ulp_debounce_counter + 1) = 28.14ms.
     */
    ulp_set_wakeup_period(0, timer);
    printf("minimum pulse width has to be: %d us\n", timer*(debounce_0+1) );

    ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );

    printf("debounce time is: %d ms\n", (int)(1.52*(debounce_0+1)) );


    // Start the program
    err = ulp_run(&ulp_entry - RTC_SLOW_MEM);
    ESP_ERROR_CHECK(err);
}

static void update_pulse_count(void)
{
    const char* namespace = "plusecnt";
    const char* count_key_0 = "count0";
    const char* count_key_1 = "count1";

    uint32_t pulse_count;
    uint32_t pulse_count_from_ulp;
    esp_err_t err;
    uint16_t *ulp_variable;

    ESP_ERROR_CHECK( nvs_flash_init() );
    nvs_handle_t handle;
    ESP_ERROR_CHECK( nvs_open(namespace, NVS_READWRITE, &handle));

    // Se guarda los pulsos del primer pin-----------------------------------------------
    pulse_count = 0;
    err = nvs_get_u32(handle, count_key_0, &pulse_count);
    assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
    //printf("Read pulse count from NVS: %5d\n", pulse_count);

    /* ULP program counts signal edges, convert that to the number of pulses */
    ulp_variable = (&ulp_edge_count);
    pulse_count_from_ulp = (*ulp_variable & UINT16_MAX) / 2;
    /* In case of an odd number of edges, keep one until next time */
    *ulp_variable = *ulp_variable % 2;
    printf("Pulse count from IN0: %5d\n", pulse_count_from_ulp);

    /* Save the new pulse count to NVS */
    pulse_count += pulse_count_from_ulp;
    ESP_ERROR_CHECK(nvs_set_u32(handle, count_key_0, pulse_count));
    ESP_ERROR_CHECK(nvs_commit(handle));
    printf("Wrote IN0 updated pulse count to NVS: %5d\n\n", pulse_count);

    // Se guardan los pulsos del segundo pin --------------------------------------------
    pulse_count = 0;
    err = nvs_get_u32(handle, count_key_1, &pulse_count);
    assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
    //printf("Read pulse count from NVS: %5d\n", pulse_count);

    /* ULP program counts signal edges, convert that to the number of pulses */
    ulp_variable = (&ulp_edge_count);
    ulp_variable+=2;
    pulse_count_from_ulp = (*ulp_variable & UINT16_MAX) / 2;
    /* In case of an odd number of edges, keep one until next time */
    *ulp_variable = *ulp_variable % 2;
    printf("Pulse count from IN1: %5d\n", pulse_count_from_ulp);

    /* Save the new pulse count to NVS */
    pulse_count += pulse_count_from_ulp;
    ESP_ERROR_CHECK(nvs_set_u32(handle, count_key_1, pulse_count));
    ESP_ERROR_CHECK(nvs_commit(handle));
    printf("Wrote updated pulse count to NVS: %5d\n\n", pulse_count);

    nvs_close(handle);
}

static void IRAM_ATTR ulp_isr_handler(void *args)
{
    //ets_printf("ULP interrupt: Active ---> Deepsleep\n");
    printf("ULP interrupt: Active ---> Deepsleep\n");

    // Add customer code here.
    update_pulse_count();

    ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );
    esp_deep_sleep_start();
}

static void ulp_isr_install()
{
    ESP_ERROR_CHECK( rtc_isr_register(&ulp_isr_handler, NULL, RTC_CNTL_ULP_CP_INT_ENA_M) );
    REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA_M);
}

The program is a slightly modified version of the single pin ULP pulse counter example, with the difference that it counts in two input pins, so most the code is the same as in the example.

What I added are a couple functions ulp_isr_handler and ulp_isr_install that should configure the interruption when the RTC_CNTL_ULP_CP_INT_ENA_M changes but when I try to run it, it does nothing.
I'm doubting the function: rtc_isr_register which I found in an old post of this forum but it's no longer available in the espressif docs (https://docs.espressif.com/projects/esp ... ea=default)

Does anybody know how to attach/create an interrupt with that register? or what's the new function to do that?

I also read the "interrupt allocation" page (https://docs.espressif.com/projects/esp ... alloc.html) with no success since there is no reference to a ULP interruption.

Best regards,
P

boarchuz
Posts: 287
Joined: Tue Aug 21, 2018 5:28 am

Re: ULP WAKE function and Main CPU interruptions

Postby boarchuz » Tue Sep 15, 2020 9:47 pm

You're using the wrong mask here:

Code: Select all

ESP_ERROR_CHECK( rtc_isr_register(&ulp_isr_handler, NULL, RTC_CNTL_ULP_CP_INT_ENA_M) );
You want RTC_CNTL_SAR_INT_ST_M
See:
https://gist.github.com/igrr/4b002047fc ... main-c-L31

Palonso
Posts: 58
Joined: Tue Sep 24, 2019 8:43 pm

Re: ULP WAKE function and Main CPU interruptions

Postby Palonso » Wed Sep 16, 2020 9:07 pm

Thanks for noticing that mistake and sharing that example, it was a BIG help. Also I was able to change it to TaskNotification to make ti faster too.

Best regards,
P

Who is online

Users browsing this forum: Google [Bot], k.ifantidis, Majestic-12 [Bot] and 39 guests