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);
...
...