diff --git a/README.md b/README.md index 5349529..dc00dfb 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ This repository allows you to program Freedom E300 boards using the Arduino IDE. You can install this repository in two ways: -* Using the Arduino Boards Manager (Currently supported for Linux and macOS) to download precompiled binaries -* Manually compiling the tools (Suggested for platforms not supported by the above). +* Using the Arduino Boards Manager to download precompiled binaries. +* Manually compiling the tools. Follow the instructions below to install the Board support package. @@ -16,25 +16,26 @@ Please see the Getting Started Guides for more information on how to install and # Setup # -## Install Arduino ## +## For Windows istallation ## -Download and install Arduino IDE 1.6.12 tarball from the Arduino website. Unpack it and run their installation script as directed. +Follow the insructions in the [https://github.com/westerndigitalcorporation/CincoWinPkg/blob/master/README.md](https://github.com/westerndigitalcorporation/CincoWinPkg/blob/master/README.md) +## For Linux and MacOS installation ## +### Install Arduino ### -## Install the SiFive Boards ## +Download and install Arduino IDE 1.6.12 or later tarball from the Arduino website. Unpack it and run their installation script as directed. -Use one of the following methods: - -### Option 1: Installing Through the Arduino IDE ### +### Install the SiFive Boards ### -This is supported for macOS and Linux. +Use one of the following methods: -Add the [http://static.dev.sifive.com/bsp/arduino/package_sifive_index.json](http://static.dev.sifive.com/bsp/arduino/package_sifive_index.json) to the Additional Board URLs. +#### Option 1: Installing Through the Arduino IDE #### -Use the Board Manager to search for and install the "SiFive" boards. +Add the [https://raw.githubusercontent.com/westerndigitalcorporation/CincoWinPkg/master/package_sifive_index.json](https://raw.githubusercontent.com/westerndigitalcorporation/CincoWinPkg/master/package_sifive_index.json) +to the Additional Boards Manager URLs in the `Preferences->Settings`. -### Option 2: Install this Repo Manually ### +#### Option 2: Install this Repo Manually #### -This is generally not supported. You can use this technique to install on platforms that aren't supported by the Board Manager, or if you want to work on the code in this repository. +** This is generally not supported. ** You can use this technique if you want to work on the code in this repository. 1. Clone this Repository @@ -68,31 +69,27 @@ This is generally not supported. You can use this technique to install on platfo If you installed the Freedom E SDK some other way, use that installation location instead. -# Select Your Board # +### Select Your board: ### -Restart and launch the Arduino IDE. +Select the board in the Arduino Menu `Tools->Board->HiFive1` -Select the board (e.g. Freedom E300 Arty Dev Kit) on the Arduino Menu - -Tools->Board->Freedom E 300 Dev Kit - -# Select Your Toolchain # +### Select Your Toolchain ### If you installed the tools using the Arduino Package Manager, select `Tools -> Tool Install Location -> Default`. -If you compiled the Freedom E SDK manually, -select `Tools -> Tool Install Location -> Manual`. +If you compiled the Freedom E SDK manually, select +`Tools -> Tool Install Location -> Manual`. -# Select OpenOCD as the Programmer # +### Select OpenOCD as the Programmer ### -If you installed the tools using the Arduino Package Manager, -select `Tools->Programmer->SiFive OpenOCD` +If you installed the tools using the Arduino Package Manager, select +`Tools->Programmer->SiFive OpenOCD` If you installed the tools manually, select `Tools->Programmer-> Manual SiFive OpenOCD` -# Write & Upload Your Program # +### Write & Upload Your Program ### Select an example program and modify it as usual. diff --git a/hardware/freedom_e/boards.txt b/hardware/freedom_e/boards.txt index c80e9e7..1bad39d 100644 --- a/hardware/freedom_e/boards.txt +++ b/hardware/freedom_e/boards.txt @@ -7,7 +7,7 @@ freedom_e310_arty_devkit.name=Freedom E300 Arty DevKit freedom_e310_arty_devkit.menu.toolsloc.default=Default freedom_e310_arty_devkit.menu.toolsloc.manual=Manual -freedom_e310_arty_devkit.menu.toolsloc.default.compiler.path={runtime.tools.riscv32-unknown-elf-gcc.path}/bin/ +freedom_e310_arty_devkit.menu.toolsloc.default.compiler.path={runtime.tools.riscv64-unknown-elf-gcc.path}/bin/ freedom_e310_arty_devkit.menu.toolsloc.manual.compiler.path= #Point to the file for ./variants//pins_arduino.h @@ -17,6 +17,8 @@ freedom_e310_arty_devkit.menu.clksrc.fpga=65MHz FPGA Clock freedom_e310_arty_devkit.menu.clksrc.fpga.build.f_cpu=65000000L freedom_e310_arty_devkit.build.mcu=rv32imac +freedom_e310_arty_devkit.build.mabi=ilp32 +freedom_e310_arty_devkit.build.mcmodel=medany #"The 'core' file directory for this board, in ./cores freedom_e310_arty_devkit.build.core=arduino @@ -36,7 +38,7 @@ hifive1.name=HiFive 1 hifive1.menu.toolsloc.default=Default hifive1.menu.toolsloc.manual=Manual -hifive1.menu.toolsloc.default.compiler.path={runtime.tools.riscv32-unknown-elf-gcc.path}/bin/ +hifive1.menu.toolsloc.default.compiler.path={runtime.tools.riscv64-unknown-elf-gcc.path}/bin/ hifive1.menu.toolsloc.manual.compiler.path= hifive1.menu.clksrc.hfxtal=16 MHz XTAL @@ -51,6 +53,8 @@ hifive1.menu.clksrc.hfrosc.build.f_cpu=320000000L hifive1.build.variant=standard hifive1.build.mcu=rv32imac +hifive1.build.mabi=ilp32 +hifive1.build.mcmodel=medany #"The 'core' file directory for this board, in ./cores hifive1.build.core=arduino diff --git a/hardware/freedom_e/cores/arduino/Arduino.h b/hardware/freedom_e/cores/arduino/Arduino.h index 776b4af..d4f6263 100644 --- a/hardware/freedom_e/cores/arduino/Arduino.h +++ b/hardware/freedom_e/cores/arduino/Arduino.h @@ -68,6 +68,10 @@ typedef void (*voidFuncPtr)( void ) ; #include "HardwareSerial.h" #include "wiring_pulse.h" +// Tone function prototypes +void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); +void noTone(uint8_t _pin); + #endif // __cplusplus // Include board variant diff --git a/hardware/freedom_e/cores/arduino/Stream.cpp b/hardware/freedom_e/cores/arduino/Stream.cpp new file mode 100644 index 0000000..d284631 --- /dev/null +++ b/hardware/freedom_e/cores/arduino/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 "Arduino.h" +#include "Stream.h" + +#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait + +// protected 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 +} + +// protected 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(LookaheadMode lookahead, bool detectDecimal) +{ + int c; + while (1) { + c = timedPeek(); + + if( c < 0 || + c == '-' || + (c >= '0' && c <= '9') || + (detectDecimal && c == '.')) return c; + + switch( lookahead ){ + case SKIP_NONE: return -1; // Fail code. + case SKIP_WHITESPACE: + switch( c ){ + case ' ': + case '\t': + case '\r': + case '\n': break; + default: return -1; // Fail code. + } + case SKIP_ALL: + break; + } + 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. +// lookahead determines how parseInt looks ahead in the stream. +// See LookaheadMode enumeration at the top of the file. +// Lookahead is terminated by the first character that is not a valid part of an integer. +// Once parsing commences, 'ignore' will be skipped in the stream. +long Stream::parseInt(LookaheadMode lookahead, char ignore) +{ + bool isNegative = false; + long value = 0; + int c; + + c = peekNextDigit(lookahead, false); + // ignore non numeric leading characters + if(c < 0) + return 0; // zero returned if timeout + + do{ + if(c == ignore) + ; // ignore this character + 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 == ignore ); + + if(isNegative) + value = -value; + return value; +} + +// as parseInt but returns a floating point value +float Stream::parseFloat(LookaheadMode lookahead, char ignore) +{ + bool isNegative = false; + bool isFraction = false; + long value = 0; + int c; + float fraction = 1.0; + + c = peekNextDigit(lookahead, true); + // ignore non numeric leading characters + if(c < 0) + return 0; // zero returned if timeout + + do{ + if(c == ignore) + ; // 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 == '.' && !isFraction) || c == ignore ); + + 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/hardware/freedom_e/cores/arduino/Stream.h b/hardware/freedom_e/cores/arduino/Stream.h index 5cf5ddf..8e950c7 100644 --- a/hardware/freedom_e/cores/arduino/Stream.h +++ b/hardware/freedom_e/cores/arduino/Stream.h @@ -28,34 +28,45 @@ // compatability macros for testing /* #define getInt() parseInt() -#define getInt(skipChar) parseInt(skipchar) +#define getInt(ignore) parseInt(ignore) #define getFloat() parseFloat() -#define getFloat(skipChar) parseFloat(skipChar) +#define getFloat(ignore) parseFloat(ignore) #define getString( pre_string, post_string, buffer, length) readBytesBetween( pre_string, terminator, buffer, length) */ +// This enumeration provides the lookahead options for parseInt(), parseFloat() +// The rules set out here are used until either the first valid character is found +// or a time out occurs due to lack of input. +enum LookaheadMode{ + SKIP_ALL, // All invalid characters are ignored. + SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid. + SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped. +}; + +#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field + 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 + int timedRead(); // read stream with timeout + int timedPeek(); // peek stream with timeout + int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // 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 - + unsigned long getTimeout(void) { return _timeout; } + 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) @@ -64,18 +75,23 @@ class Stream : public Print 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(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR); + // returns the first valid (long) integer value from the current position. + // lookahead determines how parseInt looks ahead in the stream. + // See LookaheadMode enumeration at the top of the file. + // Lookahead is terminated by the first character that is not a valid part of an integer. + // Once parsing commences, 'ignore' will be skipped in the stream. - 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 + float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR); + // 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); } @@ -92,11 +108,22 @@ class Stream : public Print 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 + long parseInt(char ignore) { return parseInt(SKIP_ALL, ignore); } + float parseFloat(char ignore) { return parseFloat(SKIP_ALL, ignore); } + // These overload exists for compatibility with any class that has derived + // Stream and used parseFloat/Int with a custom ignore character. To keep + // the public API simple, these overload remains protected. + + 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); }; +#undef NO_IGNORE_CHAR #endif diff --git a/hardware/freedom_e/cores/arduino/Tone.cpp b/hardware/freedom_e/cores/arduino/Tone.cpp new file mode 100644 index 0000000..84db931 --- /dev/null +++ b/hardware/freedom_e/cores/arduino/Tone.cpp @@ -0,0 +1,122 @@ +/* + A Tone Generator Library for the HiFive1 + + Copyright (c) 2018 Western Digital Corporation or its affiliates. + + 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 + + SPDX-License-Identifier: LGPL-2.1-only + + This is a port of the Arduino Tone library + https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Tone.cpp + Copyright (c) 2015 Brett Hagman + under GNU Lesser General Public License v2.1 or later (LGPL-2.1-or-later) +*/ + +#include "Arduino.h" +#include "encoding.h" + +// Set scaling exponent based on clk to support 20hz-20khz human hearing range +#if F_CPU == 16000000L + #define PWM_SCALE_16 0x6 // 4-125000 Hz, precision loss after D10 (18795 Hz) + #define PWM_SCALE_8 0x8 // 246-31250 Hz, precision loss after D8 (4699 Hz) +#elif F_CPU == 256000000L + #define PWM_SCALE_16 0xA // 4-125000 Hz + #define PWM_SCALE_8 0xC // 246-31250 Hz +#elif F_CPU == 320000000L + #define PWM_SCALE_16 0xA // 5-156250 Hz, precision loss after G10 (25088 Hz) + #define PWM_SCALE_8 0xC // 307-39062 Hz, precision loss after G8 (6272 Hz) +#else + #define PWM_SCALE_16 0x0 + #define PWM_SCALE_8 0x0 +#endif + +// Lookup table of comparator offsets +static uint8_t pwm_cmp_offsets[4] = { + PWM_CMP0, + PWM_CMP1, + PWM_CMP2, + PWM_CMP3 +}; + +// Which PWM pin currently has an active duration counting down +static volatile uint8_t timed_pin = 0; + +void noTone(uint8_t pin) { + uint32_t pwm_base_addr = (uint32_t) variant_pwm[variant_pin_map[pin].pwm_num]; + uint8_t pwm_cmp_offset = pwm_cmp_offsets[variant_pin_map[pin].pwm_cmp_num]; + + _REG32(pwm_base_addr, PWM_CFG) = 0; + _REG32(pwm_base_addr, PWM_COUNT) = 0; + _REG32(pwm_base_addr, PWM_CMP0) = 0; + _REG32(pwm_base_addr, pwm_cmp_offset) = 0; + GPIO_REG(GPIO_IOF_SEL) &= ~digitalPinToBitMask(pin); + GPIO_REG(GPIO_IOF_EN) &= ~digitalPinToBitMask(pin); +} + +static void tone_duration_isr() { + AON_REG(AON_RTCCMP) = 0xffffffff; // Clear interrupt by maxing out comparator + AON_REG(AON_RTCCFG) &= ~(1 << 12); // Clear rtcenalways + noTone(timed_pin); + timed_pin = 0; +} + +void tone(uint8_t pin, unsigned int frequency, unsigned long duration) { + uint8_t pwm_num = variant_pin_map[pin].pwm_num; + uint8_t pwm_cmp_num = variant_pin_map[pin].pwm_cmp_num; + // Do nothing if not a PWM pin, or frequency is invalid + if (pwm_num > variant_pwm_size || pwm_cmp_num == 0 || frequency == 0) { + return; + } + + if (duration > 0) { + // If timer is already active for another pin, stop the tone on that pin + if (timed_pin != 0) { + noTone(timed_pin); + } + // If rtcenalways set, RTC probably in use, so only start timer if cleared + if (!(AON_REG(AON_RTCCFG) & (1 << 12))) { + AON_REG(AON_RTCHI) = 0; + AON_REG(AON_RTCLO) = 0; + // Equivalent to round(32.768 * duration). Max duration is 131071999ms + AON_REG(AON_RTCCMP) = (uint32_t) ((32768ULL * duration + 500) / 1000); + AON_REG(AON_RTCCFG) = 1 << 12; // RTC counter enable (rtcenalways) + + timed_pin = pin; + attachInterrupt(INT_RTCCMP, tone_duration_isr, RISING); + set_csr(mstatus, MSTATUS_MIE); + } + } + + uint8_t scale = (pin >= 9 && pin <= 11) ? PWM_SCALE_8 : PWM_SCALE_16; + // Equivalent to round((F_CPU >> scale) / frequency) + uint16_t threshold = ((F_CPU >> scale) + frequency / 2) / frequency; + + uint32_t pwm_base_addr = (uint32_t) variant_pwm[pwm_num]; + uint8_t pwm_cmp_offset = pwm_cmp_offsets[pwm_cmp_num]; + + // Set I/O function to PWM + GPIO_REG(GPIO_IOF_SEL) |= digitalPinToBitMask(pin); + GPIO_REG(GPIO_IOF_EN) |= digitalPinToBitMask(pin); + + // PWMx_REG(offset) is a nicer macro but we need to pass in PWM base addrs + // Set PWM registers with CMP0 as the period, and CMPx as the square wave + _REG32(pwm_base_addr, PWM_CFG) = 0; + _REG32(pwm_base_addr, PWM_COUNT) = 0; + _REG32(pwm_base_addr, PWM_CFG) = (PWM_CFG_ZEROCMP | PWM_CFG_ENALWAYS | scale); + // Set the period on CMP0, and 50% duty cycle on other comparator + _REG32(pwm_base_addr, PWM_CMP0) = (uint16_t) threshold; + _REG32(pwm_base_addr, pwm_cmp_offset) = (uint16_t) (threshold / 2); +} \ No newline at end of file diff --git a/hardware/freedom_e/cores/arduino/main.cpp b/hardware/freedom_e/cores/arduino/main.cpp index 99708ae..831071d 100644 --- a/hardware/freedom_e/cores/arduino/main.cpp +++ b/hardware/freedom_e/cores/arduino/main.cpp @@ -35,7 +35,6 @@ uint32_t mtime_lo(void) } - static void freedom_e300_clock_setup () { // This is a very coarse parameterization. To revisit in the future @@ -59,29 +58,38 @@ static void freedom_e300_clock_setup () { // We make no effort in this code to make the SPI // faster for a slow clock. - if (F_CPU == 256000000UL) { - + if (F_CPU == 320000000UL) { + + PRCI_use_pll(1, //Use HFXTAL as the reference + 0, // Bypass = 0, really use the PLL + 1, // Set DIVR to divide-by-2 to get 8MHz frequency + 0x27, // Set DIVF to get 640Mhz frequency + 1, // Set DIVQ to divide-by-2 to get 320 MHz frequency + 1, // Set final divider to div-by-1. + -1, // We don't care about HFROSC + -1); + + } else if (F_CPU == 256000000UL) { + PRCI_use_pll(1, //Use HFXTAL as the reference - 0, // Bypass = 0, really use the PLL - 1, // Set DIVR to divide-by-2 to get 8MHz frequency - 0x1F, // Set DIVF to get 512Mhz frequency - 1, // Set DIVQ to divide-by-2 to get 256 MHz frequency - 1, // Set final divider to div-by-1. - -1, // We don't care about HFROSC - -1); - - - } else if (F_CPU == 16000000UL) { - + 0, // Bypass = 0, really use the PLL + 1, // Set DIVR to divide-by-2 to get 8MHz frequency + 0x1F, // Set DIVF to get 512Mhz frequency + 1, // Set DIVQ to divide-by-2 to get 256 MHz frequency + 1, // Set final divider to div-by-1. + -1, // We don't care about HFROSC + -1); + + } else if (F_CPU == 16000000UL) { + PRCI_use_hfxosc(1); - + } else { PRCI_set_hfrosctrim_for_f_cpu(F_CPU, PRCI_FREQ_CLOSEST); } #endif - } void freedom_e300_specific_initialization(void) @@ -92,9 +100,9 @@ void freedom_e300_specific_initialization(void) write_csr(mstatus, MSTATUS_FS); // allow FPU instructions without trapping write_csr(fcsr, 0); // initialize rounding mode, undefined at reset } - + freedom_e300_clock_setup(); - + } /* @@ -108,12 +116,12 @@ int main( void ) calc_inv(F_CPU/1000000, &f_cpu_1000000_inv); freedom_e300_specific_initialization(); setup(); - + do { loop(); if (serialEventRun) serialEventRun(); } while (1); - + return 0; } diff --git a/hardware/freedom_e/cores/arduino/wiring.c b/hardware/freedom_e/cores/arduino/wiring.c index 5a3bdb9..917cbb7 100644 --- a/hardware/freedom_e/cores/arduino/wiring.c +++ b/hardware/freedom_e/cores/arduino/wiring.c @@ -40,14 +40,14 @@ const uint32_t variant_pwm_size = sizeof(variant_pwm) / sizeof(uint32_t*); int_inverse f_cpu_1000_inv; int_inverse f_cpu_1000000_inv; -void calc_inv(uint32_t n, int_inverse * res){ +void calc_inv(uint32_t n, int_inverse * res) { uint32_t one = ~0; uint32_t d = one/n; uint32_t r = one%n + 1; if (r >= n) ++d; if (d == 0) --d; uint32_t shift = 0; - while ((d & 0x80000000) == 0){ + while ((d & 0x80000000) == 0) { d <<= 1; ++shift; } @@ -56,7 +56,7 @@ void calc_inv(uint32_t n, int_inverse * res){ res->shift = shift; } -inline uint32_t divide32_using_inverse(uint32_t n, int_inverse *inv){ +inline uint32_t divide32_using_inverse(uint32_t n, int_inverse *inv) { uint32_t d = (uint32_t)(((uint64_t)n * inv->mult) >> 32); d >>= inv->shift; @@ -69,7 +69,7 @@ inline uint32_t divide32_using_inverse(uint32_t n, int_inverse *inv){ // e.g. for divisors up to a million, n can have up to 45 bits // On RV32IM with divide32_using_inverse inlines this uses 5 multiplies, // 33 instructions, zero branches, 3 loads, 0 stores. -uint64_t divide64_using_inverse(uint64_t n, int_inverse *inv){ +uint64_t divide64_using_inverse(uint64_t n, int_inverse *inv) { uint32_t preshift = (31 - inv->shift) & 31; uint64_t d = (uint64_t)divide32_using_inverse(n >> preshift, inv) << preshift; uint32_t r = n - d * inv->n; @@ -78,18 +78,14 @@ uint64_t divide64_using_inverse(uint64_t n, int_inverse *inv){ } -uint32_t -millis() -{ +uint32_t millis() { uint64_t x; rdmcycle(&x); x = divide64_using_inverse(x, &f_cpu_1000_inv); return((uint32_t) (x & 0xFFFFFFFF)); } -uint32_t -micros(void) -{ +uint32_t micros(void) { uint64_t x; rdmcycle(&x); // For Power-of-two MHz F_CPU, @@ -98,7 +94,7 @@ micros(void) #if F_CPU==16000000 x = x / (F_CPU / 1000000); #else -#if F_CPU==256000000 +#if F_CPU==256000000 x = x / (F_CPU / 1000000); #else x = divide64_using_inverse(x, &f_cpu_1000000_inv); @@ -109,24 +105,10 @@ micros(void) void -delay(uint32_t dwMs) -{ +delay(uint32_t dwMs) { uint64_t current, later; rdmcycle(¤t); later = current + dwMs * (F_CPU/1000); - if (later > current) // usual case - { - while (later > current) { - rdmcycle(¤t); - } - } - else // wrap. Though this is unlikely to be hit w/ 64-bit mcycle - { - while (later < current) { - rdmcycle(¤t); - } - while (current < later) { - rdmcycle(¤t); - } - } + while (later > current) + rdmcycle(¤t); } diff --git a/hardware/freedom_e/cores/arduino/wiring.h b/hardware/freedom_e/cores/arduino/wiring.h index 546c6f6..72b28ab 100644 --- a/hardware/freedom_e/cores/arduino/wiring.h +++ b/hardware/freedom_e/cores/arduino/wiring.h @@ -22,11 +22,8 @@ __BEGIN_DECLS -/** - * - */ -extern void initVariant( void ) ; -extern void init( void ) ; +extern void initVariant(void); +extern void init(void); struct variant_pin_map_s { uint8_t io_port; @@ -45,7 +42,7 @@ typedef struct { uint32_t n; uint32_t mult; uint32_t shift; -} int_inverse ; +} int_inverse; extern int_inverse f_cpu_1000_inv; extern int_inverse f_cpu_1000000_inv; @@ -55,30 +52,55 @@ void calc_inv(uint32_t n, int_inverse * res); uint32_t divide32_using_inverse(uint32_t n, int_inverse *inv); uint64_t divide64_using_inverse(uint64_t n, int_inverse *inv); -#define rdmcycle(x) { \ - uint32_t lo, hi, hi2; \ - __asm__ __volatile__ ("1:\n\t" \ - "csrr %0, mcycleh\n\t" \ - "csrr %1, mcycle\n\t" \ - "csrr %2, mcycleh\n\t" \ - "bne %0, %2, 1b\n\t" \ - : "=r" (hi), "=r" (lo), "=r" (hi2)) ; \ - *(x) = lo | ((uint64_t) hi << 32); \ - } +#define rdmcycle(x) { \ + uint32_t lo, hi, hi2; \ + __asm__ __volatile__ ("1:\n\t" \ + "csrr %0, mcycleh\n\t" \ + "csrr %1, mcycle\n\t" \ + "csrr %2, mcycleh\n\t" \ + "bne %0, %2, 1b\n\t" \ + : "=r" (hi), "=r" (lo), "=r" (hi2)) ; \ + *(x) = lo | ((uint64_t) hi << 32); \ +} + +/** + * Use asm to prevent GCC from performing loop unrolling even though the flags + * corresponding to this behaviour explicitly say don't do so. Otherwise, innocent + * call to the inlined delayMicroseconds(10) is expressed in about 0.35KB of + * code size. + */ +#define usdelay16mz(usec) { \ + uint32_t cnt = usec; \ + __asm__ __volatile__ ("1:\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "addi %[cnt], %[cnt], -1\n\t" \ + "bnez %[cnt], 1b\n\t" \ + : [cnt] "+r" (cnt)); \ +} /** * \brief Returns the number of milliseconds since the board began running the current program. * * \return Number of milliseconds since the program started (uint32_t) */ -extern uint32_t millis( void ) ; +extern uint32_t millis(void); /** * \brief Returns the number of microseconds since the board began running the current program. * * \note There are 1,000 microseconds in a millisecond and 1,000,000 microseconds in a second. */ -extern uint32_t micros( void ) ; +extern uint32_t micros(void); /** * \brief Pauses the program for the amount of time (in miliseconds) specified as parameter. @@ -86,7 +108,7 @@ extern uint32_t micros( void ) ; * * \param dwMs the number of milliseconds to pause (uint32_t) */ -extern void delay( uint32_t dwMs ) ; +extern void delay(uint32_t dwMs); /** * \brief Pauses the program for the amount of time (in microseconds) specified as parameter. @@ -99,25 +121,64 @@ static inline void delayMicroseconds(uint32_t usec) { if (usec == 0) { return; } - // TODO: Short delays at low frequencies. uint64_t current, later; - rdmcycle(¤t); - later = current + usec * (F_CPU/1000000); - if (later > current) // usual case - { - while (later > current) { - rdmcycle(¤t); - } +#if F_CPU==16000000 + if (usec <= 4) { + /** + * The rdmcycle() takes 10 cycles or ~630ns to complete at 16MHz CPU clk. + * Together with 64bit arithmetics it led to low accuracy and high jitter + * for below 4 usec delay values. Replacing the rdmcycle() polling with + * "nop" sequence improved the situation. + * + * Test results (excluding GPIO overhead of about 1.3us x 2 when measured + * with "GPIO_REG(GPIO_OUTPUT_VAL) ^= (1 << PIN_3_OFFSET)") + * + * usec "nop" "rdmcycle()" "rdmcycle() w/o overhead" + * 1 1.13 1.6 - 2.2 1.6 - 2.2 + * 2 2.3 2.9 - 3.1 2.2 - 2.4 + * 3 3.1 4.07 2.9 - 3.5 + * 4 4 5.2 3.8 - 4 + * 5 4.8 5.8 5.2 + * 10 9 10.8 10.2 + */ + usdelay16mz(usec); + } else { +#endif + rdmcycle(¤t); + // Minus 12 cycles to compensate rdmcycle() loop overhead + later = current + usec * (F_CPU/1000000) - 12; + while (later > current) + rdmcycle(¤t); +#if F_CPU==16000000 + } +#endif +} + +#define rdcyclelo() ({ \ + uint32_t lo; \ + __asm__ __volatile__ ("csrr %0, mcycle\n\t" \ + : "=r" (lo)); \ + lo; \ +}) + +static inline void delayCycles(uint32_t) __attribute__((always_inline, unused)); +static inline void delayCycles(uint32_t cycles) { + volatile uint32_t current; + uint32_t later; + current = rdcyclelo(); + later = current + cycles; + if (later > current) { // usual case + while (later > current) { + current = rdcyclelo(); + } + } else { // wrap around case + while (later < current) { + current = rdcyclelo(); } - else // wrap. Though this is unlikely to be hit w/ 64-bit mcycle - { - while (later < current) { - rdmcycle(¤t); - } - while (current < later) { - rdmcycle(¤t); - } + while (current < later) { + current = rdcyclelo(); } + } } __END_DECLS diff --git a/hardware/freedom_e/cores/arduino/wiring_constants.h b/hardware/freedom_e/cores/arduino/wiring_constants.h index f66135a..037db46 100644 --- a/hardware/freedom_e/cores/arduino/wiring_constants.h +++ b/hardware/freedom_e/cores/arduino/wiring_constants.h @@ -19,6 +19,8 @@ #ifndef _WIRING_CONSTANTS_ #define _WIRING_CONSTANTS_ +#include "encoding.h" + __BEGIN_DECLS #define HIGH 0x1 @@ -43,8 +45,6 @@ enum BitOrder { MSBFIRST = 1 }; -// LOW 0 -// HIGH 1 #define CHANGE 2 #define FALLING 3 #define RISING 4 @@ -72,8 +72,8 @@ enum BitOrder { #define degrees(rad) ((rad)*RAD_TO_DEG) #define sq(x) ((x)*(x)) -#define interrupts() __enable_irq() -#define noInterrupts() __disable_irq() +#define interrupts() set_csr(mie, MIP_MEIP | MIP_MTIP | MIP_MSIP) +#define noInterrupts() clear_csr(mie, MIP_MEIP | MIP_MTIP | MIP_MSIP) #define lowByte(w) ((uint8_t) ((w) & 0xff)) #define highByte(w) ((uint8_t) ((w) >> 8)) diff --git a/hardware/freedom_e/cores/arduino/wiring_digital.c b/hardware/freedom_e/cores/arduino/wiring_digital.c index 0c89372..0ea4fa8 100644 --- a/hardware/freedom_e/cores/arduino/wiring_digital.c +++ b/hardware/freedom_e/cores/arduino/wiring_digital.c @@ -34,18 +34,17 @@ pinMode(uint32_t pin, uint32_t mode) } } - void digitalWrite(uint32_t pin, uint32_t val) { if (pin >= variant_pin_map_size) return; - + + uint32_t bitmask = digitalPinToBitMask(pin); if (val) - GPIO_REG(GPIO_OUTPUT_VAL) |= digitalPinToBitMask(pin); + GPIO_REG(GPIO_OUTPUT_VAL) |= bitmask; else - GPIO_REG(GPIO_OUTPUT_VAL) &= ~digitalPinToBitMask(pin); - + GPIO_REG(GPIO_OUTPUT_VAL) &= ~bitmask; } int diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp b/hardware/freedom_e/libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp new file mode 100644 index 0000000..10eae3c --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp @@ -0,0 +1,2318 @@ +/*------------------------------------------------------------------------- + Arduino library to control a wide variety of WS2811- and WS2812-based RGB + LED devices such as Adafruit FLORA RGB Smart Pixels and NeoPixel strips. + Currently handles 400 and 800 KHz bitstreams on 8, 12 and 16 MHz ATmega + MCUs, with LEDs wired for various color orders. Handles most output pins + (possible exception with upper PORT registers on the Arduino Mega). + + Written by Phil Burgess / Paint Your Dragon for Adafruit Industries, + contributions by PJRC, Michael Miller and other members of the open + source community. + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing products + from Adafruit! + + ------------------------------------------------------------------------- + This file is part of the Adafruit NeoPixel library. + + NeoPixel 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 3 of + the License, or (at your option) any later version. + + NeoPixel 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 NeoPixel. If not, see + . + -------------------------------------------------------------------------*/ + +#include "Adafruit_NeoPixel.h" + +#if defined(NRF52) +#include "nrf.h" + +// Interrupt is only disabled if there is no PWM device available +// Note: Adafruit Bluefruit nrf52 does not use this option +//#define NRF52_DISABLE_INT +#endif + +// Constructor when length, pin and type are known at compile-time: +Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint8_t p, neoPixelType t) : + begun(false), brightness(0), pixels(NULL), endTime(0) +{ + updateType(t); + updateLength(n); + setPin(p); +} + +// via Michael Vogt/neophob: empty constructor is used when strand length +// isn't known at compile-time; situations where program config might be +// read from internal flash memory or an SD card, or arrive via serial +// command. If using this constructor, MUST follow up with updateType(), +// updateLength(), etc. to establish the strand type, length and pin number! +Adafruit_NeoPixel::Adafruit_NeoPixel() : +#ifdef NEO_KHZ400 + is800KHz(true), +#endif + begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL), + rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) +{ +} + +Adafruit_NeoPixel::~Adafruit_NeoPixel() { + if(pixels) free(pixels); + if(pin >= 0) pinMode(pin, INPUT); +} + +void Adafruit_NeoPixel::begin(void) { + if(pin >= 0) { + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + } + begun = true; + +} + +void Adafruit_NeoPixel::updateLength(uint16_t n) { + if(pixels) free(pixels); // Free existing data (if any) + + // Allocate new data -- note: ALL PIXELS ARE CLEARED + numBytes = n * ((wOffset == rOffset) ? 3 : 4); + if((pixels = (uint8_t *)malloc(numBytes))) { + memset(pixels, 0, numBytes); + numLEDs = n; + } else { + numLEDs = numBytes = 0; + } +} + +void Adafruit_NeoPixel::updateType(neoPixelType t) { + boolean oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW + + wOffset = (t >> 6) & 0b11; // See notes in header file + rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets + gOffset = (t >> 2) & 0b11; + bOffset = t & 0b11; +#ifdef NEO_KHZ400 + is800KHz = (t < 256); // 400 KHz flag is 1<<8 +#endif + + // If bytes-per-pixel has changed (and pixel data was previously + // allocated), re-allocate to new size. Will clear any data. + if(pixels) { + boolean newThreeBytesPerPixel = (wOffset == rOffset); + if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs); + } +} + +#if defined(ESP8266) +// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution +extern "C" void ICACHE_RAM_ATTR espShow( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +#elif defined(ESP32) +extern "C" void espShow( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +#endif // ESP8266 + +void Adafruit_NeoPixel::show(void) { + + if(!pixels) return; + + // Data latch = 300+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while(!canShow()); + // endTime is a private member (rather than global var) so that multiple + // instances on different pins can be quickly issued in succession (each + // instance doesn't delay the next). + + // In order to make this code runtime-configurable to work with any pin, + // SBI/CBI instructions are eschewed in favor of full PORT writes via the + // OUT or ST instructions. It relies on two facts: that peripheral + // functions (such as PWM) take precedence on output pins, so our PORT- + // wide writes won't interfere, and that interrupts are globally disabled + // while data is being issued to the LEDs, so no other code will be + // accessing the PORT. The code takes an initial 'snapshot' of the PORT + // state, computes 'pin high' and 'pin low' values, and writes these back + // to the PORT register as needed. + + // NRF52 may use PWM + DMA (if available), may not need to disable interrupt +#ifndef NRF52 + noInterrupts(); // Need 100% focus on instruction timing +#endif + +#ifdef __AVR__ +// AVR MCUs -- ATmega & ATtiny (no XMEGA) --------------------------------- + + volatile uint16_t + i = numBytes; // Loop counter + volatile uint8_t + *ptr = pixels, // Pointer to next byte + b = *ptr++, // Current byte value + hi, // PORT w/output bit set high + lo; // PORT w/output bit set low + + // Hand-tuned assembly code issues data to the LED drivers at a specific + // rate. There's separate code for different CPU speeds (8, 12, 16 MHz) + // for both the WS2811 (400 KHz) and WS2812 (800 KHz) drivers. The + // datastream timing for the LED drivers allows a little wiggle room each + // way (listed in the datasheets), so the conditions for compiling each + // case are set up for a range of frequencies rather than just the exact + // 8, 12 or 16 MHz values, permitting use with some close-but-not-spot-on + // devices (e.g. 16.5 MHz DigiSpark). The ranges were arrived at based + // on the datasheet figures and have not been extensively tested outside + // the canonical 8/12/16 MHz speeds; there's no guarantee these will work + // close to the extremes (or possibly they could be pushed further). + // Keep in mind only one CPU speed case actually gets compiled; the + // resulting program isn't as massive as it might look from source here. + +// 8 MHz(ish) AVR --------------------------------------------------------- +#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + + volatile uint8_t n1, n2 = 0; // First, next bits out + + // Squeezing an 800 KHz stream out of an 8 MHz chip requires code + // specific to each PORT register. + + // 10 instruction clocks per bit: HHxxxxxLLL + // OUT instructions: ^ ^ ^ (T=0,2,7) + + // PORTD OUTPUT ---------------------------------------------------- + +#if defined(PORTD) + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + if(port == &PORTD) { + #endif // defined(PORTB/C/F) + + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + // Dirty trick: RJMPs proceeding to the next instruction are used + // to delay two clock cycles in one instruction word (rather than + // using two NOPs). This was necessary in order to squeeze the + // loop down to exactly 64 words -- the maximum possible for a + // relative branch. + + asm volatile( + "headD:" "\n\t" // Clk Pseudocode + // Bit 7: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 6: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 5: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 4: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 3: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 2: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 1: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet) + // Bit 0: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "brne headD" "\n" // 2 while(i) (Z flag set above) + : [byte] "+r" (b), + [n1] "+r" (n1), + [n2] "+r" (n2), + [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTD)), + [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + } else // other PORT(s) + #endif // defined(PORTB/C/F) +#endif // defined(PORTD) + + // PORTB OUTPUT ---------------------------------------------------- + +#if defined(PORTB) + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + if(port == &PORTB) { + #endif // defined(PORTD/C/F) + + // Same as above, just switched to PORTB and stripped of comments. + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + asm volatile( + "headB:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headB" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + } + #endif + #if defined(PORTC) || defined(PORTF) + else + #endif // defined(PORTC/F) +#endif // defined(PORTB) + + // PORTC OUTPUT ---------------------------------------------------- + +#if defined(PORTC) + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + if(port == &PORTC) { + #endif // defined(PORTD/B/F) + + // Same as above, just switched to PORTC and stripped of comments. + hi = PORTC | pinMask; + lo = PORTC & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + asm volatile( + "headC:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headC" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + } + #endif // defined(PORTD/B/F) + #if defined(PORTF) + else + #endif +#endif // defined(PORTC) + + // PORTF OUTPUT ---------------------------------------------------- + +#if defined(PORTF) + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + if(port == &PORTF) { + #endif // defined(PORTD/B/C) + + hi = PORTF | pinMask; + lo = PORTF & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + asm volatile( + "headF:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headF" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + } + #endif // defined(PORTD/B/C) +#endif // defined(PORTF) + +#ifdef NEO_KHZ400 + } else { // end 800 KHz, do 400 KHz + + // Timing is more relaxed; unrolling the inner loop for each bit is + // not necessary. Still using the peculiar RJMPs as 2X NOPs, not out + // of need but just to trim the code size down a little. + // This 400-KHz-datastream-on-8-MHz-CPU code is not quite identical + // to the 800-on-16 code later -- the hi/lo timing between WS2811 and + // WS2812 is not simply a 2:1 scale! + + // 20 inst. clocks per bit: HHHHxxxxxxLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,4,10) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) + "dec %[bit]" "\n\t" // 1 bit-- (T = 8) + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 10) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) + "nop" "\n\t" // 1 nop (T = 13) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [hi] "r" (hi), + [lo] "r" (lo), + [ptr] "e" (ptr)); + } +#endif // NEO_KHZ400 + +// 12 MHz(ish) AVR -------------------------------------------------------- +#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + + // In the 12 MHz case, an optimized 800 KHz datastream (no dead time + // between bytes) requires a PORT-specific loop similar to the 8 MHz + // code (but a little more relaxed in this case). + + // 15 instruction clocks per bit: HHHHxxxxxxLLLLL + // OUT instructions: ^ ^ ^ (T=0,4,10) + + volatile uint8_t next; + + // PORTD OUTPUT ---------------------------------------------------- + +#if defined(PORTD) + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + if(port == &PORTD) { + #endif // defined(PORTB/C/F) + + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Don't "optimize" the OUT calls into the bitTime subroutine; + // we're exploiting the RCALL and RET as 3- and 4-cycle NOPs! + asm volatile( + "headD:" "\n\t" // (T = 0) + "out %[port], %[hi]" "\n\t" // (T = 1) + "rcall bitTimeD" "\n\t" // Bit 7 (T = 15) + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 6 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 5 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 4 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 3 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 2 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 1 + // Bit 0: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1) + "rjmp .+0" "\n\t" // 2 nop nop (T = 3) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5) + "out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9) + "nop" "\n\t" // 1 (T = 10) + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 13) + "brne headD" "\n\t" // 2 if(i != 0) -> (next byte) + "rjmp doneD" "\n\t" + "bitTimeD:" "\n\t" // nop nop nop (T = 4) + "out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5) + "mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7) + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9) + "nop" "\n\t" // 1 (T = 10) + "out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11) + "ret" "\n\t" // 4 nop nop nop nop (T = 15) + "doneD:" "\n" + : [byte] "+r" (b), + [next] "+r" (next), + [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTD)), + [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + } else // other PORT(s) + #endif // defined(PORTB/C/F) +#endif // defined(PORTD) + + // PORTB OUTPUT ---------------------------------------------------- + +#if defined(PORTB) + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + if(port == &PORTB) { + #endif // defined(PORTD/C/F) + + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Same as above, just set for PORTB & stripped of comments + asm volatile( + "headB:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headB" "\n\t" + "rjmp doneB" "\n\t" + "bitTimeB:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneB:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + } + #endif + #if defined(PORTC) || defined(PORTF) + else + #endif // defined(PORTC/F) +#endif // defined(PORTB) + + // PORTC OUTPUT ---------------------------------------------------- + +#if defined(PORTC) + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + if(port == &PORTC) { + #endif // defined(PORTD/B/F) + + hi = PORTC | pinMask; + lo = PORTC & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Same as above, just set for PORTC & stripped of comments + asm volatile( + "headC:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headC" "\n\t" + "rjmp doneC" "\n\t" + "bitTimeC:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneC:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + } + #endif // defined(PORTD/B/F) + #if defined(PORTF) + else + #endif +#endif // defined(PORTC) + + // PORTF OUTPUT ---------------------------------------------------- + +#if defined(PORTF) + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + if(port == &PORTF) { + #endif // defined(PORTD/B/C) + + hi = PORTF | pinMask; + lo = PORTF & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Same as above, just set for PORTF & stripped of comments + asm volatile( + "headF:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headF" "\n\t" + "rjmp doneC" "\n\t" + "bitTimeC:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneC:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + } + #endif // defined(PORTD/B/C) +#endif // defined(PORTF) + +#ifdef NEO_KHZ400 + } else { // 400 KHz + + // 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,6,15) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head30:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" "\n\t" // 2 nop nop (T = 6) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8) + "rjmp .+0" "\n\t" // 2 nop nop (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "nop" "\n\t" // 1 nop (T = 15) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17) + "rjmp .+0" "\n\t" // 2 nop nop (T = 19) + "dec %[bit]" "\n\t" // 1 bit-- (T = 20) + "breq nextbyte30" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22) + "rjmp .+0" "\n\t" // 2 nop nop (T = 24) + "rjmp .+0" "\n\t" // 2 nop nop (T = 26) + "rjmp .+0" "\n\t" // 2 nop nop (T = 28) + "rjmp head30" "\n\t" // 2 -> head30 (next bit out) + "nextbyte30:" "\n\t" // (T = 22) + "nop" "\n\t" // 1 nop (T = 23) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 28) + "brne head30" "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [hi] "r" (hi), + [lo] "r" (lo), + [ptr] "e" (ptr)); + } +#endif // NEO_KHZ400 + +// 16 MHz(ish) AVR -------------------------------------------------------- +#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + + // WS2811 and WS2812 have different hi/lo duty cycles; this is + // similar but NOT an exact copy of the prior 400-on-8 code. + + // 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL + // ST instructions: ^ ^ ^ (T=0,5,13) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "dec %[bit]" "\n\t" // 1 bit-- (T = 5) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8) + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "nop" "\n\t" // 1 nop (T = 13) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) + "nop" "\n\t" // 1 nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 10) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) + "nop" "\n\t" // 1 nop (T = 16) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + +#ifdef NEO_KHZ400 + } else { // 400 KHz + + // The 400 KHz clock on 16 MHz MCU is the most 'relaxed' version. + + // 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,8,20) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head40:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" "\n\t" // 2 nop nop (T = 6) + "rjmp .+0" "\n\t" // 2 nop nop (T = 8) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp .+0" "\n\t" // 2 nop nop (T = 20) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22) + "nop" "\n\t" // 1 nop (T = 23) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24) + "dec %[bit]" "\n\t" // 1 bit-- (T = 25) + "breq nextbyte40" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27) + "nop" "\n\t" // 1 nop (T = 28) + "rjmp .+0" "\n\t" // 2 nop nop (T = 30) + "rjmp .+0" "\n\t" // 2 nop nop (T = 32) + "rjmp .+0" "\n\t" // 2 nop nop (T = 34) + "rjmp .+0" "\n\t" // 2 nop nop (T = 36) + "rjmp .+0" "\n\t" // 2 nop nop (T = 38) + "rjmp head40" "\n\t" // 2 -> head40 (next bit out) + "nextbyte40:" "\n\t" // (T = 27) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30) + "rjmp .+0" "\n\t" // 2 nop nop (T = 32) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34) + "rjmp .+0" "\n\t" // 2 nop nop (T = 36) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38) + "brne head40" "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + } +#endif // NEO_KHZ400 + +#else + #error "CPU SPEED NOT SUPPORTED" +#endif // end F_CPU ifdefs on __AVR__ + +// END AVR ---------------------------------------------------------------- + + +#elif defined(__arm__) + +// ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due --------------------------- + +#if defined(TEENSYDUINO) && defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6 +#define CYCLES_800_T0H (F_CPU / 4000000) +#define CYCLES_800_T1H (F_CPU / 1250000) +#define CYCLES_800 (F_CPU / 800000) +#define CYCLES_400_T0H (F_CPU / 2000000) +#define CYCLES_400_T1H (F_CPU / 833333) +#define CYCLES_400 (F_CPU / 400000) + + uint8_t *p = pixels, + *end = p + numBytes, pix, mask; + volatile uint8_t *set = portSetRegister(pin), + *clr = portClearRegister(pin); + uint32_t cyc; + + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + cyc = ARM_DWT_CYCCNT + CYCLES_800; + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800); + cyc = ARM_DWT_CYCCNT; + *set = 1; + if(pix & mask) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); + } else { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); + } + *clr = 1; + } + } + while(ARM_DWT_CYCCNT - cyc < CYCLES_800); +#ifdef NEO_KHZ400 + } else { // 400 kHz bitstream + cyc = ARM_DWT_CYCCNT + CYCLES_400; + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + cyc = ARM_DWT_CYCCNT; + *set = 1; + if(pix & mask) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); + } else { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); + } + *clr = 1; + } + } + while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + } +#endif // NEO_KHZ400 + +#elif defined(TEENSYDUINO) && defined(__MKL26Z64__) // Teensy-LC + +#if F_CPU == 48000000 + uint8_t *p = pixels, + pix, count, dly, + bitmask = digitalPinToBitMask(pin); + volatile uint8_t *reg = portSetRegister(pin); + uint32_t num = numBytes; + asm volatile( + "L%=_begin:" "\n\t" + "ldrb %[pix], [%[p], #0]" "\n\t" + "lsl %[pix], #24" "\n\t" + "movs %[count], #7" "\n\t" + "L%=_loop:" "\n\t" + "lsl %[pix], #1" "\n\t" + "bcs L%=_loop_one" "\n\t" + "L%=_loop_zero:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_loop_delay_T0H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T0H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_loop_delay_T0L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T0L" "\n\t" + "b L%=_next" "\n\t" + "L%=_loop_one:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_loop_delay_T1H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T1H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_loop_delay_T1L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T1L" "\n\t" + "nop" "\n\t" + "L%=_next:" "\n\t" + "sub %[count], #1" "\n\t" + "bne L%=_loop" "\n\t" + "lsl %[pix], #1" "\n\t" + "bcs L%=_last_one" "\n\t" + "L%=_last_zero:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_last_delay_T0H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T0H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #10" "\n\t" + "L%=_last_delay_T0L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T0L" "\n\t" + "b L%=_repeat" "\n\t" + "L%=_last_one:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_last_delay_T1H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T1H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #1" "\n\t" + "L%=_last_delay_T1L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T1L" "\n\t" + "nop" "\n\t" + "L%=_repeat:" "\n\t" + "add %[p], #1" "\n\t" + "sub %[num], #1" "\n\t" + "bne L%=_begin" "\n\t" + "L%=_done:" "\n\t" + : [p] "+r" (p), + [pix] "=&r" (pix), + [count] "=&r" (count), + [dly] "=&r" (dly), + [num] "+r" (num) + : [bitmask] "r" (bitmask), + [reg] "r" (reg) + ); +#else +#error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" +#endif // F_CPU == 48000000 + +// Begin of support for NRF52832 based boards ------------------------- + +#elif defined(NRF52) +// [[[Begin of the Neopixel NRF52 EasyDMA implementation +// by the Hackerspace San Salvador]]] +// This technique uses the PWM peripheral on the NRF52. The PWM uses the +// EasyDMA feature included on the chip. This technique loads the duty +// cycle configuration for each cycle when the PWM is enabled. For this +// to work we need to store a 16 bit configuration for each bit of the +// RGB(W) values in the pixel buffer. +// Comparator values for the PWM were hand picked and are guaranteed to +// be 100% organic to preserve freshness and high accuracy. Current +// parameters are: +// * PWM Clock: 16Mhz +// * Minimum step time: 62.5ns +// * Time for zero in high (T0H): 0.31ms +// * Time for one in high (T1H): 0.75ms +// * Cycle time: 1.25us +// * Frequency: 800Khz +// For 400Khz we just double the calculated times. +// ---------- BEGIN Constants for the EasyDMA implementation ----------- +// The PWM starts the duty cycle in LOW. To start with HIGH we +// need to set the 15th bit on each register. + +// WS2812 (rev A) timing is 0.35 and 0.7us +//#define MAGIC_T0H 5UL | (0x8000) // 0.3125us +//#define MAGIC_T1H 12UL | (0x8000) // 0.75us + +// WS2812B (rev B) timing is 0.4 and 0.8 us +#define MAGIC_T0H 6UL | (0x8000) // 0.375us +#define MAGIC_T1H 13UL | (0x8000) // 0.8125us + +// WS2811 (400 khz) timing is 0.5 and 1.2 +#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us +#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us + +// For 400Khz, we double value of CTOPVAL +#define CTOPVAL 20UL // 1.25us +#define CTOPVAL_400KHz 40UL // 2.5us + +// ---------- END Constants for the EasyDMA implementation ------------- +// +// If there is no device available an alternative cycle-counter +// implementation is tried. +// The nRF52832 runs with a fixed clock of 64Mhz. The alternative +// implementation is the same as the one used for the Teensy 3.0/1/2 but +// with the Nordic SDK HAL & registers syntax. +// The number of cycles was hand picked and is guaranteed to be 100% +// organic to preserve freshness and high accuracy. +// ---------- BEGIN Constants for cycle counter implementation --------- +#define CYCLES_800_T0H 18 // ~0.36 uS +#define CYCLES_800_T1H 41 // ~0.76 uS +#define CYCLES_800 71 // ~1.25 uS + +#define CYCLES_400_T0H 26 // ~0.50 uS +#define CYCLES_400_T1H 70 // ~1.26 uS +#define CYCLES_400 156 // ~2.50 uS +// ---------- END of Constants for cycle counter implementation -------- + + // To support both the SoftDevice + Neopixels we use the EasyDMA + // feature from the NRF25. However this technique implies to + // generate a pattern and store it on the memory. The actual + // memory used in bytes corresponds to the following formula: + // totalMem = numBytes*8*2+(2*2) + // The two additional bytes at the end are needed to reset the + // sequence. + // + // If there is not enough memory, we will fall back to cycle counter + // using DWT + uint32_t pattern_size = numBytes*8*sizeof(uint16_t)+2*sizeof(uint16_t); + uint16_t* pixels_pattern = NULL; + + NRF_PWM_Type* pwm = NULL; + + // Try to find a free PWM device, which is not enabled + // and has no connected pins + NRF_PWM_Type* PWM[3] = {NRF_PWM0, NRF_PWM1, NRF_PWM2}; + for(int device = 0; device<3; device++) { + if( (PWM[device]->ENABLE == 0) && + (PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) && + (PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) && + (PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) && + (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk) + ) { + pwm = PWM[device]; + break; + } + } + + // only malloc if there is PWM device available + if ( pwm != NULL ) { + #ifdef ARDUINO_FEATHER52 // use thread-safe malloc + pixels_pattern = (uint16_t *) rtos_malloc(pattern_size); + #else + pixels_pattern = (uint16_t *) malloc(pattern_size); + #endif + } + + // Use the identified device to choose the implementation + // If a PWM device is available use DMA + if( (pixels_pattern != NULL) && (pwm != NULL) ) { + uint16_t pos = 0; // bit position + + for(uint16_t n=0; n0; mask >>= 1, i++) { + #ifdef NEO_KHZ400 + if( !is800KHz ) { + pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz; + }else + #endif + { + pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H; + } + + pos++; + } + } + + // Zero padding to indicate the end of que sequence + pixels_pattern[++pos] = 0 | (0x8000); // Seq end + pixels_pattern[++pos] = 0 | (0x8000); // Seq end + + // Set the wave mode to count UP + pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos); + + // Set the PWM to use the 16MHz clock + pwm->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos); + + // Setting of the maximum count + // but keeping it on 16Mhz allows for more granularity just + // in case someone wants to do more fine-tuning of the timing. +#ifdef NEO_KHZ400 + if( !is800KHz ) { + pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos); + }else +#endif + { + pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos); + } + + // Disable loops, we want the sequence to repeat only once + pwm->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos); + + // On the "Common" setting the PWM uses the same pattern for the + // for supported sequences. The pattern is stored on half-word + // of 16bits + pwm->DECODER = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) | + (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos); + + // Pointer to the memory storing the patter + pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos; + + // Calculation of the number of steps loaded from memory. + pwm->SEQ[0].CNT = (pattern_size/sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos; + + // The following settings are ignored with the current config. + pwm->SEQ[0].REFRESH = 0; + pwm->SEQ[0].ENDDELAY = 0; + + // The Neopixel implementation is a blocking algorithm. DMA + // allows for non-blocking operation. To "simulate" a blocking + // operation we enable the interruption for the end of sequence + // and block the execution thread until the event flag is set by + // the peripheral. +// pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<PSEL.OUT[0] = g_ADigitalPinMap[pin]; + + // Enable the PWM + pwm->ENABLE = 1; + + // After all of this and many hours of reading the documentation + // we are ready to start the sequence... + pwm->EVENTS_SEQEND[0] = 0; + pwm->TASKS_SEQSTART[0] = 1; + + // But we have to wait for the flag to be set. + while(!pwm->EVENTS_SEQEND[0]) + { + #ifdef ARDUINO_FEATHER52 + yield(); + #endif + } + + // Before leave we clear the flag for the event. + pwm->EVENTS_SEQEND[0] = 0; + + // We need to disable the device and disconnect + // all the outputs before leave or the device will not + // be selected on the next call. + // TODO: Check if disabling the device causes performance issues. + pwm->ENABLE = 0; + + pwm->PSEL.OUT[0] = 0xFFFFFFFFUL; + + #ifdef ARDUINO_FEATHER52 // use thread-safe free + rtos_free(pixels_pattern); + #else + free(pixels_pattern); + #endif + }// End of DMA implementation + // --------------------------------------------------------------------- + else{ + // Fall back to DWT + #ifdef ARDUINO_FEATHER52 + // Bluefruit Feather 52 uses freeRTOS + // Critical Section is used since it does not block SoftDevice execution + taskENTER_CRITICAL(); + #elif defined(NRF52_DISABLE_INT) + // If you are using the Bluetooth SoftDevice we advise you to not disable + // the interrupts. Disabling the interrupts even for short periods of time + // causes the SoftDevice to stop working. + // Disable the interrupts only in cases where you need high performance for + // the LEDs and if you are not using the EasyDMA feature. + __disable_irq(); + #endif + + uint32_t pinMask = 1UL << g_ADigitalPinMap[pin]; + + uint32_t CYCLES_X00 = CYCLES_800; + uint32_t CYCLES_X00_T1H = CYCLES_800_T1H; + uint32_t CYCLES_X00_T0H = CYCLES_800_T0H; + +#ifdef NEO_KHZ400 + if( !is800KHz ) + { + CYCLES_X00 = CYCLES_400; + CYCLES_X00_T1H = CYCLES_400_T1H; + CYCLES_X00_T0H = CYCLES_400_T0H; + } +#endif + + // Enable DWT in debug core + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + + // Tries to re-send the frame if is interrupted by the SoftDevice. + while(1) { + uint8_t *p = pixels; + + uint32_t cycStart = DWT->CYCCNT; + uint32_t cyc = 0; + + for(uint16_t n=0; n>= 1) { + while(DWT->CYCCNT - cyc < CYCLES_X00); + cyc = DWT->CYCCNT; + + NRF_GPIO->OUTSET |= pinMask; + + if(pix & mask) { + while(DWT->CYCCNT - cyc < CYCLES_X00_T1H); + } else { + while(DWT->CYCCNT - cyc < CYCLES_X00_T0H); + } + + NRF_GPIO->OUTCLR |= pinMask; + } + } + while(DWT->CYCCNT - cyc < CYCLES_X00); + + + // If total time longer than 25%, resend the whole data. + // Since we are likely to be interrupted by SoftDevice + if ( (DWT->CYCCNT - cycStart) < ( 8*numBytes*((CYCLES_X00*5)/4) ) ) { + break; + } + + // re-send need 300us delay + delayMicroseconds(300); + } + + // Enable interrupts again + #ifdef ARDUINO_FEATHER52 + taskEXIT_CRITICAL(); + #elif defined(NRF52_DISABLE_INT) + __enable_irq(); + #endif + } +// END of NRF52 implementation + +#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__) // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo and others + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + + uint8_t *ptr, *end, p, bitMask, portNum; + uint32_t pinMask; + + portNum = g_APinDescription[pin].ulPort; + pinMask = 1ul << g_APinDescription[pin].ulPin; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + + volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), + *clr = &(PORT->Group[portNum].OUTCLR.reg); + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + *clr = pinMask; + } else { + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } + if(bitMask >>= 1) { + asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + for(;;) { + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;"); + *clr = pinMask; + } else { + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;"); + } + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + if(bitMask >>= 1) { + asm("nop; nop; nop; nop; nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } + } +#endif + +#elif defined (__SAMD51__) // M4 @ 120mhz + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + + uint8_t *ptr, *end, p, bitMask, portNum; + uint32_t pinMask; + + portNum = g_APinDescription[pin].ulPort; + pinMask = 1ul << g_APinDescription[pin].ulPin; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + + volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), + *clr = &(PORT->Group[portNum].OUTCLR.reg); + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + if(p & bitMask) { // ONE + // High 800ns + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + // Low 450ns + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop;"); + } else { // ZERO + // High 400ns + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop;"); + // Low 850ns + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + } + if(bitMask >>= 1) { + // Move on to the next pixel + asm("nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + // ToDo! + } +#endif + +#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz) + + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + + uint8_t *ptr, *end, p, bitMask; + uint32_t pinMask; + + pinMask = BIT(PIN_MAP[pin].gpio_bit); + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + + volatile uint16_t *set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); + volatile uint16_t *clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + if(p & bitMask) { // ONE + // High 800ns + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop;"); + // Low 450ns + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop;"); + } else { // ZERO + // High 400ns + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop;"); + // Low 850ns + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } + if(bitMask >>= 1) { + // Move on to the next pixel + asm("nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + // ToDo! + } +#endif + +#elif defined (NRF51) + uint8_t *p = pixels, + pix, count, mask; + int32_t num = numBytes; + unsigned int bitmask = ( 1 << g_ADigitalPinMap[pin] ); +// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp + + volatile unsigned int *reg = (unsigned int *) (0x50000000UL + 0x508); + +// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h +// http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1 +// https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm + + asm volatile( + // "cpsid i" ; disable irq + + // b .start + "b L%=_start" "\n\t" + // .nextbit: ; C0 + "L%=_nextbit:" "\n\t" //; C0 + // str r1, [r3, #0] ; pin := hi C2 + "strb %[bitmask], [%[reg], #0]" "\n\t" //; pin := hi C2 + // tst r6, r0 ; C3 + "tst %[mask], %[pix]" "\n\t"// ; C3 + // bne .islate ; C4 + "bne L%=_islate" "\n\t" //; C4 + // str r1, [r2, #0] ; pin := lo C6 + "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C6 + // .islate: + "L%=_islate:" "\n\t" + // lsrs r6, r6, #1 ; r6 >>= 1 C7 + "lsr %[mask], %[mask], #1" "\n\t" //; r6 >>= 1 C7 + // bne .justbit ; C8 + "bne L%=_justbit" "\n\t" //; C8 + + // ; not just a bit - need new byte + // adds r4, #1 ; r4++ C9 + "add %[p], #1" "\n\t" //; r4++ C9 + // subs r5, #1 ; r5-- C10 + "sub %[num], #1" "\n\t" //; r5-- C10 + // bcc .stop ; if (r5<0) goto .stop C11 + "bcc L%=_stop" "\n\t" //; if (r5<0) goto .stop C11 + // .start: + "L%=_start:" + // movs r6, #0x80 ; reset mask C12 + "movs %[mask], #0x80" "\n\t" //; reset mask C12 + // nop ; C13 + "nop" "\n\t" //; C13 + + // .common: ; C13 + "L%=_common:" "\n\t" //; C13 + // str r1, [r2, #0] ; pin := lo C15 + "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C15 + // ; always re-load byte - it just fits with the cycles better this way + // ldrb r0, [r4, #0] ; r0 := *r4 C17 + "ldrb %[pix], [%[p], #0]" "\n\t" //; r0 := *r4 C17 + // b .nextbit ; C20 + "b L%=_nextbit" "\n\t" //; C20 + + // .justbit: ; C10 + "L%=_justbit:" "\n\t" //; C10 + // ; no nops, branch taken is already 3 cycles + // b .common ; C13 + "b L%=_common" "\n\t" //; C13 + + // .stop: + "L%=_stop:" "\n\t" + // str r1, [r2, #0] ; pin := lo + "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo + // cpsie i ; enable irq + + : [p] "+r" (p), + [pix] "=&r" (pix), + [count] "=&r" (count), + [mask] "=&r" (mask), + [num] "+r" (num) + : [bitmask] "r" (bitmask), + [reg] "r" (reg) + ); + +#elif defined(__SAM3X8E__) // Arduino Due + + #define SCALE VARIANT_MCK / 2UL / 1000000UL + #define INST (2UL * F_CPU / VARIANT_MCK) + #define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST)) + #define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST)) + #define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST)) + #define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST)) + #define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST)) + #define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST)) + + int pinMask, time0, time1, period, t; + Pio *port; + volatile WoReg *portSet, *portClear, *timeValue, *timeReset; + uint8_t *p, *end, pix, mask; + + pmc_set_writeprotect(false); + pmc_enable_periph_clk((uint32_t)TC3_IRQn); + TC_Configure(TC1, 0, + TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1); + TC_Start(TC1, 0); + + pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into + port = g_APinDescription[pin].pPort; // declarations above. Want to + portSet = &(port->PIO_SODR); // burn a few cycles after + portClear = &(port->PIO_CODR); // starting timer to minimize + timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. + timeReset = &(TC1->TC_CHANNEL[0].TC_CCR); + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + time0 = TIME_800_0; + time1 = TIME_800_1; + period = PERIOD_800; +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + time0 = TIME_400_0; + time1 = TIME_400_1; + period = PERIOD_400; + } +#endif + + for(t = time0;; t = time0) { + if(pix & mask) t = time1; + while(*timeValue < period); + *portSet = pinMask; + *timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG; + while(*timeValue < t); + *portClear = pinMask; + if(!(mask >>= 1)) { // This 'inside-out' loop logic utilizes + if(p >= end) break; // idle time to minimize inter-byte delays. + pix = *p++; + mask = 0x80; + } + } + while(*timeValue < period); // Wait for last bit + TC_Stop(TC1, 0); + +#endif // end Due + +// END ARM ---------------------------------------------------------------- + + +#elif defined(ESP8266) || defined(ESP32) + +// ESP8266 ---------------------------------------------------------------- + + // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution + espShow(pin, pixels, numBytes, is800KHz); + +#elif defined(__ARDUINO_ARC__) + +// Arduino 101 ----------------------------------------------------------- + +#define NOPx7 { __builtin_arc_nop(); \ + __builtin_arc_nop(); __builtin_arc_nop(); \ + __builtin_arc_nop(); __builtin_arc_nop(); \ + __builtin_arc_nop(); __builtin_arc_nop(); } + + PinDescription *pindesc = &g_APinDescription[pin]; + register uint32_t loop = 8 * numBytes; // one loop to handle all bytes and all bits + register uint8_t *p = pixels; + register uint32_t currByte = (uint32_t) (*p); + register uint32_t currBit = 0x80 & currByte; + register uint32_t bitCounter = 0; + register uint32_t first = 1; + + // The loop is unusual. Very first iteration puts all the way LOW to the wire - + // constant LOW does not affect NEOPIXEL, so there is no visible effect displayed. + // During that very first iteration CPU caches instructions in the loop. + // Because of the caching process, "CPU slows down". NEOPIXEL pulse is very time sensitive + // that's why we let the CPU cache first and we start regular pulse from 2nd iteration + if (pindesc->ulGPIOType == SS_GPIO) { + register uint32_t reg = pindesc->ulGPIOBase + SS_GPIO_SWPORTA_DR; + uint32_t reg_val = __builtin_arc_lr((volatile uint32_t)reg); + register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); + register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); + + loop += 1; // include first, special iteration + while(loop--) { + if(!first) { + currByte <<= 1; + bitCounter++; + } + + // 1 is >550ns high and >450ns low; 0 is 200..500ns high and >450ns low + __builtin_arc_sr(first ? reg_bit_low : reg_bit_high, (volatile uint32_t)reg); + if(currBit) { // ~400ns HIGH (740ns overall) + NOPx7 + NOPx7 + } + // ~340ns HIGH + NOPx7 + __builtin_arc_nop(); + + // 820ns LOW; per spec, max allowed low here is 5000ns */ + __builtin_arc_sr(reg_bit_low, (volatile uint32_t)reg); + NOPx7 + NOPx7 + + if(bitCounter >= 8) { + bitCounter = 0; + currByte = (uint32_t) (*++p); + } + + currBit = 0x80 & currByte; + first = 0; + } + } else if(pindesc->ulGPIOType == SOC_GPIO) { + register uint32_t reg = pindesc->ulGPIOBase + SOC_GPIO_SWPORTA_DR; + uint32_t reg_val = MMIO_REG_VAL(reg); + register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); + register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); + + loop += 1; // include first, special iteration + while(loop--) { + if(!first) { + currByte <<= 1; + bitCounter++; + } + MMIO_REG_VAL(reg) = first ? reg_bit_low : reg_bit_high; + if(currBit) { // ~430ns HIGH (740ns overall) + NOPx7 + NOPx7 + __builtin_arc_nop(); + } + // ~310ns HIGH + NOPx7 + + // 850ns LOW; per spec, max allowed low here is 5000ns */ + MMIO_REG_VAL(reg) = reg_bit_low; + NOPx7 + NOPx7 + + if(bitCounter >= 8) { + bitCounter = 0; + currByte = (uint32_t) (*++p); + } + + currBit = 0x80 & currByte; + first = 0; + } + } +#elif defined(FREEDOM_E300_HIFIVE1) +/* + Hifive1 implementation uses bit-banging on GPIO register + logic with timing approximated via nanosecond per cycle with + +/- timing offset introduced by delays from branching determined + by measuring off of scope. + */ + uint8_t *p = pixels, + *end = p + numBytes, pix, mask; + +#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + +// for WS2812B +#if F_CPU == 256000000 +#define TIME1_HIGH (800 - 110) +#define TIME1_LOW (450 - 150) +#define TIME0_HIGH (400 - 120) +#define TIME0_LOW (850 - 130) +#elif F_CPU == 320000000 +#define TIME1_HIGH (800 - 120) +#define TIME1_LOW (450 - 120) +#define TIME0_HIGH (400 - 120) +#define TIME0_LOW (850 - 150) +#else +/* It appears that the GPIO performance on HiFive1 may be the bottleneck + to make 16MHz clock speed working. When we use sw and amoxor instruction + to write to GPIO register, we can only write high and low on interval + ~600ns and with branch instructions it is too long to work reliably. + */ +#error "CPU SPEED NOT SUPPORTED DUE TO HARDWARE LIMITATION" +#endif + +// for WS2812, commented out like NRF52 +//#if F_CPU == 256000000 +//#define TIME1_HIGH (700 - 110) +//#define TIME1_LOW (600 - 130) +//#define TIME0_HIGH (350 - 120) +//#define TIME0_LOW (800 - 130) +//#elif F_CPU == 320000000 +//#define TIME1_HIGH (700 - 120) +//#define TIME1_LOW (600 - 130) +//#define TIME0_HIGH (350 - 120) +//#define TIME0_LOW (800 - 150) +//#else +//#error "CPU SPEED NOT SUPPORTED DUE TO HARDWARE LIMITATION" +//#endif + + const unsigned nsec_per_cycle = ((1000 + (F_CPU / 1000000) / 2) / (F_CPU / 1000000)); + + uint32_t t1h = TIME1_HIGH / nsec_per_cycle; + uint32_t t1l = TIME1_LOW / nsec_per_cycle; + uint32_t t0h = TIME0_HIGH / nsec_per_cycle; + uint32_t t0l = TIME0_LOW / nsec_per_cycle; + + uint32_t pinBitMask = digitalPinToBitMask(pin); + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + if(pix & mask) { + GPIO_REG(GPIO_OUTPUT_VAL) |= pinBitMask; + delayCycles(t1h); + + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(pinBitMask); + delayCycles(t1l); + } else { + GPIO_REG(GPIO_OUTPUT_VAL) |= pinBitMask; + delayCycles(t0h); + + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(pinBitMask); + delayCycles(t0l); + } + } + } +#ifdef NEO_KHZ400 + } else { // 400 kHz bitstream + // ToDo! + } +#endif // NEO_KHZ400 + +#else +#error Architecture not supported +#endif + + +// END ARCHITECTURE SELECT ------------------------------------------------ + +#ifndef NRF52 + interrupts(); +#endif + + endTime = micros(); // Save EOD time for latch on next call +} + +// Set the output pin number +void Adafruit_NeoPixel::setPin(uint8_t p) { + if(begun && (pin >= 0)) pinMode(pin, INPUT); + pin = p; + if(begun) { + pinMode(p, OUTPUT); + digitalWrite(p, LOW); + } +#ifdef __AVR__ + port = portOutputRegister(digitalPinToPort(p)); + pinMask = digitalPinToBitMask(p); +#endif +} + +// Set pixel color from separate R,G,B components: +void Adafruit_NeoPixel::setPixelColor( + uint16_t n, uint8_t r, uint8_t g, uint8_t b) { + + if(n < numLEDs) { + if(brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + } + uint8_t *p; + if(wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = 0; // But only R,G,B passed -- set W to 0 + } + p[rOffset] = r; // R,G,B always stored + p[gOffset] = g; + p[bOffset] = b; + } +} + +void Adafruit_NeoPixel::setPixelColor( + uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + + if(n < numLEDs) { + if(brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + w = (w * brightness) >> 8; + } + uint8_t *p; + if(wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = w; // Store W + } + p[rOffset] = r; // Store R,G,B + p[gOffset] = g; + p[bOffset] = b; + } +} + +// Set pixel color from 'packed' 32-bit RGB color: +void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { + if(n < numLEDs) { + uint8_t *p, + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + if(brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + } + if(wOffset == rOffset) { + p = &pixels[n * 3]; + } else { + p = &pixels[n * 4]; + uint8_t w = (uint8_t)(c >> 24); + p[wOffset] = brightness ? ((w * brightness) >> 8) : w; + } + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; + } +} + +// Convert separate R,G,B into packed 32-bit RGB color. +// Packed format is always RGB, regardless of LED strand color order. +uint32_t Adafruit_NeoPixel::Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + +// Convert separate R,G,B,W into packed 32-bit WRGB color. +// Packed format is always WRGB, regardless of LED strand color order. +uint32_t Adafruit_NeoPixel::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + +// Query color from previously-set pixel (returns packed 32-bit RGB value) +uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const { + if(n >= numLEDs) return 0; // Out of bounds, return no color. + + uint8_t *p; + + if(wOffset == rOffset) { // Is RGB-type device + p = &pixels[n * 3]; + if(brightness) { + // Stored color was decimated by setBrightness(). Returned value + // attempts to scale back to an approximation of the original 24-bit + // value used when setting the pixel color, but there will always be + // some error -- those bits are simply gone. Issue is most + // pronounced at low brightness levels. + return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ( (uint32_t)(p[bOffset] << 8) / brightness ); + } else { + // No brightness adjustment has been made -- return 'raw' color + return ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; + } + } else { // Is RGBW-type device + p = &pixels[n * 4]; + if(brightness) { // Return scaled color + return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) | + (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ( (uint32_t)(p[bOffset] << 8) / brightness ); + } else { // Return raw color + return ((uint32_t)p[wOffset] << 24) | + ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; + } + } +} + +// Returns pointer to pixels[] array. Pixel data is stored in device- +// native format and is not translated here. Application will need to be +// aware of specific pixel data format and handle colors appropriately. +uint8_t *Adafruit_NeoPixel::getPixels(void) const { + return pixels; +} + +uint16_t Adafruit_NeoPixel::numPixels(void) const { + return numLEDs; +} + +// Adjust output brightness; 0=darkest (off), 255=brightest. This does +// NOT immediately affect what's currently displayed on the LEDs. The +// next call to show() will refresh the LEDs at this level. However, +// this process is potentially "lossy," especially when increasing +// brightness. The tight timing in the WS2811/WS2812 code means there +// aren't enough free cycles to perform this scaling on the fly as data +// is issued. So we make a pass through the existing color data in RAM +// and scale it (subsequent graphics commands also work at this +// brightness level). If there's a significant step up in brightness, +// the limited number of steps (quantization) in the old data will be +// quite visible in the re-scaled version. For a non-destructive +// change, you'll need to re-render the full strip data. C'est la vie. +void Adafruit_NeoPixel::setBrightness(uint8_t b) { + // Stored brightness value is different than what's passed. + // This simplifies the actual scaling math later, allowing a fast + // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t, + // adding 1 here may (intentionally) roll over...so 0 = max brightness + // (color values are interpreted literally; no scaling), 1 = min + // brightness (off), 255 = just below max brightness. + uint8_t newBrightness = b + 1; + if(newBrightness != brightness) { // Compare against prior value + // Brightness has changed -- re-scale existing data in RAM + uint8_t c, + *ptr = pixels, + oldBrightness = brightness - 1; // De-wrap old brightness value + uint16_t scale; + if(oldBrightness == 0) scale = 0; // Avoid /0 + else if(b == 255) scale = 65535 / oldBrightness; + else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; + for(uint16_t i=0; i> 8; + } + brightness = newBrightness; + } +} + +//Return the brightness value +uint8_t Adafruit_NeoPixel::getBrightness(void) const { + return brightness - 1; +} + +void Adafruit_NeoPixel::clear() { + memset(pixels, 0, numBytes); +} + +/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255). + Copy & paste this snippet into a Python REPL to regenerate: +import math +for x in range(256): + print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))), + if x&15 == 15: print +*/ +static const uint8_t PROGMEM _sineTable[256] = { + 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, + 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, + 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, + 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, + 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, + 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, + 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, + 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, + 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, + 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, + 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, + 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, + 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, + 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, + 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124}; + +/* Similar to above, but for an 8-bit gamma-correction table. + Copy & paste this snippet into a Python REPL to regenerate: +import math +gamma=2.6 +for x in range(256): + print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))), + if x&15 == 15: print +*/ +static const uint8_t PROGMEM _gammaTable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, + 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, + 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, + 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, + 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120, + 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148, + 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180, + 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215, + 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255}; + +uint8_t Adafruit_NeoPixel::sine8(uint8_t x) const { + return pgm_read_byte(&_sineTable[x]); // 0-255 in, 0-255 out +} + +uint8_t Adafruit_NeoPixel::gamma8(uint8_t x) const { + return pgm_read_byte(&_gammaTable[x]); // 0-255 in, 0-255 out +} + diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.h b/hardware/freedom_e/libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.h new file mode 100644 index 0000000..9fa35bc --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.h @@ -0,0 +1,182 @@ +/*-------------------------------------------------------------------- + This file is part of the Adafruit NeoPixel library. + + NeoPixel 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 3 of + the License, or (at your option) any later version. + + NeoPixel 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 NeoPixel. If not, see + . + --------------------------------------------------------------------*/ + +#ifndef ADAFRUIT_NEOPIXEL_H +#define ADAFRUIT_NEOPIXEL_H + +#if (ARDUINO >= 100) + #include +#else + #include + #include +#endif + +// The order of primary colors in the NeoPixel data stream can vary +// among device types, manufacturers and even different revisions of +// the same item. The third parameter to the Adafruit_NeoPixel +// constructor encodes the per-pixel byte offsets of the red, green +// and blue primaries (plus white, if present) in the data stream -- +// the following #defines provide an easier-to-use named version for +// each permutation. e.g. NEO_GRB indicates a NeoPixel-compatible +// device expecting three bytes per pixel, with the first byte +// containing the green value, second containing red and third +// containing blue. The in-memory representation of a chain of +// NeoPixels is the same as the data-stream order; no re-ordering of +// bytes is required when issuing data to the chain. + +// Bits 5,4 of this value are the offset (0-3) from the first byte of +// a pixel to the location of the red color byte. Bits 3,2 are the +// green offset and 1,0 are the blue offset. If it is an RGBW-type +// device (supporting a white primary in addition to R,G,B), bits 7,6 +// are the offset to the white byte...otherwise, bits 7,6 are set to +// the same value as 5,4 (red) to indicate an RGB (not RGBW) device. +// i.e. binary representation: +// 0bWWRRGGBB for RGBW devices +// 0bRRRRGGBB for RGB + +// RGB NeoPixel permutations; white and red offsets are always same +// Offset: W R G B +#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) +#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) +#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) +#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) +#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) +#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) + +// RGBW NeoPixel permutations; all 4 offsets are distinct +// Offset: W R G B +#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) +#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) +#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) +#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) +#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) +#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) + +#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) +#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) +#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) +#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) +#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) +#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) + +#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) +#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) +#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) +#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) +#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) +#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) + +#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) +#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) +#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) +#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) +#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) +#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) + +// Add NEO_KHZ400 to the color order value to indicate a 400 KHz +// device. All but the earliest v1 NeoPixels expect an 800 KHz data +// stream, this is the default if unspecified. Because flash space +// is very limited on ATtiny devices (e.g. Trinket, Gemma), v1 +// NeoPixels aren't handled by default on those chips, though it can +// be enabled by removing the ifndef/endif below -- but code will be +// bigger. Conversely, can disable the NEO_KHZ400 line on other MCUs +// to remove v1 support and save a little space. + +#define NEO_KHZ800 0x0000 // 800 KHz datastream +#ifndef __AVR_ATtiny85__ +#define NEO_KHZ400 0x0100 // 400 KHz datastream +#endif + +// If 400 KHz support is enabled, the third parameter to the constructor +// requires a 16-bit value (in order to select 400 vs 800 KHz speed). +// If only 800 KHz is enabled (as is default on ATtiny), an 8-bit value +// is sufficient to encode pixel color order, saving some space. + +#ifdef NEO_KHZ400 +typedef uint16_t neoPixelType; +#else +typedef uint8_t neoPixelType; +#endif + +class Adafruit_NeoPixel { + + public: + + // Constructor: number of LEDs, pin number, LED type + Adafruit_NeoPixel(uint16_t n, uint8_t p=6, neoPixelType t=NEO_GRB + NEO_KHZ800); + Adafruit_NeoPixel(void); + ~Adafruit_NeoPixel(); + + void + begin(void), + show(void), + setPin(uint8_t p), + setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b), + setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w), + setPixelColor(uint16_t n, uint32_t c), + setBrightness(uint8_t), + clear(), + updateLength(uint16_t n), + updateType(neoPixelType t); + uint8_t + *getPixels(void) const, + getBrightness(void) const, + sine8(uint8_t) const, + gamma8(uint8_t) const; + int8_t + getPin(void) { return pin; }; + uint16_t + numPixels(void) const; + static uint32_t + Color(uint8_t r, uint8_t g, uint8_t b), + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w); + uint32_t + getPixelColor(uint16_t n) const; + inline bool + canShow(void) { return (micros() - endTime) >= 300L; } + + protected: + + boolean +#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled... + is800KHz, // ...true if 800 KHz pixels +#endif + begun; // true if begin() previously called + uint16_t + numLEDs, // Number of RGB LEDs in strip + numBytes; // Size of 'pixels' buffer below (3 or 4 bytes/pixel) + int8_t + pin; // Output pin number (-1 if not yet set) + uint8_t + brightness, + *pixels, // Holds LED color values (3 or 4 bytes each) + rOffset, // Index of red byte within each 3- or 4-byte pixel + gOffset, // Index of green byte + bOffset, // Index of blue byte + wOffset; // Index of white byte (same as rOffset if no white) + uint32_t + endTime; // Latch timing reference +#ifdef __AVR__ + volatile uint8_t + *port; // Output PORT register + uint8_t + pinMask; // Output PORT bitmask +#endif +}; + +#endif // ADAFRUIT_NEOPIXEL_H diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/COPYING b/hardware/freedom_e/libraries/Adafruit_NeoPixel/COPYING new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/README.md b/hardware/freedom_e/libraries/Adafruit_NeoPixel/README.md new file mode 100644 index 0000000..356bb31 --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/README.md @@ -0,0 +1,51 @@ +# Adafruit NeoPixel Library [![Build Status](https://travis-ci.org/adafruit/Adafruit_NeoPixel.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_NeoPixel) + +Arduino library for controlling single-wire-based LED pixels and strip such as the [Adafruit 60 LED/meter Digital LED strip][strip], the [Adafruit FLORA RGB Smart Pixel][flora], the [Adafruit Breadboard-friendly RGB Smart Pixel][pixel], the [Adafruit NeoPixel Stick][stick], and the [Adafruit NeoPixel Shield][shield]. + +After downloading, rename folder to 'Adafruit_NeoPixel' and install in Arduino Libraries folder. Restart Arduino IDE, then open File->Sketchbook->Library->Adafruit_NeoPixel->strandtest sketch. + +Compatibility notes: Port A is not supported on any AVR processors at this time + +[flora]: http://adafruit.com/products/1060 +[strip]: http://adafruit.com/products/1138 +[pixel]: http://adafruit.com/products/1312 +[stick]: http://adafruit.com/products/1426 +[shield]: http://adafruit.com/products/1430 + +--- + +## Supported chipsets + +We have included code for the following chips - *sometimes these break for exciting reasons that we can't control* in which case please open an issue! + + * AVR ATmega and ATtiny (any 8-bit) - 8 MHz, 12 MHz and 16 MHz + * Teensy 3.x and LC + * Arduino Due + * Arduino 101 + * ATSAMD21 (Arduino Zero/M0 and other SAMD21 boards) @ 48 MHz + * ATSAMD51 @ 120 MHz + * Adafruit STM32 Feather @ 120 MHz + * ESP8266 any speed + * ESP32 any speed + * Nordic nRF52 (Adafruit Feather nRF52), nRF51 (micro:bit) + +Check forks for other architectures not listed here! + +--- + +### Roadmap + +The PRIME DIRECTIVE is to maintain backward compatibility with existing Arduino sketches -- many are hosted elsewhere and don't track changes here, some are in print and can never be changed! + +Please don't reformat code for the sake of reformatting code. The resulting large "visual diff" makes it impossible to untangle actual bug fixes from merely rearranged lines. (Exception for first item in wishlist below.) + +Things I'd Like To Do But There's No Official Timeline So Please Don't Count On Any Of This Ever Being Canonical: + + * For the show() function (with all the delicate pixel timing stuff), break out each architecture into separate source files rather than the current unmaintainable tangle of #ifdef statements! + * Really the only reason I've never incorporated an HSV color function is that I haven't settled on a type and range for the hue element (mathematically an integer from 0 to 1529 yields a "most correct" approach but it's weird to use and would probably annoy people). + * Add a fill function with the arguments: (color, first, count). Count, if unspecified, fills to end of strip. First, if unspecified, is zero. Color, if unspecified, is zero (effectively a strip clear operation). Do NOT then implement fifty billion minor variations (such as first, last). No. This argument sequence was very specifically chosen because reasons, and equivalents to such variations are trivially made in one's call. Just one fill function, please. + * At such time that the prior two items are settled, revisit the DotStar library (and maybe even LPD8806 or anything else we've got) and add the same functions and behaviors so there's a good degree of sketch compatibility across different pixel types. + * I wouldn't mind paring down strandtest a bit. More diagnostic, less Amiga demo. + * Please don't use updateLength() or updateType() in new code. They should not have been implemented this way (use the C++ 'new' operator with the regular constructor instead) and are only sticking around because of the Prime Directive. setPin() is OK for now though, it's a trick we can use to 'recycle' pixel memory across multiple strips. + * In the M0 and M4 code, use the hardware systick counter for bit timing rather than hand-tweaked NOPs (a temporary kludge at the time because I wasn't reading systick correctly). + * As currently written, brightness scaling is still a "destructive" operation -- pixel values are altered in RAM and the original value as set can't be accurately read back, only approximated, which has been confusing and frustrating to users. It was done this way at the time because NeoPixel timing is strict, AVR microcontrollers (all we had at the time) are limited, and assembly language is hard. All the 32-bit architectures should have no problem handling nondestructive brightness scaling -- calculating each byte immediately before it's sent out the wire, maintaining the original set value in RAM -- the work just hasn't been done. There's a fair chance even the AVR code could manage it with some intense focus. (The DotStar library achieves nondestructive brightness scaling because it doesn't have to manage data timing so carefully...every architecture, even ATtiny, just takes whatever cycles it needs for the multiply/shift operations.) diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/esp8266.c b/hardware/freedom_e/libraries/Adafruit_NeoPixel/esp8266.c new file mode 100644 index 0000000..e316ee7 --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/esp8266.c @@ -0,0 +1,82 @@ +// This is a mash-up of the Due show() code + insights from Michael Miller's +// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus +// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution. + +#if defined(ESP8266) || defined(ESP32) + +#include +#ifdef ESP8266 +#include +#endif + +static uint32_t _getCycleCount(void) __attribute__((always_inline)); +static inline uint32_t _getCycleCount(void) { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +#ifdef ESP8266 +void ICACHE_RAM_ATTR espShow( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) { +#else +void espShow( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) { +#endif + +#define CYCLES_800_T0H (F_CPU / 2500000) // 0.4us +#define CYCLES_800_T1H (F_CPU / 1250000) // 0.8us +#define CYCLES_800 (F_CPU / 800000) // 1.25us per bit +#define CYCLES_400_T0H (F_CPU / 2000000) // 0.5uS +#define CYCLES_400_T1H (F_CPU / 833333) // 1.2us +#define CYCLES_400 (F_CPU / 400000) // 2.5us per bit + + uint8_t *p, *end, pix, mask; + uint32_t t, time0, time1, period, c, startTime, pinMask; + + pinMask = _BV(pin); + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; + startTime = 0; + +#ifdef NEO_KHZ400 + if(is800KHz) { +#endif + time0 = CYCLES_800_T0H; + time1 = CYCLES_800_T1H; + period = CYCLES_800; +#ifdef NEO_KHZ400 + } else { // 400 KHz bitstream + time0 = CYCLES_400_T0H; + time1 = CYCLES_400_T1H; + period = CYCLES_400; + } +#endif + + for(t = time0;; t = time0) { + if(pix & mask) t = time1; // Bit high duration + while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start +#ifdef ESP8266 + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high +#else + gpio_set_level(pin, HIGH); +#endif + startTime = c; // Save start time + while(((c = _getCycleCount()) - startTime) < t); // Wait high duration +#ifdef ESP8266 + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask); // Set low +#else + gpio_set_level(pin, LOW); +#endif + if(!(mask >>= 1)) { // Next bit/byte + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + } + while((_getCycleCount() - startTime) < period); // Wait for last bit +} + +#endif // ESP8266 diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/.esp8266.test.skip b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/.esp8266.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/.trinket.test.skip b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/.trinket.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/RGBWstrandtest.ino b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/RGBWstrandtest.ino new file mode 100644 index 0000000..043aaa0 --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/RGBWstrandtest/RGBWstrandtest.ino @@ -0,0 +1,264 @@ +#include +#ifdef __AVR__ + #include +#endif + +#define PIN 6 + +#define NUM_LEDS 60 + +#define BRIGHTNESS 50 + +Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRBW + NEO_KHZ800); + +byte neopix_gamma[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, + 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, + 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, + 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, + 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; + + +void setup() { + // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket + #if defined (__AVR_ATtiny85__) + if (F_CPU == 16000000) clock_prescale_set(clock_div_1); + #endif + // End of trinket special code + strip.setBrightness(BRIGHTNESS); + strip.begin(); + strip.show(); // Initialize all pixels to 'off' +} + +void loop() { + // Some example procedures showing how to display to the pixels: + colorWipe(strip.Color(255, 0, 0), 50); // Red + colorWipe(strip.Color(0, 255, 0), 50); // Green + colorWipe(strip.Color(0, 0, 255), 50); // Blue + colorWipe(strip.Color(0, 0, 0, 255), 50); // White + + whiteOverRainbow(20,75,5); + + pulseWhite(5); + + // fullWhite(); + // delay(2000); + + rainbowFade2White(3,3,1); + + +} + +// Fill the dots one after the other with a color +void colorWipe(uint32_t c, uint8_t wait) { + for(uint16_t i=0; i= 0 ; j--){ + for(uint16_t i=0; i 255 - fadeMax ){ + fadeVal--; + } + + strip.show(); + delay(wait); + } + + } + + + + delay(500); + + + for(int k = 0 ; k < whiteLoops ; k ++){ + + for(int j = 0; j < 256 ; j++){ + + for(uint16_t i=0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, strip.Color(0,0,0, neopix_gamma[j] ) ); + } + strip.show(); + } + + delay(2000); + for(int j = 255; j >= 0 ; j--){ + + for(uint16_t i=0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, strip.Color(0,0,0, neopix_gamma[j] ) ); + } + strip.show(); + } + } + + delay(500); + + +} + +void whiteOverRainbow(uint8_t wait, uint8_t whiteSpeed, uint8_t whiteLength ) { + + if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1; + + int head = whiteLength - 1; + int tail = 0; + + int loops = 3; + int loopNum = 0; + + static unsigned long lastTime = 0; + + + while(true){ + for(int j=0; j<256; j++) { + for(uint16_t i=0; i= tail && i <= head) || (tail > head && i >= tail) || (tail > head && i <= head) ){ + strip.setPixelColor(i, strip.Color(0,0,0, 255 ) ); + } + else{ + strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); + } + + } + + if(millis() - lastTime > whiteSpeed) { + head++; + tail++; + if(head == strip.numPixels()){ + loopNum++; + } + lastTime = millis(); + } + + if(loopNum == loops) return; + + head%=strip.numPixels(); + tail%=strip.numPixels(); + strip.show(); + delay(wait); + } + } + +} +void fullWhite() { + + for(uint16_t i=0; i> 16); +} +uint8_t green(uint32_t c) { + return (c >> 8); +} +uint8_t blue(uint32_t c) { + return (c); +} + +#if defined(FREEDOM_E300_HIFIVE1) +void* __dso_handle; +#endif // FREEDOM_E300_HIFIVE1 diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/.test.skip b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/BLESerial.cpp b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/BLESerial.cpp new file mode 100644 index 0000000..d1693de --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/BLESerial.cpp @@ -0,0 +1,133 @@ +#include "BLESerial.h" + +// #define BLE_SERIAL_DEBUG + +BLESerial* BLESerial::_instance = NULL; + +BLESerial::BLESerial(unsigned char req, unsigned char rdy, unsigned char rst) : + BLEPeripheral(req, rdy, rst) +{ + this->_txCount = 0; + this->_rxHead = this->_rxTail = 0; + this->_flushed = 0; + BLESerial::_instance = this; + + addAttribute(this->_uartService); + addAttribute(this->_uartNameDescriptor); + setAdvertisedServiceUuid(this->_uartService.uuid()); + addAttribute(this->_rxCharacteristic); + addAttribute(this->_rxNameDescriptor); + this->_rxCharacteristic.setEventHandler(BLEWritten, BLESerial::_received); + addAttribute(this->_txCharacteristic); + addAttribute(this->_txNameDescriptor); +} + +void BLESerial::begin(...) { + BLEPeripheral::begin(); + #ifdef BLE_SERIAL_DEBUG + Serial.println(F("BLESerial::begin()")); + #endif +} + +void BLESerial::poll() { + if (millis() < this->_flushed + 100) { + BLEPeripheral::poll(); + } else { + flush(); + } +} + +void BLESerial::end() { + this->_rxCharacteristic.setEventHandler(BLEWritten, NULL); + this->_rxHead = this->_rxTail = 0; + flush(); + BLEPeripheral::disconnect(); +} + +int BLESerial::available(void) { + BLEPeripheral::poll(); + int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer); + #ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLESerial::available() = ")); + Serial.println(retval); + #endif + return retval; +} + +int BLESerial::peek(void) { + BLEPeripheral::poll(); + if (this->_rxTail == this->_rxHead) return -1; + uint8_t byte = this->_rxBuffer[this->_rxTail]; + #ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLESerial::peek() = ")); + Serial.print((char) byte); + Serial.print(F(" 0x")); + Serial.println(byte, HEX); + #endif + return byte; +} + +int BLESerial::read(void) { + BLEPeripheral::poll(); + if (this->_rxTail == this->_rxHead) return -1; + this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer); + uint8_t byte = this->_rxBuffer[this->_rxTail]; + #ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLESerial::read() = ")); + Serial.print((char) byte); + Serial.print(F(" 0x")); + Serial.println(byte, HEX); + #endif + return byte; +} + +void BLESerial::flush(void) { + if (this->_txCount == 0) return; + this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount); + this->_flushed = millis(); + this->_txCount = 0; + BLEPeripheral::poll(); + #ifdef BLE_SERIAL_DEBUG + Serial.println(F("BLESerial::flush()")); + #endif +} + +size_t BLESerial::write(uint8_t byte) { + BLEPeripheral::poll(); + if (this->_txCharacteristic.subscribed() == false) return 0; + this->_txBuffer[this->_txCount++] = byte; + if (this->_txCount == sizeof(this->_txBuffer)) flush(); + #ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLESerial::write(")); + Serial.print((char) byte); + Serial.print(F(" 0x")); + Serial.print(byte, HEX); + Serial.println(F(") = 1")); + #endif + return 1; +} + +BLESerial::operator bool() { + bool retval = BLEPeripheral::connected(); + #ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLESerial::operator bool() = ")); + Serial.println(retval); + #endif + return retval; +} + +void BLESerial::_received(const uint8_t* data, size_t size) { + for (int i = 0; i < size; i++) { + this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer); + this->_rxBuffer[this->_rxHead] = data[i]; + } + #ifdef BLE_SERIAL_DEBUG + Serial.print(F("BLESerial::received(")); + for (int i = 0; i < size; i++) Serial.print((char) data[i]); + Serial.println(F(")")); + #endif +} + +void BLESerial::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic) { + BLESerial::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength()); +} diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/BLESerial.h b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/BLESerial.h new file mode 100644 index 0000000..01904c7 --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/BLESerial.h @@ -0,0 +1,46 @@ +#ifndef _BLE_SERIAL_H_ +#define _BLE_SERIAL_H_ + +#include +#include + +class BLESerial : public BLEPeripheral, public Stream +{ + public: + BLESerial(unsigned char req, unsigned char rdy, unsigned char rst); + + void begin(...); + void poll(); + void end(); + + virtual int available(void); + virtual int peek(void); + virtual int read(void); + virtual void flush(void); + virtual size_t write(uint8_t byte); + using Print::write; + virtual operator bool(); + + private: + unsigned long _flushed; + static BLESerial* _instance; + + size_t _rxHead; + size_t _rxTail; + size_t _rxCount() const; + uint8_t _rxBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH]; + size_t _txCount; + uint8_t _txBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH]; + + BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); + BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART"); + BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, BLE_ATTRIBUTE_MAX_VALUE_LENGTH); + BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)"); + BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, BLE_ATTRIBUTE_MAX_VALUE_LENGTH); + BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)"); + + void _received(const uint8_t* data, size_t size); + static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic); +}; + +#endif diff --git a/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/StrandtestBLE.ino b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/StrandtestBLE.ino new file mode 100644 index 0000000..857a975 --- /dev/null +++ b/hardware/freedom_e/libraries/Adafruit_NeoPixel/examples/StrandtestBLE/StrandtestBLE.ino @@ -0,0 +1,212 @@ +/**************************************************************************** + * This example was developed by the Hackerspace San Salvador to demonstrate + * the simultaneous use of the NeoPixel library and the Bluetooth SoftDevice. + * To compile this example you'll need to add support for the NRF52 based + * following the instructions at: + * https://github.com/sandeepmistry/arduino-nRF5 + * Or adding the following URL to the board manager URLs on Arduino preferences: + * https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json + * Then you can install the BLEPeripheral library avaiable at: + * https://github.com/sandeepmistry/arduino-BLEPeripheral + * To test it, compile this example and use the UART module from the nRF + * Toolbox App for Android. Edit the interface and send the characters + * 'a' to 'i' to switch the animation. + * There is a delay because this example blocks the thread of execution but + * the change will be shown after the current animation ends. (This might + * take a couple of seconds) + * For more info write us at: info _at- teubi.co + */ +#include +#include +#include "BLESerial.h" +#include + +#define PIN 15 + +// Parameter 1 = number of pixels in strip +// Parameter 2 = Arduino pin number (most are valid) +// Parameter 3 = pixel type flags, add together as needed: +// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) +// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) +// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) +// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) +// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) +Adafruit_NeoPixel strip = Adafruit_NeoPixel(64, PIN, NEO_GRB + NEO_KHZ800); + +// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across +// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input +// and minimize distance between Arduino and first pixel. Avoid connecting +// on a live circuit...if you must, connect GND first. + +// define pins (varies per shield/board) +#define BLE_REQ 10 +#define BLE_RDY 2 +#define BLE_RST 9 + +// create ble serial instance, see pinouts above +BLESerial BLESerial(BLE_REQ, BLE_RDY, BLE_RST); + +uint8_t current_state = 0; +uint8_t rgb_values[3]; + +void setup() { + Serial.begin(115200); + Serial.println("Hello World!"); + // custom services and characteristics can be added as well + BLESerial.setLocalName("UART_HS"); + BLESerial.begin(); + + strip.begin(); + changeColor(strip.Color(0, 0, 0)); + + //pinMode(PIN, OUTPUT); + //digitalWrite(PIN, LOW); + + current_state = 'a'; +} + + +void loop() { + while(BLESerial.available()) { + uint8_t character = BLESerial.read(); + switch(character) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + current_state = character; + break; + }; + } + switch(current_state) { + case 'a': + colorWipe(strip.Color(255, 0, 0), 20); // Red + break; + case 'b': + colorWipe(strip.Color(0, 255, 0), 20); // Green + break; + case 'c': + colorWipe(strip.Color(0, 0, 255), 20); // Blue + break; + case 'd': + theaterChase(strip.Color(255, 0, 0), 20); // Red + break; + case 'e': + theaterChase(strip.Color(0, 255, 0), 20); // Green + break; + case 'f': + theaterChase(strip.Color(255, 0, 255), 20); // Green + break; + case 'g': + rainbowCycle(20); + break; + case 'h': + rainbow(20); + break; + case 'i': + theaterChaseRainbow(20); + break; + } +} + +void changeColor(uint32_t c) { + for(uint16_t i=0; i + +#define BUTTON_PIN 2 // Digital IO pin connected to the button. This will be + // driven with a pull-up resistor so the switch should + // pull the pin to ground momentarily. On a high -> low + // transition the button press logic will execute. + +#define PIXEL_PIN 6 // Digital IO pin connected to the NeoPixels. + +#define PIXEL_COUNT 16 + +// Parameter 1 = number of pixels in strip, neopixel stick has 8 +// Parameter 2 = pin number (most are valid) +// Parameter 3 = pixel type flags, add together as needed: +// NEO_RGB Pixels are wired for RGB bitstream +// NEO_GRB Pixels are wired for GRB bitstream, correct for neopixel stick +// NEO_KHZ400 400 KHz bitstream (e.g. FLORA pixels) +// NEO_KHZ800 800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick +Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800); + +bool oldState = HIGH; +int showType = 0; + +void setup() { + pinMode(BUTTON_PIN, INPUT_PULLUP); + strip.begin(); + strip.show(); // Initialize all pixels to 'off' +} + +void loop() { + // Get current button state. + bool newState = digitalRead(BUTTON_PIN); + + // Check if state changed from high to low (button press). + if (newState == LOW && oldState == HIGH) { + // Short delay to debounce button. + delay(20); + // Check if button is still low after debounce. + newState = digitalRead(BUTTON_PIN); + if (newState == LOW) { + showType++; + if (showType > 9) + showType=0; + startShow(showType); + } + } + + // Set the last button state to the old state. + oldState = newState; +} + +void startShow(int i) { + switch(i){ + case 0: colorWipe(strip.Color(0, 0, 0), 50); // Black/off + break; + case 1: colorWipe(strip.Color(255, 0, 0), 50); // Red + break; + case 2: colorWipe(strip.Color(0, 255, 0), 50); // Green + break; + case 3: colorWipe(strip.Color(0, 0, 255), 50); // Blue + break; + case 4: theaterChase(strip.Color(127, 127, 127), 50); // White + break; + case 5: theaterChase(strip.Color(127, 0, 0), 50); // Red + break; + case 6: theaterChase(strip.Color( 0, 0, 127), 50); // Blue + break; + case 7: rainbow(20); + break; + case 8: rainbowCycle(20); + break; + case 9: theaterChaseRainbow(50); + break; + } +} + +// Fill the dots one after the other with a color +void colorWipe(uint32_t c, uint8_t wait) { + for(uint16_t i=0; i +#ifdef __AVR__ + #include +#endif + +// Which pin on the Arduino is connected to the NeoPixels? +// On a Trinket or Gemma we suggest changing this to 1 +#define PIN 6 + +// How many NeoPixels are attached to the Arduino? +#define NUMPIXELS 16 + +// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals. +// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest +// example for more information on possible values. +Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); + +int delayval = 500; // delay for half a second + +void setup() { + // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket +#if defined (__AVR_ATtiny85__) + if (F_CPU == 16000000) clock_prescale_set(clock_div_1); +#endif + // End of trinket special code + + pixels.begin(); // This initializes the NeoPixel library. +} + +void loop() { + + // For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one. + + for(int i=0;i +#ifdef __AVR__ + #include +#endif + +#define PIN 6 + +// Parameter 1 = number of pixels in strip +// Parameter 2 = Arduino pin number (most are valid) +// Parameter 3 = pixel type flags, add together as needed: +// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) +// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) +// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) +// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) +// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) +Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800); + +// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across +// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input +// and minimize distance between Arduino and first pixel. Avoid connecting +// on a live circuit...if you must, connect GND first. + +void setup() { + // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket + #if defined (__AVR_ATtiny85__) + if (F_CPU == 16000000) clock_prescale_set(clock_div_1); + #endif + // End of trinket special code + + + strip.begin(); + strip.show(); // Initialize all pixels to 'off' +} + +void loop() { + // Some example procedures showing how to display to the pixels: + colorWipe(strip.Color(255, 0, 0), 50); // Red + colorWipe(strip.Color(0, 255, 0), 50); // Green + colorWipe(strip.Color(0, 0, 255), 50); // Blue +//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW + // Send a theater pixel chase in... + theaterChase(strip.Color(127, 127, 127), 50); // White + theaterChase(strip.Color(127, 0, 0), 50); // Red + theaterChase(strip.Color(0, 0, 127), 50); // Blue + + rainbow(20); + rainbowCycle(20); + theaterChaseRainbow(50); +} + +// Fill the dots one after the other with a color +void colorWipe(uint32_t c, uint8_t wait) { + for(uint16_t i=0; i +sentence=Arduino library for controlling single-wire-based LED pixels and strip. +paragraph=Arduino library for controlling single-wire-based LED pixels and strip. +category=Display +url=https://github.com/adafruit/Adafruit_NeoPixel +architectures=* diff --git a/hardware/freedom_e/libraries/RGBL/examples/RgbLedBlink/RgbLedBlink.ino b/hardware/freedom_e/libraries/RGBL/examples/RgbLedBlink/RgbLedBlink.ino new file mode 100644 index 0000000..611ea69 --- /dev/null +++ b/hardware/freedom_e/libraries/RGBL/examples/RgbLedBlink/RgbLedBlink.ino @@ -0,0 +1,27 @@ +/* + Example of the HiFive1 builtin RGB LED blinking with RGBL library. + + On HiFive1 the RGB LED is connected to the following GPIOs: + Red - GPIO6 + Green - GPIO3 + Blue - GPIO5 + + This example code is in the public domain. + + By Michael Zaidman , 2018 +*/ + +#include + +RGBL led; + +void setup() { + led.offAll(); +} + +void loop() { + led.blink(RED, 1000); + led.blink(GREEN, 1000); + led.blink(BLUE, 1000); +} + diff --git a/hardware/freedom_e/libraries/RGBL/examples/RgbLedFade/RgbLedFade.ino b/hardware/freedom_e/libraries/RGBL/examples/RgbLedFade/RgbLedFade.ino new file mode 100644 index 0000000..d04ca90 --- /dev/null +++ b/hardware/freedom_e/libraries/RGBL/examples/RgbLedFade/RgbLedFade.ino @@ -0,0 +1,23 @@ +/* + Example of the HiFive1 builtin RGB LED fading with RGBL library. + + On HiFive1 the RGB LED is connected to the following GPIOs: + Red - GPIO6 + Green - GPIO3 + Blue - GPIO5 + + This example code is in the public domain. + + By Michael Zaidman , 2018 +*/ + +#include + +RGBL led; + +void setup() { +} + +void loop() { + led.fade(10); +} diff --git a/hardware/freedom_e/libraries/RGBL/keywords.txt b/hardware/freedom_e/libraries/RGBL/keywords.txt new file mode 100644 index 0000000..a03f47a --- /dev/null +++ b/hardware/freedom_e/libraries/RGBL/keywords.txt @@ -0,0 +1,27 @@ +####################################### +# Syntax Coloring Map RGBL +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### +RGBL KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +on KEYWORD2 +off KEYWORD2 +offAll KEYWORD2 +writeAll KEYWORD2 +blink KEYWORD2 +fade KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### +RED LITERAL1 +GREEN LITERAL1 +BLUE LITERAL1 +COLOR LITERAL1 diff --git a/hardware/freedom_e/libraries/RGBL/library.properties b/hardware/freedom_e/libraries/RGBL/library.properties new file mode 100644 index 0000000..c152a61 --- /dev/null +++ b/hardware/freedom_e/libraries/RGBL/library.properties @@ -0,0 +1,10 @@ +name=RGB LED +version=1.0 +author=Michael Zaidman +maintainer=Michael Zaidman +sentence=Lightweight RGB LED library. +paragraph= +category=Display +url= +architectures=* + diff --git a/hardware/freedom_e/libraries/RGBL/src/RGBL.cpp b/hardware/freedom_e/libraries/RGBL/src/RGBL.cpp new file mode 100644 index 0000000..c5c88c6 --- /dev/null +++ b/hardware/freedom_e/libraries/RGBL/src/RGBL.cpp @@ -0,0 +1,85 @@ +/* + Lightweight RGB LED library. + + Copyright (c) 2018 Western Digital Corporation or its affiliates. + SPDX-License-Identifier: LGPL-2.1-only + + 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. + + 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 + + Written by Michael Zaidman , 2018 +*/ + +#include "RGBL.h" + +RGBL::RGBL(int brightnessLow, int brightnessHigh, int redPin, int greenPin, int bluePin) { + + pin[RED] = redPin; + pin[GREEN] = greenPin; + pin[BLUE] = bluePin; + + lowLightLimit = brightnessLow; + highLightLimit = brightnessHigh; + + redVal = highLightLimit; + greenVal = lowLightLimit; + blueVal = lowLightLimit; + + pinMode(redPin, OUTPUT); + pinMode(greenPin, OUTPUT); + pinMode(bluePin, OUTPUT); +} + +void RGBL::on(led_color led) { + analogWrite(pin[led],mapVal(highLightLimit)); +} + +void RGBL::off(led_color led) { + analogWrite(pin[led],mapVal(0)); +} + +void RGBL::offAll() { + writeAll(0, 0, 0); +} + +void RGBL::writeAll(int redVal, int greenVal, int blueVal) { + analogWrite(pin[RED],mapVal(redVal)); + analogWrite(pin[GREEN],mapVal(greenVal)); + analogWrite(pin[BLUE],mapVal(blueVal)); +} + +void RGBL::blink(led_color led, int msec) { + on(led); + delay(msec/2); + off(led); + delay(msec/2); +} + +void RGBL::fade(int msec) +{ + if(redVal > lowLightLimit && blueVal == lowLightLimit) { + redVal--; + greenVal++; + } + if(greenVal > lowLightLimit && redVal == lowLightLimit) { + greenVal--; + blueVal++; + } + if(blueVal > lowLightLimit && greenVal == lowLightLimit) { + blueVal--; + redVal++; + } + writeAll(redVal, greenVal, blueVal); + delay(msec); +} diff --git a/hardware/freedom_e/libraries/RGBL/src/RGBL.h b/hardware/freedom_e/libraries/RGBL/src/RGBL.h new file mode 100644 index 0000000..831f2d5 --- /dev/null +++ b/hardware/freedom_e/libraries/RGBL/src/RGBL.h @@ -0,0 +1,57 @@ +/* + Lightweight RGB LED library. + + Copyright (c) 2018 Western Digital Corporation or its affiliates. + SPDX-License-Identifier: LGPL-2.1-only + + 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. + + 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 + + Written by Michael Zaidman , 2018 +*/ + +#ifndef RGBL_H +#define RGBL_H + +#include "Arduino.h" + +typedef enum { + RED = 0, + GREEN, + BLUE, + COLOR +} led_color; + +class RGBL { + int redPin; + int greenPin; + int bluePin; + int redVal; + int greenVal; + int blueVal; + int lowLightLimit; + int highLightLimit; + int pin[COLOR]; + int mapVal(unsigned char val) { return 255 - (unsigned char)val; } +public: + RGBL(int brightnessLow=0, int brightnessHigh=90, int redPin=6, int greenPin=3, int bluePin=5); + void on(led_color led); + void off(led_color led); + void offAll(); + void writeAll(int redVal, int greenVal, int blueVal); + void blink(led_color led, int msec); + void fade(int msec); +}; + +#endif diff --git a/hardware/freedom_e/libraries/Servo/README.adoc b/hardware/freedom_e/libraries/Servo/README.adoc new file mode 100644 index 0000000..dd3f0ba --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/README.adoc @@ -0,0 +1,25 @@ += Servo Library for Arduino = + +This library allows an Arduino board to control RC (hobby) servo motors. + +For more information about this library please visit us at +http://www.arduino.cc/en/Reference/Servo + +== License == + +Copyright (c) 2013 Arduino LLC. All right reserved. +Copyright (c) 2009 Michael Margolis. 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 diff --git a/hardware/freedom_e/libraries/Servo/examples/Knob/Knob.ino b/hardware/freedom_e/libraries/Servo/examples/Knob/Knob.ino new file mode 100644 index 0000000..0db8770 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/examples/Knob/Knob.ino @@ -0,0 +1,27 @@ +/* + Controlling a servo position using a potentiometer (variable resistor) + by Michal Rinott + + modified on 8 Nov 2013 + by Scott Fitzgerald + http://www.arduino.cc/en/Tutorial/Knob +*/ + +#include + +Servo myservo; // create servo object to control a servo + +int potpin = 0; // analog pin used to connect the potentiometer +int val; // variable to read the value from the analog pin + +void setup() { + myservo.attach(9); // attaches the servo on pin 9 to the servo object +} + +void loop() { + val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023) + val = map(val, 0, 1023, 0, 180); // scale it to use it with the servo (value between 0 and 180) + myservo.write(val); // sets the servo position according to the scaled value + delay(15); // waits for the servo to get there +} + diff --git a/hardware/freedom_e/libraries/Servo/examples/Sweep/Sweep.ino b/hardware/freedom_e/libraries/Servo/examples/Sweep/Sweep.ino new file mode 100644 index 0000000..df904af --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/examples/Sweep/Sweep.ino @@ -0,0 +1,32 @@ +/* Sweep + by BARRAGAN + This example code is in the public domain. + + modified 8 Nov 2013 + by Scott Fitzgerald + http://www.arduino.cc/en/Tutorial/Sweep +*/ + +#include + +Servo myservo; // create servo object to control a servo +// twelve servo objects can be created on most boards + +int pos = 0; // variable to store the servo position + +void setup() { + myservo.attach(9); // attaches the servo on pin 9 to the servo object +} + +void loop() { + for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees + // in steps of 1 degree + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position + } + for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position + } +} + diff --git a/hardware/freedom_e/libraries/Servo/keywords.txt b/hardware/freedom_e/libraries/Servo/keywords.txt new file mode 100644 index 0000000..0a7ca1e --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map Servo +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Servo KEYWORD1 Servo + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +attach KEYWORD2 +detach KEYWORD2 +write KEYWORD2 +read KEYWORD2 +attached KEYWORD2 +writeMicroseconds KEYWORD2 +readMicroseconds KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/hardware/freedom_e/libraries/Servo/library.properties b/hardware/freedom_e/libraries/Servo/library.properties new file mode 100644 index 0000000..94dfc92 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/library.properties @@ -0,0 +1,9 @@ +name=Servo +version=1.1.2 +author=Michael Margolis, Arduino +maintainer=Arduino +sentence=Allows Arduino/Genuino boards to control a variety of servo motors. +paragraph=This library can control a great number of servos.
It makes careful use of timers: the library can control 12 servos using only 1 timer.
On the Arduino Due you can control up to 60 servos.
+category=Device Control +url=http://www.arduino.cc/en/Reference/Servo +architectures=avr,sam,samd,nrf52,stm32f4 diff --git a/hardware/freedom_e/libraries/Servo/src/Servo.h b/hardware/freedom_e/libraries/Servo/src/Servo.h new file mode 100644 index 0000000..2ee66d5 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/Servo.h @@ -0,0 +1,131 @@ +/* + Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Copyright (c) 2009 Michael Margolis. 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 +*/ + +/* + A servo is activated by creating an instance of the Servo class passing + the desired pin to the attach() method. + The servos are pulsed in the background using the value most recently + written using the write() method. + + Note that analogWrite of PWM on pins associated with the timer are + disabled when the first servo is attached. + Timers are seized as needed in groups of 12 servos - 24 servos use two + timers, 48 servos will use four. + The sequence used to sieze timers is defined in timers.h + + The methods are: + + Servo - Class for manipulating servo motors connected to Arduino pins. + + attach(pin ) - Attaches a servo motor to an i/o pin. + attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds + default min is 544, max is 2400 + + write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) + writeMicroseconds() - Sets the servo pulse width in microseconds + read() - Gets the last written servo pulse width as an angle between 0 and 180. + readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release) + attached() - Returns true if there is a servo attached. + detach() - Stops an attached servos from pulsing its i/o pin. + */ + +#ifndef Servo_h +#define Servo_h + +#include + +/* + * Defines for 16 bit timers used with Servo library + * + * If _useTimerX is defined then TimerX is a 16 bit timer on the current board + * timer16_Sequence_t enumerates the sequence that the timers should be allocated + * _Nbr_16timers indicates how many 16 bit timers are available. + */ + +// Architecture specific include +#if defined(ARDUINO_ARCH_AVR) +#include "avr/ServoTimers.h" +#elif defined(ARDUINO_ARCH_SAM) +#include "sam/ServoTimers.h" +#elif defined(ARDUINO_ARCH_SAMD) +#include "samd/ServoTimers.h" +#elif defined(ARDUINO_ARCH_STM32F4) +#include "stm32f4/ServoTimers.h" +#elif defined(ARDUINO_ARCH_NRF52) +#include "nrf52/ServoTimers.h" +#elif defined(FREEDOM_E300_HIFIVE1) +#else +#error "This library only supports boards with an AVR, SAM, SAMD, NRF52, HIFIVE1 or STM32F4 processor." +#endif + +#define Servo_VERSION 2 // software version of this library + +#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo +#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo +#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached +#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds + +#if !defined(FREEDOM_E300_HIFIVE1) +#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer +#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER) +#else +#define SERVOS_PER_TIMER 3 // the maximum number of servos controlled by one timer +#define PWM_16TIMERS 2 +#define MAX_SERVOS (PWM_16TIMERS * SERVOS_PER_TIMER) //(_Nbr_16timers * SERVOS_PER_TIMER) +#endif + +#define INVALID_SERVO 255 // flag indicating an invalid servo index + +#if !defined(ARDUINO_ARCH_STM32F4) + +typedef struct { + uint8_t nbr :6 ; // a pin number from 0 to 63 + uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false +} ServoPin_t ; + +typedef struct { + ServoPin_t Pin; + volatile unsigned int ticks; +} servo_t; + +class Servo +{ +public: + Servo(); + uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure + uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. + void detach(); + void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds + void writeMicroseconds(int value); // Write pulse width in microseconds + int read(); // returns current pulse width as an angle between 0 and 180 degrees + int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release) + bool attached(); // return true if this servo is attached, otherwise false +private: + uint8_t servoIndex; // index into the channel data for this servo +#if defined(FREEDOM_E300_HIFIVE1) + int min; + int max; +#else + int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH + int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH +#endif +}; + +#endif +#endif diff --git a/hardware/freedom_e/libraries/Servo/src/avr/Servo.cpp b/hardware/freedom_e/libraries/Servo/src/avr/Servo.cpp new file mode 100644 index 0000000..ed7376e --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/avr/Servo.cpp @@ -0,0 +1,318 @@ +/* + Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Copyright (c) 2009 Michael Margolis. 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 + */ + +#if defined(ARDUINO_ARCH_AVR) + +#include +#include + +#include "Servo.h" + +#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009 +#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds + + +#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009 + +//#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER) + +static servo_t servos[MAX_SERVOS]; // static array of servo structures +static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) + +uint8_t ServoCount = 0; // the total number of attached servos + + +// convenience macros +#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo +#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer +#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel +#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel + +#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo +#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo + +/************ static functions common to all instances ***********************/ + +static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA) +{ + if( Channel[timer] < 0 ) + *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer + else{ + if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true ) + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated + } + + Channel[timer]++; // increment to the next channel + if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { + *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks; + if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high + } + else { + // finished all channels so wait for the refresh period to expire before starting over + if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed + *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL); + else + *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed + Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + } +} + +#ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform +// Interrupt handlers for Arduino +#if defined(_useTimer1) +SIGNAL (TIMER1_COMPA_vect) +{ + handle_interrupts(_timer1, &TCNT1, &OCR1A); +} +#endif + +#if defined(_useTimer3) +SIGNAL (TIMER3_COMPA_vect) +{ + handle_interrupts(_timer3, &TCNT3, &OCR3A); +} +#endif + +#if defined(_useTimer4) +SIGNAL (TIMER4_COMPA_vect) +{ + handle_interrupts(_timer4, &TCNT4, &OCR4A); +} +#endif + +#if defined(_useTimer5) +SIGNAL (TIMER5_COMPA_vect) +{ + handle_interrupts(_timer5, &TCNT5, &OCR5A); +} +#endif + +#elif defined WIRING +// Interrupt handlers for Wiring +#if defined(_useTimer1) +void Timer1Service() +{ + handle_interrupts(_timer1, &TCNT1, &OCR1A); +} +#endif +#if defined(_useTimer3) +void Timer3Service() +{ + handle_interrupts(_timer3, &TCNT3, &OCR3A); +} +#endif +#endif + + +static void initISR(timer16_Sequence_t timer) +{ +#if defined (_useTimer1) + if(timer == _timer1) { + TCCR1A = 0; // normal counting mode + TCCR1B = _BV(CS11); // set prescaler of 8 + TCNT1 = 0; // clear the timer count +#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__) + TIFR |= _BV(OCF1A); // clear any pending interrupts; + TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt +#else + // here if not ATmega8 or ATmega128 + TIFR1 |= _BV(OCF1A); // clear any pending interrupts; + TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt +#endif +#if defined(WIRING) + timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service); +#endif + } +#endif + +#if defined (_useTimer3) + if(timer == _timer3) { + TCCR3A = 0; // normal counting mode + TCCR3B = _BV(CS31); // set prescaler of 8 + TCNT3 = 0; // clear the timer count +#if defined(__AVR_ATmega128__) + TIFR |= _BV(OCF3A); // clear any pending interrupts; + ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt +#else + TIFR3 = _BV(OCF3A); // clear any pending interrupts; + TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt +#endif +#if defined(WIRING) + timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only +#endif + } +#endif + +#if defined (_useTimer4) + if(timer == _timer4) { + TCCR4A = 0; // normal counting mode + TCCR4B = _BV(CS41); // set prescaler of 8 + TCNT4 = 0; // clear the timer count + TIFR4 = _BV(OCF4A); // clear any pending interrupts; + TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt + } +#endif + +#if defined (_useTimer5) + if(timer == _timer5) { + TCCR5A = 0; // normal counting mode + TCCR5B = _BV(CS51); // set prescaler of 8 + TCNT5 = 0; // clear the timer count + TIFR5 = _BV(OCF5A); // clear any pending interrupts; + TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt + } +#endif +} + +static void finISR(timer16_Sequence_t timer) +{ + //disable use of the given timer +#if defined WIRING // Wiring + if(timer == _timer1) { + #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) + TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt + #else + TIMSK &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt + #endif + timerDetach(TIMER1OUTCOMPAREA_INT); + } + else if(timer == _timer3) { + #if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__) + TIMSK3 &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt + #else + ETIMSK &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt + #endif + timerDetach(TIMER3OUTCOMPAREA_INT); + } +#else + //For arduino - in future: call here to a currently undefined function to reset the timer + (void) timer; // squash "unused parameter 'timer' [-Wunused-parameter]" warning +#endif +} + +static boolean isTimerActive(timer16_Sequence_t timer) +{ + // returns true if any servo is active on this timer + for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) { + if(SERVO(timer,channel).Pin.isActive == true) + return true; + } + return false; +} + + +/****************** end of static functions ******************************/ + +Servo::Servo() +{ + if( ServoCount < MAX_SERVOS) { + this->servoIndex = ServoCount++; // assign a servo index to this instance + servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009 + } + else + this->servoIndex = INVALID_SERVO ; // too many servos +} + +uint8_t Servo::attach(int pin) +{ + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) +{ + if(this->servoIndex < MAX_SERVOS ) { + pinMode( pin, OUTPUT) ; // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128 + this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS + this->max = (MAX_PULSE_WIDTH - max)/4; + // initialize the timer if it has not already been initialized + timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) + initISR(timer); + servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive + } + return this->servoIndex ; +} + +void Servo::detach() +{ + servos[this->servoIndex].Pin.isActive = false; + timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) { + finISR(timer); + } +} + +void Servo::write(int value) +{ + if(value < MIN_PULSE_WIDTH) + { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) + if(value < 0) value = 0; + if(value > 180) value = 180; + value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX()); + } + this->writeMicroseconds(value); +} + +void Servo::writeMicroseconds(int value) +{ + // calculate and store the values for the given channel + byte channel = this->servoIndex; + if( (channel < MAX_SERVOS) ) // ensure channel is valid + { + if( value < SERVO_MIN() ) // ensure pulse width is valid + value = SERVO_MIN(); + else if( value > SERVO_MAX() ) + value = SERVO_MAX(); + + value = value - TRIM_DURATION; + value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009 + + uint8_t oldSREG = SREG; + cli(); + servos[channel].ticks = value; + SREG = oldSREG; + } +} + +int Servo::read() // return the value as degrees +{ + return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180); +} + +int Servo::readMicroseconds() +{ + unsigned int pulsewidth; + if( this->servoIndex != INVALID_SERVO ) + pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION ; // 12 aug 2009 + else + pulsewidth = 0; + + return pulsewidth; +} + +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive ; +} + +#endif // ARDUINO_ARCH_AVR + diff --git a/hardware/freedom_e/libraries/Servo/src/avr/ServoTimers.h b/hardware/freedom_e/libraries/Servo/src/avr/ServoTimers.h new file mode 100644 index 0000000..9794c8e --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/avr/ServoTimers.h @@ -0,0 +1,59 @@ +/* + Servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 + Copyright (c) 2009 Michael Margolis. 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 +*/ + +/* + * Defines for 16 bit timers used with Servo library + * + * If _useTimerX is defined then TimerX is a 16 bit timer on the current board + * timer16_Sequence_t enumerates the sequence that the timers should be allocated + * _Nbr_16timers indicates how many 16 bit timers are available. + */ + +/** + * AVR Only definitions + * -------------------- + */ + +// Say which 16 bit timers can be used and in what order +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define _useTimer5 +#define _useTimer1 +#define _useTimer3 +#define _useTimer4 +typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t; + +#elif defined(__AVR_ATmega32U4__) +#define _useTimer1 +typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t; + +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +#define _useTimer3 +#define _useTimer1 +typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t; + +#elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega2561__) +#define _useTimer3 +#define _useTimer1 +typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t; + +#else // everything else +#define _useTimer1 +typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t; +#endif + diff --git a/hardware/freedom_e/libraries/Servo/src/hifive1/Servo.cpp b/hardware/freedom_e/libraries/Servo/src/hifive1/Servo.cpp new file mode 100644 index 0000000..de41906 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/hifive1/Servo.cpp @@ -0,0 +1,158 @@ +/* + This is a port of the Servo library for the HiFive1 + + Copyright (c) 2018 Western Digital Corporation or its affiliates. + + 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 + + SPDX-License-Identifier: LGPL-2.1-only + + This is a port of the Servo library https://github.com/arduino-libraries/Servo + Copyright (c) 2016 Arduino. All right reserved. + under GNU Lesser General Public License v2.1 or later (LGPL-2.1-or-later) +*/ + +#if defined(FREEDOM_E300_HIFIVE1) +#include +#include + +int pwmvalues[20] = {0}; // one for each pin for now! + +static servo_t servos[MAX_SERVOS]; // static array of servo structures + +uint8_t ServoCount = 0; // the total number of attached servos + +Servo::Servo() +{ + if (ServoCount < MAX_SERVOS) + this->servoIndex = ServoCount++; // assign a servo index to this instance + else + this->servoIndex = INVALID_SERVO; // too many servos +} + +uint8_t Servo::attach(int pin) +{ + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) +{ + if (this->servoIndex < MAX_SERVOS) + { + pinMode(pin, OUTPUT); // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + this->min = min; + this->max = max; + servos[this->servoIndex].Pin.isActive = true; + writeMicroseconds(DEFAULT_PULSE_WIDTH); + } + return this->servoIndex; +} + +void Servo::detach() +{ + servos[this->servoIndex].Pin.isActive = false; +} + +void Servo::write(int value) +{ + if (value < 0) + value = 0; + else if (value > 180) + value = 180; + value = map(value, 0, 180, this->min, this->max); + + writeMicroseconds(value); +} + +void Servo::writeMicroseconds(int us_value) +{ + if (us_value < this->min) + us_value = this->min; + else if (us_value > this->max) + us_value = this->min; + + int pin = servos[this->servoIndex].Pin.nbr; + + uint32_t pwmpin = digitalPinToBitMask(pin); + + // configure GPIO port for pin + GPIO_REG(GPIO_IOF_EN) |= pwmpin; + GPIO_REG(GPIO_IOF_SEL) |= pwmpin; + GPIO_REG(GPIO_OUTPUT_XOR) |= pwmpin; // flip output + + unsigned char pwmscale = 10; // 2^10=1024 + unsigned int period = (0.020 * F_CPU) / 1024; // 20ms period or freq=50Hz + unsigned int pulse = (us_value * (F_CPU / 1000000)) / 1000; // pulse width + + switch (pin) + { + case 3: + //case 4: // PWM_CMP0 + case 5: + case 6: + PWM1_REG(PWM_CFG) = 0; // disable pwm + PWM1_REG(PWM_COUNT) = 0; // counter increments at the F_CPU speed + PWM1_REG(PWM_CMP0) = period; // pin 4 + if (pin == 3) PWM1_REG(PWM_CMP1) = pulse; // pin 3 + else if (pin == 5) PWM1_REG(PWM_CMP2) = pulse; + else if (pin == 6) PWM1_REG(PWM_CMP3) = pulse; + PWM1_REG(PWM_CFG) = pwmscale | PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP; + break; + + //case 16:// GPIO10 PWM2_0 16 16bit J4 + case 17: // GPIO11 PWM2_1 17 + case 18: // GPIO12 PWM2_2 18 J3 & J4 + case 19: // GPIO13 PWM2_3 19 J3 & J4 + PWM2_REG(PWM_CFG) = 0; // disable pwm + PWM2_REG(PWM_COUNT) = 0; // counter increments at the F_CPU speed + PWM2_REG(PWM_CMP0) = period; + if (pin == 17) PWM2_REG(PWM_CMP1) = pulse; + else if (pin == 18) PWM2_REG(PWM_CMP2) = pulse; + else if (pin == 19) PWM2_REG(PWM_CMP3) = pulse; + PWM2_REG(PWM_CFG) = pwmscale | PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP; + break; + + // 8bit PWM0 cannot generate the 50Hz frequency required for the pattern. + //case 8: // GPIO0 PWM0_0 8 8bit J3 + case 9: // GPIO1 PWM0_1 9 + case 10: // GPIO2 PWM0_2 10 + case 11: // GPIO3 PWM0_3 11 + // 8bit PWM0 is not supported + break; + + default: + return; + } + pwmvalues[pin] = us_value; +} + +int Servo::read() // return the value as degrees +{ + return map(readMicroseconds(), this->min, this->max, 0, 180); +} + +int Servo::readMicroseconds() +{ + uint8_t pin = servos[this->servoIndex].Pin.nbr; + return pwmvalues[pin]; +} + +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive; +} + +#endif diff --git a/hardware/freedom_e/libraries/Servo/src/nrf52/Servo.cpp b/hardware/freedom_e/libraries/Servo/src/nrf52/Servo.cpp new file mode 100644 index 0000000..a49f093 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/nrf52/Servo.cpp @@ -0,0 +1,134 @@ +/* + Copyright (c) 2016 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 +*/ + +#if defined(ARDUINO_ARCH_NRF52) + +#include +#include + + +static servo_t servos[MAX_SERVOS]; // static array of servo structures + +uint8_t ServoCount = 0; // the total number of attached servos + + + +uint32_t group_pins[3][NRF_PWM_CHANNEL_COUNT]={{NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}, {NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}, {NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED, NRF_PWM_PIN_NOT_CONNECTED}}; +static uint16_t seq_values[3][NRF_PWM_CHANNEL_COUNT]={{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; + +Servo::Servo() +{ + if (ServoCount < MAX_SERVOS) { + this->servoIndex = ServoCount++; // assign a servo index to this instance + } else { + this->servoIndex = INVALID_SERVO; // too many servos + } + +} + +uint8_t Servo::attach(int pin) +{ + + return this->attach(pin, 0, 2500); +} + + +uint8_t Servo::attach(int pin, int min, int max) +{ + int servo_min, servo_max; + if (this->servoIndex < MAX_SERVOS) { + pinMode(pin, OUTPUT); // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + + if(min < servo_min) min = servo_min; + if (max > servo_max) max = servo_max; + this->min = min; + this->max = max; + + servos[this->servoIndex].Pin.isActive = true; + + } + return this->servoIndex; +} + +void Servo::detach() +{ + servos[this->servoIndex].Pin.isActive = false; +} + + +void Servo::write(int value) +{ + if (value < 0) + value = 0; + else if (value > 180) + value = 180; + value = map(value, 0, 180, MIN_PULSE, MAX_PULSE); + + writeMicroseconds(value); +} + + +void Servo::writeMicroseconds(int value) +{ + uint8_t channel, instance; + uint8_t pin = servos[this->servoIndex].Pin.nbr; + //instance of pwm module is MSB - look at VWariant.h + instance=(g_APinDescription[pin].ulPWMChannel & 0xF0)/16; + //index of pwm channel is LSB - look at VWariant.h + channel=g_APinDescription[pin].ulPWMChannel & 0x0F; + group_pins[instance][channel]=g_APinDescription[pin].ulPin; + NRF_PWM_Type * PWMInstance = instance == 0 ? NRF_PWM0 : (instance == 1 ? NRF_PWM1 : NRF_PWM2); + //configure pwm instance and enable it + seq_values[instance][channel]= value | 0x8000; + nrf_pwm_sequence_t const seq={ + seq_values[instance], + NRF_PWM_VALUES_LENGTH(seq_values), + 0, + 0 + }; + nrf_pwm_pins_set(PWMInstance, group_pins[instance]); + nrf_pwm_enable(PWMInstance); + nrf_pwm_configure(PWMInstance, NRF_PWM_CLK_125kHz, NRF_PWM_MODE_UP, 2500); // 20ms - 50Hz + nrf_pwm_decoder_set(PWMInstance, NRF_PWM_LOAD_INDIVIDUAL, NRF_PWM_STEP_AUTO); + nrf_pwm_sequence_set(PWMInstance, 0, &seq); + nrf_pwm_loop_set(PWMInstance, 0UL); + nrf_pwm_task_trigger(PWMInstance, NRF_PWM_TASK_SEQSTART0); +} + +int Servo::read() // return the value as degrees +{ + return map(readMicroseconds(), MIN_PULSE, MAX_PULSE, 0, 180); +} + +int Servo::readMicroseconds() +{ + uint8_t channel, instance; + uint8_t pin=servos[this->servoIndex].Pin.nbr; + instance=(g_APinDescription[pin].ulPWMChannel & 0xF0)/16; + channel=g_APinDescription[pin].ulPWMChannel & 0x0F; + // remove the 16th bit we added before + return seq_values[instance][channel] & 0x7FFF; +} + +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive; +} + +#endif // ARDUINO_ARCH_NRF52 \ No newline at end of file diff --git a/hardware/freedom_e/libraries/Servo/src/nrf52/ServoTimers.h b/hardware/freedom_e/libraries/Servo/src/nrf52/ServoTimers.h new file mode 100644 index 0000000..51759be --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/nrf52/ServoTimers.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2016 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 +*/ + +/* + * NRF52 doesn't use timer, but pwm. This file include definitions to keep + * compatibility with the Servo library standards. + */ + +#ifndef __SERVO_TIMERS_H__ +#define __SERVO_TIMERS_H__ + +/** + * NRF52 Only definitions + * --------------------- + */ + +#define MIN_PULSE 55 +#define MAX_PULSE 284 + +// define one timer in order to have MAX_SERVOS = 12 +typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t; + +#endif // __SERVO_TIMERS_H__ \ No newline at end of file diff --git a/hardware/freedom_e/libraries/Servo/src/sam/Servo.cpp b/hardware/freedom_e/libraries/Servo/src/sam/Servo.cpp new file mode 100644 index 0000000..21f901f --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/sam/Servo.cpp @@ -0,0 +1,283 @@ +/* + Copyright (c) 2013 Arduino LLC. 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 +*/ + +#if defined(ARDUINO_ARCH_SAM) + +#include +#include + +#define usToTicks(_us) (( clockCyclesPerMicrosecond() * _us) / 32) // converts microseconds to tick +#define ticksToUs(_ticks) (( (unsigned)_ticks * 32)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds + +#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays + +static servo_t servos[MAX_SERVOS]; // static array of servo structures + +uint8_t ServoCount = 0; // the total number of attached servos + +static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) + +// convenience macros +#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo +#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer +#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel +#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel + +#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo +#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo + +/************ static functions common to all instances ***********************/ + +//------------------------------------------------------------------------------ +/// Interrupt handler for the TC0 channel 1. +//------------------------------------------------------------------------------ +void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel); +#if defined (_useTimer1) +void HANDLER_FOR_TIMER1(void) { + Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1); +} +#endif +#if defined (_useTimer2) +void HANDLER_FOR_TIMER2(void) { + Servo_Handler(_timer2, TC_FOR_TIMER2, CHANNEL_FOR_TIMER2); +} +#endif +#if defined (_useTimer3) +void HANDLER_FOR_TIMER3(void) { + Servo_Handler(_timer3, TC_FOR_TIMER3, CHANNEL_FOR_TIMER3); +} +#endif +#if defined (_useTimer4) +void HANDLER_FOR_TIMER4(void) { + Servo_Handler(_timer4, TC_FOR_TIMER4, CHANNEL_FOR_TIMER4); +} +#endif +#if defined (_useTimer5) +void HANDLER_FOR_TIMER5(void) { + Servo_Handler(_timer5, TC_FOR_TIMER5, CHANNEL_FOR_TIMER5); +} +#endif + +void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel) +{ + // clear interrupt + tc->TC_CHANNEL[channel].TC_SR; + if (Channel[timer] < 0) { + tc->TC_CHANNEL[channel].TC_CCR |= TC_CCR_SWTRG; // channel set to -1 indicated that refresh interval completed so reset the timer + } else { + if (SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true) { + digitalWrite(SERVO(timer,Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated + } + } + + Channel[timer]++; // increment to the next channel + if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { + tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer,Channel[timer]).ticks; + if(SERVO(timer,Channel[timer]).Pin.isActive == true) { // check if activated + digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high + } + } + else { + // finished all channels so wait for the refresh period to expire before starting over + if( (tc->TC_CHANNEL[channel].TC_CV) + 4 < usToTicks(REFRESH_INTERVAL) ) { // allow a few ticks to ensure the next OCR1A not missed + tc->TC_CHANNEL[channel].TC_RA = (unsigned int)usToTicks(REFRESH_INTERVAL); + } + else { + tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + 4; // at least REFRESH_INTERVAL has elapsed + } + Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + } +} + +static void _initISR(Tc *tc, uint32_t channel, uint32_t id, IRQn_Type irqn) +{ + pmc_enable_periph_clk(id); + TC_Configure(tc, channel, + TC_CMR_TCCLKS_TIMER_CLOCK3 | // MCK/32 + TC_CMR_WAVE | // Waveform mode + TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC + + /* 84MHz, MCK/32, for 1.5ms: 3937 */ + TC_SetRA(tc, channel, 2625); // 1ms + + /* Configure and enable interrupt */ + NVIC_EnableIRQ(irqn); + // TC_IER_CPAS: RA Compare + tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPAS; + + // Enables the timer clock and performs a software reset to start the counting + TC_Start(tc, channel); +} + +static void initISR(timer16_Sequence_t timer) +{ +#if defined (_useTimer1) + if (timer == _timer1) + _initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1); +#endif +#if defined (_useTimer2) + if (timer == _timer2) + _initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2); +#endif +#if defined (_useTimer3) + if (timer == _timer3) + _initISR(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3, ID_TC_FOR_TIMER3, IRQn_FOR_TIMER3); +#endif +#if defined (_useTimer4) + if (timer == _timer4) + _initISR(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4, ID_TC_FOR_TIMER4, IRQn_FOR_TIMER4); +#endif +#if defined (_useTimer5) + if (timer == _timer5) + _initISR(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5, ID_TC_FOR_TIMER5, IRQn_FOR_TIMER5); +#endif +} + +static void finISR(timer16_Sequence_t timer) +{ +#if defined (_useTimer1) + TC_Stop(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1); +#endif +#if defined (_useTimer2) + TC_Stop(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2); +#endif +#if defined (_useTimer3) + TC_Stop(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3); +#endif +#if defined (_useTimer4) + TC_Stop(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4); +#endif +#if defined (_useTimer5) + TC_Stop(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5); +#endif +} + + +static boolean isTimerActive(timer16_Sequence_t timer) +{ + // returns true if any servo is active on this timer + for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) { + if(SERVO(timer,channel).Pin.isActive == true) + return true; + } + return false; +} + +/****************** end of static functions ******************************/ + +Servo::Servo() +{ + if (ServoCount < MAX_SERVOS) { + this->servoIndex = ServoCount++; // assign a servo index to this instance + servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values + } else { + this->servoIndex = INVALID_SERVO; // too many servos + } +} + +uint8_t Servo::attach(int pin) +{ + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) +{ + timer16_Sequence_t timer; + + if (this->servoIndex < MAX_SERVOS) { + pinMode(pin, OUTPUT); // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128 + this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS + this->max = (MAX_PULSE_WIDTH - max)/4; + // initialize the timer if it has not already been initialized + timer = SERVO_INDEX_TO_TIMER(servoIndex); + if (isTimerActive(timer) == false) { + initISR(timer); + } + servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive + } + return this->servoIndex; +} + +void Servo::detach() +{ + timer16_Sequence_t timer; + + servos[this->servoIndex].Pin.isActive = false; + timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) { + finISR(timer); + } +} + +void Servo::write(int value) +{ + // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) + if (value < MIN_PULSE_WIDTH) + { + if (value < 0) + value = 0; + else if (value > 180) + value = 180; + + value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX()); + } + writeMicroseconds(value); +} + +void Servo::writeMicroseconds(int value) +{ + // calculate and store the values for the given channel + byte channel = this->servoIndex; + if( (channel < MAX_SERVOS) ) // ensure channel is valid + { + if (value < SERVO_MIN()) // ensure pulse width is valid + value = SERVO_MIN(); + else if (value > SERVO_MAX()) + value = SERVO_MAX(); + + value = value - TRIM_DURATION; + value = usToTicks(value); // convert to ticks after compensating for interrupt overhead + servos[channel].ticks = value; + } +} + +int Servo::read() // return the value as degrees +{ + return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180); +} + +int Servo::readMicroseconds() +{ + unsigned int pulsewidth; + if (this->servoIndex != INVALID_SERVO) + pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION; + else + pulsewidth = 0; + + return pulsewidth; +} + +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive; +} + +#endif // ARDUINO_ARCH_SAM + diff --git a/hardware/freedom_e/libraries/Servo/src/sam/ServoTimers.h b/hardware/freedom_e/libraries/Servo/src/sam/ServoTimers.h new file mode 100644 index 0000000..13f736a --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/sam/ServoTimers.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2013 Arduino LLC. 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 +*/ + +/* + * Defines for 16 bit timers used with Servo library + * + * If _useTimerX is defined then TimerX is a 16 bit timer on the current board + * timer16_Sequence_t enumerates the sequence that the timers should be allocated + * _Nbr_16timers indicates how many 16 bit timers are available. + */ + +/** + * SAM Only definitions + * -------------------- + */ + +// For SAM3X: +#define _useTimer1 +#define _useTimer2 +#define _useTimer3 +#define _useTimer4 +#define _useTimer5 + +/* + TC0, chan 0 => TC0_Handler + TC0, chan 1 => TC1_Handler + TC0, chan 2 => TC2_Handler + TC1, chan 0 => TC3_Handler + TC1, chan 1 => TC4_Handler + TC1, chan 2 => TC5_Handler + TC2, chan 0 => TC6_Handler + TC2, chan 1 => TC7_Handler + TC2, chan 2 => TC8_Handler + */ + +#if defined (_useTimer1) +#define TC_FOR_TIMER1 TC1 +#define CHANNEL_FOR_TIMER1 0 +#define ID_TC_FOR_TIMER1 ID_TC3 +#define IRQn_FOR_TIMER1 TC3_IRQn +#define HANDLER_FOR_TIMER1 TC3_Handler +#endif +#if defined (_useTimer2) +#define TC_FOR_TIMER2 TC1 +#define CHANNEL_FOR_TIMER2 1 +#define ID_TC_FOR_TIMER2 ID_TC4 +#define IRQn_FOR_TIMER2 TC4_IRQn +#define HANDLER_FOR_TIMER2 TC4_Handler +#endif +#if defined (_useTimer3) +#define TC_FOR_TIMER3 TC1 +#define CHANNEL_FOR_TIMER3 2 +#define ID_TC_FOR_TIMER3 ID_TC5 +#define IRQn_FOR_TIMER3 TC5_IRQn +#define HANDLER_FOR_TIMER3 TC5_Handler +#endif +#if defined (_useTimer4) +#define TC_FOR_TIMER4 TC0 +#define CHANNEL_FOR_TIMER4 2 +#define ID_TC_FOR_TIMER4 ID_TC2 +#define IRQn_FOR_TIMER4 TC2_IRQn +#define HANDLER_FOR_TIMER4 TC2_Handler +#endif +#if defined (_useTimer5) +#define TC_FOR_TIMER5 TC0 +#define CHANNEL_FOR_TIMER5 0 +#define ID_TC_FOR_TIMER5 ID_TC0 +#define IRQn_FOR_TIMER5 TC0_IRQn +#define HANDLER_FOR_TIMER5 TC0_Handler +#endif + +typedef enum { _timer1, _timer2, _timer3, _timer4, _timer5, _Nbr_16timers } timer16_Sequence_t ; + diff --git a/hardware/freedom_e/libraries/Servo/src/samd/Servo.cpp b/hardware/freedom_e/libraries/Servo/src/samd/Servo.cpp new file mode 100644 index 0000000..42a3877 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/samd/Servo.cpp @@ -0,0 +1,297 @@ +/* + Copyright (c) 2015 Arduino LLC. 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 +*/ + +#if defined(ARDUINO_ARCH_SAMD) + +#include +#include + +#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / 16) // converts microseconds to tick +#define ticksToUs(_ticks) (((unsigned) _ticks * 16) / clockCyclesPerMicrosecond()) // converts from ticks back to microseconds + +#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays + +static servo_t servos[MAX_SERVOS]; // static array of servo structures + +uint8_t ServoCount = 0; // the total number of attached servos + +static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval) + +// convenience macros +#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo +#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer +#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel +#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel + +#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo +#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo + +#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY); + +/************ static functions common to all instances ***********************/ + +void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel, uint8_t intFlag); +#if defined (_useTimer1) +void HANDLER_FOR_TIMER1(void) { + Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, INTFLAG_BIT_FOR_TIMER_1); +} +#endif +#if defined (_useTimer2) +void HANDLER_FOR_TIMER2(void) { + Servo_Handler(_timer2, TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, INTFLAG_BIT_FOR_TIMER_2); +} +#endif + +void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel, uint8_t intFlag) +{ + if (currentServoIndex[timer] < 0) { + tc->COUNT16.COUNT.reg = (uint16_t) 0; + WAIT_TC16_REGS_SYNC(tc) + } else { + if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) { + digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW); // pulse this channel low if activated + } + } + + // Select the next servo controlled by this timer + currentServoIndex[timer]++; + + if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) { + if (SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) { // check if activated + digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high + } + + // Get the counter value + uint16_t tcCounterValue = tc->COUNT16.COUNT.reg; + WAIT_TC16_REGS_SYNC(tc) + + tc->COUNT16.CC[channel].reg = (uint16_t) (tcCounterValue + SERVO(timer, currentServoIndex[timer]).ticks); + WAIT_TC16_REGS_SYNC(tc) + } + else { + // finished all channels so wait for the refresh period to expire before starting over + + // Get the counter value + uint16_t tcCounterValue = tc->COUNT16.COUNT.reg; + WAIT_TC16_REGS_SYNC(tc) + + if (tcCounterValue + 4UL < usToTicks(REFRESH_INTERVAL)) { // allow a few ticks to ensure the next OCR1A not missed + tc->COUNT16.CC[channel].reg = (uint16_t) usToTicks(REFRESH_INTERVAL); + } + else { + tc->COUNT16.CC[channel].reg = (uint16_t) (tcCounterValue + 4UL); // at least REFRESH_INTERVAL has elapsed + } + WAIT_TC16_REGS_SYNC(tc) + + currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + } + + // Clear the interrupt + tc->COUNT16.INTFLAG.reg = intFlag; +} + +static inline void resetTC (Tc* TCx) +{ + // Disable TCx + TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; + WAIT_TC16_REGS_SYNC(TCx) + + // Reset TCx + TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; + WAIT_TC16_REGS_SYNC(TCx) + while (TCx->COUNT16.CTRLA.bit.SWRST); +} + +static void _initISR(Tc *tc, uint8_t channel, uint32_t id, IRQn_Type irqn, uint8_t gcmForTimer, uint8_t intEnableBit) +{ + // Enable GCLK for timer 1 (timer counter input clock) + GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(gcmForTimer)); + while (GCLK->STATUS.bit.SYNCBUSY); + + // Reset the timer + // TODO this is not the right thing to do if more than one channel per timer is used by the Servo library + resetTC(tc); + + // Set timer counter mode to 16 bits + tc->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16; + + // Set timer counter mode as normal PWM + tc->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_NPWM; + + // Set the prescaler factor to GCLK_TC/16. At nominal 48MHz GCLK_TC this is 3000 ticks per millisecond + tc->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16; + + // Count up + tc->COUNT16.CTRLBCLR.bit.DIR = 1; + WAIT_TC16_REGS_SYNC(tc) + + // First interrupt request after 1 ms + tc->COUNT16.CC[channel].reg = (uint16_t) usToTicks(1000UL); + WAIT_TC16_REGS_SYNC(tc) + + // Configure interrupt request + // TODO this should be changed if more than one channel per timer is used by the Servo library + NVIC_DisableIRQ(irqn); + NVIC_ClearPendingIRQ(irqn); + NVIC_SetPriority(irqn, 0); + NVIC_EnableIRQ(irqn); + + // Enable the match channel interrupt request + tc->COUNT16.INTENSET.reg = intEnableBit; + + // Enable the timer and start it + tc->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; + WAIT_TC16_REGS_SYNC(tc) +} + +static void initISR(timer16_Sequence_t timer) +{ +#if defined (_useTimer1) + if (timer == _timer1) + _initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1, GCM_FOR_TIMER_1, INTENSET_BIT_FOR_TIMER_1); +#endif +#if defined (_useTimer2) + if (timer == _timer2) + _initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2, GCM_FOR_TIMER_2, INTENSET_BIT_FOR_TIMER_2); +#endif +} + +static void finISR(timer16_Sequence_t timer) +{ +#if defined (_useTimer1) + // Disable the match channel interrupt request + TC_FOR_TIMER1->COUNT16.INTENCLR.reg = INTENCLR_BIT_FOR_TIMER_1; +#endif +#if defined (_useTimer2) + // Disable the match channel interrupt request + TC_FOR_TIMER2->COUNT16.INTENCLR.reg = INTENCLR_BIT_FOR_TIMER_2; +#endif +} + +static boolean isTimerActive(timer16_Sequence_t timer) +{ + // returns true if any servo is active on this timer + for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) { + if(SERVO(timer,channel).Pin.isActive == true) + return true; + } + return false; +} + +/****************** end of static functions ******************************/ + +Servo::Servo() +{ + if (ServoCount < MAX_SERVOS) { + this->servoIndex = ServoCount++; // assign a servo index to this instance + servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values + } else { + this->servoIndex = INVALID_SERVO; // too many servos + } +} + +uint8_t Servo::attach(int pin) +{ + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) +{ + timer16_Sequence_t timer; + + if (this->servoIndex < MAX_SERVOS) { + pinMode(pin, OUTPUT); // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128 + this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS + this->max = (MAX_PULSE_WIDTH - max)/4; + // initialize the timer if it has not already been initialized + timer = SERVO_INDEX_TO_TIMER(servoIndex); + if (isTimerActive(timer) == false) { + initISR(timer); + } + servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive + } + return this->servoIndex; +} + +void Servo::detach() +{ + timer16_Sequence_t timer; + + servos[this->servoIndex].Pin.isActive = false; + timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) { + finISR(timer); + } +} + +void Servo::write(int value) +{ + // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) + if (value < MIN_PULSE_WIDTH) + { + if (value < 0) + value = 0; + else if (value > 180) + value = 180; + + value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX()); + } + writeMicroseconds(value); +} + +void Servo::writeMicroseconds(int value) +{ + // calculate and store the values for the given channel + byte channel = this->servoIndex; + if( (channel < MAX_SERVOS) ) // ensure channel is valid + { + if (value < SERVO_MIN()) // ensure pulse width is valid + value = SERVO_MIN(); + else if (value > SERVO_MAX()) + value = SERVO_MAX(); + + value = value - TRIM_DURATION; + value = usToTicks(value); // convert to ticks after compensating for interrupt overhead + servos[channel].ticks = value; + } +} + +int Servo::read() // return the value as degrees +{ + return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180); +} + +int Servo::readMicroseconds() +{ + unsigned int pulsewidth; + if (this->servoIndex != INVALID_SERVO) + pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION; + else + pulsewidth = 0; + + return pulsewidth; +} + +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive; +} + +#endif // ARDUINO_ARCH_SAMD diff --git a/hardware/freedom_e/libraries/Servo/src/samd/ServoTimers.h b/hardware/freedom_e/libraries/Servo/src/samd/ServoTimers.h new file mode 100644 index 0000000..17acfb5 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/samd/ServoTimers.h @@ -0,0 +1,71 @@ +/* + Copyright (c) 2015 Arduino LLC. 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 +*/ + +/* + * Defines for 16 bit timers used with Servo library + * + * If _useTimerX is defined then TimerX is a 16 bit timer on the current board + * timer16_Sequence_t enumerates the sequence that the timers should be allocated + * _Nbr_16timers indicates how many 16 bit timers are available. + */ + +#ifndef __SERVO_TIMERS_H__ +#define __SERVO_TIMERS_H__ + +/** + * SAMD Only definitions + * --------------------- + */ + +// For SAMD: +#define _useTimer1 +//#define _useTimer2 // <- TODO do not activate until the code in Servo.cpp has been changed in order + // to manage more than one channel per timer on the SAMD architecture + +#if defined (_useTimer1) +#define TC_FOR_TIMER1 TC4 +#define CHANNEL_FOR_TIMER1 0 +#define INTENSET_BIT_FOR_TIMER_1 TC_INTENSET_MC0 +#define INTENCLR_BIT_FOR_TIMER_1 TC_INTENCLR_MC0 +#define INTFLAG_BIT_FOR_TIMER_1 TC_INTFLAG_MC0 +#define ID_TC_FOR_TIMER1 ID_TC4 +#define IRQn_FOR_TIMER1 TC4_IRQn +#define HANDLER_FOR_TIMER1 TC4_Handler +#define GCM_FOR_TIMER_1 GCM_TC4_TC5 +#endif +#if defined (_useTimer2) +#define TC_FOR_TIMER2 TC4 +#define CHANNEL_FOR_TIMER2 1 +#define INTENSET_BIT_FOR_TIMER_2 TC_INTENSET_MC1 +#define INTENCLR_BIT_FOR_TIMER_2 TC_INTENCLR_MC1 +#define ID_TC_FOR_TIMER2 ID_TC4 +#define IRQn_FOR_TIMER2 TC4_IRQn +#define HANDLER_FOR_TIMER2 TC4_Handler +#define GCM_FOR_TIMER_2 GCM_TC4_TC5 +#endif + +typedef enum { +#if defined (_useTimer1) + _timer1, +#endif +#if defined (_useTimer2) + _timer2, +#endif + _Nbr_16timers } timer16_Sequence_t; + +#endif // __SERVO_TIMERS_H__ diff --git a/hardware/freedom_e/libraries/Servo/src/stm32f4/Servo.cpp b/hardware/freedom_e/libraries/Servo/src/stm32f4/Servo.cpp new file mode 100644 index 0000000..01d05d9 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/stm32f4/Servo.cpp @@ -0,0 +1,194 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010, LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +#if defined(ARDUINO_ARCH_STM32F4) + +#include "ServoTimers.h" + +#include "boards.h" +#include "io.h" +#include "pwm.h" +#include "math.h" + +// 20 millisecond period config. For a 1-based prescaler, +// +// (prescaler * overflow / CYC_MSEC) msec = 1 timer cycle = 20 msec +// => prescaler * overflow = 20 * CYC_MSEC +// +// This picks the smallest prescaler that allows an overflow < 2^16. +#define MAX_OVERFLOW ((1 << 16) - 1) +#define CYC_MSEC (1000 * CYCLES_PER_MICROSECOND) +#define TAU_MSEC 20 +#define TAU_USEC (TAU_MSEC * 1000) +#define TAU_CYC (TAU_MSEC * CYC_MSEC) +#define SERVO_PRESCALER (TAU_CYC / MAX_OVERFLOW + 1) +#define SERVO_OVERFLOW ((uint16)round((double)TAU_CYC / SERVO_PRESCALER)) + +// Unit conversions +#define US_TO_COMPARE(us) ((uint16)map((us), 0, TAU_USEC, 0, SERVO_OVERFLOW)) +#define COMPARE_TO_US(c) ((uint32)map((c), 0, SERVO_OVERFLOW, 0, TAU_USEC)) +#define ANGLE_TO_US(a) ((uint16)(map((a), this->minAngle, this->maxAngle, \ + this->minPW, this->maxPW))) +#define US_TO_ANGLE(us) ((int16)(map((us), this->minPW, this->maxPW, \ + this->minAngle, this->maxAngle))) + +Servo::Servo() { + this->resetFields(); +} + +bool Servo::attach(uint8 pin, uint16 minPW, uint16 maxPW, int16 minAngle, int16 maxAngle) +{ + // SerialUSB.begin(115200); + // SerialUSB.println(MAX_OVERFLOW); + + + timer_dev *tdev = PIN_MAP[pin].timer_device; + + analogWriteResolution(16); + + int prescaler = 6; + int overflow = 65400; + int minPW_correction = 300; + int maxPW_correction = 300; + + pinMode(pin, OUTPUT); + + + if (tdev == NULL) { + // don't reset any fields or ASSERT(0), to keep driving any + // previously attach()ed servo. + return false; + } + + if ( (tdev == TIMER1) || (tdev == TIMER8) || (tdev == TIMER10) || (tdev == TIMER11)) + { + prescaler = 54; + overflow = 65400; + minPW_correction = 40; + maxPW_correction = 50; + } + + if ( (tdev == TIMER2) || (tdev == TIMER3) || (tdev == TIMER4) || (tdev == TIMER5) ) + { + prescaler = 6; + overflow = 64285; + minPW_correction = 370; + maxPW_correction = 350; + } + + if ( (tdev == TIMER6) || (tdev == TIMER7) ) + { + prescaler = 6; + overflow = 65400; + minPW_correction = 0; + maxPW_correction = 0; + } + + if ( (tdev == TIMER9) || (tdev == TIMER12) || (tdev == TIMER13) || (tdev == TIMER14) ) + { + prescaler = 6; + overflow = 65400; + minPW_correction = 30; + maxPW_correction = 0; + } + + if (this->attached()) { + this->detach(); + } + + this->pin = pin; + this->minPW = (minPW + minPW_correction); + this->maxPW = (maxPW + maxPW_correction); + this->minAngle = minAngle; + this->maxAngle = maxAngle; + + timer_pause(tdev); + timer_set_prescaler(tdev, prescaler); // prescaler is 1-based + timer_set_reload(tdev, overflow); + timer_generate_update(tdev); + timer_resume(tdev); + + return true; +} + +bool Servo::detach() { + if (!this->attached()) { + return false; + } + + timer_dev *tdev = PIN_MAP[this->pin].timer_device; + uint8 tchan = PIN_MAP[this->pin].timer_channel; + timer_set_mode(tdev, tchan, TIMER_DISABLED); + + this->resetFields(); + + return true; +} + +void Servo::write(int degrees) { + degrees = constrain(degrees, this->minAngle, this->maxAngle); + this->writeMicroseconds(ANGLE_TO_US(degrees)); +} + +int Servo::read() const { + int a = US_TO_ANGLE(this->readMicroseconds()); + // map() round-trips in a weird way we mostly correct for here; + // the round-trip is still sometimes off-by-one for write(1) and + // write(179). + return a == this->minAngle || a == this->maxAngle ? a : a + 1; +} + +void Servo::writeMicroseconds(uint16 pulseWidth) { + if (!this->attached()) { + ASSERT(0); + return; + } + pulseWidth = constrain(pulseWidth, this->minPW, this->maxPW); + analogWrite(this->pin, US_TO_COMPARE(pulseWidth)); +} + +uint16 Servo::readMicroseconds() const { + if (!this->attached()) { + ASSERT(0); + return 0; + } + + stm32_pin_info pin_info = PIN_MAP[this->pin]; + uint16 compare = timer_get_compare(pin_info.timer_device, + pin_info.timer_channel); + + return COMPARE_TO_US(compare); +} + +void Servo::resetFields(void) { + this->pin = NOT_ATTACHED; + this->minAngle = MIN_ANGLE; + this->maxAngle = MAX_ANGLE; + this->minPW = MIN_PULSE_WIDTH; + this->maxPW = MAX_PULSE_WIDTH; +} + +#endif diff --git a/hardware/freedom_e/libraries/Servo/src/stm32f4/ServoTimers.h b/hardware/freedom_e/libraries/Servo/src/stm32f4/ServoTimers.h new file mode 100644 index 0000000..12d55b7 --- /dev/null +++ b/hardware/freedom_e/libraries/Servo/src/stm32f4/ServoTimers.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010, LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + + /* + * Arduino srl - www.arduino.org + * 2017 Feb 23: Edited by Francesco Alessi (alfran) - francesco@arduino.org + */ +#ifndef _SERVO_H_ +#define _SERVO_H_ + +#include "types.h" +#include "timer.h" + +#include "wiring.h" /* hack for IDE compile */ + +/* + * Note on Arduino compatibility: + * + * In the Arduino implementation, PWM is done "by hand" in the sense + * that timer channels are hijacked in groups and an ISR is set which + * toggles Servo::attach()ed pins using digitalWrite(). + * + * While this scheme allows any pin to drive a servo, it chews up + * cycles and complicates the programmer's notion of when a particular + * timer channel will be in use. + * + * This implementation only allows Servo instances to attach() to pins + * that already have a timer channel associated with them, and just + * uses pwmWrite() to drive the wave. + * + * This introduces an incompatibility: while the Arduino + * implementation of attach() returns the affected channel on success + * and 0 on failure, this one returns true on success and false on + * failure. + * + * RC Servos expect a pulse every 20ms. Since periods are set for + * entire timers, rather than individual channels, attach()ing a Servo + * to a pin can interfere with other pins associated with the same + * timer. As always, your board's pin map is your friend. + */ + +// Pin number of unattached pins +#define NOT_ATTACHED (-1) + +#define _Nbr_16timers 14 // mumber of STM32F469 Timers +#define SERVOS_PER_TIMER 4 // Number of timer channels + + +// Default min/max pulse widths (in microseconds) and angles (in +// degrees). Values chosen for Arduino compatibility. These values +// are part of the public API; DO NOT CHANGE THEM. +#define MIN_ANGLE 0 +#define MAX_ANGLE 180 + +#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo +#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo + +/** Class for interfacing with RC servomotors. */ +class Servo { +public: + /** + * @brief Construct a new Servo instance. + * + * The new instance will not be attached to any pin. + */ + Servo(); + + /** + * @brief Associate this instance with a servomotor whose input is + * connected to pin. + * + * If this instance is already attached to a pin, it will be + * detached before being attached to the new pin. This function + * doesn't detach any interrupt attached with the pin's timer + * channel. + * + * @param pin Pin connected to the servo pulse wave input. This + * pin must be capable of PWM output. + * + * @param minPulseWidth Minimum pulse width to write to pin, in + * microseconds. This will be associated + * with a minAngle degree angle. Defaults to + * SERVO_DEFAULT_MIN_PW = 544. + * + * @param maxPulseWidth Maximum pulse width to write to pin, in + * microseconds. This will be associated + * with a maxAngle degree angle. Defaults to + * SERVO_DEFAULT_MAX_PW = 2400. + * + * @param minAngle Target angle (in degrees) associated with + * minPulseWidth. Defaults to + * SERVO_DEFAULT_MIN_ANGLE = 0. + * + * @param maxAngle Target angle (in degrees) associated with + * maxPulseWidth. Defaults to + * SERVO_DEFAULT_MAX_ANGLE = 180. + * + * @sideeffect May set pinMode(pin, PWM). + * + * @return true if successful, false when pin doesn't support PWM. + */ + + bool attach(uint8 pin, + uint16 minPulseWidth=MIN_PULSE_WIDTH, + uint16 maxPulseWidth=MAX_PULSE_WIDTH, + int16 minAngle=MIN_ANGLE, + int16 maxAngle=MAX_ANGLE); + /** + * @brief Stop driving the servo pulse train. + * + * If not currently attached to a motor, this function has no effect. + * + * @return true if this call did anything, false otherwise. + */ + bool detach(); + + /** + * @brief Set the servomotor target angle. + * + * @param angle Target angle, in degrees. If the target angle is + * outside the range specified at attach() time, it + * will be clamped to lie in that range. + * + * @see Servo::attach() + */ + void write(int angle); + + /** + * @brief Set the pulse width, in microseconds. + * + * @param pulseWidth Pulse width to send to the servomotor, in + * microseconds. If outside of the range + * specified at attach() time, it is clamped to + * lie in that range. + * + * @see Servo::attach() + */ + void writeMicroseconds(uint16 pulseWidth); + + /** + * Get the servomotor's target angle, in degrees. This will + * lie inside the range specified at attach() time. + * + * @see Servo::attach() + */ + int read() const; + + /** + * Get the current pulse width, in microseconds. This will + * lie within the range specified at attach() time. + * + * @see Servo::attach() + */ + uint16 readMicroseconds() const; + + + /** + * @brief Check if this instance is attached to a servo. + * @return true if this instance is attached to a servo, false otherwise. + * @see Servo::attachedPin() + */ + bool attached() const { return this->pin != NOT_ATTACHED; } + + /** + * @brief Get the pin this instance is attached to. + * @return Pin number if currently attached to a pin, NOT_ATTACHED + * otherwise. + * @see Servo::attach() + */ + int attachedPin() const { return this->pin; } + +private: + int16 pin; + uint16 minPW; + uint16 maxPW; + int16 minAngle; + int16 maxAngle; + + void resetFields(void); +}; + + + +#endif /* _SERVO_H_ */ diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/README.md b/hardware/freedom_e/libraries/SimpleTouchscreen/README.md new file mode 100644 index 0000000..3adf549 --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/README.md @@ -0,0 +1,78 @@ +# README + +The simple touch screen library for SPI "2.8 TFT SPI 240x320 V1.1" LCD display +panel like [https://www.amazon.com/HiLetgo-240X320-Resolution-Display-ILI9341/dp/B073R7BH1B](https://www.amazon.com/HiLetgo-240X320-Resolution-Display-ILI9341/dp/B073R7BH1B) + +![alt text](DSC_0058.JPG) + +- Calibrated for SPI "2.8 TFT SPI 240x320 V1.1" ILI9341 LCD display panel bundled +with XPT2046 touchscreen controller. +- Returns raw (in touchscreen resolution) or normilized (in LCD resolution) X and Y reading. +- Tested with HiFive1 and Arduino Uno boards. +- Tested with Adafruit_ILI9341 and Adafruit_GFX.h LCD libraries. +- For HiFive1 we do not need 5V to 3.3V voltage converters, just set IOREF jumper +to 3.3V. The pins were connected as follows: + +|TFTLCD |HiFive1| +|----------|-------| +|T_IRQ |D2 | +|T_DO |D12 | +|T_DI |D11 | +|T_CS |D7 | +|T_CLK |D13 | +|SDO(MISO) |D12 | +|LED |3.3V | +|CLK |D13 | +|SDI(MOSI) |D11 | +|D/C |D9 | +|RESET |RESET | +|CS |D10 | +|GND |GND | +|VCC |3.3V | + +For LCD I used the standard Adafruit_ILI9341 library with default SPI clock of 24MHz. +Both, HiFive1 and TFT panel SPIs can be run at 40MHz clock when HiFive1 is +configured to run at 256MHz or 320MHz CPU clock. + +### Why yet another touchscreen library? +Looked for touch screen library for "2.8 TFT SPI 240x320 V1.1" aftermarket panel +to use with RISC-V based HiFive1 board. Examined URTouch and XPT2046 libraries +but nothing worked for HiFive1 board out of the box. On top of that, none of the +libraries provided touchscreen to LCD resolution normalization. +So, I wrote the SimpleTouchscreen library with intention to keep it simple :-) + +### Why to calibrate? +- The resolution of the touchscreen and LCD are different. In our example, +the 320x240 LCD is bundled with 4096x4096 touch screen. +- The touch screen panel is assembled on top of the LCD panel. Different +manufacturers do this differently. The start coordinates of the touch screen +panel not necessary match the start coordinates of the LCD panel. Moreover, the +size of the touch screen can exceed the size of the LCD screen. For example, the +X=0, Y=0 of the above LCD corresponds to X=390, Y=220 of the touch screen panel. + +### How to calibrate +- You will need to figure out new calibration values (LowX, LowY, HighX, HighY) +for your TFT panel and pass them into begin() method. This will override the +default calibration values. +- Run SimpleTouchCalibrate.ino. You should see the raw X and Y values shown on +the screen upon every touch. +- Make sure that the display is landscape oriented (which is a default). +- Use touch screen pen to press in the top left corner of the screen and write +down the LowX and LowY values. +``` +For example, you got X=300 and Y=200 +``` +- Press in the bottom right corner of the screen and record the new +HighX and HighY values. +``` +Let's assume you got X=3300 and Y=3200. +``` +- Pass these values into the begin() method: +``` +ts.begin(300, 3300, 200, 3200); +``` +- If the geometry of the screen is different, pass the new geometry, like width +and height into begin() as well: +``` +ts.begin(300, 3300, 200, 3200, 480, 320); +``` diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/SimpleTouchscreen.cpp b/hardware/freedom_e/libraries/SimpleTouchscreen/SimpleTouchscreen.cpp new file mode 100644 index 0000000..fd654e2 --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/SimpleTouchscreen.cpp @@ -0,0 +1,177 @@ +/* + SimpleTouchscreen library. + + Copyright (c) 2018 Western Digital Corporation or its affiliates. + SPDX-License-Identifier: LGPL-2.1-only + + 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. + + 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 + + Written by Michael Zaidman , 2018 +*/ + +#include "SimpleTouchscreen.h" + + +void SimpleTouchscreen::begin(bool calibrate) { + + _calibrate = calibrate; + pinMode(_pinSS, OUTPUT); + pinMode(_pinIRQ, INPUT); + digitalWrite(_pinSS, HIGH); +} + +void SimpleTouchscreen::begin(uint16_t tsLsLowX, uint16_t tsLsHighX, + uint16_t tsLsLowY, uint16_t tsLsHighY, + uint16_t lcdLsX, uint16_t lcdLsY) { + + _tsLandscapeLowX = tsLsLowX; + _tsLandscapeLowY = tsLsLowY; + + _lcdLandscapeX = lcdLsX; + _lcdLandscapeY = lcdLsY; + + _ts2lsdRatioX = (tsLsHighX - tsLsLowX) / lcdLsX; + _ts2lsdRatioY = (tsLsHighY - tsLsLowY) / lcdLsY; + + begin(false); +} + +bool SimpleTouchscreen::dataAvailable() { + + if (!digitalRead(_pinIRQ)) + return true; + + return false; +} + +uint16_t SimpleTouchscreen::_get16(uint8_t cmd) { + + uint8_t data1, data2; + uint16_t datax; + + SPI.transfer(cmd); + data1 = SPI.transfer(0x00); + data2 = SPI.transfer(0x00); + datax = (((data1 << 8) + data2) >> 3) & 0xFFF; + + return datax; +} + +uint16_t SimpleTouchscreen::_getX() { + return (_get16(0x90)); +} + +uint16_t SimpleTouchscreen::_getY() { + return (_get16(0xD0)); +} + +uint16_t SimpleTouchscreen::_getZ() { + return (4095 - _get16(0xD0)); +} + +void SimpleTouchscreen::_round(int16_t *val, int16_t max) { + if (*val < 0) *val = 0; + if (*val > max) *val = max; +} + +void SimpleTouchscreen::read() { + + if (digitalRead(_pinIRQ)) + return; + + int sumx = 0, tmpx = 0; + int sumy = 0, tmpy = 0; + int minx1 = 0x8000, maxx1 = 0; + int minx2 = 0x8000, maxx2 = 0; + int miny1 = 0x8000, maxy1 = 0; + int miny2 = 0x8000, maxy2 = 0; + int samples = 0; + + SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); + digitalWrite(_pinSS, LOW); + for (int i = 0; i < SAMPLES; i++) + if (_getZ() > 300) { + tmpx = _getX(); + tmpy = _getY(); + + sumx += tmpx; + sumy += tmpy; + if (tmpx < minx1) { + minx2 = minx1; + minx1 = tmpx; + } else if (tmpx < minx2) { + minx2 = tmpx; + } + if (tmpx > maxx1) { + maxx2 = maxx1; + maxx1 = tmpx; + } else if (tmpx > maxx2) { + maxx2 = tmpx; + } + if (tmpy < miny1) { + miny2 = miny1; + miny1 = tmpy; + } else if (tmpy < miny2) { + miny2 = tmpy; + } + if (tmpy > maxy1) { + maxy2 = maxy1; + maxy1 = tmpy; + } else if (tmpy > maxy2) { + maxy2 = tmpy; + } + samples++; + } + digitalWrite(_pinSS, HIGH); + SPI.endTransaction(); + + if (samples != SAMPLES) + return; + + _x = (sumx - minx1 - minx2 - maxx1 - maxx2) / GOOD_SAMPLES; + _y = (sumy - miny1 - miny2 - maxy1 - maxy2) / GOOD_SAMPLES; + + if (_calibrate) + return; + + _x = (_x - _tsLandscapeLowX) / _ts2lsdRatioX; + _y = (_y - _tsLandscapeLowY) / _ts2lsdRatioY; + + int temp = _x; + switch (_rotation) { + case PORTRATE_0: + _x = _y; + _y = _lcdLandscapeX - temp; + break; + case LANDSCAPE_90: + _x = _lcdLandscapeX - _x; + _y = _lcdLandscapeY - _y; + break; + case PORTRATE_180: + _x = _lcdLandscapeY - _y; + _y = temp; + break; + case LANDSCAPE_270: + break; + } + + if (IS_LANDSCAPE(_rotation)) { + _round(&_x, _lcdLandscapeX); + _round(&_y, _lcdLandscapeY); + } else { + _round(&_x, _lcdLandscapeY); + _round(&_y, _lcdLandscapeX); + } +} diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/SimpleTouchscreen.h b/hardware/freedom_e/libraries/SimpleTouchscreen/SimpleTouchscreen.h new file mode 100644 index 0000000..f711dab --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/SimpleTouchscreen.h @@ -0,0 +1,118 @@ +/* + SimpleTouchscreen library. + + Copyright (c) 2018 Western Digital Corporation or its affiliates. + SPDX-License-Identifier: LGPL-2.1-only + + 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. + + 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 + + + Tailored for SPI "2.8 TFT SPI 240x320 V1.1" ILI9341 based LCD display panel + with XPT2046 touch screen controller. + + See README.md for how to calibrate for different panels. + + Tested with HiFive1 and Arduino Uno boards. + Tested with Adafruit_ILI9341 and Adafruit_GFX.h LCD libraries. + + For HiFive1 we don't need 5V to 3.3V voltage converters, just set IOREF jumper + to 3.3V. The pins were connected as following: + + TFTLCD HiFive1 + ====== ======= + T_IRQ D2 + T_DO D12 + T_DI D11 + T_CS D7 + T_CLK D13 + SDO(MISO) D12 + LED 3.3V + CLK D13 + SDI(MOSI) D11 + D/C D9 + RESET RESET + CS D10 + GND GND + VCC 3.3V + + Written by Michael Zaidman , 2018 +*/ +#ifndef TOUCHSCREEN_H +#define TOUCHSCREEN_H + +#include +#include + +// Default display geometry +#define LCD_LANDSCAPE_X 320 +#define LCD_LANDSCAPE_Y 240 + +// Touch screen calibration for for SPI "2.8 TFT SPI 240x320 V1.1" LCD panel. +// See README.md to figure out how to calibrate library for different panels. +#define TS_LANDSCAPE_X_LO 390 +#define TS_LANDSCAPE_Y_LO 220 +#define TS_LANDSCAPE_X_HI 3920 +#define TS_LANDSCAPE_Y_HI 3900 + +// Rotation positions, same encoding as in Adafruit_ILI9341 library. +#define PORTRATE_0 0 +#define LANDSCAPE_90 1 +#define PORTRATE_180 2 +#define LANDSCAPE_270 3 +#define IS_LANDSCAPE(rotation) (rotation & 1) + +// Reading accuracy +#define SAMPLES 8 // keep it power of 2 + 4 +#define GOOD_SAMPLES (SAMPLES - 4) + +class SimpleTouchscreen { + int16_t _x, _y; + uint8_t _rotation; + uint8_t _pinSS, _pinIRQ; + + // LCD landscape geometry (width and height resolution) + uint16_t _lcdLandscapeX, _lcdLandscapeY; + + // Touch screen calibration + uint16_t _tsLandscapeLowX, _tsLandscapeLowY; + uint16_t _ts2lsdRatioX, _ts2lsdRatioY; + + uint16_t _get16(uint8_t cmd); + void _round(int16_t *val, int16_t max); + uint16_t _getX(); + uint16_t _getY(); + uint16_t _getZ(); + bool _calibrate; + +public: + SimpleTouchscreen( uint8_t tIRQ, uint8_t tSS) : _rotation(LANDSCAPE_270) + { _pinIRQ = tIRQ; _pinSS = tSS; } + void setRotation(uint8_t n) { _rotation = n % 4; } + + void begin(bool calibrate); + void begin(uint16_t tsLsLowX = TS_LANDSCAPE_X_LO, + uint16_t tsLsHighX = TS_LANDSCAPE_X_HI, + uint16_t tsLsLowY = TS_LANDSCAPE_Y_LO, + uint16_t tsLsHighY = TS_LANDSCAPE_Y_HI, + uint16_t lcdLsX = LCD_LANDSCAPE_X, + uint16_t lcdLsY = LCD_LANDSCAPE_Y); + + bool dataAvailable(); + void read(); + uint16_t getX() { return (this->_x); }; + uint16_t getY() { return (this->_y); }; +}; + +#endif diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchCalibrate/SimpleTouchCalibrate.ino b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchCalibrate/SimpleTouchCalibrate.ino new file mode 100644 index 0000000..1ef6f82 --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchCalibrate/SimpleTouchCalibrate.ino @@ -0,0 +1,77 @@ +/* + Example of the SimpleTouchscreen library usage - find calibration values for + SPI TFT LCD panel bundled with XPT2046 touch screen controller. + + Tested with SPI "2.8 TFT SPI 240x320 V1.1" ILI9341 based LCD display panel + with XPT2046 touch screen controller and HiFive1 board from SiFive. + + The example uses standard Adafruit libraries to operate the LCD. + + For HiFive1 we don't need 5V to 3.3V voltage converters, just set IOREF jumper + to 3.3V. See README for LCD panel to HiFive1 connection details. + + For Arduino UNO you will need a voltage level conversion. + + By default, library is calibrated for SPI "2.8 TFT SPI 240x320 V1.1" panel. + See README for how to calibrate it for different panels. + + Both, HiFive1 board and the TFT panel are capable of running at 40MHz SPI + clock when connected directly as described in README. You can try this by + passing 40000000 into lcd.begin(). + + Michael Zaidman , 2018 + + This example code is in the public domain. +*/ + +//#include "Adafruit_GFX.h" +#include "Adafruit_ILI9341.h" +#include "SimpleTouchscreen.h" + +//LCD +#define TFT_DC 9 +#define TFT_CS 10 + +//Touchscreen +#define T_SS 7 +#define T_IRQ 2 + +Adafruit_ILI9341 lcd = Adafruit_ILI9341(TFT_CS, TFT_DC); +SimpleTouchscreen ts = SimpleTouchscreen(T_IRQ, T_SS); + +word x, y; + +void setRotation (byte rotation) { + lcd.setRotation(rotation); + ts.setRotation(rotation); +} + +void paintResults() { + lcd.setTextColor(ILI9341_BLACK); + lcd.setTextSize(2); + lcd.fillRoundRect(20, 100, 90, 25, 10, ILI9341_YELLOW); + lcd.setCursor(25, 105); + lcd.print("x="); + lcd.print(x); + lcd.fillRoundRect(120, 100, 90, 25, 10, ILI9341_YELLOW); + lcd.setCursor(125, 105); + lcd.print("y="); + lcd.print(y); +} + +void setup() { + lcd.begin(12000000); + ts.begin(true); + setRotation(LANDSCAPE_270); + lcd.fillScreen(ILI9341_BLACK); + paintResults(); +} + +void loop() { + if (ts.dataAvailable()) { + ts.read(); + x = ts.getX(); + y = ts.getY(); + paintResults(); + } +} diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchDraw/SimpleTouchDraw.ino b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchDraw/SimpleTouchDraw.ino new file mode 100644 index 0000000..308ebdf --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchDraw/SimpleTouchDraw.ino @@ -0,0 +1,71 @@ +/* + Example of the SimpleTouchscreen library usage - drawing with touch screen pen. + + Tested with SPI "2.8 TFT SPI 240x320 V1.1" ILI9341 based LCD display panel + with XPT2046 touch screen controller and HiFive1 board from SiFive. + + The example uses standard Adafruit libraries to operate the LCD. + + For HiFive1 we don't need 5V to 3.3V voltage converters, just set IOREF jumper + to 3.3V. See README for LCD panel to HiFive1 connection details. + + For Arduino UNO you will need a voltage level conversion. + + By default, library is calibrated for SPI "2.8 TFT SPI 240x320 V1.1" panel. + See README for how to calibrate it for different panels. + + Both, HiFive1 board and the TFT panel are capable of running at 40MHz SPI + clock when connected directly as described in README. You can try this by + passing 40000000 into lcd.begin(). + + Michael Zaidman , 2018 + + This example code is in the public domain. +*/ + +#include "Adafruit_ILI9341.h" +#include "SimpleTouchscreen.h" + +//LCD +#define TFT_DC 9 +#define TFT_CS 10 + +//Touchscreen +#define T_SS 7 +#define T_IRQ 2 + +Adafruit_ILI9341 lcd = Adafruit_ILI9341(TFT_CS, TFT_DC); +SimpleTouchscreen ts = SimpleTouchscreen(T_IRQ, T_SS); + +Adafruit_GFX_Button bt; + +word x, y; + +void setRotation (byte rotation) { + lcd.setRotation(rotation); + ts.setRotation(rotation); +} + +void setup() { + lcd.begin(12000000); + ts.begin(); + setRotation(LANDSCAPE_270); + lcd.fillScreen(ILI9341_BLACK); + bt.initButton(&lcd, 28, 20, 50, 30, ILI9341_YELLOW, + ILI9341_RED, ILI9341_WHITE, "Clr", 2); + bt.drawButton(); +} + +void loop() { + if (ts.dataAvailable()) { + ts.read(); + x = ts.getX(); + y = ts.getY(); + if (bt.contains(x, y)) { + lcd.fillScreen(ILI9341_BLACK); + bt.drawButton(); + } else { + lcd.drawFastVLine(x, y, 1, ILI9341_YELLOW); + } + } +} diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchDrawPalete/SimpleTouchDrawPalete.ino b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchDrawPalete/SimpleTouchDrawPalete.ino new file mode 100644 index 0000000..56d15d3 --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchDrawPalete/SimpleTouchDrawPalete.ino @@ -0,0 +1,91 @@ +/* + Example of the SimpleTouchscreen library usage - drawing with touch screen pen. + + Tested with SPI "2.8 TFT SPI 240x320 V1.1" ILI9341 based LCD display panel + with XPT2046 touch screen controller and HiFive1 board from SiFive. + + The example uses standard Adafruit libraries to operate the LCD. + + For HiFive1 we don't need 5V to 3.3V voltage converters, just set IOREF jumper + to 3.3V. See README for LCD panel to HiFive1 connection details. + + For Arduino UNO you will need a voltage level conversion. + + By default, library is calibrated for SPI "2.8 TFT SPI 240x320 V1.1" panel. + See README for how to calibrate it for different panels. + + Both, HiFive1 board and the TFT panel are capable of running at 40MHz SPI + clock when connected directly as described in README. You can try this by + passing 40000000 into lcd.begin(). + + Michael Zaidman , 2018 + + This example code is in the public domain. +*/ + +#include "Adafruit_ILI9341.h" +#include "SimpleTouchscreen.h" + +//LCD +#define TFT_DC 9 +#define TFT_CS 10 + +//Touchscreen +#define T_SS 7 +#define T_IRQ 2 + +Adafruit_ILI9341 lcd = Adafruit_ILI9341(TFT_CS, TFT_DC); +SimpleTouchscreen ts = SimpleTouchscreen(T_IRQ, T_SS); + +#define BUTTONS 7 + +char btLabels[BUTTONS][4] = { "Clr", "", "", "", "", "", "", }; +uint16_t btColors[BUTTONS] = { ILI9341_BLACK, ILI9341_RED, + ILI9341_GREEN, ILI9341_BLUE, + ILI9341_YELLOW, ILI9341_CYAN, + ILI9341_MAGENTA, }; +Adafruit_GFX_Button bt[BUTTONS]; + +int color = ILI9341_YELLOW; + +void setRotation (byte rotation) { + lcd.setRotation(rotation); + ts.setRotation(rotation); +} + +void initScreen() { + lcd.fillScreen(ILI9341_BLACK); + for (int i = 0; i < BUTTONS; i++) + bt[i].drawButton(); +} + +void setup() { + lcd.begin(12000000); + ts.begin(); + setRotation(LANDSCAPE_90); + for (int i = 0; i < BUTTONS; i++) + bt[i].initButton(&lcd, 320 - 20, 15 + (i * 35), 40, 30, + ILI9341_WHITE, btColors[i], ILI9341_WHITE, btLabels[i], 2); + initScreen(); +} + +void loop() { + int x, y; + + if (ts.dataAvailable()) { + ts.read(); + x = ts.getX(); + y = ts.getY(); + if (bt[0].contains(x, y)) + initScreen(); + else { + for (int i = 1; i < BUTTONS; i++) { + if (bt[i].contains(x, y)) { + color = btColors[i]; + break; + } + } + lcd.drawFastVLine(x, y, 1, color); + } + } +} diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchTest/SimpleTouchTest.ino b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchTest/SimpleTouchTest.ino new file mode 100644 index 0000000..8bf6ad6 --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/examples/SimpleTouchTest/SimpleTouchTest.ino @@ -0,0 +1,94 @@ +/* + Example of the SimpleTouchscreen library usage. + + Tested with SPI "2.8 TFT SPI 240x320 V1.1" ILI9341 based LCD display panel + with XPT2046 touch screen controller and HiFive1 board from SiFive. + + The example uses standard Adafruit libraries to operate the LCD. + + For HiFive1 we don't need 5V to 3.3V voltage converters, just set IOREF jumper + to 3.3V. See README for LCD panel to HiFive1 connection details. + + For Arduino UNO you will need a voltage level conversion. + + By default, library is calibrated for SPI "2.8 TFT SPI 240x320 V1.1" panel. + See README for how to calibrate it for different panels. + + Both, HiFive1 board and the TFT panel are capable of running at 40MHz SPI + clock when connected directly as described in README. You can try this by + passing 40000000 into lcd.begin(). + + Michael Zaidman , 2018 + + This example code is in the public domain. +*/ + +#include "Adafruit_ILI9341.h" +#include "SimpleTouchscreen.h" + +//LCD +#define TFT_DC 9 +#define TFT_CS 10 + +//Touchscreen +#define T_SS 7 +#define T_IRQ 2 + +Adafruit_ILI9341 lcd = Adafruit_ILI9341(TFT_CS, TFT_DC); +SimpleTouchscreen ts = SimpleTouchscreen(T_IRQ, T_SS); + +Adafruit_GFX_Button bt; + +word x, y; + +void setRotation (byte rotation) { + lcd.setRotation(rotation); + ts.setRotation(rotation); +} + +void setup() { + lcd.begin(12000000); + ts.begin(); + setRotation(LANDSCAPE_270); + bt.initButton(&lcd, 60, 40, 80, 46, ILI9341_YELLOW, + ILI9341_RED, ILI9341_WHITE, "TEST", 2); + lcd.fillScreen(ILI9341_BLACK); + bt.drawButton(); + paintResults(); +} + +void loop() { + if (ts.dataAvailable()) { + ts.read(); + x = ts.getX(); + y = ts.getY(); + + paintResults(); + + if (bt.contains(x, y)) { + lcd.fillScreen(ILI9341_RED); + paintResults(); + bt.drawButton(true); + while(!ts.dataAvailable()); + lcd.fillScreen(ILI9341_BLACK); + paintResults(); + bt.drawButton(false); + lcd.setTextColor(ILI9341_WHITE); + lcd.setCursor(20, 90); + lcd.print("Touch anywhere..."); + } + } +} + +void paintResults() { + lcd.setTextColor(ILI9341_BLACK); + lcd.setTextSize(2); + lcd.fillRoundRect(20, 120, 90, 25, 10, ILI9341_YELLOW); + lcd.setCursor(25, 125); + lcd.print("x="); + lcd.print(x); + lcd.fillRoundRect(120, 120, 90, 25, 10, ILI9341_YELLOW); + lcd.setCursor(125, 125); + lcd.print("y="); + lcd.print(y); +} diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/keywords.txt b/hardware/freedom_e/libraries/SimpleTouchscreen/keywords.txt new file mode 100644 index 0000000..1d3a831 --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/keywords.txt @@ -0,0 +1,27 @@ +####################################### +# Syntax Coloring Map SimpleTouchscreen +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SimpleTouchscreen KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +setRotation KEYWORD2 +begin KEYWORD2 +dataAvailable KEYWORD2 +read KEYWORD2 +getX KEYWORD2 +getY KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +PORTRATE_0 LITERAL1 +LANDSCAPE_90 LITERAL1 +PORTRATE_180 LITERAL1 +LANDSCAPE_270 LITERAL1 diff --git a/hardware/freedom_e/libraries/SimpleTouchscreen/library.properties b/hardware/freedom_e/libraries/SimpleTouchscreen/library.properties new file mode 100644 index 0000000..739775b --- /dev/null +++ b/hardware/freedom_e/libraries/SimpleTouchscreen/library.properties @@ -0,0 +1,9 @@ +name=SimpleTouchscreen +version=1.0 +author=Michael Zaidman +maintainer=Michael Zaidman +sentence=Simple touch screen library. +paragraph= +category=Display +url= +architectures=* diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/README.md b/hardware/freedom_e/libraries/SlowSoftI2CMaster/README.md new file mode 100644 index 0000000..6d89e93 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/README.md @@ -0,0 +1,75 @@ +# SlowSoftI2CMaster + + +Another bit-banging I2C library, very similar to *SoftI2CMaster*, allowing you to use any Arduino pins for SDA and SCL. The difference to the SoftI2CMaster library is that it is entirely written in C++, so you can use it also on ARM boards, such as Zero and Due. + + +## Features + +This library has the following features: +* architecture independent, compatible with all MCUs supported by the Arduino IDE +* can make use of any pin +* supports multiple I2C buses +* internal MCU pullup resistors can be used +* timeout on ACK polling for busy devices (new!) +* supports only master mode +* no bus arbitration (i.e., only one master allowed on bus) +* no clock stretching supported +* **slow** (70 kHz on a Zero, 45 kHz on an UNO) +* optional Wire library compatible interface +* LGPL license +* +* Updated 26/9/2018: +* Added support for custom clock rates. +* After testing on the RiscV based HiFive1 board by SiFive. +* The max I2C clock rates and limitations are: +* 140 KHz at 320 MHz CPU Clock on HiFive1 +* 110 KHz at 256 MHz CPU Clock on HiFive1 +* Higher rates than these currently aren't supported. +*(These limitations were tested by using the HiFive1 board as a Master device +* and using an Uno board as a hardware slave): + +## Usage + +The library can be used in a similar way as the [SoftI2CMaster](https://github.com/felias-fogg/SoftI2CMaster) library. See the description of the methods in the documentation of this library. +However, here all parameters and options are not defined as compile-time constants, but they are passed as parameters when creating a new instance. + +## Example + + // Simple sketch to read out one register of an I2C device + #include + + // create new instance with A4 as SDA, A5 as SCL and enable internal pullups + SlowSoftI2CMaster si = SlowSoftI2CMaster(A4, A5, true); + + #define I2C_7BITADDR 0x68 // DS1307 + #define MEMLOC 0x0A + #define ADDRLEN 1 + + + void setup(void) { + Serial.begin(38400); + if (!si.i2c_init()) // initialize I2C module + Serial.println("I2C init failed"); + } + + void loop(void){ + if (!si.i2c_start((I2C_7BITADDR<<1)|I2C_WRITE)) { // init transfer + Serial.println("I2C device busy"); + return; + } + si.i2c_write(MEMLOC); // send memory to device + si.i2c_rep_start((I2C_7BITADDR<<1)|I2C_READ); // restart for reading + byte val = si.i2c_read(true); // read one byte and send NAK afterwards + si.i2c_stop(); // stop communication + Serial.println(val); + delay(1000); + } + + +You can use the library in a way such that the internal pullup resistors of the AVR are used (as shown in the example). However, note that this implies that when switching between HIGH and LOW the bus will temporarily in a high-impedance state, which is outside the I2C specification. Furthermore, the internal pull-ups are around 50k and so might not deliver enough current. Be careful when using this option and consider it as a potential source of errors. + +## Alternative interface +As in the case of the SoftI2CMaster library, there exists a [wrapper library](https://github.com/felias-fogg/SlowSoftWire) that emulates the functionality of the Wire class. + +This software is published under the [LGPL](http://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/SlowSoftI2CMaster.cpp b/hardware/freedom_e/libraries/SlowSoftI2CMaster/SlowSoftI2CMaster.cpp new file mode 100644 index 0000000..8ac0a4e --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/SlowSoftI2CMaster.cpp @@ -0,0 +1,167 @@ +/* Arduino Slow Software I2C Master + Copyright (c) 2017 Bernhard Nebel. + + 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 3 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 + +#if defined (FREEDOM_E300_HIFIVE1) + #if (F_CPU == 320000000) + #define SKEW 7 + #elif (F_CPU == 256000000) + #define SKEW 9 + #else + #define SKEW = 1 + #endif +#elif defined (__AVR_ATmega328P__) + #define SKEW 23 +#else + #define SKEW = 1 +#endif + +SlowSoftI2CMaster::SlowSoftI2CMaster(uint8_t sda, uint8_t scl, bool pullup) { + _sda = sda; + _scl = scl; + _pullup = pullup; +} + +// Init function. Needs to be called once in the beginning. +// Returns false if SDA or SCL are low, which probably means +// a I2C bus lockup or that the lines are not pulled up. +bool SlowSoftI2CMaster::i2c_init(void) { + _delay = 2; + digitalWrite(_sda, LOW); + digitalWrite(_scl, LOW); + setHigh(_sda); + setHigh(_scl); + if (digitalRead(_sda) == LOW || digitalRead(_scl) == LOW) return false; + return true; +} + +// Set clock functions: clock is in Hz. +// The function is used for setting a custom clock. +// SKEW is the deviation of the clock input from the desired clock. +// If input clock is below max I2C clock rate then _delay = 2. +// 2 is the lowest delay that doesn't produce errors during a stress test. +void SlowSoftI2CMaster::setClock(uint32_t clock) +{ + if(clock < (1000000 / SKEW)) // Maximum possible I2C clock rate + _delay = (1000000 / clock) - SKEW; + else + _delay = 2; +} + +// Start transfer function: is the 8-bit I2C address (including the R/W +// bit). +// Return: true if the slave replies with an "acknowledge", false otherwise +bool SlowSoftI2CMaster::i2c_start(uint8_t addr) { + setLow(_sda); + delayMicroseconds(_delay); + setLow(_scl); + return i2c_write(addr); +} + +// Try to start transfer until an ACK is returned +bool SlowSoftI2CMaster::i2c_start_wait(uint8_t addr) { + long retry = I2C_MAXWAIT; + while (!i2c_start(addr)) { + i2c_stop(); + if (--retry == 0) return false; + } + return true; +} + +// Repeated start function: After having claimed the bus with a start condition, +// you can address another or the same chip again without an intervening +// stop condition. +// Return: true if the slave replies with an "acknowledge", false otherwise +bool SlowSoftI2CMaster::i2c_rep_start(uint8_t addr) { + setHigh(_sda); + setHigh(_scl); + delayMicroseconds(_delay); + return i2c_start(addr); +} + +// Issue a stop condition, freeing the bus. +void SlowSoftI2CMaster::i2c_stop(void) { + setLow(_sda); + delayMicroseconds(_delay); + setHigh(_scl); + delayMicroseconds(_delay); + setHigh(_sda); + delayMicroseconds(_delay); +} + +// Write one byte to the slave chip that had been addressed +// by the previous start call. is the byte to be sent. +// Return: true if the slave replies with an "acknowledge", false otherwise +bool SlowSoftI2CMaster::i2c_write(uint8_t value) { + for (uint8_t curr = 0X80; curr != 0; curr >>= 1) { + if (curr & value) setHigh(_sda); else setLow(_sda); + setHigh(_scl); + delayMicroseconds(_delay); + setLow(_scl); + } + // get Ack or Nak + setHigh(_sda); + setHigh(_scl); + delayMicroseconds(_delay); + uint8_t ack = digitalRead(_sda); + setLow(_scl); + delayMicroseconds(_delay); + setLow(_sda); + return ack == 0; +} + +// Read one byte. If is true, we send a NAK after having received +// the byte in order to terminate the read sequence. +uint8_t SlowSoftI2CMaster::i2c_read(bool last) { + uint8_t b = 0; + setHigh(_sda); + for (uint8_t i = 0; i < 8; i++) { + b <<= 1; + delayMicroseconds(_delay); + setHigh(_scl); + if (digitalRead(_sda)) b |= 1; + setLow(_scl); + } + if (last) setHigh(_sda); else setLow(_sda); + setHigh(_scl); + delayMicroseconds(_delay); + setLow(_scl); + delayMicroseconds(_delay); + setLow(_sda); + return b; +} + +void SlowSoftI2CMaster::setLow(uint8_t pin) { + noInterrupts(); + if (_pullup) + digitalWrite(pin, LOW); + pinMode(pin, OUTPUT); + interrupts(); +} + + +void SlowSoftI2CMaster::setHigh(uint8_t pin) { + noInterrupts(); + if (_pullup) + pinMode(pin, INPUT_PULLUP); + else + pinMode(pin, INPUT); + interrupts(); +} \ No newline at end of file diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/SlowSoftI2CMaster.h b/hardware/freedom_e/libraries/SlowSoftI2CMaster/SlowSoftI2CMaster.h new file mode 100644 index 0000000..5a81f6e --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/SlowSoftI2CMaster.h @@ -0,0 +1,52 @@ +/* Arduino Slow Software I2C Master + Copyright (c) 2017 Bernhard Nebel. + + 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 3 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 SLOW_SOFT_I2C_MASTER_H +#define SLOW_SOFT_I2C_MASTER_H + +#include +#include + +#define I2C_READ 1 +#define I2C_WRITE 0 +#define BUFFER_LENGTH 32 +#define I2C_MAXWAIT 5000 + +class SlowSoftI2CMaster { + public: + SlowSoftI2CMaster(uint8_t sda, uint8_t scl, bool internal_pullup = false); + bool i2c_init(void); + void setClock(uint32_t clock); + bool i2c_start(uint8_t addr); + bool i2c_start_wait(uint8_t addr); + bool i2c_rep_start(uint8_t addr); + void i2c_stop(void); + bool i2c_write(uint8_t value); + uint8_t i2c_read(bool last); + bool error; + + private: + void setHigh(uint8_t pin); + void setLow(uint8_t pin); + uint8_t _sda; + uint8_t _scl; + uint32_t _delay; // in microseconds + bool _pullup; +}; + +#endif diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/BMA020Slow/BMA020Slow.ino b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/BMA020Slow/BMA020Slow.ino new file mode 100644 index 0000000..8ae2d98 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/BMA020Slow/BMA020Slow.ino @@ -0,0 +1,90 @@ +// -*- c++ -*- +// Simple sketch to read out BMA020 using SlowSoftI2CMaster + +// Readout BMA020 chip + + +#include +#include + + +SlowSoftI2CMaster si = SlowSoftI2CMaster(A4, A5); + +#define BMAADDR 0x70 +#define LEDPIN 13 + +int xval, yval, zval; + + +boolean setControlBits(uint8_t cntr) +{ + if (!si.i2c_start(BMAADDR | I2C_WRITE)) { + return false; + } + if (!si.i2c_write(0x0A)) { + return false; + } + if (!si.i2c_write(cntr)) { + return false; + } + si.i2c_stop(); + Serial.println(F("stop done")); + return true; +} + +boolean initBma(void) +{ + if (!setControlBits(B00000010)) return false; + delay(100); + return true; +} + +int readOneVal(boolean last) +{ + uint8_t msb, lsb; + lsb = si.i2c_read(false); + msb = si.i2c_read(last); + if (last) si.i2c_stop(); + return (int)((msb<<8)|lsb)/64; +} + +boolean readBma(void) +{ + xval = 0xFFFF; + yval = 0xFFFF; + zval = 0xFFFF; + if (!si.i2c_start(BMAADDR | I2C_WRITE)) return false; + if (!si.i2c_write(0x02)) return false; + if (!si.i2c_rep_start(BMAADDR | I2C_READ)) return false; + xval = readOneVal(false); + yval = readOneVal(false); + zval = readOneVal(true); + return true; +} + + + +//------------------------------------------------------------------------------ +void setup(void) { + Serial.begin(19200); + si.i2c_init(); + if (!initBma()) { + Serial.println(F("INIT ERROR")); + while (1); + } + +} + +void loop(void){ + if (!readBma()) { + Serial.println(F("READ ERROR")); + while (1); + } + Serial.print(F("X=")); + Serial.print(xval); + Serial.print(F(" Y=")); + Serial.print(yval); + Serial.print(F(" Z=")); + Serial.println(zval); + delay(300); +} diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/Eeprom24AA1025Slow/Eeprom24AA1025Slow.ino b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/Eeprom24AA1025Slow/Eeprom24AA1025Slow.ino new file mode 100644 index 0000000..a991497 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/Eeprom24AA1025Slow/Eeprom24AA1025Slow.ino @@ -0,0 +1,298 @@ +// Sketch to explore 24AA1024 using SlowSoftI2CMaster + +#include + +SlowSoftI2CMaster si = SlowSoftI2CMaster(A4, A5, false); + +#define EEPROMADDR 0xA6 // set by jumper (A0 and A1 = High) +#define MAXADDR 0x1FFFF +#define MAXTESTADDR 0x007FF + +//------------------------------------------------------------------------------ +/* + * read one byte from address + */ +boolean readEEPROM(unsigned long address, uint8_t *byte) { + // issue a start condition, send device address and write direction bit + if (!si.i2c_start(EEPROMADDR | I2C_WRITE | (address&0x10000 ? 8 : 0) )) return false; + + // send the address + if (!si.i2c_write((address>>8)&0xFF)) return false; + if (!si.i2c_write(address&0xFF)) return false; + + // issue a repeated start condition, send device address and read direction bit + if (!si.i2c_rep_start(EEPROMADDR | I2C_READ | (address&0x10000 ? 8 : 0) ))return false; + + *byte = si.i2c_read(true); + + si.i2c_stop(); + return true; +} +//------------------------------------------------------------------------------ +/* + *burst read + */ +boolean readBurstEEPROM(unsigned long start, unsigned long stop) { + // does not handle the transition from 0x0FFFF to 0x10000 + // since we only use it for performance evaluation, we do not care! + unsigned long addr = start; + uint8_t byte; + // issue a start condition, send device address and write direction bit + if (!si.i2c_start(EEPROMADDR | I2C_WRITE | (addr&0x10000 ? 8 : 0) )) return false; + + // send the address + if (!si.i2c_write((addr>>8)&0xFF)) return false; + if (!si.i2c_write(addr&0xFF)) return false; + + // issue a repeated start condition, send device address and read direction bit + if (!si.i2c_rep_start(EEPROMADDR | I2C_READ | (addr&0x10000 ? 8 : 0) ))return false; + addr++; + while (addr++ < stop) byte = si.i2c_read(false); + byte = si.i2c_read(true); + si.i2c_stop(); + return true; +} + + +//------------------------------------------------------------------------------ + +/* + * write 1 byte to 'address' in eeprom + */ +boolean writeEEPROM(long unsigned address, uint8_t byte) { + // issue a start condition, send device address and write direction bit + if (!si.i2c_start(EEPROMADDR | I2C_WRITE | (address&0x10000 ? 8 : 0))) return false; + + // send the address + if (!si.i2c_write((address>>8)&0xFF)) return false; + if (!si.i2c_write(address&0xFF)) return false; + + + // send data to EEPROM + if (!si.i2c_write(byte)) return false; + + // issue a stop condition + si.i2c_stop(); + + delay(6); + + return true; +} + +//------------------------------------------------------------------------------ +/* + * delete eeprom + */ +boolean deleteEEPROM(long unsigned from, unsigned long to, uint8_t byte, + boolean poll) { + + unsigned long tempto, i; + boolean firstpage = true; + + while (from <= to) { + tempto = ((from/128)+1)*128-1; + if (tempto > to) tempto = to; + if (firstpage || !poll) { + if (!si.i2c_start(EEPROMADDR | I2C_WRITE | (from&0x10000 ? 8 : 0))) + return false; + } else si.i2c_start_wait(EEPROMADDR | I2C_WRITE | (from&0x10000 ? 8 : 0)); + // send the address + if (!si.i2c_write((from>>8)&0xFF)) return false; + if (!si.i2c_write(from&0xFF)) return false; + + + // send data to EEPROM + for (i=from; i<=tempto; i++) + if (!si.i2c_write(byte)) return false; + // issue a stop condition + si.i2c_stop(); + + // wait for ack again + if (!poll) delay(6); + + from = tempto+1; + firstpage = false; + } + return true; +} + +//------------------------------------------------------------------------------ +boolean performanceTest() { + unsigned long eeaddr; + unsigned long startmicros, endmicros; + int avgtime; + boolean OK = true; + uint8_t byte; + + Serial.println(F("\nPerformance test:")); + + Serial.println(F("Sequential reads ...")); + startmicros = micros(); + OK &= readBurstEEPROM(0,MAXTESTADDR); + endmicros = micros(); + Serial.print(F("Time: ")); + avgtime = (endmicros-startmicros)/(MAXTESTADDR+1); + Serial.print(avgtime); + Serial.println(F(" micro secs/byte")); + + Serial.println(F("Random reads ...")); + startmicros = micros(); + for (eeaddr = 0; eeaddr <= MAXTESTADDR; eeaddr++) + OK &= readEEPROM(eeaddr,&byte); + endmicros = micros(); + Serial.print(F("Time: ")); + avgtime = (endmicros-startmicros)/(MAXTESTADDR+1); + Serial.print(avgtime); + Serial.println(F(" micro secs/byte")); + + Serial.println(F("Random writes ...")); + startmicros = micros(); + for (eeaddr = 0; eeaddr <= MAXTESTADDR; eeaddr++) + OK &= writeEEPROM(eeaddr,0x55); + endmicros = micros(); + Serial.print(F("Time: ")); + avgtime = (endmicros-startmicros)/(MAXTESTADDR+1); + Serial.print(avgtime); + Serial.println(F(" micro secs/byte")); + + Serial.println(F("Page writes with wait ...")); + startmicros = micros(); + OK &= deleteEEPROM(0,MAXTESTADDR,0xFF,false); + endmicros = micros(); + Serial.print(F("Time: ")); + avgtime = (endmicros-startmicros)/(MAXTESTADDR+1); + Serial.print(avgtime); + Serial.println(F(" micro secs/byte")); + + Serial.println(F("Page writes with poll ...")); + startmicros = micros(); + OK &= deleteEEPROM(0,MAXTESTADDR,0xFF,true); + endmicros = micros(); + Serial.print(F("Time: ")); + avgtime = (endmicros-startmicros)/(MAXTESTADDR+1); + Serial.print(avgtime); + Serial.println(F(" micro secs/byte")); + + return OK; +} +//------------------------------------------------------------------------------ + +unsigned long parseHex() { + unsigned long result = 0L; + char byte = '\0'; + + // while (hexdigit(byte)) { + while (byte != '\r' && byte != '#') { + while (!Serial.available()); + byte = Serial.read(); + // if (!hexdigit(byte)) break; + if (byte == '\r' || byte == '#') break; + if (byte >= 'a' && byte <= 'f') + byte = byte -'a' + 'A'; + if ((byte >= '0' && byte <= '9') || + (byte >= 'A' && byte <= 'F')) { + Serial.print(byte); + if (byte >= '0' && byte <= '9') byte = byte - '0'; + else byte = byte - 'A' + 10; + result = result * 16; + result = result + byte; + } + } + Serial.println(); + return result; +} + +void help (void) { + Serial.println(); + Serial.println(F("r - read byte from address")); + Serial.println(F("w - write byte to address")); + Serial.println(F("d - delete from start address to end address")); + Serial.println(F("l - list memory range")); + Serial.println(F("p - test performance")); + Serial.println(F("h - help message")); + Serial.println(F("Finish all numeric inputs with '#'")); +} + +//------------------------------------------------------------------------------ + + + +void setup(void) { + Serial.begin(115200); + Serial.println(F("\n\nTest program for EEPROM 24AA1025 (SlowSoftI2CMaster)")); + help(); +} + + +void loop(void) { + char cmd; + uint8_t byte; + boolean noterror; + unsigned long addr, toaddr; + + while (!Serial.available()); + cmd = Serial.read(); + switch (cmd) { + case 'r': Serial.print(F("Read from addr: ")); + addr = parseHex(); + Serial.println(F("Reading...")); + noterror = readEEPROM(addr,&byte); + Serial.print(addr,HEX); + Serial.print(F(": ")); + if (byte < 0x10) Serial.print("0"); + Serial.println(byte,HEX); + if (!noterror) Serial.println(F("Error while reading")); + break; + case 'w': + Serial.print(F("Write to addr: ")); + addr = parseHex(); + Serial.print(F("Value: ")); + byte = parseHex(); + Serial.println(F("Writing...")); + noterror = writeEEPROM(addr,byte); + if (!noterror) Serial.println(F("Error while reading")); + break; + case 'd': + Serial.print(F("Delete from addr: ")); + addr = parseHex(); + Serial.print(F("to addr: ")); + toaddr = parseHex(); + Serial.print(F("Value: ")); + byte = parseHex(); + Serial.print(F("Deleting ... ")); + noterror = deleteEEPROM(addr,toaddr,byte,false); + Serial.println(F("...done")); + if (!noterror) Serial.println(F("Error while deleting")); + break; + case 'l': + Serial.print(F("List from addr: ")); + addr = parseHex(); + Serial.print(F("to addr: ")); + toaddr = parseHex(); + while (addr <= toaddr) { + noterror = readEEPROM(addr,&byte); + Serial.print(addr,HEX); + Serial.print(F(": ")); + if (byte < 0x10) Serial.print("0"); + Serial.println(byte,HEX); + if (!noterror) Serial.println(F("Error while reading")); + addr++; + } + break; + case 'p': + noterror = performanceTest(); + if (!noterror) Serial.println(F("Error while executing performance test")); + break; + case 'h': + help(); + break; + case '\r': + case '\n': + case ' ': + break; + default: + Serial.println(F("Unknown command")); + Serial.println(); + help(); + } +} diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/I2CScanSlow/I2CScanSlow.ino b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/I2CScanSlow/I2CScanSlow.ino new file mode 100644 index 0000000..d1d41ff --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/I2CScanSlow/I2CScanSlow.ino @@ -0,0 +1,63 @@ +// -*-c++-*- +// Scan I2C bus for device responses + +#include +#include + +// use true as third arg, if you do not have external pullups +SlowSoftI2CMaster si = SlowSoftI2CMaster(A4, A5, true); + +//------------------------------------------------------------------------------ + +void setup(void) { + Serial.begin(19200); + Serial.println(F("Intializing ...")); + if (!si.i2c_init()) + Serial.println(F("Initialization error. SDA or SCL are low")); + else + Serial.println(F("...done")); +} + +void loop(void) +{ + uint8_t add = 0; + int found = false; + Serial.println("Scanning ..."); + + Serial.println(" 8-bit 7-bit addr"); + // try read + do { + if (si.i2c_start(add | I2C_READ)) { + found = true; + si.i2c_read(true); + si.i2c_stop(); + Serial.print("Read: 0x"); + if (add < 0x0F) Serial.print(0, HEX); + Serial.print(add+I2C_READ, HEX); + Serial.print(" 0x"); + if (add>>1 < 0x0F) Serial.print(0, HEX); + Serial.println(add>>1, HEX); + } else si.i2c_stop(); + add += 2; + } while (add); + + // try write + add = 0; + do { + if (si.i2c_start(add | I2C_WRITE)) { + found = true; + si.i2c_stop(); + Serial.print("Write: 0x"); + if (add < 0x0F) Serial.print(0, HEX); + Serial.print(add+I2C_WRITE, HEX); + Serial.print(" 0x"); + if (add>>1 < 0x0F) Serial.print(0, HEX); + Serial.println(add>>1, HEX); + } else si.i2c_stop(); + si.i2c_stop(); + add += 2; + } while (add); + if (!found) Serial.println(F("No I2C device found.")); + Serial.println("Done\n\n"); + delay(1000); +} diff --git a/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/simpleSlowSoftI2C/simpleSlowSoftI2C.ino b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/simpleSlowSoftI2C/simpleSlowSoftI2C.ino new file mode 100644 index 0000000..a01a1ac --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftI2CMaster/examples/simpleSlowSoftI2C/simpleSlowSoftI2C.ino @@ -0,0 +1,30 @@ +// -*- c++ -*- +// Simple sketch to read out one register of an I2C device +#include + +SlowSoftI2CMaster si = SlowSoftI2CMaster(A4, A5, true); + +#define I2C_7BITADDR 0x68 // DS1307 +#define MEMLOC 0x0A +#define ADDRLEN 1 + + +void setup(void) { + Serial.begin(38400); + if (!si.i2c_init()) + Serial.println("I2C init failed"); +} + +void loop(void){ + if (!si.i2c_start((I2C_7BITADDR<<1)|I2C_WRITE)) { + Serial.println("I2C device busy"); + return; + } + for (byte i=1; i + +SlowSoftWire::SlowSoftWire(uint8_t sda, uint8_t scl, bool internal_pullup): + si2c(sda, scl, internal_pullup) { } + +void SlowSoftWire::begin(void) { + rxBufferIndex = 0; + rxBufferLength = 0; + error = 0; + transmitting = false; + + si2c.i2c_init(); +} + +void SlowSoftWire::setClock(uint32_t clock) { + si2c.setClock(clock); +} + +void SlowSoftWire::beginTransmission(uint8_t address) { + if (transmitting) { + error = (si2c.i2c_rep_start((address<<1)|I2C_WRITE) ? 0 : 2); + } else { + error = (si2c.i2c_start((address<<1)|I2C_WRITE) ? 0 : 2); + } + // indicate that we are transmitting + transmitting = 1; +} + +void SlowSoftWire::beginTransmission(int address) { + beginTransmission((uint8_t)address); +} + +uint8_t SlowSoftWire::endTransmission(uint8_t sendStop) +{ + uint8_t transError = error; + if (sendStop) { + si2c.i2c_stop(); + transmitting = 0; + } + error = 0; + return transError; +} + +size_t SlowSoftWire::write(uint8_t data) { + if (si2c.i2c_write(data)) { + return 1; + } else { + if (error == 0) error = 3; + return 0; + } +} + +size_t SlowSoftWire::write(const uint8_t *data, size_t quantity) { + size_t trans = 0; + for(size_t i = 0; i < quantity; ++i){ + trans += write(data[i]); + } + return trans; +} + +uint8_t SlowSoftWire::requestFrom(uint8_t address, uint8_t quantity, + uint32_t iaddress, uint8_t isize, uint8_t sendStop) { + uint8_t localerror = 0; + if (isize > 0) { + // send internal address; this mode allows sending a repeated start to access + // some devices' internal registers. This function is executed by the hardware + // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) + beginTransmission(address); + // the maximum size of internal address is 3 bytes + if (isize > 3){ + isize = 3; + } + // write internal register address - most significant byte first + while (isize-- > 0) + write((uint8_t)(iaddress >> (isize*8))); + endTransmission(false); + } + // clamp to buffer length + if(quantity > BUFFER_LENGTH){ + quantity = BUFFER_LENGTH; + } + localerror = !si2c.i2c_rep_start((address<<1) | I2C_READ); + if (error == 0 && localerror) error = 2; + // perform blocking read into buffer + for (uint8_t cnt=0; cnt < quantity; cnt++) + rxBuffer[cnt] = si2c.i2c_read(cnt == quantity-1); + // set rx buffer iterator vars + rxBufferIndex = 0; + rxBufferLength = quantity; + if (sendStop) { + transmitting = 0; + si2c.i2c_stop(); + } + return quantity; +} + +uint8_t SlowSoftWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop); +} + +uint8_t SlowSoftWire::requestFrom(int address, int quantity, int sendStop) { + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); +} + + +uint8_t SlowSoftWire::requestFrom(uint8_t address, uint8_t quantity) { + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); +} + +uint8_t SlowSoftWire::requestFrom(int address, int quantity) { + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); +} + +int SlowSoftWire::available(void) { + return rxBufferLength - rxBufferIndex; +} + +int SlowSoftWire::read(void) { + int value = -1; + if(rxBufferIndex < rxBufferLength){ + value = rxBuffer[rxBufferIndex]; + ++rxBufferIndex; + } + return value; +} + +int SlowSoftWire::peek(void) { + int value = -1; + + if(rxBufferIndex < rxBufferLength){ + value = rxBuffer[rxBufferIndex]; + } + return value; +} + +void SlowSoftWire::flush(void) { +} + diff --git a/hardware/freedom_e/libraries/SlowSoftWire/SlowSoftWire.h b/hardware/freedom_e/libraries/SlowSoftWire/SlowSoftWire.h new file mode 100644 index 0000000..ec7bd89 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/SlowSoftWire.h @@ -0,0 +1,45 @@ +/* Wire-like wrapper for SlowSoftI2CMaster */ + +#ifndef SLOW_SOFT_WIRE +#define SLOW_SOFT_WIRE +#include + + + +class SlowSoftWire : public Stream +{ +private: + uint8_t rxBuffer[BUFFER_LENGTH]; + uint8_t rxBufferIndex; + uint8_t rxBufferLength; + uint8_t transmitting; + uint8_t error; + SlowSoftI2CMaster si2c; +public: + SlowSoftWire(uint8_t sda, uint8_t scl, bool internal_pullup = false); + void begin(void); + void end(void); + void setClock(uint32_t clock); + void beginTransmission(uint8_t address); + void beginTransmission(int address); + uint8_t endTransmission(uint8_t sendStop = true); + size_t write(uint8_t data); + size_t write(const uint8_t *data, size_t quantity); + uint8_t requestFrom(uint8_t address, uint8_t quantity, + uint32_t iaddress, uint8_t isize, uint8_t sendStop); + uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop); + uint8_t requestFrom(int address, int quantity, int sendStop); + uint8_t requestFrom(uint8_t address, uint8_t quantity); + uint8_t requestFrom(int address, int quantity); + int available(void); + int read(void); + int peek(void); + void flush(void); + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } + using Print::write; +}; + +#endif diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/BMA020SlowWire/BMA020SlowWire.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/BMA020SlowWire/BMA020SlowWire.ino new file mode 100644 index 0000000..482b005 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/BMA020SlowWire/BMA020SlowWire.ino @@ -0,0 +1,83 @@ +// -*- c++ -*- +// Simple sketch to read out BMA020 using SoftI2C + +// Readout BMA020 chip + + +#include +#include + + +#include + +SlowSoftWire Wire = SlowSoftWire(A4, A5); + +#define BMAADDR 0x38 + +int xval, yval, zval; + + +boolean setControlBits(uint8_t cntr) +{ + Wire.beginTransmission(BMAADDR); + Wire.write(0x0A); + Wire.write(cntr); + return (Wire.endTransmission() == 0); +} + +boolean initBma(void) +{ + if (!setControlBits(B00000010)) return false; + delay(100); + return true; +} + +int readOneVal(void) +{ + uint8_t msb, lsb; + lsb = Wire.read(); + msb = Wire.read(); + return (int)((msb<<8)|lsb)/64; +} + +boolean readBma(void) +{ + xval = 0xFFFF; + yval = 0xFFFF; + zval = 0xFFFF; + Wire.beginTransmission(BMAADDR); + Wire.write(0x02); + if (Wire.endTransmission(false) != 0) return false; + Wire.requestFrom(BMAADDR, 6, true); + xval = readOneVal(); + yval = readOneVal(); + zval = readOneVal(); + return true; +} + + + +//------------------------------------------------------------------------------ +void setup(void) { + Serial.begin(19200); + Wire.begin(); + if (!initBma()) { + Serial.println(F("INIT ERROR")); + while (1); + } + +} + +void loop(void){ + if (!readBma()) { + Serial.println(F("READ ERROR")); + while (1); + } + Serial.print(F("X=")); + Serial.print(xval); + Serial.print(F(" Y=")); + Serial.print(yval); + Serial.print(F(" Z=")); + Serial.println(zval); + delay(300); +} diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/HW_I2C_Uno_StressTest_MasterTransmit/HW_I2C_Uno_StressTest_MasterTransmit.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/HW_I2C_Uno_StressTest_MasterTransmit/HW_I2C_Uno_StressTest_MasterTransmit.ino new file mode 100644 index 0000000..eaa54cf --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/HW_I2C_Uno_StressTest_MasterTransmit/HW_I2C_Uno_StressTest_MasterTransmit.ino @@ -0,0 +1,41 @@ +/* Hardware I2C Uno Stress Test Master Transmit + * By Roy Weisfeld + * + * Run this to test I2c reliability between 2 devices, a Master and a Slave. + * The test runs a loop that sends chars from 0 to 256 the number of itirations the user defines. + * (This is the Master Device Code used on the Uno) + * SlowSoftWire Library is needed for this sketch to work. + * Uno needs to be connected via A4 --> A4, A5 --> A5, GND --> GND. + * + * Created 13 September 2018 + * + * This example code is in the public domain. + */ + +#include + +#define CHARS 256 + +int counter = 0; +uint8_t character; + +void setup() { + Wire.begin(); + Serial.begin(9600); + Wire.setClock(250000L); + character = 0; +} + +void loop() { + Wire.beginTransmission(8); + Wire.write(character); + Wire.endTransmission(); + character++; + counter++; + if(counter == CHARS) { + counter = 0; + } + if(character == CHARS) { + character = 0; + } + } diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/HW_I2C_Uno_StressTest_SlaveRecieve/HW_I2C_Uno_StressTest_SlaveRecieve.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/HW_I2C_Uno_StressTest_SlaveRecieve/HW_I2C_Uno_StressTest_SlaveRecieve.ino new file mode 100644 index 0000000..4d3bd1d --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/HW_I2C_Uno_StressTest_SlaveRecieve/HW_I2C_Uno_StressTest_SlaveRecieve.ino @@ -0,0 +1,67 @@ +/* Hardware I2C Uno Stress Test Slave Recieve + * By Roy Weisfeld + * + * Run this to test I2c reliability between 2 devices, a Master and a Slave. + * The test runs a loop that sends chars from 0 to 256 the number of itirations the user defines. + * + * (This is the Slave Device Code used on the Uno. + * It is recommended to use the SW_I2C_SiFi_StressTest_MasterTransmit. + * sketch on a HiFive1 Device) + * + * Created 13 September 2018 + * + * This example code is in the public domain. + */ + +#include + +#define ITERATIONS 10 //Change value here for more itirations +#define CHARS 256 + +uint8_t temp; +uint16_t success[256]; +uint16_t fails = 0; +int counter = 0; +int iteration = 0; + +void setup() { + Wire.begin(8); + Wire.onReceive(receiveEvent); + //Serial.begin(9600); + //Serial.print("Started Test"); + //Serial.println(); +} + +void loop() { + if(iteration == ITERATIONS) { //Only starts printing results after defined itiration number + Serial.begin(9600); + for(int j = 0; j < CHARS; j++) { + Serial.print(j); //Uncomment this block to check if you would like + Serial.print("\t|\t"); //To see a detailed list of where the test failed + Serial.print(success[j]); + Serial.println(); + if(success[j] < ITERATIONS) { + fails += ITERATIONS - success[j]; + } + } + iteration++; + Serial.print("Chars have failed "); + Serial.print(fails); + Serial.print(" times"); + Serial.println(); + } +} + +void receiveEvent(int howMany) { + if(iteration < ITERATIONS) {//Only stops reading test after defined itiration number + if (Wire.available() > 0) { + temp = Wire.read(); + success[temp]++; + counter++; + if(counter == CHARS) { + iteration++; + counter = 0; + } + } + } +} diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/I2CScanSlowWire/I2CScanSlowWire.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/I2CScanSlowWire/I2CScanSlowWire.ino new file mode 100644 index 0000000..a0d5c17 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/I2CScanSlowWire/I2CScanSlowWire.ino @@ -0,0 +1,90 @@ +// -*- c++ -*- + +// -------------------------------------- +// i2c_scanner (using SoftWire and SoftI2CMaster) +// +// Version 1 +// This program (or code that looks like it) +// can be found in many places. +// For example on the Arduino.cc forum. +// The original author is not know. +// Version 2, Juni 2012, Using Arduino 1.0.1 +// Adapted to be as simple as possible by Arduino.cc user Krodal +// Version 3, Feb 26 2013 +// V3 by louarnold +// Version 4, March 3, 2013, Using Arduino 1.0.3 +// by Arduino.cc user Krodal. +// Changes by louarnold removed. +// Scanning addresses changed from 0...127 to 1...119, +// according to the i2c scanner by Nick Gammon +// http://www.gammon.com.au/forum/?id=10896 +// Version 5, March 28, 2013 +// As version 4, but address scans now to 127. +// A sensor seems to use address 120. +// Version 6, November 27, 2015. +// Added waiting for the Leonardo serial communication. +// Version 7, October 1, 2016 +// Changed it to use the SoftWire library +// Version 8, August 5, 2017 +// Added third parameter for internal pullups +// +// This sketch tests the standard 7-bit addresses +// Devices with higher bit address might not be seen properly. +// + +#include + +SlowSoftWire Wire = SlowSoftWire(A4, A5, true); + +void setup() +{ + Wire.begin(); + + Serial.begin(19200); + while (!Serial); // Leonardo: wait for serial monitor + Serial.println("\nI2C Scanner"); +} + + +void loop() +{ + byte error, address; + int nDevices; + + Serial.println(F("Scanning I2C bus (7-bit addresses) ...")); + + nDevices = 0; + for(address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print(F("I2C device found at address 0x")); + if (address<16) + Serial.print(F("0")); + Serial.print(address,HEX); + Serial.println(F(" !")); + + nDevices++; + } + else if (error==4) + { + Serial.print(F("Unknow error at address 0x")); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + else + Serial.println("done\n"); + + delay(5000); // wait 5 seconds for next scan +} + diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/ReadMe_Testing_Software_I2C.txt b/hardware/freedom_e/libraries/SlowSoftWire/examples/ReadMe_Testing_Software_I2C.txt new file mode 100644 index 0000000..f0f3c8d --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/ReadMe_Testing_Software_I2C.txt @@ -0,0 +1,13 @@ +Steps for testing Software I2C: + +1. Hardware Wire library - Master (UNO) to Hardware Wire library - Slave (UNO). +This is done by wiring pins Master --> Slave: A4(SDA) --> A4(SDA), A5(SCL) --> A5(SCL), GND --> GND. +Then you will need RecieverSlave (Wire Lib) and also MasterTransmit (Wire Lib) . + +2. Software Wire library - Master (UNO) to Hardware Wire library - Slave (UNO). +This is done by the same wiring but this time a setup is required on the Master. +Then you will need RecieverSlave (Wire Lib) and also SW_I2C_Uno_TransmitterMaster. + +3. Software Wire library - Master (SiFi) to Hardware Wire library - Slave (UNO). +This is done by wiring pins Master --> Slave: 18(SDA) --> A4(SDA), 19(SCL) --> A5(SCL), GND --> GND. +Then you will need RecieverSlave (Wire Lib) and also SW_I2C_SiFi_TransmitterMaster. \ No newline at end of file diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_SiFi_StressTest_MasterTransmit/SW_I2C_SiFi_StressTest_MasterTransmit.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_SiFi_StressTest_MasterTransmit/SW_I2C_SiFi_StressTest_MasterTransmit.ino new file mode 100644 index 0000000..b3e029a --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_SiFi_StressTest_MasterTransmit/SW_I2C_SiFi_StressTest_MasterTransmit.ino @@ -0,0 +1,42 @@ +/* Hardware I2C SiFi Stress Test Master Transmitter + * By Roy Weisfeld + * + * Run this to test I2c reliability between 2 devices, a Master and a Slave. + * The test runs a loop that sends chars from 0 to 256 the number of itirations the user defines. + * (This is the Master Device Code used on the HiFive1) + * SlowSoftWire Library is needed for this sketch to work. + * HiFive1 needs to be connected via 2 digital ports of your choosing (In this case I used 18 for SDA and 19 for SCL), + * and also connect the ground from this device to the second one. + * + * Created 13 September 2018 + * + * This example code is in the public domain. + */ + +#include + +int counter = 0; +SlowSoftWire Wire = SlowSoftWire(18, 19, true); //HiFive1 can use internal pullups +uint8_t character; +#define CHARS 256 //Change value here for number of chars + +void setup() { + Wire.begin(); + Wire.setClock(100000L); + Serial.begin(9600); + character = 0; +} + +void loop() { + Wire.beginTransmission(8); + Wire.write(character); + character++; + counter++; + if(counter == CHARS) { + delay(1); + counter = 0; + } + if(character == CHARS) { + character = 0; + } + } diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_SiFi_TransmitterMaster/SW_I2C_SiFi_TransmitterMaster.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_SiFi_TransmitterMaster/SW_I2C_SiFi_TransmitterMaster.ino new file mode 100644 index 0000000..15ba6a0 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_SiFi_TransmitterMaster/SW_I2C_SiFi_TransmitterMaster.ino @@ -0,0 +1,30 @@ +/* Software I2C SiFi Transmitter Master + * By Roy Weisfeld + * + * Example for basic I2C Transmitting using Software I2C Library with SiFive1 board + * For this to work you also need a reciever slave (An example exists in the library) + * Connect this SiFive1 board to another board using SDA --> SDA, SCL --> SCL + * and GND --> GND. + * + * Created 13 September 2018 + * + * This example code is in the public domain. + */ +#include + +SlowSoftWire Wire = SlowSoftWire(18, 19, true); //SDA on port 18, SCL on port 19 + +void setup(void) { + Serial.begin(57600); + Wire.begin(); +} + +byte x = 0; + +void loop(void) { + Wire.beginTransmission(8); //Begin Master transmission to Address 8 + Wire.write(x); // sends one byte + Wire.endTransmission(); // stop transmitting + x++; + delay(100); +} diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_Uno_StressTest_MasterTransmit/SW_I2C_Uno_StressTest_MasterTransmit.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_Uno_StressTest_MasterTransmit/SW_I2C_Uno_StressTest_MasterTransmit.ino new file mode 100644 index 0000000..26a5705 --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_Uno_StressTest_MasterTransmit/SW_I2C_Uno_StressTest_MasterTransmit.ino @@ -0,0 +1,44 @@ +/* Software I2C Uno Stress Test Master Transmit + * By Roy Weisfeld + * + * Run this to test I2c reliability between 2 devices, a Master and a Slave. + * The test runs a loop that sends chars from 0 to 256 the number of itirations the user defines. + * (This is the Master Device Code used on the Uno) + * SlowSoftWire Library is needed for this sketch to work. + * Uno needs to be connected via 2 digital ports of your choosing (In this case I used A4 == 18 for SDA and A5 == 19 for SCL), + * and also connect the ground from this device to the second one. + * + * Created 13 September 2018 + * + * This example code is in the public domain. + */ + +#include + +#define CHARS 256 + +int counter = 0; +uint8_t character; + +SlowSoftWire Wire = SlowSoftWire(18, 19, true); //Uno can use internal pullups + +void setup() { + Wire.begin(); + Wire.setClock(100000L); + Serial.begin(9600); + character = 0; +} + +void loop() { + Wire.beginTransmission(8); + Wire.write(character); + character++; + counter++; + if(counter == CHARS) { + //delay(1); + counter = 0; + } + if(character == CHARS) { + character = 0; + } + } diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_Uno_TransmitterMaster/SW_I2C_Uno_TransmitterMaster.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_Uno_TransmitterMaster/SW_I2C_Uno_TransmitterMaster.ino new file mode 100644 index 0000000..49c5a7c --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/SW_I2C_Uno_TransmitterMaster/SW_I2C_Uno_TransmitterMaster.ino @@ -0,0 +1,31 @@ +/* Hardware I2C Uno Transmitter Test Master + * By Roy Weisfeld + * + * Example for basic I2C Transmitting using Software I2C Library with Uno board + * For this to work you also need a reciever slave (An example exists in the library) + * Connect this Uno board to another board using SDA --> SDA, SCL --> SCL + * and GND --> GND. + * + * Created 13 September 2018 + * + * This example code is in the public domain. + */ +#include + +SlowSoftWire Wire = SlowSoftWire(A4, A5); // SDA on port A4, SCL on port A5 + +void setup(void) { + Serial.begin(57600); + Wire.begin(); +} + +byte x = 0; + +void loop(void) { + Wire.beginTransmission(8); // Begin Master transmission to Address 8 + Wire.write("x is "); // sends five bytes + Wire.write(x); // sends one byte + Wire.endTransmission(); // stop transmitting + x++; + delay(1000); +} diff --git a/hardware/freedom_e/libraries/SlowSoftWire/examples/simpleSlowWire/simpleSlowWire.ino b/hardware/freedom_e/libraries/SlowSoftWire/examples/simpleSlowWire/simpleSlowWire.ino new file mode 100644 index 0000000..9f4964e --- /dev/null +++ b/hardware/freedom_e/libraries/SlowSoftWire/examples/simpleSlowWire/simpleSlowWire.ino @@ -0,0 +1,26 @@ +// -*- c++ -*- +// Simple sketch to read out one register of an I2C device + +#define I2C_7BITADDR 0x68 // DS1307 +#define MEMLOC 0x0A +#define ADDRLEN 1 + +#include + +SlowSoftWire Wire = SlowSoftWire(A4, A5); + +void setup(void) { + Serial.begin(57600); + Wire.begin(); +} + +void loop(void){ + Wire.beginTransmission(I2C_7BITADDR); + for (byte i=1; i + SoftwareSerial32 serial(10, 11); +#else + #include + SoftwareSerial serial(10, 11); +#endif + +static const uint32_t iters = 10; + +uint32_t succ_arr[256]; + +uint32_t count = 0; +uint32_t misses = 0; + + +void setup() { + serial.begin(115200); + Serial.begin(9600); +} + +void loop() { + Serial.println("Started test"); + while (count < (iters << 8)) { + if (serial.available()) { + succ_arr[serial.read()]++; + count++; + } + } + + for (int i = 0; i < 256; i++) { + if (succ_arr[i] < 10) + misses += (10 - succ_arr[i]); + } + Serial.println("Chr\t|\tCount"); + for (int i = 0; i < 256; i++) { + Serial.print(i, HEX); + Serial.print("\t|\t"); + Serial.print(succ_arr[i]); + Serial.println(); + } + Serial.print("misses: "); + Serial.println(misses); + while (1){} +} diff --git a/hardware/freedom_e/libraries/SoftwareSerial32/examples/transmitter/transmitter.ino b/hardware/freedom_e/libraries/SoftwareSerial32/examples/transmitter/transmitter.ino new file mode 100644 index 0000000..7aae8a0 --- /dev/null +++ b/hardware/freedom_e/libraries/SoftwareSerial32/examples/transmitter/transmitter.ino @@ -0,0 +1,28 @@ +#if defined(FREEDOM_E300) + #include + SoftwareSerial32 s(10, 11); +#else + #include + SoftwareSerial s(10, 11); +#endif + +uint8_t character; +uint8_t counter = 0; + +static const uint8_t burst_load = 50; + +void setup() { + s.begin(115200); + character = 0; + counter = 0; +} + +void loop() { + s.write(character); + character++; + counter++; + if (counter % burst_load == 0) { + delay(1); + counter = 0; + } +} diff --git a/hardware/freedom_e/libraries/SoftwareSerial32/library.properties b/hardware/freedom_e/libraries/SoftwareSerial32/library.properties new file mode 100644 index 0000000..88a8136 --- /dev/null +++ b/hardware/freedom_e/libraries/SoftwareSerial32/library.properties @@ -0,0 +1,10 @@ +name=SoftwareSerial32 +version=3.0.5 +author=Shadi Kamal +maintainer=Shadi Kamal +sentence=Software serial library for 32-bit MCUs +paragraph=Simultaneous RX & TX, does not require additional TIMERs, interrupts not disabled during RX, based on NeoSWSerial by SlashDevin +category=Communication +url= +architectures=riscv +includes=SoftwareSerial32.h diff --git a/hardware/freedom_e/libraries/SoftwareSerial32/src/SoftwareSerial32.cpp b/hardware/freedom_e/libraries/SoftwareSerial32/src/SoftwareSerial32.cpp new file mode 100644 index 0000000..34b3c68 --- /dev/null +++ b/hardware/freedom_e/libraries/SoftwareSerial32/src/SoftwareSerial32.cpp @@ -0,0 +1,746 @@ +//--------------------------------------------------------------------------- +// +// SoftwareSerial32, based on NeoSWSerial +// Copyright (C) 2015-2016, SlashDevin +// Copyright (c) 2018 Western Digital Corporation or its affiliates. +// +// SoftwareSerial32 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. +// +// SoftwareSerial32 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: +// +// . +// +// Modified by Shadi Kamal , 2018 +//--------------------------------------------------------------------------- +// +// Methods +// ------- +// +// begin(baudRate) - initialization, optionally set baudrate, and then enable RX +// listen() - enables RX interrupts, allowing data to enter the RX buffer +// ignore() - disables RX interrupts +// setBaudRate(baudRate) - selects the baud rate (9600, 19200, 31250, 38400) +// - any other value is ignored +// available() - returns the number of characters in the RX buffer +// read() - returns a single character from the buffer +// write(s) - transmits a string +// +// print() is supported +//============================================================================= + +#include +//#define _SWSERIAL32_DBG + +#if F_CPU == 320000000L + // 320 MHZ + static const uint32_t CYCLES_PER_BIT_9600_320MHZ = 33333; + static const uint32_t BITS_PER_TICK_9600_Q16_320MHZ = 2; + // 9600 * CYCLE_RESOLUTION_320MHZ(1/320M) = 9600 * 0.000000003125 * (2^16) + // = 0.00003 * (2^16) = 1.96608 = ~2 + + // These could be omiited and used as a right shift due to them being multiples of 2 + static const uint32_t CYCLES_PER_BIT_19200_320MHZ = 16667; + static const uint32_t BITS_PER_TICK_19200_Q16_320MHZ = 4; + + static const uint32_t CYCLES_PER_BIT_31250_320MHZ = 10240; + static const uint32_t BITS_PER_TICK_31250_Q17_320MHZ = 13; + + static const uint32_t CYCLES_PER_BIT_38400_320MHZ = 8333; + static const uint32_t BITS_PER_TICK_38400_Q16_320MHZ = 8; + + static const uint32_t CYCLES_PER_BIT_57600_320MHZ = 5556; + static const uint32_t BITS_PER_TICK_57600_Q15_320MHZ = 6; + + static const uint32_t CYCLES_PER_BIT_115200_320MHZ = 2778; + static const uint32_t BITS_PER_TICK_115200_Q13_320MHZ = 3; + + static const uint32_t CYCLES_PER_BIT_230400_320MHZ = 1389; + static const uint32_t BITS_PER_TICK_230400_Q16_320MHZ = 47; + +#elif F_CPU == 256000000L + // 256 MHZ + static const uint32_t CYCLES_PER_BIT_9600_256MHZ = 26667; + static const uint32_t BITS_PER_TICK_9600_Q17_256MHZ = 5; // need to find another multiplier since it's 2.5 + + static const uint32_t CYCLES_PER_BIT_19200_256MHZ = 13333; + static const uint32_t BITS_PER_TICK_19200_Q16_256MHZ = 5; + + static const uint32_t CYCLES_PER_BIT_31250_256MHZ = 8192; + static const uint32_t BITS_PER_TICK_31250_Q16_256MHZ = 8; + + static const uint32_t CYCLES_PER_BIT_38400_256MHZ = 6667; + static const uint32_t BITS_PER_TICK_38400_Q16_256MHZ = 10; + + static const uint32_t CYCLES_PER_BIT_57600_256MHZ = 4444; + static const uint32_t BITS_PER_TICK_57600_Q18_256MHZ = 59; + + static const uint32_t CYCLES_PER_BIT_115200_256MHZ = 2222; + static const uint32_t BITS_PER_TICK_115200_Q17_256MHZ = 59; + + static const uint32_t CYCLES_PER_BIT_230400_256MHZ = 1111; + static const uint32_t BITS_PER_TICK_230400_Q10_256MHZ = 1; + + static const uint32_t CYCLES_PER_BIT_250000_256MHZ = 1024; + static const uint32_t BITS_PER_TICK_250000_Q10_256MHZ = 1; + + static const uint32_t CYCLES_PER_BIT_500000_256MHZ = 512; + static const uint32_t BITS_PER_TICK_500000_Q9_256MHZ = 1; + + static const uint32_t CYCLES_PER_BIT_1000000_256MHZ = 256; + static const uint32_t BITS_PER_TICK_1000000_Q8_256MHZ = 1; + +#else + // 16 MHZ + static const uint32_t CYCLES_PER_BIT_4800_16MHZ = 3333; + static const uint32_t BITS_PER_CYCLE_4800_Q16_16MHZ = 20; + + static const uint32_t CYCLES_PER_BIT_9600_16MHZ = 1667; + static const uint32_t BITS_PER_TICK_9600_Q13_16MHZ = 5; + + static const uint32_t CYCLES_PER_BIT_19200_16MHZ = 833; + static const uint32_t BITS_PER_TICK_19200_Q12_16MHZ = 5; + + static const uint32_t CYCLES_PER_BIT_31250_16MHZ = 512; + static const uint32_t BITS_PER_TICK_31250_Q16_16MHZ = 128; + + static const uint32_t CYCLES_PER_BIT_38400_16MHZ = 417; + static const uint32_t BITS_PER_TICK_38400_Q11_16MHZ = 5; + + static const uint32_t CYCLES_PER_BIT_57600_16MHZ = 278; + static const uint32_t BITS_PER_TICK_57600_Q16_16MHZ = 236; + + static const uint32_t CYCLES_PER_BIT_115200_16MHZ = 139; + static const uint32_t BITS_PER_TICK_115200_Q13_16MHZ = 59; + +#endif + +static const uint8_t WAITING_FOR_START_BIT = 0xFF; +void attachPCINT( uint8_t ); + +// An array to hold and manage active UART instances +static SoftwareSerial32* SWUARTInstanceMap[MAX_NUM_MUART]; + +uint32_t SoftwareSerial32::bitTimes( uint32_t dt) +{ + return (uint32_t)((uint64_t)((dt + rxWindowWidth) * bitsPerTick) >> shiftScaler); +} + +void SoftwareSerial32::begin(uint32_t baudRate) +{ + setBaudRate( baudRate ); + listen(); +} + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::listen() +{ + if (listener) + listener->ignore(); + + pinMode(rxPin, INPUT); + rxBitMask = digitalPinToBitMask( rxPin ); + rxPort = portInputRegister( digitalPinToPort( rxPin ) ); + + txBitMask = digitalPinToBitMask( txPin ); + txPort = portOutputRegister( digitalPinToPort( txPin ) ); + if (txPort) + *txPort |= txBitMask; // high = idle + pinMode(txPin, OUTPUT); + + noInterrupts(); + attachPCINT(rxPin); + + rxState = WAITING_FOR_START_BIT; + rxHead = rxTail = 0; // no characters in buffer + flush(); + + switch (_baudRate) + { + case 9600: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_9600_320MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_9600_320MHZ; + bitsPerTick = BITS_PER_TICK_9600_Q16_320MHZ; + shiftScaler = 16; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_9600_256MHZ / 3; + txBitWidth = CYCLES_PER_BIT_9600_256MHZ; + bitsPerTick = BITS_PER_TICK_9600_Q17_256MHZ; + shiftScaler = 17; + #else + rxWindowWidth = CYCLES_PER_BIT_9600_16MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_9600_16MHZ; + bitsPerTick = BITS_PER_TICK_9600_Q13_16MHZ; + shiftScaler = 13; + #endif + break; + case 19200: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_19200_320MHZ / 3; + txBitWidth = CYCLES_PER_BIT_19200_320MHZ; + bitsPerTick = BITS_PER_TICK_19200_Q16_320MHZ; + shiftScaler = 16; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_19200_256MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_19200_256MHZ; + bitsPerTick = BITS_PER_TICK_19200_Q16_256MHZ; + shiftScaler = 16; + #else + rxWindowWidth = CYCLES_PER_BIT_19200_16MHZ / 3; + txBitWidth = CYCLES_PER_BIT_19200_16MHZ; + bitsPerTick = BITS_PER_TICK_19200_Q12_16MHZ; + shiftScaler = 12; + #endif + break; + case 31250: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_31250_320MHZ / 3; + txBitWidth = CYCLES_PER_BIT_31250_320MHZ; + bitsPerTick = BITS_PER_TICK_31250_Q17_320MHZ; + shiftScaler = 17; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_31250_256MHZ / 3; + txBitWidth = CYCLES_PER_BIT_31250_256MHZ; + bitsPerTick = BITS_PER_TICK_31250_Q16_256MHZ; + shiftScaler = 16; + #else + rxWindowWidth = CYCLES_PER_BIT_31250_16MHZ / 3; + txBitWidth = CYCLES_PER_BIT_31250_16MHZ; + bitsPerTick = BITS_PER_TICK_31250_Q16_16MHZ; + shiftScaler = 16; + #endif + break; + case 38400: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_38400_320MHZ / 3; + txBitWidth = CYCLES_PER_BIT_38400_320MHZ; + bitsPerTick = BITS_PER_TICK_38400_Q16_320MHZ; + shiftScaler = 16; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_38400_256MHZ / 3; + txBitWidth = CYCLES_PER_BIT_38400_256MHZ; + bitsPerTick = BITS_PER_TICK_38400_Q16_256MHZ; + shiftScaler = 16; + #else + rxWindowWidth = CYCLES_PER_BIT_38400_16MHZ / 3; + txBitWidth = CYCLES_PER_BIT_38400_16MHZ; + bitsPerTick = BITS_PER_TICK_38400_Q11_16MHZ; + shiftScaler = 11; + #endif + break; + case 57600: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_57600_320MHZ / 3; + txBitWidth = CYCLES_PER_BIT_57600_320MHZ; + bitsPerTick = BITS_PER_TICK_57600_Q15_320MHZ; + shiftScaler = 15; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_57600_256MHZ / 3; + txBitWidth = CYCLES_PER_BIT_57600_256MHZ; + bitsPerTick = BITS_PER_TICK_57600_Q18_256MHZ; + shiftScaler = 18; + #else + rxWindowWidth = CYCLES_PER_BIT_57600_16MHZ / 3; + txBitWidth = CYCLES_PER_BIT_57600_16MHZ; + bitsPerTick = BITS_PER_TICK_57600_Q16_16MHZ; + shiftScaler = 16; + #endif + break; + case 115200: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_115200_320MHZ / 3; + txBitWidth = CYCLES_PER_BIT_115200_320MHZ; + bitsPerTick = BITS_PER_TICK_115200_Q13_320MHZ; + shiftScaler = 13; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_115200_256MHZ / 3; + txBitWidth = CYCLES_PER_BIT_115200_256MHZ; + bitsPerTick = BITS_PER_TICK_115200_Q17_256MHZ; + shiftScaler = 17; + #else + rxWindowWidth = CYCLES_PER_BIT_115200_16MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_115200_16MHZ; + bitsPerTick = BITS_PER_TICK_115200_Q13_16MHZ; + shiftScaler = 13; + #endif + break; + case 230400: + #if F_CPU == 320000000L + rxWindowWidth = CYCLES_PER_BIT_230400_320MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_230400_320MHZ; + bitsPerTick = BITS_PER_TICK_230400_Q16_320MHZ; + shiftScaler = 16; + #elif F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_230400_256MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_230400_256MHZ; + bitsPerTick = BITS_PER_TICK_230400_Q10_256MHZ; + shiftScaler = 10; + #endif + break; + case 250000: + #if F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_250000_256MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_250000_256MHZ; + bitsPerTick = BITS_PER_TICK_250000_Q10_256MHZ; + shiftScaler = 10; + #endif + break; + case 500000: + #if F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_500000_256MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_500000_256MHZ; + bitsPerTick = BITS_PER_TICK_500000_Q9_256MHZ; + shiftScaler = 9; + #endif + break; + case 1000000: + #if F_CPU == 256000000L + rxWindowWidth = CYCLES_PER_BIT_1000000_256MHZ >> 1; + txBitWidth = CYCLES_PER_BIT_1000000_256MHZ; + bitsPerTick = BITS_PER_TICK_1000000_Q8_256MHZ; + shiftScaler = 8; + #endif + break; + default: + break; + } + + listener = this; + SWUARTInstanceMap[rxPin] = this; + +#if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_EN) |= (1 << RED_LED_OFFSET); + GPIO_REG(GPIO_INPUT_EN) &= ~(1 << RED_LED_OFFSET); + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << RED_LED_OFFSET); + GPIO_REG(GPIO_OUTPUT_EN) |= (1 << GREEN_LED_OFFSET); + GPIO_REG(GPIO_INPUT_EN) &= ~(1 << GREEN_LED_OFFSET); + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << GREEN_LED_OFFSET); + GPIO_REG(GPIO_OUTPUT_EN) |= (1 << BLUE_LED_OFFSET); + GPIO_REG(GPIO_INPUT_EN) &= ~(1 << BLUE_LED_OFFSET); + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << BLUE_LED_OFFSET); + + Serial.println("SWUART Instance information:"); + Serial.print("\tlistener:\t"); + Serial.println((uint32_t)(listener), HEX); + Serial.print("\ttxBitWidth:\t"); + Serial.println(txBitWidth); + Serial.print("\trxWindowWidth:\t"); + Serial.println(rxWindowWidth); + Serial.print("\tbitsPerTick:\t"); + Serial.println(bitsPerTick); + Serial.print("\tshiftScaler:\t"); + Serial.println(shiftScaler); + Serial.print("\trxState:\t"); + Serial.println(rxState); + Serial.print("\tprev_t0:\t"); + Serial.println((uint32_t)(prev_t0)); + Serial.print("\trxMask:\t\t"); + Serial.println(rxMask); + Serial.print("\trxValue:\t"); + Serial.println(rxValue); + + Serial.print("\trxBuffer:\t"); + Serial.print("["); + for (int i = 0; i < RX_BUFFER_SIZE; i++) { + Serial.print(rxBuffer[i], HEX); + if (i < RX_BUFFER_SIZE - 1) + Serial.print(", "); + } + Serial.println("]"); + + Serial.print("\trxHead:\t\t"); + Serial.println(rxHead); + Serial.print("\trxTail:\t\t"); + Serial.println(rxTail); + Serial.print("\trxBitMask:\t"); + Serial.println(rxBitMask, BIN); + Serial.print("\ttxBitMask:\t"); + Serial.println(txBitMask, BIN); + Serial.print("\trxPort:\t\t"); + Serial.println((uint32_t)(rxPort), HEX); + Serial.print("\ttxPort:\t\t"); + Serial.println((uint32_t)(txPort), HEX); + + Serial.println("=====SWUARTInstanceMap====="); + for (int i = 0; i < MAX_NUM_MUART; i++) { + Serial.print((uint32_t)(SWUARTInstanceMap[i]), HEX); + if (i < MAX_NUM_MUART - 1) + Serial.print(",\t"); + if (i != 0 && i % 5 == 0) + Serial.println(); + } + Serial.println(); + Serial.println(); +#endif + + interrupts(); // For some reason this is turned off by default + set_csr(mstatus, MSTATUS_MIE); +} // listen + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::ignore() +{ + if (listener) { + noInterrupts(); + listener = (SoftwareSerial32 *) NULL; + // Call the globally defined PLIC handling detachInterrupt + ::detachInterrupt(digitalPinToInterrupt(rxPin)); + interrupts(); + } +} // ignore + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::setBaudRate(uint32_t baudRate) +{ + if (((baudRate == 9600) || + (baudRate == 19200) || + (baudRate == 31250) || + (baudRate == 38400) || + (baudRate == 57600) || + (baudRate == 115200) || + ((F_CPU >= 256000000L) && + ((baudRate == 230400) || + (baudRate == 250000) || + (baudRate == 500000) || + (baudRate == 1000000)))) && + (_baudRate != baudRate)) { + + _baudRate = baudRate; + + if (this == listener) + listen(); + } +} // setBaudRate + +//---------------------------------------------------------------------------- + +int SoftwareSerial32::available() +{ + uint8_t avail = ((rxHead - rxTail + RX_BUFFER_SIZE) % RX_BUFFER_SIZE); + + if (avail == 0) { + noInterrupts(); + if (checkRxTime()) + avail = 1; + + interrupts(); + } + return avail; +} // available + +//---------------------------------------------------------------------------- + +int SoftwareSerial32::read() +{ + if (rxHead == rxTail) return -1; + uint8_t c = rxBuffer[rxTail]; + rxTail = (rxTail + 1) % RX_BUFFER_SIZE; + + return c; + +} // read + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::attachInterrupt( isr_t fn ) +{ + noInterrupts(); + _isr = fn; + interrupts(); +} // attachInterrupt + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::startChar() +{ + rxState = 0; // got a start bit + rxMask = 0x01; // bit mask, lsb first + rxValue = 0x00; // RX character to be, a blank slate +} // startChar + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::rxISR( uint32_t pin ) +{ + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << RED_LED_OFFSET); + #endif + noInterrupts(); + uint64_t t0; + rdmcycle(&t0); + + // Load context from pinout to state map + SoftwareSerial32* context = SWUARTInstanceMap[pin]; + + uint32_t rxPort = *portInputRegister(digitalPinToPort(pin)); + uint32_t d = rxPort & context->rxBitMask; + + if (context->rxState == WAITING_FOR_START_BIT) { + + // If it looks like a start bit then initialize, + // otherwise ignore the rising edge and exit. + + if (d != 0) return; // it's high so not a start bit, exit + context->startChar(); + + } else { // data bit or stop bit (probably) received + + // Determine how many bit periods have elapsed since the last transition. + + uint32_t rxBits = context->bitTimes(t0 - context->prev_t0); + uint8_t bitsLeft = 9 - context->rxState; // ignores stop bit + bool nextCharStarted = (rxBits > bitsLeft); + + uint32_t bitsThisFrame = nextCharStarted ? bitsLeft : rxBits; + + context->rxState += bitsThisFrame; + + // Set all those bits + + if (d == 0) { + // back fill previous bits with 1's + while (bitsThisFrame-- > 0) { + context->rxValue |= context->rxMask; + context->rxMask = context->rxMask << 1; + } + context->rxMask = context->rxMask << 1; + } else { // d==1 + // previous bits were 0's so only this bit is a 1. + context->rxMask = context->rxMask << (bitsThisFrame-1); + context->rxValue |= context->rxMask; + } + + // If 8th bit or stop bit then the character is complete. + + if (context->rxState > 7) { + context->rxChar( context->rxValue ); + + if ((d == 1) || !nextCharStarted) { + context->rxState = WAITING_FOR_START_BIT; + // DISABLE STOP BIT TIMER + + } else { + // The last char ended with 1's, so this 0 is actually + // the start bit of the next character. + + context->startChar(); + } + } + } + context->prev_t0 = t0; // remember time stamp + // The same bitmask is used for the GPIO fields registers and is already obtained + // by digitalPinToBitMask called by #::listen + GPIO_REG(GPIO_FALL_IP) |= context->rxBitMask; + GPIO_REG(GPIO_RISE_IP) |= context->rxBitMask; + interrupts(); + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << RED_LED_OFFSET); + #endif +} // rxISR + +//---------------------------------------------------------------------------- + +bool SoftwareSerial32::checkRxTime() +{ + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << GREEN_LED_OFFSET); + #endif + if (rxState != WAITING_FOR_START_BIT) { + + uint32_t d = *rxPort & rxBitMask; + + if (d) { + // Ended on a 1, see if it has been too long + uint64_t t0; + rdmcycle(&t0); + uint32_t rxBits = bitTimes( t0-prev_t0 ); + uint8_t bitsLeft = 9 - rxState; + bool completed = (rxBits > bitsLeft); + + if (completed) { + + while (bitsLeft-- > 0) { + rxValue |= rxMask; + rxMask = rxMask << 1; + } + + rxState = WAITING_FOR_START_BIT; + rxChar( rxValue ); + if (!_isr) { + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << GREEN_LED_OFFSET); + #endif + return true; + } + } + } + } + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << GREEN_LED_OFFSET); + #endif + return false; +} // checkRxTime + +//---------------------------------------------------------------------------- + +void SoftwareSerial32::rxChar( uint8_t c ) +{ + if (listener) { + if (listener->_isr) + listener->_isr( c ); + else { + uint8_t index = (rxHead+1) % RX_BUFFER_SIZE; + if (index != rxTail) { + rxBuffer[rxHead] = c; + rxHead = index; + } + } + } + +} // rxChar + +//---------------------------------------------------------------------------- + +#ifdef NEOSWSERIAL_EXTERNAL_PCINT + + // Client code must call SoftwareSerial32::rxISR(PINB) in PCINT handler + +#else + + // Must define all of the vectors even though only one is used. + + // This handy PCINT code for different boards is based on PinChangeInterrupt.* + // from the excellent Cosa project: http://github.com/mikaelpatel/Cosa + + #define PCINT_ISR(pin) \ + extern "C" { \ + static void PCINT ## pin ## _vect (void) \ + { \ + SoftwareSerial32::rxISR(pin); \ + } } + + PCINT_ISR(2); + PCINT_ISR(3); + PCINT_ISR(4); + PCINT_ISR(5); + PCINT_ISR(6); + PCINT_ISR(7); + PCINT_ISR(8); + PCINT_ISR(9); + PCINT_ISR(10); + PCINT_ISR(11); + PCINT_ISR(12); + PCINT_ISR(13); + PCINT_ISR(15); + PCINT_ISR(16); + PCINT_ISR(17); + PCINT_ISR(18); + PCINT_ISR(19); + + // Initialize all interrupts per pin and put in the appropriate dispatcher + // Unsupported interrupt pins: 0, 1 and 14 + + static void emptyISR() {} + + typedef void (*PCINTisr_ptr_t)(void); + PCINTisr_ptr_t PCINTdispatcher[20] = { + emptyISR, + emptyISR, + PCINT2_vect, + PCINT3_vect, + PCINT4_vect, + PCINT5_vect, + PCINT6_vect, + PCINT7_vect, + PCINT8_vect, + PCINT9_vect, + PCINT10_vect, + PCINT11_vect, + PCINT12_vect, + PCINT13_vect, + emptyISR, + PCINT15_vect, + PCINT16_vect, + PCINT17_vect, + PCINT18_vect, + PCINT19_vect + }; + + void attachPCINT(uint8_t rxPin) + { + ::attachInterrupt(digitalPinToInterrupt(rxPin), PCINTdispatcher[rxPin], CHANGE); + } + +#endif + +//----------------------------------------------------------------------------- +// Instead of using a TX buffer and interrupt +// service, the transmit function is a simple timer0 based delay loop. +// +// Interrupts are disabled while the character is being transmitted and +// re-enabled after each character. + +size_t SoftwareSerial32::write(uint8_t txChar) +{ + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << BLUE_LED_OFFSET); + #endif + + if (!txPort) + return 0; + + uint32_t txBit = 0; + uint32_t b = 0; + noInterrupts(); + uint64_t t0, t1; + rdmcycle(&t0); + + while (txBit++ < 9) { // repeat for start bit + 8 data bits + if (b) // if bit is set + *txPort |= txBitMask; // set TX line high + else + *txPort &= ~txBitMask; // else set TX line low + + // Hold the line for the bit duration + rdmcycle(&t1); + while ((uint32_t)(t1 - t0) < txBitWidth) { + // If a HW interrupt is pending while writing use rxISR to handle it + // in order to be able to support the asynchronousity of a UART + if (read_csr(mip) & MIP_MEIP) { + clear_csr(mip, MIP_MEIP); + GPIO_REG(GPIO_FALL_IP) |= txBitMask; + GPIO_REG(GPIO_RISE_IP) |= txBitMask; + rxISR(rxPin); + } else { + checkRxTime(); + } + rdmcycle(&t1); + } + t0 += txBitWidth; // advance start time + b = txChar & 0x01; // get next bit in the character to send + txChar = txChar >> 1; // shift character to expose the following bit + } + + *txPort |= txBitMask; // stop bit is high + rdmcycle(&t1); + interrupts(); + while ((uint32_t)(t1 - t0) < txBitWidth) { + checkRxTime(); + rdmcycle(&t1); + } + + #if defined(_SWSERIAL32_DBG) + GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << BLUE_LED_OFFSET); + #endif + + return 1; // 1 character sent +} diff --git a/hardware/freedom_e/libraries/SoftwareSerial32/src/SoftwareSerial32.h b/hardware/freedom_e/libraries/SoftwareSerial32/src/SoftwareSerial32.h new file mode 100644 index 0000000..faca690 --- /dev/null +++ b/hardware/freedom_e/libraries/SoftwareSerial32/src/SoftwareSerial32.h @@ -0,0 +1,99 @@ +#ifndef SoftwareSerial32_h +#define SoftwareSerial32_h + +#include "Arduino.h" + + +//--------------------------------------------------------------------------- +// +// SoftwareSerial32, based on NeoSWSerial +// Copyright (C) 2015-2016, SlashDevin +// Copyright (c) 2018 Western Digital Corporation or its affiliates. +// +// SoftwareSerial32 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. +// +// SoftwareSerial32 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: +// +// . +// +// Modified by Shadi Kamal , 2018 +//--------------------------------------------------------------------------- + +static const uint8_t RX_BUFFER_SIZE = 64; // power of 2 for optimal speeds +static const uint8_t MAX_NUM_MUART = 20; + +class SoftwareSerial32 : public Stream +{ + SoftwareSerial32( const SoftwareSerial32 & ); // Not allowed + SoftwareSerial32 & operator =( const SoftwareSerial32 & ); // Not allowed + +public: + SoftwareSerial32(uint8_t receivePin, uint8_t transmitPin) + { + // TODO: Block generation of more than MAX_NUM_MUART UART instances + rxPin = receivePin; + txPin = transmitPin; + _isr = (isr_t) NULL; + } + + void begin(uint32_t baudRate=9600); // initialize, set baudrate, listen + void listen(); // enable RX interrupts + void ignore(); // disable RX interrupts + void setBaudRate(uint32_t baudRate); // 9600 [default], 19200, 38400 + + virtual int available(); + virtual int read(); + virtual size_t write(uint8_t txChar); + using Stream::write; // make the base class overloads visible + virtual int peek() { return 0; }; + virtual void flush() {}; + void end() { ignore(); } + + typedef void (* isr_t)( uint8_t ); + void attachInterrupt( isr_t fn ); + void detachInterrupt() { attachInterrupt( (isr_t) NULL ); }; + + uint32_t bitTimes( uint32_t dt ); + +private: + uint8_t rxPin, txPin; + volatile uint32_t *rxPort; + + uint32_t _baudRate; + isr_t _isr; + + void rxChar( uint8_t rx ); // buffer or dispatch one received character + + bool checkRxTime(); + + void startChar(); + + // Member variables + SoftwareSerial32 *listener = (SoftwareSerial32 *) NULL; + uint32_t txBitWidth; + uint32_t rxWindowWidth; + uint32_t bitsPerTick; + uint8_t shiftScaler; + uint8_t rxState; // 0: got start bit; >0: bits rcvd + uint64_t prev_t0; // mcycle is a 64-bit register which indicates the time t0 in cycles + uint8_t rxMask; // bit mask for building received character + uint8_t rxValue; // character being built + uint8_t rxBuffer[RX_BUFFER_SIZE]; + uint8_t rxHead; // buffer pointer input + uint8_t rxTail; // buffer pointer output + uint32_t rxBitMask, txBitMask; + volatile uint32_t *txPort; // Hifive1 has 32-bit registers + +public: + // visible only so the ISRs can call it... + static void rxISR( uint32_t pin_num ); + + //#define NEOSWSERIAL_EXTERNAL_PCINT // uncomment to use your own PCINT ISRs +}; +#endif diff --git a/hardware/freedom_e/platform.txt b/hardware/freedom_e/platform.txt index a2a48c4..c35431b 100644 --- a/hardware/freedom_e/platform.txt +++ b/hardware/freedom_e/platform.txt @@ -1,29 +1,29 @@ name= SiFive Freedom E300 Boards -version=1.0.1 +version=1.1.0 # Compile variables # ---------------------- -compiler.path={runtime.tools.riscv32-unknown-elf-gcc.path}/bin/ -compiler.c.cmd=riscv32-unknown-elf-gcc -compiler.cpp.cmd=riscv32-unknown-elf-g++ -compiler.ld.cmd=riscv32-unknown-elf-ld -compiler.ar.cmd=riscv32-unknown-elf-ar -compiler.objcopy.cmd=riscv32-unknown-elf-objcopy -compiler.elf2hex.cmd=riscv32-unknown-elf-objcopy -compiler.size.cmd=riscv32-unknown-elf-size +compiler.path={runtime.tools.riscv64-unknown-elf-gcc.path}/bin/ +compiler.c.cmd=riscv64-unknown-elf-gcc +compiler.cpp.cmd=riscv64-unknown-elf-g++ +compiler.ld.cmd=riscv64-unknown-elf-ld +compiler.ar.cmd=riscv64-unknown-elf-ar +compiler.objcopy.cmd=riscv64-unknown-elf-objcopy +compiler.elf2hex.cmd=riscv64-unknown-elf-objcopy +compiler.size.cmd=riscv64-unknown-elf-size compiler.sdk.path={runtime.platform.path}/freedom-e-sdk/bsp compiler.preproc.flags=-I{build.system.path}/include -I{compiler.sdk.path}/include -I{compiler.sdk.path}/env -I{compiler.sdk.path}/drivers -I{compiler.sdk.path}/env/{build.boardenv} -compiler.c.flags=-c -O2 -march={build.mcu} -fpeel-loops -ffreestanding -ffunction-sections -fdata-sections -Wall {compiler.preproc.flags} -include sys/cdefs.h -g +compiler.c.flags=-c -O2 -march={build.mcu} -mabi={build.mabi} -mcmodel={build.mcmodel} -fpeel-loops -ffreestanding -ffunction-sections -fdata-sections -MMD -Wall {compiler.preproc.flags} -include sys/cdefs.h -compiler.cpp.flags=-c -O2 -march={build.mcu} -fpeel-loops -ffreestanding -ffunction-sections -fdata-sections -fpermissive -Wall -fno-rtti -fno-exceptions {compiler.preproc.flags} -include sys/cdefs.h -g +compiler.cpp.flags=-c -O2 -march={build.mcu} -mabi={build.mabi} -mcmodel={build.mcmodel} -fpeel-loops -ffreestanding -ffunction-sections -fdata-sections -MMD -fpermissive -Wall -fno-rtti -fno-exceptions {compiler.preproc.flags} -include sys/cdefs.h -compiler.ld.flags=-T {build.ldscript} -nostartfiles -Wl,-N -Wl,--gc-sections -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=sbrk +compiler.ld.flags=-T {build.ldscript} -march={build.mcu} -mabi={build.mabi} -mcmodel={build.mcmodel} -nostartfiles -Xlinker -defsym=__stack_size=0x200 -Wl,-N -Wl,--gc-sections -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=sbrk -compiler.S.flags=-c -march={build.mcu} {compiler.preproc.flags} -g +compiler.S.flags=-c -march={build.mcu} -mabi={build.mabi} -mcmodel={build.mcmodel} {compiler.preproc.flags} compiler.ar.flags=rcs @@ -81,15 +81,15 @@ recipe.size.regex=\s*[0-9]+\s+[0-9]+\s+[0-9]+\s+([0-9]+).* # ------------------- tools.openocd.path={runtime.tools.openocd.path}/bin/ tools.openocd.cmd=openocd -tools.openocd.program.params.verbose=-v -tools.openocd.program.params.quiet=-q +tools.openocd.program.params.verbose=-d2 +tools.openocd.program.params.quiet=-d0 tools.openocd.program.config={runtime.platform.path}/freedom-e-sdk/bsp/env/{build.boardenv}/openocd.cfg -tools.openocd.program.pattern="{path}{cmd}" -f {program.config} -c "flash protect 0 64 last off; program {build.path}/{build.project_name}.elf verify; resume 0x20400000; exit" +tools.openocd.program.pattern="{path}{cmd}" -d0 -f {program.config} -c "flash protect 0 64 last off; program {{{build.path}/{build.project_name}.elf}} verify; resume 0x20400000; exit" tools.manual_openocd.path= tools.manual_openocd.cmd=openocd -tools.manual_openocd.program.params.verbose=-v -tools.manual_openocd.program.params.quiet=-q +tools.manual_openocd.program.params.verbose=-d2 +tools.manual_openocd.program.params.quiet=-d0 tools.manual_openocd.program.config={runtime.platform.path}/freedom-e-sdk/bsp/env/{build.boardenv}/openocd.cfg -tools.manual_openocd.program.pattern="{path}{cmd}" -f {program.config} -c "flash protect 0 64 last off; program {build.path}/{build.project_name}.elf verify; resume 0x20400000; exit" +tools.manual_openocd.program.pattern="{path}{cmd}" -d0 -f {program.config} -c "flash protect 0 64 last off; program {{{build.path}/{build.project_name}.elf}} verify; resume 0x20400000; exit"