├── readme ├── Examples ├── NewSoftSerialTest │ └── NewSoftSerialTest.pde └── TwoNSSTest │ └── TwoNSSTest.pde ├── keywords.txt ├── NewSoftSerial.h └── NewSoftSerial.cpp /readme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirleech/NewSoftSerial/HEAD/readme -------------------------------------------------------------------------------- /Examples/NewSoftSerialTest/NewSoftSerialTest.pde: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | NewSoftSerial mySerial(2, 3); 5 | 6 | void setup() 7 | { 8 | Serial.begin(57600); 9 | Serial.println("Goodnight moon!"); 10 | 11 | // set the data rate for the NewSoftSerial port 12 | mySerial.begin(4800); 13 | mySerial.println("Hello, world?"); 14 | } 15 | 16 | void loop() // run over and over again 17 | { 18 | 19 | if (mySerial.available()) { 20 | Serial.print((char)mySerial.read()); 21 | } 22 | if (Serial.available()) { 23 | mySerial.print((char)Serial.read()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Examples/TwoNSSTest/TwoNSSTest.pde: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | NewSoftSerial nss(2, 3); 4 | NewSoftSerial nss2(4, 5); 5 | 6 | void setup() 7 | { 8 | nss2.begin(4800); 9 | nss.begin(4800); 10 | Serial.begin(115200); 11 | } 12 | 13 | void loop() 14 | { 15 | // Every 10 seconds switch from 16 | // one serial GPS device to the other 17 | if ((millis() / 10000) % 2 == 0) 18 | { 19 | if (nss.available()) 20 | { 21 | Serial.print(nss.read(), BYTE); 22 | } 23 | } 24 | 25 | else 26 | { 27 | if (nss2.available()) 28 | { 29 | Serial.print(nss2.read(), BYTE); 30 | } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for NewSoftSerial 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | NewSoftSerial KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | setTX KEYWORD2 16 | setRX KEYWORD2 17 | begin KEYWORD2 18 | end KEYWORD2 19 | read KEYWORD2 20 | available KEYWORD2 21 | active KEYWORD2 22 | overflow KEYWORD2 23 | library_version KEYWORD2 24 | enable_timer0 KEYWORD2 25 | flush KEYWORD2 26 | 27 | ####################################### 28 | # Constants (LITERAL1) 29 | ####################################### 30 | 31 | -------------------------------------------------------------------------------- /NewSoftSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | NewSoftSerial.h - Multi-instance software serial library 3 | Copyright (c) 2006 David A. Mellis. All rights reserved. 4 | -- Interrupt-driven receive and other improvements by ladyada 5 | -- Tuning, circular buffer, derivation from class Print, 6 | multi-instance support, porting to 8MHz processors, 7 | various optimizations, PROGMEM delay tables, inverse logic and 8 | direct port writing by Mikal Hart 9 | 10 | This library is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU Lesser General Public 12 | License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. 14 | 15 | This library is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public 21 | License along with this library; if not, write to the Free Software 22 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | The latest version of this library can always be found at 25 | http://arduiniana.org. 26 | */ 27 | 28 | #ifndef NewSoftSerial_h 29 | #define NewSoftSerial_h 30 | 31 | #include 32 | #include "Print.h" 33 | 34 | /****************************************************************************** 35 | * Definitions 36 | ******************************************************************************/ 37 | 38 | #define _NewSS_MAX_RX_BUFF 64 // RX buffer size 39 | #define _NewSS_VERSION 10 // software version of this library 40 | #ifndef GCC_VERSION 41 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 42 | #endif 43 | 44 | class NewSoftSerial : public Print 45 | { 46 | private: 47 | // per object data 48 | uint8_t _receivePin; 49 | uint8_t _receiveBitMask; 50 | volatile uint8_t *_receivePortRegister; 51 | uint8_t _transmitBitMask; 52 | volatile uint8_t *_transmitPortRegister; 53 | 54 | uint16_t _rx_delay_centering; 55 | uint16_t _rx_delay_intrabit; 56 | uint16_t _rx_delay_stopbit; 57 | uint16_t _tx_delay; 58 | 59 | uint16_t _buffer_overflow:1; 60 | uint16_t _inverse_logic:1; 61 | 62 | // static data 63 | static char _receive_buffer[_NewSS_MAX_RX_BUFF]; 64 | static volatile uint8_t _receive_buffer_tail; 65 | static volatile uint8_t _receive_buffer_head; 66 | static NewSoftSerial *active_object; 67 | 68 | // private methods 69 | void recv(); 70 | bool activate(); 71 | virtual void write(uint8_t byte); 72 | uint8_t rx_pin_read(); 73 | void tx_pin_write(uint8_t pin_state); 74 | void setTX(uint8_t transmitPin); 75 | void setRX(uint8_t receivePin); 76 | 77 | // private static method for timing 78 | static inline void tunedDelay(uint16_t delay); 79 | 80 | public: 81 | // public methods 82 | NewSoftSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); 83 | ~NewSoftSerial(); 84 | void begin(long speed); 85 | void end(); 86 | int read(); 87 | uint8_t available(void); 88 | bool active() { return this == active_object; } 89 | bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; } 90 | static int library_version() { return _NewSS_VERSION; } 91 | static void enable_timer0(bool enable); 92 | void flush(); 93 | 94 | // public only for easy access by interrupt handlers 95 | static inline void handle_interrupt(); 96 | }; 97 | 98 | // Arduino 0012 workaround 99 | #undef int 100 | #undef char 101 | #undef long 102 | #undef byte 103 | #undef float 104 | #undef abs 105 | #undef round 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /NewSoftSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | NewSoftSerial.cpp - Multi-instance software serial library 3 | Copyright (c) 2006 David A. Mellis. All rights reserved. 4 | -- Interrupt-driven receive and other improvements by ladyada 5 | -- Tuning, circular buffer, derivation from class Print, 6 | multi-instance support, porting to 8MHz processors, 7 | various optimizations, PROGMEM delay tables, inverse logic and 8 | direct port writing by Mikal Hart 9 | 10 | This library is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU Lesser General Public 12 | License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. 14 | 15 | This library is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public 21 | License along with this library; if not, write to the Free Software 22 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | The latest version of this library can always be found at 25 | http://arduiniana.org. 26 | */ 27 | 28 | // When set, _DEBUG co-opts pins 11 and 13 for debugging with an 29 | // oscilloscope or logic analyzer. Beware: it also slightly modifies 30 | // the bit times, so don't rely on it too much at high baud rates 31 | #define _DEBUG 0 32 | #define _DEBUG_PIN1 11 33 | #define _DEBUG_PIN2 13 34 | // 35 | // Includes 36 | // 37 | #include 38 | #include 39 | #include "WConstants.h" 40 | #include "pins_arduino.h" 41 | #include "NewSoftSerial.h" 42 | 43 | // Abstractions for maximum portability between processors 44 | // These are macros to associate pins to pin change interrupts 45 | #if !defined(digitalPinToPCICR) // Courtesy Paul Stoffregen 46 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) 47 | #define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)NULL)) 48 | #define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1)) 49 | #define digitalPinToPCMSK(p) (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)NULL)))) 50 | #define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14))) 51 | #else 52 | #define digitalPinToPCICR(p) ((uint8_t *)NULL) 53 | #define digitalPinToPCICRbit(p) 0 54 | #define digitalPinToPCMSK(p) ((uint8_t *)NULL) 55 | #define digitalPinToPCMSKbit(p) 0 56 | #endif 57 | #endif 58 | 59 | // 60 | // Lookup table 61 | // 62 | typedef struct _DELAY_TABLE 63 | { 64 | long baud; 65 | unsigned short rx_delay_centering; 66 | unsigned short rx_delay_intrabit; 67 | unsigned short rx_delay_stopbit; 68 | unsigned short tx_delay; 69 | } DELAY_TABLE; 70 | 71 | #if F_CPU == 16000000 72 | 73 | static const DELAY_TABLE PROGMEM table[] = 74 | { 75 | // baud rxcenter rxintra rxstop tx 76 | { 115200, 1, 17, 17, 12, }, 77 | { 57600, 10, 37, 37, 33, }, 78 | { 38400, 25, 57, 57, 54, }, 79 | { 31250, 31, 70, 70, 68, }, 80 | { 28800, 34, 77, 77, 74, }, 81 | { 19200, 54, 117, 117, 114, }, 82 | { 14400, 74, 156, 156, 153, }, 83 | { 9600, 114, 236, 236, 233, }, 84 | { 4800, 233, 474, 474, 471, }, 85 | { 2400, 471, 950, 950, 947, }, 86 | { 1200, 947, 1902, 1902, 1899, }, 87 | { 300, 3804, 7617, 7617, 7614, }, 88 | }; 89 | 90 | const int XMIT_START_ADJUSTMENT = 5; 91 | 92 | #elif F_CPU == 8000000 93 | 94 | static const DELAY_TABLE table[] PROGMEM = 95 | { 96 | // baud rxcenter rxintra rxstop tx 97 | { 115200, 1, 5, 5, 3, }, 98 | { 57600, 1, 15, 15, 13, }, 99 | { 38400, 2, 25, 26, 23, }, 100 | { 31250, 7, 32, 33, 29, }, 101 | { 28800, 11, 35, 35, 32, }, 102 | { 19200, 20, 55, 55, 52, }, 103 | { 14400, 30, 75, 75, 72, }, 104 | { 9600, 50, 114, 114, 112, }, 105 | { 4800, 110, 233, 233, 230, }, 106 | { 2400, 229, 472, 472, 469, }, 107 | { 1200, 467, 948, 948, 945, }, 108 | { 300, 1895, 3805, 3805, 3802, }, 109 | }; 110 | 111 | const int XMIT_START_ADJUSTMENT = 4; 112 | 113 | #elif F_CPU == 20000000 114 | 115 | // 20MHz support courtesy of the good people at macegr.com. 116 | // Thanks, Garrett! 117 | 118 | static const DELAY_TABLE PROGMEM table[] = 119 | { 120 | // baud rxcenter rxintra rxstop tx 121 | { 115200, 3, 21, 21, 18, }, 122 | { 57600, 20, 43, 43, 41, }, 123 | { 38400, 37, 73, 73, 70, }, 124 | { 31250, 45, 89, 89, 88, }, 125 | { 28800, 46, 98, 98, 95, }, 126 | { 19200, 71, 148, 148, 145, }, 127 | { 14400, 96, 197, 197, 194, }, 128 | { 9600, 146, 297, 297, 294, }, 129 | { 4800, 296, 595, 595, 592, }, 130 | { 2400, 592, 1189, 1189, 1186, }, 131 | { 1200, 1187, 2379, 2379, 2376, }, 132 | { 300, 4759, 9523, 9523, 9520, }, 133 | }; 134 | 135 | const int XMIT_START_ADJUSTMENT = 6; 136 | 137 | #else 138 | 139 | #error This version of NewSoftSerial supports only 20, 16 and 8MHz processors 140 | 141 | #endif 142 | 143 | // 144 | // Statics 145 | // 146 | NewSoftSerial *NewSoftSerial::active_object = 0; 147 | char NewSoftSerial::_receive_buffer[_NewSS_MAX_RX_BUFF]; 148 | volatile uint8_t NewSoftSerial::_receive_buffer_tail = 0; 149 | volatile uint8_t NewSoftSerial::_receive_buffer_head = 0; 150 | 151 | // 152 | // Debugging 153 | // 154 | // This function generates a brief pulse 155 | // for debugging or measuring on an oscilloscope. 156 | inline void DebugPulse(uint8_t pin, uint8_t count) 157 | { 158 | #if _DEBUG 159 | volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); 160 | 161 | uint8_t val = *pport; 162 | while (count--) 163 | { 164 | *pport = val | digitalPinToBitMask(pin); 165 | *pport = val; 166 | } 167 | #endif 168 | } 169 | 170 | // 171 | // Private methods 172 | // 173 | 174 | /* static */ 175 | inline void NewSoftSerial::tunedDelay(uint16_t delay) { 176 | uint8_t tmp=0; 177 | 178 | asm volatile("sbiw %0, 0x01 \n\t" 179 | "ldi %1, 0xFF \n\t" 180 | "cpi %A0, 0xFF \n\t" 181 | "cpc %B0, %1 \n\t" 182 | "brne .-10 \n\t" 183 | : "+r" (delay), "+a" (tmp) 184 | : "0" (delay) 185 | ); 186 | } 187 | 188 | // This function sets the current object as the "active" 189 | // one and returns true if it replaces another 190 | bool NewSoftSerial::activate(void) 191 | { 192 | if (active_object != this) 193 | { 194 | _buffer_overflow = false; 195 | uint8_t oldSREG = SREG; 196 | cli(); 197 | _receive_buffer_head = _receive_buffer_tail = 0; 198 | active_object = this; 199 | SREG = oldSREG; 200 | return true; 201 | } 202 | 203 | return false; 204 | } 205 | 206 | // 207 | // The receive routine called by the interrupt handler 208 | // 209 | void NewSoftSerial::recv() 210 | { 211 | 212 | #if GCC_VERSION < 40302 213 | // Work-around for avr-gcc 4.3.0 OSX version bug 214 | // Preserve the registers that the compiler misses 215 | // (courtesy of Arduino forum user *etracer*) 216 | asm volatile( 217 | "push r18 \n\t" 218 | "push r19 \n\t" 219 | "push r20 \n\t" 220 | "push r21 \n\t" 221 | "push r22 \n\t" 222 | "push r23 \n\t" 223 | "push r26 \n\t" 224 | "push r27 \n\t" 225 | ::); 226 | #endif 227 | 228 | uint8_t d = 0; 229 | 230 | // If RX line is high, then we don't see any start bit 231 | // so interrupt is probably not for us 232 | if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) 233 | { 234 | // Wait approximately 1/2 of a bit width to "center" the sample 235 | tunedDelay(_rx_delay_centering); 236 | DebugPulse(_DEBUG_PIN2, 1); 237 | 238 | // Read each of the 8 bits 239 | for (uint8_t i=0x1; i; i <<= 1) 240 | { 241 | tunedDelay(_rx_delay_intrabit); 242 | DebugPulse(_DEBUG_PIN2, 1); 243 | uint8_t noti = ~i; 244 | if (rx_pin_read()) 245 | d |= i; 246 | else // else clause added to ensure function timing is ~balanced 247 | d &= noti; 248 | } 249 | 250 | // skip the stop bit 251 | tunedDelay(_rx_delay_stopbit); 252 | DebugPulse(_DEBUG_PIN2, 1); 253 | 254 | if (_inverse_logic) 255 | d = ~d; 256 | 257 | // if buffer full, set the overflow flag and return 258 | if ((_receive_buffer_tail + 1) % _NewSS_MAX_RX_BUFF != _receive_buffer_head) 259 | { 260 | // save new data in buffer: tail points to where byte goes 261 | _receive_buffer[_receive_buffer_tail] = d; // save new byte 262 | _receive_buffer_tail = (_receive_buffer_tail + 1) % _NewSS_MAX_RX_BUFF; 263 | } 264 | else 265 | { 266 | #if _DEBUG // for scope: pulse pin as overflow indictator 267 | DebugPulse(_DEBUG_PIN1, 1); 268 | #endif 269 | _buffer_overflow = true; 270 | } 271 | } 272 | 273 | #if GCC_VERSION < 40302 274 | // Work-around for avr-gcc 4.3.0 OSX version bug 275 | // Restore the registers that the compiler misses 276 | asm volatile( 277 | "pop r27 \n\t" 278 | "pop r26 \n\t" 279 | "pop r23 \n\t" 280 | "pop r22 \n\t" 281 | "pop r21 \n\t" 282 | "pop r20 \n\t" 283 | "pop r19 \n\t" 284 | "pop r18 \n\t" 285 | ::); 286 | #endif 287 | } 288 | 289 | void NewSoftSerial::tx_pin_write(uint8_t pin_state) 290 | { 291 | if (pin_state == LOW) 292 | *_transmitPortRegister &= ~_transmitBitMask; 293 | else 294 | *_transmitPortRegister |= _transmitBitMask; 295 | } 296 | 297 | uint8_t NewSoftSerial::rx_pin_read() 298 | { 299 | return *_receivePortRegister & _receiveBitMask; 300 | } 301 | 302 | // 303 | // Interrupt handling 304 | // 305 | 306 | /* static */ 307 | inline void NewSoftSerial::handle_interrupt() 308 | { 309 | if (active_object) 310 | { 311 | active_object->recv(); 312 | } 313 | } 314 | 315 | #if defined(PCINT0_vect) 316 | ISR(PCINT0_vect) 317 | { 318 | NewSoftSerial::handle_interrupt(); 319 | } 320 | #endif 321 | 322 | #if defined(PCINT1_vect) 323 | ISR(PCINT1_vect) 324 | { 325 | NewSoftSerial::handle_interrupt(); 326 | } 327 | #endif 328 | 329 | #if defined(PCINT2_vect) 330 | ISR(PCINT2_vect) 331 | { 332 | NewSoftSerial::handle_interrupt(); 333 | } 334 | #endif 335 | 336 | #if defined(PCINT3_vect) 337 | ISR(PCINT3_vect) 338 | { 339 | NewSoftSerial::handle_interrupt(); 340 | } 341 | #endif 342 | 343 | // 344 | // Constructor 345 | // 346 | NewSoftSerial::NewSoftSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : 347 | _rx_delay_centering(0), 348 | _rx_delay_intrabit(0), 349 | _rx_delay_stopbit(0), 350 | _tx_delay(0), 351 | _buffer_overflow(false), 352 | _inverse_logic(inverse_logic) 353 | { 354 | setTX(transmitPin); 355 | setRX(receivePin); 356 | } 357 | 358 | // 359 | // Destructor 360 | // 361 | NewSoftSerial::~NewSoftSerial() 362 | { 363 | end(); 364 | } 365 | 366 | void NewSoftSerial::setTX(uint8_t tx) 367 | { 368 | pinMode(tx, OUTPUT); 369 | digitalWrite(tx, HIGH); 370 | _transmitBitMask = digitalPinToBitMask(tx); 371 | uint8_t port = digitalPinToPort(tx); 372 | _transmitPortRegister = portOutputRegister(port); 373 | } 374 | 375 | void NewSoftSerial::setRX(uint8_t rx) 376 | { 377 | pinMode(rx, INPUT); 378 | if (!_inverse_logic) 379 | digitalWrite(rx, HIGH); // pullup for normal logic! 380 | _receivePin = rx; 381 | _receiveBitMask = digitalPinToBitMask(rx); 382 | uint8_t port = digitalPinToPort(rx); 383 | _receivePortRegister = portInputRegister(port); 384 | } 385 | 386 | // 387 | // Public methods 388 | // 389 | 390 | void NewSoftSerial::begin(long speed) 391 | { 392 | _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; 393 | 394 | for (unsigned i=0; i