diff --git a/.gitignore b/.gitignore index 60b7a717bd..9321044f7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -html/ *.gch +*~ +/.vscode +/docs/html +/docs/latex diff --git a/PORTING.md b/PORTING.md index 2b4ade2ed7..beb4e6c65d 100644 --- a/PORTING.md +++ b/PORTING.md @@ -1,6 +1,33 @@ -=New platform porting guide= +New platform porting guide +========================== -== Setting up the basic files/folders == +# Fast porting for a new board on existing hardware + +Sometimes "porting" FastLED simply consists of supplying new pin definitions for the given platform. For example, platforms/avr/fastpin_avr.h contains various pin definitions for all the AVR variant chipsets/boards that FastLED supports. Defining a set of pins involves setting up a set of definitions - for example here's one full set from the avr fastpin file: + +``` +#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) + +_FL_IO(A); _FL_IO(B); _FL_IO(C); _FL_IO(D); + +#define MAX_PIN 31 +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B); +_FL_DEFPIN(4, 4, B); _FL_DEFPIN(5, 5, B); _FL_DEFPIN(6, 6, B); _FL_DEFPIN(7, 7, B); +_FL_DEFPIN(8, 0, D); _FL_DEFPIN(9, 1, D); _FL_DEFPIN(10, 2, D); _FL_DEFPIN(11, 3, D); +_FL_DEFPIN(12, 4, D); _FL_DEFPIN(13, 5, D); _FL_DEFPIN(14, 6, D); _FL_DEFPIN(15, 7, D); +_FL_DEFPIN(16, 0, C); _FL_DEFPIN(17, 1, C); _FL_DEFPIN(18, 2, C); _FL_DEFPIN(19, 3, C); +_FL_DEFPIN(20, 4, C); _FL_DEFPIN(21, 5, C); _FL_DEFPIN(22, 6, C); _FL_DEFPIN(23, 7, C); +_FL_DEFPIN(24, 0, A); _FL_DEFPIN(25, 1, A); _FL_DEFPIN(26, 2, A); _FL_DEFPIN(27, 3, A); +_FL_DEFPIN(28, 4, A); _FL_DEFPIN(29, 5, A); _FL_DEFPIN(30, 6, A); _FL_DEFPIN(31, 7, A); + +#define HAS_HARDWARE_PIN_SUPPORT 1 +``` + +The ```_FL_IO``` macro is used to define the port registers for the platform while the ```_FL_DEFPIN``` macro is used to define pins. The parameters to the macro are the pin number, the bit on the port that represents that pin, and the port identifier itself. On some platforms, like the AVR, ports are identified by letter. On other platforms, like arm, ports are identified by number. + +The ```HAS_HARDWARE_PIN_SUPPORT``` define tells the rest of the FastLED library that there is hardware pin support available. There may be other platform specific defines for things like hardware SPI ports and such. + +## Setting up the basic files/folders * Create platform directory (e.g. platforms/arm/kl26) * Create configuration header led_sysdefs_arm_kl26.h: @@ -12,7 +39,7 @@ * Modify led_sysdefs.h to conditionally include platform sysdefs header file * Modify platforms.h to conditionally include platform fastled header -== Porting fastpin.h == +## Porting fastpin.h The heart of the FastLED library is the fast pin accesss. This is a templated class that provides 1-2 cycle pin access, bypassing digital write and other such things. As such, this will usually be the first bit of the library that you will want to port when moving to a new platform. Once you have FastPIN up and running then you can do some basic work like testing toggles or running bit-bang'd SPI output. @@ -20,10 +47,10 @@ There's two low level FastPin classes. There's the base FastPIN template class, Explaining how the macros work and should be used is currently beyond the scope of this document. -== Porting fastspi.h == +## Porting fastspi.h This is where you define the low level interface to the hardware SPI system (including a writePixels method that does a bunch of housekeeping for writing led data). Use the fastspi_nop.h file as a reference for the methods that need to be implemented. There are ofteh other useful methods that can help with the internals of the SPI code, I recommend taking a look at how the various platforms implement their SPI classes. -== Porting clockless.h == +## Porting clockless.h This is where you define the code for the clockless controllers. Across ARM platforms this will usually be fairly similar - though different arm platforms will have different clock sources that you can/should use. diff --git a/README.md b/README.md index af3bb68be5..78748c8ab3 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,14 @@ To use this, add this define somewhere above where you `#include `: IMPORTANT NOTE: For AVR based systems, avr-gcc 4.8.x is supported and tested. This means Arduino 1.6.5 and later. -FastLED 3.2 +FastLED 3.3 =========== This is a library for easily & efficiently controlling a wide variety of LED chipsets, like the ones sold by adafruit (Neopixel, DotStar, LPD8806), Sparkfun (WS2801), and aliexpress. In addition to writing to the leds, this library also includes a number of functions for high-performing 8bit math for manipulating your RGB values, as well as low level classes for abstracting out access to pins and SPI hardware, while -still keeping things as fast as possible. Tested with Arduino up to 1.6.5 from arduino.cc. +still keeping things as fast as possible. Tested with Arduino up to 1.6.5 from arduino.cc. Quick note for people installing from GitHub repo zips, rename the folder FastLED before copying it to your Arduino/libraries folder. Github likes putting -branchname into the name of the folder, which unfortunately, makes Arduino cranky! @@ -74,7 +74,7 @@ Here's a list of all the LED chipsets are supported. More details on the led ch * TM1803 - 3 wire chipset, sold by radio shack * UCS1903 - another 3 wire led chipset, cheap * GW6205 - another 3 wire led chipset -* LPD8806 - SPI based chpiset, very high speed +* LPD8806 - SPI based chipset, very high speed * WS2801 - SPI based chipset, cheap and widely available * SM16716 - SPI based chipset * APA102 - SPI based chipset @@ -94,13 +94,14 @@ Right now the library is supported on a variety of arduino compatable platforms. * Arduino & compatibles - straight up arduino devices, uno, duo, leonardo, mega, nano, etc... * Arduino Yún * Adafruit Trinket & Gemma - Trinket Pro may be supported, but haven't tested to confirm yet -* Teensy 2, Teensy++ 2, Teensy 3.0, Teensy 3.1/3.2, Teensy LC - arduino compataible from pjrc.com with some extra goodies (note the teensy 3, 3.1, and LC are ARM, not AVR!) +* Teensy 2, Teensy++ 2, Teensy 3.0, Teensy 3.1/3.2, Teensy LC, Teensy 3.5, Teensy 3.6, and Teensy 4.0 - arduino compataible from pjrc.com with some extra goodies (note the teensy 3, 3.1, and LC are ARM, not AVR!) * Arduino Due and the digistump DigiX * RFDuino * SparkCore * Arduino Zero * ESP8266 using the arduino board definitions from http://arduino.esp8266.com/stable/package_esp8266com_index.json - please be sure to also read https://github.com/FastLED/FastLED/wiki/ESP8266-notes for information specific to the 8266. * The wino board - http://wino-board.com +* ESP32 based boards What types of platforms are we thinking about supporting in the future? Here's a short list: ChipKit32, Maple, Beagleboard diff --git a/bitswap.cpp b/bitswap.cpp deleted file mode 100644 index 67530c72ac..0000000000 --- a/bitswap.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#define FASTLED_INTERNAL -#include "FastLED.h" - -/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating -/// data into LSB for a faster write (the code using this data can happily walk the array backwards) -void transpose8x1_noinline(unsigned char *A, unsigned char *B) { - uint32_t x, y, t; - - // Load the array and pack it into x and y. - y = *(unsigned int*)(A); - x = *(unsigned int*)(A+4); - - // pre-transform x - t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); - t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); - - // pre-transform y - t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); - t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); - - // final transform - t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); - y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); - x = t; - - *((uint32_t*)B) = y; - *((uint32_t*)(B+4)) = x; -} diff --git a/color.h b/color.h deleted file mode 100644 index 1ed60b4d65..0000000000 --- a/color.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef __INC_COLOR_H -#define __INC_COLOR_H - -#include "FastLED.h" - -FASTLED_NAMESPACE_BEGIN - -///@file color.h -/// contains definitions for color correction and temperature -///@defgroup ColorEnums Color correction/temperature -/// definitions for color correction and light temperatures -///@{ -typedef enum { - // Color correction starting points - - /// typical values for SMD5050 LEDs - ///@{ - TypicalSMD5050=0xFFB0F0 /* 255, 176, 240 */, - TypicalLEDStrip=0xFFB0F0 /* 255, 176, 240 */, - ///@} - - /// typical values for 8mm "pixels on a string" - /// also for many through-hole 'T' package LEDs - ///@{ - Typical8mmPixel=0xFFE08C /* 255, 224, 140 */, - TypicalPixelString=0xFFE08C /* 255, 224, 140 */, - ///@} - - /// uncorrected color - UncorrectedColor=0xFFFFFF - -} LEDColorCorrection; - - -typedef enum { - /// @name Black-body radiation light sources - /// Black-body radiation light sources emit a (relatively) continuous - /// spectrum, and can be described as having a Kelvin 'temperature' - ///@{ - /// 1900 Kelvin - Candle=0xFF9329 /* 1900 K, 255, 147, 41 */, - /// 2600 Kelvin - Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */, - /// 2850 Kelvin - Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */, - /// 3200 Kelvin - Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */, - /// 5200 Kelvin - CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */, - /// 5400 Kelvin - HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */, - /// 6000 Kelvin - DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */, - /// 7000 Kelvin - OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */, - /// 20000 Kelvin - ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */, - ///@} - - /// @name Gaseous light sources - /// Gaseous light sources emit discrete spectral bands, and while we can - /// approximate their aggregate hue with RGB values, they don't actually - /// have a proper Kelvin temperature. - ///@{ - WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */, - StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */, - CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */, - FullSpectrumFluorescent=0xFFF4F2 /* 0 K, 255, 244, 242 */, - GrowLightFluorescent=0xFFEFF7 /* 0 K, 255, 239, 247 */, - BlackLightFluorescent=0xA700FF /* 0 K, 167, 0, 255 */, - MercuryVapor=0xD8F7FF /* 0 K, 216, 247, 255 */, - SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */, - MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */, - HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */, - ///@} - - /// Uncorrected temperature 0xFFFFFF - UncorrectedTemperature=0xFFFFFF -} ColorTemperature; - -FASTLED_NAMESPACE_END - -///@} -#endif diff --git a/component.mk b/component.mk index 27ad11a7e5..874ca9b0ac 100644 --- a/component.mk +++ b/component.mk @@ -1 +1,2 @@ -COMPONENT_ADD_INCLUDEDIRS := . +COMPONENT_ADD_INCLUDEDIRS := ./src src/platforms/esp/32 +COMPONENT_SRCDIRS := ./src src/platforms/esp/32 diff --git a/docs/Doxyfile b/docs/Doxyfile index eb300236d6..25e4f92e10 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.8 +# Doxyfile 1.8.18 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,13 +32,13 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "FastLED" +PROJECT_NAME = FastLED # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 3.1 +PROJECT_NUMBER = 3.3.3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -46,10 +46,10 @@ PROJECT_NUMBER = 3.1 PROJECT_BRIEF = -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = @@ -58,9 +58,9 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = ../docs -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -93,14 +93,22 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -119,7 +127,6 @@ REPEAT_BRIEF = YES # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = - # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. @@ -135,7 +142,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -179,6 +186,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -205,9 +222,9 @@ MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -226,16 +243,15 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -264,19 +280,28 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. @@ -285,7 +310,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,10 +318,19 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -318,7 +352,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -336,13 +370,20 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -401,7 +442,7 @@ LOOKUP_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -411,35 +452,41 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -464,21 +511,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -492,21 +539,28 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also +# names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -534,14 +588,14 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. @@ -586,27 +640,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -631,8 +683,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -677,7 +729,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -696,7 +748,7 @@ CITE_BIB_FILES = QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -704,7 +756,7 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -721,12 +773,19 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -750,15 +809,15 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = . lib8tion +INPUT = ../ ../lib8tion # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -766,15 +825,21 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = - # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. @@ -788,7 +853,7 @@ RECURSIVE = NO # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = M0-clocklessnotes.md TODO.md +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -857,6 +922,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -866,11 +935,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -918,7 +991,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -930,7 +1003,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -950,12 +1023,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -977,6 +1050,35 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1007,7 +1109,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1018,7 +1120,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = html/docs/3.1 +HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). @@ -1073,10 +1175,10 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra stylesheet files is of importance (e.g. the last -# stylesheet in the list overrules the setting of the previous ones in the +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1093,9 +1195,9 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1124,12 +1226,24 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1153,13 +1267,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1198,7 +1312,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1221,28 +1335,28 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1274,7 +1388,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1282,7 +1396,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1291,7 +1405,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1299,7 +1413,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1307,7 +1421,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1356,13 +1470,13 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. -# The default value is: . +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES @@ -1384,13 +1498,24 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png The default and svg Looks nicer but requires the +# pdf2svg tool. +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1400,7 +1525,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1411,9 +1536,15 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1439,11 +1570,11 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example @@ -1482,7 +1613,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1499,9 +1630,9 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1512,9 +1643,9 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1550,7 +1681,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = YES @@ -1566,22 +1697,36 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1599,9 +1744,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1616,9 +1764,9 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, -# for the replacement values of the other commands the user is refered to -# HTML_HEADER. +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = @@ -1634,6 +1782,17 @@ LATEX_HEADER = LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1652,7 +1811,7 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a +# the PDF file directly from the LaTeX files. Set this option to YES, to get a # higher quality PDF documentation. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1687,17 +1846,33 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1712,7 +1887,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1732,9 +1907,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1743,17 +1918,27 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1797,7 +1982,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1811,7 +1996,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1820,11 +2005,18 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1838,7 +2030,7 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the # program listings (including syntax highlighting and cross-referencing # information) to the DOCBOOK output. Note that enabling this will significantly # increase the size of the DOCBOOK output. @@ -1851,10 +2043,10 @@ DOCBOOK_PROGRAMLISTING = NO # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1863,7 +2055,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1871,7 +2063,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1879,9 +2071,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1901,14 +2093,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. -ENABLE_PREPROCESSING = NO +ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -1924,7 +2116,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -2000,37 +2192,32 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more @@ -2039,15 +2226,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2055,7 +2233,7 @@ MSCGEN_PATH = DIA_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2068,7 +2246,7 @@ HIDE_UNDOC_RELATIONS = YES # set to NO # The default value is: NO. -HAVE_DOT = YES +HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2128,7 +2306,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2180,7 +2358,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2191,7 +2370,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2214,11 +2394,15 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2266,10 +2450,19 @@ DIAFILE_DIRS = # PlantUML is not used or called during a preprocessing step. Doxygen will # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -# This tag requires that the tag HAVE_DOT is set to YES. PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2306,7 +2499,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2323,7 +2516,7 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot # files that are used to generate the various graphs. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. diff --git a/examples/Blink/Blink.ino b/examples/Blink/Blink.ino index 3364869421..443896ec07 100644 --- a/examples/Blink/Blink.ino +++ b/examples/Blink/Blink.ino @@ -3,9 +3,10 @@ // How many leds in your strip? #define NUM_LEDS 1 -// For led chips like Neopixels, which have a data line, ground, and power, you just +// For led chips like WS2812, which have a data line, ground, and power, you just // need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock, // ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN +// Clock pin only needed for SPI based chipsets when not using hardware SPI #define DATA_PIN 3 #define CLOCK_PIN 13 @@ -13,33 +14,47 @@ CRGB leds[NUM_LEDS]; void setup() { - // Uncomment/edit one of the following lines for your leds arrangement. - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); + // Uncomment/edit one of the following lines for your leds arrangement. + // ## Clockless types ## + FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is assumed + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // ## Clocked (SPI) types ## + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical } void loop() { diff --git a/examples/ColorPalette/ColorPalette.ino b/examples/ColorPalette/ColorPalette.ino index 4d64efb385..6ccd5c1b64 100644 --- a/examples/ColorPalette/ColorPalette.ino +++ b/examples/ColorPalette/ColorPalette.ino @@ -62,7 +62,7 @@ void FillLEDsFromPaletteColors( uint8_t colorIndex) { uint8_t brightness = 255; - for( int i = 0; i < NUM_LEDS; i++) { + for( int i = 0; i < NUM_LEDS; ++i) { leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending); colorIndex += 3; } @@ -101,7 +101,7 @@ void ChangePalettePeriodically() // This function fills the palette with totally random colors. void SetupTotallyRandomPalette() { - for( int i = 0; i < 16; i++) { + for( int i = 0; i < 16; ++i) { currentPalette[i] = CHSV( random8(), 255, random8()); } } diff --git a/examples/FirstLight/FirstLight.ino b/examples/FirstLight/FirstLight.ino index 9f561b08a2..8eaf4e231b 100644 --- a/examples/FirstLight/FirstLight.ino +++ b/examples/FirstLight/FirstLight.ino @@ -14,11 +14,12 @@ // How many leds are in the strip? #define NUM_LEDS 60 -// Data pin that led data will be written out over -#define DATA_PIN 3 - +// For led chips like WS2812, which have a data line, ground, and power, you just +// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock, +// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN // Clock pin only needed for SPI based chipsets when not using hardware SPI -//#define CLOCK_PIN 8 +#define DATA_PIN 3 +#define CLOCK_PIN 13 // This is an array of leds. One item for each led in your strip. CRGB leds[NUM_LEDS]; @@ -28,34 +29,47 @@ void setup() { // sanity check delay - allows reprogramming if accidently blowing power w/leds delay(2000); - // Uncomment one of the following lines for your leds arrangement. - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); + // Uncomment/edit one of the following lines for your leds arrangement. + // ## Clockless types ## + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is assumed + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // ## Clocked (SPI) types ## + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical } // This function runs over and over, and is where you do the magic to light diff --git a/examples/Multiple/MultiArrays/MultiArrays.ino b/examples/Multiple/MultiArrays/MultiArrays.ino index 50241c394b..9d3cbb6b7f 100644 --- a/examples/Multiple/MultiArrays/MultiArrays.ino +++ b/examples/Multiple/MultiArrays/MultiArrays.ino @@ -33,7 +33,7 @@ void loop() { // clear our current dot before we move on redLeds[i] = CRGB::Black; greenLeds[i] = CRGB::Black; - blueLeds[i] = CRGB::Blue; + blueLeds[i] = CRGB::Black; delay(100); } @@ -46,7 +46,7 @@ void loop() { // clear our current dot before we move on redLeds[i] = CRGB::Black; greenLeds[i] = CRGB::Black; - blueLeds[i] = CRGB::Blue; + blueLeds[i] = CRGB::Black; delay(100); } } diff --git a/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino b/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino index fc7c9e7732..8c447b54d6 100644 --- a/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino +++ b/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino @@ -1,6 +1,6 @@ #include -#define NUM_LEDS_PER_STRIP 64 +#define NUM_LEDS_PER_STRIP 16 // Note: this can be 12 if you're using a teensy 3 and don't mind soldering the pads on the back #define NUM_STRIPS 16 @@ -17,15 +17,24 @@ CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP]; // WS2811_PORTD: 25,26,27,28,14,15,29,11 // + +// IBCC outputs; + void setup() { + delay(5000); + Serial.begin(57600); + Serial.println("Starting..."); // LEDS.addLeds(leds, NUM_LEDS_PER_STRIP); // LEDS.addLeds(leds, NUM_LEDS_PER_STRIP); // LEDS.addLeds(leds, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip); LEDS.addLeds(leds, NUM_LEDS_PER_STRIP); - LEDS.setBrightness(32); + + // Teensy 4 parallel output example + // LEDS.addLeds(leds,NUM_LEDS_PER_STRIP); } void loop() { + Serial.println("Loop...."); static uint8_t hue = 0; for(int i = 0; i < NUM_STRIPS; i++) { for(int j = 0; j < NUM_LEDS_PER_STRIP; j++) { @@ -43,5 +52,5 @@ void loop() { hue++; LEDS.show(); - LEDS.delay(10); + // LEDS.delay(100); } diff --git a/examples/Pacifica/Pacifica.ino b/examples/Pacifica/Pacifica.ino new file mode 100644 index 0000000000..29ac9fcc74 --- /dev/null +++ b/examples/Pacifica/Pacifica.ino @@ -0,0 +1,152 @@ +// +// "Pacifica" +// Gentle, blue-green ocean waves. +// December 2019, Mark Kriegsman and Mary Corey March. +// For Dan. +// + +#define FASTLED_ALLOW_INTERRUPTS 0 +#include +FASTLED_USING_NAMESPACE + +#define DATA_PIN 3 +#define NUM_LEDS 60 +#define MAX_POWER_MILLIAMPS 500 +#define LED_TYPE WS2812B +#define COLOR_ORDER GRB + +////////////////////////////////////////////////////////////////////////// + +CRGB leds[NUM_LEDS]; + +void setup() { + delay( 3000); // 3 second delay for boot recovery, and a moment of silence + FastLED.addLeds(leds, NUM_LEDS) + .setCorrection( TypicalLEDStrip ); + FastLED.setMaxPowerInVoltsAndMilliamps( 5, MAX_POWER_MILLIAMPS); +} + +void loop() +{ + EVERY_N_MILLISECONDS( 20) { + pacifica_loop(); + FastLED.show(); + } +} + +////////////////////////////////////////////////////////////////////////// +// +// The code for this animation is more complicated than other examples, and +// while it is "ready to run", and documented in general, it is probably not +// the best starting point for learning. Nevertheless, it does illustrate some +// useful techniques. +// +////////////////////////////////////////////////////////////////////////// +// +// In this animation, there are four "layers" of waves of light. +// +// Each layer moves independently, and each is scaled separately. +// +// All four wave layers are added together on top of each other, and then +// another filter is applied that adds "whitecaps" of brightness where the +// waves line up with each other more. Finally, another pass is taken +// over the led array to 'deepen' (dim) the blues and greens. +// +// The speed and scale and motion each layer varies slowly within independent +// hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions +// with a lot of oddly specific numeric ranges. +// +// These three custom blue-green color palettes were inspired by the colors found in +// the waters off the southern coast of California, https://goo.gl/maps/QQgd97jjHesHZVxQ7 +// +CRGBPalette16 pacifica_palette_1 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 }; +CRGBPalette16 pacifica_palette_2 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F }; +CRGBPalette16 pacifica_palette_3 = + { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33, + 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF }; + + +void pacifica_loop() +{ + // Increment the four "color index start" counters, one for each wave layer. + // Each is incremented at a different speed, and the speeds vary over time. + static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4; + static uint32_t sLastms = 0; + uint32_t ms = GET_MILLIS(); + uint32_t deltams = ms - sLastms; + sLastms = ms; + uint16_t speedfactor1 = beatsin16(3, 179, 269); + uint16_t speedfactor2 = beatsin16(4, 179, 269); + uint32_t deltams1 = (deltams * speedfactor1) / 256; + uint32_t deltams2 = (deltams * speedfactor2) / 256; + uint32_t deltams21 = (deltams1 + deltams2) / 2; + sCIStart1 += (deltams1 * beatsin88(1011,10,13)); + sCIStart2 -= (deltams21 * beatsin88(777,8,11)); + sCIStart3 -= (deltams1 * beatsin88(501,5,7)); + sCIStart4 -= (deltams2 * beatsin88(257,4,6)); + + // Clear out the LED array to a dim background blue-green + fill_solid( leds, NUM_LEDS, CRGB( 2, 6, 10)); + + // Render each of four layers, with different scales and speeds, that vary over time + pacifica_one_layer( pacifica_palette_1, sCIStart1, beatsin16( 3, 11 * 256, 14 * 256), beatsin8( 10, 70, 130), 0-beat16( 301) ); + pacifica_one_layer( pacifica_palette_2, sCIStart2, beatsin16( 4, 6 * 256, 9 * 256), beatsin8( 17, 40, 80), beat16( 401) ); + pacifica_one_layer( pacifica_palette_3, sCIStart3, 6 * 256, beatsin8( 9, 10,38), 0-beat16(503)); + pacifica_one_layer( pacifica_palette_3, sCIStart4, 5 * 256, beatsin8( 8, 10,28), beat16(601)); + + // Add brighter 'whitecaps' where the waves lines up more + pacifica_add_whitecaps(); + + // Deepen the blues and greens a bit + pacifica_deepen_colors(); +} + +// Add one layer of waves into the led array +void pacifica_one_layer( CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) +{ + uint16_t ci = cistart; + uint16_t waveangle = ioff; + uint16_t wavescale_half = (wavescale / 2) + 20; + for( uint16_t i = 0; i < NUM_LEDS; i++) { + waveangle += 250; + uint16_t s16 = sin16( waveangle ) + 32768; + uint16_t cs = scale16( s16 , wavescale_half ) + wavescale_half; + ci += cs; + uint16_t sindex16 = sin16( ci) + 32768; + uint8_t sindex8 = scale16( sindex16, 240); + CRGB c = ColorFromPalette( p, sindex8, bri, LINEARBLEND); + leds[i] += c; + } +} + +// Add extra 'white' to areas where the four layers of light have lined up brightly +void pacifica_add_whitecaps() +{ + uint8_t basethreshold = beatsin8( 9, 55, 65); + uint8_t wave = beat8( 7 ); + + for( uint16_t i = 0; i < NUM_LEDS; i++) { + uint8_t threshold = scale8( sin8( wave), 20) + basethreshold; + wave += 7; + uint8_t l = leds[i].getAverageLight(); + if( l > threshold) { + uint8_t overage = l - threshold; + uint8_t overage2 = qadd8( overage, overage); + leds[i] += CRGB( overage, overage2, qadd8( overage2, overage2)); + } + } +} + +// Deepen the blues and greens +void pacifica_deepen_colors() +{ + for( uint16_t i = 0; i < NUM_LEDS; i++) { + leds[i].blue = scale8( leds[i].blue, 145); + leds[i].green= scale8( leds[i].green, 200); + leds[i] |= CRGB( 2, 5, 7); + } +} diff --git a/examples/Pintest/Pintest.ino b/examples/Pintest/Pintest.ino index a63f0d46ff..a8141520a7 100644 --- a/examples/Pintest/Pintest.ino +++ b/examples/Pintest/Pintest.ino @@ -1,7 +1,10 @@ -#include +#include + +char fullstrBuffer[64]; const char *getPort(void *portPtr) { +// AVR port checks #ifdef PORTA if(portPtr == (void*)&PORTA) { return "PORTA"; } #endif @@ -38,6 +41,8 @@ const char *getPort(void *portPtr) { #ifdef PORTL if(portPtr == (void*)&PORTL) { return "PORTL"; } #endif + +// Teensy 3.x port checks #ifdef GPIO_A_PDOR if(portPtr == (void*)&GPIO_A_PDOR) { return "GPIO_A_PDOR"; } #endif @@ -65,41 +70,130 @@ const char *getPort(void *portPtr) { #ifdef REG_PIO_D_ODSR if(portPtr == (void*)®_PIO_D_ODSR) { return "REG_PIO_D_ODSR"; } #endif - return "unknown"; + +// Teensy 4 port checks +#ifdef GPIO1_DR + if(portPtr == (void*)&GPIO1_DR) { return "GPIO1_DR"; } +#endif +#ifdef GPIO2_DR +if(portPtr == (void*)&GPIO2_DR) { return "GPIO21_DR"; } +#endif +#ifdef GPIO3_DR +if(portPtr == (void*)&GPIO3_DR) { return "GPIO3_DR"; } +#endif +#ifdef GPIO4_DR +if(portPtr == (void*)&GPIO4_DR) { return "GPIO4_DR"; } +#endif + String unknown_str = "Unknown: " + String((size_t)portPtr, HEX); + strncpy(fullstrBuffer, unknown_str.c_str(), unknown_str.length()); + fullstrBuffer[sizeof(fullstrBuffer)-1] = '\0'; + return fullstrBuffer; } template void CheckPin() { CheckPin(); - RwReg *systemThinksPortIs = portOutputRegister(digitalPinToPort(PIN)); + void *systemThinksPortIs = (void*)portOutputRegister(digitalPinToPort(PIN)); RwReg systemThinksMaskIs = digitalPinToBitMask(PIN); - + Serial.print("Pin "); Serial.print(PIN); Serial.print(": Port "); - - if(systemThinksPortIs == FastPin::port()) { + + if(systemThinksPortIs == (void*)FastPin::port()) { Serial.print("valid & mask "); - } else { - Serial.print("invalid, is "); Serial.print(getPort((void*)FastPin::port())); Serial.print(" should be "); + } else { + Serial.print("invalid, is "); Serial.print(getPort((void*)FastPin::port())); Serial.print(" should be "); Serial.print(getPort((void*)systemThinksPortIs)); Serial.print(" & mask "); } if(systemThinksMaskIs == FastPin::mask()) { Serial.println("valid."); - } else { + } else { Serial.print("invalid, is "); Serial.print(FastPin::mask()); Serial.print(" should be "); Serial.println(systemThinksMaskIs); } -} +} + +template<> void CheckPin<255> () {} + + +template const char *_GetPinPort(void *ptr) { + if (__FL_PORT_INFO<_PORT>::hasPort() && (ptr == (void*)__FL_PORT_INFO<_PORT>::portAddr())) { + return __FL_PORT_INFO<_PORT>::portName(); + } else { + return _GetPinPort<_PORT - 1>(ptr); + } +} +template<> const char *_GetPinPort<-1>(void *ptr) { + return NULL; +} + +const char *GetPinPort(void *ptr) { + return _GetPinPort<'Z'>(ptr); +} + +static uint8_t pcount = 0; + + +template void PrintPins() { + PrintPins(); + + RwReg *systemThinksPortIs = portOutputRegister(digitalPinToPort(PIN)); + RwReg systemThinksMaskIs = digitalPinToBitMask(PIN); + + int maskBit = 0; + while(systemThinksMaskIs > 1) { systemThinksMaskIs >>= 1; maskBit++; } + + const char *pinport = GetPinPort((void*)systemThinksPortIs); + if (pinport) { + Serial.print("__FL_DEFPIN("); Serial.print(PIN); + Serial.print(","); Serial.print(maskBit); + Serial.print(","); Serial.print(pinport); + Serial.print("); "); + pcount++; + if(pcount == 4) { pcount = 0; Serial.println(""); } + } else { + // Serial.print("Not found for pin "); Serial.println(PIN); + } +} -template<> void CheckPin<-1> () {} +template<> void PrintPins<0>() { + RwReg *systemThinksPortIs = portOutputRegister(digitalPinToPort(0)); + RwReg systemThinksMaskIs = digitalPinToBitMask(0); -void setup() { + int maskBit = 0; + while(systemThinksMaskIs > 1) { systemThinksMaskIs >>= 1; maskBit++; } + + const char *pinport = GetPinPort((void*)systemThinksPortIs); + if (pinport) { + Serial.print("__FL_DEFPIN("); Serial.print(0); + Serial.print(","); Serial.print(maskBit); + Serial.print(","); Serial.print(pinport); + Serial.print("); "); + pcount++; + if(pcount == 4) { pcount = 0; Serial.println(""); } + } +} + +int counter = 0; +void setup() { + delay(5000); Serial.begin(38400); Serial.println("resetting!"); } -void loop() { +void loop() { + Serial.println(counter); + +#ifdef MAX_PIN CheckPin(); - delay(10000); +#endif + + Serial.println("-----"); +#ifdef NUM_DIGITAL_PINS + PrintPins(); +#endif + Serial.println("------"); + + delay(100000); } diff --git a/examples/Pride2015/Pride2015.ino b/examples/Pride2015/Pride2015.ino new file mode 100644 index 0000000000..0fbd3a5bce --- /dev/null +++ b/examples/Pride2015/Pride2015.ino @@ -0,0 +1,82 @@ +#include "FastLED.h" + +// Pride2015 +// Animated, ever-changing rainbows. +// by Mark Kriegsman + +#if FASTLED_VERSION < 3001000 +#error "Requires FastLED 3.1 or later; check github for latest code." +#endif + +#define DATA_PIN 3 +//#define CLK_PIN 4 +#define LED_TYPE WS2811 +#define COLOR_ORDER GRB +#define NUM_LEDS 200 +#define BRIGHTNESS 255 + +CRGB leds[NUM_LEDS]; + + +void setup() { + delay(3000); // 3 second delay for recovery + + // tell FastLED about the LED strip configuration + FastLED.addLeds(leds, NUM_LEDS) + .setCorrection(TypicalLEDStrip) + .setDither(BRIGHTNESS < 255); + + // set master brightness control + FastLED.setBrightness(BRIGHTNESS); +} + + +void loop() +{ + pride(); + FastLED.show(); +} + + +// This function draws rainbows with an ever-changing, +// widely-varying set of parameters. +void pride() +{ + static uint16_t sPseudotime = 0; + static uint16_t sLastMillis = 0; + static uint16_t sHue16 = 0; + + uint8_t sat8 = beatsin88( 87, 220, 250); + uint8_t brightdepth = beatsin88( 341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); + uint8_t msmultiplier = beatsin88(147, 23, 60); + + uint16_t hue16 = sHue16;//gHue * 256; + uint16_t hueinc16 = beatsin88(113, 1, 3000); + + uint16_t ms = millis(); + uint16_t deltams = ms - sLastMillis ; + sLastMillis = ms; + sPseudotime += deltams * msmultiplier; + sHue16 += deltams * beatsin88( 400, 5,9); + uint16_t brightnesstheta16 = sPseudotime; + + for( uint16_t i = 0 ; i < NUM_LEDS; i++) { + hue16 += hueinc16; + uint8_t hue8 = hue16 / 256; + + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16( brightnesstheta16 ) + 32768; + + uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; + uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; + bri8 += (255 - brightdepth); + + CRGB newcolor = CHSV( hue8, sat8, bri8); + + uint16_t pixelnumber = i; + pixelnumber = (NUM_LEDS-1) - pixelnumber; + + nblend( leds[pixelnumber], newcolor, 64); + } +} diff --git a/examples/RGBCalibrate/RGBCalibrate.ino b/examples/RGBCalibrate/RGBCalibrate.ino index 72e21507b2..5ff33805a2 100644 --- a/examples/RGBCalibrate/RGBCalibrate.ino +++ b/examples/RGBCalibrate/RGBCalibrate.ino @@ -23,12 +23,14 @@ // ////////////////////////////////////////////////// -#define NUM_LEDS 6 +#define NUM_LEDS 7 -// Data pin that led data will be written out over -#define DATA_PIN 6 +// For led chips like WS2812, which have a data line, ground, and power, you just +// need to define DATA_PIN. For led chipsets that are SPI based (four wires - data, clock, +// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN // Clock pin only needed for SPI based chipsets when not using hardware SPI -//#define CLOCK_PIN 8 +#define DATA_PIN 3 +#define CLOCK_PIN 13 CRGB leds[NUM_LEDS]; @@ -36,27 +38,48 @@ void setup() { // sanity check delay - allows reprogramming if accidently blowing power w/leds delay(2000); - // Uncomment one of the following lines for your leds arrangement. - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); + // Uncomment/edit one of the following lines for your leds arrangement. + // ## Clockless types ## + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.setBrightness(CRGB(255,255,255)); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // FastLED.addLeds(leds, NUM_LEDS); // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); - FastLED.addLeds(leds, NUM_LEDS); - + // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); + // ## Clocked (SPI) types ## + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // GRB ordering is typical // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // FastLED.addLeds(leds, NUM_LEDS); - // FastLED.addLeds(leds, NUM_LEDS); + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + // FastLED.addLeds(leds, NUM_LEDS); // BGR ordering is typical + + // FastLED.setBrightness(CRGB(255,255,255)); } void loop() { @@ -69,4 +92,4 @@ void loop() { leds[6] = CRGB(0,0,0); FastLED.show(); delay(1000); -} \ No newline at end of file +} diff --git a/examples/TwinkleFox/TwinkleFox.ino b/examples/TwinkleFox/TwinkleFox.ino new file mode 100644 index 0000000000..4821139bd6 --- /dev/null +++ b/examples/TwinkleFox/TwinkleFox.ino @@ -0,0 +1,383 @@ +#include "FastLED.h" + +#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000) +#warning "Requires FastLED 3.1 or later; check github for latest code." +#endif + + +#define NUM_LEDS 100 +#define LED_TYPE WS2811 +#define COLOR_ORDER GRB +#define DATA_PIN 3 +//#define CLK_PIN 4 +#define VOLTS 12 +#define MAX_MA 4000 + +// TwinkleFOX: Twinkling 'holiday' lights that fade in and out. +// Colors are chosen from a palette; a few palettes are provided. +// +// This December 2015 implementation improves on the December 2014 version +// in several ways: +// - smoother fading, compatible with any colors and any palettes +// - easier control of twinkle speed and twinkle density +// - supports an optional 'background color' +// - takes even less RAM: zero RAM overhead per pixel +// - illustrates a couple of interesting techniques (uh oh...) +// +// The idea behind this (new) implementation is that there's one +// basic, repeating pattern that each pixel follows like a waveform: +// The brightness rises from 0..255 and then falls back down to 0. +// The brightness at any given point in time can be determined as +// as a function of time, for example: +// brightness = sine( time ); // a sine wave of brightness over time +// +// So the way this implementation works is that every pixel follows +// the exact same wave function over time. In this particular case, +// I chose a sawtooth triangle wave (triwave8) rather than a sine wave, +// but the idea is the same: brightness = triwave8( time ). +// +// Of course, if all the pixels used the exact same wave form, and +// if they all used the exact same 'clock' for their 'time base', all +// the pixels would brighten and dim at once -- which does not look +// like twinkling at all. +// +// So to achieve random-looking twinkling, each pixel is given a +// slightly different 'clock' signal. Some of the clocks run faster, +// some run slower, and each 'clock' also has a random offset from zero. +// The net result is that the 'clocks' for all the pixels are always out +// of sync from each other, producing a nice random distribution +// of twinkles. +// +// The 'clock speed adjustment' and 'time offset' for each pixel +// are generated randomly. One (normal) approach to implementing that +// would be to randomly generate the clock parameters for each pixel +// at startup, and store them in some arrays. However, that consumes +// a great deal of precious RAM, and it turns out to be totally +// unnessary! If the random number generate is 'seeded' with the +// same starting value every time, it will generate the same sequence +// of values every time. So the clock adjustment parameters for each +// pixel are 'stored' in a pseudo-random number generator! The PRNG +// is reset, and then the first numbers out of it are the clock +// adjustment parameters for the first pixel, the second numbers out +// of it are the parameters for the second pixel, and so on. +// In this way, we can 'store' a stable sequence of thousands of +// random clock adjustment parameters in literally two bytes of RAM. +// +// There's a little bit of fixed-point math involved in applying the +// clock speed adjustments, which are expressed in eighths. Each pixel's +// clock speed ranges from 8/8ths of the system clock (i.e. 1x) to +// 23/8ths of the system clock (i.e. nearly 3x). +// +// On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels +// smoothly at over 50 updates per seond. +// +// -Mark Kriegsman, December 2015 + +CRGBArray leds; + +// Overall twinkle speed. +// 0 (VERY slow) to 8 (VERY fast). +// 4, 5, and 6 are recommended, default is 4. +#define TWINKLE_SPEED 4 + +// Overall twinkle density. +// 0 (NONE lit) to 8 (ALL lit at once). +// Default is 5. +#define TWINKLE_DENSITY 5 + +// How often to change color palettes. +#define SECONDS_PER_PALETTE 30 +// Also: toward the bottom of the file is an array +// called "ActivePaletteList" which controls which color +// palettes are used; you can add or remove color palettes +// from there freely. + +// Background color for 'unlit' pixels +// Can be set to CRGB::Black if desired. +CRGB gBackgroundColor = CRGB::Black; +// Example of dim incandescent fairy light background color +// CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16); + +// If AUTO_SELECT_BACKGROUND_COLOR is set to 1, +// then for any palette where the first two entries +// are the same, a dimmed version of that color will +// automatically be used as the background color. +#define AUTO_SELECT_BACKGROUND_COLOR 0 + +// If COOL_LIKE_INCANDESCENT is set to 1, colors will +// fade out slighted 'reddened', similar to how +// incandescent bulbs change color as they get dim down. +#define COOL_LIKE_INCANDESCENT 1 + + +CRGBPalette16 gCurrentPalette; +CRGBPalette16 gTargetPalette; + +void setup() { + delay( 3000 ); //safety startup delay + FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA); + FastLED.addLeds(leds, NUM_LEDS) + .setCorrection(TypicalLEDStrip); + + chooseNextColorPalette(gTargetPalette); +} + + +void loop() +{ + EVERY_N_SECONDS( SECONDS_PER_PALETTE ) { + chooseNextColorPalette( gTargetPalette ); + } + + EVERY_N_MILLISECONDS( 10 ) { + nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 12); + } + + drawTwinkles( leds); + + FastLED.show(); +} + + +// This function loops over each pixel, calculates the +// adjusted 'clock' that this pixel should use, and calls +// "CalculateOneTwinkle" on each pixel. It then displays +// either the twinkle color of the background color, +// whichever is brighter. +void drawTwinkles( CRGBSet& L) +{ + // "PRNG16" is the pseudorandom number generator + // It MUST be reset to the same starting value each time + // this function is called, so that the sequence of 'random' + // numbers that it generates is (paradoxically) stable. + uint16_t PRNG16 = 11337; + + uint32_t clock32 = millis(); + + // Set up the background color, "bg". + // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of + // the current palette are identical, then a deeply faded version of + // that color is used for the background color + CRGB bg; + if( (AUTO_SELECT_BACKGROUND_COLOR == 1) && + (gCurrentPalette[0] == gCurrentPalette[1] )) { + bg = gCurrentPalette[0]; + uint8_t bglight = bg.getAverageLight(); + if( bglight > 64) { + bg.nscale8_video( 16); // very bright, so scale to 1/16th + } else if( bglight > 16) { + bg.nscale8_video( 64); // not that bright, so scale to 1/4th + } else { + bg.nscale8_video( 86); // dim, scale to 1/3rd. + } + } else { + bg = gBackgroundColor; // just use the explicitly defined background color + } + + uint8_t backgroundBrightness = bg.getAverageLight(); + + for( CRGB& pixel: L) { + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number + uint16_t myclockoffset16= PRNG16; // use that number as clock offset + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number + // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths) + uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08; + uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; + uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel + + // We now have the adjusted 'clock' for this pixel, now we call + // the function that computes what color the pixel should be based + // on the "brightness = f( time )" idea. + CRGB c = computeOneTwinkle( myclock30, myunique8); + + uint8_t cbright = c.getAverageLight(); + int16_t deltabright = cbright - backgroundBrightness; + if( deltabright >= 32 || (!bg)) { + // If the new pixel is significantly brighter than the background color, + // use the new color. + pixel = c; + } else if( deltabright > 0 ) { + // If the new pixel is just slightly brighter than the background color, + // mix a blend of the new color and the background color + pixel = blend( bg, c, deltabright * 8); + } else { + // if the new pixel is not at all brighter than the background color, + // just use the background color. + pixel = bg; + } + } +} + + +// This function takes a time in pseudo-milliseconds, +// figures out brightness = f( time ), and also hue = f( time ) +// The 'low digits' of the millisecond time are used as +// input to the brightness wave function. +// The 'high digits' are used to select a color, so that the color +// does not change over the course of the fade-in, fade-out +// of one cycle of the brightness wave function. +// The 'high digits' are also used to determine whether this pixel +// should light at all during this cycle, based on the TWINKLE_DENSITY. +CRGB computeOneTwinkle( uint32_t ms, uint8_t salt) +{ + uint16_t ticks = ms >> (8-TWINKLE_SPEED); + uint8_t fastcycle8 = ticks; + uint16_t slowcycle16 = (ticks >> 8) + salt; + slowcycle16 += sin8( slowcycle16); + slowcycle16 = (slowcycle16 * 2053) + 1384; + uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); + + uint8_t bright = 0; + if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) { + bright = attackDecayWave8( fastcycle8); + } + + uint8_t hue = slowcycle8 - salt; + CRGB c; + if( bright > 0) { + c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND); + if( COOL_LIKE_INCANDESCENT == 1 ) { + coolLikeIncandescent( c, fastcycle8); + } + } else { + c = CRGB::Black; + } + return c; +} + + +// This function is like 'triwave8', which produces a +// symmetrical up-and-down triangle sawtooth waveform, except that this +// function produces a triangle wave with a faster attack and a slower decay: +// +// / \ +// / \ +// / \ +// / \ +// + +uint8_t attackDecayWave8( uint8_t i) +{ + if( i < 86) { + return i * 3; + } else { + i -= 86; + return 255 - (i + (i/2)); + } +} + +// This function takes a pixel, and if its in the 'fading down' +// part of the cycle, it adjusts the color a little bit like the +// way that incandescent bulbs fade toward 'red' as they dim. +void coolLikeIncandescent( CRGB& c, uint8_t phase) +{ + if( phase < 128) return; + + uint8_t cooling = (phase - 128) >> 4; + c.g = qsub8( c.g, cooling); + c.b = qsub8( c.b, cooling * 2); +} + +// A mostly red palette with green accents and white trim. +// "CRGB::Gray" is used as white to keep the brightness more uniform. +const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM = +{ CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray, + CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green }; + +// A mostly (dark) green palette with red berries. +#define Holly_Green 0x00580c +#define Holly_Red 0xB00402 +const TProgmemRGBPalette16 Holly_p FL_PROGMEM = +{ Holly_Green, Holly_Green, Holly_Green, Holly_Green, + Holly_Green, Holly_Green, Holly_Green, Holly_Green, + Holly_Green, Holly_Green, Holly_Green, Holly_Green, + Holly_Green, Holly_Green, Holly_Green, Holly_Red +}; + +// A red and white striped palette +// "CRGB::Gray" is used as white to keep the brightness more uniform. +const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM = +{ CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray, + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray }; + +// A mostly blue palette with white accents. +// "CRGB::Gray" is used as white to keep the brightness more uniform. +const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM = +{ CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, + CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, + CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, + CRGB::Blue, CRGB::Gray, CRGB::Gray, CRGB::Gray }; + +// A pure "fairy light" palette with some brightness variations +#define HALFFAIRY ((CRGB::FairyLight & 0xFEFEFE) / 2) +#define QUARTERFAIRY ((CRGB::FairyLight & 0xFCFCFC) / 4) +const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM = +{ CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, + HALFFAIRY, HALFFAIRY, CRGB::FairyLight, CRGB::FairyLight, + QUARTERFAIRY, QUARTERFAIRY, CRGB::FairyLight, CRGB::FairyLight, + CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight }; + +// A palette of soft snowflakes with the occasional bright one +const TProgmemRGBPalette16 Snow_p FL_PROGMEM = +{ 0x304048, 0x304048, 0x304048, 0x304048, + 0x304048, 0x304048, 0x304048, 0x304048, + 0x304048, 0x304048, 0x304048, 0x304048, + 0x304048, 0x304048, 0x304048, 0xE0F0FF }; + +// A palette reminiscent of large 'old-school' C9-size tree lights +// in the five classic colors: red, orange, green, blue, and white. +#define C9_Red 0xB80400 +#define C9_Orange 0x902C02 +#define C9_Green 0x046002 +#define C9_Blue 0x070758 +#define C9_White 0x606820 +const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM = +{ C9_Red, C9_Orange, C9_Red, C9_Orange, + C9_Orange, C9_Red, C9_Orange, C9_Red, + C9_Green, C9_Green, C9_Green, C9_Green, + C9_Blue, C9_Blue, C9_Blue, + C9_White +}; + +// A cold, icy pale blue palette +#define Ice_Blue1 0x0C1040 +#define Ice_Blue2 0x182080 +#define Ice_Blue3 0x5080C0 +const TProgmemRGBPalette16 Ice_p FL_PROGMEM = +{ + Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, + Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, + Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, + Ice_Blue2, Ice_Blue2, Ice_Blue2, Ice_Blue3 +}; + + +// Add or remove palette names from this list to control which color +// palettes are used, and in what order. +const TProgmemRGBPalette16* ActivePaletteList[] = { + &RetroC9_p, + &BlueWhite_p, + &RainbowColors_p, + &FairyLight_p, + &RedGreenWhite_p, + &PartyColors_p, + &RedWhite_p, + &Snow_p, + &Holly_p, + &Ice_p +}; + + +// Advance to the next color palette in the list (above). +void chooseNextColorPalette( CRGBPalette16& pal) +{ + const uint8_t numberOfPalettes = sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]); + static uint8_t whichPalette = -1; + whichPalette = addmod8( whichPalette, 1, numberOfPalettes); + + pal = *(ActivePaletteList[whichPalette]); +} diff --git a/examples/XYMatrix/XYMatrix.ino b/examples/XYMatrix/XYMatrix.ino index 53c21411f7..010ffe7cf1 100644 --- a/examples/XYMatrix/XYMatrix.ino +++ b/examples/XYMatrix/XYMatrix.ino @@ -30,6 +30,7 @@ const uint8_t kMatrixHeight = 16; // Param for different pixel layouts const bool kMatrixSerpentineLayout = true; +const bool kMatrixVertical = false; // Set 'kMatrixSerpentineLayout' to false if your pixels are // laid out all running the same way, like this: // @@ -88,17 +89,29 @@ uint16_t XY( uint8_t x, uint8_t y) uint16_t i; if( kMatrixSerpentineLayout == false) { - i = (y * kMatrixWidth) + x; + if (kMatrixVertical == false) { + i = (y * kMatrixWidth) + x; + } else { + i = kMatrixHeight * (kMatrixWidth - (x+1))+y; + } } if( kMatrixSerpentineLayout == true) { - if( y & 0x01) { - // Odd rows run backwards - uint8_t reverseX = (kMatrixWidth - 1) - x; - i = (y * kMatrixWidth) + reverseX; - } else { - // Even rows run forwards - i = (y * kMatrixWidth) + x; + if (kMatrixVertical == false) { + if( y & 0x01) { + // Odd rows run backwards + uint8_t reverseX = (kMatrixWidth - 1) - x; + i = (y * kMatrixWidth) + reverseX; + } else { + // Even rows run forwards + i = (y * kMatrixWidth) + x; + } + } else { // vertical positioning + if ( x & 0x01) { + i = kMatrixHeight * (kMatrixWidth - (x+1))+y; + } else { + i = kMatrixHeight * (kMatrixWidth - x) - (y+1); + } } } diff --git a/keywords.txt b/keywords.txt index c30552d8d6..75df500ca0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -294,41 +294,52 @@ CRGB::YellowGreen KEYWORD2 ####################################### # Chipsets -LPD6803 LITERAL1 -LPD8806 LITERAL1 -WS2801 LITERAL1 -WS2803 LITERAL1 -P9813 LITERAL1 -SM16716 LITERAL1 APA102 LITERAL1 +APA104 LITERAL1 +APA106 LITERAL1 DMXSERIAL LITERAL1 DMXSIMPLE LITERAL1 -TM1829 LITERAL1 -TM1809 LITERAL1 -TM1804 LITERAL1 -TM1803 LITERAL1 -APA104 LITERAL1 -WS2811 LITERAL1 -WS2812 LITERAL1 -WS2812B LITERAL1 -WS2811_400 LITERAL1 -WS2813 LITERAL1 -NEOPIXEL LITERAL1 -UCS1903 LITERAL1 -UCS1903B LITERAL1 +DOTSTAR LITERAL1 +GE8822 LITERAL1 +GS1903 LITERAL1 GW6205 LITERAL1 GW6205B LITERAL1 +GW6205_400 LITERAL1 LPD1886 LITERAL1 +LPD1886_8BIT LITERAL1 +LPD6803 LITERAL1 +LPD8806 LITERAL1 +NEOPIXEL LITERAL1 OCTOWS2811 LITERAL1 OCTOWS2811_400 LITERAL1 OCTOWS2813 LITERAL1 -WS2812SERIAL LITERAL1 -SMART_MATRIX LITERAL1 -GE8822 LITERAL1 +P9813 LITERAL1 +PIXIE LITERAL1 +PL9823 LITERAL1 +SK6812 LITERAL1 +SK6822 LITERAL1 +SK9822 LITERAL1 SM16703 LITERAL1 -GS1903 LITERAL1 -LPD6803 LITERAL1 - +SM16716 LITERAL1 +SMART_MATRIX LITERAL1 +TM1803 LITERAL1 +TM1804 LITERAL1 +TM1809 LITERAL1 +TM1812 LITERAL1 +TM1829 LITERAL1 +UCS1903 LITERAL1 +UCS1903B LITERAL1 +UCS1904 LITERAL1 +UCS2903 LITERAL1 +WS2801 LITERAL1 +WS2803 LITERAL1 +WS2811 LITERAL1 +WS2811_400 LITERAL1 +WS2812 LITERAL1 +WS2812B LITERAL1 +WS2812SERIAL LITERAL1 +WS2813 LITERAL1 +WS2852 LITERAL1 # RGB orderings RGB LITERAL1 diff --git a/library.json b/library.json index 0e8eb2eaaf..7b1dd4710e 100644 --- a/library.json +++ b/library.json @@ -12,17 +12,32 @@ "name": "Mark Kriegsman", "url": "https://github.com/kriegsman", "maintainer": true + }, + { + "name": "Sam Guyer", + "url": "https://github.com/samguyer", + "maintainer": true + }, + { + "name": "Jason Coon", + "url": "https://github.com/jasoncoon", + "maintainer": true + }, + { + "name": "Josh Huber", + "url": "https://github.com/uberjay", + "maintainer": true } ], "repository": { "type": "git", "url": "https://github.com/FastLED/FastLED.git" }, - "version": "3.2.6", + "version": "3.3.3", "license": "MIT", "homepage": "http://fastled.io", "frameworks": "arduino", - "platforms": "atmelavr, atmelsam, freescalekinetis, nordicnrf51, nxplpc, ststm32, teensy, espressif8266, espressif32", + "platforms": "atmelavr, atmelsam, freescalekinetis, nordicnrf51, nxplpc, ststm32, teensy, espressif8266, espressif32, nordicnrf52", "export": { "exclude": [ "docs", @@ -33,8 +48,9 @@ "srcFilter": [ "+<*.c>", "+<*.cpp>", - "+<*.h>" + "+<*.h>", + "+" ], "libArchive": false - } + } } diff --git a/library.properties b/library.properties index 619b50953e..2ebea658bb 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=FastLED -version=3.2.6 +version=3.3.3 author=Daniel Garcia maintainer=Daniel Garcia sentence=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions. @@ -7,3 +7,4 @@ paragraph=Multi-platform library for controlling dozens of different types of LE category=Display url=https://github.com/FastLED/FastLED architectures=* +includes=FastLED.h diff --git a/pixelset.h b/pixelset.h deleted file mode 100644 index 097df6d67a..0000000000 --- a/pixelset.h +++ /dev/null @@ -1,305 +0,0 @@ -#ifndef __INC_PIXELSET_H -#define __INC_PIXELSET_H - -#include "FastLED.h" - -#ifndef abs -#include -#endif - -/// Represents a set of CRGB led objects. Provides the [] array operator, and works like a normal array in that case. -/// This should be kept in sync with the set of functions provided by CRGB as well as functions in colorutils. Note -/// that a pixel set is a window into another set of led data, it is not its own set of led data. -template -class CPixelView { -public: - const int8_t dir; - const int len; - PIXEL_TYPE * const leds; - PIXEL_TYPE * const end_pos; - -public: - - /// PixelSet copy constructor - inline CPixelView(const CPixelView & other) : dir(other.dir), len(other.len), leds(other.leds), end_pos(other.end_pos) {} - - /// pixelset constructor for a pixel set starting at the given PIXEL_TYPE* and going for _len leds. Note that the length - /// can be backwards, creating a PixelSet that walks backwards over the data - /// @param leds point to the raw led data - /// @param len how many leds in this set - inline CPixelView(PIXEL_TYPE *_leds, int _len) : dir(_len < 0 ? -1 : 1), len(_len), leds(_leds), end_pos(_leds + _len) {} - - /// PixelSet constructor for the given set of leds, with start and end boundaries. Note that start can be after - /// end, resulting in a set that will iterate backwards - /// @param leds point to the raw led data - /// @param start the start index of the leds for this array - /// @param end the end index of the leds for this array - inline CPixelView(PIXEL_TYPE *_leds, int _start, int _end) : dir(((_end-_start)<0) ? -1 : 1), len((_end - _start) + dir), leds(_leds + _start), end_pos(_leds + _start + len) {} - - /// Get the size of this set - /// @return the size of the set - int size() { return abs(len); } - - /// Whether or not this set goes backwards - /// @return whether or not the set is backwards - bool reversed() { return len < 0; } - - /// do these sets point to the same thing (note, this is different from the contents of the set being the same) - bool operator==(const CPixelView & rhs) const { return leds == rhs.leds && len == rhs.len && dir == rhs.dir; } - - /// do these sets point to the different things (note, this is different from the contents of the set being the same) - bool operator!=(const CPixelView & rhs) const { return leds != rhs.leds || len != rhs.len || dir != rhs.dir; } - - /// access a single element in this set, just like an array operator - inline PIXEL_TYPE & operator[](int x) const { if(dir & 0x80) { return leds[-x]; } else { return leds[x]; } } - - /// Access an inclusive subset of the leds in this set. Note that start can be greater than end, which will - /// result in a reverse ordering for many functions (useful for mirroring) - /// @param start the first element from this set for the new subset - /// @param end the last element for the new subset - inline CPixelView operator()(int start, int end) { return CPixelView(leds, start, end); } - - /// Access an inclusive subset of the leds in this set, starting from the first. - /// @param end the last element for the new subset - /// Not sure i want this? inline CPixelView operator()(int end) { return CPixelView(leds, 0, end); } - - /// Return the reverse ordering of this set - inline CPixelView operator-() { return CPixelView(leds + len - dir, len - dir, 0); } - - /// Return a pointer to the first element in this set - inline operator PIXEL_TYPE* () const { return leds; } - - /// Assign the passed in color to all elements in this set - /// @param color the new color for the elements in the set - inline CPixelView & operator=(const PIXEL_TYPE & color) { - for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) = color; } - return *this; - } - - - void dump() const { -/** - Serial.print("len: "); Serial.print(len); Serial.print(", dir:"); Serial.print((int)dir); - Serial.print(", range:"); Serial.print((uint32_t)leds); Serial.print("-"); Serial.print((uint32_t)end_pos); - Serial.print(", diff:"); Serial.print((int32_t)(end_pos - leds)); - Serial.println(""); - **/ - } - - /// Copy the contents of the passed in set to our set. Note if one set is smaller than the other, only the - /// smallest number of items will be copied over. - inline CPixelView & operator=(const CPixelView & rhs) { - for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { - (*pixel) = (*rhspixel); - } - return *this; - } - - /// @name modification/scaling operators - //@{ - /// Add the passed in value to r,g, b for all the pixels in this set - inline CPixelView & addToRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) += inc; } return *this; } - /// Add every pixel in the other set to this set - inline CPixelView & operator+=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) += (*rhspixel); } return *this; } - - /// Subtract the passed in value from r,g,b for all pixels in this set - inline CPixelView & subFromRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) -= inc; } return *this; } - /// Subtract every pixel in the other set from this set - inline CPixelView & operator-=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) -= (*rhspixel); } return *this; } - - /// Increment every pixel value in this set - inline CPixelView & operator++() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; } - /// Increment every pixel value in this set - inline CPixelView & operator++(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; } - - /// Decrement every pixel value in this set - inline CPixelView & operator--() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; } - /// Decrement every pixel value in this set - inline CPixelView & operator--(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; } - - /// Divide every led by the given value - inline CPixelView & operator/=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) /= d; } return *this; } - /// Shift every led in this set right by the given number of bits - inline CPixelView & operator>>=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) >>= d; } return *this; } - /// Multiply every led in this set by the given value - inline CPixelView & operator*=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) *= d; } return *this; } - - /// Scale every led by the given scale - inline CPixelView & nscale8_video(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this;} - /// Scale down every led by the given scale - inline CPixelView & operator%=(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this; } - /// Fade every led down by the given scale - inline CPixelView & fadeLightBy(uint8_t fadefactor) { return nscale8_video(255 - fadefactor); } - - /// Scale every led by the given scale - inline CPixelView & nscale8(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; } - /// Scale every led by the given scale - inline CPixelView & nscale8(PIXEL_TYPE & scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; } - /// Scale every led in this set by every led in the other set - inline CPixelView & nscale8(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel).nscale8((*rhspixel)); } return *this; } - - /// Fade every led down by the given scale - inline CPixelView & fadeToBlackBy(uint8_t fade) { return nscale8(255 - fade); } - - /// Apply the PIXEL_TYPE |= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel to the higher of the two values) - inline CPixelView & operator|=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= rhs; } return *this; } - /// Apply the PIXEL_TYPE |= operator to every pixel in this set with every pixel in the passed in set - inline CPixelView & operator|=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) |= (*rhspixel); } return *this; } - /// Apply the PIXEL_TYPE |= operator to every pixel in this set - inline CPixelView & operator|=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= d; } return *this; } - - /// Apply the PIXEL_TYPE &= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel down to the lower of the two values) - inline CPixelView & operator&=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= rhs; } return *this; } - /// Apply the PIXEL_TYPE &= operator to every pixel in this set with every pixel in the passed in set - inline CPixelView & operator&=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) &= (*rhspixel); } return *this; } - /// APply the PIXEL_TYPE &= operator to every pixel in this set with the passed in value - inline CPixelView & operator&=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= d; } return *this; } - //@} - - /// Returns whether or not any leds in this set are non-zero - inline operator bool() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { if((*pixel)) return true; } return false; } - - // Color util functions - inline CPixelView & fill_solid(const PIXEL_TYPE & color) { *this = color; return *this; } - inline CPixelView & fill_solid(const CHSV & color) { if(dir>0) { *this = color; return *this; } } - - inline CPixelView & fill_rainbow(uint8_t initialhue, uint8_t deltahue=5) { - if(dir >= 0) { - ::fill_rainbow(leds,len,initialhue,deltahue); - } else { - ::fill_rainbow(leds+len+1,-len,initialhue,deltahue); - } - return *this; - } - - inline CPixelView & fill_gradient(const CHSV & startcolor, const CHSV & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) { - if(dir >= 0) { - ::fill_gradient(leds,len,startcolor, endcolor, directionCode); - } else { - ::fill_gradient(leds + len + 1, (-len), endcolor, startcolor, directionCode); - } - return *this; - } - - inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, TGradientDirectionCode directionCode = SHORTEST_HUES) { - if(dir >= 0) { - ::fill_gradient(leds, len, c1, c2, c3, directionCode); - } else { - ::fill_gradient(leds + len + 1, -len, c3, c2, c1, directionCode); - } - return *this; - } - - inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, const CHSV & c4, TGradientDirectionCode directionCode = SHORTEST_HUES) { - if(dir >= 0) { - ::fill_gradient(leds, len, c1, c2, c3, c4, directionCode); - } else { - ::fill_gradient(leds + len + 1, -len, c4, c3, c2, c1, directionCode); - } - return *this; - } - - inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & startcolor, const PIXEL_TYPE & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) { - if(dir >= 0) { - ::fill_gradient_RGB(leds,len,startcolor, endcolor); - } else { - ::fill_gradient_RGB(leds + len + 1, (-len), endcolor, startcolor); - } - return *this; - } - - inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3) { - if(dir >= 0) { - ::fill_gradient_RGB(leds, len, c1, c2, c3); - } else { - ::fill_gradient_RGB(leds + len + 1, -len, c3, c2, c1); - } - return *this; - } - - inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3, const PIXEL_TYPE & c4) { - if(dir >= 0) { - ::fill_gradient_RGB(leds, len, c1, c2, c3, c4); - } else { - ::fill_gradient_RGB(leds + len + 1, -len, c4, c3, c2, c1); - } - return *this; - } - - inline CPixelView & nblend(const PIXEL_TYPE & overlay, fract8 amountOfOverlay) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { ::nblend((*pixel), overlay, amountOfOverlay); } return *this; } - inline CPixelView & nblend(const CPixelView & rhs, fract8 amountOfOverlay) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { ::nblend((*pixel), (*rhspixel), amountOfOverlay); } return *this; } - - // Note: only bringing in a 1d blur, not sure 2d blur makes sense when looking at sub arrays - inline CPixelView & blur1d(fract8 blur_amount) { - if(dir >= 0) { - ::blur1d(leds, len, blur_amount); - } else { - ::blur1d(leds + len + 1, -len, blur_amount); - } - return *this; - } - - inline CPixelView & napplyGamma_video(float gamma) { - if(dir >= 0) { - ::napplyGamma_video(leds, len, gamma); - } else { - ::napplyGamma_video(leds + len + 1, -len, gamma); - } - return *this; - } - - inline CPixelView & napplyGamma_video(float gammaR, float gammaG, float gammaB) { - if(dir >= 0) { - ::napplyGamma_video(leds, len, gammaR, gammaG, gammaB); - } else { - ::napplyGamma_video(leds + len + 1, -len, gammaR, gammaG, gammaB); - } - return *this; - } - - // TODO: Make this a fully specified/proper iterator - template - class pixelset_iterator_base { - T * leds; - const int8_t dir; - public: - __attribute__((always_inline)) inline pixelset_iterator_base(const pixelset_iterator_base & rhs) : leds(rhs.leds), dir(rhs.dir) {} - __attribute__((always_inline)) inline pixelset_iterator_base(T * _leds, const char _dir) : leds(_leds), dir(_dir) {} - - __attribute__((always_inline)) inline pixelset_iterator_base& operator++() { leds += dir; return *this; } - __attribute__((always_inline)) inline pixelset_iterator_base operator++(int) { pixelset_iterator_base tmp(*this); leds += dir; return tmp; } - - __attribute__((always_inline)) inline bool operator==(pixelset_iterator_base & other) const { return leds == other.leds; } // && set==other.set; } - __attribute__((always_inline)) inline bool operator!=(pixelset_iterator_base & other) const { return leds != other.leds; } // || set != other.set; } - - __attribute__((always_inline)) inline PIXEL_TYPE& operator*() const { return *leds; } - }; - - typedef pixelset_iterator_base iterator; - typedef pixelset_iterator_base const_iterator; - - iterator begin() { return iterator(leds, dir); } - iterator end() { return iterator(end_pos, dir); } - - iterator begin() const { return iterator(leds, dir); } - iterator end() const { return iterator(end_pos, dir); } - - const_iterator cbegin() const { return const_iterator(leds, dir); } - const_iterator cend() const { return const_iterator(end_pos, dir); } -}; - -typedef CPixelView CRGBSet; - -__attribute__((always_inline)) -inline CRGB *operator+(const CRGBSet & pixels, int offset) { return (CRGB*)pixels + offset; } - - -template -class CRGBArray : public CPixelView { - CRGB rawleds[SIZE]; -public: - CRGBArray() : CPixelView(rawleds, SIZE) {} - using CPixelView::operator=; -}; - -#endif diff --git a/platforms/arm/d21/clockless_arm_d21.h b/platforms/arm/d21/clockless_arm_d21.h deleted file mode 100644 index 366a6bdeb4..0000000000 --- a/platforms/arm/d21/clockless_arm_d21.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef __INC_CLOCKLESS_ARM_D21 -#define __INC_CLOCKLESS_ARM_D21 - -#include "../common/m0clockless.h" -FASTLED_NAMESPACE_BEGIN -#define FASTLED_HAS_CLOCKLESS 1 - -template -class ClocklessController : public CPixelLEDController { - typedef typename FastPinBB::port_ptr_t data_ptr_t; - typedef typename FastPinBB::port_t data_t; - - data_t mPinMask; - data_ptr_t mPort; - CMinWait mWait; -public: - virtual void init() { - FastPinBB::setOutput(); - mPinMask = FastPinBB::mask(); - mPort = FastPinBB::port(); - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - - virtual void showPixels(PixelController & pixels) { - mWait.wait(); - cli(); - if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); - } - sei(); - mWait.mark(); - } - - // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then - // gcc will use register Y for the this pointer. - static uint32_t showRGBInternal(PixelController pixels) { - struct M0ClocklessData data; - data.d[0] = pixels.d[0]; - data.d[1] = pixels.d[1]; - data.d[2] = pixels.d[2]; - data.s[0] = pixels.mScale[0]; - data.s[1] = pixels.mScale[1]; - data.s[2] = pixels.mScale[2]; - data.e[0] = pixels.e[0]; - data.e[1] = pixels.e[1]; - data.e[2] = pixels.e[2]; - data.adj = pixels.mAdvance; - - typename FastPin::port_ptr_t portBase = FastPin::port(); - return showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin::mask(), pixels.mData, pixels.mLen, &data); - } - - -}; - -FASTLED_NAMESPACE_END - - -#endif // __INC_CLOCKLESS_ARM_D21 diff --git a/platforms/arm/d21/fastpin_arm_d21.h b/platforms/arm/d21/fastpin_arm_d21.h deleted file mode 100644 index e35b4cd8e0..0000000000 --- a/platforms/arm/d21/fastpin_arm_d21.h +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef __INC_FASTPIN_ARM_SAM_H -#define __INC_FASTPIN_ARM_SAM_H - -FASTLED_NAMESPACE_BEGIN - -#if defined(FASTLED_FORCE_SOFTWARE_PINS) -#warning "Software pin support forced, pin access will be slightly slower." -#define NO_HARDWARE_PIN_SUPPORT -#undef HAS_HARDWARE_PIN_SUPPORT - -#else - -/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this -/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found -/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. -/// The registers are data output, set output, clear output, toggle output, input, and direction - -template class _ARMPIN { -public: - typedef volatile uint32_t * port_ptr_t; - typedef uint32_t port_t; - - #if 0 - inline static void setOutput() { - if(_BIT<8) { - _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4)); - } else { - _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4)); - } - } - inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } - #endif - - inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } - inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } - - inline static void hi() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTSET.reg = _MASK; } - inline static void lo() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTCLR.reg = _MASK; } - inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUT.reg = val; } - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTTGL.reg = _MASK; } - - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - - inline static port_t hival() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg | _MASK; } - inline static port_t loval() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg & ~_MASK; } - inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUT.reg; } - inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTSET.reg; } - inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTCLR.reg; } - inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } -}; - -#define _R(T) struct __gen_struct_ ## T -#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } }; - -#define _IO32(L) _RD32(GPIO ## L) - -#define _DEFPIN_ARM(PIN, L, BIT) template<> class FastPin : public _ARMPIN {}; - -// Actual pin definitions -#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) - -#define MAX_PIN 17 -_DEFPIN_ARM( 8,1,23); -_DEFPIN_ARM( 0,1, 9); _DEFPIN_ARM( 1,1, 8); _DEFPIN_ARM( 2,1, 2); _DEFPIN_ARM( 3,1, 3); -_DEFPIN_ARM( 6,0, 5); _DEFPIN_ARM( 9,0, 6); _DEFPIN_ARM(10,0, 7); _DEFPIN_ARM(12,0, 2); -_DEFPIN_ARM(A6,1, 9); _DEFPIN_ARM(A7,1, 8); _DEFPIN_ARM(A5,1, 2); _DEFPIN_ARM(A4,1, 3); -_DEFPIN_ARM(A1,0, 5); _DEFPIN_ARM(A2,0, 6); _DEFPIN_ARM(A3,0, 7); _DEFPIN_ARM(A0,0, 2); - -#define HAS_HARDWARE_PIN_SUPPORT 1 - - -#elif defined(ADAFRUIT_HALLOWING) - -#define MAX_PIN 20 -// 0 & 1 -_DEFPIN_ARM( 0, 0, 9); _DEFPIN_ARM( 1, 0, 10); -// 2, 3, 4 -_DEFPIN_ARM( 2, 0, 14); _DEFPIN_ARM( 3, 0, 11); _DEFPIN_ARM( 4, 0, 8); -// 5, 6, 7 -_DEFPIN_ARM( 5, 0, 15); _DEFPIN_ARM( 6, 0, 18); _DEFPIN_ARM( 7, 0, 0); -// 8, 9, 10 -_DEFPIN_ARM( 8, 0, 12); _DEFPIN_ARM( 9, 0, 19); _DEFPIN_ARM(10, 0, 20); -// 11, 12, 13 -_DEFPIN_ARM(11, 0, 21); _DEFPIN_ARM(12, 0, 22); _DEFPIN_ARM(13, 0, 23); -// 14, 15, 16 (A0 - A2) -_DEFPIN_ARM(14, 0, 2); _DEFPIN_ARM(15, 1, 8); _DEFPIN_ARM(16, 1, 9); -// 17, 18, 19 (A3 - A5) -_DEFPIN_ARM(17, 0, 4); _DEFPIN_ARM(18, 0, 5); _DEFPIN_ARM(19, 0, 6); - -#define SPI_DATA PIN_SPI_MOSI -#define SPI_CLOCK PIN_SPI_SCK - -#define HAS_HARDWARE_PIN_SUPPORT 1 - - -#elif defined(ARDUINO_SAMD_ZERO) - -#define MAX_PIN 42 -_DEFPIN_ARM( 0,0,10); _DEFPIN_ARM( 1,0,11); _DEFPIN_ARM( 2,0, 8); _DEFPIN_ARM( 3,0, 9); -_DEFPIN_ARM( 4,0,14); _DEFPIN_ARM( 5,0,15); _DEFPIN_ARM( 6,0,20); _DEFPIN_ARM( 7,0,21); -_DEFPIN_ARM( 8,0, 6); _DEFPIN_ARM( 9,0, 7); _DEFPIN_ARM(10,0,18); _DEFPIN_ARM(11,0,16); -_DEFPIN_ARM(12,0,19); _DEFPIN_ARM(13,0,17); _DEFPIN_ARM(14,0, 2); _DEFPIN_ARM(15,1, 8); -_DEFPIN_ARM(16,1, 9); _DEFPIN_ARM(17,0, 4); _DEFPIN_ARM(18,0, 5); _DEFPIN_ARM(19,1, 2); -_DEFPIN_ARM(20,0,22); _DEFPIN_ARM(21,0,23); _DEFPIN_ARM(22,0,12); _DEFPIN_ARM(23,1,11); -_DEFPIN_ARM(24,1,10); _DEFPIN_ARM(25,1, 3); _DEFPIN_ARM(26,0,27); _DEFPIN_ARM(27,0,28); -_DEFPIN_ARM(28,0,24); _DEFPIN_ARM(29,0,25); _DEFPIN_ARM(30,1,22); _DEFPIN_ARM(31,1,23); -_DEFPIN_ARM(32,0,22); _DEFPIN_ARM(33,0,23); _DEFPIN_ARM(34,0,19); _DEFPIN_ARM(35,0,16); -_DEFPIN_ARM(36,0,18); _DEFPIN_ARM(37,0,17); _DEFPIN_ARM(38,0,13); _DEFPIN_ARM(39,0,21); -_DEFPIN_ARM(40,0, 6); _DEFPIN_ARM(41,0, 7); _DEFPIN_ARM(42,0, 3); - -#define SPI_DATA 24 -#define SPI_CLOCK 23 - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ARDUINO_SODAQ_AUTONOMO) - -#define MAX_PIN 56 -_DEFPIN_ARM( 0,0, 9); _DEFPIN_ARM( 1,0,10); _DEFPIN_ARM( 2,0,11); _DEFPIN_ARM( 3,1,10); -_DEFPIN_ARM( 4,1,11); _DEFPIN_ARM( 5,1,12); _DEFPIN_ARM( 6,1,13); _DEFPIN_ARM( 7,1,14); -_DEFPIN_ARM( 8,1,15); _DEFPIN_ARM( 9,0,14); _DEFPIN_ARM(10,0,15); _DEFPIN_ARM(11,0,16); -_DEFPIN_ARM(12,0,17); _DEFPIN_ARM(13,0,18); _DEFPIN_ARM(14,0,19); _DEFPIN_ARM(15,1,16); -_DEFPIN_ARM(16,0, 8); _DEFPIN_ARM(17,0,28); _DEFPIN_ARM(18,1,17); _DEFPIN_ARM(19,0, 2); -_DEFPIN_ARM(20,0, 6); _DEFPIN_ARM(21,0, 5); _DEFPIN_ARM(22,0, 4); _DEFPIN_ARM(23,1, 9); -_DEFPIN_ARM(24,1, 8); _DEFPIN_ARM(25,1, 7); _DEFPIN_ARM(26,1, 6); _DEFPIN_ARM(27,1, 5); -_DEFPIN_ARM(28,1, 4); _DEFPIN_ARM(29,0, 7); _DEFPIN_ARM(30,1, 3); _DEFPIN_ARM(31,1, 2); -_DEFPIN_ARM(32,1, 1); _DEFPIN_ARM(33,1, 0); _DEFPIN_ARM(34,0, 3); _DEFPIN_ARM(35,0, 3); -_DEFPIN_ARM(36,1,30); _DEFPIN_ARM(37,1,31); _DEFPIN_ARM(38,1,22); _DEFPIN_ARM(39,1,23); -_DEFPIN_ARM(40,0,12); _DEFPIN_ARM(41,0,13); _DEFPIN_ARM(42,0,22); _DEFPIN_ARM(43,0,23); -_DEFPIN_ARM(44,0,20); _DEFPIN_ARM(45,0,21); _DEFPIN_ARM(46,0,27); _DEFPIN_ARM(47,0,24); -_DEFPIN_ARM(48,0,25); _DEFPIN_ARM(49,1,13); _DEFPIN_ARM(50,1,14); _DEFPIN_ARM(51,0,17); -_DEFPIN_ARM(52,0,18); _DEFPIN_ARM(53,1,12); _DEFPIN_ARM(54,1,13); _DEFPIN_ARM(55,1,14); -_DEFPIN_ARM(56,1,15); - -#define SPI_DATA 44 -#define SPI_CLOCK 45 - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ARDUINO_SAMD_WINO) - -#define MAX_PIN 22 -_DEFPIN_ARM( 0, 0, 23); _DEFPIN_ARM( 1, 0, 22); _DEFPIN_ARM( 2, 0, 16); _DEFPIN_ARM( 3, 0, 17); -_DEFPIN_ARM( 4, 0, 18); _DEFPIN_ARM( 5, 0, 19); _DEFPIN_ARM( 6, 0, 24); _DEFPIN_ARM( 7, 0, 25); -_DEFPIN_ARM( 8, 0, 27); _DEFPIN_ARM( 9, 0, 28); _DEFPIN_ARM( 10, 0, 30); _DEFPIN_ARM( 11, 0, 31); -_DEFPIN_ARM( 12, 0, 15); _DEFPIN_ARM( 13, 0, 14); _DEFPIN_ARM( 14, 0, 2); _DEFPIN_ARM( 15, 0, 3); -_DEFPIN_ARM( 16, 0, 4); _DEFPIN_ARM( 17, 0, 5); _DEFPIN_ARM( 18, 0, 6); _DEFPIN_ARM( 19, 0, 7); -_DEFPIN_ARM( 20, 0, 8); _DEFPIN_ARM( 21, 0, 9); _DEFPIN_ARM( 22, 0, 10); _DEFPIN_ARM( 23, 0, 11); - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ARDUINO_SAMD_MKR1000) - -#define MAX_PIN 22 -_DEFPIN_ARM( 0, 0, 22); _DEFPIN_ARM( 1, 0, 23); _DEFPIN_ARM( 2, 0, 10); _DEFPIN_ARM( 3, 0, 11); -_DEFPIN_ARM( 4, 1, 10); _DEFPIN_ARM( 5, 1, 11); _DEFPIN_ARM( 6, 0, 20); _DEFPIN_ARM( 7, 0, 21); -_DEFPIN_ARM( 8, 0, 16); _DEFPIN_ARM( 9, 0, 17); _DEFPIN_ARM( 10, 0, 19); _DEFPIN_ARM( 11, 0, 8); -_DEFPIN_ARM( 12, 0, 9); _DEFPIN_ARM( 13, 1, 23); _DEFPIN_ARM( 14, 1, 22); _DEFPIN_ARM( 15, 0, 2); -_DEFPIN_ARM( 16, 1, 2); _DEFPIN_ARM( 17, 1, 3); _DEFPIN_ARM( 18, 0, 4); _DEFPIN_ARM( 19, 0, 5); -_DEFPIN_ARM( 20, 0, 6); _DEFPIN_ARM( 21, 0, 7); - -#define SPI_DATA 8 -#define SPI_CLOCK 9 - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ARDUINO_GEMMA_M0) - -#define MAX_PIN 4 -_DEFPIN_ARM( 0, 0, 4); _DEFPIN_ARM( 1, 0, 2); _DEFPIN_ARM( 2, 0, 5); -_DEFPIN_ARM( 3, 0, 0); _DEFPIN_ARM( 4, 0, 1); - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ADAFRUIT_TRINKET_M0) - -#define MAX_PIN 7 -_DEFPIN_ARM( 0, 0, 8); _DEFPIN_ARM( 1, 0, 2); _DEFPIN_ARM( 2, 0, 9); -_DEFPIN_ARM( 3, 0, 7); _DEFPIN_ARM( 4, 0, 6); _DEFPIN_ARM( 7, 0, 0); _DEFPIN_ARM( 8, 0, 1); - -#define SPI_DATA 4 -#define SPI_CLOCK 3 - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#endif - - - -#endif // FASTLED_FORCE_SOFTWARE_PINS - -FASTLED_NAMESPACE_END - - -#endif // __INC_FASTPIN_ARM_SAM_H diff --git a/platforms/arm/d51/clockless_arm_d51.h b/platforms/arm/d51/clockless_arm_d51.h deleted file mode 100644 index a543ec18ab..0000000000 --- a/platforms/arm/d51/clockless_arm_d51.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef __INC_CLOCKLESS_ARM_D51 -#define __INC_CLOCKLESS_ARM_D51 - -// D51 is an M4 chip, however the M0 clockless logic seems to work. -#include "../common/m0clockless.h" -FASTLED_NAMESPACE_BEGIN -#define FASTLED_HAS_CLOCKLESS 1 - -template -class ClocklessController : public CPixelLEDController { - typedef typename FastPinBB::port_ptr_t data_ptr_t; - typedef typename FastPinBB::port_t data_t; - - data_t mPinMask; - data_ptr_t mPort; - CMinWait mWait; -public: - virtual void init() { - FastPinBB::setOutput(); - mPinMask = FastPinBB::mask(); - mPort = FastPinBB::port(); - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - - virtual void showPixels(PixelController & pixels) { - mWait.wait(); - cli(); - if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); - } - sei(); - mWait.mark(); - } - - // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then - // gcc will use register Y for the this pointer. - static uint32_t showRGBInternal(PixelController pixels) { - struct M0ClocklessData data; - data.d[0] = pixels.d[0]; - data.d[1] = pixels.d[1]; - data.d[2] = pixels.d[2]; - data.s[0] = pixels.mScale[0]; - data.s[1] = pixels.mScale[1]; - data.s[2] = pixels.mScale[2]; - data.e[0] = pixels.e[0]; - data.e[1] = pixels.e[1]; - data.e[2] = pixels.e[2]; - data.adj = pixels.mAdvance; - - typename FastPin::port_ptr_t portBase = FastPin::port(); - return showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin::mask(), pixels.mData, pixels.mLen, &data); - } - - -}; - -FASTLED_NAMESPACE_END - - -#endif // __INC_CLOCKLESS_ARM_D51 diff --git a/platforms/arm/d51/fastpin_arm_d51.h b/platforms/arm/d51/fastpin_arm_d51.h deleted file mode 100644 index 5562d846f4..0000000000 --- a/platforms/arm/d51/fastpin_arm_d51.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef __INC_FASTPIN_ARM_D51_H -#define __INC_FASTPIN_ARM_D51_H - -FASTLED_NAMESPACE_BEGIN - -#if defined(FASTLED_FORCE_SOFTWARE_PINS) -#warning "Software pin support forced, pin access will be slightly slower." -#define NO_HARDWARE_PIN_SUPPORT -#undef HAS_HARDWARE_PIN_SUPPORT - -#else - -/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this -/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found -/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. -/// The registers are data output, set output, clear output, toggle output, input, and direction - -template class _ARMPIN { -public: - typedef volatile uint32_t * port_ptr_t; - typedef uint32_t port_t; - - #if 0 - inline static void setOutput() { - if(_BIT<8) { - _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4)); - } else { - _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4)); - } - } - inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } - #endif - - inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } - inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } - - inline static void hi() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTSET.reg = _MASK; } - inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTCLR.reg = _MASK; } - inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT->Group[_GRP].OUT.reg = val; } - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTTGL.reg = _MASK; } - - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - - inline static port_t hival() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg | _MASK; } - inline static port_t loval() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg & ~_MASK; } - inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUT.reg; } - inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTSET.reg; } - inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTCLR.reg; } - inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } -}; - -#define _R(T) struct __gen_struct_ ## T -#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } }; - -#define _IO32(L) _RD32(GPIO ## L) - -#define _DEFPIN_ARM(PIN, L, BIT) template<> class FastPin : public _ARMPIN {}; - -// Actual pin definitions -#if defined(ADAFRUIT_ITSYBITSY_M4_EXPRESS) - -#define MAX_PIN 19 -// D0-D13, including D6+D8 (DotStar CLK + DATA) -_DEFPIN_ARM( 0, 0, 16); _DEFPIN_ARM( 1, 0, 17); _DEFPIN_ARM( 2, 0, 7); _DEFPIN_ARM( 3, 1, 22); -_DEFPIN_ARM( 4, 0, 14); _DEFPIN_ARM( 5, 0, 15); _DEFPIN_ARM( 6, 1, 2); _DEFPIN_ARM( 7, 0, 18); -_DEFPIN_ARM( 8, 1, 3); _DEFPIN_ARM( 9, 0, 19); _DEFPIN_ARM(10, 0, 20); _DEFPIN_ARM(11, 0, 21); -_DEFPIN_ARM(12, 0, 23); _DEFPIN_ARM(13, 0, 22); -// A0-A5 -_DEFPIN_ARM(14, 0, 2); _DEFPIN_ARM(15, 0, 5); _DEFPIN_ARM(16, 1, 8); _DEFPIN_ARM(17, 1, 9); -_DEFPIN_ARM(18, 0, 4); _DEFPIN_ARM(19, 0, 6); /* A6 is present in variant.h but couldn't find it on the schematic */ -// SDA/SCL -_DEFPIN_ARM(21, 0, 12); _DEFPIN_ARM(22, 0, 13); -// MISO/SCK/MOSI -_DEFPIN_ARM(23, 1, 23); _DEFPIN_ARM(24, 0, 1); _DEFPIN_ARM(25, 0, 0); - -#define SPI_DATA 25 -#define SPI_CLOCK 24 - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#endif - - - -#endif // FASTLED_FORCE_SOFTWARE_PINS - -FASTLED_NAMESPACE_END - - -#endif // __INC_FASTPIN_ARM_D51_H diff --git a/platforms/arm/k20/smartmatrix_t3.h b/platforms/arm/k20/smartmatrix_t3.h deleted file mode 100644 index 95af46cf0a..0000000000 --- a/platforms/arm/k20/smartmatrix_t3.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef __INC_SMARTMATRIX_T3_H -#define __INC_SMARTMATRIX_T3_H - -#ifdef SmartMatrix_h -#include - -FASTLED_NAMESPACE_BEGIN - -extern SmartMatrix *pSmartMatrix; - -// note - dmx simple must be included before FastSPI for this code to be enabled -class CSmartMatrixController : public CPixelLEDController { - SmartMatrix matrix; - -public: - // initialize the LED controller - virtual void init() { - // Initialize 32x32 LED Matrix - matrix.begin(); - matrix.setBrightness(255); - matrix.setColorCorrection(ccNone); - - // Clear screen - clearLeds(0); - matrix.swapBuffers(); - pSmartMatrix = &matrix; - } - - virtual void showPixels(PixelController & pixels) { - if(SMART_MATRIX_CAN_TRIPLE_BUFFER) { - rgb24 *md = matrix.getRealBackBuffer(); - } else { - rgb24 *md = matrix.backBuffer(); - } - while(pixels.has(1)) { - md->red = pixels.loadAndScale0(); - md->green = pixels.loadAndScale1(); - md->blue = pixels.loadAndScale2(); - md++; - pixels.advanceData(); - pixels.stepDithering(); - } - matrix.swapBuffers(); - if(SMART_MATRIX_CAN_TRIPLE_BUFFER && pixels.advanceBy() > 0) { - matrix.setBackBuffer(pixels.mData); - } - } - -}; - -FASTLED_NAMESPACE_END - -#endif - -#endif diff --git a/platforms/arm/k20/ws2812serial_controller.h b/platforms/arm/k20/ws2812serial_controller.h deleted file mode 100644 index 0bca7d5eab..0000000000 --- a/platforms/arm/k20/ws2812serial_controller.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef __INC_WS2812SERIAL_CONTROLLER_H -#define __INC_WS2812SERIAL_CONTROLLER_H - -#ifdef USE_WS2812SERIAL - -FASTLED_NAMESPACE_BEGIN - -template -class CWS2812SerialController : public CPixelLEDController { - WS2812Serial *pserial; - uint8_t *drawbuffer,*framebuffer; - - void _init(int nLeds) { - if (pserial == NULL) { - drawbuffer = (uint8_t*)malloc(nLeds * 3); - framebuffer = (uint8_t*)malloc(nLeds * 12); - pserial = new WS2812Serial(nLeds, framebuffer, drawbuffer, DATA_PIN, WS2812_RGB); - pserial->begin(); - } - } -public: - CWS2812SerialController() { pserial = NULL; } - - virtual void init() { /* do nothing yet */ } - - virtual void showPixels(PixelController & pixels) { - _init(pixels.size()); - - uint8_t *p = drawbuffer; - - while(pixels.has(1)) { - *p++ = pixels.loadAndScale0(); - *p++ = pixels.loadAndScale1(); - *p++ = pixels.loadAndScale2(); - pixels.stepDithering(); - pixels.advanceData(); - } - pserial->show(); - } - -}; - -FASTLED_NAMESPACE_END - -#endif // USE_WS2812SERIAL -#endif // __INC_WS2812SERIAL_CONTROLLER_H diff --git a/platforms/arm/nrf51/clockless_arm_nrf51.h b/platforms/arm/nrf51/clockless_arm_nrf51.h deleted file mode 100644 index b748957993..0000000000 --- a/platforms/arm/nrf51/clockless_arm_nrf51.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef __INC_CLOCKLESS_ARM_NRF51 -#define __INC_CLOCKLESS_ARM_NRF51 - -#if defined(NRF51) - -#include -#define FASTLED_HAS_CLOCKLESS 1 - -#if (FASTLED_ALLOW_INTERRUPTS==1) -#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0; -#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; } -#define INNER_SEI sei(); -#else -#define SEI_CHK -#define CLI_CHK -#define INNER_SEI delaycycles<1>(); -#endif - - -#include "../common/m0clockless.h" -template -class ClocklessController : public CPixelLEDController { - typedef typename FastPinBB::port_ptr_t data_ptr_t; - typedef typename FastPinBB::port_t data_t; - - data_t mPinMask; - data_ptr_t mPort; - CMinWait mWait; -public: - virtual void init() { - FastPinBB::setOutput(); - mPinMask = FastPinBB::mask(); - mPort = FastPinBB::port(); - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - - virtual void showPixels(PixelController & pixels) { - mWait.wait(); - cli(); - if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); - } - sei(); - mWait.mark(); - } - - // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then - // gcc will use register Y for the this pointer. - static uint32_t showRGBInternal(PixelController pixels) { - struct M0ClocklessData data; - data.d[0] = pixels.d[0]; - data.d[1] = pixels.d[1]; - data.d[2] = pixels.d[2]; - data.s[0] = pixels.mScale[0]; - data.s[1] = pixels.mScale[1]; - data.s[2] = pixels.mScale[2]; - data.e[0] = pixels.e[0]; - data.e[1] = pixels.e[1]; - data.e[2] = pixels.e[2]; - data.adj = pixels.mAdvance; - - typename FastPin::port_ptr_t portBase = FastPin::port(); - - // timer mode w/prescaler of 0 - LED_TIMER->MODE = TIMER_MODE_MODE_Timer; - LED_TIMER->PRESCALER = 0; - LED_TIMER->EVENTS_COMPARE[0] = 0; - LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; - LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk; - LED_TIMER->TASKS_START = 1; - - int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin::mask(), pixels.mData, pixels.mLen, &data); - - LED_TIMER->TASKS_STOP = 1; - return ret; // 0x00FFFFFF - _VAL; - } -}; - - -#endif // NRF51 -#endif // __INC_CLOCKLESS_ARM_NRF51 diff --git a/platforms/arm/nrf51/fastpin_arm_nrf51.h b/platforms/arm/nrf51/fastpin_arm_nrf51.h deleted file mode 100644 index 4125f9a38e..0000000000 --- a/platforms/arm/nrf51/fastpin_arm_nrf51.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef __FASTPIN_ARM_NRF51_H -#define __FASTPIN_ARM_NRF51_H - -#if defined(NRF51) -/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this -/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found -/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. -/// The registers are data output, set output, clear output, toggle output, input, and direction -#if 0 -template class _ARMPIN { -public: - typedef volatile uint32_t * port_ptr_t; - typedef uint32_t port_t; - - inline static void setOutput() { _DIRSET::r() = _MASK; } - inline static void setInput() { _DIRCLR::r() = _MASK; } - - inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; } - inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; } - inline static void set(register port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; } - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; } - - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - - inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; } - inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; } - inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); } - inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } -}; - -#define ADDR(X) *(volatile uint32_t*)X -#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset)) -#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518) -#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C) -#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508) -#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C) -#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504) - -#define _RD32_NRF(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; }}; - -_RD32_NRF(NR_DIRSET); -_RD32_NRF(NR_DIRCLR); -_RD32_NRF(NR_OUTSET); -_RD32_NRF(NR_OUTCLR); -_RD32_NRF(NR_OUT); - -#define _DEFPIN_ARM(PIN) template<> class FastPin : public _ARMPIN {}; -#else - -typedef struct { /*!< GPIO Structure */ - // __I uint32_t RESERVED0[321]; - __IO uint32_t OUT; /*!< Write GPIO port. */ - __IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */ - __IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */ - __I uint32_t IN; /*!< Read GPIO port. */ - __IO uint32_t DIR; /*!< Direction of GPIO pins. */ - __IO uint32_t DIRSET; /*!< DIR set register. */ - __IO uint32_t DIRCLR; /*!< DIR clear register. */ - __I uint32_t RESERVED1[120]; - __IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */ -} FL_NRF_GPIO_Type; - -#define FL_NRF_GPIO_BASE 0x50000504UL -#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE) - -template class _ARMPIN { -public: - typedef volatile uint32_t * port_ptr_t; - typedef uint32_t port_t; - - inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; } - inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; } - - inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; } - inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; } - inline static void set(register port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; } - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; } - - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - - inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; } - inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; } - inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; } - inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } - - inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; } -}; - - -#define _DEFPIN_ARM(PIN) template<> class FastPin : public _ARMPIN {}; -#endif - -// Actual pin definitions -#define MAX_PIN 31 -_DEFPIN_ARM(0); _DEFPIN_ARM(1); _DEFPIN_ARM(2); _DEFPIN_ARM(3); -_DEFPIN_ARM(4); _DEFPIN_ARM(5); _DEFPIN_ARM(6); _DEFPIN_ARM(7); -_DEFPIN_ARM(8); _DEFPIN_ARM(9); _DEFPIN_ARM(10); _DEFPIN_ARM(11); -_DEFPIN_ARM(12); _DEFPIN_ARM(13); _DEFPIN_ARM(14); _DEFPIN_ARM(15); -_DEFPIN_ARM(16); _DEFPIN_ARM(17); _DEFPIN_ARM(18); _DEFPIN_ARM(19); -_DEFPIN_ARM(20); _DEFPIN_ARM(21); _DEFPIN_ARM(22); _DEFPIN_ARM(23); -_DEFPIN_ARM(24); _DEFPIN_ARM(25); _DEFPIN_ARM(26); _DEFPIN_ARM(27); -_DEFPIN_ARM(28); _DEFPIN_ARM(29); _DEFPIN_ARM(30); _DEFPIN_ARM(31); - -#define HAS_HARDWARE_PIN_SUPPORT - -#endif - -#endif diff --git a/platforms/arm/nrf51/fastspi_arm_nrf51.h b/platforms/arm/nrf51/fastspi_arm_nrf51.h deleted file mode 100644 index 539fd65646..0000000000 --- a/platforms/arm/nrf51/fastspi_arm_nrf51.h +++ /dev/null @@ -1,150 +0,0 @@ -#ifndef __INC_FASTSPI_NRF_H -#define __INC_FASTSPI_NRF_H - -#ifdef NRF51 - -#ifndef FASTLED_FORCE_SOFTWARE_SPI -#define FASTLED_ALL_PINS_HARDWARE_SPI - -// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should -// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the -// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead) -template -class NRF51SPIOutput { - - struct saveData { - uint32_t sck; - uint32_t mosi; - uint32_t miso; - uint32_t freq; - uint32_t enable; - } mSavedData; - - void saveSPIData() { - mSavedData.sck = NRF_SPI0->PSELSCK; - mSavedData.mosi = NRF_SPI0->PSELMOSI; - mSavedData.miso = NRF_SPI0->PSELMISO; - mSavedData.freq = NRF_SPI0->FREQUENCY; - mSavedData.enable = NRF_SPI0->ENABLE; - } - - void restoreSPIData() { - NRF_SPI0->PSELSCK = mSavedData.sck; - NRF_SPI0->PSELMOSI = mSavedData.mosi; - NRF_SPI0->PSELMISO = mSavedData.miso; - NRF_SPI0->FREQUENCY = mSavedData.freq; - mSavedData.enable = NRF_SPI0->ENABLE; - } - -public: - NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); } - NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); } - - // set the object representing the selectable - void setSelect(Selectable *pSelect) { /* TODO */ } - - // initialize the SPI subssytem - void init() { - FastPin<_DATA_PIN>::setOutput(); - FastPin<_CLOCK_PIN>::setOutput(); - NRF_SPI0->PSELSCK = _CLOCK_PIN; - NRF_SPI0->PSELMOSI = _DATA_PIN; - NRF_SPI0->PSELMISO = 0xFFFFFFFF; - NRF_SPI0->FREQUENCY = 0x80000000; - NRF_SPI0->ENABLE = 1; - NRF_SPI0->EVENTS_READY = 0; - } - - // latch the CS select - void select() { saveSPIData(); init(); } - - // release the CS select - void release() { shouldWait(); restoreSPIData(); } - - static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) { - // static bool sWait=false; - // bool oldWait = sWait; - // sWait = wait; - // never going to bother with waiting since we're always running the spi clock at max speed on the rfduino - // TODO: When we set clock rate, implement/fix waiting properly, otherwise the world hangs up - return false; - } - - // wait until all queued up data has been written - static void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; } - static void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; } - - // write a byte out via SPI (returns immediately on writing register) - static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); NRF_SPI0->TXD = b; NRF_SPI0->INTENCLR; shouldWait(true); } - - // write a word out via SPI (returns immediately on writing register) - static void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); } - - // A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes) - static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } } - - // A full cycle of writing a value for len bytes, including select, release, and waiting - void writeBytesValue(uint8_t value, int len) { - select(); - while(len--) { - writeByte(value); - } - waitFully(); - release(); - } - - // A full cycle of writing a raw block of data out, including select, release, and waiting - template void writeBytes(uint8_t *data, int len) { - uint8_t *end = data + len; - select(); - while(data != end) { - writeByte(D::adjust(*data++)); - } - D::postBlock(len); - waitFully(); - release(); - } - - void writeBytes(uint8_t *data, int len) { - writeBytes(data, len); - } - - // write a single bit out, which bit from the passed in byte is determined by template parameter - template inline static void writeBit(uint8_t b) { - waitFully(); - NRF_SPI0->ENABLE = 0; - if(b & 1<::hi(); - } else { - FastPin<_DATA_PIN>::lo(); - } - FastPin<_CLOCK_PIN>::toggle(); - FastPin<_CLOCK_PIN>::toggle(); - NRF_SPI0->ENABLE = 1; - } - - template void writePixels(PixelController pixels) { - select(); - int len = pixels.mLen; - while(pixels.has(1)) { - if(FLAGS & FLAG_START_BIT) { - writeBit<0>(1); - } - writeByte(D::adjust(pixels.loadAndScale0())); - writeByte(D::adjust(pixels.loadAndScale1())); - writeByte(D::adjust(pixels.loadAndScale2())); - - pixels.advanceData(); - pixels.stepDithering(); - } - D::postBlock(len); - waitFully(); - release(); - } - -}; - -#endif -#endif - -#endif diff --git a/platforms/arm/sam/clockless_block_arm_sam.h b/platforms/arm/sam/clockless_block_arm_sam.h deleted file mode 100644 index 355f945d0e..0000000000 --- a/platforms/arm/sam/clockless_block_arm_sam.h +++ /dev/null @@ -1,184 +0,0 @@ - #ifndef __INC_BLOCK_CLOCKLESS_H -#define __INC_BLOCK_CLOCKLESS_H - -FASTLED_NAMESPACE_BEGIN - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point -// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the -// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles. -// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -#if defined(__SAM3X8E__) -#define PORT_MASK (((1< -class InlineBlockClocklessController : public CPixelLEDController { - typedef typename FastPin::port_ptr_t data_ptr_t; - typedef typename FastPin::port_t data_t; - - data_t mPinMask; - data_ptr_t mPort; - CMinWait mWait; -public: - virtual int size() { return CLEDController::size() * LANES; } - virtual void init() { - static_assert(LANES <= 8, "Maximum of 8 lanes for Due parallel controllers!"); - if(FIRST_PIN == PORTA_FIRST_PIN) { - switch(LANES) { - case 8: FastPin<31>::setOutput(); - case 7: FastPin<58>::setOutput(); - case 6: FastPin<100>::setOutput(); - case 5: FastPin<59>::setOutput(); - case 4: FastPin<60>::setOutput(); - case 3: FastPin<61>::setOutput(); - case 2: FastPin<68>::setOutput(); - case 1: FastPin<69>::setOutput(); - } - } else if(FIRST_PIN == PORTD_FIRST_PIN) { - switch(LANES) { - case 8: FastPin<11>::setOutput(); - case 7: FastPin<29>::setOutput(); - case 6: FastPin<15>::setOutput(); - case 5: FastPin<14>::setOutput(); - case 4: FastPin<28>::setOutput(); - case 3: FastPin<27>::setOutput(); - case 2: FastPin<26>::setOutput(); - case 1: FastPin<25>::setOutput(); - } - } else if(FIRST_PIN == PORTB_FIRST_PIN) { - switch(LANES) { - case 8: FastPin<97>::setOutput(); - case 7: FastPin<96>::setOutput(); - case 6: FastPin<95>::setOutput(); - case 5: FastPin<94>::setOutput(); - case 4: FastPin<93>::setOutput(); - case 3: FastPin<92>::setOutput(); - case 2: FastPin<91>::setOutput(); - case 1: FastPin<90>::setOutput(); - } - } - mPinMask = FastPin::mask(); - mPort = FastPin::port(); - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - - virtual void showPixels(PixelController & pixels) { - mWait.wait(); - showRGBInternal(pixels); - sei(); - mWait.mark(); - } - - static uint32_t showRGBInternal(PixelController &allpixels) { - // Serial.println("Entering show"); - - int nLeds = allpixels.mLen; - - // Setup the pixel controller and load/scale the first byte - Lines b0,b1,b2; - - allpixels.preStepFirstByteDithering(); - for(uint8_t i = 0; i < LANES; i++) { - b0.bytes[i] = allpixels.loadAndScale0(i); - } - - // Setup and start the clock - TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1); - pmc_enable_periph_clk(DUE_TIMER_ID); - TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL); - - #if (FASTLED_ALLOW_INTERRUPTS == 1) - cli(); - #endif - uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL)); - while(nLeds--) { - allpixels.stepDithering(); - #if (FASTLED_ALLOW_INTERRUPTS == 1) - cli(); - if(DUE_TIMER_VAL > next_mark) { - if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { - sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL; - } - } - #endif - - // Write first byte, read next byte - writeBits<8+XTRA0,1>(next_mark, b0, b1, allpixels); - - // Write second byte, read 3rd byte - writeBits<8+XTRA0,2>(next_mark, b1, b2, allpixels); - - allpixels.advanceData(); - // Write third byte - writeBits<8+XTRA0,0>(next_mark, b2, b0, allpixels); - - #if (FASTLED_ALLOW_INTERRUPTS == 1) - sei(); - #endif - } - - return DUE_TIMER_VAL; - } - - template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, Lines & b3, PixelController &pixels) { // , register uint32_t & b2) { - Lines b2; - transpose8x1(b.bytes,b2.bytes); - - register uint8_t d = pixels.template getd(pixels); - register uint8_t scale = pixels.template getscale(pixels); - - for(uint32_t i = 0; (i < LANES) && (i<8); i++) { - while(DUE_TIMER_VAL < next_mark); - next_mark = (DUE_TIMER_VAL+TOTAL); - - *FastPin::sport() = PORT_MASK; - - while((next_mark - DUE_TIMER_VAL) > (T2+T3+6)); - *FastPin::cport() = (~b2.bytes[7-i]) & PORT_MASK; - - while((next_mark - (DUE_TIMER_VAL)) > T3); - *FastPin::cport() = PORT_MASK; - - b3.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); - } - - for(uint32_t i = LANES; i < 8; i++) { - while(DUE_TIMER_VAL < next_mark); - next_mark = (DUE_TIMER_VAL+TOTAL); - *FastPin::sport() = PORT_MASK; - - while((next_mark - DUE_TIMER_VAL) > (T2+T3+6)); - *FastPin::cport() = (~b2.bytes[7-i]) & PORT_MASK; - - while((next_mark - DUE_TIMER_VAL) > T3); - *FastPin::cport() = PORT_MASK; - } - } - - -}; - -#endif - -FASTLED_NAMESPACE_END - -#endif diff --git a/platforms/arm/stm32/clockless_arm_stm32.h b/platforms/arm/stm32/clockless_arm_stm32.h deleted file mode 100644 index e4b4de08a5..0000000000 --- a/platforms/arm/stm32/clockless_arm_stm32.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef __INC_CLOCKLESS_ARM_STM32_H -#define __INC_CLOCKLESS_ARM_STM32_H - -FASTLED_NAMESPACE_BEGIN -// Definition for a single channel clockless controller for the stm32 family of chips, like that used in the spark core -// See clockless.h for detailed info on how the template parameters are used. - -#define FASTLED_HAS_CLOCKLESS 1 - -template -class ClocklessController : public CPixelLEDController { - typedef typename FastPin::port_ptr_t data_ptr_t; - typedef typename FastPin::port_t data_t; - - data_t mPinMask; - data_ptr_t mPort; - CMinWait mWait; -public: - virtual void init() { - FastPin::setOutput(); - mPinMask = FastPin::mask(); - mPort = FastPin::port(); - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - -protected: - - virtual void showPixels(PixelController & pixels) { - mWait.wait(); - if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); - } - mWait.mark(); - } - -#define _CYCCNT (*(volatile uint32_t*)(0xE0001004UL)) - - template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) { - for(register uint32_t i = BITS-1; i > 0; i--) { - while(_CYCCNT < (T1+T2+T3-20)); - FastPin::fastset(port, hi); - _CYCCNT = 4; - if(b&0x80) { - while(_CYCCNT < (T1+T2-20)); - FastPin::fastset(port, lo); - } else { - while(_CYCCNT < (T1-10)); - FastPin::fastset(port, lo); - } - b <<= 1; - } - - while(_CYCCNT < (T1+T2+T3-20)); - FastPin::fastset(port, hi); - _CYCCNT = 4; - - if(b&0x80) { - while(_CYCCNT < (T1+T2-20)); - FastPin::fastset(port, lo); - } else { - while(_CYCCNT < (T1-10)); - FastPin::fastset(port, lo); - } - } - - // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then - // gcc will use register Y for the this pointer. - static uint32_t showRGBInternal(PixelController pixels) { - // Get access to the clock - CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; - DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; - DWT->CYCCNT = 0; - - register data_ptr_t port = FastPin::port(); - register data_t hi = *port | FastPin::mask();; - register data_t lo = *port & ~FastPin::mask();; - *port = lo; - - // Setup the pixel controller and load/scale the first byte - pixels.preStepFirstByteDithering(); - register uint8_t b = pixels.loadAndScale0(); - - cli(); - - uint32_t next_mark = (T1+T2+T3); - - DWT->CYCCNT = 0; - while(pixels.has(1)) { - pixels.stepDithering(); - #if (FASTLED_ALLOW_INTERRUPTS == 1) - cli(); - // if interrupts took longer than 45µs, punt on the current frame - if(DWT->CYCCNT > next_mark) { - if((DWT->CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; } - } - - hi = *port | FastPin::mask(); - lo = *port & ~FastPin::mask(); - #endif - - // Write first byte, read next byte - writeBits<8+XTRA0>(next_mark, port, hi, lo, b); - b = pixels.loadAndScale1(); - - // Write second byte, read 3rd byte - writeBits<8+XTRA0>(next_mark, port, hi, lo, b); - b = pixels.loadAndScale2(); - - // Write third byte, read 1st byte of next pixel - writeBits<8+XTRA0>(next_mark, port, hi, lo, b); - b = pixels.advanceAndLoadAndScale0(); - #if (FASTLED_ALLOW_INTERRUPTS == 1) - sei(); - #endif - }; - - sei(); - return DWT->CYCCNT; - } -}; - -FASTLED_NAMESPACE_END - - #endif diff --git a/platforms/arm/stm32/cm3_regs.h b/platforms/arm/stm32/cm3_regs.h deleted file mode 100644 index f81f24cbd3..0000000000 --- a/platforms/arm/stm32/cm3_regs.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef __CM3_REGS -#define __CM3_REGS - -#include - -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - - -typedef struct -{ - __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ - __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ - __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ - __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ -} CoreDebug_Type; - -#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ -#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ - -#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ -#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ - -typedef struct -{ - __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ - __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ - __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ - __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ - __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ - __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ - __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ - __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ - __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ - __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ - __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ - uint32_t RESERVED0[1]; - __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ - __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ - __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ - uint32_t RESERVED1[1]; - __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ - __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ - __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ - uint32_t RESERVED2[1]; - __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ - __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ - __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ -} DWT_Type; - - -#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ -#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ - -#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ -#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ - -#endif // __CM3_REGS diff --git a/platforms/arm/stm32/fastpin_arm_stm32.h b/platforms/arm/stm32/fastpin_arm_stm32.h deleted file mode 100644 index 57309cd9c3..0000000000 --- a/platforms/arm/stm32/fastpin_arm_stm32.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef __FASTPIN_ARM_STM32_H -#define __FASTPIN_ARM_STM32_H - -FASTLED_NAMESPACE_BEGIN - -#if defined(FASTLED_FORCE_SOFTWARE_PINS) -#warning "Software pin support forced, pin access will be sloightly slower." -#define NO_HARDWARE_PIN_SUPPORT -#undef HAS_HARDWARE_PIN_SUPPORT - -#else - -/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this -/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found -/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. -/// The registers are data output, set output, clear output, toggle output, input, and direction - -template class _ARMPIN { -public: - typedef volatile uint32_t * port_ptr_t; - typedef uint32_t port_t; - - #if 0 - inline static void setOutput() { - if(_BIT<8) { - _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4)); - } else { - _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4)); - } - } - inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } - #endif - - inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } - inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } - - inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = _MASK; } - inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BRR = _MASK; } - // inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = (_MASK<<16); } - inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO::r()->ODR = val; } - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { if(_GPIO::r()->ODR & _MASK) { lo(); } else { hi(); } } - - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - - inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO::r()->ODR | _MASK; } - inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO::r()->ODR & ~_MASK; } - inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO::r()->ODR; } - inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR; } - inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BRR; } - inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } -}; - -#if defined(STM32F10X_MD) - #define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile GPIO_TypeDef * r() { return T; } }; - #define _IO32(L) _RD32(GPIO ## L) -#elif defined(__STM32F1__) - #define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline gpio_reg_map* r() { return T->regs; } }; - #define _IO32(L) _RD32(GPIO ## L) -#else - #error "Platform not supported" -#endif - -#define _R(T) struct __gen_struct_ ## T -#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; - -// Actual pin definitions -#if defined(SPARK) // Sparkfun STM32F103 based board - -_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); _IO32(F); _IO32(G); - - -#define MAX_PIN 19 -_DEFPIN_ARM(0, 7, B); -_DEFPIN_ARM(1, 6, B); -_DEFPIN_ARM(2, 5, B); -_DEFPIN_ARM(3, 4, B); -_DEFPIN_ARM(4, 3, B); -_DEFPIN_ARM(5, 15, A); -_DEFPIN_ARM(6, 14, A); -_DEFPIN_ARM(7, 13, A); -_DEFPIN_ARM(8, 8, A); -_DEFPIN_ARM(9, 9, A); -_DEFPIN_ARM(10, 0, A); -_DEFPIN_ARM(11, 1, A); -_DEFPIN_ARM(12, 4, A); -_DEFPIN_ARM(13, 5, A); -_DEFPIN_ARM(14, 6, A); -_DEFPIN_ARM(15, 7, A); -_DEFPIN_ARM(16, 0, B); -_DEFPIN_ARM(17, 1, B); -_DEFPIN_ARM(18, 3, A); -_DEFPIN_ARM(19, 2, A); - - -#define SPI_DATA 15 -#define SPI_CLOCK 13 - -#define HAS_HARDWARE_PIN_SUPPORT - -#endif // SPARK - -#if defined(__STM32F1__) // Generic STM32F103 aka "Blue Pill" - -_IO32(A); _IO32(B); _IO32(C); - -#define MAX_PIN 46 - -_DEFPIN_ARM(10, 0, A); // PA0 - PA7 -_DEFPIN_ARM(11, 1, A); -_DEFPIN_ARM(12, 2, A); -_DEFPIN_ARM(13, 3, A); -_DEFPIN_ARM(14, 4, A); -_DEFPIN_ARM(15, 5, A); -_DEFPIN_ARM(16, 6, A); -_DEFPIN_ARM(17, 7, A); -_DEFPIN_ARM(29, 8, A); // PA8 - PA15 -_DEFPIN_ARM(30, 9, A); -_DEFPIN_ARM(31, 10, A); -_DEFPIN_ARM(32, 11, A); -_DEFPIN_ARM(33, 12, A); -_DEFPIN_ARM(34, 13, A); -_DEFPIN_ARM(37, 14, A); -_DEFPIN_ARM(38, 15, A); - -_DEFPIN_ARM(18, 0, B); // PB0 - PB11 -_DEFPIN_ARM(19, 1, B); -_DEFPIN_ARM(20, 2, B); -_DEFPIN_ARM(39, 3, B); -_DEFPIN_ARM(40, 4, B); -_DEFPIN_ARM(41, 5, B); -_DEFPIN_ARM(42, 6, B); -_DEFPIN_ARM(43, 7, B); -_DEFPIN_ARM(45, 8, B); -_DEFPIN_ARM(46, 9, B); -_DEFPIN_ARM(21, 10, B); -_DEFPIN_ARM(22, 11, B); - -_DEFPIN_ARM(2, 13, C); // PC13 - PC15 -_DEFPIN_ARM(3, 14, C); -_DEFPIN_ARM(4, 15, C); - -#define SPI_DATA BOARD_SPI1_MOSI_PIN -#define SPI_CLOCK BOARD_SPI1_SCK_PIN - -#define HAS_HARDWARE_PIN_SUPPORT - -#endif // __STM32F1__ - -#endif // FASTLED_FORCE_SOFTWARE_PINS - -FASTLED_NAMESPACE_END - -#endif // __INC_FASTPIN_ARM_STM32 diff --git a/platforms/avr/fastpin_avr.h b/platforms/avr/fastpin_avr.h deleted file mode 100644 index 4e25cf8d4f..0000000000 --- a/platforms/avr/fastpin_avr.h +++ /dev/null @@ -1,345 +0,0 @@ -#ifndef __INC_FASTPIN_AVR_H -#define __INC_FASTPIN_AVR_H - -FASTLED_NAMESPACE_BEGIN - -#if defined(FASTLED_FORCE_SOFTWARE_PINS) -#warning "Software pin support forced, pin access will be slightly slower." -#define NO_HARDWARE_PIN_SUPPORT -#undef HAS_HARDWARE_PIN_SUPPORT - -#else - -#define AVR_PIN_CYCLES(_PIN) ((((int)FastPin<_PIN>::port())-0x20 < 64) ? 1 : 2) - -/// Class definition for a Pin where we know the port registers at compile time for said pin. This allows us to make -/// a lot of optimizations, as the inlined hi/lo methods will devolve to a single io register write/bitset. -template class _AVRPIN { -public: - typedef volatile uint8_t * port_ptr_t; - typedef uint8_t port_t; - - inline static void setOutput() { _DDR::r() |= _MASK; } - inline static void setInput() { _DDR::r() &= ~_MASK; } - - inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; } - inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; } - inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; } - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { _PIN::r() = _MASK; } - - inline static void hi(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t /*port*/, register uint8_t val) __attribute__ ((always_inline)) { set(val); } - - inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; } - inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; } - inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); } - inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } -}; - - - -/// AVR definitions for pins. Getting around the fact that I can't pass GPIO register addresses in as template arguments by instead creating -/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register. A similar -/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!) -typedef volatile uint8_t & reg8_t; -#define _R(T) struct __gen_struct_ ## T -#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }}; -#define _IO(L) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L); -#define _DEFPIN_AVR(_PIN, MASK, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, MASK, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {}; - -#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) -_IO(B); - -#if defined(__AVR_ATtiny25__) -#pragma message "ATtiny25 has very limited storage. This library could use up to more than 100% of its flash size" -#endif - -#define MAX_PIN 5 - -_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); _DEFPIN_AVR(3, 0x08, B); -_DEFPIN_AVR(4, 0x10, B); _DEFPIN_AVR(5, 0x20, B); - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny441__) -#define MAX_PIN 11 -_IO(A); _IO(B); - -_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); -_DEFPIN_AVR(3, 0x80, A); _DEFPIN_AVR(4, 0x40, A); _DEFPIN_AVR(5, 0x20, A); -_DEFPIN_AVR(6, 0x10, A); _DEFPIN_AVR(7, 0x08, A); _DEFPIN_AVR(8, 0x04, A); -_DEFPIN_AVR(9, 0x02, A); _DEFPIN_AVR(10, 0x01, A); _DEFPIN_AVR(11, 0x08, B); - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ARDUINO_AVR_DIGISPARK) // digispark pin layout -#define MAX_PIN 5 -#define HAS_HARDWARE_PIN_SUPPORT 1 -_IO(A); _IO(B); - -_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); -_DEFPIN_AVR(3, 0x80, A); _DEFPIN_AVR(4, 0x40, A); _DEFPIN_AVR(5, 0x20, A); - -#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) -_IO(A); _IO(B); - -#define MAX_PIN 10 - -_DEFPIN_AVR(0, 0x01, A); _DEFPIN_AVR(1, 0x02, A); _DEFPIN_AVR(2, 0x04, A); _DEFPIN_AVR(3, 0x08, A); -_DEFPIN_AVR(4, 0x10, A); _DEFPIN_AVR(5, 0x20, A); _DEFPIN_AVR(6, 0x40, A); _DEFPIN_AVR(7, 0x80, A); -_DEFPIN_AVR(8, 0x04, B); _DEFPIN_AVR(9, 0x02, B); _DEFPIN_AVR(10, 0x01, B); - -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(ARDUINO_AVR_DIGISPARKPRO) - -_IO(A); _IO(B); -#define MAX_PIN 12 - -_DEFPIN_AVR(0, 0x01, B); _DEFPIN_AVR(1, 0x02, B); _DEFPIN_AVR(2, 0x04, B); _DEFPIN_AVR(3, 0x20, B); -_DEFPIN_AVR(4, 0x08, B); _DEFPIN_AVR(5, 0x80, A); _DEFPIN_AVR(6, 0x01, A); _DEFPIN_AVR(7, 0x02, A); -_DEFPIN_AVR(8, 0x04, A); _DEFPIN_AVR(9, 0x08, A); _DEFPIN_AVR(10, 0x10, A); _DEFPIN_AVR(11, 0x20, A); -_DEFPIN_AVR(12, 0x40, A); - -#elif defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__) -_IO(A); _IO(B); - -#define MAX_PIN 15 - -_DEFPIN_AVR(0, 0x01, A); _DEFPIN_AVR(1, 0x02, A); _DEFPIN_AVR(2, 0x04, A); _DEFPIN_AVR(3, 0x08, A); -_DEFPIN_AVR(4, 0x10, A); _DEFPIN_AVR(5, 0x20, A); _DEFPIN_AVR(6, 0x40, A); _DEFPIN_AVR(7, 0x80, A); -_DEFPIN_AVR(8, 0x01, B); _DEFPIN_AVR(9, 0x02, B); _DEFPIN_AVR(10, 0x04, B); _DEFPIN_AVR(11, 0x08, B); -_DEFPIN_AVR(12, 0x10, B); _DEFPIN_AVR(13, 0x20, B); _DEFPIN_AVR(14, 0x40, B); _DEFPIN_AVR(15, 0x80, B); - -#define SPI_DATA 4 -#define SPI_CLOCK 5 -#define AVR_HARDWARE_SPI 1 - -#define HAS_HARDWARE_PIN_SUPPORT 1 -#elif defined(ARDUINO_HOODLOADER2) && (defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__)) || defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) - -_IO(D); _IO(B); _IO(C); - -#define MAX_PIN 20 - -_DEFPIN_AVR( 0, 0x01, B); _DEFPIN_AVR( 1, 0x02, B); _DEFPIN_AVR( 2, 0x04, B); _DEFPIN_AVR( 3, 0x08, B); -_DEFPIN_AVR( 4, 0x10, B); _DEFPIN_AVR( 5, 0x20, B); _DEFPIN_AVR( 6, 0x40, B); _DEFPIN_AVR( 7, 0x80, B); - -_DEFPIN_AVR( 8, 0x80, C); _DEFPIN_AVR( 9, 0x40, C); _DEFPIN_AVR( 10, 0x20,C); _DEFPIN_AVR( 11, 0x10, C); -_DEFPIN_AVR( 12, 0x04, C); _DEFPIN_AVR( 13, 0x01, D); _DEFPIN_AVR( 14, 0x02, D); _DEFPIN_AVR(15, 0x04, D); -_DEFPIN_AVR( 16, 0x08, D); _DEFPIN_AVR( 17, 0x10, D); _DEFPIN_AVR( 18, 0x20, D); _DEFPIN_AVR( 19, 0x40, D); -_DEFPIN_AVR( 20, 0x80, D); - -#define HAS_HARDWARE_PIN_SUPPORT 1 -// #define SPI_DATA 2 -// #define SPI_CLOCK 1 -// #define AVR_HARDWARE_SPI 1 - -#elif defined(IS_BEAN) - -// Accelerated port definitions for arduino avrs -_IO(D); _IO(B); _IO(C); - -#define MAX_PIN 19 -_DEFPIN_AVR( 0, 0x40, D); _DEFPIN_AVR( 1, 0x02, B); _DEFPIN_AVR( 2, 0x04, B); _DEFPIN_AVR( 3, 0x08, B); -_DEFPIN_AVR( 4, 0x10, B); _DEFPIN_AVR( 5, 0x20, B); _DEFPIN_AVR( 6, 0x01, D); _DEFPIN_AVR( 7, 0x80, D); -_DEFPIN_AVR( 8, 0x01, B); _DEFPIN_AVR( 9, 0x02, D); _DEFPIN_AVR(10, 0x04, D); _DEFPIN_AVR(11, 0x08, D); -_DEFPIN_AVR(12, 0x10, D); _DEFPIN_AVR(13, 0x20, D); _DEFPIN_AVR(14, 0x01, C); _DEFPIN_AVR(15, 0x02, C); -_DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _DEFPIN_AVR(19, 0x20, C); - -#define SPI_DATA 3 -#define SPI_CLOCK 5 -#define SPI_SELECT 2 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#ifndef __AVR_ATmega8__ -#define SPI_UART0_DATA 9 -#define SPI_UART0_CLOCK 12 -#endif - -#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega8__) -// Accelerated port definitions for arduino avrs -_IO(D); _IO(B); _IO(C); - -#define MAX_PIN 19 -_DEFPIN_AVR( 0, 0x01, D); _DEFPIN_AVR( 1, 0x02, D); _DEFPIN_AVR( 2, 0x04, D); _DEFPIN_AVR( 3, 0x08, D); -_DEFPIN_AVR( 4, 0x10, D); _DEFPIN_AVR( 5, 0x20, D); _DEFPIN_AVR( 6, 0x40, D); _DEFPIN_AVR( 7, 0x80, D); -_DEFPIN_AVR( 8, 0x01, B); _DEFPIN_AVR( 9, 0x02, B); _DEFPIN_AVR(10, 0x04, B); _DEFPIN_AVR(11, 0x08, B); -_DEFPIN_AVR(12, 0x10, B); _DEFPIN_AVR(13, 0x20, B); _DEFPIN_AVR(14, 0x01, C); _DEFPIN_AVR(15, 0x02, C); -_DEFPIN_AVR(16, 0x04, C); _DEFPIN_AVR(17, 0x08, C); _DEFPIN_AVR(18, 0x10, C); _DEFPIN_AVR(19, 0x20, C); - -#define SPI_DATA 11 -#define SPI_CLOCK 13 -#define SPI_SELECT 10 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#ifndef __AVR_ATmega8__ -#define SPI_UART0_DATA 1 -#define SPI_UART0_CLOCK 4 -#endif - -#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) - -_IO(A); _IO(B); _IO(C); _IO(D); - -#define MAX_PIN 31 -_DEFPIN_AVR(0, 1<<0, B); _DEFPIN_AVR(1, 1<<1, B); _DEFPIN_AVR(2, 1<<2, B); _DEFPIN_AVR(3, 1<<3, B); -_DEFPIN_AVR(4, 1<<4, B); _DEFPIN_AVR(5, 1<<5, B); _DEFPIN_AVR(6, 1<<6, B); _DEFPIN_AVR(7, 1<<7, B); -_DEFPIN_AVR(8, 1<<0, D); _DEFPIN_AVR(9, 1<<1, D); _DEFPIN_AVR(10, 1<<2, D); _DEFPIN_AVR(11, 1<<3, D); -_DEFPIN_AVR(12, 1<<4, D); _DEFPIN_AVR(13, 1<<5, D); _DEFPIN_AVR(14, 1<<6, D); _DEFPIN_AVR(15, 1<<7, D); -_DEFPIN_AVR(16, 1<<0, C); _DEFPIN_AVR(17, 1<<1, C); _DEFPIN_AVR(18, 1<<2, C); _DEFPIN_AVR(19, 1<<3, C); -_DEFPIN_AVR(20, 1<<4, C); _DEFPIN_AVR(21, 1<<5, C); _DEFPIN_AVR(22, 1<<6, C); _DEFPIN_AVR(23, 1<<7, C); -_DEFPIN_AVR(24, 1<<0, A); _DEFPIN_AVR(25, 1<<1, A); _DEFPIN_AVR(26, 1<<2, A); _DEFPIN_AVR(27, 1<<3, A); -_DEFPIN_AVR(28, 1<<4, A); _DEFPIN_AVR(29, 1<<5, A); _DEFPIN_AVR(30, 1<<6, A); _DEFPIN_AVR(31, 1<<7, A); - -#define SPI_DATA 5 -#define SPI_CLOCK 7 -#define SPI_SELECT 4 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega256RFR2__) - -// AKA the Pinoccio - -_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); - -_DEFPIN_AVR( 0, 1<<0, E); _DEFPIN_AVR( 1, 1<<1, E); _DEFPIN_AVR( 2, 1<<7, B); _DEFPIN_AVR( 3, 1<<3, E); -_DEFPIN_AVR( 4, 1<<4, E); _DEFPIN_AVR( 5, 1<<5, E); _DEFPIN_AVR( 6, 1<<2, E); _DEFPIN_AVR( 7, 1<<6, E); -_DEFPIN_AVR( 8, 1<<5, D); _DEFPIN_AVR( 9, 1<<0, B); _DEFPIN_AVR(10, 1<<2, B); _DEFPIN_AVR(11, 1<<3, B); -_DEFPIN_AVR(12, 1<<1, B); _DEFPIN_AVR(13, 1<<2, D); _DEFPIN_AVR(14, 1<<3, D); _DEFPIN_AVR(15, 1<<0, D); -_DEFPIN_AVR(16, 1<<1, D); _DEFPIN_AVR(17, 1<<4, D); _DEFPIN_AVR(18, 1<<7, E); _DEFPIN_AVR(19, 1<<6, D); -_DEFPIN_AVR(20, 1<<7, D); _DEFPIN_AVR(21, 1<<4, B); _DEFPIN_AVR(22, 1<<5, B); _DEFPIN_AVR(23, 1<<6, B); -_DEFPIN_AVR(24, 1<<0, F); _DEFPIN_AVR(25, 1<<1, F); _DEFPIN_AVR(26, 1<<2, F); _DEFPIN_AVR(27, 1<<3, F); -_DEFPIN_AVR(28, 1<<4, F); _DEFPIN_AVR(29, 1<<5, F); _DEFPIN_AVR(30, 1<<6, F); _DEFPIN_AVR(31, 1<<7, F); - -#define SPI_DATA 10 -#define SPI_CLOCK 12 -#define SPI_SELECT 9 - -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -// megas - -_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); _IO(G); _IO(H); _IO(J); _IO(K); _IO(L); - -#define MAX_PIN 69 -_DEFPIN_AVR(0, 1, E); _DEFPIN_AVR(1, 2, E); _DEFPIN_AVR(2, 16, E); _DEFPIN_AVR(3, 32, E); -_DEFPIN_AVR(4, 32, G); _DEFPIN_AVR(5, 8, E); _DEFPIN_AVR(6, 8, H); _DEFPIN_AVR(7, 16, H); -_DEFPIN_AVR(8, 32, H); _DEFPIN_AVR(9, 64, H); _DEFPIN_AVR(10, 16, B); _DEFPIN_AVR(11, 32, B); -_DEFPIN_AVR(12, 64, B); _DEFPIN_AVR(13, 128, B); _DEFPIN_AVR(14, 2, J); _DEFPIN_AVR(15, 1, J); -_DEFPIN_AVR(16, 2, H); _DEFPIN_AVR(17, 1, H); _DEFPIN_AVR(18, 8, D); _DEFPIN_AVR(19, 4, D); -_DEFPIN_AVR(20, 2, D); _DEFPIN_AVR(21, 1, D); _DEFPIN_AVR(22, 1, A); _DEFPIN_AVR(23, 2, A); -_DEFPIN_AVR(24, 4, A); _DEFPIN_AVR(25, 8, A); _DEFPIN_AVR(26, 16, A); _DEFPIN_AVR(27, 32, A); -_DEFPIN_AVR(28, 64, A); _DEFPIN_AVR(29, 128, A); _DEFPIN_AVR(30, 128, C); _DEFPIN_AVR(31, 64, C); -_DEFPIN_AVR(32, 32, C); _DEFPIN_AVR(33, 16, C); _DEFPIN_AVR(34, 8, C); _DEFPIN_AVR(35, 4, C); -_DEFPIN_AVR(36, 2, C); _DEFPIN_AVR(37, 1, C); _DEFPIN_AVR(38, 128, D); _DEFPIN_AVR(39, 4, G); -_DEFPIN_AVR(40, 2, G); _DEFPIN_AVR(41, 1, G); _DEFPIN_AVR(42, 128, L); _DEFPIN_AVR(43, 64, L); -_DEFPIN_AVR(44, 32, L); _DEFPIN_AVR(45, 16, L); _DEFPIN_AVR(46, 8, L); _DEFPIN_AVR(47, 4, L); -_DEFPIN_AVR(48, 2, L); _DEFPIN_AVR(49, 1, L); _DEFPIN_AVR(50, 8, B); _DEFPIN_AVR(51, 4, B); -_DEFPIN_AVR(52, 2, B); _DEFPIN_AVR(53, 1, B); _DEFPIN_AVR(54, 1, F); _DEFPIN_AVR(55, 2, F); -_DEFPIN_AVR(56, 4, F); _DEFPIN_AVR(57, 8, F); _DEFPIN_AVR(58, 16, F); _DEFPIN_AVR(59, 32, F); -_DEFPIN_AVR(60, 64, F); _DEFPIN_AVR(61, 128, F); _DEFPIN_AVR(62, 1, K); _DEFPIN_AVR(63, 2, K); -_DEFPIN_AVR(64, 4, K); _DEFPIN_AVR(65, 8, K); _DEFPIN_AVR(66, 16, K); _DEFPIN_AVR(67, 32, K); -_DEFPIN_AVR(68, 64, K); _DEFPIN_AVR(69, 128, K); - -#define SPI_DATA 51 -#define SPI_CLOCK 52 -#define SPI_SELECT 53 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -// Leonardo, teensy, blinkm -#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY) - -// teensy defs -_IO(B); _IO(C); _IO(D); _IO(E); _IO(F); - -#define MAX_PIN 23 -_DEFPIN_AVR(0, 1, B); _DEFPIN_AVR(1, 2, B); _DEFPIN_AVR(2, 4, B); _DEFPIN_AVR(3, 8, B); -_DEFPIN_AVR(4, 128, B); _DEFPIN_AVR(5, 1, D); _DEFPIN_AVR(6, 2, D); _DEFPIN_AVR(7, 4, D); -_DEFPIN_AVR(8, 8, D); _DEFPIN_AVR(9, 64, C); _DEFPIN_AVR(10, 128, C); _DEFPIN_AVR(11, 64, D); -_DEFPIN_AVR(12, 128, D); _DEFPIN_AVR(13, 16, B); _DEFPIN_AVR(14, 32, B); _DEFPIN_AVR(15, 64, B); -_DEFPIN_AVR(16, 128, F); _DEFPIN_AVR(17, 64, F); _DEFPIN_AVR(18, 32, F); _DEFPIN_AVR(19, 16, F); -_DEFPIN_AVR(20, 2, F); _DEFPIN_AVR(21, 1, F); _DEFPIN_AVR(22, 16, D); _DEFPIN_AVR(23, 32, D); - -#define SPI_DATA 2 -#define SPI_CLOCK 1 -#define SPI_SELECT 0 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -// PD3/PD5 -#define SPI_UART1_DATA 8 -#define SPI_UART1_CLOCK 23 - -#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) -// teensy++ 2 defs - -_IO(A); _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); - -#define MAX_PIN 45 -_DEFPIN_AVR(0, 1, D); _DEFPIN_AVR(1, 2, D); _DEFPIN_AVR(2, 4, D); _DEFPIN_AVR(3, 8, D); -_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 32, D); _DEFPIN_AVR(6, 64, D); _DEFPIN_AVR(7, 128, D); -_DEFPIN_AVR(8, 1, E); _DEFPIN_AVR(9, 2, E); _DEFPIN_AVR(10, 1, C); _DEFPIN_AVR(11, 2, C); -_DEFPIN_AVR(12, 4, C); _DEFPIN_AVR(13, 8, C); _DEFPIN_AVR(14, 16, C); _DEFPIN_AVR(15, 32, C); -_DEFPIN_AVR(16, 64, C); _DEFPIN_AVR(17, 128, C); _DEFPIN_AVR(18, 64, E); _DEFPIN_AVR(19, 128, E); -_DEFPIN_AVR(20, 1, B); _DEFPIN_AVR(21, 2, B); _DEFPIN_AVR(22, 4, B); _DEFPIN_AVR(23, 8, B); -_DEFPIN_AVR(24, 16, B); _DEFPIN_AVR(25, 32, B); _DEFPIN_AVR(26, 64, B); _DEFPIN_AVR(27, 128, B); -_DEFPIN_AVR(28, 1, A); _DEFPIN_AVR(29, 2, A); _DEFPIN_AVR(30, 4, A); _DEFPIN_AVR(31, 8, A); -_DEFPIN_AVR(32, 16, A); _DEFPIN_AVR(33, 32, A); _DEFPIN_AVR(34, 64, A); _DEFPIN_AVR(35, 128, A); -_DEFPIN_AVR(36, 16, E); _DEFPIN_AVR(37, 32, E); _DEFPIN_AVR(38, 1, F); _DEFPIN_AVR(39, 2, F); -_DEFPIN_AVR(40, 4, F); _DEFPIN_AVR(41, 8, F); _DEFPIN_AVR(42, 16, F); _DEFPIN_AVR(43, 32, F); -_DEFPIN_AVR(44, 64, F); _DEFPIN_AVR(45, 128, F); - -#define SPI_DATA 22 -#define SPI_CLOCK 21 -#define SPI_SELECT 20 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -// PD3/PD5 -#define SPI_UART1_DATA 3 -#define SPI_UART1_CLOCK 5 - - -#elif defined(__AVR_ATmega32U4__) - -// leonard defs -_IO(B); _IO(C); _IO(D); _IO(E); _IO(F); - -#define MAX_PIN 30 -_DEFPIN_AVR(0, 4, D); _DEFPIN_AVR(1, 8, D); _DEFPIN_AVR(2, 2, D); _DEFPIN_AVR(3, 1, D); -_DEFPIN_AVR(4, 16, D); _DEFPIN_AVR(5, 64, C); _DEFPIN_AVR(6, 128, D); _DEFPIN_AVR(7, 64, E); -_DEFPIN_AVR(8, 16, B); _DEFPIN_AVR(9, 32, B); _DEFPIN_AVR(10, 64, B); _DEFPIN_AVR(11, 128, B); -_DEFPIN_AVR(12, 64, D); _DEFPIN_AVR(13, 128, C); _DEFPIN_AVR(14, 8, B); _DEFPIN_AVR(15, 2, B); -_DEFPIN_AVR(16, 4, B); _DEFPIN_AVR(17, 1, B); _DEFPIN_AVR(18, 128, F); _DEFPIN_AVR(19, 64, F); -_DEFPIN_AVR(20, 32, F); _DEFPIN_AVR(21, 16, F); _DEFPIN_AVR(22, 2, F); _DEFPIN_AVR(23, 1, F); -_DEFPIN_AVR(24, 16, D); _DEFPIN_AVR(25, 128, D); _DEFPIN_AVR(26, 16, B); _DEFPIN_AVR(27, 32, B); -_DEFPIN_AVR(28, 64, B); _DEFPIN_AVR(29, 64, D); _DEFPIN_AVR(30, 32, D); - -#define SPI_DATA 16 -#define SPI_CLOCK 15 -#define AVR_HARDWARE_SPI 1 -#define HAS_HARDWARE_PIN_SUPPORT 1 - -// PD3/PD5 -#define SPI_UART1_DATA 1 -#define SPI_UART1_CLOCK 30 - - -#endif - -#endif // FASTLED_FORCE_SOFTWARE_PINS - -FASTLED_NAMESPACE_END - -#endif // __INC_FASTPIN_AVR_H diff --git a/platforms/esp/32/clockless_esp32.h b/platforms/esp/32/clockless_esp32.h deleted file mode 100644 index 58b3c3bcba..0000000000 --- a/platforms/esp/32/clockless_esp32.h +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Integration into FastLED ClocklessController - * Copyright (c) 2018 Samuel Z. Guyer - * Copyright (c) 2017 Thomas Basler - * Copyright (c) 2017 Martin F. Falatic - * - * ESP32 support is provided using the RMT peripheral device -- a unit - * on the chip designed specifically for generating (and receiving) - * precisely-timed digital signals. Nominally for use in infrared - * remote controls, we use it to generate the signals for clockless - * LED strips. The main advantage of using the RMT device is that, - * once programmed, it generates the signal asynchronously, allowing - * the CPU to continue executing other code. It is also not vulnerable - * to interrupts or other timing problems that could disrupt the signal. - * - * The implementation strategy is borrowed from previous work and from - * the RMT support built into the ESP32 IDF. The RMT device has 8 - * channels, which can be programmed independently to send sequences - * of high/low bits. Memory for each channel is limited, however, so - * in order to send a long sequence of bits, we need to continuously - * refill the buffer until all the data is sent. To do this, we fill - * half the buffer and then set an interrupt to go off when that half - * is sent. Then we refill that half while the second half is being - * sent. This strategy effectively overlaps computation (by the CPU) - * and communication (by the RMT). - * - * Since the RMT device only has 8 channels, we need a strategy to - * allow more than 8 LED controllers. Our driver assigns controllers - * to channels on the fly, queuing up controllers as necessary until a - * channel is free. The main showPixels routine just fires off the - * first 8 controllers; the interrupt handler starts new controllers - * asynchronously as previous ones finish. So, for example, it can - * send the data for 8 controllers simultaneously, but 16 controllers - * would take approximately twice as much time. - * - * There is a #define that allows a program to control the total - * number of channels that the driver is allowed to use. It defaults - * to 8 -- use all the channels. Setting it to 1, for example, results - * in fully serial output: - * - * #define FASTLED_RMT_MAX_CHANNELS 1 - * - * OTHER RMT APPLICATIONS - * - * The default FastLED driver takes over control of the RMT interrupt - * handler, making it hard to use the RMT device for other - * (non-FastLED) purposes. You can change it's behavior to use the ESP - * core driver instead, allowing other RMT applications to - * co-exist. To switch to this mode, add the following directive - * before you include FastLED.h: - * - * #define FASTLED_RMT_BUILTIN_DRIVER 1 - * - * There may be a performance penalty for using this mode. We need to - * compute the RMT signal for the entire LED strip ahead of time, - * rather than overlapping it with communication. We also need a large - * buffer to hold the signal specification. Each bit of pixel data is - * represented by a 32-bit pulse specification, so it is a 32X blow-up - * in memory use. - * - * - * Based on public domain code created 19 Nov 2016 by Chris Osborn - * http://insentricity.com * - * - */ -/* - * 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. - */ - -#pragma once - -FASTLED_NAMESPACE_BEGIN - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp32-hal.h" -#include "esp_intr.h" -#include "driver/gpio.h" -#include "driver/rmt.h" -#include "driver/periph_ctrl.h" -#include "freertos/semphr.h" -#include "soc/rmt_struct.h" - -#include "esp_log.h" - -#ifdef __cplusplus -} -#endif - -__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { - uint32_t cyc; - __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc)); - return cyc; -} - -#define FASTLED_HAS_CLOCKLESS 1 - -// -- Configuration constants -#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */ -#define MAX_PULSES 32 /* A channel has a 64 "pulse" buffer - we use half per pass */ - -// -- Convert ESP32 cycles back into nanoseconds -#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ) - -// -- Convert nanoseconds into RMT cycles -#define F_CPU_RMT ( 80000000L) -#define NS_PER_SEC (1000000000L) -#define CYCLES_PER_SEC (F_CPU_RMT/DIVIDER) -#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) -#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) - -// -- Convert ESP32 cycles to RMT cycles -#define TO_RMT_CYCLES(_CLKS) NS_TO_CYCLES(ESPCLKS_TO_NS(_CLKS)) - -// -- Number of cycles to signal the strip to latch -#define RMT_RESET_DURATION NS_TO_CYCLES(50000) - -// -- Core or custom driver -#ifndef FASTLED_RMT_BUILTIN_DRIVER -#define FASTLED_RMT_BUILTIN_DRIVER false -#endif - -// -- Max number of controllers we can support -#ifndef FASTLED_RMT_MAX_CONTROLLERS -#define FASTLED_RMT_MAX_CONTROLLERS 32 -#endif - -// -- Number of RMT channels to use (up to 8) -// Redefine this value to 1 to force serial output -#ifndef FASTLED_RMT_MAX_CHANNELS -#define FASTLED_RMT_MAX_CHANNELS 8 -#endif - -// -- Array of all controllers -static CLEDController * gControllers[FASTLED_RMT_MAX_CONTROLLERS]; - -// -- Current set of active controllers, indexed by the RMT -// channel assigned to them. -static CLEDController * gOnChannel[FASTLED_RMT_MAX_CHANNELS]; - -static int gNumControllers = 0; -static int gNumStarted = 0; -static int gNumDone = 0; -static int gNext = 0; - -static intr_handle_t gRMT_intr_handle = NULL; - -// -- Global semaphore for the whole show process -// Semaphore is not given until all data has been sent -static xSemaphoreHandle gTX_sem = NULL; - -static bool gInitialized = false; - -template -class ClocklessController : public CPixelLEDController -{ - // -- RMT has 8 channels, numbered 0 to 7 - rmt_channel_t mRMT_channel; - - // -- Store the GPIO pin - gpio_num_t mPin; - - // -- This instantiation forces a check on the pin choice - FastPin mFastPin; - - // -- Timing values for zero and one bits, derived from T1, T2, and T3 - rmt_item32_t mZero; - rmt_item32_t mOne; - - // -- State information for keeping track of where we are in the pixel data - uint8_t * mPixelData = NULL; - int mSize = 0; - int mCurByte; - uint16_t mCurPulse; - - // -- Buffer to hold all of the pulses. For the version that uses - // the RMT driver built into the ESP core. - rmt_item32_t * mBuffer; - uint16_t mBufferSize; - -public: - - void init() - { - // -- Precompute rmt items corresponding to a zero bit and a one bit - // according to the timing values given in the template instantiation - // T1H - mOne.level0 = 1; - mOne.duration0 = TO_RMT_CYCLES(T1+T2); - // T1L - mOne.level1 = 0; - mOne.duration1 = TO_RMT_CYCLES(T3); - - // T0H - mZero.level0 = 1; - mZero.duration0 = TO_RMT_CYCLES(T1); - // T0L - mZero.level1 = 0; - mZero.duration1 = TO_RMT_CYCLES(T2 + T3); - - gControllers[gNumControllers] = this; - gNumControllers++; - - mPin = gpio_num_t(DATA_PIN); - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - -protected: - - void initRMT() - { - // -- Only need to do this once - if (gInitialized) return; - - for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) { - gOnChannel[i] = NULL; - - // -- RMT configuration for transmission - rmt_config_t rmt_tx; - rmt_tx.channel = rmt_channel_t(i); - rmt_tx.rmt_mode = RMT_MODE_TX; - rmt_tx.gpio_num = mPin; // The particular pin will be assigned later - rmt_tx.mem_block_num = 1; - rmt_tx.clk_div = DIVIDER; - rmt_tx.tx_config.loop_en = false; - rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; - rmt_tx.tx_config.carrier_en = false; - rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - rmt_tx.tx_config.idle_output_en = true; - - // -- Apply the configuration - rmt_config(&rmt_tx); - - if (FASTLED_RMT_BUILTIN_DRIVER) { - rmt_driver_install(rmt_channel_t(i), 0, 0); - } else { - // -- Set up the RMT to send 1/2 of the pulse buffer and then - // generate an interrupt. When we get this interrupt we - // fill the other half in preparation (kind of like double-buffering) - rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, MAX_PULSES); - } - } - - // -- Create a semaphore to block execution until all the controllers are done - if (gTX_sem == NULL) { - gTX_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(gTX_sem); - } - - if ( ! FASTLED_RMT_BUILTIN_DRIVER) { - // -- Allocate the interrupt if we have not done so yet. This - // interrupt handler must work for all different kinds of - // strips, so it delegates to the refill function for each - // specific instantiation of ClocklessController. - if (gRMT_intr_handle == NULL) - esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, interruptHandler, 0, &gRMT_intr_handle); - } - - gInitialized = true; - } - - // -- Show pixels - // This is the main entry point for the controller. - virtual void showPixels(PixelController & pixels) - { - if (gNumStarted == 0) { - // -- First controller: make sure everything is set up - initRMT(); - xSemaphoreTake(gTX_sem, portMAX_DELAY); - } - - // -- Initialize the local state, save a pointer to the pixel - // data. We need to make a copy because pixels is a local - // variable in the calling function, and this data structure - // needs to outlive this call to showPixels. - - //if (mPixels != NULL) delete mPixels; - //mPixels = new PixelController(pixels); - if (FASTLED_RMT_BUILTIN_DRIVER) - convertAllPixelData(pixels); - else - copyPixelData(pixels); - - // -- Keep track of the number of strips we've seen - gNumStarted++; - - // -- The last call to showPixels is the one responsible for doing - // all of the actual worl - if (gNumStarted == gNumControllers) { - gNext = 0; - - // -- First, fill all the available channels - int channel = 0; - while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) { - startNext(channel); - channel++; - } - - // -- Wait here while the rest of the data is sent. The interrupt handler - // will keep refilling the RMT buffers until it is all sent; then it - // gives the semaphore back. - xSemaphoreTake(gTX_sem, portMAX_DELAY); - xSemaphoreGive(gTX_sem); - - // -- Reset the counters - gNumStarted = 0; - gNumDone = 0; - gNext = 0; - } - } - - // -- Copy pixel data - // Make a safe copy of the pixel data, so that the FastLED show - // function can continue to the next controller while the RMT - // device starts sending this data asynchronously. - virtual void copyPixelData(PixelController & pixels) - { - // -- Make sure we have a buffer of the right size - // (3 bytes per pixel) - int size_needed = pixels.size() * 3; - if (size_needed > mSize) { - if (mPixelData != NULL) free(mPixelData); - mSize = size_needed; - mPixelData = (uint8_t *) malloc( mSize); - } - - // -- Cycle through the R,G, and B values in the right order, - // storing the resulting raw pixel data in the buffer. - int cur = 0; - while (pixels.has(1)) { - mPixelData[cur++] = pixels.loadAndScale0(); - mPixelData[cur++] = pixels.loadAndScale1(); - mPixelData[cur++] = pixels.loadAndScale2(); - pixels.advanceData(); - pixels.stepDithering(); - } - } - - // -- Convert all pixels to RMT pulses - // This function is only used when the user chooses to use the - // built-in RMT driver, which needs all of the RMT pulses - // up-front. - virtual void convertAllPixelData(PixelController & pixels) - { - // -- Compute the pulse values for the whole strip at once. - // Requires a large buffer - mBufferSize = pixels.size() * 3 * 8; - - if (mBuffer == NULL) { - mBuffer = (rmt_item32_t *) calloc( mBufferSize, sizeof(rmt_item32_t)); - } - - // -- Cycle through the R,G, and B values in the right order, - // storing the pulses in the big buffer - mCurPulse = 0; - int cur = 0; - uint32_t byteval; - while (pixels.has(1)) { - byteval = pixels.loadAndScale0(); - convertByte(byteval); - byteval = pixels.loadAndScale1(); - convertByte(byteval); - byteval = pixels.loadAndScale2(); - convertByte(byteval); - pixels.advanceData(); - pixels.stepDithering(); - } - - mBuffer[mCurPulse-1].duration1 = RMT_RESET_DURATION; - assert(mCurPulse == mBufferSize); - } - - void convertByte(uint32_t byteval) - { - // -- Write one byte's worth of RMT pulses to the big buffer - byteval <<= 24; - for (register uint32_t j = 0; j < 8; j++) { - mBuffer[mCurPulse] = (byteval & 0x80000000L) ? mOne : mZero; - byteval <<= 1; - mCurPulse++; - } - } - - // -- Start up the next controller - // This method is static so that it can dispatch to the - // appropriate startOnChannel method of the given controller. - static void IRAM_ATTR startNext(int channel) - { - if (gNext < gNumControllers) { - ClocklessController * pController = static_cast(gControllers[gNext]); - pController->startOnChannel(channel); - gNext++; - } - } - - // -- Start this controller on the given channel - // This function just initiates the RMT write; it does not wait - // for it to finish. - void IRAM_ATTR startOnChannel(int channel) - { - // -- Assign this channel and configure the RMT - mRMT_channel = rmt_channel_t(channel); - - // -- Store a reference to this controller, so we can get it - // inside the interrupt handler - gOnChannel[channel] = this; - - // -- Assign the pin to this channel - rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); - - if (FASTLED_RMT_BUILTIN_DRIVER) { - // -- Use the built-in RMT driver to send all the data in one shot - rmt_register_tx_end_callback(doneOnChannel, 0); - rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false); - } else { - // -- Use our custom driver to send the data incrementally - - // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); - - // -- Initialize the counters that keep track of where we are in - // the pixel data. - mCurPulse = 0; - mCurByte = 0; - - // -- Fill both halves of the buffer - fillHalfRMTBuffer(); - fillHalfRMTBuffer(); - - // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); - - // -- Start the RMT TX operation - rmt_tx_start(mRMT_channel, true); - } - } - - // -- A controller is done - // This function is called when a controller finishes writing - // its data. It is called either by the custom interrupt - // handler (below), or as a callback from the built-in - // interrupt handler. It is static because we don't know which - // controller is done until we look it up. - static void IRAM_ATTR doneOnChannel(rmt_channel_t channel, void * arg) - { - if (channel >= FASTLED_RMT_MAX_CHANNELS) return; - - ClocklessController * controller = static_cast(gOnChannel[channel]); - portBASE_TYPE HPTaskAwoken = 0; - - // -- Turn off output on the pin - gpio_matrix_out(controller->mPin, 0x100, 0, 0); - - gOnChannel[channel] = NULL; - gNumDone++; - - if (gNumDone == gNumControllers) { - // -- If this is the last controller, signal that we are all done - xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } else { - // -- Otherwise, if there are still controllers waiting, then - // start the next one on this channel - if (gNext < gNumControllers) - startNext(channel); - } - } - - // -- Custom interrupt handler - // This interrupt handler handles two cases: a controller is - // done writing its data, or a controller needs to fill the - // next half of the RMT buffer with data. - static IRAM_ATTR void interruptHandler(void *arg) - { - // -- The basic structure of this code is borrowed from the - // interrupt handler in esp-idf/components/driver/rmt.c - uint32_t intr_st = RMT.int_st.val; - uint8_t channel; - - for (channel = 0; channel < FASTLED_RMT_MAX_CHANNELS; channel++) { - int tx_done_bit = channel * 3; - int tx_next_bit = channel + 24; - - if (gOnChannel[channel] != NULL) { - - // -- More to send on this channel - if (intr_st & BIT(tx_next_bit)) { - RMT.int_clr.val |= BIT(tx_next_bit); - - // -- Refill the half of the buffer that we just finished, - // allowing the other half to proceed. - ClocklessController * controller = static_cast(gOnChannel[channel]); - controller->fillHalfRMTBuffer(); - } else { - // -- Transmission is complete on this channel - if (intr_st & BIT(tx_done_bit)) { - RMT.int_clr.val |= BIT(tx_done_bit); - doneOnChannel(rmt_channel_t(channel), 0); - } - } - } - } - } - - // -- Fill the RMT buffer - // This function fills the next 32 slots in the RMT write - // buffer with pixel data. It also handles the case where the - // pixel data is exhausted, so we need to fill the RMT buffer - // with zeros to signal that it's done. - void IRAM_ATTR fillHalfRMTBuffer() - { - uint32_t one_val = mOne.val; - uint32_t zero_val = mZero.val; - - // -- Convert (up to) 32 bits of the raw pixel data into - // into RMT pulses that encode the zeros and ones. - int pulses = 0; - uint32_t byteval; - while (pulses < 32 && mCurByte < mSize) { - // -- Get one byte - byteval = mPixelData[mCurByte++]; - byteval <<= 24; - // Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the - // rmt_item32_t value corresponding to the buffered bit value - for (register uint32_t j = 0; j < 8; j++) { - uint32_t val = (byteval & 0x80000000L) ? one_val : zero_val; - RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val; - byteval <<= 1; - mCurPulse++; - } - pulses += 8; - } - - // -- When we reach the end of the pixel data, fill the rest of the - // RMT buffer with 0's, which signals to the device that we're done. - if (mCurByte == mSize) { - while (pulses < 32) { - RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = 0; - mCurPulse++; - pulses++; - } - } - - // -- When we have filled the back half the buffer, reset the position to the first half - if (mCurPulse >= MAX_PULSES*2) - mCurPulse = 0; - } -}; - -FASTLED_NAMESPACE_END diff --git a/platforms/esp/32/clockless_esp32.h.orig b/platforms/esp/32/clockless_esp32.h.orig deleted file mode 100644 index e0cd00dae9..0000000000 --- a/platforms/esp/32/clockless_esp32.h.orig +++ /dev/null @@ -1,786 +0,0 @@ -/* - * Integration into FastLED ClocklessController 2017 Thomas Basler - * - * Modifications Copyright (c) 2017 Martin F. Falatic - * - * Modifications Copyright (c) 2018 Samuel Z. Guyer - * - * ESP32 support is provided using the RMT peripheral device -- a unit - * on the chip designed specifically for generating (and receiving) - * precisely-timed digital signals. Nominally for use in infrared - * remote controls, we use it to generate the signals for clockless - * LED strips. The main advantage of using the RMT device is that, - * once programmed, it generates the signal asynchronously, allowing - * the CPU to continue executing other code. It is also not vulnerable - * to interrupts or other timing problems that could disrupt the signal. - * - * The implementation strategy is borrowed from previous work and from - * the RMT support built into the ESP32 IDF. The RMT device has 8 - * channels, which can be programmed independently to send sequences - * of high/low bits. Memory for each channel is limited, however, so - * in order to send a long sequence of bits, we need to continuously - * refill the buffer until all the data is sent. To do this, we fill - * half the buffer and then set an interrupt to go off when that half - * is sent. Then we refill that half while the second half is being - * sent. This strategy effectively overlaps computation (by the CPU) - * and communication (by the RMT). - * - * Since the RMT device only has 8 channels, we need a strategy to - * allow more than 8 LED controllers. Our driver assigns controllers - * to channels on the fly, queuing up controllers as necessary until a - * channel is free. The main showPixels routine just fires off the - * first 8 controllers; the interrupt handler starts new controllers - * asynchronously as previous ones finish. So, for example, it can - * send the data for 8 controllers simultaneously, but 16 controllers - * would take approximately twice as much time. - * - * There is a #define that allows a program to control the total - * number of channels that the driver is allowed to use. It defaults - * to 8 -- use all the channels. Setting it to 1, for example, results - * in fully serial output: - * - * #define FASTLED_RMT_MAX_CHANNELS 1 - * - * OTHER RMT APPLICATIONS - * - * The default FastLED driver takes over control of the RMT interrupt - * handler, making it hard to use the RMT device for other - * (non-FastLED) purposes. You can change it's behavior to use the ESP - * core driver instead, allowing other RMT applications to - * co-exist. To switch to this mode, add the following directive - * before you include FastLED.h: - * - * #define FASTLED_RMT_BUILTIN_DRIVER - * - * There may be a performance penalty for using this mode. We need to - * compute the RMT signal for the entire LED strip ahead of time, - * rather than overlapping it with communication. We also need a large - * buffer to hold the signal specification. Each bit of pixel data is - * represented by a 32-bit pulse specification, so it is a 32X blow-up - * in memory use. - * - * - * Based on public domain code created 19 Nov 2016 by Chris Osborn - * http://insentricity.com * - * - */ -/* - * 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. - */ - -#pragma once - -FASTLED_NAMESPACE_BEGIN - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp32-hal.h" -#include "esp_intr.h" -#include "driver/gpio.h" -#include "driver/rmt.h" -#include "driver/periph_ctrl.h" -#include "freertos/semphr.h" -#include "soc/rmt_struct.h" - -#include "esp_log.h" - -#ifdef __cplusplus -} -#endif - -__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { - uint32_t cyc; - __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc)); - return cyc; -} - -#define FASTLED_HAS_CLOCKLESS 1 - -// -- Configuration constants -#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */ -#define MAX_PULSES 32 /* A channel has a 64 "pulse" buffer - we use half per pass */ - -// -- Convert ESP32 cycles back into nanoseconds -#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ) - -// -- Convert nanoseconds into RMT cycles -#define F_CPU_RMT ( 80000000L) -#define NS_PER_SEC (1000000000L) -#define CYCLES_PER_SEC (F_CPU_RMT/DIVIDER) -#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) -#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) - -// -- Convert ESP32 cycles to RMT cycles -#define TO_RMT_CYCLES(_CLKS) NS_TO_CYCLES(ESPCLKS_TO_NS(_CLKS)) - -// -- Number of cycles to signal the strip to latch -#define RMT_RESET_DURATION NS_TO_CYCLES(50000) - -// -- Core or custom driver -#ifndef FASTLED_RMT_BUILTIN_DRIVER -#define FASTLED_RMT_BUILTIN_DRIVER false -#endif - -// -- Max number of controllers we can support -#ifndef FASTLED_RMT_MAX_CONTROLLERS -#define FASTLED_RMT_MAX_CONTROLLERS 32 -#endif - -// -- Number of RMT channels to use (up to 8) -// Redefine this value to 1 to force serial output -#ifndef FASTLED_RMT_MAX_CHANNELS -#define FASTLED_RMT_MAX_CHANNELS 8 -#endif - -// -- Array of all controllers -static CLEDController * gControllers[FASTLED_RMT_MAX_CONTROLLERS]; - -// -- Current set of active controllers, indexed by the RMT -// channel assigned to them. -static CLEDController * gOnChannel[FASTLED_RMT_MAX_CHANNELS]; - -static int gNumControllers = 0; -static int gNumStarted = 0; -static int gNumDone = 0; -static int gNext = 0; - -static intr_handle_t gRMT_intr_handle = NULL; - -// -- Global semaphore for the whole show process -// Semaphore is not given until all data has been sent -static xSemaphoreHandle gTX_sem = NULL; - -static bool gInitialized = false; - -template -class ClocklessController : public CPixelLEDController -{ - // -- RMT has 8 channels, numbered 0 to 7 - rmt_channel_t mRMT_channel; - - // -- Store the GPIO pin - gpio_num_t mPin; -<<<<<<< HEAD - - // -- This instantiation forces a check on the pin choice - FastPin mFastPin; - - // -- Timing values for zero and one bits, derived from T1, T2, and T3 - rmt_item32_t mZero; - rmt_item32_t mOne; - -======= - - // -- Timing values for zero and one bits, derived from T1, T2, and T3 - rmt_item32_t mZero; - rmt_item32_t mOne; - ->>>>>>> upstream/master - // -- State information for keeping track of where we are in the pixel data - PixelController * mPixels = NULL; - void * mPixelSpace = NULL; - uint8_t mRGB_channel; - uint16_t mCurPulse; - - // -- Buffer to hold all of the pulses. For the version that uses - // the RMT driver built into the ESP core. - rmt_item32_t * mBuffer; - uint16_t mBufferSize; - -public: - - virtual void init() - { - // -- Precompute rmt items corresponding to a zero bit and a one bit - // according to the timing values given in the template instantiation - // T1H - mOne.level0 = 1; - mOne.duration0 = TO_RMT_CYCLES(T1+T2); - // T1L - mOne.level1 = 0; - mOne.duration1 = TO_RMT_CYCLES(T3); - - // T0H - mZero.level0 = 1; - mZero.duration0 = TO_RMT_CYCLES(T1); - // T0L - mZero.level1 = 0; - mZero.duration1 = TO_RMT_CYCLES(T2 + T3); - -<<<<<<< HEAD - gControllers[gNumControllers] = this; - gNumControllers++; - - mPin = gpio_num_t(DATA_PIN); -======= - gControllers[gNumControllers] = this; - gNumControllers++; - - mPin = gpio_num_t(DATA_PIN); ->>>>>>> upstream/master - } - - virtual uint16_t getMaxRefreshRate() const { return 400; } - -protected: - - void initRMT() - { -<<<<<<< HEAD - // -- Only need to do this once - if (gInitialized) return; - - for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) { - gOnChannel[i] = NULL; - - // -- RMT configuration for transmission - rmt_config_t rmt_tx; - rmt_tx.channel = rmt_channel_t(i); - rmt_tx.rmt_mode = RMT_MODE_TX; - rmt_tx.gpio_num = mPin; // The particular pin will be assigned later - rmt_tx.mem_block_num = 1; - rmt_tx.clk_div = DIVIDER; - rmt_tx.tx_config.loop_en = false; - rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; - rmt_tx.tx_config.carrier_en = false; - rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - rmt_tx.tx_config.idle_output_en = true; - - // -- Apply the configuration - rmt_config(&rmt_tx); - - if (FASTLED_RMT_BUILTIN_DRIVER) { - rmt_driver_install(rmt_channel_t(i), 0, 0); - } else { - // -- Set up the RMT to send 1/2 of the pulse buffer and then - // generate an interrupt. When we get this interrupt we - // fill the other half in preparation (kind of like double-buffering) - rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, MAX_PULSES); - } - } - - // -- Create a semaphore to block execution until all the controllers are done - if (gTX_sem == NULL) { - gTX_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(gTX_sem); - } - - if ( ! FASTLED_RMT_BUILTIN_DRIVER) { - // -- Allocate the interrupt if we have not done so yet. This - // interrupt handler must work for all different kinds of - // strips, so it delegates to the refill function for each - // specific instantiation of ClocklessController. - if (gRMT_intr_handle == NULL) - esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, interruptHandler, 0, &gRMT_intr_handle); - } - - gInitialized = true; - } - - virtual void showPixels(PixelController & pixels) - { - if (gNumStarted == 0) { - // -- First controller: make sure everything is set up - initRMT(); - xSemaphoreTake(gTX_sem, portMAX_DELAY); - } - - // -- Initialize the local state, save a pointer to the pixel - // data. We need to make a copy because pixels is a local - // variable in the calling function, and this data structure - // needs to outlive this call to showPixels. - - if (mPixels != NULL) delete mPixels; - mPixels = new PixelController(pixels); - - // -- Keep track of the number of strips we've seen - gNumStarted++; - - // -- The last call to showPixels is the one responsible for doing - // all of the actual worl - if (gNumStarted == gNumControllers) { - gNext = 0; - - // -- First, fill all the available channels - int channel = 0; - while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) { - startNext(channel); - channel++; - } - - // -- Wait here while the rest of the data is sent. The interrupt handler - // will keep refilling the RMT buffers until it is all sent; then it - // gives the semaphore back. - xSemaphoreTake(gTX_sem, portMAX_DELAY); - xSemaphoreGive(gTX_sem); - - // -- Reset the counters - gNumStarted = 0; - gNumDone = 0; - gNext = 0; - } - } - - // -- Start up the next controller - // This method is static so that it can dispatch to the appropriate - // startOnChannel method of the given controller. - static void startNext(int channel) - { - if (gNext < gNumControllers) { - ClocklessController * pController = static_cast(gControllers[gNext]); - pController->startOnChannel(channel); - gNext++; - } - } - - virtual void startOnChannel(int channel) - { - // -- Assign this channel and configure the RMT - mRMT_channel = rmt_channel_t(channel); - - // -- Store a reference to this controller, so we can get it - // inside the interrupt handler - gOnChannel[channel] = this; - - // -- Assign the pin to this channel - rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); - - if (FASTLED_RMT_BUILTIN_DRIVER) { - // -- Use the built-in RMT driver to send all the data in one shot - rmt_register_tx_end_callback(doneOnChannel, 0); - writeAllRMTItems(); - } else { - // -- Use our custom driver to send the data incrementally - - // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); - - // -- Initialize the counters that keep track of where we are in - // the pixel data. - mCurPulse = 0; - mRGB_channel = 0; - - // -- Fill both halves of the buffer - fillHalfRMTBuffer(); - fillHalfRMTBuffer(); - - // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); - - // -- Start the RMT TX operation - rmt_tx_start(mRMT_channel, true); - } - } - - static void doneOnChannel(rmt_channel_t channel, void * arg) - { - ClocklessController * controller = static_cast(gOnChannel[channel]); - portBASE_TYPE HPTaskAwoken = 0; - - // -- Turn off output on the pin - gpio_matrix_out(controller->mPin, 0x100, 0, 0); - - gOnChannel[channel] = NULL; - gNumDone++; - - if (gNumDone == gNumControllers) { - // -- If this is the last controller, signal that we are all done - xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } else { - // -- Otherwise, if there are still controllers waiting, then - // start the next one on this channel - if (gNext < gNumControllers) - startNext(channel); - } -======= - // -- Only need to do this once - if (gInitialized) return; - - for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) { - gOnChannel[i] = NULL; - - // -- RMT configuration for transmission - rmt_config_t rmt_tx; - rmt_tx.channel = rmt_channel_t(i); - rmt_tx.rmt_mode = RMT_MODE_TX; - rmt_tx.gpio_num = mPin; // The particular pin will be assigned later - rmt_tx.mem_block_num = 1; - rmt_tx.clk_div = DIVIDER; - rmt_tx.tx_config.loop_en = false; - rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; - rmt_tx.tx_config.carrier_en = false; - rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - rmt_tx.tx_config.idle_output_en = true; - - // -- Apply the configuration - rmt_config(&rmt_tx); - - if (FASTLED_RMT_BUILTIN_DRIVER) { - rmt_driver_install(rmt_channel_t(i), 0, 0); - } else { - // -- Set up the RMT to send 1/2 of the pulse buffer and then - // generate an interrupt. When we get this interrupt we - // fill the other half in preparation (kind of like double-buffering) - rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, MAX_PULSES); - } - } - - // -- Create a semaphore to block execution until all the controllers are done - if (gTX_sem == NULL) { - gTX_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(gTX_sem); - } - - if ( ! FASTLED_RMT_BUILTIN_DRIVER) { - // -- Allocate the interrupt if we have not done so yet. This - // interrupt handler must work for all different kinds of - // strips, so it delegates to the refill function for each - // specific instantiation of ClocklessController. - if (gRMT_intr_handle == NULL) - esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, interruptHandler, 0, &gRMT_intr_handle); - } - - gInitialized = true; - } - - virtual void showPixels(PixelController & pixels) - { - if (gNumStarted == 0) { - // -- First controller: make sure everything is set up - initRMT(); - xSemaphoreTake(gTX_sem, portMAX_DELAY); - } - - // -- Initialize the local state, save a pointer to the pixel - // data. We need to make a copy because pixels is a local - // variable in the calling function, and this data structure - // needs to outlive this call to showPixels. - - if (mPixels != NULL) delete mPixels; - mPixels = new PixelController(pixels); - - // -- Keep track of the number of strips we've seen - gNumStarted++; - - // -- The last call to showPixels is the one responsible for doing - // all of the actual worl - if (gNumStarted == gNumControllers) { - gNext = 0; - - // -- First, fill all the available channels - int channel = 0; - while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) { - startNext(channel); - channel++; - } - - // -- Wait here while the rest of the data is sent. The interrupt handler - // will keep refilling the RMT buffers until it is all sent; then it - // gives the semaphore back. - xSemaphoreTake(gTX_sem, portMAX_DELAY); - xSemaphoreGive(gTX_sem); - - // -- Reset the counters - gNumStarted = 0; - gNumDone = 0; - gNext = 0; - } - } - - // -- Start up the next controller - // This method is static so that it can dispatch to the appropriate - // startOnChannel method of the given controller. - static void startNext(int channel) - { - if (gNext < gNumControllers) { - ClocklessController * pController = static_cast(gControllers[gNext]); - pController->startOnChannel(channel); - gNext++; - } - } - - virtual void startOnChannel(int channel) - { - // -- Assign this channel and configure the RMT - mRMT_channel = rmt_channel_t(channel); - - // -- Store a reference to this controller, so we can get it - // inside the interrupt handler - gOnChannel[channel] = this; - - // -- Assign the pin to this channel - rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); - - if (FASTLED_RMT_BUILTIN_DRIVER) { - // -- Use the built-in RMT driver to send all the data in one shot - rmt_register_tx_end_callback(doneOnChannel, 0); - writeAllRMTItems(); - } else { - // -- Use our custom driver to send the data incrementally - - // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); - - // -- Initialize the counters that keep track of where we are in - // the pixel data. - mCurPulse = 0; - mRGB_channel = 0; - - // -- Fill both halves of the buffer - fillHalfRMTBuffer(); - fillHalfRMTBuffer(); - - // -- Turn on the interrupts - rmt_set_tx_intr_en(mRMT_channel, true); - - // -- Start the RMT TX operation - rmt_tx_start(mRMT_channel, true); - } - } - - static void doneOnChannel(rmt_channel_t channel, void * arg) - { - ClocklessController * controller = static_cast(gOnChannel[channel]); - portBASE_TYPE HPTaskAwoken = 0; - - // -- Turn off output on the pin - gpio_matrix_out(controller->mPin, 0x100, 0, 0); - - gOnChannel[channel] = NULL; - gNumDone++; - - if (gNumDone == gNumControllers) { - // -- If this is the last controller, signal that we are all done - xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken); - if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); - } else { - // -- Otherwise, if there are still controllers waiting, then - // start the next one on this channel - if (gNext < gNumControllers) - startNext(channel); - } ->>>>>>> upstream/master - } - - static IRAM_ATTR void interruptHandler(void *arg) - { - // -- The basic structure of this code is borrowed from the - // interrupt handler in esp-idf/components/driver/rmt.c - uint32_t intr_st = RMT.int_st.val; - uint8_t channel; - - for (channel = 0; channel < FASTLED_RMT_MAX_CHANNELS; channel++) { - int tx_done_bit = channel * 3; - int tx_next_bit = channel + 24; - - if (gOnChannel[channel] != NULL) { - -<<<<<<< HEAD - ClocklessController * controller = static_cast(gOnChannel[channel]); - - // -- More to send on this channel - if (intr_st & BIT(tx_next_bit)) { - RMT.int_clr.val |= BIT(tx_next_bit); - - // -- Refill the half of the buffer that we just finished, - // allowing the other half to proceed. - controller->fillHalfRMTBuffer(); - } - - // -- Transmission is complete on this channel - if (intr_st & BIT(tx_done_bit)) { - RMT.int_clr.val |= BIT(tx_done_bit); - doneOnChannel(rmt_channel_t(channel), 0); -======= - ClocklessController * controller = static_cast(gOnChannel[channel]); - - // -- More to send on this channel - if (intr_st & BIT(tx_next_bit)) { - RMT.int_clr.val |= BIT(tx_next_bit); - - // -- Refill the half of the buffer that we just finished, - // allowing the other half to proceed. - controller->fillHalfRMTBuffer(); - } - - // -- Transmission is complete on this channel - if (intr_st & BIT(tx_done_bit)) { - RMT.int_clr.val |= BIT(tx_done_bit); - doneOnChannel(rmt_channel_t(channel), 0); ->>>>>>> upstream/master - } - } - } - } - - virtual void fillHalfRMTBuffer() - { - // -- Fill half of the RMT pulse buffer - - // The buffer holds 64 total pulse items, so this loop converts - // as many pixels as can fit in half of the buffer (MAX_PULSES = - // 32 items). In our case, each pixel consists of three bytes, - // each bit turns into one pulse item -- 24 items per pixel. So, - // each half of the buffer can hold 1 and 1/3 of a pixel. - - // The member variable mCurPulse keeps track of which of the 64 - // items we are writing. During the first call to this method it - // fills 0-31; in the second call it fills 32-63, and then wraps - // back around to zero. - - // When we run out of pixel data, just fill the remaining items - // with zero pulses. - - uint16_t pulse_count = 0; // Ranges from 0-31 (half a buffer) - uint32_t byteval = 0; - uint32_t one_val = mOne.val; - uint32_t zero_val = mZero.val; - bool done_strip = false; - - while (pulse_count < MAX_PULSES) { - if (! mPixels->has(1)) { -<<<<<<< HEAD - if (mCurPulse > 0) { - // -- Extend the last pulse to force the strip to latch. Honestly, I'm not - // sure if this is really necessary. - // RMTMEM.chan[mRMT_channel].data32[mCurPulse-1].duration1 = RMT_RESET_DURATION; - } -======= ->>>>>>> upstream/master - done_strip = true; - break; - } - - // -- Cycle through the R,G, and B values in the right order - switch (mRGB_channel) { - case 0: - byteval = mPixels->loadAndScale0(); - mRGB_channel = 1; - break; - case 1: - byteval = mPixels->loadAndScale1(); - mRGB_channel = 2; - break; - case 2: - byteval = mPixels->loadAndScale2(); - mPixels->advanceData(); - mPixels->stepDithering(); - mRGB_channel = 0; - break; - default: - break; - } - - byteval <<= 24; - // Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the - // rmt_item32_t value corresponding to the buffered bit value - for (register uint32_t j = 0; j < 8; j++) { - uint32_t val = (byteval & 0x80000000L) ? one_val : zero_val; - RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val; - byteval <<= 1; - mCurPulse++; - pulse_count++; - } -<<<<<<< HEAD -======= - - if (done_strip) - RMTMEM.chan[mRMT_channel].data32[mCurPulse-1].duration1 = RMT_RESET_DURATION; ->>>>>>> upstream/master - } - - if (done_strip) { - // -- And fill the remaining items with zero pulses. The zero values triggers - // the tx_done interrupt. - while (pulse_count < MAX_PULSES) { - RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = 0; - mCurPulse++; - pulse_count++; - } - } - - // -- When we have filled the back half the buffer, reset the position to the first half - if (mCurPulse >= MAX_PULSES*2) - mCurPulse = 0; - } - - virtual void writeAllRMTItems() - { - // -- Compute the pulse values for the whole strip at once. - // Requires a large buffer -<<<<<<< HEAD - mBufferSize = mPixels->size() * 3 * 8; -======= - mBufferSize = mPixels->size() * 3 * 8; ->>>>>>> upstream/master - - // TODO: need a specific number here - if (mBuffer == NULL) { - mBuffer = (rmt_item32_t *) calloc( mBufferSize, sizeof(rmt_item32_t)); - } - - mCurPulse = 0; - mRGB_channel = 0; - uint32_t byteval = 0; - while (mPixels->has(1)) { - // -- Cycle through the R,G, and B values in the right order - switch (mRGB_channel) { - case 0: - byteval = mPixels->loadAndScale0(); - mRGB_channel = 1; - break; - case 1: - byteval = mPixels->loadAndScale1(); - mRGB_channel = 2; - break; - case 2: - byteval = mPixels->loadAndScale2(); - mPixels->advanceData(); - mPixels->stepDithering(); - mRGB_channel = 0; - break; - default: - break; - } - - byteval <<= 24; - // Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the - // rmt_item32_t value corresponding to the buffered bit value - for (register uint32_t j = 0; j < 8; j++) { - mBuffer[mCurPulse] = (byteval & 0x80000000L) ? mOne : mZero; - byteval <<= 1; - mCurPulse++; - } - } - - mBuffer[mCurPulse-1].duration1 = RMT_RESET_DURATION; - assert(mCurPulse == mBufferSize); - -<<<<<<< HEAD - rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false); -======= - rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false); ->>>>>>> upstream/master - } -}; - -FASTLED_NAMESPACE_END diff --git a/platforms/esp/32/fastled_esp32.h b/platforms/esp/32/fastled_esp32.h deleted file mode 100644 index fabbfeda3d..0000000000 --- a/platforms/esp/32/fastled_esp32.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "fastpin_esp32.h" -#include "clockless_esp32.h" -// #include "clockless_block_esp32.h" diff --git a/platforms/esp/8266/fastpin_esp8266.h b/platforms/esp/8266/fastpin_esp8266.h deleted file mode 100644 index b8095b8597..0000000000 --- a/platforms/esp/8266/fastpin_esp8266.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -FASTLED_NAMESPACE_BEGIN - -struct FASTLED_ESP_IO { - volatile uint32_t _GPO; - volatile uint32_t _GPOS; - volatile uint32_t _GPOC; -}; - -#define _GPB (*(FASTLED_ESP_IO*)(0x60000000+(0x300))) - - -template class _ESPPIN { - -public: - typedef volatile uint32_t * port_ptr_t; - typedef uint32_t port_t; - - inline static void setOutput() { pinMode(PIN, OUTPUT); } - inline static void setInput() { pinMode(PIN, INPUT); } - - inline static void hi() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPOS = MASK; } else { GP16O |= MASK; } } - inline static void lo() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPOC = MASK; } else { GP16O &= ~MASK; } } - inline static void set(register port_t val) __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPO = val; } else { GP16O = val; }} - - inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - - inline static void toggle() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPO ^= MASK; } else { GP16O ^= MASK; } } - - inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } - inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } - inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - - inline static port_t hival() __attribute__ ((always_inline)) { if (PIN<16) { return GPO | MASK; } else { return GP16O | MASK; } } - inline static port_t loval() __attribute__ ((always_inline)) { if (PIN<16) { return GPO & ~MASK; } else { return GP16O & ~MASK; } } - inline static port_ptr_t port() __attribute__ ((always_inline)) { if(PIN<16) { return &_GPB._GPO; } else { return &GP16O; } } - inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPB._GPOS; } // there is no GP160 support for this - inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPB._GPOC; } - inline static port_t mask() __attribute__ ((always_inline)) { return MASK; } - - inline static bool isset() __attribute__ ((always_inline)) { return (PIN < 16) ? (GPO & MASK) : (GP16O & MASK); } -}; - -#define _DEFPIN_ESP8266(PIN, REAL_PIN) template<> class FastPin : public _ESPPIN {}; - - -#ifdef FASTLED_ESP8266_RAW_PIN_ORDER -#define MAX_PIN 16 -_DEFPIN_ESP8266(0,0); _DEFPIN_ESP8266(1,1); _DEFPIN_ESP8266(2,2); _DEFPIN_ESP8266(3,3); -_DEFPIN_ESP8266(4,4); _DEFPIN_ESP8266(5,5); - -// These pins should be disabled, as they always cause WDT resets -// _DEFPIN_ESP8266(6,6); _DEFPIN_ESP8266(7,7); -// _DEFPIN_ESP8266(8,8); _DEFPIN_ESP8266(9,9); _DEFPIN_ESP8266(10,10); _DEFPIN_ESP8266(11,11); - -_DEFPIN_ESP8266(12,12); _DEFPIN_ESP8266(13,13); _DEFPIN_ESP8266(14,14); _DEFPIN_ESP8266(15,15); -_DEFPIN_ESP8266(16,16); - -#define PORTA_FIRST_PIN 12 -#elif defined(FASTLED_ESP8266_D1_PIN_ORDER) -#define MAX_PIN 15 -_DEFPIN_ESP8266(0,3); -_DEFPIN_ESP8266(1,1); -_DEFPIN_ESP8266(2,16); -_DEFPIN_ESP8266(3,5); -_DEFPIN_ESP8266(4,4); -_DEFPIN_ESP8266(5,14); -_DEFPIN_ESP8266(6,12); -_DEFPIN_ESP8266(7,13); -_DEFPIN_ESP8266(8,0); -_DEFPIN_ESP8266(9,2); -_DEFPIN_ESP8266(10,15); -_DEFPIN_ESP8266(11,13); -_DEFPIN_ESP8266(12,12); -_DEFPIN_ESP8266(13,14); -_DEFPIN_ESP8266(14,4); -_DEFPIN_ESP8266(15,5); - -#define PORTA_FIRST_PIN 12 - -#else // if defined(FASTLED_ESP8266_NODEMCU_PIN_ORDER) -#define MAX_PIN 10 - -// This seems to be the standard Dxx pin mapping on most of the esp boards that i've found -_DEFPIN_ESP8266(0,16); _DEFPIN_ESP8266(1,5); _DEFPIN_ESP8266(2,4); _DEFPIN_ESP8266(3,0); -_DEFPIN_ESP8266(4,2); _DEFPIN_ESP8266(5,14); _DEFPIN_ESP8266(6,12); _DEFPIN_ESP8266(7,13); -_DEFPIN_ESP8266(8,15); _DEFPIN_ESP8266(9,3); _DEFPIN_ESP8266(10,1); - -#define PORTA_FIRST_PIN 6 - -// The rest of the pins - these are generally not available -// _DEFPIN_ESP8266(11,6); -// _DEFPIN_ESP8266(12,7); _DEFPIN_ESP8266(13,8); _DEFPIN_ESP8266(14,9); _DEFPIN_ESP8266(15,10); -// _DEFPIN_ESP8266(16,11); - -#endif - -#define HAS_HARDWARE_PIN_SUPPORT - -#define FASTLED_NAMESPACE_END diff --git a/preview_changes.txt b/preview_changes.txt deleted file mode 100644 index dbbd94668a..0000000000 --- a/preview_changes.txt +++ /dev/null @@ -1,19 +0,0 @@ -FastLED 3.1 preview changes: -* UART in SPI mode support on AVR -* Support for using both hardware SPI pinsets on the Teensy 3.x - 11/13 and 7/14. -* Added UCS1904 support -* Better AVR cycle counting -* Split WS2812/WS2811 timings -* Added DOTSTAR definition for adafruit dotstar pixels (aka APA102) -* 8-way parallel output on teensy 3, 3.1 (portc,portd), due/digix (porta, portb, portd) -* 12-way parallel output on teensy 3, 3.1 (portc) -* 16-way parallel output on teensy 3, 3.1 (portc & portd paired) -* refresh rate limiting -* interrupt friendly code on teensy 3/3.1 -* -interrupt friendly code on AVR- <-- disabled for now -* interrupt friendly code on the due -* code re-org for future wider platform support -* Spark Core support -* arduino zero support (no hardware spi yet) -* greatly improved clockless output for avr -* greatly improved clockless output for arm m0 boards diff --git a/release_notes.md b/release_notes.md index 7590bbd62b..9c38eedf76 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,3 +1,51 @@ +FastLED 3.3.3 +============= + +* Improved support for ESP32, Teensy4, ATmega16, nRF52, and ARM STM32. +* Added animation examples: "TwinkleFox" holiday lights, "Pride2015" moving rainbows, and "Pacifica" gentle ocean waves +* Fixed a few bugs including a rare divide-by-zero crash +* Cleaned up code and examples a bit +* Said our sad farwells to FastLED founder Daniel Garcia, who we lost in a tragic accident on September 2nd, 2019. Dan's beautiful code and warm kindness have been at the heart of the library, and our community, for ten years. FastLED will continue with help from all across the FastLED world, and Dan's spirit will be with us whenever the lights shine and glow. Thank you, Dan, for everything. + + +FastLED 3.3.2 +============= + +* Fix APA102 compile error #870 +* Normalize pin definition macros so that we can have an .ino file that can be used to output what pin/port mappings should be for a platform +* Add defnition for ATmega32 + +FastLED 3.3.1 +============= + +* Fix teensy build issue +* Bring in sam's RMT timing fix + +FastLED 3.3.0 +============== +* Preliminary Teensy 4 support +* Fix #861 - power computation for OctoWS2811 +* keywords and other minor changes for compilers (#854, #845) +* Fix some nrf52 issues (#856), #840 + +FastLED 3.2.10 +============== +* Adafruit Metro M4 Airlift support +* Arduino Nano 33 IOT preliminary definitions +* Bug fixes + +FastLED 3.2.9 +============= +* Update ItsyBitsy support +* Remove conflicting types courtesy of an esp8266 framework update +* Fixes to clockless M0 code to allow for more interrupt enabled environments +* ATTiny25 compilation fix +* Some STM32 fixes (the platform still seems unhappy, though) +* NRF52 support +* Updated ESP32 support - supporting up to 24-way parallel output + + + FastLED 3.2.6 ============= @@ -27,7 +75,7 @@ FastLED 3.2.1 * ATmega644P support * Adafruit Hallowwing (Thanks to Lady Ada) * Improved STM 32 support -* Some user contributed cleanups +* Some user contributed cleanups * ESP32 APA102 output fix FastLED3.2 @@ -36,7 +84,7 @@ FastLED3.2 * various minor contributed fixes FastLED 3.1.8 -============= +============= * Added support for Adafruit Circuit Playground Express (Thanks to Lady Ada) * Improved support for Adafruit Gemma and Trinket m0 (Thanks to Lady Ada) * Added support for PJRC's WS2812Serial (Thanks to Paul Stoffregen) @@ -69,26 +117,26 @@ FastLED3.1.3 =============== * Add SK6822 timings -* Add ESP8266 support - note, only tested w/the arduino esp8266 build environment +* Add ESP8266 support - note, only tested w/the arduino esp8266 build environment * Improvements to hsv2rgb, palette, and noise performance * Improvements to rgb2hsv accuracy -* Fixed noise discontinuity +* Fixed noise discontinuity * Add wino board support * Fix scale8 (so now, scale8(255,255) == 255, not 254!) -* Add ESP8266 parallel output support +* Add ESP8266 parallel output support FastLED3.1.1 ============ * Enabled RFDuino/nrf51822 hardware SPI support -* Fix edge case bug w/HSV palette blending +* Fix edge case bug w/HSV palette blending * Fix power management issue w/parallel output * Use static_asserts for some more useful compile time errors around bad pins * Roll power management into FastLED.show/delay directly * Support for adafruit pixies on arduino type platforms that have SoftwareSerial * TODO: support hardware serial on platforms that have it available * Add UCS2903 timings -* Preliminary CPixelView/CRGBSet code - more flexible treatment of groups of arrays +* Preliminary CPixelView/CRGBSet code - more flexible treatment of groups of arrays * https://github.com/FastLED/FastLED/wiki/RGBSet-Reference @@ -100,7 +148,7 @@ FastLED3.1.0 * RFDuino/nrf51822 * Spark Core * Major internal code reoganization -* Started doxygen based documentation +* Started doxygen based documentation * Lots of bug/performance fixes * Parallel output on various arm platforms * lots of new stuff @@ -149,7 +197,7 @@ FastLED3.0 * Fixed DMXSERIAL/DMXSIMPLE support * Timing adjustments for existing SPI chipsets * Cleaned up the code layout to make platform support easier -* Many bug fixes +* Many bug fixes * A number of performance/memory improvements * Remove Squant (takes up space!) @@ -159,7 +207,7 @@ FastLED2 ## Full release of the library ## Release Candidate 6 -* Rename library, offically, to FastLED, move to github +* Rename library, offically, to FastLED, move to github * Update keywords with all the new stuffs ## Release Candidate 5 @@ -175,7 +223,7 @@ FastLED2 * Added ability to set pixel color directly from HSV * Added ability to retrieve current random16 seed -## Release Candidate 2 +## Release Candidate 2 * mostly bug fixes * Fix SPI macro definitions for latest teensy3 software update * Teensy 2 compilation fix @@ -188,7 +236,7 @@ FastLED2 * high speed memory management operations * library for interpolation/easing functions * various api changes, addition of clear and showColor functions -* scale value applied to all show methods +* scale value applied to all show methods * bug fixes for SM16716 * performance improvements, lpd8806 exceeds 22Mbit now * hardware def fixes @@ -217,5 +265,3 @@ en if you're using the hardware SPI pins ## Preview 1 * Initial release - - diff --git a/FastLED.cpp b/src/FastLED.cpp similarity index 89% rename from FastLED.cpp rename to src/FastLED.cpp index 94f2302124..255dcfa3e0 100644 --- a/FastLED.cpp +++ b/src/FastLED.cpp @@ -31,8 +31,8 @@ CFastLED::CFastLED() { } CLEDController &CFastLED::addLeds(CLEDController *pLed, - struct CRGB *data, - int nLedsOrOffset, int nLedsIfOffset) { + struct CRGB *data, + int nLedsOrOffset, int nLedsIfOffset) { int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0; int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset; @@ -67,7 +67,7 @@ int CFastLED::count() { int x = 0; CLEDController *pCur = CLEDController::head(); while( pCur) { - x++; + ++x; pCur = pCur->next(); } return x; @@ -204,30 +204,33 @@ extern int noise_min; extern int noise_max; void CFastLED::countFPS(int nFrames) { - static int br = 0; - static uint32_t lastframe = 0; // millis(); + static int br = 0; + static uint32_t lastframe = 0; // millis(); - if(br++ >= nFrames) { + if(br++ >= nFrames) { uint32_t now = millis(); now -= lastframe; + if(now == 0) { + now = 1; // prevent division by zero below + } m_nFPS = (br * 1000) / now; - br = 0; - lastframe = millis(); - } + br = 0; + lastframe = millis(); + } } void CFastLED::setMaxRefreshRate(uint16_t refresh, bool constrain) { - if(constrain) { - // if we're constraining, the new value of m_nMinMicros _must_ be higher than previously (because we're only - // allowed to slow things down if constraining) - if(refresh > 0) { - m_nMinMicros = ( (1000000/refresh) > m_nMinMicros) ? (1000000/refresh) : m_nMinMicros; - } - } else if(refresh > 0) { - m_nMinMicros = 1000000 / refresh; - } else { - m_nMinMicros = 0; - } + if(constrain) { + // if we're constraining, the new value of m_nMinMicros _must_ be higher than previously (because we're only + // allowed to slow things down if constraining) + if(refresh > 0) { + m_nMinMicros = ((1000000 / refresh) > m_nMinMicros) ? (1000000 / refresh) : m_nMinMicros; + } + } else if(refresh > 0) { + m_nMinMicros = 1000000 / refresh; + } else { + m_nMinMicros = 0; + } } extern "C" int atexit(void (* /*func*/ )()) { return 0; } diff --git a/FastLED.h b/src/FastLED.h similarity index 96% rename from FastLED.h rename to src/FastLED.h index 5bb07526d1..0cc1acc495 100644 --- a/FastLED.h +++ b/src/FastLED.h @@ -8,12 +8,12 @@ #define FASTLED_HAS_PRAGMA_MESSAGE #endif -#define FASTLED_VERSION 3002006 +#define FASTLED_VERSION 3003002 #ifndef FASTLED_INTERNAL # ifdef FASTLED_HAS_PRAGMA_MESSAGE -# pragma message "FastLED version 3.002.006" +# pragma message "FastLED version 3.003.003" # else -# warning FastLED version 3.002.006 (Not really a warning, just telling you here.) +# warning FastLED version 3.003.003 (Not really a warning, just telling you here.) # endif #endif @@ -220,7 +220,7 @@ class CFastLED { /// @tparam RGB_ORDER - the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) /// @tparam SPI_DATA_RATE - the data rate to drive the SPI clock at, defined using DATA_RATE_MHZ or DATA_RATE_KHZ macros /// @returns a reference to the added controller - template CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + template CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { switch(CHIPSET) { case LPD6803: { static LPD6803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } case LPD8806: { static LPD8806Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } @@ -271,7 +271,7 @@ class CFastLED { return addLeds(data, nLedsOrOffset, nLedsIfOffset); } - template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { return addLeds(data, nLedsOrOffset, nLedsIfOffset); } @@ -315,6 +315,14 @@ class CFastLED { return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } +#if defined(__FASTLED_HAS_FIBCC) && (__FASTLED_HAS_FIBCC == 1) + template class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB> + static CLEDController &addLeds(struct CRGB *data, int nLeds) { + static __FIBCC c; + return addLeds(&c, data, nLeds); + } +#endif + #ifdef FASTSPI_USE_DMX_SIMPLE template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) @@ -548,19 +556,19 @@ class CFastLED { uint16_t getFPS() { return m_nFPS; } /// Get how many controllers have been registered - /// @returns the number of controllers (strips) that have been added with addLeds + /// @returns the number of controllers (strips) that have been added with addLeds int count(); /// Get a reference to a registered controller - /// @returns a reference to the Nth controller + /// @returns a reference to the Nth controller CLEDController & operator[](int x); /// Get the number of leds in the first controller - /// @returns the number of LEDs in the first controller + /// @returns the number of LEDs in the first controller int size() { return (*this)[0].size(); } /// Get a pointer to led data for the first controller - /// @returns pointer to the CRGB buffer for the first controller + /// @returns pointer to the CRGB buffer for the first controller CRGB *leds() { return (*this)[0].leds(); } }; diff --git a/src/bitswap.cpp b/src/bitswap.cpp new file mode 100644 index 0000000000..5be71f0277 --- /dev/null +++ b/src/bitswap.cpp @@ -0,0 +1,28 @@ +#define FASTLED_INTERNAL +#include "FastLED.h" + +/// Simplified form of bits rotating function. Based on code found here - http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt - rotating +/// data into LSB for a faster write (the code using this data can happily walk the array backwards) +void transpose8x1_noinline(unsigned char *A, unsigned char *B) { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + y = *(unsigned int*)(A); + x = *(unsigned int*)(A+4); + + // pre-transform x + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + + // pre-transform y + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + // final transform + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + + *((uint32_t*)B) = y; + *((uint32_t*)(B+4)) = x; +} diff --git a/bitswap.h b/src/bitswap.h similarity index 99% rename from bitswap.h rename to src/bitswap.h index 64fed49e3f..79eec540b2 100644 --- a/bitswap.h +++ b/src/bitswap.h @@ -126,7 +126,7 @@ __attribute__((always_inline)) inline void swapbits8(bitswap_type in, bitswap_ty // SWAPSB(b.c,1); // SWAPSB(b.d,0); - for(int i = 0; i < 8; i++) { + for(int i = 0; i < 8; ++i) { just8bits work; work.a3 = in.word[0] >> 31; work.a2 = in.word[0] >> 23; @@ -145,7 +145,7 @@ __attribute__((always_inline)) inline void swapbits8(bitswap_type in, bitswap_ty /// Slow version of the 8 byte by 8 bit rotation __attribute__((always_inline)) inline void slowswap(unsigned char *A, unsigned char *B) { - for(int row = 0; row < 7; row++) { + for(int row = 0; row < 7; ++row) { uint8_t x = A[row]; uint8_t bit = (1< #define HAS_PIXIE @@ -28,6 +28,7 @@ template class PixieController : public CPixelLEDController { SoftwareSerial Serial; CMinWait<2000> mWait; + public: PixieController() : Serial(-1, DATA_PIN) {} @@ -77,7 +78,7 @@ class PixieController : public CPixelLEDController { /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) -template +template class LPD8806Controller : public CPixelLEDController { typedef SPIOutput SPI; @@ -92,8 +93,8 @@ class LPD8806Controller : public CPixelLEDController { }; SPI mSPI; -public: +public: LPD8806Controller() {} virtual void init() { mSPI.init(); @@ -118,11 +119,12 @@ class LPD8806Controller : public CPixelLEDController { /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(1) -template +template class WS2801Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; CMinWait<1000> mWaitDelay; + public: WS2801Controller() {} @@ -132,7 +134,6 @@ class WS2801Controller : public CPixelLEDController { } protected: - virtual void showPixels(PixelController & pixels) { mWaitDelay.wait(); mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels); @@ -140,7 +141,7 @@ class WS2801Controller : public CPixelLEDController { } }; -template +template class WS2803Controller : public WS2801Controller {}; /// LPD6803 controller class (LPD1101). @@ -151,7 +152,7 @@ class WS2803Controller : public WS2801Controller +template class LPD6803Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; @@ -166,7 +167,6 @@ class LPD6803Controller : public CPixelLEDController { } protected: - virtual void showPixels(PixelController & pixels) { mSPI.select(); @@ -201,7 +201,7 @@ class LPD6803Controller : public CPixelLEDController { /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) -template +template class APA102Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; @@ -232,7 +232,6 @@ class APA102Controller : public CPixelLEDController { } protected: - virtual void showPixels(PixelController & pixels) { mSPI.select(); @@ -266,7 +265,7 @@ class APA102Controller : public CPixelLEDController { /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(24) -template +template class SK9822Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; @@ -297,7 +296,6 @@ class SK9822Controller : public CPixelLEDController { } protected: - virtual void showPixels(PixelController & pixels) { mSPI.select(); @@ -340,7 +338,7 @@ class SK9822Controller : public CPixelLEDController { /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(10) -template +template class P9813Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; @@ -360,7 +358,6 @@ class P9813Controller : public CPixelLEDController { } protected: - virtual void showPixels(PixelController & pixels) { mSPI.select(); @@ -390,7 +387,7 @@ class P9813Controller : public CPixelLEDController { /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(16) -template +template class SM16716Controller : public CPixelLEDController { typedef SPIOutput SPI; SPI mSPI; @@ -398,10 +395,15 @@ class SM16716Controller : public CPixelLEDController { void writeHeader() { // Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes) mSPI.select(); - mSPI.writeBytesValueRaw(0, 6); - mSPI.waitFully(); mSPI.template writeBit<0>(0); + mSPI.writeByte(0); + mSPI.writeByte(0); + mSPI.writeByte(0); mSPI.template writeBit<0>(0); + mSPI.writeByte(0); + mSPI.writeByte(0); + mSPI.writeByte(0); + mSPI.waitFully(); mSPI.release(); } @@ -413,7 +415,6 @@ class SM16716Controller : public CPixelLEDController { } protected: - virtual void showPixels(PixelController & pixels) { // Make sure the FLAG_START_BIT flag is set to ensure that an extra 1 bit is sent at the start // of each triplet of bytes for rgb data @@ -428,6 +429,16 @@ class SM16716Controller : public CPixelLEDController { // // Clockless template instantiations - see clockless.h for how the timing values are used // +// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. +// At T=0 : the line is raised hi to start a bit +// At T=T1 : the line is dropped low to transmit a zero bit +// At T=T1+T2 : the line is dropped low to transmit a one bit +// At T=T1+T2+T3 : the cycle is concluded (next bit can be sent) +// +// The units used for T1, T2, and T3 is nanoseconds. +// For 8MHz/16MHz/24MHz frequencies, these values are also guaranteed +// to be integral multiples of an 8MHz clock (125ns increments). +// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef FASTLED_HAS_CLOCKLESS @@ -435,10 +446,16 @@ class SM16716Controller : public CPixelLEDController { /// Provides timing definitions for the variety of clockless controllers supplied by the library. /// @{ +// Allow clock that clockless controller is based on to have different +// frequency than the CPU. +#if !defined(CLOCKLESS_FREQUENCY) + #define CLOCKLESS_FREQUENCY F_CPU +#endif + // We want to force all avr's to use the Trinket controller when running at 8Mhz, because even the 328's at 8Mhz // need the more tightly defined timeframes. -#if (F_CPU == 8000000 || F_CPU == 16000000 || F_CPU == 24000000) // || F_CPU == 48000000 || F_CPU == 96000000) // 125ns/clock -#define FMUL (F_CPU/8000000) +#if defined(__LGT8F__) || (CLOCKLESS_FREQUENCY == 8000000 || CLOCKLESS_FREQUENCY == 16000000 || CLOCKLESS_FREQUENCY == 24000000) // || CLOCKLESS_FREQUENCY == 48000000 || CLOCKLESS_FREQUENCY == 96000000) // 125ns/clock +#define FMUL (CLOCKLESS_FREQUENCY/8000000) // GE8822 template @@ -493,7 +510,7 @@ template class TM1803Controller400Khz : public ClocklessController {}; template -class TM1829Controller800Khz : public ClocklessController {}; +class TM1829Controller800Khz : public ClocklessController {}; template class GW6205Controller400Khz : public ClocklessController {}; @@ -505,82 +522,93 @@ template class PL9823Controller : public ClocklessController {}; #else + +// Similar to NS() macro, this calculates the number of cycles for +// the clockless chipset (which may differ from CPU cycles) + +#ifdef FASTLED_TEENSY4 +// just use raw nanosecond values for the teensy4 +#define C_NS(_NS) _NS +#else +#define C_NS(_NS) (((_NS * ((CLOCKLESS_FREQUENCY / 1000000L)) + 999)) / 1000) +#endif + // GE8822 - 350ns 660ns 350ns template -class GE8822Controller800Khz : public ClocklessController {}; +class GE8822Controller800Khz : public ClocklessController {}; // GW6205@400khz - 800ns, 800ns, 800ns template -class GW6205Controller400Khz : public ClocklessController {}; +class GW6205Controller400Khz : public ClocklessController {}; // GW6205@400khz - 400ns, 400ns, 400ns template -class GW6205Controller800Khz : public ClocklessController {}; +class GW6205Controller800Khz : public ClocklessController {}; // UCS1903 - 500ns, 1500ns, 500ns template -class UCS1903Controller400Khz : public ClocklessController {}; +class UCS1903Controller400Khz : public ClocklessController {}; // UCS1903B - 400ns, 450ns, 450ns template -class UCS1903BController800Khz : public ClocklessController {}; +class UCS1903BController800Khz : public ClocklessController {}; // UCS1904 - 400ns, 400ns, 450ns template -class UCS1904Controller800Khz : public ClocklessController {}; +class UCS1904Controller800Khz : public ClocklessController {}; // UCS2903 - 250ns, 750ns, 250ns template -class UCS2903Controller : public ClocklessController {}; +class UCS2903Controller : public ClocklessController {}; // TM1809 - 350ns, 350ns, 550ns template -class TM1809Controller800Khz : public ClocklessController {}; +class TM1809Controller800Khz : public ClocklessController {}; // WS2811 - 320ns, 320ns, 640ns template -class WS2811Controller800Khz : public ClocklessController {}; +class WS2811Controller800Khz : public ClocklessController {}; // WS2813 - 320ns, 320ns, 640ns template -class WS2813Controller : public ClocklessController {}; +class WS2813Controller : public ClocklessController {}; // WS2812 - 250ns, 625ns, 375ns template -class WS2812Controller800Khz : public ClocklessController {}; +class WS2812Controller800Khz : public ClocklessController {}; // WS2811@400khz - 800ns, 800ns, 900ns template -class WS2811Controller400Khz : public ClocklessController {}; +class WS2811Controller400Khz : public ClocklessController {}; // 750NS, 750NS, 750NS template -class TM1803Controller400Khz : public ClocklessController {}; +class TM1803Controller400Khz : public ClocklessController {}; template -class TM1829Controller800Khz : public ClocklessController {}; +class TM1829Controller800Khz : public ClocklessController {}; template -class TM1829Controller1600Khz : public ClocklessController {}; +class TM1829Controller1600Khz : public ClocklessController {}; template -class LPD1886Controller1250Khz : public ClocklessController {}; +class LPD1886Controller1250Khz : public ClocklessController {}; template -class LPD1886Controller1250Khz_8bit : public ClocklessController {}; +class LPD1886Controller1250Khz_8bit : public ClocklessController {}; template -class SK6822Controller : public ClocklessController {}; +class SK6822Controller : public ClocklessController {}; template -class SK6812Controller : public ClocklessController {}; +class SK6812Controller : public ClocklessController {}; template -class SM16703Controller : public ClocklessController {}; +class SM16703Controller : public ClocklessController {}; template -class PL9823Controller : public ClocklessController {}; +class PL9823Controller : public ClocklessController {}; #endif ///@} diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000000..63687cb55f --- /dev/null +++ b/src/color.h @@ -0,0 +1,84 @@ +#ifndef __INC_COLOR_H +#define __INC_COLOR_H + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +///@file color.h +/// contains definitions for color correction and temperature +///@defgroup ColorEnums Color correction/temperature +/// definitions for color correction and light temperatures +///@{ +typedef enum { + // Color correction starting points + + /// typical values for SMD5050 LEDs + ///@{ + TypicalSMD5050=0xFFB0F0 /* 255, 176, 240 */, + TypicalLEDStrip=0xFFB0F0 /* 255, 176, 240 */, + ///@} + + /// typical values for 8mm "pixels on a string" + /// also for many through-hole 'T' package LEDs + ///@{ + Typical8mmPixel=0xFFE08C /* 255, 224, 140 */, + TypicalPixelString=0xFFE08C /* 255, 224, 140 */, + ///@} + + /// uncorrected color + UncorrectedColor=0xFFFFFF + +} LEDColorCorrection; + + +typedef enum { + /// @name Black-body radiation light sources + /// Black-body radiation light sources emit a (relatively) continuous + /// spectrum, and can be described as having a Kelvin 'temperature' + ///@{ + /// 1900 Kelvin + Candle=0xFF9329 /* 1900 K, 255, 147, 41 */, + /// 2600 Kelvin + Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */, + /// 2850 Kelvin + Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */, + /// 3200 Kelvin + Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */, + /// 5200 Kelvin + CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */, + /// 5400 Kelvin + HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */, + /// 6000 Kelvin + DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */, + /// 7000 Kelvin + OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */, + /// 20000 Kelvin + ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */, + ///@} + + /// @name Gaseous light sources + /// Gaseous light sources emit discrete spectral bands, and while we can + /// approximate their aggregate hue with RGB values, they don't actually + /// have a proper Kelvin temperature. + ///@{ + WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */, + StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */, + CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */, + FullSpectrumFluorescent=0xFFF4F2 /* 0 K, 255, 244, 242 */, + GrowLightFluorescent=0xFFEFF7 /* 0 K, 255, 239, 247 */, + BlackLightFluorescent=0xA700FF /* 0 K, 167, 0, 255 */, + MercuryVapor=0xD8F7FF /* 0 K, 216, 247, 255 */, + SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */, + MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */, + HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */, + ///@} + + /// Uncorrected temperature 0xFFFFFF + UncorrectedTemperature=0xFFFFFF +} ColorTemperature; + +FASTLED_NAMESPACE_END + +///@} +#endif diff --git a/colorpalettes.cpp b/src/colorpalettes.cpp similarity index 92% rename from colorpalettes.cpp rename to src/colorpalettes.cpp index 3c3a1f519b..68e42f0353 100644 --- a/colorpalettes.cpp +++ b/src/colorpalettes.cpp @@ -161,14 +161,14 @@ extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM = // you want a 'standard' FastLED rainbow as well. DEFINE_GRADIENT_PALETTE( Rainbow_gp ) { - 0, 255, 0, 0, // Red - 32, 171, 85, 0, // Orange - 64, 171,171, 0, // Yellow - 96, 0,255, 0, // Green - 128, 0,171, 85, // Aqua - 160, 0, 0,255, // Blue - 192, 85, 0,171, // Purple - 224, 171, 0, 85, // Pink - 255, 255, 0, 0};// and back to Red + 0, 255, 0, 0, // Red + 32, 171, 85, 0, // Orange + 64, 171, 171, 0, // Yellow + 96, 0, 255, 0, // Green + 128, 0, 171, 85, // Aqua + 160, 0, 0, 255, // Blue + 192, 85, 0, 171, // Purple + 224, 171, 0, 85, // Pink + 255, 255, 0, 0};// and back to Red #endif diff --git a/colorpalettes.h b/src/colorpalettes.h similarity index 100% rename from colorpalettes.h rename to src/colorpalettes.h diff --git a/colorutils.cpp b/src/colorutils.cpp similarity index 94% rename from colorutils.cpp rename to src/colorutils.cpp index 10d3592455..c40f486058 100644 --- a/colorutils.cpp +++ b/src/colorutils.cpp @@ -13,7 +13,7 @@ FASTLED_NAMESPACE_BEGIN void fill_solid( struct CRGB * leds, int numToFill, const struct CRGB& color) { - for( int i = 0; i < numToFill; i++) { + for( int i = 0; i < numToFill; ++i) { leds[i] = color; } } @@ -21,7 +21,7 @@ void fill_solid( struct CRGB * leds, int numToFill, void fill_solid( struct CHSV * targetArray, int numToFill, const struct CHSV& hsvColor) { - for( int i = 0; i < numToFill; i++) { + for( int i = 0; i < numToFill; ++i) { targetArray[i] = hsvColor; } } @@ -41,7 +41,7 @@ void fill_rainbow( struct CRGB * pFirstLED, int numToFill, hsv.hue = initialhue; hsv.val = 255; hsv.sat = 240; - for( int i = 0; i < numToFill; i++) { + for( int i = 0; i < numToFill; ++i) { pFirstLED[i] = hsv; hsv.hue += deltahue; } @@ -55,7 +55,7 @@ void fill_rainbow( struct CHSV * targetArray, int numToFill, hsv.hue = initialhue; hsv.val = 255; hsv.sat = 240; - for( int i = 0; i < numToFill; i++) { + for( int i = 0; i < numToFill; ++i) { targetArray[i] = hsv; hsv.hue += deltahue; } @@ -98,7 +98,7 @@ void fill_gradient_RGB( CRGB* leds, accum88 r88 = startcolor.r << 8; accum88 g88 = startcolor.g << 8; accum88 b88 = startcolor.b << 8; - for( uint16_t i = startpos; i <= endpos; i++) { + for( uint16_t i = startpos; i <= endpos; ++i) { leds[i] = CRGB( r88 >> 8, g88 >> 8, b88 >> 8); r88 += rdelta87; g88 += gdelta87; @@ -171,7 +171,7 @@ void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale) { - for( uint16_t i = 0; i < num_leds; i++) { + for( uint16_t i = 0; i < num_leds; ++i) { leds[i].nscale8_video( scale); } } @@ -204,7 +204,7 @@ void nscale8_raw( CRGB* leds, uint16_t num_leds, uint8_t scale) void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale) { - for( uint16_t i = 0; i < num_leds; i++) { + for( uint16_t i = 0; i < num_leds; ++i) { leds[i].nscale8( scale); } } @@ -216,7 +216,7 @@ void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask) fg = colormask.g; fb = colormask.b; - for( uint16_t i = 0; i < numLeds; i++) { + for( uint16_t i = 0; i < numLeds; ++i) { leds[i].r = scale8_LEAVING_R1_DIRTY( leds[i].r, fr); leds[i].g = scale8_LEAVING_R1_DIRTY( leds[i].g, fg); leds[i].b = scale8 ( leds[i].b, fb); @@ -261,10 +261,10 @@ CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ) void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay) { - for( uint16_t i = count; i; i--) { + for( uint16_t i = count; i; --i) { nblend( *existing, *overlay, amountOfOverlay); - existing++; - overlay++; + ++existing; + ++overlay; } } @@ -277,7 +277,7 @@ CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ) CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 ) { - for( uint16_t i = 0; i < count; i++) { + for( uint16_t i = 0; i < count; ++i) { dest[i] = blend(src1[i], src2[i], amountOfsrc2); } return dest; @@ -338,10 +338,10 @@ CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGrad void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode ) { if(existing == overlay) return; - for( uint16_t i = count; i; i--) { + for( uint16_t i = count; i; --i) { nblend( *existing, *overlay, amountOfOverlay, directionCode); - existing++; - overlay++; + ++existing; + ++overlay; } } @@ -354,7 +354,7 @@ CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectio CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode ) { - for( uint16_t i = 0; i < count; i++) { + for( uint16_t i = 0; i < count; ++i) { dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode); } return dest; @@ -385,7 +385,7 @@ void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount) uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for( uint16_t i = 0; i < numLeds; i++) { + for( uint16_t i = 0; i < numLeds; ++i) { CRGB cur = leds[i]; CRGB part = cur; part.nscale8( seep); @@ -406,7 +406,7 @@ void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) // blurRows: perform a blur1d on every row of a rectangular matrix void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) { - for( uint8_t row = 0; row < height; row++) { + for( uint8_t row = 0; row < height; ++row) { CRGB* rowbase = leds + (row * width); blur1d( rowbase, width, blur_amount); } @@ -418,9 +418,9 @@ void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) // blur columns uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; - for( uint8_t col = 0; col < width; col++) { + for( uint8_t col = 0; col < width; ++col) { CRGB carryover = CRGB::Black; - for( uint8_t i = 0; i < height; i++) { + for( uint8_t i = 0; i < height; ++i) { CRGB cur = leds[XY(col,i)]; CRGB part = cur; part.nscale8( seep); @@ -529,7 +529,7 @@ CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightne if( hi4 == 15 ) { entry = &(pal[0]); } else { - entry++; + ++entry; } uint8_t f2 = lo4 << 4; @@ -556,25 +556,25 @@ CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightne if( brightness != 255) { if( brightness ) { - brightness++; // adjust for rounding + ++brightness; // adjust for rounding // Now, since brightness is nonzero, we don't need the full scale8_video logic; // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - red1++; + ++red1; #endif } if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - green1++; + ++green1; #endif } if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - blue1++; + ++blue1; #endif } cleanup_R1(); @@ -634,25 +634,25 @@ CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t b if( brightness != 255) { if( brightness ) { - brightness++; // adjust for rounding + ++brightness; // adjust for rounding // Now, since brightness is nonzero, we don't need the full scale8_video logic; // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - red1++; + ++red1; #endif } if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - green1++; + ++green1; #endif } if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - blue1++; + ++blue1; #endif } cleanup_R1(); @@ -698,7 +698,7 @@ CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightne if( hi5 == 31 ) { entry = &(pal[0]); } else { - entry++; + ++entry; } uint8_t f2 = lo3 << 5; @@ -725,25 +725,25 @@ CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightne if( brightness != 255) { if( brightness ) { - brightness++; // adjust for rounding + ++brightness; // adjust for rounding // Now, since brightness is nonzero, we don't need the full scale8_video logic; // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - red1++; + ++red1; #endif } if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - green1++; + ++green1; #endif } if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - blue1++; + ++blue1; #endif } cleanup_R1(); @@ -809,25 +809,25 @@ CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t b if( brightness != 255) { if( brightness ) { - brightness++; // adjust for rounding + ++brightness; // adjust for rounding // Now, since brightness is nonzero, we don't need the full scale8_video logic; // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. if( red1 ) { red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - red1++; + ++red1; #endif } if( green1 ) { green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - green1++; + ++green1; #endif } if( blue1 ) { blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); #if !(FASTLED_SCALE8_FIXED==1) - blue1++; + ++blue1; #endif } cleanup_R1(); @@ -852,7 +852,7 @@ CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightn uint8_t blue = entry->blue; if( brightness != 255) { - brightness++; // adjust for rounding + ++brightness; // adjust for rounding red = scale8_video_LEAVING_R1_DIRTY( red, brightness); green = scale8_video_LEAVING_R1_DIRTY( green, brightness); blue = scale8_video_LEAVING_R1_DIRTY( blue, brightness); @@ -883,7 +883,7 @@ CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t b if( hi4 == 15 ) { entry = &(pal[0]); } else { - entry++; + ++entry; } uint8_t f2 = lo4 << 4; @@ -973,7 +973,7 @@ CHSV ColorFromPalette( const struct CHSVPalette32& pal, uint8_t index, uint8_t b if( hi5 == 31 ) { entry = &(pal[0]); } else { - entry++; + ++entry; } uint8_t f2 = lo3 << 5; @@ -1050,14 +1050,14 @@ CHSV ColorFromPalette( const struct CHSVPalette256& pal, uint8_t index, uint8_t void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); } } void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); } } @@ -1065,7 +1065,7 @@ void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { uint8_t j = i * 2; destpal32[j+0] = srcpal16[i]; destpal32[j+1] = srcpal16[i]; @@ -1074,7 +1074,7 @@ void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { uint8_t j = i * 2; destpal32[j+0] = srcpal16[i]; destpal32[j+1] = srcpal16[i]; @@ -1083,14 +1083,14 @@ void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); } } void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); } } @@ -1117,18 +1117,18 @@ void nblendPaletteTowardPalette( CRGBPalette16& current, CRGBPalette16& target, p2 = (uint8_t*)target.entries; const uint8_t totalChannels = sizeof(CRGBPalette16); - for( uint8_t i = 0; i < totalChannels; i++) { + for( uint8_t i = 0; i < totalChannels; ++i) { // if the values are equal, no changes are needed if( p1[i] == p2[i] ) { continue; } // if the current value is less than the target, increase it by one - if( p1[i] < p2[i] ) { p1[i]++; changes++; } + if( p1[i] < p2[i] ) { ++p1[i]; ++changes; } // if the current value is greater than the target, // increase it by one (or two if it's still greater). if( p1[i] > p2[i] ) { - p1[i]--; changes++; - if( p1[i] > p2[i] ) { p1[i]--; } + --p1[i]; ++changes; + if( p1[i] > p2[i] ) { --p1[i]; } } // if we've hit the maximum number of changes, exit @@ -1182,14 +1182,14 @@ CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB) void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma) { - for( uint16_t i = 0; i < count; i++) { + for( uint16_t i = 0; i < count; ++i) { rgbarray[i] = applyGamma_video( rgbarray[i], gamma); } } void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB) { - for( uint16_t i = 0; i < count; i++) { + for( uint16_t i = 0; i < count; ++i) { rgbarray[i] = applyGamma_video( rgbarray[i], gammaR, gammaG, gammaB); } } diff --git a/colorutils.h b/src/colorutils.h similarity index 94% rename from colorutils.h rename to src/colorutils.h index 4fcf394047..f09d525fe7 100644 --- a/colorutils.h +++ b/src/colorutils.h @@ -168,7 +168,7 @@ void fill_gradient( T* targetArray, accum88 hue88 = startcolor.hue << 8; accum88 sat88 = startcolor.sat << 8; accum88 val88 = startcolor.val << 8; - for( uint16_t i = startpos; i <= endpos; i++) { + for( uint16_t i = startpos; i <= endpos; ++i) { targetArray[i] = CHSV( hue88 >> 8, sat88 >> 8, val88 >> 8); hue88 += huedelta87; sat88 += satdelta87; @@ -452,17 +452,17 @@ class CHSVPalette16 { CHSVPalette16( const CHSVPalette16& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CHSVPalette16& operator=( const CHSVPalette16& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CHSVPalette16( const TProgmemHSVPalette16& rhs) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); entries[i].hue = xyz.red; entries[i].sat = xyz.green; @@ -471,7 +471,7 @@ class CHSVPalette16 { } CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); entries[i].hue = xyz.red; entries[i].sat = xyz.green; @@ -508,10 +508,10 @@ class CHSVPalette16 { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; - for( uint8_t i = 0; i < (sizeof( entries)); i++) { + for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; - p++; - q++; + ++p; + ++q; } return true; } @@ -555,11 +555,11 @@ class CHSVPalette256 { CHSVPalette256( const CHSVPalette256& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CHSVPalette256& operator=( const CHSVPalette256& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } @@ -613,10 +613,10 @@ class CHSVPalette256 { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; - for( uint16_t i = 0; i < (sizeof( entries)); i++) { + for( uint16_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; - p++; - q++; + ++p; + ++q; } return true; } @@ -660,45 +660,45 @@ class CRGBPalette16 { CRGBPalette16( const CRGBPalette16& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CRGBPalette16( const CRGB rhs[16]) { - memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); } CRGBPalette16& operator=( const CRGBPalette16& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CRGBPalette16& operator=( const CRGB rhs[16]) { - memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); return *this; } CRGBPalette16( const CHSVPalette16& rhs) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } CRGBPalette16( const CHSV rhs[16]) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } } CRGBPalette16& operator=( const CHSVPalette16& rhs) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette16& operator=( const CHSV rhs[16]) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } return *this; @@ -706,13 +706,13 @@ class CRGBPalette16 { CRGBPalette16( const TProgmemRGBPalette16& rhs) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } } CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) { - for( uint8_t i = 0; i < 16; i++) { + for( uint8_t i = 0; i < 16; ++i) { entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } return *this; @@ -723,10 +723,10 @@ class CRGBPalette16 { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; - for( uint8_t i = 0; i < (sizeof( entries)); i++) { + for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; - p++; - q++; + ++p; + ++q; } return true; } @@ -828,7 +828,7 @@ class CRGBPalette16 { uint16_t count = 0; do { u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); - count++;; + ++count; } while ( u.index != 255); int8_t lastSlotUsed = -1; @@ -840,7 +840,7 @@ class CRGBPalette16 { uint8_t istart8 = 0; uint8_t iend8 = 0; while( indexstart < 255) { - progent++; + ++progent; u.dword = FL_PGM_READ_DWORD_NEAR( progent); int indexend = u.index; CRGB rgbend( u.r, u.g, u.b); @@ -870,7 +870,7 @@ class CRGBPalette16 { uint16_t count = 0; do { u = *(ent + count); - count++;; + ++count; } while ( u.index != 255); int8_t lastSlotUsed = -1; @@ -883,7 +883,7 @@ class CRGBPalette16 { uint8_t istart8 = 0; uint8_t iend8 = 0; while( indexstart < 255) { - ent++; + ++ent; u = *ent; int indexend = u.index; CRGB rgbend( u.r, u.g, u.b); @@ -918,7 +918,7 @@ class CHSVPalette32 { const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) { - for( uint8_t i = 0; i < 2; i++) { + for( uint8_t i = 0; i < 2; ++i) { entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; @@ -928,17 +928,17 @@ class CHSVPalette32 { CHSVPalette32( const CHSVPalette32& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CHSVPalette32& operator=( const CHSVPalette32& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CHSVPalette32( const TProgmemHSVPalette32& rhs) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); entries[i].hue = xyz.red; entries[i].sat = xyz.green; @@ -947,7 +947,7 @@ class CHSVPalette32 { } CHSVPalette32& operator=( const TProgmemHSVPalette32& rhs) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); entries[i].hue = xyz.red; entries[i].sat = xyz.green; @@ -984,10 +984,10 @@ class CHSVPalette32 { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; - for( uint8_t i = 0; i < (sizeof( entries)); i++) { + for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; - p++; - q++; + ++p; + ++q; } return true; } @@ -1024,7 +1024,7 @@ class CRGBPalette32 { const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) { - for( uint8_t i = 0; i < 2; i++) { + for( uint8_t i = 0; i < 2; ++i) { entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; @@ -1034,45 +1034,45 @@ class CRGBPalette32 { CRGBPalette32( const CRGBPalette32& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CRGBPalette32( const CRGB rhs[32]) { - memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); } CRGBPalette32& operator=( const CRGBPalette32& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CRGBPalette32& operator=( const CRGB rhs[32]) { - memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); return *this; } CRGBPalette32( const CHSVPalette32& rhs) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } CRGBPalette32( const CHSV rhs[32]) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } } CRGBPalette32& operator=( const CHSVPalette32& rhs) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette32& operator=( const CHSV rhs[32]) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } return *this; @@ -1080,13 +1080,13 @@ class CRGBPalette32 { CRGBPalette32( const TProgmemRGBPalette32& rhs) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } } CRGBPalette32& operator=( const TProgmemRGBPalette32& rhs) { - for( uint8_t i = 0; i < 32; i++) { + for( uint8_t i = 0; i < 32; ++i) { entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } return *this; @@ -1097,10 +1097,10 @@ class CRGBPalette32 { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; - for( uint8_t i = 0; i < (sizeof( entries)); i++) { + for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; - p++; - q++; + ++p; + ++q; } return true; } @@ -1225,7 +1225,7 @@ class CRGBPalette32 { uint16_t count = 0; do { u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); - count++;; + ++count; } while ( u.index != 255); int8_t lastSlotUsed = -1; @@ -1237,7 +1237,7 @@ class CRGBPalette32 { uint8_t istart8 = 0; uint8_t iend8 = 0; while( indexstart < 255) { - progent++; + ++progent; u.dword = FL_PGM_READ_DWORD_NEAR( progent); int indexend = u.index; CRGB rgbend( u.r, u.g, u.b); @@ -1267,7 +1267,7 @@ class CRGBPalette32 { uint16_t count = 0; do { u = *(ent + count); - count++;; + ++count; } while ( u.index != 255); int8_t lastSlotUsed = -1; @@ -1280,7 +1280,7 @@ class CRGBPalette32 { uint8_t istart8 = 0; uint8_t iend8 = 0; while( indexstart < 255) { - ent++; + ++ent; u = *ent; int indexend = u.index; CRGB rgbend( u.r, u.g, u.b); @@ -1322,45 +1322,45 @@ class CRGBPalette256 { CRGBPalette256( const CRGBPalette256& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CRGBPalette256( const CRGB rhs[256]) { - memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); } CRGBPalette256& operator=( const CRGBPalette256& rhs) { - memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CRGBPalette256& operator=( const CRGB rhs[256]) { - memmove8( &(entries[0]), &(rhs[0]), sizeof( entries)); + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); return *this; } CRGBPalette256( const CHSVPalette256& rhs) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } CRGBPalette256( const CHSV rhs[256]) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } } CRGBPalette256& operator=( const CHSVPalette256& rhs) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette256& operator=( const CHSV rhs[256]) { - for( int i = 0; i < 256; i++) { + for( int i = 0; i < 256; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } return *this; @@ -1393,10 +1393,10 @@ class CRGBPalette256 { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; - for( uint16_t i = 0; i < (sizeof( entries)); i++) { + for( uint16_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; - p++; - q++; + ++p; + ++q; } return true; } @@ -1475,7 +1475,7 @@ class CRGBPalette256 { int indexstart = 0; while( indexstart < 255) { - progent++; + ++progent; u.dword = FL_PGM_READ_DWORD_NEAR( progent); int indexend = u.index; CRGB rgbend( u.r, u.g, u.b); @@ -1494,7 +1494,7 @@ class CRGBPalette256 { int indexstart = 0; while( indexstart < 255) { - ent++; + ++ent; u = *ent; int indexend = u.index; CRGB rgbend( u.r, u.g, u.b); @@ -1557,7 +1557,7 @@ void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, const PALETTE& pal, uint8_t brightness, TBlendType blendType) { uint8_t colorIndex = startIndex; - for( uint16_t i = 0; i < N; i++) { + for( uint16_t i = 0; i < N; ++i) { L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType); colorIndex += incIndex; } @@ -1572,7 +1572,7 @@ void map_data_into_colors_through_palette( uint8_t opacity=255, TBlendType blendType=LINEARBLEND) { - for( uint16_t i = 0; i < dataCount; i++) { + for( uint16_t i = 0; i < dataCount; ++i) { uint8_t d = dataArray[i]; CRGB rgb = ColorFromPalette( pal, d, brightness, blendType); if( opacity == 255 ) { diff --git a/src/controller.h b/src/controller.h new file mode 100644 index 0000000000..fe32d70d32 --- /dev/null +++ b/src/controller.h @@ -0,0 +1,418 @@ +#ifndef __INC_CONTROLLER_H +#define __INC_CONTROLLER_H + +///@file controller.h +/// base definitions used by led controllers for writing out led data + +#include "FastLED.h" +#include "led_sysdefs.h" +#include "pixeltypes.h" +#include "color.h" +#include + +FASTLED_NAMESPACE_BEGIN + +#define RO(X) RGB_BYTE(RGB_ORDER, X) +#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3) + +#define RGB_BYTE0(RO) ((RO>>6) & 0x3) +#define RGB_BYTE1(RO) ((RO>>3) & 0x3) +#define RGB_BYTE2(RO) ((RO) & 0x3) + +// operator byte *(struct CRGB[] arr) { return (byte*)arr; } + +#define DISABLE_DITHER 0x00 +#define BINARY_DITHER 0x01 +typedef uint8_t EDitherMode; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LED Controller interface definition +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available. +/// Note that the showARGB method is not impelemented for all controllers yet. Note also the methods for eventual checking +/// of background writing of data (I'm looking at you, teensy 3.0 DMA controller!). If you want to pass LED controllers around +/// to methods, make them references to this type, keeps your code saner. However, most people won't be seeing/using these objects +/// directly at all +class CLEDController { +protected: + friend class CFastLED; + CRGB *m_Data; + CLEDController *m_pNext; + CRGB m_ColorCorrection; + CRGB m_ColorTemperature; + EDitherMode m_DitherMode; + int m_nLeds; + static CLEDController *m_pHead; + static CLEDController *m_pTail; + + /// set all the leds on the controller to a given color + ///@param data the crgb color to set the leds to + ///@param nLeds the number of leds to set to this color + ///@param scale the rgb scaling value for outputting color + virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0; + + /// write the passed in rgb data out to the leds managed by this controller + ///@param data the rgb data to write out to the strip + ///@param nLeds the number of leds being written out + ///@param scale the rgb scaling to apply to each led before writing it out + virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0; + +public: + /// create an led controller object, add it to the chain of controllers + CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) { + m_pNext = NULL; + if(m_pHead==NULL) { m_pHead = this; } + if(m_pTail != NULL) { m_pTail->m_pNext = this; } + m_pTail = this; + } + + ///initialize the LED controller + virtual void init() = 0; + + ///clear out/zero out the given number of leds. + virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); } + + /// show function w/integer brightness, will scale for color correction and temperature + void show(const struct CRGB *data, int nLeds, uint8_t brightness) { + show(data, nLeds, getAdjustment(brightness)); + } + + /// show function w/integer brightness, will scale for color correction and temperature + void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) { + showColor(data, nLeds, getAdjustment(brightness)); + } + + /// show function using the "attached to this controller" led data + void showLeds(uint8_t brightness=255) { + show(m_Data, m_nLeds, getAdjustment(brightness)); + } + + /// show the given color on the led strip + void showColor(const struct CRGB & data, uint8_t brightness=255) { + showColor(data, m_nLeds, getAdjustment(brightness)); + } + + /// get the first led controller in the chain of controllers + static CLEDController *head() { return m_pHead; } + /// get the next controller in the chain after this one. will return NULL at the end of the chain + CLEDController *next() { return m_pNext; } + + /// set the default array of leds to be used by this controller + CLEDController & setLeds(CRGB *data, int nLeds) { + m_Data = data; + m_nLeds = nLeds; + return *this; + } + + /// zero out the led data managed by this controller + void clearLedData() { + if(m_Data) { + memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds); + } + } + + /// How many leds does this controller manage? + virtual int size() { return m_nLeds; } + + /// Pointer to the CRGB array for this controller + CRGB* leds() { return m_Data; } + + /// Reference to the n'th item in the controller + CRGB &operator[](int x) { return m_Data[x]; } + + /// set the dithering mode for this controller to use + inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; } + /// get the dithering option currently set for this controller + inline uint8_t getDither() { return m_DitherMode; } + + /// the the color corrction to use for this controller, expressed as an rgb object + CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; } + /// set the color correction to use for this controller + CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; } + /// get the correction value used by this controller + CRGB getCorrection() { return m_ColorCorrection; } + + /// set the color temperature, aka white point, for this controller + CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; } + /// set the color temperature, aka white point, for this controller + CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; } + /// get the color temperature, aka whipe point, for this controller + CRGB getTemperature() { return m_ColorTemperature; } + + /// Get the combined brightness/color adjustment for this controller + CRGB getAdjustment(uint8_t scale) { + return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature); + } + + static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) { + #if defined(NO_CORRECTION) && (NO_CORRECTION==1) + return CRGB(scale,scale,scale); + #else + CRGB adj(0,0,0); + + if(scale > 0) { + for(uint8_t i = 0; i < 3; ++i) { + uint8_t cc = colorCorrection.raw[i]; + uint8_t ct = colorTemperature.raw[i]; + if(cc > 0 && ct > 0) { + uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale; + work /= 0x10000L; + adj.raw[i] = work & 0xFF; + } + } + } + + return adj; + #endif + } + virtual uint16_t getMaxRefreshRate() const { return 0; } +}; + +// Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including +// support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will +// centralize 8/12/16 conversions here as well. +template +struct PixelController { + const uint8_t *mData; + int mLen,mLenRemaining; + uint8_t d[3]; + uint8_t e[3]; + CRGB mScale; + int8_t mAdvance; + int mOffsets[LANES]; + + PixelController(const PixelController & other) { + d[0] = other.d[0]; + d[1] = other.d[1]; + d[2] = other.d[2]; + e[0] = other.e[0]; + e[1] = other.e[1]; + e[2] = other.e[2]; + mData = other.mData; + mScale = other.mScale; + mAdvance = other.mAdvance; + mLenRemaining = mLen = other.mLen; + for(int i = 0; i < LANES; ++i) { mOffsets[i] = other.mOffsets[i]; } + + } + + void initOffsets(int len) { + int nOffset = 0; + for(int i = 0; i < LANES; ++i) { + mOffsets[i] = nOffset; + if((1<1) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>2) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>4) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>8) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>16) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>32) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>64) + \ + (UPDATES_PER_FULL_DITHER_CYCLE>128) ) +#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS + + // R is the digther signal 'counter'. + static uint8_t R = 0; + ++R; + + // R is wrapped around at 2^ditherBits, + // so if ditherBits is 2, R will cycle through (0,1,2,3) + uint8_t ditherBits = VIRTUAL_BITS; + R &= (0x01 << ditherBits) - 1; + + // Q is the "unscaled dither signal" itself. + // It's initialized to the reversed bits of R. + // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192) + uint8_t Q = 0; + + // Reverse bits in a byte + { + if(R & 0x01) { Q |= 0x80; } + if(R & 0x02) { Q |= 0x40; } + if(R & 0x04) { Q |= 0x20; } + if(R & 0x08) { Q |= 0x10; } + if(R & 0x10) { Q |= 0x08; } + if(R & 0x20) { Q |= 0x04; } + if(R & 0x40) { Q |= 0x02; } + if(R & 0x80) { Q |= 0x01; } + } + + // Now we adjust Q to fall in the center of each range, + // instead of at the start of the range. + // If ditherBits is 2, Q will be (0, 128, 64, 192) at first, + // and this adjustment makes it (31, 159, 95, 223). + if( ditherBits < 8) { + Q += 0x01 << (7 - ditherBits); + } + + // D and E form the "scaled dither signal" + // which is added to pixel values to affect the + // actual dithering. + + // Setup the initial D and E values + for(int i = 0; i < 3; ++i) { + uint8_t s = mScale.raw[i]; + e[i] = s ? (256/s) + 1 : 0; + d[i] = scale8(Q, e[i]); +#if (FASTLED_SCALE8_FIXED == 1) + if(d[i]) (--d[i]); +#endif + if(e[i]) --e[i]; + } +#endif + } + + // Do we have n pixels left to process? + __attribute__((always_inline)) inline bool has(int n) { + return mLenRemaining >= n; + } + + // toggle dithering enable + void enable_dithering(EDitherMode dither) { + switch(dither) { + case BINARY_DITHER: init_binary_dithering(); break; + default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break; + } + } + + __attribute__((always_inline)) inline int size() { return mLen; } + + // get the amount to advance the pointer by + __attribute__((always_inline)) inline int advanceBy() { return mAdvance; } + + // advance the data pointer forward, adjust position counter + __attribute__((always_inline)) inline void advanceData() { mData += mAdvance; --mLenRemaining;} + + // step the dithering forward + __attribute__((always_inline)) inline void stepDithering() { + // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN + // clockless_trinket.h! + d[0] = e[0] - d[0]; + d[1] = e[1] - d[1]; + d[2] = e[2] - d[2]; + } + + // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately + __attribute__((always_inline)) inline void preStepFirstByteDithering() { + d[RO(0)] = e[RO(0)] - d[RO(0)]; + } + + template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; } + template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; } + + template __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; } + template __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; } + + template __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); } + template __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); } + + // composite shortcut functions for loading, dithering, and scaling + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale(pc, pc.dither(pc, pc.loadByte(pc))); } + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale(pc, pc.dither(pc, pc.loadByte(pc, lane))); } + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither(pc, pc.loadByte(pc, lane), d), scale); } + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte(pc, lane), scale); } + + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale(pc); } + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale(pc, lane); } + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale(pc, lane, scale); } + + template __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; } + template __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; } + + // Helper functions to get around gcc stupidities + __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane, uint8_t scale) { return loadAndScale<0>(*this, lane, scale); } + __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane, uint8_t scale) { return loadAndScale<1>(*this, lane, scale); } + __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane, uint8_t scale) { return loadAndScale<2>(*this, lane, scale); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane, uint8_t scale) { return advanceAndLoadAndScale<0>(*this, lane, scale); } + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane, uint8_t scale) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane, scale); } + + __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); } + __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); } + __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); } + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); } + + __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); } + __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); } + __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); } + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); } + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); } + + __attribute__((always_inline)) inline uint8_t getScale0() { return getscale<0>(*this); } + __attribute__((always_inline)) inline uint8_t getScale1() { return getscale<1>(*this); } + __attribute__((always_inline)) inline uint8_t getScale2() { return getscale<2>(*this); } +}; + +template class CPixelLEDController : public CLEDController { +protected: + virtual void showPixels(PixelController & pixels) = 0; + + /// set all the leds on the controller to a given color + ///@param data the crgb color to set the leds to + ///@param nLeds the numner of leds to set to this color + ///@param scale the rgb scaling value for outputting color + virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) { + PixelController pixels(data, nLeds, scale, getDither()); + showPixels(pixels); + } + + /// write the passed in rgb data out to the leds managed by this controller + ///@param data the rgb data to write out to the strip + ///@param nLeds the number of leds being written out + ///@param scale the rgb scaling to apply to each led before writing it out + virtual void show(const struct CRGB *data, int nLeds, CRGB scale) { + PixelController pixels(data, nLeds, scale, getDither()); + showPixels(pixels); + } + +public: + CPixelLEDController() : CLEDController() {} +}; + + +FASTLED_NAMESPACE_END + +#endif diff --git a/cpp_compat.h b/src/cpp_compat.h similarity index 100% rename from cpp_compat.h rename to src/cpp_compat.h diff --git a/dmx.h b/src/dmx.h similarity index 100% rename from dmx.h rename to src/dmx.h diff --git a/src/fastled_config.h b/src/fastled_config.h new file mode 100644 index 0000000000..6e415274d1 --- /dev/null +++ b/src/fastled_config.h @@ -0,0 +1,69 @@ +#ifndef __INC_FASTLED_CONFIG_H +#define __INC_FASTLED_CONFIG_H + +#include "FastLED.h" + +///@file fastled_config.h +/// contains definitions that can be used to configure FastLED at compile time + +// Use this option only for debugging pin access and forcing software pin access. Note that +// software pin access only works in Arduino based environments. Forces use of digitalWrite +// methods for pin access vs. direct hardware port access +// #define FASTLED_FORCE_SOFTWARE_PINS + +// Use this option only for debugging bitbang'd spi access or to work around bugs in hardware +// spi access. Forces use of bit-banged spi, even on pins that has hardware SPI available. +// #define FASTLED_FORCE_SOFTWARE_SPI + +// Use this to force FastLED to allow interrupts in the clockless chipsets (or to force it to +// disallow), overriding the default on platforms that support this. Set the value to 1 to +// allow interrupts or 0 to disallow them. +// #define FASTLED_ALLOW_INTERRUPTS 1 +// #define FASTLED_ALLOW_INTERRUPTS 0 + +// Use this to allow some integer overflows/underflows in the inoise functions. +// The original implementions allowed this, and had some discontinuties in the noise +// output. It's technically an implementation bug, and was fixed, but you may wish +// to preserve the old look and feel of the inoise functions in your existing animations. +// The default is 0: NO overflow, and 'continuous' noise output, aka the fixed way. +// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 0 +// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 1 + +// Use this toggle whether or not to use the 'fixed' FastLED scale8. The initial scale8 +// had a problem where scale8(255,255) would give you 254. This is now fixed, and that +// fix is enabled by default. However, if for some reason you have code that is not +// working right as a result of this (e.g. code that was expecting the old scale8 behavior) +// you can disable it here. +#define FASTLED_SCALE8_FIXED 1 +// #define FASTLED_SCALE8_FIXED 0 + +// Use this toggle whether to use 'fixed' FastLED pixel blending, including ColorFromPalette. +// The prior pixel blend functions had integer-rounding math errors that led to +// small errors being inadvertently added to the low bits of blended colors, including colors +// retrieved from color palettes using LINEAR_BLEND. This is now fixed, and the +// fix is enabled by default. However, if for some reason you wish to run with the old +// blending, including the integer rounding and color errors, you can disable the bugfix here. +#define FASTLED_BLEND_FIXED 1 +// #define FASTLED_BLEND_FIXED 0 + +// Use this toggle whether to use 'fixed' FastLED 8- and 16-bit noise functions. +// The prior noise functions had some math errors that led to 'discontinuities' in the +// output, which by definition should be smooth and continuous. The bug led to +// noise function output that had 'edges' and glitches in it. This is now fixed, and the +// fix is enabled by default. However, if for some reason you wish to run with the old +// noise code, including the glitches, you can disable the bugfix here. +#define FASTLED_NOISE_FIXED 1 +//#define FASTLED_NOISE_FIXED 0 + +// Use this to determine how many times FastLED will attempt to re-transmit a frame if interrupted +// for too long by interrupts. +#ifndef FASTLED_INTERRUPT_RETRY_COUNT +#define FASTLED_INTERRUPT_RETRY_COUNT 2 +#endif + +// Use this toggle to enable global brightness in contollers that support is (ADA102 and SK9822). +// It changes how color scaling works and uses global brightness before scaling down color values. +// This enable much more accurate color control on low brightness settings. +//#define FASTLED_USE_GLOBAL_BRIGHTNESS 1 + +#endif diff --git a/fastled_delay.h b/src/fastled_delay.h similarity index 99% rename from fastled_delay.h rename to src/fastled_delay.h index 4649f7d003..a14e8a2941 100644 --- a/fastled_delay.h +++ b/src/fastled_delay.h @@ -12,6 +12,7 @@ FASTLED_NAMESPACE_BEGIN /// this should make sure that chipsets that have template class CMinWait { uint16_t mLastMicros; + public: CMinWait() { mLastMicros = 0; } @@ -51,8 +52,8 @@ template class CMinWait { // predeclaration to not upset the compiler template inline void delaycycles(); template inline void delaycycles_min1() { - delaycycles<1>(); - delaycycles(); + delaycycles<1>(); + delaycycles(); } diff --git a/fastled_progmem.h b/src/fastled_progmem.h similarity index 97% rename from fastled_progmem.h rename to src/fastled_progmem.h index 5527c62356..dfcb9effb0 100644 --- a/fastled_progmem.h +++ b/src/fastled_progmem.h @@ -69,7 +69,7 @@ FASTLED_NAMESPACE_BEGIN // force 4-byte alignment as needed. The FastLED gradient // palette code uses 'read dword', and now uses this macro // to make sure that gradient palettes are 4-byte aligned. -#if defined(FASTLED_ARM) || defined(ESP32) +#if defined(FASTLED_ARM) || defined(ESP32) || defined(ESP8266) #define FL_ALIGN_PROGMEM __attribute__ ((aligned (4))) #else #define FL_ALIGN_PROGMEM diff --git a/fastpin.h b/src/fastpin.h similarity index 87% rename from fastpin.h rename to src/fastpin.h index beb3a2d819..085a7d1b53 100644 --- a/fastpin.h +++ b/src/fastpin.h @@ -42,6 +42,7 @@ class Pin : public Selectable { mPort = (volatile RwReg*)portOutputRegister(digitalPinToPort(mPin)); mInPort = (volatile RoReg*)portInputRegister(digitalPinToPort(mPin)); } + public: Pin(int pin) : mPin(pin) { _init(); } @@ -98,6 +99,7 @@ class Pin : public Selectable { mPort = NULL; mInPort = NULL; } + public: Pin(int pin) : mPin(pin) { _init(); } @@ -169,6 +171,7 @@ template class FastPin { sInPort = portInputRegister(digitalPinToPort(PIN)); #endif } + public: typedef volatile RwReg * port_ptr_t; typedef RwReg port_t; @@ -206,8 +209,8 @@ template class FastPin { static_assert(validpin(), "Invalid pin specified"); - static void _init() { - } + static void _init() { } + public: typedef volatile RwReg * port_ptr_t; typedef RwReg port_t; @@ -241,6 +244,30 @@ template class FastPinBB : public FastPin {}; typedef volatile uint32_t & reg32_t; typedef volatile uint32_t * ptr_reg32_t; +// Utility templates for tracking down information about pins and ports +template struct __FL_PORT_INFO { + static bool hasPort() { return 0; } + static const char *portName() { return "--"; } + static const void *portAddr() { return NULL; } +}; + +// Give us our instantiations for defined ports - we're going to abuse this later for +// auto discovery of pin/port mappings for new variants. Use _FL_DEFINE_PORT for ports that +// are numeric in nature, e.g. GPIO0, GPIO1. Use _FL_DEFINE_PORT3 for ports that are letters. +// The first parameter will be the letter, the second parameter will be an integer/counter of smoe kind +// (this is because attempts to turn macro parameters into character constants break in some compilers) +#define _FL_DEFINE_PORT(L, BASE) template<> struct __FL_PORT_INFO { \ + static bool hasPort() { return 1; } \ + static const char *portName() { return #L; } \ + typedef BASE __t_baseType; \ + static const void *portAddr() { return (void*)&__t_baseType::r(); } }; + +#define _FL_DEFINE_PORT3(L, LC, BASE) template<> struct __FL_PORT_INFO { \ + static bool hasPort() { return 1; } \ + static const char *portName() { return #L; } \ + typedef BASE __t_baseType; \ + static const void *portAddr() { return (void*)&__t_baseType::r(); } }; + FASTLED_NAMESPACE_END #pragma GCC diagnostic pop diff --git a/fastspi.h b/src/fastspi.h similarity index 68% rename from fastspi.h rename to src/fastspi.h index 8e2a593be5..2245ffe924 100644 --- a/fastspi.h +++ b/src/fastspi.h @@ -13,6 +13,10 @@ FASTLED_NAMESPACE_BEGIN #if defined(FASTLED_TEENSY3) && (F_CPU > 48000000) #define DATA_RATE_MHZ(X) (((48000000L / 1000000L) / X)) #define DATA_RATE_KHZ(X) (((48000000L / 1000L) / X)) +#elif defined(FASTLED_TEENSY4) // && (ARM_HARDWARE_SPI) +// just use clocks +#define DATA_RATE_MHZ(X) (1000000 * (X)) +#define DATA_RATE_KHZ(X) (1000 * (X)) #else #define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X) #define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X) @@ -26,44 +30,65 @@ FASTLED_NAMESPACE_BEGIN ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(FASTLED_ALL_PINS_HARDWARE_SPI) -template +template class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #endif -template +template class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #ifndef FASTLED_FORCE_SOFTWARE_SPI #if defined(NRF51) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) -template +template class SPIOutput : public NRF51SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #endif +#if defined(NRF52_SERIES) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) +template +class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; +#endif + +#if defined(FASTLED_APOLLO3) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) +template +class SPIOutput : public APOLLO3HardwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; +#endif + #if defined(SPI_DATA) && defined(SPI_CLOCK) #if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI) -template +template class SPIOutput : public ARMHardwareSPIOutput {}; #if defined(SPI2_DATA) -template +template class SPIOutput : public ARMHardwareSPIOutput {}; -template +template class SPIOutput : public ARMHardwareSPIOutput {}; -template +template class SPIOutput : public ARMHardwareSPIOutput {}; #endif +#elif defined(FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI) + +template +class SPIOutput : public Teesy4HardwareSPIOutput {}; + +template +class SPIOutput : public Teesy4HardwareSPIOutput {}; + +template +class SPIOutput : public Teesy4HardwareSPIOutput {}; + #elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI) -#define DECLARE_SPI0(__DATA,__CLOCK) template\ +#define DECLARE_SPI0(__DATA,__CLOCK) template\ class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40076000> {}; - #define DECLARE_SPI1(__DATA,__CLOCK) template\ + #define DECLARE_SPI1(__DATA,__CLOCK) template\ class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40077000> {}; DECLARE_SPI0(7,13); @@ -80,24 +105,24 @@ DECLARE_SPI1(21,20); #elif defined(__SAM3X8E__) -template +template class SPIOutput : public SAMHardwareSPIOutput {}; #elif defined(AVR_HARDWARE_SPI) -template +template class SPIOutput : public AVRHardwareSPIOutput {}; #if defined(SPI_UART0_DATA) -template +template class SPIOutput : public AVRUSART0SPIOutput {}; #endif #if defined(SPI_UART1_DATA) -template +template class SPIOutput : public AVRUSART1SPIOutput {}; #endif @@ -115,7 +140,7 @@ class SPIOutput : public AVRUSART1SP #endif // #if defined(USART_DATA) && defined(USART_CLOCK) -// template +// template // class AVRSPIOutput : public AVRUSARTSPIOutput {}; // #endif diff --git a/fastspi_bitbang.h b/src/fastspi_bitbang.h similarity index 95% rename from fastspi_bitbang.h rename to src/fastspi_bitbang.h index d48e32bcb3..86663f17bd 100644 --- a/fastspi_bitbang.h +++ b/src/fastspi_bitbang.h @@ -15,7 +15,7 @@ FASTLED_NAMESPACE_BEGIN // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class AVRSoftwareSPIOutput { // The data types for pointers to the pin port - typedef'd here from the Pin definition because on avr these // are pointers to 8 bit values, while on arm they are 32 bit @@ -113,10 +113,16 @@ class AVRSoftwareSPIOutput { public: // We want to make sure that the clock pulse is held high for a nininum of 35ns. - #define MIN_DELAY (NS(35) - 3) +#if defined(FASTLED_TEENSY4) + #define DELAY_NS (1000 / (SPI_SPEED/1000000)) + #define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0); + #define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0); +#else + #define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1) - #define CLOCK_HI_DELAY delaycycles(); delaycycles<(((SPI_SPEED-6) / 2) - MIN_DELAY)>(); - #define CLOCK_LO_DELAY delaycycles<(((SPI_SPEED-6) / 4))>(); + #define CLOCK_HI_DELAY do { delaycycles(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0); + #define CLOCK_LO_DELAY do { delaycycles<((SPI_SPEED > 10) ? ((SPI_SPEED-6) / 2) : (SPI_SPEED))>(); } while(0); +#endif // write the BIT'th bit out via spi, setting the data pin then strobing the clcok template __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) { @@ -126,8 +132,8 @@ class AVRSoftwareSPIOutput { #ifdef ESP32 // try to ensure we never have adjacent write opcodes to the same register FastPin::lo(); - FastPin::hi(); CLOCK_HI_DELAY; - FastPin::toggle(); CLOCK_LO_DELAY; + FastPin::hi(); CLOCK_HI_DELAY; + FastPin::toggle(); CLOCK_LO_DELAY; #else FastPin::hi(); CLOCK_HI_DELAY; FastPin::lo(); CLOCK_LO_DELAY; @@ -137,7 +143,7 @@ class AVRSoftwareSPIOutput { FastPin::hi(); CLOCK_HI_DELAY; #ifdef ESP32 // try to ensure we never have adjacent write opcodes to the same register - FastPin::toggle(); CLOCK_HI_DELAY; + FastPin::toggle(); CLOCK_HI_DELAY; #else FastPin::lo(); CLOCK_LO_DELAY; #endif @@ -197,6 +203,7 @@ class AVRSoftwareSPIOutput { } #endif } + public: // select the SPI output (TODO: research whether this really means hi or lo. Alt TODO: move select responsibility out of the SPI classes diff --git a/fastspi_dma.h b/src/fastspi_dma.h similarity index 100% rename from fastspi_dma.h rename to src/fastspi_dma.h diff --git a/fastspi_nop.h b/src/fastspi_nop.h similarity index 96% rename from fastspi_nop.h rename to src/fastspi_nop.h index 5c5da010a8..1dcd2961e0 100644 --- a/fastspi_nop.h +++ b/src/fastspi_nop.h @@ -10,7 +10,7 @@ FASTLED_NAMESPACE_BEGIN /// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should /// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the /// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead) -template +template class NOPSPIOutput { Selectable *m_pSelect; diff --git a/fastspi_ref.h b/src/fastspi_ref.h similarity index 97% rename from fastspi_ref.h rename to src/fastspi_ref.h index f68e63ef46..a12a962ae8 100644 --- a/fastspi_ref.h +++ b/src/fastspi_ref.h @@ -8,9 +8,10 @@ FASTLED_NAMESPACE_BEGIN // A skeletal implementation of hardware SPI support. Fill in the necessary code for init, waiting, and writing. The rest of // the method implementations should provide a starting point, even if not hte most efficient to start with -template +template class REFHardwareSPIOutput { Selectable *m_pSelect; + public: SAMHardwareSPIOutput() { m_pSelect = NULL; } SAMHArdwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } diff --git a/fastspi_types.h b/src/fastspi_types.h similarity index 77% rename from fastspi_types.h rename to src/fastspi_types.h index 5510bba80b..ea7d46ce39 100644 --- a/fastspi_types.h +++ b/src/fastspi_types.h @@ -19,9 +19,9 @@ FASTLED_NAMESPACE_BEGIN /// TODO: Convinience macro for building these class DATA_NOP { public: - static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data) { return data; } - static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data, register uint8_t scale) { return scale8(data, scale); } - static __attribute__((always_inline)) inline void postBlock(int /* len */) { } + static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data) { return data; } + static __attribute__((always_inline)) inline uint8_t adjust(register uint8_t data, register uint8_t scale) { return scale8(data, scale); } + static __attribute__((always_inline)) inline void postBlock(int /* len */) { } }; #define FLAG_START_BIT 0x80 diff --git a/hsv2rgb.cpp b/src/hsv2rgb.cpp similarity index 99% rename from hsv2rgb.cpp rename to src/hsv2rgb.cpp index cdb576bcc6..1fb8d56b77 100644 --- a/hsv2rgb.cpp +++ b/src/hsv2rgb.cpp @@ -496,19 +496,19 @@ void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) void hsv2rgb_raw(const struct CHSV * phsv, struct CRGB * prgb, int numLeds) { - for(int i = 0; i < numLeds; i++) { + for(int i = 0; i < numLeds; ++i) { hsv2rgb_raw(phsv[i], prgb[i]); } } void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) { - for(int i = 0; i < numLeds; i++) { + for(int i = 0; i < numLeds; ++i) { hsv2rgb_rainbow(phsv[i], prgb[i]); } } void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) { - for(int i = 0; i < numLeds; i++) { + for(int i = 0; i < numLeds; ++i) { hsv2rgb_spectrum(phsv[i], prgb[i]); } } diff --git a/hsv2rgb.h b/src/hsv2rgb.h similarity index 100% rename from hsv2rgb.h rename to src/hsv2rgb.h diff --git a/led_sysdefs.h b/src/led_sysdefs.h similarity index 60% rename from led_sysdefs.h rename to src/led_sysdefs.h index 13fbf4c32a..8ee568bfca 100644 --- a/led_sysdefs.h +++ b/src/led_sysdefs.h @@ -7,6 +7,8 @@ #if defined(NRF51) || defined(__RFduino__) || defined (__Simblee__) #include "platforms/arm/nrf51/led_sysdefs_arm_nrf51.h" +#elif defined(NRF52_SERIES) +#include "platforms/arm/nrf52/led_sysdefs_arm_nrf52.h" #elif defined(__MK20DX128__) || defined(__MK20DX256__) // Include k20/T3 headers #include "platforms/arm/k20/led_sysdefs_arm_k20.h" @@ -16,20 +18,39 @@ #elif defined(__MKL26Z64__) // Include kl26/T-LC headers #include "platforms/arm/kl26/led_sysdefs_arm_kl26.h" +#elif defined(__IMXRT1062__) +// teensy4 +#include "platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h" #elif defined(__SAM3X8E__) // Include sam/due headers #include "platforms/arm/sam/led_sysdefs_arm_sam.h" #elif defined(STM32F10X_MD) || defined(__STM32F1__) #include "platforms/arm/stm32/led_sysdefs_arm_stm32.h" -#elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) || defined(__SAMD51G19A__) +#elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) || defined(__SAMD51G19A__) || defined(__SAMD51J19A__) #include "platforms/arm/d21/led_sysdefs_arm_d21.h" #elif defined(ESP8266) #include "platforms/esp/8266/led_sysdefs_esp8266.h" #elif defined(ESP32) #include "platforms/esp/32/led_sysdefs_esp32.h" -#else +#elif defined(__AVR__) || defined(__AVR_ATmega4809__) // AVR platforms #include "platforms/avr/led_sysdefs_avr.h" +#elif defined(ARDUINO_ARCH_APOLLO3) +// Apollo3 platforms (e.g. the Ambiq Micro Apollo3 Blue as used by the SparkFun Artemis platforms) +#include "platforms/apollo3/led_sysdefs_apollo3.h" +#else +// +// We got here because we don't recognize the platform that you're +// trying to compile for: it's not AVR, or an ESP or ARM that we recognize. +// +// If you're reading this because you got the error below, +// and if this new platform is just a minor variant of an +// existing supported ARM platform, you may be able to add +// a new 'defined(XXX)' selector in the apporpriate code above. +// +// If this platform is a new microcontroller, see "PORTING.md". +// +#error "This platform isn't recognized by FastLED... yet. See comments in FastLED/led_sysdefs.h for options." #endif #ifndef FASTLED_NAMESPACE_BEGIN diff --git a/lib8tion.cpp b/src/lib8tion.cpp similarity index 98% rename from lib8tion.cpp rename to src/lib8tion.cpp index 1306e5c0cb..ecb051d80a 100644 --- a/lib8tion.cpp +++ b/src/lib8tion.cpp @@ -142,7 +142,7 @@ void test1abs( int8_t i) void testabs() { delay(5000); - for( int8_t q = -128; q != 127; q++) { + for( int8_t q = -128; q != 127; ++q) { test1abs(q); } for(;;){}; @@ -225,7 +225,7 @@ void testnscale8x3() { delay(5000); byte r, g, b, sc; - for( byte z = 0; z < 10; z++) { + for( byte z = 0; z < 10; ++z) { r = random8(); g = random8(); b = random8(); sc = random8(); Serial.print("nscale8x3_video( "); diff --git a/lib8tion.h b/src/lib8tion.h similarity index 95% rename from lib8tion.h rename to src/lib8tion.h index 24c5d0abae..0cc3baa4fc 100644 --- a/lib8tion.h +++ b/src/lib8tion.h @@ -181,7 +181,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #if !defined(__AVR__) #include // for memmove, memcpy, and memset if not defined here -#endif +#endif // end of !defined(__AVR__) #if defined(__arm__) @@ -195,7 +195,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun // Generic ARM #define QADD8_C 1 #define QADD7_C 1 -#endif +#endif // end of defined(FASTLED_TEENSY3) #define QSUB8_C 1 #define SCALE8_C 1 @@ -213,6 +213,30 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define AVG15_C 1 #define BLEND8_C 1 +// end of #if defined(__arm__) + +#elif defined(ARDUINO_ARCH_APOLLO3) + +// Default to using the standard C functions for now +#define QADD8_C 1 +#define QADD7_C 1 +#define QSUB8_C 1 +#define SCALE8_C 1 +#define SCALE16BY8_C 1 +#define SCALE16_C 1 +#define ABS8_C 1 +#define MUL8_C 1 +#define QMUL8_C 1 +#define ADD8_C 1 +#define SUB8_C 1 +#define EASE8_C 1 +#define AVG8_C 1 +#define AVG7_C 1 +#define AVG16_C 1 +#define AVG15_C 1 +#define BLEND8_C 1 + +// end of #elif defined(ARDUINO_ARCH_APOLLO3) #elif defined(__AVR__) @@ -274,7 +298,9 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun #define QMUL8_AVRASM 0 #define EASE8_AVRASM 0 #define BLEND8_AVRASM 0 -#endif +#endif // end of !defined(LIB8_ATTINY) + +// end of #elif defined(__AVR__) #else @@ -799,17 +825,20 @@ LIB8STATIC uint8_t squarewave8( uint8_t in, uint8_t pulsewidth=128) /// Template class for represneting fractional ints. template class q { - T i:I; - T f:F; + T i:I; + T f:F; public: - q(float fx) { i = fx; f = (fx-i) * (1<>F); } - uint16_t operator*(uint16_t v) { return (v*i) + ((v*f)>>F); } - int32_t operator*(int32_t v) { return (v*i) + ((v*f)>>F); } - int16_t operator*(int16_t v) { return (v*i) + ((v*f)>>F); } + q(float fx) { i = fx; f = (fx-i) * (1<>F); } + uint16_t operator*(uint16_t v) { return (v*i) + ((v*f)>>F); } + int32_t operator*(int32_t v) { return (v*i) + ((v*f)>>F); } + int16_t operator*(int16_t v) { return (v*i) + ((v*f)>>F); } #ifdef FASTLED_ARM - int operator*(int v) { return (v*i) + ((v*f)>>F); } + int operator*(int v) { return (v*i) + ((v*f)>>F); } +#endif +#ifdef FASTLED_APOLLO3 + int operator*(int v) { return (v*i) + ((v*f)>>F); } #endif }; @@ -820,6 +849,9 @@ template static int16_t operator*(int16_t v, q & q #ifdef FASTLED_ARM template static int operator*(int v, q & q) { return q * v; } #endif +#ifdef FASTLED_APOLLO3 +template static int operator*(int v, q & q) { return q * v; } +#endif /// A 4.4 integer (4 bits integer, 4 bits fraction) typedef q q44; @@ -1016,17 +1048,17 @@ LIB8STATIC uint16_t div1024_32_16( uint32_t in32) uint16_t out16; #if defined(__AVR__) asm volatile ( - " lsr %D[in] \n\t" - " ror %C[in] \n\t" - " ror %B[in] \n\t" - " lsr %D[in] \n\t" - " ror %C[in] \n\t" - " ror %B[in] \n\t" - " mov %B[out],%C[in] \n\t" - " mov %A[out],%B[in] \n\t" - : [in] "+r" (in32), - [out] "=r" (out16) - ); + " lsr %D[in] \n\t" + " ror %C[in] \n\t" + " ror %B[in] \n\t" + " lsr %D[in] \n\t" + " ror %C[in] \n\t" + " ror %B[in] \n\t" + " mov %B[out],%C[in] \n\t" + " mov %A[out],%B[in] \n\t" + : [in] "+r" (in32), + [out] "=r" (out16) + ); #else out16 = (in32 >> 10) & 0xFFFF; #endif diff --git a/lib8tion/math8.h b/src/lib8tion/math8.h similarity index 68% rename from lib8tion/math8.h rename to src/lib8tion/math8.h index 4dab820ffc..a83b1ad253 100644 --- a/lib8tion/math8.h +++ b/src/lib8tion/math8.h @@ -28,18 +28,19 @@ LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j) return t; #elif QADD8_AVRASM == 1 asm volatile( - /* First, add j to i, conditioning the C flag */ - "add %0, %1 \n\t" - - /* Now test the C flag. - If C is clear, we branch around a load of 0xFF into i. - If C is set, we go ahead and load 0xFF into i. - */ - "brcc L_%= \n\t" - "ldi %0, 0xFF \n\t" - "L_%=: " - : "+a" (i) - : "a" (j) ); + /* First, add j to i, conditioning the C flag */ + "add %0, %1 \n\t" + + /* Now test the C flag. + If C is clear, we branch around a load of 0xFF into i. + If C is set, we go ahead and load 0xFF into i. + */ + "brcc L_%= \n\t" + "ldi %0, 0xFF \n\t" + "L_%=: " + : "+a" (i) + : "a" (j) + ); return i; #elif QADD8_ARM_DSP_ASM == 1 asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j)); @@ -61,19 +62,19 @@ LIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j) return t; #elif QADD7_AVRASM == 1 asm volatile( - /* First, add j to i, conditioning the V flag */ - "add %0, %1 \n\t" - - /* Now test the V flag. - If V is clear, we branch around a load of 0x7F into i. - If V is set, we go ahead and load 0x7F into i. - */ - "brvc L_%= \n\t" - "ldi %0, 0x7F \n\t" - "L_%=: " - : "+a" (i) - : "a" (j) ); - + /* First, add j to i, conditioning the V flag */ + "add %0, %1 \n\t" + + /* Now test the V flag. + If V is clear, we branch around a load of 0x7F into i. + If V is set, we go ahead and load 0x7F into i. + */ + "brvc L_%= \n\t" + "ldi %0, 0x7F \n\t" + "L_%=: " + : "+a" (i) + : "a" (j) + ); return i; #elif QADD7_ARM_DSP_ASM == 1 asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j)); @@ -94,19 +95,19 @@ LIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j) #elif QSUB8_AVRASM == 1 asm volatile( - /* First, subtract j from i, conditioning the C flag */ - "sub %0, %1 \n\t" - - /* Now test the C flag. - If C is clear, we branch around a load of 0x00 into i. - If C is set, we go ahead and load 0x00 into i. - */ - "brcc L_%= \n\t" - "ldi %0, 0x00 \n\t" - "L_%=: " - : "+a" (i) - : "a" (j) ); - + /* First, subtract j from i, conditioning the C flag */ + "sub %0, %1 \n\t" + + /* Now test the C flag. + If C is clear, we branch around a load of 0x00 into i. + If C is set, we go ahead and load 0x00 into i. + */ + "brcc L_%= \n\t" + "ldi %0, 0x00 \n\t" + "L_%=: " + : "+a" (i) + : "a" (j) + ); return i; #else #error "No implementation for qsub8 available." @@ -136,11 +137,12 @@ LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j) return t; #elif ADD8_AVRASM == 1 // Add i(one byte) to j(two bytes) - asm volatile( "add %A[j], %[i] \n\t" - "adc %B[j], __zero_reg__ \n\t" - : [j] "+a" (j) - : [i] "a" (i) - ); + asm volatile( + "add %A[j], %[i] \n\t" + "adc %B[j], __zero_reg__ \n\t" + : [j] "+a" (j) + : [i] "a" (i) + ); return i; #else #error "No implementation for add8to16 available." @@ -172,12 +174,13 @@ LIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j) return (i + j) >> 1; #elif AVG8_AVRASM == 1 asm volatile( - /* First, add j to i, 9th bit overflows into C flag */ - "add %0, %1 \n\t" - /* Divide by two, moving C flag into high 8th bit */ - "ror %0 \n\t" - : "+a" (i) - : "a" (j) ); + /* First, add j to i, 9th bit overflows into C flag */ + "add %0, %1 \n\t" + /* Divide by two, moving C flag into high 8th bit */ + "ror %0 \n\t" + : "+a" (i) + : "a" (j) + ); return i; #else #error "No implementation for avg8 available." @@ -193,16 +196,17 @@ LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j) return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1; #elif AVG16_AVRASM == 1 asm volatile( - /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */ - "add %A[i], %A[j] \n\t" - /* Now, add C + jHi to iHi, 17th bit overflows into C flag */ - "adc %B[i], %B[j] \n\t" - /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */ - "ror %B[i] \n\t" - /* Divide iLo by two, moving C flag into high 8th bit */ - "ror %A[i] \n\t" - : [i] "+a" (i) - : [j] "a" (j) ); + /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */ + "add %A[i], %A[j] \n\t" + /* Now, add C + jHi to iHi, 17th bit overflows into C flag */ + "adc %B[i], %B[j] \n\t" + /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */ + "ror %B[i] \n\t" + /* Divide iLo by two, moving C flag into high 8th bit */ + "ror %A[i] \n\t" + : [i] "+a" (i) + : [j] "a" (j) + ); return i; #else #error "No implementation for avg16 available." @@ -220,11 +224,12 @@ LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j) return ((i + j) >> 1) + (i & 0x1); #elif AVG7_AVRASM == 1 asm volatile( - "asr %1 \n\t" - "asr %0 \n\t" - "adc %0, %1 \n\t" - : "+a" (i) - : "a" (j) ); + "asr %1 \n\t" + "asr %0 \n\t" + "adc %0, %1 \n\t" + : "+a" (i) + : "a" (j) + ); return i; #else #error "No implementation for avg7 available." @@ -241,17 +246,18 @@ LIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j) return ((int32_t)((int32_t)(i) + (int32_t)(j)) >> 1) + (i & 0x1); #elif AVG15_AVRASM == 1 asm volatile( - /* first divide j by 2, throwing away lowest bit */ - "asr %B[j] \n\t" - "ror %A[j] \n\t" - /* now divide i by 2, with lowest bit going into C */ - "asr %B[i] \n\t" - "ror %A[i] \n\t" - /* add j + C to i */ - "adc %A[i], %A[j] \n\t" - "adc %B[i], %B[j] \n\t" - : [i] "+a" (i) - : [j] "a" (j) ); + /* first divide j by 2, throwing away lowest bit */ + "asr %B[j] \n\t" + "ror %A[j] \n\t" + /* now divide i by 2, with lowest bit going into C */ + "asr %B[i] \n\t" + "ror %A[i] \n\t" + /* add j + C to i */ + "adc %A[i], %A[j] \n\t" + "adc %B[i], %B[j] \n\t" + : [i] "+a" (i) + : [j] "a" (j) + ); return i; #else #error "No implementation for avg15 available." @@ -271,12 +277,12 @@ LIB8STATIC_ALWAYS_INLINE uint8_t mod8( uint8_t a, uint8_t m) { #if defined(__AVR__) asm volatile ( - "L_%=: sub %[a],%[m] \n\t" - " brcc L_%= \n\t" - " add %[a],%[m] \n\t" - : [a] "+r" (a) - : [m] "r" (m) - ); + "L_%=: sub %[a],%[m] \n\t" + " brcc L_%= \n\t" + " add %[a],%[m] \n\t" + : [a] "+r" (a) + : [m] "r" (m) + ); #else while( a >= m) a -= m; #endif @@ -298,13 +304,13 @@ LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m) { #if defined(__AVR__) asm volatile ( - " add %[a],%[b] \n\t" - "L_%=: sub %[a],%[m] \n\t" - " brcc L_%= \n\t" - " add %[a],%[m] \n\t" - : [a] "+r" (a) - : [b] "r" (b), [m] "r" (m) - ); + " add %[a],%[b] \n\t" + "L_%=: sub %[a],%[m] \n\t" + " brcc L_%= \n\t" + " add %[a],%[m] \n\t" + : [a] "+r" (a) + : [b] "r" (b), [m] "r" (m) + ); #else a += b; while( a >= m) a -= m; @@ -327,13 +333,13 @@ LIB8STATIC uint8_t submod8( uint8_t a, uint8_t b, uint8_t m) { #if defined(__AVR__) asm volatile ( - " sub %[a],%[b] \n\t" - "L_%=: sub %[a],%[m] \n\t" - " brcc L_%= \n\t" - " add %[a],%[m] \n\t" - : [a] "+r" (a) - : [b] "r" (b), [m] "r" (m) - ); + " sub %[a],%[b] \n\t" + "L_%=: sub %[a],%[m] \n\t" + " brcc L_%= \n\t" + " add %[a],%[m] \n\t" + : [a] "+r" (a) + : [b] "r" (b), [m] "r" (m) + ); #else a -= b; while( a >= m) a -= m; @@ -348,16 +354,16 @@ LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j) return ((int)i * (int)(j) ) & 0xFF; #elif MUL8_AVRASM == 1 asm volatile( - /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */ - "mul %0, %1 \n\t" - /* Extract the LOW 8-bits (r0) */ - "mov %0, r0 \n\t" - /* Restore r1 to "0"; it's expected to always be that */ - "clr __zero_reg__ \n\t" - : "+a" (i) - : "a" (j) - : "r0", "r1"); - + /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */ + "mul %0, %1 \n\t" + /* Extract the LOW 8-bits (r0) */ + "mov %0, r0 \n\t" + /* Restore r1 to "0"; it's expected to always be that */ + "clr __zero_reg__ \n\t" + : "+a" (i) + : "a" (j) + : "r0", "r1" + ); return i; #else #error "No implementation for mul8 available." @@ -375,24 +381,24 @@ LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j) return p; #elif QMUL8_AVRASM == 1 asm volatile( - /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */ - " mul %0, %1 \n\t" - /* If high byte of result is zero, all is well. */ - " tst r1 \n\t" - " breq Lnospill_%= \n\t" - /* If high byte of result > 0, saturate low byte to 0xFF */ - " ldi %0,0xFF \n\t" - " rjmp Ldone_%= \n\t" - "Lnospill_%=: \n\t" - /* Extract the LOW 8-bits (r0) */ - " mov %0, r0 \n\t" - "Ldone_%=: \n\t" - /* Restore r1 to "0"; it's expected to always be that */ - " clr __zero_reg__ \n\t" - : "+a" (i) - : "a" (j) - : "r0", "r1"); - + /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */ + " mul %0, %1 \n\t" + /* If high byte of result is zero, all is well. */ + " tst r1 \n\t" + " breq Lnospill_%= \n\t" + /* If high byte of result > 0, saturate low byte to 0xFF */ + " ldi %0,0xFF \n\t" + " rjmp Ldone_%= \n\t" + "Lnospill_%=: \n\t" + /* Extract the LOW 8-bits (r0) */ + " mov %0, r0 \n\t" + "Ldone_%=: \n\t" + /* Restore r1 to "0"; it's expected to always be that */ + " clr __zero_reg__ \n\t" + : "+a" (i) + : "a" (j) + : "r0", "r1" + ); return i; #else #error "No implementation for qmul8 available." @@ -407,16 +413,15 @@ LIB8STATIC_ALWAYS_INLINE int8_t abs8( int8_t i) if( i < 0) i = -i; return i; #elif ABS8_AVRASM == 1 - - asm volatile( - /* First, check the high bit, and prepare to skip if it's clear */ - "sbrc %0, 7 \n" + /* First, check the high bit, and prepare to skip if it's clear */ + "sbrc %0, 7 \n" - /* Negate the value */ - "neg %0 \n" + /* Negate the value */ + "neg %0 \n" - : "+r" (i) : "r" (i) ); + : "+r" (i) : "r" (i) + ); return i; #else #error "No implementation for abs8 available." diff --git a/lib8tion/random8.h b/src/lib8tion/random8.h similarity index 88% rename from lib8tion/random8.h rename to src/lib8tion/random8.h index ba60cf5755..d834abd51d 100644 --- a/lib8tion/random8.h +++ b/src/lib8tion/random8.h @@ -12,13 +12,19 @@ #define FASTLED_RAND16_2053 ((uint16_t)(2053)) #define FASTLED_RAND16_13849 ((uint16_t)(13849)) +#if defined(LIB8_ATTINY) +#define APPLY_FASTLED_RAND16_2053(x) (x << 11) + (x << 2) + x +#else +#define APPLY_FASTLED_RAND16_2053(x) (x * FASTLED_RAND16_2053) +#endif + /// random number seed extern uint16_t rand16seed;// = RAND16_SEED; /// Generate an 8-bit random number LIB8STATIC uint8_t random8() { - rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849; + rand16seed = APPLY_FASTLED_RAND16_2053(rand16seed) + FASTLED_RAND16_13849; // return the sum of the high and low bytes, for better // mixing and non-sequential correlation return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) + @@ -28,7 +34,7 @@ LIB8STATIC uint8_t random8() /// Generate a 16 bit random number LIB8STATIC uint16_t random16() { - rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849; + rand16seed = APPLY_FASTLED_RAND16_2053(rand16seed) + FASTLED_RAND16_13849; return rand16seed; } diff --git a/lib8tion/scale8.h b/src/lib8tion/scale8.h similarity index 62% rename from lib8tion/scale8.h rename to src/lib8tion/scale8.h index 56392258d5..6324475b66 100644 --- a/lib8tion/scale8.h +++ b/src/lib8tion/scale8.h @@ -55,7 +55,7 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale) : [work] "+r" (work), [cnt] "+r" (cnt) : [scale] "r" (scale), [i] "r" (i) : - ); + ); return work; #else asm volatile( @@ -69,18 +69,18 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale) // walk and chew gum at the same time "adc %0, r1 \n\t" #else - /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ - "mul %0, %1 \n\t" - /* Move the high 8-bits of the product (r1) back to i */ - "mov %0, r1 \n\t" - /* Restore r1 to "0"; it's expected to always be that */ + /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ + "mul %0, %1 \n\t" + /* Move the high 8-bits of the product (r1) back to i */ + "mov %0, r1 \n\t" + /* Restore r1 to "0"; it's expected to always be that */ #endif - "clr __zero_reg__ \n\t" - - : "+a" (i) /* writes to i */ - : "a" (scale) /* uses scale */ - : "r0", "r1" /* clobbers r0, r1 */ ); + "clr __zero_reg__ \n\t" + : "+a" (i) /* writes to i */ + : "a" (scale) /* uses scale */ + : "r0", "r1" /* clobbers r0, r1 */ + ); /* Return the result */ return i; #endif @@ -115,8 +115,8 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale) "L_%=: \n\t" : [j] "+a" (j) : [i] "a" (i), [scale] "a" (scale) - : "r0", "r1"); - + : "r0", "r1" + ); return j; // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; // asm volatile( @@ -127,11 +127,9 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale) // " add %0, %2 \n" // " clr __zero_reg__ \n" // "L_%=: \n" - // : "+a" (i) // : "a" (scale), "a" (nonzeroscale) // : "r0", "r1"); - // // Return the result // return i; #else @@ -153,28 +151,27 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scal #endif #elif SCALE8_AVRASM == 1 asm volatile( - #if (FASTLED_SCALE8_FIXED==1) - // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 - "mul %0, %1 \n\t" - // Add i to r0, possibly setting the carry flag - "add r0, %0 \n\t" - // load the immediate 0 into i (note, this does _not_ touch any flags) - "ldi %0, 0x00 \n\t" - // walk and chew gum at the same time - "adc %0, r1 \n\t" - #else - /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ - "mul %0, %1 \n\t" - /* Move the high 8-bits of the product (r1) back to i */ - "mov %0, r1 \n\t" - #endif - /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */ - /* "clr __zero_reg__ \n\t" */ - - : "+a" (i) /* writes to i */ - : "a" (scale) /* uses scale */ - : "r0", "r1" /* clobbers r0, r1 */ ); - +#if (FASTLED_SCALE8_FIXED==1) + // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 + "mul %0, %1 \n\t" + // Add i to r0, possibly setting the carry flag + "add r0, %0 \n\t" + // load the immediate 0 into i (note, this does _not_ touch any flags) + "ldi %0, 0x00 \n\t" + // walk and chew gum at the same time + "adc %0, r1 \n\t" +#else + /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ + "mul %0, %1 \n\t" + /* Move the high 8-bits of the product (r1) back to i */ + "mov %0, r1 \n\t" +#endif + /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */ + /* "clr __zero_reg__ \n\t" */ + : "+a" (i) /* writes to i */ + : "a" (scale) /* uses scale */ + : "r0", "r1" /* clobbers r0, r1 */ + ); // Return the result return i; #else @@ -197,27 +194,28 @@ LIB8STATIC_ALWAYS_INLINE void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale #endif #elif SCALE8_AVRASM == 1 asm volatile( - #if (FASTLED_SCALE8_FIXED==1) - // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 - "mul %0, %1 \n\t" - // Add i to r0, possibly setting the carry flag - "add r0, %0 \n\t" - // load the immediate 0 into i (note, this does _not_ touch any flags) - "ldi %0, 0x00 \n\t" - // walk and chew gum at the same time - "adc %0, r1 \n\t" - #else - /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ - "mul %0, %1 \n\t" - /* Move the high 8-bits of the product (r1) back to i */ - "mov %0, r1 \n\t" - #endif - /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */ - /* "clr __zero_reg__ \n\t" */ - - : "+a" (i) /* writes to i */ - : "a" (scale) /* uses scale */ - : "r0", "r1" /* clobbers r0, r1 */ ); +#if (FASTLED_SCALE8_FIXED==1) + // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 + "mul %0, %1 \n\t" + // Add i to r0, possibly setting the carry flag + "add r0, %0 \n\t" + // load the immediate 0 into i (note, this does _not_ touch any flags) + "ldi %0, 0x00 \n\t" + // walk and chew gum at the same time + "adc %0, r1 \n\t" +#else + /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ + "mul %0, %1 \n\t" + /* Move the high 8-bits of the product (r1) back to i */ + "mov %0, r1 \n\t" +#endif + /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */ + /* "clr __zero_reg__ \n\t" */ + + : "+a" (i) /* writes to i */ + : "a" (scale) /* uses scale */ + : "r0", "r1" /* clobbers r0, r1 */ + ); #else #error "No implementation for nscale8_LEAVING_R1_DIRTY available." #endif @@ -246,8 +244,8 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract "L_%=: \n\t" : [j] "+a" (j) : [i] "a" (i), [scale] "a" (scale) - : "r0", "r1"); - + : "r0", "r1" + ); return j; // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; // asm volatile( @@ -258,11 +256,9 @@ LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract // " add %0, %2 \n" // " clr __zero_reg__ \n" // "L_%=: \n" - // : "+a" (i) // : "a" (scale), "a" (nonzeroscale) // : "r0", "r1"); - // // Return the result // return i; #else @@ -289,7 +285,8 @@ LIB8STATIC_ALWAYS_INLINE void nscale8_video_LEAVING_R1_DIRTY( uint8_t & i, fract "L_%=: \n\t" : [i] "+a" (i) : [scale] "a" (scale) - : "r0", "r1"); + : "r0", "r1" + ); #else #error "No implementation for scale8_video_LEAVING_R1_DIRTY available." #endif @@ -427,50 +424,50 @@ LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale ) #if FASTLED_SCALE8_FIXED == 1 uint16_t result = 0; asm volatile( - // result.A = HighByte( (i.A x scale) + i.A ) - " mul %A[i], %[scale] \n\t" - " add r0, %A[i] \n\t" - // " adc r1, [zero] \n\t" - // " mov %A[result], r1 \n\t" - " adc %A[result], r1 \n\t" - - // result.A-B += i.B x scale - " mul %B[i], %[scale] \n\t" - " add %A[result], r0 \n\t" - " adc %B[result], r1 \n\t" - - // cleanup r1 - " clr __zero_reg__ \n\t" - - // result.A-B += i.B - " add %A[result], %B[i] \n\t" - " adc %B[result], __zero_reg__ \n\t" - - : [result] "+r" (result) - : [i] "r" (i), [scale] "r" (scale) - : "r0", "r1" - ); + // result.A = HighByte( (i.A x scale) + i.A ) + " mul %A[i], %[scale] \n\t" + " add r0, %A[i] \n\t" + // " adc r1, [zero] \n\t" + // " mov %A[result], r1 \n\t" + " adc %A[result], r1 \n\t" + + // result.A-B += i.B x scale + " mul %B[i], %[scale] \n\t" + " add %A[result], r0 \n\t" + " adc %B[result], r1 \n\t" + + // cleanup r1 + " clr __zero_reg__ \n\t" + + // result.A-B += i.B + " add %A[result], %B[i] \n\t" + " adc %B[result], __zero_reg__ \n\t" + + : [result] "+r" (result) + : [i] "r" (i), [scale] "r" (scale) + : "r0", "r1" + ); return result; #else uint16_t result = 0; asm volatile( - // result.A = HighByte(i.A x j ) - " mul %A[i], %[scale] \n\t" - " mov %A[result], r1 \n\t" - //" clr %B[result] \n\t" - - // result.A-B += i.B x j - " mul %B[i], %[scale] \n\t" - " add %A[result], r0 \n\t" - " adc %B[result], r1 \n\t" - - // cleanup r1 - " clr __zero_reg__ \n\t" - - : [result] "+r" (result) - : [i] "r" (i), [scale] "r" (scale) - : "r0", "r1" - ); + // result.A = HighByte(i.A x j ) + " mul %A[i], %[scale] \n\t" + " mov %A[result], r1 \n\t" + //" clr %B[result] \n\t" + + // result.A-B += i.B x j + " mul %B[i], %[scale] \n\t" + " add %A[result], r0 \n\t" + " adc %B[result], r1 \n\t" + + // cleanup r1 + " clr __zero_reg__ \n\t" + + : [result] "+r" (result) + : [i] "r" (i), [scale] "r" (scale) + : "r0", "r1" + ); return result; #endif #else @@ -503,137 +500,137 @@ LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale ) // will be zero, which is not what we want. uint32_t result; asm volatile( - // result.A-B = i.A x scale.A - " mul %A[i], %A[scale] \n\t" - // save results... - // basic idea: - //" mov %A[result], r0 \n\t" - //" mov %B[result], r1 \n\t" - // which can be written as... - " movw %A[result], r0 \n\t" - // Because we're going to add i.A-B to - // result.A-D, we DO need to keep both - // the r0 and r1 portions of the product - // UNlike in the 'unfixed scale8' version. - // So the movw here is needed. - : [result] "=r" (result) - : [i] "r" (i), - [scale] "r" (scale) - : "r0", "r1" - ); - - asm volatile( - // result.C-D = i.B x scale.B - " mul %B[i], %B[scale] \n\t" - //" mov %C[result], r0 \n\t" - //" mov %D[result], r1 \n\t" - " movw %C[result], r0 \n\t" - : [result] "+r" (result) - : [i] "r" (i), - [scale] "r" (scale) - : "r0", "r1" - ); + // result.A-B = i.A x scale.A + " mul %A[i], %A[scale] \n\t" + // save results... + // basic idea: + //" mov %A[result], r0 \n\t" + //" mov %B[result], r1 \n\t" + // which can be written as... + " movw %A[result], r0 \n\t" + // Because we're going to add i.A-B to + // result.A-D, we DO need to keep both + // the r0 and r1 portions of the product + // UNlike in the 'unfixed scale8' version. + // So the movw here is needed. + : [result] "=r" (result) + : [i] "r" (i), + [scale] "r" (scale) + : "r0", "r1" + ); - const uint8_t zero = 0; asm volatile( - // result.B-D += i.B x scale.A - " mul %B[i], %A[scale] \n\t" - - " add %B[result], r0 \n\t" - " adc %C[result], r1 \n\t" - " adc %D[result], %[zero] \n\t" - - // result.B-D += i.A x scale.B - " mul %A[i], %B[scale] \n\t" - - " add %B[result], r0 \n\t" - " adc %C[result], r1 \n\t" - " adc %D[result], %[zero] \n\t" - - // cleanup r1 - " clr r1 \n\t" - - : [result] "+r" (result) - : [i] "r" (i), - [scale] "r" (scale), - [zero] "r" (zero) - : "r0", "r1" - ); + // result.C-D = i.B x scale.B + " mul %B[i], %B[scale] \n\t" + //" mov %C[result], r0 \n\t" + //" mov %D[result], r1 \n\t" + " movw %C[result], r0 \n\t" + : [result] "+r" (result) + : [i] "r" (i), + [scale] "r" (scale) + : "r0", "r1" +); + +const uint8_t zero = 0; +asm volatile( + // result.B-D += i.B x scale.A + " mul %B[i], %A[scale] \n\t" + + " add %B[result], r0 \n\t" + " adc %C[result], r1 \n\t" + " adc %D[result], %[zero] \n\t" + + // result.B-D += i.A x scale.B + " mul %A[i], %B[scale] \n\t" + + " add %B[result], r0 \n\t" + " adc %C[result], r1 \n\t" + " adc %D[result], %[zero] \n\t" + + // cleanup r1 + " clr r1 \n\t" + + : [result] "+r" (result) + : [i] "r" (i), + [scale] "r" (scale), + [zero] "r" (zero) + : "r0", "r1" + ); asm volatile( - // result.A-D += i.A-B - " add %A[result], %A[i] \n\t" - " adc %B[result], %B[i] \n\t" - " adc %C[result], %[zero] \n\t" - " adc %D[result], %[zero] \n\t" - : [result] "+r" (result) - : [i] "r" (i), - [zero] "r" (zero) - ); + // result.A-D += i.A-B + " add %A[result], %A[i] \n\t" + " adc %B[result], %B[i] \n\t" + " adc %C[result], %[zero] \n\t" + " adc %D[result], %[zero] \n\t" + : [result] "+r" (result) + : [i] "r" (i), + [zero] "r" (zero) + ); result = result >> 16; return result; #else uint32_t result; asm volatile( - // result.A-B = i.A x scale.A - " mul %A[i], %A[scale] \n\t" - // save results... - // basic idea: - //" mov %A[result], r0 \n\t" - //" mov %B[result], r1 \n\t" - // which can be written as... - " movw %A[result], r0 \n\t" - // We actually don't need to do anything with r0, - // as result.A is never used again here, so we - // could just move the high byte, but movw is - // one clock cycle, just like mov, so might as - // well, in case we want to use this code for - // a generic 16x16 multiply somewhere. - - : [result] "=r" (result) - : [i] "r" (i), - [scale] "r" (scale) - : "r0", "r1" - ); + // result.A-B = i.A x scale.A + " mul %A[i], %A[scale] \n\t" + // save results... + // basic idea: + //" mov %A[result], r0 \n\t" + //" mov %B[result], r1 \n\t" + // which can be written as... + " movw %A[result], r0 \n\t" + // We actually don't need to do anything with r0, + // as result.A is never used again here, so we + // could just move the high byte, but movw is + // one clock cycle, just like mov, so might as + // well, in case we want to use this code for + // a generic 16x16 multiply somewhere. + + : [result] "=r" (result) + : [i] "r" (i), + [scale] "r" (scale) + : "r0", "r1" + ); asm volatile( - // result.C-D = i.B x scale.B - " mul %B[i], %B[scale] \n\t" - //" mov %C[result], r0 \n\t" - //" mov %D[result], r1 \n\t" - " movw %C[result], r0 \n\t" - : [result] "+r" (result) - : [i] "r" (i), - [scale] "r" (scale) - : "r0", "r1" - ); + // result.C-D = i.B x scale.B + " mul %B[i], %B[scale] \n\t" + //" mov %C[result], r0 \n\t" + //" mov %D[result], r1 \n\t" + " movw %C[result], r0 \n\t" + : [result] "+r" (result) + : [i] "r" (i), + [scale] "r" (scale) + : "r0", "r1" + ); const uint8_t zero = 0; asm volatile( - // result.B-D += i.B x scale.A - " mul %B[i], %A[scale] \n\t" + // result.B-D += i.B x scale.A + " mul %B[i], %A[scale] \n\t" - " add %B[result], r0 \n\t" - " adc %C[result], r1 \n\t" - " adc %D[result], %[zero] \n\t" + " add %B[result], r0 \n\t" + " adc %C[result], r1 \n\t" + " adc %D[result], %[zero] \n\t" - // result.B-D += i.A x scale.B - " mul %A[i], %B[scale] \n\t" + // result.B-D += i.A x scale.B + " mul %A[i], %B[scale] \n\t" - " add %B[result], r0 \n\t" - " adc %C[result], r1 \n\t" - " adc %D[result], %[zero] \n\t" + " add %B[result], r0 \n\t" + " adc %C[result], r1 \n\t" + " adc %D[result], %[zero] \n\t" - // cleanup r1 - " clr r1 \n\t" + // cleanup r1 + " clr r1 \n\t" - : [result] "+r" (result) - : [i] "r" (i), - [scale] "r" (scale), - [zero] "r" (zero) - : "r0", "r1" - ); + : [result] "+r" (result) + : [i] "r" (i), + [scale] "r" (scale), + [zero] "r" (zero) + : "r0", "r1" + ); result = result >> 16; return result; diff --git a/lib8tion/trig8.h b/src/lib8tion/trig8.h similarity index 88% rename from lib8tion/trig8.h rename to src/lib8tion/trig8.h index 4907c6ff30..c5896ef8e9 100644 --- a/lib8tion/trig8.h +++ b/src/lib8tion/trig8.h @@ -161,15 +161,15 @@ LIB8STATIC uint8_t sin8_avr( uint8_t theta) uint8_t offset = theta; asm volatile( - "sbrc %[theta],6 \n\t" - "com %[offset] \n\t" - : [theta] "+r" (theta), [offset] "+r" (offset) - ); + "sbrc %[theta],6 \n\t" + "com %[offset] \n\t" + : [theta] "+r" (theta), [offset] "+r" (offset) + ); offset &= 0x3F; // 0..63 uint8_t secoffset = offset & 0x0F; // 0..15 - if( theta & 0x40) secoffset++; + if( theta & 0x40) ++secoffset; uint8_t m16; uint8_t b; @@ -179,24 +179,24 @@ LIB8STATIC uint8_t sin8_avr( uint8_t theta) const uint8_t* p = b_m16_interleave; p += s2; b = *p; - p++; + ++p; m16 = *p; uint8_t mx; uint8_t xr1; asm volatile( - "mul %[m16],%[secoffset] \n\t" - "mov %[mx],r0 \n\t" - "mov %[xr1],r1 \n\t" - "eor r1, r1 \n\t" - "swap %[mx] \n\t" - "andi %[mx],0x0F \n\t" - "swap %[xr1] \n\t" - "andi %[xr1], 0xF0 \n\t" - "or %[mx], %[xr1] \n\t" - : [mx] "=d" (mx), [xr1] "=d" (xr1) - : [m16] "d" (m16), [secoffset] "d" (secoffset) - ); + "mul %[m16],%[secoffset] \n\t" + "mov %[mx],r0 \n\t" + "mov %[xr1],r1 \n\t" + "eor r1, r1 \n\t" + "swap %[mx] \n\t" + "andi %[mx],0x0F \n\t" + "swap %[xr1] \n\t" + "andi %[xr1], 0xF0 \n\t" + "or %[mx], %[xr1] \n\t" + : [mx] "=d" (mx), [xr1] "=d" (xr1) + : [m16] "d" (m16), [secoffset] "d" (secoffset) + ); int8_t y = mx + b; if( theta & 0x80 ) y = -y; @@ -223,14 +223,14 @@ LIB8STATIC uint8_t sin8_C( uint8_t theta) offset &= 0x3F; // 0..63 uint8_t secoffset = offset & 0x0F; // 0..15 - if( theta & 0x40) secoffset++; + if( theta & 0x40) ++secoffset; uint8_t section = offset >> 4; // 0..3 uint8_t s2 = section * 2; const uint8_t* p = b_m16_interleave; p += s2; uint8_t b = *p; - p++; + ++p; uint8_t m16 = *p; uint8_t mx = (m16 * secoffset) >> 4; diff --git a/noise.cpp b/src/noise.cpp similarity index 54% rename from noise.cpp rename to src/noise.cpp index 7d42d64dce..3a40c476f3 100644 --- a/noise.cpp +++ b/src/noise.cpp @@ -6,21 +6,24 @@ FASTLED_NAMESPACE_BEGIN #define P(x) FL_PGM_READ_BYTE_NEAR(p + x) -FL_PROGMEM static uint8_t const p[] = { 151,160,137,91,90,15, - 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, - 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, - 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, - 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, - 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, - 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, - 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, - 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, - 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, - 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, - 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, - 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151 - }; - +FL_PROGMEM static uint8_t const p[] = { + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, + 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, + 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, + 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, + 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, + 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, + 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, + 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, + 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, + 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, + 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, + 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, + 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, + 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, + 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, + 151}; #if FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW == 1 #define AVG15(U,V) (((U)+(V)) >> 1) @@ -74,55 +77,55 @@ static int16_t inline __attribute__((always_inline)) avg15_inline_avr_mul( int1 #endif static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y, int16_t z) { #if 0 - switch(hash & 0xF) { - case 0: return (( x) + ( y))>>1; - case 1: return ((-x) + ( y))>>1; - case 2: return (( x) + (-y))>>1; - case 3: return ((-x) + (-y))>>1; - case 4: return (( x) + ( z))>>1; - case 5: return ((-x) + ( z))>>1; - case 6: return (( x) + (-z))>>1; - case 7: return ((-x) + (-z))>>1; - case 8: return (( y) + ( z))>>1; - case 9: return ((-y) + ( z))>>1; - case 10: return (( y) + (-z))>>1; - case 11: return ((-y) + (-z))>>1; - case 12: return (( y) + ( x))>>1; - case 13: return ((-y) + ( z))>>1; - case 14: return (( y) + (-x))>>1; - case 15: return ((-y) + (-z))>>1; - } + switch(hash & 0xF) { + case 0: return (( x) + ( y))>>1; + case 1: return ((-x) + ( y))>>1; + case 2: return (( x) + (-y))>>1; + case 3: return ((-x) + (-y))>>1; + case 4: return (( x) + ( z))>>1; + case 5: return ((-x) + ( z))>>1; + case 6: return (( x) + (-z))>>1; + case 7: return ((-x) + (-z))>>1; + case 8: return (( y) + ( z))>>1; + case 9: return ((-y) + ( z))>>1; + case 10: return (( y) + (-z))>>1; + case 11: return ((-y) + (-z))>>1; + case 12: return (( y) + ( x))>>1; + case 13: return ((-y) + ( z))>>1; + case 14: return (( y) + (-x))>>1; + case 15: return ((-y) + (-z))>>1; + } #else - hash = hash&15; - int16_t u = hash<8?x:y; - int16_t v = hash<4?y:hash==12||hash==14?x:z; - if(hash&1) { u = -u; } - if(hash&2) { v = -v; } + hash = hash&15; + int16_t u = hash<8?x:y; + int16_t v = hash<4?y:hash==12||hash==14?x:z; + if(hash&1) { u = -u; } + if(hash&2) { v = -v; } - return AVG15(u,v); + return AVG15(u,v); #endif } static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x, int16_t y) { - hash = hash & 7; - int16_t u,v; - if(hash < 4) { u = x; v = y; } else { u = y; v = x; } - if(hash&1) { u = -u; } - if(hash&2) { v = -v; } + hash = hash & 7; + int16_t u,v; + if(hash < 4) { u = x; v = y; } else { u = y; v = x; } + if(hash&1) { u = -u; } + if(hash&2) { v = -v; } - return AVG15(u,v); + return AVG15(u,v); } static int16_t inline __attribute__((always_inline)) grad16(uint8_t hash, int16_t x) { - hash = hash & 15; - int16_t u,v; - if(hash > 8) { u=x;v=x; } - else if(hash < 4) { u=x;v=1; } - else { u=1;v=x; } - if(hash&1) { u = -u; } - if(hash&2) { v = -v; } + hash = hash & 15; + int16_t u,v; + if(hash > 8) { u=x;v=x; } + else if(hash < 4) { u=x;v=1; } + else { u=1;v=x; } + if(hash&1) { u = -u; } + if(hash&2) { v = -v; } - return AVG15(u,v); + return AVG15(u,v); } // selectBasedOnHashBit performs this: @@ -150,115 +153,115 @@ static int8_t inline __attribute__((always_inline)) selectBasedOnHashBit(uint8_t static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y, int8_t z) { #if 0 - switch(hash & 0xF) { - case 0: return (( x) + ( y))>>1; - case 1: return ((-x) + ( y))>>1; - case 2: return (( x) + (-y))>>1; - case 3: return ((-x) + (-y))>>1; - case 4: return (( x) + ( z))>>1; - case 5: return ((-x) + ( z))>>1; - case 6: return (( x) + (-z))>>1; - case 7: return ((-x) + (-z))>>1; - case 8: return (( y) + ( z))>>1; - case 9: return ((-y) + ( z))>>1; - case 10: return (( y) + (-z))>>1; - case 11: return ((-y) + (-z))>>1; - case 12: return (( y) + ( x))>>1; - case 13: return ((-y) + ( z))>>1; - case 14: return (( y) + (-x))>>1; - case 15: return ((-y) + (-z))>>1; - } + switch(hash & 0xF) { + case 0: return (( x) + ( y))>>1; + case 1: return ((-x) + ( y))>>1; + case 2: return (( x) + (-y))>>1; + case 3: return ((-x) + (-y))>>1; + case 4: return (( x) + ( z))>>1; + case 5: return ((-x) + ( z))>>1; + case 6: return (( x) + (-z))>>1; + case 7: return ((-x) + (-z))>>1; + case 8: return (( y) + ( z))>>1; + case 9: return ((-y) + ( z))>>1; + case 10: return (( y) + (-z))>>1; + case 11: return ((-y) + (-z))>>1; + case 12: return (( y) + ( x))>>1; + case 13: return ((-y) + ( z))>>1; + case 14: return (( y) + (-x))>>1; + case 15: return ((-y) + (-z))>>1; + } #else - hash &= 0xF; + hash &= 0xF; - int8_t u, v; - //u = (hash&8)?y:x; - u = selectBasedOnHashBit( hash, 3, y, x); + int8_t u, v; + //u = (hash&8)?y:x; + u = selectBasedOnHashBit( hash, 3, y, x); #if 1 - v = hash<4?y:hash==12||hash==14?x:z; + v = hash<4?y:hash==12||hash==14?x:z; #else - // Verbose version for analysis; generates idenitical code. - if( hash < 4) { // 00 01 02 03 - v = y; - } else { - if( hash==12 || hash==14) { // 0C 0E - v = x; - } else { - v = z; // 04 05 06 07 08 09 0A 0B 0D 0F - } - } + // Verbose version for analysis; generates idenitical code. + if( hash < 4) { // 00 01 02 03 + v = y; + } else { + if( hash==12 || hash==14) { // 0C 0E + v = x; + } else { + v = z; // 04 05 06 07 08 09 0A 0B 0D 0F + } + } #endif - if(hash&1) { u = -u; } - if(hash&2) { v = -v; } + if(hash&1) { u = -u; } + if(hash&2) { v = -v; } - return avg7(u,v); + return avg7(u,v); #endif } static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y) { - // since the tests below can be done bit-wise on the bottom - // three bits, there's no need to mask off the higher bits - // hash = hash & 7; + // since the tests below can be done bit-wise on the bottom + // three bits, there's no need to mask off the higher bits + // hash = hash & 7; - int8_t u,v; - if( hash & 4) { - u = y; v = x; - } else { - u = x; v = y; - } + int8_t u,v; + if( hash & 4) { + u = y; v = x; + } else { + u = x; v = y; + } - if(hash&1) { u = -u; } - if(hash&2) { v = -v; } + if(hash&1) { u = -u; } + if(hash&2) { v = -v; } - return avg7(u,v); + return avg7(u,v); } static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x) { - // since the tests below can be done bit-wise on the bottom - // four bits, there's no need to mask off the higher bits - // hash = hash & 15; + // since the tests below can be done bit-wise on the bottom + // four bits, there's no need to mask off the higher bits + // hash = hash & 15; - int8_t u,v; - if(hash & 8) { - u=x; v=x; - } else { - if(hash & 4) { - u=1; v=x; - } else { - u=x; v=1; - } - } + int8_t u,v; + if(hash & 8) { + u=x; v=x; + } else { + if(hash & 4) { + u=1; v=x; + } else { + u=x; v=1; + } + } - if(hash&1) { u = -u; } - if(hash&2) { v = -v; } + if(hash&1) { u = -u; } + if(hash&2) { v = -v; } - return avg7(u,v); + return avg7(u,v); } #ifdef FADE_12 uint16_t logfade12(uint16_t val) { - return scale16(val,val)>>4; + return scale16(val,val)>>4; } static int16_t inline __attribute__((always_inline)) lerp15by12( int16_t a, int16_t b, fract16 frac) { - //if(1) return (lerp(frac,a,b)); + //if(1) return (lerp(frac,a,b)); int16_t result; if( b > a) { uint16_t delta = b - a; uint16_t scaled = scale16(delta,frac<<4); result = a + scaled; - } else { + } else { uint16_t delta = a - b; uint16_t scaled = scale16(delta,frac<<4); - result = a - scaled; - } + result = a - scaled; + } return result; } #endif @@ -285,183 +288,183 @@ static int8_t inline __attribute__((always_inline)) lerp7by8( int8_t a, int8_t b int16_t inoise16_raw(uint32_t x, uint32_t y, uint32_t z) { - // Find the unit cube containing the point - uint8_t X = (x>>16)&0xFF; - uint8_t Y = (y>>16)&0xFF; - uint8_t Z = (z>>16)&0xFF; - - // Hash cube corner coordinates - uint8_t A = P(X)+Y; - uint8_t AA = P(A)+Z; - uint8_t AB = P(A+1)+Z; - uint8_t B = P(X+1)+Y; - uint8_t BA = P(B) + Z; - uint8_t BB = P(B+1)+Z; - - // Get the relative position of the point in the cube - uint16_t u = x & 0xFFFF; - uint16_t v = y & 0xFFFF; - uint16_t w = z & 0xFFFF; - - // Get a signed version of the above for the grad function - int16_t xx = (u >> 1) & 0x7FFF; - int16_t yy = (v >> 1) & 0x7FFF; - int16_t zz = (w >> 1) & 0x7FFF; - uint16_t N = 0x8000L; - - u = EASE16(u); v = EASE16(v); w = EASE16(w); - - // skip the log fade adjustment for the moment, otherwise here we would - // adjust fade values for u,v,w - int16_t X1 = LERP(grad16(P(AA), xx, yy, zz), grad16(P(BA), xx - N, yy, zz), u); - int16_t X2 = LERP(grad16(P(AB), xx, yy-N, zz), grad16(P(BB), xx - N, yy - N, zz), u); - int16_t X3 = LERP(grad16(P(AA+1), xx, yy, zz-N), grad16(P(BA+1), xx - N, yy, zz-N), u); - int16_t X4 = LERP(grad16(P(AB+1), xx, yy-N, zz-N), grad16(P(BB+1), xx - N, yy - N, zz - N), u); - - int16_t Y1 = LERP(X1,X2,v); - int16_t Y2 = LERP(X3,X4,v); + // Find the unit cube containing the point + uint8_t X = (x>>16)&0xFF; + uint8_t Y = (y>>16)&0xFF; + uint8_t Z = (z>>16)&0xFF; + + // Hash cube corner coordinates + uint8_t A = P(X)+Y; + uint8_t AA = P(A)+Z; + uint8_t AB = P(A+1)+Z; + uint8_t B = P(X+1)+Y; + uint8_t BA = P(B) + Z; + uint8_t BB = P(B+1)+Z; + + // Get the relative position of the point in the cube + uint16_t u = x & 0xFFFF; + uint16_t v = y & 0xFFFF; + uint16_t w = z & 0xFFFF; + + // Get a signed version of the above for the grad function + int16_t xx = (u >> 1) & 0x7FFF; + int16_t yy = (v >> 1) & 0x7FFF; + int16_t zz = (w >> 1) & 0x7FFF; + uint16_t N = 0x8000L; + + u = EASE16(u); v = EASE16(v); w = EASE16(w); + + // skip the log fade adjustment for the moment, otherwise here we would + // adjust fade values for u,v,w + int16_t X1 = LERP(grad16(P(AA), xx, yy, zz), grad16(P(BA), xx - N, yy, zz), u); + int16_t X2 = LERP(grad16(P(AB), xx, yy-N, zz), grad16(P(BB), xx - N, yy - N, zz), u); + int16_t X3 = LERP(grad16(P(AA+1), xx, yy, zz-N), grad16(P(BA+1), xx - N, yy, zz-N), u); + int16_t X4 = LERP(grad16(P(AB+1), xx, yy-N, zz-N), grad16(P(BB+1), xx - N, yy - N, zz - N), u); + + int16_t Y1 = LERP(X1,X2,v); + int16_t Y2 = LERP(X3,X4,v); + + int16_t ans = LERP(Y1,Y2,w); - int16_t ans = LERP(Y1,Y2,w); - - return ans; + return ans; } uint16_t inoise16(uint32_t x, uint32_t y, uint32_t z) { - int32_t ans = inoise16_raw(x,y,z); - ans = ans + 19052L; - uint32_t pan = ans; - // pan = (ans * 220L) >> 7. That's the same as: - // pan = (ans * 440L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR. - // Identical math, except for the highest bit, which we don't care about anyway, - // since we're returning the 'middle' 16 out of a 32-bit value anyway. - pan *= 440L; - return (pan>>8); + int32_t ans = inoise16_raw(x,y,z); + ans = ans + 19052L; + uint32_t pan = ans; + // pan = (ans * 220L) >> 7. That's the same as: + // pan = (ans * 440L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR. + // Identical math, except for the highest bit, which we don't care about anyway, + // since we're returning the 'middle' 16 out of a 32-bit value anyway. + pan *= 440L; + return (pan>>8); - // // return scale16by8(pan,220)<<1; - // return ((inoise16_raw(x,y,z)+19052)*220)>>7; - // return scale16by8(inoise16_raw(x,y,z)+19052,220)<<1; + // // return scale16by8(pan,220)<<1; + // return ((inoise16_raw(x,y,z)+19052)*220)>>7; + // return scale16by8(inoise16_raw(x,y,z)+19052,220)<<1; } int16_t inoise16_raw(uint32_t x, uint32_t y) { - // Find the unit cube containing the point - uint8_t X = x>>16; - uint8_t Y = y>>16; + // Find the unit cube containing the point + uint8_t X = x>>16; + uint8_t Y = y>>16; - // Hash cube corner coordinates - uint8_t A = P(X)+Y; - uint8_t AA = P(A); - uint8_t AB = P(A+1); - uint8_t B = P(X+1)+Y; - uint8_t BA = P(B); - uint8_t BB = P(B+1); + // Hash cube corner coordinates + uint8_t A = P(X)+Y; + uint8_t AA = P(A); + uint8_t AB = P(A+1); + uint8_t B = P(X+1)+Y; + uint8_t BA = P(B); + uint8_t BB = P(B+1); - // Get the relative position of the point in the cube - uint16_t u = x & 0xFFFF; - uint16_t v = y & 0xFFFF; + // Get the relative position of the point in the cube + uint16_t u = x & 0xFFFF; + uint16_t v = y & 0xFFFF; - // Get a signed version of the above for the grad function - int16_t xx = (u >> 1) & 0x7FFF; - int16_t yy = (v >> 1) & 0x7FFF; - uint16_t N = 0x8000L; + // Get a signed version of the above for the grad function + int16_t xx = (u >> 1) & 0x7FFF; + int16_t yy = (v >> 1) & 0x7FFF; + uint16_t N = 0x8000L; - u = EASE16(u); v = EASE16(v); + u = EASE16(u); v = EASE16(v); - int16_t X1 = LERP(grad16(P(AA), xx, yy), grad16(P(BA), xx - N, yy), u); - int16_t X2 = LERP(grad16(P(AB), xx, yy-N), grad16(P(BB), xx - N, yy - N), u); + int16_t X1 = LERP(grad16(P(AA), xx, yy), grad16(P(BA), xx - N, yy), u); + int16_t X2 = LERP(grad16(P(AB), xx, yy-N), grad16(P(BB), xx - N, yy - N), u); - int16_t ans = LERP(X1,X2,v); + int16_t ans = LERP(X1,X2,v); - return ans; + return ans; } uint16_t inoise16(uint32_t x, uint32_t y) { - int32_t ans = inoise16_raw(x,y); - ans = ans + 17308L; - uint32_t pan = ans; - // pan = (ans * 242L) >> 7. That's the same as: - // pan = (ans * 484L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR. - // Identical math, except for the highest bit, which we don't care about anyway, - // since we're returning the 'middle' 16 out of a 32-bit value anyway. - pan *= 484L; - return (pan>>8); - - // return (uint32_t)(((int32_t)inoise16_raw(x,y)+(uint32_t)17308)*242)>>7; - // return scale16by8(inoise16_raw(x,y)+17308,242)<<1; + int32_t ans = inoise16_raw(x,y); + ans = ans + 17308L; + uint32_t pan = ans; + // pan = (ans * 242L) >> 7. That's the same as: + // pan = (ans * 484L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR. + // Identical math, except for the highest bit, which we don't care about anyway, + // since we're returning the 'middle' 16 out of a 32-bit value anyway. + pan *= 484L; + return (pan>>8); + + // return (uint32_t)(((int32_t)inoise16_raw(x,y)+(uint32_t)17308)*242)>>7; + // return scale16by8(inoise16_raw(x,y)+17308,242)<<1; } int16_t inoise16_raw(uint32_t x) { - // Find the unit cube containing the point - uint8_t X = x>>16; + // Find the unit cube containing the point + uint8_t X = x>>16; - // Hash cube corner coordinates - uint8_t A = P(X); - uint8_t AA = P(A); - uint8_t B = P(X+1); - uint8_t BA = P(B); + // Hash cube corner coordinates + uint8_t A = P(X); + uint8_t AA = P(A); + uint8_t B = P(X+1); + uint8_t BA = P(B); - // Get the relative position of the point in the cube - uint16_t u = x & 0xFFFF; + // Get the relative position of the point in the cube + uint16_t u = x & 0xFFFF; - // Get a signed version of the above for the grad function - int16_t xx = (u >> 1) & 0x7FFF; - uint16_t N = 0x8000L; + // Get a signed version of the above for the grad function + int16_t xx = (u >> 1) & 0x7FFF; + uint16_t N = 0x8000L; - u = EASE16(u); + u = EASE16(u); - int16_t ans = LERP(grad16(P(AA), xx), grad16(P(BA), xx - N), u); + int16_t ans = LERP(grad16(P(AA), xx), grad16(P(BA), xx - N), u); - return ans; + return ans; } uint16_t inoise16(uint32_t x) { - return ((uint32_t)((int32_t)inoise16_raw(x) + 17308L)) << 1; + return ((uint32_t)((int32_t)inoise16_raw(x) + 17308L)) << 1; } int8_t inoise8_raw(uint16_t x, uint16_t y, uint16_t z) { - // Find the unit cube containing the point - uint8_t X = x>>8; - uint8_t Y = y>>8; - uint8_t Z = z>>8; - - // Hash cube corner coordinates - uint8_t A = P(X)+Y; - uint8_t AA = P(A)+Z; - uint8_t AB = P(A+1)+Z; - uint8_t B = P(X+1)+Y; - uint8_t BA = P(B) + Z; - uint8_t BB = P(B+1)+Z; - - // Get the relative position of the point in the cube - uint8_t u = x; - uint8_t v = y; - uint8_t w = z; - - // Get a signed version of the above for the grad function - int8_t xx = ((uint8_t)(x)>>1) & 0x7F; - int8_t yy = ((uint8_t)(y)>>1) & 0x7F; - int8_t zz = ((uint8_t)(z)>>1) & 0x7F; - uint8_t N = 0x80; - - u = EASE8(u); v = EASE8(v); w = EASE8(w); - - int8_t X1 = lerp7by8(grad8(P(AA), xx, yy, zz), grad8(P(BA), xx - N, yy, zz), u); - int8_t X2 = lerp7by8(grad8(P(AB), xx, yy-N, zz), grad8(P(BB), xx - N, yy - N, zz), u); - int8_t X3 = lerp7by8(grad8(P(AA+1), xx, yy, zz-N), grad8(P(BA+1), xx - N, yy, zz-N), u); - int8_t X4 = lerp7by8(grad8(P(AB+1), xx, yy-N, zz-N), grad8(P(BB+1), xx - N, yy - N, zz - N), u); + // Find the unit cube containing the point + uint8_t X = x>>8; + uint8_t Y = y>>8; + uint8_t Z = z>>8; + + // Hash cube corner coordinates + uint8_t A = P(X)+Y; + uint8_t AA = P(A)+Z; + uint8_t AB = P(A+1)+Z; + uint8_t B = P(X+1)+Y; + uint8_t BA = P(B) + Z; + uint8_t BB = P(B+1)+Z; + + // Get the relative position of the point in the cube + uint8_t u = x; + uint8_t v = y; + uint8_t w = z; + + // Get a signed version of the above for the grad function + int8_t xx = ((uint8_t)(x)>>1) & 0x7F; + int8_t yy = ((uint8_t)(y)>>1) & 0x7F; + int8_t zz = ((uint8_t)(z)>>1) & 0x7F; + uint8_t N = 0x80; + + u = EASE8(u); v = EASE8(v); w = EASE8(w); + + int8_t X1 = lerp7by8(grad8(P(AA), xx, yy, zz), grad8(P(BA), xx - N, yy, zz), u); + int8_t X2 = lerp7by8(grad8(P(AB), xx, yy-N, zz), grad8(P(BB), xx - N, yy - N, zz), u); + int8_t X3 = lerp7by8(grad8(P(AA+1), xx, yy, zz-N), grad8(P(BA+1), xx - N, yy, zz-N), u); + int8_t X4 = lerp7by8(grad8(P(AB+1), xx, yy-N, zz-N), grad8(P(BB+1), xx - N, yy - N, zz - N), u); + + int8_t Y1 = lerp7by8(X1,X2,v); + int8_t Y2 = lerp7by8(X3,X4,v); + + int8_t ans = lerp7by8(Y1,Y2,w); - int8_t Y1 = lerp7by8(X1,X2,v); - int8_t Y2 = lerp7by8(X3,X4,v); - - int8_t ans = lerp7by8(Y1,Y2,w); - - return ans; + return ans; } uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z) { -// return scale8(76+(inoise8_raw(x,y,z)),215)<<1; + //return scale8(76+(inoise8_raw(x,y,z)),215)<<1; int8_t n = inoise8_raw( x, y, z); // -64..+64 n+= 64; // 0..128 uint8_t ans = qadd8( n, n); // 0..255 @@ -470,36 +473,36 @@ uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z) { int8_t inoise8_raw(uint16_t x, uint16_t y) { - // Find the unit cube containing the point - uint8_t X = x>>8; - uint8_t Y = y>>8; + // Find the unit cube containing the point + uint8_t X = x>>8; + uint8_t Y = y>>8; - // Hash cube corner coordinates - uint8_t A = P(X)+Y; - uint8_t AA = P(A); - uint8_t AB = P(A+1); - uint8_t B = P(X+1)+Y; - uint8_t BA = P(B); - uint8_t BB = P(B+1); + // Hash cube corner coordinates + uint8_t A = P(X)+Y; + uint8_t AA = P(A); + uint8_t AB = P(A+1); + uint8_t B = P(X+1)+Y; + uint8_t BA = P(B); + uint8_t BB = P(B+1); - // Get the relative position of the point in the cube - uint8_t u = x; - uint8_t v = y; + // Get the relative position of the point in the cube + uint8_t u = x; + uint8_t v = y; - // Get a signed version of the above for the grad function - int8_t xx = ((uint8_t)(x)>>1) & 0x7F; - int8_t yy = ((uint8_t)(y)>>1) & 0x7F; - uint8_t N = 0x80; + // Get a signed version of the above for the grad function + int8_t xx = ((uint8_t)(x)>>1) & 0x7F; + int8_t yy = ((uint8_t)(y)>>1) & 0x7F; + uint8_t N = 0x80; - u = EASE8(u); v = EASE8(v); - - int8_t X1 = lerp7by8(grad8(P(AA), xx, yy), grad8(P(BA), xx - N, yy), u); - int8_t X2 = lerp7by8(grad8(P(AB), xx, yy-N), grad8(P(BB), xx - N, yy - N), u); + u = EASE8(u); v = EASE8(v); - int8_t ans = lerp7by8(X1,X2,v); + int8_t X1 = lerp7by8(grad8(P(AA), xx, yy), grad8(P(BA), xx - N, yy), u); + int8_t X2 = lerp7by8(grad8(P(AB), xx, yy-N), grad8(P(BB), xx - N, yy - N), u); - return ans; - // return scale8((70+(ans)),234)<<1; + int8_t ans = lerp7by8(X1,X2,v); + + return ans; + // return scale8((70+(ans)),234)<<1; } @@ -562,8 +565,8 @@ uint8_t inoise8(uint16_t x) { void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16_t x, int scale, uint16_t time) { uint32_t _xx = x; uint32_t scx = scale; - for(int o = 0; o < octaves; o++) { - for(int i = 0,xx=_xx; i < num_points; i++, xx+=scx) { + for(int o = 0; o < octaves; ++o) { + for(int i = 0,xx=_xx; i < num_points; ++i, xx+=scx) { pData[i] = qadd8(pData[i],inoise8(xx,time)>>o); } @@ -575,8 +578,8 @@ void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16 void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint32_t x, int scale, uint32_t time) { uint32_t _xx = x; uint32_t scx = scale; - for(int o = 0; o < octaves; o++) { - for(int i = 0,xx=_xx; i < num_points; i++, xx+=scx) { + for(int o = 0; o < octaves; ++o) { + for(int i = 0,xx=_xx; i < num_points; ++i, xx+=scx) { uint32_t accum = (inoise16(xx,time))>>o; accum += (pData[i]<<8); if(accum > 65535) { accum = 65535; } @@ -601,19 +604,19 @@ void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, q fract8 invamp = 255-amplitude; uint16_t xx = x; - for(int i = 0; i < height; i++, y+=scaley) { + for(int i = 0; i < height; ++i, y+=scaley) { uint8_t *pRow = pData + (i*width); xx = x; - for(int j = 0; j < width; j++, xx+=scalex) { + for(int j = 0; j < width; ++j, xx+=scalex) { uint8_t noise_base = inoise8(xx,y,time); noise_base = (0x80 & noise_base) ? (noise_base - 127) : (127 - noise_base); noise_base = scale8(noise_base<<1,amplitude); if(skip == 1) { pRow[j] = scale8(pRow[j],invamp) + noise_base; } else { - for(int ii = i; ii<(i+skip) && ii>= 8; - for(int i = 0; i < height; i++) { + for(int i = 0; i < height; ++i) { int wb = i*width; - for(int j = 0; j < width; j++) { + for(int j = 0; j < width; ++j) { CRGB led(CHSV(hue_shift + (H[h1-i][w1-j]),196,V[i][j])); int pos = j; diff --git a/noise.h b/src/noise.h similarity index 100% rename from noise.h rename to src/noise.h diff --git a/src/pixelset.h b/src/pixelset.h new file mode 100644 index 0000000000..b8488c2ced --- /dev/null +++ b/src/pixelset.h @@ -0,0 +1,306 @@ +#ifndef __INC_PIXELSET_H +#define __INC_PIXELSET_H + +#include "FastLED.h" + +#ifndef abs +#include +#endif + +///// Represents a set of CRGB led objects. Provides the [] array operator, and works like a normal array in that case. +///// This should be kept in sync with the set of functions provided by CRGB as well as functions in colorutils. Note +///// that a pixel set is a window into another set of led data, it is not its own set of led data. +template +class CPixelView { +public: + const int8_t dir; + const int len; + PIXEL_TYPE * const leds; + PIXEL_TYPE * const end_pos; + +public: + /// PixelSet copy constructor + inline CPixelView(const CPixelView & other) : dir(other.dir), len(other.len), leds(other.leds), end_pos(other.end_pos) {} + + /// pixelset constructor for a pixel set starting at the given PIXEL_TYPE* and going for _len leds. Note that the length + /// can be backwards, creating a PixelSet that walks backwards over the data + /// @param leds point to the raw led data + /// @param len how many leds in this set + inline CPixelView(PIXEL_TYPE *_leds, int _len) : dir(_len < 0 ? -1 : 1), len(_len), leds(_leds), end_pos(_leds + _len) {} + + /// PixelSet constructor for the given set of leds, with start and end boundaries. Note that start can be after + /// end, resulting in a set that will iterate backwards + /// @param leds point to the raw led data + /// @param start the start index of the leds for this array + /// @param end the end index of the leds for this array + inline CPixelView(PIXEL_TYPE *_leds, int _start, int _end) : dir(((_end-_start)<0) ? -1 : 1), len((_end - _start) + dir), leds(_leds + _start), end_pos(_leds + _start + len) {} + + /// Get the size of this set + /// @return the size of the set + int size() { return abs(len); } + + /// Whether or not this set goes backwards + /// @return whether or not the set is backwards + bool reversed() { return len < 0; } + + /// do these sets point to the same thing (note, this is different from the contents of the set being the same) + bool operator==(const CPixelView & rhs) const { return leds == rhs.leds && len == rhs.len && dir == rhs.dir; } + + /// do these sets point to the different things (note, this is different from the contents of the set being the same) + bool operator!=(const CPixelView & rhs) const { return leds != rhs.leds || len != rhs.len || dir != rhs.dir; } + + /// access a single element in this set, just like an array operator + inline PIXEL_TYPE & operator[](int x) const { if(dir & 0x80) { return leds[-x]; } else { return leds[x]; } } + + /// Access an inclusive subset of the leds in this set. Note that start can be greater than end, which will + /// result in a reverse ordering for many functions (useful for mirroring) + /// @param start the first element from this set for the new subset + /// @param end the last element for the new subset + inline CPixelView operator()(int start, int end) { return CPixelView(leds, start, end); } + + /// Access an inclusive subset of the leds in this set, starting from the first. + /// @param end the last element for the new subset + /// Not sure i want this? inline CPixelView operator()(int end) { return CPixelView(leds, 0, end); } + + /// Return the reverse ordering of this set + inline CPixelView operator-() { return CPixelView(leds, len - dir, 0); } + + /// Return a pointer to the first element in this set + inline operator PIXEL_TYPE* () const { return leds; } + + /// Assign the passed in color to all elements in this set + /// @param color the new color for the elements in the set + inline CPixelView & operator=(const PIXEL_TYPE & color) { + for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) = color; } + return *this; + } + + + void dump() const { + /** + Serial.print("len: "); Serial.print(len); Serial.print(", dir:"); Serial.print((int)dir); + Serial.print(", range:"); Serial.print((uint32_t)leds); Serial.print("-"); Serial.print((uint32_t)end_pos); + Serial.print(", diff:"); Serial.print((int32_t)(end_pos - leds)); + Serial.println(""); + **/ + } + + /// Copy the contents of the passed in set to our set. Note if one set is smaller than the other, only the + /// smallest number of items will be copied over. + inline CPixelView & operator=(const CPixelView & rhs) { + for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { + (*pixel) = (*rhspixel); + } + return *this; + } + + /// @name modification/scaling operators + //@{ + /// Add the passed in value to r,g, b for all the pixels in this set + inline CPixelView & addToRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) += inc; } return *this; } + /// Add every pixel in the other set to this set + inline CPixelView & operator+=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) += (*rhspixel); } return *this; } + + /// Subtract the passed in value from r,g,b for all pixels in this set + inline CPixelView & subFromRGB(uint8_t inc) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) -= inc; } return *this; } + /// Subtract every pixel in the other set from this set + inline CPixelView & operator-=(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) -= (*rhspixel); } return *this; } + + /// Increment every pixel value in this set + inline CPixelView & operator++() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; } + /// Increment every pixel value in this set + inline CPixelView & operator++(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)++; } return *this; } + + /// Decrement every pixel value in this set + inline CPixelView & operator--() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; } + /// Decrement every pixel value in this set + inline CPixelView & operator--(int DUMMY_ARG) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel)--; } return *this; } + + /// Divide every led by the given value + inline CPixelView & operator/=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) /= d; } return *this; } + /// Shift every led in this set right by the given number of bits + inline CPixelView & operator>>=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) >>= d; } return *this; } + /// Multiply every led in this set by the given value + inline CPixelView & operator*=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) *= d; } return *this; } + + /// Scale every led by the given scale + inline CPixelView & nscale8_video(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this;} + /// Scale down every led by the given scale + inline CPixelView & operator%=(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8_video(scaledown); } return *this; } + /// Fade every led down by the given scale + inline CPixelView & fadeLightBy(uint8_t fadefactor) { return nscale8_video(255 - fadefactor); } + + /// Scale every led by the given scale + inline CPixelView & nscale8(uint8_t scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; } + /// Scale every led by the given scale + inline CPixelView & nscale8(PIXEL_TYPE & scaledown) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel).nscale8(scaledown); } return *this; } + /// Scale every led in this set by every led in the other set + inline CPixelView & nscale8(CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel).nscale8((*rhspixel)); } return *this; } + + /// Fade every led down by the given scale + inline CPixelView & fadeToBlackBy(uint8_t fade) { return nscale8(255 - fade); } + + /// Apply the PIXEL_TYPE |= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel to the higher of the two values) + inline CPixelView & operator|=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= rhs; } return *this; } + /// Apply the PIXEL_TYPE |= operator to every pixel in this set with every pixel in the passed in set + inline CPixelView & operator|=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) |= (*rhspixel); } return *this; } + /// Apply the PIXEL_TYPE |= operator to every pixel in this set + inline CPixelView & operator|=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) |= d; } return *this; } + + /// Apply the PIXEL_TYPE &= operator to every pixel in this set with the given PIXEL_TYPE value (bringing each channel down to the lower of the two values) + inline CPixelView & operator&=(const PIXEL_TYPE & rhs) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= rhs; } return *this; } + /// Apply the PIXEL_TYPE &= operator to every pixel in this set with every pixel in the passed in set + inline CPixelView & operator&=(const CPixelView & rhs) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { (*pixel) &= (*rhspixel); } return *this; } + /// APply the PIXEL_TYPE &= operator to every pixel in this set with the passed in value + inline CPixelView & operator&=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= d; } return *this; } + //@} + + /// Returns whether or not any leds in this set are non-zero + inline operator bool() { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { if((*pixel)) return true; } return false; } + + // Color util functions + inline CPixelView & fill_solid(const PIXEL_TYPE & color) { *this = color; return *this; } + inline CPixelView & fill_solid(const CHSV & color) { if(dir>0) { *this = color; return *this; } } + + inline CPixelView & fill_rainbow(uint8_t initialhue, uint8_t deltahue=5) { + if(dir >= 0) { + ::fill_rainbow(leds,len,initialhue,deltahue); + } else { + ::fill_rainbow(leds+len+1,-len,initialhue,deltahue); + } + return *this; + } + + inline CPixelView & fill_gradient(const CHSV & startcolor, const CHSV & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient(leds,len,startcolor, endcolor, directionCode); + } else { + ::fill_gradient(leds + len + 1, (-len), endcolor, startcolor, directionCode); + } + return *this; + } + + inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient(leds, len, c1, c2, c3, directionCode); + } else { + ::fill_gradient(leds + len + 1, -len, c3, c2, c1, directionCode); + } + return *this; + } + + inline CPixelView & fill_gradient(const CHSV & c1, const CHSV & c2, const CHSV & c3, const CHSV & c4, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient(leds, len, c1, c2, c3, c4, directionCode); + } else { + ::fill_gradient(leds + len + 1, -len, c4, c3, c2, c1, directionCode); + } + return *this; + } + + inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & startcolor, const PIXEL_TYPE & endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES) { + if(dir >= 0) { + ::fill_gradient_RGB(leds,len,startcolor, endcolor); + } else { + ::fill_gradient_RGB(leds + len + 1, (-len), endcolor, startcolor); + } + return *this; + } + + inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3) { + if(dir >= 0) { + ::fill_gradient_RGB(leds, len, c1, c2, c3); + } else { + ::fill_gradient_RGB(leds + len + 1, -len, c3, c2, c1); + } + return *this; + } + + inline CPixelView & fill_gradient_RGB(const PIXEL_TYPE & c1, const PIXEL_TYPE & c2, const PIXEL_TYPE & c3, const PIXEL_TYPE & c4) { + if(dir >= 0) { + ::fill_gradient_RGB(leds, len, c1, c2, c3, c4); + } else { + ::fill_gradient_RGB(leds + len + 1, -len, c4, c3, c2, c1); + } + return *this; + } + + inline CPixelView & nblend(const PIXEL_TYPE & overlay, fract8 amountOfOverlay) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { ::nblend((*pixel), overlay, amountOfOverlay); } return *this; } + inline CPixelView & nblend(const CPixelView & rhs, fract8 amountOfOverlay) { for(iterator pixel = begin(), rhspixel = rhs.begin(), _end = end(), rhs_end = rhs.end(); (pixel != _end) && (rhspixel != rhs_end); ++pixel, ++rhspixel) { ::nblend((*pixel), (*rhspixel), amountOfOverlay); } return *this; } + + // Note: only bringing in a 1d blur, not sure 2d blur makes sense when looking at sub arrays + inline CPixelView & blur1d(fract8 blur_amount) { + if(dir >= 0) { + ::blur1d(leds, len, blur_amount); + } else { + ::blur1d(leds + len + 1, -len, blur_amount); + } + return *this; + } + + inline CPixelView & napplyGamma_video(float gamma) { + if(dir >= 0) { + ::napplyGamma_video(leds, len, gamma); + } else { + ::napplyGamma_video(leds + len + 1, -len, gamma); + } + return *this; + } + + inline CPixelView & napplyGamma_video(float gammaR, float gammaG, float gammaB) { + if(dir >= 0) { + ::napplyGamma_video(leds, len, gammaR, gammaG, gammaB); + } else { + ::napplyGamma_video(leds + len + 1, -len, gammaR, gammaG, gammaB); + } + return *this; + } + + // TODO: Make this a fully specified/proper iterator + template + class pixelset_iterator_base { + T * leds; + const int8_t dir; + + public: + __attribute__((always_inline)) inline pixelset_iterator_base(const pixelset_iterator_base & rhs) : leds(rhs.leds), dir(rhs.dir) {} + __attribute__((always_inline)) inline pixelset_iterator_base(T * _leds, const char _dir) : leds(_leds), dir(_dir) {} + + __attribute__((always_inline)) inline pixelset_iterator_base& operator++() { leds += dir; return *this; } + __attribute__((always_inline)) inline pixelset_iterator_base operator++(int) { pixelset_iterator_base tmp(*this); leds += dir; return tmp; } + + __attribute__((always_inline)) inline bool operator==(pixelset_iterator_base & other) const { return leds == other.leds; } // && set==other.set; } + __attribute__((always_inline)) inline bool operator!=(pixelset_iterator_base & other) const { return leds != other.leds; } // || set != other.set; } + + __attribute__((always_inline)) inline PIXEL_TYPE& operator*() const { return *leds; } + }; + + typedef pixelset_iterator_base iterator; + typedef pixelset_iterator_base const_iterator; + + iterator begin() { return iterator(leds, dir); } + iterator end() { return iterator(end_pos, dir); } + + iterator begin() const { return iterator(leds, dir); } + iterator end() const { return iterator(end_pos, dir); } + + const_iterator cbegin() const { return const_iterator(leds, dir); } + const_iterator cend() const { return const_iterator(end_pos, dir); } +}; + +typedef CPixelView CRGBSet; + +__attribute__((always_inline)) +inline CRGB *operator+(const CRGBSet & pixels, int offset) { return (CRGB*)pixels + offset; } + + +template +class CRGBArray : public CPixelView { + CRGB rawleds[SIZE]; + +public: + CRGBArray() : CPixelView(rawleds, SIZE) {} + using CPixelView::operator=; +}; + +#endif diff --git a/pixeltypes.h b/src/pixeltypes.h similarity index 93% rename from pixeltypes.h rename to src/pixeltypes.h index ff327fd9a8..6e91723df0 100644 --- a/pixeltypes.h +++ b/src/pixeltypes.h @@ -38,11 +38,21 @@ struct CHSV { uint8_t raw[3]; }; - /// default values are UNITIALIZED - inline CHSV() __attribute__((always_inline)) + /// Array access operator to index into the chsv object + inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) { + return raw[x]; } + /// Array access operator to index into the chsv object + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return raw[x]; + } + + /// default values are UNITIALIZED + inline CHSV() __attribute__((always_inline)) = default; + /// allow construction from H, S, V inline CHSV( uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) : h(ih), s(is), v(iv) @@ -50,20 +60,9 @@ struct CHSV { } /// allow copy construction - inline CHSV(const CHSV& rhs) __attribute__((always_inline)) - { - h = rhs.h; - s = rhs.s; - v = rhs.v; - } + inline CHSV(const CHSV& rhs) __attribute__((always_inline)) = default; - inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) - { - h = rhs.h; - s = rhs.s; - v = rhs.v; - return *this; - } + inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) = default; inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) { @@ -106,7 +105,7 @@ struct CRGB { uint8_t raw[3]; }; - /// Array access operator to index into the crgb object + /// Array access operator to index into the crgb object inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) { return raw[x]; @@ -119,9 +118,7 @@ struct CRGB { } // default values are UNINITIALIZED - inline CRGB() __attribute__((always_inline)) - { - } + inline CRGB() __attribute__((always_inline)) = default; /// allow construction from R, G, B inline CRGB( uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline)) @@ -150,13 +147,7 @@ struct CRGB { } /// allow copy construction - inline CRGB(const CRGB& rhs) __attribute__((always_inline)) - { - r = rhs.r; - g = rhs.g; - b = rhs.b; - } - + inline CRGB(const CRGB& rhs) __attribute__((always_inline)) = default; /// allow construction from HSV color inline CRGB(const CHSV& rhs) __attribute__((always_inline)) { @@ -164,13 +155,7 @@ struct CRGB { } /// allow assignment from one RGB struct to another - inline CRGB& operator= (const CRGB& rhs) __attribute__((always_inline)) - { - r = rhs.r; - g = rhs.g; - b = rhs.b; - return *this; - } + inline CRGB& operator= (const CRGB& rhs) __attribute__((always_inline)) = default; /// allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline)) @@ -305,10 +290,10 @@ struct CRGB { /// right shift each of the channels by a constant inline CRGB& operator>>= (uint8_t d) { - r >>= d; - g >>= d; - b >>= d; - return *this; + r >>= d; + g >>= d; + b >>= d; + return *this; } /// multiply each of the channels by a constant, @@ -438,11 +423,11 @@ struct CRGB { #if (defined SmartMatrix_h || defined SmartMatrix3_h) operator rgb24() const { - rgb24 ret; - ret.red = r; - ret.green = g; - ret.blue = b; - return ret; + rgb24 ret; + ret.red = r; + ret.green = g; + ret.blue = b; + return ret; } #endif @@ -478,7 +463,7 @@ struct CRGB { uint8_t max = red; if( green > max) max = green; if( blue > max) max = blue; - + // stop div/0 when color is black if(max > 0) { uint16_t factor = ((uint16_t)(limit) * 256) / max; @@ -491,25 +476,25 @@ struct CRGB { /// return a new CRGB object after performing a linear interpolation between this object and the passed in object inline CRGB lerp8( const CRGB& other, fract8 frac) const { - CRGB ret; + CRGB ret; - ret.r = lerp8by8(r,other.r,frac); - ret.g = lerp8by8(g,other.g,frac); - ret.b = lerp8by8(b,other.b,frac); + ret.r = lerp8by8(r,other.r,frac); + ret.g = lerp8by8(g,other.g,frac); + ret.b = lerp8by8(b,other.b,frac); - return ret; + return ret; } /// return a new CRGB object after performing a linear interpolation between this object and the passed in object inline CRGB lerp16( const CRGB& other, fract16 frac) const { - CRGB ret; + CRGB ret; - ret.r = lerp16by16(r<<8,other.r<<8,frac)>>8; - ret.g = lerp16by16(g<<8,other.g<<8,frac)>>8; - ret.b = lerp16by16(b<<8,other.b<<8,frac)>>8; + ret.r = lerp16by16(r<<8,other.r<<8,frac)>>8; + ret.g = lerp16by16(g<<8,other.g<<8,frac)>>8; + ret.b = lerp16by16(b<<8,other.b<<8,frac)>>8; - return ret; + return ret; } /// getParity returns 0 or 1, depending on the @@ -553,14 +538,14 @@ struct CRGB { // going 'up' if( (b > 0) && (b < 255)) { if( r == g && g == b) { - r++; - g++; + ++r; + ++g; } - b++; + ++b; } else if( (r > 0) && (r < 255)) { - r++; + ++r; } else if( (g > 0) && (g < 255)) { - g++; + ++g; } else { if( r == g && g == b) { r ^= 0x01; @@ -572,14 +557,14 @@ struct CRGB { // going 'down' if( b > 1) { if( r == g && g == b) { - r--; - g--; + --r; + --g; } - b--; + --b; } else if( g > 1) { - g--; + --g; } else if( r > 1) { - r--; + --r; } else { if( r == g && g == b) { r ^= 0x01; diff --git a/src/platforms.cpp b/src/platforms.cpp new file mode 100644 index 0000000000..5b6847ad67 --- /dev/null +++ b/src/platforms.cpp @@ -0,0 +1,40 @@ +#define FASTLED_INTERNAL + + +// Interrupt handlers cannot be defined in the header. +// They must be defined as C functions, or they won't +// be found (due to name mangling), and thus won't +// override any default weak definition. +#if defined(NRF52_SERIES) + + #include "platforms/arm/nrf52/led_sysdefs_arm_nrf52.h" + #include "platforms/arm/nrf52/arbiter_nrf52.h" + + uint32_t isrCount; + + #ifdef __cplusplus + extern "C" { + #endif + // NOTE: Update platforms.cpp in root of FastLED library if this changes + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0) + void PWM0_IRQHandler(void) { ++isrCount; PWM_Arbiter<0>::isr_handler(); } + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1) + void PWM1_IRQHandler(void) { ++isrCount; PWM_Arbiter<1>::isr_handler(); } + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2) + void PWM2_IRQHandler(void) { ++isrCount; PWM_Arbiter<2>::isr_handler(); } + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3) + void PWM3_IRQHandler(void) { ++isrCount; PWM_Arbiter<3>::isr_handler(); } + #endif + #ifdef __cplusplus + } + #endif + +#endif // defined(NRF52_SERIES) + + + +// FASTLED_NAMESPACE_BEGIN +// FASTLED_NAMESPACE_END diff --git a/platforms.h b/src/platforms.h similarity index 78% rename from platforms.h rename to src/platforms.h index 88bf462d4e..7969c9e4de 100644 --- a/platforms.h +++ b/src/platforms.h @@ -7,6 +7,8 @@ #if defined(NRF51) #include "platforms/arm/nrf51/fastled_arm_nrf51.h" +#elif defined(NRF52_SERIES) +#include "platforms/arm/nrf52/fastled_arm_nrf52.h" #elif defined(__MK20DX128__) || defined(__MK20DX256__) // Include k20/T3 headers #include "platforms/arm/k20/fastled_arm_k20.h" @@ -16,6 +18,9 @@ #elif defined(__MKL26Z64__) // Include kl26/T-LC headers #include "platforms/arm/kl26/fastled_arm_kl26.h" +#elif defined(__IMXRT1062__) +// teensy4 +#include "platforms/arm/mxrt1062/fastled_arm_mxrt1062.h" #elif defined(__SAM3X8E__) // Include sam/due headers #include "platforms/arm/sam/fastled_arm_sam.h" @@ -23,12 +28,14 @@ #include "platforms/arm/stm32/fastled_arm_stm32.h" #elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) #include "platforms/arm/d21/fastled_arm_d21.h" -#elif defined(__SAMD51G19A__) +#elif defined(__SAMD51G19A__) || defined(__SAMD51J19A__) #include "platforms/arm/d51/fastled_arm_d51.h" #elif defined(ESP8266) #include "platforms/esp/8266/fastled_esp8266.h" #elif defined(ESP32) #include "platforms/esp/32/fastled_esp32.h" +#elif defined(ARDUINO_ARCH_APOLLO3) +#include "platforms/apollo3/fastled_apollo3.h" #else // AVR platforms #include "platforms/avr/fastled_avr.h" diff --git a/src/platforms/apollo3/clockless_apollo3.h b/src/platforms/apollo3/clockless_apollo3.h new file mode 100644 index 0000000000..fa487c2f76 --- /dev/null +++ b/src/platforms/apollo3/clockless_apollo3.h @@ -0,0 +1,184 @@ +#ifndef __INC_CLOCKLESS_APOLLO3_H +#define __INC_CLOCKLESS_APOLLO3_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_APOLLO3) + +// Clockless support for the SparkFun Artemis / Ambiq Micro Apollo3 Blue +// Uses SysTick to govern the pulse timing + +//***************************************************************************** +// +// Code taken from Ambiq Micro's am_hal_systick.c +// and converted to inline static for speed +// +//! @brief Get the current count value in the SYSTICK. +//! +//! This function gets the current count value in the systick timer. +//! +//! @return Current count value. +// +//***************************************************************************** +__attribute__ ((always_inline)) inline static uint32_t __am_hal_systick_count() { + return SysTick->VAL; +} + +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + CMinWait mWait; + +public: + virtual void init() { + // Initialize everything + + // Configure DATA_PIN for FastGPIO (settings are in fastpin_apollo3.h) + FastPin::setOutput(); + FastPin::lo(); + + // Make sure the system clock is running at the full 48MHz + am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0); + + // Make sure interrupts are enabled + //am_hal_interrupt_master_enable(); + + // Enable SysTick Interrupts in the NVIC + //NVIC_EnableIRQ(SysTick_IRQn); + + // SysTick is 24-bit and counts down (not up) + + // Stop the SysTick (just in case it is already running). + // This clears the ENABLE bit in the SysTick Control and Status Register (SYST_CSR). + // In Ambiq naming convention, the control register is SysTick->CTRL + am_hal_systick_stop(); + + // Call SysTick_Config + // This is defined in core_cm4.h + // It loads the specified LOAD value into the SysTick Reload Value Register (SYST_RVR) + // In Ambiq naming convention, the reload register is SysTick->LOAD + // It sets the SysTick interrupt priority + // It clears the SysTick Current Value Register (SYST_CVR) + // In Ambiq naming convention, the current value register is SysTick->VAL + // Finally it sets these bits in the SysTick Control and Status Register (SYST_CSR): + // CLKSOURCE: SysTick uses the processor clock + // TICKINT: When the count reaches zero, the SysTick exception (interrupt) is changed to pending + // ENABLE: Enables the counter + // SysTick_Config returns 0 if successful. 1 indicates a failure (the LOAD value was invalid). + SysTick_Config(0xFFFFFFUL); // The LOAD value needs to be 24-bit + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } + + template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register uint8_t & b) { + // SysTick counts down (not up) and is 24-bit + for(register uint32_t i = BITS-1; i > 0; i--) { // We could speed this up by using Bit Banding + while(__am_hal_systick_count() > next_mark) { ; } // Wait for the remainder of this cycle to complete + // Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3 + // SysTick counts down (not up) and is 24-bit + next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL; + FastPin::hi(); + if(b&0x80) { + // "1 code" = longer pulse width + while((__am_hal_systick_count() - next_mark) > (T3+(3*(F_CPU/24000000)))) { ; } + FastPin::lo(); + } else { + // "0 code" = shorter pulse width + while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; } + FastPin::lo(); + } + b <<= 1; + } + + while(__am_hal_systick_count() > next_mark) { ; }// Wait for the remainder of this cycle to complete + // Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3 + // SysTick counts down (not up) and is 24-bit + next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL; + FastPin::hi(); + if(b&0x80) { + // "1 code" = longer pulse width + while((__am_hal_systick_count() - next_mark) > (T3+(2*(F_CPU/24000000)))) { ; } + FastPin::lo(); + } else { + // "0 code" = shorter pulse width + while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; } + FastPin::lo(); + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController pixels) { + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint8_t b = pixels.loadAndScale0(); + + cli(); + + // Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3 + // SysTick counts down (not up) and is 24-bit + // The subtraction could underflow (wrap round) so let's mask the result to 24 bits + register uint32_t next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL; + + while(pixels.has(1)) { // Keep going for as long as we have pixels + pixels.stepDithering(); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + + // Have we already missed the next_mark? + if(__am_hal_systick_count() < next_mark) { + // If we have exceeded next_mark by an excessive amount, then bail (return 0) + if((next_mark - __am_hal_systick_count()) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; } + } + #endif + + // Write first byte, read next byte + writeBits<8+XTRA0>(next_mark, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(next_mark, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(next_mark, b); + b = pixels.advanceAndLoadAndScale0(); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; // end of while(pixels.has(1)) + + // Unfortunately SysTick relies on an interrupt to reload it once it reaches zero + // and having interrupts disabled for most of the above means the interrupt doesn't get serviced. + // So we had better reload it here instead... + am_hal_systick_load(0xFFFFFFUL); + + sei(); + return (1); + } + +}; + + +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/apollo3/fastled_apollo3.h b/src/platforms/apollo3/fastled_apollo3.h new file mode 100644 index 0000000000..4c727dd0c5 --- /dev/null +++ b/src/platforms/apollo3/fastled_apollo3.h @@ -0,0 +1,8 @@ +#ifndef __INC_FASTLED_APOLLO3_H +#define __INC_FASTLED_APOLLO3_H + +#include "fastpin_apollo3.h" +#include "fastspi_apollo3.h" +#include "clockless_apollo3.h" + +#endif diff --git a/src/platforms/apollo3/fastpin_apollo3.h b/src/platforms/apollo3/fastpin_apollo3.h new file mode 100644 index 0000000000..6d0f1e60be --- /dev/null +++ b/src/platforms/apollo3/fastpin_apollo3.h @@ -0,0 +1,153 @@ +#ifndef __INC_FASTPIN_APOLLO3_H +#define __INC_FASTPIN_APOLLO3_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be slightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +template class _APOLLO3PIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); am_hal_gpio_fastgpio_enable(PAD); } + inline static void setInput() { am_hal_gpio_fastgpio_disable(PAD); pinMode(PIN, INPUT); } + + inline static void hi() __attribute__ ((always_inline)) { am_hal_gpio_fastgpio_set(PAD); } + inline static void lo() __attribute__ ((always_inline)) { am_hal_gpio_fastgpio_clr(PAD); } + inline static void set(register port_t val) __attribute__ ((always_inline)) { if(val) { am_hal_gpio_fastgpio_set(PAD); } else { am_hal_gpio_fastgpio_clr(PAD); } } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { if( am_hal_gpio_fastgpio_read(PAD)) { lo(); } else { hi(); } } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { set(val); } + + inline static port_t hival() __attribute__ ((always_inline)) { return 0; } + inline static port_t loval() __attribute__ ((always_inline)) { return 0; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return NULL; } + inline static port_t mask() __attribute__ ((always_inline)) { return 0; } +}; + +// For the Apollo3 we need to define both the pin number and the associated pad +// to avoid having to use ap3_gpio_pin2pad for fastgpio (which would slow things down) +#define _FL_DEFPIN(PIN, PAD) template<> class FastPin : public _APOLLO3PIN {}; + +// Actual (pin, pad) definitions +#if defined(ARDUINO_SFE_EDGE) + +#define MAX_PIN 49 +_FL_DEFPIN(0, 0); _FL_DEFPIN(1, 1); _FL_DEFPIN(3, 3); _FL_DEFPIN(4, 4); +_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9); +_FL_DEFPIN(10, 10); _FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14); +_FL_DEFPIN(15, 15); _FL_DEFPIN(17, 17); +_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(22, 22); _FL_DEFPIN(23, 23); _FL_DEFPIN(24, 24); +_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29); +_FL_DEFPIN(33, 33); +_FL_DEFPIN(36, 36); _FL_DEFPIN(37, 37); _FL_DEFPIN(38, 38); _FL_DEFPIN(39, 39); +_FL_DEFPIN(40, 40); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44); +_FL_DEFPIN(46, 46); _FL_DEFPIN(47, 47); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SFE_EDGE2) + +#define MAX_PIN 49 +_FL_DEFPIN(0, 0); +_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9); +_FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14); +_FL_DEFPIN(15, 15); _FL_DEFPIN(16, 16); _FL_DEFPIN(17, 17); _FL_DEFPIN(18, 18); _FL_DEFPIN(19, 19); +_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(23, 23); +_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29); +_FL_DEFPIN(31, 31); _FL_DEFPIN(32, 32); _FL_DEFPIN(33, 33); _FL_DEFPIN(34, 34); +_FL_DEFPIN(35, 35); _FL_DEFPIN(37, 37); _FL_DEFPIN(39, 39); +_FL_DEFPIN(40, 40); _FL_DEFPIN(41, 41); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44); +_FL_DEFPIN(45, 45); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AM_AP3_SFE_BB_ARTEMIS) + +#define MAX_PIN 31 +_FL_DEFPIN(0, 25); _FL_DEFPIN(1, 24); _FL_DEFPIN(2, 35); _FL_DEFPIN(3, 4); _FL_DEFPIN(4, 22); +_FL_DEFPIN(5, 23); _FL_DEFPIN(6, 27); _FL_DEFPIN(7, 28); _FL_DEFPIN(8, 32); _FL_DEFPIN(9, 12); +_FL_DEFPIN(10, 13); _FL_DEFPIN(11, 7); _FL_DEFPIN(12, 6); _FL_DEFPIN(13, 5); _FL_DEFPIN(14, 40); +_FL_DEFPIN(15, 39); _FL_DEFPIN(16, 29); _FL_DEFPIN(17, 11); _FL_DEFPIN(18, 34); _FL_DEFPIN(19, 33); +_FL_DEFPIN(20, 16); _FL_DEFPIN(21, 31); _FL_DEFPIN(22, 48); _FL_DEFPIN(23, 49); _FL_DEFPIN(24, 8); +_FL_DEFPIN(25, 9); _FL_DEFPIN(26, 10); _FL_DEFPIN(27, 38); _FL_DEFPIN(28, 42); _FL_DEFPIN(29, 43); +_FL_DEFPIN(30, 36); _FL_DEFPIN(31, 37); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AM_AP3_SFE_BB_ARTEMIS_NANO) + +#define MAX_PIN 23 +_FL_DEFPIN(0, 13); _FL_DEFPIN(1, 33); _FL_DEFPIN(2, 11); _FL_DEFPIN(3, 29); _FL_DEFPIN(4, 18); +_FL_DEFPIN(5, 31); _FL_DEFPIN(6, 43); _FL_DEFPIN(7, 42); _FL_DEFPIN(8, 38); _FL_DEFPIN(9, 39); +_FL_DEFPIN(10, 40); _FL_DEFPIN(11, 5); _FL_DEFPIN(12, 7); _FL_DEFPIN(13, 6); _FL_DEFPIN(14, 35); +_FL_DEFPIN(15, 32); _FL_DEFPIN(16, 12); _FL_DEFPIN(17, 32); _FL_DEFPIN(18, 12); _FL_DEFPIN(19, 19); +_FL_DEFPIN(20, 48); _FL_DEFPIN(21, 49); _FL_DEFPIN(22, 36); _FL_DEFPIN(23, 37); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AM_AP3_SFE_THING_PLUS) + +#define MAX_PIN 28 +_FL_DEFPIN(0, 25); _FL_DEFPIN(1, 24); _FL_DEFPIN(2, 44); _FL_DEFPIN(3, 35); _FL_DEFPIN(4, 4); +_FL_DEFPIN(5, 22); _FL_DEFPIN(6, 23); _FL_DEFPIN(7, 27); _FL_DEFPIN(8, 28); _FL_DEFPIN(9, 32); +_FL_DEFPIN(10, 14); _FL_DEFPIN(11, 7); _FL_DEFPIN(12, 6); _FL_DEFPIN(13, 5); _FL_DEFPIN(14, 40); +_FL_DEFPIN(15, 39); _FL_DEFPIN(16, 43); _FL_DEFPIN(17, 42); _FL_DEFPIN(18, 26); _FL_DEFPIN(19, 33); +_FL_DEFPIN(20, 13); _FL_DEFPIN(21, 11); _FL_DEFPIN(22, 29); _FL_DEFPIN(23, 12); _FL_DEFPIN(24, 31); +_FL_DEFPIN(25, 48); _FL_DEFPIN(26, 49); _FL_DEFPIN(27, 36); _FL_DEFPIN(28, 37); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AM_AP3_SFE_BB_ARTEMIS_ATP) || defined(ARDUINO_SFE_ARTEMIS) + +#define MAX_PIN 49 +_FL_DEFPIN(0, 0); _FL_DEFPIN(1, 1); _FL_DEFPIN(2, 2); _FL_DEFPIN(3, 3); _FL_DEFPIN(4, 4); +_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9); +_FL_DEFPIN(10, 10); _FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14); +_FL_DEFPIN(15, 15); _FL_DEFPIN(16, 16); _FL_DEFPIN(17, 17); _FL_DEFPIN(18, 18); _FL_DEFPIN(19, 19); +_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(22, 22); _FL_DEFPIN(23, 23); _FL_DEFPIN(24, 24); +_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29); +_FL_DEFPIN(31, 31); _FL_DEFPIN(32, 32); _FL_DEFPIN(33, 33); _FL_DEFPIN(34, 34); +_FL_DEFPIN(35, 35); _FL_DEFPIN(36, 36); _FL_DEFPIN(37, 37); _FL_DEFPIN(38, 38); _FL_DEFPIN(39, 39); +_FL_DEFPIN(40, 40); _FL_DEFPIN(41, 41); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44); +_FL_DEFPIN(45, 45); _FL_DEFPIN(47, 47); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AM_AP3_SFE_ARTEMIS_DK) + +#define MAX_PIN 49 +_FL_DEFPIN(0, 0); _FL_DEFPIN(1, 1); _FL_DEFPIN(2, 2); _FL_DEFPIN(3, 3); _FL_DEFPIN(4, 4); +_FL_DEFPIN(5, 5); _FL_DEFPIN(6, 6); _FL_DEFPIN(7, 7); _FL_DEFPIN(8, 8); _FL_DEFPIN(9, 9); +_FL_DEFPIN(10, 10); _FL_DEFPIN(11, 11); _FL_DEFPIN(12, 12); _FL_DEFPIN(13, 13); _FL_DEFPIN(14, 14); +_FL_DEFPIN(15, 15); _FL_DEFPIN(16, 16); _FL_DEFPIN(17, 17); _FL_DEFPIN(18, 18); _FL_DEFPIN(19, 19); +_FL_DEFPIN(20, 20); _FL_DEFPIN(21, 21); _FL_DEFPIN(22, 22); _FL_DEFPIN(23, 23); _FL_DEFPIN(24, 24); +_FL_DEFPIN(25, 25); _FL_DEFPIN(26, 26); _FL_DEFPIN(27, 27); _FL_DEFPIN(28, 28); _FL_DEFPIN(29, 29); +_FL_DEFPIN(31, 31); _FL_DEFPIN(32, 32); _FL_DEFPIN(33, 33); _FL_DEFPIN(34, 34); +_FL_DEFPIN(35, 35); _FL_DEFPIN(36, 36); _FL_DEFPIN(37, 37); _FL_DEFPIN(38, 38); _FL_DEFPIN(39, 39); +_FL_DEFPIN(40, 40); _FL_DEFPIN(41, 41); _FL_DEFPIN(42, 42); _FL_DEFPIN(43, 43); _FL_DEFPIN(44, 44); +_FL_DEFPIN(45, 45); _FL_DEFPIN(47, 47); _FL_DEFPIN(48, 48); _FL_DEFPIN(49, 49); +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#else + +#error "Unrecognised APOLLO3 board!" + +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_AVR_H diff --git a/src/platforms/apollo3/fastspi_apollo3.h b/src/platforms/apollo3/fastspi_apollo3.h new file mode 100644 index 0000000000..0c77d31978 --- /dev/null +++ b/src/platforms/apollo3/fastspi_apollo3.h @@ -0,0 +1,134 @@ +#ifndef __INC_FASTSPI_APOLLO3_H +#define __INC_FASTSPI_APOLLO3_H + +// This is the implementation of fastspi for the Apollo3. +// It uses fastgpio instead of actual SPI, which means you can use it on all pins. +// It can run slightly faster than the default fastpin (bit banging). + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_APOLLO3) + +#define FASTLED_ALL_PINS_HARDWARE_SPI + +template +class APOLLO3HardwareSPIOutput { + Selectable *m_pSelect; + +public: + APOLLO3HardwareSPIOutput() { m_pSelect = NULL; } + APOLLO3HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + + // set the object representing the selectable + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + // initialize the pins for fastgpio + void init() { + FastPin<_CLOCK_PIN>::setOutput(); + FastPin<_CLOCK_PIN>::lo(); + FastPin<_DATA_PIN>::setOutput(); + FastPin<_DATA_PIN>::lo(); + } + + // latch the CS select + void inline select() { /* TODO */ } + + // release the CS select + void inline release() { /* TODO */ } + + // wait until all queued up data has been written + static void waitFully() { /* TODO */ } + + // write a byte as bits + static void writeByte(uint8_t b) { + writeBit<7>(b); + writeBit<6>(b); + writeBit<5>(b); + writeBit<4>(b); + writeBit<3>(b); + writeBit<2>(b); + writeBit<1>(b); + writeBit<0>(b); + } + + // write a word out via SPI (returns immediately on writing register) + static void writeWord(uint16_t w) { + writeByte((uint8_t)((w >> 8) & 0xff)); + writeByte((uint8_t)(w & 0xff)); + } + + // A raw set of writing byte values, assumes setup/init/waiting done elsewhere + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { writeByte(value); } + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytesValue(uint8_t value, int len) { + select(); + writeBytesValueRaw(value, len); + release(); + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + template void writeBytes(register uint8_t *data, int len) { + uint8_t *end = data + len; + select(); + // could be optimized to write 16bit words out instead of 8bit bytes + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + waitFully(); + release(); + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytes(register uint8_t *data, int len) { writeBytes(data, len); } + + // write a single bit out, which bit from the passed in byte is determined by template parameter + template inline static void writeBit(uint8_t b) { + //waitFully(); + if(b & (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + for (uint32_t d = (_SPI_CLOCK_DIVIDER >> 1); d > 0; d--) { __NOP(); } + FastPin<_CLOCK_PIN>::lo(); + for (uint32_t d = (_SPI_CLOCK_DIVIDER >> 1); d > 0; d--) { __NOP(); } + } + + // write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template + // parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping + template void writePixels(PixelController pixels) { + select(); + + int len = pixels.mLen; + + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + } + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + //waitFully(); + release(); + } + +}; + +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/apollo3/led_sysdefs_apollo3.h b/src/platforms/apollo3/led_sysdefs_apollo3.h new file mode 100644 index 0000000000..be74e24de8 --- /dev/null +++ b/src/platforms/apollo3/led_sysdefs_apollo3.h @@ -0,0 +1,39 @@ +#ifndef __INC_LED_SYSDEFS_APOLLO3_H +#define __INC_LED_SYSDEFS_APOLLO3_H + +#define FASTLED_APOLLO3 + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 1 +#endif + +// Default to allowing interrupts +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 1 +#endif + +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + +#ifndef F_CPU +#define F_CPU 48000000 +#endif + +// Default to NOT using PROGMEM +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 0 +#endif + +// data type defs +typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */ +typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */ + +#define FASTLED_NO_PINMAP + +// reusing/abusing cli/sei defs for due +// These should be fine for the Apollo3. It has its own defines in cmsis_gcc.h +#define cli() __disable_irq(); //__disable_fault_irq(); +#define sei() __enable_irq(); //__enable_fault_irq(); + +#endif diff --git a/platforms/arm/common/m0clockless.h b/src/platforms/arm/common/m0clockless.h similarity index 99% rename from platforms/arm/common/m0clockless.h rename to src/platforms/arm/common/m0clockless.h index d5a0cf6f6b..6fd865954d 100644 --- a/platforms/arm/common/m0clockless.h +++ b/src/platforms/arm/common/m0clockless.h @@ -25,9 +25,9 @@ showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, u // high register variable register const uint8_t *leds = _leds; #if (FASTLED_SCALE8_FIXED == 1) - pData->s[0]++; - pData->s[1]++; - pData->s[2]++; + ++pData->s[0]; + ++pData->s[1]; + ++pData->s[2]; #endif asm __volatile__ ( /////////////////////////////////////////////////////////////////////////// diff --git a/src/platforms/arm/d21/clockless_arm_d21.h b/src/platforms/arm/d21/clockless_arm_d21.h new file mode 100644 index 0000000000..16526ed674 --- /dev/null +++ b/src/platforms/arm/d21/clockless_arm_d21.h @@ -0,0 +1,61 @@ +#ifndef __INC_CLOCKLESS_ARM_D21 +#define __INC_CLOCKLESS_ARM_D21 + +#include "../common/m0clockless.h" +FASTLED_NAMESPACE_BEGIN +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPinBB::port_ptr_t data_ptr_t; + typedef typename FastPinBB::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual void init() { + FastPinBB::setOutput(); + mPinMask = FastPinBB::mask(); + mPort = FastPinBB::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + cli(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + sei(); + mWait.mark(); + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController pixels) { + struct M0ClocklessData data; + data.d[0] = pixels.d[0]; + data.d[1] = pixels.d[1]; + data.d[2] = pixels.d[2]; + data.s[0] = pixels.mScale[0]; + data.s[1] = pixels.mScale[1]; + data.s[2] = pixels.mScale[2]; + data.e[0] = pixels.e[0]; + data.e[1] = pixels.e[1]; + data.e[2] = pixels.e[2]; + data.adj = pixels.mAdvance; + + typename FastPin::port_ptr_t portBase = FastPin::port(); + return showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin::mask(), pixels.mData, pixels.mLen, &data); + } + +}; + +FASTLED_NAMESPACE_END + + +#endif // __INC_CLOCKLESS_ARM_D21 diff --git a/platforms/arm/d21/fastled_arm_d21.h b/src/platforms/arm/d21/fastled_arm_d21.h similarity index 100% rename from platforms/arm/d21/fastled_arm_d21.h rename to src/platforms/arm/d21/fastled_arm_d21.h diff --git a/src/platforms/arm/d21/fastpin_arm_d21.h b/src/platforms/arm/d21/fastpin_arm_d21.h new file mode 100644 index 0000000000..93dc43c657 --- /dev/null +++ b/src/platforms/arm/d21/fastpin_arm_d21.h @@ -0,0 +1,274 @@ +#ifndef __INC_FASTPIN_ARM_SAM_H +#define __INC_FASTPIN_ARM_SAM_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be slightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found +/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. +/// The registers are data output, set output, clear output, toggle output, input, and direction + +template class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + #if 0 + inline static void setOutput() { + if(_BIT<8) { + _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4)); + } else { + _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4)); + } + } + inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + #endif + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTSET.reg = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTCLR.reg = _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUT.reg = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTTGL.reg = _MASK; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUT.reg; } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTSET.reg; } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTCLR.reg; } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +#define _R(T) struct __gen_struct_ ## T +#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } }; + +#define _FL_IO(L) _RD32(GPIO ## L) + +#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; + +// Actual pin definitions +#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) + +#define MAX_PIN 17 +_FL_DEFPIN( 8,23,1); +_FL_DEFPIN( 0, 9,1); _FL_DEFPIN( 1, 8,1); _FL_DEFPIN( 2, 2,1); _FL_DEFPIN( 3, 3,1); +_FL_DEFPIN( 6, 5,0); _FL_DEFPIN( 9, 6,0); _FL_DEFPIN(10, 7,0); _FL_DEFPIN(12, 2,0); +_FL_DEFPIN(A6, 9,1); _FL_DEFPIN(A7, 8,1); _FL_DEFPIN(A5, 2,1); _FL_DEFPIN(A4, 3,1); +_FL_DEFPIN(A1, 5,0); _FL_DEFPIN(A2, 6,0); _FL_DEFPIN(A3, 7,0); _FL_DEFPIN(A0, 2,0); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + + +#elif defined(ADAFRUIT_HALLOWING) + +#define MAX_PIN 20 +// 0 & 1 +_FL_DEFPIN( 0, 9, 0); _FL_DEFPIN( 1, 10, 0); +// 2, 3, 4 +_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 11, 0); _FL_DEFPIN( 4, 8, 0); +// 5, 6, 7 +_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 18, 0); _FL_DEFPIN( 7, 0, 0); +// 8, 9, 10 +_FL_DEFPIN( 8, 12, 0); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); +// 11, 12, 13 +_FL_DEFPIN(11, 21, 0); _FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0); +// 14, 15, 16 (A0 - A2) +_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 8, 1); _FL_DEFPIN(16, 9, 1); +// 17, 18, 19 (A3 - A5) +_FL_DEFPIN(17, 4, 0); _FL_DEFPIN(18, 5, 0); _FL_DEFPIN(19, 6, 0); + +#define SPI_DATA PIN_SPI_MOSI +#define SPI_CLOCK PIN_SPI_SCK + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(SEEED_XIAO_M0) + +#define MAX_PIN 10 +_FL_DEFPIN( 0, 2,0); _FL_DEFPIN( 1, 4,0); _FL_DEFPIN( 2,10,0); _FL_DEFPIN( 3,11,0); +_FL_DEFPIN( 4, 8,0); _FL_DEFPIN( 5, 9,0); _FL_DEFPIN( 6, 8,1); _FL_DEFPIN( 7, 9,1); +_FL_DEFPIN( 8, 7,0); _FL_DEFPIN( 9, 5,0); _FL_DEFPIN(10, 6,0); + +#define SPI_DATA 9 +#define SPI_CLOCK 8 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SEEED_ZERO) + +#define MAX_PIN 24 + +_FL_DEFPIN( 0,11,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,14,0); _FL_DEFPIN( 3,9,0); +_FL_DEFPIN( 4,8,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0); +_FL_DEFPIN( 8,6,0); _FL_DEFPIN( 9,7,0); _FL_DEFPIN( 10,18,0); _FL_DEFPIN( 11,16,0); +_FL_DEFPIN( 12,19,0); _FL_DEFPIN( 13,17,0); _FL_DEFPIN( 14,2,0); _FL_DEFPIN( 15,8,1); +_FL_DEFPIN( 16,9,1); _FL_DEFPIN( 17,4,0); _FL_DEFPIN( 18,5,0); _FL_DEFPIN( 19,2,1); +_FL_DEFPIN( 20,22,0); _FL_DEFPIN( 21,23,0); _FL_DEFPIN( 22,12,0); +_FL_DEFPIN( 23,10,1);//MOSI +_FL_DEFPIN( 24,11,1);//SCK + +#define SPI_DATA 23 +#define SPI_CLOCK 24 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SAMD_ZERO) + +#define MAX_PIN 42 +_FL_DEFPIN( 0,10,0); _FL_DEFPIN( 1,11,0); _FL_DEFPIN( 2, 8,0); _FL_DEFPIN( 3, 9,0); +_FL_DEFPIN( 4,14,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0); +_FL_DEFPIN( 8, 6,0); _FL_DEFPIN( 9, 7,0); _FL_DEFPIN(10,18,0); _FL_DEFPIN(11,16,0); +_FL_DEFPIN(12,19,0); _FL_DEFPIN(13,17,0); _FL_DEFPIN(14, 2,0); _FL_DEFPIN(15, 8,1); +_FL_DEFPIN(16, 9,1); _FL_DEFPIN(17, 4,0); _FL_DEFPIN(18, 5,0); _FL_DEFPIN(19, 2,1); +_FL_DEFPIN(20,22,0); _FL_DEFPIN(21,23,0); _FL_DEFPIN(22,12,0); _FL_DEFPIN(23,11,1); +_FL_DEFPIN(24,10,1); _FL_DEFPIN(25, 3,1); _FL_DEFPIN(26,27,0); _FL_DEFPIN(27,28,0); +_FL_DEFPIN(28,24,0); _FL_DEFPIN(29,25,0); _FL_DEFPIN(30,22,1); _FL_DEFPIN(31,23,1); +_FL_DEFPIN(32,22,0); _FL_DEFPIN(33,23,0); _FL_DEFPIN(34,19,0); _FL_DEFPIN(35,16,0); +_FL_DEFPIN(36,18,0); _FL_DEFPIN(37,17,0); _FL_DEFPIN(38,13,0); _FL_DEFPIN(39,21,0); +_FL_DEFPIN(40, 6,0); _FL_DEFPIN(41, 7,0); _FL_DEFPIN(42, 3,0); + +#define SPI_DATA 24 +#define SPI_CLOCK 23 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SODAQ_AUTONOMO) + +#define MAX_PIN 56 +_FL_DEFPIN( 0, 9,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,11,0); _FL_DEFPIN( 3,10,1); +_FL_DEFPIN( 4,11,1); _FL_DEFPIN( 5,12,1); _FL_DEFPIN( 6,13,1); _FL_DEFPIN( 7,14,1); +_FL_DEFPIN( 8,15,1); _FL_DEFPIN( 9,14,0); _FL_DEFPIN(10,15,0); _FL_DEFPIN(11,16,0); +_FL_DEFPIN(12,17,0); _FL_DEFPIN(13,18,0); _FL_DEFPIN(14,19,0); _FL_DEFPIN(15,16,1); +_FL_DEFPIN(16, 8,0); _FL_DEFPIN(17,28,0); _FL_DEFPIN(18,17,1); _FL_DEFPIN(19, 2,0); +_FL_DEFPIN(20, 6,0); _FL_DEFPIN(21, 5,0); _FL_DEFPIN(22, 4,0); _FL_DEFPIN(23, 9,1); +_FL_DEFPIN(24, 8,1); _FL_DEFPIN(25, 7,1); _FL_DEFPIN(26, 6,1); _FL_DEFPIN(27, 5,1); +_FL_DEFPIN(28, 4,1); _FL_DEFPIN(29, 7,0); _FL_DEFPIN(30, 3,1); _FL_DEFPIN(31, 2,1); +_FL_DEFPIN(32, 1,1); _FL_DEFPIN(33, 0,1); _FL_DEFPIN(34, 3,0); _FL_DEFPIN(35, 3,0); +_FL_DEFPIN(36,30,1); _FL_DEFPIN(37,31,1); _FL_DEFPIN(38,22,1); _FL_DEFPIN(39,23,1); +_FL_DEFPIN(40,12,0); _FL_DEFPIN(41,13,0); _FL_DEFPIN(42,22,0); _FL_DEFPIN(43,23,0); +_FL_DEFPIN(44,20,0); _FL_DEFPIN(45,21,0); _FL_DEFPIN(46,27,0); _FL_DEFPIN(47,24,0); +_FL_DEFPIN(48,25,0); _FL_DEFPIN(49,13,1); _FL_DEFPIN(50,14,1); _FL_DEFPIN(51,17,0); +_FL_DEFPIN(52,18,0); _FL_DEFPIN(53,12,1); _FL_DEFPIN(54,13,1); _FL_DEFPIN(55,14,1); +_FL_DEFPIN(56,15,1); + +#define SPI_DATA 44 +#define SPI_CLOCK 45 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SAMD_WINO) + +#define MAX_PIN 22 +_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 16, 0); _FL_DEFPIN( 3, 17, 0); +_FL_DEFPIN( 4, 18, 0); _FL_DEFPIN( 5, 19, 0); _FL_DEFPIN( 6, 24, 0); _FL_DEFPIN( 7, 25, 0); +_FL_DEFPIN( 8, 27, 0); _FL_DEFPIN( 9, 28, 0); _FL_DEFPIN( 10, 30, 0); _FL_DEFPIN( 11, 31, 0); +_FL_DEFPIN( 12, 15, 0); _FL_DEFPIN( 13, 14, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 3, 0); +_FL_DEFPIN( 16, 4, 0); _FL_DEFPIN( 17, 5, 0); _FL_DEFPIN( 18, 6, 0); _FL_DEFPIN( 19, 7, 0); +_FL_DEFPIN( 20, 8, 0); _FL_DEFPIN( 21, 9, 0); _FL_DEFPIN( 22, 10, 0); _FL_DEFPIN( 23, 11, 0); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) + +#define MAX_PIN 22 +_FL_DEFPIN( 0, 22, 0); _FL_DEFPIN( 1, 23, 0); _FL_DEFPIN( 2, 10, 0); _FL_DEFPIN( 3, 11, 0); +_FL_DEFPIN( 4, 10, 1); _FL_DEFPIN( 5, 11, 1); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0); +_FL_DEFPIN( 8, 16, 0); _FL_DEFPIN( 9, 17, 0); _FL_DEFPIN( 10, 19, 0); _FL_DEFPIN( 11, 8, 0); +_FL_DEFPIN( 12, 9, 0); _FL_DEFPIN( 13, 23, 1); _FL_DEFPIN( 14, 22, 1); _FL_DEFPIN( 15, 2, 0); +_FL_DEFPIN( 16, 2, 1); _FL_DEFPIN( 17, 3, 1); _FL_DEFPIN( 18, 4, 0); _FL_DEFPIN( 19, 5, 0); +_FL_DEFPIN( 20, 6, 0); _FL_DEFPIN( 21, 7, 0); + +#define SPI_DATA 8 +#define SPI_CLOCK 9 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_SAMD_NANO_33_IOT) + +#define MAX_PIN 26 +_FL_DEFPIN( 0, 23, 1); _FL_DEFPIN( 1, 22, 1); _FL_DEFPIN( 2, 10, 1); _FL_DEFPIN( 3, 11, 1); +_FL_DEFPIN( 4, 7, 0); _FL_DEFPIN( 5, 5, 0); _FL_DEFPIN( 6, 4, 0); _FL_DEFPIN( 7, 6, 0); +_FL_DEFPIN( 8, 18, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN( 10, 21, 0); _FL_DEFPIN( 11, 16, 0); +_FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 2, 1); +_FL_DEFPIN( 16, 11, 1); _FL_DEFPIN( 17, 10, 0); _FL_DEFPIN( 18, 8, 1); _FL_DEFPIN( 19, 9, 1); +_FL_DEFPIN( 20, 9, 0); _FL_DEFPIN( 21, 3, 1); _FL_DEFPIN( 22, 12, 0); _FL_DEFPIN( 23, 13, 0); +_FL_DEFPIN( 24, 14, 0); _FL_DEFPIN( 25, 15, 0); + +#define SPI_DATA 22 +#define SPI_CLOCK 25 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_GEMMA_M0) + +#define MAX_PIN 4 +_FL_DEFPIN( 0, 4, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 5, 0); +_FL_DEFPIN( 3, 0, 0); _FL_DEFPIN( 4, 1, 0); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ADAFRUIT_TRINKET_M0) + +#define MAX_PIN 7 +_FL_DEFPIN( 0, 8, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 9, 0); +_FL_DEFPIN( 3, 7, 0); _FL_DEFPIN( 4, 6, 0); _FL_DEFPIN( 7, 0, 0); _FL_DEFPIN( 8, 1, 0); + +#define SPI_DATA 4 +#define SPI_CLOCK 3 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ADAFRUIT_QTPY_M0) + +#define MAX_PIN 10 +_FL_DEFPIN( 0, 2, 0); _FL_DEFPIN( 1, 3, 0); _FL_DEFPIN( 2, 4, 0); _FL_DEFPIN( 3, 5, 0); +_FL_DEFPIN( 4, 16, 0); _FL_DEFPIN( 5, 17, 0); _FL_DEFPIN( 6, 6, 0); _FL_DEFPIN( 7, 7, 0); +_FL_DEFPIN( 8, 11, 0); _FL_DEFPIN( 9, 9, 0); _FL_DEFPIN( 10, 10, 0); + +#define SPI_DATA 10 +#define SPI_CLOCK 8 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + + +#elif defined(ADAFRUIT_ITSYBITSY_M0) + +#define MAX_PIN 16 +_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 9, 0); _FL_DEFPIN( 4, 8, 0); +_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0); +_FL_DEFPIN( 8, 6, 0); _FL_DEFPIN( 9, 7, 0); _FL_DEFPIN( 10, 18, 0); +_FL_DEFPIN( 11, 16, 0); _FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0); +_FL_DEFPIN( 29, 10, 0); // MOSI +_FL_DEFPIN( 30, 11, 0); // SCK +_FL_DEFPIN( 40, 0, 0); //APA102 Clock +_FL_DEFPIN( 41, 0, 1) //APA102 Data + +#define SPI_DATA 29 +#define SPI_CLOCK 30 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#endif + + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + + +#endif // __INC_FASTPIN_ARM_SAM_H diff --git a/platforms/arm/d21/led_sysdefs_arm_d21.h b/src/platforms/arm/d21/led_sysdefs_arm_d21.h similarity index 100% rename from platforms/arm/d21/led_sysdefs_arm_d21.h rename to src/platforms/arm/d21/led_sysdefs_arm_d21.h diff --git a/src/platforms/arm/d51/README.txt b/src/platforms/arm/d51/README.txt new file mode 100644 index 0000000000..b00fb670af --- /dev/null +++ b/src/platforms/arm/d51/README.txt @@ -0,0 +1,4 @@ +FastLED updates for adafruit FEATHER M4 and fixes to ITSBITSY M4 compiles + SAMD51 + +only tested on FEATHER M4 with DOTSTAR and neopixel strips diff --git a/src/platforms/arm/d51/clockless_arm_d51.h b/src/platforms/arm/d51/clockless_arm_d51.h new file mode 100644 index 0000000000..2bf00d2728 --- /dev/null +++ b/src/platforms/arm/d51/clockless_arm_d51.h @@ -0,0 +1,128 @@ +#ifndef __INC_CLOCKLESS_ARM_D51 +#define __INC_CLOCKLESS_ARM_D51 + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for SAMD51 +// See clockless.h for detailed info on how the template parameters are used. +#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control +#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks +#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register +#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count +#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register + + +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual void init() { + FastPin::setOutput(); + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } + + template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) { + for(register uint32_t i = BITS-1; i > 0; --i) { + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + FastPin::fastset(port, hi); + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } else { + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } + b <<= 1; + } + + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + FastPin::fastset(port, hi); + + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } else { + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + FastPin::fastset(port, lo); + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController pixels) { + // Get access to the clock + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + ARM_DWT_CYCCNT = 0; + + register data_ptr_t port = FastPin::port(); + register data_t hi = *port | FastPin::mask(); + register data_t lo = *port & ~FastPin::mask(); + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint8_t b = pixels.loadAndScale0(); + + cli(); + uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + + while(pixels.has(1)) { + pixels.stepDithering(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + // if interrupts took longer than 45µs, punt on the current frame + if(ARM_DWT_CYCCNT > next_mark) { + if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; } + } + + hi = *port | FastPin::mask(); + lo = *port & ~FastPin::mask(); + #endif + // Write first byte, read next byte + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.advanceAndLoadAndScale0(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + + sei(); + return ARM_DWT_CYCCNT; + } +}; + +FASTLED_NAMESPACE_END + +#endif diff --git a/platforms/arm/d51/fastled_arm_d51.h b/src/platforms/arm/d51/fastled_arm_d51.h similarity index 100% rename from platforms/arm/d51/fastled_arm_d51.h rename to src/platforms/arm/d51/fastled_arm_d51.h diff --git a/src/platforms/arm/d51/fastpin_arm_d51.h b/src/platforms/arm/d51/fastpin_arm_d51.h new file mode 100644 index 0000000000..9d31cedb4b --- /dev/null +++ b/src/platforms/arm/d51/fastpin_arm_d51.h @@ -0,0 +1,138 @@ +#ifndef __INC_FASTPIN_ARM_D51_H +#define __INC_FASTPIN_ARM_D51_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be slightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found +/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. +/// The registers are data output, set output, clear output, toggle output, input, and direction + +template class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + #if 0 + inline static void setOutput() { + if(_BIT<8) { + _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4)); + } else { + _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4)); + } + } + inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + #endif + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTSET.reg = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTCLR.reg = _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT->Group[_GRP].OUT.reg = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTTGL.reg = _MASK; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUT.reg; } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTSET.reg; } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTCLR.reg; } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +#define _R(T) struct __gen_struct_ ## T +#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } }; + +#define _FL_IO(L) _RD32(GPIO ## L) + +#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; + +// Actual pin definitions +#if defined(ADAFRUIT_ITSYBITSY_M4_EXPRESS) + +#define MAX_PIN 19 +// D0-D13, including D6+D8 (DotStar CLK + DATA) +_FL_DEFPIN( 0, 16, 0); _FL_DEFPIN( 1, 17, 0); _FL_DEFPIN( 2, 7, 0); _FL_DEFPIN( 3, 22, 1); +_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 2, 1); _FL_DEFPIN( 7, 18, 0); +_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0); +_FL_DEFPIN(12, 23, 0); _FL_DEFPIN(13, 22, 0); +// A0-A5 +_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1); +_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */ +// SDA/SCL +_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0); + +// 23..25 MISO/SCK/MOSI +_FL_DEFPIN(23, 23, 1); _FL_DEFPIN(24, 1, 0); _FL_DEFPIN(25, 0, 0); + +#define SPI_DATA 25 +#define SPI_CLOCK 24 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +// Actual pin definitions +#elif defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) + +#define MAX_PIN 20 +// D0-D13, including D6+D8 (DotStar CLK + DATA) +_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 17, 1); _FL_DEFPIN( 3, 16, 1); +_FL_DEFPIN( 4, 13, 1); _FL_DEFPIN( 5, 14, 1); _FL_DEFPIN( 6, 15, 1); _FL_DEFPIN( 7, 12, 1); +_FL_DEFPIN( 8, 21, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN(10, 18, 0); _FL_DEFPIN(11, 19, 0); +_FL_DEFPIN(12, 17, 0); _FL_DEFPIN(13, 16, 0); +// A0-A5 +_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 6, 0); _FL_DEFPIN(17, 0, 1); +_FL_DEFPIN(18, 8, 1); _FL_DEFPIN(19, 9, 1); +// SDA/SCL +_FL_DEFPIN(22, 2, 1); _FL_DEFPIN(23, 3, 1); + +// 23..25 MISO/SCK/MOSI +_FL_DEFPIN(24, 14, 0); _FL_DEFPIN(25, 13, 0); _FL_DEFPIN(26, 12, 0); + +#define SPI_DATA 26 +#define SPI_CLOCK 25 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ADAFRUIT_FEATHER_M4_EXPRESS) + +#define MAX_PIN 19 +// D0-D13, including D8 (neopixel) no pins 2 3 +_FL_DEFPIN( 0, 17, 1); _FL_DEFPIN( 1, 16, 1); +_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 16, 0); _FL_DEFPIN( 6, 18, 0); +_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0); +_FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0); +// A0-A5 +_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1); +_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */ +// SDA/SCL +_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0); +// 23..25 MISO/MOSI/SCK +_FL_DEFPIN(23, 22, 1); _FL_DEFPIN(24, 23, 1); _FL_DEFPIN(25, 17, 0); + +#define SPI_DATA 24 +#define SPI_CLOCK 25 + +#define HAS_HARDWARE_PIN_SUPPORT 1 +#endif + + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + + +#endif // __INC_FASTPIN_ARM_D51_H diff --git a/platforms/arm/d51/led_sysdefs_arm_d51.h b/src/platforms/arm/d51/led_sysdefs_arm_d51.h similarity index 100% rename from platforms/arm/d51/led_sysdefs_arm_d51.h rename to src/platforms/arm/d51/led_sysdefs_arm_d51.h diff --git a/platforms/arm/k20/clockless_arm_k20.h b/src/platforms/arm/k20/clockless_arm_k20.h similarity index 92% rename from platforms/arm/k20/clockless_arm_k20.h rename to src/platforms/arm/k20/clockless_arm_k20.h index bc2090b3ad..0a7f7b94b7 100644 --- a/platforms/arm/k20/clockless_arm_k20.h +++ b/src/platforms/arm/k20/clockless_arm_k20.h @@ -17,6 +17,7 @@ class ClocklessController : public CPixelLEDController { data_t mPinMask; data_ptr_t mPort; CMinWait mWait; + public: virtual void init() { FastPin::setOutput(); @@ -27,18 +28,17 @@ class ClocklessController : public CPixelLEDController { virtual uint16_t getMaxRefreshRate() const { return 400; } protected: - virtual void showPixels(PixelController & pixels) { - mWait.wait(); + mWait.wait(); if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); - } - mWait.mark(); - } + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) { - for(register uint32_t i = BITS-1; i > 0; i--) { + for(register uint32_t i = BITS-1; i > 0; --i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); FastPin::fastset(port, hi); @@ -74,8 +74,8 @@ class ClocklessController : public CPixelLEDController { ARM_DWT_CYCCNT = 0; register data_ptr_t port = FastPin::port(); - register data_t hi = *port | FastPin::mask();; - register data_t lo = *port & ~FastPin::mask();; + register data_t hi = *port | FastPin::mask(); + register data_t lo = *port & ~FastPin::mask(); *port = lo; // Setup the pixel controller and load/scale the first byte diff --git a/platforms/arm/k20/clockless_block_arm_k20.h b/src/platforms/arm/k20/clockless_block_arm_k20.h similarity index 97% rename from platforms/arm/k20/clockless_block_arm_k20.h rename to src/platforms/arm/k20/clockless_block_arm_k20.h index 66c6191c77..9beeb9fa9f 100644 --- a/platforms/arm/k20/clockless_block_arm_k20.h +++ b/src/platforms/arm/k20/clockless_block_arm_k20.h @@ -27,6 +27,7 @@ class InlineBlockClocklessController : public CPixelLEDController mWait; + public: virtual int size() { return CLEDController::size() * LANES; } @@ -93,7 +94,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels); register uint8_t scale = pixels.template getscale(pixels); - for(register uint32_t i = 0; i < (USED_LANES/2); i++) { + for(register uint32_t i = 0; i < (USED_LANES/2); ++i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PORT_MASK; @@ -117,7 +118,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels,USED_LANES-1,d,scale); } - for(register uint32_t i = USED_LANES/2; i < 8; i++) { + for(register uint32_t i = USED_LANES/2; i < 8; ++i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PORT_MASK; @@ -150,7 +151,7 @@ class InlineBlockClocklessController : public CPixelLEDController mWait; + public: virtual void init() { static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!"); @@ -250,7 +252,7 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController(pixels); register uint8_t scale = pixels.template getscale(pixels); - for(register uint32_t i = 0; (i < LANES) && (i < 8); i++) { + for(register uint32_t i = 0; (i < LANES) && (i < 8); ++i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PMASK_LO; @@ -286,7 +288,7 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; -#define _IO32(L) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); +#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR)); -#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin : public _ARMPIN class FastPin : public _ARMPIN {}; \ template<> class FastPinBB : public _ARMPIN_BITBAND {}; // Actual pin definitions -#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY) +_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4); -_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); +#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY) #define MAX_PIN 33 -_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 12, A); -_DEFPIN_ARM(4, 13, A); _DEFPIN_ARM(5, 7, D); _DEFPIN_ARM(6, 4, D); _DEFPIN_ARM(7, 2, D); -_DEFPIN_ARM(8, 3, D); _DEFPIN_ARM(9, 3, C); _DEFPIN_ARM(10, 4, C); _DEFPIN_ARM(11, 6, C); -_DEFPIN_ARM(12, 7, C); _DEFPIN_ARM(13, 5, C); _DEFPIN_ARM(14, 1, D); _DEFPIN_ARM(15, 0, C); -_DEFPIN_ARM(16, 0, B); _DEFPIN_ARM(17, 1, B); _DEFPIN_ARM(18, 3, B); _DEFPIN_ARM(19, 2, B); -_DEFPIN_ARM(20, 5, D); _DEFPIN_ARM(21, 6, D); _DEFPIN_ARM(22, 1, C); _DEFPIN_ARM(23, 2, C); -_DEFPIN_ARM(24, 5, A); _DEFPIN_ARM(25, 19, B); _DEFPIN_ARM(26, 1, E); _DEFPIN_ARM(27, 9, C); -_DEFPIN_ARM(28, 8, C); _DEFPIN_ARM(29, 10, C); _DEFPIN_ARM(30, 11, C); _DEFPIN_ARM(31, 0, E); -_DEFPIN_ARM(32, 18, B); _DEFPIN_ARM(33, 4, A); +_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 12, A); +_FL_DEFPIN(4, 13, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D); +_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C); +_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C); +_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B); +_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C); +_FL_DEFPIN(24, 5, A); _FL_DEFPIN(25, 19, B); _FL_DEFPIN(26, 1, E); _FL_DEFPIN(27, 9, C); +_FL_DEFPIN(28, 8, C); _FL_DEFPIN(29, 10, C); _FL_DEFPIN(30, 11, C); _FL_DEFPIN(31, 0, E); +_FL_DEFPIN(32, 18, B); _FL_DEFPIN(33, 4, A); #define SPI_DATA 11 #define SPI_CLOCK 13 diff --git a/platforms/arm/k20/fastspi_arm_k20.h b/src/platforms/arm/k20/fastspi_arm_k20.h similarity index 98% rename from platforms/arm/k20/fastspi_arm_k20.h rename to src/platforms/arm/k20/fastspi_arm_k20.h index 70210a396a..3d492558dc 100644 --- a/platforms/arm/k20/fastspi_arm_k20.h +++ b/src/platforms/arm/k20/fastspi_arm_k20.h @@ -29,6 +29,7 @@ template class BitWork { public: static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork::highestBit(); } }; + template class BitWork { public: static int highestBit() __attribute__((always_inline)) { return 0; } @@ -86,7 +87,7 @@ template void getScalars(uint32_t & preScalar, uint32_t & scalar, uint dbl = 0; if(scalar == 0) { dbl = 1; } - else if(scalar < 3) { scalar--; } + else if(scalar < 3) { --scalar; } } } return; @@ -94,7 +95,7 @@ template void getScalars(uint32_t & preScalar, uint32_t & scalar, uint #define SPIX (*(SPI_t*)pSPIX) -template +template class ARMHardwareSPIOutput { Selectable *m_pSelect; SPIState gState; @@ -246,7 +247,6 @@ class ARMHardwareSPIOutput { ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } - void init() { // set the pins to output FastPin<_DATA_PIN>::setOutput(); @@ -316,8 +316,8 @@ class ARMHardwareSPIOutput { if(WAIT_STATE == PRE) { wait(); } cli(); SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) | - ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | - SPI_PUSHR_CTAS(1) | (w & 0xFFFF); + ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | + SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); if(WAIT_STATE == POST) { wait(); } @@ -327,8 +327,8 @@ class ARMHardwareSPIOutput { if(WAIT_STATE == PRE) { wait(); } cli(); SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) | - ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | - SPI_PUSHR_CTAS(0) | (b & 0xFF); + ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | + SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); if(WAIT_STATE == POST) { wait(); } diff --git a/platforms/arm/k20/led_sysdefs_arm_k20.h b/src/platforms/arm/k20/led_sysdefs_arm_k20.h similarity index 100% rename from platforms/arm/k20/led_sysdefs_arm_k20.h rename to src/platforms/arm/k20/led_sysdefs_arm_k20.h diff --git a/platforms/arm/k20/octows2811_controller.h b/src/platforms/arm/k20/octows2811_controller.h similarity index 84% rename from platforms/arm/k20/octows2811_controller.h rename to src/platforms/arm/k20/octows2811_controller.h index 63f6d8f62d..f365e61f8d 100644 --- a/platforms/arm/k20/octows2811_controller.h +++ b/src/platforms/arm/k20/octows2811_controller.h @@ -28,7 +28,7 @@ class COctoWS2811Controller : public CPixelLEDController { } public: COctoWS2811Controller() { pocto = NULL; } - + virtual int size() { return CLEDController::size() * 8; } virtual void init() { /* do nothing yet */ } @@ -44,11 +44,11 @@ class COctoWS2811Controller : public CPixelLEDController { while(pixels.has(1)) { Lines b; - for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale0(i); } + for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale0(i); } transpose8x1_MSB(b.bytes,pData); pData += 8; - for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale1(i); } + for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale1(i); } transpose8x1_MSB(b.bytes,pData); pData += 8; - for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale2(i); } + for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale2(i); } transpose8x1_MSB(b.bytes,pData); pData += 8; pixels.stepDithering(); pixels.advanceData(); diff --git a/src/platforms/arm/k20/smartmatrix_t3.h b/src/platforms/arm/k20/smartmatrix_t3.h new file mode 100644 index 0000000000..c9747f0b41 --- /dev/null +++ b/src/platforms/arm/k20/smartmatrix_t3.h @@ -0,0 +1,54 @@ +#ifndef __INC_SMARTMATRIX_T3_H +#define __INC_SMARTMATRIX_T3_H + +#ifdef SmartMatrix_h +#include + +FASTLED_NAMESPACE_BEGIN + +extern SmartMatrix *pSmartMatrix; + +// note - dmx simple must be included before FastSPI for this code to be enabled +class CSmartMatrixController : public CPixelLEDController { + SmartMatrix matrix; + +public: + // initialize the LED controller + virtual void init() { + // Initialize 32x32 LED Matrix + matrix.begin(); + matrix.setBrightness(255); + matrix.setColorCorrection(ccNone); + + // Clear screen + clearLeds(0); + matrix.swapBuffers(); + pSmartMatrix = &matrix; + } + + virtual void showPixels(PixelController & pixels) { + if(SMART_MATRIX_CAN_TRIPLE_BUFFER) { + rgb24 *md = matrix.getRealBackBuffer(); + } else { + rgb24 *md = matrix.backBuffer(); + } + while(pixels.has(1)) { + md->red = pixels.loadAndScale0(); + md->green = pixels.loadAndScale1(); + md->blue = pixels.loadAndScale2(); + md++; + pixels.advanceData(); + pixels.stepDithering(); + } + matrix.swapBuffers(); + if(SMART_MATRIX_CAN_TRIPLE_BUFFER && pixels.advanceBy() > 0) { + matrix.setBackBuffer(pixels.mData); + } + } +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif diff --git a/src/platforms/arm/k20/ws2812serial_controller.h b/src/platforms/arm/k20/ws2812serial_controller.h new file mode 100644 index 0000000000..a761dd49ee --- /dev/null +++ b/src/platforms/arm/k20/ws2812serial_controller.h @@ -0,0 +1,47 @@ +#ifndef __INC_WS2812SERIAL_CONTROLLER_H +#define __INC_WS2812SERIAL_CONTROLLER_H + +#ifdef USE_WS2812SERIAL + +FASTLED_NAMESPACE_BEGIN + +template +class CWS2812SerialController : public CPixelLEDController { + WS2812Serial *pserial; + uint8_t *drawbuffer,*framebuffer; + + void _init(int nLeds) { + if (pserial == NULL) { + drawbuffer = (uint8_t*)malloc(nLeds * 3); + framebuffer = (uint8_t*)malloc(nLeds * 12); + pserial = new WS2812Serial(nLeds, framebuffer, drawbuffer, DATA_PIN, WS2812_RGB); + pserial->begin(); + } + } + +public: + CWS2812SerialController() { pserial = NULL; } + + virtual void init() { /* do nothing yet */ } + + virtual void showPixels(PixelController & pixels) { + _init(pixels.size()); + + uint8_t *p = drawbuffer; + + while(pixels.has(1)) { + *p++ = pixels.loadAndScale0(); + *p++ = pixels.loadAndScale1(); + *p++ = pixels.loadAndScale2(); + pixels.stepDithering(); + pixels.advanceData(); + } + pserial->show(); + } + +}; + +FASTLED_NAMESPACE_END + +#endif // USE_WS2812SERIAL +#endif // __INC_WS2812SERIAL_CONTROLLER_H diff --git a/platforms/arm/k66/clockless_arm_k66.h b/src/platforms/arm/k66/clockless_arm_k66.h similarity index 92% rename from platforms/arm/k66/clockless_arm_k66.h rename to src/platforms/arm/k66/clockless_arm_k66.h index 6102105754..e9dcc0cd39 100644 --- a/platforms/arm/k66/clockless_arm_k66.h +++ b/src/platforms/arm/k66/clockless_arm_k66.h @@ -17,6 +17,7 @@ class ClocklessController : public CPixelLEDController { data_t mPinMask; data_ptr_t mPort; CMinWait mWait; + public: virtual void init() { FastPin::setOutput(); @@ -27,18 +28,17 @@ class ClocklessController : public CPixelLEDController { virtual uint16_t getMaxRefreshRate() const { return 400; } protected: - virtual void showPixels(PixelController & pixels) { - mWait.wait(); + mWait.wait(); if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); - } - mWait.mark(); - } + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) { - for(register uint32_t i = BITS-1; i > 0; i--) { + for(register uint32_t i = BITS-1; i > 0; --i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); FastPin::fastset(port, hi); @@ -74,8 +74,8 @@ class ClocklessController : public CPixelLEDController { ARM_DWT_CYCCNT = 0; register data_ptr_t port = FastPin::port(); - register data_t hi = *port | FastPin::mask();; - register data_t lo = *port & ~FastPin::mask();; + register data_t hi = *port | FastPin::mask(); + register data_t lo = *port & ~FastPin::mask(); *port = lo; // Setup the pixel controller and load/scale the first byte diff --git a/platforms/arm/k66/clockless_block_arm_k66.h b/src/platforms/arm/k66/clockless_block_arm_k66.h similarity index 94% rename from platforms/arm/k66/clockless_block_arm_k66.h rename to src/platforms/arm/k66/clockless_block_arm_k66.h index 85a8cc719d..70f8c7a590 100644 --- a/platforms/arm/k66/clockless_block_arm_k66.h +++ b/src/platforms/arm/k66/clockless_block_arm_k66.h @@ -30,6 +30,7 @@ class InlineBlockClocklessController : public CPixelLEDController mWait; + public: virtual int size() { return CLEDController::size() * LANES; } @@ -107,7 +108,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels); register uint8_t scale = pixels.template getscale(pixels); - for(register uint32_t i = 0; i < (USED_LANES/2); i++) { + for(register uint32_t i = 0; i < (USED_LANES/2); ++i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PORT_MASK; @@ -131,7 +132,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels,USED_LANES-1,d,scale); } - for(register uint32_t i = USED_LANES/2; i < 8; i++) { + for(register uint32_t i = USED_LANES/2; i < 8; ++i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PORT_MASK; @@ -153,7 +154,7 @@ class InlineBlockClocklessController : public CPixelLEDController &allpixels) { + static uint32_t showRGBInternal(PixelController &allpixels) { // Get access to the clock ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; @@ -164,7 +165,7 @@ class InlineBlockClocklessController : public CPixelLEDController mWait; + public: virtual void init() { static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!"); @@ -242,11 +244,11 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController & pixels) { mWait.wait(); uint32_t clocks = showRGBInternal(pixels); - #if FASTLED_ALLOW_INTERRUPTS == 0 + #if FASTLED_ALLOW_INTERRUPTS == 0 // Adjust the timer long microsTaken = CLKS_TO_MICROS(clocks); MS_COUNTER += (1 + (microsTaken / 1000)); - #endif + #endif mWait.mark(); } @@ -264,7 +266,7 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController(pixels); register uint8_t scale = pixels.template getscale(pixels); - for(register uint32_t i = 0; (i < LANES) && (i < 8); i++) { + for(register uint32_t i = 0; (i < LANES) && (i < 8); ++i) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PMASK_LO; @@ -286,10 +288,9 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController &allpixels) { + static uint32_t showRGBInternal(PixelController &allpixels) { // Get access to the clock ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; @@ -300,7 +301,7 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController next_mark) { - if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; } + if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { + sei(); + return ARM_DWT_CYCCNT; } } - #endif + #endif // Write first byte, read next byte writeBits<8+XTRA0,1>(next_mark, b0, allpixels); @@ -327,9 +330,9 @@ class SixteenWayInlineBlockClocklessController : public CPixelLEDController(next_mark, b0, allpixels); - #if 0 && (FASTLED_ALLOW_INTERRUPTS == 1) + #if 0 && (FASTLED_ALLOW_INTERRUPTS == 1) sei(); - #endif + #endif }; sei(); diff --git a/platforms/arm/k66/fastled_arm_k66.h b/src/platforms/arm/k66/fastled_arm_k66.h similarity index 100% rename from platforms/arm/k66/fastled_arm_k66.h rename to src/platforms/arm/k66/fastled_arm_k66.h diff --git a/platforms/arm/k66/fastpin_arm_k66.h b/src/platforms/arm/k66/fastpin_arm_k66.h similarity index 74% rename from platforms/arm/k66/fastpin_arm_k66.h rename to src/platforms/arm/k66/fastpin_arm_k66.h index e201096ccb..ef48396c45 100644 --- a/platforms/arm/k66/fastpin_arm_k66.h +++ b/src/platforms/arm/k66/fastpin_arm_k66.h @@ -78,35 +78,35 @@ template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; -#define _IO32(L) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); +#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR)); -#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin : public _ARMPIN class FastPin : public _ARMPIN {}; \ template<> class FastPinBB : public _ARMPIN_BITBAND {}; +_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4); + // Actual pin definitions #if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY) -_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); - #define MAX_PIN 63 -_DEFPIN_ARM( 0, 16, B); _DEFPIN_ARM( 1, 17, B); _DEFPIN_ARM( 2, 0, D); _DEFPIN_ARM( 3, 12, A); -_DEFPIN_ARM( 4, 13, A); _DEFPIN_ARM( 5, 7, D); _DEFPIN_ARM( 6, 4, D); _DEFPIN_ARM( 7, 2, D); -_DEFPIN_ARM( 8, 3, D); _DEFPIN_ARM( 9, 3, C); _DEFPIN_ARM(10, 4, C); _DEFPIN_ARM(11, 6, C); -_DEFPIN_ARM(12, 7, C); _DEFPIN_ARM(13, 5, C); _DEFPIN_ARM(14, 1, D); _DEFPIN_ARM(15, 0, C); -_DEFPIN_ARM(16, 0, B); _DEFPIN_ARM(17, 1, B); _DEFPIN_ARM(18, 3, B); _DEFPIN_ARM(19, 2, B); -_DEFPIN_ARM(20, 5, D); _DEFPIN_ARM(21, 6, D); _DEFPIN_ARM(22, 1, C); _DEFPIN_ARM(23, 2, C); -_DEFPIN_ARM(24, 26, E); _DEFPIN_ARM(25, 5, A); _DEFPIN_ARM(26, 14, A); _DEFPIN_ARM(27, 15, A); -_DEFPIN_ARM(28, 16, A); _DEFPIN_ARM(29, 18, B); _DEFPIN_ARM(30, 19, B); _DEFPIN_ARM(31, 10, B); -_DEFPIN_ARM(32, 11, B); _DEFPIN_ARM(33, 24, E); _DEFPIN_ARM(34, 25, E); _DEFPIN_ARM(35, 8, C); -_DEFPIN_ARM(36, 9, C); _DEFPIN_ARM(37, 10, C); _DEFPIN_ARM(38, 11, C); _DEFPIN_ARM(39, 17, A); -_DEFPIN_ARM(40, 28, A); _DEFPIN_ARM(41, 29, A); _DEFPIN_ARM(42, 26, A); _DEFPIN_ARM(43, 20, B); -_DEFPIN_ARM(44, 22, B); _DEFPIN_ARM(45, 23, B); _DEFPIN_ARM(46, 21, B); _DEFPIN_ARM(47, 8, D); -_DEFPIN_ARM(48, 9, D); _DEFPIN_ARM(49, 4, B); _DEFPIN_ARM(50, 5, B); _DEFPIN_ARM(51, 14, D); -_DEFPIN_ARM(52, 13, D); _DEFPIN_ARM(53, 12, D); _DEFPIN_ARM(54, 15, D); _DEFPIN_ARM(55, 11, D); -_DEFPIN_ARM(56, 10, E); _DEFPIN_ARM(57, 11, E); _DEFPIN_ARM(58, 0, E); _DEFPIN_ARM(59, 1, E); -_DEFPIN_ARM(60, 2, E); _DEFPIN_ARM(61, 3, E); _DEFPIN_ARM(62, 4, E); _DEFPIN_ARM(63, 5, E); +_FL_DEFPIN( 0, 16, B); _FL_DEFPIN( 1, 17, B); _FL_DEFPIN( 2, 0, D); _FL_DEFPIN( 3, 12, A); +_FL_DEFPIN( 4, 13, A); _FL_DEFPIN( 5, 7, D); _FL_DEFPIN( 6, 4, D); _FL_DEFPIN( 7, 2, D); +_FL_DEFPIN( 8, 3, D); _FL_DEFPIN( 9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C); +_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C); +_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B); +_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C); +_FL_DEFPIN(24, 26, E); _FL_DEFPIN(25, 5, A); _FL_DEFPIN(26, 14, A); _FL_DEFPIN(27, 15, A); +_FL_DEFPIN(28, 16, A); _FL_DEFPIN(29, 18, B); _FL_DEFPIN(30, 19, B); _FL_DEFPIN(31, 10, B); +_FL_DEFPIN(32, 11, B); _FL_DEFPIN(33, 24, E); _FL_DEFPIN(34, 25, E); _FL_DEFPIN(35, 8, C); +_FL_DEFPIN(36, 9, C); _FL_DEFPIN(37, 10, C); _FL_DEFPIN(38, 11, C); _FL_DEFPIN(39, 17, A); +_FL_DEFPIN(40, 28, A); _FL_DEFPIN(41, 29, A); _FL_DEFPIN(42, 26, A); _FL_DEFPIN(43, 20, B); +_FL_DEFPIN(44, 22, B); _FL_DEFPIN(45, 23, B); _FL_DEFPIN(46, 21, B); _FL_DEFPIN(47, 8, D); +_FL_DEFPIN(48, 9, D); _FL_DEFPIN(49, 4, B); _FL_DEFPIN(50, 5, B); _FL_DEFPIN(51, 14, D); +_FL_DEFPIN(52, 13, D); _FL_DEFPIN(53, 12, D); _FL_DEFPIN(54, 15, D); _FL_DEFPIN(55, 11, D); +_FL_DEFPIN(56, 10, E); _FL_DEFPIN(57, 11, E); _FL_DEFPIN(58, 0, E); _FL_DEFPIN(59, 1, E); +_FL_DEFPIN(60, 2, E); _FL_DEFPIN(61, 3, E); _FL_DEFPIN(62, 4, E); _FL_DEFPIN(63, 5, E); diff --git a/platforms/arm/k66/fastspi_arm_k66.h b/src/platforms/arm/k66/fastspi_arm_k66.h similarity index 98% rename from platforms/arm/k66/fastspi_arm_k66.h rename to src/platforms/arm/k66/fastspi_arm_k66.h index 7e598cff4b..f990741b4f 100644 --- a/platforms/arm/k66/fastspi_arm_k66.h +++ b/src/platforms/arm/k66/fastspi_arm_k66.h @@ -37,6 +37,7 @@ template class BitWork { public: static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork::highestBit(); } }; + template class BitWork { public: static int highestBit() __attribute__((always_inline)) { return 0; } @@ -94,7 +95,7 @@ template void getScalars(uint32_t & preScalar, uint32_t & scalar, uint dbl = 0; if(scalar == 0) { dbl = 1; } - else if(scalar < 3) { scalar--; } + else if(scalar < 3) { --scalar; } } } return; @@ -102,7 +103,7 @@ template void getScalars(uint32_t & preScalar, uint32_t & scalar, uint #define SPIX (*(SPI_t*)pSPIX) -template +template class ARMHardwareSPIOutput { Selectable *m_pSelect; SPIState gState; @@ -248,7 +249,6 @@ class ARMHardwareSPIOutput { // CORE_PIN14_CONFIG = gState.pins[3]; } - public: ARMHardwareSPIOutput() { m_pSelect = NULL; } ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } @@ -323,8 +323,8 @@ class ARMHardwareSPIOutput { static void writeWord(uint16_t w) __attribute__((always_inline)) { if(WAIT_STATE == PRE) { wait(); } SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) | - ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | - SPI_PUSHR_CTAS(1) | (w & 0xFFFF); + ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | + SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; if(WAIT_STATE == POST) { wait(); } } @@ -332,8 +332,8 @@ class ARMHardwareSPIOutput { static void writeByte(uint8_t b) __attribute__((always_inline)) { if(WAIT_STATE == PRE) { wait(); } SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) | - ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | - SPI_PUSHR_CTAS(0) | (b & 0xFF); + ((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) | + SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; if(WAIT_STATE == POST) { wait(); } } diff --git a/platforms/arm/k66/led_sysdefs_arm_k66.h b/src/platforms/arm/k66/led_sysdefs_arm_k66.h similarity index 100% rename from platforms/arm/k66/led_sysdefs_arm_k66.h rename to src/platforms/arm/k66/led_sysdefs_arm_k66.h diff --git a/platforms/arm/kl26/clockless_arm_kl26.h b/src/platforms/arm/kl26/clockless_arm_kl26.h similarity index 100% rename from platforms/arm/kl26/clockless_arm_kl26.h rename to src/platforms/arm/kl26/clockless_arm_kl26.h diff --git a/platforms/arm/kl26/fastled_arm_kl26.h b/src/platforms/arm/kl26/fastled_arm_kl26.h similarity index 100% rename from platforms/arm/kl26/fastled_arm_kl26.h rename to src/platforms/arm/kl26/fastled_arm_kl26.h diff --git a/platforms/arm/kl26/fastpin_arm_kl26.h b/src/platforms/arm/kl26/fastpin_arm_kl26.h similarity index 77% rename from platforms/arm/kl26/fastpin_arm_kl26.h rename to src/platforms/arm/kl26/fastpin_arm_kl26.h index 4c30cd784f..8b3cbdfef8 100644 --- a/platforms/arm/kl26/fastpin_arm_kl26.h +++ b/src/platforms/arm/kl26/fastpin_arm_kl26.h @@ -50,26 +50,26 @@ template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; -#define _IO32(L) _RD32(FGPIO ## L ## _PDOR); _RD32(FGPIO ## L ## _PSOR); _RD32(FGPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(FGPIO ## L ## _PDIR); _RD32(FGPIO ## L ## _PDDR); +#define _FL_IO(L,C) _RD32(FGPIO ## L ## _PDOR); _RD32(FGPIO ## L ## _PSOR); _RD32(FGPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(FGPIO ## L ## _PDIR); _RD32(FGPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(FGPIO ## L ## _PDOR)); -#define _DEFPIN_ARM(PIN, BIT, L) template<> class FastPin : public _ARMPIN class FastPin : public _ARMPIN {}; \ /* template<> class FastPinBB : public _ARMPIN_BITBAND {}; */ +_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4); + // Actual pin definitions #if defined(FASTLED_TEENSYLC) && defined(CORE_TEENSY) -_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); - #define MAX_PIN 26 -_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 1, A); -_DEFPIN_ARM(4, 2, A); _DEFPIN_ARM(5, 7, D); _DEFPIN_ARM(6, 4, D); _DEFPIN_ARM(7, 2, D); -_DEFPIN_ARM(8, 3, D); _DEFPIN_ARM(9, 3, C); _DEFPIN_ARM(10, 4, C); _DEFPIN_ARM(11, 6, C); -_DEFPIN_ARM(12, 7, C); _DEFPIN_ARM(13, 5, C); _DEFPIN_ARM(14, 1, D); _DEFPIN_ARM(15, 0, C); -_DEFPIN_ARM(16, 0, B); _DEFPIN_ARM(17, 1, B); _DEFPIN_ARM(18, 3, B); _DEFPIN_ARM(19, 2, B); -_DEFPIN_ARM(20, 5, D); _DEFPIN_ARM(21, 6, D); _DEFPIN_ARM(22, 1, C); _DEFPIN_ARM(23, 2, C); -_DEFPIN_ARM(24, 20, E); _DEFPIN_ARM(25, 21, E); _DEFPIN_ARM(26, 30, E); +_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 1, A); +_FL_DEFPIN(4, 2, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D); +_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C); +_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C); +_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B); +_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C); +_FL_DEFPIN(24, 20, E); _FL_DEFPIN(25, 21, E); _FL_DEFPIN(26, 30, E); #define SPI_DATA 11 #define SPI_CLOCK 13 diff --git a/platforms/arm/kl26/fastspi_arm_kl26.h b/src/platforms/arm/kl26/fastspi_arm_kl26.h similarity index 98% rename from platforms/arm/kl26/fastspi_arm_kl26.h rename to src/platforms/arm/kl26/fastspi_arm_kl26.h index 869b60546f..b1e766774d 100644 --- a/platforms/arm/kl26/fastspi_arm_kl26.h +++ b/src/platforms/arm/kl26/fastspi_arm_kl26.h @@ -82,7 +82,7 @@ template void getScalars(uint8_t & sppr, uint8_t & spr) { #define SPIX (*(KINETISL_SPI_t*)pSPIX) #define ARM_HARDWARE_SPI -template +template class ARMHardwareSPIOutput { Selectable *m_pSelect; diff --git a/platforms/arm/kl26/led_sysdefs_arm_kl26.h b/src/platforms/arm/kl26/led_sysdefs_arm_kl26.h similarity index 96% rename from platforms/arm/kl26/led_sysdefs_arm_kl26.h rename to src/platforms/arm/kl26/led_sysdefs_arm_kl26.h index 466d72953c..575e639992 100644 --- a/platforms/arm/kl26/led_sysdefs_arm_kl26.h +++ b/src/platforms/arm/kl26/led_sysdefs_arm_kl26.h @@ -13,7 +13,7 @@ // Default to allowing interrupts #ifndef FASTLED_ALLOW_INTERRUPTS -#define FASTLED_ALLOW_INTERRUPTS 1 +// #define FASTLED_ALLOW_INTERRUPTS 1 #endif #if FASTLED_ALLOW_INTERRUPTS == 1 diff --git a/src/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h new file mode 100644 index 0000000000..a7bcddf040 --- /dev/null +++ b/src/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h @@ -0,0 +1,212 @@ +#ifndef __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H +#define __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the teensy4 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY4) + +#define __FL_T4_MASK ((1<<(LANES))-1) +template +class FlexibleInlineBlockClocklessController : public CPixelLEDController { + uint8_t m_bitOffsets[16]; + uint8_t m_nActualLanes; + uint8_t m_nLowBit; + uint8_t m_nHighBit; + uint32_t m_nWriteMask; + uint8_t m_nOutBlocks; + uint32_t m_offsets[3]; + CMinWait mWait; + +public: + virtual int size() { return CLEDController::size() * m_nActualLanes; } + + // For each pin, if we've hit our lane count, break, otherwise set the pin to output, + // store the bit offset in our offset array, add this pin to the write mask, and if this + // pin ends a block sequence, then break out of the switch as well + #define _BLOCK_PIN(P) case P: { \ + if(m_nActualLanes == LANES) break; \ + FastPin

::setOutput(); \ + m_bitOffsets[m_nActualLanes++] = FastPin

::pinbit(); \ + m_nWriteMask |= FastPin

::mask(); \ + if( P == 27 || P == 7 || P == 30) break; \ + } + + virtual void init() { + // pre-initialize + memset(m_bitOffsets,0,16); + m_nActualLanes = 0; + m_nLowBit = 33; + m_nHighBit = 0; + m_nWriteMask = 0; + + // setup the bits and data tracking for parallel output + switch(FIRST_PIN) { + // GPIO6 block output + _BLOCK_PIN( 1); + _BLOCK_PIN( 0); + _BLOCK_PIN(24); + _BLOCK_PIN(25); + _BLOCK_PIN(19); + _BLOCK_PIN(18); + _BLOCK_PIN(14); + _BLOCK_PIN(15); + _BLOCK_PIN(17); + _BLOCK_PIN(16); + _BLOCK_PIN(22); + _BLOCK_PIN(23); + _BLOCK_PIN(20); + _BLOCK_PIN(21); + _BLOCK_PIN(26); + _BLOCK_PIN(27); + // GPIO7 block output + _BLOCK_PIN(10); + _BLOCK_PIN(12); + _BLOCK_PIN(11); + _BLOCK_PIN(13); + _BLOCK_PIN( 6); + _BLOCK_PIN( 9); + _BLOCK_PIN(32); + _BLOCK_PIN( 8); + _BLOCK_PIN( 7); + // GPIO 37 block output + _BLOCK_PIN(37); + _BLOCK_PIN(36); + _BLOCK_PIN(35); + _BLOCK_PIN(34); + _BLOCK_PIN(39); + _BLOCK_PIN(38); + _BLOCK_PIN(28); + _BLOCK_PIN(31); + _BLOCK_PIN(30); + } + + for(int i = 0; i < m_nActualLanes; ++i) { + if(m_bitOffsets[i] < m_nLowBit) { m_nLowBit = m_bitOffsets[i]; } + if(m_bitOffsets[i] > m_nHighBit) { m_nHighBit = m_bitOffsets[i]; } + } + + m_nOutBlocks = (m_nHighBit + 8)/8; + + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + #if FASTLED_ALLOW_INTERRUPTS == 0 + uint32_t clocks = showRGBInternal(pixels); + // Adjust the timer + long microsTaken = CLKS_TO_MICROS(clocks); + MS_COUNTER += (1 + (microsTaken / 1000)); + #else + showRGBInternal(pixels); + #endif + mWait.mark(); + } + + typedef union { + uint8_t bytes[32]; + uint8_t bg[4][8]; + uint16_t shorts[16]; + uint32_t raw[8]; + } _outlines; + + + template __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register _outlines & b, PixelController &pixels) { + _outlines b2; + transpose8x1(b.bg[3], b2.bg[3]); + transpose8x1(b.bg[2], b2.bg[2]); + transpose8x1(b.bg[1], b2.bg[1]); + transpose8x1(b.bg[0], b2.bg[0]); + + register uint8_t d = pixels.template getd(pixels); + register uint8_t scale = pixels.template getscale(pixels); + + int x = 0; + for(uint32_t i = 8; i > 0;) { + --i; + while(ARM_DWT_CYCCNT < next_mark); + *FastPin::sport() = m_nWriteMask; + next_mark = ARM_DWT_CYCCNT + m_offsets[0]; + + uint32_t out = (b2.bg[3][i] << 24) | (b2.bg[2][i] << 16) | (b2.bg[1][i] << 8) | b2.bg[0][i]; + + out = ((~out) & m_nWriteMask); + while((next_mark - ARM_DWT_CYCCNT) > m_offsets[1]); + *FastPin::cport() = out; + + out = m_nWriteMask; + while((next_mark - ARM_DWT_CYCCNT) > m_offsets[2]); + *FastPin::cport() = out; + + // Read and store up to two bytes + if (x < m_nActualLanes) { + b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale(pixels, x, d, scale); + ++x; + if (x < m_nActualLanes) { + b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale(pixels, x, d, scale); + ++x; + } + } + } + } + + uint32_t showRGBInternal(PixelController &allpixels) { + allpixels.preStepFirstByteDithering(); + _outlines b0; + uint32_t start = ARM_DWT_CYCCNT; + + for(int i = 0; i < m_nActualLanes; ++i) { + b0.bytes[m_bitOffsets[i]] = allpixels.loadAndScale0(i); + } + + cli(); + + m_offsets[0] = _FASTLED_NS_TO_DWT(T1+T2+T3); + m_offsets[1] = _FASTLED_NS_TO_DWT(T2+T3); + m_offsets[2] = _FASTLED_NS_TO_DWT(T3); + uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD)); + + uint32_t next_mark = ARM_DWT_CYCCNT + m_offsets[0]; + + while(allpixels.has(1)) { + allpixels.stepDithering(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + // if interrupts took longer than 45µs, punt on the current frame + if(ARM_DWT_CYCCNT > next_mark) { + if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; } + } + #endif + // Write first byte, read next byte + writeBits<8+XTRA0,1>(next_mark, b0, allpixels); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0,2>(next_mark, b0, allpixels); + allpixels.advanceData(); + + // Write third byte + writeBits<8+XTRA0,0>(next_mark, b0, allpixels); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + } + + sei(); + + return ARM_DWT_CYCCNT - start; + } +}; + +template class CHIPSET, uint8_t DATA_PIN, int NUM_LANES, EOrder RGB_ORDER=GRB> +class __FIBCC : public FlexibleInlineBlockClocklessController::__T1(),CHIPSET::__T2(),CHIPSET::__T3(),RGB_ORDER,CHIPSET::__XTRA0(),CHIPSET::__FLIP(),CHIPSET::__WAIT_TIME()> {}; + +#define __FASTLED_HAS_FIBCC 1 + +#endif //defined(FASTLED_TEENSY4) + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h new file mode 100644 index 0000000000..ed3be816c9 --- /dev/null +++ b/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h @@ -0,0 +1,131 @@ +#ifndef __INC_CLOCKLESS_ARM_MXRT1062_H +#define __INC_CLOCKLESS_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the teensy4 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY4) + +#define FASTLED_HAS_CLOCKLESS 1 + +#define _FASTLED_NS_TO_DWT(_NS) (((F_CPU_ACTUAL>>16)*(_NS)) / (1000000000UL>>16)) + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + uint32_t off[3]; + +public: + static constexpr int __DATA_PIN() { return DATA_PIN; } + static constexpr int __T1() { return T1; } + static constexpr int __T2() { return T2; } + static constexpr int __T3() { return T3; } + static constexpr EOrder __RGB_ORDER() { return RGB_ORDER; } + static constexpr int __XTRA0() { return XTRA0; } + static constexpr bool __FLIP() { return FLIP; } + static constexpr int __WAIT_TIME() { return WAIT_TIME; } + + virtual void init() { + FastPin::setOutput(); + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + FastPin::lo(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } + + template __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register uint32_t & b) { + for(register uint32_t i = BITS-1; i > 0; --i) { + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + off[0]; + FastPin::hi(); + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > off[1]); + FastPin::lo(); + } else { + while((next_mark - ARM_DWT_CYCCNT) > off[2]); + FastPin::lo(); + } + b <<= 1; + } + + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + off[1]; + FastPin::hi(); + + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > off[2]); + FastPin::lo(); + } else { + while((next_mark - ARM_DWT_CYCCNT) > off[2]); + FastPin::lo(); + } + } + + uint32_t showRGBInternal(PixelController pixels) { + uint32_t start = ARM_DWT_CYCCNT; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint32_t b = pixels.loadAndScale0(); + + cli(); + + off[0] = _FASTLED_NS_TO_DWT(T1+T2+T3); + off[1] = _FASTLED_NS_TO_DWT(T2+T3); + off[2] = _FASTLED_NS_TO_DWT(T3); + + uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD)); + + uint32_t next_mark = ARM_DWT_CYCCNT + off[0]; + + while(pixels.has(1)) { + pixels.stepDithering(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + // if interrupts took longer than 45µs, punt on the current frame + if(ARM_DWT_CYCCNT > next_mark) { + if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; } + } + #endif + // Write first byte, read next byte + writeBits<8+XTRA0>(next_mark, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(next_mark, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(next_mark, b); + b = pixels.advanceAndLoadAndScale0(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + + sei(); + return ARM_DWT_CYCCNT - start; + } +}; +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h new file mode 100644 index 0000000000..5098af335a --- /dev/null +++ b/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h @@ -0,0 +1,12 @@ +#ifndef __INC_FASTLED_ARM_MXRT1062_H +#define __INC_FASTLED_ARM_MXRT1062_H + +#include "fastpin_arm_mxrt1062.h" +#include "fastspi_arm_mxrt1062.h" +#include "../k20/octows2811_controller.h" +#include "../k20/ws2812serial_controller.h" +#include "../k20/smartmatrix_t3.h" +#include "clockless_arm_mxrt1062.h" +#include "block_clockless_arm_mxrt1062.h" + +#endif diff --git a/src/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h new file mode 100644 index 0000000000..8960a8c9c6 --- /dev/null +++ b/src/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h @@ -0,0 +1,91 @@ +#ifndef __FASTPIN_ARM_MXRT1062_H +#define __FASTPIN_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be slightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +/// Template definition for teensy 4.0 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. It calls through to pinMode for setting input/output on pins +/// The registers are data output, set output, clear output, toggle output, input, and direction +template class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _GPIO_DR_SET::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _GPIO_DR_CLEAR::r() = _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO_DR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _GPIO_DR_TOGGLE::r() = _MASK; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO_DR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO_DR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO_DR::r(); } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO_DR_SET::r(); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO_DR_CLEAR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } + inline static uint32_t pinbit() __attribute__ ((always_inline)) { return _BIT; } +}; + + +#define _R(T) struct __gen_struct_ ## T +#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } }; +#define _FL_IO(L) _RD32(GPIO ## L ## _DR); _RD32(GPIO ## L ## _DR_SET); _RD32(GPIO ## L ## _DR_CLEAR); _RD32(GPIO ## L ## _DR_TOGGLE); _FL_DEFINE_PORT(L, _R(GPIO ## L ## _DR)); + +// From the teensy core - it looks like there's the "default set" of port registers at GPIO1-5 - but then there +// are a mirrored set for GPIO1-4 at GPIO6-9, which in the teensy core is referred to as "fast" - while the pin definitiosn +// at https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=193716&viewfull=1#post193716 +// refer to GPIO1-4, we're going to use GPIO6-9 in the definitions below because the fast registers are what +// the teensy core is using internally +#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; + +#if defined(FASTLED_TEENSY4) && defined(CORE_TEENSY) +_FL_IO(1); _FL_IO(2); _FL_IO(3); _FL_IO(4); _FL_IO(5); +_FL_IO(6); _FL_IO(7); _FL_IO(8); _FL_IO(9); + +#define MAX_PIN 39 +_FL_DEFPIN( 0, 3,6); _FL_DEFPIN( 1, 2,6); _FL_DEFPIN( 2, 4,9); _FL_DEFPIN( 3, 5,9); +_FL_DEFPIN( 4, 6,9); _FL_DEFPIN( 5, 8,9); _FL_DEFPIN( 6,10,7); _FL_DEFPIN( 7,17,7); +_FL_DEFPIN( 8,16,7); _FL_DEFPIN( 9,11,7); _FL_DEFPIN(10, 0,7); _FL_DEFPIN(11, 2,7); +_FL_DEFPIN(12, 1,7); _FL_DEFPIN(13, 3,7); _FL_DEFPIN(14,18,6); _FL_DEFPIN(15,19,6); +_FL_DEFPIN(16,23,6); _FL_DEFPIN(17,22,6); _FL_DEFPIN(18,17,6); _FL_DEFPIN(19,16,6); +_FL_DEFPIN(20,26,6); _FL_DEFPIN(21,27,6); _FL_DEFPIN(22,24,6); _FL_DEFPIN(23,25,6); +_FL_DEFPIN(24,12,6); _FL_DEFPIN(25,13,6); _FL_DEFPIN(26,30,6); _FL_DEFPIN(27,31,6); +_FL_DEFPIN(28,18,8); _FL_DEFPIN(29,31,9); _FL_DEFPIN(30,23,8); _FL_DEFPIN(31,22,8); +_FL_DEFPIN(32,12,7); _FL_DEFPIN(33, 7,9); _FL_DEFPIN(34,15,8); _FL_DEFPIN(35,14,8); +_FL_DEFPIN(36,13,8); _FL_DEFPIN(37,12,8); _FL_DEFPIN(38,17,8); _FL_DEFPIN(39,16,8); + +#define HAS_HARDWARE_PIN_SUPPORT + +#define ARM_HARDWARE_SPI +#define SPI_DATA 11 +#define SPI_CLOCK 13 + +#define SPI1_DATA 26 +#define SPI1_CLOCK 27 + +#define SPI2_DATA 35 +#define SPI2_CLOCK 37 + +#endif // defined FASTLED_TEENSY4 + +#endif // FASTLED_FORCE_SOFTWARE_PINSs + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h new file mode 100644 index 0000000000..068c7be185 --- /dev/null +++ b/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h @@ -0,0 +1,140 @@ +#ifndef __INC_FASTSPI_ARM_MXRT1062_H +#define __INC_FASTSPI_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +#if defined (FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI) +#include + +template +class Teesy4HardwareSPIOutput { + Selectable *m_pSelect; + uint32_t m_bitCount; + uint32_t m_bitData; + inline IMXRT_LPSPI_t & port() __attribute__((always_inline)) { + switch(_SPI_INDEX) { + case 0: + return IMXRT_LPSPI4_S; + case 1: + return IMXRT_LPSPI3_S; + case 2: + return IMXRT_LPSPI1_S; + } + } + +public: + Teesy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;} + Teesy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;} + + // set the object representing the selectable -- ignore for now + void setSelect(Selectable *pSelect) { /* TODO */ } + + // initialize the SPI subssytem + void init() { _SPIObject.begin(); } + + // latch the CS select + void inline select() __attribute__((always_inline)) { + // begin the SPI transaction + _SPIObject.beginTransaction(SPISettings(_SPI_CLOCK_RATE, MSBFIRST, SPI_MODE0)); + if(m_pSelect != NULL) { m_pSelect->select(); } + } + + // release the CS select + void inline release() __attribute__((always_inline)) { + if(m_pSelect != NULL) { m_pSelect->release(); } + _SPIObject.endTransaction(); + } + + // wait until all queued up data has been written + static void waitFully() { /* TODO */ } + + // write a byte out via SPI (returns immediately on writing register) - + void inline writeByte(uint8_t b) __attribute__((always_inline)) { + if(m_bitCount == 0) { + _SPIObject.transfer(b); + } else { + // There's been a bit of data written, add that to the output as well + uint32_t outData = (m_bitData << 8) | b; + uint32_t tcr = port().TCR; + port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8+m_bitCount) - 1); // turn on 9 bit mode + port().TDR = outData; // output 9 bit data. + while ((port().RSR & LPSPI_RSR_RXEMPTY)) ; // wait while the RSR fifo is empty... + port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8) - 1); // turn back on 8 bit mode + port().RDR; + m_bitCount = 0; + } + } + + // write a word out via SPI (returns immediately on writing register) + void inline writeWord(uint16_t w) __attribute__((always_inline)) { + writeByte(((w>>8) & 0xFF)); + _SPIObject.transfer(w & 0xFF); + } + + // A raw set of writing byte values, assumes setup/init/waiting done elsewhere + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { _SPIObject.transfer(value); } + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytesValue(uint8_t value, int len) { + select(); writeBytesValueRaw(value, len); release(); + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + template void writeBytes(register uint8_t *data, int len) { + uint8_t *end = data + len; + select(); + // could be optimized to write 16bit words out instead of 8bit bytes + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + waitFully(); + release(); + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytes(register uint8_t *data, int len) { writeBytes(data, len); } + + // write a single bit out, which bit from the passed in byte is determined by template parameter + template inline void writeBit(uint8_t b) { + m_bitData = (m_bitData<<1) | ((b&(1< void writePixels(PixelController pixels) { + select(); + int len = pixels.mLen; + + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + } + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + release(); + } + +}; + + +#endif + +FASTLED_NAMESPACE_END +#endif diff --git a/src/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h b/src/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h new file mode 100644 index 0000000000..ac4908254c --- /dev/null +++ b/src/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h @@ -0,0 +1,43 @@ +#ifndef __INC_LED_SYSDEFS_ARM_MXRT1062_H +#define __INC_LED_SYSDEFS_ARM_MXRT1062_H + +#define FASTLED_TEENSY4 +#define FASTLED_ARM + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 1 +#endif + +// Default to allowing interrupts +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 1 +#endif + +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + +#if (F_CPU == 96000000) +#define CLK_DBL 1 +#endif + +// Get some system include files +#include +#include // for cli/se definitions + +// Define the register types +#if defined(ARDUINO) // && ARDUINO < 150 +typedef volatile uint32_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */ +typedef volatile uint32_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */ +#endif + +// extern volatile uint32_t systick_millis_count; +// # define MS_COUNTER systick_millis_count + +// Teensy4 provides progmem +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + + +#endif diff --git a/src/platforms/arm/nrf51/clockless_arm_nrf51.h b/src/platforms/arm/nrf51/clockless_arm_nrf51.h new file mode 100644 index 0000000000..c607e61e5f --- /dev/null +++ b/src/platforms/arm/nrf51/clockless_arm_nrf51.h @@ -0,0 +1,84 @@ +#ifndef __INC_CLOCKLESS_ARM_NRF51 +#define __INC_CLOCKLESS_ARM_NRF51 + +#if defined(NRF51) + +#include +#define FASTLED_HAS_CLOCKLESS 1 + +#if (FASTLED_ALLOW_INTERRUPTS==1) +#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0; +#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; } +#define INNER_SEI sei(); +#else +#define SEI_CHK +#define CLI_CHK +#define INNER_SEI delaycycles<1>(); +#endif + + +#include "../common/m0clockless.h" +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPinBB::port_ptr_t data_ptr_t; + typedef typename FastPinBB::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual void init() { + FastPinBB::setOutput(); + mPinMask = FastPinBB::mask(); + mPort = FastPinBB::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + cli(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + sei(); + mWait.mark(); + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController pixels) { + struct M0ClocklessData data; + data.d[0] = pixels.d[0]; + data.d[1] = pixels.d[1]; + data.d[2] = pixels.d[2]; + data.s[0] = pixels.mScale[0]; + data.s[1] = pixels.mScale[1]; + data.s[2] = pixels.mScale[2]; + data.e[0] = pixels.e[0]; + data.e[1] = pixels.e[1]; + data.e[2] = pixels.e[2]; + data.adj = pixels.mAdvance; + + typename FastPin::port_ptr_t portBase = FastPin::port(); + + // timer mode w/prescaler of 0 + LED_TIMER->MODE = TIMER_MODE_MODE_Timer; + LED_TIMER->PRESCALER = 0; + LED_TIMER->EVENTS_COMPARE[0] = 0; + LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; + LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk; + LED_TIMER->TASKS_START = 1; + + int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin::mask(), pixels.mData, pixels.mLen, &data); + + LED_TIMER->TASKS_STOP = 1; + return ret; // 0x00FFFFFF - _VAL; + } +}; + + +#endif // NRF51 +#endif // __INC_CLOCKLESS_ARM_NRF51 diff --git a/platforms/arm/nrf51/fastled_arm_nrf51.h b/src/platforms/arm/nrf51/fastled_arm_nrf51.h similarity index 100% rename from platforms/arm/nrf51/fastled_arm_nrf51.h rename to src/platforms/arm/nrf51/fastled_arm_nrf51.h diff --git a/src/platforms/arm/nrf51/fastpin_arm_nrf51.h b/src/platforms/arm/nrf51/fastpin_arm_nrf51.h new file mode 100644 index 0000000000..6005c44830 --- /dev/null +++ b/src/platforms/arm/nrf51/fastpin_arm_nrf51.h @@ -0,0 +1,119 @@ +#ifndef __FASTPIN_ARM_NRF51_H +#define __FASTPIN_ARM_NRF51_H + +#if defined(NRF51) +/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found +/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. +/// The registers are data output, set output, clear output, toggle output, input, and direction +#if 0 +template class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { _DIRSET::r() = _MASK; } + inline static void setInput() { _DIRCLR::r() = _MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +#define ADDR(X) *(volatile uint32_t*)X +#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset)) +#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518) +#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C) +#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508) +#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C) +#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504) + +#define _RD32_NRF(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; }}; + +_RD32_NRF(NR_DIRSET); +_RD32_NRF(NR_DIRCLR); +_RD32_NRF(NR_OUTSET); +_RD32_NRF(NR_OUTCLR); +_RD32_NRF(NR_OUT); + +#define _FL_DEFPIN(PIN) template<> class FastPin : public _ARMPIN {}; +#else + +typedef struct { /*!< GPIO Structure */ + // __I uint32_t RESERVED0[321]; + __IO uint32_t OUT; /*!< Write GPIO port. */ + __IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */ + __IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */ + __I uint32_t IN; /*!< Read GPIO port. */ + __IO uint32_t DIR; /*!< Direction of GPIO pins. */ + __IO uint32_t DIRSET; /*!< DIR set register. */ + __IO uint32_t DIRCLR; /*!< DIR clear register. */ + __I uint32_t RESERVED1[120]; + __IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */ +} FL_NRF_GPIO_Type; + +#define FL_NRF_GPIO_BASE 0x50000504UL +#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE) + +template class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; } + inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; } + + inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } + + inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; } +}; + + +#define _FL_DEFPIN(PIN) template<> class FastPin : public _ARMPIN {}; +#endif + +// Actual pin definitions +#define MAX_PIN 31 +_FL_DEFPIN(0); _FL_DEFPIN(1); _FL_DEFPIN(2); _FL_DEFPIN(3); +_FL_DEFPIN(4); _FL_DEFPIN(5); _FL_DEFPIN(6); _FL_DEFPIN(7); +_FL_DEFPIN(8); _FL_DEFPIN(9); _FL_DEFPIN(10); _FL_DEFPIN(11); +_FL_DEFPIN(12); _FL_DEFPIN(13); _FL_DEFPIN(14); _FL_DEFPIN(15); +_FL_DEFPIN(16); _FL_DEFPIN(17); _FL_DEFPIN(18); _FL_DEFPIN(19); +_FL_DEFPIN(20); _FL_DEFPIN(21); _FL_DEFPIN(22); _FL_DEFPIN(23); +_FL_DEFPIN(24); _FL_DEFPIN(25); _FL_DEFPIN(26); _FL_DEFPIN(27); +_FL_DEFPIN(28); _FL_DEFPIN(29); _FL_DEFPIN(30); _FL_DEFPIN(31); + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif + +#endif diff --git a/src/platforms/arm/nrf51/fastspi_arm_nrf51.h b/src/platforms/arm/nrf51/fastspi_arm_nrf51.h new file mode 100644 index 0000000000..6826ebcba3 --- /dev/null +++ b/src/platforms/arm/nrf51/fastspi_arm_nrf51.h @@ -0,0 +1,149 @@ +#ifndef __INC_FASTSPI_NRF_H +#define __INC_FASTSPI_NRF_H + +#ifdef NRF51 + +#ifndef FASTLED_FORCE_SOFTWARE_SPI +#define FASTLED_ALL_PINS_HARDWARE_SPI + +// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should +// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the +// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead) +template +class NRF51SPIOutput { + + struct saveData { + uint32_t sck; + uint32_t mosi; + uint32_t miso; + uint32_t freq; + uint32_t enable; + } mSavedData; + + void saveSPIData() { + mSavedData.sck = NRF_SPI0->PSELSCK; + mSavedData.mosi = NRF_SPI0->PSELMOSI; + mSavedData.miso = NRF_SPI0->PSELMISO; + mSavedData.freq = NRF_SPI0->FREQUENCY; + mSavedData.enable = NRF_SPI0->ENABLE; + } + + void restoreSPIData() { + NRF_SPI0->PSELSCK = mSavedData.sck; + NRF_SPI0->PSELMOSI = mSavedData.mosi; + NRF_SPI0->PSELMISO = mSavedData.miso; + NRF_SPI0->FREQUENCY = mSavedData.freq; + mSavedData.enable = NRF_SPI0->ENABLE; + } + +public: + NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); } + NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); } + + // set the object representing the selectable + void setSelect(Selectable *pSelect) { /* TODO */ } + + // initialize the SPI subssytem + void init() { + FastPin<_DATA_PIN>::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); + NRF_SPI0->PSELSCK = _CLOCK_PIN; + NRF_SPI0->PSELMOSI = _DATA_PIN; + NRF_SPI0->PSELMISO = 0xFFFFFFFF; + NRF_SPI0->FREQUENCY = 0x80000000; + NRF_SPI0->ENABLE = 1; + NRF_SPI0->EVENTS_READY = 0; + } + + // latch the CS select + void select() { saveSPIData(); init(); } + + // release the CS select + void release() { shouldWait(); restoreSPIData(); } + + static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) { + // static bool sWait=false; + // bool oldWait = sWait; + // sWait = wait; + // never going to bother with waiting since we're always running the spi clock at max speed on the rfduino + // TODO: When we set clock rate, implement/fix waiting properly, otherwise the world hangs up + return false; + } + + // wait until all queued up data has been written + static void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; } + static void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; } + + // write a byte out via SPI (returns immediately on writing register) + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); NRF_SPI0->TXD = b; NRF_SPI0->INTENCLR; shouldWait(true); } + + // write a word out via SPI (returns immediately on writing register) + static void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); } + + // A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes) + static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytesValue(uint8_t value, int len) { + select(); + while(len--) { + writeByte(value); + } + waitFully(); + release(); + } + + // A full cycle of writing a raw block of data out, including select, release, and waiting + template void writeBytes(uint8_t *data, int len) { + uint8_t *end = data + len; + select(); + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + waitFully(); + release(); + } + + void writeBytes(uint8_t *data, int len) { + writeBytes(data, len); + } + + // write a single bit out, which bit from the passed in byte is determined by template parameter + template inline static void writeBit(uint8_t b) { + waitFully(); + NRF_SPI0->ENABLE = 0; + if(b & 1<::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + FastPin<_CLOCK_PIN>::toggle(); + FastPin<_CLOCK_PIN>::toggle(); + NRF_SPI0->ENABLE = 1; + } + + template void writePixels(PixelController pixels) { + select(); + int len = pixels.mLen; + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + } + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + waitFully(); + release(); + } +}; + +#endif +#endif + +#endif diff --git a/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h b/src/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h similarity index 100% rename from platforms/arm/nrf51/led_sysdefs_arm_nrf51.h rename to src/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h diff --git a/src/platforms/arm/nrf52/arbiter_nrf52.h b/src/platforms/arm/nrf52/arbiter_nrf52.h new file mode 100644 index 0000000000..8972d2d233 --- /dev/null +++ b/src/platforms/arm/nrf52/arbiter_nrf52.h @@ -0,0 +1,114 @@ +#ifndef __INC_ARBITER_NRF52 +#define __INC_ARBITER_NRF52 + +#if defined(NRF52_SERIES) + +#include "led_sysdefs_arm_nrf52.h" + +//FASTLED_NAMESPACE_BEGIN + +typedef void (*FASTLED_NRF52_PWM_INTERRUPT_HANDLER)(); + +// a trick learned from other embedded projects .. +// use the enum as an index to a statically-allocated array +// to store unique information for that instance. +// also provides a count of how many instances were enabled. +// +// See led_sysdefs_arm_nrf52.h for selection.... +// +typedef enum _FASTLED_NRF52_ENABLED_PWM_INSTANCE { +#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0) + FASTLED_NRF52_PWM0_INSTANCE_IDX, +#endif +#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1) + FASTLED_NRF52_PWM1_INSTANCE_IDX, +#endif +#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2) + FASTLED_NRF52_PWM2_INSTANCE_IDX, +#endif +#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3) + FASTLED_NRF52_PWM3_INSTANCE_IDX, +#endif + FASTLED_NRF52_PWM_INSTANCE_COUNT +} FASTLED_NRF52_ENABLED_PWM_INSTANCES; + +static_assert(FASTLED_NRF52_PWM_INSTANCE_COUNT > 0, "Instance count must be greater than zero -- define FASTLED_NRF52_ENABLE_PWM_INSTNACE[n] (replace `[n]` with digit)"); + +template +class PWM_Arbiter { +private: + static_assert(_PWM_ID < 32, "PWM_ID over 31 breaks current arbitration bitmask"); + //const uint32_t _ACQUIRE_MASK = (1u << _PWM_ID) ; + //const uint32_t _CLEAR_MASK = ~((uint32_t)(1u << _PWM_ID)); + static uint32_t s_PwmInUse; + static NRF_PWM_Type * const s_PWM; + static IRQn_Type const s_PWM_IRQ; + static FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile s_Isr; + +public: + static void isr_handler() { + return s_Isr(); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static bool isAcquired() { + return (0u != (s_PwmInUse & 1u)); // _ACQUIRE_MASK + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void acquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) { + while (!tryAcquire(isr)); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) { + uint32_t oldValue = __sync_fetch_and_or(&s_PwmInUse, 1u); // _ACQUIRE_MASK + if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK + s_Isr = isr; + return true; + } + return false; + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseFromIsr() { + uint32_t oldValue = __sync_fetch_and_and(&s_PwmInUse, ~1u); // _CLEAR_MASK + if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK + // TODO: This should never be true... indicates was not held. + // Assert here? + (void)oldValue; + } + return; + } + FASTLED_NRF52_INLINE_ATTRIBUTE static NRF_PWM_Type * getPWM() { + return s_PWM; + } + FASTLED_NRF52_INLINE_ATTRIBUTE static IRQn_Type getIRQn() { return s_PWM_IRQ; } +}; +template NRF_PWM_Type * const PWM_Arbiter<_PWM_ID>::s_PWM = + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0) + (_PWM_ID == 0 ? NRF_PWM0 : + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1) + (_PWM_ID == 1 ? NRF_PWM1 : + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2) + (_PWM_ID == 2 ? NRF_PWM2 : + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3) + (_PWM_ID == 3 ? NRF_PWM3 : + #endif + (NRF_PWM_Type*)-1 + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0) + ) + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1) + ) + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2) + ) + #endif + #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3) + ) + #endif + ; +template IRQn_Type const PWM_Arbiter<_PWM_ID>::s_PWM_IRQ = ((IRQn_Type)((uint8_t)((uint32_t)(s_PWM) >> 12))); +template uint32_t PWM_Arbiter<_PWM_ID>::s_PwmInUse = 0; +template FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile PWM_Arbiter<_PWM_ID>::s_Isr = NULL; + +//FASTLED_NAMESPACE_END + +#endif // NRF52_SERIES +#endif // __INC_ARBITER_NRF52 \ No newline at end of file diff --git a/src/platforms/arm/nrf52/clockless_arm_nrf52.h b/src/platforms/arm/nrf52/clockless_arm_nrf52.h new file mode 100644 index 0000000000..1dd3cd94d6 --- /dev/null +++ b/src/platforms/arm/nrf52/clockless_arm_nrf52.h @@ -0,0 +1,390 @@ +#ifndef __INC_CLOCKLESS_ARM_NRF52 +#define __INC_CLOCKLESS_ARM_NRF52 + +#if defined(NRF52_SERIES) + + +//FASTLED_NAMESPACE_BEGIN + +#define FASTLED_HAS_CLOCKLESS 1 +#define FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING 144 // TODO: Figure out how to safely let this be calller-defined.... + +// nRF52810 has a single PWM peripheral (PWM0) +// nRF52832 has three PWM peripherals (PWM0, PWM1, PWM2) +// nRF52840 has four PWM peripherals (PWM0, PWM1, PWM2, PWM3) +// NOTE: Update platforms.cpp in root of FastLED library if this changes +#define FASTLED_NRF52_PWM_ID 0 + +extern uint32_t isrCount; + + +template +class ClocklessController : public CPixelLEDController<_RGB_ORDER> { + static_assert(FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING > 0, "Maximum string length must be positive value (FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING)"); + static_assert(_T1 > 0 , "negative values are not allowed"); + static_assert(_T2 > 0 , "negative values are not allowed"); + static_assert(_T3 > 0 , "negative values are not allowed"); + static_assert(_T1 < (0x8000u-2u), "_T1 must fit in 15 bits"); + static_assert(_T2 < (0x8000u-2u), "_T2 must fit in 15 bits"); + static_assert(_T3 < (0x8000u-2u), "_T3 must fit in 15 bits"); + static_assert(_T1 < (0x8000u-2u), "_T0H must fit in 15 bits"); + static_assert(_T1+_T2 < (0x8000u-2u), "_T1H must fit in 15 bits"); + static_assert(_T1+_T2+_T3 < (0x8000u-2u), "_TOP must fit in 15 bits"); + static_assert(_T1+_T2+_T3 <= PWM_COUNTERTOP_COUNTERTOP_Msk, "_TOP too large for peripheral"); + +private: + static const bool _INITIALIZE_PIN_HIGH = (_FLIP ? 1 : 0); + static const uint16_t _POLARITY_BIT = (_FLIP ? 0 : 0x8000); + + static const uint8_t _BITS_PER_PIXEL = (8 + _XTRA0) * 3; // NOTE: 3 means RGB only... + static const uint16_t _PWM_BUFFER_COUNT = (_BITS_PER_PIXEL * FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING); + static const uint8_t _T0H = ((uint16_t)(_T1 )); + static const uint8_t _T1H = ((uint16_t)(_T1+_T2 )); + static const uint8_t _TOP = ((uint16_t)(_T1+_T2+_T3)); + + // may as well be static, as can only attach one LED string per _DATA_PIN.... + static uint16_t s_SequenceBuffer[_PWM_BUFFER_COUNT]; + static uint16_t s_SequenceBufferValidElements; + static volatile uint32_t s_SequenceBufferInUse; + static CMinWait<_WAIT_TIME_MICROSECONDS> mWait; // ensure data has time to latch + + FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePinState() { + FastPin<_DATA_PIN>::setOutput(); + if (_INITIALIZE_PIN_HIGH) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePwmInstance(NRF_PWM_Type * pwm) { + + // Pins must be set before enabling the peripheral + pwm->PSEL.OUT[0] = FastPin<_DATA_PIN>::nrf_pin(); + pwm->PSEL.OUT[1] = NRF_PWM_PIN_NOT_CONNECTED; + pwm->PSEL.OUT[2] = NRF_PWM_PIN_NOT_CONNECTED; + pwm->PSEL.OUT[3] = NRF_PWM_PIN_NOT_CONNECTED; + nrf_pwm_enable(pwm); + nrf_pwm_configure(pwm, NRF_PWM_CLK_16MHz, NRF_PWM_MODE_UP, _TOP); + nrf_pwm_decoder_set(pwm, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO); + + // clear any prior shorts / interrupt enable bits + nrf_pwm_shorts_set(pwm, 0); + nrf_pwm_int_set(pwm, 0); + // clear all prior events + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_STOPPED); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED0); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED1); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND0); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND1); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_PWMPERIODEND); + nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_LOOPSDONE); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_ConfigurePwmSequence(NRF_PWM_Type * pwm) { + // config is easy, using SEQ0, no loops... + nrf_pwm_sequence_t sequenceConfig; + sequenceConfig.values.p_common = &(s_SequenceBuffer[0]); + sequenceConfig.length = s_SequenceBufferValidElements; + sequenceConfig.repeats = 0; // send the data once, and only once + sequenceConfig.end_delay = 0; // no extra delay at the end of SEQ[0] / SEQ[1] + nrf_pwm_sequence_set(pwm, 0, &sequenceConfig); + nrf_pwm_sequence_set(pwm, 1, &sequenceConfig); + nrf_pwm_loop_set(pwm, 0); + + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_EnableInterruptsAndShortcuts(NRF_PWM_Type * pwm) { + IRQn_Type irqn = PWM_Arbiter::getIRQn(); + // TODO: check API results... + uint32_t result; + + result = sd_nvic_SetPriority(irqn, configMAX_SYSCALL_INTERRUPT_PRIORITY); + (void)result; + result = sd_nvic_EnableIRQ(irqn); + (void)result; + + // shortcuts prevent (up to) 4-cycle delay from interrupt handler to next action + uint32_t shortsToEnable = 0; + shortsToEnable |= NRF_PWM_SHORT_SEQEND0_STOP_MASK; ///< SEQEND[0] --> STOP task. + shortsToEnable |= NRF_PWM_SHORT_SEQEND1_STOP_MASK; ///< SEQEND[1] --> STOP task. + //shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK; ///< LOOPSDONE --> SEQSTART[0] task. + //shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK; ///< LOOPSDONE --> SEQSTART[1] task. + shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_STOP_MASK; ///< LOOPSDONE --> STOP task. + nrf_pwm_shorts_set(pwm, shortsToEnable); + + // mark which events should cause interrupts... + uint32_t interruptsToEnable = 0; + interruptsToEnable |= NRF_PWM_INT_SEQEND0_MASK; + interruptsToEnable |= NRF_PWM_INT_SEQEND1_MASK; + interruptsToEnable |= NRF_PWM_INT_LOOPSDONE_MASK; + interruptsToEnable |= NRF_PWM_INT_STOPPED_MASK; + nrf_pwm_int_set(pwm, interruptsToEnable); + + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_StartTask(NRF_PWM_Type * pwm) { + nrf_pwm_task_trigger(pwm, NRF_PWM_TASK_SEQSTART0); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void spinAcquireSequenceBuffer() { + while (!tryAcquireSequenceBuffer()); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquireSequenceBuffer() { + return __sync_bool_compare_and_swap(&s_SequenceBufferInUse, 0, 1); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseSequenceBuffer() { + uint32_t tmp = __sync_val_compare_and_swap(&s_SequenceBufferInUse, 1, 0); + if (tmp != 1) { + // TODO: Error / Assert / log ? + } + } + +public: + static void isr_handler() { + NRF_PWM_Type * pwm = PWM_Arbiter::getPWM(); + IRQn_Type irqn = PWM_Arbiter::getIRQn(); + + // Currently, only use SEQUENCE 0, so only event + // of consequence is LOOPSDONE ... + if (nrf_pwm_event_check(pwm,NRF_PWM_EVENT_STOPPED)) { + nrf_pwm_event_clear(pwm,NRF_PWM_EVENT_STOPPED); + + // update the minimum time to next call + mWait.mark(); + // mark the sequence as no longer in use -- pointer, comparator, exchange value + releaseSequenceBuffer(); + // prevent further interrupts from PWM events + nrf_pwm_int_set(pwm, 0); + // disable PWM interrupts - None of the PWM IRQs are shared + // with other peripherals, avoiding complexity of shared IRQs. + sd_nvic_DisableIRQ(irqn); + // disable the PWM instance + nrf_pwm_disable(pwm); + // may take up to 4 cycles for writes to propagate (APB bus @ 16MHz) + asm __volatile__ ( "NOP; NOP; NOP; NOP;" ); + // release the PWM arbiter to be re-used by another LED string + PWM_Arbiter::releaseFromIsr(); + } + } + + + virtual void init() { + FASTLED_NRF52_DEBUGPRINT("Clockless Timings:\n"); + FASTLED_NRF52_DEBUGPRINT(" T0H == %d", _T0H); + FASTLED_NRF52_DEBUGPRINT(" T1H == %d", _T1H); + FASTLED_NRF52_DEBUGPRINT(" TOP == %d\n", _TOP); + // to avoid pin initialization from causing first LED to have invalid color, + // call mWait.mark() to ensure data latches before color data gets sent. + startPwmPlayback_InitializePinState(); + mWait.mark(); + + } + virtual uint16_t getMaxRefreshRate() const { return 800; } + + virtual void showPixels(PixelController<_RGB_ORDER> & pixels) { + // wait for the only sequence buffer to become available + spinAcquireSequenceBuffer(); + prepareSequenceBuffers(pixels); + // ensure any prior data had time to latch + mWait.wait(); + startPwmPlayback(s_SequenceBufferValidElements); + return; + } + + template + FASTLED_NRF52_INLINE_ATTRIBUTE static void WriteBitToSequence(uint8_t byte, uint16_t * e) { + *e = _POLARITY_BIT | (((byte & (1u << _BIT)) == 0) ? _T0H : _T1H); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void prepareSequenceBuffers(PixelController<_RGB_ORDER> & pixels) { + s_SequenceBufferValidElements = 0; + int32_t remainingSequenceElements = _PWM_BUFFER_COUNT; + uint16_t * e = s_SequenceBuffer; + uint32_t size_needed = pixels.size(); // count of pixels + size_needed *= (8 + _XTRA0); // bits per pixel + size_needed *= 2; // each bit takes two bytes + + if (size_needed > _PWM_BUFFER_COUNT) { + // TODO: assert()? + return; + } + + while (pixels.has(1) && (remainingSequenceElements >= _BITS_PER_PIXEL)) { + uint8_t b0 = pixels.loadAndScale0(); + WriteBitToSequence<7>(b0, e); ++e; + WriteBitToSequence<6>(b0, e); ++e; + WriteBitToSequence<5>(b0, e); ++e; + WriteBitToSequence<4>(b0, e); ++e; + WriteBitToSequence<3>(b0, e); ++e; + WriteBitToSequence<2>(b0, e); ++e; + WriteBitToSequence<1>(b0, e); ++e; + WriteBitToSequence<0>(b0, e); ++e; + if (_XTRA0 > 0) { + for (int i = 0; i < _XTRA0; ++i) { + WriteBitToSequence<0>(0,e); ++e; + } + } + uint8_t b1 = pixels.loadAndScale1(); + WriteBitToSequence<7>(b1, e); ++e; + WriteBitToSequence<6>(b1, e); ++e; + WriteBitToSequence<5>(b1, e); ++e; + WriteBitToSequence<4>(b1, e); ++e; + WriteBitToSequence<3>(b1, e); ++e; + WriteBitToSequence<2>(b1, e); ++e; + WriteBitToSequence<1>(b1, e); ++e; + WriteBitToSequence<0>(b1, e); ++e; + if (_XTRA0 > 0) { + for (int i = 0; i < _XTRA0; ++i) { + WriteBitToSequence<0>(0,e); ++e; + } + } + uint8_t b2 = pixels.loadAndScale2(); + WriteBitToSequence<7>(b2, e); ++e; + WriteBitToSequence<6>(b2, e); ++e; + WriteBitToSequence<5>(b2, e); ++e; + WriteBitToSequence<4>(b2, e); ++e; + WriteBitToSequence<3>(b2, e); ++e; + WriteBitToSequence<2>(b2, e); ++e; + WriteBitToSequence<1>(b2, e); ++e; + WriteBitToSequence<0>(b2, e); ++e; + if (_XTRA0 > 0) { + for (int i = 0; i < _XTRA0; ++i) { + WriteBitToSequence<0>(0,e); ++e; + } + } + + // advance pixel and sequence pointers + s_SequenceBufferValidElements += _BITS_PER_PIXEL; + remainingSequenceElements -= _BITS_PER_PIXEL; + pixels.advanceData(); + pixels.stepDithering(); + } + } + + + FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback(uint16_t bytesToSend) { + PWM_Arbiter::acquire(isr_handler); + NRF_PWM_Type * pwm = PWM_Arbiter::getPWM(); + + // mark the sequence as being in-use + __sync_fetch_and_or(&s_SequenceBufferInUse, 1); + + startPwmPlayback_InitializePinState(); + startPwmPlayback_InitializePwmInstance(pwm); + startPwmPlayback_ConfigurePwmSequence(pwm); + startPwmPlayback_EnableInterruptsAndShortcuts(pwm); + startPwmPlayback_StartTask(pwm); + return; + } + + +#if 0 + FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t* getRawSequenceBuffer() { return s_SequenceBuffer; } + FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getRawSequenceBufferSize() { return _PWM_BUFFER_COUNT; } + FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getSequenceBufferInUse() { return s_SequenceBufferInUse; } + FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawSequenceBuffer(uint16_t bytesToSend) { + mWait.wait(); // ensure min time between updates + startPwmPlayback(bytesToSend); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawBytes(uint8_t * arrayOfBytes, uint16_t bytesToSend) { + // wait for sequence buffer to be available + while (s_SequenceBufferInUse != 0); + + s_SequenceBufferValidElements = 0; + int32_t remainingSequenceElements = _PWM_BUFFER_COUNT; + uint16_t * e = s_SequenceBuffer; + uint8_t * nextByte = arrayOfBytes; + for (uint16_t bytesRemain = bytesToSend; + (remainingSequenceElements >= 8) && (bytesRemain > 0); + --bytesRemain, + remainingSequenceElements -= 8, + s_SequenceBufferValidElements += 8 + ) { + uint8_t b = *nextByte; + WriteBitToSequence<7,false>(b, e); ++e; + WriteBitToSequence<6,false>(b, e); ++e; + WriteBitToSequence<5,false>(b, e); ++e; + WriteBitToSequence<4,false>(b, e); ++e; + WriteBitToSequence<3,false>(b, e); ++e; + WriteBitToSequence<2,false>(b, e); ++e; + WriteBitToSequence<1,false>(b, e); ++e; + WriteBitToSequence<0,false>(b, e); ++e; + if (_XTRA0 > 0) { + for (int i = 0; i < _XTRA0; ++i) { + WriteBitToSequence<0,_FLIP>(0,e); ++e; + } + } + } + mWait.wait(); // ensure min time between updates + + startPwmPlayback(s_SequenceBufferValidElements); + } +#endif // 0 + +}; + +template +uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferValidElements = 0; +template +uint32_t volatile ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferInUse = 0; +template +uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBuffer[_PWM_BUFFER_COUNT]; +template +CMinWait<_WAIT_TIME_MICROSECONDS> ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::mWait; + +/* nrf_pwm solution +// +// When the nRF52 softdevice (e.g., BLE) is enabled, the CPU can be pre-empted +// at any time for radio interrupts. These interrupts cannot be disabled. +// The problem is, even simple BLE advertising interrupts may take **`348μs`** +// (per softdevice 1.40, see http://infocenter.nordicsemi.com/pdf/S140_SDS_v1.3.pdf) +// +// The nRF52 chips have a decent Easy-DMA-enabled PWM peripheral. +// +// The major downside: +// [] The PWM peripheral has a fixed input buffer size at 16 bits per clock cycle. +// (each clockless protocol bit == 2 bytes) +// +// The major upsides include: +// [] Fully asynchronous, freeing CPU for other tasks +// [] Softdevice interrupts do not affect PWM clocked output (reliable clocking) +// +// The initial solution generally does the following for showPixels(): +// [] wait for a sequence buffer to become available +// [] prepare the entire LED string's sequence (see `prepareSequenceBuffers()`) +// [] ensures minimum wait time from prior sequence's end +// +// Options after initial solution working: +// [] + +// TODO: Double-buffers, so one can be doing DMA while the second +// buffer is being prepared. +// TODO: Pool of buffers, so can keep N-1 active in DMA, while +// preparing data in the final buffer? +// Write another class similar to PWM_Arbiter, only for +// tracking use of sequence buffers? +// TODO: Use volatile variable to track buffers that the +// prior DMA operation is finished with, so can fill +// in those buffers with newly-prepared data... +// apis to send the pre-generated buffer. This would be essentially asynchronous, +// and result in efficient run time if the pixels are either (a) static, or +// (b) cycle through a limited number of options whose converted results can +// be cached and re-used. While simple, this method takes lots of extra RAM... +// 16 bits for every full clock (high/low) cycle. +// +// Clockless chips typically send 24 bits (3x 8-bit) per pixel. +// One odd clockless chip sends 36 bits (3x 12-bit) per pixel. +// Each bit requires a 16-bit sequence entry for the PWM peripheral. +// This gives approximately: +// 24 bpp 36 bpp +// ========================================== +// 1 pixel 48 bytes 72 bytes +// 32 pixels 1,536 bytes 2,304 bytes +// 64 pixels 3,072 bytes 4,608 bytes +// +// +// UPDATE: this is the method I'm choosing, to get _SOMETHING_ +// clockless working... 3k RAM for 64 pixels is acceptable +// for a first release, as it allows re-use of FASTLED +// color correction, dithering, etc. .... +*/ + +//FASTLED_NAMESPACE_END + +#endif // NRF52_SERIES +#endif // __INC_CLOCKLESS_ARM_NRF52 \ No newline at end of file diff --git a/src/platforms/arm/nrf52/fastled_arm_nrf52.h b/src/platforms/arm/nrf52/fastled_arm_nrf52.h new file mode 100644 index 0000000000..453003068e --- /dev/null +++ b/src/platforms/arm/nrf52/fastled_arm_nrf52.h @@ -0,0 +1,11 @@ +#ifndef __INC_FASTLED_ARM_NRF52_H +#define __INC_FASTLED_ARM_NRF52_H + +#include "led_sysdefs_arm_nrf52.h" +#include "arbiter_nrf52.h" +#include "fastpin_arm_nrf52.h" +#include "fastspi_arm_nrf52.h" +#include "clockless_arm_nrf52.h" + +#endif // #ifndef __INC_FASTLED_ARM_NRF52_H + diff --git a/src/platforms/arm/nrf52/fastpin_arm_nrf52.h b/src/platforms/arm/nrf52/fastpin_arm_nrf52.h new file mode 100644 index 0000000000..9d0a8ec990 --- /dev/null +++ b/src/platforms/arm/nrf52/fastpin_arm_nrf52.h @@ -0,0 +1,190 @@ +#ifndef __FASTPIN_ARM_NRF52_H +#define __FASTPIN_ARM_NRF52_H + + +/* +// +// Background: +// =========== +// the nRF52 has more than 32 ports, and thus must support +// two distinct GPIO port registers. +// +// For the nRF52 series, the structure to control the port is +// `NRF_GPIO_Type`, with separate addresses mapped for set, clear, etc. +// The two ports are defined as NRF_P0 and NRF_P1. +// An example declaration for the ports is: +// #define NRF_P0_BASE 0x50000000UL +// #define NRF_P1_BASE 0x50000300UL +// #define NRF_P0 ((NRF_GPIO_Type*)NRF_P0_BASE) +// #define NRF_P1 ((NRF_GPIO_Type*)NRF_P1_BASE) +// +// Therefore, ideally, the _FL_DEFPIN() macro would simply +// conditionally pass either NRF_P0 or NRF_P1 to the underlying +// FastPin<> template class class. +// +// The "pin" provided to the FastLED<> template (and which +// the _FL_DEFPIN() macro specializes for valid pins) is NOT +// the microcontroller port.pin, but the Arduino digital pin. +// Some boards have an identity mapping (e.g., nRF52832 Feather) +// but most do not. Therefore, the _FL_DEFPIN() macro +// must translate the Arduino pin to the mcu port.pin. +// +// +// Difficulties: +// ============= +// The goal is to avoid any such lookups, using compile-time +// optimized functions for speed, in line with FastLED's +// overall design goals. This means constexpr, compile-time +// and aggressive inlining of functions.... +// +// Right away, this precludes the use of g_ADigitalPinMap, +// which is not constexpr, and thus not available for +// preprocessor/compile-time optimizations. Therefore, +// we have to specialize FastPin, given a +// compile-time value for PIN, into at least a PORT and +// a BITMASK for the port. +// +// Arduino compiles using C++11 for at least Feather nRF52840 Express. +// C++11 is very restrictive about template parameters. +// Template parameters can only be: +// 1. a type (as most people expect) +// 2. a template +// 3. a constexpr native integer type +// +// Therefore, attempts to use `NRF_GPIO_Type *` as a +// template parameter will fail.... +// +// Solution: +// ========= +// The solution chosen is to define a unique structure for each port, +// whose SOLE purpose is to have a static inline function that +// returns the `NRF_GPIO_Type *` that is needed. +// +// Thus, while it's illegal to pass `NRF_P0` as a template +// parameter, it's perfectly legal to pass `__generated_struct_NRF_P0`, +// and have the template call a well-known `static inline` function +// that returns `NRF_P0` ... which is itself a compile-time constant. +// +// Note that additional magic can be applied that will automatically +// generate the structures. If you want to add that to this platform, +// check out the KL26 platform files for a starting point. +// +*/ + +// manually define two structures, to avoid fighting with preprocessor macros +struct __generated_struct_NRF_P0 { + FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() { + return NRF_P0_BASE; + } +}; +// Not all NRF52 chips have two ports. Only define if P1 is present. +#if defined(NRF_P1_BASE) +struct __generated_struct_NRF_P1 { + FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() { + return NRF_P1_BASE; + } +}; +#endif + + +// The actual class template can then use a typename, for what is essentially a constexpr NRF_GPIO_Type* +template class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + FASTLED_NRF52_INLINE_ATTRIBUTE static void setOutput() { + // OK for this to be more than one instruction, as unusual to quickly switch input/output modes + nrf_gpio_cfg( + nrf_pin(), + NRF_GPIO_PIN_DIR_OUTPUT, // set pin as output + NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering + NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled + NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds + NRF_GPIO_PIN_NOSENSE // pin sense level disabled + ); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void setInput() { + // OK for this to be more than one instruction, as unusual to quickly switch input/output modes + nrf_gpio_cfg( + nrf_pin(), + NRF_GPIO_PIN_DIR_INPUT, // set pin as input + NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering + NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled + NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds + NRF_GPIO_PIN_NOSENSE // pin sense level disabled + ); + } + FASTLED_NRF52_INLINE_ATTRIBUTE static void hi() { (reinterpret_cast(_PORT::r()))->OUTSET = _MASK; } // sets _MASK in the SET OUTPUT register (output set high) + FASTLED_NRF52_INLINE_ATTRIBUTE static void lo() { (reinterpret_cast(_PORT::r()))->OUTCLR = _MASK; } // sets _MASK in the CLEAR OUTPUT register (output set low) + FASTLED_NRF52_INLINE_ATTRIBUTE static void toggle() { (reinterpret_cast(_PORT::r()))->OUT ^= _MASK; } // toggles _MASK bits in the OUTPUT GPIO port directly + FASTLED_NRF52_INLINE_ATTRIBUTE static void strobe() { toggle(); toggle(); } // BUGBUG -- Is this used by FastLED? Without knowing (for example) SPI Speed? + FASTLED_NRF52_INLINE_ATTRIBUTE static port_t hival() { return (reinterpret_cast(_PORT::r()))->OUT | _MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 1 + FASTLED_NRF52_INLINE_ATTRIBUTE static port_t loval() { return (reinterpret_cast(_PORT::r()))->OUT & ~_MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 0 + FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t port() { return &((reinterpret_cast(_PORT::r()))->OUT); } // gets raw pointer to OUTPUT GPIO port + FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t cport() { return &((reinterpret_cast(_PORT::r()))->OUTCLR); } // gets raw pointer to SET DIRECTION GPIO port + FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t sport() { return &((reinterpret_cast(_PORT::r()))->OUTSET); } // gets raw pointer to CLEAR DIRECTION GPIO port + FASTLED_NRF52_INLINE_ATTRIBUTE static port_t mask() { return _MASK; } // gets the value of _MASK + FASTLED_NRF52_INLINE_ATTRIBUTE static void hi (register port_ptr_t port) { hi(); } // sets _MASK in the SET OUTPUT register (output set high) + FASTLED_NRF52_INLINE_ATTRIBUTE static void lo (register port_ptr_t port) { lo(); } // sets _MASK in the CLEAR OUTPUT register (output set low) + FASTLED_NRF52_INLINE_ATTRIBUTE static void set(register port_t val ) { (reinterpret_cast(_PORT::r()))->OUT = val; } // sets entire port's value (optimization used by FastLED) + FASTLED_NRF52_INLINE_ATTRIBUTE static void fastset(register port_ptr_t port, register port_t val) { *port = val; } + constexpr static uint32_t nrf_pin2() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); } + constexpr static bool LowSpeedOnlyRecommended() { + // Caller must always determine if high speed use if allowed on a given pin, + // because it depends on more than just the chip packaging ... it depends on entire board (and even system) design. + return false; // choosing default to be FALSE, to allow users to ATTEMPT to use high-speed on pins where support is not known + } + // Expose the nrf pin (port/pin combined), port, and pin as properties (e.g., for setting up SPI) + + FASTLED_NRF52_INLINE_ATTRIBUTE static uint32_t nrf_pin() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); } +}; + +// +// BOARD_PIN can be either the pin portion of a port.pin, or the combined NRF_GPIO_PIN_MAP() number. +// For example both the following two defines refer to P1.15 (pin 47) as Arduino pin 3: +// _FL_DEFPIN(3, 15, 1); +// _FL_DEFPIN(3, 47, 1); +// +// Similarly, the following defines are all equivalent: +// _DEFPIN_ARM_IDENTITY_P1(47); +// _FL_DEFPIN(47, 15, 1); +// _FL_DEFPIN(47, 47, 1); +// + +#define _FL_DEFPIN(ARDUINO_PIN, BOARD_PIN, BOARD_PORT) \ + template<> class FastPin : \ + public _ARMPIN< \ + 1u << (BOARD_PIN & 31u), \ + __generated_struct_NRF_P ## BOARD_PORT, \ + (BOARD_PIN / 32), \ + BOARD_PIN & 31u \ + > \ + {} + +#define _DEFPIN_ARM_IDENTITY_P0(ARDUINO_PIN) \ + template<> class FastPin : \ + public _ARMPIN< \ + 1u << (ARDUINO_PIN & 31u), \ + __generated_struct_NRF_P0, \ + 0, \ + (ARDUINO_PIN & 31u) + 0 \ + > \ + {} + +#define _DEFPIN_ARM_IDENTITY_P1(ARDUINO_PIN) \ + template<> class FastPin : \ + public _ARMPIN< \ + 1u << (ARDUINO_PIN & 31u), \ + __generated_struct_NRF_P1, \ + 1, \ + (ARDUINO_PIN & 31u) + 32 \ + > \ + {} + +// The actual pin definitions are in a separate header file... +#include "fastpin_arm_nrf52_variants.h" + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif // #ifndef __FASTPIN_ARM_NRF52_H diff --git a/src/platforms/arm/nrf52/fastpin_arm_nrf52_variants.h b/src/platforms/arm/nrf52/fastpin_arm_nrf52_variants.h new file mode 100644 index 0000000000..9020655c8a --- /dev/null +++ b/src/platforms/arm/nrf52/fastpin_arm_nrf52_variants.h @@ -0,0 +1,723 @@ +#ifndef __FASTPIN_ARM_NRF52_VARIANTS_H +#define __FASTPIN_ARM_NRF52_VARIANTS_H + +// use this to determine if found variant or not (avoid multiple boards at once) +#undef __FASTPIN_ARM_NRF52_VARIANT_FOUND + +// Adafruit Bluefruit nRF52832 Feather +// From https://www.adafruit.com/package_adafruit_index.json +#if defined (ARDUINO_NRF52832_FEATHER) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Adafruit Bluefruit nRF52832 Feather is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _DEFPIN_ARM_IDENTITY_P0( 0); // xtal 1 + _DEFPIN_ARM_IDENTITY_P0( 1); // xtal 2 + _DEFPIN_ARM_IDENTITY_P0( 2); // a0 + _DEFPIN_ARM_IDENTITY_P0( 3); // a1 + _DEFPIN_ARM_IDENTITY_P0( 4); // a2 + _DEFPIN_ARM_IDENTITY_P0( 5); // a3 + _DEFPIN_ARM_IDENTITY_P0( 6); // TXD + _DEFPIN_ARM_IDENTITY_P0( 7); // GPIO #7 + _DEFPIN_ARM_IDENTITY_P0( 8); // RXD + _DEFPIN_ARM_IDENTITY_P0( 9); // NFC1 + _DEFPIN_ARM_IDENTITY_P0(10); // NFC2 + _DEFPIN_ARM_IDENTITY_P0(11); // GPIO #11 + _DEFPIN_ARM_IDENTITY_P0(12); // SCK + _DEFPIN_ARM_IDENTITY_P0(13); // MOSI + _DEFPIN_ARM_IDENTITY_P0(14); // MISO + _DEFPIN_ARM_IDENTITY_P0(15); // GPIO #15 + _DEFPIN_ARM_IDENTITY_P0(16); // GPIO #16 + _DEFPIN_ARM_IDENTITY_P0(17); // LED #1 (red) + _DEFPIN_ARM_IDENTITY_P0(18); // SWO + _DEFPIN_ARM_IDENTITY_P0(19); // LED #2 (blue) + _DEFPIN_ARM_IDENTITY_P0(20); // DFU + // _DEFPIN_ARM_IDENTITY_P0(21); // Reset -- not valid to use for FastLED? + // _DEFPIN_ARM_IDENTITY_P0(22); // Factory Reset -- not vaild to use for FastLED? + // _DEFPIN_ARM_IDENTITY_P0(23); // N/A + // _DEFPIN_ARM_IDENTITY_P0(24); // N/A + _DEFPIN_ARM_IDENTITY_P0(25); // SDA + _DEFPIN_ARM_IDENTITY_P0(26); // SCL + _DEFPIN_ARM_IDENTITY_P0(27); // GPIO #27 + _DEFPIN_ARM_IDENTITY_P0(28); // A4 + _DEFPIN_ARM_IDENTITY_P0(29); // A5 + _DEFPIN_ARM_IDENTITY_P0(30); // A6 + _DEFPIN_ARM_IDENTITY_P0(31); // A7 +#endif // defined (ARDUINO_NRF52832_FEATHER) + +// Adafruit Circuit Playground Bluefruit +// From https://www.adafruit.com/package_adafruit_index.json +#if defined (ARDUINO_NRF52840_CIRCUITPLAY) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + + // This board is a bit of a mess ... as it defines + // multiple arduino pins to map to a single Port/Pin + // combination. + + // Use PIN_NEOPIXEL (D8) for the ten built-in neopixels + _FL_DEFPIN( 8, 13, 0); // P0.13 -- D8 / Neopixels + + // PIN_A0 is connect to an amplifier, and thus *might* + // not be suitable for use with FastLED. + // Do not enable this pin until can confirm + // signal integrity from this pin. + // + // NOTE: it might also be possible if first disable + // the amp using D11 ("speaker shutdown" pin) + // + // _FL_DEFPIN(14, 26, 0); // P0.26 -- A0 / D12 / Audio Out + _FL_DEFPIN(15, 2, 0); // P0.02 -- A1 / D6 + _FL_DEFPIN(16, 29, 0); // P0.29 -- A2 / D9 + _FL_DEFPIN(17, 3, 0); // P0.03 -- A3 / D10 + _FL_DEFPIN(18, 4, 0); // P0.04 -- A4 / D3 / SCL + _FL_DEFPIN(19, 5, 0); // P0.05 -- A5 / D2 / SDA + _FL_DEFPIN(20, 30, 0); // P0.30 -- A6 / D0 / UART RX + _FL_DEFPIN(21, 14, 0); // P0.14 -- AREF / D1 / UART TX + +#endif + +// Adafruit Bluefruit nRF52840 Feather Express +// From https://www.adafruit.com/package_adafruit_index.json +#if defined (ARDUINO_NRF52840_FEATHER) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + + // Arduino pins 0..7 + _FL_DEFPIN( 0, 25, 0); // D0 is P0.25 -- UART TX + //_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 -- UART RX + _FL_DEFPIN( 2, 10, 0); // D2 is P0.10 -- NFC2 + _FL_DEFPIN( 3, 47, 1); // D3 is P1.15 -- PIN_LED1 (red) + _FL_DEFPIN( 4, 42, 1); // D4 is P1.10 -- PIN_LED2 (blue) + _FL_DEFPIN( 5, 40, 1); // D5 is P1.08 -- SPI/SS + _FL_DEFPIN( 6, 7, 0); // D6 is P0.07 + _FL_DEFPIN( 7, 34, 1); // D7 is P1.02 -- PIN_DFU (Button) + + // Arduino pins 8..15 + _FL_DEFPIN( 8, 16, 0); // D8 is P0.16 -- PIN_NEOPIXEL + _FL_DEFPIN( 9, 26, 0); // D9 is P0.26 + _FL_DEFPIN(10, 27, 0); // D10 is P0.27 + _FL_DEFPIN(11, 6, 0); // D11 is P0.06 + _FL_DEFPIN(12, 8, 0); // D12 is P0.08 + _FL_DEFPIN(13, 41, 1); // D13 is P1.09 + _FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0 + _FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1 + + // Arduino pins 16..23 + _FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2 + _FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3 + _FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4 + _FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5 + //_FL_DEFPIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery! + //_FL_DEFPIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF + _FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA + _FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL + + // Arduino pins 24..31 + _FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO + _FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI + _FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK + //_FL_DEFPIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK + //_FL_DEFPIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS + //_FL_DEFPIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0 + //_FL_DEFPIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1 + //_FL_DEFPIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2 + + // Arduino pins 32..34 + //_FL_DEFPIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3 + //_FL_DEFPIN(33, 9, 0); // D33 is NFC1, only accessible via test point +#endif // defined (ARDUINO_NRF52840_FEATHER) + +// Adafruit Bluefruit nRF52840 Metro Express +// From https://www.adafruit.com/package_adafruit_index.json +#if defined (ARDUINO_NRF52840_METRO) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Adafruit Bluefruit nRF52840 Metro Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART TX) + _FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART RX) + _FL_DEFPIN( 2, 10, 1); // D2 is P1.10 + _FL_DEFPIN( 3, 4, 1); // D3 is P1.04 + _FL_DEFPIN( 4, 11, 1); // D4 is P1.11 + _FL_DEFPIN( 5, 12, 1); // D5 is P1.12 + _FL_DEFPIN( 6, 14, 1); // D6 is P1.14 + _FL_DEFPIN( 7, 26, 0); // D7 is P0.26 + _FL_DEFPIN( 8, 27, 0); // D8 is P0.27 + _FL_DEFPIN( 9, 12, 0); // D9 is P0.12 + _FL_DEFPIN(10, 6, 0); // D10 is P0.06 + _FL_DEFPIN(11, 8, 0); // D11 is P0.08 + _FL_DEFPIN(12, 9, 1); // D12 is P1.09 + _FL_DEFPIN(13, 14, 0); // D13 is P0.14 + _FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0) + _FL_DEFPIN(15, 5, 0); // D15 is P0.05 (A1) + _FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2) + _FL_DEFPIN(17, 30, 0); // D17 is P0.30 (A3) + _FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4) + _FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5) + _FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A6, battery) + _FL_DEFPIN(21, 31, 0); // D21 is P0.31 (A7, ARef) + _FL_DEFPIN(22, 15, 0); // D22 is P0.15 (SDA) + _FL_DEFPIN(23, 16, 0); // D23 is P0.16 (SCL) + _FL_DEFPIN(24, 11, 0); // D24 is P0.11 (SPI MISO) + _FL_DEFPIN(25, 8, 1); // D25 is P1.08 (SPI MOSI) + _FL_DEFPIN(26, 7, 0); // D26 is P0.07 (SPI SCK ) + //_FL_DEFPIN(27, 19, 0); // D27 is P0.19 (QSPI CLK ) + //_FL_DEFPIN(28, 20, 0); // D28 is P0.20 (QSPI CS ) + //_FL_DEFPIN(29, 17, 0); // D29 is P0.17 (QSPI Data 0) + //_FL_DEFPIN(30, 23, 0); // D30 is P0.23 (QSPI Data 1) + //_FL_DEFPIN(31, 22, 0); // D31 is P0.22 (QSPI Data 2) + //_FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI Data 3) + _FL_DEFPIN(33, 13, 1); // D33 is P1.13 LED1 + _FL_DEFPIN(34, 15, 1); // D34 is P1.15 LED2 + _FL_DEFPIN(35, 13, 0); // D35 is P0.13 NeoPixel + _FL_DEFPIN(36, 0, 1); // D36 is P1.02 Switch + _FL_DEFPIN(37, 0, 1); // D37 is P1.00 SWO/DFU + _FL_DEFPIN(38, 9, 0); // D38 is P0.09 NFC1 + _FL_DEFPIN(39, 10, 0); // D39 is P0.10 NFC2 +#endif // defined (ARDUINO_NRF52840_METRO) + +// Adafruit Bluefruit on nRF52840DK PCA10056 +// From https://www.adafruit.com/package_adafruit_index.json +#if defined (ARDUINO_NRF52840_PCA10056) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + + #if defined(USE_ARDUINO_PIN_NUMBERING) + #error "Define of `USE_ARDUINO_PIN_NUMBERING` has known errors in pin mapping -- select different mapping" + #elif defined(FASTLED_NRF52_USE_ARDUINO_UNO_R3_HEADER_PIN_NUMBERING) + /* The following allows defining and using the FastPin<> templates, + using the Arduino UNO R3 connector pin definitions. + */ + _FL_DEFPIN( 0, 1, 1); // D0 is P1.01 + _FL_DEFPIN( 1, 2, 1); // D1 is P1.02 + _FL_DEFPIN( 2, 3, 1); // D2 is P1.03 + _FL_DEFPIN( 3, 4, 1); // D3 is P1.04 + _FL_DEFPIN( 4, 5, 1); // D4 is P1.05 + _FL_DEFPIN( 5, 6, 1); // D5 is P1.06 + _FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option) + _FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option) + _FL_DEFPIN( 8, 10, 1); // D8 is P1.10 + _FL_DEFPIN( 9, 11, 1); // D9 is P1.11 + _FL_DEFPIN(10, 12, 1); // D10 is P1.12 + _FL_DEFPIN(11, 13, 1); // D11 is P1.13 + _FL_DEFPIN(12, 14, 1); // D12 is P1.14 + _FL_DEFPIN(13, 15, 1); // D13 is P1.15 + // Arduino UNO uses pins D14..D19 to map to header pins A0..A5 + // AREF has no equivalent digital pin map on Arduino, would be P0.02 + _FL_DEFPIN(14, 3, 0); // D14 / A0 is P0.03 + _FL_DEFPIN(15, 4, 0); // D15 / A1 is P0.04 + _FL_DEFPIN(16, 28, 0); // D16 / A2 is P0.28 + _FL_DEFPIN(17, 29, 0); // D17 / A3 is P0.29 + // Cannot determine which pin on PCA10056 would be intended solely from UNO R3 digital pin number + //_FL_DEFPIN(18, 30, 0); // D18 could be one of two pins: A4 would be P0.30, SDA would be P0.26 + //_FL_DEFPIN(19, 31, 0); // D19 could be one of two pins: A5 would be P0.31, SCL would be P0.27 + #elif defined(FASTLED_NRF52_USE_ARDUINO_MEGA_2560_REV3_HEADER_PIN_NUMBERING) + /* The following allows defining and using the FastPin<> templates, + using the Arduino UNO R3 connector pin definitions. + */ + _FL_DEFPIN( 0, 1, 1); // D0 is P1.01 + _FL_DEFPIN( 1, 2, 1); // D1 is P1.02 + _FL_DEFPIN( 2, 3, 1); // D2 is P1.03 + _FL_DEFPIN( 3, 4, 1); // D3 is P1.04 + _FL_DEFPIN( 4, 5, 1); // D4 is P1.05 + _FL_DEFPIN( 5, 6, 1); // D5 is P1.06 + _FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option) + _FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option) + _FL_DEFPIN( 8, 10, 1); // D8 is P1.10 + _FL_DEFPIN( 9, 11, 1); // D9 is P1.11 + _FL_DEFPIN(10, 12, 1); // D10 is P1.12 + _FL_DEFPIN(11, 13, 1); // D11 is P1.13 + _FL_DEFPIN(12, 14, 1); // D12 is P1.14 + _FL_DEFPIN(13, 15, 1); // D13 is P1.15 + + // Arduino MEGA 2560 has additional digital pins on lower digital header + _FL_DEFPIN(14, 10, 0); // D14 is P0.10 + _FL_DEFPIN(15, 9, 0); // D15 is P0.09 + _FL_DEFPIN(16, 8, 0); // D16 is P0.08 + _FL_DEFPIN(17, 7, 0); // D17 is P0.07 + _FL_DEFPIN(18, 6, 0); // D14 is P0.06 + _FL_DEFPIN(19, 5, 0); // D15 is P0.05 + // Cannot determine which pin on PCA10056 would be intended solely from UNO MEGA 2560 digital pin number + //_FL_DEFPIN(20, 1, 0); // D20 could be one of two pins: D20 on lower header would be P0.01, SDA would be P0.26 + //_FL_DEFPIN(21, 0, 0); // D21 could be one of two pins: D21 on lower header would be P0.00, SCL would be P0.27 + + // Arduino MEGA 2560 has D22-D53 exposed on perpendicular two-row header + // PCA10056 has support for D22-D38 via a 2x19 header at that location (D39 is GND on PCA10056) + _FL_DEFPIN(22, 11, 0); // D22 is P0.11 + _FL_DEFPIN(23, 12, 0); // D23 is P0.12 + _FL_DEFPIN(24, 13, 0); // D24 is P0.13 + _FL_DEFPIN(25, 14, 0); // D25 is P0.14 + _FL_DEFPIN(26, 15, 0); // D26 is P0.15 + _FL_DEFPIN(27, 16, 0); // D27 is P0.16 + // _FL_DEFPIN(28, 17, 0); // D28 is P0.17 (QSPI !CS ) + // _FL_DEFPIN(29, 18, 0); // D29 is P0.18 (RESET) + // _FL_DEFPIN(30, 19, 0); // D30 is P0.19 (QSPI CLK) + // _FL_DEFPIN(31, 20, 0); // D31 is P0.20 (QSPI DIO0) + // _FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI DIO1) + // _FL_DEFPIN(33, 22, 0); // D33 is P0.22 (QSPI DIO2) + // _FL_DEFPIN(34, 23, 0); // D34 is P0.23 (QSPI DIO3) + _FL_DEFPIN(35, 24, 0); // D35 is P0.24 + _FL_DEFPIN(36, 25, 0); // D36 is P0.25 + _FL_DEFPIN(37, 0, 1); // D37 is P1.00 + _FL_DEFPIN(38, 9, 1); // D38 is P1.09 + // _FL_DEFPIN(39, , 0); // D39 is P0. + + + // Arduino MEGA 2560 uses pins D54..D59 to map to header pins A0..A5 + // (it also has D60..D69 for A6..A15, which have no corresponding header on PCA10056) + // AREF has no equivalent digital pin map on Arduino, would be P0.02 + _FL_DEFPIN(54, 3, 0); // D54 / A0 is P0.03 + _FL_DEFPIN(55, 4, 0); // D55 / A1 is P0.04 + _FL_DEFPIN(56, 28, 0); // D56 / A2 is P0.28 + _FL_DEFPIN(57, 29, 0); // D57 / A3 is P0.29 + _FL_DEFPIN(58, 30, 0); // D58 / A4 is P0.30 + _FL_DEFPIN(59, 31, 0); // D59 / A5 is P0.31 + + #else // identity mapping of arduino pin to port/pin + /* 48 pins, defined using natural mapping in Adafruit's variant.cpp (!) */ + _DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 (XL1 .. ensure SB4 bridged, SB2 cut) + _DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (XL2 .. ensure SB3 bridged, SB1 cut) + _DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (AIN0) + _DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (AIN1) + _DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (AIN2 / UART CTS option) + _DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (AIN3 / UART RTS) + _DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (UART TxD) + _DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (TRACECLK / UART CTS default) + _DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (UART RxD) + _DEFPIN_ARM_IDENTITY_P0( 9); // P0.09 (NFC1) + _DEFPIN_ARM_IDENTITY_P0(10); // P0.10 (NFC2) + _DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (TRACEDATA2 / BUTTON1 default) + _DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (TRACEDATA1 / BUTTON2 default) + _DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED1) + _DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (LED2) + _DEFPIN_ARM_IDENTITY_P0(15); // P0.15 (LED3) + _DEFPIN_ARM_IDENTITY_P0(16); // P0.16 (LED4) + //_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (QSPI !CS ) + //_DEFPIN_ARM_IDENTITY_P0(18); // P0.18 (RESET) + //_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (QSPI CLK ) + //_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (QSPI DIO0) + //_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (QSPI DIO1) + //_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (QSPI DIO2) + //_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (QSPI DIO3) + _DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (BUTTON3) + _DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (BUTTON4) + _DEFPIN_ARM_IDENTITY_P0(26); // P0.26 + _DEFPIN_ARM_IDENTITY_P0(27); // P0.27 + _DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (AIN4) + _DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (AIN5) + _DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (AIN6) + _DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (AIN7) + _DEFPIN_ARM_IDENTITY_P0(32); // P1.00 (SWO / TRACEDATA0) + _DEFPIN_ARM_IDENTITY_P0(33); // P1.01 + _DEFPIN_ARM_IDENTITY_P0(34); // P1.02 + _DEFPIN_ARM_IDENTITY_P0(35); // P1.03 + _DEFPIN_ARM_IDENTITY_P0(36); // P1.04 + _DEFPIN_ARM_IDENTITY_P0(37); // P1.05 + _DEFPIN_ARM_IDENTITY_P0(38); // P1.06 + _DEFPIN_ARM_IDENTITY_P0(39); // P1.07 (BUTTON1 option) + _DEFPIN_ARM_IDENTITY_P0(40); // P1.08 (BUTTON2 option) + _DEFPIN_ARM_IDENTITY_P0(41); // P1.09 (TRACEDATA3) + _DEFPIN_ARM_IDENTITY_P0(42); // P1.10 + _DEFPIN_ARM_IDENTITY_P0(43); // P1.11 + _DEFPIN_ARM_IDENTITY_P0(44); // P1.12 + _DEFPIN_ARM_IDENTITY_P0(45); // P1.13 + _DEFPIN_ARM_IDENTITY_P0(46); // P1.14 + _DEFPIN_ARM_IDENTITY_P0(47); // P1.15 + #endif +#endif // defined (ARDUINO_NRF52840_PCA10056) + +// Adafruit ItsyBitsy nRF52840 Express +// From https://www.adafruit.com/package_adafruit_index.json +#if defined (ARDUINO_NRF52_ITSYBITSY) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Adafruit ItsyBitsy nRF52840 Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + + // [D0 .. D13] (digital) + _FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART RX) + _FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART TX) + _FL_DEFPIN( 2, 2, 1); // D2 is P1.02 + _FL_DEFPIN( 3, 6, 0); // D3 is P0.06 LED + _FL_DEFPIN( 4, 29, 0); // D4 is P0.29 Button + _FL_DEFPIN( 5, 27, 0); // D5 is P0.27 + _FL_DEFPIN( 6, 9, 1); // D6 is P1.09 (DotStar Clock) + _FL_DEFPIN( 7, 8, 1); // D7 is P1.08 + _FL_DEFPIN( 8, 8, 0); // D8 is P0.08 (DotStar Data) + _FL_DEFPIN( 9, 7, 0); // D9 is P0.07 + _FL_DEFPIN(10, 5, 0); // D10 is P0.05 + _FL_DEFPIN(11, 26, 0); // D11 is P0.26 + _FL_DEFPIN(12, 11, 0); // D12 is P0.11 + _FL_DEFPIN(13, 12, 0); // D13 is P0.12 + + // [D14 .. D20] (analog [A0 .. A6]) + _FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0) + _FL_DEFPIN(15, 30, 0); // D15 is P0.30 (A1) + _FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2) + _FL_DEFPIN(17, 31, 0); // D17 is P0.31 (A3) + _FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4) + _FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5) + _FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6/D10) + + // [D21 .. D22] (I2C) + _FL_DEFPIN(21, 16, 0); // D21 is P0.16 (SDA) + _FL_DEFPIN(22, 14, 0); // D22 is P0.14 (SCL) + + // [D23 .. D25] (SPI) + _FL_DEFPIN(23, 20, 0); // D23 is P0.20 (SPI MISO) + _FL_DEFPIN(24, 15, 0); // D24 is P0.15 (SPI MOSI) + _FL_DEFPIN(25, 13, 0); // D25 is P0.13 (SPI SCK ) + + // [D26 .. D31] (QSPI) + _FL_DEFPIN(26, 19, 0); // D26 is P0.19 (QSPI CLK) + _FL_DEFPIN(27, 23, 0); // D27 is P0.23 (QSPI CS) + _FL_DEFPIN(28, 21, 0); // D28 is P0.21 (QSPI Data 0) + _FL_DEFPIN(29, 22, 0); // D29 is P0.22 (QSPI Data 1) + _FL_DEFPIN(30, 0, 1); // D30 is P1.00 (QSPI Data 2) + _FL_DEFPIN(31, 17, 0); // D31 is P0.17 (QSPI Data 3) + +#endif // defined (ARDUINO_NRF52_ITSYBITSY) + +// Electronut labs bluey +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/bluey/variant.cpp +#if defined(ARDUINO_ELECTRONUT_BLUEY) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Electronut labs bluey is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _FL_DEFPIN( 0, 26, 0); // D0 is P0.26 + _FL_DEFPIN( 1, 27, 0); // D1 is P0.27 + _FL_DEFPIN( 2, 22, 0); // D2 is P0.22 (SPI SS ) + _FL_DEFPIN( 3, 23, 0); // D3 is P0.23 (SPI MOSI) + _FL_DEFPIN( 4, 24, 0); // D4 is P0.24 (SPI MISO, also A3) + _FL_DEFPIN( 5, 25, 0); // D5 is P0.25 (SPI SCK ) + _FL_DEFPIN( 6, 16, 0); // D6 is P0.16 (Button) + _FL_DEFPIN( 7, 19, 0); // D7 is P0.19 (R) + _FL_DEFPIN( 8, 18, 0); // D8 is P0.18 (G) + _FL_DEFPIN( 9, 17, 0); // D9 is P0.17 (B) + _FL_DEFPIN(10, 11, 0); // D10 is P0.11 (SCL) + _FL_DEFPIN(11, 12, 0); // D11 is P0.12 (DRDYn) + _FL_DEFPIN(12, 13, 0); // D12 is P0.13 (SDA) + _FL_DEFPIN(13, 14, 0); // D13 is P0.17 (INT) + _FL_DEFPIN(14, 15, 0); // D14 is P0.15 (INT1) + _FL_DEFPIN(15, 20, 0); // D15 is P0.20 (INT2) + _FL_DEFPIN(16, 2, 0); // D16 is P0.02 (A0) + _FL_DEFPIN(17, 3, 0); // D17 is P0.03 (A1) + _FL_DEFPIN(18, 4, 0); // D18 is P0.04 (A2) + _FL_DEFPIN(19, 24, 0); // D19 is P0.24 (A3, also D4/SPI MISO) -- is this right? + _FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A4) + _FL_DEFPIN(21, 30, 0); // D21 is P0.30 (A5) + _FL_DEFPIN(22, 31, 0); // D22 is P0.31 (A6) + _FL_DEFPIN(23, 8, 0); // D23 is P0.08 (RX) + _FL_DEFPIN(24, 6, 0); // D24 is P0.06 (TX) + _FL_DEFPIN(25, 5, 0); // D25 is P0.05 (RTS) + _FL_DEFPIN(26, 7, 0); // D26 is P0.07 (CTS) +#endif // defined(ARDUINO_ELECTRONUT_BLUEY) + +// Electronut labs hackaBLE +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE/variant.cpp +#if defined(ARDUINO_ELECTRONUT_HACKABLE) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Electronut labs hackaBLE is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _FL_DEFPIN( 0, 14, 0); // D0 is P0.14 (RX) + _FL_DEFPIN( 1, 13, 0); // D1 is P0.13 (TX) + _FL_DEFPIN( 2, 12, 0); // D2 is P0.12 + _FL_DEFPIN( 3, 11, 0); // D3 is P0.11 (SPI MOSI) + _FL_DEFPIN( 4, 8, 0); // D4 is P0.08 (SPI MISO) + _FL_DEFPIN( 5, 7, 0); // D5 is P0.07 (SPI SCK ) + _FL_DEFPIN( 6, 6, 0); // D6 is P0.06 + _FL_DEFPIN( 7, 27, 0); // D7 is P0.27 + _FL_DEFPIN( 8, 26, 0); // D8 is P0.26 + _FL_DEFPIN( 9, 25, 0); // D9 is P0.25 + _FL_DEFPIN(10, 5, 0); // D10 is P0.05 (A3) + _FL_DEFPIN(11, 4, 0); // D11 is P0.04 (A2) + _FL_DEFPIN(12, 3, 0); // D12 is P0.03 (A1) + _FL_DEFPIN(13, 2, 0); // D13 is P0.02 (A0 / AREF) + _FL_DEFPIN(14, 23, 0); // D14 is P0.23 + _FL_DEFPIN(15, 22, 0); // D15 is P0.22 + _FL_DEFPIN(16, 18, 0); // D16 is P0.18 + _FL_DEFPIN(17, 16, 0); // D17 is P0.16 + _FL_DEFPIN(18, 15, 0); // D18 is P0.15 + _FL_DEFPIN(19, 24, 0); // D19 is P0.24 + _FL_DEFPIN(20, 28, 0); // D20 is P0.28 (A4) + _FL_DEFPIN(21, 29, 0); // D21 is P0.29 (A5) + _FL_DEFPIN(22, 30, 0); // D22 is P0.30 (A6) + _FL_DEFPIN(23, 31, 0); // D23 is P0.31 (A7) + _FL_DEFPIN(24, 19, 0); // D24 is P0.19 (RED LED) + _FL_DEFPIN(25, 20, 0); // D25 is P0.20 (GREEN LED) + _FL_DEFPIN(26, 17, 0); // D26 is P0.17 (BLUE LED) +#endif // defined(ARDUINO_ELECTRONUT_HACKABLE) + +// Electronut labs hackaBLE_v2 +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE_v2/variant.cpp +// (32 pins, natural mapping) +#if defined(ARDUINO_ELECTRONUT_hackaBLE_v2) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Electronut labs hackaBLE_v2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 + _DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 + _DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A0 / SDA / AREF) + _DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A1 / SCL ) + _DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A2) + _DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A3) + _DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 + _DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (RX) + _DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (TX) + _DEFPIN_ARM_IDENTITY_P0( 9); // P0.09 + _DEFPIN_ARM_IDENTITY_P0(10); // P0.10 + _DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (SPI MISO) + _DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (SPI MOSI) + _DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (SPI SCK ) + _DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (SPI SS ) + _DEFPIN_ARM_IDENTITY_P0(15); // P0.15 + _DEFPIN_ARM_IDENTITY_P0(16); // P0.16 + _DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (BLUE LED) + _DEFPIN_ARM_IDENTITY_P0(18); // P0.18 + _DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (RED LED) + _DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (GREEN LED) + // _DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (RESET) + _DEFPIN_ARM_IDENTITY_P0(22); // P0.22 + _DEFPIN_ARM_IDENTITY_P0(23); // P0.23 + _DEFPIN_ARM_IDENTITY_P0(24); // P0.24 + _DEFPIN_ARM_IDENTITY_P0(25); // P0.25 + _DEFPIN_ARM_IDENTITY_P0(26); // P0.26 + _DEFPIN_ARM_IDENTITY_P0(27); // P0.27 + _DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (A4) + _DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (A5) + _DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (A6) + _DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (A7) +#endif // defined(ARDUINO_ELECTRONUT_hackaBLE_v2) + +// RedBear Blend 2 +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_Blend2/variant.cpp +#if defined(ARDUINO_RB_BLEND_2) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "RedBear Blend 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _FL_DEFPIN( 0, 11, 0); // D0 is P0.11 + _FL_DEFPIN( 1, 12, 0); // D1 is P0.12 + _FL_DEFPIN( 2, 13, 0); // D2 is P0.13 + _FL_DEFPIN( 3, 14, 0); // D3 is P0.14 + _FL_DEFPIN( 4, 15, 0); // D4 is P0.15 + _FL_DEFPIN( 5, 16, 0); // D5 is P0.16 + _FL_DEFPIN( 6, 17, 0); // D6 is P0.17 + _FL_DEFPIN( 7, 18, 0); // D7 is P0.18 + _FL_DEFPIN( 8, 19, 0); // D8 is P0.19 + _FL_DEFPIN( 9, 20, 0); // D9 is P0.20 + _FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS ) + _FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI) + _FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO) + _FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED) + _FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0) + _FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1) + _FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2) + _FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3) + _FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4) + _FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5) + _FL_DEFPIN(20, 26, 0); // D20 is P0.26 (SDA) + _FL_DEFPIN(21, 27, 0); // D21 is P0.27 (SCL) + _FL_DEFPIN(22, 8, 0); // D22 is P0.08 (RX) + _FL_DEFPIN(23, 6, 0); // D23 is P0.06 (TX) + _FL_DEFPIN(24, 2, 0); // D24 is P0.02 (AREF) +#endif // defined(ARDUINO_RB_BLEND_2) + +// RedBear BLE Nano 2 +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_BLENano2/variant.cpp +#if defined(ARDUINO_RB_BLE_NANO_2) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "RedBear BLE Nano 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _FL_DEFPIN( 0, 30, 0); // D0 is P0.30 (A0 / RX) + _FL_DEFPIN( 1, 29, 0); // D1 is P0.29 (A1 / TX) + _FL_DEFPIN( 2, 28, 0); // D2 is P0.28 (A2 / SDA) + _FL_DEFPIN( 3, 2, 0); // D3 is P0.02 (A3 / SCL) + _FL_DEFPIN( 4, 5, 0); // D4 is P0.05 (A4) + _FL_DEFPIN( 5, 4, 0); // D5 is P0.04 (A5) + _FL_DEFPIN( 6, 3, 0); // D6 is P0.03 (SPI SS ) + _FL_DEFPIN( 7, 6, 0); // D7 is P0.06 (SPI MOSI) + _FL_DEFPIN( 8, 7, 0); // D8 is P0.07 (SPI MISO) + _FL_DEFPIN( 9, 8, 0); // D9 is P0.08 (SPI SCK ) + // _FL_DEFPIN(10, 21, 0); // D10 is P0.21 (RESET) + _FL_DEFPIN(13, 11, 0); // D11 is P0.11 (LED) +#endif // defined(ARDUINO_RB_BLE_NANO_2) + +// Nordic Semiconductor nRF52 DK +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/nRF52DK/variant.cpp +#if defined(ARDUINO_NRF52_DK) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Nordic Semiconductor nRF52 DK is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + _FL_DEFPIN( 0, 11, 0); // D0 is P0.11 + _FL_DEFPIN( 1, 12, 0); // D1 is P0.12 + _FL_DEFPIN( 2, 13, 0); // D2 is P0.13 (BUTTON1) + _FL_DEFPIN( 3, 14, 0); // D3 is P0.14 (BUTTON2) + _FL_DEFPIN( 4, 15, 0); // D4 is P0.15 (BUTTON3) + _FL_DEFPIN( 5, 16, 0); // D5 is P0.16 (BUTTON4) + _FL_DEFPIN( 6, 17, 0); // D6 is P0.17 (LED1) + _FL_DEFPIN( 7, 18, 0); // D7 is P0.18 (LED2) + _FL_DEFPIN( 8, 19, 0); // D8 is P0.19 (LED3) + _FL_DEFPIN( 9, 20, 0); // D9 is P0.20 (LED4) + _FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS ) + _FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI) + _FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO) + _FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED) + _FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0) + _FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1) + _FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2) + _FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3) + _FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4) + _FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5) + _FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6) + _FL_DEFPIN(21, 2, 0); // D21 is P0.02 (A7 / AREF) + _FL_DEFPIN(22, 26, 0); // D22 is P0.26 (SDA) + _FL_DEFPIN(23, 27, 0); // D23 is P0.27 (SCL) + _FL_DEFPIN(24, 8, 0); // D24 is P0.08 (RX) + _FL_DEFPIN(25, 6, 0); // D25 is P0.06 (TX) +#endif // defined(ARDUINO_NRF52_DK) + +// Taida Century nRF52 mini board +// https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/Taida_Century_nRF52_minidev/variant.cpp +#if defined(ARDUINO_STCT_NRF52_minidev) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Taida Century nRF52 mini board is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + //_FL_DEFPIN( 0, 25, 0); // D0 is P0.xx (near radio!) + //_FL_DEFPIN( 1, 26, 0); // D1 is P0.xx (near radio!) + //_FL_DEFPIN( 2, 27, 0); // D2 is P0.xx (near radio!) + //_FL_DEFPIN( 3, 28, 0); // D3 is P0.xx (near radio!) + //_FL_DEFPIN( 4, 29, 0); // D4 is P0.xx (Not connected, near radio!) + //_FL_DEFPIN( 5, 30, 0); // D5 is P0.xx (LED1, near radio!) + //_FL_DEFPIN( 6, 31, 0); // D6 is P0.xx (LED2, near radio!) + _FL_DEFPIN( 7, 2, 0); // D7 is P0.xx (SDA) + _FL_DEFPIN( 8, 3, 0); // D8 is P0.xx (SCL) + _FL_DEFPIN( 9, 4, 0); // D9 is P0.xx (BUTTON1 / NFC1) + _FL_DEFPIN(10, 5, 0); // D10 is P0.xx + //_FL_DEFPIN(11, 0, 0); // D11 is P0.xx (Not connected) + //_FL_DEFPIN(12, 1, 0); // D12 is P0.xx (Not connected) + _FL_DEFPIN(13, 6, 0); // D13 is P0.xx + _FL_DEFPIN(14, 7, 0); // D14 is P0.xx + _FL_DEFPIN(15, 8, 0); // D15 is P0.xx + //_FL_DEFPIN(16, 9, 0); // D16 is P0.xx (Not connected) + //_FL_DEFPIN(17, 10, 0); // D17 is P0.xx (NFC2, Not connected) + _FL_DEFPIN(18, 11, 0); // D18 is P0.xx (RXD) + _FL_DEFPIN(19, 12, 0); // D19 is P0.xx (TXD) + _FL_DEFPIN(20, 13, 0); // D20 is P0.xx (SPI SS ) + _FL_DEFPIN(21, 14, 0); // D21 is P0.xx (SPI MISO) + _FL_DEFPIN(22, 15, 0); // D22 is P0.xx (SPI MOSI) + _FL_DEFPIN(23, 16, 0); // D23 is P0.xx (SPI SCK ) + _FL_DEFPIN(24, 17, 0); // D24 is P0.xx (A0) + _FL_DEFPIN(25, 18, 0); // D25 is P0.xx (A1) + _FL_DEFPIN(26, 19, 0); // D26 is P0.xx (A2) + _FL_DEFPIN(27, 20, 0); // D27 is P0.xx (A3) + //_FL_DEFPIN(28, 22, 0); // D28 is P0.xx (A4, near radio!) + //_FL_DEFPIN(29, 23, 0); // D29 is P0.xx (A5, near radio!) + _FL_DEFPIN(30, 24, 0); // D30 is P0.xx + // _FL_DEFPIN(31, 21, 0); // D31 is P0.21 (RESET) +#endif // defined(ARDUINO_STCT_NRF52_minidev) + +// Generic nRF52832 +// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/boards.txt +#if defined(ARDUINO_GENERIC) && ( defined(NRF52832_XXAA) || defined(NRF52832_XXAB) ) + #if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND) + #error "Cannot define more than one board at a time" + #else + #define __FASTPIN_ARM_NRF52_VARIANT_FOUND + #endif + #if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING) + #warning "Using `generic` NRF52832 board is an untested configuration -- test and let use know your results via https://github.com/FastLED/FastLED/issues" + #endif + + _DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 ( UART RX + _DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (A0, UART TX) + _DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A1) + _DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A2) + _DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A3) + _DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A4) + _DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (A5) + _DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 + _DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 + _DEFPIN_ARM_IDENTITY_P0( 9); // P0.09 + _DEFPIN_ARM_IDENTITY_P0(10); // P0.10 + _DEFPIN_ARM_IDENTITY_P0(11); // P0.11 + _DEFPIN_ARM_IDENTITY_P0(12); // P0.12 + _DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED) + _DEFPIN_ARM_IDENTITY_P0(14); // P0.14 + _DEFPIN_ARM_IDENTITY_P0(15); // P0.15 + _DEFPIN_ARM_IDENTITY_P0(16); // P0.16 + _DEFPIN_ARM_IDENTITY_P0(17); // P0.17 + _DEFPIN_ARM_IDENTITY_P0(18); // P0.18 + _DEFPIN_ARM_IDENTITY_P0(19); // P0.19 + _DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (I2C SDA) + _DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (I2C SCL) + _DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (SPI MISO) + _DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (SPI MOSI) + _DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (SPI SCK ) + _DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (SPI SS ) + _DEFPIN_ARM_IDENTITY_P0(26); // P0.26 + _DEFPIN_ARM_IDENTITY_P0(27); // P0.27 + _DEFPIN_ARM_IDENTITY_P0(28); // P0.28 + _DEFPIN_ARM_IDENTITY_P0(29); // P0.29 + _DEFPIN_ARM_IDENTITY_P0(30); // P0.30 + _DEFPIN_ARM_IDENTITY_P0(31); // P0.31 +#endif // defined(ARDUINO_GENERIC) + + +#endif // __FASTPIN_ARM_NRF52_VARIANTS_H diff --git a/src/platforms/arm/nrf52/fastspi_arm_nrf52.h b/src/platforms/arm/nrf52/fastspi_arm_nrf52.h new file mode 100644 index 0000000000..89d006e398 --- /dev/null +++ b/src/platforms/arm/nrf52/fastspi_arm_nrf52.h @@ -0,0 +1,340 @@ +#ifndef __FASTSPI_ARM_NRF52_H +#define __FASTSPI_ARM_NRF52_H + + +#ifndef FASTLED_FORCE_SOFTWARE_SPI + + #include + + #define FASTLED_ALL_PINS_HARDWARE_SPI + + + // NRF52810 has SPIM0: Frequencies from 125kbps to 8Mbps + // NRF52832 adds SPIM1, SPIM2 (same frequencies) + // NRF52840 adds SPIM3 (same frequencies), adds SPIM3 that can be @ up to 32Mbps frequency(!) + #if !defined(FASTLED_NRF52_SPIM) + #define FASTLED_NRF52_SPIM NRF_SPIM0 + #endif + + /* This class is slightly simpler than fastpin, as it can rely on fastpin + * to handle the mapping to the underlying PN.XX board-level pins... + */ + + /// SPI_CLOCK_DIVIDER is number of CPU clock cycles per SPI transmission bit? + template + class NRF52SPIOutput { + private: + // static variables -- always using same SPIM instance + static bool s_InUse; + static bool s_NeedToWait; // a data transfer was started, and completion event was not cleared. + + /* + // TODO -- Workaround nRF52840 errata #198, which relates to + // contention between SPIM3 and CPU over AHB. + // The workaround is to ensure the SPIM TX buffer + // is on a different / dedicated RAM block. + // This also avoids AHB contention generally, so + // should be applied to all supported boards. + // + // But... how to allocate m_Buffer[] to be at a + // specific memory range? Also, might need to + // avoid use of single-transaction writeBytes() + // as cannot control where that memory lies.... + */ + static uint8_t s_BufferIndex; + static uint8_t s_Buffer[2][2]; // 2x two-byte buffers, allows one buffer currently being sent, and a second one being prepped to send. + + // This allows saving the configuration of the SPIM instance + // upon select(), and restoring the configuration upon release(). + struct spim_config { + uint32_t inten; + uint32_t shorts; + uint32_t sck_pin; + uint32_t mosi_pin; + uint32_t miso_pin; + uint32_t frequency; + // data pointers, RX/TX counts not saved as would only hide bugs + uint32_t config; // mode & bit order + uint32_t orc; + +#if false // additional configuration to save/restore for SPIM3 + uint32_t csn_pin; + uint32_t csn_polarity; // CSNPOL + uint32_t csn_duration; // IFTIMING.CSNDUR + uint32_t rx_delay; // IFTIMING.RXDELAY + uint32_t dcx_pin; // PSELDCX + uint32_t dcx_config; // DCXCNT +#endif + + } m_SpiSavedConfig; + void saveSpimConfig() { + m_SpiSavedConfig.inten = FASTLED_NRF52_SPIM->INTENSET; + m_SpiSavedConfig.shorts = FASTLED_NRF52_SPIM->SHORTS; + m_SpiSavedConfig.sck_pin = FASTLED_NRF52_SPIM->PSEL.SCK; + m_SpiSavedConfig.mosi_pin = FASTLED_NRF52_SPIM->PSEL.MOSI; + m_SpiSavedConfig.miso_pin = FASTLED_NRF52_SPIM->PSEL.MISO; + m_SpiSavedConfig.frequency = FASTLED_NRF52_SPIM->FREQUENCY; + m_SpiSavedConfig.config = FASTLED_NRF52_SPIM->CONFIG; + m_SpiSavedConfig.orc = FASTLED_NRF52_SPIM->ORC; + +#if false // additional configuration to save/restore for SPIM3 + m_SpiSavedConfig.csn_pin = FASTLED_NRF52_SPIM->PSEL.CSN; + m_SpiSavedConfig.csn_polarity = FASTLED_NRF52_SPIM->CSNPOL; + m_SpiSavedConfig.csn_duration = FASTLED_NRF52_SPIM->IFTIMING.CSNDUR; + m_SpiSavedConfig.dcx_pin = FASTLED_NRF52_SPIM->PSELDCX; + m_SpiSavedConfig.dcx_config = FASTLED_NRF52_SPIM->DCXCNT; +#endif + } + void restoreSpimConfig() { + // 0. ASSERT() the SPIM instance is not enabled + + FASTLED_NRF52_SPIM->INTENCLR = 0xFFFFFFFF; + FASTLED_NRF52_SPIM->INTENSET = m_SpiSavedConfig.inten; + FASTLED_NRF52_SPIM->SHORTS = m_SpiSavedConfig.shorts; + FASTLED_NRF52_SPIM->PSEL.SCK = m_SpiSavedConfig.sck_pin; + FASTLED_NRF52_SPIM->PSEL.MOSI = m_SpiSavedConfig.mosi_pin; + FASTLED_NRF52_SPIM->PSEL.MISO = m_SpiSavedConfig.miso_pin; + FASTLED_NRF52_SPIM->FREQUENCY = m_SpiSavedConfig.frequency; + FASTLED_NRF52_SPIM->CONFIG = m_SpiSavedConfig.config; + FASTLED_NRF52_SPIM->ORC = m_SpiSavedConfig.orc; + +#if false // additional configuration to save/restore for SPIM3 + FASTLED_NRF52_SPIM->PSEL.CSN = m_SpiSavedConfig.csn_pin; + FASTLED_NRF52_SPIM->CSNPOL = m_SpiSavedConfig.csn_polarity; + FASTLED_NRF52_SPIM->IFTIMING.CSNDUR = m_SpiSavedConfig.csn_duration; + FASTLED_NRF52_SPIM->PSELDCX = m_SpiSavedConfig.dcx_pin; + FASTLED_NRF52_SPIM->DCXCNT = m_SpiSavedConfig.dcx_config; +#endif + } + + public: + NRF52SPIOutput() {} + + // Low frequency GPIO is for signals with a frequency up to 10 kHz. Lowest speed SPIM is 125kbps. + static_assert(!FastPin<_DATA_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified"); + static_assert(!FastPin<_CLOCK_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified"); + + /// initialize the SPI subssytem + void init() { + // 0. ASSERT() the SPIM instance is not enabled / in use + //ASSERT(m_SPIM->ENABLE != (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos)); + + // 1. set pins to output/H0H1 drive/etc. + FastPin<_DATA_PIN>::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); + + // 2. Configure SPIMx + nrf_spim_configure( + FASTLED_NRF52_SPIM, + NRF_SPIM_MODE_0, + NRF_SPIM_BIT_ORDER_MSB_FIRST + ); + nrf_spim_frequency_set( + FASTLED_NRF52_SPIM, + NRF_SPIM_FREQ_4M // BUGBUG -- use _SPI_CLOCK_DIVIDER to determine frequency + ); + nrf_spim_pins_set( + FASTLED_NRF52_SPIM, + FastPin<_CLOCK_PIN>::nrf_pin(), + FastPin<_DATA_PIN>::nrf_pin(), + NRF_SPIM_PIN_NOT_CONNECTED + ); + + // 4. Ensure events are cleared + nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END); + nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED); + + // 5. Enable the SPIM instance + nrf_spim_enable(FASTLED_NRF52_SPIM); + } + + /// latch the CS select + void select() { + //ASSERT(!s_InUse); + saveSpimConfig(); + s_InUse = true; + init(); + } + + /// release the CS select + void release() { + //ASSERT(s_InUse); + waitFully(); + s_InUse = false; + restoreSpimConfig(); + } + + /// wait until all queued up data has been written + static void waitFully() { + if (!s_NeedToWait) return; + // else, need to wait for END event + while(!FASTLED_NRF52_SPIM->EVENTS_END) {}; + s_NeedToWait = 0; + // only use two events in this code... + nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END); + nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED); + return; + } + // wait only until we can add a new transaction into the registers + // (caller must still waitFully() before actually starting this next transaction) + static void wait() { + if (!s_NeedToWait) return; + while (!FASTLED_NRF52_SPIM->EVENTS_STARTED) {}; + // leave the event set here... caller must waitFully() and start next transaction + return; + } + + /// write a byte out via SPI (returns immediately on writing register) + static void writeByte(uint8_t b) { + wait(); + // cannot use pointer to stack, so copy to m_buffer[] + uint8_t i = (s_BufferIndex ? 1u : 0u); + s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap + + s_Buffer[i][0u] = b; // cannot use the stack location, so copy to a more permanent buffer... + nrf_spim_tx_buffer_set( + FASTLED_NRF52_SPIM, + &(s_Buffer[i][0u]), + 1 + ); + + waitFully(); + nrf_spim_task_trigger( + FASTLED_NRF52_SPIM, + NRF_SPIM_TASK_START + ); + return; + } + + /// write a word out via SPI (returns immediately on writing register) + static void writeWord(uint16_t w) { + wait(); + // cannot use pointer to stack, so copy to m_buffer[] + uint8_t i = (s_BufferIndex ? 1u : 0u); + s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap + + s_Buffer[i][0u] = (w >> 8u); // cannot use the stack location, so copy to a more permanent buffer... + s_Buffer[i][1u] = (w & 0xFFu); // cannot use the stack location, so copy to a more permanent buffer... + nrf_spim_tx_buffer_set( + FASTLED_NRF52_SPIM, + &(s_Buffer[i][0u]), + 2 + ); + + waitFully(); + nrf_spim_task_trigger( + FASTLED_NRF52_SPIM, + NRF_SPIM_TASK_START + ); + return; + } + + /// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes) + static void writeBytesValueRaw(uint8_t value, int len) { + while (len--) { writeByte(value); } + } + + /// A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytesValue(uint8_t value, int len) { + select(); + writeBytesValueRaw(value, len); + waitFully(); + release(); + } + + /// A full cycle of writing a raw block of data out, including select, release, and waiting + void writeBytes(uint8_t *data, int len) { + // This is a special-case, with no adjustment of the bytes... write them directly... + select(); + wait(); + nrf_spim_tx_buffer_set( + FASTLED_NRF52_SPIM, + data, + len + ); + waitFully(); + nrf_spim_task_trigger( + FASTLED_NRF52_SPIM, + NRF_SPIM_TASK_START + ); + waitFully(); + release(); + } + + /// A full cycle of writing a raw block of data out, including select, release, and waiting + template void writeBytes(uint8_t *data, int len) { + uint8_t * end = data + len; + select(); + wait(); + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + waitFully(); + release(); + } + /// specialization for DATA_NOP ... + //template void writeBytes(uint8_t * data, int len) { + // writeBytes(data, len); + //} + + /// write a single bit out, which bit from the passed in byte is determined by template parameter + template inline static void writeBit(uint8_t b) { + // SPIM instance must be finished transmitting and then disabled + waitFully(); + nrf_spim_disable(FASTLED_NRF52_SPIM); + // set the data pin to appropriate state + if (b & (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + // delay 1/2 cycle per SPI bit + delaycycles<_SPI_CLOCK_DIVIDER/2>(); + FastPin<_CLOCK_PIN>::toggle(); + delaycycles<_SPI_CLOCK_DIVIDER/2>(); + FastPin<_CLOCK_PIN>::toggle(); + // re-enable the SPIM instance + nrf_spim_enable(FASTLED_NRF52_SPIM); + } + + /// write out pixel data from the given PixelController object, including select, release, and waiting + template void writePixels(PixelController pixels) { + select(); + int len = pixels.mLen; + // TODO: If user indicates a pre-allocated double-buffer, + // then process all the pixels at once into that buffer, + // then use the non-templated WriteBytes(data, len) function + // to write the entire buffer as a single SPI transaction. + while (pixels.has(1)) { + if (FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + } + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + waitFully(); + release(); + } + }; + + // Static member definition and initialization using templates. + // see https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template#answer-3229919 + template + bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_InUse = false; + template + bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_NeedToWait = false; + template + uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_BufferIndex = 0; + template + uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_Buffer[2][2] = {{0,0},{0,0}}; + +#endif // #ifndef FASTLED_FORCE_SOFTWARE_SPI + + + +#endif // #ifndef __FASTPIN_ARM_NRF52_H diff --git a/src/platforms/arm/nrf52/led_sysdefs_arm_nrf52.h b/src/platforms/arm/nrf52/led_sysdefs_arm_nrf52.h new file mode 100644 index 0000000000..3a7ea5820a --- /dev/null +++ b/src/platforms/arm/nrf52/led_sysdefs_arm_nrf52.h @@ -0,0 +1,52 @@ +#ifndef __LED_SYSDEFS_ARM_NRF52 +#define __LED_SYSDEFS_ARM_NRF52 + +#define FASTLED_ARM + +#ifndef F_CPU + #define F_CPU 64000000 // the NRF52 series has a 64MHz CPU +#endif + +// even though CPU is at 64MHz, use the 8MHz-defined timings because... +// PWM module runs at 16MHz +// SPI0..2 runs at 8MHz +#define CLOCKLESS_FREQUENCY 16000000 // the NRF52 has EasyDMA for PWM module at 16MHz + +#ifndef F_TIMER + #define F_TIMER 16000000 // the NRF52 timer is 16MHz, even though CPU is 64MHz +#endif + +#if !defined(FASTLED_USE_PROGMEM) + #define FASTLED_USE_PROGMEM 0 // nRF52 series have flat memory model +#endif + +#if !defined(FASTLED_ALLOW_INTERRUPTS) + #define FASTLED_ALLOW_INTERRUPTS 1 +#endif + +// Use PWM instance 0 +// See clockless_arm_nrf52.h and (in root of library) platforms.cpp +#define FASTLED_NRF52_ENABLE_PWM_INSTANCE0 + +#if defined(FASTLED_NRF52_NEVER_INLINE) + #define FASTLED_NRF52_INLINE_ATTRIBUTE __attribute__((always_inline)) inline +#else + #define FASTLED_NRF52_INLINE_ATTRIBUTE __attribute__((always_inline)) inline +#endif + + + +#include +#include // for FastSPI +#include // for Clockless +#include // for Clockless / anything else using interrupts +typedef __I uint32_t RoReg; +typedef __IO uint32_t RwReg; + +#define cli() __disable_irq() +#define sei() __enable_irq() + +#define FASTLED_NRF52_DEBUGPRINT(format, ...)\ +// do { FastLED_NRF52_DebugPrint(format, ##__VA_ARGS__); } while(0); + +#endif // __LED_SYSDEFS_ARM_NRF52 diff --git a/platforms/arm/sam/clockless_arm_sam.h b/src/platforms/arm/sam/clockless_arm_sam.h similarity index 89% rename from platforms/arm/sam/clockless_arm_sam.h rename to src/platforms/arm/sam/clockless_arm_sam.h index 0fc621d2aa..d7c57940b6 100644 --- a/platforms/arm/sam/clockless_arm_sam.h +++ b/src/platforms/arm/sam/clockless_arm_sam.h @@ -22,6 +22,7 @@ class ClocklessController : public CPixelLEDController { data_t mPinMask; data_ptr_t mPort; CMinWait mWait; + public: virtual void init() { FastPinBB::setOutput(); @@ -32,15 +33,14 @@ class ClocklessController : public CPixelLEDController { virtual uint16_t getMaxRefreshRate() const { return 400; } protected: - - virtual void showPixels(PixelController & pixels) { - mWait.wait(); - if(!showRGBInternal(pixels)) { - sei(); delayMicroseconds(WAIT_TIME); cli(); - showRGBInternal(pixels); + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); } - mWait.mark(); - } template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register uint8_t & b) { // Make sure we don't slot into a wrapping spot, this will delay up to 12.5µs for WS2812 @@ -48,7 +48,7 @@ class ClocklessController : public CPixelLEDController { // while(VAL < (TOTAL*10)) { bShift=true; } // if(bShift) { next_mark = (VAL-TOTAL); }; - for(register uint32_t i = BITS; i > 0; i--) { + for(register uint32_t i = BITS; i > 0; --i) { // wait to start the bit, then set the pin high while(DUE_TIMER_VAL < next_mark); next_mark = (DUE_TIMER_VAL+TOTAL); @@ -90,7 +90,9 @@ class ClocklessController : public CPixelLEDController { #if (FASTLED_ALLOW_INTERRUPTS == 1) cli(); if(DUE_TIMER_VAL > next_mark) { - if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return 0; } + if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { + sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return 0; + } } #endif diff --git a/src/platforms/arm/sam/clockless_block_arm_sam.h b/src/platforms/arm/sam/clockless_block_arm_sam.h new file mode 100644 index 0000000000..a179989150 --- /dev/null +++ b/src/platforms/arm/sam/clockless_block_arm_sam.h @@ -0,0 +1,183 @@ + #ifndef __INC_BLOCK_CLOCKLESS_H +#define __INC_BLOCK_CLOCKLESS_H + +FASTLED_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point +// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the +// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(__SAM3X8E__) +#define PORT_MASK (((1< +class InlineBlockClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual int size() { return CLEDController::size() * LANES; } + virtual void init() { + static_assert(LANES <= 8, "Maximum of 8 lanes for Due parallel controllers!"); + if(FIRST_PIN == PORTA_FIRST_PIN) { + switch(LANES) { + case 8: FastPin<31>::setOutput(); + case 7: FastPin<58>::setOutput(); + case 6: FastPin<100>::setOutput(); + case 5: FastPin<59>::setOutput(); + case 4: FastPin<60>::setOutput(); + case 3: FastPin<61>::setOutput(); + case 2: FastPin<68>::setOutput(); + case 1: FastPin<69>::setOutput(); + } + } else if(FIRST_PIN == PORTD_FIRST_PIN) { + switch(LANES) { + case 8: FastPin<11>::setOutput(); + case 7: FastPin<29>::setOutput(); + case 6: FastPin<15>::setOutput(); + case 5: FastPin<14>::setOutput(); + case 4: FastPin<28>::setOutput(); + case 3: FastPin<27>::setOutput(); + case 2: FastPin<26>::setOutput(); + case 1: FastPin<25>::setOutput(); + } + } else if(FIRST_PIN == PORTB_FIRST_PIN) { + switch(LANES) { + case 8: FastPin<97>::setOutput(); + case 7: FastPin<96>::setOutput(); + case 6: FastPin<95>::setOutput(); + case 5: FastPin<94>::setOutput(); + case 4: FastPin<93>::setOutput(); + case 3: FastPin<92>::setOutput(); + case 2: FastPin<91>::setOutput(); + case 1: FastPin<90>::setOutput(); + } + } + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + showRGBInternal(pixels); + sei(); + mWait.mark(); + } + + static uint32_t showRGBInternal(PixelController &allpixels) { + // Serial.println("Entering show"); + + int nLeds = allpixels.mLen; + + // Setup the pixel controller and load/scale the first byte + Lines b0,b1,b2; + + allpixels.preStepFirstByteDithering(); + for(uint8_t i = 0; i < LANES; i++) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + + // Setup and start the clock + TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1); + pmc_enable_periph_clk(DUE_TIMER_ID); + TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + #endif + uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL)); + while(nLeds--) { + allpixels.stepDithering(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + if(DUE_TIMER_VAL > next_mark) { + if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { + sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL; + } + } + #endif + + // Write first byte, read next byte + writeBits<8+XTRA0,1>(next_mark, b0, b1, allpixels); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0,2>(next_mark, b1, b2, allpixels); + + allpixels.advanceData(); + // Write third byte + writeBits<8+XTRA0,0>(next_mark, b2, b0, allpixels); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + } + + return DUE_TIMER_VAL; + } + + template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, Lines & b3, PixelController &pixels) { // , register uint32_t & b2) { + Lines b2; + transpose8x1(b.bytes,b2.bytes); + + register uint8_t d = pixels.template getd(pixels); + register uint8_t scale = pixels.template getscale(pixels); + + for(uint32_t i = 0; (i < LANES) && (i<8); i++) { + while(DUE_TIMER_VAL < next_mark); + next_mark = (DUE_TIMER_VAL+TOTAL); + + *FastPin::sport() = PORT_MASK; + + while((next_mark - DUE_TIMER_VAL) > (T2+T3+6)); + *FastPin::cport() = (~b2.bytes[7-i]) & PORT_MASK; + + while((next_mark - (DUE_TIMER_VAL)) > T3); + *FastPin::cport() = PORT_MASK; + + b3.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + } + + for(uint32_t i = LANES; i < 8; i++) { + while(DUE_TIMER_VAL < next_mark); + next_mark = (DUE_TIMER_VAL+TOTAL); + *FastPin::sport() = PORT_MASK; + + while((next_mark - DUE_TIMER_VAL) > (T2+T3+6)); + *FastPin::cport() = (~b2.bytes[7-i]) & PORT_MASK; + + while((next_mark - DUE_TIMER_VAL) > T3); + *FastPin::cport() = PORT_MASK; + } + } +}; + +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/platforms/arm/sam/fastled_arm_sam.h b/src/platforms/arm/sam/fastled_arm_sam.h similarity index 100% rename from platforms/arm/sam/fastled_arm_sam.h rename to src/platforms/arm/sam/fastled_arm_sam.h diff --git a/platforms/arm/sam/fastpin_arm_sam.h b/src/platforms/arm/sam/fastpin_arm_sam.h similarity index 65% rename from platforms/arm/sam/fastpin_arm_sam.h rename to src/platforms/arm/sam/fastpin_arm_sam.h index 2bb7804298..e1354c7346 100644 --- a/platforms/arm/sam/fastpin_arm_sam.h +++ b/src/platforms/arm/sam/fastpin_arm_sam.h @@ -78,49 +78,49 @@ template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; -#define DUE_IO32(L) _RD32(REG_PIO ## L ## _ODSR); _RD32(REG_PIO ## L ## _SODR); _RD32(REG_PIO ## L ## _CODR); _RD32(REG_PIO ## L ## _OER); +#define _FL_IO(L,C) _RD32(REG_PIO ## L ## _ODSR); _RD32(REG_PIO ## L ## _SODR); _RD32(REG_PIO ## L ## _CODR); _RD32(REG_PIO ## L ## _OER); _FL_DEFINE_PORT3(L, C, _R(REG_PIO ## L ## _ODSR)); -#define _DEFPIN_DUE(PIN, BIT, L) template<> class FastPin : public _DUEPIN class FastPin : public _DUEPIN {}; \ template<> class FastPinBB : public _DUEPIN_BITBAND {}; -#if defined(__SAM3X8E__) +_FL_IO(A,0); +_FL_IO(B,1); +_FL_IO(C,2); +_FL_IO(D,3); -DUE_IO32(A); -DUE_IO32(B); -DUE_IO32(C); -DUE_IO32(D); +#if defined(__SAM3X8E__) #define MAX_PIN 78 -_DEFPIN_DUE(0, 8, A); _DEFPIN_DUE(1, 9, A); _DEFPIN_DUE(2, 25, B); _DEFPIN_DUE(3, 28, C); -_DEFPIN_DUE(4, 26, C); _DEFPIN_DUE(5, 25, C); _DEFPIN_DUE(6, 24, C); _DEFPIN_DUE(7, 23, C); -_DEFPIN_DUE(8, 22, C); _DEFPIN_DUE(9, 21, C); _DEFPIN_DUE(10, 29, C); _DEFPIN_DUE(11, 7, D); -_DEFPIN_DUE(12, 8, D); _DEFPIN_DUE(13, 27, B); _DEFPIN_DUE(14, 4, D); _DEFPIN_DUE(15, 5, D); -_DEFPIN_DUE(16, 13, A); _DEFPIN_DUE(17, 12, A); _DEFPIN_DUE(18, 11, A); _DEFPIN_DUE(19, 10, A); -_DEFPIN_DUE(20, 12, B); _DEFPIN_DUE(21, 13, B); _DEFPIN_DUE(22, 26, B); _DEFPIN_DUE(23, 14, A); -_DEFPIN_DUE(24, 15, A); _DEFPIN_DUE(25, 0, D); _DEFPIN_DUE(26, 1, D); _DEFPIN_DUE(27, 2, D); -_DEFPIN_DUE(28, 3, D); _DEFPIN_DUE(29, 6, D); _DEFPIN_DUE(30, 9, D); _DEFPIN_DUE(31, 7, A); -_DEFPIN_DUE(32, 10, D); _DEFPIN_DUE(33, 1, C); _DEFPIN_DUE(34, 2, C); _DEFPIN_DUE(35, 3, C); -_DEFPIN_DUE(36, 4, C); _DEFPIN_DUE(37, 5, C); _DEFPIN_DUE(38, 6, C); _DEFPIN_DUE(39, 7, C); -_DEFPIN_DUE(40, 8, C); _DEFPIN_DUE(41, 9, C); _DEFPIN_DUE(42, 19, A); _DEFPIN_DUE(43, 20, A); -_DEFPIN_DUE(44, 19, C); _DEFPIN_DUE(45, 18, C); _DEFPIN_DUE(46, 17, C); _DEFPIN_DUE(47, 16, C); -_DEFPIN_DUE(48, 15, C); _DEFPIN_DUE(49, 14, C); _DEFPIN_DUE(50, 13, C); _DEFPIN_DUE(51, 12, C); -_DEFPIN_DUE(52, 21, B); _DEFPIN_DUE(53, 14, B); _DEFPIN_DUE(54, 16, A); _DEFPIN_DUE(55, 24, A); -_DEFPIN_DUE(56, 23, A); _DEFPIN_DUE(57, 22, A); _DEFPIN_DUE(58, 6, A); _DEFPIN_DUE(59, 4, A); -_DEFPIN_DUE(60, 3, A); _DEFPIN_DUE(61, 2, A); _DEFPIN_DUE(62, 17, B); _DEFPIN_DUE(63, 18, B); -_DEFPIN_DUE(64, 19, B); _DEFPIN_DUE(65, 20, B); _DEFPIN_DUE(66, 15, B); _DEFPIN_DUE(67, 16, B); -_DEFPIN_DUE(68, 1, A); _DEFPIN_DUE(69, 0, A); _DEFPIN_DUE(70, 17, A); _DEFPIN_DUE(71, 18, A); -_DEFPIN_DUE(72, 30, C); _DEFPIN_DUE(73, 21, A); _DEFPIN_DUE(74, 25, A); _DEFPIN_DUE(75, 26, A); -_DEFPIN_DUE(76, 27, A); _DEFPIN_DUE(77, 28, A); _DEFPIN_DUE(78, 23, B); +_FL_DEFPIN(0, 8, A); _FL_DEFPIN(1, 9, A); _FL_DEFPIN(2, 25, B); _FL_DEFPIN(3, 28, C); +_FL_DEFPIN(4, 26, C); _FL_DEFPIN(5, 25, C); _FL_DEFPIN(6, 24, C); _FL_DEFPIN(7, 23, C); +_FL_DEFPIN(8, 22, C); _FL_DEFPIN(9, 21, C); _FL_DEFPIN(10, 29, C); _FL_DEFPIN(11, 7, D); +_FL_DEFPIN(12, 8, D); _FL_DEFPIN(13, 27, B); _FL_DEFPIN(14, 4, D); _FL_DEFPIN(15, 5, D); +_FL_DEFPIN(16, 13, A); _FL_DEFPIN(17, 12, A); _FL_DEFPIN(18, 11, A); _FL_DEFPIN(19, 10, A); +_FL_DEFPIN(20, 12, B); _FL_DEFPIN(21, 13, B); _FL_DEFPIN(22, 26, B); _FL_DEFPIN(23, 14, A); +_FL_DEFPIN(24, 15, A); _FL_DEFPIN(25, 0, D); _FL_DEFPIN(26, 1, D); _FL_DEFPIN(27, 2, D); +_FL_DEFPIN(28, 3, D); _FL_DEFPIN(29, 6, D); _FL_DEFPIN(30, 9, D); _FL_DEFPIN(31, 7, A); +_FL_DEFPIN(32, 10, D); _FL_DEFPIN(33, 1, C); _FL_DEFPIN(34, 2, C); _FL_DEFPIN(35, 3, C); +_FL_DEFPIN(36, 4, C); _FL_DEFPIN(37, 5, C); _FL_DEFPIN(38, 6, C); _FL_DEFPIN(39, 7, C); +_FL_DEFPIN(40, 8, C); _FL_DEFPIN(41, 9, C); _FL_DEFPIN(42, 19, A); _FL_DEFPIN(43, 20, A); +_FL_DEFPIN(44, 19, C); _FL_DEFPIN(45, 18, C); _FL_DEFPIN(46, 17, C); _FL_DEFPIN(47, 16, C); +_FL_DEFPIN(48, 15, C); _FL_DEFPIN(49, 14, C); _FL_DEFPIN(50, 13, C); _FL_DEFPIN(51, 12, C); +_FL_DEFPIN(52, 21, B); _FL_DEFPIN(53, 14, B); _FL_DEFPIN(54, 16, A); _FL_DEFPIN(55, 24, A); +_FL_DEFPIN(56, 23, A); _FL_DEFPIN(57, 22, A); _FL_DEFPIN(58, 6, A); _FL_DEFPIN(59, 4, A); +_FL_DEFPIN(60, 3, A); _FL_DEFPIN(61, 2, A); _FL_DEFPIN(62, 17, B); _FL_DEFPIN(63, 18, B); +_FL_DEFPIN(64, 19, B); _FL_DEFPIN(65, 20, B); _FL_DEFPIN(66, 15, B); _FL_DEFPIN(67, 16, B); +_FL_DEFPIN(68, 1, A); _FL_DEFPIN(69, 0, A); _FL_DEFPIN(70, 17, A); _FL_DEFPIN(71, 18, A); +_FL_DEFPIN(72, 30, C); _FL_DEFPIN(73, 21, A); _FL_DEFPIN(74, 25, A); _FL_DEFPIN(75, 26, A); +_FL_DEFPIN(76, 27, A); _FL_DEFPIN(77, 28, A); _FL_DEFPIN(78, 23, B); // digix pins -_DEFPIN_DUE(90, 0, B); _DEFPIN_DUE(91, 1, B); _DEFPIN_DUE(92, 2, B); _DEFPIN_DUE(93, 3, B); -_DEFPIN_DUE(94, 4, B); _DEFPIN_DUE(95, 5, B); _DEFPIN_DUE(96, 6, B); _DEFPIN_DUE(97, 7, B); -_DEFPIN_DUE(98, 8, B); _DEFPIN_DUE(99, 9, B); _DEFPIN_DUE(100, 5, A); _DEFPIN_DUE(101, 22, B); -_DEFPIN_DUE(102, 23, B); _DEFPIN_DUE(103, 24, B); _DEFPIN_DUE(104, 27, C); _DEFPIN_DUE(105, 20, C); -_DEFPIN_DUE(106, 11, C); _DEFPIN_DUE(107, 10, C); _DEFPIN_DUE(108, 21, A); _DEFPIN_DUE(109, 30, C); -_DEFPIN_DUE(110, 29, B); _DEFPIN_DUE(111, 30, B); _DEFPIN_DUE(112, 31, B); _DEFPIN_DUE(113, 28, B); +_FL_DEFPIN(90, 0, B); _FL_DEFPIN(91, 1, B); _FL_DEFPIN(92, 2, B); _FL_DEFPIN(93, 3, B); +_FL_DEFPIN(94, 4, B); _FL_DEFPIN(95, 5, B); _FL_DEFPIN(96, 6, B); _FL_DEFPIN(97, 7, B); +_FL_DEFPIN(98, 8, B); _FL_DEFPIN(99, 9, B); _FL_DEFPIN(100, 5, A); _FL_DEFPIN(101, 22, B); +_FL_DEFPIN(102, 23, B); _FL_DEFPIN(103, 24, B); _FL_DEFPIN(104, 27, C); _FL_DEFPIN(105, 20, C); +_FL_DEFPIN(106, 11, C); _FL_DEFPIN(107, 10, C); _FL_DEFPIN(108, 21, A); _FL_DEFPIN(109, 30, C); +_FL_DEFPIN(110, 29, B); _FL_DEFPIN(111, 30, B); _FL_DEFPIN(112, 31, B); _FL_DEFPIN(113, 28, B); #define SPI_DATA 75 #define SPI_CLOCK 76 diff --git a/platforms/arm/sam/fastspi_arm_sam.h b/src/platforms/arm/sam/fastspi_arm_sam.h similarity index 98% rename from platforms/arm/sam/fastspi_arm_sam.h rename to src/platforms/arm/sam/fastspi_arm_sam.h index eb9abe4cb7..a9446439b8 100644 --- a/platforms/arm/sam/fastspi_arm_sam.h +++ b/src/platforms/arm/sam/fastspi_arm_sam.h @@ -6,7 +6,7 @@ FASTLED_NAMESPACE_BEGIN #if defined(__SAM3X8E__) #define m_SPI ((Spi*)SPI0) -template +template class SAMHardwareSPIOutput { Selectable *m_pSelect; diff --git a/platforms/arm/sam/led_sysdefs_arm_sam.h b/src/platforms/arm/sam/led_sysdefs_arm_sam.h similarity index 100% rename from platforms/arm/sam/led_sysdefs_arm_sam.h rename to src/platforms/arm/sam/led_sysdefs_arm_sam.h diff --git a/src/platforms/arm/stm32/clockless_arm_stm32.h b/src/platforms/arm/stm32/clockless_arm_stm32.h new file mode 100644 index 0000000000..0ac8a5d436 --- /dev/null +++ b/src/platforms/arm/stm32/clockless_arm_stm32.h @@ -0,0 +1,126 @@ +#ifndef __INC_CLOCKLESS_ARM_STM32_H +#define __INC_CLOCKLESS_ARM_STM32_H + +FASTLED_NAMESPACE_BEGIN +// Definition for a single channel clockless controller for the stm32 family of chips, like that used in the spark core +// See clockless.h for detailed info on how the template parameters are used. + +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual void init() { + FastPin::setOutput(); + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } + +#define _CYCCNT (*(volatile uint32_t*)(0xE0001004UL)) + + template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) { + for(register uint32_t i = BITS-1; i > 0; --i) { + while(_CYCCNT < (T1+T2+T3-20)); + FastPin::fastset(port, hi); + _CYCCNT = 4; + if(b&0x80) { + while(_CYCCNT < (T1+T2-20)); + FastPin::fastset(port, lo); + } else { + while(_CYCCNT < (T1-10)); + FastPin::fastset(port, lo); + } + b <<= 1; + } + + while(_CYCCNT < (T1+T2+T3-20)); + FastPin::fastset(port, hi); + _CYCCNT = 4; + + if(b&0x80) { + while(_CYCCNT < (T1+T2-20)); + FastPin::fastset(port, lo); + } else { + while(_CYCCNT < (T1-10)); + FastPin::fastset(port, lo); + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController pixels) { + // Get access to the clock + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + DWT->CYCCNT = 0; + + register data_ptr_t port = FastPin::port(); + register data_t hi = *port | FastPin::mask();; + register data_t lo = *port & ~FastPin::mask();; + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint8_t b = pixels.loadAndScale0(); + + cli(); + + uint32_t next_mark = (T1+T2+T3); + + DWT->CYCCNT = 0; + while(pixels.has(1)) { + pixels.stepDithering(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + cli(); + // if interrupts took longer than 45µs, punt on the current frame + if(DWT->CYCCNT > next_mark) { + if((DWT->CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; } + } + + hi = *port | FastPin::mask(); + lo = *port & ~FastPin::mask(); + #endif + + // Write first byte, read next byte + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(next_mark, port, hi, lo, b); + b = pixels.advanceAndLoadAndScale0(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + + sei(); + return DWT->CYCCNT; + } +}; + +FASTLED_NAMESPACE_END + +#endif diff --git a/src/platforms/arm/stm32/cm3_regs.h b/src/platforms/arm/stm32/cm3_regs.h new file mode 100644 index 0000000000..7bb7f759cc --- /dev/null +++ b/src/platforms/arm/stm32/cm3_regs.h @@ -0,0 +1,63 @@ +#ifndef __CM3_REGS +#define __CM3_REGS + +#include + +#ifdef __cplusplus +#define __I volatile /*!< Defines 'read only' permissions */ +#else +#define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + + +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + + +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +#endif // __CM3_REGS diff --git a/platforms/arm/stm32/fastled_arm_stm32.h b/src/platforms/arm/stm32/fastled_arm_stm32.h similarity index 100% rename from platforms/arm/stm32/fastled_arm_stm32.h rename to src/platforms/arm/stm32/fastled_arm_stm32.h diff --git a/src/platforms/arm/stm32/fastpin_arm_stm32.h b/src/platforms/arm/stm32/fastpin_arm_stm32.h new file mode 100644 index 0000000000..bc69912c38 --- /dev/null +++ b/src/platforms/arm/stm32/fastpin_arm_stm32.h @@ -0,0 +1,176 @@ +#ifndef __FASTPIN_ARM_STM32_H +#define __FASTPIN_ARM_STM32_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be sloightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found +/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning. +/// The registers are data output, set output, clear output, toggle output, input, and direction + +template class _ARMPIN { + +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + #if 0 + inline static void setOutput() { + if(_BIT<8) { + _CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4)); + } else { + _CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4)); + } + } + inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + #endif + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BRR = _MASK; } + // inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = (_MASK<<16); } + inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO::r()->ODR = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { if(_GPIO::r()->ODR & _MASK) { lo(); } else { hi(); } } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO::r()->ODR | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO::r()->ODR & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO::r()->ODR; } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR; } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BRR; } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +#if defined(STM32F10X_MD) +#define _R(T) struct __gen_struct_ ## T +#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile GPIO_TypeDef * r() { return T; } }; +#define _FL_IO(L,C) _RD32(GPIO ## L); _FL_DEFINE_PORT3(L, C, _R(GPIO ## L)); +#elif defined(__STM32F1__) +#define _R(T) struct __gen_struct_ ## T +#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline gpio_reg_map* r() { return T->regs; } }; +#define _FL_IO(L,C) _RD32(GPIO ## L); _FL_DEFINE_PORT3(L, C, _R(GPIO ## L)); +#else +#error "Platform not supported" +#endif + +#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; + +#ifdef GPIOA +_FL_IO(A,0); +#endif +#ifdef GPIOB +_FL_IO(B,1); +#endif +#ifdef GPIOC +_FL_IO(C,2); +#endif +#ifdef GPIOD +_FL_IO(D,3); +#endif +#ifdef GPIOE +_FL_IO(E,4); +#endif +#ifdef GPIOF +_FL_IO(F,5); +#endif +#ifdef GPIOG +_FL_IO(G,6); +#endif + +// Actual pin definitions +#if defined(SPARK) // Sparkfun STM32F103 based board + +#define MAX_PIN 19 +_FL_DEFPIN(0, 7, B); +_FL_DEFPIN(1, 6, B); +_FL_DEFPIN(2, 5, B); +_FL_DEFPIN(3, 4, B); +_FL_DEFPIN(4, 3, B); +_FL_DEFPIN(5, 15, A); +_FL_DEFPIN(6, 14, A); +_FL_DEFPIN(7, 13, A); +_FL_DEFPIN(8, 8, A); +_FL_DEFPIN(9, 9, A); +_FL_DEFPIN(10, 0, A); +_FL_DEFPIN(11, 1, A); +_FL_DEFPIN(12, 4, A); +_FL_DEFPIN(13, 5, A); +_FL_DEFPIN(14, 6, A); +_FL_DEFPIN(15, 7, A); +_FL_DEFPIN(16, 0, B); +_FL_DEFPIN(17, 1, B); +_FL_DEFPIN(18, 3, A); +_FL_DEFPIN(19, 2, A); + +#define SPI_DATA 15 +#define SPI_CLOCK 13 + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif // SPARK + +#if defined(__STM32F1__) // Generic STM32F103 aka "Blue Pill" + +#define MAX_PIN 46 + +_FL_DEFPIN(10, 0, A); // PA0 - PA7 +_FL_DEFPIN(11, 1, A); +_FL_DEFPIN(12, 2, A); +_FL_DEFPIN(13, 3, A); +_FL_DEFPIN(14, 4, A); +_FL_DEFPIN(15, 5, A); +_FL_DEFPIN(16, 6, A); +_FL_DEFPIN(17, 7, A); +_FL_DEFPIN(29, 8, A); // PA8 - PA15 +_FL_DEFPIN(30, 9, A); +_FL_DEFPIN(31, 10, A); +_FL_DEFPIN(32, 11, A); +_FL_DEFPIN(33, 12, A); +_FL_DEFPIN(34, 13, A); +_FL_DEFPIN(37, 14, A); +_FL_DEFPIN(38, 15, A); + +_FL_DEFPIN(18, 0, B); // PB0 - PB11 +_FL_DEFPIN(19, 1, B); +_FL_DEFPIN(20, 2, B); +_FL_DEFPIN(39, 3, B); +_FL_DEFPIN(40, 4, B); +_FL_DEFPIN(41, 5, B); +_FL_DEFPIN(42, 6, B); +_FL_DEFPIN(43, 7, B); +_FL_DEFPIN(45, 8, B); +_FL_DEFPIN(46, 9, B); +_FL_DEFPIN(21, 10, B); +_FL_DEFPIN(22, 11, B); + +_FL_DEFPIN(2, 13, C); // PC13 - PC15 +_FL_DEFPIN(3, 14, C); +_FL_DEFPIN(4, 15, C); + +#define SPI_DATA BOARD_SPI1_MOSI_PIN +#define SPI_CLOCK BOARD_SPI1_SCK_PIN + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif // __STM32F1__ + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_ARM_STM32 diff --git a/platforms/arm/stm32/led_sysdefs_arm_stm32.h b/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h similarity index 66% rename from platforms/arm/stm32/led_sysdefs_arm_stm32.h rename to src/platforms/arm/stm32/led_sysdefs_arm_stm32.h index 6b9ce7cad5..afcf178535 100644 --- a/platforms/arm/stm32/led_sysdefs_arm_stm32.h +++ b/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h @@ -3,25 +3,25 @@ #if defined(STM32F10X_MD) - #include +#include - #define FASTLED_NAMESPACE_BEGIN namespace NSFastLED { - #define FASTLED_NAMESPACE_END } - #define FASTLED_USING_NAMESPACE using namespace NSFastLED; +#define FASTLED_NAMESPACE_BEGIN namespace NSFastLED { +#define FASTLED_NAMESPACE_END } +#define FASTLED_USING_NAMESPACE using namespace NSFastLED; - // reusing/abusing cli/sei defs for due - #define cli() __disable_irq(); __disable_fault_irq(); - #define sei() __enable_irq(); __enable_fault_irq(); +// reusing/abusing cli/sei defs for due +#define cli() __disable_irq(); __disable_fault_irq(); +#define sei() __enable_irq(); __enable_fault_irq(); #elif defined (__STM32F1__) - #include "cm3_regs.h" +#include "cm3_regs.h" - #define cli() nvic_globalirq_disable() - #define sei() nvic_globalirq_enable() +#define cli() nvic_globalirq_disable() +#define sei() nvic_globalirq_enable() #else - #error "Platform not supported" +#error "Platform not supported" #endif #define FASTLED_ARM @@ -56,6 +56,6 @@ typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile u #define FASTLED_NO_PINMAP #ifndef F_CPU - #define F_CPU 72000000 +#define F_CPU 72000000 #endif #endif diff --git a/platforms/avr/clockless_trinket.h b/src/platforms/avr/clockless_trinket.h similarity index 94% rename from platforms/avr/clockless_trinket.h rename to src/platforms/avr/clockless_trinket.h index 69f33d6aff..a84c0329b0 100644 --- a/platforms/avr/clockless_trinket.h +++ b/src/platforms/avr/clockless_trinket.h @@ -49,7 +49,11 @@ template<> __attribute__((always_inline)) inline void _dc<-2>(register uint8_t & template<> __attribute__((always_inline)) inline void _dc<-1>(register uint8_t & ) {} template<> __attribute__((always_inline)) inline void _dc< 0>(register uint8_t & ) {} template<> __attribute__((always_inline)) inline void _dc< 1>(register uint8_t & ) {asm __volatile__("mov r0,r0":::);} +#if defined(__LGT8F__) +template<> __attribute__((always_inline)) inline void _dc< 2>(register uint8_t & loopvar) { _dc<1>(loopvar); _dc<1>(loopvar); } +#else template<> __attribute__((always_inline)) inline void _dc< 2>(register uint8_t & ) {asm __volatile__("rjmp .+0":::);} +#endif template<> __attribute__((always_inline)) inline void _dc< 3>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<1>(loopvar); } template<> __attribute__((always_inline)) inline void _dc< 4>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); } template<> __attribute__((always_inline)) inline void _dc< 5>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<3>(loopvar); } @@ -97,6 +101,7 @@ class ClocklessController : public CPixelLEDController { typedef typename FastPin::port_t data_t; CMinWait mWait; + public: virtual void init() { FastPin::setOutput(); @@ -105,7 +110,6 @@ class ClocklessController : public CPixelLEDController { virtual uint16_t getMaxRefreshRate() const { return 400; } protected: - virtual void showPixels(PixelController & pixels) { mWait.wait(); @@ -167,7 +171,13 @@ class ClocklessController : public CPixelLEDController { } #define USE_ASM_MACROS -// The variables that our various asm statemetns use. The same block of variables needs to be declared for +#if defined(__AVR_ATmega4809__) +#define ASM_VAR_PORT "r" (((PORT_t*)FastPin::port())->OUT) +#else +#define ASM_VAR_PORT "M" (FastPin::port() - 0x20) +#endif + +// The variables that our various asm statements use. The same block of variables needs to be declared for // all the asm blocks because GCC is pretty stupid and it would clobber variables happily or optimize code away too aggressively #define ASM_VARS : /* write variables */ \ [count] "+x" (count), \ @@ -189,13 +199,11 @@ class ClocklessController : public CPixelLEDController { [e0] "r" (e0), \ [e1] "r" (e1), \ [e2] "r" (e2), \ - [PORT] "M" (FastPin::port()-0x20), \ + [PORT] ASM_VAR_PORT, \ [O0] "M" (RGB_BYTE0(RGB_ORDER)), \ [O1] "M" (RGB_BYTE1(RGB_ORDER)), \ [O2] "M" (RGB_BYTE2(RGB_ORDER)) \ : "cc" /* clobber registers */ - - // Note: the code in the else in HI1/LO1 will be turned into an sts (2 cycle, 2 word) opcode // 1 cycle, write hi to the port #define HI1 FASTLED_SLOW_CLOCK_ADJUST if((int)(FastPin::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[hi]" ASM_VARS ); } else { *FastPin::port()=hi; } @@ -322,6 +330,9 @@ class ClocklessController : public CPixelLEDController { #define DADVANCE 3 #define DUSE (0xFF - (DADVANCE-1)) +// Silence compiler warnings about switch/case that is explicitly intended to fall through. +#define FL_FALLTHROUGH __attribute__ ((fallthrough)); + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then // gcc will use register Y for the this pointer. static void /*__attribute__((optimize("O0")))*/ /*__attribute__ ((always_inline))*/ showRGBInternal(PixelController & pixels) { @@ -403,9 +414,9 @@ class ClocklessController : public CPixelLEDController { HI1 _D1(1) QLO2(b0, 1) RORSC14(b1,7) _D2(4) LO1 RORCLC2(b1) _D3(2) HI1 _D1(1) QLO2(b0, 0) switch(XTRA0) { - case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) - case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) - case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) + case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH + case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH + case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) } MOV_ADDDE14(b0,b1,d1,e1) _D2(4) LO1 _D3(0) @@ -419,9 +430,9 @@ class ClocklessController : public CPixelLEDController { HI1 _D1(1) QLO2(b0, 1) RORSC24(b1,7) _D2(4) LO1 RORCLC2(b1) _D3(2) HI1 _D1(1) QLO2(b0, 0) switch(XTRA0) { - case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) - case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) - case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) + case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH + case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH + case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) } @@ -438,9 +449,9 @@ class ClocklessController : public CPixelLEDController { HI1 _D1(1) QLO2(b0, 1) RORSC04(b1,7) _D2(4) LO1 RORCLC2(b1) _D3(2) HI1 _D1(1) QLO2(b0, 0) switch(XTRA0) { - case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) - case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) - case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) + case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH + case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH + case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) FL_FALLTHROUGH case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) } MOV_ADDDE04(b0,b1,d0,e0) _D2(4) LO1 _D3(5) diff --git a/platforms/avr/fastled_avr.h b/src/platforms/avr/fastled_avr.h similarity index 100% rename from platforms/avr/fastled_avr.h rename to src/platforms/avr/fastled_avr.h diff --git a/src/platforms/avr/fastpin_avr.h b/src/platforms/avr/fastpin_avr.h new file mode 100644 index 0000000000..db2beeaf27 --- /dev/null +++ b/src/platforms/avr/fastpin_avr.h @@ -0,0 +1,410 @@ +#ifndef __INC_FASTPIN_AVR_H +#define __INC_FASTPIN_AVR_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be slightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +#define AVR_PIN_CYCLES(_PIN) ((((int)FastPin<_PIN>::port())-0x20 < 64) ? 1 : 2) + +/// Class definition for a Pin where we know the port registers at compile time for said pin. This allows us to make +/// a lot of optimizations, as the inlined hi/lo methods will devolve to a single io register write/bitset. +template class _AVRPIN { +public: + typedef volatile uint8_t * port_ptr_t; + typedef uint8_t port_t; + + inline static void setOutput() { _DDR::r() |= _MASK; } + inline static void setInput() { _DDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; } + inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _PIN::r() = _MASK; } + + inline static void hi(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t /*port*/, register uint8_t val) __attribute__ ((always_inline)) { set(val); } + + inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + + + +/// AVR definitions for pins. Getting around the fact that I can't pass GPIO register addresses in as template arguments by instead creating +/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register. A similar +/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!) +typedef volatile uint8_t & reg8_t; +#define _R(T) struct __gen_struct_ ## T +#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }}; + +// Register name equivalent (using flat names) +#if defined(AVR_ATtinyxy7) || defined(AVR_ATtinyxy6) || defined(AVR_ATtinyxy4) || defined(AVR_ATtinyxy2) || defined(__AVR_ATmega4809__) +// ATtiny series 0/1 and ATmega series 0 +#define _FL_IO(L,C) _RD8(PORT ## L ## _DIR); _RD8(PORT ## L ## _OUT); _RD8(PORT ## L ## _IN); _FL_DEFINE_PORT3(L, C, _R(PORT ## L ## _OUT)); +#define _FL_DEFPIN(_PIN, BIT, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, 1< {}; +#else +// Others +#define _FL_IO(L,C) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L); _FL_DEFINE_PORT3(L, C, _R(PORT ## L)); +#define _FL_DEFPIN(_PIN, BIT, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, 1< {}; +#endif + +// Pre-do all the port definitions +#ifdef PORTA + _FL_IO(A,0) +#endif +#ifdef PORTB + _FL_IO(B,1) +#endif +#ifdef PORTC + _FL_IO(C,2) +#endif +#ifdef PORTD + _FL_IO(D,3) +#endif +#ifdef PORTE + _FL_IO(E,4) +#endif +#ifdef PORTF + _FL_IO(F,5) +#endif +#ifdef PORTG + _FL_IO(G,6) +#endif +#ifdef PORTH + _FL_IO(H,7) +#endif +#ifdef PORTI + _FL_IO(I,8) +#endif +#ifdef PORTJ + _FL_IO(J,9) +#endif +#ifdef PORTK + _FL_IO(K,10) +#endif +#ifdef PORTL + _FL_IO(L,11) +#endif +#ifdef PORTM + _FL_IO(M,12) +#endif +#ifdef PORTN + _FL_IO(N,13) +#endif + +#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) + +#if defined(__AVR_ATtiny25__) +#pragma message "ATtiny25 has very limited storage. This library could use up to more than 100% of its flash size" +#endif + +#define MAX_PIN 5 + +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B); +_FL_DEFPIN(4, 4, B); _FL_DEFPIN(5, 5, B); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny441__) +#define MAX_PIN 11 + +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); +_FL_DEFPIN(3, 7, A); _FL_DEFPIN(4, 6, A); _FL_DEFPIN(5, 5, A); +_FL_DEFPIN(6, 4, A); _FL_DEFPIN(7, 3, A); _FL_DEFPIN(8, 2, A); +_FL_DEFPIN(9, 1, A); _FL_DEFPIN(10, 0, A); _FL_DEFPIN(11, 3, B); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AVR_DIGISPARK) // digispark pin layout +#define MAX_PIN 5 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); +_FL_DEFPIN(3, 7, A); _FL_DEFPIN(4, 6, A); _FL_DEFPIN(5, 5, A); + +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + +#define MAX_PIN 10 + +_FL_DEFPIN(0, 0, A); _FL_DEFPIN(1, 1, A); _FL_DEFPIN(2, 2, A); _FL_DEFPIN(3, 3, A); +_FL_DEFPIN(4, 4, A); _FL_DEFPIN(5, 5, A); _FL_DEFPIN(6, 6, A); _FL_DEFPIN(7, 7, A); +_FL_DEFPIN(8, 2, B); _FL_DEFPIN(9, 1, B); _FL_DEFPIN(10, 0, B); + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ARDUINO_AVR_DIGISPARKPRO) + +#define MAX_PIN 12 + +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 5, B); +_FL_DEFPIN(4, 3, B); _FL_DEFPIN(5, 7, A); _FL_DEFPIN(6, 0, A); _FL_DEFPIN(7, 1, A); +_FL_DEFPIN(8, 2, A); _FL_DEFPIN(9, 3, A); _FL_DEFPIN(10, 4, A); _FL_DEFPIN(11, 5, A); +_FL_DEFPIN(12, 6, A); + +#elif defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__) + +#define MAX_PIN 15 + +_FL_DEFPIN(0, 0, A); _FL_DEFPIN(1, 1, A); _FL_DEFPIN(2, 2, A); _FL_DEFPIN(3, 3, A); +_FL_DEFPIN(4, 4, A); _FL_DEFPIN(5, 5, A); _FL_DEFPIN(6, 6, A); _FL_DEFPIN(7, 7, A); +_FL_DEFPIN(8, 0, B); _FL_DEFPIN(9, 1, B); _FL_DEFPIN(10, 2, B); _FL_DEFPIN(11, 3, B); +_FL_DEFPIN(12, 4, B); _FL_DEFPIN(13, 5, B); _FL_DEFPIN(14, 6, B); _FL_DEFPIN(15, 7, B); + +#define SPI_DATA 4 +#define SPI_CLOCK 5 +#define AVR_HARDWARE_SPI 1 + +#define HAS_HARDWARE_PIN_SUPPORT 1 +#elif defined(ARDUINO_HOODLOADER2) && (defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__)) || defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__) + +#define MAX_PIN 20 + +_FL_DEFPIN( 0, 0, B); _FL_DEFPIN( 1, 1, B); _FL_DEFPIN( 2, 2, B); _FL_DEFPIN( 3, 3, B); +_FL_DEFPIN( 4, 4, B); _FL_DEFPIN( 5, 5, B); _FL_DEFPIN( 6, 6, B); _FL_DEFPIN( 7, 7, B); + +_FL_DEFPIN( 8, 7, C); _FL_DEFPIN( 9, 6, C); _FL_DEFPIN( 10, 5,C); _FL_DEFPIN( 11, 4, C); +_FL_DEFPIN( 12, 2, C); _FL_DEFPIN( 13, 0, D); _FL_DEFPIN( 14, 1, D); _FL_DEFPIN(15, 2, D); +_FL_DEFPIN( 16, 3, D); _FL_DEFPIN( 17, 4, D); _FL_DEFPIN( 18, 5, D); _FL_DEFPIN( 19, 6, D); +_FL_DEFPIN( 20, 7, D); + +#define HAS_HARDWARE_PIN_SUPPORT 1 +// #define SPI_DATA 2 +// #define SPI_CLOCK 1 +// #define AVR_HARDWARE_SPI 1 + +#elif defined(IS_BEAN) + +#define MAX_PIN 19 +_FL_DEFPIN( 0, 6, D); _FL_DEFPIN( 1, 1, B); _FL_DEFPIN( 2, 2, B); _FL_DEFPIN( 3, 3, B); +_FL_DEFPIN( 4, 4, B); _FL_DEFPIN( 5, 5, B); _FL_DEFPIN( 6, 0, D); _FL_DEFPIN( 7, 7, D); +_FL_DEFPIN( 8, 0, B); _FL_DEFPIN( 9, 1, D); _FL_DEFPIN(10, 2, D); _FL_DEFPIN(11, 3, D); +_FL_DEFPIN(12, 4, D); _FL_DEFPIN(13, 5, D); _FL_DEFPIN(14, 0, C); _FL_DEFPIN(15, 1, C); +_FL_DEFPIN(16, 2, C); _FL_DEFPIN(17, 3, C); _FL_DEFPIN(18, 4, C); _FL_DEFPIN(19, 5, C); + +#define SPI_DATA 3 +#define SPI_CLOCK 5 +#define SPI_SELECT 2 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#ifndef __AVR_ATmega8__ +#define SPI_UART0_DATA 9 +#define SPI_UART0_CLOCK 12 +#endif + +#elif defined(ARDUINO_AVR_NANO_EVERY) + +#define MAX_PIN 22 +_FL_DEFPIN(0, 5, C); _FL_DEFPIN(1, 4, C); _FL_DEFPIN(2, 0, A); _FL_DEFPIN(3, 5, F); +_FL_DEFPIN(4, 6, C); _FL_DEFPIN(5, 2, B); _FL_DEFPIN(6, 4, F); _FL_DEFPIN(7, 1, A); +_FL_DEFPIN(8, 3, E); _FL_DEFPIN(9, 0, B); _FL_DEFPIN(10, 1, B); _FL_DEFPIN(11, 0, E); +_FL_DEFPIN(12, 1, E); _FL_DEFPIN(13, 2, E); _FL_DEFPIN(14, 3, D); _FL_DEFPIN(15, 2, D); +_FL_DEFPIN(16, 1, D); _FL_DEFPIN(17, 0, D); _FL_DEFPIN(18, 2, A); _FL_DEFPIN(19, 3, A); +_FL_DEFPIN(20, 4, D); _FL_DEFPIN(21, 5, D); _FL_DEFPIN(22, 2, A); + +// To confirm for the SPI interfaces +//#define SPI_DATA 18 +//#define SPI_CLOCK 13 +//#define SPI_SELECT 19 +//#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +//#define SPI_UART0_DATA 1 +//#define SPI_UART0_CLOCK 4 + +#elif defined(__AVR_ATmega4809__) + +#define MAX_PIN 21 +_FL_DEFPIN(0, 4, C); _FL_DEFPIN(1, 5, C); _FL_DEFPIN(2, 0, A); _FL_DEFPIN(3, 5, F); +_FL_DEFPIN(4, 6, C); _FL_DEFPIN(5, 2, B); _FL_DEFPIN(6, 4, F); _FL_DEFPIN(7, 1, A); +_FL_DEFPIN(8, 3, E); _FL_DEFPIN(9, 0, B); _FL_DEFPIN(10, 1, B); _FL_DEFPIN(11, 0, E); +_FL_DEFPIN(12, 1, E); _FL_DEFPIN(13, 2, E); _FL_DEFPIN(14, 3, D); _FL_DEFPIN(15, 2, D); +_FL_DEFPIN(16, 1, D); _FL_DEFPIN(17, 0, D); _FL_DEFPIN(18, 2, A); _FL_DEFPIN(19, 3, A); +_FL_DEFPIN(20, 4, D); _FL_DEFPIN(21, 5, D); + +// To confirm for the SPI interfaces +//#define SPI_DATA 18 +//#define SPI_CLOCK 13 +//#define SPI_SELECT 19 +//#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +//#define SPI_UART0_DATA 1 +//#define SPI_UART0_CLOCK 4 + +#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega8__) + +#define MAX_PIN 19 +_FL_DEFPIN( 0, 0, D); _FL_DEFPIN( 1, 1, D); _FL_DEFPIN( 2, 2, D); _FL_DEFPIN( 3, 3, D); +_FL_DEFPIN( 4, 4, D); _FL_DEFPIN( 5, 5, D); _FL_DEFPIN( 6, 6, D); _FL_DEFPIN( 7, 7, D); +_FL_DEFPIN( 8, 0, B); _FL_DEFPIN( 9, 1, B); _FL_DEFPIN(10, 2, B); _FL_DEFPIN(11, 3, B); +_FL_DEFPIN(12, 4, B); _FL_DEFPIN(13, 5, B); _FL_DEFPIN(14, 0, C); _FL_DEFPIN(15, 1, C); +_FL_DEFPIN(16, 2, C); _FL_DEFPIN(17, 3, C); _FL_DEFPIN(18, 4, C); _FL_DEFPIN(19, 5, C); + +#define SPI_DATA 11 +#define SPI_CLOCK 13 +#define SPI_SELECT 10 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#ifndef __AVR_ATmega8__ +#define SPI_UART0_DATA 1 +#define SPI_UART0_CLOCK 4 +#endif + +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega32__) || defined(__AVR_ATmega16__) + +#define MAX_PIN 31 +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B); +_FL_DEFPIN(4, 4, B); _FL_DEFPIN(5, 5, B); _FL_DEFPIN(6, 6, B); _FL_DEFPIN(7, 7, B); +_FL_DEFPIN(8, 0, D); _FL_DEFPIN(9, 1, D); _FL_DEFPIN(10, 2, D); _FL_DEFPIN(11, 3, D); +_FL_DEFPIN(12, 4, D); _FL_DEFPIN(13, 5, D); _FL_DEFPIN(14, 6, D); _FL_DEFPIN(15, 7, D); +_FL_DEFPIN(16, 0, C); _FL_DEFPIN(17, 1, C); _FL_DEFPIN(18, 2, C); _FL_DEFPIN(19, 3, C); +_FL_DEFPIN(20, 4, C); _FL_DEFPIN(21, 5, C); _FL_DEFPIN(22, 6, C); _FL_DEFPIN(23, 7, C); +_FL_DEFPIN(24, 0, A); _FL_DEFPIN(25, 1, A); _FL_DEFPIN(26, 2, A); _FL_DEFPIN(27, 3, A); +_FL_DEFPIN(28, 4, A); _FL_DEFPIN(29, 5, A); _FL_DEFPIN(30, 6, A); _FL_DEFPIN(31, 7, A); + +#define SPI_DATA 5 +#define SPI_CLOCK 7 +#define SPI_SELECT 4 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega256RFR2__) + +// AKA the Pinoccio +_FL_DEFPIN( 0, 0, E); _FL_DEFPIN( 1, 1, E); _FL_DEFPIN( 2, 7, B); _FL_DEFPIN( 3, 3, E); +_FL_DEFPIN( 4, 4, E); _FL_DEFPIN( 5, 5, E); _FL_DEFPIN( 6, 2, E); _FL_DEFPIN( 7, 6, E); +_FL_DEFPIN( 8, 5, D); _FL_DEFPIN( 9, 0, B); _FL_DEFPIN(10, 2, B); _FL_DEFPIN(11, 3, B); +_FL_DEFPIN(12, 1, B); _FL_DEFPIN(13, 2, D); _FL_DEFPIN(14, 3, D); _FL_DEFPIN(15, 0, D); +_FL_DEFPIN(16, 1, D); _FL_DEFPIN(17, 4, D); _FL_DEFPIN(18, 7, E); _FL_DEFPIN(19, 6, D); +_FL_DEFPIN(20, 7, D); _FL_DEFPIN(21, 4, B); _FL_DEFPIN(22, 5, B); _FL_DEFPIN(23, 6, B); +_FL_DEFPIN(24, 0, F); _FL_DEFPIN(25, 1, F); _FL_DEFPIN(26, 2, F); _FL_DEFPIN(27, 3, F); +_FL_DEFPIN(28, 4, F); _FL_DEFPIN(29, 5, F); _FL_DEFPIN(30, 6, F); _FL_DEFPIN(31, 7, F); + +#define SPI_DATA 10 +#define SPI_CLOCK 12 +#define SPI_SELECT 9 + +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// megas +#define MAX_PIN 69 +_FL_DEFPIN(0, 0, E); _FL_DEFPIN(1, 1, E); _FL_DEFPIN(2, 4, E); _FL_DEFPIN(3, 5, E); +_FL_DEFPIN(4, 5, G); _FL_DEFPIN(5, 3, E); _FL_DEFPIN(6, 3, H); _FL_DEFPIN(7, 4, H); +_FL_DEFPIN(8, 5, H); _FL_DEFPIN(9, 6, H); _FL_DEFPIN(10, 4, B); _FL_DEFPIN(11, 5, B); +_FL_DEFPIN(12, 6, B); _FL_DEFPIN(13, 7, B); _FL_DEFPIN(14, 1, J); _FL_DEFPIN(15, 0, J); +_FL_DEFPIN(16, 1, H); _FL_DEFPIN(17, 0, H); _FL_DEFPIN(18, 3, D); _FL_DEFPIN(19, 2, D); +_FL_DEFPIN(20, 1, D); _FL_DEFPIN(21, 0, D); _FL_DEFPIN(22, 0, A); _FL_DEFPIN(23, 1, A); +_FL_DEFPIN(24, 2, A); _FL_DEFPIN(25, 3, A); _FL_DEFPIN(26, 4, A); _FL_DEFPIN(27, 5, A); +_FL_DEFPIN(28, 6, A); _FL_DEFPIN(29, 7, A); _FL_DEFPIN(30, 7, C); _FL_DEFPIN(31, 6, C); +_FL_DEFPIN(32, 5, C); _FL_DEFPIN(33, 4, C); _FL_DEFPIN(34, 3, C); _FL_DEFPIN(35, 2, C); +_FL_DEFPIN(36, 1, C); _FL_DEFPIN(37, 0, C); _FL_DEFPIN(38, 7, D); _FL_DEFPIN(39, 2, G); +_FL_DEFPIN(40, 1, G); _FL_DEFPIN(41, 0, G); _FL_DEFPIN(42, 7, L); _FL_DEFPIN(43, 6, L); +_FL_DEFPIN(44, 5, L); _FL_DEFPIN(45, 4, L); _FL_DEFPIN(46, 3, L); _FL_DEFPIN(47, 2, L); +_FL_DEFPIN(48, 1, L); _FL_DEFPIN(49, 0, L); _FL_DEFPIN(50, 3, B); _FL_DEFPIN(51, 2, B); +_FL_DEFPIN(52, 1, B); _FL_DEFPIN(53, 0, B); _FL_DEFPIN(54, 0, F); _FL_DEFPIN(55, 1, F); +_FL_DEFPIN(56, 2, F); _FL_DEFPIN(57, 3, F); _FL_DEFPIN(58, 4, F); _FL_DEFPIN(59, 5, F); +_FL_DEFPIN(60, 6, F); _FL_DEFPIN(61, 7, F); _FL_DEFPIN(62, 0, K); _FL_DEFPIN(63, 1, K); +_FL_DEFPIN(64, 2, K); _FL_DEFPIN(65, 3, K); _FL_DEFPIN(66, 4, K); _FL_DEFPIN(67, 5, K); +_FL_DEFPIN(68, 6, K); _FL_DEFPIN(69, 7, K); + +#define SPI_DATA 51 +#define SPI_CLOCK 52 +#define SPI_SELECT 53 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +// Leonardo, teensy, blinkm +#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY) + +// teensy defs +#define MAX_PIN 23 +_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B); +_FL_DEFPIN(4, 7, B); _FL_DEFPIN(5, 0, D); _FL_DEFPIN(6, 1, D); _FL_DEFPIN(7, 2, D); +_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 6, C); _FL_DEFPIN(10, 7, C); _FL_DEFPIN(11, 6, D); +_FL_DEFPIN(12, 7, D); _FL_DEFPIN(13, 4, B); _FL_DEFPIN(14, 5, B); _FL_DEFPIN(15, 6, B); +_FL_DEFPIN(16, 7, F); _FL_DEFPIN(17, 6, F); _FL_DEFPIN(18, 5, F); _FL_DEFPIN(19, 4, F); +_FL_DEFPIN(20, 1, F); _FL_DEFPIN(21, 0, F); _FL_DEFPIN(22, 4, D); _FL_DEFPIN(23, 5, D); + +#define SPI_DATA 2 +#define SPI_CLOCK 1 +#define SPI_SELECT 0 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +// PD3/PD5 +#define SPI_UART1_DATA 8 +#define SPI_UART1_CLOCK 23 + +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +// teensy++ 2 defs +#define MAX_PIN 45 +_FL_DEFPIN(0, 0, D); _FL_DEFPIN(1, 1, D); _FL_DEFPIN(2, 2, D); _FL_DEFPIN(3, 3, D); +_FL_DEFPIN(4, 4, D); _FL_DEFPIN(5, 5, D); _FL_DEFPIN(6, 6, D); _FL_DEFPIN(7, 7, D); +_FL_DEFPIN(8, 0, E); _FL_DEFPIN(9, 1, E); _FL_DEFPIN(10, 0, C); _FL_DEFPIN(11, 1, C); +_FL_DEFPIN(12, 2, C); _FL_DEFPIN(13, 3, C); _FL_DEFPIN(14, 4, C); _FL_DEFPIN(15, 5, C); +_FL_DEFPIN(16, 6, C); _FL_DEFPIN(17, 7, C); _FL_DEFPIN(18, 6, E); _FL_DEFPIN(19, 7, E); +_FL_DEFPIN(20, 0, B); _FL_DEFPIN(21, 1, B); _FL_DEFPIN(22, 2, B); _FL_DEFPIN(23, 3, B); +_FL_DEFPIN(24, 4, B); _FL_DEFPIN(25, 5, B); _FL_DEFPIN(26, 6, B); _FL_DEFPIN(27, 7, B); +_FL_DEFPIN(28, 0, A); _FL_DEFPIN(29, 1, A); _FL_DEFPIN(30, 2, A); _FL_DEFPIN(31, 3, A); +_FL_DEFPIN(32, 4, A); _FL_DEFPIN(33, 5, A); _FL_DEFPIN(34, 6, A); _FL_DEFPIN(35, 7, A); +_FL_DEFPIN(36, 4, E); _FL_DEFPIN(37, 5, E); _FL_DEFPIN(38, 0, F); _FL_DEFPIN(39, 1, F); +_FL_DEFPIN(40, 2, F); _FL_DEFPIN(41, 3, F); _FL_DEFPIN(42, 4, F); _FL_DEFPIN(43, 5, F); +_FL_DEFPIN(44, 6, F); _FL_DEFPIN(45, 7, F); + +#define SPI_DATA 22 +#define SPI_CLOCK 21 +#define SPI_SELECT 20 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +// PD3/PD5 +#define SPI_UART1_DATA 3 +#define SPI_UART1_CLOCK 5 + + +#elif defined(__AVR_ATmega32U4__) + +// leonard defs +#define MAX_PIN 30 +_FL_DEFPIN(0, 2, D); _FL_DEFPIN(1, 3, D); _FL_DEFPIN(2, 1, D); _FL_DEFPIN(3, 0, D); +_FL_DEFPIN(4, 4, D); _FL_DEFPIN(5, 6, C); _FL_DEFPIN(6, 7, D); _FL_DEFPIN(7, 6, E); +_FL_DEFPIN(8, 4, B); _FL_DEFPIN(9, 5, B); _FL_DEFPIN(10, 6, B); _FL_DEFPIN(11, 7, B); +_FL_DEFPIN(12, 6, D); _FL_DEFPIN(13, 7, C); _FL_DEFPIN(14, 3, B); _FL_DEFPIN(15, 1, B); +_FL_DEFPIN(16, 2, B); _FL_DEFPIN(17, 0, B); _FL_DEFPIN(18, 7, F); _FL_DEFPIN(19, 6, F); +_FL_DEFPIN(20, 5, F); _FL_DEFPIN(21, 4, F); _FL_DEFPIN(22, 1, F); _FL_DEFPIN(23, 0, F); +_FL_DEFPIN(24, 4, D); _FL_DEFPIN(25, 7, D); _FL_DEFPIN(26, 4, B); _FL_DEFPIN(27, 5, B); +_FL_DEFPIN(28, 6, B); _FL_DEFPIN(29, 6, D); _FL_DEFPIN(30, 5, D); + +#define SPI_DATA 16 +#define SPI_CLOCK 15 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +// PD3/PD5 +#define SPI_UART1_DATA 1 +#define SPI_UART1_CLOCK 30 + + +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_AVR_H diff --git a/platforms/avr/fastspi_avr.h b/src/platforms/avr/fastspi_avr.h similarity index 97% rename from platforms/avr/fastspi_avr.h rename to src/platforms/avr/fastspi_avr.h index fc14d59638..245e40654b 100644 --- a/platforms/avr/fastspi_avr.h +++ b/src/platforms/avr/fastspi_avr.h @@ -20,7 +20,7 @@ FASTLED_NAMESPACE_BEGIN #define UCPHA1 1 #endif -template +template class AVRUSART1SPIOutput { Selectable *m_pSelect; @@ -167,7 +167,7 @@ class AVRUSART1SPIOutput { #endif #if defined(UBRR0) -template +template class AVRUSART0SPIOutput { Selectable *m_pSelect; @@ -187,7 +187,6 @@ class AVRUSART0SPIOutput { FastPin<_CLOCK_PIN>::setOutput(); FastPin<_DATA_PIN>::setOutput(); - // must be done last, see page 206 setSPIRate(); } @@ -249,12 +248,12 @@ class AVRUSART0SPIOutput { setSPIRate(); } - void release() { - if(m_pSelect != NULL) { - m_pSelect->release(); - } - disable_pins(); + void release() { + if(m_pSelect != NULL) { + m_pSelect->release(); } + disable_pins(); + } static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { @@ -329,10 +328,11 @@ class AVRUSART0SPIOutput { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class AVRHardwareSPIOutput { Selectable *m_pSelect; bool mWait; + public: AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;} AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } @@ -506,10 +506,11 @@ class AVRHardwareSPIOutput { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class AVRHardwareSPIOutput { Selectable *m_pSelect; bool mWait; + public: AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;} AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } diff --git a/platforms/avr/led_sysdefs_avr.h b/src/platforms/avr/led_sysdefs_avr.h similarity index 95% rename from platforms/avr/led_sysdefs_avr.h rename to src/platforms/avr/led_sysdefs_avr.h index 2d9722d077..05d6e5ed56 100644 --- a/platforms/avr/led_sysdefs_avr.h +++ b/src/platforms/avr/led_sysdefs_avr.h @@ -46,6 +46,9 @@ extern volatile unsigned long timer0_millis_count; # elif defined(ATTINY_CORE) extern volatile unsigned long millis_timer_millis; # define MS_COUNTER millis_timer_millis +# elif defined(__AVR_ATmega4809__) +extern volatile unsigned long timer_millis; +# define MS_COUNTER timer_millis # else extern volatile unsigned long timer0_millis; # define MS_COUNTER timer0_millis diff --git a/platforms/esp/32/clockless_block_esp32.h b/src/platforms/esp/32/clockless_block_esp32.h similarity index 96% rename from platforms/esp/32/clockless_block_esp32.h rename to src/platforms/esp/32/clockless_block_esp32.h index 8ab5807af8..45b7671cf8 100644 --- a/platforms/esp/32/clockless_block_esp32.h +++ b/src/platforms/esp/32/clockless_block_esp32.h @@ -24,6 +24,7 @@ class InlineBlockClocklessController : public CPixelLEDController mWait; + public: virtual int size() { return CLEDController::size() * LANES; } @@ -34,7 +35,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels); register uint8_t scale = pixels.template getscale(pixels); - for(register uint32_t i = 0; i < USED_LANES; i++) { + for(register uint32_t i = 0; i < USED_LANES; ++i) { while((__clock_cycles() - last_mark) < (T1+T2+T3)); last_mark = __clock_cycles(); *FastPin::sport() = PORT_MASK << REAL_FIRST_PIN; @@ -100,7 +101,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels,i,d,scale); } - for(register uint32_t i = USED_LANES; i < 8; i++) { + for(register uint32_t i = USED_LANES; i < 8; ++i) { while((__clock_cycles() - last_mark) < (T1+T2+T3)); last_mark = __clock_cycles(); *FastPin::sport() = PORT_MASK << REAL_FIRST_PIN; @@ -121,7 +122,7 @@ class InlineBlockClocklessController : public CPixelLEDController +class ClocklessController : public CPixelLEDController +{ + // -- Store the GPIO pin + gpio_num_t mPin; + + // -- This instantiation forces a check on the pin choice + FastPin mFastPin; + + // -- Save the pixel controller + PixelController * mPixels; + + // -- Make sure we can't call show() too quickly + CMinWait<50> mWait; + +public: + void init() + { + i2sInit(); + + // -- Allocate space to save the pixel controller + // during parallel output + mPixels = (PixelController *) malloc(sizeof(PixelController)); + + gControllers[gNumControllers] = this; + int my_index = gNumControllers; + ++gNumControllers; + + // -- Set up the pin We have to do two things: configure the + // actual GPIO pin, and route the output from the default + // pin (determined by the I2S device) to the pin we + // want. We compute the default pin using the index of this + // controller in the array. This order is crucial because + // the bits must go into the DMA buffer in the same order. + mPin = gpio_num_t(DATA_PIN); + + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DATA_PIN], PIN_FUNC_GPIO); + gpio_set_direction(mPin, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT); + pinMode(mPin,OUTPUT); + gpio_matrix_out(mPin, i2s_base_pin_index + my_index, false, false); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + static int pgcd(int smallest,int precision,int a,int b,int c) + { + int pgc_=1; + for( int i=smallest;i>0;--i) + { + + if( a%i<=precision && b%i<=precision && c%i<=precision) + { + pgc_=i; + break; + } + } + return pgc_; + } + + /** Compute pules/bit patterns + * + * This is Yves Bazin's mad code for computing the pulse pattern + * and clock timing given the target signal given by T1, T2, and + * T3. In general, these parameters are interpreted as follows: + * + * a "1" bit is encoded by setting the pin HIGH to T1+T2 ns, then LOW for T3 ns + * a "0" bit is encoded by setting the pin HIGH to T1 ns, then LOW for T2+T3 ns + * + */ + static void initBitPatterns() + { + // Precompute the bit patterns based on the I2S sample rate + // Serial.println("Setting up fastled using I2S"); + + // -- First, convert back to ns from CPU clocks + uint32_t T1ns = ESPCLKS_TO_NS(T1); + uint32_t T2ns = ESPCLKS_TO_NS(T2); + uint32_t T3ns = ESPCLKS_TO_NS(T3); + + // Serial.print("T1 = "); Serial.print(T1); Serial.print(" ns "); Serial.println(T1ns); + // Serial.print("T2 = "); Serial.print(T2); Serial.print(" ns "); Serial.println(T2ns); + // Serial.print("T3 = "); Serial.print(T3); Serial.print(" ns "); Serial.println(T3ns); + + /* + We calculate the best pcgd to the timing + ie + WS2811 77 77 154 => 1 1 2 => nb pulses= 4 + WS2812 60 150 90 => 2 5 3 => nb pulses=10 + */ + int smallest=0; + if (T1>T2) + smallest=T2; + else + smallest=T1; + if(smallest>T3) + smallest=T3; + double freq=(double)1/(double)(T1ns + T2ns + T3ns); + // Serial.printf("chipset frequency:%f Khz\n", 1000000L*freq); + // Serial.printf("smallest %d\n",smallest); + int pgc_=1; + int precision=0; + pgc_=pgcd(smallest,precision,T1,T2,T3); + //Serial.printf("%f\n",I2S_MAX_CLK/(1000000000L*freq)); + while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_PULSE_PER_BIT) //while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_CLK/(1000000000L*freq)) + { + ++precision; + pgc_=pgcd(smallest,precision,T1,T2,T3); + //Serial.printf("%d %d\n",pgc_,(a+b+c)/pgc_); + } + pgc_=pgcd(smallest,precision,T1,T2,T3); + // Serial.printf("pgcd %d precision:%d\n",pgc_,precision); + // Serial.printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_); + gPulsesPerBit=(int)T1/pgc_ +(int)T2/pgc_ +(int)T3/pgc_; + /* + we calculate the duration of one pulse nd htre base frequency of the led + ie WS2812B F=1/(250+625+375)=800kHz or 1250ns + as we need 10 pulses each pulse is 125ns => frequency 800Khz*10=8MHz + WS2811 T=320+320+641=1281ns qnd we need 4 pulses => pulse duration 320.25ns =>frequency 3.1225605Mhz + + */ + + freq=1000000000L*freq*gPulsesPerBit; + // Serial.printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000); + + /* + we do calculate the needed N a and b + as f=basefred/(N+b/a); + as a is max 63 the precision for the decimal is 1/63 + + */ + + CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq); + double v=I2S_BASE_CLK/freq-CLOCK_DIVIDER_N; + + double prec=(double)1/63; + int a=1; + int b=0; + CLOCK_DIVIDER_A=1; + CLOCK_DIVIDER_B=0; + for(a=1;a<64;++a) + { + for(b=0;bbuffer = (uint8_t *)heap_caps_malloc(bytes, MALLOC_CAP_DMA); + memset(b->buffer, 0, bytes); + + b->descriptor.length = bytes; + b->descriptor.size = bytes; + b->descriptor.owner = 1; + b->descriptor.sosf = 1; + b->descriptor.buf = b->buffer; + b->descriptor.offset = 0; + b->descriptor.empty = 0; + b->descriptor.eof = 1; + b->descriptor.qe.stqe_next = 0; + + return b; + } + + static void i2sInit() + { + // -- Only need to do this once + if (gInitialized) return; + + // -- Construct the bit patterns for ones and zeros + initBitPatterns(); + + // -- Choose whether to use I2S device 0 or device 1 + // Set up the various device-specific parameters + int interruptSource; + if (I2S_DEVICE == 0) { + i2s = &I2S0; + periph_module_enable(PERIPH_I2S0_MODULE); + interruptSource = ETS_I2S0_INTR_SOURCE; + i2s_base_pin_index = I2S0O_DATA_OUT0_IDX; + } else { + i2s = &I2S1; + periph_module_enable(PERIPH_I2S1_MODULE); + interruptSource = ETS_I2S1_INTR_SOURCE; + i2s_base_pin_index = I2S1O_DATA_OUT0_IDX; + } + + // -- Reset everything + i2sReset(); + i2sReset_DMA(); + i2sReset_FIFO(); + + // -- Main configuration + i2s->conf.tx_msb_right = 1; + i2s->conf.tx_mono = 0; + i2s->conf.tx_short_sync = 0; + i2s->conf.tx_msb_shift = 0; + i2s->conf.tx_right_first = 1; // 0;//1; + i2s->conf.tx_slave_mod = 0; + + // -- Set parallel mode + i2s->conf2.val = 0; + i2s->conf2.lcd_en = 1; + i2s->conf2.lcd_tx_wrx2_en = 0; // 0 for 16 or 32 parallel output + i2s->conf2.lcd_tx_sdx2_en = 0; // HN + + // -- Set up the clock rate and sampling + i2s->sample_rate_conf.val = 0; + i2s->sample_rate_conf.tx_bits_mod = 32; // Number of parallel bits/pins + i2s->sample_rate_conf.tx_bck_div_num = 1; + i2s->clkm_conf.val = 0; + i2s->clkm_conf.clka_en = 0; + + // -- Data clock is computed as Base/(div_num + (div_b/div_a)) + // Base is 80Mhz, so 80/(10 + 0/1) = 8Mhz + // One cycle is 125ns + i2s->clkm_conf.clkm_div_a = CLOCK_DIVIDER_A; + i2s->clkm_conf.clkm_div_b = CLOCK_DIVIDER_B; + i2s->clkm_conf.clkm_div_num = CLOCK_DIVIDER_N; + + i2s->fifo_conf.val = 0; + i2s->fifo_conf.tx_fifo_mod_force_en = 1; + i2s->fifo_conf.tx_fifo_mod = 3; // 32-bit single channel data + i2s->fifo_conf.tx_data_num = 32; // fifo length + i2s->fifo_conf.dscr_en = 1; // fifo will use dma + + i2s->conf1.val = 0; + i2s->conf1.tx_stop_en = 0; + i2s->conf1.tx_pcm_bypass = 1; + + i2s->conf_chan.val = 0; + i2s->conf_chan.tx_chan_mod = 1; // Mono mode, with tx_msb_right = 1, everything goes to right-channel + + i2s->timing.val = 0; + + // -- Allocate two DMA buffers + dmaBuffers[0] = allocateDMABuffer(32 * NUM_COLOR_CHANNELS * gPulsesPerBit); + dmaBuffers[1] = allocateDMABuffer(32 * NUM_COLOR_CHANNELS * gPulsesPerBit); + + // -- Arrange them as a circularly linked list + dmaBuffers[0]->descriptor.qe.stqe_next = &(dmaBuffers[1]->descriptor); + dmaBuffers[1]->descriptor.qe.stqe_next = &(dmaBuffers[0]->descriptor); + + // -- Allocate i2s interrupt + SET_PERI_REG_BITS(I2S_INT_ENA_REG(I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S); + esp_intr_alloc(interruptSource, 0, // ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3, + &interruptHandler, 0, &gI2S_intr_handle); + + // -- Create a semaphore to block execution until all the controllers are done + if (gTX_sem == NULL) { + gTX_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(gTX_sem); + } + + // Serial.println("Init I2S"); + gInitialized = true; + } + + /** Clear DMA buffer + * + * Yves' clever trick: initialize the bits that we know must be 0 + * or 1 regardless of what bit they encode. + */ + static void empty( uint32_t *buf) + { + for(int i=0;i<8*NUM_COLOR_CHANNELS;++i) + { + int offset=gPulsesPerBit*i; + for(int j=0;j & pixels) + { + if (gNumStarted == 0) { + // -- First controller: make sure everything is set up + xSemaphoreTake(gTX_sem, portMAX_DELAY); + } + + // -- Initialize the local state, save a pointer to the pixel + // data. We need to make a copy because pixels is a local + // variable in the calling function, and this data structure + // needs to outlive this call to showPixels. + (*mPixels) = pixels; + + // -- Keep track of the number of strips we've seen + ++gNumStarted; + + // Serial.print("Show pixels "); + // Serial.println(gNumStarted); + + // -- The last call to showPixels is the one responsible for doing + // all of the actual work + if (gNumStarted == gNumControllers) { + empty((uint32_t*)dmaBuffers[0]->buffer); + empty((uint32_t*)dmaBuffers[1]->buffer); + gCurBuffer = 0; + gDoneFilling = false; + + // -- Prefill both buffers + fillBuffer(); + fillBuffer(); + + // -- Make sure it's been at least 50ms since last show + mWait.wait(); + + i2sStart(); + + // -- Wait here while the rest of the data is sent. The interrupt handler + // will keep refilling the DMA buffers until it is all sent; then it + // gives the semaphore back. + xSemaphoreTake(gTX_sem, portMAX_DELAY); + xSemaphoreGive(gTX_sem); + + i2sStop(); + + mWait.mark(); + + // -- Reset the counters + gNumStarted = 0; + } + } + + // -- Custom interrupt handler + static IRAM_ATTR void interruptHandler(void *arg) + { + if (i2s->int_st.out_eof) { + i2s->int_clr.val = i2s->int_raw.val; + + if ( ! gDoneFilling) { + fillBuffer(); + } else { + portBASE_TYPE HPTaskAwoken = 0; + xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); + } + } + } + + /** Fill DMA buffer + * + * This is where the real work happens: take a row of pixels (one + * from each strip), transpose and encode the bits, and store + * them in the DMA buffer for the I2S peripheral to read. + */ + static void fillBuffer() + { + // -- Alternate between buffers + volatile uint32_t * buf = (uint32_t *) dmaBuffers[gCurBuffer]->buffer; + gCurBuffer = (gCurBuffer + 1) % NUM_DMA_BUFFERS; + + // -- Get the requested pixel from each controller. Store the + // data for each color channel in a separate array. + uint32_t has_data_mask = 0; + for (int i = 0; i < gNumControllers; ++i) { + // -- Store the pixels in reverse controller order starting at index 23 + // This causes the bits to come out in the right position after we + // transpose them. + int bit_index = 23-i; + ClocklessController * pController = static_cast(gControllers[i]); + if (pController->mPixels->has(1)) { + gPixelRow[0][bit_index] = pController->mPixels->loadAndScale0(); + gPixelRow[1][bit_index] = pController->mPixels->loadAndScale1(); + gPixelRow[2][bit_index] = pController->mPixels->loadAndScale2(); + pController->mPixels->advanceData(); + pController->mPixels->stepDithering(); + + // -- Record that this controller still has data to send + has_data_mask |= (1 << (i+8)); + } + } + + // -- None of the strips has data? We are done. + if (has_data_mask == 0) { + gDoneFilling = true; + return; + } + + // -- Transpose and encode the pixel data for the DMA buffer + // int buf_index = 0; + for (int channel = 0; channel < NUM_COLOR_CHANNELS; ++channel) { + + // -- Tranpose each array: all the bit 7's, then all the bit 6's, ... + transpose32(gPixelRow[channel], gPixelBits[channel][0] ); + + //Serial.print("Channel: "); Serial.print(channel); Serial.print(" "); + for (int bitnum = 0; bitnum < 8; ++bitnum) { + uint8_t * row = (uint8_t *) (gPixelBits[channel][bitnum]); + uint32_t bit = (row[0] << 24) | (row[1] << 16) | (row[2] << 8) | row[3]; + + /* SZG: More general, but too slow: + for (int pulse_num = 0; pulse_num < gPulsesPerBit; ++pulse_num) { + buf[buf_index++] = has_data_mask & ( (bit & gOneBit[pulse_num]) | (~bit & gZeroBit[pulse_num]) ); + } + */ + + // -- Only fill in the pulses that are different between the "0" and "1" encodings + for(int pulse_num = ones_for_zero; pulse_num < ones_for_one; ++pulse_num) { + buf[bitnum*gPulsesPerBit+channel*8*gPulsesPerBit+pulse_num] = has_data_mask & bit; + } + } + } + } + + static void transpose32(uint8_t * pixels, uint8_t * bits) + { + transpose8rS32(& pixels[0], 1, 4, & bits[0]); + transpose8rS32(& pixels[8], 1, 4, & bits[1]); + transpose8rS32(& pixels[16], 1, 4, & bits[2]); + //transpose8rS32(& pixels[24], 1, 4, & bits[3]); Can only use 24 bits + } + + /** Transpose 8x8 bit matrix + * From Hacker's Delight + */ + static void transpose8rS32(uint8_t * A, int m, int n, uint8_t * B) + { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + + x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m]; + y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m]; + + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + + B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x; + B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y; + } + + /** Start I2S transmission + */ + static void i2sStart() + { + // esp_intr_disable(gI2S_intr_handle); + // Serial.println("I2S start"); + i2sReset(); + //Serial.println(dmaBuffers[0]->sampleCount()); + i2s->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN; + i2s->out_link.addr = (uint32_t) & (dmaBuffers[0]->descriptor); + i2s->out_link.start = 1; + ////vTaskDelay(5); + i2s->int_clr.val = i2s->int_raw.val; + // //vTaskDelay(5); + i2s->int_ena.out_dscr_err = 1; + //enable interrupt + ////vTaskDelay(5); + esp_intr_enable(gI2S_intr_handle); + // //vTaskDelay(5); + i2s->int_ena.val = 0; + i2s->int_ena.out_eof = 1; + + //start transmission + i2s->conf.tx_start = 1; + } + + static void i2sReset() + { + // Serial.println("I2S reset"); + const unsigned long lc_conf_reset_flags = I2S_IN_RST_M | I2S_OUT_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M; + i2s->lc_conf.val |= lc_conf_reset_flags; + i2s->lc_conf.val &= ~lc_conf_reset_flags; + + const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M; + i2s->conf.val |= conf_reset_flags; + i2s->conf.val &= ~conf_reset_flags; + } + + static void i2sReset_DMA() + { + i2s->lc_conf.in_rst=1; i2s->lc_conf.in_rst=0; + i2s->lc_conf.out_rst=1; i2s->lc_conf.out_rst=0; + } + + static void i2sReset_FIFO() + { + i2s->conf.rx_fifo_reset=1; i2s->conf.rx_fifo_reset=0; + i2s->conf.tx_fifo_reset=1; i2s->conf.tx_fifo_reset=0; + } + + static void i2sStop() + { + // Serial.println("I2S stop"); + esp_intr_disable(gI2S_intr_handle); + i2sReset(); + i2s->conf.rx_start = 0; + i2s->conf.tx_start = 0; + } +}; + +FASTLED_NAMESPACE_END diff --git a/src/platforms/esp/32/clockless_rmt_esp32.cpp b/src/platforms/esp/32/clockless_rmt_esp32.cpp new file mode 100644 index 0000000000..987f1bfc66 --- /dev/null +++ b/src/platforms/esp/32/clockless_rmt_esp32.cpp @@ -0,0 +1,419 @@ + +#ifdef ESP32 + +#define FASTLED_INTERNAL +#include "FastLED.h" + +// -- Forward reference +class ESP32RMTController; + +// -- Array of all controllers +// This array is filled at the time controllers are registered +// (Usually when the sketch calls addLeds) +static ESP32RMTController * gControllers[FASTLED_RMT_MAX_CONTROLLERS]; + +// -- Current set of active controllers, indexed by the RMT +// channel assigned to them. +static ESP32RMTController * gOnChannel[FASTLED_RMT_MAX_CHANNELS]; + +static int gNumControllers = 0; +static int gNumStarted = 0; +static int gNumDone = 0; +static int gNext = 0; + +static intr_handle_t gRMT_intr_handle = NULL; + +// -- Global semaphore for the whole show process +// Semaphore is not given until all data has been sent +static xSemaphoreHandle gTX_sem = NULL; + +// -- Make sure we can't call show() too quickly +CMinWait<50> gWait; + +static bool gInitialized = false; + +ESP32RMTController::ESP32RMTController(int DATA_PIN, int T1, int T2, int T3) + : mPixelData(0), + mSize(0), + mCur(0), + mWhichHalf(0), + mBuffer(0), + mBufferSize(0), + mCurPulse(0) +{ + // -- Precompute rmt items corresponding to a zero bit and a one bit + // according to the timing values given in the template instantiation + // T1H + mOne.level0 = 1; + mOne.duration0 = ESP_TO_RMT_CYCLES(T1+T2); // TO_RMT_CYCLES(T1+T2); + // T1L + mOne.level1 = 0; + mOne.duration1 = ESP_TO_RMT_CYCLES(T3); // TO_RMT_CYCLES(T3); + + // T0H + mZero.level0 = 1; + mZero.duration0 = ESP_TO_RMT_CYCLES(T1); // TO_RMT_CYCLES(T1); + // T0L + mZero.level1 = 0; + mZero.duration1 = ESP_TO_RMT_CYCLES(T2+T3); // TO_RMT_CYCLES(T2 + T3); + + gControllers[gNumControllers] = this; + gNumControllers++; + + // -- Expected number of CPU cycles between buffer fills + mCyclesPerFill = (T1 + T2 + T3) * PULSES_PER_FILL; + + // -- If there is ever an interval greater than 1.5 times + // the expected time, then bail out. + mMaxCyclesPerFill = mCyclesPerFill + mCyclesPerFill/2; + + mPin = gpio_num_t(DATA_PIN); +} + +// -- Get or create the buffer for the pixel data +// We can't allocate it ahead of time because we don't have +// the PixelController object until show is called. +uint32_t * ESP32RMTController::getPixelBuffer(int size_in_bytes) +{ + if (mPixelData == 0) { + mSize = ((size_in_bytes-1) / sizeof(uint32_t)) + 1; + mPixelData = (uint32_t *) calloc( mSize, sizeof(uint32_t)); + } + return mPixelData; +} + +// -- Initialize RMT subsystem +// This only needs to be done once +void ESP32RMTController::init() +{ + if (gInitialized) return; + + for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) { + gOnChannel[i] = NULL; + + // -- RMT configuration for transmission + rmt_config_t rmt_tx; + rmt_tx.channel = rmt_channel_t(i); + rmt_tx.rmt_mode = RMT_MODE_TX; + rmt_tx.gpio_num = gpio_num_t(0); // The particular pin will be assigned later + rmt_tx.mem_block_num = FASTLED_RMT_MEM_BLOCKS; + rmt_tx.clk_div = DIVIDER; + rmt_tx.tx_config.loop_en = false; + rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; + rmt_tx.tx_config.carrier_en = false; + rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; + rmt_tx.tx_config.idle_output_en = true; + + // -- Apply the configuration + rmt_config(&rmt_tx); + + if (FASTLED_RMT_BUILTIN_DRIVER) { + rmt_driver_install(rmt_channel_t(i), 0, 0); + } else { + // -- Set up the RMT to send 32 bits of the pulse buffer and then + // generate an interrupt. When we get this interrupt we + // fill the other part in preparation (like double-buffering) + rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, PULSES_PER_FILL); + } + } + + // -- Create a semaphore to block execution until all the controllers are done + if (gTX_sem == NULL) { + gTX_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(gTX_sem); + } + + if ( ! FASTLED_RMT_BUILTIN_DRIVER) { + // -- Allocate the interrupt if we have not done so yet. This + // interrupt handler must work for all different kinds of + // strips, so it delegates to the refill function for each + // specific instantiation of ClocklessController. + if (gRMT_intr_handle == NULL) + esp_intr_alloc(ETS_RMT_INTR_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3, interruptHandler, 0, &gRMT_intr_handle); + } + + gInitialized = true; +} + +// -- Show this string of pixels +// This is the main entry point for the pixel controller +void ESP32RMTController::showPixels() +{ + if (gNumStarted == 0) { + // -- First controller: make sure everything is set up + ESP32RMTController::init(); + +#if FASTLED_ESP32_FLASH_LOCK == 1 + // -- Make sure no flash operations happen right now + spi_flash_op_lock(); +#endif + } + + // -- Keep track of the number of strips we've seen + gNumStarted++; + + // -- The last call to showPixels is the one responsible for doing + // all of the actual worl + if (gNumStarted == gNumControllers) { + gNext = 0; + + // -- This Take always succeeds immediately + xSemaphoreTake(gTX_sem, portMAX_DELAY); + + // -- Make sure it's been at least 50us since last show + gWait.wait(); + + // -- First, fill all the available channels + int channel = 0; + while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) { + ESP32RMTController::startNext(channel); + // -- Important: when we use more than one memory block, we need to + // skip the channels that would otherwise overlap in memory. + channel += FASTLED_RMT_MEM_BLOCKS; + } + + // -- Wait here while the data is sent. The interrupt handler + // will keep refilling the RMT buffers until it is all + // done; then it gives the semaphore back. + xSemaphoreTake(gTX_sem, portMAX_DELAY); + xSemaphoreGive(gTX_sem); + + // -- Make sure we don't call showPixels too quickly + gWait.mark(); + + // -- Reset the counters + gNumStarted = 0; + gNumDone = 0; + gNext = 0; + +#if FASTLED_ESP32_FLASH_LOCK == 1 + // -- Release the lock on flash operations + spi_flash_op_unlock(); +#endif + + } +} + +// -- Start up the next controller +// This method is static so that it can dispatch to the +// appropriate startOnChannel method of the given controller. +void ESP32RMTController::startNext(int channel) +{ + if (gNext < gNumControllers) { + ESP32RMTController * pController = gControllers[gNext]; + pController->startOnChannel(channel); + gNext++; + } +} + +// -- Start this controller on the given channel +// This function just initiates the RMT write; it does not wait +// for it to finish. +void ESP32RMTController::startOnChannel(int channel) +{ + // -- Assign this channel and configure the RMT + mRMT_channel = rmt_channel_t(channel); + + // -- Store a reference to this controller, so we can get it + // inside the interrupt handler + gOnChannel[channel] = this; + + // -- Assign the pin to this channel + rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); + + if (FASTLED_RMT_BUILTIN_DRIVER) { + // -- Use the built-in RMT driver to send all the data in one shot + rmt_register_tx_end_callback(doneOnChannel, 0); + rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false); + } else { + // -- Use our custom driver to send the data incrementally + + // -- Initialize the counters that keep track of where we are in + // the pixel data and the RMT buffer + mRMT_mem_start = & (RMTMEM.chan[mRMT_channel].data32[0].val); + mRMT_mem_ptr = mRMT_mem_start; + mCur = 0; + mWhichHalf = 0; + mLastFill = 0; + + // -- Fill both halves of the RMT buffer (a totaly of 64 bits of pixel data) + fillNext(false); + fillNext(false); + + // -- Turn on the interrupts + rmt_set_tx_intr_en(mRMT_channel, true); + + // -- Kick off the transmission + tx_start(); + } +} + +// -- Start RMT transmission +// Setting this RMT flag is what actually kicks off the peripheral +void ESP32RMTController::tx_start() +{ + rmt_tx_start(mRMT_channel, true); + mLastFill = __clock_cycles(); +} + +// -- A controller is done +// This function is called when a controller finishes writing +// its data. It is called either by the custom interrupt +// handler (below), or as a callback from the built-in +// interrupt handler. It is static because we don't know which +// controller is done until we look it up. +void ESP32RMTController::doneOnChannel(rmt_channel_t channel, void * arg) +{ + ESP32RMTController * pController = gOnChannel[channel]; + + // -- Turn off output on the pin + // SZG: Do I really need to do this? + gpio_matrix_out(pController->mPin, 0x100, 0, 0); + + // -- Turn off the interrupts + rmt_set_tx_intr_en(channel, false); + + gOnChannel[channel] = NULL; + gNumDone++; + + if (gNumDone == gNumControllers) { + // -- If this is the last controller, signal that we are all done + if (FASTLED_RMT_BUILTIN_DRIVER) { + xSemaphoreGive(gTX_sem); + } else { + portBASE_TYPE HPTaskAwoken = 0; + xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR(); + } + } else { + // -- Otherwise, if there are still controllers waiting, then + // start the next one on this channel + if (gNext < gNumControllers) { + startNext(channel); + } + } +} + +// -- Custom interrupt handler +// This interrupt handler handles two cases: a controller is +// done writing its data, or a controller needs to fill the +// next half of the RMT buffer with data. +void IRAM_ATTR ESP32RMTController::interruptHandler(void *arg) +{ + // -- The basic structure of this code is borrowed from the + // interrupt handler in esp-idf/components/driver/rmt.c + uint32_t intr_st = RMT.int_st.val; + uint8_t channel; + + bool stuff_to_do = false; + for (channel = 0; channel < FASTLED_RMT_MAX_CHANNELS; channel++) { + int tx_done_bit = channel * 3; + int tx_next_bit = channel + 24; + + ESP32RMTController * pController = gOnChannel[channel]; + if (pController != NULL) { + if (intr_st & BIT(tx_next_bit)) { + // -- More to send on this channel + pController->fillNext(true); + RMT.int_clr.val |= BIT(tx_next_bit); + } else { + // -- Transmission is complete on this channel + if (intr_st & BIT(tx_done_bit)) { + RMT.int_clr.val |= BIT(tx_done_bit); + doneOnChannel(rmt_channel_t(channel), 0); + } + } + } + } +} + +// -- Fill RMT buffer +// Puts 32 bits of pixel data into the next 32 slots in the RMT memory +// Each data bit is represented by a 32-bit RMT item that specifies how +// long to hold the signal high, followed by how long to hold it low. +void IRAM_ATTR ESP32RMTController::fillNext(bool check_time) +{ + uint32_t now = __clock_cycles(); + if (check_time) { + if (mLastFill != 0 and now > mLastFill) { + uint32_t delta = (now - mLastFill); + if (delta > mMaxCyclesPerFill) { + Serial.print(delta); + Serial.print(" BAIL "); + Serial.println(mCur); + mCur = mSize; + rmt_tx_stop(mRMT_channel); + } + } + } + mLastFill = now; + + // -- Get the zero and one values into local variables + register uint32_t one_val = mOne.val; + register uint32_t zero_val = mZero.val; + + // -- Use locals for speed + volatile register uint32_t * pItem = mRMT_mem_ptr; + + for (register int i = 0; i < PULSES_PER_FILL/32; i++) { + if (mCur < mSize) { + + // -- Get the next four bytes of pixel data + register uint32_t pixeldata = mPixelData[mCur]; + mCur++; + + // Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the + // rmt_item32_t value corresponding to the buffered bit value + for (register uint32_t j = 0; j < 32; j++) { + *pItem++ = (pixeldata & 0x80000000L) ? one_val : zero_val; + // Replaces: RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val; + + pixeldata <<= 1; + } + } else { + // -- No more data; signal to the RMT we are done + for (uint32_t j = 0; j < 32; j++) { + * mRMT_mem_ptr++ = 0; + } + } + } + + // -- Flip to the other half, resetting the pointer if necessary + mWhichHalf++; + if (mWhichHalf == 2) { + pItem = mRMT_mem_start; + mWhichHalf = 0; + } + + // -- Store the new pointer back into the object + mRMT_mem_ptr = pItem; +} + +// -- Init pulse buffer +// Set up the buffer that will hold all of the pulse items for this +// controller. +// This function is only used when the built-in RMT driver is chosen +void ESP32RMTController::initPulseBuffer(int size_in_bytes) +{ + if (mBuffer == 0) { + // -- Each byte has 8 bits, each bit needs a 32-bit RMT item + mBufferSize = size_in_bytes * 8 * 4; + mBuffer = (rmt_item32_t *) calloc( mBufferSize, sizeof(rmt_item32_t)); + } + mCurPulse = 0; +} + +// -- Convert a byte into RMT pulses +// This function is only used when the built-in RMT driver is chosen +void ESP32RMTController::convertByte(uint32_t byteval) +{ + // -- Write one byte's worth of RMT pulses to the big buffer + byteval <<= 24; + for (register uint32_t j = 0; j < 8; j++) { + mBuffer[mCurPulse] = (byteval & 0x80000000L) ? mOne : mZero; + byteval <<= 1; + mCurPulse++; + } +} + +#endif diff --git a/src/platforms/esp/32/clockless_rmt_esp32.h b/src/platforms/esp/32/clockless_rmt_esp32.h new file mode 100644 index 0000000000..68861b5006 --- /dev/null +++ b/src/platforms/esp/32/clockless_rmt_esp32.h @@ -0,0 +1,411 @@ +/* + * Integration into FastLED ClocklessController + * Copyright (c) 2018,2019,2020 Samuel Z. Guyer + * Copyright (c) 2017 Thomas Basler + * Copyright (c) 2017 Martin F. Falatic + * + * ESP32 support is provided using the RMT peripheral device -- a unit + * on the chip designed specifically for generating (and receiving) + * precisely-timed digital signals. Nominally for use in infrared + * remote controls, we use it to generate the signals for clockless + * LED strips. The main advantage of using the RMT device is that, + * once programmed, it generates the signal asynchronously, allowing + * the CPU to continue executing other code. It is also not vulnerable + * to interrupts or other timing problems that could disrupt the signal. + * + * The implementation strategy is borrowed from previous work and from + * the RMT support built into the ESP32 IDF. The RMT device has 8 + * channels, which can be programmed independently to send sequences + * of high/low bits. Memory for each channel is limited, however, so + * in order to send a long sequence of bits, we need to continuously + * refill the buffer until all the data is sent. To do this, we fill + * half the buffer and then set an interrupt to go off when that half + * is sent. Then we refill that half while the second half is being + * sent. This strategy effectively overlaps computation (by the CPU) + * and communication (by the RMT). + * + * Since the RMT device only has 8 channels, we need a strategy to + * allow more than 8 LED controllers. Our driver assigns controllers + * to channels on the fly, queuing up controllers as necessary until a + * channel is free. The main showPixels routine just fires off the + * first 8 controllers; the interrupt handler starts new controllers + * asynchronously as previous ones finish. So, for example, it can + * send the data for 8 controllers simultaneously, but 16 controllers + * would take approximately twice as much time. + * + * There is a #define that allows a program to control the total + * number of channels that the driver is allowed to use. It defaults + * to 8 -- use all the channels. Setting it to 1, for example, results + * in fully serial output: + * + * #define FASTLED_RMT_MAX_CHANNELS 1 + * + * OTHER RMT APPLICATIONS + * + * The default FastLED driver takes over control of the RMT interrupt + * handler, making it hard to use the RMT device for other + * (non-FastLED) purposes. You can change it's behavior to use the ESP + * core driver instead, allowing other RMT applications to + * co-exist. To switch to this mode, add the following directive + * before you include FastLED.h: + * + * #define FASTLED_RMT_BUILTIN_DRIVER 1 + * + * There may be a performance penalty for using this mode. We need to + * compute the RMT signal for the entire LED strip ahead of time, + * rather than overlapping it with communication. We also need a large + * buffer to hold the signal specification. Each bit of pixel data is + * represented by a 32-bit pulse specification, so it is a 32X blow-up + * in memory use. + * + * NEW: Use of Flash memory on the ESP32 can interfere with the timing + * of pixel output. The ESP-IDF system code disables all other + * code running on *either* core during these operation. To prevent + * this from happening, define this flag. It will force flash + * operations to wait until the show() is done. + * + * #define FASTLED_ESP32_FLASH_LOCK 1 + * + * NEW (June 2020): The RMT controller has been split into two + * classes: ClocklessController, which is an instantiation of the + * FastLED CPixelLEDController template, and ESP32RMTController, + * which just handles driving the RMT peripheral. One benefit of + * this design is that ESP32RMTContoller is not a template, so + * its methods can be marked with the IRAM_ATTR and end up in + * IRAM memory. Another benefit is that all of the color channel + * processing is done up-front, in the templated class, so we + * can fill the RMT buffers more quickly. + * + * IN THEORY, this design would also allow FastLED.show() to + * send the data while the program continues to prepare the next + * frame of data. + * + * Based on public domain code created 19 Nov 2016 by Chris Osborn + * http://insentricity.com * + * + */ +/* + * 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. + */ + +#pragma once + +FASTLED_NAMESPACE_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "esp_intr.h" +#include "driver/gpio.h" +#include "driver/rmt.h" +#include "driver/periph_ctrl.h" +#include "freertos/semphr.h" +#include "soc/rmt_struct.h" + +#include "esp_log.h" + +extern void spi_flash_op_lock(void); +extern void spi_flash_op_unlock(void); + +#ifdef __cplusplus +} +#endif + +__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { + uint32_t cyc; + __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc)); + return cyc; +} + +#define FASTLED_HAS_CLOCKLESS 1 +#define NUM_COLOR_CHANNELS 3 + +// NOT CURRENTLY IMPLEMENTED: +// -- Set to true to print debugging information about timing +// Useful for finding out if timing is being messed up by other things +// on the processor (WiFi, for example) +//#ifndef FASTLED_RMT_SHOW_TIMER +//#define FASTLED_RMT_SHOW_TIMER false +//#endif + +// -- Configuration constants +#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */ + +// -- RMT memory configuration +// By default we use two memory blocks for each RMT channel instead of 1. The +// reason is that one memory block is only 64 bits, which causes the refill +// interrupt to fire too often. When combined with WiFi, this leads to conflicts +// between interrupts and weird flashy effects on the LEDs. Special thanks to +// Brian Bulkowski for finding this problem and developing a fix. +#ifndef FASTLED_RMT_MEM_BLOCKS +#define FASTLED_RMT_MEM_BLOCKS 2 +#endif + +#define MAX_PULSES (64 * FASTLED_RMT_MEM_BLOCKS) /* One block has a 64 "pulse" buffer */ +#define PULSES_PER_FILL (MAX_PULSES / 2) /* Half of the channel buffer */ + +// -- Convert ESP32 CPU cycles to RMT device cycles, taking into account the divider +#define F_CPU_RMT ( 80000000L) +#define RMT_CYCLES_PER_SEC (F_CPU_RMT/DIVIDER) +#define RMT_CYCLES_PER_ESP_CYCLE (F_CPU / RMT_CYCLES_PER_SEC) +#define ESP_TO_RMT_CYCLES(n) ((n) / (RMT_CYCLES_PER_ESP_CYCLE)) + +// -- Number of cycles to signal the strip to latch +#define NS_PER_CYCLE ( 1000000000L / RMT_CYCLES_PER_SEC ) +#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) +#define RMT_RESET_DURATION NS_TO_CYCLES(50000) + +// -- Core or custom driver +#ifndef FASTLED_RMT_BUILTIN_DRIVER +#define FASTLED_RMT_BUILTIN_DRIVER false +#endif + +// -- Max number of controllers we can support +#ifndef FASTLED_RMT_MAX_CONTROLLERS +#define FASTLED_RMT_MAX_CONTROLLERS 32 +#endif + +// -- Number of RMT channels to use (up to 8, but 4 by default) +// Redefine this value to 1 to force serial output +#ifndef FASTLED_RMT_MAX_CHANNELS +#define FASTLED_RMT_MAX_CHANNELS (8/FASTLED_RMT_MEM_BLOCKS) +#endif + +class ESP32RMTController +{ +private: + + // -- RMT has 8 channels, numbered 0 to 7 + rmt_channel_t mRMT_channel; + + // -- Store the GPIO pin + gpio_num_t mPin; + + // -- Timing values for zero and one bits, derived from T1, T2, and T3 + rmt_item32_t mZero; + rmt_item32_t mOne; + + // -- Total expected time to send 32 bits + // Each strip should get an interrupt roughly at this interval + uint32_t mCyclesPerFill; + uint32_t mMaxCyclesPerFill; + uint32_t mLastFill; + + // -- Pixel data + uint32_t * mPixelData; + int mSize; + int mCur; + + // -- RMT memory + volatile uint32_t * mRMT_mem_ptr; + volatile uint32_t * mRMT_mem_start; + int mWhichHalf; + + // -- Buffer to hold all of the pulses. For the version that uses + // the RMT driver built into the ESP core. + rmt_item32_t * mBuffer; + uint16_t mBufferSize; // bytes + int mCurPulse; + +public: + + // -- Constructor + // Mainly just stores the template parameters from the LEDController as + // member variables. + ESP32RMTController(int DATA_PIN, int T1, int T2, int T3); + + // -- Get max cycles per fill + uint32_t IRAM_ATTR getMaxCyclesPerFill() const { return mMaxCyclesPerFill; } + + // -- Get or create the pixel data buffer + uint32_t * getPixelBuffer(int size_in_bytes); + + // -- Initialize RMT subsystem + // This only needs to be done once + static void init(); + + // -- Show this string of pixels + // This is the main entry point for the pixel controller + void IRAM_ATTR showPixels(); + + // -- Start up the next controller + // This method is static so that it can dispatch to the + // appropriate startOnChannel method of the given controller. + static void IRAM_ATTR startNext(int channel); + + // -- Start this controller on the given channel + // This function just initiates the RMT write; it does not wait + // for it to finish. + void IRAM_ATTR startOnChannel(int channel); + + // -- Start RMT transmission + // Setting this RMT flag is what actually kicks off the peripheral + void IRAM_ATTR tx_start(); + + // -- A controller is done + // This function is called when a controller finishes writing + // its data. It is called either by the custom interrupt + // handler (below), or as a callback from the built-in + // interrupt handler. It is static because we don't know which + // controller is done until we look it up. + static void IRAM_ATTR doneOnChannel(rmt_channel_t channel, void * arg); + + // -- Custom interrupt handler + // This interrupt handler handles two cases: a controller is + // done writing its data, or a controller needs to fill the + // next half of the RMT buffer with data. + static void IRAM_ATTR interruptHandler(void *arg); + + // -- Fill RMT buffer + // Puts 32 bits of pixel data into the next 32 slots in the RMT memory + // Each data bit is represented by a 32-bit RMT item that specifies how + // long to hold the signal high, followed by how long to hold it low. + // NOTE: Now the default is to use 128-bit buffers, so half a buffer is + // is 64 bits. See FASTLED_RMT_MEM_BLOCKS + void IRAM_ATTR fillNext(bool check_time); + + // -- Init pulse buffer + // Set up the buffer that will hold all of the pulse items for this + // controller. + // This function is only used when the built-in RMT driver is chosen + void initPulseBuffer(int size_in_bytes); + + // -- Convert a byte into RMT pulses + // This function is only used when the built-in RMT driver is chosen + void convertByte(uint32_t byteval); +}; + +template +class ClocklessController : public CPixelLEDController +{ +private: + + // -- The actual controller object for ESP32 + ESP32RMTController mRMTController; + + // -- This instantiation forces a check on the pin choice + FastPin mFastPin; + +public: + + ClocklessController() + : mRMTController(DATA_PIN, T1, T2, T3) + {} + + void init() + { + // mRMTController = new ESP32RMTController(DATA_PIN, T1, T2, T3); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + + // -- Load pixel data + // This method loads all of the pixel data into a separate buffer for use by + // by the RMT driver. Copying does two important jobs: it fixes the color + // order for the pixels, and it performs the scaling/adjusting ahead of time. + // It also packs the bytes into 32 bit chunks with the right bit order. + void loadPixelData(PixelController & pixels) + { + // -- Make sure the buffer is allocated + int size_in_bytes = pixels.size() * 3; + uint32_t * pData = mRMTController.getPixelBuffer(size_in_bytes); + + // -- Read out the pixel data using the pixel controller methods that + // perform the scaling and adjustments + int count = 0; + int which = 0; + while (pixels.has(1)) { + // -- Get the next four bytes of data + uint8_t four[4] = {0,0,0,0}; + for (int i = 0; i < 4; i++) { + switch (which) { + case 0: + four[i] = pixels.loadAndScale0(); + break; + case 1: + four[i] = pixels.loadAndScale1(); + break; + case 2: + four[i] = pixels.loadAndScale2(); + pixels.advanceData(); + pixels.stepDithering(); + break; + } + // -- Move to the next color + which++; + if (which > 2) which = 0; + + // -- Stop if there's no more data + if ( ! pixels.has(1)) break; + } + + // -- Pack the four bytes into a 32-bit value with the right bit order + uint8_t a = four[0]; + uint8_t b = four[1]; + uint8_t c = four[2]; + uint8_t d = four[3]; + pData[count++] = a << 24 | b << 16 | c << 8 | d; + } + } + + // -- Show pixels + // This is the main entry point for the controller. + virtual void showPixels(PixelController & pixels) + { + if (FASTLED_RMT_BUILTIN_DRIVER) { + convertAllPixelData(pixels); + } else { + loadPixelData(pixels); + } + + mRMTController.showPixels(); + } + + // -- Convert all pixels to RMT pulses + // This function is only used when the user chooses to use the + // built-in RMT driver, which needs all of the RMT pulses + // up-front. + void convertAllPixelData(PixelController & pixels) + { + // -- Make sure the data buffer is allocated + mRMTController.initPulseBuffer(pixels.size() * 3); + + // -- Cycle through the R,G, and B values in the right order, + // storing the pulses in the big buffer + + uint32_t byteval; + while (pixels.has(1)) { + byteval = pixels.loadAndScale0(); + mRMTController.convertByte(byteval); + byteval = pixels.loadAndScale1(); + mRMTController.convertByte(byteval); + byteval = pixels.loadAndScale2(); + mRMTController.convertByte(byteval); + pixels.advanceData(); + pixels.stepDithering(); + } + } +}; + + +FASTLED_NAMESPACE_END diff --git a/src/platforms/esp/32/fastled_esp32.h b/src/platforms/esp/32/fastled_esp32.h new file mode 100644 index 0000000000..edf27e7d99 --- /dev/null +++ b/src/platforms/esp/32/fastled_esp32.h @@ -0,0 +1,11 @@ +#pragma once + +#include "fastpin_esp32.h" + +#ifdef FASTLED_ESP32_I2S +#include "clockless_i2s_esp32.h" +#else +#include "clockless_rmt_esp32.h" +#endif + +// #include "clockless_block_esp32.h" diff --git a/platforms/esp/32/fastpin_esp32.h b/src/platforms/esp/32/fastpin_esp32.h similarity index 61% rename from platforms/esp/32/fastpin_esp32.h rename to src/platforms/esp/32/fastpin_esp32.h index fd03d5c813..7876b281f9 100644 --- a/platforms/esp/32/fastpin_esp32.h +++ b/src/platforms/esp/32/fastpin_esp32.h @@ -3,7 +3,6 @@ FASTLED_NAMESPACE_BEGIN template class _ESPPIN { - public: typedef volatile uint32_t * port_ptr_t; typedef uint32_t port_t; @@ -11,7 +10,7 @@ template class _ESPPIN { inline static void setOutput() { pinMode(PIN, OUTPUT); } inline static void setInput() { pinMode(PIN, INPUT); } - inline static void hi() __attribute__ ((always_inline)) { + inline static void hi() __attribute__ ((always_inline)) { if (PIN < 32) GPIO.out_w1ts = MASK; else GPIO.out1_w1ts.val = MASK; } @@ -28,9 +27,9 @@ template class _ESPPIN { inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } - inline static void toggle() __attribute__ ((always_inline)) { - if(PIN < 32) { GPIO.out ^= MASK; } - else { GPIO.out1.val ^=MASK; } + inline static void toggle() __attribute__ ((always_inline)) { + if(PIN < 32) { GPIO.out ^= MASK; } + else { GPIO.out1.val ^=MASK; } } inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } @@ -52,7 +51,7 @@ template class _ESPPIN { else return &GPIO.out1.val; } - inline static port_ptr_t sport() __attribute__ ((always_inline)) { + inline static port_ptr_t sport() __attribute__ ((always_inline)) { if (PIN < 32) return &GPIO.out_w1ts; else return &GPIO.out1_w1ts.val; } @@ -70,46 +69,45 @@ template class _ESPPIN { } }; -#define _DEFPIN_ESP32(PIN) template<> class FastPin : public _ESPPIN {}; -#define _DEFPIN_32_33_ESP32(PIN) template<> class FastPin : public _ESPPIN {}; +#define _FL_DEFPIN(PIN) template<> class FastPin : public _ESPPIN {}; -_DEFPIN_ESP32(0); -_DEFPIN_ESP32(1); // WARNING: Using TX causes flashiness when uploading -_DEFPIN_ESP32(2); -_DEFPIN_ESP32(3); // WARNING: Using RX causes flashiness when uploading -_DEFPIN_ESP32(4); -_DEFPIN_ESP32(5); +_FL_DEFPIN(0); +_FL_DEFPIN(1); // WARNING: Using TX causes flashiness when uploading +_FL_DEFPIN(2); +_FL_DEFPIN(3); // WARNING: Using RX causes flashiness when uploading +_FL_DEFPIN(4); +_FL_DEFPIN(5); // -- These pins are not safe to use: -// _DEFPIN_ESP32(6,6); _DEFPIN_ESP32(7,7); _DEFPIN_ESP32(8,8); -// _DEFPIN_ESP32(9,9); _DEFPIN_ESP32(10,10); _DEFPIN_ESP32(11,11); +// _FL_DEFPIN(6,6); _FL_DEFPIN(7,7); _FL_DEFPIN(8,8); +// _FL_DEFPIN(9,9); _FL_DEFPIN(10,10); _FL_DEFPIN(11,11); -_DEFPIN_ESP32(12); -_DEFPIN_ESP32(13); -_DEFPIN_ESP32(14); -_DEFPIN_ESP32(15); -_DEFPIN_ESP32(16); -_DEFPIN_ESP32(17); -_DEFPIN_ESP32(18); -_DEFPIN_ESP32(19); +_FL_DEFPIN(12); +_FL_DEFPIN(13); +_FL_DEFPIN(14); +_FL_DEFPIN(15); +_FL_DEFPIN(16); +_FL_DEFPIN(17); +_FL_DEFPIN(18); +_FL_DEFPIN(19); -// No pin 20 : _DEFPIN_ESP32(20,20); +// No pin 20 : _FL_DEFPIN(20,20); -_DEFPIN_ESP32(21); // Works, but note that GPIO21 is I2C SDA -_DEFPIN_ESP32(22); // Works, but note that GPIO22 is I2C SCL -_DEFPIN_ESP32(23); +_FL_DEFPIN(21); // Works, but note that GPIO21 is I2C SDA +_FL_DEFPIN(22); // Works, but note that GPIO22 is I2C SCL +_FL_DEFPIN(23); -// No pin 24 : _DEFPIN_ESP32(24,24); +// No pin 24 : _FL_DEFPIN(24,24); -_DEFPIN_ESP32(25); -_DEFPIN_ESP32(26); -_DEFPIN_ESP32(27); +_FL_DEFPIN(25); +_FL_DEFPIN(26); +_FL_DEFPIN(27); -// No pin 28-31: _DEFPIN_ESP32(28,28); _DEFPIN_ESP32(29,29); _DEFPIN_ESP32(30,30); _DEFPIN_ESP32(31,31); +// No pin 28-31: _FL_DEFPIN(28,28); _FL_DEFPIN(29,29); _FL_DEFPIN(30,30); _FL_DEFPIN(31,31); // Need special handling for pins > 31 -_DEFPIN_32_33_ESP32(32); -_DEFPIN_32_33_ESP32(33); +_FL_DEFPIN(32); +_FL_DEFPIN(33); #define HAS_HARDWARE_PIN_SUPPORT diff --git a/platforms/esp/32/led_sysdefs_esp32.h b/src/platforms/esp/32/led_sysdefs_esp32.h similarity index 96% rename from platforms/esp/32/led_sysdefs_esp32.h rename to src/platforms/esp/32/led_sysdefs_esp32.h index 68e782398e..5cd374e2f4 100644 --- a/platforms/esp/32/led_sysdefs_esp32.h +++ b/src/platforms/esp/32/led_sysdefs_esp32.h @@ -12,7 +12,7 @@ typedef volatile uint32_t RoReg; typedef volatile uint32_t RwReg; typedef unsigned long prog_uint32_t; -typedef bool boolean; + // Default to NOT using PROGMEM here #ifndef FASTLED_USE_PROGMEM diff --git a/platforms/esp/8266/clockless_block_esp8266.h b/src/platforms/esp/8266/clockless_block_esp8266.h similarity index 76% rename from platforms/esp/8266/clockless_block_esp8266.h rename to src/platforms/esp/8266/clockless_block_esp8266.h index 40c91612f7..3eccbe1e81 100644 --- a/platforms/esp/8266/clockless_block_esp8266.h +++ b/src/platforms/esp/8266/clockless_block_esp8266.h @@ -23,6 +23,7 @@ class InlineBlockClocklessController : public CPixelLEDController::port_t data_t; CMinWait mWait; + public: virtual int size() { return CLEDController::size() * LANES; } @@ -31,13 +32,13 @@ class InlineBlockClocklessController : public CPixelLEDController static void initPin() { - _ESPPIN::setOutput(); - } + template static void initPin() { + _ESPPIN::setOutput(); + } - virtual void init() { + virtual void init() { void (* funcs[])() ={initPin<12>, initPin<13>, initPin<14>, initPin<15>, initPin<4>, initPin<5>}; for (uint8_t i = 0; i < USED_LANES; ++i) { funcs[i](); } - } + } - virtual uint16_t getMaxRefreshRate() const { return 400; } + virtual uint16_t getMaxRefreshRate() const { return 400; } typedef union { uint8_t bytes[8]; @@ -69,14 +70,14 @@ class InlineBlockClocklessController : public CPixelLEDController __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register Lines & b, PixelController &pixels) { // , register uint32_t & b2) { - Lines b2 = b; + template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register Lines & b, PixelController &pixels) { // , register uint32_t & b2) { + Lines b2 = b; transpose8x1_noinline(b.bytes,b2.bytes); register uint8_t d = pixels.template getd(pixels); register uint8_t scale = pixels.template getscale(pixels); - for(register uint32_t i = 0; i < USED_LANES; i++) { + for(register uint32_t i = 0; i < USED_LANES; ++i) { while((__clock_cycles() - last_mark) < (T1+T2+T3)); last_mark = __clock_cycles(); *FastPin::sport() = PIN_MASK; @@ -91,7 +92,7 @@ class InlineBlockClocklessController : public CPixelLEDController(pixels,i,d,scale); } - for(register uint32_t i = USED_LANES; i < 8; i++) { + for(register uint32_t i = USED_LANES; i < 8; ++i) { while((__clock_cycles() - last_mark) < (T1+T2+T3)); last_mark = __clock_cycles(); *FastPin::sport() = PIN_MASK; @@ -105,14 +106,14 @@ class InlineBlockClocklessController : public CPixelLEDController &allpixels) { + static uint32_t ICACHE_RAM_ATTR showRGBInternal(PixelController &allpixels) { // Setup the pixel controller and load/scale the first byte Lines b0; - for(int i = 0; i < USED_LANES; i++) { + for(int i = 0; i < USED_LANES; ++i) { b0.bytes[i] = allpixels.loadAndScale0(i); } allpixels.preStepFirstByteDithering(); @@ -132,26 +133,26 @@ class InlineBlockClocklessController : public CPixelLEDController(last_mark, b0, allpixels); - #if (FASTLED_ALLOW_INTERRUPTS == 1) + #if (FASTLED_ALLOW_INTERRUPTS == 1) os_intr_unlock(); - #endif + #endif allpixels.stepDithering(); - #if (FASTLED_ALLOW_INTERRUPTS == 1) - os_intr_lock(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + os_intr_lock(); // if interrupts took longer than 45µs, punt on the current frame if((int32_t)(__clock_cycles()-last_mark) > 0) { if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { os_intr_unlock(); return 0; } } - #endif + #endif }; - os_intr_unlock(); + os_intr_unlock(); #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES - _frame_cnt++; + ++_frame_cnt; #endif - return __clock_cycles() - _start; + return __clock_cycles() - _start; } }; diff --git a/src/platforms/esp/8266/clockless_esp8266.h b/src/platforms/esp/8266/clockless_esp8266.h new file mode 100644 index 0000000000..131f24671b --- /dev/null +++ b/src/platforms/esp/8266/clockless_esp8266.h @@ -0,0 +1,117 @@ +#pragma once + +FASTLED_NAMESPACE_BEGIN + +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES +extern uint32_t _frame_cnt; +extern uint32_t _retry_cnt; +#endif + +// Info on reading cycle counter from https://github.com/kbeckmann/nodemcu-firmware/blob/ws2812-dual/app/modules/ws2812.c +__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() { + uint32_t cyc; + __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc)); + return cyc; +} + +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; +public: + virtual void init() { + FastPin::setOutput(); + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + + virtual void showPixels(PixelController & pixels) { + // mWait.wait(); + int cnt = FASTLED_INTERRUPT_RETRY_COUNT; + while((showRGBInternal(pixels)==0) && cnt--) { + #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_retry_cnt; + #endif + os_intr_unlock(); + delayMicroseconds(WAIT_TIME); + os_intr_lock(); + } + // mWait.mark(); + } + +#define _ESP_ADJ (0) +#define _ESP_ADJ2 (0) + + template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register uint32_t b) { + b <<= 24; b = ~b; + for(register uint32_t i = BITS; i > 0; --i) { + while((__clock_cycles() - last_mark) < (T1+T2+T3)); + last_mark = __clock_cycles(); + FastPin::hi(); + + while((__clock_cycles() - last_mark) < T1); + if(b & 0x80000000L) { FastPin::lo(); } + b <<= 1; + + while((__clock_cycles() - last_mark) < (T1+T2)); + FastPin::lo(); + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t ICACHE_RAM_ATTR showRGBInternal(PixelController pixels) { + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint32_t b = pixels.loadAndScale0(); + pixels.preStepFirstByteDithering(); + os_intr_lock(); + uint32_t start = __clock_cycles(); + uint32_t last_mark = start; + while(pixels.has(1)) { + // Write first byte, read next byte + writeBits<8+XTRA0>(last_mark, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(last_mark, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(last_mark, b); + b = pixels.advanceAndLoadAndScale0(); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + os_intr_unlock(); + #endif + + pixels.stepDithering(); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + os_intr_lock(); + // if interrupts took longer than 45µs, punt on the current frame + if((int32_t)(__clock_cycles()-last_mark) > 0) { + if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { sei(); return 0; } + } + #endif + }; + + os_intr_unlock(); + #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_frame_cnt; + #endif + return __clock_cycles() - start; + } +}; + +FASTLED_NAMESPACE_END diff --git a/src/platforms/esp/8266/fastled_esp8266.h b/src/platforms/esp/8266/fastled_esp8266.h new file mode 100644 index 0000000000..8c4048db68 --- /dev/null +++ b/src/platforms/esp/8266/fastled_esp8266.h @@ -0,0 +1,5 @@ +#pragma once + +#include "fastpin_esp8266.h" +#include "clockless_esp8266.h" +#include "clockless_block_esp8266.h" diff --git a/src/platforms/esp/8266/fastpin_esp8266.h b/src/platforms/esp/8266/fastpin_esp8266.h new file mode 100644 index 0000000000..d64119f95a --- /dev/null +++ b/src/platforms/esp/8266/fastpin_esp8266.h @@ -0,0 +1,100 @@ +#pragma once + +FASTLED_NAMESPACE_BEGIN + +struct FASTLED_ESP_IO { + volatile uint32_t _GPO; + volatile uint32_t _GPOS; + volatile uint32_t _GPOC; +}; + +#define _GPB (*(FASTLED_ESP_IO*)(0x60000000+(0x300))) + + +template class _ESPPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); } + inline static void setInput() { pinMode(PIN, INPUT); } + + inline static void hi() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPOS = MASK; } else { GP16O = 1; } } + inline static void lo() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPOC = MASK; } else { GP16O = 0; } } + inline static void set(register port_t val) __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPO = val; } else { GP16O = val; }} + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPO ^= MASK; } else { GP16O ^= MASK; } } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { if (PIN<16) { return GPO | MASK; } else { return GP16O | MASK; } } + inline static port_t loval() __attribute__ ((always_inline)) { if (PIN<16) { return GPO & ~MASK; } else { return GP16O & ~MASK; } } + inline static port_ptr_t port() __attribute__ ((always_inline)) { if(PIN<16) { return &_GPB._GPO; } else { return &GP16O; } } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPB._GPOS; } // there is no GP160 support for this + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPB._GPOC; } + inline static port_t mask() __attribute__ ((always_inline)) { return MASK; } + + inline static bool isset() __attribute__ ((always_inline)) { return (PIN < 16) ? (GPO & MASK) : (GP16O & MASK); } +}; + +#define _FL_DEFPIN(PIN, REAL_PIN) template<> class FastPin : public _ESPPIN {}; + + +#ifdef FASTLED_ESP8266_RAW_PIN_ORDER +#define MAX_PIN 16 +_FL_DEFPIN(0,0); _FL_DEFPIN(1,1); _FL_DEFPIN(2,2); _FL_DEFPIN(3,3); +_FL_DEFPIN(4,4); _FL_DEFPIN(5,5); + +// These pins should be disabled, as they always cause WDT resets +// _FL_DEFPIN(6,6); _FL_DEFPIN(7,7); +// _FL_DEFPIN(8,8); _FL_DEFPIN(9,9); _FL_DEFPIN(10,10); _FL_DEFPIN(11,11); + +_FL_DEFPIN(12,12); _FL_DEFPIN(13,13); _FL_DEFPIN(14,14); _FL_DEFPIN(15,15); +_FL_DEFPIN(16,16); + +#define PORTA_FIRST_PIN 12 +#elif defined(FASTLED_ESP8266_D1_PIN_ORDER) +#define MAX_PIN 15 +_FL_DEFPIN(0,3); +_FL_DEFPIN(1,1); +_FL_DEFPIN(2,16); +_FL_DEFPIN(3,5); +_FL_DEFPIN(4,4); +_FL_DEFPIN(5,14); +_FL_DEFPIN(6,12); +_FL_DEFPIN(7,13); +_FL_DEFPIN(8,0); +_FL_DEFPIN(9,2); +_FL_DEFPIN(10,15); +_FL_DEFPIN(11,13); +_FL_DEFPIN(12,12); +_FL_DEFPIN(13,14); +_FL_DEFPIN(14,4); +_FL_DEFPIN(15,5); + +#define PORTA_FIRST_PIN 12 + +#else // if defined(FASTLED_ESP8266_NODEMCU_PIN_ORDER) +#define MAX_PIN 10 + +// This seems to be the standard Dxx pin mapping on most of the esp boards that i've found +_FL_DEFPIN(0,16); _FL_DEFPIN(1,5); _FL_DEFPIN(2,4); _FL_DEFPIN(3,0); +_FL_DEFPIN(4,2); _FL_DEFPIN(5,14); _FL_DEFPIN(6,12); _FL_DEFPIN(7,13); +_FL_DEFPIN(8,15); _FL_DEFPIN(9,3); _FL_DEFPIN(10,1); + +#define PORTA_FIRST_PIN 6 + +// The rest of the pins - these are generally not available +// _FL_DEFPIN(11,6); +// _FL_DEFPIN(12,7); _FL_DEFPIN(13,8); _FL_DEFPIN(14,9); _FL_DEFPIN(15,10); +// _FL_DEFPIN(16,11); + +#endif + +#define HAS_HARDWARE_PIN_SUPPORT + +FASTLED_NAMESPACE_END diff --git a/src/platforms/esp/8266/led_sysdefs_esp8266.h b/src/platforms/esp/8266/led_sysdefs_esp8266.h new file mode 100644 index 0000000000..26dffdcf52 --- /dev/null +++ b/src/platforms/esp/8266/led_sysdefs_esp8266.h @@ -0,0 +1,39 @@ +#pragma once + +#ifndef ESP8266 +#define ESP8266 +#endif + +#define FASTLED_ESP8266 + +// Use system millis timer +#define FASTLED_HAS_MILLIS + +typedef volatile uint32_t RoReg; +typedef volatile uint32_t RwReg; +typedef uint32_t prog_uint32_t; + + +// Default to NOT using PROGMEM here +#ifndef FASTLED_USE_PROGMEM +# define FASTLED_USE_PROGMEM 0 +#endif + +#ifndef FASTLED_ALLOW_INTERRUPTS +# define FASTLED_ALLOW_INTERRUPTS 1 +# define INTERRUPT_THRESHOLD 0 +#endif + +#define NEED_CXX_BITS + +// These can be overridden +#if !defined(FASTLED_ESP8266_RAW_PIN_ORDER) && !defined(FASTLED_ESP8266_NODEMCU_PIN_ORDER) && !defined(FASTLED_ESP8266_D1_PIN_ORDER) +# ifdef ARDUINO_ESP8266_NODEMCU +# define FASTLED_ESP8266_NODEMCU_PIN_ORDER +# else +# define FASTLED_ESP8266_RAW_PIN_ORDER +# endif +#endif + +// #define cli() os_intr_lock(); +// #define sei() os_intr_lock(); diff --git a/power_mgt.cpp b/src/power_mgt.cpp similarity index 93% rename from power_mgt.cpp rename to src/power_mgt.cpp index 8e46d93f8d..e15fa709b5 100644 --- a/power_mgt.cpp +++ b/src/power_mgt.cpp @@ -60,7 +60,7 @@ uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds ) red32 += *p++; green32 += *p++; blue32 += *p++; - count--; + --count; } red32 *= gRed_mW; @@ -88,7 +88,7 @@ uint8_t calculate_max_brightness_for_power_mW(const CRGB* ledbuffer, uint16_t nu uint8_t recommended_brightness = target_brightness; if(requested_power_mW > max_power_mW) { - recommended_brightness = (uint32_t)((uint8_t)(target_brightness) * (uint32_t)(max_power_mW)) / ((uint32_t)(requested_power_mW)); + recommended_brightness = (uint32_t)((uint8_t)(target_brightness) * (uint32_t)(max_power_mW)) / ((uint32_t)(requested_power_mW)); } return recommended_brightness; @@ -163,23 +163,23 @@ void set_max_power_indicator_LED( uint8_t pinNumber) void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps) { - FastLED.setMaxPowerInVoltsAndMilliamps(volts, milliamps); + FastLED.setMaxPowerInVoltsAndMilliamps(volts, milliamps); } void set_max_power_in_milliwatts( uint32_t powerInmW) { - FastLED.setMaxPowerInMilliWatts(powerInmW); + FastLED.setMaxPowerInMilliWatts(powerInmW); } void show_at_max_brightness_for_power() { - // power management usage is now in FastLED.show, no need for this function - FastLED.show(); + // power management usage is now in FastLED.show, no need for this function + FastLED.show(); } void delay_at_max_brightness_for_power( uint16_t ms) { - FastLED.delay(ms); + FastLED.delay(ms); } FASTLED_NAMESPACE_END diff --git a/power_mgt.h b/src/power_mgt.h similarity index 97% rename from power_mgt.h rename to src/power_mgt.h index f156f7a251..6871881859 100644 --- a/power_mgt.h +++ b/src/power_mgt.h @@ -21,11 +21,11 @@ FASTLED_NAMESPACE_BEGIN /// @deprecated - use FastLED.setMaxPowerInVoltsAndMilliamps() void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps); /// Set the maximum power used in watts +/// @deprecated - use FastLED.setMaxPowerInMilliWatts void set_max_power_in_milliwatts( uint32_t powerInmW); -/// Select a ping with an led that will be flashed to indicate that power management +/// Select a pin with an led that will be flashed to indicate that power management /// is pulling down the brightness -/// @deprecated - use FastLED.setMaxPowerInMilliWatts void set_max_power_indicator_LED( uint8_t pinNumber); // zero = no indicator LED diff --git a/wiring.cpp b/src/wiring.cpp similarity index 98% rename from wiring.cpp rename to src/wiring.cpp index b2af51cd08..744373a172 100644 --- a/wiring.cpp +++ b/src/wiring.cpp @@ -71,10 +71,10 @@ unsigned long micros() { #ifdef TIFR0 if ((TIFR0 & _BV(TOV0)) && (t < 255)) - m++; + ++m; #else if ((TIFR & _BV(TOV0)) && (t < 255)) - m++; + ++m; #endif SREG = oldSREG; @@ -88,7 +88,7 @@ void delay(unsigned long ms) while (ms > 0) { if (((uint16_t)micros() - start) >= 1000) { - ms--; + --ms; start += 1000; } }