From 18f97c4013ec541d43be67b8b05e5a6927712ebd Mon Sep 17 00:00:00 2001 From: kfazz Date: Tue, 22 Aug 2017 10:30:33 -0400 Subject: [PATCH] add HardwareSerial and SoftwareSerial for Re-ARM. HardwareSerial has been tested on Uart0 (debug header) and Uart3 (i2c connector) Software Serial has been tested to work bi-directionally at 9600 and 115200 using pins 6 and 63 on J5, and unidirectionally (write only) at 250000. The code used to test was Teemuatlut's tmc2208 patch, and a few small changes to main used to echo recieved chars back to a host pc. --- Marlin/frameworks/CMSIS/LPC1768/lib/Print.cpp | 5 +- .../frameworks/CMSIS/LPC1768/lib/Stream.cpp | 319 +++++++++ Marlin/frameworks/CMSIS/LPC1768/lib/Stream.h | 117 ++++ Marlin/frameworks/CMSIS/LPC1768/lib/WString.h | 229 +++++++ Marlin/src/HAL/HAL_LPC1768/HardwareSerial.cpp | 636 ++++++++++++++++++ Marlin/src/HAL/HAL_LPC1768/HardwareSerial.h | 149 ++++ Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.cpp | 398 +++++++++++ Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.h | 119 ++++ Marlin/src/HAL/HAL_LPC1768/WInterrupts.cpp | 224 ++++++ Marlin/src/HAL/HAL_LPC1768/arduino.h | 7 + Marlin/src/HAL/HAL_LPC1768/pinmap_re_arm.h | 44 ++ 11 files changed, 2246 insertions(+), 1 deletion(-) create mode 100644 Marlin/frameworks/CMSIS/LPC1768/lib/Stream.cpp create mode 100644 Marlin/frameworks/CMSIS/LPC1768/lib/Stream.h create mode 100644 Marlin/frameworks/CMSIS/LPC1768/lib/WString.h create mode 100644 Marlin/src/HAL/HAL_LPC1768/HardwareSerial.cpp create mode 100644 Marlin/src/HAL/HAL_LPC1768/HardwareSerial.h create mode 100644 Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.cpp create mode 100644 Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.h create mode 100644 Marlin/src/HAL/HAL_LPC1768/WInterrupts.cpp diff --git a/Marlin/frameworks/CMSIS/LPC1768/lib/Print.cpp b/Marlin/frameworks/CMSIS/LPC1768/lib/Print.cpp index d5ea7c7f4..9219d0515 100644 --- a/Marlin/frameworks/CMSIS/LPC1768/lib/Print.cpp +++ b/Marlin/frameworks/CMSIS/LPC1768/lib/Print.cpp @@ -28,7 +28,10 @@ #include "Print.h" #include -#define PrintfEnable 0 +#define PrintfEnable 1 +typedef signed short sint16_t; +typedef signed long sint32_t; + // Public Methods ////////////////////////////////////////////////////////////// /* default implementation: may be overridden */ diff --git a/Marlin/frameworks/CMSIS/LPC1768/lib/Stream.cpp b/Marlin/frameworks/CMSIS/LPC1768/lib/Stream.cpp new file mode 100644 index 000000000..b1e507e9e --- /dev/null +++ b/Marlin/frameworks/CMSIS/LPC1768/lib/Stream.cpp @@ -0,0 +1,319 @@ +/* + Stream.cpp - adds parsing methods to Stream class + Copyright (c) 2008 David A. Mellis. 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 + + Created July 2011 + parsing functions based on TextFinder library by Michael Margolis + + findMulti/findUntil routines written by Jim Leonard/Xuth + */ + +#include +#include "../../../../src/HAL/HAL_LPC1768/arduino.h" + +#include "Stream.h" + +#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait +#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field + +// private method to read stream with timeout +int Stream::timedRead() +{ + int c; + _startMillis = millis(); + do { + c = read(); + if (c >= 0) return c; + } while(millis() - _startMillis < _timeout); + return -1; // -1 indicates timeout +} + +// private method to peek stream with timeout +int Stream::timedPeek() +{ + int c; + _startMillis = millis(); + do { + c = peek(); + if (c >= 0) return c; + } while(millis() - _startMillis < _timeout); + return -1; // -1 indicates timeout +} + +// returns peek of the next digit in the stream or -1 if timeout +// discards non-numeric characters +int Stream::peekNextDigit() +{ + int c; + while (1) { + c = timedPeek(); + if (c < 0) return c; // timeout + if (c == '-') return c; + if (c >= '0' && c <= '9') return c; + read(); // discard non-numeric + } +} + +// Public Methods +////////////////////////////////////////////////////////////// + +void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait +{ + _timeout = timeout; +} + + // find returns true if the target string is found +bool Stream::find(char *target) +{ + return findUntil(target, strlen(target), NULL, 0); +} + +// reads data from the stream until the target string of given length is found +// returns true if target string is found, false if timed out +bool Stream::find(char *target, size_t length) +{ + return findUntil(target, length, NULL, 0); +} + +// as find but search ends if the terminator string is found +bool Stream::findUntil(char *target, char *terminator) +{ + return findUntil(target, strlen(target), terminator, strlen(terminator)); +} + +// reads data from the stream until the target string of the given length is found +// search terminated if the terminator string is found +// returns true if target string is found, false if terminated or timed out +bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) +{ + if (terminator == NULL) { + MultiTarget t[1] = {{target, targetLen, 0}}; + return findMulti(t, 1) == 0 ? true : false; + } else { + MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}}; + return findMulti(t, 2) == 0 ? true : false; + } +} + + +// returns the first valid (long) integer value from the current position. +// initial characters that are not digits (or the minus sign) are skipped +// function is terminated by the first character that is not a digit. +long Stream::parseInt() +{ + return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout) +} + +// as above but a given skipChar is ignored +// this allows format characters (typically commas) in values to be ignored +long Stream::parseInt(char skipChar) +{ + bool isNegative = false; + long value = 0; + int c; + + c = peekNextDigit(); + // ignore non numeric leading characters + if(c < 0) + return 0; // zero returned if timeout + + do{ + if(c == skipChar) + ; // ignore this charactor + else if(c == '-') + isNegative = true; + else if(c >= '0' && c <= '9') // is c a digit? + value = value * 10 + c - '0'; + read(); // consume the character we got with peek + c = timedPeek(); + } + while( (c >= '0' && c <= '9') || c == skipChar ); + + if(isNegative) + value = -value; + return value; +} + + +// as parseInt but returns a floating point value +float Stream::parseFloat() +{ + return parseFloat(NO_SKIP_CHAR); +} + +// as above but the given skipChar is ignored +// this allows format characters (typically commas) in values to be ignored +float Stream::parseFloat(char skipChar){ + bool isNegative = false; + bool isFraction = false; + long value = 0; + char c; + float fraction = 1.0; + + c = peekNextDigit(); + // ignore non numeric leading characters + if(c < 0) + return 0; // zero returned if timeout + + do{ + if(c == skipChar) + ; // ignore + else if(c == '-') + isNegative = true; + else if (c == '.') + isFraction = true; + else if(c >= '0' && c <= '9') { // is c a digit? + value = value * 10 + c - '0'; + if(isFraction) + fraction *= 0.1; + } + read(); // consume the character we got with peek + c = timedPeek(); + } + while( (c >= '0' && c <= '9') || c == '.' || c == skipChar ); + + if(isNegative) + value = -value; + if(isFraction) + return value * fraction; + else + return value; +} + +// read characters from stream into buffer +// terminates if length characters have been read, or timeout (see setTimeout) +// returns the number of characters placed in the buffer +// the buffer is NOT null terminated. +// +size_t Stream::readBytes(char *buffer, size_t length) +{ + size_t count = 0; + while (count < length) { + int c = timedRead(); + if (c < 0) break; + *buffer++ = (char)c; + count++; + } + return count; +} + + +// as readBytes with terminator character +// terminates if length characters have been read, timeout, or if the terminator character detected +// returns the number of characters placed in the buffer (0 means no valid data found) + +size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) +{ + if (length < 1) return 0; + size_t index = 0; + while (index < length) { + int c = timedRead(); + if (c < 0 || c == terminator) break; + *buffer++ = (char)c; + index++; + } + return index; // return number of characters, not including null terminator +} + +String Stream::readString() +{ + String ret; + int c = timedRead(); + while (c >= 0) + { + ret += (char)c; + c = timedRead(); + } + return ret; +} + +String Stream::readStringUntil(char terminator) +{ + String ret; + int c = timedRead(); + while (c >= 0 && c != terminator) + { + ret += (char)c; + c = timedRead(); + } + return ret; +} + +int Stream::findMulti( struct Stream::MultiTarget *targets, int tCount) { + // any zero length target string automatically matches and would make + // a mess of the rest of the algorithm. + for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { + if (t->len <= 0) + return t - targets; + } + + while (1) { + int c = timedRead(); + if (c < 0) + return -1; + + for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { + // the simple case is if we match, deal with that first. + if (c == t->str[t->index]) { + if (++t->index == t->len) + return t - targets; + else + continue; + } + + // if not we need to walk back and see if we could have matched further + // down the stream (ie '1112' doesn't match the first position in '11112' + // but it will match the second position so we can't just reset the current + // index to 0 when we find a mismatch. + if (t->index == 0) + continue; + + int origIndex = t->index; + do { + --t->index; + // first check if current char works against the new current index + if (c != t->str[t->index]) + continue; + + // if it's the only char then we're good, nothing more to check + if (t->index == 0) { + t->index++; + break; + } + + // otherwise we need to check the rest of the found string + int diff = origIndex - t->index; + size_t i; + for (i = 0; i < t->index; ++i) { + if (t->str[i] != t->str[i + diff]) + break; + } + + // if we successfully got through the previous loop then our current + // index is good. + if (i == t->index) { + t->index++; + break; + } + + // otherwise we just try the next index + } while (t->index); + } + } + // unreachable + return -1; +} diff --git a/Marlin/frameworks/CMSIS/LPC1768/lib/Stream.h b/Marlin/frameworks/CMSIS/LPC1768/lib/Stream.h new file mode 100644 index 000000000..a0be7a9e6 --- /dev/null +++ b/Marlin/frameworks/CMSIS/LPC1768/lib/Stream.h @@ -0,0 +1,117 @@ +/* + Stream.h - base class for character-based streams. + Copyright (c) 2010 David A. Mellis. 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 + + parsing functions based on TextFinder library by Michael Margolis +*/ + +#ifndef Stream_h +#define Stream_h + +#include +#include +#include "Print.h" +#include "WString.h" + +// compatability macros for testing +/* +#define getInt() parseInt() +#define getInt(skipChar) parseInt(skipchar) +#define getFloat() parseFloat() +#define getFloat(skipChar) parseFloat(skipChar) +#define getString( pre_string, post_string, buffer, length) +readBytesBetween( pre_string, terminator, buffer, length) +*/ + +class Stream : public Print +{ + protected: + unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read + unsigned long _startMillis; // used for timeout measurement + int timedRead(); // private method to read stream with timeout + int timedPeek(); // private method to peek stream with timeout + int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout + + public: + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + virtual void flush() = 0; + + Stream() {_timeout=1000;} + +// parsing methods + + void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second + + bool find(char *target); // reads data from the stream until the target string is found + bool find(uint8_t *target) { return find ((char *)target); } + // returns true if target string is found, false if timed out (see setTimeout) + + bool find(char *target, size_t length); // reads data from the stream until the target string of given length is found + bool find(uint8_t *target, size_t length) { return find ((char *)target, length); } + // returns true if target string is found, false if timed out + + bool find(char target) { return find (&target, 1); } + + bool findUntil(char *target, char *terminator); // as find but search ends if the terminator string is found + bool findUntil(uint8_t *target, char *terminator) { return findUntil((char *)target, terminator); } + + bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen); // as above but search ends if the terminate string is found + bool findUntil(uint8_t *target, size_t targetLen, char *terminate, size_t termLen) {return findUntil((char *)target, targetLen, terminate, termLen); } + + + long parseInt(); // returns the first valid (long) integer value from the current position. + // initial characters that are not digits (or the minus sign) are skipped + // integer is terminated by the first character that is not a digit. + + float parseFloat(); // float version of parseInt + + size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer + size_t readBytes( uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); } + // terminates if length characters have been read or timeout (see setTimeout) + // returns the number of characters placed in the buffer (0 means no valid data found) + + size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character + size_t readBytesUntil( char terminator, uint8_t *buffer, size_t length) { return readBytesUntil(terminator, (char *)buffer, length); } + // terminates if length characters have been read, timeout, or if the terminator character detected + // returns the number of characters placed in the buffer (0 means no valid data found) + + // Arduino String functions to be added here + String readString(); + String readStringUntil(char terminator); + + protected: + long parseInt(char skipChar); // as above but the given skipChar is ignored + // as above but the given skipChar is ignored + // this allows format characters (typically commas) in values to be ignored + + float parseFloat(char skipChar); // as above but the given skipChar is ignored + + struct MultiTarget { + const char *str; // string you're searching for + size_t len; // length of string you're searching for + size_t index; // index used by the search routine. + }; + + // This allows you to search for an arbitrary number of strings. + // Returns index of the target that is found first or -1 if timeout occurs. + int findMulti(struct MultiTarget *targets, int tCount); +}; + + +#endif diff --git a/Marlin/frameworks/CMSIS/LPC1768/lib/WString.h b/Marlin/frameworks/CMSIS/LPC1768/lib/WString.h new file mode 100644 index 000000000..8a1876424 --- /dev/null +++ b/Marlin/frameworks/CMSIS/LPC1768/lib/WString.h @@ -0,0 +1,229 @@ +/* + WString.h - String library for Wiring & Arduino + ...mostly rewritten by Paul Stoffregen... + Copyright (c) 2009-10 Hernando Barragan. All right reserved. + Copyright 2011, Paul Stoffregen, paul@pjrc.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 +*/ + +#ifndef String_class_h +#define String_class_h +#ifdef __cplusplus + +#include +#include +#include +//#include + +// When compiling programs with this class, the following gcc parameters +// dramatically increase performance and memory (RAM) efficiency, typically +// with little or no increase in code size. +// -felide-constructors +// -std=c++0x + +class __FlashStringHelper; +#define F(string_literal) (reinterpret_cast(PSTR(string_literal))) + +// An inherited class for holding the result of a concatenation. These +// result objects are assumed to be writable by subsequent concatenations. +class StringSumHelper; + +// The string class +class String +{ + // use a function pointer to allow for "if (s)" without the + // complications of an operator bool(). for more information, see: + // http://www.artima.com/cppsource/safebool.html + typedef void (String::*StringIfHelperType)() const; + void StringIfHelper() const {} + +public: + // constructors + // creates a copy of the initial value. + // if the initial value is null or invalid, or if memory allocation + // fails, the string will be marked as invalid (i.e. "if (s)" will + // be false). + String(const char *cstr = ""); + String(const String &str); + String(const __FlashStringHelper *str); + #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) + String(String &&rval); + String(StringSumHelper &&rval); + #endif + explicit String(char c); + explicit String(unsigned char, unsigned char base=10); + explicit String(int, unsigned char base=10); + explicit String(unsigned int, unsigned char base=10); + explicit String(long, unsigned char base=10); + explicit String(unsigned long, unsigned char base=10); + explicit String(float, unsigned char decimalPlaces=2); + explicit String(double, unsigned char decimalPlaces=2); + ~String(void); + + // memory management + // return true on success, false on failure (in which case, the string + // is left unchanged). reserve(0), if successful, will validate an + // invalid string (i.e., "if (s)" will be true afterwards) + unsigned char reserve(unsigned int size); + inline unsigned int length(void) const {return len;} + + // creates a copy of the assigned value. if the value is null or + // invalid, or if the memory allocation fails, the string will be + // marked as invalid ("if (s)" will be false). + String & operator = (const String &rhs); + String & operator = (const char *cstr); + String & operator = (const __FlashStringHelper *str); + #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) + String & operator = (String &&rval); + String & operator = (StringSumHelper &&rval); + #endif + + // concatenate (works w/ built-in types) + + // returns true on success, false on failure (in which case, the string + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsucessful. + unsigned char concat(const String &str); + unsigned char concat(const char *cstr); + unsigned char concat(char c); + unsigned char concat(unsigned char c); + unsigned char concat(int num); + unsigned char concat(unsigned int num); + unsigned char concat(long num); + unsigned char concat(unsigned long num); + unsigned char concat(float num); + unsigned char concat(double num); + unsigned char concat(const __FlashStringHelper * str); + + // if there's not enough memory for the concatenated value, the string + // will be left unchanged (but this isn't signalled in any way) + String & operator += (const String &rhs) {concat(rhs); return (*this);} + String & operator += (const char *cstr) {concat(cstr); return (*this);} + String & operator += (char c) {concat(c); return (*this);} + String & operator += (unsigned char num) {concat(num); return (*this);} + String & operator += (int num) {concat(num); return (*this);} + String & operator += (unsigned int num) {concat(num); return (*this);} + String & operator += (long num) {concat(num); return (*this);} + String & operator += (unsigned long num) {concat(num); return (*this);} + String & operator += (float num) {concat(num); return (*this);} + String & operator += (double num) {concat(num); return (*this);} + String & operator += (const __FlashStringHelper *str){concat(str); return (*this);} + + friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs); + friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr); + friend StringSumHelper & operator + (const StringSumHelper &lhs, char c); + friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, int num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, long num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, float num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, double num); + friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs); + + // comparison (only works w/ Strings and "strings") + operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; } + int compareTo(const String &s) const; + unsigned char equals(const String &s) const; + unsigned char equals(const char *cstr) const; + unsigned char operator == (const String &rhs) const {return equals(rhs);} + unsigned char operator == (const char *cstr) const {return equals(cstr);} + unsigned char operator != (const String &rhs) const {return !equals(rhs);} + unsigned char operator != (const char *cstr) const {return !equals(cstr);} + unsigned char operator < (const String &rhs) const; + unsigned char operator > (const String &rhs) const; + unsigned char operator <= (const String &rhs) const; + unsigned char operator >= (const String &rhs) const; + unsigned char equalsIgnoreCase(const String &s) const; + unsigned char startsWith( const String &prefix) const; + unsigned char startsWith(const String &prefix, unsigned int offset) const; + unsigned char endsWith(const String &suffix) const; + + // character acccess + char charAt(unsigned int index) const; + void setCharAt(unsigned int index, char c); + char operator [] (unsigned int index) const; + char& operator [] (unsigned int index); + void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const + { getBytes((unsigned char *)buf, bufsize, index); } + const char* c_str() const { return buffer; } + char* begin() { return buffer; } + char* end() { return buffer + length(); } + const char* begin() const { return c_str(); } + const char* end() const { return c_str() + length(); } + + // search + int indexOf( char ch ) const; + int indexOf( char ch, unsigned int fromIndex ) const; + int indexOf( const String &str ) const; + int indexOf( const String &str, unsigned int fromIndex ) const; + int lastIndexOf( char ch ) const; + int lastIndexOf( char ch, unsigned int fromIndex ) const; + int lastIndexOf( const String &str ) const; + int lastIndexOf( const String &str, unsigned int fromIndex ) const; + String substring( unsigned int beginIndex ) const { return substring(beginIndex, len); }; + String substring( unsigned int beginIndex, unsigned int endIndex ) const; + + // modification + void replace(char find, char replace); + void replace(const String& find, const String& replace); + void remove(unsigned int index); + void remove(unsigned int index, unsigned int count); + void toLowerCase(void); + void toUpperCase(void); + void trim(void); + + // parsing/conversion + long toInt(void) const; + float toFloat(void) const; + double toDouble(void) const; + +protected: + char *buffer; // the actual char array + unsigned int capacity; // the array length minus one (for the '\0') + unsigned int len; // the String length (not counting the '\0') +protected: + void init(void); + void invalidate(void); + unsigned char changeBuffer(unsigned int maxStrLen); + unsigned char concat(const char *cstr, unsigned int length); + + // copy and move + String & copy(const char *cstr, unsigned int length); + String & copy(const __FlashStringHelper *pstr, unsigned int length); + #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) + void move(String &rhs); + #endif +}; + +class StringSumHelper : public String +{ +public: + StringSumHelper(const String &s) : String(s) {} + StringSumHelper(const char *p) : String(p) {} + StringSumHelper(char c) : String(c) {} + StringSumHelper(unsigned char num) : String(num) {} + StringSumHelper(int num) : String(num) {} + StringSumHelper(unsigned int num) : String(num) {} + StringSumHelper(long num) : String(num) {} + StringSumHelper(unsigned long num) : String(num) {} + StringSumHelper(float num) : String(num) {} + StringSumHelper(double num) : String(num) {} +}; + +#endif // __cplusplus +#endif // String_class_h diff --git a/Marlin/src/HAL/HAL_LPC1768/HardwareSerial.cpp b/Marlin/src/HAL/HAL_LPC1768/HardwareSerial.cpp new file mode 100644 index 000000000..b195fab1c --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/HardwareSerial.cpp @@ -0,0 +1,636 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../../../macros.h" +#include "../HAL.h" +#include "HardwareSerial.h" +#define UART3 3 +HardwareSerial Serial3 = HardwareSerial(UART3); + +volatile uint32_t UART0Status, UART1Status, UART2Status, UART3Status; +volatile uint8_t UART0TxEmpty = 1, UART1TxEmpty = 1, UART2TxEmpty=1, UART3TxEmpty=1; +volatile uint8_t UART0Buffer[UARTRXQUEUESIZE], UART1Buffer[UARTRXQUEUESIZE], UART2Buffer[UARTRXQUEUESIZE], UART3Buffer[UARTRXQUEUESIZE]; +volatile uint32_t UART0RxQueueWritePos = 0, UART1RxQueueWritePos = 0, UART2RxQueueWritePos = 0, UART3RxQueueWritePos = 0; +volatile uint32_t UART0RxQueueReadPos = 0, UART1RxQueueReadPos = 0, UART2RxQueueReadPos = 0, UART3RxQueueReadPos = 0; +volatile uint8_t dummy; + + void HardwareSerial::begin(uint32_t baudrate) { + uint32_t Fdiv; + uint32_t pclkdiv, pclk; + + if ( PortNum == 0 ) + { + LPC_PINCON->PINSEL0 &= ~0x000000F0; + LPC_PINCON->PINSEL0 |= 0x00000050; /* RxD0 is P0.3 and TxD0 is P0.2 */ + /* By default, the PCLKSELx value is zero, thus, the PCLK for + all the peripherals is 1/4 of the SystemFrequency. */ + /* Bit 6~7 is for UART0 */ + pclkdiv = (LPC_SC->PCLKSEL0 >> 6) & 0x03; + switch ( pclkdiv ) + { + case 0x00: + default: + pclk = SystemCoreClock/4; + break; + case 0x01: + pclk = SystemCoreClock; + break; + case 0x02: + pclk = SystemCoreClock/2; + break; + case 0x03: + pclk = SystemCoreClock/8; + break; + } + + LPC_UART0->LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */ + Fdiv = ( pclk / 16 ) / baudrate ; /*baud rate */ + LPC_UART0->DLM = Fdiv / 256; + LPC_UART0->DLL = Fdiv % 256; + LPC_UART0->LCR = 0x03; /* DLAB = 0 */ + LPC_UART0->FCR = 0x07; /* Enable and reset TX and RX FIFO. */ + + NVIC_EnableIRQ(UART0_IRQn); + + LPC_UART0->IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART0 interrupt */ + } + else if ( PortNum == 1 ) + { + LPC_PINCON->PINSEL4 &= ~0x0000000F; + LPC_PINCON->PINSEL4 |= 0x0000000A; /* Enable RxD1 P2.1, TxD1 P2.0 */ + + /* By default, the PCLKSELx value is zero, thus, the PCLK for + all the peripherals is 1/4 of the SystemFrequency. */ + /* Bit 8,9 are for UART1 */ + pclkdiv = (LPC_SC->PCLKSEL0 >> 8) & 0x03; + switch ( pclkdiv ) + { + case 0x00: + default: + pclk = SystemCoreClock/4; + break; + case 0x01: + pclk = SystemCoreClock; + break; + case 0x02: + pclk = SystemCoreClock/2; + break; + case 0x03: + pclk = SystemCoreClock/8; + break; + } + + LPC_UART1->LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */ + Fdiv = ( pclk / 16 ) / baudrate ; /*baud rate */ + LPC_UART1->DLM = Fdiv / 256; + LPC_UART1->DLL = Fdiv % 256; + LPC_UART1->LCR = 0x03; /* DLAB = 0 */ + LPC_UART1->FCR = 0x07; /* Enable and reset TX and RX FIFO. */ + + NVIC_EnableIRQ(UART1_IRQn); + + LPC_UART1->IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART1 interrupt */ + } + else if ( PortNum == 2 ) + { + //LPC_PINCON->PINSEL4 &= ~0x000F0000; /*Pinsel4 Bits 16-19*/ + //LPC_PINCON->PINSEL4 |= 0x000A0000; /* RxD2 is P2.9 and TxD2 is P2.8, value 10*/ + LPC_PINCON->PINSEL0 &= ~0x00F00000; /*Pinsel0 Bits 20-23*/ + LPC_PINCON->PINSEL0 |= 0x00500000; /* RxD2 is P0.11 and TxD2 is P0.10, value 01*/ + + LPC_SC->PCONP |= 1<<24; //Enable PCUART2 + /* By default, the PCLKSELx value is zero, thus, the PCLK for + all the peripherals is 1/4 of the SystemFrequency. */ + /* Bit 6~7 is for UART3 */ + pclkdiv = (LPC_SC->PCLKSEL1 >> 16) & 0x03; + switch ( pclkdiv ) + { + case 0x00: + default: + pclk = SystemCoreClock/4; + break; + case 0x01: + pclk = SystemCoreClock; + break; + case 0x02: + pclk = SystemCoreClock/2; + break; + case 0x03: + pclk = SystemCoreClock/8; + break; + } + LPC_UART2->LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */ + Fdiv = ( pclk / 16 ) / baudrate ; /*baud rate */ + LPC_UART2->DLM = Fdiv / 256; + LPC_UART2->DLL = Fdiv % 256; + LPC_UART2->LCR = 0x03; /* DLAB = 0 */ + LPC_UART2->FCR = 0x07; /* Enable and reset TX and RX FIFO. */ + + NVIC_EnableIRQ(UART2_IRQn); + + LPC_UART2->IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART3 interrupt */ + } + else if ( PortNum == 3 ) + { + LPC_PINCON->PINSEL0 &= ~0x0000000F; + LPC_PINCON->PINSEL0 |= 0x0000000A; /* RxD3 is P0.1 and TxD3 is P0.0 */ + LPC_SC->PCONP |= 1<<4 | 1<<25; //Enable PCUART1 + /* By default, the PCLKSELx value is zero, thus, the PCLK for + all the peripherals is 1/4 of the SystemFrequency. */ + /* Bit 6~7 is for UART3 */ + pclkdiv = (LPC_SC->PCLKSEL1 >> 18) & 0x03; + switch ( pclkdiv ) + { + case 0x00: + default: + pclk = SystemCoreClock/4; + break; + case 0x01: + pclk = SystemCoreClock; + break; + case 0x02: + pclk = SystemCoreClock/2; + break; + case 0x03: + pclk = SystemCoreClock/8; + break; + } + LPC_UART3->LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */ + Fdiv = ( pclk / 16 ) / baudrate ; /*baud rate */ + LPC_UART3->DLM = Fdiv / 256; + LPC_UART3->DLL = Fdiv % 256; + LPC_UART3->LCR = 0x03; /* DLAB = 0 */ + LPC_UART3->FCR = 0x07; /* Enable and reset TX and RX FIFO. */ + + NVIC_EnableIRQ(UART3_IRQn); + + LPC_UART3->IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART3 interrupt */ + } + } + + int HardwareSerial::read() { + uint8_t rx; + if ( PortNum == 0 ) + { + if (UART0RxQueueReadPos == UART0RxQueueWritePos) + return -1; + + // Read from "head" + rx = UART0Buffer[UART0RxQueueReadPos]; // grab next byte + UART0RxQueueReadPos = (UART0RxQueueReadPos + 1) % UARTRXQUEUESIZE; + return rx; + } + if ( PortNum == 1 ) + { + if (UART1RxQueueReadPos == UART1RxQueueWritePos) + return -1; + + // Read from "head" + rx = UART1Buffer[UART1RxQueueReadPos]; // grab next byte + UART1RxQueueReadPos = (UART1RxQueueReadPos + 1) % UARTRXQUEUESIZE; + return rx; + } + if ( PortNum == 2 ) + { + if (UART2RxQueueReadPos == UART2RxQueueWritePos) + return -1; + + // Read from "head" + rx = UART2Buffer[UART2RxQueueReadPos]; // grab next byte + UART2RxQueueReadPos = (UART2RxQueueReadPos + 1) % UARTRXQUEUESIZE; + return rx; + } + if ( PortNum == 3 ) + { + if (UART3RxQueueReadPos == UART3RxQueueWritePos) + return -1; + + // Read from "head" + rx = UART3Buffer[UART3RxQueueReadPos]; // grab next byte + UART3RxQueueReadPos = (UART3RxQueueReadPos + 1) % UARTRXQUEUESIZE; + return rx; + } + return 0; + } + + size_t HardwareSerial::write(uint8_t send) { + if ( PortNum == 0 ) + { + /* THRE status, contain valid data */ + while ( !(UART0TxEmpty & 0x01) ); + LPC_UART0->THR = send; + UART0TxEmpty = 0; /* not empty in the THR until it shifts out */ + } + else if (PortNum == 1) + { + + /* THRE status, contain valid data */ + while ( !(UART1TxEmpty & 0x01) ); + LPC_UART1->THR = send; + UART1TxEmpty = 0; /* not empty in the THR until it shifts out */ + + + } + else if ( PortNum == 2 ) + { + /* THRE status, contain valid data */ + while ( !(UART2TxEmpty & 0x01) ); + LPC_UART2->THR = send; + UART2TxEmpty = 0; /* not empty in the THR until it shifts out */ + + } + else if ( PortNum == 3 ) + { + /* THRE status, contain valid data */ + while ( !(UART3TxEmpty & 0x01) ); + LPC_UART3->THR = send; + UART3TxEmpty = 0; /* not empty in the THR until it shifts out */ + + } + return 0; + } + + int HardwareSerial::available() { + if ( PortNum == 0 ) +{ + return (UART0RxQueueWritePos + UARTRXQUEUESIZE - UART0RxQueueReadPos) % UARTRXQUEUESIZE; +} +if ( PortNum == 1 ) +{ + return (UART1RxQueueWritePos + UARTRXQUEUESIZE - UART1RxQueueReadPos) % UARTRXQUEUESIZE; +} +if ( PortNum == 2 ) +{ + return (UART2RxQueueWritePos + UARTRXQUEUESIZE - UART2RxQueueReadPos) % UARTRXQUEUESIZE; +} +if ( PortNum == 3 ) +{ + return (UART3RxQueueWritePos + UARTRXQUEUESIZE - UART3RxQueueReadPos) % UARTRXQUEUESIZE; +} +return 0; + } + + void HardwareSerial::flush() { + if ( PortNum == 0 ) +{ + UART0RxQueueWritePos = 0; + UART0RxQueueReadPos = 0; + +} +if ( PortNum == 1 ) +{ + UART1RxQueueWritePos = 0; + UART1RxQueueReadPos = 0; +} +if ( PortNum == 2 ) +{ + UART2RxQueueWritePos = 0; + UART2RxQueueReadPos = 0; +} +if ( PortNum == 3 ) +{ + UART3RxQueueWritePos = 0; + UART3RxQueueReadPos = 0; +} +return; + } + + void HardwareSerial::printf(const char *format, ...) { + static char buffer[256]; + va_list vArgs; + va_start(vArgs, format); + int length = vsnprintf((char *) buffer, 256, (char const *) format, vArgs); + va_end(vArgs); + if (length > 0 && length < 256) { + for (int i = 0; i < length;) { + write(buffer[i]); + ++i; + } + } + } + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************** +** Function name: UART0_IRQHandler +** +** Descriptions: UART0 interrupt handler +** +** parameters: None +** Returned value: None +** +*****************************************************************************/ +void UART0_IRQHandler (void) +{ + uint8_t IIRValue, LSRValue; + uint8_t Dummy = Dummy; + + IIRValue = LPC_UART0->IIR; + + IIRValue >>= 1; /* skip pending bit in IIR */ + IIRValue &= 0x07; /* check bit 1~3, interrupt identification */ + if ( IIRValue == IIR_RLS ) /* Receive Line Status */ + { + LSRValue = LPC_UART0->LSR; + /* Receive Line Status */ + if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) ) + { + /* There are errors or break interrupt */ + /* Read LSR will clear the interrupt */ + UART0Status = LSRValue; + Dummy = LPC_UART0->RBR; /* Dummy read on RX to clear + interrupt, then bail out */ + return; + } + if ( LSRValue & LSR_RDR ) /* Receive Data Ready */ + { + /* If no error on RLS, normal ready, save into the data buffer. */ + /* Note: read RBR will clear the interrupt */ + if ((UART0RxQueueWritePos+1) % UARTRXQUEUESIZE != UART0RxQueueReadPos) + { + UART0Buffer[UART0RxQueueWritePos] = LPC_UART0->RBR; + UART0RxQueueWritePos = (UART0RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + else + dummy = LPC_UART0->RBR;; + } + } + else if ( IIRValue == IIR_RDA ) /* Receive Data Available */ + { + /* Receive Data Available */ + if ((UART0RxQueueWritePos+1) % UARTRXQUEUESIZE != UART0RxQueueReadPos) + { + UART0Buffer[UART0RxQueueWritePos] = LPC_UART0->RBR; + UART0RxQueueWritePos = (UART0RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + else + dummy = LPC_UART1->RBR;; + } + else if ( IIRValue == IIR_CTI ) /* Character timeout indicator */ + { + /* Character Time-out indicator */ + UART0Status |= 0x100; /* Bit 9 as the CTI error */ + } + else if ( IIRValue == IIR_THRE ) /* THRE, transmit holding register empty */ + { + /* THRE interrupt */ + LSRValue = LPC_UART0->LSR; /* Check status in the LSR to see if + valid data in U0THR or not */ + if ( LSRValue & LSR_THRE ) + { + UART0TxEmpty = 1; + } + else + { + UART0TxEmpty = 0; + } + } +} + +/***************************************************************************** +** Function name: UART1_IRQHandler +** +** Descriptions: UART1 interrupt handler +** +** parameters: None +** Returned value: None +** +*****************************************************************************/ +void UART1_IRQHandler (void) +{ + uint8_t IIRValue, LSRValue; + uint8_t Dummy = Dummy; + + IIRValue = LPC_UART1->IIR; + + IIRValue >>= 1; /* skip pending bit in IIR */ + IIRValue &= 0x07; /* check bit 1~3, interrupt identification */ + if ( IIRValue == IIR_RLS ) /* Receive Line Status */ + { + LSRValue = LPC_UART1->LSR; + /* Receive Line Status */ + if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) ) + { + /* There are errors or break interrupt */ + /* Read LSR will clear the interrupt */ + UART1Status = LSRValue; + Dummy = LPC_UART1->RBR; /* Dummy read on RX to clear + interrupt, then bail out */ + return; + } + if ( LSRValue & LSR_RDR ) /* Receive Data Ready */ + { + /* If no error on RLS, normal ready, save into the data buffer. */ + /* Note: read RBR will clear the interrupt */ + if ((UART1RxQueueWritePos+1) % UARTRXQUEUESIZE != UART1RxQueueReadPos) + { + UART1Buffer[UART1RxQueueWritePos] = LPC_UART1->RBR; + UART1RxQueueWritePos =(UART1RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + else + dummy = LPC_UART1->RBR;; + } + } + else if ( IIRValue == IIR_RDA ) /* Receive Data Available */ + { + /* Receive Data Available */ + if ((UART1RxQueueWritePos+1) % UARTRXQUEUESIZE != UART1RxQueueReadPos) + { + UART1Buffer[UART1RxQueueWritePos] = LPC_UART1->RBR; + UART1RxQueueWritePos = (UART1RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + else + dummy = LPC_UART1->RBR;; + } + else if ( IIRValue == IIR_CTI ) /* Character timeout indicator */ + { + /* Character Time-out indicator */ + UART1Status |= 0x100; /* Bit 9 as the CTI error */ + } + else if ( IIRValue == IIR_THRE ) /* THRE, transmit holding register empty */ + { + /* THRE interrupt */ + LSRValue = LPC_UART1->LSR; /* Check status in the LSR to see if + valid data in U0THR or not */ + if ( LSRValue & LSR_THRE ) + { + UART1TxEmpty = 1; + } + else + { + UART1TxEmpty = 0; + } + } + +} +/***************************************************************************** +** Function name: UART2_IRQHandler +** +** Descriptions: UART2 interrupt handler +** +** parameters: None +** Returned value: None +** +*****************************************************************************/ +void UART2_IRQHandler (void) +{ + uint8_t IIRValue, LSRValue; + uint8_t Dummy = Dummy; + + IIRValue = LPC_UART2->IIR; + + IIRValue >>= 1; /* skip pending bit in IIR */ + IIRValue &= 0x07; /* check bit 1~3, interrupt identification */ + if ( IIRValue == IIR_RLS ) /* Receive Line Status */ + { + LSRValue = LPC_UART2->LSR; + /* Receive Line Status */ + if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) ) + { + /* There are errors or break interrupt */ + /* Read LSR will clear the interrupt */ + UART2Status = LSRValue; + Dummy = LPC_UART2->RBR; /* Dummy read on RX to clear + interrupt, then bail out */ + return; + } + if ( LSRValue & LSR_RDR ) /* Receive Data Ready */ + { + /* If no error on RLS, normal ready, save into the data buffer. */ + /* Note: read RBR will clear the interrupt */ + if ((UART2RxQueueWritePos+1) % UARTRXQUEUESIZE != UART2RxQueueReadPos) + { + UART2Buffer[UART2RxQueueWritePos] = LPC_UART2->RBR; + UART2RxQueueWritePos = (UART2RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + } + } + else if ( IIRValue == IIR_RDA ) /* Receive Data Available */ + { + /* Receive Data Available */ + if ((UART2RxQueueWritePos+1) % UARTRXQUEUESIZE != UART2RxQueueReadPos) + { + UART2Buffer[UART2RxQueueWritePos] = LPC_UART2->RBR; + UART2RxQueueWritePos = (UART2RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + else + dummy = LPC_UART2->RBR;; + } + else if ( IIRValue == IIR_CTI ) /* Character timeout indicator */ + { + /* Character Time-out indicator */ + UART2Status |= 0x100; /* Bit 9 as the CTI error */ + } + else if ( IIRValue == IIR_THRE ) /* THRE, transmit holding register empty */ + { + /* THRE interrupt */ + LSRValue = LPC_UART2->LSR; /* Check status in the LSR to see if + valid data in U0THR or not */ + if ( LSRValue & LSR_THRE ) + { + UART2TxEmpty = 1; + } + else + { + UART2TxEmpty = 0; + } + } +} +/***************************************************************************** +** Function name: UART3_IRQHandler +** +** Descriptions: UART0 interrupt handler +** +** parameters: None +** Returned value: None +** +*****************************************************************************/ +void UART3_IRQHandler (void) +{ + uint8_t IIRValue, LSRValue; + uint8_t Dummy = Dummy; + + IIRValue = LPC_UART3->IIR; + + IIRValue >>= 1; /* skip pending bit in IIR */ + IIRValue &= 0x07; /* check bit 1~3, interrupt identification */ + if ( IIRValue == IIR_RLS ) /* Receive Line Status */ + { + LSRValue = LPC_UART3->LSR; + /* Receive Line Status */ + if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) ) + { + /* There are errors or break interrupt */ + /* Read LSR will clear the interrupt */ + UART3Status = LSRValue; + Dummy = LPC_UART3->RBR; /* Dummy read on RX to clear + interrupt, then bail out */ + return; + } + if ( LSRValue & LSR_RDR ) /* Receive Data Ready */ + { + /* If no error on RLS, normal ready, save into the data buffer. */ + /* Note: read RBR will clear the interrupt */ + if ((UART3RxQueueWritePos+1) % UARTRXQUEUESIZE != UART3RxQueueReadPos) + { + UART3Buffer[UART3RxQueueWritePos] = LPC_UART3->RBR; + UART3RxQueueWritePos = (UART3RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + } + } + else if ( IIRValue == IIR_RDA ) /* Receive Data Available */ + { + /* Receive Data Available */ + if ((UART3RxQueueWritePos+1) % UARTRXQUEUESIZE != UART3RxQueueReadPos) + { + UART3Buffer[UART3RxQueueWritePos] = LPC_UART3->RBR; + UART3RxQueueWritePos = (UART3RxQueueWritePos+1) % UARTRXQUEUESIZE; + } + else + dummy = LPC_UART3->RBR;; + } + else if ( IIRValue == IIR_CTI ) /* Character timeout indicator */ + { + /* Character Time-out indicator */ + UART3Status |= 0x100; /* Bit 9 as the CTI error */ + } + else if ( IIRValue == IIR_THRE ) /* THRE, transmit holding register empty */ + { + /* THRE interrupt */ + LSRValue = LPC_UART3->LSR; /* Check status in the LSR to see if + valid data in U0THR or not */ + if ( LSRValue & LSR_THRE ) + { + UART3TxEmpty = 1; + } + else + { + UART3TxEmpty = 0; + } + } +} + +#ifdef __cplusplus +} +#endif diff --git a/Marlin/src/HAL/HAL_LPC1768/HardwareSerial.h b/Marlin/src/HAL/HAL_LPC1768/HardwareSerial.h new file mode 100644 index 000000000..90a773dda --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/HardwareSerial.h @@ -0,0 +1,149 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef HARDWARE_SERIAL_H_ +#define HARDWARE_SERIAL_H_ + +#include +#include +#include + +extern "C" { + #include + +//#include +} + +#define IER_RBR 0x01 +#define IER_THRE 0x02 +#define IER_RLS 0x04 + +#define IIR_PEND 0x01 +#define IIR_RLS 0x03 +#define IIR_RDA 0x02 +#define IIR_CTI 0x06 +#define IIR_THRE 0x01 + +#define LSR_RDR 0x01 +#define LSR_OE 0x02 +#define LSR_PE 0x04 +#define LSR_FE 0x08 +#define LSR_BI 0x10 +#define LSR_THRE 0x20 +#define LSR_TEMT 0x40 +#define LSR_RXFE 0x80 + +#define UARTRXQUEUESIZE 0x10 + +class HardwareSerial : public Stream { +private: +uint8_t PortNum; +uint32_t baudrate; + +public: + HardwareSerial(uint32_t uart) : + PortNum(uart) + { + } + + void begin(uint32_t baudrate); + int read(); + size_t write(uint8_t send); + int available(); + void flush(); + void printf(const char *format, ...); + int peek() { + return 0; + }; + + + operator bool() { + return true; + } + + void print(const char value[]) { + printf("%s" , value); + } + void print(char value, int = 0) { + printf("%c" , value); + } + void print(unsigned char value, int = 0) { + printf("%u" , value); + } + void print(int value, int = 0) { + printf("%d" , value); + } + void print(unsigned int value, int = 0) { + printf("%u" , value); + } + void print(long value, int = 0) { + printf("%ld" , value); + } + void print(unsigned long value, int = 0) { + printf("%lu" , value); + } + + void print(float value, int round = 6) { + printf("%f" , value); + } + void print(double value, int round = 6) { + printf("%f" , value ); + } + + void println(const char value[]) { + printf("%s\n" , value); + } + void println(char value, int = 0) { + printf("%c\n" , value); + } + void println(unsigned char value, int = 0) { + printf("%u\r\n" , value); + } + void println(int value, int = 0) { + printf("%d\n" , value); + } + void println(unsigned int value, int = 0) { + printf("%u\n" , value); + } + void println(long value, int = 0) { + printf("%ld\n" , value); + } + void println(unsigned long value, int = 0) { + printf("%lu\n" , value); + } + void println(float value, int round = 6) { + printf("%f\n" , value ); + } + void println(double value, int round = 6) { + printf("%f\n" , value ); + } + void println(void) { + print('\n'); + } + +}; +//extern HardwareSerial Serial0; +//extern HardwareSerial Serial1; +//extern HardwareSerial Serial2; +extern HardwareSerial Serial3; + +#endif /* MARLIN_SRC_HAL_HAL_SERIAL_H_ */ diff --git a/Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.cpp b/Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.cpp new file mode 100644 index 000000000..9b5eab549 --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.cpp @@ -0,0 +1,398 @@ +/* +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 8MHz 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) +-- 20MHz 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. +*/ + + +// +// Includes +// +//#include +#include "../../../macros.h" +#include "../HAL.h" +#include +#include +#include "arduino.h" +#include "pinmapping.h" +#include "pinmap_re_arm.h" +#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; + +typedef struct _DELAY_TABLE +{ + 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 +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 + //{ 74880, 69, 139, 62, 162, }, // estimation +// { 57600, 100, 185, 1, 208, }, // Done but not good due to instruction cycle error + //{ 38400, 13, 26, 26, 26, }, // Done + //{ 19200, 26, 52, 52, 52, }, // Done + { 9600, 52, 104, 104, 104, }, // Done + //{ 4800, 104, 208, 208, 208, }, + //{ 2400, 208, 417, 417, 417, }, + //{ 1200, 416, 833, 833, 833,}, +}; + +// +// Private methods +// + +#if 0 +/* static */ +inline void SoftwareSerial::tunedDelay(uint32_t count) { + + asm volatile( + + "mov r3, %[loopsPerMicrosecond] \n\t" //load the initial loop counter + "1: \n\t" + "sub r3, r3, #1 \n\t" + "bne 1b \n\t" + + ://empty output list + :[loopsPerMicrosecond] "r" (count) + :"r3", "cc" //clobber list + ); + +} +#else +inline void SoftwareSerial::tunedDelay(uint32_t count) { + delayMicroseconds(count); +} +#endif + +// This function sets the current object as the "listening" +// one and returns true if it replaces another +bool SoftwareSerial::listen() +{ + if (!_rx_delay_stopbit) + return false; + + if (active_object != this) + { + 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. +bool SoftwareSerial::stopListening() +{ + if (active_object == this) + { + setRxIntMsk(false); + active_object = NULL; + return true; + } + return false; +} + +// +// The receive routine called by the interrupt handler +// +void SoftwareSerial::recv() +{ + 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()) + { + // 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 + for (uint8_t i=8; i > 0; --i) + { + tunedDelay(_rx_delay_intrabit); + d >>= 1; + if (rx_pin_read()) + d |= 0x80; + } + + if (_inverse_logic) + d = ~d; + + // if buffer full, set the overflow flag and return + uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + if (next != _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 = next; + } + else + { + _buffer_overflow = true; + } + tunedDelay(_rx_delay_stopbit); + // Re-enable interrupts when we're sure to be inside the stop bit + setRxIntMsk(true);//__enable_irq();// + + } +} + +uint32_t SoftwareSerial::rx_pin_read() +{ + return digitalRead(_receivePin); +} + +// +// Interrupt handling +// + +/* static */ +inline void SoftwareSerial::handle_interrupt() +{ + if (active_object) + { + active_object->recv(); + } +} +extern "C" void intWrapper() { + SoftwareSerial::handle_interrupt(); +} +// +// Constructor +// +SoftwareSerial::SoftwareSerial(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 +// +SoftwareSerial::~SoftwareSerial() +{ + end(); +} + +void SoftwareSerial::setTX(uint8_t tx) +{ + // 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; + +} + +void SoftwareSerial::setRX(uint8_t rx) +{ + pinMode(rx, INPUT_PULLUP); // pullup for normal logic! + //if (!_inverse_logic) + // digitalWrite(rx, HIGH); + _receivePin = rx; + _receivePort = pin_map[rx].port; + _receivePortPin = pin_map[rx].pin; +/* GPIO_T * rxPort = digitalPinToPort(rx); + _receivePortRegister = portInputRegister(rxPort); + _receiveBitMask = digitalPinToBitMask(rx);*/ + +} + +// +// Public methods +// + +void SoftwareSerial::begin(long speed) +{ + _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; + + for(uint8_t i = 0; i < sizeof(table)/sizeof(table[0]); ++i) + { + long baud = table[i].baud; + if(baud == speed) + { + _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); + +} + +void SoftwareSerial::setRxIntMsk(bool enable) +{ + if (enable) + GpioEnableInt(_receivePort,_receivePin,CHANGE); + else + GpioDisableInt(_receivePort,_receivePin); +} + +void SoftwareSerial::end() +{ + stopListening(); +} + + +// Read data from buffer +int SoftwareSerial::read() +{ + if (!isListening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // 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; +} + +int SoftwareSerial::available() +{ + if (!isListening()) + return 0; + + return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF; +} + +size_t SoftwareSerial::write(uint8_t b) +{ + // 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; + + if(inv) + b = ~b; + + cli(); // turn off interrupts for a clean txmit + + // Write the start bit + if (inv) + digitalWrite(_transmitPin, 1); + else + digitalWrite(_transmitPin, 0); + + tunedDelay(delay); + + // Write each of the 8 bits + for (uint8_t i = 8; i > 0; --i) + { + if (b & 1) // choose bit + digitalWrite(_transmitPin, 1); // send 1 //(GPIO_Desc[_transmitPin].P)->DOUT |= GPIO_Desc[_transmitPin].bit; + else + digitalWrite(_transmitPin, 0); // send 0 //(GPIO_Desc[_transmitPin].P)->DOUT &= ~GPIO_Desc[_transmitPin].bit; + + tunedDelay(delay); + b >>= 1; + } + + // restore pin to natural state + if (inv) + digitalWrite(_transmitPin, 0); + else + digitalWrite(_transmitPin, 1); + + sei(); // turn interrupts back on + tunedDelay(delay); + + return 1; +} + +void SoftwareSerial::flush() +{ + if (!isListening()) + return; + + cli(); + _receive_buffer_head = _receive_buffer_tail = 0; + sei(); +} + +int SoftwareSerial::peek() +{ + if (!isListening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + return _receive_buffer[_receive_buffer_head]; +} diff --git a/Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.h b/Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.h new file mode 100644 index 000000000..e78c59744 --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/SoftwareSerial.h @@ -0,0 +1,119 @@ +/* +SoftwareSerial.h (formerly NewSoftSerial.h) - +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 8MHz 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) +-- 20MHz 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. +*/ + +#ifndef SoftwareSerial_h +#define SoftwareSerial_h + +#include "arduino.h" +#include +//#include "serial.h" + #include +#include + +/****************************************************************************** +* Definitions +******************************************************************************/ + +#define _SS_MAX_RX_BUFF 64 // RX buffer size + +class SoftwareSerial : public Stream +{ +private: + // per object data + uint8_t _receivePin; + uint8_t _transmitPin; +// uint32_t _receiveBitMask; // for rx interrupts + uint32_t _receivePort; + uint32_t _receivePortPin; + + + // Expressed as 4-cycle delays (must never be 0!) + 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 unsigned char _receive_buffer[_SS_MAX_RX_BUFF]; + static volatile uint8_t _receive_buffer_tail; + static volatile uint8_t _receive_buffer_head; + static SoftwareSerial *active_object; + + // private methods + void recv() __attribute__((__always_inline__)); + uint32_t rx_pin_read(); + void tx_pin_write(uint8_t pin_state) __attribute__((__always_inline__)); + void setTX(uint8_t transmitPin); + void setRX(uint8_t receivePin); + void setRxIntMsk(bool enable) __attribute__((__always_inline__)); + + // private static method for timing + static inline void tunedDelay(uint32_t delay); + +public: + // public methods + + SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); + ~SoftwareSerial(); + void begin(long speed); + bool listen(); + void end(); + bool isListening() { return this == active_object; } + bool stopListening(); + bool overflow() { bool ret = _buffer_overflow; if (ret) _buffer_overflow = false; return ret; } + int peek(); + + virtual size_t write(uint8_t byte); + virtual int read(); + virtual int available(); + virtual void flush(); + operator bool() { return true; } + + using Print::write; + //using HalSerial::write; + + // public only for easy access by interrupt handlers + static inline void handle_interrupt() __attribute__((__always_inline__)); +}; + +// Arduino 0012 workaround +#undef int +#undef char +#undef long +#undef byte +#undef float +#undef abs +#undef round + +#endif diff --git a/Marlin/src/HAL/HAL_LPC1768/WInterrupts.cpp b/Marlin/src/HAL/HAL_LPC1768/WInterrupts.cpp new file mode 100644 index 000000000..cfb591b0a --- /dev/null +++ b/Marlin/src/HAL/HAL_LPC1768/WInterrupts.cpp @@ -0,0 +1,224 @@ +/* + Copyright (c) 2011-2012 Arduino. 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 +*/ + +#include "../../../macros.h" +#include "../HAL.h" +#include "arduino.h" +#include "pinmapping.h" +//#include "HAL_timers.h" +#include "fastio.h" + +#define GNUM 31 + +typedef void (*interruptCB)(void); + +static interruptCB callbacksP0[GNUM]; +static interruptCB callbacksP2[GNUM]; + + extern "C" void GpioEnableInt(uint32_t port, uint32_t pin, uint32_t mode); + extern "C" void GpioDisableInt(uint32_t port, uint32_t pin); + +//void deadloop(void) {} + +/* Configure PIO interrupt sources */ +static void __initialize() { + int i; + for (i=0; iIO0IntClr = (1 << pin); + if (mode ==RISING) { + LPC_GPIOINT->IO0IntEnR |= (1<IO0IntEnF &= ~(1<IO0IntEnF |= (1<IO0IntEnR &= ~(1<IO0IntEnR |= (1<IO0IntEnF |= (1<IO2IntClr = (1 << pin); + if (mode ==RISING) { + LPC_GPIOINT->IO2IntEnR |= (1<IO2IntEnF &= ~(1<IO2IntEnF |= (1<IO2IntEnR &= ~(1<IO2IntEnR |= (1<IO2IntEnF |= (1<IO0IntEnR &= ~(1<IO0IntEnF &= ~(1<IO0IntClr = 1 << pin; + } + else { + LPC_GPIOINT->IO2IntEnR &= ~(1<IO2IntEnF &= ~(1<IO2IntClr = 1 << pin; + } +} + +bool isPowerOf2(unsigned int n) + +{ + + return n == 1 || (n & (n-1)) == 0; + +} + +#if 0 +extern "C" void EINT3_IRQHandler () { + LPC_GPIOINT->IO0IntClr = LPC_GPIOINT->IO2IntClr = 0xFFFFFFFF; + TOGGLE(13); + //NVIC_ClearPendingIRQ(EINT3_IRQn); +} +#else +extern "C" void EINT3_IRQHandler(void) +{ + // Read in all current interrupt registers. We do this once as the +// GPIO interrupt registers are on the APB bus, and this is slow. +uint32_t rise0 = LPC_GPIOINT->IO0IntStatR; +uint32_t fall0 = LPC_GPIOINT->IO0IntStatF; +uint32_t rise2 = LPC_GPIOINT->IO2IntStatR; +uint32_t fall2 = LPC_GPIOINT->IO2IntStatF; +//Clear teh interrupts ASAP +LPC_GPIOINT->IO0IntClr = LPC_GPIOINT->IO2IntClr = 0xFFFFFFFF; +NVIC_ClearPendingIRQ(EINT3_IRQn); +uint8_t bitloc; +if (rise0 == 0) + goto fall0; + /* multiple pins changes happened.*/ + while(rise0 > 0) { //Continue as long as there are interrupts pending + bitloc = 31 - __CLZ(rise0); //CLZ returns number of leading zeros, 31 minus that is location of first pending interrupt + if (callbacksP0[bitloc]!=0) + callbacksP0[bitloc](); + rise0 -= 1<IO0IntClr = fall0;*/ +else { +while(fall0 > 0) { + bitloc = 31 - __CLZ(fall0); + if (callbacksP0[bitloc]!=0) + callbacksP0[bitloc](); + fall0 -= 1<IO2IntClr = rise2; +}*/ +else { +while(rise2 > 0) { + bitloc = 31 - __CLZ(rise2); + if (callbacksP2[bitloc]!=0) + callbacksP2[bitloc](); + //LPC_GPIOINT->IO2IntClr = 1 << bitloc; + rise2 -= 1<IO2IntClr = fall2; +}*/ +else { +while(fall2 > 0) { + bitloc = 31 - __CLZ(fall2); + if (callbacksP2[bitloc]!=0) + callbacksP2[bitloc](); + //LPC_GPIOINT->IO2IntClr = 1 << bitloc; + fall2 -= 1<IO0IntClr = LPC_GPIOINT->IO2IntClr = 0xFFFFFFFF; +//NVIC_ClearPendingIRQ(EINT3_IRQn); +return; //silences warning +} +} +#endif diff --git a/Marlin/src/HAL/HAL_LPC1768/arduino.h b/Marlin/src/HAL/HAL_LPC1768/arduino.h index a8a67d5c7..f34e8217a 100644 --- a/Marlin/src/HAL/HAL_LPC1768/arduino.h +++ b/Marlin/src/HAL/HAL_LPC1768/arduino.h @@ -28,6 +28,9 @@ #define LOW 0x00 #define HIGH 0x01 +#define CHANGE 0x02 +#define FALLING 0x03 +#define RISING 0x04 #define INPUT 0x00 #define OUTPUT 0x01 @@ -64,6 +67,10 @@ typedef uint8_t byte; //Interrupts void cli(void); // Disable void sei(void); // Enable +void attachInterrupt(uint32_t pin, void (*callback)(void), uint32_t mode); +void detachInterrupt(uint32_t pin); +extern "C" void GpioEnableInt(uint32_t port, uint32_t pin, uint32_t mode); +extern "C" void GpioDisableInt(uint32_t port, uint32_t pin); // Program Memory #define pgm_read_ptr(address_short) (*(address_short)) diff --git a/Marlin/src/HAL/HAL_LPC1768/pinmap_re_arm.h b/Marlin/src/HAL/HAL_LPC1768/pinmap_re_arm.h index 65c57ada4..2726fdefe 100644 --- a/Marlin/src/HAL/HAL_LPC1768/pinmap_re_arm.h +++ b/Marlin/src/HAL/HAL_LPC1768/pinmap_re_arm.h @@ -95,6 +95,50 @@ const adc_pin_data adc_pin_map[] = { r == 54 ? 1 :\ r == 60 ? 1 : 0) +#define NUM_INTERRUPT_PINS 35 + +#define INTERRUPT_PIN(r) ( r< 0 ? 0 :\ + r == 0 ? 1 :\ + r == 1 ? 1 :\ + r == 8 ? 1 :\ + r == 9 ? 1 :\ + r == 10 ? 1 :\ + r == 12 ? 1 :\ + r == 16 ? 1 :\ + r == 20 ? 1 :\ + r == 21 ? 1 :\ + r == 24 ? 1 :\ + r == 25 ? 1 :\ + r == 26 ? 1 :\ + r == 28 ? 1 :\ + r == 34 ? 1 :\ + r == 35 ? 1 :\ + r == 36 ? 1 :\ + r == 38 ? 1 :\ + r == 46 ? 1 :\ + r == 48 ? 1 :\ + r == 50 ? 1 :\ + r == 51 ? 1 :\ + r == 52 ? 1 :\ + r == 54 ? 1 :\ + r == 55 ? 1 :\ + r == 56 ? 1 :\ + r == 57 ? 1 :\ + r == 58 ? 1 :\ + r == 59 ? 1 :\ + r == 60 ? 1 :\ + r == 61 ? 1 :\ + r == 62 ? 1 :\ + r == 63 ? 1 :\ + r == 67 ? 1 :\ + r == 68 ? 1 :\ + r == 69 ? 1 : 0) + /*Internal SD Card */ + /*r == 80 ? 1 :\ + r == 81 ? 1 :\ + r == 82 ? 1 :\ + r == 83 ? 1 :\*/ + const pin_data pin_map[] = { // pin map for variable pin function {0,3}, // DIO0 RXD0 A6 J4-4 AUX-1 {0,2}, // DIO1 TXD0 A7 J4-5 AUX-1