diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index bdb0cab..0000000 --- a/.gitattributes +++ /dev/null @@ -1,17 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto - -# Custom for Visual Studio -*.cs diff=csharp - -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain diff --git a/.github/workflows/build_examples.yml b/.github/workflows/build_examples.yml new file mode 100644 index 0000000..3287024 --- /dev/null +++ b/.github/workflows/build_examples.yml @@ -0,0 +1,45 @@ +# based on https://github.com/arduino-libraries/ArduinoBLE/blob/master/.github/workflows/compile-examples.yml +name: Build Lib Examples + +on: + + # Trigger workflow on every push and pull request + push: + pull_request: + + # Also run periodically to catch breakage caused by changes to external resources (libraries, platforms). + schedule: + # Minute, Hour, Day of Month, Month, Day of Week (UTC timezone) + #- cron: '20 21 */7 * *' + - cron: '0 3 2 * *' + +jobs: + + # Build for AVR + build-for-avr: + runs-on: ubuntu-latest + env: + SKETCHES_REPORTS_PATH: sketches-reports + strategy: + fail-fast: false + matrix: + board: + - fqbn: arduino:avr:mega # Arduino Mega2560 + artifact-name-suffix: arduino_avr_mega + steps: + - uses: actions/checkout@v4.2.2 # check-out this repo + - uses: arduino/compile-sketches@v1.1.2 # build Arduino examples + with: + github-token: ${{ secrets.GITHUB_TOKEN }} # required for private repos + fqbn: ${{ matrix.board.fqbn }} + libraries: | + - source-path: ./ + sketch-paths: | + - examples + cli-compile-flags: | + - --warnings="all" + - uses: actions/upload-artifact@v4.4.3 # upload test reports + with: + if-no-files-found: error + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: sketches-report-${{ matrix.board.artifact-name-suffix }} diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 96374c4..04b2abc --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ $RECYCLE.BIN/ Network Trash Folder Temporary Items .apdisk + +# permit editing examples in Arduino IDE +.development diff --git a/1.0.5/NeoHWSerial.cpp b/1.0.5/NeoHWSerial.cpp deleted file mode 100644 index 91bd919..0000000 --- a/1.0.5/NeoHWSerial.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/* - NeoHWSerial.h - Hardware serial library - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Created 2006 Nicholas Zambetti - Modified 28 September 2010 by Mark Sproul - Modified 14 August 2012 by Alarus - Modified 31 October 2015 by SlashDev -*/ - -#include -#include -#include -#include "Arduino.h" -#include "wiring_private.h" -#include "NeoHWSerial.h" - -// this next line disables the entire NeoHWSerial.cpp, -// this is so I can support Attiny series and any other chip without a uart -#if defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H) - -#include "NeoHWSerial.h" - -/* - * on ATmega8, the uart and its bits are not numbered, so there is no "TXC0" - * definition. - */ -#if !defined(TXC0) -#if defined(TXC) -#define TXC0 TXC -#elif defined(TXC1) -// Some devices have uart1 but no uart0 -#define TXC0 TXC1 -#else -#error TXC0 not definable in NeoHWSerial.h -#endif -#endif - -// Define constants and variables for buffering incoming serial data. We're -// using a ring buffer (I think), in which head is the index of the location -// to which to write the next incoming character and tail is the index of the -// location from which to read. -#if (RAMEND < 1000) - #define SERIAL_BUFFER_SIZE 16 -#else - #define SERIAL_BUFFER_SIZE 64 -#endif - -struct ring_buffer -{ - unsigned char buffer[SERIAL_BUFFER_SIZE]; - volatile unsigned int head; - volatile unsigned int tail; -}; - -#if defined(USBCON) - ring_buffer rx_buffer = { { 0 }, 0, 0}; - ring_buffer tx_buffer = { { 0 }, 0, 0}; -#endif -#if defined(UBRRH) || defined(UBRR0H) - ring_buffer rx_buffer = { { 0 }, 0, 0 }; - ring_buffer tx_buffer = { { 0 }, 0, 0 }; -#endif -#if defined(UBRR1H) - ring_buffer rx_buffer1 = { { 0 }, 0, 0 }; - ring_buffer tx_buffer1 = { { 0 }, 0, 0 }; -#endif -#if defined(UBRR2H) - ring_buffer rx_buffer2 = { { 0 }, 0, 0 }; - ring_buffer tx_buffer2 = { { 0 }, 0, 0 }; -#endif -#if defined(UBRR3H) - ring_buffer rx_buffer3 = { { 0 }, 0, 0 }; - ring_buffer tx_buffer3 = { { 0 }, 0, 0 }; -#endif - -void NeoHWSerial::store_char( uint8_t c ) -{ - if (_isr) - _isr( c ); - else { - int i = (unsigned int)(_rx_buffer->head + 1) % SERIAL_BUFFER_SIZE; - - // if we should be storing the received character into the location - // just before the tail (meaning that the head would advance to the - // current location of the tail), we're about to overflow the buffer - // and so we don't write the character or advance the head. - if (i != _rx_buffer->tail) { - _rx_buffer->buffer[ _rx_buffer->head ] = c; - _rx_buffer->head = i; - } - } -} - -#if !defined(USART0_RX_vect) && defined(USART1_RX_vect) -// do nothing - on the 32u4 the first USART is USART1 -#else -#if !defined(USART_RX_vect) && !defined(USART0_RX_vect) && \ - !defined(USART_RXC_vect) - #error "Don't know what the Data Received vector is called for the first UART" -#else -#if defined(USART_RX_vect) - ISR(USART_RX_vect) -#elif defined(USART0_RX_vect) - ISR(USART0_RX_vect) -#elif defined(USART_RXC_vect) - ISR(USART_RXC_vect) // ATmega8 -#endif - { - #if defined(UDR0) - if (bit_is_clear(UCSR0A, UPE0)) { - unsigned char c = UDR0; - NeoSerial.store_char(c); - } else { - unsigned char c = UDR0; - }; - #elif defined(UDR) - if (bit_is_clear(UCSRA, PE)) { - unsigned char c = UDR; - NeoSerial.store_char(c); - } else { - unsigned char c = UDR; - }; - #else - #error UDR not defined - #endif - } -#endif -#endif - -#if defined(USART1_RX_vect) - ISR(USART1_RX_vect) - { - if (bit_is_clear(UCSR1A, UPE1)) { - unsigned char c = UDR1; - NeoSerial1.store_char(c); - } else { - unsigned char c = UDR1; - }; - } -#endif - -#if defined(USART2_RX_vect) && defined(UDR2) - ISR(USART2_RX_vect) - { - if (bit_is_clear(UCSR2A, UPE2)) { - unsigned char c = UDR2; - NeoSerial2.store_char(c); - } else { - unsigned char c = UDR2; - }; - } -#endif - -#if defined(USART3_RX_vect) && defined(UDR3) - ISR(USART3_RX_vect) - { - if (bit_is_clear(UCSR3A, UPE3)) { - unsigned char c = UDR3; - NeoSerial3.store_char(c); - } else { - unsigned char c = UDR3; - }; - } -#endif - - -#if !defined(USART0_UDRE_vect) && defined(USART1_UDRE_vect) -// do nothing - on the 32u4 the first USART is USART1 -#else -#if !defined(UART0_UDRE_vect) && !defined(UART_UDRE_vect) && !defined(USART0_UDRE_vect) && !defined(USART_UDRE_vect) - #error "Don't know what the Data Register Empty vector is called for the first UART" -#else -#if defined(UART0_UDRE_vect) -ISR(UART0_UDRE_vect) -#elif defined(UART_UDRE_vect) -ISR(UART_UDRE_vect) -#elif defined(USART0_UDRE_vect) -ISR(USART0_UDRE_vect) -#elif defined(USART_UDRE_vect) -ISR(USART_UDRE_vect) -#endif -{ - if (tx_buffer.head == tx_buffer.tail) { - // Buffer empty, so disable interrupts -#if defined(UCSR0B) - cbi(UCSR0B, UDRIE0); -#else - cbi(UCSRB, UDRIE); -#endif - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = tx_buffer.buffer[tx_buffer.tail]; - tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE; - - #if defined(UDR0) - UDR0 = c; - #elif defined(UDR) - UDR = c; - #else - #error UDR not defined - #endif - } -} -#endif -#endif - -#ifdef USART1_UDRE_vect -ISR(USART1_UDRE_vect) -{ - if (tx_buffer1.head == tx_buffer1.tail) { - // Buffer empty, so disable interrupts - cbi(UCSR1B, UDRIE1); - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = tx_buffer1.buffer[tx_buffer1.tail]; - tx_buffer1.tail = (tx_buffer1.tail + 1) % SERIAL_BUFFER_SIZE; - - UDR1 = c; - } -} -#endif - -#ifdef USART2_UDRE_vect -ISR(USART2_UDRE_vect) -{ - if (tx_buffer2.head == tx_buffer2.tail) { - // Buffer empty, so disable interrupts - cbi(UCSR2B, UDRIE2); - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = tx_buffer2.buffer[tx_buffer2.tail]; - tx_buffer2.tail = (tx_buffer2.tail + 1) % SERIAL_BUFFER_SIZE; - - UDR2 = c; - } -} -#endif - -#ifdef USART3_UDRE_vect -ISR(USART3_UDRE_vect) -{ - if (tx_buffer3.head == tx_buffer3.tail) { - // Buffer empty, so disable interrupts - cbi(UCSR3B, UDRIE3); - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = tx_buffer3.buffer[tx_buffer3.tail]; - tx_buffer3.tail = (tx_buffer3.tail + 1) % SERIAL_BUFFER_SIZE; - - UDR3 = c; - } -} -#endif - - -// Constructors //////////////////////////////////////////////////////////////// - -NeoHWSerial::NeoHWSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, - volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, - volatile uint8_t *ucsra, volatile uint8_t *ucsrb, - volatile uint8_t *ucsrc, volatile uint8_t *udr, - uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x) -{ - _rx_buffer = rx_buffer; - _tx_buffer = tx_buffer; - _ubrrh = ubrrh; - _ubrrl = ubrrl; - _ucsra = ucsra; - _ucsrb = ucsrb; - _ucsrc = ucsrc; - _udr = udr; - _rxen = rxen; - _txen = txen; - _rxcie = rxcie; - _udrie = udrie; - _u2x = u2x; - _isr = (isr_t) NULL; -} - -// Public Methods ////////////////////////////////////////////////////////////// - -void NeoHWSerial::begin(unsigned long baud) -{ - uint16_t baud_setting; - bool use_u2x = true; - -#if F_CPU == 16000000UL - // hardcoded exception for compatibility with the bootloader shipped - // with the Duemilanove and previous boards and the firmware on the 8U2 - // on the Uno and Mega 2560. - if (baud == 57600) { - use_u2x = false; - } -#endif - -try_again: - - if (use_u2x) { - *_ucsra = 1 << _u2x; - baud_setting = (F_CPU / 4 / baud - 1) / 2; - } else { - *_ucsra = 0; - baud_setting = (F_CPU / 8 / baud - 1) / 2; - } - - if ((baud_setting > 4095) && use_u2x) - { - use_u2x = false; - goto try_again; - } - - // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) - *_ubrrh = baud_setting >> 8; - *_ubrrl = baud_setting; - - transmitting = false; - - sbi(*_ucsrb, _rxen); - sbi(*_ucsrb, _txen); - sbi(*_ucsrb, _rxcie); - cbi(*_ucsrb, _udrie); -} - -void NeoHWSerial::begin(unsigned long baud, byte config) -{ - uint16_t baud_setting; - uint8_t current_config; - bool use_u2x = true; - -#if F_CPU == 16000000UL - // hardcoded exception for compatibility with the bootloader shipped - // with the Duemilanove and previous boards and the firmware on the 8U2 - // on the Uno and Mega 2560. - if (baud == 57600) { - use_u2x = false; - } -#endif - -try_again: - - if (use_u2x) { - *_ucsra = 1 << _u2x; - baud_setting = (F_CPU / 4 / baud - 1) / 2; - } else { - *_ucsra = 0; - baud_setting = (F_CPU / 8 / baud - 1) / 2; - } - - if ((baud_setting > 4095) && use_u2x) - { - use_u2x = false; - goto try_again; - } - - // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) - *_ubrrh = baud_setting >> 8; - *_ubrrl = baud_setting; - - //set the data bits, parity, and stop bits -#if defined(__AVR_ATmega8__) - config |= 0x80; // select UCSRC register (shared with UBRRH) -#endif - *_ucsrc = config; - - sbi(*_ucsrb, _rxen); - sbi(*_ucsrb, _txen); - sbi(*_ucsrb, _rxcie); - cbi(*_ucsrb, _udrie); -} - -void NeoHWSerial::end() -{ - // wait for transmission of outgoing data - while (_tx_buffer->head != _tx_buffer->tail) - ; - - cbi(*_ucsrb, _rxen); - cbi(*_ucsrb, _txen); - cbi(*_ucsrb, _rxcie); - cbi(*_ucsrb, _udrie); - - // clear any received data - _rx_buffer->head = _rx_buffer->tail; -} - -int NeoHWSerial::available(void) -{ - return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE; -} - -int NeoHWSerial::peek(void) -{ - if (_rx_buffer->head == _rx_buffer->tail) { - return -1; - } else { - return _rx_buffer->buffer[_rx_buffer->tail]; - } -} - -int NeoHWSerial::read(void) -{ - // if the head isn't ahead of the tail, we don't have any characters - if (_rx_buffer->head == _rx_buffer->tail) { - return -1; - } else { - unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; - _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE; - return c; - } -} - -void NeoHWSerial::flush() -{ - // UDR is kept full while the buffer is not empty, so TXC triggers when EMPTY && SENT - while (transmitting && ! (*_ucsra & _BV(TXC0))); - transmitting = false; -} - -size_t NeoHWSerial::write(uint8_t c) -{ - int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE; - - // If the output buffer is full, there's nothing for it other than to - // wait for the interrupt handler to empty it a bit - // ???: return 0 here instead? - while (i == _tx_buffer->tail) - ; - - _tx_buffer->buffer[_tx_buffer->head] = c; - _tx_buffer->head = i; - - sbi(*_ucsrb, _udrie); - // clear the TXC bit -- "can be cleared by writing a one to its bit location" - transmitting = true; - sbi(*_ucsra, TXC0); - - return 1; -} - -NeoHWSerial::operator bool() { - return true; -} - -void NeoHWSerial::attachInterrupt( isr_t fn ) -{ - uint8_t oldSREG = SREG; - cli(); - _isr = fn; - SREG = oldSREG; -} - -// Preinstantiate Objects ////////////////////////////////////////////////////// - -#if defined(UBRRH) && defined(UBRRL) - NeoHWSerial NeoSerial(&rx_buffer, &tx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X); -#elif defined(UBRR0H) && defined(UBRR0L) - NeoHWSerial NeoSerial(&rx_buffer, &tx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0); -#elif defined(USBCON) - // do nothing - Serial object and buffers are initialized in CDC code -#else - #error no serial port defined (port 0) -#endif - -#if defined(UBRR1H) - NeoHWSerial NeoSerial1(&rx_buffer1, &tx_buffer1, &UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1); -#endif -#if defined(UBRR2H) - NeoHWSerial NeoSerial2(&rx_buffer2, &tx_buffer2, &UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2); -#endif -#if defined(UBRR3H) - NeoHWSerial NeoSerial3(&rx_buffer3, &tx_buffer3, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3); -#endif - -#endif // whole file - diff --git a/1.0.5/NeoHWSerial.h b/1.0.5/NeoHWSerial.h deleted file mode 100644 index 93192dc..0000000 --- a/1.0.5/NeoHWSerial.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - NeoHWSerial.h - Hardware serial library - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Created 2006 Nicholas Zambetti - Modified 28 September 2010 by Mark Sproul - Modified 14 August 2012 by Alarus - Modified 31 October 2015 by SlashDev -*/ - -#ifndef NeoHWSerial_h -#define NeoHWSerial_h - -#include - -#include "Stream.h" - -struct ring_buffer; - -class NeoHWSerial : public Stream -{ - private: - NeoHWSerial( const NeoHWSerial & ); - NeoHWSerial & operator =( const NeoHWSerial &); - - ring_buffer *_rx_buffer; - ring_buffer *_tx_buffer; - volatile uint8_t *_ubrrh; - volatile uint8_t *_ubrrl; - volatile uint8_t *_ucsra; - volatile uint8_t *_ucsrb; - volatile uint8_t *_ucsrc; - volatile uint8_t *_udr; - uint8_t _rxen; - uint8_t _txen; - uint8_t _rxcie; - uint8_t _udrie; - uint8_t _u2x; - bool transmitting; - public: - NeoHWSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, - volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, - volatile uint8_t *ucsra, volatile uint8_t *ucsrb, - volatile uint8_t *ucsrc, volatile uint8_t *udr, - uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x); - void begin(unsigned long); - void begin(unsigned long, uint8_t); - void end(); - virtual int available(void); - virtual int peek(void); - virtual int read(void); - virtual void flush(void); - virtual size_t write(uint8_t); - inline size_t write(unsigned long n) { return write((uint8_t)n); } - inline size_t write(long n) { return write((uint8_t)n); } - inline size_t write(unsigned int n) { return write((uint8_t)n); } - inline size_t write(int n) { return write((uint8_t)n); } - using Print::write; // pull in write(str) and write(buf, size) from Print - operator bool(); - - typedef void (* isr_t)( uint8_t ); - void attachInterrupt( isr_t fn ); - void detachInterrupt() { attachInterrupt( (isr_t) NULL ); }; - - void store_char( uint8_t rx ); - private: - isr_t _isr; -}; - -// Define config for Serial.begin(baud, config); -#define SERIAL_5N1 0x00 -#define SERIAL_6N1 0x02 -#define SERIAL_7N1 0x04 -#define SERIAL_8N1 0x06 -#define SERIAL_5N2 0x08 -#define SERIAL_6N2 0x0A -#define SERIAL_7N2 0x0C -#define SERIAL_8N2 0x0E -#define SERIAL_5E1 0x20 -#define SERIAL_6E1 0x22 -#define SERIAL_7E1 0x24 -#define SERIAL_8E1 0x26 -#define SERIAL_5E2 0x28 -#define SERIAL_6E2 0x2A -#define SERIAL_7E2 0x2C -#define SERIAL_8E2 0x2E -#define SERIAL_5O1 0x30 -#define SERIAL_6O1 0x32 -#define SERIAL_7O1 0x34 -#define SERIAL_8O1 0x36 -#define SERIAL_5O2 0x38 -#define SERIAL_6O2 0x3A -#define SERIAL_7O2 0x3C -#define SERIAL_8O2 0x3E - -#if defined(UBRRH) || defined(UBRR0H) - extern NeoHWSerial NeoSerial; -#elif defined(USBCON) - #include "USBAPI.h" -// extern NeoHWSerial Serial_; -#endif -#if defined(UBRR1H) - extern NeoHWSerial NeoSerial1; -#endif -#if defined(UBRR2H) - extern NeoHWSerial NeoSerial2; -#endif -#if defined(UBRR3H) - extern NeoHWSerial NeoSerial3; -#endif - -#endif diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..4e0ab09 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 gicking + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 34ddd3a..b18cbc6 --- a/README.md +++ b/README.md @@ -1,26 +1,31 @@ -The **NeoHWSerial** class is a drop-in replacement for the Arduino built-in class `HardwareSerial`. It adds the capability to handle received characters with a user-defined function *during* the RX interrupt. This can improve performance significantly by eliminating all processing associated with storing and retrieving characters in the ring buffer **and** all polling code that constantly checked for new characters: `available` and `read` are unnecessary. +The **NeoHWSerial** class is a drop-in replacement for the Arduino built-in class `HardwareSerial`. It adds the capability to handle received characters with a user-defined function *during* the RX interrupt. This can improve performance significantly by eliminating all processing associated with storing and retrieving characters in the ring buffer **and** all polling code that constantly checked for new characters: `available` and `read` are unnecessary. -### Installation +Note: This is a minor update of [NeoHWSerial by SlashDevin](https://github.com/SlashDevin/NeoHWSerial) for AVR. Changes: + - updated library structure to Arduino IDE >=v1.5.6 library format (see [here](https://arduino.github.io/arduino-cli/latest/library-specification/)) + - pass UART status byte to user receive function. Required e.g. for break detection for [LIN bus](https://en.wikipedia.org/wiki/Local_Interconnect_Network) + - support optional storing to ring buffer after return from user routine via return value -1. Download the NeoHWSerial master zip file. -2. Extract all files into a tempory working directory. -3. Create a `NeoHWSerial` subdirectory in your `Arduino/Libraries` directory. -4. Copy all files from the version number subdirectory that corresponds to your IDE version into the `NeoHWSerial` subdirectory you created in step 1. For example, if you are using Arduino IDE version 1.0.5, copy all files from the extracted `NeoHWSerial-master/1.0.5` subdirectory into your `Arduino/Libraries/NeoHWSerial` subdirectory. -### Usage +## Installation + +This library can be installed via the library manager of the Arduino IDE. Alternatively download repository and copy the folder to your Arduino library path. + + +## Usage To handle all received characters with your function, you must register it with the specific `NeoSerial[n]` instance: #include - + volatile uint32_t newlines = 0UL; - - static void handleRxChar( uint8_t c ) + + static bool handleRxChar( uint8_t c, uint8_t status ) { if (c == '\n') newlines++; + return false; // don't store c in ring buffer } - + void setup() { NeoSerial1.attachInterrupt( handleRxChar ); @@ -29,14 +34,14 @@ To handle all received characters with your function, you must register it with Remember that the registered function is called from an interrupt context, and it should return as quickly as possible. Taking too much time in the function will cause many unpredictable behaviors, including loss of received data. See the similar warnings for the built-in [`attachInterrupt`](https://www.arduino.cc/en/Reference/AttachInterrupt) for digital pins. -The registered function will be called from the ISR whenever a character is received. The received character **will not** be stored in the `rx_buffer`, and it **will not** be returned from `read()`. Any characters that were received and buffered before `attachInterrupt` was called remain in `rx_buffer`, and could be retrieved by calling `read()`. +The registered function will be called from the ISR whenever a character is received. The received character is stored in the `rx_buffer` after return from the user function, if the user function returns a _true_ value. Characters that were received and buffered before `attachInterrupt` was called remain in `rx_buffer`, and could be retrieved by calling `read()`. If `attachInterrupt` is never called, or it is passed a `NULL` function, the normal buffering occurs, and all received characters must be obtained by calling `read()`. The original `HardwareSerial` files were modified to include two new methods, `attachInterrupt` and `detachInterrupt`, and one new data member, the private `_isr`: ``` - typedef void (* isr_t)( uint8_t ); + typedef void (* isr_t)( uint8_t, uint8_t ); void attachInterrupt( isr_t fn ); void detachInterrupt() { attachInterrupt( (isr_t) NULL ); }; @@ -44,13 +49,13 @@ The original `HardwareSerial` files were modified to include two new methods, `a isr_t _isr; ``` -### NOTES +## Notes To avoid name collisions, all `HardwareSerial` instances are prefixed with "Neo" in this replacement library. All parts of your sketch, including other libraries, must use -* `NeoSerial` instead of `Serial`, +* `NeoSerial` instead of `Serial`, * `NeoSerial1` instead of `Serial1`, -* `NeoSerial2` instead of `Serial2`, and +* `NeoSerial2` instead of `Serial2`, and * `NeoSerial3` instead of `Serial3`. If there are any references to the original `HardwareSerial` instances, you will get one or more linker errors: @@ -71,3 +76,26 @@ As new Arduino IDEs are released, new versions will appear in the root of this r ### See Also If you are also using software serial ports, you may be interested in [NeoICSerial](https://github.com/SlashDevin/NeoICSerial) or [NeoSWSerial](https://github.com/SlashDevin/NeoSWSerial). + + +---------------- + +Revision History +---------------- + +**v1.6.9 (2025-09-13)** + - fix bug that corrupts buffer storage if no user function is attached to Rx-ISR + - add methods to disable & enable receiver + +**v1.6.8 (2025-01-26)** + - add conditional compile for AVR architecture only + +**v1.6.7 (2024-10-25)** + - fix indexing bug for ring buffer storage + - add CI tests via Github Actions + +**v1.6.6 (2020-11-01)** + - fork from [NeoHWSerial by SlashDevin](https://github.com/SlashDevin/NeoHWSerial) + - update library structure to Arduino IDE >=v1.5.6 format (see [here](https://arduino.github.io/arduino-cli/latest/library-specification/)) + - pass UART status byte to user receive function. Required e.g. for break detection for [LIN bus](https://en.wikipedia.org/wiki/Local_Interconnect_Network) + - support optional storing to ring buffer after return from user routine via return value diff --git a/examples/LIN_sniffer/LIN-Master/LIN-Master.ino b/examples/LIN_sniffer/LIN-Master/LIN-Master.ino new file mode 100644 index 0000000..7d83176 --- /dev/null +++ b/examples/LIN_sniffer/LIN-Master/LIN-Master.ino @@ -0,0 +1,56 @@ +////// +// LIN communication error codes +////// +#define SUCCESS 0 // no error +#define ERROR_TIMOUT 1 // receive timeout +#define ERROR_CHK 2 // checksum error +#define ERROR_MISC 255 // misc error + + +////// +// global macros +////// +#define LIN_SERIAL Serial1 // used Serial interface +#define LIN_BAUDRATE 19200 // LIN baudrate + + +////// +// global variables +////// +uint8_t LIN_err = SUCCESS; + + +////// +// setup routine (called once) +////// +void setup() +{ + // communication to PC + Serial.begin(115200); + while (!Serial); + + // initialize LIN interface + LIN_SERIAL.begin(LIN_BAUDRATE); + while (!LIN_SERIAL); + +} // setup + + +////// +// loop routine (called periodically) +////// +void loop() +{ + static uint8_t Tx[8] = {0,1,2,3,4,5,6,7}; + uint8_t Rx[8]; + + // send master request + LIN_sendMasterRequest(2, 0x12, 8, Tx); + Tx[0]++; + delay(100); + + // send/ receive slave response + LIN_receiveSlaveResponse(2, 0x23, 8, Rx); + delay(100); + +} // loop diff --git a/examples/LIN_sniffer/LIN-Master/LIN_protocol.ino b/examples/LIN_sniffer/LIN-Master/LIN_protocol.ino new file mode 100644 index 0000000..a09dadf --- /dev/null +++ b/examples/LIN_sniffer/LIN-Master/LIN_protocol.ino @@ -0,0 +1,210 @@ +/** + \fn byte LIN_protectID(byte id) + + \brief protect LIN ID with parity + + \param[in] id frame ID (protection optional) + + \return protected LIN identifier + + calculate protected LIN identifier as described in LIN2.0 spec "2.3.1.3 Protected identifier field" +*/ +byte LIN_protectID(byte id) +{ + byte pid; + uint8_t tmp; // temporary variable use to calculate the parity bits + + // copy (unprotected) ID + pid = id; + + // protect ID with parity bits + pid = (uint8_t) (pid & 0x3F); //0x3F = 00111111 =63 + tmp = (uint8_t) ((pid ^ (pid>>1) ^ (pid>>2) ^ (pid>>4)) & 0x01); // -> tmp[0] = PI0 = ID0^ID1^ID2^ID4 + pid |= (uint8_t) (tmp<<6); + tmp = (uint8_t) (~((pid>>1) ^ (pid>>3) ^ (pid>>4) ^ (pid>>5)) & 0x01); // -> tmp[0] = PI1 = ~(ID1^ID3^ID4^ID5) + pid |= (uint8_t) (tmp<<7); + + // return protected identifier + return(pid); + +} // LIN_protectID + + + +/** + \fn byte LIN_sendMasterRequest(uint8_t vers, byte id, uint8_t numData, byte *data) + + \brief send LIN master request frame + + \param[in] vers LIN version (for checksum) + \param[in] id frame ID (protection optional) + \param[in] numData number of data bytes (0..8) + \param[in] data Tx data bytes (up to 8) + + \return error code + + send a LIN master request frame. For an explanation of the LIN bus + and protocoll e.g. see https://en.wikipedia.org/wiki/Local_Interconnect_Network +*/ +byte LIN_sendMasterRequest(uint8_t vers, byte id, uint8_t numData, byte *data) +{ + uint8_t chk; // tmp, + uint16_t chk_tmp=0; + byte buf[11]; // frame Tx buffer: 0x55 + id + 8B data + chk + + // protect ID (see LIN2.0 spec "2.3.1.3 Protected identifier field") + id = LIN_protectID(id); + + // LIN2.x uses extended checksum which includes protected ID, i.e. including parity bits + // LIN1.x uses classical checksum only over data bytes + // Diagnostic frames with ID=0x3C/PID=0x3C and ID=0x3D/PID=0x7D always use classical checksum (see LIN2.0 spec "2.3.1.5 Checkum") + if (!((vers == 1) || (id == 0x3C) || (id == 0x7D))) + chk_tmp = (uint16_t) id; + for (uint8_t i=0; i255) + chk_tmp -= 255; + } + chk = (uint8_t)(0xFF - ((uint8_t) chk_tmp)); + + // construct Tx frame + buf[0] = 0x55; // sync field + buf[1] = id; // protected identifier + for (uint8_t i=0; i13bit @ baudrate) + LIN_SERIAL.begin(LIN_BAUDRATE / 2); + LIN_SERIAL.write(0x00); + LIN_SERIAL.flush(); + LIN_SERIAL.begin(LIN_BAUDRATE); + + // send sync field + frame ID + data + checksum + LIN_SERIAL.write(buf, 3+numData); + + // wait for the send is complete + LIN_SERIAL.flush(); + + // empty receive buffer, just to be sure + while(LIN_SERIAL.available()) + LIN_SERIAL.read(); + + // return error status + return(SUCCESS); + +} // LIN_sendMasterRequest + + + +/** + \fn byte LIN_receiveSlaveResponse(uint8_t vers, byte id, uint8_t numData, byte *data) + + \brief receive LIN slave response frame + + \param[in] vers LIN version 1 or 2 (for checksum) + \param[in] id frame ID (protection optional) + \param[in] numData number of data bytes (0..8) + \param[out] data Rx data bytes (up to 8) + + \return error code + + receive a LIN slave response frame. For an explanation of the LIN bus + and protocoll e.g. see https://en.wikipedia.org/wiki/Local_Interconnect_Network + +*/ +byte LIN_receiveSlaveResponse(uint8_t vers, byte id, uint8_t numData, byte *data) +{ + uint8_t tmp, chkCalc, numRx; //chkRcv, + uint16_t chk_tmp=0; + byte buf[3]; // frame Tx buffer: 0x55 + id + + // protect ID (see LIN2.0 spec "2.3.1.3 Protected identifier field") + id = LIN_protectID(id); + + // LIN2.x uses extended checksum which includes protected ID, i.e. including parity bits + // LIN1.x uses classical checksum only over data bytes + // Diagnostic frames with ID 0x3C and 0x3D always use classical checksum (see LIN spec "2.3.1.5 Checkum") + if (!((vers == 1) || (id == 0x3C) || (id == 0x3D))) // 3C = 00111100 = 60; 3D = 00111101 = 61 + chk_tmp = (uint16_t) id; + + // construct Tx frame + buf[0] = 0x55; // sync field + buf[1] = id; // protected identifier + + // clear Rx buffer + for (uint8_t i=0; i<8; i++) + data[i] = 0x00; + + // set timeout for data reception + LIN_SERIAL.setTimeout(10); + + // empty receive buffer, just to be sure + while(LIN_SERIAL.available()) + LIN_SERIAL.read(); + + // generate sync break (low for >13bit @ baudrate) + LIN_SERIAL.begin(LIN_BAUDRATE / 2); + LIN_SERIAL.write(0x00); + LIN_SERIAL.flush(); + LIN_SERIAL.begin(LIN_BAUDRATE); + + // send sync field + frame ID + LIN_SERIAL.write(buf, 2); + + // wait for the send is complete and clear receive buffer + LIN_SERIAL.flush(); + + // read 3B LIN echo (break+sync+id) + for (tmp=0; tmp<3; tmp++) + LIN_SERIAL.read(); + + // receive data+chk with timeout + numRx = LIN_SERIAL.readBytes(data, numData+1); + + + // check for timeout + if (numRx != numData+1) { + + // clear Rx buffer + for (uint8_t i=0; i<8; i++) + data[i] = 0x00; + + // return error code + return(ERROR_TIMOUT); + + } // if timeout + + // calculate checksum over received bytes + for (uint8_t i=0; i255) + chk_tmp -= 255; + + } // checksum calculation + + // bitwise invert claculated checksum + chkCalc = (uint8_t) (0xFF - ((uint8_t) chk_tmp)); + + // compare calculated vs. received checksum + if (data[numData] != chkCalc) + { + // clear Rx buffer + for (uint8_t i=0; i + + +//////////////// +// GLOBAL MACROS / DEFINES +//////////////// + +// LIN versions (required for checksum calculation) +#define LIN_V1 1 +#define LIN_V2 2 + + +//////////////// +// GLOBAL STRUCTS / TYPEDEFS +//////////////// + +// frame type +typedef struct +{ + int numData; // number of data bytes + + union + { + uint8_t bufRx[12]; // raw frame: BRK, SYNC, ID, DATA0..8, CHK + + // access to individual bytes by name + struct + { + uint8_t BRK; // sync break (always 0x00) + uint8_t SYNC; // sync field (always 0x55) + uint8_t ID; // frame ID + uint8_t DATA[8]; // data bytes (max. 8) + uint8_t CHK; // frame checksum + }; + + }; + +} frame_t; + + +//////////////// +// GLOBAL VARIABLES +//////////////// + +// global frame buffer +frame_t frame; + +// flag to indicate that frame was received. Set in Serial Rx-ISR on reception of next BRK +bool flagFrame = false; + + +//////////////// +// GLOBAL FUNCTIONS +//////////////// + +///////////////////// +// calculate the LIN frame checksum +// note: checksum is different for v1.x and v2.x; exceptions are diagnostic frames which use v1.x checksum +///////////////////// +uint8_t LIN_checksum(uint8_t version, uint8_t id, uint8_t numData, uint8_t *data) +{ + uint16_t chk=0x00; + + // LIN2.x uses extended checksum which includes protected ID, i.e. including parity bits + // LIN1.x uses classical checksum only over data bytes + // Diagnostic frames with ID 0x3C and 0x3D/0x7D always use classical checksum (see LIN spec "2.3.1.5 Checkum") + if (!((version == LIN_V1) || (id == 0x3C) || (id == 0x7D))) // if version 2 & no diagnostic frames (0x3C=60 (PID=0x3C) or 0x3D=61 (PID=0x7D)) + chk = (uint16_t) id; + + // loop over data bytes + for (uint8_t i = 0; i < numData; i++) + { + chk += (uint16_t) (data[i]); + if (chk>255) + chk -= 255; + } + chk = (uint8_t)(0xFF - ((uint8_t) chk)); // bitwise invert + + // return frame checksum + return chk; + +} // LIN_checksum() + + + +///////////////////// +// custom Serial receive interrupt function +///////////////////// +static bool ISR_Rx( uint8_t rx, uint8_t UCSRA ) +{ + static uint8_t idx = 0; // byte index in frame + static uint8_t Rx[12]; + + // check for LIN sync break (=0x00 with framing error) + if ((rx == 0x00) && (UCSRA & (1< set flag. Skip for first frame + if (frame.BRK != 0xFF) + flagFrame = true; + + // copy previous frame to global variables to avoid conflict + frame.numData = (int) idx - 3; + memcpy(frame.bufRx, Rx, 12); + frame.CHK = Rx[idx]; + + // reset byte index in frame + idx=0; + + } // sync break received + + + // normal frame bytes -> just increase byte index in frame + else + { + // avoid buffer overrun + if (idx<11) + idx++; + + } // normal frame bytes + + + // copy received byte to local buffer + Rx[idx] = rx; + + // do not store received data to Serial ring buffer + return false; + +} // ISR_Rx() + + + +///////////////////// +// initialization +///////////////////// +void setup() +{ + // initialize BRK to 0xFF to indicate 1st frame + frame.BRK = 0xFF; + + // initilize PC connection + NeoSerial.begin(115200); + + // initialize LIN interface + NeoSerial2.begin(19200); + + // attach protocol handler to used Serial + NeoSerial2.attachInterrupt( ISR_Rx ); + +} // setup() + + + +///////////////////// +// main loop +///////////////////// +void loop() +{ + // a LIN frame was received --> handle it + if (flagFrame) + { + // handle each frame only once + flagFrame = false; + + // check sync break (must be 0x00 w/ framing error) + if (frame.BRK != 0x00) + { + NeoSerial.print("error: wrong BRK, expect 0x00, read 0x"); + NeoSerial.println((int) (frame.BRK), HEX); + } + + // check sync field (must be 0x55) + else if (frame.SYNC != 0x55) + { + NeoSerial.print("error: wrong SYNC, expect 0x55, read 0x"); + NeoSerial.println((int) (frame.SYNC), HEX); + } + + // check frame checksum (different for LIN v1.x and v2.x!) + else if (frame.CHK != LIN_checksum(LIN_V2, frame.ID, frame.numData, frame.DATA)) + { + NeoSerial.print("error: checksum error, expect 0x"); + NeoSerial.print((int) (LIN_checksum(LIN_V2, frame.ID, frame.numData, frame.DATA)), HEX); + NeoSerial.print(", read 0x"); + NeoSerial.println((int) (frame.CHK), HEX); + } + + // no error -> handle frame (here only print) + else { + + // print ID + NeoSerial.print("ID:"); + NeoSerial.print((int) frame.ID, HEX); + NeoSerial.print(" "); + + // print data bytes + NeoSerial.print("DATA:"); + for (int i=0; i + + +//////////////// +// INCLUDE MACROS +//////////////// + +// comment out for handling only in ISR +//#define STORE_BUFFER + + +//////////////// +// GLOBAL VARIABLES +//////////////// + +// flag to indicate a BRK (set in rx_handler()) +bool BRK = false; + + +//////////////// +// GLOBAL FUNCTIONS +//////////////// + +///////////////////// +// custom Serial receive interrupt handler +///////////////////// +bool rx_handler(uint8_t byte, uint8_t status) +{ + // avoid compiler warnings + (void)(byte); + (void)(status); + + // check for framing error + BRK = (bool) (status & (1< byte is stored in Serial buffer + #if defined(STORE_BUFFER) + return true; + + // return false -> byte is not stored in Serial buffer + #else + return false; + #endif + +} // rx_handler() + + +///////////////////// +// initialization +///////////////////// +void setup() +{ + // serial console + NeoSerial.begin(115200); + + // sender + NeoSerial1.begin(19200); + + // receiver + NeoSerial2.begin(19200); + NeoSerial2.attachInterrupt(rx_handler); + +} // setup() + + +///////////////////// +// main loop +///////////////////// +void loop() { + + uint8_t c; + + // send BRK via Serial1 (0x00 w/o stop bit) + NeoSerial1.begin(9600); + NeoSerial1.write(0x00); + NeoSerial1.flush(); + + // receive BRK via Serial2 + if (NeoSerial2.available()) + { + c = NeoSerial2.read(); + NeoSerial.print("loop: Rx=0x"); + NeoSerial.print(c, HEX); + if (BRK) + NeoSerial.print(", BRK"); + NeoSerial.println(); + } + BRK = false; + + // send SYNC via Serial1 (0x55) + NeoSerial1.begin(19200); + NeoSerial1.write(0x55); + NeoSerial1.flush(); + + // receive BRK via Serial2 + if (NeoSerial2.available()) + { + c = NeoSerial2.read(); + NeoSerial.print("loop: Rx=0x"); + NeoSerial.print(c, HEX); + if (BRK) + NeoSerial.print(", BRK"); + NeoSerial.println(); + } + BRK = false; + + // wait some time + delay(2000); + NeoSerial.println(); + +} // loop() diff --git a/1.0.5/examples/echo/echo.ino b/examples/echo/echo.ino old mode 100644 new mode 100755 similarity index 66% rename from 1.0.5/examples/echo/echo.ino rename to examples/echo/echo.ino index ebb213e..be540e4 --- a/1.0.5/examples/echo/echo.ino +++ b/examples/echo/echo.ino @@ -1,20 +1,21 @@ -#include - // // Example program to show how to attach to the received character // interrupt with NeoHWSerial. // -static volatile uint16_t count = 0; +#include + +static volatile uint16_t ISR_count = 0; -static void char_received( uint8_t c ) +static bool char_received( uint8_t c, uint8_t status ) { // This is a little naughty, as it will try to block // in this ISR if the tx_buffer is full. For this example, // we are only sending as many characters as we have received, // and they arrive at the same rate we are sending them. NeoSerial.write( c ); - count++; + ISR_count++; + return false; // do not store received data to ring buffer } void setup() @@ -28,17 +29,21 @@ void loop() { delay( 1000 ); - uint8_t oldSREG = SREG; + // get number of calls to char_received() noInterrupts(); - uint16_t old_count = count; - count = 0; - SREG = oldSREG; - - if (old_count) { + uint16_t echo_count = ISR_count; + ISR_count = 0; + interrupts(); + + if (echo_count) + { NeoSerial.print( '\n' ); - NeoSerial.print( old_count ); + NeoSerial.print( echo_count ); NeoSerial.println( F(" characters echoed") ); - } else + } + else + { NeoSerial.print( '.' ); + } NeoSerial.flush(); -} \ No newline at end of file +} diff --git a/keywords.txt b/keywords.txt new file mode 100755 index 0000000..8c46adb --- /dev/null +++ b/keywords.txt @@ -0,0 +1,65 @@ +############################################### +# Syntax coloring for NeoHWSerial library +############################################### + +################################### +# Classes and Datatypes (KEYWORD1) +################################### + +NeoSerial KEYWORD1 +NeoSerial0 KEYWORD1 +NeoSerial1 KEYWORD1 +NeoSerial2 KEYWORD1 +NeoSerial3 KEYWORD1 + +################################### +# Methods and Functions (KEYWORD2) +################################### + +begin KEYWORD2 +end KEYWORD2 +available KEYWORD2 +peek KEYWORD2 +read KEYWORD2 +availableForWrite KEYWORD2 +flush KEYWORD2 +write KEYWORD2 +attachInterrupt KEYWORD2 +detachInterrupt KEYWORD2 +serialEventRun KEYWORD2 + +################################### +# Constants (LITERAL1) +################################### + +HAVE_HWSERIAL0 LITERAL1 +HAVE_HWSERIAL1 LITERAL1 +HAVE_HWSERIAL2 LITERAL1 +HAVE_HWSERIAL3 LITERAL1 +SERIAL_TX_BUFFER_SIZE LITERAL1 +SERIAL_5N1 LITERAL1 +SERIAL_6N1 LITERAL1 +SERIAL_7N1 LITERAL1 +SERIAL_8N1 LITERAL1 +SERIAL_5N2 LITERAL1 +SERIAL_6N2 LITERAL1 +SERIAL_7N2 LITERAL1 +SERIAL_8N2 LITERAL1 +SERIAL_5E1 LITERAL1 +SERIAL_6E1 LITERAL1 +SERIAL_7E1 LITERAL1 +SERIAL_8E1 LITERAL1 +SERIAL_5E2 LITERAL1 +SERIAL_6E2 LITERAL1 +SERIAL_7E2 LITERAL1 +SERIAL_8E2 LITERAL1 +SERIAL_5O1 LITERAL1 +SERIAL_6O1 LITERAL1 +SERIAL_7O1 LITERAL1 +SERIAL_8O1 LITERAL1 +SERIAL_5O2 LITERAL1 +SERIAL_6O2 LITERAL1 +SERIAL_7O2 LITERAL1 +SERIAL_8O2 LITERAL1 + +##################### END ##################### diff --git a/library.properties b/library.properties new file mode 100755 index 0000000..132ab63 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=NeoHWSerial +version=1.6.9 +author=Georg Icking-Konert +maintainer=Georg Icking-Konert +sentence=NeoHWSerial for AVR +paragraph=Adaptation of NeoHWSerial by SlashDevin for AVR +category=Communication +url=https://github.com/gicking/NeoHWSerial +architectures=avr diff --git a/1.6.5r2/NeoHWSerial.cpp b/src/NeoHWSerial.cpp old mode 100644 new mode 100755 similarity index 61% rename from 1.6.5r2/NeoHWSerial.cpp rename to src/NeoHWSerial.cpp index 24cc147..27e8857 --- a/1.6.5r2/NeoHWSerial.cpp +++ b/src/NeoHWSerial.cpp @@ -15,49 +15,81 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - + Modified 23 November 2006 by David A. Mellis Modified 28 September 2010 by Mark Sproul Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert */ +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) + #include #include #include #include -#include "Arduino.h" +#include +#include -#include -#include +#include "NeoHWSerial.h" +#include "NeoHWSerial_private.h" -// this next line disables the entire HardwareSerial.cpp, +// this next line disables the entire HardwareSerial.cpp, // this is so I can support Attiny series and any other chip without a uart #if defined(HAVE_HWSERIAL0) || defined(HAVE_HWSERIAL1) || defined(HAVE_HWSERIAL2) || defined(HAVE_HWSERIAL3) -// Actual interrupt handlers ////////////////////////////////////////////////////////////// +// NeoSerialEvent functions are weak, so when the user doesn't define them, +// the linker just sets their address to 0 (which is checked below). +// The NeoSerialx_available is just a wrapper around NeoSerialx.available(), +// but we can refer to it weakly so we don't pull in the entire +// NeoHWSerial instance if the user doesn't also refer to it. +#if defined(HAVE_HWSERIAL0) + void serialEvent() __attribute__((weak)); // called by main() -> name fixed + bool NeoSerial0_available() __attribute__((weak)); +#endif -void NeoHWSerial::_tx_udr_empty_irq(void) -{ - // If interrupts are enabled, there must be more data in the output - // buffer. Send the next byte - unsigned char c = _tx_buffer[_tx_buffer_tail]; - _tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE; +#if defined(HAVE_HWSERIAL1) + void serialEvent1() __attribute__((weak)); // called by main() -> name fixed + bool NeoSerial1_available() __attribute__((weak)); +#endif - *_udr = c; +#if defined(HAVE_HWSERIAL2) + void serialEvent2() __attribute__((weak)); // called by main() -> name fixed + bool NeoSerial2_available() __attribute__((weak)); +#endif - // clear the TXC bit -- "can be cleared by writing a one to its bit - // location". This makes sure flush() won't return until the bytes - // actually got written - sbi(*_ucsra, TXC0); +#if defined(HAVE_HWSERIAL3) + void serialEvent3() __attribute__((weak)); // called by main() -> name fixed + bool NeoSerial3_available() __attribute__((weak)); +#endif - if (_tx_buffer_head == _tx_buffer_tail) { - // Buffer empty, so disable interrupts - cbi(*_ucsrb, UDRIE0); - } +void serialEventRun(void) // called by main() -> name fixed +{ +#if defined(HAVE_HWSERIAL0) + if (NeoSerial0_available && serialEvent && NeoSerial0_available()) serialEvent(); +#endif +#if defined(HAVE_HWSERIAL1) + if (NeoSerial1_available && serialEvent1 && NeoSerial1_available()) serialEvent1(); +#endif +#if defined(HAVE_HWSERIAL2) + if (NeoSerial2_available && serialEvent2 && NeoSerial2_available()) serialEvent2(); +#endif +#if defined(HAVE_HWSERIAL3) + if (NeoSerial3_available && serialEvent3 && NeoSerial3_available()) serialEvent3(); +#endif } +// macro to guard critical sections when needed for large TX buffer sizes +#if (SERIAL_TX_BUFFER_SIZE>256) + #define TX_BUFFER_ATOMIC ATOMIC_BLOCK(ATOMIC_RESTORESTATE) +#else + #define TX_BUFFER_ATOMIC +#endif + + // Public Methods ////////////////////////////////////////////////////////////// void NeoHWSerial::begin(unsigned long baud, byte config) @@ -88,7 +120,7 @@ void NeoHWSerial::begin(unsigned long baud, byte config) config |= 0x80; // select UCSRC register (shared with UBRRH) #endif *_ucsrc = config; - + sbi(*_ucsrb, RXEN0); sbi(*_ucsrb, TXEN0); sbi(*_ucsrb, RXCIE0); @@ -98,14 +130,13 @@ void NeoHWSerial::begin(unsigned long baud, byte config) void NeoHWSerial::end() { // wait for transmission of outgoing data - while (_tx_buffer_head != _tx_buffer_tail) - ; + flush(); cbi(*_ucsrb, RXEN0); cbi(*_ucsrb, TXEN0); cbi(*_ucsrb, RXCIE0); cbi(*_ucsrb, UDRIE0); - + // clear any received data _rx_buffer_head = _rx_buffer_tail; } @@ -138,15 +169,13 @@ int NeoHWSerial::read(void) int NeoHWSerial::availableForWrite(void) { -#if (SERIAL_TX_BUFFER_SIZE>256) - uint8_t oldSREG = SREG; - cli(); -#endif - tx_buffer_index_t head = _tx_buffer_head; - tx_buffer_index_t tail = _tx_buffer_tail; -#if (SERIAL_TX_BUFFER_SIZE>256) - SREG = oldSREG; -#endif + tx_buffer_index_t head; + tx_buffer_index_t tail; + + TX_BUFFER_ATOMIC { + head = _tx_buffer_head; + tail = _tx_buffer_tail; + } if (head >= tail) return SERIAL_TX_BUFFER_SIZE - 1 - head + tail; return tail - head - 1; } @@ -179,13 +208,27 @@ size_t NeoHWSerial::write(uint8_t c) // significantly improve the effective datarate at high (> // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) { - *_udr = c; - sbi(*_ucsra, TXC0); + // If TXC is cleared before writing UDR and the previous byte + // completes before writing to UDR, TXC will be set but a byte + // is still being transmitted causing flush() to return too soon. + // So writing UDR must happen first. + // Writing UDR and clearing TC must be done atomically, otherwise + // interrupts might delay the TXC clear so the byte written to UDR + // is transmitted (setting TXC) before clearing TXC. Then TXC will + // be cleared when no bytes are left, causing flush() to hang + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + *_udr = c; +#ifdef MPCM0 + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); +#else + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << TXC0))); +#endif + } return 1; } tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE; - - // If the output buffer is full, there's nothing for it other than to + + // If the output buffer is full, there's nothing for it other than to // wait for the interrupt handler to empty it a bit while (i == _tx_buffer_tail) { if (bit_is_clear(SREG, SREG_I)) { @@ -194,17 +237,22 @@ size_t NeoHWSerial::write(uint8_t c) // interrupt has happened and call the handler to free up // space for us. if(bit_is_set(*_ucsra, UDRE0)) - _tx_udr_empty_irq(); + _tx_udr_empty_irq(); } else { // nop, the interrupt handler will free up space for us } } _tx_buffer[_tx_buffer_head] = c; - _tx_buffer_head = i; - - sbi(*_ucsrb, UDRIE0); - + + // make atomic to prevent execution of ISR between setting the + // head pointer and setting the interrupt flag resulting in buffer + // retransmission + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + _tx_buffer_head = i; + sbi(*_ucsrb, UDRIE0); + } + return 1; } @@ -212,8 +260,10 @@ void NeoHWSerial::attachInterrupt( isr_t fn ) { uint8_t oldSREG = SREG; cli(); - _isr = fn; + _isr = fn; SREG = oldSREG; } -#endif // whole file +#endif // HAVE_HWSERIAL0 || HAVE_HWSERIAL1 || HAVE_HWSERIAL2 || HAVE_HWSERIAL3 + +#endif // ARDUINO_ARCH_AVR diff --git a/1.6.5r2/NeoHWSerial.h b/src/NeoHWSerial.h old mode 100644 new mode 100755 similarity index 55% rename from 1.6.5r2/NeoHWSerial.h rename to src/NeoHWSerial.h index 0606c65..72358c3 --- a/1.6.5r2/NeoHWSerial.h +++ b/src/NeoHWSerial.h @@ -20,14 +20,19 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert + Modified 26 January 2025 by Georg Icking-Konert */ +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) + #ifndef NeoHWSerial_h #define NeoHWSerial_h #include - -#include "Stream.h" +#include +#include // Define constants and variables for buffering incoming serial data. We're // using a ring buffer (I think), in which head is the index of the location @@ -35,29 +40,34 @@ // location from which to read. // NOTE: a "power of 2" buffer size is reccomended to dramatically // optimize all the modulo operations for ring buffers. +// WARNING: When buffer sizes are increased to > 256, the buffer index +// variables are automatically increased in size, but the extra +// atomicity guards needed for that are not implemented. This will +// often work, but occasionally a race condition can occur that makes +// NeoHWSerial behave erratically. See https://github.com/arduino/Arduino/issues/2405 #if !defined(SERIAL_TX_BUFFER_SIZE) -#if (RAMEND < 1000) -#define SERIAL_TX_BUFFER_SIZE 16 -#else -#define SERIAL_TX_BUFFER_SIZE 64 -#endif + #if ((RAMEND - RAMSTART) < 1023) + #define SERIAL_TX_BUFFER_SIZE 16 + #else + #define SERIAL_TX_BUFFER_SIZE 64 + #endif #endif #if !defined(SERIAL_RX_BUFFER_SIZE) -#if (RAMEND < 1000) -#define SERIAL_RX_BUFFER_SIZE 16 -#else -#define SERIAL_RX_BUFFER_SIZE 64 -#endif + #if ((RAMEND - RAMSTART) < 1023) + #define SERIAL_RX_BUFFER_SIZE 16 + #else + #define SERIAL_RX_BUFFER_SIZE 64 + #endif #endif #if (SERIAL_TX_BUFFER_SIZE>256) -typedef uint16_t tx_buffer_index_t; + typedef uint16_t tx_buffer_index_t; #else -typedef uint8_t tx_buffer_index_t; + typedef uint8_t tx_buffer_index_t; #endif #if (SERIAL_RX_BUFFER_SIZE>256) -typedef uint16_t rx_buffer_index_t; + typedef uint16_t rx_buffer_index_t; #else -typedef uint8_t rx_buffer_index_t; + typedef uint8_t rx_buffer_index_t; #endif // Define config for Serial.begin(baud, config); @@ -120,7 +130,7 @@ class NeoHWSerial : public Stream virtual int available(void); virtual int peek(void); virtual int read(void); - int availableForWrite(void); + virtual int availableForWrite(void); virtual void flush(void); virtual size_t write(uint8_t); inline size_t write(unsigned long n) { return write((uint8_t)n); } @@ -129,14 +139,81 @@ class NeoHWSerial : public Stream inline size_t write(int n) { return write((uint8_t)n); } using Print::write; // pull in write(str) and write(buf, size) from Print operator bool() { return true; } - - // Interrupt handlers - Not intended to be called externally - inline void _rx_complete_irq(void); - void _tx_udr_empty_irq(void); - - typedef void (* isr_t)( uint8_t ); + inline void enable_rx(void) { sbi(*_ucsrb, RXEN0); } + inline void disable_rx(void) { cbi(*_ucsrb, RXEN0); } + + // Receive interrupt handler - Not intended to be called externally + inline void _rx_complete_irq(void) + { + volatile bool saveToBuffer; + volatile unsigned char status, data; + + // get Rx status and data + status = *_ucsra; + data = *_udr; + + // user receive function was attached -> call it with data and status byte + if (_isr) + saveToBuffer = _isr( data, status ); + + // no user routine attached -> save data in ring buffer + else + saveToBuffer = true; + + // default: save data in ring buffer + if (saveToBuffer) { + + // No Parity error, read byte and store it in the buffer if there is room + if (bit_is_clear(status, UPE0)) { + rx_buffer_index_t i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != _rx_buffer_tail) { + _rx_buffer[_rx_buffer_head] = data; + _rx_buffer_head = i; + } + } + + // Parity or framing error, don't do anything (data is dropped) + else { } + + } // if saveToBuffer + } // _rx_complete_irq() + + // Transmit interrupt handler - Not intended to be called externally + inline void _tx_udr_empty_irq(void) + { + // If interrupts are enabled, there must be more data in the output + // buffer. Send the next byte + unsigned char c = _tx_buffer[_tx_buffer_tail]; + _tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE; + + *_udr = c; + + // clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written. Other r/w bits are preserved, and zeroes + // written to the rest. + + #ifdef MPCM0 + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); + #else + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << TXC0))); + #endif + + if (_tx_buffer_head == _tx_buffer_tail) { + // Buffer empty, so disable interrupts + cbi(*_ucsrb, UDRIE0); + } + } // _tx_udr_empty_irq() + + typedef bool (* isr_t)( uint8_t d, uint8_t s ); void attachInterrupt( isr_t fn ); void detachInterrupt() { attachInterrupt( (isr_t) NULL ); }; + private: isr_t _isr; @@ -161,4 +238,8 @@ class NeoHWSerial : public Stream #define HAVE_HWSERIAL3 #endif -#endif +extern void serialEventRun(void) __attribute__((weak)); // called by main() -> name fixed + +#endif // NeoHWSerial_h + +#endif // ARDUINO_ARCH_AVR diff --git a/1.6.5r2/NeoHWSerial0.cpp b/src/NeoHWSerial0.cpp old mode 100644 new mode 100755 similarity index 55% rename from 1.6.5r2/NeoHWSerial0.cpp rename to src/NeoHWSerial0.cpp index 7ccd93d..4b01caa --- a/1.6.5r2/NeoHWSerial0.cpp +++ b/src/NeoHWSerial0.cpp @@ -21,11 +21,15 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert */ +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) + #include "Arduino.h" -#include -#include +#include "NeoHWSerial.h" +#include "NeoHWSerial_private.h" // Each NeoHWSerial is defined in its own file, sine the linker pulls // in the entire file when any element inside is used. --gc-sections can @@ -37,38 +41,47 @@ #if defined(HAVE_HWSERIAL0) -#if defined(USART_RX_vect) - ISR(USART_RX_vect) -#elif defined(USART0_RX_vect) - ISR(USART0_RX_vect) -#elif defined(USART_RXC_vect) - ISR(USART_RXC_vect) // ATmega8 -#else - #error "Don't know what the Data Received vector is called for NeoSerial" -#endif + #if defined(USART_RX_vect) + ISR(USART_RX_vect) + #elif defined(USART0_RX_vect) + ISR(USART0_RX_vect) + #elif defined(USART_RXC_vect) + ISR(USART_RXC_vect) // ATmega8 + #else + #error "Don't know what the Data Received vector is called for NeoSerial" + #endif + { + NeoSerial._rx_complete_irq(); + } + + #if defined(UART0_UDRE_vect) + ISR(UART0_UDRE_vect) + #elif defined(UART_UDRE_vect) + ISR(UART_UDRE_vect) + #elif defined(USART0_UDRE_vect) + ISR(USART0_UDRE_vect) + #elif defined(USART_UDRE_vect) + ISR(USART_UDRE_vect) + #else + #error "Don't know what the Data Register Empty vector is called for Serial" + #endif { - NeoSerial._rx_complete_irq(); + NeoSerial._tx_udr_empty_irq(); } -#if defined(UART0_UDRE_vect) -ISR(UART0_UDRE_vect) -#elif defined(UART_UDRE_vect) -ISR(UART_UDRE_vect) -#elif defined(USART0_UDRE_vect) -ISR(USART0_UDRE_vect) -#elif defined(USART_UDRE_vect) -ISR(USART_UDRE_vect) -#else - #error "Don't know what the Data Register Empty vector is called for Serial" -#endif -{ - NeoSerial._tx_udr_empty_irq(); -} + #if defined(UBRRH) && defined(UBRRL) + NeoHWSerial NeoSerial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR); + #else + NeoHWSerial NeoSerial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0); + #endif -#if defined(UBRRH) && defined(UBRRL) - NeoHWSerial NeoSerial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR); -#else - NeoHWSerial NeoSerial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0); -#endif + // Function that can be weakly referenced by serialEventRun to prevent + // pulling in this file if it's not otherwise used. + bool NeoSerial0_available() + { + return NeoSerial.available(); + } #endif // HAVE_HWSERIAL0 + +#endif // ARDUINO_ARCH_AVR diff --git a/1.6.5r2/NeoHWSerial1.cpp b/src/NeoHWSerial1.cpp old mode 100644 new mode 100755 similarity index 61% rename from 1.6.5r2/NeoHWSerial1.cpp rename to src/NeoHWSerial1.cpp index f96165c..675603e --- a/1.6.5r2/NeoHWSerial1.cpp +++ b/src/NeoHWSerial1.cpp @@ -21,11 +21,15 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert */ +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) + #include "Arduino.h" -#include -#include +#include "NeoHWSerial.h" +#include "NeoHWSerial_private.h" // Each NeoHWSerial is defined in its own file, sine the linker pulls // in the entire file when any element inside is used. --gc-sections can @@ -37,28 +41,37 @@ #if defined(HAVE_HWSERIAL1) -#if defined(UART1_RX_vect) -ISR(UART1_RX_vect) -#elif defined(USART1_RX_vect) -ISR(USART1_RX_vect) -#else -#error "Don't know what the Data Register Empty vector is called for Serial1" -#endif -{ - NeoSerial1._rx_complete_irq(); -} + #if defined(UART1_RX_vect) + ISR(UART1_RX_vect) + #elif defined(USART1_RX_vect) + ISR(USART1_RX_vect) + #else + #error "Don't know what the Data Register Empty vector is called for Serial1" + #endif + { + NeoSerial1._rx_complete_irq(); + } + + #if defined(UART1_UDRE_vect) + ISR(UART1_UDRE_vect) + #elif defined(USART1_UDRE_vect) + ISR(USART1_UDRE_vect) + #else + #error "Don't know what the Data Register Empty vector is called for Serial1" + #endif + { + NeoSerial1._tx_udr_empty_irq(); + } -#if defined(UART1_UDRE_vect) -ISR(UART1_UDRE_vect) -#elif defined(USART1_UDRE_vect) -ISR(USART1_UDRE_vect) -#else -#error "Don't know what the Data Register Empty vector is called for Serial1" -#endif -{ - NeoSerial1._tx_udr_empty_irq(); -} + NeoHWSerial NeoSerial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1); -NeoHWSerial NeoSerial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1); + // Function that can be weakly referenced by serialEventRun to prevent + // pulling in this file if it's not otherwise used. + bool NeoSerial1_available() + { + return NeoSerial1.available(); + } #endif // HAVE_HWSERIAL1 + +#endif // ARDUINO_ARCH_AVR diff --git a/1.6.5r2/NeoHWSerial2.cpp b/src/NeoHWSerial2.cpp old mode 100644 new mode 100755 similarity index 70% rename from 1.6.5r2/NeoHWSerial2.cpp rename to src/NeoHWSerial2.cpp index f51a1ce..9b894b4 --- a/1.6.5r2/NeoHWSerial2.cpp +++ b/src/NeoHWSerial2.cpp @@ -21,11 +21,16 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert */ -#include "Arduino.h" -#include -#include +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) + +#include + +#include "NeoHWSerial.h" +#include "NeoHWSerial_private.h" // Each NeoHWSerial is defined in its own file, sine the linker pulls // in the entire file when any element inside is used. --gc-sections can @@ -37,16 +42,25 @@ #if defined(HAVE_HWSERIAL2) -ISR(USART2_RX_vect) -{ - NeoSerial2._rx_complete_irq(); -} + ISR(USART2_RX_vect) + { + NeoSerial2._rx_complete_irq(); + } -ISR(USART2_UDRE_vect) -{ - NeoSerial2._tx_udr_empty_irq(); -} + ISR(USART2_UDRE_vect) + { + NeoSerial2._tx_udr_empty_irq(); + } -NeoHWSerial NeoSerial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2); + NeoHWSerial NeoSerial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2); + + // Function that can be weakly referenced by serialEventRun to prevent + // pulling in this file if it's not otherwise used. + bool NeoSerial2_available() + { + return NeoSerial2.available(); + } #endif // HAVE_HWSERIAL2 + +#endif // ARDUINO_ARCH_AVR diff --git a/1.6.5r2/NeoHWSerial3.cpp b/src/NeoHWSerial3.cpp old mode 100644 new mode 100755 similarity index 71% rename from 1.6.5r2/NeoHWSerial3.cpp rename to src/NeoHWSerial3.cpp index 783f148..f3013ee --- a/1.6.5r2/NeoHWSerial3.cpp +++ b/src/NeoHWSerial3.cpp @@ -21,11 +21,15 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert */ +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) + #include "Arduino.h" -#include -#include +#include "NeoHWSerial.h" +#include "NeoHWSerial_private.h" // Each NeoHWSerial is defined in its own file, sine the linker pulls // in the entire file when any element inside is used. --gc-sections can @@ -37,16 +41,25 @@ #if defined(HAVE_HWSERIAL3) -ISR(USART3_RX_vect) -{ - NeoSerial3._rx_complete_irq(); -} + ISR(USART3_RX_vect) + { + NeoSerial3._rx_complete_irq(); + } + + ISR(USART3_UDRE_vect) + { + NeoSerial3._tx_udr_empty_irq(); + } -ISR(USART3_UDRE_vect) -{ - NeoSerial3._tx_udr_empty_irq(); -} + NeoHWSerial NeoSerial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3); -NeoHWSerial NeoSerial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3); + // Function that can be weakly referenced by serialEventRun to prevent + // pulling in this file if it's not otherwise used. + bool NeoSerial3_available() + { + return NeoSerial3.available(); + } #endif // HAVE_HWSERIAL3 + +#endif // ARDUINO_ARCH_AVR diff --git a/1.6.5r2/NeoHWSerial_private.h b/src/NeoHWSerial_private.h old mode 100644 new mode 100755 similarity index 58% rename from 1.6.5r2/NeoHWSerial_private.h rename to src/NeoHWSerial_private.h index 816e6e3..f682565 --- a/1.6.5r2/NeoHWSerial_private.h +++ b/src/NeoHWSerial_private.h @@ -20,11 +20,15 @@ Modified 28 September 2010 by Mark Sproul Modified 14 August 2012 by Alarus Modified 2 November 2015 by SlashDev + Modified 31 October 2020 by Georg Icking-Konert */ -#include "wiring_private.h" +// only compile for AVR architecture +#if defined(ARDUINO_ARCH_AVR) -// this next line disables the entire HardwareSerial.cpp, +#include + +// this next line disables the entire HardwareSerial.cpp, // this is so I can support Attiny series and any other chip without a uart #if defined(HAVE_HWSERIAL0) || defined(HAVE_HWSERIAL1) || defined(HAVE_HWSERIAL2) || defined(HAVE_HWSERIAL3) @@ -34,34 +38,34 @@ // HardwareSerial constructor also works, but makes the code bigger and // slower. #if !defined(TXC0) -#if defined(TXC) -// Some chips like ATmega8 don't have UPE, only PE. The other bits are -// named as expected. -#if !defined(UPE) && defined(PE) -#define UPE PE -#endif -// On ATmega8, the uart and its bits are not numbered, so there is no TXC0 etc. -#define TXC0 TXC -#define RXEN0 RXEN -#define TXEN0 TXEN -#define RXCIE0 RXCIE -#define UDRIE0 UDRIE -#define U2X0 U2X -#define UPE0 UPE -#define UDRE0 UDRE -#elif defined(TXC1) -// Some devices have uart1 but no uart0 -#define TXC0 TXC1 -#define RXEN0 RXEN1 -#define TXEN0 TXEN1 -#define RXCIE0 RXCIE1 -#define UDRIE0 UDRIE1 -#define U2X0 U2X1 -#define UPE0 UPE1 -#define UDRE0 UDRE1 -#else -#error No UART found in NeoHWSerial.cpp -#endif + #if defined(TXC) + // Some chips like ATmega8 don't have UPE, only PE. The other bits are + // named as expected. + #if !defined(UPE) && defined(PE) + #define UPE PE + #endif + // On ATmega8, the uart and its bits are not numbered, so there is no TXC0 etc. + #define TXC0 TXC + #define RXEN0 RXEN + #define TXEN0 TXEN + #define RXCIE0 RXCIE + #define UDRIE0 UDRIE + #define U2X0 U2X + #define UPE0 UPE + #define UDRE0 UDRE + #elif defined(TXC1) + // Some devices have uart1 but no uart0 + #define TXC0 TXC1 + #define RXEN0 RXEN1 + #define TXEN0 TXEN1 + #define RXCIE0 RXCIE1 + #define UDRIE0 UDRIE1 + #define U2X0 U2X1 + #define UPE0 UPE1 + #define UDRE0 UDRE1 + #else + #error No UART found in NeoHWSerial.cpp + #endif // defined(TXC) #endif // !defined TXC0 // Check at compiletime that it is really ok to use the bit positions of @@ -70,17 +74,17 @@ #if defined(TXC1) && (TXC1 != TXC0 || RXEN1 != RXEN0 || RXCIE1 != RXCIE0 || \ UDRIE1 != UDRIE0 || U2X1 != U2X0 || UPE1 != UPE0 || \ UDRE1 != UDRE0) -#error "Not all bit positions for UART1 are the same as for UART0" + #error "Not all bit positions for UART1 are the same as for UART0" #endif #if defined(TXC2) && (TXC2 != TXC0 || RXEN2 != RXEN0 || RXCIE2 != RXCIE0 || \ UDRIE2 != UDRIE0 || U2X2 != U2X0 || UPE2 != UPE0 || \ UDRE2 != UDRE0) -#error "Not all bit positions for UART2 are the same as for UART0" + #error "Not all bit positions for UART2 are the same as for UART0" #endif #if defined(TXC3) && (TXC3 != TXC0 || RXEN3 != RXEN0 || RXCIE3 != RXCIE0 || \ UDRIE3 != UDRIE0 || U3X3 != U3X0 || UPE3 != UPE0 || \ UDRE3 != UDRE0) -#error "Not all bit positions for UART3 are the same as for UART0" + #error "Not all bit positions for UART3 are the same as for UART0" #endif // Constructors //////////////////////////////////////////////////////////////// @@ -94,36 +98,10 @@ NeoHWSerial::NeoHWSerial( _udr(udr), _rx_buffer_head(0), _rx_buffer_tail(0), _tx_buffer_head(0), _tx_buffer_tail(0), - _isr(0) + _isr(NULL) { } -// Actual interrupt handlers ////////////////////////////////////////////////////////////// - -void NeoHWSerial::_rx_complete_irq(void) -{ - if (bit_is_clear(*_ucsra, UPE0)) { - // No Parity error, read byte and store it in the buffer if there is - // room - unsigned char c = *_udr; - if (_isr) - _isr( c ); - else { - rx_buffer_index_t i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE; - - // if we should be storing the received character into the location - // just before the tail (meaning that the head would advance to the - // current location of the tail), we're about to overflow the buffer - // and so we don't write the character or advance the head. - if (i != _rx_buffer_tail) { - _rx_buffer[_rx_buffer_head] = c; - _rx_buffer_head = i; - } - } - } else { - // Parity error, read byte but discard it - *_udr; - }; -} +#endif // HAVE_HWSERIAL0 || HAVE_HWSERIAL1 || HAVE_HWSERIAL2 || HAVE_HWSERIAL3 -#endif // whole file +#endif // ARDUINO_ARCH_AVR