RTOS safe buffered serial driver
Fork of SerialDriver by
SerialDriver.cpp@4:3c0d0c37ad75, 2015-02-23 (annotated)
- Committer:
- rosterloh84
- Date:
- Mon Feb 23 13:06:32 2015 +0000
- Revision:
- 4:3c0d0c37ad75
- Parent:
- 3:ea9719695b6a
Added tx and rx overflow callback and message complete callback
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 | 0:cd0d79be0c1a | 4 | : SerialBase(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 | 0:cd0d79be0c1a | 45 | } |
BlazeX | 0:cd0d79be0c1a | 46 | |
BlazeX | 0:cd0d79be0c1a | 47 | int SerialDriver::putc(int c, unsigned int timeoutMs) |
BlazeX | 0:cd0d79be0c1a | 48 | { |
BlazeX | 0:cd0d79be0c1a | 49 | // critical section, isr could modify cursors |
BlazeX | 0:cd0d79be0c1a | 50 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 51 | |
BlazeX | 0:cd0d79be0c1a | 52 | if(isTxBufferFull()) |
BlazeX | 0:cd0d79be0c1a | 53 | { |
BlazeX | 0:cd0d79be0c1a | 54 | // wait for free space |
BlazeX | 0:cd0d79be0c1a | 55 | while(semTxBufferFull.wait(0) > 0); // clear semaphore |
BlazeX | 0:cd0d79be0c1a | 56 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 57 | |
BlazeX | 0:cd0d79be0c1a | 58 | // let isr work |
BlazeX | 0:cd0d79be0c1a | 59 | semTxBufferFull.wait(timeoutMs); |
BlazeX | 0:cd0d79be0c1a | 60 | |
BlazeX | 0:cd0d79be0c1a | 61 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 62 | if(isTxBufferFull()) // still full? drop byte! |
BlazeX | 0:cd0d79be0c1a | 63 | { |
BlazeX | 3:ea9719695b6a | 64 | numTxDrops++; |
BlazeX | 0:cd0d79be0c1a | 65 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 66 | return 0; |
BlazeX | 0:cd0d79be0c1a | 67 | } |
BlazeX | 0:cd0d79be0c1a | 68 | } |
BlazeX | 0:cd0d79be0c1a | 69 | |
BlazeX | 0:cd0d79be0c1a | 70 | // write this byte to tx buffer |
BlazeX | 0:cd0d79be0c1a | 71 | txBuffer[txIn]= (unsigned char)c; |
BlazeX | 0:cd0d79be0c1a | 72 | txIn= (txIn+1) % txBufferLength; |
BlazeX | 0:cd0d79be0c1a | 73 | txCount++; |
BlazeX | 0:cd0d79be0c1a | 74 | |
BlazeX | 0:cd0d79be0c1a | 75 | // its over, isr can come |
BlazeX | 0:cd0d79be0c1a | 76 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 77 | |
BlazeX | 0:cd0d79be0c1a | 78 | // Let's write (isr will check writeability itself) |
BlazeX | 0:cd0d79be0c1a | 79 | onTxIrq(); |
BlazeX | 0:cd0d79be0c1a | 80 | |
BlazeX | 0:cd0d79be0c1a | 81 | return 1; |
BlazeX | 0:cd0d79be0c1a | 82 | } |
BlazeX | 0:cd0d79be0c1a | 83 | |
BlazeX | 0:cd0d79be0c1a | 84 | void SerialDriver::onTxIrq() |
BlazeX | 0:cd0d79be0c1a | 85 | { |
BlazeX | 0:cd0d79be0c1a | 86 | // prevent fire another TxIrq now |
BlazeX | 0:cd0d79be0c1a | 87 | disableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 88 | |
BlazeX | 0:cd0d79be0c1a | 89 | // write as long as you can |
BlazeX | 0:cd0d79be0c1a | 90 | bool wasFull= isTxBufferFull(); |
BlazeX | 0:cd0d79be0c1a | 91 | while(SerialBase::writeable() && !isTxBufferEmtpy()) |
BlazeX | 0:cd0d79be0c1a | 92 | { |
BlazeX | 0:cd0d79be0c1a | 93 | // take byte from tx buffer and put it out |
BlazeX | 0:cd0d79be0c1a | 94 | SerialBase::_base_putc(txBuffer[txOut]); |
BlazeX | 0:cd0d79be0c1a | 95 | txOut= (txOut+1) % txBufferLength; |
BlazeX | 0:cd0d79be0c1a | 96 | txCount--; |
BlazeX | 0:cd0d79be0c1a | 97 | } |
BlazeX | 0:cd0d79be0c1a | 98 | |
BlazeX | 0:cd0d79be0c1a | 99 | if(wasFull && !isTxBufferFull()) // more bytes can come |
BlazeX | 0:cd0d79be0c1a | 100 | semTxBufferFull.release(); |
BlazeX | 0:cd0d79be0c1a | 101 | |
BlazeX | 0:cd0d79be0c1a | 102 | // ok, let's wait for next writability |
BlazeX | 0:cd0d79be0c1a | 103 | enableTxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 104 | } |
BlazeX | 0:cd0d79be0c1a | 105 | |
BlazeX | 0:cd0d79be0c1a | 106 | |
BlazeX | 0:cd0d79be0c1a | 107 | int SerialDriver::getc(unsigned int timeoutMs) |
BlazeX | 0:cd0d79be0c1a | 108 | { |
BlazeX | 0:cd0d79be0c1a | 109 | // Let's read (isr will check readability itself) |
BlazeX | 0:cd0d79be0c1a | 110 | onRxIrq(); |
BlazeX | 0:cd0d79be0c1a | 111 | |
BlazeX | 0:cd0d79be0c1a | 112 | // critical section, isr could modify cursors |
BlazeX | 0:cd0d79be0c1a | 113 | disableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 114 | |
BlazeX | 0:cd0d79be0c1a | 115 | if(isRxBufferEmpty()) |
BlazeX | 0:cd0d79be0c1a | 116 | { |
BlazeX | 0:cd0d79be0c1a | 117 | // wait for new byte |
BlazeX | 0:cd0d79be0c1a | 118 | while(semRxBufferEmpty.wait(0) > 0); // clear semaphore |
BlazeX | 0:cd0d79be0c1a | 119 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 120 | |
BlazeX | 0:cd0d79be0c1a | 121 | // let isr work |
BlazeX | 0:cd0d79be0c1a | 122 | semRxBufferEmpty.wait(timeoutMs); |
BlazeX | 0:cd0d79be0c1a | 123 | |
BlazeX | 0:cd0d79be0c1a | 124 | disableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 125 | if(isRxBufferEmpty()) // still empty? nothing received! |
BlazeX | 0:cd0d79be0c1a | 126 | { |
BlazeX | 0:cd0d79be0c1a | 127 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 128 | return -1; |
BlazeX | 0:cd0d79be0c1a | 129 | } |
BlazeX | 0:cd0d79be0c1a | 130 | } |
BlazeX | 0:cd0d79be0c1a | 131 | |
BlazeX | 0:cd0d79be0c1a | 132 | // get byte from rx buffer |
BlazeX | 0:cd0d79be0c1a | 133 | int c= (int)rxBuffer[rxOut]; |
BlazeX | 0:cd0d79be0c1a | 134 | rxOut= (rxOut+1) % rxBufferLength; |
BlazeX | 0:cd0d79be0c1a | 135 | rxCount--; |
BlazeX | 0:cd0d79be0c1a | 136 | |
BlazeX | 0:cd0d79be0c1a | 137 | // its over, isr can come |
BlazeX | 0:cd0d79be0c1a | 138 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 139 | |
BlazeX | 0:cd0d79be0c1a | 140 | return c; |
BlazeX | 0:cd0d79be0c1a | 141 | } |
BlazeX | 0:cd0d79be0c1a | 142 | |
BlazeX | 0:cd0d79be0c1a | 143 | void SerialDriver::onRxIrq() |
BlazeX | 0:cd0d79be0c1a | 144 | { |
BlazeX | 0:cd0d79be0c1a | 145 | // prevent fire another RxIrq now |
BlazeX | 0:cd0d79be0c1a | 146 | disableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 147 | |
BlazeX | 0:cd0d79be0c1a | 148 | // read as long as you can |
BlazeX | 0:cd0d79be0c1a | 149 | bool wasEmpty= isRxBufferEmpty(); |
BlazeX | 0:cd0d79be0c1a | 150 | while(SerialBase::readable()) |
BlazeX | 0:cd0d79be0c1a | 151 | { |
BlazeX | 0:cd0d79be0c1a | 152 | // get byte and store it to the RX buffer |
BlazeX | 0:cd0d79be0c1a | 153 | int c= SerialBase::_base_getc(); |
BlazeX | 0:cd0d79be0c1a | 154 | if(!isRxBufferFull()) |
BlazeX | 0:cd0d79be0c1a | 155 | { |
BlazeX | 0:cd0d79be0c1a | 156 | rxBuffer[rxIn]= (unsigned char)c; |
BlazeX | 0:cd0d79be0c1a | 157 | rxIn= (rxIn+1) % rxBufferLength; |
BlazeX | 0:cd0d79be0c1a | 158 | rxCount++; |
rosterloh84 | 4:3c0d0c37ad75 | 159 | if ('\n' == c) { |
rosterloh84 | 4:3c0d0c37ad75 | 160 | _callback_auto_detect.call(); |
rosterloh84 | 4:3c0d0c37ad75 | 161 | } |
BlazeX | 3:ea9719695b6a | 162 | } |
rosterloh84 | 4:3c0d0c37ad75 | 163 | else { // drop byte :( |
BlazeX | 3:ea9719695b6a | 164 | numRxDrops++; |
rosterloh84 | 4:3c0d0c37ad75 | 165 | _callback_rx_overflow.call(); |
rosterloh84 | 4:3c0d0c37ad75 | 166 | } |
BlazeX | 0:cd0d79be0c1a | 167 | } |
BlazeX | 0:cd0d79be0c1a | 168 | |
BlazeX | 0:cd0d79be0c1a | 169 | if(wasEmpty && !isRxBufferEmpty()) // more bytes can go |
BlazeX | 0:cd0d79be0c1a | 170 | semRxBufferEmpty.release(); |
BlazeX | 0:cd0d79be0c1a | 171 | |
BlazeX | 0:cd0d79be0c1a | 172 | // ok, let's wait for next readability |
BlazeX | 0:cd0d79be0c1a | 173 | enableRxInterrupt(); |
BlazeX | 0:cd0d79be0c1a | 174 | } |
BlazeX | 0:cd0d79be0c1a | 175 | |
BlazeX | 0:cd0d79be0c1a | 176 | |
BlazeX | 0:cd0d79be0c1a | 177 | int SerialDriver::write(const unsigned char * buffer, const unsigned int length, bool block) |
BlazeX | 0:cd0d79be0c1a | 178 | { |
BlazeX | 0:cd0d79be0c1a | 179 | // try to put all bytes |
BlazeX | 0:cd0d79be0c1a | 180 | for(int i= 0; i < length; i++) |
BlazeX | 0:cd0d79be0c1a | 181 | if(!putc(buffer[i], block ? osWaitForever : 0)) |
BlazeX | 0:cd0d79be0c1a | 182 | return i; // putc failed, but already put i bytes |
BlazeX | 0:cd0d79be0c1a | 183 | |
BlazeX | 0:cd0d79be0c1a | 184 | return length; // put all bytes |
BlazeX | 0:cd0d79be0c1a | 185 | } |
BlazeX | 0:cd0d79be0c1a | 186 | |
BlazeX | 0:cd0d79be0c1a | 187 | int SerialDriver::read(unsigned char * buffer, const unsigned int length, bool block) |
BlazeX | 0:cd0d79be0c1a | 188 | { |
BlazeX | 0:cd0d79be0c1a | 189 | // try to get all bytes |
BlazeX | 0:cd0d79be0c1a | 190 | int c; |
BlazeX | 0:cd0d79be0c1a | 191 | for(int i= 0; i < length; i++) |
BlazeX | 0:cd0d79be0c1a | 192 | { |
BlazeX | 0:cd0d79be0c1a | 193 | c= getc(block ? osWaitForever : 0); |
BlazeX | 0:cd0d79be0c1a | 194 | if(c < 0) |
BlazeX | 0:cd0d79be0c1a | 195 | return i; // getc failed, but already got i bytes |
BlazeX | 0:cd0d79be0c1a | 196 | buffer[i]= (unsigned char)c; |
BlazeX | 0:cd0d79be0c1a | 197 | } |
BlazeX | 0:cd0d79be0c1a | 198 | |
BlazeX | 0:cd0d79be0c1a | 199 | return length; // got all bytes |
BlazeX | 0:cd0d79be0c1a | 200 | } |
BlazeX | 0:cd0d79be0c1a | 201 | |
BlazeX | 0:cd0d79be0c1a | 202 | |
BlazeX | 0:cd0d79be0c1a | 203 | int SerialDriver::puts(const char * str, bool block) |
BlazeX | 0:cd0d79be0c1a | 204 | { |
BlazeX | 0:cd0d79be0c1a | 205 | // the same as write, but get length from strlen |
BlazeX | 0:cd0d79be0c1a | 206 | const int len= strlen(str); |
BlazeX | 0:cd0d79be0c1a | 207 | return write((const unsigned char *)str, len, block); |
BlazeX | 0:cd0d79be0c1a | 208 | } |
BlazeX | 0:cd0d79be0c1a | 209 | |
BlazeX | 0:cd0d79be0c1a | 210 | int SerialDriver::printf(const char * format, ...) |
BlazeX | 0:cd0d79be0c1a | 211 | { |
BlazeX | 0:cd0d79be0c1a | 212 | // Parts of this are copied from mbed RawSerial ;) |
BlazeX | 0:cd0d79be0c1a | 213 | std::va_list arg; |
BlazeX | 0:cd0d79be0c1a | 214 | va_start(arg, format); |
BlazeX | 0:cd0d79be0c1a | 215 | |
BlazeX | 0:cd0d79be0c1a | 216 | int length= vsnprintf(NULL, 0, format, arg); |
BlazeX | 0:cd0d79be0c1a | 217 | char *temp = new char[length + 1]; |
BlazeX | 0:cd0d79be0c1a | 218 | vsprintf(temp, format, arg); |
BlazeX | 0:cd0d79be0c1a | 219 | puts(temp, true); |
BlazeX | 0:cd0d79be0c1a | 220 | delete[] temp; |
BlazeX | 0:cd0d79be0c1a | 221 | |
BlazeX | 0:cd0d79be0c1a | 222 | va_end(arg); |
BlazeX | 0:cd0d79be0c1a | 223 | return length; |
BlazeX | 0:cd0d79be0c1a | 224 | } |
BlazeX | 0:cd0d79be0c1a | 225 | |
BlazeX | 3:ea9719695b6a | 226 | // still thinking of XTN |