Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitSerial.cpp Source File

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 }