mbed_ticker_api.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /* mbed Microcontroller Library
  2. * Copyright (c) 2015 ARM Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <stdio.h>
  17. #include <stddef.h>
  18. #include "hal/ticker_api.h"
  19. #include "platform/mbed_critical.h"
  20. #include "mbed_assert.h"
  21. static void schedule_interrupt(const ticker_data_t *const ticker);
  22. static void update_present_time(const ticker_data_t *const ticker);
  23. /*
  24. * Initialize a ticker instance.
  25. */
  26. static void initialize(const ticker_data_t *ticker)
  27. {
  28. // return if the queue has already been initialized, in that case the
  29. // interface used by the queue is already initialized.
  30. if (ticker->queue->initialized) {
  31. return;
  32. }
  33. ticker->interface->init();
  34. const ticker_info_t *info = ticker->interface->get_info();
  35. uint32_t frequency = info->frequency;
  36. if (info->frequency == 0) {
  37. MBED_ASSERT(0);
  38. frequency = 1000000;
  39. }
  40. uint8_t frequency_shifts = 0;
  41. for (uint8_t i = 31; i > 0; --i) {
  42. if ((1 << i) == frequency) {
  43. frequency_shifts = i;
  44. break;
  45. }
  46. }
  47. uint32_t bits = info->bits;
  48. if ((info->bits > 32) || (info->bits < 4)) {
  49. MBED_ASSERT(0);
  50. bits = 32;
  51. }
  52. uint32_t max_delta = 0x7 << (bits - 4); // 7/16th
  53. uint64_t max_delta_us =
  54. ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency;
  55. ticker->queue->event_handler = NULL;
  56. ticker->queue->head = NULL;
  57. ticker->queue->tick_last_read = ticker->interface->read();
  58. ticker->queue->tick_remainder = 0;
  59. ticker->queue->frequency = frequency;
  60. ticker->queue->frequency_shifts = frequency_shifts;
  61. ticker->queue->bitmask = ((uint64_t)1 << bits) - 1;
  62. ticker->queue->max_delta = max_delta;
  63. ticker->queue->max_delta_us = max_delta_us;
  64. ticker->queue->present_time = 0;
  65. ticker->queue->dispatching = false;
  66. ticker->queue->initialized = true;
  67. update_present_time(ticker);
  68. schedule_interrupt(ticker);
  69. }
  70. /**
  71. * Set the event handler function of a ticker instance.
  72. */
  73. static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
  74. {
  75. ticker->queue->event_handler = handler;
  76. }
  77. /*
  78. * Convert a 32 bit timestamp into a 64 bit timestamp.
  79. *
  80. * A 64 bit timestamp is used as the point of time of reference while the
  81. * timestamp to convert is relative to this point of time.
  82. *
  83. * The lower 32 bits of the timestamp returned will be equal to the timestamp to
  84. * convert.
  85. *
  86. * If the timestamp to convert is less than the lower 32 bits of the time
  87. * reference then the timestamp to convert is seen as an overflowed value and
  88. * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit
  89. * of the reference point + 1.
  90. * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the
  91. * reference point.
  92. *
  93. * @param ref: The 64 bit timestamp of reference.
  94. * @param timestamp: The timestamp to convert.
  95. */
  96. static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp)
  97. {
  98. bool overflow = timestamp < ((timestamp_t) ref) ? true : false;
  99. us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp;
  100. if (overflow) {
  101. result += (1ULL << 32);
  102. }
  103. return result;
  104. }
  105. /**
  106. * Update the present timestamp value of a ticker.
  107. */
  108. static void update_present_time(const ticker_data_t *const ticker)
  109. {
  110. ticker_event_queue_t *queue = ticker->queue;
  111. uint32_t ticker_time = ticker->interface->read();
  112. if (ticker_time == ticker->queue->tick_last_read) {
  113. // No work to do
  114. return;
  115. }
  116. uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask;
  117. queue->tick_last_read = ticker_time;
  118. uint64_t elapsed_us;
  119. if (1000000 == queue->frequency) {
  120. // Optimized for 1MHz
  121. elapsed_us = elapsed_ticks;
  122. } else if (0 != queue->frequency_shifts) {
  123. // Optimized for frequencies divisible by 2
  124. uint64_t us_x_ticks = elapsed_ticks * 1000000;
  125. elapsed_us = us_x_ticks >> queue->frequency_shifts;
  126. // Update remainder
  127. queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts);
  128. if (queue->tick_remainder >= queue->frequency) {
  129. elapsed_us += 1;
  130. queue->tick_remainder -= queue->frequency;
  131. }
  132. } else {
  133. // General case
  134. uint64_t us_x_ticks = elapsed_ticks * 1000000;
  135. elapsed_us = us_x_ticks / queue->frequency;
  136. // Update remainder
  137. queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency;
  138. if (queue->tick_remainder >= queue->frequency) {
  139. elapsed_us += 1;
  140. queue->tick_remainder -= queue->frequency;
  141. }
  142. }
  143. // Update current time
  144. queue->present_time += elapsed_us;
  145. }
  146. /**
  147. * Given the absolute timestamp compute the hal tick timestamp.
  148. */
  149. static timestamp_t compute_tick(const ticker_data_t *const ticker, us_timestamp_t timestamp)
  150. {
  151. ticker_event_queue_t *queue = ticker->queue;
  152. us_timestamp_t delta_us = timestamp - queue->present_time;
  153. timestamp_t delta = ticker->queue->max_delta;
  154. if (delta_us <= ticker->queue->max_delta_us) {
  155. // Checking max_delta_us ensures the operation will not overflow
  156. if (1000000 == queue->frequency) {
  157. // Optimized for 1MHz
  158. delta = delta_us;
  159. if (delta > ticker->queue->max_delta) {
  160. delta = ticker->queue->max_delta;
  161. }
  162. } else if (0 != queue->frequency_shifts) {
  163. // Optimized frequencies divisible by 2
  164. delta = (delta_us << ticker->queue->frequency_shifts) / 1000000;
  165. if (delta > ticker->queue->max_delta) {
  166. delta = ticker->queue->max_delta;
  167. }
  168. } else {
  169. // General case
  170. delta = delta_us * queue->frequency / 1000000;
  171. if (delta > ticker->queue->max_delta) {
  172. delta = ticker->queue->max_delta;
  173. }
  174. }
  175. }
  176. return (queue->tick_last_read + delta) & queue->bitmask;
  177. }
  178. /**
  179. * Return 1 if the tick has incremented to or past match_tick, otherwise 0.
  180. */
  181. int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick)
  182. {
  183. if (match_tick > prev_tick) {
  184. return (cur_tick >= match_tick) || (cur_tick < prev_tick);
  185. } else {
  186. return (cur_tick < prev_tick) && (cur_tick >= match_tick);
  187. }
  188. }
  189. /**
  190. * Compute the time when the interrupt has to be triggered and schedule it.
  191. *
  192. * If there is no event in the queue or the next event to execute is in more
  193. * than ticker.queue.max_delta ticks from now then the ticker irq will be
  194. * scheduled in ticker.queue.max_delta ticks. Otherwise the irq will be
  195. * scheduled to happen when the running counter reach the timestamp of the
  196. * first event in the queue.
  197. *
  198. * @note If there is no event in the queue then the interrupt is scheduled to
  199. * in ticker.queue.max_delta. This is necessary to keep track
  200. * of the timer overflow.
  201. */
  202. static void schedule_interrupt(const ticker_data_t *const ticker)
  203. {
  204. ticker_event_queue_t *queue = ticker->queue;
  205. if (ticker->queue->dispatching) {
  206. // Don't schedule the next interrupt until dispatching is
  207. // finished. This prevents repeated calls to interface->set_interrupt
  208. return;
  209. }
  210. update_present_time(ticker);
  211. if (ticker->queue->head) {
  212. us_timestamp_t present = ticker->queue->present_time;
  213. us_timestamp_t match_time = ticker->queue->head->timestamp;
  214. // if the event at the head of the queue is in the past then schedule
  215. // it immediately.
  216. if (match_time <= present) {
  217. ticker->interface->fire_interrupt();
  218. return;
  219. }
  220. timestamp_t match_tick = compute_tick(ticker, match_time);
  221. // The time has been checked to be future, but it could still round
  222. // to the last tick as a result of us to ticks conversion
  223. if (match_tick == queue->tick_last_read) {
  224. // Match time has already expired so fire immediately
  225. ticker->interface->fire_interrupt();
  226. return;
  227. }
  228. ticker->interface->set_interrupt(match_tick);
  229. timestamp_t cur_tick = ticker->interface->read();
  230. if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) {
  231. ticker->interface->fire_interrupt();
  232. }
  233. } else {
  234. uint32_t match_tick =
  235. (queue->tick_last_read + queue->max_delta) & queue->bitmask;
  236. ticker->interface->set_interrupt(match_tick);
  237. }
  238. }
  239. void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
  240. {
  241. initialize(ticker);
  242. core_util_critical_section_enter();
  243. set_handler(ticker, handler);
  244. core_util_critical_section_exit();
  245. }
  246. void ticker_irq_handler(const ticker_data_t *const ticker)
  247. {
  248. core_util_critical_section_enter();
  249. ticker->interface->clear_interrupt();
  250. /* Go through all the pending TimerEvents */
  251. ticker->queue->dispatching = true;
  252. while (1) {
  253. if (ticker->queue->head == NULL) {
  254. break;
  255. }
  256. // update the current timestamp used by the queue
  257. update_present_time(ticker);
  258. if (ticker->queue->head->timestamp <= ticker->queue->present_time) {
  259. // This event was in the past:
  260. // point to the following one and execute its handler
  261. ticker_event_t *p = ticker->queue->head;
  262. ticker->queue->head = ticker->queue->head->next;
  263. if (ticker->queue->event_handler != NULL) {
  264. (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events
  265. }
  266. /* Note: We continue back to examining the head because calling the
  267. * event handler may have altered the chain of pending events. */
  268. } else {
  269. break;
  270. }
  271. }
  272. ticker->queue->dispatching = false;
  273. schedule_interrupt(ticker);
  274. core_util_critical_section_exit();
  275. }
  276. void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id)
  277. {
  278. core_util_critical_section_enter();
  279. // update the current timestamp
  280. update_present_time(ticker);
  281. us_timestamp_t absolute_timestamp = convert_timestamp(
  282. ticker->queue->present_time,
  283. timestamp
  284. );
  285. // defer to ticker_insert_event_us
  286. ticker_insert_event_us(
  287. ticker,
  288. obj, absolute_timestamp, id
  289. );
  290. core_util_critical_section_exit();
  291. }
  292. void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id)
  293. {
  294. core_util_critical_section_enter();
  295. // update the current timestamp
  296. update_present_time(ticker);
  297. // initialise our data
  298. obj->timestamp = timestamp;
  299. obj->id = id;
  300. /* Go through the list until we either reach the end, or find
  301. an element this should come before (which is possibly the
  302. head). */
  303. ticker_event_t *prev = NULL, *p = ticker->queue->head;
  304. while (p != NULL) {
  305. /* check if we come before p */
  306. if (timestamp < p->timestamp) {
  307. break;
  308. }
  309. /* go to the next element */
  310. prev = p;
  311. p = p->next;
  312. }
  313. /* if we're at the end p will be NULL, which is correct */
  314. obj->next = p;
  315. /* if prev is NULL we're at the head */
  316. if (prev == NULL) {
  317. ticker->queue->head = obj;
  318. schedule_interrupt(ticker);
  319. } else {
  320. prev->next = obj;
  321. }
  322. core_util_critical_section_exit();
  323. }
  324. void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj)
  325. {
  326. core_util_critical_section_enter();
  327. // remove this object from the list
  328. if (ticker->queue->head == obj) {
  329. // first in the list, so just drop me
  330. ticker->queue->head = obj->next;
  331. schedule_interrupt(ticker);
  332. } else {
  333. // find the object before me, then drop me
  334. ticker_event_t *p = ticker->queue->head;
  335. while (p != NULL) {
  336. if (p->next == obj) {
  337. p->next = obj->next;
  338. break;
  339. }
  340. p = p->next;
  341. }
  342. }
  343. core_util_critical_section_exit();
  344. }
  345. timestamp_t ticker_read(const ticker_data_t *const ticker)
  346. {
  347. return ticker_read_us(ticker);
  348. }
  349. us_timestamp_t ticker_read_us(const ticker_data_t *const ticker)
  350. {
  351. initialize(ticker);
  352. core_util_critical_section_enter();
  353. update_present_time(ticker);
  354. core_util_critical_section_exit();
  355. return ticker->queue->present_time;
  356. }
  357. int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp)
  358. {
  359. int ret = 0;
  360. /* if head is NULL, there are no pending events */
  361. core_util_critical_section_enter();
  362. if (data->queue->head != NULL) {
  363. *timestamp = data->queue->head->timestamp;
  364. ret = 1;
  365. }
  366. core_util_critical_section_exit();
  367. return ret;
  368. }