Attempting to publish a tree
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
MicroBitSerial.cpp
00001 /* 00002 The MIT License (MIT) 00003 00004 Copyright (c) 2016 British Broadcasting Corporation. 00005 This software is provided by Lancaster University by arrangement with the BBC. 00006 00007 Permission is hereby granted, free of charge, to any person obtaining a 00008 copy of this software and associated documentation files (the "Software"), 00009 to deal in the Software without restriction, including without limitation 00010 the rights to use, copy, modify, merge, publish, distribute, sublicense, 00011 and/or sell copies of the Software, and to permit persons to whom the 00012 Software is furnished to do so, subject to the following conditions: 00013 00014 The above copyright notice and this permission notice shall be included in 00015 all copies or substantial portions of the Software. 00016 00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00023 DEALINGS IN THE SOFTWARE. 00024 */ 00025 00026 #include "mbed.h" 00027 #include "MicroBitSerial.h" 00028 #include "ErrorNo.h" 00029 #include "MicroBitComponent.h" 00030 #include "MicroBitFiber.h" 00031 #include "NotifyEvents.h" 00032 00033 uint8_t MicroBitSerial::status = 0; 00034 00035 int MicroBitSerial::baudrate = 0; 00036 00037 /** 00038 * Constructor. 00039 * Create an instance of MicroBitSerial 00040 * 00041 * @param tx the Pin to be used for transmission 00042 * 00043 * @param rx the Pin to be used for receiving data 00044 * 00045 * @param rxBufferSize the size of the buffer to be used for receiving bytes 00046 * 00047 * @param txBufferSize the size of the buffer to be used for transmitting bytes 00048 * 00049 * @code 00050 * MicroBitSerial serial(USBTX, USBRX); 00051 * @endcode 00052 * @note the default baud rate is 115200. More API details can be found: 00053 * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/SerialBase.h 00054 * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/RawSerial.h 00055 * 00056 * Buffers aren't allocated until the first send or receive respectively. 00057 */ 00058 MicroBitSerial::MicroBitSerial(PinName tx, PinName rx, uint8_t rxBufferSize, uint8_t txBufferSize) : RawSerial(tx,rx), delimeters() 00059 { 00060 this->rxBuffSize = rxBufferSize; 00061 this->txBuffSize = txBufferSize; 00062 00063 this->rxBuff = NULL; 00064 this->txBuff = NULL; 00065 00066 this->rxBuffHead = 0; 00067 this->rxBuffTail = 0; 00068 00069 this->txBuffHead = 0; 00070 this->txBuffTail = 0; 00071 00072 this->rxBuffHeadMatch = -1; 00073 00074 this->baud(MICROBIT_SERIAL_DEFAULT_BAUD_RATE); 00075 00076 #if CONFIG_ENABLED(MICROBIT_DBG) 00077 SERIAL_DEBUG = this; 00078 #endif 00079 00080 } 00081 00082 /** 00083 * An internal interrupt callback for MicroBitSerial configured for when a 00084 * character is received. 00085 * 00086 * Each time a character is received fill our circular buffer! 00087 */ 00088 void MicroBitSerial::dataReceived() 00089 { 00090 if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT)) 00091 return; 00092 00093 //get the received character 00094 char c = getc(); 00095 00096 int delimeterOffset = 0; 00097 int delimLength = this->delimeters.length(); 00098 00099 //iterate through our delimeters (if any) to see if there is a match 00100 while(delimeterOffset < delimLength) 00101 { 00102 //fire an event if there is to block any waiting fibers 00103 if(this->delimeters.charAt(delimeterOffset) == c) 00104 MicroBitEvent(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH); 00105 00106 delimeterOffset++; 00107 } 00108 00109 uint16_t newHead = (rxBuffHead + 1) % rxBuffSize; 00110 00111 //look ahead to our newHead value to see if we are about to collide with the tail 00112 if(newHead != rxBuffTail) 00113 { 00114 //if we are not, store the character, and update our actual head. 00115 this->rxBuff[rxBuffHead] = c; 00116 rxBuffHead = newHead; 00117 00118 //if we have any fibers waiting for a specific number of characters, unblock them 00119 if(rxBuffHeadMatch >= 0) 00120 if(rxBuffHead == rxBuffHeadMatch) 00121 { 00122 rxBuffHeadMatch = -1; 00123 MicroBitEvent(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_HEAD_MATCH); 00124 } 00125 } 00126 else 00127 //otherwise, our buffer is full, send an event to the user... 00128 MicroBitEvent(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_RX_FULL); 00129 } 00130 00131 /** 00132 * An internal interrupt callback for MicroBitSerial. 00133 * 00134 * Each time the Serial module's buffer is empty, write a character if we have 00135 * characters to write. 00136 */ 00137 void MicroBitSerial::dataWritten() 00138 { 00139 if(txBuffTail == txBuffHead || !(status & MICROBIT_SERIAL_TX_BUFF_INIT)) 00140 return; 00141 00142 //send our current char 00143 putc(txBuff[txBuffTail]); 00144 00145 uint16_t nextTail = (txBuffTail + 1) % txBuffSize; 00146 00147 //unblock any waiting fibers that are waiting for transmission to finish. 00148 if(nextTail == txBuffHead) 00149 { 00150 MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_SERIAL_EVT_TX_EMPTY); 00151 detach(Serial::TxIrq); 00152 } 00153 00154 //update our tail! 00155 txBuffTail = nextTail; 00156 } 00157 00158 /** 00159 * An internal method to configure an interrupt on tx buffer and also 00160 * a best effort copy operation to move bytes from a user buffer to our txBuff 00161 * 00162 * @param string a pointer to the first character of the users' buffer. 00163 * 00164 * @param len the length of the string, and ultimately the maximum number of bytes 00165 * that will be copied dependent on the state of txBuff 00166 * 00167 * @return the number of bytes copied into the buffer. 00168 */ 00169 int MicroBitSerial::setTxInterrupt(uint8_t *string, int len) 00170 { 00171 int copiedBytes = 0; 00172 00173 for(copiedBytes = 0; copiedBytes < len; copiedBytes++) 00174 { 00175 uint16_t nextHead = (txBuffHead + 1) % txBuffSize; 00176 if(nextHead != txBuffTail) 00177 { 00178 this->txBuff[txBuffHead] = string[copiedBytes]; 00179 txBuffHead = nextHead; 00180 } 00181 else 00182 break; 00183 } 00184 00185 fiber_wake_on_event(MICROBIT_ID_NOTIFY, MICROBIT_SERIAL_EVT_TX_EMPTY); 00186 00187 //set the TX interrupt 00188 attach(this, &MicroBitSerial::dataWritten, Serial::TxIrq); 00189 00190 return copiedBytes; 00191 } 00192 00193 /** 00194 * Locks the mutex so that others can't use this serial instance for reception 00195 */ 00196 void MicroBitSerial::lockRx() 00197 { 00198 status |= MICROBIT_SERIAL_RX_IN_USE; 00199 } 00200 00201 /** 00202 * Locks the mutex so that others can't use this serial instance for transmission 00203 */ 00204 void MicroBitSerial::lockTx() 00205 { 00206 status |= MICROBIT_SERIAL_TX_IN_USE; 00207 } 00208 00209 /** 00210 * Unlocks the mutex so that others can use this serial instance for reception 00211 */ 00212 void MicroBitSerial::unlockRx() 00213 { 00214 status &= ~MICROBIT_SERIAL_RX_IN_USE; 00215 } 00216 00217 /** 00218 * Unlocks the mutex so that others can use this serial instance for transmission 00219 */ 00220 void MicroBitSerial::unlockTx() 00221 { 00222 status &= ~MICROBIT_SERIAL_TX_IN_USE; 00223 } 00224 00225 /** 00226 * We do not want to always have our buffers initialised, especially if users to not 00227 * use them. We only bring them up on demand. 00228 */ 00229 int MicroBitSerial::initialiseRx() 00230 { 00231 if((status & MICROBIT_SERIAL_RX_BUFF_INIT)) 00232 { 00233 //ensure that we receive no interrupts after freeing our buffer 00234 detach(Serial::RxIrq); 00235 free(this->rxBuff); 00236 } 00237 00238 status &= ~MICROBIT_SERIAL_RX_BUFF_INIT; 00239 00240 if((this->rxBuff = (uint8_t *)malloc(rxBuffSize)) == NULL) 00241 return MICROBIT_NO_RESOURCES; 00242 00243 this->rxBuffHead = 0; 00244 this->rxBuffTail = 0; 00245 00246 //set the receive interrupt 00247 status |= MICROBIT_SERIAL_RX_BUFF_INIT; 00248 attach(this, &MicroBitSerial::dataReceived, Serial::RxIrq); 00249 00250 return MICROBIT_OK; 00251 } 00252 00253 /** 00254 * We do not want to always have our buffers initialised, especially if users to not 00255 * use them. We only bring them up on demand. 00256 */ 00257 int MicroBitSerial::initialiseTx() 00258 { 00259 if((status & MICROBIT_SERIAL_TX_BUFF_INIT)) 00260 { 00261 //ensure that we receive no interrupts after freeing our buffer 00262 detach(Serial::TxIrq); 00263 free(this->txBuff); 00264 } 00265 00266 status &= ~MICROBIT_SERIAL_TX_BUFF_INIT; 00267 00268 if((this->txBuff = (uint8_t *)malloc(txBuffSize)) == NULL) 00269 return MICROBIT_NO_RESOURCES; 00270 00271 this->txBuffHead = 0; 00272 this->txBuffTail = 0; 00273 00274 status |= MICROBIT_SERIAL_TX_BUFF_INIT; 00275 00276 return MICROBIT_OK; 00277 } 00278 00279 /** 00280 * An internal method that either spin waits if mode is set to SYNC_SPINWAIT 00281 * or puts the fiber to sleep if the mode is set to SYNC_SLEEP 00282 * 00283 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP 00284 */ 00285 void MicroBitSerial::send(MicroBitSerialMode mode) 00286 { 00287 if(mode == SYNC_SPINWAIT) 00288 while(txBufferedSize() > 0); 00289 00290 if(mode == SYNC_SLEEP) 00291 fiber_sleep(0); 00292 } 00293 00294 /** 00295 * Reads a single character from the rxBuff 00296 * 00297 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00298 * gives a different behaviour: 00299 * 00300 * ASYNC - A character is read from the rxBuff if available, if there 00301 * are no characters to be read, a value of zero is returned immediately. 00302 * 00303 * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there 00304 * are no characters to be read, this method will spin 00305 * (lock up the processor) until a character is available. 00306 * 00307 * SYNC_SLEEP - A character is read from the rxBuff if available, if there 00308 * are no characters to be read, the calling fiber sleeps 00309 * until there is a character available. 00310 * 00311 * Defaults to SYNC_SLEEP. 00312 * 00313 * @return a character from the circular buffer, or MICROBIT_NO_DATA is there 00314 * are no characters in the buffer. 00315 */ 00316 int MicroBitSerial::getChar(MicroBitSerialMode mode) 00317 { 00318 if(mode == ASYNC) 00319 { 00320 if(!isReadable()) 00321 return MICROBIT_NO_DATA; 00322 } 00323 00324 if(mode == SYNC_SPINWAIT) 00325 while(!isReadable()); 00326 00327 if(mode == SYNC_SLEEP) 00328 { 00329 if(!isReadable()) 00330 eventAfter(1, mode); 00331 } 00332 00333 char c = rxBuff[rxBuffTail]; 00334 00335 rxBuffTail = (rxBuffTail + 1) % rxBuffSize; 00336 00337 return c; 00338 } 00339 00340 /** 00341 * An internal method that copies values from a circular buffer to a linear buffer. 00342 * 00343 * @param circularBuff a pointer to the source circular buffer 00344 * 00345 * @param circularBuffSize the size of the circular buffer 00346 * 00347 * @param linearBuff a pointer to the destination linear buffer 00348 * 00349 * @param tailPosition the tail position in the circular buffer you want to copy from 00350 * 00351 * @param headPosition the head position in the circular buffer you want to copy to 00352 * 00353 * @note this method assumes that the linear buffer has the appropriate amount of 00354 * memory to contain the copy operation 00355 */ 00356 void MicroBitSerial::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition) 00357 { 00358 int toBuffIndex = 0; 00359 00360 while(tailPosition != headPosition) 00361 { 00362 linearBuff[toBuffIndex++] = circularBuff[tailPosition]; 00363 00364 tailPosition = (tailPosition + 1) % circularBuffSize; 00365 } 00366 } 00367 00368 /** 00369 * Sends a single character over the serial line. 00370 * 00371 * @param c the character to send 00372 * 00373 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00374 * gives a different behaviour: 00375 * 00376 * ASYNC - the character is copied into the txBuff and returns immediately. 00377 * 00378 * SYNC_SPINWAIT - the character is copied into the txBuff and this method 00379 * will spin (lock up the processor) until the character has 00380 * been sent. 00381 * 00382 * SYNC_SLEEP - the character is copied into the txBuff and the fiber sleeps 00383 * until the character has been sent. This allows other fibers 00384 * to continue execution. 00385 * 00386 * Defaults to SYNC_SLEEP. 00387 * 00388 * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber 00389 * is using the serial instance for transmission. 00390 */ 00391 int MicroBitSerial::sendChar(char c, MicroBitSerialMode mode) 00392 { 00393 if(txInUse()) 00394 return MICROBIT_SERIAL_IN_USE; 00395 00396 lockTx(); 00397 00398 //lazy initialisation of our tx buffer 00399 if(!(status & MICROBIT_SERIAL_TX_BUFF_INIT)) 00400 { 00401 int result = initialiseTx(); 00402 00403 if(result != MICROBIT_OK) 00404 return result; 00405 } 00406 00407 uint8_t toTransmit[2] = { c, '\0'}; 00408 00409 int bytesWritten = setTxInterrupt(toTransmit, 1); 00410 00411 send(mode); 00412 00413 unlockTx(); 00414 00415 return bytesWritten; 00416 } 00417 00418 /** 00419 * Sends a ManagedString over the serial line. 00420 * 00421 * @param s the string to send 00422 * 00423 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00424 * gives a different behaviour: 00425 * 00426 * ASYNC - bytes are copied into the txBuff and returns immediately. 00427 * 00428 * SYNC_SPINWAIT - bytes are copied into the txBuff and this method 00429 * will spin (lock up the processor) until all bytes 00430 * have been sent. 00431 * 00432 * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps 00433 * until all bytes have been sent. This allows other fibers 00434 * to continue execution. 00435 * 00436 * Defaults to SYNC_SLEEP. 00437 * 00438 * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber 00439 * is using the serial instance for transmission. 00440 */ 00441 int MicroBitSerial::send(ManagedString s, MicroBitSerialMode mode) 00442 { 00443 return send((uint8_t *)s.toCharArray(), s.length(), mode); 00444 } 00445 00446 /** 00447 * Sends a buffer of known length over the serial line. 00448 * 00449 * @param buffer a pointer to the first character of the buffer 00450 * 00451 * @param len the number of bytes that are safely available to read. 00452 * 00453 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00454 * gives a different behaviour: 00455 * 00456 * ASYNC - bytes are copied into the txBuff and returns immediately. 00457 * 00458 * SYNC_SPINWAIT - bytes are copied into the txBuff and this method 00459 * will spin (lock up the processor) until all bytes 00460 * have been sent. 00461 * 00462 * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps 00463 * until all bytes have been sent. This allows other fibers 00464 * to continue execution. 00465 * 00466 * Defaults to SYNC_SLEEP. 00467 * 00468 * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber 00469 * is using the serial instance for transmission. 00470 */ 00471 int MicroBitSerial::send(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode) 00472 { 00473 if(txInUse()) 00474 return MICROBIT_SERIAL_IN_USE; 00475 00476 lockTx(); 00477 00478 //lazy initialisation of our tx buffer 00479 if(!(status & MICROBIT_SERIAL_TX_BUFF_INIT)) 00480 { 00481 int result = initialiseTx(); 00482 00483 if(result != MICROBIT_OK) 00484 return result; 00485 } 00486 00487 int bytesWritten = setTxInterrupt(buffer, bufferLen); 00488 00489 send(mode); 00490 00491 unlockTx(); 00492 00493 return bytesWritten; 00494 } 00495 00496 /** 00497 * Reads a single character from the rxBuff 00498 * 00499 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00500 * gives a different behaviour: 00501 * 00502 * ASYNC - A character is read from the rxBuff if available, if there 00503 * are no characters to be read, a value of MICROBIT_NO_DATA is returned immediately. 00504 * 00505 * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there 00506 * are no characters to be read, this method will spin 00507 * (lock up the processor) until a character is available. 00508 * 00509 * SYNC_SLEEP - A character is read from the rxBuff if available, if there 00510 * are no characters to be read, the calling fiber sleeps 00511 * until there is a character available. 00512 * 00513 * Defaults to SYNC_SLEEP. 00514 * 00515 * @return a character, MICROBIT_SERIAL_IN_USE if another fiber is using the serial instance for reception, 00516 * MICROBIT_NO_RESOURCES if buffer allocation did not complete successfully, or MICROBIT_NO_DATA if 00517 * the rx buffer is empty and the mode given is ASYNC. 00518 */ 00519 int MicroBitSerial::read(MicroBitSerialMode mode) 00520 { 00521 if(rxInUse()) 00522 return MICROBIT_SERIAL_IN_USE; 00523 00524 lockRx(); 00525 00526 //lazy initialisation of our buffers 00527 if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT)) 00528 { 00529 int result = initialiseRx(); 00530 00531 if(result != MICROBIT_OK) 00532 return result; 00533 } 00534 00535 char c = (char)getChar(mode); 00536 00537 unlockRx(); 00538 00539 return c; 00540 } 00541 00542 /** 00543 * Reads multiple characters from the rxBuff and returns them as a ManagedString 00544 * 00545 * @param size the number of characters to read. 00546 * 00547 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00548 * gives a different behaviour: 00549 * 00550 * ASYNC - If the desired number of characters are available, this will return 00551 * a ManagedString with the expected size. Otherwise, it will read however 00552 * many characters there are available. 00553 * 00554 * SYNC_SPINWAIT - If the desired number of characters are available, this will return 00555 * a ManagedString with the expected size. Otherwise, this method will spin 00556 * (lock up the processor) until the desired number of characters have been read. 00557 * 00558 * SYNC_SLEEP - If the desired number of characters are available, this will return 00559 * a ManagedString with the expected size. Otherwise, the calling fiber sleeps 00560 * until the desired number of characters have been read. 00561 * 00562 * Defaults to SYNC_SLEEP. 00563 * 00564 * @return A ManagedString, or an empty ManagedString if an error was encountered during the read. 00565 */ 00566 ManagedString MicroBitSerial::read(int size, MicroBitSerialMode mode) 00567 { 00568 uint8_t buff[size + 1]; 00569 00570 memclr(&buff, size + 1); 00571 00572 int returnedSize = read((uint8_t *)buff, size, mode); 00573 00574 if(returnedSize <= 0) 00575 return ManagedString(); 00576 00577 return ManagedString((char *)buff, returnedSize); 00578 } 00579 00580 /** 00581 * Reads multiple characters from the rxBuff and fills a user buffer. 00582 * 00583 * @param buffer a pointer to a user allocated buffer. 00584 * 00585 * @param bufferLen the amount of data that can be safely stored 00586 * 00587 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00588 * gives a different behaviour: 00589 * 00590 * ASYNC - If the desired number of characters are available, this will fill 00591 * the given buffer. Otherwise, it will fill the buffer with however 00592 * many characters there are available. 00593 * 00594 * SYNC_SPINWAIT - If the desired number of characters are available, this will fill 00595 * the given buffer. Otherwise, this method will spin (lock up the processor) 00596 * and fill the buffer until the desired number of characters have been read. 00597 * 00598 * SYNC_SLEEP - If the desired number of characters are available, this will fill 00599 * the given buffer. Otherwise, the calling fiber sleeps 00600 * until the desired number of characters have been read. 00601 * 00602 * Defaults to SYNC_SLEEP. 00603 * 00604 * @return the number of characters read, or MICROBIT_SERIAL_IN_USE if another fiber 00605 * is using the instance for receiving. 00606 */ 00607 int MicroBitSerial::read(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode) 00608 { 00609 if(rxInUse()) 00610 return MICROBIT_SERIAL_IN_USE; 00611 00612 lockRx(); 00613 00614 //lazy initialisation of our rx buffer 00615 if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT)) 00616 { 00617 int result = initialiseRx(); 00618 00619 if(result != MICROBIT_OK) 00620 return result; 00621 } 00622 00623 int bufferIndex = 0; 00624 00625 int temp = 0; 00626 00627 if(mode == ASYNC) 00628 { 00629 while((temp = getChar(mode)) != MICROBIT_NO_DATA && bufferIndex < bufferLen) 00630 { 00631 buffer[bufferIndex] = (char)temp; 00632 bufferIndex++; 00633 } 00634 } 00635 00636 if(mode == SYNC_SPINWAIT) 00637 { 00638 while(bufferIndex < bufferLen) 00639 { 00640 buffer[bufferIndex] = (char)getChar(mode); 00641 bufferIndex++; 00642 } 00643 } 00644 00645 if(mode == SYNC_SLEEP) 00646 { 00647 if(bufferLen > rxBufferedSize()) 00648 eventAfter(bufferLen - rxBufferedSize(), mode); 00649 00650 while(bufferIndex < bufferLen) 00651 { 00652 buffer[bufferIndex] = (char)getChar(mode); 00653 bufferIndex++; 00654 } 00655 } 00656 00657 unlockRx(); 00658 00659 return bufferIndex; 00660 } 00661 00662 00663 /** 00664 * Reads until one of the delimeters matches a character in the rxBuff 00665 * 00666 * @param delimeters a ManagedString containing a sequence of delimeter characters e.g. ManagedString("\r\n") 00667 * 00668 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00669 * gives a different behaviour: 00670 * 00671 * ASYNC - If one of the delimeters matches a character already in the rxBuff 00672 * this method will return a ManagedString up to the delimeter. 00673 * Otherwise, it will return an Empty ManagedString. 00674 * 00675 * SYNC_SPINWAIT - If one of the delimeters matches a character already in the rxBuff 00676 * this method will return a ManagedString up to the delimeter. 00677 * Otherwise, this method will spin (lock up the processor) until a 00678 * received character matches one of the delimeters. 00679 * 00680 * SYNC_SLEEP - If one of the delimeters matches a character already in the rxBuff 00681 * this method will return a ManagedString up to the delimeter. 00682 * Otherwise, the calling fiber sleeps until a character matching one 00683 * of the delimeters is seen. 00684 * 00685 * Defaults to SYNC_SLEEP. 00686 * 00687 * @return A ManagedString containing the characters up to a delimeter, or an Empty ManagedString, 00688 * if another fiber is currently using this instance for reception. 00689 * 00690 * @note delimeters are matched on a per byte basis. 00691 */ 00692 ManagedString MicroBitSerial::readUntil(ManagedString delimeters, MicroBitSerialMode mode) 00693 { 00694 00695 if(rxInUse()) 00696 return ManagedString(); 00697 00698 //lazy initialisation of our rx buffer 00699 if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT)) 00700 { 00701 int result = initialiseRx(); 00702 00703 if(result != MICROBIT_OK) 00704 return result; 00705 } 00706 00707 lockRx(); 00708 00709 int localTail = rxBuffTail; 00710 int preservedTail = rxBuffTail; 00711 00712 int foundIndex = -1; 00713 00714 //ASYNC mode just iterates through our stored characters checking for any matches. 00715 while(localTail != rxBuffHead && foundIndex == -1) 00716 { 00717 //we use localTail to prevent modification of the actual tail. 00718 char c = rxBuff[localTail]; 00719 00720 for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++) 00721 if(delimeters.charAt(delimeterIterator) == c) 00722 foundIndex = localTail; 00723 00724 localTail = (localTail + 1) % rxBuffSize; 00725 } 00726 00727 //if our mode is SYNC_SPINWAIT and we didn't see any matching characters in our buffer 00728 //spin until we find a match! 00729 if(mode == SYNC_SPINWAIT) 00730 { 00731 while(foundIndex == -1) 00732 { 00733 while(localTail == rxBuffHead); 00734 00735 char c = rxBuff[localTail]; 00736 00737 for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++) 00738 if(delimeters.charAt(delimeterIterator) == c) 00739 foundIndex = localTail; 00740 00741 localTail = (localTail + 1) % rxBuffSize; 00742 } 00743 } 00744 00745 //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a 00746 //matching character. 00747 if(mode == SYNC_SLEEP && foundIndex == -1) 00748 { 00749 eventOn(delimeters, mode); 00750 00751 foundIndex = rxBuffHead - 1; 00752 00753 this->delimeters = ManagedString(); 00754 } 00755 00756 if(foundIndex >= 0) 00757 { 00758 //calculate our local buffer size 00759 int localBuffSize = (preservedTail > foundIndex) ? (rxBuffSize - preservedTail) + foundIndex : foundIndex - preservedTail; 00760 00761 uint8_t localBuff[localBuffSize + 1]; 00762 00763 memclr(&localBuff, localBuffSize + 1); 00764 00765 circularCopy(rxBuff, rxBuffSize, localBuff, preservedTail, foundIndex); 00766 00767 //plus one for the character we listened for... 00768 rxBuffTail = (rxBuffTail + localBuffSize + 1) % rxBuffSize; 00769 00770 unlockRx(); 00771 00772 return ManagedString((char *)localBuff, localBuffSize); 00773 } 00774 00775 unlockRx(); 00776 00777 return ManagedString(); 00778 } 00779 00780 /** 00781 * A wrapper around the inherited method "baud" so we can trap the baud rate 00782 * as it changes and restore it if redirect() is called. 00783 * 00784 * @param baudrate the new baudrate. See: 00785 * - https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/serial_api.c 00786 * for permitted baud rates. 00787 * 00788 * @return MICROBIT_INVALID_PARAMETER if baud rate is less than 0, otherwise MICROBIT_OK. 00789 * 00790 * @note the underlying implementation chooses the first allowable rate at or above that requested. 00791 */ 00792 void MicroBitSerial::baud(int baudrate) 00793 { 00794 if(baudrate < 0) 00795 return; 00796 00797 this->baudrate = baudrate; 00798 00799 RawSerial::baud(baudrate); 00800 } 00801 00802 /** 00803 * A way of dynamically configuring the serial instance to use pins other than USBTX and USBRX. 00804 * 00805 * @param tx the new transmission pin. 00806 * 00807 * @param rx the new reception pin. 00808 * 00809 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently transmitting or receiving, otherwise MICROBIT_OK. 00810 */ 00811 int MicroBitSerial::redirect(PinName tx, PinName rx) 00812 { 00813 if(txInUse() || rxInUse()) 00814 return MICROBIT_SERIAL_IN_USE; 00815 00816 lockTx(); 00817 lockRx(); 00818 00819 if(txBufferedSize() > 0) 00820 detach(Serial::TxIrq); 00821 00822 detach(Serial::RxIrq); 00823 00824 serial_init(&_serial, tx, rx); 00825 00826 attach(this, &MicroBitSerial::dataReceived, Serial::RxIrq); 00827 00828 if(txBufferedSize() > 0) 00829 attach(this, &MicroBitSerial::dataWritten, Serial::TxIrq); 00830 00831 this->baud(this->baudrate); 00832 00833 unlockRx(); 00834 unlockTx(); 00835 00836 return MICROBIT_OK; 00837 } 00838 00839 /** 00840 * Configures an event to be fired after "len" characters. 00841 * 00842 * @param len the number of characters to wait before triggering the event. 00843 * 00844 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00845 * gives a different behaviour: 00846 * 00847 * ASYNC - Will configure the event and return immediately. 00848 * 00849 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00850 * 00851 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00852 * event is received. 00853 * 00854 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00855 */ 00856 int MicroBitSerial::eventAfter(int len, MicroBitSerialMode mode) 00857 { 00858 if(mode == SYNC_SPINWAIT) 00859 return MICROBIT_INVALID_PARAMETER; 00860 00861 //configure our head match... 00862 this->rxBuffHeadMatch = (rxBuffHead + len) % rxBuffSize; 00863 00864 //block! 00865 if(mode == SYNC_SLEEP) 00866 fiber_wait_for_event(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_HEAD_MATCH); 00867 00868 return MICROBIT_OK; 00869 } 00870 00871 /** 00872 * Configures an event to be fired on a match with one of the delimeters. 00873 * 00874 * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n") 00875 * 00876 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00877 * gives a different behaviour: 00878 * 00879 * ASYNC - Will configure the event and return immediately. 00880 * 00881 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00882 * 00883 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00884 * event is received. 00885 * 00886 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00887 * 00888 * @note delimeters are matched on a per byte basis. 00889 */ 00890 int MicroBitSerial::eventOn(ManagedString delimeters, MicroBitSerialMode mode) 00891 { 00892 if(mode == SYNC_SPINWAIT) 00893 return MICROBIT_INVALID_PARAMETER; 00894 00895 //configure our head match... 00896 this->delimeters = delimeters; 00897 00898 //block! 00899 if(mode == SYNC_SLEEP) 00900 fiber_wait_for_event(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH); 00901 00902 return MICROBIT_OK; 00903 } 00904 00905 /** 00906 * Determines whether there is any data waiting in our Rx buffer. 00907 * 00908 * @return 1 if we have space, 0 if we do not. 00909 * 00910 * @note We do not wrap the super's readable() method as we don't want to 00911 * interfere with communities that use manual calls to serial.readable(). 00912 */ 00913 int MicroBitSerial::isReadable() 00914 { 00915 return (rxBuffTail != rxBuffHead) ? 1 : 0; 00916 } 00917 00918 /** 00919 * Determines if we have space in our txBuff. 00920 * 00921 * @return 1 if we have space, 0 if we do not. 00922 * 00923 * @note We do not wrap the super's writeable() method as we don't want to 00924 * interfere with communities that use manual calls to serial.writeable(). 00925 */ 00926 int MicroBitSerial::isWriteable() 00927 { 00928 return (txBuffHead != (txBuffTail - 1)) ? 1 : 0; 00929 } 00930 00931 /** 00932 * Reconfigures the size of our rxBuff 00933 * 00934 * @param size the new size for our rxBuff 00935 * 00936 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 00937 * for reception, otherwise MICROBIT_OK. 00938 */ 00939 int MicroBitSerial::setRxBufferSize(uint8_t size) 00940 { 00941 if(rxInUse()) 00942 return MICROBIT_SERIAL_IN_USE; 00943 00944 lockRx(); 00945 00946 this->rxBuffSize = size; 00947 00948 int result = initialiseRx(); 00949 00950 unlockRx(); 00951 00952 return result; 00953 } 00954 00955 /** 00956 * Reconfigures the size of our txBuff 00957 * 00958 * @param size the new size for our txBuff 00959 * 00960 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 00961 * for transmission, otherwise MICROBIT_OK. 00962 */ 00963 int MicroBitSerial::setTxBufferSize(uint8_t size) 00964 { 00965 if(txInUse()) 00966 return MICROBIT_SERIAL_IN_USE; 00967 00968 lockTx(); 00969 00970 this->txBuffSize = size; 00971 00972 int result = initialiseTx(); 00973 00974 unlockTx(); 00975 00976 return result; 00977 } 00978 00979 /** 00980 * The size of our rx buffer in bytes. 00981 * 00982 * @return the current size of rxBuff in bytes 00983 */ 00984 int MicroBitSerial::getRxBufferSize() 00985 { 00986 return this->rxBuffSize; 00987 } 00988 00989 /** 00990 * The size of our tx buffer in bytes. 00991 * 00992 * @return the current size of txBuff in bytes 00993 */ 00994 int MicroBitSerial::getTxBufferSize() 00995 { 00996 return this->txBuffSize; 00997 } 00998 00999 /** 01000 * Sets the tail to match the head of our circular buffer for reception, 01001 * effectively clearing the reception buffer. 01002 * 01003 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 01004 * for reception, otherwise MICROBIT_OK. 01005 */ 01006 int MicroBitSerial::clearRxBuffer() 01007 { 01008 if(rxInUse()) 01009 return MICROBIT_SERIAL_IN_USE; 01010 01011 lockRx(); 01012 01013 rxBuffTail = rxBuffHead; 01014 01015 unlockRx(); 01016 01017 return MICROBIT_OK; 01018 } 01019 01020 /** 01021 * Sets the tail to match the head of our circular buffer for transmission, 01022 * effectively clearing the transmission buffer. 01023 * 01024 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 01025 * for transmission, otherwise MICROBIT_OK. 01026 */ 01027 int MicroBitSerial::clearTxBuffer() 01028 { 01029 if(txInUse()) 01030 return MICROBIT_SERIAL_IN_USE; 01031 01032 lockTx(); 01033 01034 txBuffTail = txBuffHead; 01035 01036 unlockTx(); 01037 01038 return MICROBIT_OK; 01039 } 01040 01041 /** 01042 * The number of bytes currently stored in our rx buffer waiting to be digested, 01043 * by the user. 01044 * 01045 * @return The currently buffered number of bytes in our rxBuff. 01046 */ 01047 int MicroBitSerial::rxBufferedSize() 01048 { 01049 if(rxBuffTail > rxBuffHead) 01050 return (rxBuffSize - rxBuffTail) + rxBuffHead; 01051 01052 return rxBuffHead - rxBuffTail; 01053 } 01054 01055 /** 01056 * The number of bytes currently stored in our tx buffer waiting to be transmitted 01057 * by the hardware. 01058 * 01059 * @return The currently buffered number of bytes in our txBuff. 01060 */ 01061 int MicroBitSerial::txBufferedSize() 01062 { 01063 if(txBuffTail > txBuffHead) 01064 return (txBuffSize - txBuffTail) + txBuffHead; 01065 01066 return txBuffHead - txBuffTail; 01067 } 01068 01069 /** 01070 * Determines if the serial bus is currently in use by another fiber for reception. 01071 * 01072 * @return The state of our mutex lock for reception. 01073 * 01074 * @note Only one fiber can call read at a time 01075 */ 01076 int MicroBitSerial::rxInUse() 01077 { 01078 return (status & MICROBIT_SERIAL_RX_IN_USE); 01079 } 01080 01081 /** 01082 * Determines if the serial bus is currently in use by another fiber for transmission. 01083 * 01084 * @return The state of our mutex lock for transmition. 01085 * 01086 * @note Only one fiber can call send at a time 01087 */ 01088 int MicroBitSerial::txInUse() 01089 { 01090 return (status & MICROBIT_SERIAL_TX_IN_USE); 01091 } 01092 01093 /** 01094 * Detaches a previously configured interrupt 01095 * 01096 * @param interruptType one of Serial::RxIrq or Serial::TxIrq 01097 */ 01098 void MicroBitSerial::detach(Serial::IrqType interruptType) 01099 { 01100 //we detach by sending a bad value to attach, for some weird reason... 01101 attach((MicroBitSerial *)NULL, &MicroBitSerial::dataReceived, interruptType); 01102 }
Generated on Tue Jul 12 2022 19:58:09 by 1.7.2