hw_timer.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /******************************************************************************
  2. * Copyright 2013-2014 Espressif Systems (Wuxi)
  3. *
  4. * FileName: hw_timer.c
  5. *
  6. * Description: hw_timer driver
  7. *
  8. * Modification history:
  9. * 2014/5/1, v1.0 create this file.
  10. *
  11. * Adapted for NodeMCU 2016
  12. *
  13. * The owner parameter should be a unique value per module using this API
  14. * It could be a pointer to a bit of data or code
  15. * e.g. #define OWNER ((os_param_t) module_init)
  16. * where module_init is a function. For builtin modules, it might be
  17. * a small numeric value that is known not to clash.
  18. *******************************************************************************/
  19. #include "platform.h"
  20. #include "c_stdio.h"
  21. #include "c_stdlib.h"
  22. #include "ets_sys.h"
  23. #include "os_type.h"
  24. #include "osapi.h"
  25. #include "hw_timer.h"
  26. //#define DEBUG_HW_TIMER
  27. //#undef NODE_DBG
  28. //#define NODE_DBG dbg_printf
  29. #define FRC1_ENABLE_TIMER BIT7
  30. #define FRC1_AUTO_LOAD BIT6
  31. //TIMER PREDIVIDED MODE
  32. typedef enum {
  33. DIVIDED_BY_1 = 0, //timer clock
  34. DIVIDED_BY_16 = 4, //divided by 16
  35. DIVIDED_BY_256 = 8, //divided by 256
  36. } TIMER_PREDIVIDED_MODE;
  37. typedef enum { //timer interrupt mode
  38. TM_LEVEL_INT = 1, // level interrupt
  39. TM_EDGE_INT = 0, //edge interrupt
  40. } TIMER_INT_MODE;
  41. /*
  42. * This represents a single user of the timer functionality. It is keyed by the owner
  43. * field.
  44. */
  45. typedef struct _timer_user {
  46. struct _timer_user *next;
  47. bool autoload;
  48. int32_t delay; // once on the active list, this is difference in delay from the preceding element
  49. int32_t autoload_delay;
  50. uint32_t expected_interrupt_time;
  51. os_param_t owner;
  52. os_param_t callback_arg;
  53. void (* user_hw_timer_cb)(os_param_t);
  54. #ifdef DEBUG_HW_TIMER
  55. int cb_count;
  56. #endif
  57. } timer_user;
  58. /*
  59. * There are two lists of timer_user blocks. The active list are those which are waiting
  60. * for timeouts to happen, and the inactive list contains idle blocks. Unfortunately
  61. * there isn't a way to clean up the inactive blocks as some modules call the
  62. * close method from interrupt level.
  63. */
  64. static timer_user *active;
  65. static timer_user *inactive;
  66. /*
  67. * There are a fair number of places when interrupts need to be disabled as many of
  68. * the methods can be called from interrupt level. The lock/unlock calls support
  69. * multiple LOCKs and then the same number of UNLOCKs are required to re-enable
  70. * interrupts. This is imolemeted by counting the number of times that lock is called.
  71. */
  72. static uint8_t lock_count;
  73. static uint8_t timer_running;
  74. static uint32_t time_next_expiry;
  75. static int32_t last_timer_load;
  76. #define LOCK() do { ets_intr_lock(); lock_count++; } while (0)
  77. #define UNLOCK() if (--lock_count == 0) ets_intr_unlock()
  78. /*
  79. * To start a timer, you write to FRCI_LOAD_ADDRESS, and that starts the counting
  80. * down. When it reaches zero, the interrupt fires -- but the counting continues.
  81. * The counter is 23 bits wide. The current value of the counter can be read
  82. * at FRC1_COUNT_ADDRESS. The unit is 200ns, and so it takes somewhat over a second
  83. * to wrap the counter.
  84. */
  85. #ifdef DEBUG_HW_TIMER
  86. void ICACHE_RAM_ATTR hw_timer_debug() {
  87. dbg_printf("timer_running=%d\n", timer_running);
  88. timer_user *tu;
  89. for (tu = active; tu; tu = tu->next) {
  90. dbg_printf("Owner: 0x%x, delay=%d, autoload=%d, autoload_delay=%d, cb_count=%d\n",
  91. tu->owner, tu->delay, tu->autoload, tu->autoload_delay, tu->cb_count);
  92. }
  93. }
  94. #endif
  95. static void ICACHE_RAM_ATTR set_timer(int delay, const char *caller) {
  96. if (delay < 1) {
  97. delay = 1;
  98. }
  99. int32_t time_left = (RTC_REG_READ(FRC1_COUNT_ADDRESS)) & ((1 << 23) - 1);
  100. RTC_REG_WRITE(FRC1_LOAD_ADDRESS, delay);
  101. if (time_left > last_timer_load) {
  102. // We have missed the interrupt
  103. time_left -= 1 << 23;
  104. }
  105. NODE_DBG("%s(%x): time_next=%d, left=%d (load=%d), delay=%d => %d\n", caller, active->owner, time_next_expiry, time_left, last_timer_load, delay, time_next_expiry - time_left + delay);
  106. time_next_expiry = time_next_expiry - time_left + delay;
  107. last_timer_load = delay;
  108. timer_running = 1;
  109. }
  110. static void ICACHE_RAM_ATTR adjust_root() {
  111. // Can only ge called with interrupts disabled
  112. // change the initial active delay so that relative stuff still works
  113. // Also, set the last_timer_load to be now
  114. int32_t time_left = (RTC_REG_READ(FRC1_COUNT_ADDRESS)) & ((1 << 23) - 1);
  115. if (time_left > last_timer_load) {
  116. // We have missed the interrupt
  117. time_left -= 1 << 23;
  118. }
  119. if (active && timer_running) {
  120. active->delay = time_left;
  121. }
  122. if (active) {
  123. NODE_DBG("adjust(%x): time_left=%d (last_load=%d)\n", active->owner, time_left, last_timer_load);
  124. } else {
  125. NODE_DBG("adjust: time_left=%d (last_load=%d)\n", time_left, last_timer_load);
  126. }
  127. last_timer_load = time_left;
  128. }
  129. /*
  130. * Find the timer_user block for this owner. This just returns
  131. * a pointer to the block, or NULL.
  132. */
  133. static timer_user * ICACHE_RAM_ATTR find_tu(os_param_t owner) {
  134. // Try the inactive chain first
  135. timer_user **p;
  136. LOCK();
  137. for (p = &inactive; *p; p = &((*p)->next)) {
  138. if ((*p)->owner == owner) {
  139. timer_user *result = *p;
  140. UNLOCK();
  141. return result;
  142. }
  143. }
  144. for (p = &active; *p; p = &((*p)->next)) {
  145. if ((*p)->owner == owner) {
  146. timer_user *result = *p;
  147. UNLOCK();
  148. return result;
  149. }
  150. }
  151. UNLOCK();
  152. return NULL;
  153. }
  154. /*
  155. * Find the timer_user block for this owner. This just returns
  156. * a pointer to the block, or NULL. If it finds the block, then it is
  157. * removed from whichever chain it is on. Note that this may require
  158. * triggering a timer.
  159. */
  160. static timer_user * ICACHE_RAM_ATTR find_tu_and_remove(os_param_t owner) {
  161. // Try the inactive chain first
  162. timer_user **p;
  163. LOCK();
  164. for (p = &inactive; *p; p = &((*p)->next)) {
  165. if ((*p)->owner == owner) {
  166. timer_user *result = *p;
  167. *p = result->next;
  168. result->next = NULL;
  169. UNLOCK();
  170. return result;
  171. }
  172. }
  173. for (p = &active; *p; p = &((*p)->next)) {
  174. if ((*p)->owner == owner) {
  175. timer_user *result = *p;
  176. bool need_to_reset = (result == active) && result->next;
  177. if (need_to_reset) {
  178. adjust_root();
  179. }
  180. // Increase the delay on the next element
  181. if (result->next) {
  182. result->next->delay += result->delay;
  183. }
  184. // Cut out of chain
  185. *p = result->next;
  186. result->next = NULL;
  187. if (need_to_reset) {
  188. set_timer(active->delay, "find_tu");
  189. }
  190. UNLOCK();
  191. return result;
  192. }
  193. }
  194. UNLOCK();
  195. return NULL;
  196. }
  197. /*
  198. * This inserts a timer_user block into the active chain. This is a sightly
  199. * complex process as it can involve triggering a timer load.
  200. */
  201. static void ICACHE_RAM_ATTR insert_active_tu(timer_user *tu) {
  202. timer_user **p;
  203. LOCK();
  204. tu->expected_interrupt_time = time_next_expiry - last_timer_load + tu->delay;
  205. for (p = &active; *p; p = &((*p)->next)) {
  206. if ((*p)->delay >= tu->delay) {
  207. break;
  208. }
  209. tu->delay -= (*p)->delay;
  210. }
  211. if (*p) {
  212. (*p)->delay -= tu->delay;
  213. }
  214. tu->next = *p;
  215. *p = tu;
  216. if (tu == active) {
  217. // We have a new leader
  218. set_timer(active->delay, "insert_active");
  219. }
  220. UNLOCK();
  221. }
  222. /******************************************************************************
  223. * FunctionName : platform_hw_timer_arm_ticks
  224. * Description : set a trigger timer delay for this timer.
  225. * Parameters : os_param_t owner
  226. * uint32 ticks :
  227. * Returns : true if it worked
  228. *******************************************************************************/
  229. bool ICACHE_RAM_ATTR platform_hw_timer_arm_ticks(os_param_t owner, uint32_t ticks)
  230. {
  231. timer_user *tu = find_tu_and_remove(owner);
  232. if (!tu) {
  233. return false;
  234. }
  235. tu->delay = ticks;
  236. tu->autoload_delay = ticks;
  237. NODE_DBG("arm(%x): ticks=%d\n", owner, ticks);
  238. LOCK();
  239. adjust_root();
  240. insert_active_tu(tu);
  241. UNLOCK();
  242. return true;
  243. }
  244. /******************************************************************************
  245. * FunctionName : platform_hw_timer_arm_us
  246. * Description : set a trigger timer delay for this timer.
  247. * Parameters : os_param_t owner
  248. * uint32 microseconds :
  249. * in autoload mode
  250. * 50 ~ 0x7fffff; for FRC1 source.
  251. * 100 ~ 0x7fffff; for NMI source.
  252. * in non autoload mode:
  253. * 10 ~ 0x7fffff;
  254. * Returns : true if it worked
  255. *******************************************************************************/
  256. bool ICACHE_RAM_ATTR platform_hw_timer_arm_us(os_param_t owner, uint32_t microseconds)
  257. {
  258. return platform_hw_timer_arm_ticks(owner, US_TO_RTC_TIMER_TICKS(microseconds));
  259. }
  260. /******************************************************************************
  261. * FunctionName : platform_hw_timer_set_func
  262. * Description : set the func, when trigger timer is up.
  263. * Parameters : os_param_t owner
  264. * void (* user_hw_timer_cb_set)(os_param_t):
  265. timer callback function
  266. * os_param_t arg
  267. * Returns : true if it worked
  268. *******************************************************************************/
  269. bool platform_hw_timer_set_func(os_param_t owner, void (* user_hw_timer_cb_set)(os_param_t), os_param_t arg)
  270. {
  271. timer_user *tu = find_tu(owner);
  272. if (!tu) {
  273. return false;
  274. }
  275. tu->callback_arg = arg;
  276. tu->user_hw_timer_cb = user_hw_timer_cb_set;
  277. NODE_DBG("set-CB(%x): %x, %x\n", tu->owner, tu->user_hw_timer_cb, tu->callback_arg);
  278. return true;
  279. }
  280. /*
  281. * This is the timer ISR. It has to find the timer that was running and trigger the callback
  282. * for that timer. By this stage, the next timer may have expired as well, and so the process
  283. * iterates. Note that if there is an autoload timer, then it should be restarted immediately.
  284. * Also, the callbacks typically do re-arm the timer, so we have to be careful not to
  285. * assume that nothing changes during the callback.
  286. */
  287. static void ICACHE_RAM_ATTR hw_timer_isr_cb(void *arg)
  288. {
  289. bool keep_going = true;
  290. adjust_root();
  291. timer_running = 0;
  292. while (keep_going && active) {
  293. keep_going = false;
  294. timer_user *fired = active;
  295. active = fired->next;
  296. if (fired->autoload) {
  297. fired->expected_interrupt_time += fired->autoload_delay;
  298. fired->delay = fired->expected_interrupt_time - (time_next_expiry - last_timer_load);
  299. insert_active_tu(fired);
  300. if (active->delay <= 0) {
  301. keep_going = true;
  302. }
  303. } else {
  304. fired->next = inactive;
  305. inactive = fired;
  306. if (active) {
  307. active->delay += fired->delay;
  308. if (active->delay <= 0) {
  309. keep_going = true;
  310. }
  311. }
  312. }
  313. if (fired->user_hw_timer_cb) {
  314. #ifdef DEBUG_HW_TIMER
  315. fired->cb_count++;
  316. #endif
  317. NODE_DBG("CB(%x): %x, %x\n", fired->owner, fired->user_hw_timer_cb, fired->callback_arg);
  318. (*(fired->user_hw_timer_cb))(fired->callback_arg);
  319. }
  320. }
  321. if (active && !timer_running) {
  322. set_timer(active->delay, "isr");
  323. }
  324. }
  325. static void ICACHE_RAM_ATTR hw_timer_nmi_cb(void)
  326. {
  327. hw_timer_isr_cb(NULL);
  328. }
  329. /******************************************************************************
  330. * FunctionName : platform_hw_timer_get_delay_ticks
  331. * Description : figure out how long since th last timer interrupt
  332. * Parameters : os_param_t owner
  333. * Returns : the number of ticks
  334. *******************************************************************************/
  335. uint32_t ICACHE_RAM_ATTR platform_hw_timer_get_delay_ticks(os_param_t owner)
  336. {
  337. timer_user *tu = find_tu(owner);
  338. if (!tu) {
  339. return 0;
  340. }
  341. LOCK();
  342. adjust_root();
  343. UNLOCK();
  344. int ret = (time_next_expiry - last_timer_load) - tu->expected_interrupt_time;
  345. if (ret < 0) {
  346. NODE_DBG("delay ticks = %d, last_timer_load=%d, tu->expected_int=%d, next_exp=%d\n", ret, last_timer_load, tu->expected_interrupt_time, time_next_expiry);
  347. }
  348. return ret < 0 ? 0 : ret;
  349. }
  350. /******************************************************************************
  351. * FunctionName : platform_hw_timer_init
  352. * Description : initialize the hardware isr timer
  353. * Parameters : os_param_t owner
  354. * FRC1_TIMER_SOURCE_TYPE source_type:
  355. * FRC1_SOURCE, timer use frc1 isr as isr source.
  356. * NMI_SOURCE, timer use nmi isr as isr source.
  357. * bool autoload:
  358. * 0, not autoload,
  359. * 1, autoload mode,
  360. * Returns : true if it worked
  361. *******************************************************************************/
  362. bool platform_hw_timer_init(os_param_t owner, FRC1_TIMER_SOURCE_TYPE source_type, bool autoload)
  363. {
  364. timer_user *tu = find_tu_and_remove(owner);
  365. if (!tu) {
  366. tu = (timer_user *) c_malloc(sizeof(*tu));
  367. if (!tu) {
  368. return false;
  369. }
  370. memset(tu, 0, sizeof(*tu));
  371. tu->owner = owner;
  372. }
  373. tu->autoload = autoload;
  374. if (!active && !inactive) {
  375. RTC_REG_WRITE(FRC1_CTRL_ADDRESS,
  376. DIVIDED_BY_16 | FRC1_ENABLE_TIMER | TM_EDGE_INT);
  377. ETS_FRC_TIMER1_INTR_ATTACH(hw_timer_isr_cb, NULL);
  378. TM1_EDGE_INT_ENABLE();
  379. ETS_FRC1_INTR_ENABLE();
  380. }
  381. LOCK();
  382. tu->next = inactive;
  383. inactive = tu;
  384. UNLOCK();
  385. return true;
  386. }
  387. /******************************************************************************
  388. * FunctionName : platform_hw_timer_close
  389. * Description : ends use of the hardware isr timer
  390. * Parameters : os_param_t owner
  391. * Returns : true if it worked
  392. *******************************************************************************/
  393. bool ICACHE_RAM_ATTR platform_hw_timer_close(os_param_t owner)
  394. {
  395. timer_user *tu = find_tu_and_remove(owner);
  396. if (tu) {
  397. LOCK();
  398. tu->next = inactive;
  399. inactive = tu;
  400. UNLOCK();
  401. }
  402. // This will never actually run....
  403. if (!active && !inactive) {
  404. /* Set no reload mode */
  405. RTC_REG_WRITE(FRC1_CTRL_ADDRESS,
  406. DIVIDED_BY_16 | TM_EDGE_INT);
  407. TM1_EDGE_INT_DISABLE();
  408. ETS_FRC1_INTR_DISABLE();
  409. }
  410. return true;
  411. }