Can I perform various operations in the callback function of gptimer?

natee.th
Posts: 26
Joined: Fri Feb 10, 2023 5:25 pm

Can I perform various operations in the callback function of gptimer?

Postby natee.th » Wed Mar 15, 2023 2:07 pm

I've used other microcontrollers and not freeRTOS. All I ever did was use a Timer to call a function I created. according to the time I specify, without going through the while loop.

But in the example of esp-idf, let me use xQueueReceive check in while loop, I think it's not interesting at all. Can I use the code my way?

If you need to use xQueueReceive in a while loop, I saw examples on the website https://embeddedexplorer.com/esp32-timer-tutorial/, use xSemaphoreTake instead, which one is more suitable?


Code: Select all

/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define LED_PIN GPIO_NUM_9

static const char *TAG = "example";

static int led_state = 0;

typedef struct
{
    uint64_t event_count;
} example_queue_element_t;

static bool IRAM_ATTR example_timer_on_alarm_cb_v2(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
    BaseType_t high_task_awoken = pdFALSE;
    QueueHandle_t queue = (QueueHandle_t)user_data;
    // Retrieve count value and send to queue
    example_queue_element_t ele = {
        .event_count = edata->count_value};

    if (led_state == 0)  // if not suitable I'll move it into the while loop.
    {
        led_state = 1;
        gpio_set_level(LED_PIN, 1);
    }
    else
    {
        led_state = 0;
        gpio_set_level(LED_PIN, 0);
    }

    xQueueSendFromISR(queue, &ele, &high_task_awoken);
    // return whether we need to yield at the end of ISR

    return (high_task_awoken == pdTRUE);
}

void app_main(void)
{

    gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
    gpio_set_level(LED_PIN, 0);
    example_queue_element_t ele;

    QueueHandle_t queue = xQueueCreate(10, sizeof(example_queue_element_t));

    if (!queue)
    {
        ESP_LOGE(TAG, "Creating queue failed");
        return;
    }

    ESP_LOGI(TAG, "Create timer handle");
    gptimer_handle_t gptimer = NULL;
    gptimer_config_t timer_config = {
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        .direction = GPTIMER_COUNT_UP,
        .resolution_hz = 1000000, // 1MHz, 1 tick=1us
    };
    ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));

    // set a new callback function

    gptimer_event_callbacks_t cbs = {
        .on_alarm = example_timer_on_alarm_cb_v2,
    };

    // cbs.on_alarm = example_timer_on_alarm_cb_v2;
    ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));
    ESP_ERROR_CHECK(gptimer_enable(gptimer));

    ESP_LOGI(TAG, "Start timer, auto-reload at alarm event");
    gptimer_alarm_config_t alarm_config2 = {
        .reload_count = 0,
        .alarm_count = 1000, // period = 0.001s
        .flags.auto_reload_on_alarm = true,
    };
    ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config2));
    ESP_ERROR_CHECK(gptimer_start(gptimer));

    printf("Timer Loop  \n");

    while (1)
    {

        if (xQueueReceive(queue, &ele, pdMS_TO_TICKS(1000)))
        {
        }
    }

    ESP_LOGI(TAG, "Stop timer");
    ESP_ERROR_CHECK(gptimer_stop(gptimer));
}

natee.th
Posts: 26
Joined: Fri Feb 10, 2023 5:25 pm

Re: Can I perform various operations in the callback function of gptimer?

Postby natee.th » Wed Mar 15, 2023 2:43 pm

Or use the High Resolution Timer (ESP Timer) for best.

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Can I perform various operations in the callback function of gptimer?

Postby MicroController » Thu Mar 16, 2023 6:23 pm

The gp timer's callbacks are directly called by the timers' ISR. So whatever code you put into the callback handler will run as part of the ISR and in an ISR context. Follow the universal recommendation: Keep the callback handler as short as possible, defer more complex work to a task instead. Tasks are much more powerful in what they can do, like waiting for an event or some time.
Start embracing and leveraging the concept of tasks; the benefits will start to materialize as soon as your application gets a bit more complex.

As to the difference between a queue and a semaphore:
A semaphore is a pretty bare-bones synchronization object. It basically only provides two operations, "notify" and "wait for notification".

A queue provides similar operations, i.e. "put message into queue" and "wait for message and take it from the queue". In addition to semaphores' functionality, a queue can transfer "messages", i.e. some kind of user data. A message can be pretty much anything, a single byte value, a pointer, an array, a struct,... You decide what you need.

So, if you only need to know when an event happens (and want to be able to "wait" for the event instead of polling), then a semaphore is sufficient and should probably be used.
If you want to convey some data with each event, a queue is probably a better option.

Who is online

Users browsing this forum: No registered users and 88 guests