Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
160 changes: 148 additions & 12 deletions AltSoftSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,17 @@ static volatile uint8_t tx_buffer_head;
static volatile uint8_t tx_buffer_tail;
#define TX_BUFFER_SIZE 68
static volatile uint8_t tx_buffer[TX_BUFFER_SIZE];
static uint8_t tx_parity;

static uint8_t data_bits, stop_bits;
static uint8_t parity; // 0 for none, 1 for odd, 2 for even
static uint8_t total_bits, almost_total_bits; // these are sums calculated during .begin() to speed up the loop in ISR(CAPTURE_INTERRUPT)

#ifndef INPUT_PULLUP
#define INPUT_PULLUP INPUT
#endif

void AltSoftSerial::init(uint32_t cycles_per_bit)
void AltSoftSerial::init(uint32_t cycles_per_bit, uint8_t config)
{
if (cycles_per_bit < 7085) {
CONFIG_TIMER_NOPRESCALE();
Expand All @@ -85,6 +89,7 @@ void AltSoftSerial::init(uint32_t cycles_per_bit)
tx_state = 0;
tx_buffer_head = 0;
tx_buffer_tail = 0;
setBitCounts(config);
ENABLE_INT_INPUT_CAPTURE();
}

Expand Down Expand Up @@ -119,6 +124,8 @@ void AltSoftSerial::writeByte(uint8_t b)
tx_state = 1;
tx_byte = b;
tx_bit = 0;
if (parity)
tx_parity = parity_even_bit(b) == (parity==2);
ENABLE_INT_COMPARE_A();
CONFIG_MATCH_CLEAR();
SET_COMPARE_A(GET_TIMER_COUNT() + 16);
Expand All @@ -135,7 +142,7 @@ ISR(COMPARE_A_INTERRUPT)
state = tx_state;
byte = tx_byte;
target = GET_COMPARE_A();
while (state < 9) {
while (state < (data_bits+1)) {
target += ticks_per_bit;
bit = byte & 1;
byte >>= 1;
Expand All @@ -154,9 +161,21 @@ ISR(COMPARE_A_INTERRUPT)
return;
}
}
if (state == 9) {
tx_state = 10;
if((!parity && state == (data_bits + 1)) || state == (data_bits + 2)) {
tx_state = data_bits + 3;
CONFIG_MATCH_SET();
SET_COMPARE_A(target + (stop_bits * ticks_per_bit));
return;
} else if (state == (data_bits + 1)) {
tx_state = data_bits + 2;
if (tx_parity != tx_bit) {
if (tx_parity) {
CONFIG_MATCH_SET();
} else {
CONFIG_MATCH_CLEAR();
}
tx_bit = tx_parity;
}
SET_COMPARE_A(target + ticks_per_bit);
return;
}
Expand All @@ -172,6 +191,8 @@ ISR(COMPARE_A_INTERRUPT)
tx_buffer_tail = tail;
tx_byte = tx_buffer[tail];
tx_bit = 0;
if (parity)
tx_parity = parity_even_bit(tx_byte) == (parity==2);
CONFIG_MATCH_CLEAR();
SET_COMPARE_A(target + ticks_per_bit);
// TODO: how to detect timing_error?
Expand All @@ -191,7 +212,7 @@ void AltSoftSerial::flushOutput(void)

ISR(CAPTURE_INTERRUPT)
{
uint8_t state, bit, head;
uint8_t state, bit, head, rx_parity;
uint16_t capture, target;
int16_t offset;

Expand All @@ -217,21 +238,27 @@ ISR(CAPTURE_INTERRUPT)
while (1) {
offset = capture - target;
if (offset < 0) break;
if (state >= 1 && state <= data_bits) // only store data bits
rx_byte = (rx_byte >> 1) | rx_bit;
target += ticks_per_bit;
state++;
if (state >= 9) {
if (state >= total_bits) { // this is 9 for 8N1 or 10 for 8E1
DISABLE_INT_COMPARE_B();
head = rx_buffer_head + 1;
if (head >= RX_BUFFER_SIZE) head = 0;
if (head != rx_buffer_tail) {
rx_buffer[head] = rx_byte;
rx_buffer_head = head;
if (!parity || (parity_even_bit(rx_byte) == (parity==2)) == rx_parity) {
head = rx_buffer_head + 1;
if (head >= RX_BUFFER_SIZE) head = 0;
if (head != rx_buffer_tail) {
rx_buffer[head] = rx_byte;
rx_buffer_head = head;
}
}
CONFIG_CAPTURE_FALLING_EDGE();
rx_bit = 0;
rx_state = 0;
return;
} else if (state < almost_total_bits) {
// in parity bit
rx_parity = rx_bit;
}
}
rx_target = target;
Expand All @@ -248,7 +275,7 @@ ISR(COMPARE_B_INTERRUPT)
CONFIG_CAPTURE_FALLING_EDGE();
state = rx_state;
bit = rx_bit ^ 0x80;
while (state < 9) {
while (state < (data_bits + 1)) {
rx_byte = (rx_byte >> 1) | bit;
state++;
}
Expand Down Expand Up @@ -303,6 +330,115 @@ void AltSoftSerial::flushInput(void)
rx_buffer_head = rx_buffer_tail;
}

void AltSoftSerial::setBitCounts(uint8_t config) {
parity = 0;
stop_bits = 1;
switch (config) {
case SERIAL_5N1:
data_bits = 5;
break;
case SERIAL_6N1:
data_bits = 6;
break;
case SERIAL_7N1:
data_bits = 7;
break;
case SERIAL_8N1:
data_bits = 8;
break;
case SERIAL_5N2:
data_bits = 5;
stop_bits = 2;
break;
case SERIAL_6N2:
data_bits = 6;
stop_bits = 2;
break;
case SERIAL_7N2:
data_bits = 7;
stop_bits = 2;
break;
case SERIAL_8N2:
data_bits = 8;
stop_bits = 2;
break;
case SERIAL_5O1:
parity = 1;
data_bits = 5;
break;
case SERIAL_6O1:
parity = 1;
data_bits = 6;
break;
case SERIAL_7O1:
parity = 1;
data_bits = 7;
break;
case SERIAL_8O1:
parity = 1;
data_bits = 8;
break;
case SERIAL_5O2:
parity = 1;
data_bits = 5;
stop_bits = 2;
break;
case SERIAL_6O2:
parity = 1;
data_bits = 6;
stop_bits = 2;
break;
case SERIAL_7O2:
parity = 1;
data_bits = 7;
stop_bits = 2;
break;
case SERIAL_8O2:
parity = 1;
data_bits = 8;
stop_bits = 2;
break;
case SERIAL_5E1:
parity = 2;
data_bits = 5;
break;
case SERIAL_6E1:
parity = 2;
data_bits = 6;
break;
case SERIAL_7E1:
parity = 2;
data_bits = 7;
break;
case SERIAL_8E1:
parity = 2;
data_bits = 8;
break;
case SERIAL_5E2:
parity = 2;
data_bits = 5;
stop_bits = 2;
break;
case SERIAL_6E2:
parity = 2;
data_bits = 6;
stop_bits = 2;
break;
case SERIAL_7E2:
parity = 2;
data_bits = 7;
stop_bits = 2;
break;
case SERIAL_8E2:
parity = 2;
data_bits = 8;
stop_bits = 2;
break;
}

total_bits = data_bits + (parity ? 1 : 0) + stop_bits;
almost_total_bits = total_bits - stop_bits;
}

#ifdef ALTSS_USE_FTM0
void ftm0_isr(void)
Expand Down
8 changes: 6 additions & 2 deletions AltSoftSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define AltSoftSerial_h

#include <inttypes.h>
#include <util/parity.h>

#if ARDUINO >= 100
#include "Arduino.h"
Expand All @@ -44,7 +45,9 @@ class AltSoftSerial : public Stream
public:
AltSoftSerial() { }
~AltSoftSerial() { end(); }
static void begin(uint32_t baud) { init((ALTSS_BASE_FREQ + baud / 2) / baud); }
//static void begin(uint32_t baud) { init((ALTSS_BASE_FREQ + baud / 2) / baud); }
static void begin(uint32_t baud, uint8_t config = SERIAL_8N1) { init((F_CPU + baud / 2) / baud, config); }
static void begin(uint32_t baud) { init((ALTSS_BASE_FREQ + baud / 2) / baud, SERIAL_8N1); }
static void end();
int peek();
int read();
Expand All @@ -68,8 +71,9 @@ class AltSoftSerial : public Stream
static void enable_timer0(bool enable) { }
static bool timing_error;
private:
static void init(uint32_t cycles_per_bit);
static void init(uint32_t cycles_per_bit, uint8_t config);
static void writeByte(uint8_t byte);
static void setBitCounts(uint8_t config);
};

#endif