MycilaJSY 13.0.0
Arduino / ESP32 library for the JSY1031, JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229, JSY-MK-333 families single-phase and three-phase AC bidirectional meters from Shenzhen Jiansiyan Technologies Co, Ltd.
Loading...
Searching...
No Matches
inlined_gptimer.h
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright (C) 2023-2025 Mathieu Carbou
4 *
5 * This file is a collections of functions and structures from gptimer_priv.h, gptimer.c and timer_hal.c
6 *
7 * They have been updated to be marked as forced inline, in order to be used from ISR in IRAM.
8 * This is required to be able to use ISR in IRAM, while doing some flash operations.
9 */
10#pragma once
11
12#include <driver/gptimer.h>
13#include <esp_check.h>
14#include <esp_err.h>
15#include <esp_memory_utils.h>
16#include <esp_pm.h>
17#include <freertos/FreeRTOS.h>
18#include <hal/timer_hal.h>
19#include <hal/timer_ll.h>
20#include <rom/ets_sys.h>
21#include <sys/lock.h>
22
23#include <atomic>
24
26// FROM gptimer_priv.h
28
29typedef struct gptimer_group_t {
30 int group_id;
31 portMUX_TYPE spinlock; // to protect per-group register level concurrent access
32 gptimer_t* timers[SOC_TIMER_GROUP_TIMERS_PER_GROUP];
34
35typedef enum {
36 GPTIMER_FSM_INIT, // Timer is initialized, but not enabled yet
37 GPTIMER_FSM_ENABLE, // Timer is enabled, but is not running yet
38 GPTIMER_FSM_RUN, // Timer is in running
39 GPTIMER_FSM_WAIT, // Timer is in the middle of state change (Intermediate state)
40} gptimer_fsm_t;
41
42struct gptimer_t {
43 gptimer_group_t* group;
44 int timer_id;
45 uint32_t resolution_hz;
46 uint64_t reload_count;
47 uint64_t alarm_count;
48 gptimer_count_direction_t direction;
49 timer_hal_context_t hal;
50 std::atomic<gptimer_fsm_t> fsm;
51 int intr_priority;
52 intr_handle_t intr;
53 portMUX_TYPE spinlock; // to protect per-timer resources concurrent accessed by task and ISR handler
54 gptimer_alarm_cb_t on_alarm;
55 void* user_ctx;
56 gptimer_clock_source_t clk_src;
57 esp_pm_lock_handle_t pm_lock; // power management lock
58#if CONFIG_PM_ENABLE
59 char pm_lock_name[GPTIMER_PM_LOCK_NAME_LEN_MAX]; // pm lock name
60#endif
61 struct {
62 uint32_t intr_shared : 1;
63 uint32_t auto_reload_on_alarm : 1;
64 uint32_t alarm_en : 1;
65 } flags;
66};
67
69// FROM gptimer.c
71
72__attribute__((always_inline)) inline esp_err_t inlined_gptimer_get_raw_count(gptimer_handle_t timer, uint64_t* value) {
73 if (timer == NULL || value == NULL) {
74 return ESP_ERR_INVALID_ARG;
75 }
76 portENTER_CRITICAL_SAFE(&timer->spinlock);
77 timer_ll_trigger_soft_capture((&timer->hal)->dev, (&timer->hal)->timer_id);
78 *value = timer_ll_get_counter_value((&timer->hal)->dev, (&timer->hal)->timer_id);
79 portEXIT_CRITICAL_SAFE(&timer->spinlock);
80 return ESP_OK;
81}
82
83__attribute__((always_inline)) inline esp_err_t inlined_gptimer_set_raw_count(gptimer_handle_t timer, uint64_t value) {
84 if (timer == NULL) {
85 return ESP_ERR_INVALID_ARG;
86 }
87 portENTER_CRITICAL_SAFE(&timer->spinlock);
89 // - `timer_ll_set_reload_value()` will only indicate the `reload_value`
90 // - `timer_ll_set_reload_value()` + ``timer_ll_trigger_soft_reload()` can update the HW counter value by software
91 // Therefore, after updating the HW counter value, we need to restore the previous `reload_value`.
92 // Attention: The following process should be protected by a lock in the driver layer.
94 // save current reload value
95 uint64_t old_reload = timer_ll_get_reload_value((&timer->hal)->dev, (&timer->hal)->timer_id);
96 timer_ll_set_reload_value((&timer->hal)->dev, (&timer->hal)->timer_id, value);
97 timer_ll_trigger_soft_reload((&timer->hal)->dev, (&timer->hal)->timer_id);
98 // restore the previous reload value
99 timer_ll_set_reload_value((&timer->hal)->dev, (&timer->hal)->timer_id, old_reload);
100 portEXIT_CRITICAL_SAFE(&timer->spinlock);
101 return ESP_OK;
102}
103
104__attribute__((always_inline)) inline esp_err_t inlined_gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t* config) {
105 if (timer == NULL) {
106 return ESP_ERR_INVALID_ARG;
107 }
108 if (config) {
109#if CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
110 // when the function is placed in IRAM, we expect the config struct is also placed in internal RAM
111 // if the cache is disabled, the function can still access the config struct
112 if (esp_ptr_internal(config) == false) {
113 return ESP_ERR_INVALID_ARG;
114 }
115#endif
116 // When auto_reload is enabled, alarm_count should not be equal to reload_count
117 bool valid_auto_reload = !config->flags.auto_reload_on_alarm || config->alarm_count != config->reload_count;
118 if (valid_auto_reload == false) {
119 return ESP_ERR_INVALID_ARG;
120 }
121
122 portENTER_CRITICAL_SAFE(&timer->spinlock);
123 timer->reload_count = config->reload_count;
124 timer->alarm_count = config->alarm_count;
125 timer->flags.auto_reload_on_alarm = config->flags.auto_reload_on_alarm;
126 timer->flags.alarm_en = true;
127
128 timer_ll_set_reload_value(timer->hal.dev, timer->timer_id, config->reload_count);
129 timer_ll_set_alarm_value(timer->hal.dev, timer->timer_id, config->alarm_count);
130 portEXIT_CRITICAL_SAFE(&timer->spinlock);
131 } else {
132 portENTER_CRITICAL_SAFE(&timer->spinlock);
133 timer->flags.auto_reload_on_alarm = false;
134 timer->flags.alarm_en = false;
135 portEXIT_CRITICAL_SAFE(&timer->spinlock);
136 }
137
138 portENTER_CRITICAL_SAFE(&timer->spinlock);
139 timer_ll_enable_auto_reload(timer->hal.dev, timer->timer_id, timer->flags.auto_reload_on_alarm);
140 timer_ll_enable_alarm(timer->hal.dev, timer->timer_id, timer->flags.alarm_en);
141 portEXIT_CRITICAL_SAFE(&timer->spinlock);
142 return ESP_OK;
143}