pulse_cnt.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. /*
  2. * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdint.h>
  7. #include <sys/lock.h>
  8. #include "sdkconfig.h"
  9. #if CONFIG_PCNT_ENABLE_DEBUG_LOG
  10. // The local log level must be defined before including esp_log.h
  11. // Set the maximum log level for this source file
  12. #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
  13. #endif
  14. #include "freertos/FreeRTOS.h"
  15. #include "esp_heap_caps.h"
  16. #include "esp_intr_alloc.h"
  17. #include "esp_attr.h"
  18. #include "esp_log.h"
  19. #include "esp_check.h"
  20. #include "esp_pm.h"
  21. #include "esp_rom_gpio.h"
  22. #include "soc/soc_caps.h"
  23. #include "soc/pcnt_periph.h"
  24. #include "soc/gpio_pins.h"
  25. #include "hal/pcnt_hal.h"
  26. #include "hal/pcnt_ll.h"
  27. #include "hal/gpio_hal.h"
  28. #include "esp_private/esp_clk.h"
  29. #include "esp_private/periph_ctrl.h"
  30. #include "driver/gpio.h"
  31. #include "driver/pulse_cnt.h"
  32. #include "esp_memory_utils.h"
  33. // If ISR handler is allowed to run whilst cache is disabled,
  34. // Make sure all the code and related variables used by the handler are in the SRAM
  35. #if CONFIG_PCNT_ISR_IRAM_SAFE || CONFIG_PCNT_CTRL_FUNC_IN_IRAM
  36. #define PCNT_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
  37. #else
  38. #define PCNT_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
  39. #endif
  40. #if CONFIG_PCNT_ISR_IRAM_SAFE
  41. #define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
  42. #else
  43. #define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
  44. #endif
  45. #define PCNT_PM_LOCK_NAME_LEN_MAX 16
  46. static const char *TAG = "pcnt";
  47. typedef struct pcnt_platform_t pcnt_platform_t;
  48. typedef struct pcnt_group_t pcnt_group_t;
  49. typedef struct pcnt_unit_t pcnt_unit_t;
  50. typedef struct pcnt_chan_t pcnt_chan_t;
  51. struct pcnt_platform_t {
  52. _lock_t mutex; // platform level mutex lock
  53. pcnt_group_t *groups[SOC_PCNT_GROUPS]; // pcnt group pool
  54. int group_ref_counts[SOC_PCNT_GROUPS]; // reference count used to protect group install/uninstall
  55. };
  56. struct pcnt_group_t {
  57. int group_id; // Group ID, index from 0
  58. portMUX_TYPE spinlock; // to protect per-group register level concurrent access
  59. pcnt_hal_context_t hal;
  60. pcnt_unit_t *units[SOC_PCNT_UNITS_PER_GROUP]; // array of PCNT units
  61. };
  62. typedef struct {
  63. pcnt_ll_watch_event_id_t event_id; // event type
  64. int watch_point_value; // value to be watched
  65. } pcnt_watch_point_t;
  66. typedef enum {
  67. PCNT_UNIT_FSM_INIT,
  68. PCNT_UNIT_FSM_ENABLE,
  69. } pcnt_unit_fsm_t;
  70. struct pcnt_unit_t {
  71. pcnt_group_t *group; // which group the pcnt unit belongs to
  72. portMUX_TYPE spinlock; // Spinlock, stop one unit from accessing different parts of a same register concurrently
  73. int unit_id; // allocated unit numerical ID
  74. int low_limit; // low limit value
  75. int high_limit; // high limit value
  76. int zero_input_gpio_num; // which gpio clear signal input
  77. int accum_value; // accumulated count value
  78. pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels
  79. pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers
  80. intr_handle_t intr; // interrupt handle
  81. esp_pm_lock_handle_t pm_lock; // PM lock, for glitch filter, as that module can only be functional under APB
  82. #if CONFIG_PM_ENABLE
  83. char pm_lock_name[PCNT_PM_LOCK_NAME_LEN_MAX]; // pm lock name
  84. #endif
  85. pcnt_unit_fsm_t fsm; // record PCNT unit's driver state
  86. pcnt_watch_cb_t on_reach; // user registered callback function
  87. void *user_data; // user data registered by user, which would be passed to the right callback function
  88. struct {
  89. uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */
  90. } flags;
  91. };
  92. struct pcnt_chan_t {
  93. pcnt_unit_t *unit; // pointer to the PCNT unit where it derives from
  94. int channel_id; // channel ID, index from 0
  95. int edge_gpio_num;
  96. int level_gpio_num;
  97. };
  98. // pcnt driver platform, it's always a singleton
  99. static pcnt_platform_t s_platform;
  100. static pcnt_group_t *pcnt_acquire_group_handle(int group_id);
  101. static void pcnt_release_group_handle(pcnt_group_t *group);
  102. static void pcnt_default_isr(void *args);
  103. static esp_err_t pcnt_register_to_group(pcnt_unit_t *unit)
  104. {
  105. pcnt_group_t *group = NULL;
  106. int unit_id = -1;
  107. for (int i = 0; i < SOC_PCNT_GROUPS; i++) {
  108. group = pcnt_acquire_group_handle(i);
  109. ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
  110. // loop to search free unit in the group
  111. portENTER_CRITICAL(&group->spinlock);
  112. for (int j = 0; j < SOC_PCNT_UNITS_PER_GROUP; j++) {
  113. if (!group->units[j]) {
  114. unit_id = j;
  115. group->units[j] = unit;
  116. break;
  117. }
  118. }
  119. portEXIT_CRITICAL(&group->spinlock);
  120. if (unit_id < 0) {
  121. pcnt_release_group_handle(group);
  122. } else {
  123. unit->group = group;
  124. unit->unit_id = unit_id;
  125. break;
  126. }
  127. }
  128. ESP_RETURN_ON_FALSE(unit_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free unit");
  129. return ESP_OK;
  130. }
  131. static void pcnt_unregister_from_group(pcnt_unit_t *unit)
  132. {
  133. pcnt_group_t *group = unit->group;
  134. int unit_id = unit->unit_id;
  135. portENTER_CRITICAL(&group->spinlock);
  136. group->units[unit_id] = NULL;
  137. portEXIT_CRITICAL(&group->spinlock);
  138. // unit has a reference on group, release it now
  139. pcnt_release_group_handle(group);
  140. }
  141. static esp_err_t pcnt_destroy(pcnt_unit_t *unit)
  142. {
  143. if (unit->pm_lock) {
  144. ESP_RETURN_ON_ERROR(esp_pm_lock_delete(unit->pm_lock), TAG, "delete pm lock failed");
  145. }
  146. if (unit->intr) {
  147. ESP_RETURN_ON_ERROR(esp_intr_free(unit->intr), TAG, "delete interrupt service failed");
  148. }
  149. if (unit->group) {
  150. pcnt_unregister_from_group(unit);
  151. }
  152. free(unit);
  153. return ESP_OK;
  154. }
  155. esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *ret_unit)
  156. {
  157. #if CONFIG_PCNT_ENABLE_DEBUG_LOG
  158. esp_log_level_set(TAG, ESP_LOG_DEBUG);
  159. #endif
  160. esp_err_t ret = ESP_OK;
  161. pcnt_unit_t *unit = NULL;
  162. ESP_GOTO_ON_FALSE(config && ret_unit, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
  163. ESP_GOTO_ON_FALSE(config->low_limit < 0 && config->high_limit > 0 && config->low_limit >= PCNT_LL_MIN_LIN &&
  164. config->high_limit <= PCNT_LL_MAX_LIM, ESP_ERR_INVALID_ARG, err, TAG,
  165. "invalid limit range:[%d,%d]", config->low_limit, config->high_limit);
  166. unit = heap_caps_calloc(1, sizeof(pcnt_unit_t), PCNT_MEM_ALLOC_CAPS);
  167. ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no mem for unit");
  168. // register unit to the group (because one group can have several units)
  169. ESP_GOTO_ON_ERROR(pcnt_register_to_group(unit), err, TAG, "register unit failed");
  170. pcnt_group_t *group = unit->group;
  171. int group_id = group->group_id;
  172. int unit_id = unit->unit_id;
  173. // to accumulate count value, we should install the interrupt handler first, and in the ISR we do the accumulation
  174. bool to_install_isr = (config->flags.accum_count == 1);
  175. if (to_install_isr) {
  176. int isr_flags = PCNT_INTR_ALLOC_FLAGS;
  177. ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(pcnt_periph_signals.groups[group_id].irq, isr_flags,
  178. (uint32_t)pcnt_ll_get_intr_status_reg(group->hal.dev), PCNT_LL_UNIT_WATCH_EVENT(unit_id),
  179. pcnt_default_isr, unit, &unit->intr), err,
  180. TAG, "install interrupt service failed");
  181. }
  182. // some events are enabled by default, disable them all
  183. pcnt_ll_disable_all_events(group->hal.dev, unit_id);
  184. // disable filter by default
  185. pcnt_ll_enable_glitch_filter(group->hal.dev, unit_id, false);
  186. // set default high/low limitation value
  187. // note: limit value takes effect only after counter clear
  188. pcnt_ll_set_high_limit_value(group->hal.dev, unit_id, config->high_limit);
  189. pcnt_ll_set_low_limit_value(group->hal.dev, unit_id, config->low_limit);
  190. unit->high_limit = config->high_limit;
  191. unit->low_limit = config->low_limit;
  192. unit->accum_value = 0;
  193. unit->flags.accum_count = config->flags.accum_count;
  194. // clear/pause register is shared by all units, so using group's spinlock
  195. portENTER_CRITICAL(&group->spinlock);
  196. pcnt_ll_stop_count(group->hal.dev, unit_id);
  197. pcnt_ll_clear_count(group->hal.dev, unit_id);
  198. // enable the interrupt if we want to accumulate the counter in the ISR
  199. pcnt_ll_enable_intr(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), to_install_isr);
  200. pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
  201. portEXIT_CRITICAL(&group->spinlock);
  202. unit->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
  203. unit->fsm = PCNT_UNIT_FSM_INIT;
  204. for (int i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) {
  205. unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; // invalid all watch point
  206. }
  207. #if SOC_PCNT_SUPPORT_ZERO_INPUT
  208. // GPIO configuration
  209. gpio_config_t gpio_conf = {
  210. .intr_type = GPIO_INTR_DISABLE,
  211. .mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
  212. .pull_down_en = true,
  213. .pull_up_en = false,
  214. };
  215. if (config->zero_input_gpio_num >= 0) {
  216. if (config->flags.invert_zero_input) {
  217. gpio_conf.pull_down_en = false;
  218. gpio_conf.pull_up_en = true;
  219. }
  220. gpio_conf.pin_bit_mask = 1ULL << config->zero_input_gpio_num;
  221. ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config zero GPIO failed");
  222. esp_rom_gpio_connect_in_signal(config->zero_input_gpio_num,
  223. pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig,
  224. config->flags.invert_zero_input);
  225. }
  226. unit->zero_input_gpio_num = config->zero_input_gpio_num;
  227. #endif // SOC_PCNT_SUPPORT_ZERO_INPUT
  228. ESP_LOGD(TAG, "new pcnt unit (%d,%d) at %p, count range:[%d,%d]", group_id, unit_id, unit, unit->low_limit, unit->high_limit);
  229. *ret_unit = unit;
  230. return ESP_OK;
  231. err:
  232. if (unit) {
  233. pcnt_destroy(unit);
  234. }
  235. return ret;
  236. }
  237. esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit)
  238. {
  239. ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  240. ESP_RETURN_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
  241. pcnt_group_t *group = unit->group;
  242. int group_id = group->group_id;
  243. int unit_id = unit->unit_id;
  244. for (int i = 0; i < SOC_PCNT_CHANNELS_PER_UNIT; i++) {
  245. ESP_RETURN_ON_FALSE(!unit->channels[i], ESP_ERR_INVALID_STATE, TAG, "channel %d still in working", i);
  246. }
  247. #if SOC_PCNT_SUPPORT_ZERO_INPUT
  248. if (unit->zero_input_gpio_num >= 0) {
  249. gpio_reset_pin(unit->zero_input_gpio_num);
  250. }
  251. #endif // SOC_PCNT_SUPPORT_ZERO_INPUT
  252. ESP_LOGD(TAG, "del unit (%d,%d)", group_id, unit_id);
  253. // recycle memory resource
  254. ESP_RETURN_ON_ERROR(pcnt_destroy(unit), TAG, "destroy pcnt unit failed");
  255. return ESP_OK;
  256. }
  257. esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config)
  258. {
  259. pcnt_group_t *group = NULL;
  260. uint32_t glitch_filter_thres = 0;
  261. ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  262. // glitch filter should be set only when unit is in init state
  263. ESP_RETURN_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
  264. group = unit->group;
  265. if (config) {
  266. glitch_filter_thres = esp_clk_apb_freq() / 1000000 * config->max_glitch_ns / 1000;
  267. ESP_RETURN_ON_FALSE(glitch_filter_thres <= PCNT_LL_MAX_GLITCH_WIDTH, ESP_ERR_INVALID_ARG, TAG, "glitch width out of range");
  268. // The filter module is working against APB clock, so lazy install PM lock
  269. #if CONFIG_PM_ENABLE
  270. if (!unit->pm_lock) {
  271. sprintf(unit->pm_lock_name, "pcnt_%d_%d", group->group_id, unit->unit_id); // e.g. pcnt_0_0
  272. ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, unit->pm_lock_name, &unit->pm_lock), TAG, "install pm lock failed");
  273. ESP_LOGD(TAG, "install APB_FREQ_MAX lock for unit (%d,%d)", group->group_id, unit->unit_id);
  274. }
  275. #endif
  276. }
  277. // filter control bit is mixed with other PCNT control bits in the same register
  278. portENTER_CRITICAL(&unit->spinlock);
  279. if (config) {
  280. pcnt_ll_set_glitch_filter_thres(group->hal.dev, unit->unit_id, glitch_filter_thres);
  281. pcnt_ll_enable_glitch_filter(group->hal.dev, unit->unit_id, true);
  282. } else {
  283. pcnt_ll_enable_glitch_filter(group->hal.dev, unit->unit_id, false);
  284. }
  285. portEXIT_CRITICAL(&unit->spinlock);
  286. return ESP_OK;
  287. }
  288. esp_err_t pcnt_unit_enable(pcnt_unit_handle_t unit)
  289. {
  290. ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  291. ESP_RETURN_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
  292. // acquire power manager lock
  293. if (unit->pm_lock) {
  294. ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(unit->pm_lock), TAG, "acquire pm_lock failed");
  295. }
  296. // enable interrupt service
  297. if (unit->intr) {
  298. ESP_RETURN_ON_ERROR(esp_intr_enable(unit->intr), TAG, "enable interrupt service failed");
  299. }
  300. unit->fsm = PCNT_UNIT_FSM_ENABLE;
  301. return ESP_OK;
  302. }
  303. esp_err_t pcnt_unit_disable(pcnt_unit_handle_t unit)
  304. {
  305. ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  306. ESP_RETURN_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "unit not in enable state");
  307. // disable interrupt service
  308. if (unit->intr) {
  309. ESP_RETURN_ON_ERROR(esp_intr_disable(unit->intr), TAG, "disable interrupt service failed");
  310. }
  311. // release power manager lock
  312. if (unit->pm_lock) {
  313. ESP_RETURN_ON_ERROR(esp_pm_lock_release(unit->pm_lock), TAG, "release APB_FREQ_MAX lock failed");
  314. }
  315. unit->fsm = PCNT_UNIT_FSM_INIT;
  316. return ESP_OK;
  317. }
  318. esp_err_t pcnt_unit_start(pcnt_unit_handle_t unit)
  319. {
  320. ESP_RETURN_ON_FALSE_ISR(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  321. ESP_RETURN_ON_FALSE_ISR(unit->fsm == PCNT_UNIT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "unit not enabled yet");
  322. pcnt_group_t *group = unit->group;
  323. // all PCNT units share the same register to control counter
  324. portENTER_CRITICAL_SAFE(&group->spinlock);
  325. pcnt_ll_start_count(group->hal.dev, unit->unit_id);
  326. portEXIT_CRITICAL_SAFE(&group->spinlock);
  327. return ESP_OK;
  328. }
  329. esp_err_t pcnt_unit_stop(pcnt_unit_handle_t unit)
  330. {
  331. ESP_RETURN_ON_FALSE_ISR(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  332. ESP_RETURN_ON_FALSE_ISR(unit->fsm == PCNT_UNIT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "unit not enabled yet");
  333. pcnt_group_t *group = unit->group;
  334. // all PCNT units share the same register to control counter
  335. portENTER_CRITICAL_SAFE(&group->spinlock);
  336. pcnt_ll_stop_count(group->hal.dev, unit->unit_id);
  337. portEXIT_CRITICAL_SAFE(&group->spinlock);
  338. return ESP_OK;
  339. }
  340. esp_err_t pcnt_unit_clear_count(pcnt_unit_handle_t unit)
  341. {
  342. pcnt_group_t *group = NULL;
  343. ESP_RETURN_ON_FALSE_ISR(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  344. group = unit->group;
  345. // all PCNT units share the same register to control counter
  346. portENTER_CRITICAL_SAFE(&group->spinlock);
  347. pcnt_ll_clear_count(group->hal.dev, unit->unit_id);
  348. portEXIT_CRITICAL_SAFE(&group->spinlock);
  349. // reset the accumulated count as well
  350. portENTER_CRITICAL_SAFE(&unit->spinlock);
  351. unit->accum_value = 0;
  352. portEXIT_CRITICAL_SAFE(&unit->spinlock);
  353. return ESP_OK;
  354. }
  355. esp_err_t pcnt_unit_get_count(pcnt_unit_handle_t unit, int *value)
  356. {
  357. pcnt_group_t *group = NULL;
  358. ESP_RETURN_ON_FALSE_ISR(unit && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  359. group = unit->group;
  360. // the accum_value is also accessed by the ISR, so adding a critical section
  361. portENTER_CRITICAL_SAFE(&unit->spinlock);
  362. *value = pcnt_ll_get_count(group->hal.dev, unit->unit_id) + unit->accum_value;
  363. portEXIT_CRITICAL_SAFE(&unit->spinlock);
  364. return ESP_OK;
  365. }
  366. esp_err_t pcnt_unit_register_event_callbacks(pcnt_unit_handle_t unit, const pcnt_event_callbacks_t *cbs, void *user_data)
  367. {
  368. ESP_RETURN_ON_FALSE(unit && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  369. // unit event callbacks should be registered in init state
  370. pcnt_group_t *group = unit->group;
  371. int group_id = group->group_id;
  372. int unit_id = unit->unit_id;
  373. #if CONFIG_PCNT_ISR_IRAM_SAFE
  374. if (cbs->on_reach) {
  375. ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_reach), ESP_ERR_INVALID_ARG, TAG, "on_reach callback not in IRAM");
  376. }
  377. if (user_data) {
  378. ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
  379. }
  380. #endif
  381. // lazy install interrupt service
  382. if (!unit->intr) {
  383. ESP_RETURN_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
  384. int isr_flags = PCNT_INTR_ALLOC_FLAGS;
  385. ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(pcnt_periph_signals.groups[group_id].irq, isr_flags,
  386. (uint32_t)pcnt_ll_get_intr_status_reg(group->hal.dev), PCNT_LL_UNIT_WATCH_EVENT(unit_id),
  387. pcnt_default_isr, unit, &unit->intr),
  388. TAG, "install interrupt service failed");
  389. }
  390. // enable/disable PCNT interrupt events
  391. portENTER_CRITICAL(&group->spinlock);
  392. pcnt_ll_enable_intr(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), cbs->on_reach != NULL);
  393. portEXIT_CRITICAL(&group->spinlock);
  394. unit->on_reach = cbs->on_reach;
  395. unit->user_data = user_data;
  396. return ESP_OK;
  397. }
  398. esp_err_t pcnt_unit_add_watch_point(pcnt_unit_handle_t unit, int watch_point)
  399. {
  400. esp_err_t ret = ESP_OK;
  401. pcnt_group_t *group = NULL;
  402. ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  403. ESP_RETURN_ON_FALSE(watch_point <= unit->high_limit && watch_point >= unit->low_limit,
  404. ESP_ERR_INVALID_ARG, TAG, "watch_point out of limit");
  405. group = unit->group;
  406. // event enable/disable is mixed with other control function in the same register
  407. portENTER_CRITICAL(&unit->spinlock);
  408. // zero cross watch point
  409. if (watch_point == 0) {
  410. if (unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].event_id != PCNT_LL_WATCH_EVENT_INVALID) {
  411. ret = ESP_ERR_INVALID_STATE; // zero cross event watcher has been installed already
  412. } else {
  413. unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].event_id = PCNT_LL_WATCH_EVENT_ZERO_CROSS;
  414. unit->watchers[PCNT_LL_WATCH_EVENT_ZERO_CROSS].watch_point_value = 0;
  415. pcnt_ll_enable_zero_cross_event(group->hal.dev, unit->unit_id, true);
  416. }
  417. }
  418. // high limit watch point
  419. else if (watch_point == unit->high_limit) {
  420. if (unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].event_id != PCNT_LL_WATCH_EVENT_INVALID) {
  421. ret = ESP_ERR_INVALID_STATE; // high limit event watcher has been installed already
  422. } else {
  423. unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].event_id = PCNT_LL_WATCH_EVENT_HIGH_LIMIT;
  424. unit->watchers[PCNT_LL_WATCH_EVENT_HIGH_LIMIT].watch_point_value = unit->high_limit;
  425. pcnt_ll_enable_high_limit_event(group->hal.dev, unit->unit_id, true);
  426. }
  427. }
  428. // low limit watch point
  429. else if (watch_point == unit->low_limit) {
  430. if (unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].event_id != PCNT_LL_WATCH_EVENT_INVALID) {
  431. ret = ESP_ERR_INVALID_STATE; // low limit event watcher has been installed already
  432. } else {
  433. unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].event_id = PCNT_LL_WATCH_EVENT_LOW_LIMIT;
  434. unit->watchers[PCNT_LL_WATCH_EVENT_LOW_LIMIT].watch_point_value = unit->low_limit;
  435. pcnt_ll_enable_low_limit_event(group->hal.dev, unit->unit_id, true);
  436. }
  437. }
  438. // other threshold watch point
  439. else {
  440. int thres_num = SOC_PCNT_THRES_POINT_PER_UNIT - 1;
  441. switch (thres_num) {
  442. case 1:
  443. if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].event_id == PCNT_LL_WATCH_EVENT_INVALID) {
  444. unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].event_id = PCNT_LL_WATCH_EVENT_THRES1;
  445. unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].watch_point_value = watch_point;
  446. pcnt_ll_set_thres_value(group->hal.dev, unit->unit_id, 1, watch_point);
  447. pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 1, true);
  448. break;
  449. } else if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES1].watch_point_value == watch_point) {
  450. ret = ESP_ERR_INVALID_STATE;
  451. break;
  452. }
  453. /* fall-through */
  454. case 0:
  455. if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].event_id == PCNT_LL_WATCH_EVENT_INVALID) {
  456. unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].event_id = PCNT_LL_WATCH_EVENT_THRES0;
  457. unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].watch_point_value = watch_point;
  458. pcnt_ll_set_thres_value(group->hal.dev, unit->unit_id, 0, watch_point);
  459. pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 0, true);
  460. break;
  461. } else if (unit->watchers[PCNT_LL_WATCH_EVENT_THRES0].watch_point_value == watch_point) {
  462. ret = ESP_ERR_INVALID_STATE;
  463. break;
  464. }
  465. /* fall-through */
  466. default:
  467. ret = ESP_ERR_NOT_FOUND; // no free threshold watch point available
  468. break;
  469. }
  470. }
  471. portEXIT_CRITICAL(&unit->spinlock);
  472. ESP_RETURN_ON_ERROR(ret, TAG, "add watchpoint %d failed", watch_point);
  473. return ESP_OK;
  474. }
  475. esp_err_t pcnt_unit_remove_watch_point(pcnt_unit_handle_t unit, int watch_point)
  476. {
  477. pcnt_group_t *group = NULL;
  478. ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  479. group = unit->group;
  480. pcnt_ll_watch_event_id_t event_id = PCNT_LL_WATCH_EVENT_INVALID;
  481. // event enable/disable is mixed with other control function in the same register
  482. portENTER_CRITICAL(&unit->spinlock);
  483. for (int i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) {
  484. if (unit->watchers[i].event_id != PCNT_LL_WATCH_EVENT_INVALID && unit->watchers[i].watch_point_value == watch_point) {
  485. event_id = unit->watchers[i].event_id;
  486. unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID;
  487. break;
  488. }
  489. }
  490. switch (event_id) {
  491. case PCNT_LL_WATCH_EVENT_ZERO_CROSS:
  492. pcnt_ll_enable_zero_cross_event(group->hal.dev, unit->unit_id, false);
  493. break;
  494. case PCNT_LL_WATCH_EVENT_LOW_LIMIT:
  495. pcnt_ll_enable_low_limit_event(group->hal.dev, unit->unit_id, false);
  496. break;
  497. case PCNT_LL_WATCH_EVENT_HIGH_LIMIT:
  498. pcnt_ll_enable_high_limit_event(group->hal.dev, unit->unit_id, false);
  499. break;
  500. case PCNT_LL_WATCH_EVENT_THRES0:
  501. pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 0, false);
  502. break;
  503. case PCNT_LL_WATCH_EVENT_THRES1:
  504. pcnt_ll_enable_thres_event(group->hal.dev, unit->unit_id, 1, false);
  505. break;
  506. default:
  507. break;
  508. }
  509. portEXIT_CRITICAL(&unit->spinlock);
  510. ESP_RETURN_ON_FALSE(event_id != PCNT_LL_WATCH_EVENT_INVALID, ESP_ERR_INVALID_STATE, TAG, "watch point %d not added yet", watch_point);
  511. return ESP_OK;
  512. }
  513. esp_err_t pcnt_new_channel(pcnt_unit_handle_t unit, const pcnt_chan_config_t *config, pcnt_channel_handle_t *ret_chan)
  514. {
  515. esp_err_t ret = ESP_OK;
  516. pcnt_chan_t *channel = NULL;
  517. pcnt_group_t *group = NULL;
  518. ESP_GOTO_ON_FALSE(unit && config && ret_chan, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
  519. ESP_GOTO_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_INIT, ESP_ERR_INVALID_STATE, err, TAG, "unit not in init state");
  520. group = unit->group;
  521. int group_id = group->group_id;
  522. int unit_id = unit->unit_id;
  523. channel = heap_caps_calloc(1, sizeof(pcnt_chan_t), PCNT_MEM_ALLOC_CAPS);
  524. ESP_GOTO_ON_FALSE(channel, ESP_ERR_NO_MEM, err, TAG, "no mem for channel");
  525. // search for a free channel
  526. int channel_id = -1;
  527. portENTER_CRITICAL(&unit->spinlock);
  528. for (int i = 0; i < SOC_PCNT_CHANNELS_PER_UNIT; i++) {
  529. if (!unit->channels[i]) {
  530. channel_id = i;
  531. unit->channels[channel_id] = channel;
  532. break;
  533. }
  534. }
  535. portEXIT_CRITICAL(&unit->spinlock);
  536. ESP_GOTO_ON_FALSE(channel_id != -1, ESP_ERR_NOT_FOUND, err, TAG, "no free channel in unit (%d,%d)", group_id, unit_id);
  537. // GPIO configuration
  538. gpio_config_t gpio_conf = {
  539. .intr_type = GPIO_INTR_DISABLE,
  540. .mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
  541. .pull_down_en = false,
  542. .pull_up_en = true,
  543. };
  544. if (config->edge_gpio_num >= 0) {
  545. gpio_conf.pin_bit_mask = 1ULL << config->edge_gpio_num;
  546. ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config edge GPIO failed");
  547. esp_rom_gpio_connect_in_signal(config->edge_gpio_num,
  548. pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].pulse_sig,
  549. config->flags.invert_edge_input);
  550. } else {
  551. // using virtual IO
  552. esp_rom_gpio_connect_in_signal(config->flags.virt_edge_io_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT,
  553. pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].pulse_sig,
  554. config->flags.invert_edge_input);
  555. }
  556. if (config->level_gpio_num >= 0) {
  557. gpio_conf.pin_bit_mask = 1ULL << config->level_gpio_num;
  558. ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config level GPIO failed");
  559. esp_rom_gpio_connect_in_signal(config->level_gpio_num,
  560. pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].control_sig,
  561. config->flags.invert_level_input);
  562. } else {
  563. // using virtual IO
  564. esp_rom_gpio_connect_in_signal(config->flags.virt_level_io_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT,
  565. pcnt_periph_signals.groups[group_id].units[unit_id].channels[channel_id].control_sig,
  566. config->flags.invert_level_input);
  567. }
  568. channel->channel_id = channel_id;
  569. channel->unit = unit;
  570. channel->edge_gpio_num = config->edge_gpio_num;
  571. channel->level_gpio_num = config->level_gpio_num;
  572. ESP_LOGD(TAG, "new pcnt channel(%d,%d,%d) at %p", group_id, unit_id, channel_id, channel);
  573. *ret_chan = channel;
  574. return ESP_OK;
  575. err:
  576. if (channel) {
  577. free(channel);
  578. }
  579. return ret;
  580. }
  581. esp_err_t pcnt_del_channel(pcnt_channel_handle_t chan)
  582. {
  583. ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  584. pcnt_unit_t *unit = chan->unit;
  585. pcnt_group_t *group = unit->group;
  586. int group_id = group->group_id;
  587. int unit_id = unit->unit_id;
  588. int channel_id = chan->channel_id;
  589. portENTER_CRITICAL(&unit->spinlock);
  590. unit->channels[channel_id] = NULL;
  591. portEXIT_CRITICAL(&unit->spinlock);
  592. if (chan->level_gpio_num >= 0) {
  593. gpio_reset_pin(chan->level_gpio_num);
  594. }
  595. if (chan->edge_gpio_num >= 0) {
  596. gpio_reset_pin(chan->edge_gpio_num);
  597. }
  598. free(chan);
  599. ESP_LOGD(TAG, "del pcnt channel(%d,%d,%d)", group_id, unit_id, channel_id);
  600. return ESP_OK;
  601. }
  602. esp_err_t pcnt_channel_set_edge_action(pcnt_channel_handle_t chan, pcnt_channel_edge_action_t pos_act, pcnt_channel_edge_action_t neg_act)
  603. {
  604. pcnt_group_t *group = NULL;
  605. ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  606. pcnt_unit_t *unit = chan->unit;
  607. group = unit->group;
  608. // mode control bits are mixed with other PCNT control bits in a same register
  609. portENTER_CRITICAL(&unit->spinlock);
  610. pcnt_ll_set_edge_action(group->hal.dev, unit->unit_id, chan->channel_id, pos_act, neg_act);
  611. portEXIT_CRITICAL(&unit->spinlock);
  612. return ESP_OK;
  613. }
  614. esp_err_t pcnt_channel_set_level_action(pcnt_channel_handle_t chan, pcnt_channel_level_action_t high_act, pcnt_channel_level_action_t low_act)
  615. {
  616. pcnt_group_t *group = NULL;
  617. ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
  618. pcnt_unit_t *unit = chan->unit;
  619. group = unit->group;
  620. // mode control bits are mixed with other PCNT control bits in a same register
  621. portENTER_CRITICAL(&unit->spinlock);
  622. pcnt_ll_set_level_action(group->hal.dev, unit->unit_id, chan->channel_id, high_act, low_act);
  623. portEXIT_CRITICAL(&unit->spinlock);
  624. return ESP_OK;
  625. }
  626. static pcnt_group_t *pcnt_acquire_group_handle(int group_id)
  627. {
  628. bool new_group = false;
  629. pcnt_group_t *group = NULL;
  630. // prevent install pcnt group concurrently
  631. _lock_acquire(&s_platform.mutex);
  632. if (!s_platform.groups[group_id]) {
  633. group = heap_caps_calloc(1, sizeof(pcnt_group_t), PCNT_MEM_ALLOC_CAPS);
  634. if (group) {
  635. new_group = true;
  636. s_platform.groups[group_id] = group; // register to platform
  637. // initialize pcnt group members
  638. group->group_id = group_id;
  639. group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
  640. // enable APB access pcnt registers
  641. periph_module_enable(pcnt_periph_signals.groups[group_id].module);
  642. periph_module_reset(pcnt_periph_signals.groups[group_id].module);
  643. // initialize HAL context
  644. pcnt_hal_init(&group->hal, group_id);
  645. }
  646. } else {
  647. group = s_platform.groups[group_id];
  648. }
  649. if (group) {
  650. // someone acquired the group handle means we have a new object that refer to this group
  651. s_platform.group_ref_counts[group_id]++;
  652. }
  653. _lock_release(&s_platform.mutex);
  654. if (new_group) {
  655. ESP_LOGD(TAG, "new group (%d) at %p", group_id, group);
  656. }
  657. return group;
  658. }
  659. static void pcnt_release_group_handle(pcnt_group_t *group)
  660. {
  661. int group_id = group->group_id;
  662. bool do_deinitialize = false;
  663. _lock_acquire(&s_platform.mutex);
  664. s_platform.group_ref_counts[group_id]--;
  665. if (s_platform.group_ref_counts[group_id] == 0) {
  666. assert(s_platform.groups[group_id]);
  667. do_deinitialize = true;
  668. s_platform.groups[group_id] = NULL; // deregister from platform
  669. periph_module_disable(pcnt_periph_signals.groups[group_id].module);
  670. }
  671. _lock_release(&s_platform.mutex);
  672. if (do_deinitialize) {
  673. free(group);
  674. ESP_LOGD(TAG, "del group (%d)", group_id);
  675. }
  676. }
  677. IRAM_ATTR static void pcnt_default_isr(void *args)
  678. {
  679. bool need_yield = false;
  680. pcnt_unit_t *unit = (pcnt_unit_t *)args;
  681. int unit_id = unit->unit_id;
  682. pcnt_group_t *group = unit->group;
  683. pcnt_watch_cb_t on_reach = unit->on_reach;
  684. uint32_t intr_status = pcnt_ll_get_intr_status(group->hal.dev);
  685. if (intr_status & PCNT_LL_UNIT_WATCH_EVENT(unit_id)) {
  686. pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
  687. // points watcher event
  688. uint32_t event_status = pcnt_ll_get_event_status(group->hal.dev, unit_id);
  689. // iter on each event_id
  690. while (event_status) {
  691. int event_id = __builtin_ffs(event_status) - 1;
  692. event_status &= (event_status - 1); // clear the right most bit
  693. portENTER_CRITICAL_ISR(&unit->spinlock);
  694. if (unit->flags.accum_count) {
  695. if (event_id == PCNT_LL_WATCH_EVENT_LOW_LIMIT) {
  696. unit->accum_value += unit->low_limit;
  697. } else if (event_id == PCNT_LL_WATCH_EVENT_HIGH_LIMIT) {
  698. unit->accum_value += unit->high_limit;
  699. }
  700. }
  701. portEXIT_CRITICAL_ISR(&unit->spinlock);
  702. // invoked user registered callback
  703. if (on_reach) {
  704. pcnt_watch_event_data_t edata = {
  705. .watch_point_value = unit->watchers[event_id].watch_point_value,
  706. .zero_cross_mode = pcnt_ll_get_zero_cross_mode(group->hal.dev, unit_id),
  707. };
  708. if (on_reach(unit, &edata, unit->user_data)) {
  709. // check if we need to yield for high priority task
  710. need_yield = true;
  711. }
  712. }
  713. }
  714. }
  715. if (need_yield) {
  716. portYIELD_FROM_ISR();
  717. }
  718. }