/* An Alternative Software Serial Library * http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html * Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com * * 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. */ // Revisions are now tracked on GitHub // https://github.com/PaulStoffregen/AltSoftSerial // // Version 1.2: Support Teensy 3.x // // Version 1.1: Improve performance in receiver code // // Version 1.0: Initial Release #include "AltSoftSerial.h" #include "config/AltSoftSerial_Boards.h" #include "config/AltSoftSerial_Timers.h" /****************************************/ /** Initialization **/ /****************************************/ bool timing_error=false; static volatile uint8_t rx_state; static uint8_t rx_byte; static uint8_t rx_bit = 0; static uint16_t rx_target; static volatile uint8_t rx_buffer_head; static volatile uint8_t rx_buffer_tail; #define RX_BUFFER_SIZE 20 static volatile uint8_t rx_buffer[RX_BUFFER_SIZE]; const static uint32_t cycles_per_bit = 1667UL; // (ALTSS_BASE_FREQ + 9600 / 2) / 9600UL; const static uint32_t rx_stop_ticks = 15419UL; #ifndef INPUT_PULLUP #define INPUT_PULLUP INPUT #endif #define MAX_COUNTS_PER_BIT 6241 // 65536 / 10.5 void softserial_init() { //Serial.printf("cycles_per_bit = %d\n", cycles_per_bit); CONFIG_TIMER_NOPRESCALE(); pinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP); digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH); pinMode(OUTPUT_COMPARE_A_PIN, OUTPUT); rx_state = 0; rx_buffer_head = 0; rx_buffer_tail = 0; ENABLE_INT_INPUT_CAPTURE(); DISABLE_INT_COMPARE_A(); } void softserial_end(void) { DISABLE_INT_COMPARE_B(); DISABLE_INT_INPUT_CAPTURE(); softserial_flush(); DISABLE_INT_COMPARE_A(); // TODO: restore timer to original settings? } /****************************************/ /** Reception **/ /****************************************/ ISR(CAPTURE_INTERRUPT) { uint8_t bit, head, state; uint16_t capture, target; uint16_t offset, offset_overflow; capture = GET_INPUT_CAPTURE(); bit = rx_bit; if (bit) { CONFIG_CAPTURE_FALLING_EDGE(); rx_bit = 0; } else { CONFIG_CAPTURE_RISING_EDGE(); rx_bit = 0x80; } state = rx_state; if (state == 0) { if (!bit) { uint16_t end = capture + rx_stop_ticks; SET_COMPARE_B(end); ENABLE_INT_COMPARE_B(); rx_target = capture + cycles_per_bit + cycles_per_bit/2; rx_state = 1; } } else { target = rx_target; offset_overflow = 65535 - cycles_per_bit; while (1) { offset = capture - target; if (offset > offset_overflow) break; rx_byte = (rx_byte >> 1) | rx_bit; target += cycles_per_bit; state++; if (state >= 9) { 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; } CONFIG_CAPTURE_FALLING_EDGE(); rx_bit = 0; rx_state = 0; return; } } rx_target = target; rx_state = state; } //if (GET_TIMER_COUNT() - capture > cycles_per_bit) AltSoftSerial::timing_error = true; } ISR(COMPARE_B_INTERRUPT) { uint8_t state, bit, head; DISABLE_INT_COMPARE_B(); CONFIG_CAPTURE_FALLING_EDGE(); state = rx_state; bit = rx_bit ^ 0x80; while (state < 9) { 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; } rx_state = 0; CONFIG_CAPTURE_FALLING_EDGE(); rx_bit = 0; } const int softserial_read() { uint8_t head, tail, out; head = rx_buffer_head; tail = rx_buffer_tail; if (head == tail) return -1; if (++tail >= RX_BUFFER_SIZE) tail = 0; out = rx_buffer[tail]; rx_buffer_tail = tail; return out; } const int softserial_available() { uint8_t head, tail; head = rx_buffer_head; tail = rx_buffer_tail; if (head >= tail) return head - tail; return RX_BUFFER_SIZE + head - tail; } void softserial_flush() { rx_buffer_head = rx_buffer_tail; } #ifdef ALTSS_USE_FTM0 void ftm0_isr(void) { uint32_t flags = FTM0_STATUS; FTM0_STATUS = 0; if (flags & (1<<0) && (FTM0_C0SC & 0x40)) altss_compare_b_interrupt(); if (flags & (1<<5)) altss_capture_interrupt(); if (flags & (1<<6) && (FTM0_C6SC & 0x40)) altss_compare_a_interrupt(); } #endif