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
8.9KB

  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. uint8_t _serialEventUSB1_default __attribute__((weak)) PROGMEM = 0 ;
  43. uint8_t _serialEventUSB2_default __attribute__((weak)) PROGMEM = 0 ;
  44. void EventResponder::triggerEventNotImmediate()
  45. {
  46. bool irq = disableInterrupts();
  47. if (_triggered == false) {
  48. // not already triggered
  49. if (_type == EventTypeYield) {
  50. // normal type, called from yield()
  51. if (firstYield == nullptr) {
  52. _next = nullptr;
  53. _prev = nullptr;
  54. firstYield = this;
  55. lastYield = this;
  56. } else {
  57. _next = nullptr;
  58. _prev = lastYield;
  59. _prev->_next = this;
  60. lastYield = this;
  61. }
  62. } else if (_type == EventTypeInterrupt) {
  63. // interrupt, called from software interrupt
  64. if (firstInterrupt == nullptr) {
  65. _next = nullptr;
  66. _prev = nullptr;
  67. firstInterrupt = this;
  68. lastInterrupt = this;
  69. } else {
  70. _next = nullptr;
  71. _prev = lastInterrupt;
  72. _prev->_next = this;
  73. lastInterrupt = this;
  74. }
  75. SCB_ICSR = SCB_ICSR_PENDSVSET; // set PendSV interrupt
  76. } else {
  77. // detached, easy :-)
  78. }
  79. _triggered = true;
  80. }
  81. enableInterrupts(irq);
  82. }
  83. void pendablesrvreq_isr(void)
  84. {
  85. EventResponder::runFromInterrupt();
  86. }
  87. void EventResponder::runFromInterrupt()
  88. {
  89. while (1) {
  90. bool irq = disableInterrupts();
  91. EventResponder *first = firstInterrupt;
  92. if (first) {
  93. firstInterrupt = first->_next;
  94. if (firstInterrupt) {
  95. firstInterrupt->_prev = nullptr;
  96. } else {
  97. lastInterrupt = nullptr;
  98. }
  99. enableInterrupts(irq);
  100. first->_triggered = false;
  101. (*(first->_function))(*first);
  102. } else {
  103. enableInterrupts(irq);
  104. break;
  105. }
  106. }
  107. }
  108. bool EventResponder::clearEvent()
  109. {
  110. bool ret = false;
  111. bool irq = disableInterrupts();
  112. if (_triggered) {
  113. if (_type == EventTypeYield) {
  114. if (_prev) {
  115. _prev->_next = _next;
  116. } else {
  117. firstYield = _next;
  118. }
  119. if (_next) {
  120. _next->_prev = _prev;
  121. } else {
  122. lastYield = _prev;
  123. }
  124. } else if (_type == EventTypeInterrupt) {
  125. if (_prev) {
  126. _prev->_next = _next;
  127. } else {
  128. firstInterrupt = _next;
  129. }
  130. if (_next) {
  131. _next->_prev = _prev;
  132. } else {
  133. lastInterrupt = _prev;
  134. }
  135. }
  136. _triggered = false;
  137. ret = true;
  138. }
  139. enableInterrupts(irq);
  140. return ret;
  141. }
  142. // this detach must be called with interrupts disabled
  143. void EventResponder::detachNoInterrupts()
  144. {
  145. if (_type == EventTypeYield) {
  146. if (_triggered) {
  147. if (_prev) {
  148. _prev->_next = _next;
  149. } else {
  150. firstYield = _next;
  151. }
  152. if (_next) {
  153. _next->_prev = _prev;
  154. } else {
  155. lastYield = _prev;
  156. }
  157. }
  158. _type = EventTypeDetached;
  159. } else if (_type == EventTypeInterrupt) {
  160. if (_triggered) {
  161. if (_prev) {
  162. _prev->_next = _next;
  163. } else {
  164. firstInterrupt = _next;
  165. }
  166. if (_next) {
  167. _next->_prev = _prev;
  168. } else {
  169. lastInterrupt = _prev;
  170. }
  171. }
  172. _type = EventTypeDetached;
  173. }
  174. }
  175. //-------------------------------------------------------------
  176. MillisTimer * MillisTimer::listWaiting = nullptr;
  177. MillisTimer * MillisTimer::listActive = nullptr;
  178. void MillisTimer::begin(unsigned long milliseconds, EventResponderRef event)
  179. {
  180. if (_state != TimerOff) end();
  181. if (!milliseconds) return;
  182. _event = &event;
  183. _ms = (milliseconds > 2)? milliseconds-2 : 0;
  184. _reload = 0;
  185. addToWaitingList();
  186. }
  187. void MillisTimer::beginRepeating(unsigned long milliseconds, EventResponderRef event)
  188. {
  189. if (_state != TimerOff) end();
  190. if (!milliseconds) return;
  191. _event = &event;
  192. _ms = (milliseconds > 2)? milliseconds-2 : 0;
  193. _reload = milliseconds;
  194. addToWaitingList();
  195. }
  196. void MillisTimer::addToWaitingList()
  197. {
  198. _prev = nullptr;
  199. bool irq = disableTimerInterrupt();
  200. _next = listWaiting;
  201. listWaiting = this; // TODO: use STREX to avoid interrupt disable
  202. _state = TimerWaiting;
  203. enableTimerInterrupt(irq);
  204. }
  205. void MillisTimer::addToActiveList() // only called by runFromTimer()
  206. {
  207. if (listActive == nullptr) {
  208. // list is empty, easy case
  209. _next = nullptr;
  210. _prev = nullptr;
  211. listActive = this;
  212. } else if (_ms < listActive->_ms) {
  213. // this timer triggers before any on the list
  214. _next = listActive;
  215. _prev = nullptr;
  216. listActive->_prev = this;
  217. // Decrement the next items wait time be our wait time as to properly handle waits for all other items...
  218. listActive->_ms -= _ms;
  219. listActive = this;
  220. } else {
  221. // add this timer somewhere after the first already on the list
  222. MillisTimer *timer = listActive;
  223. while (timer->_next) {
  224. _ms -= timer->_ms;
  225. timer = timer->_next;
  226. if (_ms < timer->_ms) {
  227. // found the right place in the middle of list
  228. _next = timer;
  229. _prev = timer->_prev;
  230. timer->_prev = this;
  231. _prev->_next = this;
  232. timer->_ms -= _ms;
  233. _state = TimerActive;
  234. return;
  235. }
  236. }
  237. // add this time at the end of the list
  238. _ms -= timer->_ms;
  239. _next = nullptr;
  240. _prev = timer;
  241. timer->_next = this;
  242. }
  243. _state = TimerActive;
  244. }
  245. void MillisTimer::end()
  246. {
  247. bool irq = disableTimerInterrupt();
  248. TimerStateType s = _state;
  249. if (s == TimerActive) {
  250. if (_next) {
  251. _next->_prev = _prev;
  252. _next->_ms += _ms; // add in the rest of our timing to next entry...
  253. }
  254. if (_prev) {
  255. _prev->_next = _next;
  256. } else {
  257. listActive = _next;
  258. }
  259. _state = TimerOff;
  260. } else if (s == TimerWaiting) {
  261. if (listWaiting == this) {
  262. listWaiting = _next;
  263. } else {
  264. MillisTimer *timer = listWaiting;
  265. while (timer) {
  266. if (timer->_next == this) {
  267. timer->_next = _next;
  268. break;
  269. }
  270. timer = timer->_next;
  271. }
  272. }
  273. _state = TimerOff;
  274. }
  275. enableTimerInterrupt(irq);
  276. }
  277. void MillisTimer::runFromTimer()
  278. {
  279. MillisTimer *timer = listActive;
  280. while (timer) {
  281. if (timer->_ms > 0) {
  282. timer->_ms--;
  283. break;
  284. } else {
  285. MillisTimer *next = timer->_next;
  286. if (next) next->_prev = nullptr;
  287. listActive = next;
  288. timer->_state = TimerOff;
  289. EventResponderRef event = *(timer->_event);
  290. event.triggerEvent(0, timer);
  291. if (timer->_reload) {
  292. timer->_ms = timer->_reload;
  293. timer->addToActiveList();
  294. }
  295. timer = listActive;
  296. }
  297. }
  298. bool irq = disableTimerInterrupt();
  299. MillisTimer *waiting = listWaiting;
  300. listWaiting = nullptr; // TODO: use STREX to avoid interrupt disable
  301. enableTimerInterrupt(irq);
  302. while (waiting) {
  303. MillisTimer *next = waiting->_next;
  304. waiting->addToActiveList();
  305. waiting = next;
  306. }
  307. }
  308. // Long ago you could install your own systick interrupt handler by just
  309. // creating your own systick_isr() function. No longer. But if you
  310. // *really* want to commandeer systick, you can still do so by writing
  311. // your function into the RAM-based vector table.
  312. //
  313. // _VectorsRam[15] = my_systick_function;
  314. //
  315. // However, for long-term portability, use a MillisTimer object to
  316. // generate an event every millisecond, and attach your function to
  317. // its EventResponder. You can attach as a software interrupt, so your
  318. // code will run at lower interrupt priority for better compatibility
  319. // with libraries using mid-to-high priority interrupts.
  320. extern "C" volatile uint32_t systick_millis_count;
  321. void systick_isr(void)
  322. {
  323. systick_millis_count++;
  324. }
  325. extern "C" void systick_isr_with_timer_events(void)
  326. {
  327. systick_millis_count++;
  328. MillisTimer::runFromTimer();
  329. }