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
206 changes: 176 additions & 30 deletions AltSoftSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,20 @@ 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)
static uint8_t byte_alignment;

#ifndef INPUT_PULLUP
#define INPUT_PULLUP INPUT
#endif

#define MAX_COUNTS_PER_BIT 6241 // 65536 / 10.5

void AltSoftSerial::init(uint32_t cycles_per_bit)
void AltSoftSerial::init(uint32_t cycles_per_bit, uint8_t config)
{
//Serial.printf("cycles_per_bit = %d\n", cycles_per_bit);
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
Expand Down Expand Up @@ -100,7 +105,14 @@ void AltSoftSerial::init(uint32_t cycles_per_bit)
}
}
ticks_per_bit = cycles_per_bit;
rx_stop_ticks = cycles_per_bit * 37 / 4;
/* [2019-08-11, stattin42]: Length of frame depends on total number of symbols/bits.
Need to adjust for number of data bits and parity bits. If not, start bit may be missed
for short formats if msb is a 1. In this case there is no edge between data/parity and
stop bit --> no capture interrupt --> relies on timer interrupt. Have not looked into
aspects of longer formats. */
// rx_stop_ticks = cycles_per_bit * 37 / 4;
setBitCounts(config); // total_bits are calculated here
rx_stop_ticks = cycles_per_bit * ((4*(total_bits))+1)/4;
pinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP);
digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH);
pinMode(OUTPUT_COMPARE_A_PIN, OUTPUT);
Expand Down Expand Up @@ -144,14 +156,15 @@ 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);
}
SREG = intr_state;
}


ISR(COMPARE_A_INTERRUPT)
{
uint8_t state, byte, bit, head, tail;
Expand All @@ -160,15 +173,22 @@ ISR(COMPARE_A_INTERRUPT)
state = tx_state;
byte = tx_byte;
target = GET_COMPARE_A();
while (state < 10) {
target += ticks_per_bit;
if (state < 9)
bit = byte & 1;
else
bit = 1; // stopbit
byte >>= 1;
while (state < 11) {
target += ticks_per_bit; // Bit start time
if (state < 9) {
bit = byte & 1; // data bit
byte >>= 1;
} else {
if (state == 9) {
bit = tx_parity; // parity bit
} else {
bit = 1; // stop bit
}
}
state++;
if (bit != tx_bit) {
if (state == (data_bits+1))
state = 9 + !parity;
if (bit != tx_bit) { // schedule flip of output for bit starting at time target
if (bit) {
CONFIG_MATCH_SET();
} else {
Expand All @@ -185,10 +205,10 @@ ISR(COMPARE_A_INTERRUPT)
head = tx_buffer_head;
tail = tx_buffer_tail;
if (head == tail) {
if (state == 10) {
if (state == 11) {
// Wait for final stop bit to finish
tx_state = 11;
SET_COMPARE_A(target + ticks_per_bit);
tx_state = 12;
SET_COMPARE_A(target + (stop_bits * ticks_per_bit));
} else {
tx_state = 0;
CONFIG_MATCH_NORMAL();
Expand All @@ -199,9 +219,11 @@ 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();
if (state == 10)
SET_COMPARE_A(target + ticks_per_bit);
if (state == 11)
SET_COMPARE_A(target + (stop_bits * ticks_per_bit));
else
SET_COMPARE_A(GET_TIMER_COUNT() + 16);
tx_state = 1;
Expand Down Expand Up @@ -249,16 +271,24 @@ ISR(CAPTURE_INTERRUPT)
while (1) {
offset = capture - target;
if (offset > offset_overflow) break;
rx_byte = (rx_byte >> 1) | rx_bit;
if (state <= data_bits) // only store data bits
rx_byte = (rx_byte >> 1) | rx_bit;
target += ticks_per_bit;
state++;
if (state >= 9) {
if (state > almost_total_bits) {
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;
/* [2019-08-11, stattin42]: Bits in rx_byte are in correct
position only for 8-bit data format. For less than 8 bits,
rx_byte needs to be shifted for LS bit to get to bit0 location.
Added byte_alignment variable for speed: */
rx_byte = rx_byte>>byte_alignment;
if (!parity || (parity_even_bit(rx_byte) == (parity==2)) == (bool)rx_bit) {
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;
Expand All @@ -280,15 +310,21 @@ ISR(COMPARE_B_INTERRUPT)
CONFIG_CAPTURE_FALLING_EDGE();
state = rx_state;
bit = rx_bit ^ 0x80;
while (state < 9) {
while (state <= data_bits) {
rx_byte = (rx_byte >> 1) | bit;
state++;
}
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;
/* [2019-08-11, stattin42]: Bits in rx_byte are in correct position only for 8-bit
data format. For less than 8 bits, rx_byte needs to be shifted for LS bit to get to
bit0 location. Added byte_alignment variable for speed: */
rx_byte = rx_byte>>byte_alignment;
if (!parity || (parity_even_bit(rx_byte) == (parity==2)) == (bool)bit) {
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;
}
}
rx_state = 0;
CONFIG_CAPTURE_FALLING_EDGE();
Expand Down Expand Up @@ -339,13 +375,123 @@ int AltSoftSerial::availableForWrite(void)

if (tail > head) return tail - head;
return TX_BUFFER_SIZE + tail - head;
};
}

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;
byte_alignment = 8-data_bits;
}

#ifdef ALTSS_USE_FTM0
void ftm0_isr(void)
Expand Down
6 changes: 4 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,7 @@ 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, uint8_t config = SERIAL_8N1) { init((ALTSS_BASE_FREQ + baud / 2) / baud, config); }
static void end();
int peek();
int read();
Expand All @@ -69,8 +70,9 @@ class AltSoftSerial : public Stream
static void enable_timer0(bool enable) { (void)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