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 
12 //=====================================================================================================================================================
13 // SoftwareSerial
14 //=====================================================================================================================================================
15 
16  #define _SS_MAX_RX_BUFF 64 // RX buffer size
17  #ifndef GCC_VERSION
18  #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
19  #endif
20 
21  namespace eagle_impl {
22 
23  //-------------------------------------------------------------------------------------------------------------------------------------------------
24  // class
25  //-------------------------------------------------------------------------------------------------------------------------------------------------
26 
47  class SoftwareSerial : public Stream {
48  private:
50  uint8_t _receivePin;
52  uint8_t _receiveBitMask;
54  volatile uint8_t *_receivePortRegister;
58  volatile uint8_t *_transmitPortRegister;
59 
67  uint16_t _tx_delay;
68 
70  uint16_t _buffer_overflow:1;
72  uint16_t _inverse_logic:1;
76  static volatile uint8_t _receive_buffer_tail;
78  static volatile uint8_t _receive_buffer_head;
81 
83  void recv(void);
88  inline uint8_t rx_pin_read(void);
93  inline void tx_pin_write(uint8_t pin_state);
98  void setTX(uint8_t transmitPin);
103  void setRX(uint8_t receivePin);
108  static inline void tunedDelay(uint16_t delay);
109 
110  public:
117  SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);
121  SoftwareSerial(void);
128  void configure(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);
129 
135  ~SoftwareSerial(void);
151  void begin(long speed);
156  bool listen(void);
158  inline void end(void);
163  inline bool isListening(void);
168  inline bool overflow(void);
173  int peek(void);
180  virtual size_t write(uint8_t byte);
185  virtual int read(void);
190  virtual int available(void);
194  virtual void flush(void);
195  //used to save codespace
196  using Print::write;
198  static inline void handle_interrupt(void);
199  };
200  }
201 
203 
204  //-------------------------------------------------------------------------------------------------------------------------------------------------
205  // definitions
206  //-------------------------------------------------------------------------------------------------------------------------------------------------
207 
208  bool SoftwareSerial::isListening(void) {
209  return this == active_object;
210  }
211 
212  bool SoftwareSerial::overflow(void) {
213  bool ret = _buffer_overflow;
214  _buffer_overflow = false;
215  return ret;
216  }
217 
218  #if EBOARD_DEBUG_MODE > 0x0
219  #define _DEBUG 0
220  #define _DEBUG_PIN1 11
221  #define _DEBUG_PIN2 13
222  #endif
223 
224  typedef struct _DELAY_TABLE {
225  long baud;
226  unsigned short rx_delay_centering;
227  unsigned short rx_delay_intrabit;
228  unsigned short rx_delay_stopbit;
229  unsigned short tx_delay;
230  } DELAY_TABLE;
231 
232  #if F_CPU == 16000000
233 
234  static const DELAY_TABLE PROGMEM table[] = {
235  // baud rxcenter rxintra rxstop tx
236  { 115200, 1, 17, 17, 12, },
237  { 57600, 10, 37, 37, 33, },
238  { 38400, 25, 57, 57, 54, },
239  { 31250, 31, 70, 70, 68, },
240  { 28800, 34, 77, 77, 74, },
241  { 19200, 54, 117, 117, 114, },
242  { 14400, 74, 156, 156, 153, },
243  { 9600, 114, 236, 236, 233, },
244  { 4800, 233, 474, 474, 471, },
245  { 2400, 471, 950, 950, 947, },
246  { 1200, 947, 1902, 1902, 1899, },
247  { 600, 1902, 3804, 3804, 3800, },
248  { 300, 3804, 7617, 7617, 7614, },
249  };
250 
251  const int XMIT_START_ADJUSTMENT = 5;
252 
253  #elif F_CPU == 8000000
254 
255  static const DELAY_TABLE table[] PROGMEM = {
256  // baud rxcenter rxintra rxstop tx
257  { 115200, 1, 5, 5, 3, },
258  { 57600, 1, 15, 15, 13, },
259  { 38400, 2, 25, 26, 23, },
260  { 31250, 7, 32, 33, 29, },
261  { 28800, 11, 35, 35, 32, },
262  { 19200, 20, 55, 55, 52, },
263  { 14400, 30, 75, 75, 72, },
264  { 9600, 50, 114, 114, 112, },
265  { 4800, 110, 233, 233, 230, },
266  { 2400, 229, 472, 472, 469, },
267  { 1200, 467, 948, 948, 945, },
268  { 600, 948, 1895, 1895, 1890, },
269  { 300, 1895, 3805, 3805, 3802, },
270  };
271 
272  const int XMIT_START_ADJUSTMENT = 4;
273 
274  #elif F_CPU == 20000000
275 
276  static const DELAY_TABLE PROGMEM table[] = {
277  // baud rxcenter rxintra rxstop tx
278  { 115200, 3, 21, 21, 18, },
279  { 57600, 20, 43, 43, 41, },
280  { 38400, 37, 73, 73, 70, },
281  { 31250, 45, 89, 89, 88, },
282  { 28800, 46, 98, 98, 95, },
283  { 19200, 71, 148, 148, 145, },
284  { 14400, 96, 197, 197, 194, },
285  { 9600, 146, 297, 297, 294, },
286  { 4800, 296, 595, 595, 592, },
287  { 2400, 592, 1189, 1189, 1186, },
288  { 1200, 1187, 2379, 2379, 2376, },
289  { 600, 2379, 4759, 4759, 4755, },
290  { 300, 4759, 9523, 9523, 9520, },
291  };
292 
293  const int XMIT_START_ADJUSTMENT = 6;
294 
295  #else
296  #error This version of SoftwareSerial supports only 20, 16 and 8MHz processors
297  #endif
298 
299  inline void SoftwareSerial::tunedDelay(uint16_t delay) {
300  uint8_t tmp=0;
301 
302  asm volatile("sbiw %0, 0x01 \n\t"
303  "ldi %1, 0xFF \n\t"
304  "cpi %A0, 0xFF \n\t"
305  "cpc %B0, %1 \n\t"
306  "brne .-10 \n\t"
307  : "+r" (delay), "+a" (tmp)
308  : "0" (delay)
309  );
310  }
311 
312  void SoftwareSerial::tx_pin_write(uint8_t pin_state) {
313  if (pin_state == LOW)
314  *_transmitPortRegister &= ~_transmitBitMask;
315  else
316  *_transmitPortRegister |= _transmitBitMask;
317  }
318 
319  uint8_t SoftwareSerial::rx_pin_read() {
320  return *_receivePortRegister & _receiveBitMask;
321  }
322 
323  inline void SoftwareSerial::handle_interrupt() {
324  if (active_object) {
325  active_object->recv();
326  }
327  }
328 
329  #if defined(PCINT0_vect)
330  ISR(PCINT0_vect) {
331  SoftwareSerial::handle_interrupt();
332  }
333  #endif
334 
335  #if defined(PCINT1_vect)
336  ISR(PCINT1_vect) {
337  SoftwareSerial::handle_interrupt();
338  }
339  #endif
340 
341  #if defined(PCINT2_vect)
342  ISR(PCINT2_vect) {
343  SoftwareSerial::handle_interrupt();
344  }
345  #endif
346 
347  #if defined(PCINT3_vect)
348  ISR(PCINT3_vect) {
349  SoftwareSerial::handle_interrupt();
350  }
351  #endif
352 
353 
354 
355  void SoftwareSerial::end() {
356  if (digitalPinToPCMSK(_receivePin))
357  *digitalPinToPCMSK(_receivePin) &= ~_BV(digitalPinToPCMSKbit(_receivePin));
358  }
359 
360 
361  SoftwareSerial *SoftwareSerial::active_object = 0;
362  char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF];
363  volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0;
364  volatile uint8_t SoftwareSerial::_receive_buffer_head = 0;
365  #if EBOARD_DEBUG_MODE > 0x0
366  inline void DebugPulse(uint8_t pin, uint8_t count) {
367  #if _DEBUG
368  volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin));
369 
370  uint8_t val = *pport;
371  while (count--) {
372  *pport = val | digitalPinToBitMask(pin);
373  *pport = val;
374  }
375  #endif
376  }
377  #endif
378 
379 
380  bool SoftwareSerial::listen() {
381  if (active_object != this) {
382  _buffer_overflow = false;
383  uint8_t oldSREG = SREG;
384  cli();
385  _receive_buffer_head = _receive_buffer_tail = 0;
386  active_object = this;
387  SREG = oldSREG;
388  return true;
389  }
390 
391  return false;
392  }
393 
394  void SoftwareSerial::recv() {
395 
396  #if GCC_VERSION < 40302
397  asm volatile(
398  "push r18 \n\t"
399  "push r19 \n\t"
400  "push r20 \n\t"
401  "push r21 \n\t"
402  "push r22 \n\t"
403  "push r23 \n\t"
404  "push r26 \n\t"
405  "push r27 \n\t"
406  ::);
407  #endif
408 
409  uint8_t d = 0;
410 
411  if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) {
412  // Wait approximately 1/2 of a bit width to "center" the sample
413  tunedDelay(_rx_delay_centering);
414  #if EBOARD_DEBUG_MODE > 0x0
415  DebugPulse(_DEBUG_PIN2, 1);
416  #endif
417  // Read each of the 8 bits
418  for (uint8_t i=0x1; i; i <<= 1) {
419  tunedDelay(_rx_delay_intrabit);
420  #if EBOARD_DEBUG_MODE > 0x0
421  DebugPulse(_DEBUG_PIN2, 1);
422  #endif
423  uint8_t noti = ~i;
424  if (rx_pin_read())
425  d |= i;
426  else
427  d &= noti;
428  }
429 
430  // skip the stop bit
431  tunedDelay(_rx_delay_stopbit);
432  #if EBOARD_DEBUG_MODE > 0x0
433  DebugPulse(_DEBUG_PIN2, 1);
434  #endif
435  if (_inverse_logic)
436  d = ~d;
437 
438  if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) {
439  _receive_buffer[_receive_buffer_tail] = d; // save new byte
440  _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
441  } else {
442  #if EBOARD_DEBUG_MODE > 0x0
443  #if _DEBUG // for scope: pulse pin as overflow indictator
444  DebugPulse(_DEBUG_PIN1, 1);
445  #endif
446  #endif
447  _buffer_overflow = true;
448  }
449  }
450 
451  #if GCC_VERSION < 40302
452  asm volatile(
453  "pop r27 \n\t"
454  "pop r26 \n\t"
455  "pop r23 \n\t"
456  "pop r22 \n\t"
457  "pop r21 \n\t"
458  "pop r20 \n\t"
459  "pop r19 \n\t"
460  "pop r18 \n\t"
461  ::);
462  #endif
463  }
464 
466 
467  void eagle_impl::SoftwareSerial::configure(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic) {
468  _rx_delay_centering = 0;
469  _rx_delay_intrabit = 0;
470  _rx_delay_stopbit = 0;
471  _tx_delay = 0;
472  _buffer_overflow = false;
473  _inverse_logic = inverse_logic;
474  setTX(transmitPin);
475  setRX(receivePin);
476  }
477 
478 
479  SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) :
480  _rx_delay_centering(0),
481  _rx_delay_intrabit(0),
482  _rx_delay_stopbit(0),
483  _tx_delay(0),
484  _buffer_overflow(false),
485  _inverse_logic(inverse_logic) {
486  setTX(transmitPin);
487  setRX(receivePin);
488  }
489 
491  end();
492  }
493 
494  void SoftwareSerial::setTX(uint8_t tx) {
495  pinMode(tx, OUTPUT);
496  digitalWrite(tx, HIGH);
497  _transmitBitMask = digitalPinToBitMask(tx);
498  _transmitPortRegister = portOutputRegister( digitalPinToPort(tx) );
499  }
500 
501  void SoftwareSerial::setRX(uint8_t rx) {
502  pinMode(rx, INPUT);
503  if (!_inverse_logic)
504  digitalWrite(rx, HIGH);
505  _receivePin = rx;
506  _receiveBitMask = digitalPinToBitMask(rx);
507  _receivePortRegister = portInputRegister( digitalPinToPort(rx) );
508  }
509 
510  void SoftwareSerial::begin(long speed) {
512 
513  for (unsigned i=0; i<sizeof(table)/sizeof(table[0]); ++i) {
514  long baud = pgm_read_dword(&table[i].baud);
515  if (baud == speed) {
516  _rx_delay_centering = pgm_read_word(&table[i].rx_delay_centering);
517  _rx_delay_intrabit = pgm_read_word(&table[i].rx_delay_intrabit);
518  _rx_delay_stopbit = pgm_read_word(&table[i].rx_delay_stopbit);
519  _tx_delay = pgm_read_word(&table[i].tx_delay);
520  break;
521  }
522  }
523 
524  if (_rx_delay_stopbit) {
525  if (digitalPinToPCICR(_receivePin)) {
526  *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin));
527  *digitalPinToPCMSK(_receivePin) |= _BV(digitalPinToPCMSKbit(_receivePin));
528  }
530  }
531 
532  #if _DEBUG
533  pinMode(_DEBUG_PIN1, OUTPUT);
534  pinMode(_DEBUG_PIN2, OUTPUT);
535  #endif
536 
537  listen();
538  }
539 
540  int SoftwareSerial::read() {
541  if (!isListening())
542  return -1;
543 
545  return -1;
546 
547  uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
549  return d;
550  }
551 
553  if (!isListening())
554  return 0;
555 
557  }
558 
559  size_t SoftwareSerial::write(uint8_t b) {
560  if (_tx_delay == 0) {
561  setWriteError();
562  return 0;
563  }
564 
565  uint8_t oldSREG = SREG;
566  cli();
567 
568  tx_pin_write(_inverse_logic ? HIGH : LOW);
569  tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
570 
571  if (_inverse_logic) {
572  for (byte mask = 0x01; mask; mask <<= 1) {
573  if (b & mask)
574  tx_pin_write(LOW);
575  else
576  tx_pin_write(HIGH);
577 
579  }
580  tx_pin_write(LOW);
581  }
582  else {
583  for (byte mask = 0x01; mask; mask <<= 1) {
584  if (b & mask)
585  tx_pin_write(HIGH);
586  else
587  tx_pin_write(LOW);
589  }
590  tx_pin_write(HIGH);
591  }
592 
593  SREG = oldSREG;
595 
596  return 1;
597  }
598 
599  void SoftwareSerial::flush() {
600  if (!isListening())
601  return;
602 
603  uint8_t oldSREG = SREG;
604  cli();
606  SREG = oldSREG;
607  }
608 
609  int SoftwareSerial::peek() {
610  if (!isListening())
611  return -1;
612 
614  return -1;
615 
617  }
619 #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
SoftwareSerial(void)
this enables eBoard to use the SoftwareSerial-interface
static volatile uint8_t _receive_buffer_head
current location in rxBuffer
void configure(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic=false)
this allows eBoard to configure the object
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 ;)
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
[328p] This is used to avoid path resolving issues and defines the common known Arduino SoftwareSeria...