mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-02-05 12:38:14 +00:00
1870 lines
58 KiB
C++
1870 lines
58 KiB
C++
|
/*
|
||
|
HardwareSerial.cpp - Hardware serial library for Wiring
|
||
|
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Lesser General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2.1 of the License, or (at your option) any later version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Lesser General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser General Public
|
||
|
License along with this library; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
|
||
|
Modified 23 November 2006 by David A. Mellis
|
||
|
Modified 28 September 2010 by Mark Sproul
|
||
|
Modified 14 August 2012 by Alarus
|
||
|
|
||
|
Updated for 'xmega' core by bob frazier, S.F.T. Inc. - http://mrp3.com/
|
||
|
|
||
|
In some cases, the xmega updates make assumptions about the pin assignments.
|
||
|
See 'pins_arduino.h' for more detail.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <inttypes.h>
|
||
|
#include "Arduino.h"
|
||
|
#include "pins_arduino.h"
|
||
|
#include "wiring_private.h"
|
||
|
#include "HardwareSerial.h"
|
||
|
|
||
|
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||
|
#define PROGMEM_ORIG PROGMEM
|
||
|
#else // PROGMEM workaround
|
||
|
|
||
|
// to avoid the bogus "initialized variables" warning
|
||
|
#ifdef PROGMEM
|
||
|
#undef PROGMEM
|
||
|
#endif // PROGMEM re-define
|
||
|
|
||
|
#define PROGMEM __attribute__((section(".progmem.hardwareserial")))
|
||
|
#define PROGMEM_ORIG __attribute__((__progmem__))
|
||
|
|
||
|
#endif // check for GNUC >= or < 4.6
|
||
|
|
||
|
|
||
|
#ifndef SERIAL_0_PORT_NAME
|
||
|
#define SERIAL_0_PORT_NAME PORTD
|
||
|
#define SERIAL_0_USART_NAME USARTD0
|
||
|
#define SERIAL_0_USART_DATA USARTD0_DATA
|
||
|
#define SERIAL_0_RXC_ISR ISR(USARTD0_RXC_vect)
|
||
|
#define SERIAL_0_DRE_ISR ISR(USARTD0_DRE_vect)
|
||
|
#define USARTD0_VECTOR_EXISTS
|
||
|
#define SERIAL_0_RX_PIN_INDEX 2
|
||
|
#define SERIAL_0_TX_PIN_INDEX 3
|
||
|
#else // check for new defs
|
||
|
|
||
|
#if !defined(USARTC0_VECTOR_EXISTS) && !defined(USARTD0_VECTOR_EXISTS) && !defined(USARTE0_VECTOR_EXISTS) && !defined(USARTF0_VECTOR_EXISTS) && !defined(USARTC1_VECTOR_EXISTS) && !defined(USARTD1_VECTOR_EXISTS) && !defined(USARTE1_VECTOR_EXISTS) && !defined(USARTF1_VECTOR_EXISTS)
|
||
|
#error you must define the 'USARTxx_VECTOR_EXISTS' macro for each serial port as of 1/14/2015 modifications
|
||
|
#endif // defined 'all that'
|
||
|
|
||
|
#endif // SERIAL_0_PORT_NAME
|
||
|
|
||
|
#ifndef SERIAL_1_PORT_NAME
|
||
|
#define SERIAL_1_PORT_NAME PORTC
|
||
|
#define SERIAL_1_USART_NAME USARTC0
|
||
|
#define SERIAL_1_USART_DATA USARTC0_DATA
|
||
|
#define SERIAL_1_RXC_ISR ISR(USARTC0_RXC_vect)
|
||
|
#define SERIAL_1_DRE_ISR ISR(USARTC0_DRE_vect)
|
||
|
#define USARTC0_VECTOR_EXISTS
|
||
|
#define SERIAL_1_RX_PIN_INDEX 2
|
||
|
#define SERIAL_1_TX_PIN_INDEX 3
|
||
|
#else // check for new defs
|
||
|
|
||
|
#if !defined(USARTC0_VECTOR_EXISTS) && !defined(USARTD0_VECTOR_EXISTS) && !defined(USARTE0_VECTOR_EXISTS) && !defined(USARTF0_VECTOR_EXISTS) && !defined(USARTC1_VECTOR_EXISTS) && !defined(USARTD1_VECTOR_EXISTS) && !defined(USARTE1_VECTOR_EXISTS) && !defined(USARTF1_VECTOR_EXISTS)
|
||
|
#error you must define the 'USARTxx_VECTOR_EXISTS' macro for each serial port as of 1/14/2015 modifications
|
||
|
#endif // defined 'all that'
|
||
|
|
||
|
#endif // SERIAL_1_PORT_NAME
|
||
|
|
||
|
|
||
|
// Define constants and variables for buffering incoming serial data. We're
|
||
|
// using a ring buffer, in which 'head' is the index of the location to
|
||
|
// which to write the next incoming character and 'tail' is the index of the
|
||
|
// location from which to read.
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// ____ _____ ____ ___ _ _ ____ _ _ _____ _____ _____ ____ ____ ___ _____ _____ //
|
||
|
// / ___| | ____|| _ \ |_ _| / \ | | | __ ) | | | || ___|| ___|| ____|| _ \ / ___||_ _||__ /| ____| //
|
||
|
// \___ \ | _| | |_) | | | / _ \ | | | _ \ | | | || |_ | |_ | _| | |_) | \___ \ | | / / | _| //
|
||
|
// ___) || |___ | _ < | | / ___ \ | |___ | |_) || |_| || _| | _| | |___ | _ < ___) || | / /_ | |___ //
|
||
|
// |____/ |_____||_| \_\|___|/_/ \_\|_____|_____|____/ \___/ |_| |_| |_____||_| \_\_____|____/|___|/____||_____| //
|
||
|
// |_____| |_____| //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// NOTE: in some cases I might want to override this. It's now "overrideable" in 'pins_arduino.h'
|
||
|
// TODO: support var length buffers from a pre-allocated linked list? as an option?
|
||
|
#ifndef SERIAL_BUFFER_SIZE
|
||
|
|
||
|
#if !defined(SERIAL_2_PORT_NAME) && !defined(SERIAL_3_PORT_NAME) && !defined(SERIAL_4_PORT_NAME) && !defined(SERIAL_5_PORT_NAME) && !defined(SERIAL_6_PORT_NAME) && !defined(SERIAL_7_PORT_NAME)
|
||
|
|
||
|
// only 2 serial ports, use larger buffer because I can - this can be overridden in 'pins_arduino.h'
|
||
|
#define SERIAL_BUFFER_SIZE 128 /* I like... big... BUFFERS! */
|
||
|
|
||
|
#else // more than 2 serial ports
|
||
|
|
||
|
#define SERIAL_BUFFER_SIZE 64 /* reduce buffer size with *many* serial ports */
|
||
|
|
||
|
#endif // more than 2 serial ports?
|
||
|
|
||
|
#endif // SERIAL_BUFFER_SIZE
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// _ _ __ __ //
|
||
|
// _ __ (_) _ __ __ _ | |__ _ _ / _| / _| ___ _ __ ___ //
|
||
|
// | '__|| || '_ \ / _` | | '_ \ | | | || |_ | |_ / _ \| '__|/ __| //
|
||
|
// | | | || | | || (_| | | |_) || |_| || _|| _|| __/| | \__ \ //
|
||
|
// |_| |_||_| |_| \__, | |_.__/ \__,_||_| |_| \___||_| |___/ //
|
||
|
// |___/ //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
struct ring_buffer
|
||
|
{
|
||
|
unsigned char buffer[SERIAL_BUFFER_SIZE];
|
||
|
#if SERIAL_BUFFER_SIZE < 256
|
||
|
// when buffer size is less than 256 bytes, use an unsigned char (it's faster, smaller)
|
||
|
volatile uint8_t/*unsigned int*/ head;
|
||
|
volatile uint8_t/*unsigned int*/ tail;
|
||
|
#else // SERIAL_BUFFER_SIZE >= 256
|
||
|
volatile unsigned int head;
|
||
|
volatile unsigned int tail;
|
||
|
#endif // SERIAL_BUFFER_SIZE
|
||
|
};
|
||
|
|
||
|
// ring buffers for serial ports 1 and 2 (must zero head/tail before use)
|
||
|
// NOTE: there are ALWAYS at LEAST 2 serial ports:
|
||
|
// these are USARTD0 and USARTC0 (on pins 2,3) by default.
|
||
|
|
||
|
ring_buffer rx_buffer; // = { { 0 }, 0, 0 }; // SERIAL_0
|
||
|
ring_buffer tx_buffer; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer rx_buffer2; // = { { 0 }, 0, 0 }; // SERIAL_1
|
||
|
ring_buffer tx_buffer2; // = { { 0 }, 0, 0 };
|
||
|
|
||
|
#ifdef SERIAL_2_PORT_NAME
|
||
|
ring_buffer rx_buffer3; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer tx_buffer3; // = { { 0 }, 0, 0 };
|
||
|
#endif // SERIAL_2_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_3_PORT_NAME
|
||
|
ring_buffer rx_buffer4; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer tx_buffer4; // = { { 0 }, 0, 0 };
|
||
|
#endif // SERIAL_3_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_4_PORT_NAME
|
||
|
ring_buffer rx_buffer5; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer tx_buffer5; // = { { 0 }, 0, 0 };
|
||
|
#endif // SERIAL_4_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_5_PORT_NAME
|
||
|
ring_buffer rx_buffer6; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer tx_buffer6; // = { { 0 }, 0, 0 };
|
||
|
#endif // SERIAL_5_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_6_PORT_NAME
|
||
|
ring_buffer rx_buffer7; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer tx_buffer7; // = { { 0 }, 0, 0 };
|
||
|
#endif // SERIAL_6_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_7_PORT_NAME
|
||
|
ring_buffer rx_buffer8; // = { { 0 }, 0, 0 };
|
||
|
ring_buffer tx_buffer8; // = { { 0 }, 0, 0 };
|
||
|
#endif // SERIAL_7_PORT_NAME
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// _____ _ ____ _ _ //
|
||
|
// | ___|| | ___ __ __ / ___| ___ _ __ | |_ _ __ ___ | | //
|
||
|
// | |_ | | / _ \\ \ /\ / / | | / _ \ | '_ \ | __|| '__|/ _ \ | | //
|
||
|
// | _| | || (_) |\ V V / | |___| (_) || | | || |_ | | | (_) || | //
|
||
|
// |_| |_| \___/ \_/\_/ \____|\___/ |_| |_| \__||_| \___/ |_| //
|
||
|
// //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
//#ifdef SERIAL_0_CTS_ENABLED
|
||
|
//static char bWasCTS0;
|
||
|
//#endif // SERIAL_0_CTS_ENABLED
|
||
|
//#ifdef SERIAL_1_CTS_ENABLED
|
||
|
//static char bWasCTS1;
|
||
|
//#endif // SERIAL_1_CTS_ENABLED
|
||
|
|
||
|
|
||
|
// TODO: _SOFT_ flow control enable/disable, arbitrary pin assignments for CTS/DTR
|
||
|
|
||
|
#if defined(SERIAL_0_CTS_ENABLED)
|
||
|
void InitSerialFlowControlInterrupt0(void)
|
||
|
{
|
||
|
register8_t *pCTRL;
|
||
|
uint8_t oldSREG;
|
||
|
|
||
|
|
||
|
pCTRL = &(SERIAL_0_CTS_PORT->PIN0CTRL) + SERIAL_0_CTS_PIN_INDEX;
|
||
|
|
||
|
SERIAL_0_CTS_PORT->DIR &= ~SERIAL_0_CTS_PIN; // it's an input
|
||
|
|
||
|
*pCTRL = PORT_OPC_PULLUP_gc | PORT_ISC_BOTHEDGES_gc; //PORT_ISC_FALLING_gc; // interrupt on falling, pulldown resistor
|
||
|
|
||
|
// this next section enables actual interrupts
|
||
|
|
||
|
oldSREG = SREG; // store the interrupt flag basically
|
||
|
|
||
|
cli(); // disable interrupts for a bit
|
||
|
|
||
|
SERIAL_0_CTS_PORT->INT1MASK &= ~SERIAL_0_CTS_PIN;
|
||
|
// TODO: 'E' series doesn't have 'INT1'
|
||
|
// SERIAL_0_CTS_PORT->INTCTRL &= ~PORT_INT1LVL_gm; // interrupt initially off
|
||
|
|
||
|
SREG = oldSREG; // restore
|
||
|
}
|
||
|
#endif // defined(SERIAL_0_CTS_ENABLED)
|
||
|
|
||
|
|
||
|
#if defined(SERIAL_1_CTS_ENABLED)
|
||
|
static void InitSerialFlowControlInterrupt1(void)
|
||
|
{
|
||
|
register8_t *pCTRL;
|
||
|
uint8_t oldSREG;
|
||
|
|
||
|
pCTRL = &(SERIAL_1_CTS_PORT->PIN0CTRL) + SERIAL_1_CTS_PIN_INDEX;
|
||
|
|
||
|
SERIAL_1_CTS_PORT->DIR &= ~SERIAL_1_CTS_PIN; // it's an input
|
||
|
|
||
|
*pCTRL = PORT_OPC_PULLUP_gc | PORT_ISC_BOTHEDGES_gc; //PORT_ISC_FALLING_gc; // interrupt on falling, pulldown resistor
|
||
|
|
||
|
// this next section enables actual interrupts
|
||
|
|
||
|
oldSREG = SREG; // store the interrupt flag basically
|
||
|
|
||
|
cli(); // disable interrupts for a bit
|
||
|
|
||
|
SERIAL_1_CTS_PORT->INT1MASK &= ~SERIAL_1_CTS_PIN; // interrupt off (for now)
|
||
|
// TODO: 'E' series doesn't have 'INT1'
|
||
|
// SERIAL_1_CTS_PORT->INTCTRL |= PORT_INT1LVL_gm; // max priority when I do this
|
||
|
|
||
|
SREG = oldSREG; // restore
|
||
|
}
|
||
|
#endif // defined(SERIAL_1_CTS_ENABLED)
|
||
|
|
||
|
|
||
|
void InitSerialFlowControlInterrupts(void)
|
||
|
{
|
||
|
uint8_t oldSREG=SREG;
|
||
|
|
||
|
cli(); // disable interrupts for a bit
|
||
|
|
||
|
#if defined(SERIAL_0_CTS_ENABLED)
|
||
|
InitSerialFlowControlInterrupt0();
|
||
|
#endif // defined(SERIAL_0_CTS_ENABLED)
|
||
|
|
||
|
#if defined(SERIAL_1_CTS_ENABLED)
|
||
|
InitSerialFlowControlInterrupt1();
|
||
|
#endif // defined(SERIAL_1_CTS_ENABLED)
|
||
|
|
||
|
SREG = oldSREG; // restore
|
||
|
}
|
||
|
|
||
|
|
||
|
// helpers for hardware flow control
|
||
|
// these will send the 'next character' _NOW_ if one is available by
|
||
|
// restoring the 'DRE' interrupt.
|
||
|
|
||
|
void serial_0_cts_callback(void)
|
||
|
{
|
||
|
uint8_t oldSREG = SREG; // get this FIRST
|
||
|
#ifdef SERIAL_0_CTS_ENABLED
|
||
|
char bCTS = SERIAL_0_CTS_PORT->IN & SERIAL_0_CTS_PIN;
|
||
|
#endif // SERIAL_0_CTS_ENABLED
|
||
|
|
||
|
|
||
|
cli(); // in case I'm currently doing somethign ELSE that affects tx_buffer
|
||
|
|
||
|
#ifdef SERIAL_0_CTS_ENABLED
|
||
|
if(!bCTS) // it's cleared - turn off the interrupt
|
||
|
{
|
||
|
SERIAL_0_CTS_PORT->INT1MASK &= ~SERIAL_0_CTS_PIN;
|
||
|
// TODO: 'E' series doesn't have 'INT1'
|
||
|
// SERIAL_0_CTS_PORT->INTCTRL |= PORT_INT1LVL_gm; // max priority when I do this
|
||
|
}
|
||
|
#endif // SERIAL_0_CTS_ENABLED
|
||
|
|
||
|
if(tx_buffer.head != tx_buffer.tail) // only when there's something to send
|
||
|
{
|
||
|
// re-enable the DRE interrupt - this will cause transmission to
|
||
|
// occur again without code duplication. see HardwareSerial::write()
|
||
|
|
||
|
(&(SERIAL_0_USART_NAME))->CTRLA /*USARTD0_CTRLA*/
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp)
|
||
|
| _BV(USART_DREINTLVL1_bp) | _BV(USART_DREINTLVL0_bp); // set int bits for rx and dre (sect 19.14.3)
|
||
|
}
|
||
|
|
||
|
SREG=oldSREG; // interrupts re-enabled
|
||
|
}
|
||
|
|
||
|
void serial_1_cts_callback(void)
|
||
|
{
|
||
|
uint8_t oldSREG = SREG; // get this FIRST
|
||
|
#ifdef SERIAL_1_CTS_ENABLED
|
||
|
char bCTS = SERIAL_1_CTS_PORT->IN & SERIAL_1_CTS_PIN;
|
||
|
#endif // SERIAL_1_CTS_ENABLED
|
||
|
|
||
|
|
||
|
cli(); // in case I'm currently doing somethign ELSE that affects tx_buffer
|
||
|
|
||
|
#ifdef SERIAL_1_CTS_ENABLED
|
||
|
if(!bCTS) // it's cleared - turn off the interrupt
|
||
|
{
|
||
|
SERIAL_1_CTS_PORT->INT1MASK &= ~SERIAL_1_CTS_PIN;
|
||
|
// TODO: 'E' series doesn't have 'INT1'
|
||
|
// SERIAL_1_CTS_PORT->INTCTRL |= PORT_INT1LVL_gm; // max priority when I do this
|
||
|
}
|
||
|
#endif // SERIAL_1_CTS_ENABLED
|
||
|
|
||
|
if (tx_buffer2.head != tx_buffer2.tail) // only when there's something to send
|
||
|
{
|
||
|
// re-enable the DRE interrupt - this will cause transmission to
|
||
|
// occur again without code duplication. see HardwareSerial::write()
|
||
|
|
||
|
USARTC0_CTRLA = _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp)
|
||
|
| _BV(USART_DREINTLVL1_bp) | _BV(USART_DREINTLVL0_bp); // set int bits for rx and dre (sect 19.14.3)
|
||
|
}
|
||
|
|
||
|
SREG=oldSREG; // interrupts re-enabled
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// _ _ _ __ _ _ //
|
||
|
// (_) _ __ | |(_) _ __ ___ / _| _ _ _ __ ___ | |_ (_) ___ _ __ ___ //
|
||
|
// | || '_ \ | || || '_ \ / _ \ | |_ | | | || '_ \ / __|| __|| | / _ \ | '_ \ / __| //
|
||
|
// | || | | || || || | | || __/ | _|| |_| || | | || (__ | |_ | || (_) || | | |\__ \ //
|
||
|
// |_||_| |_||_||_||_| |_| \___| |_| \__,_||_| |_| \___| \__||_| \___/ |_| |_||___/ //
|
||
|
// //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
inline void store_char(unsigned char c, ring_buffer *buffer)
|
||
|
{
|
||
|
unsigned int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
// if we should be storing the received character into the location
|
||
|
// just before the tail (meaning that the head would advance to the
|
||
|
// current location of the tail), we're about to overflow the buffer
|
||
|
// and so we don't write the character or advance the head.
|
||
|
if (i != buffer->tail)
|
||
|
{
|
||
|
buffer->buffer[buffer->head] = c;
|
||
|
buffer->head = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline char set_not_rts(ring_buffer *buffer)
|
||
|
{
|
||
|
unsigned int i1 = (unsigned int)(buffer->head + 3) % SERIAL_BUFFER_SIZE;
|
||
|
unsigned int i2 = (unsigned int)(buffer->head + 2) % SERIAL_BUFFER_SIZE;
|
||
|
unsigned int i3 = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
// if we should be storing the received character into the location
|
||
|
// just before the tail (meaning that the head would advance to the
|
||
|
// current location of the tail), we're about to overflow the buffer
|
||
|
// and so we don't write the character or advance the head.
|
||
|
return i1 == buffer->tail || i2 == buffer->tail || i3 == buffer->tail;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// ___ _ _ _ _ _ _ //
|
||
|
// |_ _| _ __ | |_ ___ _ __ _ __ _ _ _ __ | |_ | | | | __ _ _ __ __| || | ___ _ __ ___ //
|
||
|
// | | | '_ \ | __|/ _ \| '__|| '__|| | | || '_ \ | __| | |_| | / _` || '_ \ / _` || | / _ \| '__|/ __| //
|
||
|
// | | | | | || |_| __/| | | | | |_| || |_) || |_ | _ || (_| || | | || (_| || || __/| | \__ \ //
|
||
|
// |___||_| |_| \__|\___||_| |_| \__,_|| .__/ \__| |_| |_| \__,_||_| |_| \__,_||_| \___||_| |___/ //
|
||
|
// |_| //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// TODO: consider re-doing these to use a single ISR, via an ISR macro similar
|
||
|
// to the following:
|
||
|
//
|
||
|
// ISR(USARTC0_RXC_vect)
|
||
|
// {
|
||
|
// ...
|
||
|
// }
|
||
|
//
|
||
|
// ISR(USARTD0_RXC_vect, ISR_ALIASOF(USARTC0_RXC_vect));
|
||
|
//
|
||
|
// etc.
|
||
|
//
|
||
|
// then the ISR would figure out 'whatever' for registers, etc. by checking flags
|
||
|
// to see who triggered the interrupt. such common code could more easily allow
|
||
|
// for ISR call as currently done via 'call_isr()'. downside, might run a bit
|
||
|
// slower, but probably smaller code. Alternative to single utility function being
|
||
|
// passed the address of the serial port register block in each ISR.
|
||
|
//
|
||
|
// see http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
|
||
|
//
|
||
|
|
||
|
SERIAL_0_RXC_ISR // ISR(USARTD0_RXC_vect)
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
#ifdef SERIAL_0_RTS_ENABLED
|
||
|
if(set_not_rts(&rx_buffer)) // do I need to turn off RTS ?
|
||
|
{
|
||
|
SERIAL_0_RTS_PORT->OUT |= SERIAL_0_RTS_PIN; // set to '1'
|
||
|
SERIAL_0_RTS_PORT->DIR |= SERIAL_0_RTS_PIN; // make sure it's an output
|
||
|
}
|
||
|
#endif // SERIAL_0_RTS_ENABLED
|
||
|
|
||
|
if((&(SERIAL_0_USART_NAME))->STATUS /*USARTD0_STATUS*/ & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_0_USART_DATA; //USARTD0_DATA;
|
||
|
store_char(c, &rx_buffer);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_0_USART_DATA; //USARTD0_DATA;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SERIAL_1_RXC_ISR // ISR(USARTC0_RXC_vect)
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
#ifdef SERIAL_1_RTS_ENABLED
|
||
|
if(set_not_rts(&rx_buffer2)) // do I need to turn off RTS ?
|
||
|
{
|
||
|
SERIAL_1_RTS_PORT->OUT |= SERIAL_1_RTS_PIN; // set to '1'
|
||
|
SERIAL_1_RTS_PORT->DIR |= SERIAL_1_RTS_PIN; // make sure it's an output
|
||
|
}
|
||
|
#endif // SERIAL_0_RTS_ENABLED
|
||
|
|
||
|
if((&(SERIAL_1_USART_NAME))->STATUS /*USARTC0_STATUS*/ & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_1_USART_DATA; //USARTC0_DATA;
|
||
|
store_char(c, &rx_buffer2);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_1_USART_DATA; //USARTC0_DATA;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef SERIAL_2_PORT_NAME
|
||
|
SERIAL_2_RXC_ISR // ISR(USARTE0_RXC_vect)
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
if((&(SERIAL_2_USART_NAME))->STATUS /*USARTE0_STATUS*/ & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_2_USART_DATA; //USARTE0_DATA;
|
||
|
store_char(c, &rx_buffer3);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_2_USART_DATA; //USARTE0_DATA;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_2_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_3_PORT_NAME
|
||
|
SERIAL_3_RXC_ISR // ISR(USARTF0_RXC_vect)
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
if((&(SERIAL_3_USART_NAME))->STATUS /*USARTF0_STATUS*/ & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_3_USART_DATA; //USARTF0_DATA;
|
||
|
store_char(c, &rx_buffer4);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_3_USART_DATA; //USARTF0_DATA;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_3_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_4_PORT_NAME
|
||
|
SERIAL_4_RXC_ISR
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
if((&(SERIAL_4_USART_NAME))->STATUS & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_4_USART_DATA;
|
||
|
store_char(c, &rx_buffer4);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_4_USART_DATA;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_4_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_5_PORT_NAME
|
||
|
SERIAL_5_RXC_ISR
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
if((&(SERIAL_5_USART_NAME))->STATUS & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_5_USART_DATA;
|
||
|
store_char(c, &rx_buffer4);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_5_USART_DATA;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_5_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_6_PORT_NAME
|
||
|
SERIAL_6_RXC_ISR
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
if((&(SERIAL_6_USART_NAME))->STATUS & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_6_USART_DATA;
|
||
|
store_char(c, &rx_buffer4);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_6_USART_DATA;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_6_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_7_PORT_NAME
|
||
|
SERIAL_7_RXC_ISR
|
||
|
{
|
||
|
unsigned char c;
|
||
|
|
||
|
if((&(SERIAL_7_USART_NAME))->STATUS & _BV(USART_RXCIF_bp)) // if there is data available
|
||
|
{
|
||
|
c = SERIAL_7_USART_DATA;
|
||
|
store_char(c, &rx_buffer4);
|
||
|
}
|
||
|
else // I got an interrupt for some reason, just eat data from data reg
|
||
|
{
|
||
|
c = SERIAL_7_USART_DATA;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_7_PORT_NAME
|
||
|
|
||
|
|
||
|
SERIAL_0_DRE_ISR // ISR(USARTD0_DRE_vect)
|
||
|
{
|
||
|
#ifdef SERIAL_0_CTS_ENABLED
|
||
|
uint8_t oldSREG;
|
||
|
char bCTS = SERIAL_0_CTS_PORT->IN & SERIAL_0_CTS_PIN;
|
||
|
#endif // SERIAL_0_CTS_ENABLED
|
||
|
|
||
|
|
||
|
if (
|
||
|
#ifdef SERIAL_0_CTS_ENABLED
|
||
|
bCTS ||
|
||
|
#endif // SERIAL_0_CTS_ENABLED
|
||
|
tx_buffer.head == tx_buffer.tail)
|
||
|
{
|
||
|
#ifdef SERIAL_0_CTS_ENABLED
|
||
|
if(bCTS)
|
||
|
{
|
||
|
oldSREG = SREG; // store the interrupt flag basically
|
||
|
|
||
|
cli(); // disable interrupts for a bit (in case they were enabled)
|
||
|
|
||
|
// bWasCTS0 = 1; // to mark that I set the interrupt
|
||
|
|
||
|
SERIAL_0_CTS_PORT->INT1MASK |= SERIAL_0_CTS_PIN;
|
||
|
// TODO: 'E' series doesn't have 'INT1'
|
||
|
SERIAL_0_CTS_PORT->INTCTRL |= PORT_INT1LVL_gm; // max priority when I do this
|
||
|
|
||
|
SREG = oldSREG; // restore
|
||
|
}
|
||
|
#endif // SERIAL_0_CTS_ENABLED
|
||
|
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_0_USART_NAME))->CTRLA /*USARTD0_CTRLA*/
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer.buffer[tx_buffer.tail];
|
||
|
tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_0_USART_DATA = c; //USARTD0_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SERIAL_1_DRE_ISR // ISR(USARTC0_DRE_vect)
|
||
|
{
|
||
|
#ifdef SERIAL_1_CTS_ENABLED
|
||
|
uint8_t oldSREG;
|
||
|
char bCTS = SERIAL_1_CTS_PORT->IN & SERIAL_1_CTS_PIN;
|
||
|
#endif // SERIAL_1_CTS_ENABLED
|
||
|
|
||
|
|
||
|
if (
|
||
|
#ifdef SERIAL_1_CTS_ENABLED
|
||
|
bCTS ||
|
||
|
#endif // SERIAL_1_CTS_ENABLED
|
||
|
tx_buffer2.head == tx_buffer2.tail)
|
||
|
|
||
|
{
|
||
|
#ifdef SERIAL_1_CTS_ENABLED
|
||
|
if(bCTS)
|
||
|
{
|
||
|
oldSREG = SREG; // store the interrupt flag basically
|
||
|
|
||
|
cli(); // disable interrupts for a bit
|
||
|
|
||
|
// bWasCTS1 = 1; // to mark that I set the interrupt
|
||
|
|
||
|
SERIAL_1_CTS_PORT->INT1MASK |= SERIAL_1_CTS_PIN;
|
||
|
// TODO: 'E' series doesn't have 'INT1'
|
||
|
SERIAL_1_CTS_PORT->INTCTRL |= PORT_INT1LVL_gm; // max priority when I do this
|
||
|
|
||
|
SREG = oldSREG; // restore
|
||
|
}
|
||
|
#endif // SERIAL_1_CTS_ENABLED
|
||
|
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_1_USART_NAME))->CTRLA /*USARTC0_CTRLA*/
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer2.buffer[tx_buffer2.tail];
|
||
|
tx_buffer2.tail = (tx_buffer2.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_1_USART_DATA = c; //USARTC0_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef SERIAL_2_PORT_NAME
|
||
|
SERIAL_2_DRE_ISR // ISR(USARTE0_DRE_vect)
|
||
|
{
|
||
|
if (tx_buffer3.head == tx_buffer3.tail)
|
||
|
{
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_2_USART_NAME))->CTRLA /*USARTE0_CTRLA*/
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer3.buffer[tx_buffer3.tail];
|
||
|
tx_buffer3.tail = (tx_buffer3.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_2_USART_DATA = c; //USARTE0_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_2_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_3_PORT_NAME
|
||
|
SERIAL_3_DRE_ISR // ISR(USARTF0_DRE_vect)
|
||
|
{
|
||
|
if (tx_buffer4.head == tx_buffer4.tail)
|
||
|
{
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_3_USART_NAME))->CTRLA /*USARTF0_CTRLA*/
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer4.buffer[tx_buffer4.tail];
|
||
|
tx_buffer4.tail = (tx_buffer4.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_3_USART_DATA = c; //USARTE0_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_3_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_4_PORT_NAME
|
||
|
SERIAL_4_DRE_ISR
|
||
|
{
|
||
|
if (tx_buffer4.head == tx_buffer4.tail)
|
||
|
{
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_4_USART_NAME))->CTRLA
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer4.buffer[tx_buffer4.tail];
|
||
|
tx_buffer4.tail = (tx_buffer4.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_4_USART_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_4_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_5_PORT_NAME
|
||
|
SERIAL_5_DRE_ISR
|
||
|
{
|
||
|
if (tx_buffer4.head == tx_buffer4.tail)
|
||
|
{
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_5_USART_NAME))->CTRLA
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer4.buffer[tx_buffer4.tail];
|
||
|
tx_buffer4.tail = (tx_buffer4.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_5_USART_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_5_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_6_PORT_NAME
|
||
|
SERIAL_6_DRE_ISR
|
||
|
{
|
||
|
if (tx_buffer4.head == tx_buffer4.tail)
|
||
|
{
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_6_USART_NAME))->CTRLA
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer4.buffer[tx_buffer4.tail];
|
||
|
tx_buffer4.tail = (tx_buffer4.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_6_USART_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_6_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_7_PORT_NAME
|
||
|
SERIAL_7_DRE_ISR
|
||
|
{
|
||
|
if (tx_buffer4.head == tx_buffer4.tail)
|
||
|
{
|
||
|
// Buffer empty, so disable interrupts
|
||
|
// section 19.14.3 - the CTRLA register (interrupt stuff)
|
||
|
(&(SERIAL_7_USART_NAME))->CTRLA
|
||
|
= _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp); // only set these 2 (the DRE int is now OFF)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There is more data in the output buffer. Send the next byte
|
||
|
register unsigned char c = tx_buffer4.buffer[tx_buffer4.tail];
|
||
|
tx_buffer4.tail = (tx_buffer4.tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
|
||
|
SERIAL_7_USART_DATA = c;
|
||
|
}
|
||
|
}
|
||
|
#endif // SERIAL_7_PORT_NAME
|
||
|
|
||
|
|
||
|
// this helper function calls the ISR directly whenever the status reg has the appropriate bit set, then clears the bit
|
||
|
// call this function when you're waiting for I/O, whenever interrupts are disabled.
|
||
|
void call_isr(volatile USART_t *pPort)
|
||
|
{
|
||
|
if(pPort->STATUS & _BV(USART_RXCIF_bp))
|
||
|
{
|
||
|
if(0)
|
||
|
{
|
||
|
// so I can use 'else if' for everything else
|
||
|
}
|
||
|
#ifdef USARTD0_VECTOR_EXISTS
|
||
|
else if(pPort == &(USARTD0))
|
||
|
{
|
||
|
USARTD0_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTD0_VECTOR_EXISTS
|
||
|
#ifdef USARTD1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTD1)
|
||
|
{
|
||
|
USARTD1_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTD1_VECTOR_EXISTS
|
||
|
#ifdef USARTC0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTC0)
|
||
|
{
|
||
|
USARTC0_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTC0_VECTOR_EXISTS
|
||
|
#ifdef USARTC1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTC1)
|
||
|
{
|
||
|
USARTC1_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTC1_VECTOR_EXISTS
|
||
|
#ifdef USARTE0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTE0)
|
||
|
{
|
||
|
USARTE0_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTE0_VECTOR_EXISTS
|
||
|
#ifdef USARTE1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTE1)
|
||
|
{
|
||
|
USARTE1_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTE1_VECTOR_EXISTS
|
||
|
#ifdef USARTF0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTF0)
|
||
|
{
|
||
|
USARTF0_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTF0_VECTOR_EXISTS
|
||
|
#ifdef USARTF1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTF1)
|
||
|
{
|
||
|
USARTF1_RXC_vect();
|
||
|
}
|
||
|
#endif // USARTF1_VECTOR_EXISTS
|
||
|
|
||
|
pPort->STATUS = _BV(USART_RXCIF_bp); // clear THIS one. other bits must be written as zero
|
||
|
}
|
||
|
|
||
|
if(pPort->STATUS & _BV(USART_DREIF_bp))
|
||
|
{
|
||
|
if(0)
|
||
|
{
|
||
|
// so I can use 'else if' below
|
||
|
}
|
||
|
#ifdef USARTD0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTD0)
|
||
|
{
|
||
|
USARTD0_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTD0_VECTOR_EXISTS
|
||
|
#ifdef USARTD1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTD1)
|
||
|
{
|
||
|
USARTD1_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTD1_VECTOR_EXISTS
|
||
|
#ifdef USARTC0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTC0)
|
||
|
{
|
||
|
USARTC0_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTC0_VECTOR_EXISTS
|
||
|
#ifdef USARTC1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTC1)
|
||
|
{
|
||
|
USARTC1_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTC1_VECTOR_EXISTS
|
||
|
#ifdef USARTE0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTE0)
|
||
|
{
|
||
|
USARTE0_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTE0_VECTOR_EXISTS
|
||
|
#ifdef USARTE1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTE1)
|
||
|
{
|
||
|
USARTE1_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTE1_VECTOR_EXISTS
|
||
|
#ifdef USARTF0_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTF0)
|
||
|
{
|
||
|
USARTF0_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTF0_VECTOR_EXISTS
|
||
|
#ifdef USARTF1_VECTOR_EXISTS
|
||
|
else if(pPort == &USARTF1)
|
||
|
{
|
||
|
USARTF1_DRE_vect();
|
||
|
}
|
||
|
#endif // USARTF1_VECTOR_EXISTS
|
||
|
|
||
|
pPort->STATUS = _BV(USART_DREIF_bp); // clear THIS one. other bits must be written as zero
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// ____ _ _ _____ _ //
|
||
|
// / ___| ___ _ __ (_) __ _ | | | ____|__ __ ___ _ __ | |_ ___ //
|
||
|
// \___ \ / _ \| '__|| | / _` || | | _| \ \ / // _ \| '_ \ | __|/ __| //
|
||
|
// ___) || __/| | | || (_| || | | |___ \ V /| __/| | | || |_ \__ \ //
|
||
|
// |____/ \___||_| |_| \__,_||_| |_____| \_/ \___||_| |_| \__||___/ //
|
||
|
// //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void serialEvent() __attribute__((weak));
|
||
|
void serialEvent() {}
|
||
|
#define serialEvent_implemented
|
||
|
|
||
|
void serialEvent2() __attribute__((weak));
|
||
|
void serialEvent2() {}
|
||
|
#define serialEvent2_implemented
|
||
|
|
||
|
#ifdef SERIAL_2_PORT_NAME
|
||
|
void serialEvent3() __attribute__((weak));
|
||
|
void serialEvent3() {}
|
||
|
#define serialEvent3_implemented
|
||
|
#endif // SERIAL_2_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_3_PORT_NAME
|
||
|
void serialEvent4() __attribute__((weak));
|
||
|
void serialEvent4() {}
|
||
|
#define serialEvent4_implemented
|
||
|
#endif // SERIAL_3_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_4_PORT_NAME
|
||
|
void serialEvent5() __attribute__((weak));
|
||
|
void serialEvent5() {}
|
||
|
#define serialEvent5_implemented
|
||
|
#endif // SERIAL_4_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_5_PORT_NAME
|
||
|
void serialEvent6() __attribute__((weak));
|
||
|
void serialEvent6() {}
|
||
|
#define serialEvent6_implemented
|
||
|
#endif // SERIAL_5_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_6_PORT_NAME
|
||
|
void serialEvent7() __attribute__((weak));
|
||
|
void serialEvent7() {}
|
||
|
#define serialEvent7_implemented
|
||
|
#endif // SERIAL_6_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_7_PORT_NAME
|
||
|
void serialEvent8() __attribute__((weak));
|
||
|
void serialEvent8() {}
|
||
|
#define serialEvent8_implemented
|
||
|
#endif // SERIAL_7_PORT_NAME
|
||
|
|
||
|
void serialEventRun(void)
|
||
|
{
|
||
|
// TODO: support this
|
||
|
|
||
|
#ifdef serialEvent_implemented
|
||
|
#ifdef USBCON
|
||
|
if (Serial1.available())
|
||
|
#else // normal
|
||
|
if (Serial.available())
|
||
|
#endif // USBCON, normal
|
||
|
{
|
||
|
serialEvent();
|
||
|
}
|
||
|
#endif // serialEvent_implemented
|
||
|
|
||
|
#ifdef serialEvent2_implemented
|
||
|
if (Serial2.available())
|
||
|
{
|
||
|
serialEvent2();
|
||
|
}
|
||
|
#endif // serialEvent2_implemented
|
||
|
|
||
|
#ifdef serialEvent3_implemented
|
||
|
if (Serial3.available())
|
||
|
{
|
||
|
serialEvent3();
|
||
|
}
|
||
|
#endif // serialEvent3_implemented
|
||
|
|
||
|
#ifdef serialEvent4_implemented
|
||
|
if (Serial4.available())
|
||
|
{
|
||
|
serialEvent4();
|
||
|
}
|
||
|
#endif // serialEvent4_implemented
|
||
|
|
||
|
#ifdef serialEvent5_implemented
|
||
|
if (Serial5.available())
|
||
|
{
|
||
|
serialEvent5();
|
||
|
}
|
||
|
#endif // serialEvent5_implemented
|
||
|
|
||
|
#ifdef serialEvent6_implemented
|
||
|
if (Serial6.available())
|
||
|
{
|
||
|
serialEvent6();
|
||
|
}
|
||
|
#endif // serialEvent6_implemented
|
||
|
|
||
|
#ifdef serialEvent7_implemented
|
||
|
if (Serial7.available())
|
||
|
{
|
||
|
serialEvent7();
|
||
|
}
|
||
|
#endif // serialEvent7_implemented
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// ____ _ ____ _ ____ _ //
|
||
|
// | __ ) __ _ _ _ __| | | _ \ __ _ | |_ ___ / ___| __ _ | | ___ //
|
||
|
// | _ \ / _` || | | | / _` | | |_) |/ _` || __|/ _ \ | | / _` || | / __| //
|
||
|
// | |_) || (_| || |_| || (_| | | _ <| (_| || |_| __/ | |___| (_| || || (__ //
|
||
|
// |____/ \__,_| \__,_| \__,_| |_| \_\\__,_| \__|\___| \____|\__,_||_| \___| //
|
||
|
// //
|
||
|
// //
|
||
|
///////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// this function that uses 'canned' baud rate values is 'temporary'. A proper one that
|
||
|
// calculates the baud rate values will appear at some point in the future, once I have
|
||
|
// a nice bullet-proof algorithm for it.
|
||
|
//
|
||
|
// Note that THESE values assume F_CPU==32000000
|
||
|
|
||
|
// baud <= F_CPU / 16 for 1x, F_CPU / 8 for 2x - above that gives you a value of '1'
|
||
|
//
|
||
|
// X = clk_2x ? 8 : 16 bscale >= 0: bsel = F_CPU / ( (2 ^ bscale) * X * baud) - 1
|
||
|
// baud = F_CPU / ( (2 ^ bscale) * X * (bsel + 1) )
|
||
|
// bscale < 0: bsel = (1 / (2 ^ (bscale))) * (F_CPU / (X * baud) - 1)
|
||
|
// baud = F_CPU / ( X * (((2 ^ bscale) * bsel) + 1) )
|
||
|
//
|
||
|
// NOTE: if bsel is zero for a given bscale, then use bscale=0 and bsel=2^(bscale - 1)
|
||
|
// see section 19.3.1
|
||
|
//
|
||
|
// find 'best fit baud' by calculating the best 'bscale' and 'bsel' for a given baud
|
||
|
// bscale is -7 through +7 so this can be done in a simple loop
|
||
|
//
|
||
|
// Note that I have managed to "nuke out" some accurate integer math to make this work
|
||
|
// although the converging solutions tend to take up some time. It's still fast, though
|
||
|
// and you won't be calling this very often, now will ya?
|
||
|
|
||
|
// calculating BSEL and BAUD correctly - this lets me select _ANY_ baud rate
|
||
|
|
||
|
|
||
|
#if 1 // use NEW get_baud - it's about 810 bytes bigger, though
|
||
|
|
||
|
// GetBSEL returns the BSEL value given the baud and BSCALE
|
||
|
// this is more of an estimate. to get the right answer, this is
|
||
|
// merely a starting point. you have to converge on the solution
|
||
|
// by using a loop and picking the best 'nearby' value, up to '4' away
|
||
|
static int GetBSEL(unsigned long lBaud, int nBSCALE, int b2X)
|
||
|
{
|
||
|
long l1, l3;
|
||
|
unsigned char nFactor;
|
||
|
|
||
|
|
||
|
if(b2X)
|
||
|
{
|
||
|
nFactor = 8;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nFactor = 16;
|
||
|
}
|
||
|
|
||
|
if(nBSCALE >= 0)
|
||
|
{
|
||
|
l1 = nFactor * lBaud;
|
||
|
|
||
|
if(nBSCALE)
|
||
|
{
|
||
|
l1 = l1 << nBSCALE;
|
||
|
}
|
||
|
|
||
|
if(!l1)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if((((long)F_CPU) % l1) < (l1 >> 1))
|
||
|
{
|
||
|
l1 = (((long)F_CPU) / l1) - 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
l1 = (((long)F_CPU) / l1); // rounded off
|
||
|
}
|
||
|
}
|
||
|
else // nBSCALE < 0
|
||
|
{
|
||
|
l1 = nFactor * lBaud;
|
||
|
|
||
|
l3 = F_CPU;
|
||
|
|
||
|
if(nBSCALE > -4) // might overload if I use 32-bit integers and 32Mhz
|
||
|
{
|
||
|
l3 = l3 << (-nBSCALE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
l3 = l3 << 3;
|
||
|
l1 = l1 >> -(3 + nBSCALE);
|
||
|
}
|
||
|
|
||
|
if(l3 % l1 < (l1 >> 1))
|
||
|
{
|
||
|
l1 = l3 / l1 - 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
l1 = l3 / l1; // round up
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (int)l1;
|
||
|
}
|
||
|
|
||
|
// GetBAUD calculates the actual baud rate based on nBSCALE and nBSEL
|
||
|
// it is actually pretty accurate, matching what you seen in the manual
|
||
|
static long GetBAUD(int nBSCALE, int nBSEL, int b2X)
|
||
|
{
|
||
|
long l1, l3;
|
||
|
unsigned char nFactor;
|
||
|
#define GET_BAUD_SCALE_FACTOR 4096 /* scaling the math so I can improve accuracy */
|
||
|
|
||
|
if(b2X)
|
||
|
{
|
||
|
nFactor = 8;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nFactor = 16;
|
||
|
}
|
||
|
|
||
|
if(nBSCALE >= 0)
|
||
|
{
|
||
|
l1 = (long)nFactor * (nBSEL + 1); // nBSEL can be 1-4095; 16 * 4k is ~64k; then it gets shifted.
|
||
|
|
||
|
if(nBSCALE)
|
||
|
{
|
||
|
l1 = l1 << nBSCALE;
|
||
|
}
|
||
|
|
||
|
if(!l1)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return ((long)F_CPU) / l1; // TODO: roundoff correction?
|
||
|
}
|
||
|
|
||
|
// nBSCALE < 0
|
||
|
|
||
|
l3 = (long)nFactor * GET_BAUD_SCALE_FACTOR; // scale factor improves precision
|
||
|
|
||
|
l1 = l3 * nBSEL;
|
||
|
|
||
|
if(nBSCALE)
|
||
|
{
|
||
|
l1 = l1 >> (-nBSCALE);
|
||
|
}
|
||
|
|
||
|
l1 += l3; // the '+ 1' multiplied by nFactor * GET_BAUD_SCALE_SCALE_FACTOR
|
||
|
|
||
|
if(!l1) // unlikely
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
l3 = F_CPU % l1; // the remainder - this gives me better rounding with int math
|
||
|
|
||
|
return GET_BAUD_SCALE_FACTOR * (F_CPU / l1) // integer division, then mult by the scale
|
||
|
+ (GET_BAUD_SCALE_FACTOR * l3) / l1; // the fractional remainder [scaled]
|
||
|
}
|
||
|
|
||
|
// 'get_baud' - the official baud rate number thingy
|
||
|
// this returns (BSCALE << 12) | (BSEL & 0x3fff) for all practical purposes
|
||
|
uint16_t get_baud(unsigned long baud, uint8_t use_u2x)
|
||
|
{
|
||
|
int i1;
|
||
|
char i2;
|
||
|
char iBSCALE, iBSCALERange;
|
||
|
int iBSEL, iTemp;
|
||
|
int iMinErr, iErr;
|
||
|
|
||
|
|
||
|
|
||
|
// NOTE: 2^ABS(BSCALE) must at most be one half of the minimum number
|
||
|
// of clock cycles a frame requires
|
||
|
|
||
|
iBSCALERange = 7; // my initial maximum range
|
||
|
|
||
|
if(baud > (F_CPU / 1310720)) // so that the result fits in an integer
|
||
|
{
|
||
|
iTemp = (int)((F_CPU / 2) * 11L / baud); // half the # of clock cycles needed per 11-bits
|
||
|
|
||
|
while(iBSCALERange && (1 << iBSCALERange) >= iTemp)
|
||
|
{
|
||
|
iBSCALERange --;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iBSEL = 0; // initially zero for 'not found'
|
||
|
iBSCALE = 0;
|
||
|
iMinErr = 0x7fff; // grossly over expected value of error
|
||
|
|
||
|
for(i2=-iBSCALERange; i2 <= iBSCALERange; i2++)
|
||
|
{
|
||
|
iTemp = GetBSEL(baud, i2, use_u2x);
|
||
|
|
||
|
if(!iTemp || iTemp >= 2048) // out of range? - note actual max is 4095
|
||
|
{
|
||
|
continue; // don't even look at an invalid value
|
||
|
}
|
||
|
|
||
|
// derived experimentally, loop on range of iTemp - 4 to iTemp + 1
|
||
|
for(i1=iTemp > 4 ? iTemp - 4 : 0; i1 <= iTemp + 1; i1++)
|
||
|
{
|
||
|
iErr = (int)(GetBAUD(iBSCALE, i1, use_u2x) - baud); // my delta
|
||
|
|
||
|
if(iErr < 0) // smaller than call to 'abs()'
|
||
|
{
|
||
|
iErr = -iErr;
|
||
|
}
|
||
|
|
||
|
if(iErr < iMinErr)
|
||
|
{
|
||
|
// I shall keep the first one I find that is below the current min error
|
||
|
// and the first 'lowest' error is the one I return. This favors lower
|
||
|
// values of BSCALE which I understand helps the baud rate generator
|
||
|
// work better overall.
|
||
|
|
||
|
iBSEL = i1;
|
||
|
iBSCALE = i2;
|
||
|
iMinErr = iErr; // new error to stay below, now
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!iBSEL)
|
||
|
{
|
||
|
return 1; // highest possible baud rate
|
||
|
}
|
||
|
|
||
|
return ((uint16_t)((int)iBSCALE << 12)) | (uint16_t)(iBSEL & 0x3fff);
|
||
|
}
|
||
|
|
||
|
#else // OLD get_baud
|
||
|
|
||
|
// the OLD version used baud rate values from a lookup table
|
||
|
uint16_t get_baud(unsigned long baud, uint8_t use_u2x)
|
||
|
{
|
||
|
uint16_t i1;
|
||
|
static const unsigned long aBaud[] PROGMEM = // standard baud rates
|
||
|
{
|
||
|
2400, 4800, 9600, 14400, 19200, 28800, 31250,
|
||
|
38400, 57600, 76800, 115200, 230400, 460800, 921600
|
||
|
};
|
||
|
|
||
|
static const uint16_t a2x[] PROGMEM = // 2x constants for standard baud rates
|
||
|
{
|
||
|
(7 << 12) | 12, // 2400
|
||
|
(6 << 12) | 12, // 4800
|
||
|
(5 << 12) | 12, // 9600
|
||
|
(1 << 12) | 138, // 14400
|
||
|
(4 << 12) | 12, // 19200
|
||
|
138, // 28800
|
||
|
(2 << 12) | 31, // 31250 - MIDI baud rate
|
||
|
(3 << 12) | 12, // 38400
|
||
|
(uint16_t)(-1 << 12) | 137, // 57600
|
||
|
(2 << 12) | 12, // 76800
|
||
|
(uint16_t)(-2 << 12) | 135, // 115200
|
||
|
(uint16_t)(-3 << 12) | 131, // 230400
|
||
|
(uint16_t)(-4 << 12) | 123, // 460800
|
||
|
(uint16_t)(-5 << 12) | 107 // 921600
|
||
|
};
|
||
|
|
||
|
static const uint16_t a1x[] PROGMEM = // 1x constants for standard baud rates
|
||
|
{
|
||
|
(6 << 12) | 12, // 2400
|
||
|
(5 << 12) | 12, // 4800
|
||
|
(4 << 12) | 12, // 9600
|
||
|
138, // 14400
|
||
|
(3 << 12) | 12, // 19200
|
||
|
(uint16_t)(-1 << 12) | 137, // 28800
|
||
|
(1 << 12) | 31, // 31250 - MIDI baud rate
|
||
|
(2 << 12) | 12, // 38400
|
||
|
(uint16_t)(-2 << 12) | 135, // 57600
|
||
|
(1 << 12) | 12, // 76800
|
||
|
(uint16_t)(-3 << 12) | 131, // 115200
|
||
|
(uint16_t)(-4 << 12) | 123, // 230400
|
||
|
(uint16_t)(-5 << 12) | 107, // 460800
|
||
|
(uint16_t)(-6 << 12) | 75 // 921600
|
||
|
};
|
||
|
|
||
|
// TODO: binary search is faster, but uses more code
|
||
|
|
||
|
for(i1=0; i1 < sizeof(aBaud)/sizeof(aBaud[0]); i1++)
|
||
|
{
|
||
|
unsigned long dw1 = pgm_read_dword(&aBaud[i1]);
|
||
|
if(baud == dw1)
|
||
|
{
|
||
|
if(use_u2x)
|
||
|
{
|
||
|
return pgm_read_word(&a2x[i1]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return pgm_read_word(&a1x[i1]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1; // for now [half the maximum baud rate]
|
||
|
}
|
||
|
|
||
|
#endif // 0,1
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// _ _ _ ____ _ _ //
|
||
|
// | | | | __ _ _ __ __| |__ __ __ _ _ __ ___ / ___| ___ _ __ (_) __ _ | | //
|
||
|
// | |_| | / _` || '__|/ _` |\ \ /\ / // _` || '__|/ _ \\___ \ / _ \| '__|| | / _` || | //
|
||
|
// | _ || (_| || | | (_| | \ V V /| (_| || | | __/ ___) || __/| | | || (_| || | //
|
||
|
// |_| |_| \__,_||_| \__,_| \_/\_/ \__,_||_| \___||____/ \___||_| |_| \__,_||_| //
|
||
|
// //
|
||
|
// //
|
||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Constructors ////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void HardwareSerial::init(ring_buffer *rx_buffer0, ring_buffer *tx_buffer0,
|
||
|
uint16_t usart0)
|
||
|
{
|
||
|
register ring_buffer *pR = rx_buffer0;
|
||
|
register ring_buffer *pT = tx_buffer0;
|
||
|
|
||
|
_rx_buffer = pR;//rx_buffer0;
|
||
|
_tx_buffer = pT;//tx_buffer0;
|
||
|
_usart = (volatile USART_t *)usart0;
|
||
|
|
||
|
pR->head = 0;
|
||
|
pR->tail = 0;
|
||
|
pT->head = 0;
|
||
|
pT->tail = 0;
|
||
|
// memset(rx_buffer0, 0, sizeof(*rx_buffer0));
|
||
|
// memset(tx_buffer0, 0, sizeof(*tx_buffer0));
|
||
|
}
|
||
|
|
||
|
HardwareSerial::HardwareSerial(ring_buffer *rx_buffer0, ring_buffer *tx_buffer0,
|
||
|
uint16_t usart0) /*__attribute__ ((noinline))*/
|
||
|
{
|
||
|
register ring_buffer *pR = rx_buffer0;
|
||
|
register ring_buffer *pT = tx_buffer0;
|
||
|
|
||
|
// init(rx_buffer0, tx_buffer0, usart0); this is larger code, left for reference
|
||
|
|
||
|
_rx_buffer = pR;//rx_buffer0;
|
||
|
_tx_buffer = pT;//tx_buffer0;
|
||
|
_usart = (volatile USART_t *)usart0;
|
||
|
|
||
|
pR->head = 0;
|
||
|
pR->tail = 0;
|
||
|
pT->head = 0;
|
||
|
pT->tail = 0;
|
||
|
// memset(rx_buffer0, 0, sizeof(*rx_buffer0));
|
||
|
// memset(tx_buffer0, 0, sizeof(*tx_buffer0));
|
||
|
}
|
||
|
|
||
|
// Public Methods //////////////////////////////////////////////////////////////
|
||
|
|
||
|
// 'D' manual, section 19.5
|
||
|
// USART Initialization
|
||
|
// USART initialization should use the following sequence:
|
||
|
// 1. Set the TxD pin value high, and optionally set the XCK pin low.
|
||
|
// 2. Set the TxD and optionally the XCK pin as output.
|
||
|
// 3. Set the baud rate and frame format.
|
||
|
// 4. Set the mode of operation (enables XCK pin output in synchronous mode).
|
||
|
// 5. Enable the transmitter or the receiver, depending on the usage.
|
||
|
// For interrupt-driven USART operation, global interrupts should be disabled during the initialization.
|
||
|
// Before doing a re-initialization with a changed baud rate or frame format, be sure that there are no ongoing transmissions
|
||
|
// while the registers are changed.
|
||
|
|
||
|
void HardwareSerial::begin(unsigned long baud)
|
||
|
{
|
||
|
begin(baud, SERIAL_8N1); // eliminated replicated code (12/9/2014)
|
||
|
}
|
||
|
|
||
|
void HardwareSerial::begin(unsigned long baud, byte config)
|
||
|
{
|
||
|
uint16_t baud_setting;
|
||
|
uint8_t use_u2x;
|
||
|
uint8_t bit, bitTX=3, bitRX=2; // defaults
|
||
|
volatile uint8_t *reg;
|
||
|
volatile uint8_t *out;
|
||
|
volatile uint8_t *ctrlT;
|
||
|
volatile uint8_t *ctrlR;
|
||
|
uint8_t oldSREG;
|
||
|
|
||
|
|
||
|
if (baud <= 57600)
|
||
|
{
|
||
|
use_u2x = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
use_u2x = _BV(USART_CLK2X_bp); // enable CLK2X - bit 2 in the CTRLB register (section 19.14.4)
|
||
|
}
|
||
|
|
||
|
transmitting = false; // pre-assign
|
||
|
|
||
|
// baud rate calc - page 220 table 19-5 [for standard values]
|
||
|
// table 19-1 (page 211) for calculation formulae
|
||
|
// (also see theory discussion on page 219)
|
||
|
baud_setting = get_baud(baud, use_u2x);
|
||
|
|
||
|
// NOTE: I had some difficulty getting 300 baud to work. 600 baud worked ok though
|
||
|
// to get 300 baud to work, you might have to change things around a bit
|
||
|
|
||
|
oldSREG = SREG; // save old to restore interrupts as they were
|
||
|
cli(); // clear interrupt flag until I'm done assigning pin stuff
|
||
|
|
||
|
// pin re-mapping register and port/pin assignments
|
||
|
|
||
|
if(_usart == &SERIAL_0_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_0_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_0_RX_PIN_INDEX);
|
||
|
#ifdef SERIAL_0_REMAP
|
||
|
SERIAL_0_REMAP |= SERIAL_0_REMAP_BIT; // enable re-mapping for this port
|
||
|
#endif // SERIAL_0_REMAP
|
||
|
}
|
||
|
else if(_usart == &SERIAL_1_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_1_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_1_RX_PIN_INDEX);
|
||
|
#ifdef SERIAL_1_REMAP
|
||
|
SERIAL_1_REMAP |= SERIAL_1_REMAP_BIT; // enable re-mapping for this port
|
||
|
#endif // SERIAL_0_REMAP
|
||
|
}
|
||
|
#ifdef SERIAL_2_PORT_NAME
|
||
|
else if(_usart == &SERIAL_2_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_2_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_2_RX_PIN_INDEX);
|
||
|
#ifdef SERIAL_2_REMAP
|
||
|
SERIAL_2_REMAP |= SERIAL_2_REMAP_BIT; // enable re-mapping for this port
|
||
|
#endif // SERIAL_2_REMAP
|
||
|
}
|
||
|
#endif // SERIAL_2_PORT_NAME
|
||
|
#ifdef SERIAL_3_PORT_NAME
|
||
|
else if(_usart == &SERIAL_3_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_3_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_3_RX_PIN_INDEX);
|
||
|
#ifdef SERIAL_3_REMAP
|
||
|
SERIAL_3_REMAP |= SERIAL_3_REMAP_BIT; // enable re-mapping for this port
|
||
|
#endif // SERIAL_3_REMAP
|
||
|
}
|
||
|
#endif // SERIAL_3_PORT_NAME
|
||
|
#ifdef SERIAL_4_PORT_NAME
|
||
|
else if(_usart == &SERIAL_4_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_4_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_4_RX_PIN_INDEX);
|
||
|
|
||
|
#ifdef SERIAL_4_REMAP
|
||
|
// NOTE: no remap for serial 4 through Serial 7
|
||
|
#warning pin remap not supported for 'SERIAL_4'
|
||
|
#endif // SERIAL_4_REMAP
|
||
|
}
|
||
|
#endif // SERIAL_4_PORT_NAME
|
||
|
#ifdef SERIAL_5_PORT_NAME
|
||
|
else if(_usart == &SERIAL_5_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_5_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_5_RX_PIN_INDEX);
|
||
|
|
||
|
#ifdef SERIAL_5_REMAP
|
||
|
// NOTE: no remap for serial 4 through Serial 7
|
||
|
#warning pin remap not supported for 'SERIAL_5'
|
||
|
#endif // SERIAL_5_REMAP
|
||
|
}
|
||
|
#endif // SERIAL_5_PORT_NAME
|
||
|
#ifdef SERIAL_6_PORT_NAME
|
||
|
else if(_usart == &SERIAL_6_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_6_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_6_RX_PIN_INDEX);
|
||
|
|
||
|
#ifdef SERIAL_6_REMAP
|
||
|
// NOTE: no remap for serial 4 through Serial 7
|
||
|
#warning pin remap not supported for 'SERIAL_6'
|
||
|
#endif // SERIAL_6_REMAP
|
||
|
}
|
||
|
#endif // SERIAL_6_PORT_NAME
|
||
|
#ifdef SERIAL_7_PORT_NAME
|
||
|
else if(_usart == &SERIAL_7_USART_NAME)
|
||
|
{
|
||
|
bitTX = (SERIAL_7_TX_PIN_INDEX);
|
||
|
bitRX = (SERIAL_7_RX_PIN_INDEX);
|
||
|
|
||
|
#ifdef SERIAL_7_REMAP
|
||
|
// NOTE: no remap for serial 4 through Serial 7
|
||
|
#warning pin remap not supported for 'SERIAL_7'
|
||
|
#endif // SERIAL_7_REMAP
|
||
|
}
|
||
|
#endif // SERIAL_7_PORT_NAME
|
||
|
else
|
||
|
{
|
||
|
goto exit_point; // not valid (bail)
|
||
|
}
|
||
|
|
||
|
// USART setup - for existing ports only (otherwise we just return)
|
||
|
|
||
|
if(_usart == &USARTD0
|
||
|
#ifdef USARTD1
|
||
|
|| _usart == &USARTD1
|
||
|
#endif // USARTD1
|
||
|
)
|
||
|
{
|
||
|
reg = &PORTD_DIR;
|
||
|
out = &PORTD_OUT;
|
||
|
ctrlT = &PORTD_PIN0CTRL + bitTX;
|
||
|
ctrlR = &PORTD_PIN0CTRL + bitRX;
|
||
|
}
|
||
|
else if(_usart == &USARTC0
|
||
|
#ifdef USARTC1
|
||
|
|| _usart == &USARTC1
|
||
|
#endif // USARTC1
|
||
|
)
|
||
|
{
|
||
|
reg = &PORTC_DIR;
|
||
|
out = &PORTC_OUT;
|
||
|
ctrlT = &PORTC_PIN0CTRL + bitTX;
|
||
|
ctrlR = &PORTC_PIN0CTRL + bitRX; // note bitTX and bitRX must be correct
|
||
|
}
|
||
|
#ifdef USARTE0
|
||
|
else if(_usart == &USARTE0
|
||
|
#ifdef USARTE1
|
||
|
|| _usart == &USARTE1
|
||
|
#endif // USARTE1
|
||
|
)
|
||
|
{
|
||
|
reg = &PORTE_DIR;
|
||
|
out = &PORTE_OUT;
|
||
|
ctrlT = &PORTE_PIN0CTRL + bitTX;
|
||
|
ctrlR = &PORTE_PIN0CTRL + bitRX;
|
||
|
}
|
||
|
#endif // USARTE0
|
||
|
#ifdef USARTF0
|
||
|
else if(_usart == &USARTF0
|
||
|
#ifdef USARTE1
|
||
|
|| _usart == &USARTF1
|
||
|
#endif // USARTE1
|
||
|
)
|
||
|
{
|
||
|
reg = &PORTF_DIR;
|
||
|
out = &PORTF_OUT;
|
||
|
ctrlT = &PORTF_PIN0CTRL + bitTX;
|
||
|
ctrlR = &PORTF_PIN0CTRL + bitRX;
|
||
|
}
|
||
|
#endif // USARTF0
|
||
|
else
|
||
|
{
|
||
|
goto exit_point; // not valid (bail)
|
||
|
}
|
||
|
|
||
|
// port config, transmit bit
|
||
|
bit = 1 << bitTX;
|
||
|
*ctrlT = 0; // trigger on BOTH, totem, no pullup
|
||
|
*out |= bit; // set to 'HIGH'
|
||
|
*reg |= bit; // set as output
|
||
|
|
||
|
// port config, receive bit
|
||
|
bit = 1 << bitRX;
|
||
|
*ctrlR = 0; // triger on BOTH, no pullup
|
||
|
*out &= ~bit; // off
|
||
|
*reg &= ~bit; // set as input
|
||
|
|
||
|
|
||
|
// section 19.4.4
|
||
|
_usart->CTRLB = use_u2x; // enable clock 2x when set (everything else disabled)
|
||
|
|
||
|
|
||
|
// section 19.14.5 - USART mode, parity, bits
|
||
|
// CMODE 7:6 00 [async]
|
||
|
// PMODE 5:4 00=none 10=even 11=odd
|
||
|
// SBMODE 3 0=1 stop 1=2 stop
|
||
|
// CHSIZE 2:0 000=5 bit 001=6 bit 010=7 bit 011=8 bit 111=9 bit
|
||
|
_usart->CTRLC = config & ~(_BV(USART_CMODE1_bp)|_BV(USART_CMODE0_bp)); // make sure bits 6 and 7 are cleared
|
||
|
#ifdef USARTD0_CTRLD
|
||
|
_usart->CTRLD = 0; // E5 has this register, must assign to zero
|
||
|
#endif // USARTD0_CTRLD
|
||
|
|
||
|
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
|
||
|
|
||
|
_usart->BAUDCTRLA = (uint8_t)(baud_setting & 0xff);
|
||
|
_usart->BAUDCTRLB = (uint8_t)(baud_setting >> 8);
|
||
|
|
||
|
// section 19.4.4
|
||
|
|
||
|
// enable RX, enable TX. Bit 2 will be 1 or 0 based on clock 2x/1x. multi-processor disabled. bit 9 = 0
|
||
|
_usart->CTRLB = use_u2x | _BV(USART_RXEN_bp) | _BV(USART_TXEN_bp);
|
||
|
|
||
|
// priority 3 for RX interrupts. DRE and TX interrupts OFF (for now).
|
||
|
_usart->CTRLA = _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp);
|
||
|
|
||
|
exit_point:
|
||
|
SREG = oldSREG; // restore interrupt flag (now that I'm done assigning things)
|
||
|
}
|
||
|
|
||
|
void HardwareSerial::end()
|
||
|
{
|
||
|
// wait for transmission of outgoing data
|
||
|
// TODO: check for 'in an ISR' or stalled?
|
||
|
|
||
|
if(SREG & CPU_I_bm) // interrupts are enabled
|
||
|
{
|
||
|
// if ints not disabled it's safe to do this - wait for tx_buffer to empty
|
||
|
|
||
|
while (_tx_buffer->head != _tx_buffer->tail) { }
|
||
|
}
|
||
|
|
||
|
_usart->CTRLB = 0; // disable RX, TX
|
||
|
_usart->CTRLA = 0; // disable interrupts
|
||
|
|
||
|
// clear any received data
|
||
|
_rx_buffer->head = _rx_buffer->tail;
|
||
|
}
|
||
|
|
||
|
int HardwareSerial::available(void)
|
||
|
{
|
||
|
register int iRval;
|
||
|
register uint8_t oldSREG = SREG; // save old to restore interrupts as they were
|
||
|
|
||
|
cli(); // clear interrupt flag to prevent inconsistency
|
||
|
|
||
|
iRval = (int)((unsigned int)(SERIAL_BUFFER_SIZE + (unsigned int)_rx_buffer->head - (unsigned int)_rx_buffer->tail)
|
||
|
% SERIAL_BUFFER_SIZE);
|
||
|
|
||
|
SREG = oldSREG; // restore interrupt flag
|
||
|
|
||
|
return iRval;
|
||
|
}
|
||
|
|
||
|
int HardwareSerial::peek(void)
|
||
|
{
|
||
|
register int iRval;
|
||
|
register uint8_t oldSREG = SREG; // save old to restore interrupts as they were
|
||
|
|
||
|
cli(); // clear interrupt flag to prevent inconsistency
|
||
|
|
||
|
if (_rx_buffer->head == _rx_buffer->tail)
|
||
|
{
|
||
|
iRval = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iRval = _rx_buffer->buffer[_rx_buffer->tail];
|
||
|
}
|
||
|
|
||
|
SREG = oldSREG; // restore interrupt flag
|
||
|
|
||
|
return iRval;
|
||
|
}
|
||
|
|
||
|
int HardwareSerial::read(void)
|
||
|
{
|
||
|
register int iRval;
|
||
|
uint8_t oldSREG = SREG;
|
||
|
|
||
|
cli(); // clear interrupt flag for consistency
|
||
|
|
||
|
// each time I'm ready to read a byte, double-check that the RTS (when enabled)
|
||
|
// needs to be set to a 0 value [which enables things to be sent to me]. As
|
||
|
// I deplete the buffer, RTS will enable, and as I fill it, RTS will disable.
|
||
|
|
||
|
// This section is the 'deplete' part. So I'll set RTS to 'LOW' which is 'ok to send'
|
||
|
// if the buffer is _NOT_ too full (the set_not_rts() function determines that)
|
||
|
|
||
|
#ifdef SERIAL_0_RTS_ENABLED
|
||
|
if(_rx_buffer == &rx_buffer && // it's serial #0
|
||
|
!set_not_rts(&rx_buffer)) // do I need to turn off RTS ?
|
||
|
{
|
||
|
SERIAL_0_RTS_PORT->OUT &= ~SERIAL_0_RTS_PIN; // set to '0'
|
||
|
SERIAL_0_RTS_PORT->DIR |= SERIAL_0_RTS_PIN; // make sure it's an output
|
||
|
}
|
||
|
#endif // SERIAL_0_RTS_ENABLED
|
||
|
|
||
|
#ifdef SERIAL_1_RTS_ENABLED
|
||
|
if(_rx_buffer == &rx_buffer2 && // it's serial #1
|
||
|
!set_not_rts(&rx_buffer2)) // do I need to turn off RTS ?
|
||
|
{
|
||
|
SERIAL_1_RTS_PORT->OUT &= ~SERIAL_1_RTS_PIN; // set to '0'
|
||
|
SERIAL_1_RTS_PORT->DIR |= SERIAL_1_RTS_PIN; // make sure it's an output
|
||
|
}
|
||
|
#endif // SERIAL_1_RTS_ENABLED
|
||
|
|
||
|
// back to regular serial I/O handling
|
||
|
|
||
|
// if the head isn't ahead of the tail, we don't have any characters
|
||
|
if (_rx_buffer->head == _rx_buffer->tail)
|
||
|
{
|
||
|
iRval = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iRval = (int)(_rx_buffer->buffer[_rx_buffer->tail]);
|
||
|
|
||
|
_rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE;
|
||
|
}
|
||
|
|
||
|
SREG = oldSREG; // restore interrupt flag
|
||
|
|
||
|
return iRval;
|
||
|
}
|
||
|
|
||
|
void HardwareSerial::flush()
|
||
|
{
|
||
|
// TODO: force an 'sei' here?
|
||
|
|
||
|
// DATA is kept full while the buffer is not empty, so TXCIF triggers when EMPTY && SENT
|
||
|
while (transmitting && !(_usart->STATUS & _BV(USART_TXCIF_bp))) // TXCIF bit 6 indicates transmit complete
|
||
|
;
|
||
|
|
||
|
transmitting = false;
|
||
|
}
|
||
|
|
||
|
size_t HardwareSerial::write(uint8_t c)
|
||
|
{
|
||
|
register unsigned int i1;
|
||
|
uint8_t oldSREG;
|
||
|
|
||
|
|
||
|
oldSREG = SREG; // get this FIRST
|
||
|
cli(); // in case I'm currently doing somethign ELSE that affects the _tx_buffer
|
||
|
|
||
|
i1 = (unsigned int)((_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE); // next head after this char
|
||
|
|
||
|
// If the output buffer is full, there's nothing for it other than to
|
||
|
// wait for the interrupt handler to empty it a bit
|
||
|
|
||
|
if (i1 == _tx_buffer->tail) // the buffer is still 'full'?
|
||
|
{
|
||
|
// if the interrupt flag is cleared in 'oldSREG' we must call the ISR directly
|
||
|
// otherwise we can set the int flag and wait for it
|
||
|
|
||
|
if(oldSREG & CPU_I_bm) // interrupts were enabled
|
||
|
{
|
||
|
// make sure that the USART's RXC and DRE interupts are enabled
|
||
|
_usart->CTRLA = _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp)
|
||
|
| _BV(USART_DREINTLVL1_bp) | _BV(USART_DREINTLVL0_bp); // set int bits for rx and dre (sect 19.14.3)
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if(oldSREG & CPU_I_bm) // interrupts were enabled
|
||
|
{
|
||
|
sei(); // re-enable interrupts
|
||
|
__builtin_avr_delay_cycles((F_CPU / 2000000) + 1); // delay ~17 cycles, enough time to allow for an interrupt to happen
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
call_isr(_usart); // this will block until there is a serial interrupt condition, but in an ISR-safe manner
|
||
|
}
|
||
|
|
||
|
i1 = (unsigned int)((_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE); // next head after this char
|
||
|
|
||
|
cli(); // do this regardless (smaller than another 'if' block, no harm if already clear)
|
||
|
|
||
|
} while (i1 == _tx_buffer->tail); // while the buffer is still 'full'
|
||
|
}
|
||
|
|
||
|
// if I didn't have to wait for buffer space, I'm already covered
|
||
|
|
||
|
_tx_buffer->buffer[_tx_buffer->head] = c;
|
||
|
_tx_buffer->head = i1; // I already incremented it earlier, assume nobody ELSE modifies this
|
||
|
|
||
|
// NOTE: this messes with flow control. it will still work, however
|
||
|
// _usart->CTRLA |= _BV(1) | _BV(0); // make sure I (re)enable the DRE interrupt (sect 19.14.3)
|
||
|
_usart->CTRLA = _BV(USART_RXCINTLVL1_bp) | _BV(USART_RXCINTLVL0_bp)
|
||
|
| _BV(USART_DREINTLVL1_bp) | _BV(USART_DREINTLVL0_bp); // set int bits for rx and dre (sect 19.14.3)
|
||
|
|
||
|
transmitting = true;
|
||
|
// sbi(_usart->STATUS,6); // clear the TXCIF bit by writing a 1 to its location (sect 19.14.2)
|
||
|
_usart->STATUS = _BV(USART_TXCIF_bp); // other bits must be written as zero
|
||
|
|
||
|
SREG=oldSREG; // interrupts re-enabled
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
HardwareSerial::operator bool()
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// ___ _ _ _ ___ _ //
|
||
|
// / _ \ | |__ (_) ___ ___ | |_ |_ _| _ __ ___ | |_ __ _ _ __ ___ ___ ___ //
|
||
|
// | | | || '_ \ | | / _ \ / __|| __| | | | '_ \ / __|| __|/ _` || '_ \ / __|/ _ \/ __| //
|
||
|
// | |_| || |_) || || __/| (__ | |_ | | | | | |\__ \| |_| (_| || | | || (__| __/\__ \ //
|
||
|
// \___/ |_.__/_/ | \___| \___| \__| |___||_| |_||___/ \__|\__,_||_| |_| \___|\___||___/ //
|
||
|
// |__/ //
|
||
|
// //
|
||
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Preinstantiate Objects //////////////////////////////////////////////////////
|
||
|
|
||
|
// NOTE: object naming in 'approximate' accordance with the way it's being done with Arduino
|
||
|
// except that 'Serial1' is *special*, and only defined when USB becomes 'Serial'
|
||
|
// and 'Serial2' will always be the '2nd' serial port ('Serial1' *could* become an alias)
|
||
|
|
||
|
#ifdef USBCON
|
||
|
HardwareSerial Serial1(&rx_buffer, &tx_buffer, (uint16_t)&(SERIAL_0_USART_NAME)); // name changes to 'Serial1' when USB present
|
||
|
#else // normal
|
||
|
HardwareSerial Serial(&rx_buffer, &tx_buffer, (uint16_t)&(SERIAL_0_USART_NAME));
|
||
|
#endif // USBCON or normal
|
||
|
|
||
|
HardwareSerial Serial2(&rx_buffer2, &tx_buffer2, (uint16_t)&(SERIAL_1_USART_NAME));
|
||
|
|
||
|
#ifdef SERIAL_2_PORT_NAME /* note these names are off by 1 with the 'Serial_N_' objects */
|
||
|
HardwareSerial Serial3(&rx_buffer3, &tx_buffer3, (uint16_t)&(SERIAL_2_USART_NAME));
|
||
|
#endif // SERIAL_2_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_3_PORT_NAME
|
||
|
HardwareSerial Serial4(&rx_buffer4, &tx_buffer4, (uint16_t)&(SERIAL_3_USART_NAME));
|
||
|
#endif // SERIAL_3_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_4_PORT_NAME
|
||
|
HardwareSerial Serial5(&rx_buffer5, &tx_buffer5, (uint16_t)&(SERIAL_3_USART_NAME));
|
||
|
#endif // SERIAL_4_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_5_PORT_NAME
|
||
|
HardwareSerial Serial6(&rx_buffer6, &tx_buffer6, (uint16_t)&(SERIAL_3_USART_NAME));
|
||
|
#endif // SERIAL_5_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_6_PORT_NAME
|
||
|
HardwareSerial Serial7(&rx_buffer7, &tx_buffer7, (uint16_t)&(SERIAL_3_USART_NAME));
|
||
|
#endif // SERIAL_6_PORT_NAME
|
||
|
|
||
|
#ifdef SERIAL_7_PORT_NAME
|
||
|
HardwareSerial Serial8(&rx_buffer8, &tx_buffer8, (uint16_t)&(SERIAL_3_USART_NAME));
|
||
|
#endif // SERIAL_7_PORT_NAME
|
||
|
|
||
|
|