First init.
This commit is contained in:
350
libraries/AltSoftSerial/AltSoftSerial.cpp
Normal file
350
libraries/AltSoftSerial/AltSoftSerial.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
/* 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 **/
|
||||
/****************************************/
|
||||
|
||||
static uint16_t ticks_per_bit=0;
|
||||
bool AltSoftSerial::timing_error=false;
|
||||
|
||||
static uint8_t rx_state;
|
||||
static uint8_t rx_byte;
|
||||
static uint8_t rx_bit = 0;
|
||||
static uint16_t rx_target;
|
||||
static uint16_t rx_stop_ticks=0;
|
||||
static volatile uint8_t rx_buffer_head;
|
||||
static volatile uint8_t rx_buffer_tail;
|
||||
#define RX_BUFFER_SIZE 80
|
||||
static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
|
||||
|
||||
static volatile uint8_t tx_state=0;
|
||||
static uint8_t tx_byte;
|
||||
static uint8_t tx_bit;
|
||||
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];
|
||||
|
||||
|
||||
#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)
|
||||
{
|
||||
//Serial.printf("cycles_per_bit = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_NOPRESCALE();
|
||||
} else {
|
||||
cycles_per_bit /= 8;
|
||||
//Serial.printf("cycles_per_bit/8 = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_PRESCALE_8();
|
||||
} else {
|
||||
#if defined(CONFIG_TIMER_PRESCALE_256)
|
||||
cycles_per_bit /= 32;
|
||||
//Serial.printf("cycles_per_bit/256 = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_PRESCALE_256();
|
||||
} else {
|
||||
return; // baud rate too low for AltSoftSerial
|
||||
}
|
||||
#elif defined(CONFIG_TIMER_PRESCALE_128)
|
||||
cycles_per_bit /= 16;
|
||||
//Serial.printf("cycles_per_bit/128 = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_PRESCALE_128();
|
||||
} else {
|
||||
return; // baud rate too low for AltSoftSerial
|
||||
}
|
||||
#else
|
||||
return; // baud rate too low for AltSoftSerial
|
||||
#endif
|
||||
}
|
||||
}
|
||||
ticks_per_bit = cycles_per_bit;
|
||||
rx_stop_ticks = cycles_per_bit * 37 / 4;
|
||||
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;
|
||||
tx_state = 0;
|
||||
tx_buffer_head = 0;
|
||||
tx_buffer_tail = 0;
|
||||
ENABLE_INT_INPUT_CAPTURE();
|
||||
}
|
||||
|
||||
void AltSoftSerial::end(void)
|
||||
{
|
||||
DISABLE_INT_COMPARE_B();
|
||||
DISABLE_INT_INPUT_CAPTURE();
|
||||
flushInput();
|
||||
flushOutput();
|
||||
DISABLE_INT_COMPARE_A();
|
||||
// TODO: restore timer to original settings?
|
||||
}
|
||||
|
||||
|
||||
/****************************************/
|
||||
/** Transmission **/
|
||||
/****************************************/
|
||||
|
||||
void AltSoftSerial::writeByte(uint8_t b)
|
||||
{
|
||||
uint8_t intr_state, head;
|
||||
|
||||
head = tx_buffer_head + 1;
|
||||
if (head >= TX_BUFFER_SIZE) head = 0;
|
||||
while (tx_buffer_tail == head) ; // wait until space in buffer
|
||||
intr_state = SREG;
|
||||
cli();
|
||||
if (tx_state) {
|
||||
tx_buffer[head] = b;
|
||||
tx_buffer_head = head;
|
||||
} else {
|
||||
tx_state = 1;
|
||||
tx_byte = b;
|
||||
tx_bit = 0;
|
||||
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;
|
||||
uint16_t target;
|
||||
|
||||
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;
|
||||
state++;
|
||||
if (bit != tx_bit) {
|
||||
if (bit) {
|
||||
CONFIG_MATCH_SET();
|
||||
} else {
|
||||
CONFIG_MATCH_CLEAR();
|
||||
}
|
||||
SET_COMPARE_A(target);
|
||||
tx_bit = bit;
|
||||
tx_byte = byte;
|
||||
tx_state = state;
|
||||
// TODO: how to detect timing_error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
head = tx_buffer_head;
|
||||
tail = tx_buffer_tail;
|
||||
if (head == tail) {
|
||||
if (state == 10) {
|
||||
// Wait for final stop bit to finish
|
||||
tx_state = 11;
|
||||
SET_COMPARE_A(target + ticks_per_bit);
|
||||
} else {
|
||||
tx_state = 0;
|
||||
CONFIG_MATCH_NORMAL();
|
||||
DISABLE_INT_COMPARE_A();
|
||||
}
|
||||
} else {
|
||||
if (++tail >= TX_BUFFER_SIZE) tail = 0;
|
||||
tx_buffer_tail = tail;
|
||||
tx_byte = tx_buffer[tail];
|
||||
tx_bit = 0;
|
||||
CONFIG_MATCH_CLEAR();
|
||||
if (state == 10)
|
||||
SET_COMPARE_A(target + ticks_per_bit);
|
||||
else
|
||||
SET_COMPARE_A(GET_TIMER_COUNT() + 16);
|
||||
tx_state = 1;
|
||||
// TODO: how to detect timing_error?
|
||||
}
|
||||
}
|
||||
|
||||
void AltSoftSerial::flushOutput(void)
|
||||
{
|
||||
while (tx_state) /* wait */ ;
|
||||
}
|
||||
|
||||
|
||||
/****************************************/
|
||||
/** Reception **/
|
||||
/****************************************/
|
||||
|
||||
ISR(CAPTURE_INTERRUPT)
|
||||
{
|
||||
uint8_t state, bit, head;
|
||||
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 + ticks_per_bit + ticks_per_bit/2;
|
||||
rx_state = 1;
|
||||
}
|
||||
} else {
|
||||
target = rx_target;
|
||||
offset_overflow = 65535 - ticks_per_bit;
|
||||
while (1) {
|
||||
offset = capture - target;
|
||||
if (offset > offset_overflow) break;
|
||||
rx_byte = (rx_byte >> 1) | rx_bit;
|
||||
target += ticks_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 > ticks_per_bit) AltSoftSerial::timing_error = true;
|
||||
}
|
||||
|
||||
ISR(COMPARE_B_INTERRUPT)
|
||||
{
|
||||
uint8_t head, state, bit;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AltSoftSerial::read(void)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int AltSoftSerial::peek(void)
|
||||
{
|
||||
uint8_t head, tail;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head == tail) return -1;
|
||||
if (++tail >= RX_BUFFER_SIZE) tail = 0;
|
||||
return rx_buffer[tail];
|
||||
}
|
||||
|
||||
int AltSoftSerial::available(void)
|
||||
{
|
||||
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 AltSoftSerial::flushInput(void)
|
||||
{
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user