Teensy 4.1 core updated for C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
9.0KB

  1. /* EventResponder - Simple event-based programming for Arduino
  2. * Copyright 2017 Paul Stoffregen
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining
  5. * a copy of this software and associated documentation files (the
  6. * "Software"), to deal in the Software without restriction, including
  7. * without limitation the rights to use, copy, modify, merge, publish,
  8. * distribute, sublicense, and/or sell copies of the Software, and to
  9. * permit persons to whom the Software is furnished to do so, subject to
  10. * the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be
  13. * included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  19. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  20. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. /* EventResponder is an experimental API, almost certain to
  25. * incompatibly change as it develops. Please understand any
  26. * programs you write now using EventResponder may need to be
  27. * updated as EventResponder develops.
  28. *
  29. * Please post EventResponder post your feedback here:
  30. * https://forum.pjrc.com/threads/44723-Arduino-Events
  31. */
  32. #include <Arduino.h>
  33. #include "EventResponder.h"
  34. EventResponder * EventResponder::firstYield = nullptr;
  35. EventResponder * EventResponder::lastYield = nullptr;
  36. EventResponder * EventResponder::firstInterrupt = nullptr;
  37. EventResponder * EventResponder::lastInterrupt = nullptr;
  38. bool EventResponder::runningFromYield = false;
  39. // TODO: interrupt disable/enable needed in many places!!!
  40. // BUGBUG: See if file name order makes difference?
  41. uint8_t _serialEvent_default __attribute__((weak)) PROGMEM = 0 ;
  42. void EventResponder::triggerEventNotImmediate()
  43. {
  44. bool irq = disableInterrupts();
  45. if (_triggered == false) {
  46. // not already triggered
  47. if (_type == EventTypeYield) {
  48. // normal type, called from yield()
  49. if (firstYield == nullptr) {
  50. _next = nullptr;
  51. _prev = nullptr;
  52. firstYield = this;
  53. lastYield = this;
  54. } else {
  55. _next = nullptr;
  56. _prev = lastYield;
  57. _prev->_next = this;
  58. lastYield = this;
  59. }
  60. } else if (_type == EventTypeInterrupt) {
  61. // interrupt, called from software interrupt
  62. if (firstInterrupt == nullptr) {
  63. _next = nullptr;
  64. _prev = nullptr;
  65. firstInterrupt = this;
  66. lastInterrupt = this;
  67. } else {
  68. _next = nullptr;
  69. _prev = lastInterrupt;
  70. _prev->_next = this;
  71. lastInterrupt = this;
  72. }
  73. SCB_ICSR = SCB_ICSR_PENDSVSET; // set PendSV interrupt
  74. } else {
  75. // detached, easy :-)
  76. }
  77. _triggered = true;
  78. }
  79. enableInterrupts(irq);
  80. }
  81. extern "C" void pendablesrvreq_isr(void)
  82. {
  83. EventResponder::runFromInterrupt();
  84. }
  85. void EventResponder::runFromInterrupt()
  86. {
  87. while (1) {
  88. bool irq = disableInterrupts();
  89. EventResponder *first = firstInterrupt;
  90. if (first) {
  91. firstInterrupt = first->_next;
  92. if (firstInterrupt) {
  93. firstInterrupt->_prev = nullptr;
  94. } else {
  95. lastInterrupt = nullptr;
  96. }
  97. enableInterrupts(irq);
  98. first->_triggered = false;
  99. (*(first->_function))(*first);
  100. } else {
  101. enableInterrupts(irq);
  102. break;
  103. }
  104. }
  105. }
  106. bool EventResponder::clearEvent()
  107. {
  108. bool ret = false;
  109. bool irq = disableInterrupts();
  110. if (_triggered) {
  111. if (_type == EventTypeYield) {
  112. if (_prev) {
  113. _prev->_next = _next;
  114. } else {
  115. firstYield = _next;
  116. }
  117. if (_next) {
  118. _next->_prev = _prev;
  119. } else {
  120. lastYield = _prev;
  121. }
  122. } else if (_type == EventTypeInterrupt) {
  123. if (_prev) {
  124. _prev->_next = _next;
  125. } else {
  126. firstInterrupt = _next;
  127. }
  128. if (_next) {
  129. _next->_prev = _prev;
  130. } else {
  131. lastInterrupt = _prev;
  132. }
  133. }
  134. _triggered = false;
  135. ret = true;
  136. }
  137. enableInterrupts(irq);
  138. return ret;
  139. }
  140. // this detach must be called with interrupts disabled
  141. void EventResponder::detachNoInterrupts()
  142. {
  143. if (_type == EventTypeYield) {
  144. if (_triggered) {
  145. if (_prev) {
  146. _prev->_next = _next;
  147. } else {
  148. firstYield = _next;
  149. }
  150. if (_next) {
  151. _next->_prev = _prev;
  152. } else {
  153. lastYield = _prev;
  154. }
  155. }
  156. _type = EventTypeDetached;
  157. } else if (_type == EventTypeInterrupt) {
  158. if (_triggered) {
  159. if (_prev) {
  160. _prev->_next = _next;
  161. } else {
  162. firstInterrupt = _next;
  163. }
  164. if (_next) {
  165. _next->_prev = _prev;
  166. } else {
  167. lastInterrupt = _prev;
  168. }
  169. }
  170. _type = EventTypeDetached;
  171. }
  172. }
  173. //-------------------------------------------------------------
  174. MillisTimer * MillisTimer::listWaiting = nullptr;
  175. MillisTimer * MillisTimer::listActive = nullptr;
  176. void MillisTimer::begin(unsigned long milliseconds, EventResponderRef event)
  177. {
  178. if (_state != TimerOff) end();
  179. if (!milliseconds) return;
  180. _event = &event;
  181. _ms = (milliseconds > 2)? milliseconds-2 : 0;
  182. _reload = 0;
  183. addToWaitingList();
  184. }
  185. void MillisTimer::beginRepeating(unsigned long milliseconds, EventResponderRef event)
  186. {
  187. if (_state != TimerOff) end();
  188. if (!milliseconds) return;
  189. _event = &event;
  190. _ms = (milliseconds > 2)? milliseconds-2 : 0;
  191. _reload = milliseconds;
  192. addToWaitingList();
  193. }
  194. void MillisTimer::addToWaitingList()
  195. {
  196. _prev = nullptr;
  197. bool irq = disableTimerInterrupt();
  198. _next = listWaiting;
  199. listWaiting = this; // TODO: use STREX to avoid interrupt disable
  200. _state = TimerWaiting;
  201. enableTimerInterrupt(irq);
  202. }
  203. void MillisTimer::addToActiveList() // only called by runFromTimer()
  204. {
  205. if (listActive == nullptr) {
  206. // list is empty, easy case
  207. _next = nullptr;
  208. _prev = nullptr;
  209. listActive = this;
  210. } else if (_ms < listActive->_ms) {
  211. // this timer triggers before any on the list
  212. _next = listActive;
  213. _prev = nullptr;
  214. listActive->_prev = this;
  215. // Decrement the next items wait time be our wait time as to properly handle waits for all other items...
  216. listActive->_ms -= _ms;
  217. listActive = this;
  218. } else {
  219. // add this timer somewhere after the first already on the list
  220. MillisTimer *timer = listActive;
  221. while (timer->_next) {
  222. _ms -= timer->_ms;
  223. timer = timer->_next;
  224. if (_ms < timer->_ms) {
  225. // found the right place in the middle of list
  226. _next = timer;
  227. _prev = timer->_prev;
  228. timer->_prev = this;
  229. _prev->_next = this;
  230. timer->_ms -= _ms;
  231. _state = TimerActive;
  232. return;
  233. }
  234. }
  235. // add this time at the end of the list
  236. _ms -= timer->_ms;
  237. _next = nullptr;
  238. _prev = timer;
  239. timer->_next = this;
  240. }
  241. _state = TimerActive;
  242. }
  243. void MillisTimer::end()
  244. {
  245. bool irq = disableTimerInterrupt();
  246. TimerStateType s = _state;
  247. if (s == TimerActive) {
  248. if (_next) {
  249. _next->_prev = _prev;
  250. _next->_ms += _ms; // add in the rest of our timing to next entry...
  251. }
  252. if (_prev) {
  253. _prev->_next = _next;
  254. } else {
  255. listActive = _next;
  256. }
  257. _state = TimerOff;
  258. } else if (s == TimerWaiting) {
  259. if (listWaiting == this) {
  260. listWaiting = _next;
  261. } else {
  262. MillisTimer *timer = listWaiting;
  263. while (timer) {
  264. if (timer->_next == this) {
  265. timer->_next = _next;
  266. break;
  267. }
  268. timer = timer->_next;
  269. }
  270. }
  271. _state = TimerOff;
  272. }
  273. enableTimerInterrupt(irq);
  274. }
  275. void MillisTimer::runFromTimer()
  276. {
  277. MillisTimer *timer = listActive;
  278. while (timer) {
  279. if (timer->_ms > 0) {
  280. timer->_ms--;
  281. break;
  282. } else {
  283. MillisTimer *next = timer->_next;
  284. if (next) next->_prev = nullptr;
  285. listActive = next;
  286. timer->_state = TimerOff;
  287. EventResponderRef event = *(timer->_event);
  288. event.triggerEvent(0, timer);
  289. if (timer->_reload) {
  290. timer->_ms = timer->_reload;
  291. timer->addToActiveList();
  292. }
  293. timer = listActive;
  294. }
  295. }
  296. bool irq = disableTimerInterrupt();
  297. MillisTimer *waiting = listWaiting;
  298. listWaiting = nullptr; // TODO: use STREX to avoid interrupt disable
  299. enableTimerInterrupt(irq);
  300. while (waiting) {
  301. MillisTimer *next = waiting->_next;
  302. waiting->addToActiveList();
  303. waiting = next;
  304. }
  305. }
  306. // Long ago you could install your own systick interrupt handler by just
  307. // creating your own systick_isr() function. No longer. But if you
  308. // *really* want to commandeer systick, you can still do so by writing
  309. // your function into the RAM-based vector table.
  310. //
  311. // _VectorsRam[15] = my_systick_function;
  312. //
  313. // However, for long-term portability, use a MillisTimer object to
  314. // generate an event every millisecond, and attach your function to
  315. // its EventResponder. You can attach as a software interrupt, so your
  316. // code will run at lower interrupt priority for better compatibility
  317. // with libraries using mid-to-high priority interrupts.
  318. // TODO: this doesn't work for IMXRT - no longer using predefined names
  319. extern "C" volatile uint32_t systick_millis_count;
  320. extern "C" volatile uint32_t systick_cycle_count;
  321. extern "C" uint32_t systick_safe_read; // micros() synchronization
  322. extern "C" void systick_isr(void)
  323. {
  324. systick_cycle_count = ARM_DWT_CYCCNT;
  325. systick_millis_count++;
  326. }
  327. extern "C" void systick_isr_with_timer_events(void)
  328. {
  329. systick_cycle_count = ARM_DWT_CYCCNT;
  330. systick_millis_count++;
  331. MillisTimer::runFromTimer();
  332. }