mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal

Dependencies:   BLE_API nRF51822 mbed-dev-bin

Dependents:   microbit Microbit IoTChallenge1 microbit ... more

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     // + 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 }