2017-08-22 16:30:33 +02:00
/*
2017-11-18 09:08:03 +01:00
* SoftwareSerial . cpp ( formerly NewSoftSerial . cpp )
*
* Multi - instance software serial library for Arduino / Wiring
* - - Interrupt - driven receive and other improvements by ladyada
* ( http : //ladyada.net)
* - - Tuning , circular buffer , derivation from class Print / Stream ,
* multi - instance support , porting to 8 MHz processors ,
* various optimizations , PROGMEM delay tables , inverse logic and
* direct port writing by Mikal Hart ( http : //www.arduiniana.org)
* - - Pin change interrupt macros by Paul Stoffregen ( http : //www.pjrc.com)
* - - 20 MHz processor support by Garrett Mace ( http : //www.macetech.com)
* - - ATmega1280 / 2560 support by Brett Hagman ( http : //www.roguerobotics.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
*
* The latest version of this library can always be found at
* http : //arduiniana.org.
*/
2017-08-22 16:30:33 +02:00
2017-08-26 22:25:25 +02:00
# ifdef TARGET_LPC1768
2017-08-22 16:30:33 +02:00
//
// Includes
//
//#include <WInterrupts.h>
2017-10-25 00:28:33 +02:00
# include "../../inc/MarlinConfig.h"
2018-05-12 15:34:04 +02:00
# include "../Delay.h"
2017-08-22 16:30:33 +02:00
# include <stdint.h>
# include <stdarg.h>
2018-02-23 07:52:52 +01:00
# include <Arduino.h>
# include <pinmapping.h>
2017-08-22 16:30:33 +02:00
# include "fastio.h"
# include "SoftwareSerial.h"
void GpioEnableInt ( uint32_t port , uint32_t pin , uint32_t mode ) ;
void GpioDisableInt ( uint32_t port , uint32_t pin ) ;
//
// Statics
//
SoftwareSerial * SoftwareSerial : : active_object = 0 ;
unsigned char SoftwareSerial : : _receive_buffer [ _SS_MAX_RX_BUFF ] ;
volatile uint8_t SoftwareSerial : : _receive_buffer_tail = 0 ;
volatile uint8_t SoftwareSerial : : _receive_buffer_head = 0 ;
2018-02-25 07:13:46 +01:00
typedef struct _DELAY_TABLE {
2017-08-22 16:30:33 +02:00
long baud ;
uint16_t rx_delay_centering ;
uint16_t rx_delay_intrabit ;
uint16_t rx_delay_stopbit ;
uint16_t tx_delay ;
} DELAY_TABLE ;
// rough delay estimation
2018-02-25 07:13:46 +01:00
static const DELAY_TABLE table [ ] = {
//baud |rxcenter|rxintra |rxstop |tx { 250000, 2, 4, 4, 4, }, //Done but not good due to instruction cycle error { 115200, 4, 8, 8, 8, }, //Done but not good due to instruction cycle error
2017-08-22 16:30:33 +02:00
//{ 74880, 69, 139, 62, 162, }, // estimation
2018-04-24 16:24:26 +02:00
//{ 57600, 100, 185, 1, 208, }, // Done but not good due to instruction cycle error
2017-08-22 16:30:33 +02:00
//{ 38400, 13, 26, 26, 26, }, // Done
2018-02-25 07:13:46 +01:00
//{ 19200, 26, 52, 52, 52, }, // Done { 9600, 52, 104, 104, 104, }, // Done
2017-08-22 16:30:33 +02:00
//{ 4800, 104, 208, 208, 208, },
//{ 2400, 208, 417, 417, 417, },
//{ 1200, 416, 833, 833, 833,},
} ;
//
// Private methods
//
2018-02-25 07:13:46 +01:00
inline void SoftwareSerial : : tunedDelay ( const uint32_t count ) {
2018-05-12 15:34:04 +02:00
DELAY_US ( count ) ;
2017-08-22 16:30:33 +02:00
}
// This function sets the current object as the "listening"
// one and returns true if it replaces another
2018-02-25 07:13:46 +01:00
bool SoftwareSerial : : listen ( ) {
2017-08-22 16:30:33 +02:00
if ( ! _rx_delay_stopbit )
return false ;
2018-02-25 07:13:46 +01:00
if ( active_object ! = this ) {
2017-08-22 16:30:33 +02:00
if ( active_object )
active_object - > stopListening ( ) ;
_buffer_overflow = false ;
_receive_buffer_head = _receive_buffer_tail = 0 ;
active_object = this ;
setRxIntMsk ( true ) ;
return true ;
}
return false ;
}
// Stop listening. Returns true if we were actually listening.
2018-02-25 07:13:46 +01:00
bool SoftwareSerial : : stopListening ( ) {
if ( active_object = = this ) {
2017-08-22 16:30:33 +02:00
setRxIntMsk ( false ) ;
active_object = NULL ;
return true ;
}
return false ;
}
//
// The receive routine called by the interrupt handler
//
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : recv ( ) {
2017-08-22 16:30:33 +02:00
uint8_t d = 0 ;
// If RX line is high, then we don't see any start bit
// so interrupt is probably not for us
2018-02-25 07:13:46 +01:00
if ( _inverse_logic ? rx_pin_read ( ) : ! rx_pin_read ( ) ) {
2017-08-22 16:30:33 +02:00
// Disable further interrupts during reception, this prevents
// triggering another interrupt directly after we return, which can
// cause problems at higher baudrates.
setRxIntMsk ( false ) ; //__disable_irq();//
// Wait approximately 1/2 of a bit width to "center" the sample
tunedDelay ( _rx_delay_centering ) ;
// Read each of the 8 bits
2018-02-25 07:13:46 +01:00
for ( uint8_t i = 8 ; i > 0 ; - - i ) {
tunedDelay ( _rx_delay_intrabit ) ;
2017-08-22 16:30:33 +02:00
d > > = 1 ;
2018-02-25 07:13:46 +01:00
if ( rx_pin_read ( ) ) d | = 0x80 ;
2017-08-22 16:30:33 +02:00
}
2018-02-25 07:13:46 +01:00
if ( _inverse_logic ) d = ~ d ;
2017-08-22 16:30:33 +02:00
// if buffer full, set the overflow flag and return
uint8_t next = ( _receive_buffer_tail + 1 ) % _SS_MAX_RX_BUFF ;
2018-02-25 07:13:46 +01:00
if ( next ! = _receive_buffer_head ) {
2017-08-22 16:30:33 +02:00
// save new data in buffer: tail points to where byte goes
_receive_buffer [ _receive_buffer_tail ] = d ; // save new byte
_receive_buffer_tail = next ;
}
2018-02-25 07:13:46 +01:00
else {
2017-08-22 16:30:33 +02:00
_buffer_overflow = true ;
}
2018-02-25 07:13:46 +01:00
tunedDelay ( _rx_delay_stopbit ) ;
2017-08-22 16:30:33 +02:00
// Re-enable interrupts when we're sure to be inside the stop bit
2018-02-25 07:13:46 +01:00
setRxIntMsk ( true ) ; //__enable_irq();//
2017-08-22 16:30:33 +02:00
}
}
2018-02-25 07:13:46 +01:00
uint32_t SoftwareSerial : : rx_pin_read ( ) {
2017-08-22 16:30:33 +02:00
return digitalRead ( _receivePin ) ;
}
//
// Interrupt handling
//
/* static */
2018-02-25 07:13:46 +01:00
inline void SoftwareSerial : : handle_interrupt ( ) {
2017-08-22 16:30:33 +02:00
if ( active_object )
active_object - > recv ( ) ;
}
extern " C " void intWrapper ( ) {
SoftwareSerial : : handle_interrupt ( ) ;
}
//
// Constructor
//
2017-11-03 00:43:57 +01:00
SoftwareSerial : : SoftwareSerial ( pin_t receivePin , pin_t transmitPin , bool inverse_logic /* = false */ ) :
2017-08-22 16:30:33 +02:00
_rx_delay_centering ( 0 ) ,
_rx_delay_intrabit ( 0 ) ,
_rx_delay_stopbit ( 0 ) ,
_tx_delay ( 0 ) ,
_buffer_overflow ( false ) ,
2018-02-25 07:13:46 +01:00
_inverse_logic ( inverse_logic ) {
2017-08-22 16:30:33 +02:00
setTX ( transmitPin ) ;
setRX ( receivePin ) ;
}
//
// Destructor
//
2018-02-25 07:13:46 +01:00
SoftwareSerial : : ~ SoftwareSerial ( ) {
2017-08-22 16:30:33 +02:00
end ( ) ;
}
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : setTX ( pin_t tx ) {
2017-08-22 16:30:33 +02:00
// First write, then set output. If we do this the other way around,
// the pin would be output low for a short while before switching to
// output hihg. Now, it is input with pullup for a short while, which
// is fine. With inverse logic, either order is fine.
digitalWrite ( tx , _inverse_logic ? LOW : HIGH ) ;
pinMode ( tx , OUTPUT ) ;
_transmitPin = tx ;
}
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : setRX ( pin_t rx ) {
2017-08-22 16:30:33 +02:00
pinMode ( rx , INPUT_PULLUP ) ; // pullup for normal logic!
//if (!_inverse_logic)
// digitalWrite(rx, HIGH);
_receivePin = rx ;
2017-10-26 20:37:26 +02:00
_receivePort = LPC1768_PIN_PORT ( rx ) ;
_receivePortPin = LPC1768_PIN_PIN ( rx ) ;
2018-02-25 07:13:46 +01:00
/* GPIO_T * rxPort = digitalPinToPort(rx);
2017-08-22 16:30:33 +02:00
_receivePortRegister = portInputRegister ( rxPort ) ;
_receiveBitMask = digitalPinToBitMask ( rx ) ; */
}
//
// Public methods
//
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : begin ( long speed ) {
2017-08-22 16:30:33 +02:00
_rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0 ;
2018-02-25 07:13:46 +01:00
for ( uint8_t i = 0 ; i < sizeof ( table ) / sizeof ( table [ 0 ] ) ; + + i ) {
2017-08-22 16:30:33 +02:00
long baud = table [ i ] . baud ;
2018-02-25 07:13:46 +01:00
if ( baud = = speed ) {
2017-08-22 16:30:33 +02:00
_rx_delay_centering = table [ i ] . rx_delay_centering ;
_rx_delay_intrabit = table [ i ] . rx_delay_intrabit ;
_rx_delay_stopbit = table [ i ] . rx_delay_stopbit ;
_tx_delay = table [ i ] . tx_delay ;
break ;
}
}
attachInterrupt ( _receivePin , intWrapper , CHANGE ) ; //this->handle_interrupt, CHANGE);
listen ( ) ;
tunedDelay ( _tx_delay ) ;
}
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : setRxIntMsk ( bool enable ) {
2017-09-30 23:06:43 +02:00
if ( enable )
GpioEnableInt ( _receivePort , _receivePin , CHANGE ) ;
else
GpioDisableInt ( _receivePort , _receivePin ) ;
2017-08-22 16:30:33 +02:00
}
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : end ( ) {
2017-08-22 16:30:33 +02:00
stopListening ( ) ;
}
// Read data from buffer
2018-02-25 07:13:46 +01:00
int SoftwareSerial : : read ( ) {
if ( ! isListening ( ) ) return - 1 ;
2017-08-22 16:30:33 +02:00
// Empty buffer?
2018-02-25 07:13:46 +01:00
if ( _receive_buffer_head = = _receive_buffer_tail ) return - 1 ;
2017-08-22 16:30:33 +02:00
// Read from "head"
uint8_t d = _receive_buffer [ _receive_buffer_head ] ; // grab next byte
_receive_buffer_head = ( _receive_buffer_head + 1 ) % _SS_MAX_RX_BUFF ;
return d ;
}
2018-02-25 07:13:46 +01:00
int SoftwareSerial : : available ( ) {
if ( ! isListening ( ) ) return 0 ;
2017-08-22 16:30:33 +02:00
return ( _receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head ) % _SS_MAX_RX_BUFF ;
}
2018-02-25 07:13:46 +01:00
size_t SoftwareSerial : : write ( uint8_t b ) {
2017-08-22 16:30:33 +02:00
// By declaring these as local variables, the compiler will put them
// in registers _before_ disabling interrupts and entering the
// critical timing sections below, which makes it a lot easier to
// verify the cycle timings
bool inv = _inverse_logic ;
uint16_t delay = _tx_delay ;
2018-02-25 07:13:46 +01:00
if ( inv ) b = ~ b ;
2017-08-22 16:30:33 +02:00
cli ( ) ; // turn off interrupts for a clean txmit
// Write the start bit
2018-02-25 07:13:46 +01:00
digitalWrite ( _transmitPin , ! ! inv ) ;
2017-08-22 16:30:33 +02:00
tunedDelay ( delay ) ;
// Write each of the 8 bits
2018-02-25 07:13:46 +01:00
for ( uint8_t i = 8 ; i > 0 ; - - i ) {
digitalWrite ( _transmitPin , b & 1 ) ; // send 1 //(GPIO_Desc[_transmitPin].P)->DOUT |= GPIO_Desc[_transmitPin].bit;
// send 0 //(GPIO_Desc[_transmitPin].P)->DOUT &= ~GPIO_Desc[_transmitPin].bit;
2017-08-22 16:30:33 +02:00
tunedDelay ( delay ) ;
b > > = 1 ;
}
// restore pin to natural state
2018-02-25 07:13:46 +01:00
digitalWrite ( _transmitPin , ! inv ) ;
2017-08-22 16:30:33 +02:00
2017-09-27 11:57:33 +02:00
sei ( ) ; // turn interrupts back on
2017-08-22 16:30:33 +02:00
tunedDelay ( delay ) ;
return 1 ;
}
2018-02-25 07:13:46 +01:00
void SoftwareSerial : : flush ( ) {
if ( ! isListening ( ) ) return ;
2017-08-22 16:30:33 +02:00
cli ( ) ;
_receive_buffer_head = _receive_buffer_tail = 0 ;
sei ( ) ;
}
2018-02-25 07:13:46 +01:00
int SoftwareSerial : : peek ( ) {
2017-08-22 16:30:33 +02:00
if ( ! isListening ( ) )
return - 1 ;
// Empty buffer?
if ( _receive_buffer_head = = _receive_buffer_tail )
return - 1 ;
// Read from "head"
return _receive_buffer [ _receive_buffer_head ] ;
}
2017-08-26 22:25:25 +02:00
# endif // TARGET_LPC1768