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