Buffered Serial Port Driver for RTOS

Dependents:   nucleo_cannonball PiballNeoController

Buffered Serial Port Driver for RTOS

  • ISR driven, ring buffered IO operation
  • IO operations are idle waiting, don't waste time in RTOS :D
  • Can use external buffers
  • Based on mbed RawSerial

Example

SerialDriver Example

#include "SerialDriver.h"

SerialDriver pc(USBTX, USBRX);

int main()
{
    // setup serial port
    pc.baud(9600);
    
    // print some text
    pc.puts("This is just a string.\r\n");
    pc.printf("But this is a %s with integer %i and float %f.\r\n", "formatted text", 123, 0.456f);
    
    // now lets behave like a null modem 
    while(1)
       pc.putc(pc.getc());
}

Look at the API Documentation for more Examples.

Dependencies

Import librarymbed

The official Mbed 2 C/C++ SDK provides the software platform and libraries to build your applications.

Import librarymbed-rtos

Official mbed Real Time Operating System based on the RTX implementation of the CMSIS-RTOS API open standard.

If you find a bug, please help me to fix it. Send me a message. You can help me a lot: Write a demo program that causes the bug reproducible.

Revision:
0:cd0d79be0c1a
Child:
3:ea9719695b6a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SerialDriver.cpp	Wed Jan 14 16:30:14 2015 +0000
@@ -0,0 +1,215 @@
+#include "SerialDriver.h"
+
+SerialDriver::SerialDriver(PinName txPin, PinName rxPin, int txBufferLength_, int rxBufferLength_, unsigned char * txBuffer_, unsigned char * rxBuffer_)
+    : SerialBase(txPin, rxPin), semTxBufferFull(0), semRxBufferEmpty(0)
+{    
+    // check buffer length
+    txBufferLength= txBufferLength_;
+    if(txBufferLength <= 1)
+        error("TX buffer length must be > 1 !");
+        
+    rxBufferLength= rxBufferLength_;
+    if(rxBufferLength <= 1)
+        error("RX buffer length must be > 1 !");
+    
+    // take or allocate buffer
+    txBuffer= txBuffer_;
+    if(txBuffer == NULL)
+    {
+        txBuffer= new unsigned char[txBufferLength];
+        if(txBuffer == NULL)
+            error("Cannot allocate TX buffer!");
+    }
+    
+    rxBuffer= rxBuffer_;
+    if(rxBuffer == NULL)
+    {
+        rxBuffer= new unsigned char[rxBufferLength];
+        if(rxBuffer == NULL)
+            error("Cannot allocate RX buffer!");
+    }
+        
+    
+    // reset cursors
+    txIn= txOut= 0;
+    rxIn= rxOut= 0;
+    txCount= rxCount= 0;
+    
+    // attach interrupt routines
+    attach(this, &SerialDriver::onTxIrq, TxIrq);
+    attach(this, &SerialDriver::onRxIrq, RxIrq);
+    
+}
+
+int SerialDriver::putc(int c, unsigned int timeoutMs)
+{
+    // critical section, isr could modify cursors
+    disableTxInterrupt();
+    
+    if(isTxBufferFull())
+    {
+        // wait for free space
+        while(semTxBufferFull.wait(0) > 0);    // clear semaphore
+        enableTxInterrupt();
+        
+        // let isr work
+        semTxBufferFull.wait(timeoutMs);
+        
+        disableTxInterrupt();
+        if(isTxBufferFull()) // still full? drop byte!
+        {
+            enableTxInterrupt();
+            return 0;
+        }
+    }
+    
+    // write this byte to tx buffer
+    txBuffer[txIn]= (unsigned char)c;
+    txIn= (txIn+1) % txBufferLength;
+    txCount++;
+    
+    // its over, isr can come
+    enableTxInterrupt();
+        
+    // Let's write (isr will check writeability itself)
+    onTxIrq();
+    
+    return 1;
+}
+
+void SerialDriver::onTxIrq()
+{
+    // prevent fire another TxIrq now
+    disableTxInterrupt();
+    
+    // write as long as you can
+    bool wasFull= isTxBufferFull();
+    while(SerialBase::writeable() && !isTxBufferEmtpy())
+    {
+        // take byte from tx buffer and put it out
+        SerialBase::_base_putc(txBuffer[txOut]);
+        txOut= (txOut+1) % txBufferLength;
+        txCount--;
+    }
+    
+    if(wasFull && !isTxBufferFull())   // more bytes can come
+        semTxBufferFull.release();
+    
+    // ok, let's wait for next writability
+    enableTxInterrupt();
+}
+
+
+int SerialDriver::getc(unsigned int timeoutMs)
+{    
+    // Let's read (isr will check readability itself)
+    onRxIrq();
+    
+    // critical section, isr could modify cursors
+    disableRxInterrupt();
+    
+    if(isRxBufferEmpty())
+    {
+        // wait for new byte
+        while(semRxBufferEmpty.wait(0) > 0);    // clear semaphore
+        enableRxInterrupt();
+        
+        // let isr work
+        semRxBufferEmpty.wait(timeoutMs);
+        
+        disableRxInterrupt();
+        if(isRxBufferEmpty()) // still empty? nothing received!
+        {
+            enableRxInterrupt();
+            return -1;
+        }
+    }
+    
+    // get byte from rx buffer
+    int c= (int)rxBuffer[rxOut];
+    rxOut= (rxOut+1) % rxBufferLength;
+    rxCount--;
+    
+    // its over, isr can come
+    enableRxInterrupt();
+    
+    return c;
+}
+
+void SerialDriver::onRxIrq()
+{
+    // prevent fire another RxIrq now
+    disableRxInterrupt();
+    
+    // read as long as you can
+    bool wasEmpty= isRxBufferEmpty();
+    while(SerialBase::readable())
+    {
+        // get byte and store it to the RX buffer
+        int c= SerialBase::_base_getc();
+        if(!isRxBufferFull())
+        {
+            rxBuffer[rxIn]= (unsigned char)c;
+            rxIn= (rxIn+1) % rxBufferLength;
+            rxCount++;
+        }   // else drop byte :(
+    }
+    
+    if(wasEmpty && !isRxBufferEmpty())   // more bytes can go
+        semRxBufferEmpty.release();
+    
+    // ok, let's wait for next readability
+    enableRxInterrupt();
+}
+
+
+int SerialDriver::write(const unsigned char * buffer, const unsigned int length, bool block)
+{
+    // try to put all bytes
+    for(int i= 0; i < length; i++)
+        if(!putc(buffer[i], block ? osWaitForever : 0))
+            return i; // putc failed, but already put i bytes
+    
+    return length;  // put all bytes
+}
+    
+int SerialDriver::read(unsigned char * buffer, const unsigned int length, bool block)
+{
+    // try to get all bytes
+    int c;
+    for(int i= 0; i < length; i++)
+    {
+        c= getc(block ? osWaitForever : 0);
+        if(c < 0)
+            return i; // getc failed, but already got i bytes
+        buffer[i]= (unsigned char)c;
+    }
+    
+    return length;  // got all bytes
+}
+
+
+int SerialDriver::puts(const char * str, bool block)
+{
+    // the same as write, but get length from strlen
+    const int len= strlen(str);
+    return write((const unsigned char *)str, len, block);
+}
+
+int SerialDriver::printf(const char * format, ...)
+{
+    // Parts of this are copied from mbed RawSerial ;)
+    std::va_list arg;
+    va_start(arg, format);
+    
+    int length= vsnprintf(NULL, 0, format, arg);
+    char *temp = new char[length + 1];
+    vsprintf(temp, format, arg);
+    puts(temp, true);
+    delete[] temp;
+    
+    va_end(arg);
+    return length;
+} 
+
+// for XTN