DrZiplok@gmail.com
15 years ago
4 changed files with 448 additions and 0 deletions
@ -0,0 +1,287 @@
@@ -0,0 +1,287 @@
|
||||
// -*- Mode: C++; c-basic-offset: 8; indent-tabs-mode: nil -*-
|
||||
//
|
||||
// Interrupt-driven serial transmit/receive library.
|
||||
//
|
||||
// Copyright (c) 2010 Michael Smith. All rights reserved.
|
||||
//
|
||||
// Receive and baudrate calculations derived from the Arduino
|
||||
// HardwareSerial driver:
|
||||
//
|
||||
// Copyright (c) 2006 Nicholas Zambetti. All right reserved.
|
||||
//
|
||||
// Transmit algorithm inspired by work:
|
||||
//
|
||||
// Code Jose Julio and Jordi Munoz. DIYDrones.com
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
|
||||
#include <wiring.h> |
||||
#include <avr/interrupt.h> |
||||
#include <avr/io.h> |
||||
#include "FastSerial.h" |
||||
|
||||
#if defined(__AVR_ATmega1280__) |
||||
# define FS_MAX_PORTS 4 |
||||
#else |
||||
# define FS_MAX_PORTS 1 |
||||
#endif |
||||
|
||||
static FastSerial *ports[FS_MAX_PORTS]; |
||||
|
||||
#define RX_BUFFER_SIZE sizeof(((FastSerial::RXBuffer *)1)->bytes) |
||||
#define TX_BUFFER_SIZE sizeof(((FastSerial::TXBuffer *)1)->bytes) |
||||
|
||||
// Interrupt handlers //////////////////////////////////////////////////////////
|
||||
|
||||
#define HANDLERS(_PORT, _RXVECTOR, _TXVECTOR, _UDR) \ |
||||
SIGNAL(_RXVECTOR) \
|
||||
{ \
|
||||
unsigned char c = _UDR; \
|
||||
ports[_PORT]->receive(c); \
|
||||
} \
|
||||
\
|
||||
SIGNAL(_TXVECTOR) \
|
||||
{ \
|
||||
ports[_PORT]->transmit(); \
|
||||
} \
|
||||
struct hack |
||||
|
||||
#if defined(__AVR_ATmega8__) |
||||
HANDLERS(0, SIG_UART_RECV, SIG_UART_DATA, UDR); |
||||
#else |
||||
HANDLERS(0, SIG_USART0_RECV, SIG_USART0_DATA, UDR0); |
||||
#if defined(__AVR_ATmega1280__) |
||||
HANDLERS(1, SIG_USART1_RECV, SIG_USART1_DATA, UDR1); |
||||
HANDLERS(2, SIG_USART2_RECV, SIG_USART2_DATA, UDR2); |
||||
HANDLERS(3, SIG_USART3_RECV, SIG_USART3_DATA, UDR3); |
||||
#endif |
||||
#endif |
||||
|
||||
// Constructor /////////////////////////////////////////////////////////////////
|
||||
|
||||
FastSerial::FastSerial(uint8_t portNumber) |
||||
{ |
||||
switch(portNumber) { |
||||
#if defined(__AVR_ATmega8__) |
||||
case 0: |
||||
_ubrrh = &UBRRH; |
||||
_ubrrl = &UBRRL; |
||||
_ucsra = &UCSRA; |
||||
_ucsrb = &UCSRB; |
||||
_udr = &UDR; |
||||
_u2x = U2X; |
||||
_portEnableBits = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE); |
||||
_portTxBits = _BV(UDRIE); |
||||
break; |
||||
#else |
||||
case 0: |
||||
_ubrrh = &UBRR0H; |
||||
_ubrrl = &UBRR0L; |
||||
_ucsra = &UCSR0A; |
||||
_ucsrb = &UCSR0B; |
||||
_udr = &UDR0; |
||||
_u2x = U2X0; |
||||
_portEnableBits = _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0); |
||||
_portTxBits = _BV(UDRIE0); |
||||
break; |
||||
#if defined(__AVR_ATmega1280__) |
||||
case 1: |
||||
_ubrrh = &UBRR1H; |
||||
_ubrrl = &UBRR1L; |
||||
_ucsra = &UCSR1A; |
||||
_ucsrb = &UCSR1B; |
||||
_udr = &UDR1; |
||||
_u2x = U2X1; |
||||
_portEnableBits = _BV(RXEN1) | _BV(TXEN1) | _BV(RXCIE1); |
||||
_portTxBits = _BV(UDRIE1); |
||||
break; |
||||
case 2: |
||||
_ubrrh = &UBRR2H; |
||||
_ubrrl = &UBRR2L; |
||||
_ucsra = &UCSR2A; |
||||
_ucsrb = &UCSR2B; |
||||
_udr = &UDR2; |
||||
_u2x = U2X2; |
||||
_portEnableBits = _BV(RXEN2) | _BV(TXEN2) | _BV(RXCIE2); |
||||
_portTxBits = _BV(UDRIE2); |
||||
break; |
||||
case 3: |
||||
_ubrrh = &UBRR3H; |
||||
_ubrrl = &UBRR3L; |
||||
_ucsra = &UCSR3A; |
||||
_ucsrb = &UCSR3B; |
||||
_udr = &UDR3; |
||||
_u2x = U2X3; |
||||
_portEnableBits = _BV(RXEN3) | _BV(TXEN3) | _BV(RXCIE3); |
||||
_portTxBits = _BV(UDRIE3); |
||||
break; |
||||
#endif |
||||
#endif |
||||
default: |
||||
return; |
||||
}; |
||||
|
||||
_txBuffer.head = _txBuffer.tail = 0; |
||||
_rxBuffer.head = _rxBuffer.tail = 0; |
||||
|
||||
// claim the port
|
||||
ports[portNumber] = this; |
||||
} |
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
void FastSerial::begin(long baud) |
||||
{ |
||||
uint16_t baud_setting; |
||||
bool use_u2x; |
||||
|
||||
// U2X mode is needed for baud rates higher than (CPU Hz / 16)
|
||||
if (baud > F_CPU / 16) { |
||||
use_u2x = true; |
||||
} else { |
||||
// figure out if U2X mode would allow for a better connection
|
||||
|
||||
// calculate the percent difference between the baud-rate specified and
|
||||
// the real baud rate for both U2X and non-U2X mode (0-255 error percent)
|
||||
uint8_t nonu2x_baud_error = abs((int)(255-((F_CPU/(16*(((F_CPU/8/baud-1)/2)+1))*255)/baud))); |
||||
uint8_t u2x_baud_error = abs((int)(255-((F_CPU/(8*(((F_CPU/4/baud-1)/2)+1))*255)/baud))); |
||||
|
||||
// prefer non-U2X mode because it handles clock skew better
|
||||
use_u2x = (nonu2x_baud_error > u2x_baud_error); |
||||
} |
||||
|
||||
if (use_u2x) { |
||||
*_ucsra = _BV(_u2x); |
||||
baud_setting = (F_CPU / 4 / baud - 1) / 2; |
||||
} else { |
||||
*_ucsra = 0; |
||||
baud_setting = (F_CPU / 8 / baud - 1) / 2; |
||||
} |
||||
|
||||
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
|
||||
*_ubrrh = baud_setting >> 8; |
||||
*_ubrrl = baud_setting; |
||||
|
||||
*_ucsrb |= _portEnableBits; |
||||
} |
||||
|
||||
void FastSerial::end() |
||||
{ |
||||
*_ucsrb &= ~(_portEnableBits | _portTxBits); |
||||
} |
||||
|
||||
|
||||
uint8_t |
||||
FastSerial::available(void) |
||||
{ |
||||
return((RX_BUFFER_SIZE + _rxBuffer.head - _rxBuffer.tail) % RX_BUFFER_SIZE); |
||||
} |
||||
|
||||
int |
||||
FastSerial::read(void) |
||||
{ |
||||
uint8_t c; |
||||
|
||||
// if the head and tail are equal, the buffer is empty
|
||||
if (_rxBuffer.head == _rxBuffer.tail) |
||||
return(-1); |
||||
|
||||
// pull character from tail
|
||||
c = _rxBuffer.bytes[_rxBuffer.tail]; |
||||
_rxBuffer.tail = (_rxBuffer.tail + 1) % RX_BUFFER_SIZE; |
||||
|
||||
return(c); |
||||
} |
||||
|
||||
void |
||||
FastSerial::flush(void) |
||||
{ |
||||
// don't reverse this or there may be problems if the RX interrupt
|
||||
// occurs after reading the value of _rxBuffer.head but before writing
|
||||
// the value to _rxBuffer.tail; the previous value of head
|
||||
// may be written to tail, making it appear as if the buffer
|
||||
// don't reverse this or there may be problems if the RX interrupt
|
||||
// occurs after reading the value of head but before writing
|
||||
// the value to tail; the previous value of rx_buffer_head
|
||||
// may be written to tail, making it appear as if the buffer
|
||||
// were full, not empty.
|
||||
_rxBuffer.head = _rxBuffer.tail; |
||||
|
||||
// don't reverse this or there may be problems if the TX interrupt
|
||||
// occurs after reading the value of _txBuffer.tail but before writing
|
||||
// the value to _txBuffer.head.
|
||||
_txBuffer.tail = _rxBuffer.head; |
||||
} |
||||
|
||||
void |
||||
FastSerial::write(uint8_t c) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
// wait for room in the tx buffer
|
||||
do { |
||||
i = (_txBuffer.head + 1) % RX_BUFFER_SIZE; |
||||
} while (i == _txBuffer.tail); |
||||
|
||||
// add byte to the buffer
|
||||
_txBuffer.bytes[_txBuffer.head] = c; |
||||
_txBuffer.head = i; |
||||
|
||||
// enable the data-ready interrupt, as it may be off if the buffer is empty
|
||||
*_ucsrb |= _portTxBits; |
||||
} |
||||
|
||||
void |
||||
FastSerial::write(const uint8_t *buffer, int count) |
||||
{ |
||||
while (count--) |
||||
write(*buffer++); |
||||
} |
||||
|
||||
|
||||
// Interrupt methods ///////////////////////////////////////////////////////////
|
||||
|
||||
void |
||||
FastSerial::receive(uint8_t c) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
// 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.
|
||||
|
||||
i = (_rxBuffer.head + 1) % RX_BUFFER_SIZE; |
||||
if (i != _rxBuffer.tail) { |
||||
_rxBuffer.bytes[_rxBuffer.head] = c; |
||||
_rxBuffer.head = i; |
||||
} |
||||
} |
||||
|
||||
void |
||||
FastSerial::transmit(void) |
||||
{ |
||||
// if the buffer is not empty, send the next byte
|
||||
if (_txBuffer.head != _txBuffer.tail) { |
||||
*_udr = _txBuffer.bytes[_txBuffer.tail]; |
||||
_txBuffer.tail = (_txBuffer.tail + 1) % TX_BUFFER_SIZE; |
||||
} |
||||
|
||||
// if the buffer is (now) empty, disable the interrupt
|
||||
if (_txBuffer.head == _txBuffer.tail) |
||||
*_ucsrb &= ~_portTxBits; |
||||
} |
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
// -*- Mode: C++; c-basic-offset: 8; indent-tabs-mode: nil -*-
|
||||
//
|
||||
// Interrupt-driven serial transmit/receive library.
|
||||
//
|
||||
// Copyright (c) 2010 Michael Smith. All rights reserved.
|
||||
//
|
||||
// Receive and baudrate calculations derived from the Arduino
|
||||
// HardwareSerial driver:
|
||||
//
|
||||
// Copyright (c) 2006 Nicholas Zambetti. All right reserved.
|
||||
//
|
||||
// Transmit algorithm inspired by work:
|
||||
//
|
||||
// Code Jose Julio and Jordi Munoz. DIYDrones.com
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
//
|
||||
// Note that this library does not pre-declare drivers for serial
|
||||
// ports; the user must explicitly create drivers for the ports they
|
||||
// wish to use. This is less friendly than the stock Arduino driver,
|
||||
// but it saves 256 bytes for every unused port.
|
||||
//
|
||||
// To adjust the transmit/receive buffer sizes, change the size of the
|
||||
// 'bytes' member in the RXBuffer and TXBuffer structures.
|
||||
//
|
||||
|
||||
#ifndef FastSerial_h |
||||
#define FastSerial_h |
||||
|
||||
#include <inttypes.h> |
||||
#include <Print.h> |
||||
|
||||
// disable the stock Arduino serial driver
|
||||
#define HardwareSerial_h |
||||
|
||||
class FastSerial : public Print { |
||||
public: |
||||
FastSerial(uint8_t portNumber = 0); |
||||
|
||||
// Serial API
|
||||
void begin(long baud); |
||||
void end(void); |
||||
uint8_t available(void); |
||||
int read(void); |
||||
void flush(void); |
||||
void write(uint8_t c); |
||||
void write(const uint8_t *buffer, int count); |
||||
using Print::write; |
||||
|
||||
// Interrupt methods
|
||||
void receive(uint8_t c); |
||||
void transmit(void); |
||||
|
||||
private: |
||||
// register accessors
|
||||
volatile uint8_t *_ubrrh; |
||||
volatile uint8_t *_ubrrl; |
||||
volatile uint8_t *_ucsra; |
||||
volatile uint8_t *_ucsrb; |
||||
volatile uint8_t *_udr; |
||||
|
||||
// register magic numbers
|
||||
uint8_t _portEnableBits; // rx, tx and rx interrupt enables
|
||||
uint8_t _portTxBits; // tx data and completion interrupt enables
|
||||
uint8_t _u2x; |
||||
|
||||
// ring buffers
|
||||
struct RXBuffer { |
||||
volatile uint8_t head; |
||||
uint8_t tail; |
||||
uint8_t bytes[128]; // size must be power of 2 for best results
|
||||
}; |
||||
struct TXBuffer { |
||||
uint8_t head; |
||||
volatile uint8_t tail; |
||||
uint8_t bytes[64]; // size must be power of 2 for best results
|
||||
}; |
||||
RXBuffer _rxBuffer; |
||||
TXBuffer _txBuffer; |
||||
}; |
||||
|
||||
#endif // FastSerial_h
|
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
// -*- Mode: C++; c-basic-offset: 8; indent-tabs-mode: nil -*- |
||||
|
||||
// |
||||
// Example code for the FastSerial driver. |
||||
// |
||||
// This code is placed into the public domain. |
||||
// |
||||
|
||||
// |
||||
// Include the FastSerial library header. |
||||
// |
||||
// Note that this causes the standard Arduino Serial* driver to be |
||||
// disabled. |
||||
// |
||||
#include <FastSerial.h> |
||||
|
||||
// |
||||
// Create a FastSerial driver that looks just like the stock Arduino |
||||
// driver. |
||||
// |
||||
FastSerial Serial; |
||||
|
||||
// |
||||
// To create a driver for a different serial port, on a board that |
||||
// supports more than one, pass an argument to the constructor: |
||||
// |
||||
//FastSerial Serial2(2); |
||||
|
||||
|
||||
void setup(void) |
||||
{ |
||||
// |
||||
// Set the speed for our replacement serial port. |
||||
// |
||||
Serial.begin(38400); |
||||
|
||||
// |
||||
// And send a message. |
||||
// |
||||
Serial.println("begin"); |
||||
} |
||||
|
||||
void |
||||
loop(void) |
||||
{ |
||||
int c; |
||||
|
||||
// |
||||
// Perform a simple loopback operation. |
||||
// |
||||
c = Serial.read(); |
||||
if (-1 != c) |
||||
Serial.write(c); |
||||
} |
||||
|
Loading…
Reference in new issue