adc_monitor.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdatomic.h>
  7. #include "esp_check.h"
  8. #include "esp_memory_utils.h"
  9. #include "esp_intr_alloc.h"
  10. #include "esp_heap_caps.h"
  11. #include "freertos/FreeRTOS.h"
  12. #include "adc_continuous_internal.h"
  13. #include "soc/periph_defs.h"
  14. #include "esp_adc/adc_monitor.h"
  15. static const char *MNTOR_TAG = "adc_monitor";
  16. /**
  17. * @brief context for adc continuous driver
  18. */
  19. typedef struct adc_monitor_platform_t {
  20. adc_continuous_ctx_t *continuous_ctx; // ADC continuous driver context
  21. intr_handle_t monitor_intr_handle; // monitor intr handler
  22. portMUX_TYPE monitor_spinlock; // spinlock
  23. } adc_monitor_platform_t;
  24. // Global context of adc monitor, other member will be lazy loaded
  25. static adc_monitor_platform_t s_adc_monitor_platform = {.monitor_spinlock = portMUX_INITIALIZER_UNLOCKED};
  26. #if CONFIG_IDF_TARGET_ESP32S2
  27. // Monitor unit index need equal to ADC unit index on ESP32S2
  28. static atomic_bool s_adc_monitor_claimed[SOC_ADC_DIGI_MONITOR_NUM] = {};
  29. static esp_err_t s_adc_monitor_claim(adc_continuous_handle_t handle, adc_monitor_t *monitor_ctx, adc_unit_t unit)
  30. {
  31. assert(handle && monitor_ctx);
  32. esp_err_t ret = ESP_ERR_NOT_FOUND;
  33. bool false_var = false;
  34. if (atomic_compare_exchange_strong(&s_adc_monitor_claimed[unit], &false_var, true)) {
  35. monitor_ctx->monitor_id = unit;
  36. handle->adc_monitor[unit] = monitor_ctx;
  37. ret = ESP_OK;
  38. } else {
  39. ESP_LOGE(MNTOR_TAG, "monitor %d already in use", (int)unit);
  40. }
  41. return ret;
  42. }
  43. static esp_err_t s_adc_monitor_release(adc_monitor_t *monitor_ctx)
  44. {
  45. assert(monitor_ctx);
  46. esp_err_t ret = ESP_ERR_NOT_FOUND;
  47. bool true_var = true;
  48. if (atomic_compare_exchange_strong(&s_adc_monitor_claimed[monitor_ctx->monitor_id], &true_var, false)) {
  49. s_adc_monitor_platform.continuous_ctx->adc_monitor[monitor_ctx->monitor_id] = NULL;
  50. ret = ESP_OK;
  51. }
  52. return ret;
  53. }
  54. #else
  55. static esp_err_t s_adc_monitor_claim(adc_continuous_handle_t handle, adc_monitor_t *monitor_ctx, adc_unit_t unit)
  56. {
  57. (void)unit;
  58. assert(handle && monitor_ctx);
  59. portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  60. for (int i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) {
  61. if (!handle->adc_monitor[i]) {
  62. monitor_ctx->monitor_id = i;
  63. handle->adc_monitor[i] = monitor_ctx;
  64. portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  65. return ESP_OK;
  66. }
  67. }
  68. portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  69. ESP_LOGE(MNTOR_TAG, "no free monitor");
  70. return ESP_ERR_NOT_FOUND;
  71. }
  72. static esp_err_t s_adc_monitor_release(adc_monitor_t *monitor_ctx)
  73. {
  74. assert(monitor_ctx);
  75. portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  76. s_adc_monitor_platform.continuous_ctx->adc_monitor[monitor_ctx->monitor_id] = NULL;
  77. portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  78. return ESP_OK;
  79. }
  80. #endif
  81. static void IRAM_ATTR s_adc_digi_monitor_isr(void *args)
  82. {
  83. bool need_yield = false;
  84. uint32_t intr_val = *((uint32_t *)adc_ll_digi_monitor_get_intr_status_addr());
  85. // clear all intr flags as have save intr status in `intr_val`
  86. adc_ll_digi_monitor_clear_intr();
  87. for (uint8_t i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) {
  88. adc_monitor_handle_t monitor_handle = s_adc_monitor_platform.continuous_ctx->adc_monitor[i];
  89. // check if high threshold alert
  90. if (intr_val & ADC_LL_GET_HIGH_THRES_MASK(i)) {
  91. assert(monitor_handle);
  92. assert(monitor_handle->monitor_id == i);
  93. if (monitor_handle->cbs.on_over_high_thresh) {
  94. adc_monitor_evt_data_t event_data = {};
  95. need_yield |= monitor_handle->cbs.on_over_high_thresh(monitor_handle, &event_data, monitor_handle->user_data);
  96. }
  97. }
  98. // check if low threshold alert
  99. if (intr_val & ADC_LL_GET_LOW_THRES_MASK(i)) {
  100. assert(monitor_handle);
  101. assert(monitor_handle->monitor_id == i);
  102. if (monitor_handle->cbs.on_below_low_thresh) {
  103. adc_monitor_evt_data_t event_data = {};
  104. need_yield |= monitor_handle->cbs.on_below_low_thresh(monitor_handle, &event_data, monitor_handle->user_data);
  105. }
  106. }
  107. }
  108. if (need_yield) {
  109. portYIELD_FROM_ISR();
  110. }
  111. }
  112. static esp_err_t adc_monitor_intr_alloc(void)
  113. {
  114. esp_err_t ret = ESP_OK;
  115. int intr_flags = ESP_INTR_FLAG_LOWMED;
  116. #if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
  117. intr_flags |= ESP_INTR_FLAG_IRAM;
  118. #endif
  119. #if SOC_ADC_TEMPERATURE_SHARE_INTR
  120. intr_flags |= ESP_INTR_FLAG_SHARED;
  121. ret = esp_intr_alloc_intrstatus(ETS_APB_ADC_INTR_SOURCE, intr_flags,
  122. (uint32_t)adc_ll_digi_monitor_get_intr_status_addr(),
  123. ADC_LL_THRES_ALL_INTR_ST_M, s_adc_digi_monitor_isr, NULL, &s_adc_monitor_platform.monitor_intr_handle);
  124. #else
  125. ret = esp_intr_alloc(ETS_APB_ADC_INTR_SOURCE, intr_flags, s_adc_digi_monitor_isr, NULL, &s_adc_monitor_platform.monitor_intr_handle);
  126. #endif //SOC_ADC_TEMPERATURE_SHARE_INTR
  127. return ret;
  128. }
  129. //-------------------------------------------PUBLIC APIs--------------------------------------------//
  130. esp_err_t adc_new_continuous_monitor(adc_continuous_handle_t handle, const adc_monitor_config_t *monitor_cfg, adc_monitor_handle_t *ret_handle)
  131. {
  132. esp_err_t ret;
  133. ESP_RETURN_ON_FALSE(handle && monitor_cfg && ret_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
  134. ESP_RETURN_ON_FALSE(monitor_cfg->adc_unit < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid adc_unit");
  135. ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "ADC continuous driver should be in init state");
  136. #if CONFIG_IDF_TARGET_ESP32S2
  137. ESP_RETURN_ON_FALSE(!((monitor_cfg->h_threshold >= 0) && (monitor_cfg->l_threshold >= 0)), ESP_ERR_NOT_SUPPORTED, MNTOR_TAG, "ESP32S2 support only one threshold");
  138. #endif
  139. // alloc handler memory
  140. adc_monitor_t *monitor_ctx = heap_caps_calloc(1, sizeof(adc_monitor_t), MALLOC_CAP_INTERNAL);
  141. ESP_RETURN_ON_FALSE(monitor_ctx, ESP_ERR_NO_MEM, MNTOR_TAG, "no mem");
  142. // alloc monitor hardware
  143. ESP_GOTO_ON_ERROR(s_adc_monitor_claim(handle, monitor_ctx, monitor_cfg->adc_unit), claim_err, MNTOR_TAG, "ADC monitor claim failed");
  144. memcpy(&monitor_ctx->config, monitor_cfg, sizeof(adc_monitor_config_t));
  145. s_adc_monitor_platform.continuous_ctx = handle;
  146. // alloc cpu intr
  147. portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  148. bool alloc_intr = !s_adc_monitor_platform.monitor_intr_handle;
  149. portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock);
  150. if (alloc_intr) {
  151. ESP_GOTO_ON_ERROR(adc_monitor_intr_alloc(), intr_err, MNTOR_TAG, "esp intr alloc failed");
  152. }
  153. // config hardware
  154. adc_ll_digi_monitor_clear_intr();
  155. adc_ll_digi_monitor_set_thres(monitor_ctx->monitor_id, monitor_ctx->config.adc_unit, monitor_ctx->config.channel, monitor_ctx->config.h_threshold, monitor_ctx->config.l_threshold);
  156. *ret_handle = monitor_ctx;
  157. return ESP_OK;
  158. intr_err:
  159. s_adc_monitor_release(monitor_ctx);
  160. claim_err:
  161. free(monitor_ctx);
  162. return ret;
  163. }
  164. esp_err_t adc_continuous_monitor_register_event_callbacks(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_cbs_t *cbs, void *user_data)
  165. {
  166. ESP_RETURN_ON_FALSE(monitor_handle && cbs, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
  167. ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor should be in init state");
  168. ESP_RETURN_ON_FALSE(!(monitor_handle->cbs.on_over_high_thresh || monitor_handle->cbs.on_below_low_thresh), ESP_ERR_INVALID_STATE, MNTOR_TAG, "callbacks had beed registered");
  169. #if CONFIG_IDF_TARGET_ESP32S2
  170. ESP_RETURN_ON_FALSE(!(cbs->on_below_low_thresh && cbs->on_over_high_thresh), ESP_ERR_NOT_SUPPORTED, MNTOR_TAG, "ESP32S2 support only one threshold");
  171. #endif
  172. // If iram_safe enabled, check if user_data and cbs is iram_safe
  173. #if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE
  174. if (cbs->on_over_high_thresh) {
  175. ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_over_high_thresh), ESP_ERR_INVALID_ARG, MNTOR_TAG, "on_over_high_thresh func not in iram");
  176. }
  177. if (cbs->on_below_low_thresh) {
  178. ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_below_low_thresh), ESP_ERR_INVALID_ARG, MNTOR_TAG, "on_below_low_thresh func not in iram");
  179. }
  180. if (user_data) {
  181. ESP_RETURN_ON_FALSE(esp_ptr_in_dram(user_data) || esp_ptr_in_diram_dram(user_data), ESP_ERR_INVALID_ARG, MNTOR_TAG, "user_data not in iram");
  182. }
  183. #endif
  184. memcpy(&monitor_handle->cbs, cbs, sizeof(adc_monitor_evt_cbs_t));
  185. monitor_handle->user_data = user_data;
  186. return ESP_OK;
  187. }
  188. esp_err_t adc_continuous_monitor_enable(adc_monitor_handle_t monitor_handle)
  189. {
  190. ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
  191. ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor should be in init state");
  192. // enable peripheral intr_ena
  193. if ((monitor_handle->config.h_threshold >= 0)) {
  194. adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_HIGH, true);
  195. }
  196. if ((monitor_handle->config.l_threshold >= 0)) {
  197. adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_LOW, true);
  198. }
  199. adc_ll_digi_monitor_user_start(monitor_handle->monitor_id, true);
  200. monitor_handle->fsm = ADC_MONITOR_FSM_ENABLED;
  201. return esp_intr_enable(s_adc_monitor_platform.monitor_intr_handle);
  202. }
  203. esp_err_t adc_continuous_monitor_disable(adc_monitor_handle_t monitor_handle)
  204. {
  205. ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
  206. ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_ENABLED, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor not in running");
  207. // disable peripheral intr_ena
  208. if ((monitor_handle->config.h_threshold >= 0)) {
  209. adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_HIGH, false);
  210. }
  211. if ((monitor_handle->config.l_threshold >= 0)) {
  212. adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_LOW, false);
  213. }
  214. adc_ll_digi_monitor_user_start(monitor_handle->monitor_id, false);
  215. monitor_handle->fsm = ADC_MONITOR_FSM_INIT;
  216. return esp_intr_disable(s_adc_monitor_platform.monitor_intr_handle);
  217. }
  218. esp_err_t adc_del_continuous_monitor(adc_monitor_handle_t monitor_handle)
  219. {
  220. ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer");
  221. ESP_RETURN_ON_FALSE((monitor_handle->fsm == ADC_MONITOR_FSM_INIT) && (s_adc_monitor_platform.continuous_ctx->fsm == ADC_FSM_INIT), \
  222. ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor and ADC continuous driver should all be in init state");
  223. ESP_RETURN_ON_ERROR(s_adc_monitor_release(monitor_handle), MNTOR_TAG, "monitor not find or isn't in use");
  224. for (int i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) {
  225. if (s_adc_monitor_platform.continuous_ctx->adc_monitor[i]) {
  226. // If any other monitor not freed, then delete self and exit now. del_monitor is complete
  227. free(monitor_handle);
  228. return ESP_OK;
  229. }
  230. }
  231. // If no monitor is using, the release intr handle as well
  232. ESP_RETURN_ON_ERROR(esp_intr_free(s_adc_monitor_platform.monitor_intr_handle), MNTOR_TAG, "esp intr release failed\n");
  233. s_adc_monitor_platform.monitor_intr_handle = NULL;
  234. free(monitor_handle);
  235. return ESP_OK;
  236. }