Direct access to timer count in the new gptimer.h lib

dizcza
Posts: 55
Joined: Tue Sep 07, 2021 6:59 pm

Direct access to timer count in the new gptimer.h lib

Postby dizcza » Sat Feb 05, 2022 8:10 am

Hi, I'm porting some two-years code https://github.com/liebman/esp32-gps-nt ... .h#L45-L46 from a recently outdated timer.h to gptimer.h lib. With the latest ESP-IDF it looks like

Code: Select all

    #include "driver/timer.h"

    uint32_t inline IRAM_ATTR getValue()
    {
        TIMERG0.hw_timer[TIMER_0].update.val = 1;
        return TIMERG0.hw_timer[TIMER_0].lo.val;
    }
and the "getValue()" function is called not from ISR. The reason why the author is using direct access to the timer count is that in GPS-RTC synchronization every microsecond matters.

But I see a bunch of annoying warnings that the timer.h is outdated. Is it possible to access the timer count directly within the new gptimer.h lib? How?

xien551
Posts: 69
Joined: Wed Feb 02, 2022 4:04 am

Re: Direct access to timer count in the new gptimer.h lib

Postby xien551 » Sat Feb 05, 2022 5:28 pm

Hey dizcza, you can refer the .rst file in esp-idf\docs\en\api-reference\peripherals\gptimer.rst
gptimer_get_raw_count() may be your choice.

GPS timer synchronization is a important function for IOT application. If you have progress, please tell me, Thanks!

dizcza
Posts: 55
Joined: Tue Sep 07, 2021 6:59 pm

Re: Direct access to timer count in the new gptimer.h lib

Postby dizcza » Sun Feb 06, 2022 11:54 am

Stripping down the complexity of the "gptimer_get_raw_count()" function,

Code: Select all

__attribute__((always_inline))
static inline uint64_t timer_ll_get_counter_value(timg_dev_t *hw, uint32_t timer_num)
{
    hw->hw_timer[timer_num].update.tx_update = 1;
    // Timer register is in a different clock domain from Timer hardware logic
    // We need to wait for the update to take effect before fetching the count value
    while (hw->hw_timer[timer_num].update.tx_update) {
    }
    return ((uint64_t) hw->hw_timer[timer_num].hi.tx_hi << 32) | (hw->hw_timer[timer_num].lo.tx_lo);
}

esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, unsigned long long *value)
{
    ESP_RETURN_ON_FALSE(timer && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");

    portENTER_CRITICAL_SAFE(&timer->spinlock);
    *value = timer_ll_get_counter_value(timer->hal.dev, timer->timer_id);
    portEXIT_CRITICAL_SAFE(&timer->spinlock);
    return ESP_OK;
}
the direct access to timer count in gptimer, I guess, would be

Code: Select all

    uint32_t inline IRAM_ATTR getValue(gptimer_handle_t timer)
    {
        timer->hal.dev->hw_timer[timer->timer_id].update.tx_update = 1;
        while (timer->hal.dev->hw_timer[timer->timer_id].update.tx_update) {
        }
        return timer->hal.dev->hw_timer[timer->timer_id].lo.tx_lo;
    }
I'm wondering how safe it is not to enter and exit critical sections with "portENTER_CRITICAL_SAFE" and "portEXIT_CRITICAL_SAFE" respectively. The "getValue()" is placed in IRAM for speedup though it's called not from ISR.

xien551
Posts: 69
Joined: Wed Feb 02, 2022 4:04 am

Re: Direct access to timer count in the new gptimer.h lib

Postby xien551 » Mon Feb 07, 2022 6:57 am

Hey, Hey dizcza, Thanks for your progress.

I think you are right. the two function do the same work. The difference is whether to enter the critical safe state, make the timer into the spinlock.

One thing should be care about, when you set update.tx_update to 1. it will not change to 1 right now, but after a few cycles, that's poll. If in this few cycles, the timer memory has been accessed by other task and controlled, I guess the program may be blocked, or collapse.

I guess spinkclock has locked the memory, and can not accessed by other task. If the memory has been locked, you have to wait until it is free. When you have many tasks needing to get the value of the same Timer, this issue may happen, specially matter for tiny time-tick,e.g 0.1 us and heavy task arragment.

I am not very certain to my guess, you may do a simple test. And if your program has a large timestamp 1ms. Maybe you do not need the safe stage.

dizcza
Posts: 55
Joined: Tue Sep 07, 2021 6:59 pm

Re: Direct access to timer count in the new gptimer.h lib

Postby dizcza » Tue Feb 08, 2022 5:11 pm

I'm frustrated.

I want my code to be portable, use the new gptimer.h and be at least as fast as old timer.h does. It looks impossible.
  • How the users are supposed to get the timer count of a gptimer handle inside an interrupt function that is NOT a gptimer callback? Old good timer.h provides the "timer_group_get_counter_value_in_isr()" function for this.
  • Why is gptimer_handle_t declared in gptimer.h but defined in gptimer.c? What's the point of hiding it from the users? This prohibits the use of "timer_ll_get_counter_value(timer->hal.dev, timer->timer_id);" which I find very neat (it'd solve my problem) but I cannot use this because struct gptimer_t is not defined.
Two options - CONFIG_GPTIMER_ISR_IRAM_SAFE and CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM - have been introduced recently in the menuconfig. I'm not sure if they are meant to mitigate the problem I'm describing but it's not a solution. Eventually, I need to port the code to run on Arduino (without any further hacks).

Conclusions.

I'd gladly use gptimer.h but the limitations it brings force me to stick to timer.h.

I can open a feature request if needed.

xien551
Posts: 69
Joined: Wed Feb 02, 2022 4:04 am

Re: Direct access to timer count in the new gptimer.h lib

Postby xien551 » Thu Feb 10, 2022 12:42 am

Hey, thanks for your progress,

struct gptimer_t {
gptimer_group_t *group;
int timer_id;
unsigned int resolution_hz;
unsigned long long reload_count;
unsigned long long alarm_count;
gptimer_count_direction_t direction;
timer_hal_context_t hal;
gptimer_lifecycle_fsm_t fsm; // access to fsm should be protect by spinlock, as fsm is also accessed from ISR handler
intr_handle_t intr;
portMUX_TYPE spinlock; // to protect per-timer resources concurent accessed by task and ISR handler
gptimer_alarm_cb_t on_alarm;
void *user_ctx;
esp_pm_lock_handle_t pm_lock; // power management lock
#if CONFIG_PM_ENABLE
char pm_lock_name[GPTIMER_PM_LOCK_NAME_LEN_MAX]; // pm lock name
#endif
struct {
uint32_t intr_shared: 1;
uint32_t auto_reload_on_alarm: 1;
uint32_t alarm_en: 1;
} flags;
};
This define is just in the gptimer.c

Be safe or Be fast, this may be a dilemma. And I choose fast.

By the way, do you interested in IEEE 1588, the Precison clock synchronization protocol, I would open a new post about in the forum.

dizcza
Posts: 55
Joined: Tue Sep 07, 2021 6:59 pm

Re: Direct access to timer count in the new gptimer.h lib

Postby dizcza » Thu Feb 10, 2022 8:14 am

xien551 wrote:
Thu Feb 10, 2022 12:42 am
struct gptimer_t {
};
This define is just in the gptimer.c
Yeah, I know this. I had a look at this struct. Copying the struct definition in the source file won't help it either. It's just better, safe, and faster to stick to old timer.h lib with the function I've written in the first post.

I won't be using wifi much so perhaps the IEEE 1588 protocol is not for me at the moment. But I'm interested in how you would use it in ESP32. If you open a thread on this topic, please let me know.

ESP_morris
Posts: 290
Joined: Wed Sep 05, 2018 6:23 am

Re: Direct access to timer count in the new gptimer.h lib

Postby ESP_morris » Thu Feb 10, 2022 9:54 am

haha, I see your use case now. I think you'd better to try the MCPWM capture feature, it will capture a timestamp by hardware.

xien551
Posts: 69
Joined: Wed Feb 02, 2022 4:04 am

Re: Direct access to timer count in the new gptimer.h lib

Postby xien551 » Fri Feb 11, 2022 3:23 am

Hello, Morris, thanks for giving me a inspiration. Now I have a plan.

Dizcza, I will prompt this in my mew post. The matter is not that simple, to get the rigorous microsecond timestamp is not unreachable. Even more precision timer may be realized.

dizcza
Posts: 55
Joined: Tue Sep 07, 2021 6:59 pm

Re: Direct access to timer count in the new gptimer.h lib

Postby dizcza » Fri Feb 11, 2022 12:33 pm

ESP_morris wrote:
Thu Feb 10, 2022 9:54 am
haha, I see your use case now. I think you'd better to try the MCPWM capture feature, it will capture a timestamp by hardware.
Sharp but incisive. Thank you.

True, MCPWM is a better solution than CPU-driven timer polling. Probably, the best. I've not considered them and somehow these modules were not mentioned in other forums on GPS-RTC sync until I explicitly googled them.

I have an issue though driving an MCPWM with a low-frequency signal (1 sec). I've borrowed code from a sonar example and here is the minimal code:

Code: Select all

#define GPS_PPS_PIN    GPIO_NUM_26

static xQueueHandle cap_queue;
static uint32_t pulse_count = 0;


static bool gps_pps_isr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata,
                                  void *arg) {
    //calculate the interval in the ISR,
    //so that the interval will be always correct even when cap_queue is not handled in time and overflow.
    BaseType_t high_task_wakeup = pdFALSE;
    xQueueSendFromISR(cap_queue, &pulse_count, &high_task_wakeup);
    pulse_count++;
    return high_task_wakeup == pdTRUE;
}

void app_main(void) {
    // the queue where we read data
    cap_queue = xQueueCreate(10, sizeof(uint32_t));

    ESP_ERROR_CHECK(mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, GPS_PPS_PIN));
    ESP_ERROR_CHECK(gpio_pulldown_en(GPS_PPS_PIN));
    mcpwm_capture_config_t conf = {
        .cap_edge = MCPWM_BOTH_EDGE,
        .cap_prescale = 1,
        .capture_cb = gps_pps_isr_handler,
        .user_data = NULL
    };
    ESP_ERROR_CHECK(mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf));

    // forever loop
    while (true) {
        uint32_t pulse_count;
        xQueueReceive(cap_queue, &pulse_count, portMAX_DELAY);
        ESP_LOGI(TAG, "Pulse count %u", pulse_count);
    }
}
The gps_pps_isr_handler function is never called. Perhaps, this is somehow related to MCPWM frequency. There are mcpwm_init, mcpwm_group_set_resolution, and mcpwm_timer_set_resolution functions but I don't understand how should I initialize an MCPWM module in order to track a 1-second signal with at least 1-us resolution.

Will appreciate if someone explains it to me.

Thanks.

----

Update.

Never mind, I solved the issue.

Who is online

Users browsing this forum: Majestic-12 [Bot] and 109 guests