pulse_cnt.c 28 KB

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