├── README.md ├── SoftSerialSTM32.cpp ├── SoftSerialSTM32.h ├── examples ├── SoftSerialSTM32Test │ └── SoftSerialSTM32Test.ino ├── SoftwareSerialExample │ └── SoftwareSerialExample.ino └── TwoPortReceive │ └── TwoPortReceive.ino └── keywords.txt /README.md: -------------------------------------------------------------------------------- 1 | # SoftwareSerialSTM32 2 | 3 | Multi-instance software serial library for Arduino/Wiring 4 | 5 | o Based on Arduino SoftSerial Library with added conditional 6 | compile for STM32Duino 7 | 8 | o Usage documentation available at: 9 | https://www.arduino.cc/en/Reference/SoftwareSerial 10 | 11 | 12 | 13 | The original AVR only source code can be found at 14 | https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/libraries/SoftwareSerial 15 | 16 | License 17 | Licenses are documented in the files themselves. 18 | 19 | -------------------------------------------------------------------------------- /SoftSerialSTM32.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | SoftSerialSTM32.cpp (based on NewSoftSerial.cpp) - 3 | Multi-instance software serial library for Arduino/Wiring 4 | -- Compiles for STM32Arduino and AVR Arduino with no modifications 5 | -- Hacks for 72MHz STM32F1 and timing calibrations by Ron Curry 6 | InSyte Technologies 7 | -- Interrupt-driven receive and other improvements by ladyada 8 | (http://ladyada.net) 9 | -- Tuning, circular buffer, derivation from class Print/Stream, 10 | multi-instance support, porting to 8MHz processors, 11 | various optimizations, PROGMEM delay tables, inverse logic and 12 | direct port writing by Mikal Hart (http://www.arduiniana.org) 13 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 14 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 15 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 16 | 17 | The latest version of this library can always be found at 18 | https://github.com/wingspinner 19 | 20 | License 21 | This library is free software; you can redistribute it and/or 22 | modify it under the terms of the GNU Lesser General Public 23 | License as published by the Free Software Foundation; either 24 | version 2.1 of the License, or (at your option) any later version. 25 | 26 | This library is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 29 | Lesser General Public License for more details. 30 | 31 | You should have received a copy of the GNU Lesser General Public 32 | License along with this library; if not, write to the Free Software 33 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 | 35 | 36 | ************************************************************************/ 37 | 38 | // When set, _DEBUG co-opts pins 5 and 6 for debugging with an 39 | // oscilloscope or logic analyzer. Beware: it also slightly modifies 40 | // the bit times, so don't rely on it too much at high baud rates 41 | #define _DEBUG 0 42 | #define _DEBUG_PIN1 5 43 | #define _DEBUG_PIN2 6 44 | 45 | // 46 | // Includes 47 | // 48 | #include 49 | #include 50 | #include 51 | #include "SoftSerialSTM32.h" 52 | 53 | #ifndef __STM32F1__ 54 | 55 | #include 56 | 57 | #endif 58 | 59 | 60 | // 61 | // Lookup table 62 | // 63 | typedef struct _DELAY_TABLE 64 | { 65 | long baud; 66 | uint32_t rx_delay_centering; 67 | uint32_t rx_delay_intrabit; 68 | uint32_t rx_delay_stopbit; 69 | uint32_t tx_delay; 70 | } DELAY_TABLE; 71 | 72 | 73 | #if defined (__STM32F1__) 74 | 75 | #if F_CPU == 72000000 76 | static const DELAY_TABLE PROGMEM table[] = 77 | { 78 | // baud rxcenter rxintra rxstop tx 79 | { 115200, 6, 52, 15, 54, }, 80 | { 57600, 28, 112, 25, 117, }, 81 | { 38400, 60, 167, 25, 179, }, 82 | { 31250, 92, 223, 25, 223, }, 83 | { 28800, 91, 237, 25, 242, }, 84 | { 19200, 156, 352, 25, 367, }, 85 | { 14400, 232, 470, 25, 492, }, 86 | { 9600, 353, 718, 25, 741, }, 87 | { 4800, 808, 1476, 25, 1492, }, 88 | { 2400, 1516, 2992, 25, 2993, }, 89 | { 1200, 2872, 5993, 25, 5993, }, 90 | { 300, 11488, 23976, 25, 23976, }, 91 | }; 92 | 93 | const int XMIT_START_ADJUSTMENT = 5; 94 | 95 | #else 96 | 97 | #error This version of SoftwareSerial supports only 72MHz STM32F1 processors or all speeds of ATMega processors 98 | 99 | #endif 100 | 101 | #endif 102 | 103 | 104 | // 105 | // Statics 106 | // 107 | SoftSerialSTM32 *SoftSerialSTM32::active_object = 0; 108 | char SoftSerialSTM32::_receive_buffer[_SS_MAX_RX_BUFF]; 109 | volatile uint8_t SoftSerialSTM32::_receive_buffer_tail = 0; 110 | volatile uint8_t SoftSerialSTM32::_receive_buffer_head = 0; 111 | 112 | // 113 | // Debugging 114 | // 115 | // This function generates a brief pulse 116 | // for debugging or measuring on an oscilloscope. 117 | inline void DebugPulse(uint8_t pin, uint8_t count) 118 | { 119 | 120 | #if _DEBUG 121 | #if defined (__STM32F1__) 122 | 123 | // DebugPlus has ~ .4us impact on timing which affects 124 | // the timing of 38400 through 115200 baud requiring 125 | // adjustments to the delay table to compensate. 126 | while (count--) 127 | { 128 | digitalWrite(pin, 1); 129 | digitalWrite(pin, 0); 130 | } 131 | 132 | #else 133 | 134 | volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); 135 | 136 | uint8_t val = *pport; 137 | while (count--) 138 | { 139 | *pport = val | digitalPinToBitMask(pin); 140 | *pport = val; 141 | } 142 | 143 | #endif 144 | #endif 145 | 146 | } 147 | 148 | 149 | // 150 | // Private methods 151 | // 152 | 153 | 154 | /* static */ 155 | #if defined (__STM32F1__) 156 | 157 | // delay overhead is ~+1.487usec. one digit = ~137ns. 158 | // Therefore, total delay is ~ (delay * 0.137us) + 1.487us 159 | inline void SoftSerialSTM32::tunedDelay(uint32_t delay) { 160 | static uint32_t i; 161 | for (i = 0; i < delay; i++) { 162 | asm volatile( 163 | "nop \n\t" 164 | ::); 165 | } 166 | 167 | #else 168 | 169 | inline void SoftSerialSTM32::tunedDelay(uint16_t delay) { 170 | _delay_loop_2(delay); // 16.375 us @ 16mhz 171 | 172 | #endif 173 | 174 | } 175 | 176 | 177 | // This function sets the current object as the "listening" 178 | // one and returns true if it replaces another 179 | bool SoftSerialSTM32::listen() 180 | { 181 | if (!_rx_delay_stopbit) 182 | return false; 183 | 184 | if (active_object != this) 185 | { 186 | if (active_object) 187 | active_object->stopListening(); 188 | 189 | _buffer_overflow = false; 190 | _receive_buffer_head = _receive_buffer_tail = 0; 191 | active_object = this; 192 | 193 | #if defined (__STM32F1__) 194 | 195 | attachInterrupt(_receivePin, SoftSerialSTM32::handle_interrupt, FALLING); 196 | 197 | #else 198 | 199 | setRxIntMsk(true); 200 | 201 | #endif 202 | 203 | return true; 204 | } 205 | 206 | return false; 207 | } 208 | 209 | 210 | // Stop listening. Returns true if we were actually listening. 211 | bool SoftSerialSTM32::stopListening() 212 | { 213 | if (active_object == this) 214 | { 215 | 216 | #if defined (__STM32F1__) 217 | 218 | detachInterrupt(_receivePin); 219 | 220 | #else 221 | 222 | setRxIntMsk(false); 223 | 224 | #endif 225 | 226 | active_object = NULL; 227 | return true; 228 | } 229 | return false; 230 | } 231 | 232 | 233 | // 234 | // The receive routine called by the interrupt handler 235 | // 236 | void SoftSerialSTM32::recv() 237 | { 238 | 239 | #ifndef __STM32F1__ 240 | #if GCC_VERSION < 40302 241 | // Work-around for avr-gcc 4.3.0 OSX version bug 242 | // Preserve the registers that the compiler misses 243 | // (courtesy of Arduino forum user *etracer*) 244 | asm volatile( 245 | "push r18 \n\t" 246 | "push r19 \n\t" 247 | "push r20 \n\t" 248 | "push r21 \n\t" 249 | "push r22 \n\t" 250 | "push r23 \n\t" 251 | "push r26 \n\t" 252 | "push r27 \n\t" 253 | ::); 254 | 255 | #endif 256 | #endif 257 | 258 | uint8_t d = 0; 259 | 260 | // If RX line is high, then we don't see any start bit 261 | // so interrupt is probably not for us 262 | if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) 263 | { 264 | // Disable further interrupts during reception, this prevents 265 | // triggering another interrupt directly after we return, which can 266 | // cause problems at higher baudrates. 267 | 268 | #if defined (__STM32F1__) 269 | 270 | // ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { 271 | setRxIntMsk(false); 272 | 273 | #else 274 | 275 | setRxIntMsk(false); 276 | 277 | #endif 278 | 279 | // Wait approximately 1/2 of a bit width to "center" the sample 280 | tunedDelay(_rx_delay_centering); 281 | DebugPulse(_DEBUG_PIN2, 1); 282 | 283 | // Read each of the 8 bits 284 | for (uint8_t i=8; i > 0; --i) 285 | { 286 | tunedDelay(_rx_delay_intrabit); 287 | DebugPulse(_DEBUG_PIN2, 1); 288 | 289 | d >>= 1; 290 | if (rx_pin_read()) 291 | d |= 0x80; 292 | } 293 | 294 | if (_inverse_logic) 295 | d = ~d; 296 | 297 | // if buffer full, set the overflow flag and return 298 | uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; 299 | if (next != _receive_buffer_head) 300 | { 301 | // save new data in buffer: tail points to where byte goes 302 | _receive_buffer[_receive_buffer_tail] = d; // save new byte 303 | _receive_buffer_tail = next; 304 | } 305 | else 306 | { 307 | DebugPulse(_DEBUG_PIN1, 1); 308 | _buffer_overflow = true; 309 | } 310 | 311 | // skip the stop bit 312 | tunedDelay(_rx_delay_stopbit); 313 | DebugPulse(_DEBUG_PIN2, 1); 314 | 315 | // Re-enable interrupts when we're sure to be inside the stop bit 316 | #if defined (__STM32F1__) 317 | 318 | // } 319 | setRxIntMsk(true); 320 | 321 | #else 322 | 323 | setRxIntMsk(true); 324 | 325 | #endif 326 | 327 | } 328 | 329 | #ifndef __STM32F1__ 330 | #if GCC_VERSION < 40302 331 | 332 | // Work-around for avr-gcc 4.3.0 OSX version bug 333 | // Restore the registers that the compiler misses 334 | asm volatile( 335 | "pop r27 \n\t" 336 | "pop r26 \n\t" 337 | "pop r23 \n\t" 338 | "pop r22 \n\t" 339 | "pop r21 \n\t" 340 | "pop r20 \n\t" 341 | "pop r19 \n\t" 342 | "pop r18 \n\t" 343 | ::); 344 | 345 | #endif 346 | #endif 347 | 348 | } 349 | 350 | 351 | uint8_t SoftSerialSTM32::rx_pin_read() 352 | { 353 | #if defined (__STM32F1__) 354 | return digitalRead(_receivePin); 355 | #else 356 | return *_receivePortRegister & _receiveBitMask; 357 | #endif 358 | } 359 | 360 | 361 | // 362 | // Interrupt handling 363 | // 364 | 365 | /* static */ 366 | inline void SoftSerialSTM32::handle_interrupt() 367 | { 368 | if (active_object) 369 | { 370 | active_object->recv(); 371 | } 372 | } 373 | 374 | 375 | #ifndef __STM32F1__ 376 | 377 | #if defined(PCINT0_vect) 378 | 379 | ISR(PCINT0_vect) 380 | { 381 | SoftSerialSTM32::handle_interrupt(); 382 | } 383 | #endif 384 | 385 | #if defined(PCINT1_vect) 386 | 387 | ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect)); 388 | 389 | #endif 390 | 391 | #if defined(PCINT2_vect) 392 | 393 | ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect)); 394 | 395 | #endif 396 | 397 | #if defined(PCINT3_vect) 398 | 399 | ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect)); 400 | 401 | #endif 402 | 403 | #endif 404 | 405 | 406 | // 407 | // Constructor 408 | // 409 | SoftSerialSTM32::SoftSerialSTM32(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : 410 | _rx_delay_centering(0), 411 | _rx_delay_intrabit(0), 412 | _rx_delay_stopbit(0), 413 | _tx_delay(0), 414 | _buffer_overflow(false), 415 | _inverse_logic(inverse_logic) 416 | { 417 | setTX(transmitPin); 418 | setRX(receivePin); 419 | } 420 | 421 | 422 | // 423 | // Destructor 424 | // 425 | SoftSerialSTM32::~SoftSerialSTM32() 426 | { 427 | end(); 428 | } 429 | 430 | 431 | void SoftSerialSTM32::setTX(uint8_t tx) 432 | { 433 | // First write, then set output. If we do this the other way around, 434 | // the pin would be output low for a short while before switching to 435 | // output hihg. Now, it is input with pullup for a short while, which 436 | // is fine. With inverse logic, either order is fine. 437 | digitalWrite(tx, _inverse_logic ? LOW : HIGH); 438 | pinMode(tx, OUTPUT); 439 | _transmitPin = tx; 440 | 441 | #ifndef __STM32F1__ 442 | 443 | _transmitBitMask = digitalPinToBitMask(tx); 444 | uint8_t port = digitalPinToPort(tx); 445 | _transmitPortRegister = portOutputRegister(port); 446 | 447 | #endif 448 | } 449 | 450 | 451 | void SoftSerialSTM32::setRX(uint8_t rx) 452 | { 453 | pinMode(rx, INPUT); 454 | if (!_inverse_logic) 455 | digitalWrite(rx, HIGH); // pullup for normal logic! 456 | _receivePin = rx; 457 | 458 | #if defined (__STM32F1__) 459 | 460 | attachInterrupt(_receivePin, SoftSerialSTM32::handle_interrupt, FALLING); 461 | 462 | #else 463 | 464 | _receiveBitMask = digitalPinToBitMask(rx); 465 | uint8_t port = digitalPinToPort(rx); 466 | _receivePortRegister = portInputRegister(port); 467 | 468 | #endif 469 | } 470 | 471 | #ifndef __STM32F1__ 472 | 473 | uint16_t SoftSerialSTM32::subtract_cap(uint16_t num, uint16_t sub) { 474 | if (num > sub) 475 | return num - sub; 476 | else 477 | return 1; 478 | } 479 | 480 | #endif 481 | 482 | 483 | // 484 | // Public methods 485 | // 486 | 487 | void SoftSerialSTM32::begin(long speed) 488 | { 489 | _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; 490 | 491 | // Precalculate the various delays, in number of 4-cycle delays 492 | 493 | #if defined (__STM32F1__) 494 | 495 | for (unsigned i=0; i 40800 525 | 526 | // Timings counted from gcc 4.8.2 output. This works up to 115200 on 527 | // 16Mhz and 57600 on 8Mhz. 528 | // 529 | // When the start bit occurs, there are 3 or 4 cycles before the 530 | // interrupt flag is set, 4 cycles before the PC is set to the right 531 | // interrupt vector address and the old PC is pushed on the stack, 532 | // and then 75 cycles of instructions (including the RJMP in the 533 | // ISR vector table) until the first delay. After the delay, there 534 | // are 17 more cycles until the pin value is read (excluding the 535 | // delay in the loop). 536 | // We want to have a total delay of 1.5 bit time. Inside the loop, 537 | // we already wait for 1 bit time - 23 cycles, so here we wait for 538 | // 0.5 bit time - (71 + 18 - 22) cycles. 539 | _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23) / 4); 540 | 541 | // There are 23 cycles in each loop iteration (excluding the delay) 542 | _rx_delay_intrabit = subtract_cap(bit_delay, 23 / 4); 543 | 544 | // There are 37 cycles from the last bit read to the start of 545 | // stopbit delay and 11 cycles from the delay until the interrupt 546 | // mask is enabled again (which _must_ happen during the stopbit). 547 | // This delay aims at 3/4 of a bit time, meaning the end of the 548 | // delay will be at 1/4th of the stopbit. This allows some extra 549 | // time for ISR cleanup, which makes 115200 baud at 16Mhz work more 550 | // reliably 551 | _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11) / 4); 552 | 553 | #else // Timings counted from gcc 4.3.2 output 554 | 555 | // Note that this code is a _lot_ slower, mostly due to bad register 556 | // allocation choices of gcc. This works up to 57600 on 16Mhz and 557 | // 38400 on 8Mhz. 558 | _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11) / 4); 559 | _rx_delay_intrabit = subtract_cap(bit_delay, 11 / 4); 560 | _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17) / 4); 561 | 562 | #endif 563 | 564 | // Enable the PCINT for the entire port here, but never disable it 565 | // (others might also need it, so we disable the interrupt by using 566 | // the per-pin PCMSK register). 567 | *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin)); 568 | // Precalculate the pcint mask register and value, so setRxIntMask 569 | // can be used inside the ISR without costing too much time. 570 | _pcint_maskreg = digitalPinToPCMSK(_receivePin); 571 | _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin)); 572 | 573 | tunedDelay(_tx_delay); // if we were low this establishes the end 574 | 575 | } 576 | 577 | #endif // defined (__STM32F1__) 578 | 579 | #if _DEBUG 580 | pinMode(_DEBUG_PIN1, OUTPUT); 581 | pinMode(_DEBUG_PIN2, OUTPUT); 582 | #endif 583 | 584 | listen(); 585 | } 586 | 587 | 588 | void SoftSerialSTM32::setRxIntMsk(bool enable) 589 | { 590 | 591 | #if defined (__STM32F1__) 592 | 593 | if (enable) 594 | attachInterrupt(_receivePin, SoftSerialSTM32::handle_interrupt, FALLING); 595 | else 596 | detachInterrupt(_receivePin); 597 | 598 | #else 599 | 600 | if (enable) 601 | *_pcint_maskreg |= _pcint_maskvalue; 602 | else 603 | *_pcint_maskreg &= ~_pcint_maskvalue; 604 | 605 | #endif 606 | 607 | } 608 | 609 | 610 | void SoftSerialSTM32::end() 611 | { 612 | stopListening(); 613 | } 614 | 615 | 616 | // Read data from buffer 617 | int SoftSerialSTM32::read() 618 | { 619 | if (!isListening()) 620 | return -1; 621 | 622 | // Empty buffer? 623 | if (_receive_buffer_head == _receive_buffer_tail) 624 | return -1; 625 | 626 | // Read from "head" 627 | uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte 628 | _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; 629 | return d; 630 | } 631 | 632 | 633 | int SoftSerialSTM32::available() 634 | { 635 | if (!isListening()) 636 | return 0; 637 | 638 | return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF; 639 | } 640 | 641 | 642 | size_t SoftSerialSTM32::write(uint8_t b) 643 | { 644 | if (_tx_delay == 0) { 645 | setWriteError(); 646 | return 0; 647 | } 648 | 649 | // By declaring these as local variables, the compiler will put them 650 | // in registers _before_ disabling interrupts and entering the 651 | // critical timing sections below, which makes it a lot easier to 652 | // verify the cycle timings 653 | 654 | #if defined (__STM32F1__) 655 | bool inv = _inverse_logic; 656 | uint16_t delay = _tx_delay; 657 | #else 658 | volatile uint8_t *reg = _transmitPortRegister; 659 | uint8_t reg_mask = _transmitBitMask; 660 | uint8_t inv_mask = ~_transmitBitMask; 661 | uint8_t oldSREG = SREG; 662 | bool inv = _inverse_logic; 663 | uint16_t delay = _tx_delay; 664 | #endif 665 | 666 | if (inv) 667 | b = ~b; 668 | 669 | #if defined (__STM32F1__) 670 | 671 | noInterrupts(); 672 | 673 | #else 674 | 675 | cli(); // turn off interrupts for a clean txmit 676 | 677 | #endif 678 | 679 | // Write the start bit 680 | if (inv) 681 | #if defined (__STM32F1__) 682 | digitalWrite(_transmitPin, HIGH); 683 | else 684 | digitalWrite(_transmitPin, LOW); 685 | #else 686 | *reg |= reg_mask; 687 | else 688 | *reg &= inv_mask; 689 | #endif 690 | 691 | tunedDelay(delay); 692 | 693 | // Write each of the 8 bits 694 | for (uint8_t i = 8; i > 0; --i) 695 | { 696 | if (b & 1) // choose bit 697 | #if defined (__STM32F1__) 698 | digitalWrite(_transmitPin, HIGH); 699 | else 700 | digitalWrite(_transmitPin, LOW); 701 | #else 702 | *reg |= reg_mask; 703 | else 704 | *reg &= inv_mask; 705 | #endif 706 | 707 | tunedDelay(delay); 708 | b >>= 1; 709 | } 710 | 711 | // restore pin to natural state 712 | #if defined (__STM32F1__) 713 | 714 | if (inv) 715 | digitalWrite(_transmitPin, LOW); 716 | else 717 | digitalWrite(_transmitPin, HIGH); 718 | 719 | interrupts(); 720 | 721 | #else 722 | 723 | if (inv) 724 | *reg &= inv_mask; 725 | else 726 | *reg |= reg_mask; 727 | 728 | SREG = oldSREG; // turn interrupts back on 729 | 730 | #endif 731 | 732 | tunedDelay(_tx_delay); 733 | 734 | return 1; 735 | } 736 | 737 | 738 | void SoftSerialSTM32::flush() 739 | { 740 | if (!isListening()) 741 | return; 742 | 743 | #if defined (__STM32F1__) 744 | noInterrupts(); 745 | _receive_buffer_head = _receive_buffer_tail = 0; 746 | interrupts(); 747 | #else 748 | uint8_t oldSREG = SREG; 749 | cli(); 750 | _receive_buffer_head = _receive_buffer_tail = 0; 751 | SREG = oldSREG; 752 | #endif 753 | } 754 | 755 | 756 | int SoftSerialSTM32::peek() 757 | { 758 | if (!isListening()) 759 | return -1; 760 | 761 | // Empty buffer? 762 | if (_receive_buffer_head == _receive_buffer_tail) 763 | return -1; 764 | 765 | // Read from "head" 766 | return _receive_buffer[_receive_buffer_head]; 767 | } 768 | 769 | -------------------------------------------------------------------------------- /SoftSerialSTM32.h: -------------------------------------------------------------------------------- 1 | /* 2 | SoftSerialST32.cpp (based on NewSoftSerial.cpp) - 3 | Multi-instance software serial library for Arduino/Wiring 4 | -- Compiles for STM32Arduino and AVR Arduino with no modifications 5 | -- Hacks for 72MHz STM32F1 and timing calibrations by Ron Curry 6 | InSyte Technologies 7 | -- Interrupt-driven receive and other improvements by ladyada 8 | (http://ladyada.net) 9 | -- Tuning, circular buffer, derivation from class Print/Stream, 10 | multi-instance support, porting to 8MHz processors, 11 | various optimizations, PROGMEM delay tables, inverse logic and 12 | direct port writing by Mikal Hart (http://www.arduiniana.org) 13 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 14 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 15 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 16 | 17 | License 18 | This library is free software; you can redistribute it and/or 19 | modify it under the terms of the GNU Lesser General Public 20 | License as published by the Free Software Foundation; either 21 | version 2.1 of the License, or (at your option) any later version. 22 | 23 | This library is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 26 | Lesser General Public License for more details. 27 | 28 | You should have received a copy of the GNU Lesser General Public 29 | License along with this library; if not, write to the Free Software 30 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 31 | 32 | The latest version of this library can always be found at 33 | https://github.com/wingspinner 34 | */ 35 | 36 | #ifndef SoftSerialSTM32_h 37 | #define SoftSerialSTM32_h 38 | 39 | #include 40 | #include 41 | 42 | /****************************************************************************** 43 | * Definitions 44 | ******************************************************************************/ 45 | 46 | #define _SS_MAX_RX_BUFF 64 // RX buffer size 47 | #ifndef GCC_VERSION 48 | #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 49 | #endif 50 | #define _SSSTM32_VERSION 1.1 // Library Version 51 | 52 | class SoftSerialSTM32 : public Stream 53 | { 54 | private: 55 | // per object data 56 | uint8_t _transmitPin; 57 | uint8_t _receivePin; 58 | 59 | #ifndef __STM32F1__ 60 | 61 | uint8_t _receiveBitMask; 62 | volatile uint8_t *_receivePortRegister; 63 | uint8_t _transmitBitMask; 64 | volatile uint8_t *_transmitPortRegister; 65 | volatile uint8_t *_pcint_maskreg; 66 | uint8_t _pcint_maskvalue; 67 | 68 | // Expressed as 4-cycle delays (must never be 0!) 69 | uint16_t _rx_delay_centering; 70 | uint16_t _rx_delay_intrabit; 71 | uint16_t _rx_delay_stopbit; 72 | uint16_t _tx_delay; 73 | 74 | #else 75 | 76 | // Expressed as 4-cycle delays (must never be 0!) 77 | uint32_t _rx_delay_centering; 78 | uint32_t _rx_delay_intrabit; 79 | uint32_t _rx_delay_stopbit; 80 | uint32_t _tx_delay; 81 | 82 | #endif 83 | 84 | uint16_t _buffer_overflow:1; 85 | uint16_t _inverse_logic:1; 86 | 87 | // static data 88 | static char _receive_buffer[_SS_MAX_RX_BUFF]; 89 | static volatile uint8_t _receive_buffer_tail; 90 | static volatile uint8_t _receive_buffer_head; 91 | static SoftSerialSTM32 *active_object; 92 | 93 | // private methods 94 | void recv() __attribute__((__always_inline__)); 95 | uint8_t rx_pin_read(); 96 | void tx_pin_write(uint8_t pin_state) __attribute__((__always_inline__)); 97 | void setTX(uint8_t transmitPin); 98 | void setRX(uint8_t receivePin); 99 | void setRxIntMsk(bool enable) __attribute__((__always_inline__)); 100 | 101 | #ifndef __STM32F1__ 102 | 103 | // Return num - sub, or 1 if the result would be < 1 104 | static uint16_t subtract_cap(uint16_t num, uint16_t sub); 105 | 106 | // private static method for timing 107 | static inline void tunedDelay(uint16_t delay); 108 | 109 | #else 110 | 111 | // private static method for timing 112 | static inline void tunedDelay(uint32_t delay); 113 | 114 | #endif 115 | 116 | public: 117 | // public methods 118 | SoftSerialSTM32(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); 119 | ~SoftSerialSTM32(); 120 | 121 | static int library_version() { return _SSSTM32_VERSION; } 122 | void begin(long speed); 123 | bool listen(); 124 | void end(); 125 | bool isListening() { return this == active_object; } 126 | bool stopListening(); 127 | bool overflow() { bool ret = _buffer_overflow; if (ret) _buffer_overflow = false; return ret; } 128 | int peek(); 129 | 130 | virtual size_t write(uint8_t byte); 131 | virtual int read(); 132 | virtual int available(); 133 | virtual void flush(); 134 | operator bool() { return true; } 135 | 136 | // for use during debug only 137 | uint16_t getRXCentering() { return _rx_delay_centering; } 138 | void setRXCentering(uint16_t rxDelay) { _rx_delay_centering = rxDelay; } 139 | uint16_t getRXIntrabit() { return _rx_delay_intrabit; } 140 | void setRXIntrabit(uint16_t rxDelay) { _rx_delay_intrabit = rxDelay; } 141 | uint16_t getRXStopbit() { return _rx_delay_stopbit; } 142 | void setRXStopbit(uint16_t rxDelay) { _rx_delay_stopbit = rxDelay; } 143 | uint16_t getTXDelay() { return _tx_delay; } 144 | void setTXDelay(uint16_t txDelay) { _tx_delay = txDelay; } 145 | 146 | 147 | using Print::write; 148 | 149 | // public only for easy access by interrupt handlers 150 | static inline void handle_interrupt() __attribute__((__always_inline__)); 151 | }; 152 | 153 | // Arduino 0012 workaround 154 | #undef int 155 | #undef char 156 | #undef long 157 | #undef byte 158 | #undef float 159 | #undef abs 160 | #undef round 161 | 162 | #endif 163 | 164 | -------------------------------------------------------------------------------- /examples/SoftSerialSTM32Test/SoftSerialSTM32Test.ino: -------------------------------------------------------------------------------- 1 | /************************************************************* 2 | NewSoftSerialST32 Test Program 3 | Multi-instance Library/C++ Object for STM32Duino/ AVR Arduino 4 | Copyright 2015 Ron Curry, InSyte Technologies 5 | 6 | Notes: 7 | - This is a companion app for testing SoftSerialSTM32. It can 8 | be compiled for either AVR or an STM32 board with 9 | SOFTSERIALINT = 1. With SOFTSERIALINT = 0 it uses hardware 10 | serial port 3 on the Maple to enable as a remote test 11 | vehicle connected to another board running the same app with 12 | SOFTSERIALINT = 1. 13 | 14 | 15 | The latest version of this software can always be found at 16 | https://github.com/wingspinner 17 | 18 | License 19 | * Permission is hereby granted, free of charge, to any person 20 | * obtaining a copy of this software and associated documentation 21 | * files (the "Software"), to deal in the Software without 22 | * restriction, including without limitation the rights to use, copy, 23 | * modify, merge, publish, distribute, sublicense, and/or sell copies 24 | * of the Software, and to permit persons to whom the Software is 25 | * furnished to do so, subject to the following conditions: 26 | * 27 | * The above copyright notice and this permission notice shall be 28 | * included in all copies or substantial portions of the Software. 29 | * 30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 33 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 34 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 35 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 36 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | * SOFTWARE. 38 | **************************************************************/ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define SOFTSERIALINT 1 46 | 47 | #if SOFTSERIALINT 48 | 49 | #include "SoftSerialSTM32.h" 50 | SoftSerialSTM32 SWSerial0(8,9); 51 | 52 | #else 53 | 54 | // Set for Maple (STM32F103) hardware serial port 55 | #define SWSerial0 Serial3 56 | 57 | #endif 58 | 59 | // lDelay - Simple delay function independent of systick or interrupts 60 | // Total delay is approximately 1ms per iteration @ 72mhz clock 61 | // delay overhead is ~+1.487usec. one loop = ~137ns * 7300. 62 | // Therefore, total delay is ~ (delay * 0.137us * 7300) + 1.487us 63 | inline void lDelay(uint32_t delay) { 64 | uint32_t i, j; 65 | 66 | j = delay * 7300; 67 | for (i = 0; i < j; i++) { 68 | asm volatile( 69 | "nop \n\t" 70 | ::); 71 | } 72 | 73 | } 74 | 75 | 76 | void setup() { 77 | // put your setup code here, to run once: 78 | 79 | Serial.begin(230400); 80 | 81 | #if SOFTSERIALINT 82 | SWSerial0.begin(115200); 83 | #else 84 | SWSerial0.begin(115200, SERIAL_8N1); 85 | #endif 86 | 87 | 88 | } 89 | 90 | 91 | uint32_t baudrate[] = {300, 1200,2400, 4800,9600,14400,19200,28800,31250,38400,57600, 115200}; 92 | int16_t currentBaud = 11; 93 | bool receiveFlag = false; 94 | bool silentFlag = true; 95 | int txFlag = 0; 96 | int txTestFlag = 0; 97 | int exFlag = 0; 98 | int txSourceFlag = 0; 99 | uint16_t stepSize = 1; 100 | char testString[] = "Testing tx...\n"; 101 | 102 | void loop() { 103 | // put your main code here, to run repeatedly: 104 | char inChar; 105 | 106 | lDelay (3000); 107 | Serial.println("Starting now...."); 108 | // while (1){}; 109 | while (1) { 110 | 111 | if (exFlag) { 112 | while (1) { 113 | while (!SWSerial0.available()) { 114 | if (Serial.available()) 115 | goto jOut; 116 | } 117 | 118 | inChar = SWSerial0.read(); 119 | SWSerial0.write(inChar); 120 | } 121 | } 122 | jOut: 123 | 124 | if (txFlag) { 125 | if (txTestFlag == 0) { 126 | SWSerial0.write(testString); 127 | lDelay(10); 128 | } else if (txTestFlag == 1){ 129 | SWSerial0.write(0x55); 130 | lDelay(10); 131 | } else if (txTestFlag == 2) { 132 | SWSerial0.write(0x15); 133 | lDelay(10); 134 | } else if (txTestFlag == 3) { 135 | SWSerial0.write(0x0f); 136 | lDelay(10); 137 | } else if (txTestFlag == 4) { 138 | SWSerial0.write(0xf0); 139 | lDelay(10); 140 | } 141 | } 142 | 143 | if (receiveFlag) 144 | if (SWSerial0.available()) { 145 | inChar = SWSerial0.read(); 146 | if (!silentFlag) Serial.write(inChar); 147 | 148 | #if SOFTSERIALINT 149 | if (SWSerial0.overflow()) 150 | Serial.println("\nOVERFLOW"); 151 | #endif 152 | } 153 | 154 | if (Serial.available()) { 155 | 156 | inChar = Serial.read(); 157 | 158 | switch (inChar) { 159 | case 'x': 160 | exFlag ^= 1; 161 | Serial.print("\nSerial Exchange Test = "); Serial.println(exFlag, DEC); 162 | break; 163 | case '?': 164 | Serial.println("Commands...."); 165 | Serial.println("R - Toggle on/off receive test"); 166 | Serial.println("p - Toggle on/off print received bytes to console"); 167 | Serial.println("T - Toggle on/off transmit test"); 168 | Serial.println("5 - Toggle between sending a string or 0x55"); 169 | Serial.println("x - Toggle on/off round robin send/receive test."); 170 | Serial.println(" (Requires SoftSerialInt running on seperate device)"); 171 | Serial.println("B - Bump baud rate higher"); 172 | Serial.println("b - Bump baud rate lower"); 173 | Serial.println("s = Print status"); 174 | break; 175 | case 's': 176 | Serial.print("\n\nR - "); Serial.print("RX Test off(0) or on(1) = "); Serial.println(receiveFlag, DEC); 177 | Serial.print("p - "); Serial.print("Print to console off(0) or on(1) = "); Serial.println(silentFlag, DEC); 178 | Serial.print("T - "); Serial.print("TX Test off(0) or on(1) = "); Serial.println(txFlag, DEC); 179 | Serial.print("5 - "); if (txTestFlag) Serial.println("Sending string"); else Serial.println("Sending 0x55"); 180 | Serial.print("Baud = "); Serial.println(baudrate[currentBaud], DEC); 181 | 182 | #if SOFTSERIALINT 183 | Serial.print("Bitperiod = "); Serial.println(SWSerial0.getRXIntrabit(), DEC); 184 | Serial.print("Centering = "); Serial.println(SWSerial0.getRXCentering(), DEC); 185 | Serial.print("Stepsize = "); Serial.println(stepSize, DEC); 186 | #endif 187 | 188 | Serial.print("Available = "); Serial.println(SWSerial0.available(), DEC); 189 | break; 190 | case 'R': 191 | receiveFlag ^= true; 192 | break; 193 | case 'p': 194 | silentFlag ^= 1; 195 | break; 196 | case 'B': 197 | // bump up baud to next rate 198 | SWSerial0.end(); 199 | currentBaud++; 200 | if (currentBaud > 11) 201 | currentBaud = 0; 202 | 203 | #if SOFTSERIALINT 204 | SWSerial0.begin(baudrate[currentBaud]); 205 | #else 206 | SWSerial0.begin(baudrate[currentBaud], SERIAL_8N1); 207 | #endif 208 | 209 | Serial.println(); Serial.println(baudrate[currentBaud], DEC); 210 | break; 211 | case 'b': 212 | // bump up baud to next rate 213 | SWSerial0.end(); 214 | currentBaud--; 215 | if (currentBaud < 0) 216 | currentBaud = 11; 217 | 218 | #if SOFTSERIALINT 219 | SWSerial0.begin(baudrate[currentBaud]); 220 | #else 221 | SWSerial0.begin(baudrate[currentBaud], SERIAL_8N1); 222 | #endif 223 | 224 | Serial.println(); Serial.println(baudrate[currentBaud], DEC); 225 | break; 226 | 227 | #if SOFTSERIALINT 228 | case 'I': 229 | // bump up rx intrabit delay value 230 | SWSerial0.setRXIntrabit(SWSerial0.getRXIntrabit() + stepSize); 231 | break; 232 | case 'i': 233 | // bump up rx intrabit delay value 234 | SWSerial0.setRXIntrabit(SWSerial0.getRXIntrabit() - stepSize); 235 | break; 236 | case 'C': 237 | // bump up rx start bit delay value 238 | SWSerial0.setRXCentering(SWSerial0.getRXCentering() + stepSize); 239 | break; 240 | case 'c': 241 | // bump up rx start bit delay value 242 | SWSerial0.setRXCentering(SWSerial0.getRXCentering() - stepSize); 243 | break; 244 | case '0': 245 | // set step size to 1 246 | stepSize = 1; 247 | break; 248 | case '1': 249 | // set step size to 10 250 | stepSize = 10; 251 | break; 252 | case '2': 253 | // set step size to 100 254 | stepSize = 100; 255 | break; 256 | #endif 257 | 258 | case 'T': 259 | // do tx test 260 | txFlag ^= 1; 261 | break; 262 | case '5': 263 | txTestFlag++; 264 | if (txTestFlag > 4) 265 | txTestFlag = 0; 266 | break; 267 | default: 268 | break; 269 | } 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /examples/SoftwareSerialExample/SoftwareSerialExample.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Software serial multple serial test 3 | 4 | Receives from the hardware serial, sends to software serial. 5 | Receives from software serial, sends to hardware serial. 6 | 7 | The circuit: 8 | * RX is digital pin 10 (connect to TX of other device) 9 | * TX is digital pin 11 (connect to RX of other device) 10 | 11 | Note: 12 | Not all pins on the Mega and Mega 2560 support change interrupts, 13 | so only the following can be used for RX: 14 | 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69 15 | 16 | Not all pins on the Leonardo support change interrupts, 17 | so only the following can be used for RX: 18 | 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI). 19 | 20 | created back in the mists of time 21 | modified 25 May 2012 22 | by Tom Igoe 23 | based on Mikal Hart's example 24 | 25 | This example code is in the public domain. 26 | 27 | */ 28 | #include 29 | 30 | SoftwareSerial mySerial(10, 11); // RX, TX 31 | 32 | void setup() 33 | { 34 | // Open serial communications and wait for port to open: 35 | Serial.begin(57600); 36 | while (!Serial) { 37 | ; // wait for serial port to connect. Needed for Leonardo only 38 | } 39 | 40 | 41 | Serial.println("Goodnight moon!"); 42 | 43 | // set the data rate for the SoftwareSerial port 44 | mySerial.begin(4800); 45 | mySerial.println("Hello, world?"); 46 | } 47 | 48 | void loop() // run over and over 49 | { 50 | if (mySerial.available()) 51 | Serial.write(mySerial.read()); 52 | if (Serial.available()) 53 | mySerial.write(Serial.read()); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /examples/TwoPortReceive/TwoPortReceive.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Software serial multple serial test 3 | 4 | Receives from the two software serial ports, 5 | sends to the hardware serial port. 6 | 7 | In order to listen on a software port, you call port.listen(). 8 | When using two software serial ports, you have to switch ports 9 | by listen()ing on each one in turn. Pick a logical time to switch 10 | ports, like the end of an expected transmission, or when the 11 | buffer is empty. This example switches ports when there is nothing 12 | more to read from a port 13 | 14 | The circuit: 15 | Two devices which communicate serially are needed. 16 | * First serial device's TX attached to digital pin 2, RX to pin 3 17 | * Second serial device's TX attached to digital pin 4, RX to pin 5 18 | 19 | Note: 20 | Not all pins on the Mega and Mega 2560 support change interrupts, 21 | so only the following can be used for RX: 22 | 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69 23 | 24 | Not all pins on the Leonardo support change interrupts, 25 | so only the following can be used for RX: 26 | 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI). 27 | 28 | created 18 Apr. 2011 29 | modified 25 May 2012 30 | by Tom Igoe 31 | based on Mikal Hart's twoPortRXExample 32 | 33 | This example code is in the public domain. 34 | 35 | */ 36 | 37 | #include 38 | // software serial #1: TX = digital pin 10, RX = digital pin 11 39 | SoftwareSerial portOne(10, 11); 40 | 41 | // software serial #2: TX = digital pin 8, RX = digital pin 9 42 | // on the Mega, use other pins instead, since 8 and 9 don't work on the Mega 43 | SoftwareSerial portTwo(8, 9); 44 | 45 | void setup() 46 | { 47 | // Open serial communications and wait for port to open: 48 | Serial.begin(9600); 49 | while (!Serial) { 50 | ; // wait for serial port to connect. Needed for Leonardo only 51 | } 52 | 53 | 54 | // Start each software serial port 55 | portOne.begin(9600); 56 | portTwo.begin(9600); 57 | } 58 | 59 | void loop() 60 | { 61 | // By default, the last intialized port is listening. 62 | // when you want to listen on a port, explicitly select it: 63 | portOne.listen(); 64 | Serial.println("Data from port one:"); 65 | // while there is data coming in, read it 66 | // and send to the hardware serial port: 67 | while (portOne.available() > 0) { 68 | char inByte = portOne.read(); 69 | Serial.write(inByte); 70 | } 71 | 72 | // blank line to separate data from the two ports: 73 | Serial.println(); 74 | 75 | // Now listen on the second port 76 | portTwo.listen(); 77 | // while there is data coming in, read it 78 | // and send to the hardware serial port: 79 | Serial.println("Data from port two:"); 80 | while (portTwo.available() > 0) { 81 | char inByte = portTwo.read(); 82 | Serial.write(inByte); 83 | } 84 | 85 | // blank line to separate data from the two ports: 86 | Serial.println(); 87 | } 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for SoftwareSerial 3 | # (formerly NewSoftSerial) 4 | ####################################### 5 | 6 | ####################################### 7 | # Datatypes (KEYWORD1) 8 | ####################################### 9 | 10 | SoftwareSerial KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | begin KEYWORD2 17 | end KEYWORD2 18 | read KEYWORD2 19 | write KEYWORD2 20 | available KEYWORD2 21 | isListening KEYWORD2 22 | overflow KEYWORD2 23 | flush KEYWORD2 24 | listen KEYWORD2 25 | peek KEYWORD2 26 | 27 | ####################################### 28 | # Constants (LITERAL1) 29 | ####################################### 30 | 31 | --------------------------------------------------------------------------------