From 80071e9deaddaba0c263fecfa4c314afe510c619 Mon Sep 17 00:00:00 2001 From: Magnus Stattin Date: Thu, 23 Sep 2021 17:44:51 +0200 Subject: [PATCH] add support for different serial formats --- AltSoftSerial.cpp | 206 +++++++++++++++++++++++++++++++++++++++------- AltSoftSerial.h | 6 +- 2 files changed, 180 insertions(+), 32 deletions(-) diff --git a/AltSoftSerial.cpp b/AltSoftSerial.cpp index 916d056..f76d717 100644 --- a/AltSoftSerial.cpp +++ b/AltSoftSerial.cpp @@ -59,7 +59,12 @@ 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 @@ -67,7 +72,7 @@ static volatile uint8_t tx_buffer[TX_BUFFER_SIZE]; #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) { @@ -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); @@ -144,6 +156,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); @@ -151,7 +165,6 @@ void AltSoftSerial::writeByte(uint8_t b) SREG = intr_state; } - ISR(COMPARE_A_INTERRUPT) { uint8_t state, byte, bit, head, tail; @@ -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 { @@ -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(); @@ -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; @@ -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; @@ -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(); @@ -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) diff --git a/AltSoftSerial.h b/AltSoftSerial.h index 7860fdd..3c0e418 100644 --- a/AltSoftSerial.h +++ b/AltSoftSerial.h @@ -25,6 +25,7 @@ #define AltSoftSerial_h #include +#include #if ARDUINO >= 100 #include "Arduino.h" @@ -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(); @@ -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