eBoard ๐Ÿ‰  โ‘ โ‘งโ‘จ
Written for SIA 2017/2018
eagle_SoftwareSerial.h
Go to the documentation of this file.
1 #ifndef EAGLE_EBOARD_HELPLIB_SOFTWARESERIAL
2 #define EAGLE_EBOARD_HELPLIB_SOFTWARESERIAL
3 
4  #define _SS_MAX_RX_BUFF 64 // RX buffer size
5  #ifndef GCC_VERSION
6  #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
7  #endif
8  namespace eagle_impl {
30  class SoftwareSerial : public Stream {
31  private:
33  uint8_t _receivePin;
35  uint8_t _receiveBitMask;
37  volatile uint8_t *_receivePortRegister;
41  volatile uint8_t *_transmitPortRegister;
42 
50  uint16_t _tx_delay;
51 
53  uint16_t _buffer_overflow:1;
55  uint16_t _inverse_logic:1;
59  static volatile uint8_t _receive_buffer_tail;
61  static volatile uint8_t _receive_buffer_head;
64 
66  void recv(void);
71  inline uint8_t rx_pin_read(void);
76  inline void tx_pin_write(uint8_t pin_state);
81  void setTX(uint8_t transmitPin);
86  void setRX(uint8_t receivePin);
91  static inline void tunedDelay(uint16_t delay);
92 
93  public:
94  // public methods
101  SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);
107  ~SoftwareSerial(void);
123  void begin(long speed);
128  bool listen(void);
130  inline void end(void);
135  inline bool isListening(void);
140  inline bool overflow(void);
145  int peek(void);
152  virtual size_t write(uint8_t byte);
157  virtual int read(void);
162  virtual int available(void);
166  virtual void flush(void);
167  //used to save codespace
168  using Print::write;
170  static inline void handle_interrupt(void);
171  };
172  }
174  bool SoftwareSerial::isListening(void) {
175  return this == active_object;
176  }
177 
178  bool SoftwareSerial::overflow(void) {
179  bool ret = _buffer_overflow;
180  _buffer_overflow = false;
181  return ret;
182  }
183  /* was a workaround for arduino 0012 but not needed
184  #undef int
185  #undef char
186  #undef long
187  #undef byte
188  #undef float
189  #undef abs
190  #undef round
191  */
192  #if EBOARD_DEBUG_MODE > 0x0
193  #define _DEBUG 0
194  #define _DEBUG_PIN1 11
195  #define _DEBUG_PIN2 13
196  #endif
197  typedef struct _DELAY_TABLE {
198  long baud;
199  unsigned short rx_delay_centering;
200  unsigned short rx_delay_intrabit;
201  unsigned short rx_delay_stopbit;
202  unsigned short tx_delay;
203  } DELAY_TABLE;
204  #if F_CPU == 16000000
205 
206  static const DELAY_TABLE PROGMEM table[] = {
207  // baud rxcenter rxintra rxstop tx
208  { 115200, 1, 17, 17, 12, },
209  { 57600, 10, 37, 37, 33, },
210  { 38400, 25, 57, 57, 54, },
211  { 31250, 31, 70, 70, 68, },
212  { 28800, 34, 77, 77, 74, },
213  { 19200, 54, 117, 117, 114, },
214  { 14400, 74, 156, 156, 153, },
215  { 9600, 114, 236, 236, 233, },
216  { 4800, 233, 474, 474, 471, },
217  { 2400, 471, 950, 950, 947, },
218  { 1200, 947, 1902, 1902, 1899, },
219  { 600, 1902, 3804, 3804, 3800, },
220  { 300, 3804, 7617, 7617, 7614, },
221  };
222 
223  const int XMIT_START_ADJUSTMENT = 5;
224 
225  #elif F_CPU == 8000000
226 
227  static const DELAY_TABLE table[] PROGMEM = {
228  // baud rxcenter rxintra rxstop tx
229  { 115200, 1, 5, 5, 3, },
230  { 57600, 1, 15, 15, 13, },
231  { 38400, 2, 25, 26, 23, },
232  { 31250, 7, 32, 33, 29, },
233  { 28800, 11, 35, 35, 32, },
234  { 19200, 20, 55, 55, 52, },
235  { 14400, 30, 75, 75, 72, },
236  { 9600, 50, 114, 114, 112, },
237  { 4800, 110, 233, 233, 230, },
238  { 2400, 229, 472, 472, 469, },
239  { 1200, 467, 948, 948, 945, },
240  { 600, 948, 1895, 1895, 1890, },
241  { 300, 1895, 3805, 3805, 3802, },
242  };
243 
244  const int XMIT_START_ADJUSTMENT = 4;
245 
246  #elif F_CPU == 20000000
247 
248  static const DELAY_TABLE PROGMEM table[] = {
249  // baud rxcenter rxintra rxstop tx
250  { 115200, 3, 21, 21, 18, },
251  { 57600, 20, 43, 43, 41, },
252  { 38400, 37, 73, 73, 70, },
253  { 31250, 45, 89, 89, 88, },
254  { 28800, 46, 98, 98, 95, },
255  { 19200, 71, 148, 148, 145, },
256  { 14400, 96, 197, 197, 194, },
257  { 9600, 146, 297, 297, 294, },
258  { 4800, 296, 595, 595, 592, },
259  { 2400, 592, 1189, 1189, 1186, },
260  { 1200, 1187, 2379, 2379, 2376, },
261  { 600, 2379, 4759, 4759, 4755, },
262  { 300, 4759, 9523, 9523, 9520, },
263  };
264 
265  const int XMIT_START_ADJUSTMENT = 6;
266 
267  #else
268  #error This version of SoftwareSerial supports only 20, 16 and 8MHz processors
269  #endif
270 
271  SoftwareSerial *SoftwareSerial::active_object = 0;
272  char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF];
273  volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0;
274  volatile uint8_t SoftwareSerial::_receive_buffer_head = 0;
275  #if EBOARD_DEBUG_MODE > 0x0
276  inline void DebugPulse(uint8_t pin, uint8_t count) {
277  #if _DEBUG
278  volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin));
279 
280  uint8_t val = *pport;
281  while (count--)
282  {
283  *pport = val | digitalPinToBitMask(pin);
284  *pport = val;
285  }
286  #endif
287  }
288  #endif
289  inline void SoftwareSerial::tunedDelay(uint16_t delay) {
290  uint8_t tmp=0;
291 
292  asm volatile("sbiw %0, 0x01 \n\t"
293  "ldi %1, 0xFF \n\t"
294  "cpi %A0, 0xFF \n\t"
295  "cpc %B0, %1 \n\t"
296  "brne .-10 \n\t"
297  : "+r" (delay), "+a" (tmp)
298  : "0" (delay)
299  );
300  }
301 
302  bool SoftwareSerial::listen() {
303  if (active_object != this)
304  {
305  _buffer_overflow = false;
306  uint8_t oldSREG = SREG;
307  cli();
308  _receive_buffer_head = _receive_buffer_tail = 0;
309  active_object = this;
310  SREG = oldSREG;
311  return true;
312  }
313 
314  return false;
315  }
316 
317  void SoftwareSerial::recv() {
318 
319  #if GCC_VERSION < 40302
320  asm volatile(
321  "push r18 \n\t"
322  "push r19 \n\t"
323  "push r20 \n\t"
324  "push r21 \n\t"
325  "push r22 \n\t"
326  "push r23 \n\t"
327  "push r26 \n\t"
328  "push r27 \n\t"
329  ::);
330  #endif
331 
332  uint8_t d = 0;
333 
334  if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) {
335  // Wait approximately 1/2 of a bit width to "center" the sample
336  tunedDelay(_rx_delay_centering);
337  #if EBOARD_DEBUG_MODE > 0x0
338  DebugPulse(_DEBUG_PIN2, 1);
339  #endif
340  // Read each of the 8 bits
341  for (uint8_t i=0x1; i; i <<= 1)
342  {
343  tunedDelay(_rx_delay_intrabit);
344  #if EBOARD_DEBUG_MODE > 0x0
345  DebugPulse(_DEBUG_PIN2, 1);
346  #endif
347  uint8_t noti = ~i;
348  if (rx_pin_read())
349  d |= i;
350  else
351  d &= noti;
352  }
353 
354  // skip the stop bit
355  tunedDelay(_rx_delay_stopbit);
356  #if EBOARD_DEBUG_MODE > 0x0
357  DebugPulse(_DEBUG_PIN2, 1);
358  #endif
359  if (_inverse_logic)
360  d = ~d;
361 
362  if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) {
363  _receive_buffer[_receive_buffer_tail] = d; // save new byte
364  _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
365  }
366  else {
367  #if EBOARD_DEBUG_MODE > 0x0
368  #if _DEBUG // for scope: pulse pin as overflow indictator
369  DebugPulse(_DEBUG_PIN1, 1);
370  #endif
371  #endif
372  _buffer_overflow = true;
373  }
374  }
375 
376  #if GCC_VERSION < 40302
377  asm volatile(
378  "pop r27 \n\t"
379  "pop r26 \n\t"
380  "pop r23 \n\t"
381  "pop r22 \n\t"
382  "pop r21 \n\t"
383  "pop r20 \n\t"
384  "pop r19 \n\t"
385  "pop r18 \n\t"
386  ::);
387  #endif
388  }
389 
390  void SoftwareSerial::tx_pin_write(uint8_t pin_state) {
391  if (pin_state == LOW)
392  *_transmitPortRegister &= ~_transmitBitMask;
393  else
394  *_transmitPortRegister |= _transmitBitMask;
395  }
396 
397  uint8_t SoftwareSerial::rx_pin_read() {
398  return *_receivePortRegister & _receiveBitMask;
399  }
400 
401  inline void SoftwareSerial::handle_interrupt() {
402  if (active_object) {
403  active_object->recv();
404  }
405  }
406 
407  #if defined(PCINT0_vect)
408  ISR(PCINT0_vect) {
409  SoftwareSerial::handle_interrupt();
410  }
411  #endif
412 
413  #if defined(PCINT1_vect)
414  ISR(PCINT1_vect) {
415  SoftwareSerial::handle_interrupt();
416  }
417  #endif
418 
419  #if defined(PCINT2_vect)
420  ISR(PCINT2_vect) {
421  SoftwareSerial::handle_interrupt();
422  }
423  #endif
424 
425  #if defined(PCINT3_vect)
426  ISR(PCINT3_vect) {
427  SoftwareSerial::handle_interrupt();
428  }
429  #endif
430 
431  SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) :
432  _rx_delay_centering(0),
433  _rx_delay_intrabit(0),
434  _rx_delay_stopbit(0),
435  _tx_delay(0),
436  _buffer_overflow(false),
437  _inverse_logic(inverse_logic) {
438  setTX(transmitPin);
439  setRX(receivePin);
440  }
441 
443  end();
444  }
445 
446  void SoftwareSerial::setTX(uint8_t tx) {
447  pinMode(tx, OUTPUT);
448  digitalWrite(tx, HIGH);
449  _transmitBitMask = digitalPinToBitMask(tx);
450  uint8_t port = digitalPinToPort(tx);
451  _transmitPortRegister = portOutputRegister(port);
452  }
453 
454  void SoftwareSerial::setRX(uint8_t rx) {
455  pinMode(rx, INPUT);
456  if (!_inverse_logic)
457  digitalWrite(rx, HIGH);
458  _receivePin = rx;
459  _receiveBitMask = digitalPinToBitMask(rx);
460  uint8_t port = digitalPinToPort(rx);
461  _receivePortRegister = portInputRegister(port);
462  }
463 
464  void SoftwareSerial::begin(long speed) {
466 
467  for (unsigned i=0; i<sizeof(table)/sizeof(table[0]); ++i) {
468  long baud = pgm_read_dword(&table[i].baud);
469  if (baud == speed) {
470  _rx_delay_centering = pgm_read_word(&table[i].rx_delay_centering);
471  _rx_delay_intrabit = pgm_read_word(&table[i].rx_delay_intrabit);
472  _rx_delay_stopbit = pgm_read_word(&table[i].rx_delay_stopbit);
473  _tx_delay = pgm_read_word(&table[i].tx_delay);
474  break;
475  }
476  }
477 
478  if (_rx_delay_stopbit) {
479  if (digitalPinToPCICR(_receivePin)) {
480  *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin));
481  *digitalPinToPCMSK(_receivePin) |= _BV(digitalPinToPCMSKbit(_receivePin));
482  }
484  }
485 
486  #if _DEBUG
487  pinMode(_DEBUG_PIN1, OUTPUT);
488  pinMode(_DEBUG_PIN2, OUTPUT);
489  #endif
490 
491  listen();
492  }
493 
494  void SoftwareSerial::end() {
495  if (digitalPinToPCMSK(_receivePin))
496  *digitalPinToPCMSK(_receivePin) &= ~_BV(digitalPinToPCMSKbit(_receivePin));
497  }
498 
499 
500  int SoftwareSerial::read() {
501  if (!isListening())
502  return -1;
503 
505  return -1;
506 
507  uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
509  return d;
510  }
511 
513  if (!isListening())
514  return 0;
515 
517  }
518 
519  size_t SoftwareSerial::write(uint8_t b) {
520  if (_tx_delay == 0) {
521  setWriteError();
522  return 0;
523  }
524 
525  uint8_t oldSREG = SREG;
526  cli();
527 
528  tx_pin_write(_inverse_logic ? HIGH : LOW);
529  tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
530 
531  if (_inverse_logic) {
532  for (byte mask = 0x01; mask; mask <<= 1) {
533  if (b & mask)
534  tx_pin_write(LOW);
535  else
536  tx_pin_write(HIGH);
537 
539  }
540 
541  tx_pin_write(LOW);
542  }
543  else {
544  for (byte mask = 0x01; mask; mask <<= 1) {
545  if (b & mask)
546  tx_pin_write(HIGH);
547  else
548  tx_pin_write(LOW);
550  }
551 
552  tx_pin_write(HIGH);
553  }
554 
555  SREG = oldSREG;
557 
558  return 1;
559  }
560 
561  void SoftwareSerial::flush() {
562  if (!isListening())
563  return;
564 
565  uint8_t oldSREG = SREG;
566  cli();
568  SREG = oldSREG;
569  }
570 
571  int SoftwareSerial::peek() {
572  if (!isListening())
573  return -1;
574 
576  return -1;
577 
579  }
580 
582 #endif
uint16_t _rx_delay_intrabit
the rx startbit delay
void recv(void)
private receive routine called each time interrupt handler gets triggered
void setTX(uint8_t transmitPin)
sets a specific pin to be &#39;the chosen one&#39; as a txPin
#define _SS_MAX_RX_BUFF
void begin(long speed)
the start function to setup delay_values etc.
uint8_t _transmitBitMask
the pin mask to address the tx pin
static volatile uint8_t _receive_buffer_head
current location in rxBuffer
virtual size_t write(uint8_t byte)
writes a specific value to the tx register
bool isListening(void)
checks if this object is the listening object
uint16_t _rx_delay_stopbit
the rx stopbit dely
int peek(void)
reads the actual pointed rxBuffer element without dropping it
uint8_t rx_pin_read(void)
simple routine to read the rxPin by registers
bool listen(void)
sets the SoftwareSerial object to be the listening one gaining control over the buffers etc...
void tx_pin_write(uint8_t pin_state)
writes a bool value on the txPin by registers
this namespace contains all the Don&#39;t use manually classes ;)
SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic=false)
the constructor for the SoftwareSerial object
virtual void flush(void)
resets the position in buffer and the buffer itself if the object is listening
static char _receive_buffer[64]
the buffer for rxBuffer
~SoftwareSerial(void)
the destructor of the SoftwareSerial object
bool overflow(void)
returns the current overflow flag and disables it
virtual int available(void)
checks if there is data to read available
static void handle_interrupt(void)
used to handle interrupts on active listening object
volatile uint8_t * _transmitPortRegister
the register the reveice pin is located on
uint16_t _tx_delay
the (generic) tx delay
void setRX(uint8_t receivePin)
sets a specific pin to be &#39;the chosen one&#39; as a rxPin
void end(void)
ends communcation on the rx pin
static SoftwareSerial * active_object
the active SoftwareSerial object to operate on
uint16_t _inverse_logic
determining if all pin reads etc whould be inverted (e.g. no pullup on rx);
uint16_t _rx_delay_centering
the rx center delay
static volatile uint8_t _receive_buffer_tail
size of rxBuffer
uint8_t _receivePin
the id of the receive pin
uint16_t _buffer_overflow
determining if an _buffer_overflow occured
uint8_t _receiveBitMask
the pin mask to directly read from register (Rx)
virtual int read(void)
reads the actual pointed rxBuffer top element
volatile uint8_t * _receivePortRegister
the register the reveice pin is located on
static void tunedDelay(uint16_t delay)
apply a specific delay to achieve higher precision
This is used to avoid path resolving issues and defines the common known Arduino SoftwareSerial inter...