mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
gptimer: added a section to describe the thread safety
This commit is contained in:
parent
608a1dc237
commit
a298e44b33
@ -70,7 +70,6 @@ struct gptimer_t {
|
||||
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;
|
||||
_lock_t mutex; // to protect other resource allocation, like interrupt handle
|
||||
portMUX_TYPE spinlock; // to protect per-timer resources concurent accessed by task and ISR handler
|
||||
gptimer_alarm_cb_t on_alarm;
|
||||
void *user_ctx;
|
||||
@ -91,7 +90,6 @@ static gptimer_platform_t s_platform;
|
||||
static gptimer_group_t *gptimer_acquire_group_handle(int group_id);
|
||||
static void gptimer_release_group_handle(gptimer_group_t *group);
|
||||
static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_source_t src_clk, uint32_t resolution_hz);
|
||||
static esp_err_t gptimer_install_interrupt(gptimer_t *timer);
|
||||
IRAM_ATTR static void gptimer_default_isr(void *args);
|
||||
|
||||
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer)
|
||||
@ -109,7 +107,7 @@ esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *re
|
||||
|
||||
for (int i = 0; (i < SOC_TIMER_GROUPS) && (timer_id < 0); i++) {
|
||||
group = gptimer_acquire_group_handle(i);
|
||||
ESP_GOTO_ON_FALSE(group, ESP_ERR_NO_MEM, err, TAG, "no mem for group (%d)", group_id);
|
||||
ESP_GOTO_ON_FALSE(group, ESP_ERR_NO_MEM, err, TAG, "no mem for group (%d)", i);
|
||||
// loop to search free timer in the group
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int j = 0; j < SOC_TIMER_GROUP_TIMERS_PER_GROUP; j++) {
|
||||
@ -153,7 +151,6 @@ esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *re
|
||||
timer->fsm = GPTIMER_FSM_STOP;
|
||||
timer->direction = config->direction;
|
||||
timer->flags.intr_shared = config->flags.intr_shared;
|
||||
_lock_init(&timer->mutex);
|
||||
ESP_LOGD(TAG, "new gptimer (%d,%d) at %p, resolution=%uHz", group_id, timer_id, timer, timer->resolution_hz);
|
||||
*ret_timer = timer;
|
||||
return ESP_OK;
|
||||
@ -194,7 +191,6 @@ esp_err_t gptimer_del_timer(gptimer_handle_t timer)
|
||||
esp_pm_lock_delete(timer->pm_lock);
|
||||
ESP_LOGD(TAG, "uninstall APB_FREQ_MAX lock for timer (%d,%d)", group_id, timer_id);
|
||||
}
|
||||
_lock_close(&timer->mutex);
|
||||
free(timer);
|
||||
ESP_LOGD(TAG, "del timer (%d,%d)", group_id, timer_id);
|
||||
|
||||
@ -232,6 +228,8 @@ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer
|
||||
gptimer_group_t *group = NULL;
|
||||
ESP_RETURN_ON_FALSE(timer && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
group = timer->group;
|
||||
int group_id = group->group_id;
|
||||
int timer_id = timer->timer_id;
|
||||
|
||||
#if CONFIG_GPTIMER_ISR_IRAM_SAFE
|
||||
if (cbs->on_alarm) {
|
||||
@ -245,11 +243,17 @@ esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer
|
||||
#endif
|
||||
|
||||
// lazy install interrupt service
|
||||
ESP_RETURN_ON_ERROR(gptimer_install_interrupt(timer), TAG, "install interrupt service failed");
|
||||
if (!timer->intr) {
|
||||
// if user wants to control the interrupt allocation more precisely, we can expose more flags in `gptimer_config_t`
|
||||
int isr_flags = timer->flags.intr_shared ? ESP_INTR_FLAG_SHARED | GPTIMER_INTR_ALLOC_FLAGS : GPTIMER_INTR_ALLOC_FLAGS;
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(timer_group_periph_signals.groups[group_id].timer_irq_id[timer_id], isr_flags,
|
||||
(uint32_t)timer_ll_get_intr_status_reg(timer->hal.dev), TIMER_LL_EVENT_ALARM(timer_id),
|
||||
gptimer_default_isr, timer, &timer->intr), TAG, "install interrupt service failed");
|
||||
}
|
||||
|
||||
// enable/disable GPTimer interrupt events
|
||||
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id), cbs->on_alarm); // enable timer interrupt
|
||||
timer_ll_enable_intr(timer->hal.dev, TIMER_LL_EVENT_ALARM(timer->timer_id), cbs->on_alarm != NULL); // enable timer interrupt
|
||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||
|
||||
timer->on_alarm = cbs->on_alarm;
|
||||
@ -353,8 +357,10 @@ static gptimer_group_t *gptimer_acquire_group_handle(int group_id)
|
||||
} else {
|
||||
group = s_platform.groups[group_id];
|
||||
}
|
||||
// someone acquired the group handle means we have a new object that refer to this group
|
||||
s_platform.group_ref_counts[group_id]++;
|
||||
if (group) {
|
||||
// someone acquired the group handle means we have a new object that refer to this group
|
||||
s_platform.group_ref_counts[group_id]++;
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (new_group) {
|
||||
@ -422,34 +428,6 @@ static esp_err_t gptimer_select_periph_clock(gptimer_t *timer, gptimer_clock_sou
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t gptimer_install_interrupt(gptimer_t *timer)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
gptimer_group_t *group = timer->group;
|
||||
int group_id = group->group_id;
|
||||
int timer_id = timer->timer_id;
|
||||
bool new_isr = false;
|
||||
|
||||
if (!timer->intr) {
|
||||
_lock_acquire(&timer->mutex);
|
||||
if (!timer->intr) {
|
||||
// if user wants to control the interrupt allocation more precisely, we can expose more flags in `gptimer_config_t`
|
||||
int extra_isr_flags = timer->flags.intr_shared ? ESP_INTR_FLAG_SHARED : 0;
|
||||
ret = esp_intr_alloc_intrstatus(timer_group_periph_signals.groups[group_id].timer_irq_id[timer_id], extra_isr_flags | GPTIMER_INTR_ALLOC_FLAGS,
|
||||
(uint32_t)timer_ll_get_intr_status_reg(timer->hal.dev), TIMER_LL_EVENT_ALARM(timer_id),
|
||||
gptimer_default_isr, timer, &timer->intr);
|
||||
new_isr = (ret == ESP_OK);
|
||||
}
|
||||
_lock_release(&timer->mutex);
|
||||
}
|
||||
|
||||
if (new_isr) {
|
||||
ESP_LOGD(TAG, "install interrupt service for timer (%d,%d)", group_id, timer_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Put the default ISR handler in the IRAM for better performance
|
||||
IRAM_ATTR static void gptimer_default_isr(void *args)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ extern "C" {
|
||||
typedef struct gptimer_t *gptimer_handle_t;
|
||||
|
||||
/**
|
||||
* @brief GPTimer event data
|
||||
* @brief GPTimer alarm event data
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t count_value; /*!< Current count value */
|
||||
@ -33,7 +33,7 @@ typedef struct {
|
||||
*
|
||||
* @param[in] timer Timer handle created by `gptimer_new_timer()`
|
||||
* @param[in] edata Alarm event data, fed by driver
|
||||
* @param[in] user_ctx User data, passed from `gptimer_config_t`
|
||||
* @param[in] user_ctx User data, passed from `gptimer_register_event_callbacks()`
|
||||
* @return Whether a high priority task has been waken up by this function
|
||||
*/
|
||||
typedef bool (*gptimer_alarm_cb_t) (gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx);
|
||||
|
@ -23,11 +23,17 @@ The following sections of this document cover the typical steps to install and o
|
||||
|
||||
- `Set and Get count value <#set-and-get-count-value>`__ - covers how to force the timer counting from a start point and how to get the count value at anytime.
|
||||
|
||||
- `Start and Stop timer <#start-and-stop-timer>`__ - covers which parameters should be set up to start the timer with specific alarm event behavior.
|
||||
- `Set Up Alarm Action <#set-up-alarm-action>`__ - covers the parameters that should be set up to enable the alarm event.
|
||||
|
||||
- `Register Event Callbacks <#register-event-callbacks>`__ - covers how to hook user specific code to the alarm event callback function.
|
||||
|
||||
- `Start and Stop timer <#start-and-stop-timer>`__ - shows some typical use cases that start the timer with different alarm behavior.
|
||||
|
||||
- `Power Management <#power-management>`__ - describes how different source clock selections can affect power consumption.
|
||||
|
||||
- `IRAM safe <#iram-safe>`__ - describes tips on how to make the timer interrupt and IO control functions work better along with a disabled cache.
|
||||
- `IRAM Safe <#iram-safe>`__ - describes tips on how to make the timer interrupt and IO control functions work better along with a disabled cache.
|
||||
|
||||
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
@ -260,6 +266,12 @@ There's another Kconfig option :ref:`CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM` that can
|
||||
- :cpp:func:`gptimer_set_raw_count`
|
||||
- :cpp:func:`gptimer_set_alarm_action`
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :cpp:func:`gptimer_new_timer` is guaranteed to be thread safe by the driver, which means, user can call it from different RTOS tasks without protection by extra locks.
|
||||
Other functions that take the :cpp:type:`gptimer_handle_t` as the first positional parameter, are not thread safe. The lifecycle of the gptimer handle is maintained by the user. So user should avoid calling them concurrently. If it has to, then one should introduce another mutex to prevent the gptimer handle being accessed concurrently.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user