From eefdd34af4f422b02ce8a64830655281d278f6f6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 15 Mar 2011 21:46:22 +0100 Subject: [PATCH] small fixes, add NewSoftSerial lib --- brmdoor/.gitignore | 1 + brmdoor/NewSoftSerial.cpp | 539 ++++++++++++++++++++++++++++++++ brmdoor/NewSoftSerial.h | 107 +++++++ {sketch => brmdoor}/brmdoor.pde | 2 +- {sketch => brmdoor}/pitches.h | 0 5 files changed, 648 insertions(+), 1 deletion(-) create mode 100644 brmdoor/.gitignore create mode 100644 brmdoor/NewSoftSerial.cpp create mode 100644 brmdoor/NewSoftSerial.h rename {sketch => brmdoor}/brmdoor.pde (99%) rename {sketch => brmdoor}/pitches.h (100%) diff --git a/brmdoor/.gitignore b/brmdoor/.gitignore new file mode 100644 index 0000000..e430f5a --- /dev/null +++ b/brmdoor/.gitignore @@ -0,0 +1 @@ +cardids.h diff --git a/brmdoor/NewSoftSerial.cpp b/brmdoor/NewSoftSerial.cpp new file mode 100644 index 0000000..3616597 --- /dev/null +++ b/brmdoor/NewSoftSerial.cpp @@ -0,0 +1,539 @@ +/* +NewSoftSerial.cpp - Multi-instance software serial library +Copyright (c) 2006 David A. Mellis. All rights reserved. +-- Interrupt-driven receive and other improvements by ladyada +-- Tuning, circular buffer, derivation from class Print, + multi-instance support, porting to 8MHz processors, + various optimizations, PROGMEM delay tables, inverse logic and + direct port writing by Mikal Hart + +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 + +The latest version of this library can always be found at +http://arduiniana.org. +*/ + +// When set, _DEBUG co-opts pins 11 and 13 for debugging with an +// oscilloscope or logic analyzer. Beware: it also slightly modifies +// the bit times, so don't rely on it too much at high baud rates +#define _DEBUG 0 +#define _DEBUG_PIN1 11 +#define _DEBUG_PIN2 13 +// +// Includes +// +#include +#include +#include "WConstants.h" +#include "pins_arduino.h" +#include "NewSoftSerial.h" + +// Abstractions for maximum portability between processors +// These are macros to associate pins to pin change interrupts +#if !defined(digitalPinToPCICR) // Courtesy Paul Stoffregen +#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) +#define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)NULL)) +#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1)) +#define digitalPinToPCMSK(p) (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)NULL)))) +#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14))) +#else +#define digitalPinToPCICR(p) ((uint8_t *)NULL) +#define digitalPinToPCICRbit(p) 0 +#define digitalPinToPCMSK(p) ((uint8_t *)NULL) +#define digitalPinToPCMSKbit(p) 0 +#endif +#endif + +// +// Lookup table +// +typedef struct _DELAY_TABLE +{ + long baud; + unsigned short rx_delay_centering; + unsigned short rx_delay_intrabit; + unsigned short rx_delay_stopbit; + unsigned short tx_delay; +} DELAY_TABLE; + +#if F_CPU == 16000000 + +static const DELAY_TABLE PROGMEM table[] = +{ + // baud rxcenter rxintra rxstop tx + { 115200, 1, 17, 17, 12, }, + { 57600, 10, 37, 37, 33, }, + { 38400, 25, 57, 57, 54, }, + { 31250, 31, 70, 70, 68, }, + { 28800, 34, 77, 77, 74, }, + { 19200, 54, 117, 117, 114, }, + { 14400, 74, 156, 156, 153, }, + { 9600, 114, 236, 236, 233, }, + { 4800, 233, 474, 474, 471, }, + { 2400, 471, 950, 950, 947, }, + { 1200, 947, 1902, 1902, 1899, }, + { 300, 3804, 7617, 7617, 7614, }, +}; + +const int XMIT_START_ADJUSTMENT = 5; + +#elif F_CPU == 8000000 + +static const DELAY_TABLE table[] PROGMEM = +{ + // baud rxcenter rxintra rxstop tx + { 115200, 1, 5, 5, 3, }, + { 57600, 1, 15, 15, 13, }, + { 38400, 2, 25, 26, 23, }, + { 31250, 7, 32, 33, 29, }, + { 28800, 11, 35, 35, 32, }, + { 19200, 20, 55, 55, 52, }, + { 14400, 30, 75, 75, 72, }, + { 9600, 50, 114, 114, 112, }, + { 4800, 110, 233, 233, 230, }, + { 2400, 229, 472, 472, 469, }, + { 1200, 467, 948, 948, 945, }, + { 300, 1895, 3805, 3805, 3802, }, +}; + +const int XMIT_START_ADJUSTMENT = 4; + +#elif F_CPU == 20000000 + +// 20MHz support courtesy of the good people at macegr.com. +// Thanks, Garrett! + +static const DELAY_TABLE PROGMEM table[] = +{ + // baud rxcenter rxintra rxstop tx + { 115200, 3, 21, 21, 18, }, + { 57600, 20, 43, 43, 41, }, + { 38400, 37, 73, 73, 70, }, + { 31250, 45, 89, 89, 88, }, + { 28800, 46, 98, 98, 95, }, + { 19200, 71, 148, 148, 145, }, + { 14400, 96, 197, 197, 194, }, + { 9600, 146, 297, 297, 294, }, + { 4800, 296, 595, 595, 592, }, + { 2400, 592, 1189, 1189, 1186, }, + { 1200, 1187, 2379, 2379, 2376, }, + { 300, 4759, 9523, 9523, 9520, }, +}; + +const int XMIT_START_ADJUSTMENT = 6; + +#else + +#error This version of NewSoftSerial supports only 20, 16 and 8MHz processors + +#endif + +// +// Statics +// +NewSoftSerial *NewSoftSerial::active_object = 0; +char NewSoftSerial::_receive_buffer[_NewSS_MAX_RX_BUFF]; +volatile uint8_t NewSoftSerial::_receive_buffer_tail = 0; +volatile uint8_t NewSoftSerial::_receive_buffer_head = 0; + +// +// Debugging +// +// This function generates a brief pulse +// for debugging or measuring on an oscilloscope. +inline void DebugPulse(uint8_t pin, uint8_t count) +{ +#if _DEBUG + volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); + + uint8_t val = *pport; + while (count--) + { + *pport = val | digitalPinToBitMask(pin); + *pport = val; + } +#endif +} + +// +// Private methods +// + +/* static */ +inline void NewSoftSerial::tunedDelay(uint16_t delay) { + uint8_t tmp=0; + + asm volatile("sbiw %0, 0x01 \n\t" + "ldi %1, 0xFF \n\t" + "cpi %A0, 0xFF \n\t" + "cpc %B0, %1 \n\t" + "brne .-10 \n\t" + : "+r" (delay), "+a" (tmp) + : "0" (delay) + ); +} + +// This function sets the current object as the "active" +// one and returns true if it replaces another +bool NewSoftSerial::activate(void) +{ + if (active_object != this) + { + _buffer_overflow = false; + uint8_t oldSREG = SREG; + cli(); + _receive_buffer_head = _receive_buffer_tail = 0; + active_object = this; + SREG = oldSREG; + return true; + } + + return false; +} + +// +// The receive routine called by the interrupt handler +// +void NewSoftSerial::recv() +{ + +#if GCC_VERSION < 40302 +// Work-around for avr-gcc 4.3.0 OSX version bug +// Preserve the registers that the compiler misses +// (courtesy of Arduino forum user *etracer*) + asm volatile( + "push r18 \n\t" + "push r19 \n\t" + "push r20 \n\t" + "push r21 \n\t" + "push r22 \n\t" + "push r23 \n\t" + "push r26 \n\t" + "push r27 \n\t" + ::); +#endif + + uint8_t d = 0; + + // If RX line is high, then we don't see any start bit + // so interrupt is probably not for us + if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) + { + // Wait approximately 1/2 of a bit width to "center" the sample + tunedDelay(_rx_delay_centering); + DebugPulse(_DEBUG_PIN2, 1); + + // Read each of the 8 bits + for (uint8_t i=0x1; i; i <<= 1) + { + tunedDelay(_rx_delay_intrabit); + DebugPulse(_DEBUG_PIN2, 1); + uint8_t noti = ~i; + if (rx_pin_read()) + d |= i; + else // else clause added to ensure function timing is ~balanced + d &= noti; + } + + // skip the stop bit + tunedDelay(_rx_delay_stopbit); + DebugPulse(_DEBUG_PIN2, 1); + + if (_inverse_logic) + d = ~d; + + // if buffer full, set the overflow flag and return + if ((_receive_buffer_tail + 1) % _NewSS_MAX_RX_BUFF != _receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + _receive_buffer[_receive_buffer_tail] = d; // save new byte + _receive_buffer_tail = (_receive_buffer_tail + 1) % _NewSS_MAX_RX_BUFF; + } + else + { +#if _DEBUG // for scope: pulse pin as overflow indictator + DebugPulse(_DEBUG_PIN1, 1); +#endif + _buffer_overflow = true; + } + } + +#if GCC_VERSION < 40302 +// Work-around for avr-gcc 4.3.0 OSX version bug +// Restore the registers that the compiler misses + asm volatile( + "pop r27 \n\t" + "pop r26 \n\t" + "pop r23 \n\t" + "pop r22 \n\t" + "pop r21 \n\t" + "pop r20 \n\t" + "pop r19 \n\t" + "pop r18 \n\t" + ::); +#endif +} + +void NewSoftSerial::tx_pin_write(uint8_t pin_state) +{ + if (pin_state == LOW) + *_transmitPortRegister &= ~_transmitBitMask; + else + *_transmitPortRegister |= _transmitBitMask; +} + +uint8_t NewSoftSerial::rx_pin_read() +{ + return *_receivePortRegister & _receiveBitMask; +} + +// +// Interrupt handling +// + +/* static */ +inline void NewSoftSerial::handle_interrupt() +{ + if (active_object) + { + active_object->recv(); + } +} + +#if defined(PCINT0_vect) +ISR(PCINT0_vect) +{ + NewSoftSerial::handle_interrupt(); +} +#endif + +#if defined(PCINT1_vect) +ISR(PCINT1_vect) +{ + NewSoftSerial::handle_interrupt(); +} +#endif + +#if defined(PCINT2_vect) +ISR(PCINT2_vect) +{ + NewSoftSerial::handle_interrupt(); +} +#endif + +#if defined(PCINT3_vect) +ISR(PCINT3_vect) +{ + NewSoftSerial::handle_interrupt(); +} +#endif + +// +// Constructor +// +NewSoftSerial::NewSoftSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : + _rx_delay_centering(0), + _rx_delay_intrabit(0), + _rx_delay_stopbit(0), + _tx_delay(0), + _buffer_overflow(false), + _inverse_logic(inverse_logic) +{ + setTX(transmitPin); + setRX(receivePin); +} + +// +// Destructor +// +NewSoftSerial::~NewSoftSerial() +{ + end(); +} + +void NewSoftSerial::setTX(uint8_t tx) +{ + pinMode(tx, OUTPUT); + digitalWrite(tx, HIGH); + _transmitBitMask = digitalPinToBitMask(tx); + uint8_t port = digitalPinToPort(tx); + _transmitPortRegister = portOutputRegister(port); +} + +void NewSoftSerial::setRX(uint8_t rx) +{ + pinMode(rx, INPUT); + if (!_inverse_logic) + digitalWrite(rx, HIGH); // pullup for normal logic! + _receivePin = rx; + _receiveBitMask = digitalPinToBitMask(rx); + uint8_t port = digitalPinToPort(rx); + _receivePortRegister = portInputRegister(port); +} + +// +// Public methods +// + +void NewSoftSerial::begin(long speed) +{ + _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; + + for (unsigned i=0; i +#include "Print.h" + +/****************************************************************************** +* Definitions +******************************************************************************/ + +#define _NewSS_MAX_RX_BUFF 64 // RX buffer size +#define _NewSS_VERSION 10 // software version of this library +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +class NewSoftSerial : public Print +{ +private: + // per object data + uint8_t _receivePin; + uint8_t _receiveBitMask; + volatile uint8_t *_receivePortRegister; + uint8_t _transmitBitMask; + volatile uint8_t *_transmitPortRegister; + + uint16_t _rx_delay_centering; + uint16_t _rx_delay_intrabit; + uint16_t _rx_delay_stopbit; + uint16_t _tx_delay; + + uint16_t _buffer_overflow:1; + uint16_t _inverse_logic:1; + + // static data + static char _receive_buffer[_NewSS_MAX_RX_BUFF]; + static volatile uint8_t _receive_buffer_tail; + static volatile uint8_t _receive_buffer_head; + static NewSoftSerial *active_object; + + // private methods + void recv(); + bool activate(); + virtual void write(uint8_t byte); + uint8_t rx_pin_read(); + void tx_pin_write(uint8_t pin_state); + void setTX(uint8_t transmitPin); + void setRX(uint8_t receivePin); + + // private static method for timing + static inline void tunedDelay(uint16_t delay); + +public: + // public methods + NewSoftSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); + ~NewSoftSerial(); + void begin(long speed); + void end(); + int read(); + uint8_t available(void); + bool active() { return this == active_object; } + bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; } + static int library_version() { return _NewSS_VERSION; } + static void enable_timer0(bool enable); + void flush(); + + // public only for easy access by interrupt handlers + static inline void handle_interrupt(); +}; + +// Arduino 0012 workaround +#undef int +#undef char +#undef long +#undef byte +#undef float +#undef abs +#undef round + +#endif diff --git a/sketch/brmdoor.pde b/brmdoor/brmdoor.pde similarity index 99% rename from sketch/brmdoor.pde rename to brmdoor/brmdoor.pde index aa412ea..b7d9465 100644 --- a/sketch/brmdoor.pde +++ b/brmdoor/brmdoor.pde @@ -2,7 +2,7 @@ #define MUSIC 1 -#include +#include "NewSoftSerial.h" // pins const int soundPin = 9; /* piezo in series with 100R */ diff --git a/sketch/pitches.h b/brmdoor/pitches.h similarity index 100% rename from sketch/pitches.h rename to brmdoor/pitches.h