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