AT command firmware for MultiTech Dot devices.

Fork of mDot_AT_firmware by MultiTech

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATSerial.cpp Source File

ATSerial.cpp

00001 
00002 #include "ATSerial.h"
00003 #include "MTSLog.h"
00004 #include "Utils.h"
00005 
00006 using namespace mts;
00007 
00008 
00009 ATSerial::ATSerial(PinName txd, PinName rxd, PinName rts, PinName cts, int baud)
00010     : _serial(txd, rxd, baud),
00011     _tx_irq_enabled(false),
00012     _last_time(0),
00013     _esc_cnt(0),
00014     _esc_ch('+'),
00015     _escaped(false),
00016     _flow(false),       // Flow control disabled by default
00017     _rts(rts),
00018     _cts(cts)
00019 {
00020 
00021     if (rts != NC && cts != NC) {   // RTS and CTS must both be provided for flow control
00022         _flow = true;
00023         _rts = 0;   // Start with receive enabled
00024         _cts.fall(callback(this, &ATSerial::startWrite));   // Restart writes when able to send
00025         _hwm = mts_max(AT_SERIAL_RX_BUFFER_SIZE - 10, AT_SERIAL_RX_BUFFER_SIZE * 0.85);
00026         _lwm = AT_SERIAL_RX_BUFFER_SIZE * 0.3;
00027     }
00028 
00029     // Receive buffer
00030     _rxbuf = new mbed::CircularBuffer<char, AT_SERIAL_RX_BUFFER_SIZE>();
00031     // Transmit buffer
00032     _txbuf = new mbed::CircularBuffer<char, AT_SERIAL_TX_BUFFER_SIZE>();
00033 
00034     _timer.start();
00035     _serial.attach(callback(this, &ATSerial::handleRead), SerialBase::RxIrq);
00036 }
00037 
00038 ATSerial::~ATSerial()
00039 {
00040 }
00041 
00042 void ATSerial::baud(int baudrate) {
00043     _mutex.lock();
00044     _serial.baud(baudrate);
00045     _mutex.unlock();
00046 }
00047 
00048 void ATSerial::format(int bits, SerialBase::Parity parity, int stop_bits) {
00049     _mutex.lock();
00050     _serial.format(bits, parity, stop_bits);
00051     _mutex.unlock();
00052 }
00053 
00054 void ATSerial::flowControl(bool enable) {
00055     if (enable && (_rts != NC) && (_cts != NC)) {   // RTS and CTS must both be provided for flow control
00056         _flow = true;
00057         _rts = 0;   // Start with receive enabled
00058         _cts.fall(callback(this, &ATSerial::startWrite));   // Restart writes when able to send
00059         _hwm = mts_max(MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE - 10, MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE * 0.85);
00060         _lwm = MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE * 0.3;
00061     } else {
00062         _flow = false;
00063         _rts = 1;
00064     }
00065 }
00066 
00067 bool ATSerial::flowControl() {
00068     return _flow;
00069 }
00070 
00071 bool ATSerial::readable() {
00072     return !_rxbuf->empty();
00073 }
00074 
00075 bool ATSerial::writeable() {
00076     return !_txbuf->full();
00077 }
00078 
00079 void ATSerial::rxClear() {
00080     _mutex.lock();
00081     _rxbuf->reset();
00082     if (_flow) {
00083         _rts = 0;   // Allow receiving because receive buffer is now empty
00084     }
00085     _mutex.unlock();
00086 }
00087 
00088 void ATSerial::txClear() {
00089     _mutex.lock();
00090     _txbuf->reset();
00091     _mutex.unlock();
00092 }
00093 
00094 bool ATSerial::escaped() {
00095     _mutex.lock();
00096     std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(_timer.elapsed_time());
00097     std::chrono::milliseconds elapsed_ms = now - _last_time;
00098 
00099     // Have we seen three esc chars and 1 sec end guard has passed
00100     if (_escaped || (_esc_cnt == 3 && (elapsed_ms > 1s))) {
00101         _escaped = true;
00102 
00103     // Have we seen a couple esc chars but nothing in 500 ms
00104     } else if (_esc_cnt > 0 && _esc_cnt != 3 && elapsed_ms > 500ms) {
00105         // Write seen esc chars
00106         while (_esc_cnt) {
00107             _rxbuf->push(_esc_ch);
00108             _esc_cnt--;
00109         }
00110         _escaped = false;
00111     }
00112     _mutex.unlock();
00113 
00114     return _escaped;
00115 }
00116 
00117 void ATSerial::clearEscaped() {
00118     _mutex.lock();
00119     _esc_cnt = 0;
00120     _escaped = false;
00121     _mutex.unlock();
00122 }
00123 
00124 bool ATSerial::read(char& c) {
00125     return read(&c, 1) == 1;
00126 }
00127 
00128 int ATSerial::write(const char *buffer, size_t length) {
00129     _mutex.lock();
00130     size_t i = 0;
00131     while (i < length) {
00132         if (_txbuf->full()) {
00133             do {
00134                 _mutex.unlock();
00135                 thread_sleep_for(1);
00136                 _mutex.lock();
00137             } while (_txbuf->full());
00138         }
00139         while (i < length && !_txbuf->full())
00140         {
00141             _txbuf->push(buffer[i]);
00142             i++;
00143         }
00144         startWrite();   // Start writing data in tx buffer
00145     }
00146     _mutex.unlock();
00147     return i;
00148 }
00149 
00150 int ATSerial::writef(const char* format, ... ) {
00151     char buff[256];
00152 
00153     va_list ap;
00154     va_start(ap, format);
00155     int size = vsnprintf(buff, 256, format, ap);
00156     int n = write(buff, size);
00157     va_end(ap);
00158 
00159     return n;
00160 }
00161 
00162 int ATSerial::read(char *buffer, size_t length) {
00163     _mutex.lock();
00164     size_t r = 0;
00165     while (r < length) {
00166         if (_rxbuf->pop(buffer[r])) {
00167             r++;
00168         } else {
00169             break;
00170         }
00171     }
00172     if (_flow && _rts && _rxbuf->size() <= _lwm) {
00173         _rts = 0;   // RX buffer has room, clear RTS to continue receiving
00174     }
00175     _mutex.unlock();
00176     return r;
00177 }
00178 
00179 void ATSerial::startWrite()
00180 {
00181     core_util_critical_section_enter();
00182     if (!_tx_irq_enabled) {
00183         // only write to hardware in one place
00184         handleWrite();
00185         if (!_txbuf->empty()) {
00186             _serial.attach(callback(this, &ATSerial::handleWrite), SerialBase::TxIrq);
00187             _tx_irq_enabled = true;
00188         }
00189     }
00190     core_util_critical_section_exit();
00191 }
00192 
00193 void ATSerial::handleWrite()
00194 {
00195     char c;
00196     while (_serial.writeable()) {
00197         if (_flow && _cts) {
00198             break;  // Exit write loop when CTS is set, will resume when it is cleared
00199         }
00200 
00201         if (_txbuf->pop(c)) {
00202             _serial.write(&c, 1);
00203         } else {
00204             break;
00205         }
00206     }
00207 
00208     // Detach TX IRQ if there's no more data to write or CTS is set
00209     if (_tx_irq_enabled && (_txbuf->empty() || (_flow && _cts))) {
00210         _serial.attach(NULL, SerialBase::TxIrq);
00211         _tx_irq_enabled = false;
00212     }
00213 }
00214 
00215 
00216 void ATSerial::handleRead()
00217 {
00218     char byte;
00219     if (_serial.read(&byte, 1) < 1) { return; }
00220 
00221     std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(_timer.elapsed_time());
00222     std::chrono::milliseconds elapsed_ms = now - _last_time;
00223     _last_time = now;
00224 
00225     // Have we seen 3 esc chars but this char is before 1 sec end guard time
00226     if (_esc_cnt == 3 && (elapsed_ms < std::chrono::seconds(1))) {
00227         // Write the three chars we held back
00228         while (_esc_cnt) {
00229             _rxbuf->push(_esc_ch);
00230             _esc_cnt--;
00231         }
00232     } else if (byte == _esc_ch) {
00233         // Has 1 second passed before last char
00234         if (elapsed_ms > std::chrono::seconds(1)) {
00235             _esc_cnt = 1;
00236         // Is this second or third esc char
00237         } else if (_esc_cnt > 0 && _esc_cnt < 3) {
00238             _esc_cnt++;
00239         }
00240     } else if (_esc_cnt > 0) {
00241         // Write any esc chars held back
00242         while (_esc_cnt) {
00243             _rxbuf->push(_esc_ch);
00244             _esc_cnt--;
00245         }
00246     }
00247 
00248     if(_esc_cnt == 0) {
00249         if (_flow && !_rts && _rxbuf->size() >= _hwm) {
00250             _rts = 1;   // RX buffer too full, set RTS to stop receiving
00251                         // Data will still be received until the buffer is full
00252         }
00253 
00254         if (_rxbuf->full()) {
00255             // Overflow, drop byte
00256         } else {
00257             _rxbuf->push(byte);
00258         }
00259     }
00260 
00261 }
00262 
00263 void ATSerial::escapeChar(char esc) {
00264     _esc_ch = esc;
00265 }
00266 
00267 char ATSerial::escapeChar() {
00268     return _esc_ch;
00269 }
00270