How to schedule task to run at 2000hz?

Greg Corson
Posts: 35
Joined: Sun May 20, 2018 9:16 pm

How to schedule task to run at 2000hz?

Postby Greg Corson » Thu Apr 25, 2019 10:33 am

I need to read an accelerometer at 2000hz with a small task that runs every 500us, reads the data from spi and saves it to a buffer. The read takes less than 50us. What is a good way to schedule this task? Seems like FreeRTOS is limited to 1ms schedules.

The code needs to read the accelerometer at 2000hz in one task while another task sends the buffered data out tcp.

jcsbanks
Posts: 305
Joined: Tue Mar 28, 2017 8:03 pm

Re: How to schedule task to run at 2000hz?

Postby jcsbanks » Thu Apr 25, 2019 10:51 am

High resolution timer with callback. I use them in 100us increments with CAN bus to add interframe delays to avoid overloading the counterparty device.

Works well, but I do sometimes get interrupt watchdog timeouts if I delete them in a critical section to avoid a race.

plajjd
Posts: 54
Joined: Thu Jul 05, 2018 11:47 pm

Re: How to schedule task to run at 2000hz?

Postby plajjd » Wed May 15, 2019 6:38 pm

We have a similar requirement for running a Matlab Simulink model at 2 Khz. We implemented it as a task, but had the task wait on a semaphore. The semaphore is then "given" in a 2 kHz hardware timer interrupt.

The idea is that every 500 us when the ISR occurs, it bypasses the RTOS and jumps to the task waiting on the semaphore.

For example:

We created a 2 kHz hardware timer:

Code: Select all

    // Create Timer Group timer to periodically execute Simulink task
    // Note that this is configured to Auto-Reload each time the timer alarm interrupt fires
    simulink_tg0_timer_init(TIMER_1, (timer_idx_t)1, 0.0005);

Code: Select all

static void simulink_tg0_timer_init(timer_idx_t timer_idx, bool auto_reload, double timer_interval_sec)
{
    /* Select and initialize basic parameters of the timer */
    timer_config_t config;
    config.divider = TIMER_DIVIDER;
    config.counter_dir = TIMER_COUNT_UP;
    config.counter_en = TIMER_PAUSE;
    config.alarm_en = TIMER_ALARM_EN;
    config.intr_type = TIMER_INTR_LEVEL;
    config.auto_reload = auto_reload;
    timer_init(TIMER_GROUP_0, timer_idx, &config);

    /* Timer's counter will initially start from value below.
     * Also, if auto_reload is set, this value will be automatically reload on alarm */
    timer_set_counter_value(TIMER_GROUP_0, timer_idx, 0x00000000ULL);

    /* Configure the alarm value and the interrupt on alarm. */
    timer_set_alarm_value(TIMER_GROUP_0, timer_idx, timer_interval_sec * TIMER_SCALE);
    timer_enable_intr(TIMER_GROUP_0, timer_idx);
    timer_isr_register(TIMER_GROUP_0, timer_idx, simulink_timer_group0_isr, (void *) timer_idx, ESP_INTR_FLAG_IRAM, NULL);

    timer_start(TIMER_GROUP_0, timer_idx);
}
When then had the ISR release the a semaphore to the task:

Code: Select all

void IRAM_ATTR simulink_timer_group0_isr(void *para)
{
    int need_yield;

    int timer_idx = (int) para;

    /* Retrieve the interrupt status and the counter value from the timer that reported the interrupt */
    uint32_t intr_status = TIMERG0.int_st_timers.val;
    TIMERG0.hw_timer[timer_idx].update = 1;

    /* Clear the interrupt (either Timer 0 or 1, whichever caused this interrupt */
    if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) {
        TIMERG0.int_clr_timers.t0 = 1;
    } else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) {
        TIMERG0.int_clr_timers.t1 = 1;
    }

    /* After the alarm has been triggered we need enable it again, so it is triggered the next time */
    TIMERG0.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN;

    // Give a semaphore to the Simulink task, so that it wakes up and executes 1 loop
    if (xSemaphoreGiveFromISR(s_timer_semaphore, &need_yield) != pdPASS)
    {
        ESP_EARLY_LOGD("example", "timer queue overflow");

    	return;
    }

    if (need_yield == pdTRUE)
    {
        portYIELD_FROM_ISR();
    }

}
And finally, the task waits on the sempaphore from the ISR:

Code: Select all

    while(1)
    {
        xSemaphoreTake(s_timer_semaphore, portMAX_DELAY);
        ...
        ...
      

Who is online

Users browsing this forum: No registered users and 59 guests