collar/libraries/AltSoftSerial/AltSoftSerial.cpp

203 lines
5.2 KiB
C++

/* 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