return ESP_OK;
}
-esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
+esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
{
if (!is_initialized() || timer_armed(timer)) {
return ESP_ERR_INVALID_STATE;
return timer_insert(timer);
}
-esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
+esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
{
if (!is_initialized() || timer_armed(timer)) {
return ESP_ERR_INVALID_STATE;
return timer_insert(timer);
}
-esp_err_t esp_timer_stop(esp_timer_handle_t timer)
+esp_err_t IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
{
if (!is_initialized() || !timer_armed(timer)) {
return ESP_ERR_INVALID_STATE;
return ESP_OK;
}
-static esp_err_t timer_insert(esp_timer_handle_t timer)
+static IRAM_ATTR esp_err_t timer_insert(esp_timer_handle_t timer)
{
timer_list_lock();
#if WITH_PROFILING
return ESP_OK;
}
-static esp_err_t timer_remove(esp_timer_handle_t timer)
+static IRAM_ATTR esp_err_t timer_remove(esp_timer_handle_t timer)
{
timer_list_lock();
LIST_REMOVE(timer, list_entry);
#if WITH_PROFILING
-static void timer_insert_inactive(esp_timer_handle_t timer)
+static IRAM_ATTR void timer_insert_inactive(esp_timer_handle_t timer)
{
/* May be locked or not, depending on where this is called from.
* Lock recursively.
timer_list_unlock();
}
-static void timer_remove_inactive(esp_timer_handle_t timer)
+static IRAM_ATTR void timer_remove_inactive(esp_timer_handle_t timer)
{
timer_list_lock();
LIST_REMOVE(timer, list_entry);
#endif // WITH_PROFILING
-static bool timer_armed(esp_timer_handle_t timer)
+static IRAM_ATTR bool timer_armed(esp_timer_handle_t timer)
{
return timer->alarm > 0;
}
-static void timer_list_lock()
+static IRAM_ATTR void timer_list_lock()
{
portENTER_CRITICAL(&s_timer_lock);
}
-static void timer_list_unlock()
+static IRAM_ATTR void timer_list_unlock()
{
portEXIT_CRITICAL(&s_timer_lock);
}
}
}
-static bool is_initialized()
+static IRAM_ATTR bool is_initialized()
{
return s_timer_task != NULL;
}
// FIXME: This value is safe for 80MHz APB frequency.
// Should be modified to depend on clock frequency.
-uint64_t esp_timer_impl_get_min_period_us()
+uint64_t IRAM_ATTR esp_timer_impl_get_min_period_us()
{
return 50;
}
#define TIMER_INITIALIZED_FIELD(p_ets_timer) ((p_ets_timer)->timer_expire)
#define TIMER_INITIALIZED_VAL 0x12121212
-static bool timer_initialized(ETSTimer *ptimer)
+static IRAM_ATTR bool timer_initialized(ETSTimer *ptimer)
{
return TIMER_INITIALIZED_FIELD(ptimer) == TIMER_INITIALIZED_VAL;
}
}
-void ets_timer_arm_us(ETSTimer *ptimer, uint32_t time_us, bool repeat_flag)
+void IRAM_ATTR ets_timer_arm_us(ETSTimer *ptimer, uint32_t time_us, bool repeat_flag)
{
assert(timer_initialized(ptimer));
esp_timer_stop(ESP_TIMER(ptimer)); // no error check
}
}
-void ets_timer_arm(ETSTimer *ptimer, uint32_t time_ms, bool repeat_flag)
+void IRAM_ATTR ets_timer_arm(ETSTimer *ptimer, uint32_t time_ms, bool repeat_flag)
{
uint64_t time_us = 1000LL * (uint64_t) time_ms;
assert(timer_initialized(ptimer));
}
}
-void ets_timer_disarm(ETSTimer *ptimer)
+void IRAM_ATTR ets_timer_disarm(ETSTimer *ptimer)
{
if (timer_initialized(ptimer)) {
esp_timer_stop(ESP_TIMER(ptimer));
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
+#include "esp_spi_flash.h"
TEST_CASE("ets_timer produces correct delay", "[ets_timer]")
{
#undef N
}
+
+/* WiFi/BT coexistence will sometimes arm/disarm
+ timers from an ISR where flash may be disabled. */
+IRAM_ATTR TEST_CASE("ETSTimers arm & disarm run from IRAM", "[ets_timer]")
+{
+ void timer_func(void* arg)
+ {
+ volatile bool *b = (volatile bool *)arg;
+ *b = true;
+ }
+
+ volatile bool flag = false;
+ ETSTimer timer1;
+ const int INTERVAL = 5;
+
+ ets_timer_setfn(&timer1, &timer_func, (void *)&flag);
+
+ /* arm a disabled timer, then disarm a live timer */
+
+ g_flash_guard_default_ops.start(); // Disables flash cache
+
+ ets_timer_arm(&timer1, INTERVAL, false);
+ // redundant call is deliberate (test code path if already armed)
+ ets_timer_arm(&timer1, INTERVAL, false);
+ ets_timer_disarm(&timer1);
+
+ g_flash_guard_default_ops.end(); // Re-enables flash cache
+
+ TEST_ASSERT_FALSE(flag); // didn't expire yet
+
+ /* do the same thing but wait for the timer to expire */
+
+ g_flash_guard_default_ops.start();
+ ets_timer_arm(&timer1, INTERVAL, false);
+ g_flash_guard_default_ops.end();
+
+ vTaskDelay(2 * INTERVAL / portTICK_PERIOD_MS);
+ TEST_ASSERT_TRUE(flag);
+
+ g_flash_guard_default_ops.start();
+ ets_timer_disarm(&timer1);
+ g_flash_guard_default_ops.end();
+}