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.
SerialDriver.cpp@8:903a4162a0d1, 2015-10-29 (annotated)
- Committer:
- BlazeX
- Date:
- Thu Oct 29 19:38:11 2015 +0000
- Revision:
- 8:903a4162a0d1
- Parent:
- 7:ce54b429086f
Beta Update: Could SerialDriver inherit RawSerial rather than SerialBase? Maybe, let's try!
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
BlazeX | 0:cd0d79be0c1a | 1 | #include "SerialDriver.h" |
BlazeX | 0:cd0d79be0c1a | 2 | |
BlazeX | 0:cd0d79be0c1a | 3 | SerialDriver::SerialDriver(PinName txPin, PinName rxPin, int txBufferLength_, int rxBufferLength_, unsigned char * txBuffer_, unsigned char * rxBuffer_) |
BlazeX | 8:903a4162a0d1 | 4 | : RawSerial(txPin, rxPin), semTxBufferFull(0), semRxBufferEmpty(0) |
BlazeX | 0:cd0d79be0c1a | 5 | { |
BlazeX | 0:cd0d79be0c1a | 6 | // check buffer length |
BlazeX | 0:cd0d79be0c1a | 7 | txBufferLength= txBufferLength_; |
BlazeX | 0:cd0d79be0c1a | 8 | if(txBufferLength <= 1) |
BlazeX | 0:cd0d79be0c1a | 9 | error("TX buffer length must be > 1 !"); |
BlazeX | 0:cd0d79be0c1a | 10 | |
BlazeX | 0:cd0d79be0c1a | 11 | rxBufferLength= rxBufferLength_; |
BlazeX | 0:cd0d79be0c1a | 12 | if(rxBufferLength <= 1) |
BlazeX | 0:cd0d79be0c1a | 13 | error("RX buffer length must be > 1 !"); |
BlazeX | 0:cd0d79be0c1a | 14 | |
BlazeX | 0:cd0d79be0c1a | 15 | // take or allocate buffer |
BlazeX | 0:cd0d79be0c1a | 16 | txBuffer= txBuffer_; |
BlazeX | 0:cd0d79be0c1a | 17 | if(txBuffer == NULL) |
BlazeX | 0:cd0d79be0c1a | 18 | { |
BlazeX | 0:cd0d79be0c1a | 19 | txBuffer= new unsigned char[txBufferLength]; |
BlazeX | 0:cd0d79be0c1a | 20 | if(txBuffer == NULL) |
BlazeX | 0:cd0d79be0c1a | 21 | error("Cannot allocate TX buffer!"); |
BlazeX | 0:cd0d79be0c1a | 22 | } |
BlazeX | 0:cd0d79be0c1a | 23 | |
BlazeX | 0:cd0d79be0c1a | 24 | rxBuffer= rxBuffer_; |
BlazeX | 0:cd0d79be0c1a | 25 | if(rxBuffer == NULL) |
BlazeX | 0:cd0d79be0c1a | 26 | { |
BlazeX | 0:cd0d79be0c1a | 27 | rxBuffer= new unsigned char[rxBufferLength]; |
BlazeX | 0:cd0d79be0c1a | 28 | if(rxBuffer == NULL) |
BlazeX | 0:cd0d79be0c1a | 29 | error("Cannot allocate RX buffer!"); |
BlazeX | 0:cd0d79be0c1a | 30 | } |
BlazeX | 0:cd0d79be0c1a | 31 | |
BlazeX | 0:cd0d79be0c1a | 32 | |
BlazeX | 0:cd0d79be0c1a | 33 | // reset cursors |
BlazeX | 0:cd0d79be0c1a | 34 | txIn= txOut= 0; |
BlazeX | 0:cd0d79be0c1a | 35 | rxIn= rxOut= 0; |
BlazeX | 0:cd0d79be0c1a | 36 | txCount= rxCount= 0; |
BlazeX | 0:cd0d79be0c1a | 37 | |
BlazeX | 3:ea9719695b6a | 38 | // reset drop counters |
BlazeX | 3:ea9719695b6a | 39 | numTxDrops= 0; |
BlazeX | 3:ea9719695b6a | 40 | numRxDrops= 0; |
BlazeX | 3:ea9719695b6a | 41 | |
BlazeX | 0:cd0d79be0c1a | 42 | // attach interrupt routines |
BlazeX | 0:cd0d79be0c1a | 43 | attach(this, &SerialDriver::onTxIrq, TxIrq); |
BlazeX | 0:cd0d79be0c1a | 44 | attach(this, &SerialDriver::onRxIrq, RxIrq); |
BlazeX | 4:a41e47716932 | 45 | |
BlazeX | 4:a41e47716932 | 46 | // we need tx interrupt not yet |
BlazeX | 4:a41e47716932 | 47 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 48 | } |
BlazeX | 0:cd0d79be0c1a | 49 | |
BlazeX | 0:cd0d79be0c1a | 50 | int SerialDriver::putc(int c, unsigned int timeoutMs) |
BlazeX | 0:cd0d79be0c1a | 51 | { |
BlazeX | 0:cd0d79be0c1a | 52 | // critical section, isr could modify cursors |
BlazeX | 0:cd0d79be0c1a | 53 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 54 | |
BlazeX | 0:cd0d79be0c1a | 55 | if(isTxBufferFull()) |
BlazeX | 0:cd0d79be0c1a | 56 | { |
BlazeX | 0:cd0d79be0c1a | 57 | // wait for free space |
BlazeX | 0:cd0d79be0c1a | 58 | while(semTxBufferFull.wait(0) > 0); // clear semaphore |
BlazeX | 0:cd0d79be0c1a | 59 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 60 | |
BlazeX | 0:cd0d79be0c1a | 61 | // let isr work |
BlazeX | 0:cd0d79be0c1a | 62 | semTxBufferFull.wait(timeoutMs); |
BlazeX | 0:cd0d79be0c1a | 63 | |
BlazeX | 0:cd0d79be0c1a | 64 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 65 | if(isTxBufferFull()) // still full? drop byte! |
BlazeX | 0:cd0d79be0c1a | 66 | { |
BlazeX | 3:ea9719695b6a | 67 | numTxDrops++; |
BlazeX | 0:cd0d79be0c1a | 68 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 69 | return 0; |
BlazeX | 0:cd0d79be0c1a | 70 | } |
BlazeX | 0:cd0d79be0c1a | 71 | } |
BlazeX | 0:cd0d79be0c1a | 72 | |
BlazeX | 0:cd0d79be0c1a | 73 | // write this byte to tx buffer |
BlazeX | 0:cd0d79be0c1a | 74 | txBuffer[txIn]= (unsigned char)c; |
BlazeX | 0:cd0d79be0c1a | 75 | txIn= (txIn+1) % txBufferLength; |
BlazeX | 0:cd0d79be0c1a | 76 | txCount++; |
BlazeX | 0:cd0d79be0c1a | 77 | |
BlazeX | 0:cd0d79be0c1a | 78 | // its over, isr can come |
BlazeX | 0:cd0d79be0c1a | 79 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 80 | |
BlazeX | 0:cd0d79be0c1a | 81 | // Let's write (isr will check writeability itself) |
BlazeX | 0:cd0d79be0c1a | 82 | onTxIrq(); |
BlazeX | 0:cd0d79be0c1a | 83 | |
BlazeX | 0:cd0d79be0c1a | 84 | return 1; |
BlazeX | 0:cd0d79be0c1a | 85 | } |
BlazeX | 0:cd0d79be0c1a | 86 | |
BlazeX | 0:cd0d79be0c1a | 87 | void SerialDriver::onTxIrq() |
BlazeX | 0:cd0d79be0c1a | 88 | { |
BlazeX | 0:cd0d79be0c1a | 89 | // prevent fire another TxIrq now |
BlazeX | 0:cd0d79be0c1a | 90 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 91 | |
BlazeX | 0:cd0d79be0c1a | 92 | // write as long as you can |
BlazeX | 0:cd0d79be0c1a | 93 | bool wasFull= isTxBufferFull(); |
BlazeX | 5:cb9007ef1a30 | 94 | while(SerialBase::writeable() && !isTxBufferEmtpy()) |
BlazeX | 0:cd0d79be0c1a | 95 | { |
BlazeX | 0:cd0d79be0c1a | 96 | // take byte from tx buffer and put it out |
BlazeX | 0:cd0d79be0c1a | 97 | SerialBase::_base_putc(txBuffer[txOut]); |
BlazeX | 0:cd0d79be0c1a | 98 | txOut= (txOut+1) % txBufferLength; |
BlazeX | 0:cd0d79be0c1a | 99 | txCount--; |
BlazeX | 0:cd0d79be0c1a | 100 | } |
BlazeX | 0:cd0d79be0c1a | 101 | |
BlazeX | 0:cd0d79be0c1a | 102 | if(wasFull && !isTxBufferFull()) // more bytes can come |
BlazeX | 0:cd0d79be0c1a | 103 | semTxBufferFull.release(); |
BlazeX | 0:cd0d79be0c1a | 104 | |
BlazeX | 5:cb9007ef1a30 | 105 | // ok, let's wait for next writability, |
BlazeX | 5:cb9007ef1a30 | 106 | // if there is something to send, |
BlazeX | 4:a41e47716932 | 107 | // else we need tx interrupt not yet |
BlazeX | 7:ce54b429086f | 108 | if(!isTxBufferEmtpy()) |
BlazeX | 4:a41e47716932 | 109 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 110 | } |
BlazeX | 0:cd0d79be0c1a | 111 | |
BlazeX | 0:cd0d79be0c1a | 112 | |
BlazeX | 0:cd0d79be0c1a | 113 | int SerialDriver::getc(unsigned int timeoutMs) |
BlazeX | 0:cd0d79be0c1a | 114 | { |
BlazeX | 0:cd0d79be0c1a | 115 | // Let's read (isr will check readability itself) |
BlazeX | 0:cd0d79be0c1a | 116 | onRxIrq(); |
BlazeX | 0:cd0d79be0c1a | 117 | |
BlazeX | 0:cd0d79be0c1a | 118 | // critical section, isr could modify cursors |
BlazeX | 0:cd0d79be0c1a | 119 | disableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 120 | |
BlazeX | 0:cd0d79be0c1a | 121 | if(isRxBufferEmpty()) |
BlazeX | 0:cd0d79be0c1a | 122 | { |
BlazeX | 0:cd0d79be0c1a | 123 | // wait for new byte |
BlazeX | 0:cd0d79be0c1a | 124 | while(semRxBufferEmpty.wait(0) > 0); // clear semaphore |
BlazeX | 0:cd0d79be0c1a | 125 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 126 | |
BlazeX | 0:cd0d79be0c1a | 127 | // let isr work |
BlazeX | 0:cd0d79be0c1a | 128 | semRxBufferEmpty.wait(timeoutMs); |
BlazeX | 0:cd0d79be0c1a | 129 | |
BlazeX | 0:cd0d79be0c1a | 130 | disableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 131 | if(isRxBufferEmpty()) // still empty? nothing received! |
BlazeX | 0:cd0d79be0c1a | 132 | { |
BlazeX | 0:cd0d79be0c1a | 133 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 134 | return -1; |
BlazeX | 0:cd0d79be0c1a | 135 | } |
BlazeX | 0:cd0d79be0c1a | 136 | } |
BlazeX | 0:cd0d79be0c1a | 137 | |
BlazeX | 0:cd0d79be0c1a | 138 | // get byte from rx buffer |
BlazeX | 0:cd0d79be0c1a | 139 | int c= (int)rxBuffer[rxOut]; |
BlazeX | 0:cd0d79be0c1a | 140 | rxOut= (rxOut+1) % rxBufferLength; |
BlazeX | 0:cd0d79be0c1a | 141 | rxCount--; |
BlazeX | 0:cd0d79be0c1a | 142 | |
BlazeX | 0:cd0d79be0c1a | 143 | // its over, isr can come |
BlazeX | 0:cd0d79be0c1a | 144 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 145 | |
BlazeX | 0:cd0d79be0c1a | 146 | return c; |
BlazeX | 0:cd0d79be0c1a | 147 | } |
BlazeX | 0:cd0d79be0c1a | 148 | |
BlazeX | 0:cd0d79be0c1a | 149 | void SerialDriver::onRxIrq() |
BlazeX | 0:cd0d79be0c1a | 150 | { |
BlazeX | 0:cd0d79be0c1a | 151 | // prevent fire another RxIrq now |
BlazeX | 0:cd0d79be0c1a | 152 | disableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 153 | |
BlazeX | 0:cd0d79be0c1a | 154 | // read as long as you can |
BlazeX | 0:cd0d79be0c1a | 155 | bool wasEmpty= isRxBufferEmpty(); |
BlazeX | 0:cd0d79be0c1a | 156 | while(SerialBase::readable()) |
BlazeX | 0:cd0d79be0c1a | 157 | { |
BlazeX | 0:cd0d79be0c1a | 158 | // get byte and store it to the RX buffer |
BlazeX | 0:cd0d79be0c1a | 159 | int c= SerialBase::_base_getc(); |
BlazeX | 0:cd0d79be0c1a | 160 | if(!isRxBufferFull()) |
BlazeX | 0:cd0d79be0c1a | 161 | { |
BlazeX | 0:cd0d79be0c1a | 162 | rxBuffer[rxIn]= (unsigned char)c; |
BlazeX | 0:cd0d79be0c1a | 163 | rxIn= (rxIn+1) % rxBufferLength; |
BlazeX | 0:cd0d79be0c1a | 164 | rxCount++; |
BlazeX | 3:ea9719695b6a | 165 | } |
BlazeX | 3:ea9719695b6a | 166 | else // drop byte :( |
BlazeX | 3:ea9719695b6a | 167 | numRxDrops++; |
BlazeX | 0:cd0d79be0c1a | 168 | } |
BlazeX | 0:cd0d79be0c1a | 169 | |
BlazeX | 0:cd0d79be0c1a | 170 | if(wasEmpty && !isRxBufferEmpty()) // more bytes can go |
BlazeX | 0:cd0d79be0c1a | 171 | semRxBufferEmpty.release(); |
BlazeX | 0:cd0d79be0c1a | 172 | |
BlazeX | 0:cd0d79be0c1a | 173 | // ok, let's wait for next readability |
BlazeX | 0:cd0d79be0c1a | 174 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 175 | } |
BlazeX | 0:cd0d79be0c1a | 176 | |
BlazeX | 0:cd0d79be0c1a | 177 | |
BlazeX | 0:cd0d79be0c1a | 178 | int SerialDriver::write(const unsigned char * buffer, const unsigned int length, bool block) |
BlazeX | 0:cd0d79be0c1a | 179 | { |
BlazeX | 0:cd0d79be0c1a | 180 | // try to put all bytes |
BlazeX | 0:cd0d79be0c1a | 181 | for(int i= 0; i < length; i++) |
BlazeX | 0:cd0d79be0c1a | 182 | if(!putc(buffer[i], block ? osWaitForever : 0)) |
BlazeX | 0:cd0d79be0c1a | 183 | return i; // putc failed, but already put i bytes |
BlazeX | 0:cd0d79be0c1a | 184 | |
BlazeX | 0:cd0d79be0c1a | 185 | return length; // put all bytes |
BlazeX | 0:cd0d79be0c1a | 186 | } |
BlazeX | 0:cd0d79be0c1a | 187 | |
BlazeX | 0:cd0d79be0c1a | 188 | int SerialDriver::read(unsigned char * buffer, const unsigned int length, bool block) |
BlazeX | 0:cd0d79be0c1a | 189 | { |
BlazeX | 0:cd0d79be0c1a | 190 | // try to get all bytes |
BlazeX | 0:cd0d79be0c1a | 191 | int c; |
BlazeX | 0:cd0d79be0c1a | 192 | for(int i= 0; i < length; i++) |
BlazeX | 0:cd0d79be0c1a | 193 | { |
BlazeX | 0:cd0d79be0c1a | 194 | c= getc(block ? osWaitForever : 0); |
BlazeX | 0:cd0d79be0c1a | 195 | if(c < 0) |
BlazeX | 0:cd0d79be0c1a | 196 | return i; // getc failed, but already got i bytes |
BlazeX | 0:cd0d79be0c1a | 197 | buffer[i]= (unsigned char)c; |
BlazeX | 0:cd0d79be0c1a | 198 | } |
BlazeX | 0:cd0d79be0c1a | 199 | |
BlazeX | 0:cd0d79be0c1a | 200 | return length; // got all bytes |
BlazeX | 0:cd0d79be0c1a | 201 | } |
BlazeX | 0:cd0d79be0c1a | 202 | |
BlazeX | 0:cd0d79be0c1a | 203 | |
BlazeX | 0:cd0d79be0c1a | 204 | int SerialDriver::puts(const char * str, bool block) |
BlazeX | 0:cd0d79be0c1a | 205 | { |
BlazeX | 0:cd0d79be0c1a | 206 | // the same as write, but get length from strlen |
BlazeX | 0:cd0d79be0c1a | 207 | const int len= strlen(str); |
BlazeX | 0:cd0d79be0c1a | 208 | return write((const unsigned char *)str, len, block); |
BlazeX | 0:cd0d79be0c1a | 209 | } |
BlazeX | 0:cd0d79be0c1a | 210 | |
BlazeX | 0:cd0d79be0c1a | 211 | int SerialDriver::printf(const char * format, ...) |
BlazeX | 0:cd0d79be0c1a | 212 | { |
BlazeX | 0:cd0d79be0c1a | 213 | // Parts of this are copied from mbed RawSerial ;) |
BlazeX | 0:cd0d79be0c1a | 214 | std::va_list arg; |
BlazeX | 0:cd0d79be0c1a | 215 | va_start(arg, format); |
BlazeX | 0:cd0d79be0c1a | 216 | |
BlazeX | 0:cd0d79be0c1a | 217 | int length= vsnprintf(NULL, 0, format, arg); |
BlazeX | 0:cd0d79be0c1a | 218 | char *temp = new char[length + 1]; |
BlazeX | 4:a41e47716932 | 219 | if(temp == NULL) |
BlazeX | 4:a41e47716932 | 220 | return 0; // I can't work like this! |
BlazeX | 6:bf5fb8677212 | 221 | |
BlazeX | 0:cd0d79be0c1a | 222 | vsprintf(temp, format, arg); |
BlazeX | 0:cd0d79be0c1a | 223 | puts(temp, true); |
BlazeX | 0:cd0d79be0c1a | 224 | delete[] temp; |
BlazeX | 0:cd0d79be0c1a | 225 | |
BlazeX | 0:cd0d79be0c1a | 226 | va_end(arg); |
BlazeX | 0:cd0d79be0c1a | 227 | return length; |
BlazeX | 0:cd0d79be0c1a | 228 | } |
BlazeX | 0:cd0d79be0c1a | 229 | |
BlazeX | 3:ea9719695b6a | 230 | // still thinking of XTN |
BlazeX | 8:903a4162a0d1 | 231 |