Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for 32u4 #18

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d8f77b7
Merge pull request #1 from SlashDevin/master
SRGDamia1 Sep 8, 2017
d3f6b2d
Added Travis support
SRGDamia1 Sep 12, 2017
804e871
Added support for 32u4
SRGDamia1 Sep 12, 2017
b46608d
Added another test for myself
SRGDamia1 Sep 12, 2017
398c8da
Removed limits on baud rates for 8MHz boards
SRGDamia1 Sep 12, 2017
bd32958
Better testing script
SRGDamia1 Sep 13, 2017
78bfd3e
Merge pull request #2 from SlashDevin/master
SRGDamia1 May 18, 2018
4d45b56
Merge remote-tracking branch 'origin/master'
SRGDamia1 May 18, 2018
19a4266
Added peak and unsetting prescalers at ignore
SRGDamia1 May 18, 2018
cb04f1e
Merge branch 'devin'
SRGDamia1 May 18, 2018
cc1f99e
Interrupts off by default
SRGDamia1 May 18, 2018
8738faf
Oops, uncommented the wrong line!
SRGDamia1 May 18, 2018
283da33
Fixed some comments
SRGDamia1 May 21, 2018
63f17a1
Added missed setting of TCCR's
SRGDamia1 May 21, 2018
66f4508
Default take ISR
SRGDamia1 Jan 8, 2019
2251940
Travis on more boards
SRGDamia1 Jan 9, 2019
3cdc90d
Move tests
SRGDamia1 Jan 9, 2019
eaaea5f
Example to compile against
SRGDamia1 Jan 9, 2019
a2dbb78
Fix timer pre-setting assignment
SRGDamia1 Jan 9, 2019
075f1e0
Oops, wrong speeds
SRGDamia1 Jan 9, 2019
7046a6c
Missed ignore
SRGDamia1 Jan 9, 2019
a51d575
Skipping test on ATTINY
SRGDamia1 Jan 9, 2019
67f1a7f
Removed unwanted comment
SRGDamia1 Jan 9, 2019
dcbdc48
Get tests really working
SRGDamia1 Jan 11, 2019
02fe96e
Printing speed in test
SRGDamia1 Jan 15, 2019
545404e
Limit speeds for processors at less than 12mhz
SRGDamia1 Jan 15, 2019
f003e0a
Removed extra comment
SRGDamia1 Jan 15, 2019
a51c235
Small fix to print dropped chars in test
SRGDamia1 Jan 16, 2019
27229c4
Added notes on processors and pin numbers
SRGDamia1 Jan 16, 2019
96cfd10
Corrected 2560RFR2 tp 256RFR2, also added notes on compatibility at 8MHz
SRGDamia1 Jan 16, 2019
bf6653d
Added sign to pin vars, fixed elif
SRGDamia1 Aug 29, 2019
fff812b
version bump
SRGDamia1 Aug 29, 2019
e0a9c6e
Extra braces
SRGDamia1 Aug 29, 2019
79d693b
Make fallthrough explicity
SRGDamia1 May 24, 2021
5a0daf7
Make interrupts external by default
SRGDamia1 Jul 12, 2022
9626916
add support for RS232-type logic levels
tpwrules Jan 10, 2023
df6af12
NeoSWSerial: avoid undefined behavior when TX disable is requested
tpwrules Feb 13, 2023
304c2ae
Merge pull request #3 from tpwrules/fixes
SRGDamia1 Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ $RECYCLE.BIN/
Network Trash Folder
Temporary Items
.apdisk

# Atom / PlatformIO
.pioenvs
.piolibdeps
.clang_complete
.gcc-flags.json
lib/readme.txt
platformio.ini
17 changes: 17 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
language: python
python:
- "2.7"

sudo: false
cache:
directories:
- "~/.platformio"

env:
- PLATFORMIO_CI_SRC=test/test.ino

install:
- pip install -U platformio

script:
- platformio ci --lib="." --board=uno --board=megaatmega2560 --board=mayfly --board=feather32u4
200 changes: 137 additions & 63 deletions src/NeoSWSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,36 @@ static const uint8_t BITS_PER_TICK_31250_Q10 = 128;
static const uint8_t BITS_PER_TICK_38400_Q10 = 157;
// 1s/(38400 bits) * (1 tick)/(4 us) * 2^10 "multiplier"

// Choose the timer to use
#if F_CPU == 16000000L
#define TCNTX TCNT0
#define TCNTX TCNT0 // 8-bit timer w/ PWM, default prescaler has divisor of 64, thus 250kHz
#define PCI_FLAG_REGISTER PCIFR

#elif F_CPU == 8000000L
#if defined(__AVR_ATtiny25__) | \
defined(__AVR_ATtiny45__) | \
defined(__AVR_ATtiny85__)
#define TCNTX TCNT1
defined(__AVR_ATtiny85__)
#define TCNTX TCNT1 // 8-bit timer/counter w/ independent prescaler
#define PCI_FLAG_REGISTER GIFR
uint8_t preNSWS_TCCR1;

#elif defined(__AVR_ATmega32U4__)
#define TCNTX TCNT4 // 10-bit high speed timer, usable as 8-bit timer by ignoring high bits
#define PCI_FLAG_REGISTER PCIFR
uint8_t preNSWS_TCCR4A;
uint8_t preNSWS_TCCR4B;
uint8_t preNSWS_TCCR4C;
uint8_t preNSWS_TCCR4D;
uint8_t preNSWS_TCCR4E;

#else
#define TCNTX TCNT2
// Have to use timer 2 for an 8 MHz system because timer 0 doesn't have the correct prescaler
#define TCNTX TCNT2 // 8-bit timer w/ PWM, asynchronous opperation, and independent prescaler
SRGDamia1 marked this conversation as resolved.
Show resolved Hide resolved
#define PCI_FLAG_REGISTER PCIFR
uint8_t preNSWS_TCCR2A;
uint8_t preNSWS_TCCR2B;
#endif

#endif

static NeoSWSerial *listener = (NeoSWSerial *) NULL;
Expand Down Expand Up @@ -145,15 +162,24 @@ void NeoSWSerial::listen()
*txPort |= txBitMask; // high = idle
pinMode(txPin, OUTPUT);

// Set the timer prescaling as necessary - want to be running at 250kHz
if (F_CPU == 8000000L) {
// Have to use timer 2 for an 8 MHz system.
#if defined(__AVR_ATtiny25__) | \
defined(__AVR_ATtiny45__) | \
defined(__AVR_ATtiny85__)
TCCR1 = 0x06; // divide by 32
defined(__AVR_ATtiny85__)
TCCR1 = 0x06; // 0b00000110 - Clock Select bits 12 & 11 on - prescaling source = CK/32
// timer now running at 8MHz/32 = 250kHz
#elif defined(__AVR_ATmega32U4__)
TCCR4A = 0x00; // "normal" operation - Normal port operation, OC4A & OC4B disconnected
TCCR4B = 0x06; // 0b00000110 - Clock Select bits 42 & 41 on - prescaler set to CK/32
// timer now running at 8MHz/32 = 250kHz
TCCR4C = 0x00; // "normal" operation - Normal port operation, OC4D0 disconnected
TCCR4D = 0x00; // No fault protection
TCCR4E = 0x00; // No register locks or overrides
#else
TCCR2A = 0x00;
TCCR2B = 0x03; // divide by 32
TCCR2A = 0x00; // "normal" operation - Normal port operation, OC2A & OC2B disconnected
TCCR2B = 0x03; // 0b00000011 - Clock Select bits 21 & 20 on - prescaler set to clkT2S/32
// timer now running at 8MHz/32 = 250kHz
#endif
}

Expand All @@ -164,32 +190,28 @@ void NeoSWSerial::listen()
flush();

// Set up timings based on baud rate

switch (_baudRate) {
case 9600:
txBitWidth = TICKS_PER_BIT_9600 ;
bitsPerTick_Q10 = BITS_PER_TICK_38400_Q10 >> 2;
rxWindowWidth = 10;
break;
case 31250:
if (F_CPU > 12000000L) {
txBitWidth = TICKS_PER_BIT_31250;
bitsPerTick_Q10 = BITS_PER_TICK_31250_Q10;
rxWindowWidth = 5;
break;
} // else use 19200
case 38400:
if (F_CPU > 12000000L) {
txBitWidth = TICKS_PER_BIT_9600 >> 2;
bitsPerTick_Q10 = BITS_PER_TICK_38400_Q10 ;
rxWindowWidth = 4;
break;
} // else use 19200
case 31250:
txBitWidth = TICKS_PER_BIT_31250;
bitsPerTick_Q10 = BITS_PER_TICK_31250_Q10;
rxWindowWidth = 5;
break;
case 19200:
txBitWidth = TICKS_PER_BIT_9600 >> 1;
bitsPerTick_Q10 = BITS_PER_TICK_38400_Q10 >> 1;
rxWindowWidth = 6;
break;
case 9600: // default is 9600
txBitWidth = TICKS_PER_BIT_9600 ;
bitsPerTick_Q10 = BITS_PER_TICK_38400_Q10 >> 2;
rxWindowWidth = 10;
break;
}

// Enable the pin change interrupts
Expand Down Expand Up @@ -225,6 +247,24 @@ void NeoSWSerial::ignore()
SREG = prevSREG;
}

if (F_CPU == 8000000L) {
// Un-set the timer pre-scalers
#if defined(__AVR_ATtiny25__) | \
defined(__AVR_ATtiny45__) | \
defined(__AVR_ATtiny85__)
TCCR1 = preNSWS_TCCR1;
#elif defined(__AVR_ATmega32U4__)
TCCR4A = preNSWS_TCCR4A;
TCCR4B = preNSWS_TCCR4B;
TCCR4C = preNSWS_TCCR4C;
TCCR4D = preNSWS_TCCR4D;
TCCR4E = preNSWS_TCCR4E;
#else
TCCR2A = preNSWS_TCCR2A;
TCCR2B = preNSWS_TCCR2B;
#endif
}

} // ignore

//----------------------------------------------------------------------------
Expand All @@ -234,8 +274,8 @@ void NeoSWSerial::setBaudRate(uint16_t baudRate)
if ((
( baudRate == 9600) ||
( baudRate == 19200) ||
((baudRate == 31250) && (F_CPU == 16000000L)) ||
((baudRate == 38400) && (F_CPU == 16000000L))
( baudRate == 31250) ||
( baudRate == 38400)
)
&&
(_baudRate != baudRate)) {
Expand All @@ -252,7 +292,7 @@ void NeoSWSerial::setBaudRate(uint16_t baudRate)
int NeoSWSerial::available()
{
uint8_t avail = ((rxHead - rxTail + RX_BUFFER_SIZE) % RX_BUFFER_SIZE);

if (avail == 0) {
cli();
if (checkRxTime()) {
Expand All @@ -268,13 +308,21 @@ int NeoSWSerial::available()

//----------------------------------------------------------------------------

int NeoSWSerial::peek()
{
if (rxHead == rxTail) return -1; // Empty buffer? If yes, -1
return rxBuffer[rxHead]; // Otherwise, read from "head"
} // peek

//----------------------------------------------------------------------------

int NeoSWSerial::read()
{
if (rxHead == rxTail) return -1;
uint8_t c = rxBuffer[rxTail];
rxTail = (rxTail + 1) % RX_BUFFER_SIZE;
if (rxHead == rxTail) return -1; // Empty buffer? If yes, -1
uint8_t c = rxBuffer[rxTail]; // Otherwise, grab char at head
SRGDamia1 marked this conversation as resolved.
Show resolved Hide resolved
rxTail = (rxTail + 1) % RX_BUFFER_SIZE; // increment head

return c;
return c; // return the char

} // read

Expand Down Expand Up @@ -305,62 +353,88 @@ void NeoSWSerial::startChar()

void NeoSWSerial::rxISR( uint8_t rxPort )
{
uint8_t t0 = TCNTX; // time of data transition (plus ISR latency)
uint8_t d = rxPort & rxBitMask; // read RX data level
uint8_t t0 = TCNTX; // time of data transition (plus ISR latency)
uint8_t d = rxPort & rxBitMask; // read RX data level

// Check if we're ready for a start bit, and if this could possibly be it
// Otherwise, just ignore the interrupt and exit
if (rxState == WAITING_FOR_START_BIT) {

// If it looks like a start bit then initialize;
// otherwise ignore the rising edge and exit.

if (d != 0) return; // it's high so not a start bit, exit
// If it is HIGH it's not a start bit, exit
if (d != 0) return;
// If it is LOW, this should be a start bit
// Thus set the rxStat to 0, create an empty character, and a new mask with a 1 in the lowest place
startChar();

} else { // data bit or stop bit (probably) received
}

// if the character is incomplete, and this is not a start bit,
// then this change is from a data or stop bit
else {

DBG_NSS_ARRAY(bitTransitionTimes, bitTransitions, (t0-prev_t0));

// Determine how many bit periods have elapsed since the last transition.

// check how many bit times have passed since the last change
// the rxWindowWidth is just a fudge factor
uint16_t rxBits = bitTimes( t0-prev_t0 );
uint8_t bitsLeft = 9 - rxState; // ignores stop bit
// calculate how many *data* bits should be left
// We know the start bit is past and are ignoring the stop bit
uint8_t bitsLeft = 9 - rxState;
// note that a new character *may* have started if more bits have been
// received than should be left.
// This will also happen if last bit(s) of the character are all 0's.
bool nextCharStarted = (rxBits > bitsLeft);

if (nextCharStarted)
DBG_NSS_ARRAY(rxStartCompletionBits,rxStartCompletions,(10*rxBits + bitsLeft));

uint8_t bitsThisFrame = nextCharStarted ? bitsLeft : rxBits;

// check how many data bits have been sent in this frame
// If the total number of bits in this frame is more than the number of data
// bits remaining in the character, then the number of data bits is equal
// to the number of bits remaining for the character and partiy. If the total
// number of bits in this frame is less than the number of data bits left
// for the character and parity, then the number of data bits received
// in this frame is equal to the total number of bits received in this frame.
// translation:
// if nextCharStarted then bitsThisFrame = bitsLeft
// else bitsThisFrame = rxBits
uint8_t bitsThisFrame = nextCharStarted ? bitsLeft : rxBits;
// Tick up the rxState by that many bits
rxState += bitsThisFrame;

// Set all those bits

// Set all the bits received between the last change and this change
// If the current state is LOW (and it just became so), then all bits between
// the last change and now must have been HIGH.
if (d == 0) {
// back fill previous bits with 1's
while (bitsThisFrame-- > 0) {
rxValue |= rxMask;
rxMask = rxMask << 1;
rxValue |= rxMask; // Add a 1 to the LSB/right-most place
rxMask = rxMask << 1;; // Shift the 1 in the mask up by one position
}
rxMask = rxMask << 1;
} else { // d==1
rxMask = rxMask << 1; // Shift the 1 in the mask up by one more position
}
// If the current state is HIGH (and it just became so), then this bit is HIGH
// but all bits between the last change and now must have been LOW
else { // d==1
// previous bits were 0's so only this bit is a 1.
rxMask = rxMask << (bitsThisFrame-1);
rxValue |= rxMask;
rxMask = rxMask << (bitsThisFrame-1); // Shift the 1 in the mask up by the number of bits past
rxValue |= rxMask; // Add that shifted one to the character being created
}

// If 8th bit or stop bit then the character is complete.

if (rxState > 7) {
rxChar( rxValue );
rxChar( rxValue ); // Put the finished character into the buffer
SRGDamia1 marked this conversation as resolved.
Show resolved Hide resolved

// if this is HIGH, or we haven't exceeded the number of bits in a
// character (but have gotten all the data bits) then this should be a
// stop bit and we can start looking for a new start bit.
if ((d == 1) || !nextCharStarted) {
rxState = WAITING_FOR_START_BIT;
// DISABLE STOP BIT TIMER

rxState = WAITING_FOR_START_BIT; // DISABLE STOP BIT TIMER
} else {
// The last char ended with 1's, so this 0 is actually
// the start bit of the next character.

// If we just switched to LOW, or we've exceeded the total number of
// bits in a character, then the character must have ended with 1's/HIGH,
// and this new 0/LOW is actually the start bit of the next character.
startChar();
}
}
Expand Down Expand Up @@ -457,13 +531,13 @@ void NeoSWSerial::rxChar( uint8_t c )

#elif defined(__AVR_ATtiny25__) | \
defined(__AVR_ATtiny45__) | \
defined(__AVR_ATtiny85__)
defined(__AVR_ATtiny85__)

PCINT_ISR(0, PINB);

#elif defined(__AVR_ATtiny24__) | \
defined(__AVR_ATtiny44__) | \
defined(__AVR_ATtiny84__)
defined(__AVR_ATtiny84__)

PCINT_ISR(0, PINA);
PCINT_ISR(1, PINB);
Expand Down Expand Up @@ -535,9 +609,9 @@ size_t NeoSWSerial::write(uint8_t txChar)

uint8_t t0 = TCNTX; // start time

// TODO: This would benefit from an early break after
// TODO: This would benefit from an early break after
// the last 0 data bit. Then we could wait for the
// remaining 1 data bits and stop bit with interrupts
// remaining 1 data bits and stop bit with interrupts
// re-enabled.

while (txBit++ < 9) { // repeat for start bit + 8 data bits
Expand All @@ -551,7 +625,7 @@ size_t NeoSWSerial::write(uint8_t txChar)
(width == TICKS_PER_BIT_9600/4) &&
(txBit & 0x01)) {
// The width is 6.5 ticks, so add a tick every other bit
width++;
width++;
}

// Hold the line for the bit duration
Expand Down
Loading