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