jsdevices.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
  3. *
  4. * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. *
  10. * ----------------------------------------------------------------------------
  11. * Common low-level device handling (Events, IO buffers)
  12. * ----------------------------------------------------------------------------
  13. */
  14. #include "jsdevices.h"
  15. #include "jsparse.h"
  16. #include "jsinteractive.h"
  17. #ifdef LINUX
  18. #include <signal.h>
  19. #endif//LINUX
  20. #ifdef USE_TRIGGER
  21. #include "trigger.h"
  22. #endif
  23. #ifdef USE_CC3000
  24. #include "board_spi.h"
  25. #endif
  26. // ----------------------------------------------------------------------------
  27. // BUFFERS
  28. // ----------------------------------------------------------------------------
  29. // DATA TRANSMIT BUFFER
  30. typedef struct {
  31. IOEventFlags flags; // Where this data should be transmitted
  32. unsigned char data; // data to transmit
  33. } PACKED_FLAGS TxBufferItem;
  34. TxBufferItem txBuffer[TXBUFFERMASK+1];
  35. volatile unsigned char txHead=0, txTail=0;
  36. // ----------------------------------------------------------------------------
  37. // Queue a character for transmission
  38. void jshTransmit(IOEventFlags device, unsigned char data) {
  39. #ifdef RT_USING_JS
  40. rt_device_write(rt_console_get_device(), 0, &data, 1);
  41. #else
  42. #ifndef LINUX
  43. #ifdef USB
  44. if (device==EV_USBSERIAL && !jshIsUSBSERIALConnected()) {
  45. jshTransmitClearDevice(EV_USBSERIAL); // clear out stuff already waiting
  46. return;
  47. }
  48. #endif
  49. if (device==EV_NONE) return;
  50. unsigned char txHeadNext = (txHead+1)&TXBUFFERMASK;
  51. if (txHeadNext==txTail) {
  52. jsiSetBusy(BUSY_TRANSMIT, true);
  53. while (txHeadNext==txTail) {
  54. // wait for send to finish as buffer is about to overflow
  55. #ifdef USB
  56. // just in case USB was unplugged while we were waiting!
  57. if (!jshIsUSBSERIALConnected()) jshTransmitClearDevice(EV_USBSERIAL);
  58. #endif
  59. }
  60. jsiSetBusy(BUSY_TRANSMIT, false);
  61. }
  62. txBuffer[txHead].flags = device;
  63. txBuffer[txHead].data = (char)data;
  64. txHead = txHeadNext;
  65. jshUSARTKick(device); // set up interrupts if required
  66. #else // if PC, just put to stdout
  67. if (device==DEFAULT_CONSOLE_DEVICE) {
  68. fputc(data, stdout);
  69. fflush(stdout);
  70. }
  71. #endif
  72. #endif
  73. }
  74. // Try and get a character for transmission - could just return -1 if nothing
  75. int jshGetCharToTransmit(IOEventFlags device) {
  76. unsigned char ptr = txTail;
  77. while (txHead != ptr) {
  78. if (IOEVENTFLAGS_GETTYPE(txBuffer[ptr].flags) == device) {
  79. unsigned char data = txBuffer[ptr].data;
  80. if (ptr != txTail) { // so we weren't right at the back of the queue
  81. // we need to work back from ptr (until we hit tail), shifting everything forwards
  82. unsigned char this = ptr;
  83. unsigned char last = (unsigned char)((this+TXBUFFERMASK)&TXBUFFERMASK);
  84. while (this!=txTail) { // if this==txTail, then last is before it, so stop here
  85. txBuffer[this] = txBuffer[last];
  86. this = last;
  87. last = (unsigned char)((this+TXBUFFERMASK)&TXBUFFERMASK);
  88. }
  89. }
  90. txTail = (unsigned char)((txTail+1)&TXBUFFERMASK); // advance the tail
  91. return data; // return data
  92. }
  93. ptr = (unsigned char)((ptr+1)&TXBUFFERMASK);
  94. }
  95. return -1; // no data :(
  96. }
  97. void jshTransmitFlush() {
  98. jsiSetBusy(BUSY_TRANSMIT, true);
  99. while (jshHasTransmitData()) ; // wait for send to finish
  100. jsiSetBusy(BUSY_TRANSMIT, false);
  101. }
  102. // Clear everything from a device
  103. void jshTransmitClearDevice(IOEventFlags device) {
  104. while (jshGetCharToTransmit(device)>=0);
  105. }
  106. bool jshHasTransmitData() {
  107. #ifndef LINUX
  108. return txHead != txTail;
  109. #else
  110. return false;
  111. #endif
  112. }
  113. // ----------------------------------------------------------------------------
  114. // IO EVENT BUFFER
  115. IOEvent ioBuffer[IOBUFFERMASK+1];
  116. volatile unsigned char ioHead=0, ioTail=0;
  117. // ----------------------------------------------------------------------------
  118. void jshIOEventOverflowed() {
  119. // Error here - just light LED as we can't do much else right now
  120. }
  121. void jshPushIOCharEvent(IOEventFlags channel, char charData) {
  122. if (charData==3 && channel==jsiGetConsoleDevice()) {
  123. // Ctrl-C - force interrupt
  124. #ifdef LINUX
  125. raise(SIGINT);
  126. #endif
  127. jspSetInterrupted(true);
  128. return;
  129. }
  130. if (DEVICE_IS_USART(channel) && jshGetEventsUsed() > IOBUFFER_XOFF)
  131. jshSetFlowControlXON(channel, false);
  132. // Check for existing buffer (we must have at least 2 in the queue to avoid dropping chars though!)
  133. unsigned char nextTail = (unsigned char)((ioTail+1) & IOBUFFERMASK);
  134. if (ioHead!=ioTail && ioHead!=nextTail) {
  135. // we can do this because we only read in main loop, and we're in an interrupt here
  136. unsigned char lastHead = (unsigned char)((ioHead+IOBUFFERMASK) & IOBUFFERMASK); // one behind head
  137. if (IOEVENTFLAGS_GETTYPE(ioBuffer[lastHead].flags) == channel &&
  138. IOEVENTFLAGS_GETCHARS(ioBuffer[lastHead].flags) < IOEVENT_MAXCHARS) {
  139. // last event was for this event type, and it has chars left
  140. unsigned char c = (unsigned char)IOEVENTFLAGS_GETCHARS(ioBuffer[lastHead].flags);
  141. ioBuffer[lastHead].data.chars[c] = charData;
  142. IOEVENTFLAGS_SETCHARS(ioBuffer[lastHead].flags, c+1);
  143. return;
  144. }
  145. }
  146. // Make new buffer
  147. unsigned char nextHead = (unsigned char)((ioHead+1) & IOBUFFERMASK);
  148. if (ioTail == nextHead) {
  149. jshIOEventOverflowed();
  150. return; // queue full - dump this event!
  151. }
  152. ioBuffer[ioHead].flags = channel;
  153. IOEVENTFLAGS_SETCHARS(ioBuffer[ioHead].flags, 1);
  154. ioBuffer[ioHead].data.chars[0] = charData;
  155. ioHead = nextHead;
  156. }
  157. void jshPushIOWatchEvent(IOEventFlags channel) {
  158. JsSysTime time = jshGetSystemTime();
  159. bool state = jshGetWatchedPinState(channel);
  160. /* // This is some simple debounce code - however it requires that the event
  161. // is not taken out of ioBuffer by the main thread, which will require
  162. // a bit of fiddling in jsinteractive.c. In fact it might be worth just
  163. // doing debounce outside of the interrupt
  164. if (true) { // debounce
  165. // scan back and see if we have an event for this pin
  166. unsigned char prevHead = ioHead;
  167. while (prevHead!=ioTail && (IOEVENTFLAGS_GETTYPE(ioBuffer[prevHead].flags)!=channel))
  168. prevHead = (unsigned char)((prevHead+IOBUFFERMASK) & IOBUFFERMASK); // step back
  169. // if we have an event
  170. if (prevHead!=ioTail && (IOEVENTFLAGS_GETTYPE(ioBuffer[prevHead].flags)==channel)) {
  171. // just use it (with the same timestamp)...
  172. ioBuffer[prevHead].flags = channel | (state?EV_EXTI_IS_HIGH:0);
  173. return;
  174. }
  175. }*/
  176. #ifdef USE_TRIGGER
  177. if (trigHandleEXTI(channel | (state?EV_EXTI_IS_HIGH:0), time))
  178. return;
  179. #endif
  180. #ifdef USE_CC3000
  181. IOEvent event;
  182. event.flags = channel;
  183. if (!state && jshIsEventForPin(&event, WLAN_IRQ_PIN)) {
  184. cc3000_irq_handler();
  185. }
  186. #endif
  187. // Otherwise add this event
  188. jshPushIOEvent(channel | (state?EV_EXTI_IS_HIGH:0), time);
  189. }
  190. void jshPushIOEvent(IOEventFlags channel, JsSysTime time) {
  191. unsigned char nextHead = (unsigned char)((ioHead+1) & IOBUFFERMASK);
  192. if (ioTail == nextHead) {
  193. jshIOEventOverflowed();
  194. return; // queue full - dump this event!
  195. }
  196. ioBuffer[ioHead].flags = channel;
  197. ioBuffer[ioHead].data.time = time;
  198. ioHead = nextHead;
  199. }
  200. // returns true on success
  201. bool jshPopIOEvent(IOEvent *result) {
  202. if (ioHead==ioTail) return false;
  203. *result = ioBuffer[ioTail];
  204. ioTail = (unsigned char)((ioTail+1) & IOBUFFERMASK);
  205. return true;
  206. }
  207. bool jshHasEvents() {
  208. return ioHead!=ioTail;
  209. }
  210. int jshGetEventsUsed() {
  211. int spaceUsed = (ioHead >= ioTail) ? ((int)ioHead-(int)ioTail) : /*or rolled*/((int)ioHead+IOBUFFERMASK+1-(int)ioTail);
  212. return spaceUsed;
  213. }
  214. bool jshHasEventSpaceForChars(int n) {
  215. int spacesNeeded = 4 + (n/IOEVENT_MAXCHARS); // be sensible - leave a little spare
  216. int spaceUsed = jshGetEventsUsed();
  217. int spaceLeft = IOBUFFERMASK+1-spaceUsed;
  218. return spaceLeft > spacesNeeded;
  219. }
  220. // ----------------------------------------------------------------------------
  221. // DEVICES
  222. const char *jshGetDeviceString(IOEventFlags device) {
  223. switch (device) {
  224. #ifdef USB
  225. case EV_USBSERIAL: return "USB";
  226. #endif
  227. case EV_SERIAL1: return "Serial1";
  228. case EV_SERIAL2: return "Serial2";
  229. case EV_SERIAL3: return "Serial3";
  230. #if USARTS>=4
  231. case EV_SERIAL4: return "Serial4";
  232. #endif
  233. #if USARTS>=5
  234. case EV_SERIAL5: return "Serial5";
  235. #endif
  236. #if USARTS>=6
  237. case EV_SERIAL6: return "Serial6";
  238. #endif
  239. #if SPIS>=1
  240. case EV_SPI1: return "SPI1";
  241. #endif
  242. #if SPIS>=2
  243. case EV_SPI2: return "SPI2";
  244. #endif
  245. #if SPIS>=3
  246. case EV_SPI3: return "SPI3";
  247. #endif
  248. #if I2CS>=1
  249. case EV_I2C1: return "I2C1";
  250. #endif
  251. #if I2CS>=2
  252. case EV_I2C2: return "I2C2";
  253. #endif
  254. #if I2CS>=3
  255. case EV_I2C3: return "I2C3";
  256. #endif
  257. default: return "";
  258. }
  259. }
  260. IOEventFlags jshFromDeviceString(const char *device) {
  261. if (device[0]=='U') {
  262. #ifdef USB
  263. if (strcmp(device, "USB")==0) return EV_USBSERIAL;
  264. #endif
  265. }
  266. else if (device[0]=='S') {
  267. if (device[1]=='e' && device[2]=='r' && device[3]=='i' && device[4]=='a' && device[5]=='l') {
  268. if (strcmp(device, "Serial1")==0) return EV_SERIAL1;
  269. if (strcmp(device, "Serial2")==0) return EV_SERIAL2;
  270. if (strcmp(device, "Serial3")==0) return EV_SERIAL3;
  271. #if USARTS>=4
  272. if (strcmp(device, "Serial4")==0) return EV_SERIAL4;
  273. #endif
  274. #if USARTS>=5
  275. if (strcmp(device, "Serial5")==0) return EV_SERIAL5;
  276. #endif
  277. #if USARTS>=6
  278. if (strcmp(device, "Serial6")==0) return EV_SERIAL6;
  279. #endif
  280. }
  281. if (device[1]=='P' && device[2]=='I') {
  282. #if SPIS>=1
  283. if (strcmp(device, "SPI1")==0) return EV_SPI1;
  284. #endif
  285. #if SPIS>=2
  286. if (strcmp(device, "SPI2")==0) return EV_SPI2;
  287. #endif
  288. #if SPIS>=3
  289. if (strcmp(device, "SPI3")==0) return EV_SPI3;
  290. #endif
  291. }
  292. }
  293. else if (device[0]=='I' && device[1]=='2' && device[2]=='C') {
  294. #if I2CS>=1
  295. if (strcmp(device, "I2C1")==0) return EV_I2C1;
  296. #endif
  297. #if I2CS>=2
  298. if (strcmp(device, "I2C2")==0) return EV_I2C2;
  299. #endif
  300. #if I2CS>=3
  301. if (strcmp(device, "I2C3")==0) return EV_I2C3;
  302. #endif
  303. }
  304. return EV_NONE;
  305. }
  306. /// Set whether the host should transmit or not
  307. void jshSetFlowControlXON(IOEventFlags device, bool hostShouldTransmit) {
  308. }