diff --git a/WLED_CLS.code-workspace b/WLED_CLS.code-workspace new file mode 100644 index 00000000..c8d64714 --- /dev/null +++ b/WLED_CLS.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "stm32-for-vscode.openOCDPath": false, + "stm32-for-vscode.armToolchainPath": false + } +} \ No newline at end of file diff --git a/build/.cmake/api/v1/query/client-vscode/query.json b/build/.cmake/api/v1/query/client-vscode/query.json new file mode 100644 index 00000000..82bb9642 --- /dev/null +++ b/build/.cmake/api/v1/query/client-vscode/query.json @@ -0,0 +1 @@ +{"requests":[{"kind":"cache","version":2},{"kind":"codemodel","version":2},{"kind":"toolchains","version":1},{"kind":"cmakeFiles","version":1}]} \ No newline at end of file diff --git a/build/CMakeCache.txt b/build/CMakeCache.txt new file mode 100644 index 00000000..948b2be2 --- /dev/null +++ b/build/CMakeCache.txt @@ -0,0 +1,68 @@ +# This is the CMakeCache file. +# For build in directory: /Users/electron_rare/Documents/Lelectron_rare/Github Code/WLED_CLS/build +# It was generated by CMake: /Users/electron_rare/miniconda3/lib/python3.11/site-packages/cmake/data/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//No help, variable specified on the command line. +CMAKE_BUILD_TYPE:STRING=Debug + +//No help, variable specified on the command line. +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++ + +//No help, variable specified on the command line. +CMAKE_C_COMPILER:FILEPATH=/usr/bin/clang + +//No help, variable specified on the command line. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE + +//Value Computed by CMake. +CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/Users/electron_rare/Documents/Lelectron_rare/Github Code/WLED_CLS/build/CMakeFiles/pkgRedirects + + +######################## +# INTERNAL cache entries +######################## + +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/Users/electron_rare/Documents/Lelectron_rare/Github Code/WLED_CLS/build +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=28 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/Users/electron_rare/miniconda3/lib/python3.11/site-packages/cmake/data/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/Users/electron_rare/miniconda3/lib/python3.11/site-packages/cmake/data/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/Users/electron_rare/miniconda3/lib/python3.11/site-packages/cmake/data/bin/ctest +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Ninja +//Generator instance identifier. +CMAKE_GENERATOR_INSTANCE:INTERNAL= +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/Users/electron_rare/Documents/Lelectron_rare/Github Code/WLED_CLS/lib/FastLED +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/Users/electron_rare/miniconda3/lib/python3.11/site-packages/cmake/data/share/cmake-3.28 + diff --git a/build/CMakeFiles/cmake.check_cache b/build/CMakeFiles/cmake.check_cache new file mode 100644 index 00000000..3dccd731 --- /dev/null +++ b/build/CMakeFiles/cmake.check_cache @@ -0,0 +1 @@ +# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/lib/FastLED/.github/workflows/build.yml b/lib/FastLED/.github/workflows/build.yml new file mode 100644 index 00000000..bbb7faa4 --- /dev/null +++ b/lib/FastLED/.github/workflows/build.yml @@ -0,0 +1,29 @@ +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +name: build +jobs: + + build: + runs-on: ubuntu-latest + steps: + - name: checkout code + uses: actions/checkout@v3 + + - name: install python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: install platformio + run: | + pip install platformio==6.1.4 + + - name: build FastLED examples + run: ./ci/ci-compile diff --git a/lib/FastLED/.github/workflows/docs.yml b/lib/FastLED/.github/workflows/docs.yml new file mode 100644 index 00000000..b0966979 --- /dev/null +++ b/lib/FastLED/.github/workflows/docs.yml @@ -0,0 +1,58 @@ +name: docs + +on: + workflow_dispatch: + release: + types: released + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Fetch Repository Reference Info + id: repo-info + run: | + git fetch --prune --unshallow --tags + RELEASE_TAG=${{ github.event.release.tag_name }} + LATEST_TAG=$(git tag | grep -E '^[0-9]' | sort -V | tail -1) + GIT_SHA_SHORT=$(sed 's/\(.\{7\}\).*/\1/' <<< "$GITHUB_SHA") + PROJECT_NUMBER=${RELEASE_TAG:-${LATEST_TAG:-$GIT_SHA_SHORT}} + COMMIT_MSG=$PROJECT_NUMBER + if [ "$PROJECT_NUMBER" != "$GIT_SHA_SHORT" ]; then COMMIT_MSG+=" ($GITHUB_SHA)"; fi + echo "The project number is \"$PROJECT_NUMBER\" and the commit message is \"$COMMIT_MSG\"" + echo "::set-output name=project-number::$PROJECT_NUMBER" + echo "::set-output name=commit-message::$COMMIT_MSG" + + - name: Install Doxygen + env: + DOXYGEN_VERSION: 1.9.3 + run: | + wget -q https://www.doxygen.nl/files/doxygen-${{ env.DOXYGEN_VERSION }}.linux.bin.tar.gz + tar -xf doxygen-${{ env.DOXYGEN_VERSION }}.linux.bin.tar.gz + cd doxygen-${{ env.DOXYGEN_VERSION }} && sudo make install + sudo apt-get install libclang1-9 libclang-cpp9 + + - name: Install Themes + env: + DOXYGEN_AWESOME_VERSION: 2.0.3 + working-directory: ./docs + run: | + git clone --depth 1 -b v${{ env.DOXYGEN_AWESOME_VERSION }} https://github.com/jothepro/doxygen-awesome-css + + - name: Generate Docs + working-directory: ./docs + run: | + sed -i -E 's/(PROJECT_NUMBER\s*=\s*).*/\1 ${{ steps.repo-info.outputs.project-number }}/g' Doxyfile + doxygen Doxyfile + + - name: Deploy Docs + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: gh-pages + publish_dir: ./docs/html + destination_dir: docs + user_name: github-actions[bot] + user_email: github-actions[bot]@users.noreply.github.com + full_commit_message: Update docs for ${{ steps.repo-info.outputs.commit-message }} diff --git a/lib/FastLED/.piopm b/lib/FastLED/.piopm new file mode 100644 index 00000000..c2a78fdf --- /dev/null +++ b/lib/FastLED/.piopm @@ -0,0 +1 @@ +{"type": "library", "name": "FastLED", "version": "3.6.0", "spec": {"owner": "fastled", "id": 126, "name": "FastLED", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/lib/FastLED/CMakeLists.txt b/lib/FastLED/CMakeLists.txt new file mode 100644 index 00000000..1fc50560 --- /dev/null +++ b/lib/FastLED/CMakeLists.txt @@ -0,0 +1,25 @@ +# FastLED +# https://github.com/FastLED/FastLED +# MIT License + +cmake_minimum_required(VERSION 3.5) + +set(FastLED_SRCS + src/bitswap.cpp + src/colorpalettes.cpp + src/colorutils.cpp + src/FastLED.cpp + src/hsv2rgb.cpp + src/lib8tion.cpp + src/noise.cpp + src/platforms.cpp + src/power_mgt.cpp + src/wiring.cpp + src/platforms/esp/32/clockless_rmt_esp32.cpp + ) + +idf_component_register(SRCS ${FastLED_SRCS} + INCLUDE_DIRS "src" + REQUIRES arduino) + +project(FastLED) diff --git a/lib/FastLED/LICENSE b/lib/FastLED/LICENSE new file mode 100644 index 00000000..ebe47633 --- /dev/null +++ b/lib/FastLED/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 FastLED + +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. diff --git a/lib/FastLED/PORTING.md b/lib/FastLED/PORTING.md new file mode 100644 index 00000000..ef361016 --- /dev/null +++ b/lib/FastLED/PORTING.md @@ -0,0 +1,56 @@ +New platform porting guide +========================== + +# 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: + * Define platform flags (like FASTLED_ARM/FASTLED_TEENSY) + * Define configuration parameters re: interrupts, or clock doubling + * Include extar system header files if needed +* Create main platform include, fastled_arm_kl26.h + * Include the various other header files as needed +* Modify led_sysdefs.h to conditionally include platform sysdefs header file +* Modify platforms.h to conditionally include platform fastled header + +## Porting fastpin.h + +The heart of the FastLED library is the fast pin access. 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. + +There's two low level FastPin classes. There's the base FastPIN template class, and then there is FastPinBB which is for bit-banded access on those MCUs that support bitbanding. Note that the bitband class is optional and primarily useful in the implementation of other functionality internal to the platform. This file is also where you would do the pin to port/bit mapping defines. + +Explaining how the macros work and should be used is currently beyond the scope of this document. + +## 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 + +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/lib/FastLED/README.md b/lib/FastLED/README.md new file mode 100644 index 00000000..664b3c23 --- /dev/null +++ b/lib/FastLED/README.md @@ -0,0 +1,91 @@ +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/FastLED/public) +[![arduino-library-badge](https://www.ardu-badge.com/badge/FastLED.svg)](https://www.ardu-badge.com/FastLED) +![build status](https://github.com/FastLED/FastLED/workflows/build/badge.svg) + +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.6 +=========== + +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 8-bit 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. + +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! + +We have multiple goals with this library: + +* Quick start for new developers - hook up your LEDs and go, no need to think about specifics of the LED chipsets being used +* Zero pain switching LED chipsets - you get some new LEDs that the library supports, just change the definition of LEDs you're using, et. voila! Your code is running with the new LEDs. +* High performance - with features like zero cost global brightness scaling, high performance 8-bit math for RGB manipulation, and some of the fastest bit-bang'd SPI support around, FastLED wants to keep as many CPU cycles available for your LED patterns as possible + +## Getting Help + +If you need help with using the library, please consider going to the reddit community first, which is at http://fastled.io/r (or https://reddit.com/r/FastLED). There are thousands of great people in that group and many times you will get a quicker answer to your question there, as you will be likely to run into other people who have had the same issue. If you run into bugs with the library (compilation failures, the library doing the wrong thing), or if you'd like to request that we support a particular platform or LED chipset, then please open an issue at http://fastled.io/issues and we will try to figure out what is going wrong. + +## Simple Example + +How quickly can you get up and running with the library? Here's a simple blink program: +```cpp +#include +#define NUM_LEDS 60 +CRGB leds[NUM_LEDS]; +void setup() { FastLED.addLeds(leds, NUM_LEDS); } +void loop() { + leds[0] = CRGB::White; FastLED.show(); delay(30); + leds[0] = CRGB::Black; FastLED.show(); delay(30); +} +``` + +## Supported LED Chipsets + +Here's a list of all the LED chipsets are supported. More details on the LED chipsets are included [on our wiki page](https://github.com/FastLED/FastLED/wiki/Chipset-reference) + +* Adafruit's DotStars - aka APA102 +* Adafruit's Neopixel - aka WS2812B (also WS2811/WS2812/WS2813, also supported in lo-speed mode) - a 3 wire addressable LED chipset +* TM1809/4 - 3 wire chipset, cheaply available on aliexpress.com +* TM1803 - 3 wire chipset, sold by RadioShack +* UCS1903 - another 3 wire LED chipset, cheap +* GW6205 - another 3 wire LED chipset +* LPD8806 - SPI based chipset, very high speed +* WS2801 - SPI based chipset, cheap and widely available +* SM16716 - SPI based chipset +* APA102 - SPI based chipset +* P9813 - aka Cool Neon's Total Control Lighting +* DMX - send rgb data out over DMX using Arduino DMX libraries +* SmartMatrix panels - needs the SmartMatrix library (https://github.com/pixelmatix/SmartMatrix) +* LPD6803 - SPI based chpiset, chip CMODE pin must be set to 1 (inside oscillator mode) + +HL1606, and "595"-style shift registers are no longer supported by the library. The older Version 1 of the library ("FastSPI_LED") has support for these, but is missing many of the advanced features of current versions and is no longer being maintained. + +## Supported Platforms + +Right now the library is supported on a variety of Arduino compatible platforms. If it's ARM or AVR and uses the Arduino software (or a modified version of it to build) then it is likely supported. Note that we have a long list of upcoming platforms to support, so if you don't see what you're looking for here, ask, it may be on the roadmap (or may already be supported). N.B. at the moment we are only supporting the stock compilers that ship with the Arduino software. Support for upgraded compilers, as well as using AVR Studio and skipping the Arduino entirely, should be coming in a near future release. + +* 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, Teensy 3.5, Teensy 3.6, and Teensy 4.0 - Arduino compatible from pjrc.com with some extra goodies (note the Teensy LC, 3.2, 3.5, 3.6, 4.0 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 + +## What about that name? + +Wait, what happened to FastSPI_LED and FastSPI_LED2? The library was initially named FastSPI_LED because it was focused on very fast and efficient SPI access. However, since then, the library has expanded to support a number of LED chipsets that don't use SPI, as well as a number of math and utility functions for LED processing across the board. We decided that the name FastLED more accurately represents the totality of what the library provides, everything fast, for LEDs. + +## For more information + +Check out the official site http://fastled.io for links to documentation, issues, and news + + +*TODO* - get candy diff --git a/lib/FastLED/ci/ci-compile b/lib/FastLED/ci/ci-compile new file mode 100755 index 00000000..a7a21f2c --- /dev/null +++ b/lib/FastLED/ci/ci-compile @@ -0,0 +1,38 @@ +#!/bin/bash +# +# compile FastLED examples with platformio for various boards. This script +# is usually run by the CI, but can also be run locally. Only dependency +# is platformio. +# +# usage: +# [BOARDS=boards] [EXAMPLES=examples] ./ci-compile +# +# e.g. +# $ ./compile-ci +# - compile all board/examples combinations +# +# $ BOARDS="esp32 esp01" EXAMPLES=Blink ./compile-ci +# - compile only Blink example for the esp32 and esp8266 platforms +# +set -eou pipefail + +# List of examples that will be compiled by default +EXAMPLES=${EXAMPLES:-"Blink ColorPalette ColorTemperature Cylon DemoReel100 + Fire2012 FirstLight Multiple/MultipleStripsInOneArray + Multiple/ArrayOfLedArrays Noise NoisePlayground NoisePlusPalette Pacifica + Pride2015 RGBCalibrate RGBSetDemo TwinkleFox XYMatrix"} + +# list of boards to compile for by default +BOARDS=${BOARDS:-"uno esp32dev esp01 yun digix teensy30"} + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +BOARD_OPTS=$(for b in $BOARDS; do echo -n "--board $b "; done) + +cd "$DIR/.." + +export PLATFORMIO_EXTRA_SCRIPTS="pre:lib/ci/ci-flags.py" + +for d in $EXAMPLES ; do + echo "*** building example $d for $BOARDS ***" + pio ci $BOARD_OPTS --lib=ci --lib=src "examples/$d/"*ino +done diff --git a/lib/FastLED/ci/ci-flags.py b/lib/FastLED/ci/ci-flags.py new file mode 100644 index 00000000..044fdb1c --- /dev/null +++ b/lib/FastLED/ci/ci-flags.py @@ -0,0 +1,3 @@ +Import("env") + +env.Append(CXXFLAGS=["-Wno-register"]) diff --git a/lib/FastLED/code_of_conduct.md b/lib/FastLED/code_of_conduct.md new file mode 100644 index 00000000..d8e2efa4 --- /dev/null +++ b/lib/FastLED/code_of_conduct.md @@ -0,0 +1,134 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/lib/FastLED/component.mk b/lib/FastLED/component.mk new file mode 100644 index 00000000..874ca9b0 --- /dev/null +++ b/lib/FastLED/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS := ./src src/platforms/esp/32 +COMPONENT_SRCDIRS := ./src src/platforms/esp/32 diff --git a/lib/FastLED/examples/AnalogOutput/AnalogOutput.ino b/lib/FastLED/examples/AnalogOutput/AnalogOutput.ino new file mode 100644 index 00000000..92592b18 --- /dev/null +++ b/lib/FastLED/examples/AnalogOutput/AnalogOutput.ino @@ -0,0 +1,69 @@ +/// @file AnalogOutput.ino +/// @brief Demonstrates how to use FastLED color functions even without a "pixel-addressible" smart LED strip. +/// @example AnalogOutput.ino + +#include + +// Example showing how to use FastLED color functions +// even when you're NOT using a "pixel-addressible" smart LED strip. +// +// This example is designed to control an "analog" RGB LED strip +// (or a single RGB LED) being driven by Arduino PWM output pins. +// So this code never calls FastLED.addLEDs() or FastLED.show(). +// +// This example illustrates one way you can use just the portions +// of FastLED that you need. In this case, this code uses just the +// fast HSV color conversion code. +// +// In this example, the RGB values are output on three separate +// 'analog' PWM pins, one for red, one for green, and one for blue. + +#define REDPIN 5 +#define GREENPIN 6 +#define BLUEPIN 3 + +// showAnalogRGB: this is like FastLED.show(), but outputs on +// analog PWM output pins instead of sending data to an intelligent, +// pixel-addressable LED strip. +// +// This function takes the incoming RGB values and outputs the values +// on three analog PWM output pins to the r, g, and b values respectively. +void showAnalogRGB( const CRGB& rgb) +{ + analogWrite(REDPIN, rgb.r ); + analogWrite(GREENPIN, rgb.g ); + analogWrite(BLUEPIN, rgb.b ); +} + + + +// colorBars: flashes Red, then Green, then Blue, then Black. +// Helpful for diagnosing if you've mis-wired which is which. +void colorBars() +{ + showAnalogRGB( CRGB::Red ); delay(500); + showAnalogRGB( CRGB::Green ); delay(500); + showAnalogRGB( CRGB::Blue ); delay(500); + showAnalogRGB( CRGB::Black ); delay(500); +} + +void loop() +{ + static uint8_t hue; + hue = hue + 1; + // Use FastLED automatic HSV->RGB conversion + showAnalogRGB( CHSV( hue, 255, 255) ); + + delay(20); +} + + +void setup() { + pinMode(REDPIN, OUTPUT); + pinMode(GREENPIN, OUTPUT); + pinMode(BLUEPIN, OUTPUT); + + // Flash the "hello" color sequence: R, G, B, black. + colorBars(); +} + diff --git a/lib/FastLED/examples/Blink/Blink.ino b/lib/FastLED/examples/Blink/Blink.ino new file mode 100644 index 00000000..347ab4e5 --- /dev/null +++ b/lib/FastLED/examples/Blink/Blink.ino @@ -0,0 +1,73 @@ +/// @file Blink.ino +/// @brief Blink the first LED of an LED strip +/// @example Blink.ino + +#include + +// How many leds in your strip? +#define NUM_LEDS 1 + +// 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 + +// Define the array of leds +CRGB leds[NUM_LEDS]; + +void setup() { + // 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() { + // Turn the LED on, then pause + leds[0] = CRGB::Red; + FastLED.show(); + delay(500); + // Now turn the LED off, then pause + leds[0] = CRGB::Black; + FastLED.show(); + delay(500); +} diff --git a/lib/FastLED/examples/ColorPalette/ColorPalette.ino b/lib/FastLED/examples/ColorPalette/ColorPalette.ino new file mode 100644 index 00000000..4b692af5 --- /dev/null +++ b/lib/FastLED/examples/ColorPalette/ColorPalette.ino @@ -0,0 +1,192 @@ +/// @file ColorPalette.ino +/// @brief Demonstrates how to use @ref ColorPalettes +/// @example ColorPalette.ino + +#include + +#define LED_PIN 5 +#define NUM_LEDS 50 +#define BRIGHTNESS 64 +#define LED_TYPE WS2811 +#define COLOR_ORDER GRB +CRGB leds[NUM_LEDS]; + +#define UPDATES_PER_SECOND 100 + +// This example shows several ways to set up and use 'palettes' of colors +// with FastLED. +// +// These compact palettes provide an easy way to re-colorize your +// animation on the fly, quickly, easily, and with low overhead. +// +// USING palettes is MUCH simpler in practice than in theory, so first just +// run this sketch, and watch the pretty lights as you then read through +// the code. Although this sketch has eight (or more) different color schemes, +// the entire sketch compiles down to about 6.5K on AVR. +// +// FastLED provides a few pre-configured color palettes, and makes it +// extremely easy to make up your own color schemes with palettes. +// +// Some notes on the more abstract 'theory and practice' of +// FastLED compact palettes are at the bottom of this file. + + + +CRGBPalette16 currentPalette; +TBlendType currentBlending; + +extern CRGBPalette16 myRedWhiteBluePalette; +extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM; + + +void setup() { + delay( 3000 ); // power-up safety delay + FastLED.addLeds(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); + FastLED.setBrightness( BRIGHTNESS ); + + currentPalette = RainbowColors_p; + currentBlending = LINEARBLEND; +} + + +void loop() +{ + ChangePalettePeriodically(); + + static uint8_t startIndex = 0; + startIndex = startIndex + 1; /* motion speed */ + + FillLEDsFromPaletteColors( startIndex); + + FastLED.show(); + FastLED.delay(1000 / UPDATES_PER_SECOND); +} + +void FillLEDsFromPaletteColors( uint8_t colorIndex) +{ + uint8_t brightness = 255; + + for( int i = 0; i < NUM_LEDS; ++i) { + leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending); + colorIndex += 3; + } +} + + +// There are several different palettes of colors demonstrated here. +// +// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p, +// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p. +// +// Additionally, you can manually define your own color palettes, or you can write +// code that creates color palettes on the fly. All are shown here. + +void ChangePalettePeriodically() +{ + uint8_t secondHand = (millis() / 1000) % 60; + static uint8_t lastSecond = 99; + + if( lastSecond != secondHand) { + lastSecond = secondHand; + if( secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; } + if( secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; } + if( secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; } + if( secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; } + if( secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; } + if( secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; } + if( secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; } + if( secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; } + if( secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; } + if( secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; } + if( secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; } + } +} + +// This function fills the palette with totally random colors. +void SetupTotallyRandomPalette() +{ + for( int i = 0; i < 16; ++i) { + currentPalette[i] = CHSV( random8(), 255, random8()); + } +} + +// This function sets up a palette of black and white stripes, +// using code. Since the palette is effectively an array of +// sixteen CRGB colors, the various fill_* functions can be used +// to set them up. +void SetupBlackAndWhiteStripedPalette() +{ + // 'black out' all 16 palette entries... + fill_solid( currentPalette, 16, CRGB::Black); + // and set every fourth one to white. + currentPalette[0] = CRGB::White; + currentPalette[4] = CRGB::White; + currentPalette[8] = CRGB::White; + currentPalette[12] = CRGB::White; + +} + +// This function sets up a palette of purple and green stripes. +void SetupPurpleAndGreenPalette() +{ + CRGB purple = CHSV( HUE_PURPLE, 255, 255); + CRGB green = CHSV( HUE_GREEN, 255, 255); + CRGB black = CRGB::Black; + + currentPalette = CRGBPalette16( + green, green, black, black, + purple, purple, black, black, + green, green, black, black, + purple, purple, black, black ); +} + + +// This example shows how to set up a static color palette +// which is stored in PROGMEM (flash), which is almost always more +// plentiful than RAM. A static PROGMEM palette like this +// takes up 64 bytes of flash. +const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM = +{ + CRGB::Red, + CRGB::Gray, // 'white' is too bright compared to red and blue + CRGB::Blue, + CRGB::Black, + + CRGB::Red, + CRGB::Gray, + CRGB::Blue, + CRGB::Black, + + CRGB::Red, + CRGB::Red, + CRGB::Gray, + CRGB::Gray, + CRGB::Blue, + CRGB::Blue, + CRGB::Black, + CRGB::Black +}; + + + +// Additional notes on FastLED compact palettes: +// +// Normally, in computer graphics, the palette (or "color lookup table") +// has 256 entries, each containing a specific 24-bit RGB color. You can then +// index into the color palette using a simple 8-bit (one byte) value. +// A 256-entry color palette takes up 768 bytes of RAM, which on Arduino +// is quite possibly "too many" bytes. +// +// FastLED does offer traditional 256-element palettes, for setups that +// can afford the 768-byte cost in RAM. +// +// However, FastLED also offers a compact alternative. FastLED offers +// palettes that store 16 distinct entries, but can be accessed AS IF +// they actually have 256 entries; this is accomplished by interpolating +// between the 16 explicit entries to create fifteen intermediate palette +// entries between each pair. +// +// So for example, if you set the first two explicit entries of a compact +// palette to Green (0,255,0) and Blue (0,0,255), and then retrieved +// the first sixteen entries from the virtual palette (of 256), you'd get +// Green, followed by a smooth gradient from green-to-blue, and then Blue. diff --git a/lib/FastLED/examples/ColorTemperature/ColorTemperature.ino b/lib/FastLED/examples/ColorTemperature/ColorTemperature.ino new file mode 100644 index 00000000..437929a3 --- /dev/null +++ b/lib/FastLED/examples/ColorTemperature/ColorTemperature.ino @@ -0,0 +1,89 @@ +/// @file ColorTemperature.ino +/// @brief Demonstrates how to use @ref ColorTemperature based color correction +/// @example ColorTemperature.ino + +#include + +#define LED_PIN 3 + +// Information about the LED strip itself +#define NUM_LEDS 60 +#define CHIPSET WS2811 +#define COLOR_ORDER GRB +CRGB leds[NUM_LEDS]; + +#define BRIGHTNESS 128 + + +// FastLED provides two color-management controls: +// (1) color correction settings for each LED strip, and +// (2) master control of the overall output 'color temperature' +// +// THIS EXAMPLE demonstrates the second, "color temperature" control. +// It shows a simple rainbow animation first with one temperature profile, +// and a few seconds later, with a different temperature profile. +// +// The first pixel of the strip will show the color temperature. +// +// HELPFUL HINTS for "seeing" the effect in this demo: +// * Don't look directly at the LED pixels. Shine the LEDs aganst +// a white wall, table, or piece of paper, and look at the reflected light. +// +// * If you watch it for a bit, and then walk away, and then come back +// to it, you'll probably be able to "see" whether it's currently using +// the 'redder' or the 'bluer' temperature profile, even not counting +// the lowest 'indicator' pixel. +// +// +// FastLED provides these pre-conigured incandescent color profiles: +// Candle, Tungsten40W, Tungsten100W, Halogen, CarbonArc, +// HighNoonSun, DirectSunlight, OvercastSky, ClearBlueSky, +// FastLED provides these pre-configured gaseous-light color profiles: +// WarmFluorescent, StandardFluorescent, CoolWhiteFluorescent, +// FullSpectrumFluorescent, GrowLightFluorescent, BlackLightFluorescent, +// MercuryVapor, SodiumVapor, MetalHalide, HighPressureSodium, +// FastLED also provides an "Uncorrected temperature" profile +// UncorrectedTemperature; + +#define TEMPERATURE_1 Tungsten100W +#define TEMPERATURE_2 OvercastSky + +// How many seconds to show each temperature before switching +#define DISPLAYTIME 20 +// How many seconds to show black between switches +#define BLACKTIME 3 + +void loop() +{ + // draw a generic, no-name rainbow + static uint8_t starthue = 0; + fill_rainbow( leds + 5, NUM_LEDS - 5, --starthue, 20); + + // Choose which 'color temperature' profile to enable. + uint8_t secs = (millis() / 1000) % (DISPLAYTIME * 2); + if( secs < DISPLAYTIME) { + FastLED.setTemperature( TEMPERATURE_1 ); // first temperature + leds[0] = TEMPERATURE_1; // show indicator pixel + } else { + FastLED.setTemperature( TEMPERATURE_2 ); // second temperature + leds[0] = TEMPERATURE_2; // show indicator pixel + } + + // Black out the LEDs for a few secnds between color changes + // to let the eyes and brains adjust + if( (secs % DISPLAYTIME) < BLACKTIME) { + memset8( leds, 0, NUM_LEDS * sizeof(CRGB)); + } + + FastLED.show(); + FastLED.delay(8); +} + +void setup() { + delay( 3000 ); // power-up safety delay + // It's important to set the color correction for your LED strip here, + // so that colors can be more accurately rendered through the 'temperature' profiles + FastLED.addLeds(leds, NUM_LEDS).setCorrection( TypicalSMD5050 ); + FastLED.setBrightness( BRIGHTNESS ); +} + diff --git a/lib/FastLED/examples/Cylon/Cylon.ino b/lib/FastLED/examples/Cylon/Cylon.ino new file mode 100644 index 00000000..3f437344 --- /dev/null +++ b/lib/FastLED/examples/Cylon/Cylon.ino @@ -0,0 +1,57 @@ +/// @file Cylon.ino +/// @brief An animation that moves a single LED back and forth (Larson Scanner effect) +/// @example Cylon.ino + +#include + +// How many leds in your strip? +#define NUM_LEDS 64 + +// For led chips like Neopixels, 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 +#define DATA_PIN 2 +#define CLOCK_PIN 13 + +// Define the array of leds +CRGB leds[NUM_LEDS]; + +void setup() { + Serial.begin(57600); + Serial.println("resetting"); + FastLED.addLeds(leds,NUM_LEDS); + FastLED.setBrightness(84); +} + +void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); } } + +void loop() { + static uint8_t hue = 0; + Serial.print("x"); + // First slide the led in one direction + for(int i = 0; i < NUM_LEDS; i++) { + // Set the i'th led to red + leds[i] = CHSV(hue++, 255, 255); + // Show the leds + FastLED.show(); + // now that we've shown the leds, reset the i'th led to black + // leds[i] = CRGB::Black; + fadeall(); + // Wait a little bit before we loop around and do it again + delay(10); + } + Serial.print("x"); + + // Now go in the other direction. + for(int i = (NUM_LEDS)-1; i >= 0; i--) { + // Set the i'th led to red + leds[i] = CHSV(hue++, 255, 255); + // Show the leds + FastLED.show(); + // now that we've shown the leds, reset the i'th led to black + // leds[i] = CRGB::Black; + fadeall(); + // Wait a little bit before we loop around and do it again + delay(10); + } +} diff --git a/lib/FastLED/examples/DemoReel100/DemoReel100.ino b/lib/FastLED/examples/DemoReel100/DemoReel100.ino new file mode 100644 index 00000000..6f01e347 --- /dev/null +++ b/lib/FastLED/examples/DemoReel100/DemoReel100.ino @@ -0,0 +1,127 @@ +/// @file DemoReel100.ino +/// @brief FastLED "100 lines of code" demo reel, showing off some effects +/// @example DemoReel100.ino + +#include + +FASTLED_USING_NAMESPACE + +// FastLED "100-lines-of-code" demo reel, showing just a few +// of the kinds of animation patterns you can quickly and easily +// compose using FastLED. +// +// This example also shows one easy way to define multiple +// animations patterns and have them automatically rotate. +// +// -Mark Kriegsman, December 2014 + + +#define DATA_PIN 3 +//#define CLK_PIN 4 +#define LED_TYPE WS2811 +#define COLOR_ORDER GRB +#define NUM_LEDS 64 +CRGB leds[NUM_LEDS]; + +#define BRIGHTNESS 96 +#define FRAMES_PER_SECOND 120 + +void setup() { + delay(3000); // 3 second delay for recovery + + // tell FastLED about the LED strip configuration + FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); + //FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); + + // set master brightness control + FastLED.setBrightness(BRIGHTNESS); +} + + +// List of patterns to cycle through. Each is defined as a separate function below. +typedef void (*SimplePatternList[])(); +SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm }; + +uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current +uint8_t gHue = 0; // rotating "base color" used by many of the patterns + +void loop() +{ + // Call the current pattern function once, updating the 'leds' array + gPatterns[gCurrentPatternNumber](); + + // send the 'leds' array out to the actual LED strip + FastLED.show(); + // insert a delay to keep the framerate modest + FastLED.delay(1000/FRAMES_PER_SECOND); + + // do some periodic updates + EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow + EVERY_N_SECONDS( 10 ) { nextPattern(); } // change patterns periodically +} + +#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) + +void nextPattern() +{ + // add one to the current pattern number, and wrap around at the end + gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns); +} + +void rainbow() +{ + // FastLED's built-in rainbow generator + fill_rainbow( leds, NUM_LEDS, gHue, 7); +} + +void rainbowWithGlitter() +{ + // built-in FastLED rainbow, plus some random sparkly glitter + rainbow(); + addGlitter(80); +} + +void addGlitter( fract8 chanceOfGlitter) +{ + if( random8() < chanceOfGlitter) { + leds[ random16(NUM_LEDS) ] += CRGB::White; + } +} + +void confetti() +{ + // random colored speckles that blink in and fade smoothly + fadeToBlackBy( leds, NUM_LEDS, 10); + int pos = random16(NUM_LEDS); + leds[pos] += CHSV( gHue + random8(64), 200, 255); +} + +void sinelon() +{ + // a colored dot sweeping back and forth, with fading trails + fadeToBlackBy( leds, NUM_LEDS, 20); + int pos = beatsin16( 13, 0, NUM_LEDS-1 ); + leds[pos] += CHSV( gHue, 255, 192); +} + +void bpm() +{ + // colored stripes pulsing at a defined Beats-Per-Minute (BPM) + uint8_t BeatsPerMinute = 62; + CRGBPalette16 palette = PartyColors_p; + uint8_t beat = beatsin8( BeatsPerMinute, 64, 255); + for( int i = 0; i < NUM_LEDS; i++) { //9948 + leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10)); + } +} + +void juggle() { + // eight colored dots, weaving in and out of sync with each other + fadeToBlackBy( leds, NUM_LEDS, 20); + uint8_t dothue = 0; + for( int i = 0; i < 8; i++) { + leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255); + dothue += 32; + } +} + diff --git a/lib/FastLED/examples/Fire2012/Fire2012.ino b/lib/FastLED/examples/Fire2012/Fire2012.ino new file mode 100644 index 00000000..a6debe9b --- /dev/null +++ b/lib/FastLED/examples/Fire2012/Fire2012.ino @@ -0,0 +1,109 @@ +/// @file Fire2012.ino +/// @brief Simple one-dimensional fire animation +/// @example Fire2012.ino + +#include + +#define LED_PIN 5 +#define COLOR_ORDER GRB +#define CHIPSET WS2811 +#define NUM_LEDS 30 + +#define BRIGHTNESS 200 +#define FRAMES_PER_SECOND 60 + +bool gReverseDirection = false; + +CRGB leds[NUM_LEDS]; + +void setup() { + delay(3000); // sanity delay + FastLED.addLeds(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); + FastLED.setBrightness( BRIGHTNESS ); +} + +void loop() +{ + // Add entropy to random number generator; we use a lot of it. + // random16_add_entropy( random()); + + Fire2012(); // run simulation frame + + FastLED.show(); // display this frame + FastLED.delay(1000 / FRAMES_PER_SECOND); +} + + +// Fire2012 by Mark Kriegsman, July 2012 +// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY +//// +// This basic one-dimensional 'fire' simulation works roughly as follows: +// There's a underlying array of 'heat' cells, that model the temperature +// at each point along the line. Every cycle through the simulation, +// four steps are performed: +// 1) All cells cool down a little bit, losing heat to the air +// 2) The heat from each cell drifts 'up' and diffuses a little +// 3) Sometimes randomly new 'sparks' of heat are added at the bottom +// 4) The heat from each cell is rendered as a color into the leds array +// The heat-to-color mapping uses a black-body radiation approximation. +// +// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). +// +// This simulation scales it self a bit depending on NUM_LEDS; it should look +// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. +// +// I recommend running this simulation at anywhere from 30-100 frames per second, +// meaning an interframe delay of about 10-35 milliseconds. +// +// Looks best on a high-density LED setup (60+ pixels/meter). +// +// +// There are two main parameters you can play with to control the look and +// feel of your fire: COOLING (used in step 1 above), and SPARKING (used +// in step 3 above). +// +// COOLING: How much does the air cool as it rises? +// Less cooling = taller flames. More cooling = shorter flames. +// Default 50, suggested range 20-100 +#define COOLING 55 + +// SPARKING: What chance (out of 255) is there that a new spark will be lit? +// Higher chance = more roaring fire. Lower chance = more flickery fire. +// Default 120, suggested range 50-200. +#define SPARKING 120 + + +void Fire2012() +{ +// Array of temperature readings at each simulation cell + static uint8_t heat[NUM_LEDS]; + + // Step 1. Cool down every cell a little + for( int i = 0; i < NUM_LEDS; i++) { + heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2)); + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for( int k= NUM_LEDS - 1; k >= 2; k--) { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if( random8() < SPARKING ) { + int y = random8(7); + heat[y] = qadd8( heat[y], random8(160,255) ); + } + + // Step 4. Map from heat cells to LED colors + for( int j = 0; j < NUM_LEDS; j++) { + CRGB color = HeatColor( heat[j]); + int pixelnumber; + if( gReverseDirection ) { + pixelnumber = (NUM_LEDS-1) - j; + } else { + pixelnumber = j; + } + leds[pixelnumber] = color; + } +} + diff --git a/lib/FastLED/examples/Fire2012WithPalette/Fire2012WithPalette.ino b/lib/FastLED/examples/Fire2012WithPalette/Fire2012WithPalette.ino new file mode 100644 index 00000000..a42ef6c6 --- /dev/null +++ b/lib/FastLED/examples/Fire2012WithPalette/Fire2012WithPalette.ino @@ -0,0 +1,168 @@ +/// @file Fire2012WithPalette.ino +/// @brief Simple one-dimensional fire animation with a programmable color palette +/// @example Fire2012WithPalette.ino + +#include + +#define LED_PIN 5 +#define COLOR_ORDER GRB +#define CHIPSET WS2811 +#define NUM_LEDS 30 + +#define BRIGHTNESS 200 +#define FRAMES_PER_SECOND 60 + +bool gReverseDirection = false; + +CRGB leds[NUM_LEDS]; + +// Fire2012 with programmable Color Palette +// +// This code is the same fire simulation as the original "Fire2012", +// but each heat cell's temperature is translated to color through a FastLED +// programmable color palette, instead of through the "HeatColor(...)" function. +// +// Four different static color palettes are provided here, plus one dynamic one. +// +// The three static ones are: +// 1. the FastLED built-in HeatColors_p -- this is the default, and it looks +// pretty much exactly like the original Fire2012. +// +// To use any of the other palettes below, just "uncomment" the corresponding code. +// +// 2. a gradient from black to red to yellow to white, which is +// visually similar to the HeatColors_p, and helps to illustrate +// what the 'heat colors' palette is actually doing, +// 3. a similar gradient, but in blue colors rather than red ones, +// i.e. from black to blue to aqua to white, which results in +// an "icy blue" fire effect, +// 4. a simplified three-step gradient, from black to red to white, just to show +// that these gradients need not have four components; two or +// three are possible, too, even if they don't look quite as nice for fire. +// +// The dynamic palette shows how you can change the basic 'hue' of the +// color palette every time through the loop, producing "rainbow fire". + +CRGBPalette16 gPal; + +void setup() { + delay(3000); // sanity delay + FastLED.addLeds(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); + FastLED.setBrightness( BRIGHTNESS ); + + // This first palette is the basic 'black body radiation' colors, + // which run from black to red to bright yellow to white. + gPal = HeatColors_p; + + // These are other ways to set up the color palette for the 'fire'. + // First, a gradient from black to red to yellow to white -- similar to HeatColors_p + // gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White); + + // Second, this palette is like the heat colors, but blue/aqua instead of red/yellow + // gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White); + + // Third, here's a simpler, three-step gradient, from black to red to white + // gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::White); + +} + +void loop() +{ + // Add entropy to random number generator; we use a lot of it. + random16_add_entropy( random()); + + // Fourth, the most sophisticated: this one sets up a new palette every + // time through the loop, based on a hue that changes every time. + // The palette is a gradient from black, to a dark color based on the hue, + // to a light color based on the hue, to white. + // + // static uint8_t hue = 0; + // hue++; + // CRGB darkcolor = CHSV(hue,255,192); // pure hue, three-quarters brightness + // CRGB lightcolor = CHSV(hue,128,255); // half 'whitened', full brightness + // gPal = CRGBPalette16( CRGB::Black, darkcolor, lightcolor, CRGB::White); + + + Fire2012WithPalette(); // run simulation frame, using palette colors + + FastLED.show(); // display this frame + FastLED.delay(1000 / FRAMES_PER_SECOND); +} + + +// Fire2012 by Mark Kriegsman, July 2012 +// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY +//// +// This basic one-dimensional 'fire' simulation works roughly as follows: +// There's a underlying array of 'heat' cells, that model the temperature +// at each point along the line. Every cycle through the simulation, +// four steps are performed: +// 1) All cells cool down a little bit, losing heat to the air +// 2) The heat from each cell drifts 'up' and diffuses a little +// 3) Sometimes randomly new 'sparks' of heat are added at the bottom +// 4) The heat from each cell is rendered as a color into the leds array +// The heat-to-color mapping uses a black-body radiation approximation. +// +// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). +// +// This simulation scales it self a bit depending on NUM_LEDS; it should look +// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. +// +// I recommend running this simulation at anywhere from 30-100 frames per second, +// meaning an interframe delay of about 10-35 milliseconds. +// +// Looks best on a high-density LED setup (60+ pixels/meter). +// +// +// There are two main parameters you can play with to control the look and +// feel of your fire: COOLING (used in step 1 above), and SPARKING (used +// in step 3 above). +// +// COOLING: How much does the air cool as it rises? +// Less cooling = taller flames. More cooling = shorter flames. +// Default 55, suggested range 20-100 +#define COOLING 55 + +// SPARKING: What chance (out of 255) is there that a new spark will be lit? +// Higher chance = more roaring fire. Lower chance = more flickery fire. +// Default 120, suggested range 50-200. +#define SPARKING 120 + + +void Fire2012WithPalette() +{ +// Array of temperature readings at each simulation cell + static uint8_t heat[NUM_LEDS]; + + // Step 1. Cool down every cell a little + for( int i = 0; i < NUM_LEDS; i++) { + heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2)); + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for( int k= NUM_LEDS - 1; k >= 2; k--) { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if( random8() < SPARKING ) { + int y = random8(7); + heat[y] = qadd8( heat[y], random8(160,255) ); + } + + // Step 4. Map from heat cells to LED colors + for( int j = 0; j < NUM_LEDS; j++) { + // Scale the heat value from 0-255 down to 0-240 + // for best results with color palettes. + uint8_t colorindex = scale8( heat[j], 240); + CRGB color = ColorFromPalette( gPal, colorindex); + int pixelnumber; + if( gReverseDirection ) { + pixelnumber = (NUM_LEDS-1) - j; + } else { + pixelnumber = j; + } + leds[pixelnumber] = color; + } +} + diff --git a/lib/FastLED/examples/FirstLight/FirstLight.ino b/lib/FastLED/examples/FirstLight/FirstLight.ino new file mode 100644 index 00000000..57118096 --- /dev/null +++ b/lib/FastLED/examples/FirstLight/FirstLight.ino @@ -0,0 +1,96 @@ +/// @file FirstLight.ino +/// @brief Animate a white dot moving along a strip of LEDs +/// @example FirstLight.ino + +// Use if you want to force the software SPI subsystem to be used for some reason (generally, you don't) +// #define FASTLED_FORCE_SOFTWARE_SPI +// Use if you want to force non-accelerated pin access (hint: you really don't, it breaks lots of things) +// #define FASTLED_FORCE_SOFTWARE_SPI +// #define FASTLED_FORCE_SOFTWARE_PINS +#include + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// Move a white dot along the strip of leds. This program simply shows how to configure the leds, +// and then how to turn a single pixel white and then off, moving down the line of pixels. +// + +// How many leds are in the strip? +#define NUM_LEDS 60 + +// 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 + +// This is an array of leds. One item for each led in your strip. +CRGB leds[NUM_LEDS]; + +// This function sets up the ledsand tells the controller about them +void setup() { + // sanity check delay - allows reprogramming if accidently blowing power w/leds + delay(2000); + + // 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 +// your leds. +void loop() { + // Move a single white led + for(int whiteLed = 0; whiteLed < NUM_LEDS; whiteLed = whiteLed + 1) { + // Turn our current led on to white, then show the leds + leds[whiteLed] = CRGB::White; + + // Show the leds (only one of which is set to white, from above) + FastLED.show(); + + // Wait a little bit + delay(100); + + // Turn our current led back to black for the next loop around + leds[whiteLed] = CRGB::Black; + } +} diff --git a/lib/FastLED/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino b/lib/FastLED/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino new file mode 100644 index 00000000..a5464b99 --- /dev/null +++ b/lib/FastLED/examples/Multiple/ArrayOfLedArrays/ArrayOfLedArrays.ino @@ -0,0 +1,41 @@ +/// @file ArrayOfLedArrays.ino +/// @brief Set up three LED strips, all running from an array of arrays +/// @example ArrayOfLedArrays.ino + +// ArrayOfLedArrays - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on +// using multiple controllers. In this example, we're going to set up three NEOPIXEL strips on three +// different pins, each strip getting its own CRGB array to be played with, only this time they're going +// to be all parts of an array of arrays. + +#include + +#define NUM_STRIPS 3 +#define NUM_LEDS_PER_STRIP 60 +CRGB leds[NUM_STRIPS][NUM_LEDS_PER_STRIP]; + +// For mirroring strips, all the "special" stuff happens just in setup. We +// just addLeds multiple times, once for each strip +void setup() { + // tell FastLED there's 60 NEOPIXEL leds on pin 2 + FastLED.addLeds(leds[0], NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 3 + FastLED.addLeds(leds[1], NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 4 + FastLED.addLeds(leds[2], NUM_LEDS_PER_STRIP); + +} + +void loop() { + // This outer loop will go over each strip, one at a time + for(int x = 0; x < NUM_STRIPS; x++) { + // This inner loop will go over each led in the current strip, one at a time + for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) { + leds[x][i] = CRGB::Red; + FastLED.show(); + leds[x][i] = CRGB::Black; + delay(100); + } + } +} diff --git a/lib/FastLED/examples/Multiple/MirroringSample/MirroringSample.ino b/lib/FastLED/examples/Multiple/MirroringSample/MirroringSample.ino new file mode 100644 index 00000000..48265ff0 --- /dev/null +++ b/lib/FastLED/examples/Multiple/MirroringSample/MirroringSample.ino @@ -0,0 +1,48 @@ +/// @file MirroringSample.ino +/// @brief Demonstrates how to use multiple LED strips, each with the same data +/// @example MirroringSample.ino + +// MirroringSample - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on +// using multiple controllers. In this example, we're going to set up four NEOPIXEL strips on four +// different pins, and show the same thing on all four of them, a simple bouncing dot/cyclon type pattern + +#include + +#define NUM_LEDS_PER_STRIP 60 +CRGB leds[NUM_LEDS_PER_STRIP]; + +// For mirroring strips, all the "special" stuff happens just in setup. We +// just addLeds multiple times, once for each strip +void setup() { + // tell FastLED there's 60 NEOPIXEL leds on pin 4 + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 5 + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 6 + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 7 + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); +} + +void loop() { + for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) { + // set our current dot to red + leds[i] = CRGB::Red; + FastLED.show(); + // clear our current dot before we move on + leds[i] = CRGB::Black; + delay(100); + } + + for(int i = NUM_LEDS_PER_STRIP-1; i >= 0; i--) { + // set our current dot to red + leds[i] = CRGB::Red; + FastLED.show(); + // clear our current dot before we move on + leds[i] = CRGB::Black; + delay(100); + } +} diff --git a/lib/FastLED/examples/Multiple/MultiArrays/MultiArrays.ino b/lib/FastLED/examples/Multiple/MultiArrays/MultiArrays.ino new file mode 100644 index 00000000..c22b419b --- /dev/null +++ b/lib/FastLED/examples/Multiple/MultiArrays/MultiArrays.ino @@ -0,0 +1,56 @@ +/// @file MultiArrays.ino +/// @brief Demonstrates how to use multiple LED strips, each with their own data +/// @example MultiArrays.ino + +// MultiArrays - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on +// using multiple controllers. In this example, we're going to set up three NEOPIXEL strips on three +// different pins, each strip getting its own CRGB array to be played with + +#include + +#define NUM_LEDS_PER_STRIP 60 +CRGB redLeds[NUM_LEDS_PER_STRIP]; +CRGB greenLeds[NUM_LEDS_PER_STRIP]; +CRGB blueLeds[NUM_LEDS_PER_STRIP]; + +// For mirroring strips, all the "special" stuff happens just in setup. We +// just addLeds multiple times, once for each strip +void setup() { + // tell FastLED there's 60 NEOPIXEL leds on pin 10 + FastLED.addLeds(redLeds, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 11 + FastLED.addLeds(greenLeds, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 12 + FastLED.addLeds(blueLeds, NUM_LEDS_PER_STRIP); + +} + +void loop() { + for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) { + // set our current dot to red, green, and blue + redLeds[i] = CRGB::Red; + greenLeds[i] = CRGB::Green; + blueLeds[i] = CRGB::Blue; + FastLED.show(); + // clear our current dot before we move on + redLeds[i] = CRGB::Black; + greenLeds[i] = CRGB::Black; + blueLeds[i] = CRGB::Black; + delay(100); + } + + for(int i = NUM_LEDS_PER_STRIP-1; i >= 0; i--) { + // set our current dot to red, green, and blue + redLeds[i] = CRGB::Red; + greenLeds[i] = CRGB::Green; + blueLeds[i] = CRGB::Blue; + FastLED.show(); + // clear our current dot before we move on + redLeds[i] = CRGB::Black; + greenLeds[i] = CRGB::Black; + blueLeds[i] = CRGB::Black; + delay(100); + } +} diff --git a/lib/FastLED/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino b/lib/FastLED/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino new file mode 100644 index 00000000..604529ad --- /dev/null +++ b/lib/FastLED/examples/Multiple/MultipleStripsInOneArray/MultipleStripsInOneArray.ino @@ -0,0 +1,38 @@ +/// @file MultipleStripsInOneArray.ino +/// @brief Demonstrates how to use multiple LED strips, each with their own data in one shared array +/// @example MultipleStripsInOneArray.ino + +// MultipleStripsInOneArray - see https://github.com/FastLED/FastLED/wiki/Multiple-Controller-Examples for more info on +// using multiple controllers. In this example, we're going to set up four NEOPIXEL strips on three +// different pins, each strip will be referring to a different part of the single led array + +#include + +#define NUM_STRIPS 3 +#define NUM_LEDS_PER_STRIP 60 +#define NUM_LEDS NUM_LEDS_PER_STRIP * NUM_STRIPS + +CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP]; + +// For mirroring strips, all the "special" stuff happens just in setup. We +// just addLeds multiple times, once for each strip +void setup() { + // tell FastLED there's 60 NEOPIXEL leds on pin 2, starting at index 0 in the led array + FastLED.addLeds(leds, 0, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 3, starting at index 60 in the led array + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP); + + // tell FastLED there's 60 NEOPIXEL leds on pin 4, starting at index 120 in the led array + FastLED.addLeds(leds, 2 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP); + +} + +void loop() { + for(int i = 0; i < NUM_LEDS; i++) { + leds[i] = CRGB::Red; + FastLED.show(); + leds[i] = CRGB::Black; + delay(100); + } +} diff --git a/lib/FastLED/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino b/lib/FastLED/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino new file mode 100644 index 00000000..03078b87 --- /dev/null +++ b/lib/FastLED/examples/Multiple/OctoWS2811Demo/OctoWS2811Demo.ino @@ -0,0 +1,41 @@ +/// @file OctoWS2811Demo.ino +/// @brief Demonstrates how to use OctoWS2811 output +/// @example OctoWS2811Demo.ino + +#define USE_OCTOWS2811 +#include +#include + +#define NUM_LEDS_PER_STRIP 64 +#define NUM_STRIPS 8 + +CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP]; + +// Pin layouts on the teensy 3: +// OctoWS2811: 2,14,7,8,6,20,21,5 + +void setup() { + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + FastLED.setBrightness(32); +} + +void loop() { + static uint8_t hue = 0; + for(int i = 0; i < NUM_STRIPS; i++) { + for(int j = 0; j < NUM_LEDS_PER_STRIP; j++) { + leds[(i*NUM_LEDS_PER_STRIP) + j] = CHSV((32*i) + hue+j,192,255); + } + } + + // Set the first n leds on each strip to show which strip it is + for(int i = 0; i < NUM_STRIPS; i++) { + for(int j = 0; j <= i; j++) { + leds[(i*NUM_LEDS_PER_STRIP) + j] = CRGB::Red; + } + } + + hue++; + + FastLED.show(); + FastLED.delay(10); +} diff --git a/lib/FastLED/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino b/lib/FastLED/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino new file mode 100644 index 00000000..6761a576 --- /dev/null +++ b/lib/FastLED/examples/Multiple/ParallelOutputDemo/ParallelOutputDemo.ino @@ -0,0 +1,60 @@ +/// @file ParallelOutputDemo.ino +/// @brief Demonstrates how to write to multiple strips simultaneously +/// @example ParallelOutputDemo.ino + +#include + +#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 + +CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP]; + +// Pin layouts on the teensy 3/3.1: +// WS2811_PORTD: 2,14,7,8,6,20,21,5 +// WS2811_PORTC: 15,22,23,9,10,13,11,12,28,27,29,30 (these last 4 are pads on the bottom of the teensy) +// WS2811_PORTDC: 2,14,7,8,6,20,21,5,15,22,23,9,10,13,11,12 - 16 way parallel +// +// Pin layouts on the due +// WS2811_PORTA: 69,68,61,60,59,100,58,31 (note: pin 100 only available on the digix) +// WS2811_PORTB: 90,91,92,93,94,95,96,97 (note: only available on the digix) +// WS2811_PORTD: 25,26,27,28,14,15,29,11 +// + + +// IBCC outputs; + +void setup() { + delay(5000); + Serial.begin(57600); + Serial.println("Starting..."); + // FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + // FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + // FastLED.addLeds(leds, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip); + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP); + + // Teensy 4 parallel output example + // FastLED.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++) { + leds[(i*NUM_LEDS_PER_STRIP) + j] = CHSV((32*i) + hue+j,192,255); + } + } + + // Set the first n leds on each strip to show which strip it is + for(int i = 0; i < NUM_STRIPS; i++) { + for(int j = 0; j <= i; j++) { + leds[(i*NUM_LEDS_PER_STRIP) + j] = CRGB::Red; + } + } + + hue++; + + FastLED.show(); + // FastLED.delay(100); +} diff --git a/lib/FastLED/examples/Noise/Noise.ino b/lib/FastLED/examples/Noise/Noise.ino new file mode 100644 index 00000000..796a0b76 --- /dev/null +++ b/lib/FastLED/examples/Noise/Noise.ino @@ -0,0 +1,118 @@ +/// @file Noise.ino +/// @brief Demonstrates how to use noise generation on a 2D LED matrix +/// @example Noise.ino + +#include + +// +// Mark's xy coordinate mapping code. See the XYMatrix for more information on it. +// + +// Params for width and height +const uint8_t kMatrixWidth = 16; +const uint8_t kMatrixHeight = 16; + +#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight) +#define NUM_LEDS (kMatrixWidth * kMatrixHeight) + +// Param for different pixel layouts +const bool kMatrixSerpentineLayout = true; + + +uint16_t XY( uint8_t x, uint8_t y) +{ + uint16_t i; + + if( kMatrixSerpentineLayout == false) { + i = (y * kMatrixWidth) + x; + } + + 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; + } + } + + return i; +} + +// The leds +CRGB leds[kMatrixWidth * kMatrixHeight]; + +// The 32bit version of our coordinates +static uint16_t x; +static uint16_t y; +static uint16_t z; + +// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll +// use the z-axis for "time". speed determines how fast time moves forward. Try +// 1 for a very slow moving effect, or 60 for something that ends up looking like +// water. +// uint16_t speed = 1; // almost looks like a painting, moves very slowly +uint16_t speed = 20; // a nice starting speed, mixes well with a scale of 100 +// uint16_t speed = 33; +// uint16_t speed = 100; // wicked fast! + +// Scale determines how far apart the pixels in our noise matrix are. Try +// changing these values around to see how it affects the motion of the display. The +// higher the value of scale, the more "zoomed out" the noise iwll be. A value +// of 1 will be so zoomed in, you'll mostly see solid colors. + +// uint16_t scale = 1; // mostly just solid colors +// uint16_t scale = 4011; // very zoomed out and shimmery +uint16_t scale = 311; + +// This is the array that we keep our computed noise values in +uint16_t noise[MAX_DIMENSION][MAX_DIMENSION]; + +void setup() { + // uncomment the following lines if you want to see FPS count information + // Serial.begin(38400); + // Serial.println("resetting!"); + delay(3000); + FastLED.addLeds(leds,NUM_LEDS); + FastLED.setBrightness(96); + + // Initialize our coordinates to some random values + x = random16(); + y = random16(); + z = random16(); +} + +// Fill the x/y array of 8-bit noise values using the inoise8 function. +void fillnoise8() { + for(int i = 0; i < MAX_DIMENSION; i++) { + int ioffset = scale * i; + for(int j = 0; j < MAX_DIMENSION; j++) { + int joffset = scale * j; + noise[i][j] = inoise8(x + ioffset,y + joffset,z); + } + } + z += speed; +} + + +void loop() { + static uint8_t ihue=0; + fillnoise8(); + for(int i = 0; i < kMatrixWidth; i++) { + for(int j = 0; j < kMatrixHeight; j++) { + // We use the value at the (i,j) coordinate in the noise + // array for our brightness, and the flipped value from (j,i) + // for our pixel's hue. + leds[XY(i,j)] = CHSV(noise[j][i],255,noise[i][j]); + + // You can also explore other ways to constrain the hue used, like below + // leds[XY(i,j)] = CHSV(ihue + (noise[j][i]>>2),255,noise[i][j]); + } + } + ihue+=1; + + FastLED.show(); + // delay(10); +} diff --git a/lib/FastLED/examples/NoisePlayground/NoisePlayground.ino b/lib/FastLED/examples/NoisePlayground/NoisePlayground.ino new file mode 100644 index 00000000..25a44cd1 --- /dev/null +++ b/lib/FastLED/examples/NoisePlayground/NoisePlayground.ino @@ -0,0 +1,79 @@ +/// @file NoisePlayground.ino +/// @brief Demonstrates how to use noise generation on a 2D LED matrix +/// @example NoisePlayground.ino + +#include + +// Params for width and height +const uint8_t kMatrixWidth = 16; +const uint8_t kMatrixHeight = 16; + +#define NUM_LEDS (kMatrixWidth * kMatrixHeight) + +// Param for different pixel layouts +#define kMatrixSerpentineLayout true + +// led array +CRGB leds[kMatrixWidth * kMatrixHeight]; + +// x,y, & time values +uint32_t x,y,v_time,hue_time,hxy; + +// Play with the values of the variables below and see what kinds of effects they +// have! More octaves will make things slower. + +// how many octaves to use for the brightness and hue functions +uint8_t octaves=1; +uint8_t hue_octaves=3; + +// the 'distance' between points on the x and y axis +int xscale=57771; +int yscale=57771; + +// the 'distance' between x/y points for the hue noise +int hue_scale=1; + +// how fast we move through time & hue noise +int time_speed=1111; +int hue_speed=31; + +// adjust these values to move along the x or y axis between frames +int x_speed=331; +int y_speed=1111; + +void loop() { + // fill the led array 2/16-bit noise values + fill_2dnoise16(leds, kMatrixWidth, kMatrixHeight, kMatrixSerpentineLayout, + octaves,x,xscale,y,yscale,v_time, + hue_octaves,hxy,hue_scale,hxy,hue_scale,hue_time, false); + + FastLED.show(); + + // adjust the intra-frame time values + x += x_speed; + y += y_speed; + v_time += time_speed; + hue_time += hue_speed; + // delay(50); +} + + +void setup() { + // initialize the x/y and time values + random16_set_seed(8934); + random16_add_entropy(analogRead(3)); + + Serial.begin(57600); + Serial.println("resetting!"); + + delay(3000); + FastLED.addLeds(leds,NUM_LEDS); + FastLED.setBrightness(96); + + hxy = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16(); + x = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16(); + y = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16(); + v_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16(); + hue_time = (uint32_t)((uint32_t)random16() << 16) + (uint32_t)random16(); + +} diff --git a/lib/FastLED/examples/NoisePlusPalette/NoisePlusPalette.ino b/lib/FastLED/examples/NoisePlusPalette/NoisePlusPalette.ino new file mode 100644 index 00000000..89d124ae --- /dev/null +++ b/lib/FastLED/examples/NoisePlusPalette/NoisePlusPalette.ino @@ -0,0 +1,280 @@ +/// @file NoisePlusPalette.ino +/// @brief Demonstrates how to mix noise generation with color palettes on a 2D LED matrix +/// @example NoisePlusPalette.ino + +#include + +#define LED_PIN 3 +#define BRIGHTNESS 96 +#define LED_TYPE WS2811 +#define COLOR_ORDER GRB + +// Params for width and height +const uint8_t kMatrixWidth = 16; +const uint8_t kMatrixHeight = 16; + +// Param for different pixel layouts +const bool kMatrixSerpentineLayout = true; + + +// This example combines two features of FastLED to produce a remarkable range of +// effects from a relatively small amount of code. This example combines FastLED's +// color palette lookup functions with FastLED's Perlin noise generator, and +// the combination is extremely powerful. +// +// You might want to look at the "ColorPalette" and "Noise" examples separately +// if this example code seems daunting. +// +// +// The basic setup here is that for each frame, we generate a new array of +// 'noise' data, and then map it onto the LED matrix through a color palette. +// +// Periodically, the color palette is changed, and new noise-generation parameters +// are chosen at the same time. In this example, specific noise-generation +// values have been selected to match the given color palettes; some are faster, +// or slower, or larger, or smaller than others, but there's no reason these +// parameters can't be freely mixed-and-matched. +// +// In addition, this example includes some fast automatic 'data smoothing' at +// lower noise speeds to help produce smoother animations in those cases. +// +// The FastLED built-in color palettes (Forest, Clouds, Lava, Ocean, Party) are +// used, as well as some 'hand-defined' ones, and some proceedurally generated +// palettes. + + +#define NUM_LEDS (kMatrixWidth * kMatrixHeight) +#define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight) + +// The leds +CRGB leds[kMatrixWidth * kMatrixHeight]; + +// The 16 bit version of our coordinates +static uint16_t x; +static uint16_t y; +static uint16_t z; + +// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll +// use the z-axis for "time". speed determines how fast time moves forward. Try +// 1 for a very slow moving effect, or 60 for something that ends up looking like +// water. +uint16_t speed = 20; // speed is set dynamically once we've started up + +// Scale determines how far apart the pixels in our noise matrix are. Try +// changing these values around to see how it affects the motion of the display. The +// higher the value of scale, the more "zoomed out" the noise iwll be. A value +// of 1 will be so zoomed in, you'll mostly see solid colors. +uint16_t scale = 30; // scale is set dynamically once we've started up + +// This is the array that we keep our computed noise values in +uint8_t noise[MAX_DIMENSION][MAX_DIMENSION]; + +CRGBPalette16 currentPalette( PartyColors_p ); +uint8_t colorLoop = 1; + +void setup() { + delay(3000); + FastLED.addLeds(leds,NUM_LEDS); + FastLED.setBrightness(BRIGHTNESS); + + // Initialize our coordinates to some random values + x = random16(); + y = random16(); + z = random16(); +} + + + +// Fill the x/y array of 8-bit noise values using the inoise8 function. +void fillnoise8() { + // If we're runing at a low "speed", some 8-bit artifacts become visible + // from frame-to-frame. In order to reduce this, we can do some fast data-smoothing. + // The amount of data smoothing we're doing depends on "speed". + uint8_t dataSmoothing = 0; + if( speed < 50) { + dataSmoothing = 200 - (speed * 4); + } + + for(int i = 0; i < MAX_DIMENSION; i++) { + int ioffset = scale * i; + for(int j = 0; j < MAX_DIMENSION; j++) { + int joffset = scale * j; + + uint8_t data = inoise8(x + ioffset,y + joffset,z); + + // The range of the inoise8 function is roughly 16-238. + // These two operations expand those values out to roughly 0..255 + // You can comment them out if you want the raw noise data. + data = qsub8(data,16); + data = qadd8(data,scale8(data,39)); + + if( dataSmoothing ) { + uint8_t olddata = noise[i][j]; + uint8_t newdata = scale8( olddata, dataSmoothing) + scale8( data, 256 - dataSmoothing); + data = newdata; + } + + noise[i][j] = data; + } + } + + z += speed; + + // apply slow drift to X and Y, just for visual variation. + x += speed / 8; + y -= speed / 16; +} + +void mapNoiseToLEDsUsingPalette() +{ + static uint8_t ihue=0; + + for(int i = 0; i < kMatrixWidth; i++) { + for(int j = 0; j < kMatrixHeight; j++) { + // We use the value at the (i,j) coordinate in the noise + // array for our brightness, and the flipped value from (j,i) + // for our pixel's index into the color palette. + + uint8_t index = noise[j][i]; + uint8_t bri = noise[i][j]; + + // if this palette is a 'loop', add a slowly-changing base value + if( colorLoop) { + index += ihue; + } + + // brighten up, as the color palette itself often contains the + // light/dark dynamic range desired + if( bri > 127 ) { + bri = 255; + } else { + bri = dim8_raw( bri * 2); + } + + CRGB color = ColorFromPalette( currentPalette, index, bri); + leds[XY(i,j)] = color; + } + } + + ihue+=1; +} + +void loop() { + // Periodically choose a new palette, speed, and scale + ChangePaletteAndSettingsPeriodically(); + + // generate noise data + fillnoise8(); + + // convert the noise data to colors in the LED array + // using the current palette + mapNoiseToLEDsUsingPalette(); + + FastLED.show(); + // delay(10); +} + + + +// There are several different palettes of colors demonstrated here. +// +// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p, +// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p. +// +// Additionally, you can manually define your own color palettes, or you can write +// code that creates color palettes on the fly. + +// 1 = 5 sec per palette +// 2 = 10 sec per palette +// etc +#define HOLD_PALETTES_X_TIMES_AS_LONG 1 + +void ChangePaletteAndSettingsPeriodically() +{ + uint8_t secondHand = ((millis() / 1000) / HOLD_PALETTES_X_TIMES_AS_LONG) % 60; + static uint8_t lastSecond = 99; + + if( lastSecond != secondHand) { + lastSecond = secondHand; + if( secondHand == 0) { currentPalette = RainbowColors_p; speed = 20; scale = 30; colorLoop = 1; } + if( secondHand == 5) { SetupPurpleAndGreenPalette(); speed = 10; scale = 50; colorLoop = 1; } + if( secondHand == 10) { SetupBlackAndWhiteStripedPalette(); speed = 20; scale = 30; colorLoop = 1; } + if( secondHand == 15) { currentPalette = ForestColors_p; speed = 8; scale =120; colorLoop = 0; } + if( secondHand == 20) { currentPalette = CloudColors_p; speed = 4; scale = 30; colorLoop = 0; } + if( secondHand == 25) { currentPalette = LavaColors_p; speed = 8; scale = 50; colorLoop = 0; } + if( secondHand == 30) { currentPalette = OceanColors_p; speed = 20; scale = 90; colorLoop = 0; } + if( secondHand == 35) { currentPalette = PartyColors_p; speed = 20; scale = 30; colorLoop = 1; } + if( secondHand == 40) { SetupRandomPalette(); speed = 20; scale = 20; colorLoop = 1; } + if( secondHand == 45) { SetupRandomPalette(); speed = 50; scale = 50; colorLoop = 1; } + if( secondHand == 50) { SetupRandomPalette(); speed = 90; scale = 90; colorLoop = 1; } + if( secondHand == 55) { currentPalette = RainbowStripeColors_p; speed = 30; scale = 20; colorLoop = 1; } + } +} + +// This function generates a random palette that's a gradient +// between four different colors. The first is a dim hue, the second is +// a bright hue, the third is a bright pastel, and the last is +// another bright hue. This gives some visual bright/dark variation +// which is more interesting than just a gradient of different hues. +void SetupRandomPalette() +{ + currentPalette = CRGBPalette16( + CHSV( random8(), 255, 32), + CHSV( random8(), 255, 255), + CHSV( random8(), 128, 255), + CHSV( random8(), 255, 255)); +} + +// This function sets up a palette of black and white stripes, +// using code. Since the palette is effectively an array of +// sixteen CRGB colors, the various fill_* functions can be used +// to set them up. +void SetupBlackAndWhiteStripedPalette() +{ + // 'black out' all 16 palette entries... + fill_solid( currentPalette, 16, CRGB::Black); + // and set every fourth one to white. + currentPalette[0] = CRGB::White; + currentPalette[4] = CRGB::White; + currentPalette[8] = CRGB::White; + currentPalette[12] = CRGB::White; + +} + +// This function sets up a palette of purple and green stripes. +void SetupPurpleAndGreenPalette() +{ + CRGB purple = CHSV( HUE_PURPLE, 255, 255); + CRGB green = CHSV( HUE_GREEN, 255, 255); + CRGB black = CRGB::Black; + + currentPalette = CRGBPalette16( + green, green, black, black, + purple, purple, black, black, + green, green, black, black, + purple, purple, black, black ); +} + + +// +// Mark's xy coordinate mapping code. See the XYMatrix for more information on it. +// +uint16_t XY( uint8_t x, uint8_t y) +{ + uint16_t i; + if( kMatrixSerpentineLayout == false) { + i = (y * kMatrixWidth) + x; + } + 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; + } + } + return i; +} + diff --git a/lib/FastLED/examples/Pacifica/Pacifica.ino b/lib/FastLED/examples/Pacifica/Pacifica.ino new file mode 100644 index 00000000..cf5a7a3e --- /dev/null +++ b/lib/FastLED/examples/Pacifica/Pacifica.ino @@ -0,0 +1,156 @@ +/// @file Pacifica.ino +/// @brief Gentle, blue-green ocean wave animation +/// @example Pacifica.ino + +// +// "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/lib/FastLED/examples/Pintest/Pintest.ino b/lib/FastLED/examples/Pintest/Pintest.ino new file mode 100644 index 00000000..17ee8469 --- /dev/null +++ b/lib/FastLED/examples/Pintest/Pintest.ino @@ -0,0 +1,202 @@ +/// @file Pintest.ino +/// @brief Checks available pin outputs (for debugging) +/// @example Pintest.ino + +#include + +char fullstrBuffer[64]; + +const char *getPort(void *portPtr) { +// AVR port checks +#ifdef PORTA + if(portPtr == (void*)&PORTA) { return "PORTA"; } +#endif +#ifdef PORTB + if(portPtr == (void*)&PORTB) { return "PORTB"; } +#endif +#ifdef PORTC + if(portPtr == (void*)&PORTC) { return "PORTC"; } +#endif +#ifdef PORTD + if(portPtr == (void*)&PORTD) { return "PORTD"; } +#endif +#ifdef PORTE + if(portPtr == (void*)&PORTE) { return "PORTE"; } +#endif +#ifdef PORTF + if(portPtr == (void*)&PORTF) { return "PORTF"; } +#endif +#ifdef PORTG + if(portPtr == (void*)&PORTG) { return "PORTG"; } +#endif +#ifdef PORTH + if(portPtr == (void*)&PORTH) { return "PORTH"; } +#endif +#ifdef PORTI + if(portPtr == (void*)&PORTI) { return "PORTI"; } +#endif +#ifdef PORTJ + if(portPtr == (void*)&PORTJ) { return "PORTJ"; } +#endif +#ifdef PORTK + if(portPtr == (void*)&PORTK) { return "PORTK"; } +#endif +#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 +#ifdef GPIO_B_PDOR + if(portPtr == (void*)&GPIO_B_PDOR) { return "GPIO_B_PDOR"; } +#endif +#ifdef GPIO_C_PDOR + if(portPtr == (void*)&GPIO_C_PDOR) { return "GPIO_C_PDOR"; } +#endif +#ifdef GPIO_D_PDOR + if(portPtr == (void*)&GPIO_D_PDOR) { return "GPIO_D_PDOR"; } +#endif +#ifdef GPIO_E_PDOR + if(portPtr == (void*)&GPIO_E_PDOR) { return "GPIO_E_PDOR"; } +#endif +#ifdef REG_PIO_A_ODSR + if(portPtr == (void*)®_PIO_A_ODSR) { return "REG_PIO_A_ODSR"; } +#endif +#ifdef REG_PIO_B_ODSR + if(portPtr == (void*)®_PIO_B_ODSR) { return "REG_PIO_B_ODSR"; } +#endif +#ifdef REG_PIO_C_ODSR + if(portPtr == (void*)®_PIO_C_ODSR) { return "REG_PIO_C_ODSR"; } +#endif +#ifdef REG_PIO_D_ODSR + if(portPtr == (void*)®_PIO_D_ODSR) { return "REG_PIO_D_ODSR"; } +#endif + +// 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(); + + void *systemThinksPortIs = (void*)portOutputRegister(digitalPinToPort(PIN)); + RwReg systemThinksMaskIs = digitalPinToBitMask(PIN); + + Serial.print("Pin "); Serial.print(PIN); Serial.print(": 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 "); + Serial.print(getPort((void*)systemThinksPortIs)); + Serial.print(" & mask "); + } + + if(systemThinksMaskIs == FastPin::mask()) { + Serial.println("valid."); + } 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 PrintPins<0>() { + RwReg *systemThinksPortIs = portOutputRegister(digitalPinToPort(0)); + RwReg systemThinksMaskIs = digitalPinToBitMask(0); + + 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() { + Serial.println(counter); + +#ifdef MAX_PIN + CheckPin(); +#endif + + Serial.println("-----"); +#ifdef NUM_DIGITAL_PINS + PrintPins(); +#endif + Serial.println("------"); + + delay(100000); +} diff --git a/lib/FastLED/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino b/lib/FastLED/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino new file mode 100644 index 00000000..c61778c4 --- /dev/null +++ b/lib/FastLED/examples/Ports/PJRCSpectrumAnalyzer/PJRCSpectrumAnalyzer.ino @@ -0,0 +1,140 @@ +/// @file PJRCSpectrumAnalyzer.ino +/// @brief Creates an impressive LED light show to music input on the Teensy +/// @example PJRCSpectrumAnalyzer.ino + +// LED Audio Spectrum Analyzer Display +// +// Creates an impressive LED light show to music input +// using Teensy 3.1 with the OctoWS2811 adaptor board +// http://www.pjrc.com/store/teensy31.html +// http://www.pjrc.com/store/octo28_adaptor.html +// +// Line Level Audio Input connects to analog pin A3 +// Recommended input circuit: +// http://www.pjrc.com/teensy/gui/?info=AudioInputAnalog +// +// This example code is in the public domain. + +#define USE_OCTOWS2811 +#include +#include +#include +#include +#include +#include + +// The display size and color to use +const unsigned int matrix_width = 60; +const unsigned int matrix_height = 32; +const unsigned int myColor = 0x400020; + +// These parameters adjust the vertical thresholds +const float maxLevel = 0.5; // 1.0 = max, lower is more "sensitive" +const float dynamicRange = 40.0; // total range to display, in decibels +const float linearBlend = 0.3; // useful range is 0 to 0.7 + +CRGB leds[matrix_width * matrix_height]; + +// Audio library objects +AudioInputAnalog adc1(A3); //xy=99,55 +AudioAnalyzeFFT1024 fft; //xy=265,75 +AudioConnection patchCord1(adc1, fft); + + +// This array holds the volume level (0 to 1.0) for each +// vertical pixel to turn on. Computed in setup() using +// the 3 parameters above. +float thresholdVertical[matrix_height]; + +// This array specifies how many of the FFT frequency bin +// to use for each horizontal pixel. Because humans hear +// in octaves and FFT bins are linear, the low frequencies +// use a small number of bins, higher frequencies use more. +int frequencyBinsHorizontal[matrix_width] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, + 15, 16, 17, 18, 19, 20, 22, 23, 24, 25 +}; + + + +// Run setup once +void setup() { + // the audio library needs to be given memory to start working + AudioMemory(12); + + // compute the vertical thresholds before starting + computeVerticalLevels(); + + // turn on the display + FastLED.addLeds(leds,(matrix_width * matrix_height) / 8); +} + +// A simple xy() function to turn display matrix coordinates +// into the index numbers OctoWS2811 requires. If your LEDs +// are arranged differently, edit this code... +unsigned int xy(unsigned int x, unsigned int y) { + if ((y & 1) == 0) { + // even numbered rows (0, 2, 4...) are left to right + return y * matrix_width + x; + } else { + // odd numbered rows (1, 3, 5...) are right to left + return y * matrix_width + matrix_width - 1 - x; + } +} + +// Run repetitively +void loop() { + unsigned int x, y, freqBin; + float level; + + if (fft.available()) { + // freqBin counts which FFT frequency data has been used, + // starting at low frequency + freqBin = 0; + + for (x=0; x < matrix_width; x++) { + // get the volume for each horizontal pixel position + level = fft.read(freqBin, freqBin + frequencyBinsHorizontal[x] - 1); + + // uncomment to see the spectrum in Arduino's Serial Monitor + // Serial.print(level); + // Serial.print(" "); + + for (y=0; y < matrix_height; y++) { + // for each vertical pixel, check if above the threshold + // and turn the LED on or off + if (level >= thresholdVertical[y]) { + leds[xy(x,y)] = CRGB(myColor); + } else { + leds[xy(x,y)] = CRGB::Black; + } + } + // increment the frequency bin count, so we display + // low to higher frequency from left to right + freqBin = freqBin + frequencyBinsHorizontal[x]; + } + // after all pixels set, show them all at the same instant + FastLED.show(); + // Serial.println(); + } +} + + +// Run once from setup, the compute the vertical levels +void computeVerticalLevels() { + unsigned int y; + float n, logLevel, linearLevel; + + for (y=0; y < matrix_height; y++) { + n = (float)y / (float)(matrix_height - 1); + logLevel = pow10f(n * -1.0 * (dynamicRange / 20.0)); + linearLevel = 1.0 - n; + linearLevel = linearLevel * linearBlend; + logLevel = logLevel * (1.0 - linearBlend); + thresholdVertical[y] = (logLevel + linearLevel) * maxLevel; + } +} diff --git a/lib/FastLED/examples/Pride2015/Pride2015.ino b/lib/FastLED/examples/Pride2015/Pride2015.ino new file mode 100644 index 00000000..bc2fc0d9 --- /dev/null +++ b/lib/FastLED/examples/Pride2015/Pride2015.ino @@ -0,0 +1,86 @@ +/// @file Pride2015.ino +/// @brief Animated, ever-changing rainbows. +/// @example Pride2015.ino + +#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/lib/FastLED/examples/RGBCalibrate/RGBCalibrate.ino b/lib/FastLED/examples/RGBCalibrate/RGBCalibrate.ino new file mode 100644 index 00000000..42899bc6 --- /dev/null +++ b/lib/FastLED/examples/RGBCalibrate/RGBCalibrate.ino @@ -0,0 +1,99 @@ +/// @file RGBCalibrate.ino +/// @brief Use this to determine what the RGB ordering for your LEDs should be +/// @example RGBCalibrate.ino + +#include "FastLED.h" + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RGB Calibration code +// +// Use this sketch to determine what the RGB ordering for your chipset should be. Steps for setting up to use: + +// * Uncomment the line in setup that corresponds to the LED chipset that you are using. (Note that they +// all explicitly specify the RGB order as RGB) +// * Define DATA_PIN to the pin that data is connected to. +// * (Optional) if using software SPI for chipsets that are SPI based, define CLOCK_PIN to the clock pin +// * Compile/upload/run the sketch + +// You should see six leds on. If the RGB ordering is correct, you should see 1 red led, 2 green +// leds, and 3 blue leds. If you see different colors, the count of each color tells you what the +// position for that color in the rgb orering should be. So, for example, if you see 1 Blue, and 2 +// Red, and 3 Green leds then the rgb ordering should be BRG (Blue, Red, Green). + +// You can then test this ordering by setting the RGB ordering in the addLeds line below to the new ordering +// and it should come out correctly, 1 red, 2 green, and 3 blue. +// +////////////////////////////////////////////////// + +#define NUM_LEDS 7 + +// 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 + +CRGB leds[NUM_LEDS]; + +void setup() { + // sanity check delay - allows reprogramming if accidently blowing power w/leds + delay(2000); + + // 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.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 + + // FastLED.setBrightness(CRGB(255,255,255)); +} + +void loop() { + leds[0] = CRGB(255,0,0); + leds[1] = CRGB(0,255,0); + leds[2] = CRGB(0,255,0); + leds[3] = CRGB(0,0,255); + leds[4] = CRGB(0,0,255); + leds[5] = CRGB(0,0,255); + leds[6] = CRGB(0,0,0); + FastLED.show(); + delay(1000); +} diff --git a/lib/FastLED/examples/RGBSetDemo/RGBSetDemo.ino b/lib/FastLED/examples/RGBSetDemo/RGBSetDemo.ino new file mode 100644 index 00000000..cb52677e --- /dev/null +++ b/lib/FastLED/examples/RGBSetDemo/RGBSetDemo.ino @@ -0,0 +1,26 @@ +/// @file RGBSetDemo.ino +/// @brief Demonstrates how to create an LED group with CRGBArray +/// @example RGBSetDemo.ino + +#include +#define NUM_LEDS 40 + +CRGBArray leds; + +void setup() { FastLED.addLeds(leds, NUM_LEDS); } + +void loop(){ + static uint8_t hue; + for(int i = 0; i < NUM_LEDS/2; i++) { + // fade everything out + leds.fadeToBlackBy(40); + + // let's set an led value + leds[i] = CHSV(hue++,255,255); + + // now, let's first 20 leds to the top 20 leds, + leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0); + FastLED.delay(33); + } +} + diff --git a/lib/FastLED/examples/SmartMatrix/SmartMatrix.ino b/lib/FastLED/examples/SmartMatrix/SmartMatrix.ino new file mode 100644 index 00000000..c66058f1 --- /dev/null +++ b/lib/FastLED/examples/SmartMatrix/SmartMatrix.ino @@ -0,0 +1,130 @@ +/// @file SmartMatrix.ino +/// @brief Demonstrates how to use FastLED with the SmartMatrix library +/// @example SmartMatrix.ino + +/* This example demos a rectangular LED matrix with moving noise. + It requires the SmartMatrix library in addition to FastLED. + This SmartMatrix library is only available on Teensy boards at the moment. + It can be found at https://github.com/pixelmatix/SmartMatrix +*/ +#include +#include + +#define kMatrixWidth 32 +#define kMatrixHeight 32 +const bool kMatrixSerpentineLayout = false; + +#define NUM_LEDS (kMatrixWidth * kMatrixHeight) + +CRGB leds[kMatrixWidth * kMatrixHeight]; + + +uint16_t XY( uint8_t x, uint8_t y) +{ + uint16_t i; + + if( kMatrixSerpentineLayout == false) { + i = (y * kMatrixWidth) + x; + } + + 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; + } + } + + return i; +} + +// The 32bit version of our coordinates +static uint16_t x; +static uint16_t y; +static uint16_t z; + +// We're using the x/y dimensions to map to the x/y pixels on the matrix. We'll +// use the z-axis for "time". speed determines how fast time moves forward. Try +// 1 for a very slow moving effect, or 60 for something that ends up looking like +// water. +// uint16_t speed = 1; // almost looks like a painting, moves very slowly +uint16_t speed = 20; // a nice starting speed, mixes well with a scale of 100 +// uint16_t speed = 33; +// uint16_t speed = 100; // wicked fast! + +// Scale determines how far apart the pixels in our noise matrix are. Try +// changing these values around to see how it affects the motion of the display. The +// higher the value of scale, the more "zoomed out" the noise iwll be. A value +// of 1 will be so zoomed in, you'll mostly see solid colors. + +// uint16_t scale = 1; // mostly just solid colors +// uint16_t scale = 4011; // very zoomed out and shimmery +uint16_t scale = 31; + +// This is the array that we keep our computed noise values in +uint8_t noise[kMatrixWidth][kMatrixHeight]; + +void setup() { + // uncomment the following lines if you want to see FPS count information + // Serial.begin(38400); + // Serial.println("resetting!"); + delay(3000); + FastLED.addLeds(leds,NUM_LEDS); + FastLED.setBrightness(96); + + // Initialize our coordinates to some random values + x = random16(); + y = random16(); + z = random16(); + + // Show off smart matrix scrolling text + pSmartMatrix->setScrollMode(wrapForward); + pSmartMatrix->setScrollColor({0xff, 0xff, 0xff}); + pSmartMatrix->setScrollSpeed(15); + pSmartMatrix->setScrollFont(font6x10); + pSmartMatrix->scrollText("Smart Matrix & FastLED", -1); + pSmartMatrix->setScrollOffsetFromEdge(10); +} + +// Fill the x/y array of 8-bit noise values using the inoise8 function. +void fillnoise8() { + for(int i = 0; i < kMatrixWidth; i++) { + int ioffset = scale * i; + for(int j = 0; j < kMatrixHeight; j++) { + int joffset = scale * j; + noise[i][j] = inoise8(x + ioffset,y + joffset,z); + } + } + z += speed; +} + + +void loop() { + static uint8_t circlex = 0; + static uint8_t circley = 0; + + static uint8_t ihue=0; + fillnoise8(); + for(int i = 0; i < kMatrixWidth; i++) { + for(int j = 0; j < kMatrixHeight; j++) { + // We use the value at the (i,j) coordinate in the noise + // array for our brightness, and the flipped value from (j,i) + // for our pixel's hue. + leds[XY(i,j)] = CHSV(noise[j][i],255,noise[i][j]); + + // You can also explore other ways to constrain the hue used, like below + // leds[XY(i,j)] = CHSV(ihue + (noise[j][i]>>2),255,noise[i][j]); + } + } + ihue+=1; + + // N.B. this requires SmartMatrix modified w/triple buffering support + pSmartMatrix->fillCircle(circlex % 32,circley % 32,6,CRGB(CHSV(ihue+128,255,255))); + circlex += random16(2); + circley += random16(2); + FastLED.show(); + // delay(10); +} diff --git a/lib/FastLED/examples/TwinkleFox/TwinkleFox.ino b/lib/FastLED/examples/TwinkleFox/TwinkleFox.ino new file mode 100644 index 00000000..943b7b30 --- /dev/null +++ b/lib/FastLED/examples/TwinkleFox/TwinkleFox.ino @@ -0,0 +1,383 @@ +/// @file TwinkleFox.ino +/// @brief Twinkling "holiday" lights that fade in and out. +/// @example TwinkleFox.ino + +#include "FastLED.h" + + +#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/lib/FastLED/examples/XYMatrix/XYMatrix.ino b/lib/FastLED/examples/XYMatrix/XYMatrix.ino new file mode 100644 index 00000000..827c13e6 --- /dev/null +++ b/lib/FastLED/examples/XYMatrix/XYMatrix.ino @@ -0,0 +1,214 @@ +/// @file XYMatrix.ino +/// @brief Demonstrates how to use an XY position helper function with a 2D matrix +/// @example XYMatrix.ino + +#include + +#define LED_PIN 3 + +#define COLOR_ORDER GRB +#define CHIPSET WS2811 + +#define BRIGHTNESS 64 + +// Helper functions for an two-dimensional XY matrix of pixels. +// Simple 2-D demo code is included as well. +// +// XY(x,y) takes x and y coordinates and returns an LED index number, +// for use like this: leds[ XY(x,y) ] == CRGB::Red; +// No error checking is performed on the ranges of x and y. +// +// XYsafe(x,y) takes x and y coordinates and returns an LED index number, +// for use like this: leds[ XYsafe(x,y) ] == CRGB::Red; +// Error checking IS performed on the ranges of x and y, and an +// index of "-1" is returned. Special instructions below +// explain how to use this without having to do your own error +// checking every time you use this function. +// This is a slightly more advanced technique, and +// it REQUIRES SPECIAL ADDITIONAL setup, described below. + + +// Params for width and height +const uint8_t kMatrixWidth = 16; +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: +// +// 0 > 1 > 2 > 3 > 4 +// | +// .----<----<----<----' +// | +// 5 > 6 > 7 > 8 > 9 +// | +// .----<----<----<----' +// | +// 10 > 11 > 12 > 13 > 14 +// | +// .----<----<----<----' +// | +// 15 > 16 > 17 > 18 > 19 +// +// Set 'kMatrixSerpentineLayout' to true if your pixels are +// laid out back-and-forth, like this: +// +// 0 > 1 > 2 > 3 > 4 +// | +// | +// 9 < 8 < 7 < 6 < 5 +// | +// | +// 10 > 11 > 12 > 13 > 14 +// | +// | +// 19 < 18 < 17 < 16 < 15 +// +// Bonus vocabulary word: anything that goes one way +// in one row, and then backwards in the next row, and so on +// is call "boustrophedon", meaning "as the ox plows." + + +// This function will return the right 'led index number' for +// a given set of X and Y coordinates on your matrix. +// IT DOES NOT CHECK THE COORDINATE BOUNDARIES. +// That's up to you. Don't pass it bogus values. +// +// Use the "XY" function like this: +// +// for( uint8_t x = 0; x < kMatrixWidth; x++) { +// for( uint8_t y = 0; y < kMatrixHeight; y++) { +// +// // Here's the x, y to 'led index' in action: +// leds[ XY( x, y) ] = CHSV( random8(), 255, 255); +// +// } +// } +// +// +uint16_t XY( uint8_t x, uint8_t y) +{ + uint16_t i; + + if( kMatrixSerpentineLayout == false) { + if (kMatrixVertical == false) { + i = (y * kMatrixWidth) + x; + } else { + i = kMatrixHeight * (kMatrixWidth - (x+1))+y; + } + } + + if( kMatrixSerpentineLayout == true) { + 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); + } + } + } + + return i; +} + + +// Once you've gotten the basics working (AND NOT UNTIL THEN!) +// here's a helpful technique that can be tricky to set up, but +// then helps you avoid the needs for sprinkling array-bound-checking +// throughout your code. +// +// It requires a careful attention to get it set up correctly, but +// can potentially make your code smaller and faster. +// +// Suppose you have an 8 x 5 matrix of 40 LEDs. Normally, you'd +// delcare your leds array like this: +// CRGB leds[40]; +// But instead of that, declare an LED buffer with one extra pixel in +// it, "leds_plus_safety_pixel". Then declare "leds" as a pointer to +// that array, but starting with the 2nd element (id=1) of that array: +// CRGB leds_with_safety_pixel[41]; +// CRGB* const leds( leds_plus_safety_pixel + 1); +// Then you use the "leds" array as you normally would. +// Now "leds[0..N]" are aliases for "leds_plus_safety_pixel[1..(N+1)]", +// AND leds[-1] is now a legitimate and safe alias for leds_plus_safety_pixel[0]. +// leds_plus_safety_pixel[0] aka leds[-1] is now your "safety pixel". +// +// Now instead of using the XY function above, use the one below, "XYsafe". +// +// If the X and Y values are 'in bounds', this function will return an index +// into the visible led array, same as "XY" does. +// HOWEVER -- and this is the trick -- if the X or Y values +// are out of bounds, this function will return an index of -1. +// And since leds[-1] is actually just an alias for leds_plus_safety_pixel[0], +// it's a totally safe and legal place to access. And since the 'safety pixel' +// falls 'outside' the visible part of the LED array, anything you write +// there is hidden from view automatically. +// Thus, this line of code is totally safe, regardless of the actual size of +// your matrix: +// leds[ XYsafe( random8(), random8() ) ] = CHSV( random8(), 255, 255); +// +// The only catch here is that while this makes it safe to read from and +// write to 'any pixel', there's really only ONE 'safety pixel'. No matter +// what out-of-bounds coordinates you write to, you'll really be writing to +// that one safety pixel. And if you try to READ from the safety pixel, +// you'll read whatever was written there last, reglardless of what coordinates +// were supplied. + +#define NUM_LEDS (kMatrixWidth * kMatrixHeight) +CRGB leds_plus_safety_pixel[ NUM_LEDS + 1]; +CRGB* const leds( leds_plus_safety_pixel + 1); + +uint16_t XYsafe( uint8_t x, uint8_t y) +{ + if( x >= kMatrixWidth) return -1; + if( y >= kMatrixHeight) return -1; + return XY(x,y); +} + + +// Demo that USES "XY" follows code below + +void loop() +{ + uint32_t ms = millis(); + int32_t yHueDelta32 = ((int32_t)cos16( ms * (27/1) ) * (350 / kMatrixWidth)); + int32_t xHueDelta32 = ((int32_t)cos16( ms * (39/1) ) * (310 / kMatrixHeight)); + DrawOneFrame( ms / 65536, yHueDelta32 / 32768, xHueDelta32 / 32768); + if( ms < 5000 ) { + FastLED.setBrightness( scale8( BRIGHTNESS, (ms * 256) / 5000)); + } else { + FastLED.setBrightness(BRIGHTNESS); + } + FastLED.show(); +} + +void DrawOneFrame( uint8_t startHue8, int8_t yHueDelta8, int8_t xHueDelta8) +{ + uint8_t lineStartHue = startHue8; + for( uint8_t y = 0; y < kMatrixHeight; y++) { + lineStartHue += yHueDelta8; + uint8_t pixelHue = lineStartHue; + for( uint8_t x = 0; x < kMatrixWidth; x++) { + pixelHue += xHueDelta8; + leds[ XY(x, y)] = CHSV( pixelHue, 255, 255); + } + } +} + + +void setup() { + FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalSMD5050); + FastLED.setBrightness( BRIGHTNESS ); +} diff --git a/lib/FastLED/keywords.txt b/lib/FastLED/keywords.txt new file mode 100644 index 00000000..2766c709 --- /dev/null +++ b/lib/FastLED/keywords.txt @@ -0,0 +1,470 @@ +####################################### +# Syntax Coloring Map For FastLED +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +CFastLED KEYWORD1 +CHSV KEYWORD1 +CRGB KEYWORD1 +CRGBArray KEYWORD1 +LEDS KEYWORD1 +FastLED KEYWORD1 +FastPin KEYWORD1 +FastSPI KEYWORD1 +FastSPI_LED2 KEYWORD1 + +CLEDController KEYWORD1 + +CRGBPalette16 KEYWORD1 +CRGBPalette256 KEYWORD1 +CHSVPalette16 KEYWORD1 +CHSVPalette256 KEYWORD1 +CHSVPalette16 KEYWORD1 +CHSVPalette256 KEYWORD1 +CRGBPalette16 KEYWORD1 +CRGBPalette256 KEYWORD1 + +TProgmemPalette16 KEYWORD1 +TProgmemPalette32 KEYWORD1 +TDynamicRGBGradientPalette_byte KEYWORD1 +TDynamicRGBGradientPalette_bytes KEYWORD1 +TDynamicRGBGradientPalettePtr KEYWORD1 +TProgmemHSVPalette16 KEYWORD1 +TProgmemHSVPalette32 KEYWORD1 +TProgmemRGBGradientPalette_byte KEYWORD1 +TProgmemRGBGradientPalette_bytes KEYWORD1 +TProgmemRGBGradientPalettePtr KEYWORD1 +TProgmemRGBPalette16 KEYWORD1 +TProgmemRGBPalette32 KEYWORD1 + +TBlendType KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +# FastLED methods +addLeds KEYWORD2 +setBrightness KEYWORD2 +getBrightness KEYWORD2 +show KEYWORD2 +clear KEYWORD2 +clearData KEYWORD2 +showColor KEYWORD2 +setTemperature KEYWORD2 +setCorrection KEYWORD2 +setDither KEYWORD2 +setMaxPowerInMilliWatts KEYWORD2 +setMaxPowerInVoltsAndMilliamps KEYWORD2 +setMaxRefreshRate KEYWORD2 +countFPS KEYWORD2 +getFPS KEYWORD2 +size KEYWORD2 + +# CLEDController Methods +showColor KEYWORD2 +showLeds KEYWORD2 + +# Noise methods +inoise16_raw KEYWORD2 +inoise8_raw KEYWORD2 +inoise16 KEYWORD2 +inoise8 KEYWORD2 +fill_2dnoise16 KEYWORD2 +fill_2dnoise8 KEYWORD2 +fill_noise16 KEYWORD2 +fill_noise8 KEYWORD2 +fill_raw_2dnoise16 KEYWORD2 +fill_raw_2dnoise16into8 KEYWORD2 +fill_raw_2dnoise8 KEYWORD2 +fill_raw_noise16into8 KEYWORD2 +fill_raw_noise8 KEYWORD2 + +# Lib8tion methods +qadd8 KEYWORD2 +qadd7 KEYWORD2 +qsub8 KEYWORD2 +add8 KEYWORD2 +sub8 KEYWORD2 +scale8 KEYWORD2 +scale8_video KEYWORD2 +cleanup_R1 KEYWORD2 +nscale8x3 KEYWORD2 +nscale8x3_video KEYWORD2 +nscale8x2 KEYWORD2 +nscale8x2_video KEYWORD2 +scale16by8 KEYWORD2 +scale16by8 KEYWORD2 +scale16 KEYWORD2 +mul8 KEYWORD2 +qmul8 KEYWORD2 +abs8 KEYWORD2 +dim8_raw KEYWORD2 +dim8_video KEYWORD2 +dim8_lin KEYWORD2 +brighten8_raw KEYWORD2 +brighten8_video KEYWORD2 +brighten8_lin KEYWORD2 +random8 KEYWORD2 +random16 KEYWORD2 +random8 KEYWORD2 +random8 KEYWORD2 +random16 KEYWORD2 +random16 KEYWORD2 +random16_set_seed KEYWORD2 +random16_get_seed KEYWORD2 +random16_add_entropy KEYWORD2 +sin16_avr KEYWORD2 +sin16 KEYWORD2 +cos16 KEYWORD2 +sin8 KEYWORD2 +cos8 KEYWORD2 +lerp8by8 KEYWORD2 +lerp16by16 KEYWORD2 +lerp16by8 KEYWORD2 +lerp15by8 KEYWORD2 +lerp15by16 KEYWORD2 +map8 KEYWORD2 +ease8InOutQuad KEYWORD2 +ease8InOutCubic KEYWORD2 +ease8InOutApprox KEYWORD2 +ease8InOutApprox KEYWORD2 +triwave8 KEYWORD2 +quadwave8 KEYWORD2 +cubicwave8 KEYWORD2 +sqrt16 KEYWORD2 +blend8 KEYWORD2 + +# Beat Generators +beat88 KEYWORD2 +beat16 KEYWORD2 +beat8 KEYWORD2 +beatsin88 KEYWORD2 +beatsin16 KEYWORD2 +beatsin8 KEYWORD2 + +# Timekeeping +seconds16 KEYWORD2 +minutes16 KEYWORD2 +hours8 KEYWORD2 +bseconds16 KEYWORD2 +EVERY_N_MILLIS KEYWORD2 +EVERY_N_MILLIS_I KEYWORD2 +EVERY_N_MILLISECONDS KEYWORD2 +EVERY_N_MILLISECONDS_I KEYWORD2 +EVERY_N_SECONDS KEYWORD2 +EVERY_N_SECONDS_I KEYWORD2 +EVERY_N_BSECONDS KEYWORD2 +EVERY_N_BSECONDS_I KEYWORD2 +EVERY_N_MINUTES KEYWORD2 +EVERY_N_MINUTES_I KEYWORD2 +EVERY_N_HOURS KEYWORD2 +EVERY_N_HOURS_I KEYWORD2 + +# Color util methods +blend KEYWORD2 +nblend KEYWORD2 +ColorFromPalette KEYWORD2 +HeatColor KEYWORD2 +UpscalePalette KEYWORD2 +blend KEYWORD2 +fadeLightBy KEYWORD2 +fadeToBlackBy KEYWORD2 +fade_raw KEYWORD2 +fade_video KEYWORD2 +fill_gradient KEYWORD2 +fill_gradient_RGB KEYWORD2 +fill_palette KEYWORD2 +fill_palette_circular KEYWORD2 +fill_rainbow KEYWORD2 +fill_rainbow_circular KEYWORD2 +fill_solid KEYWORD2 +map_data_into_colors_through_palette KEYWORD2 +nblend KEYWORD2 +nscale8 KEYWORD2 +nscale8_video KEYWORD2 + +# HSV methods +hsv2grb_rainbow KEYWORD2 +hsv2rgb_spectrum KEYWORD2 +hsv2rgb_raw KEYWORD2 +fill_solid KEYWORD2 +fill_rainbow KEYWORD2 + +# Gamma Correction +applyGamma_video KEYWORD2 +napplyGamma_video KEYWORD2 + +# Colors +CRGB::AliceBlue KEYWORD2 +CRGB::Amethyst KEYWORD2 +CRGB::AntiqueWhite KEYWORD2 +CRGB::Aqua KEYWORD2 +CRGB::Aquamarine KEYWORD2 +CRGB::Azure KEYWORD2 +CRGB::Beige KEYWORD2 +CRGB::Bisque KEYWORD2 +CRGB::Black KEYWORD2 +CRGB::BlanchedAlmond KEYWORD2 +CRGB::Blue KEYWORD2 +CRGB::BlueViolet KEYWORD2 +CRGB::Brown KEYWORD2 +CRGB::BurlyWood KEYWORD2 +CRGB::CadetBlue KEYWORD2 +CRGB::Chartreuse KEYWORD2 +CRGB::Chocolate KEYWORD2 +CRGB::Coral KEYWORD2 +CRGB::CornflowerBlue KEYWORD2 +CRGB::Cornsilk KEYWORD2 +CRGB::Crimson KEYWORD2 +CRGB::Cyan KEYWORD2 +CRGB::DarkBlue KEYWORD2 +CRGB::DarkCyan KEYWORD2 +CRGB::DarkGoldenrod KEYWORD2 +CRGB::DarkGray KEYWORD2 +CRGB::DarkGrey KEYWORD2 +CRGB::DarkGreen KEYWORD2 +CRGB::DarkKhaki KEYWORD2 +CRGB::DarkMagenta KEYWORD2 +CRGB::DarkOliveGreen KEYWORD2 +CRGB::DarkOrange KEYWORD2 +CRGB::DarkOrchid KEYWORD2 +CRGB::DarkRed KEYWORD2 +CRGB::DarkSalmon KEYWORD2 +CRGB::DarkSeaGreen KEYWORD2 +CRGB::DarkSlateBlue KEYWORD2 +CRGB::DarkSlateGray KEYWORD2 +CRGB::DarkSlateGrey KEYWORD2 +CRGB::DarkTurquoise KEYWORD2 +CRGB::DarkViolet KEYWORD2 +CRGB::DeepPink KEYWORD2 +CRGB::DeepSkyBlue KEYWORD2 +CRGB::DimGray KEYWORD2 +CRGB::DimGrey KEYWORD2 +CRGB::DodgerBlue KEYWORD2 +CRGB::FireBrick KEYWORD2 +CRGB::FloralWhite KEYWORD2 +CRGB::ForestGreen KEYWORD2 +CRGB::Fuchsia KEYWORD2 +CRGB::Gainsboro KEYWORD2 +CRGB::GhostWhite KEYWORD2 +CRGB::Gold KEYWORD2 +CRGB::Goldenrod KEYWORD2 +CRGB::Gray KEYWORD2 +CRGB::Grey KEYWORD2 +CRGB::Green KEYWORD2 +CRGB::GreenYellow KEYWORD2 +CRGB::Honeydew KEYWORD2 +CRGB::HotPink KEYWORD2 +CRGB::IndianRed KEYWORD2 +CRGB::Indigo KEYWORD2 +CRGB::Ivory KEYWORD2 +CRGB::Khaki KEYWORD2 +CRGB::Lavender KEYWORD2 +CRGB::LavenderBlush KEYWORD2 +CRGB::LawnGreen KEYWORD2 +CRGB::LemonChiffon KEYWORD2 +CRGB::LightBlue KEYWORD2 +CRGB::LightCoral KEYWORD2 +CRGB::LightCyan KEYWORD2 +CRGB::LightGoldenrodYellow KEYWORD2 +CRGB::LightGreen KEYWORD2 +CRGB::LightGrey KEYWORD2 +CRGB::LightPink KEYWORD2 +CRGB::LightSalmon KEYWORD2 +CRGB::LightSeaGreen KEYWORD2 +CRGB::LightSkyBlue KEYWORD2 +CRGB::LightSlateGray KEYWORD2 +CRGB::LightSlateGrey KEYWORD2 +CRGB::LightSteelBlue KEYWORD2 +CRGB::LightYellow KEYWORD2 +CRGB::Lime KEYWORD2 +CRGB::LimeGreen KEYWORD2 +CRGB::Linen KEYWORD2 +CRGB::Magenta KEYWORD2 +CRGB::Maroon KEYWORD2 +CRGB::MediumAquamarine KEYWORD2 +CRGB::MediumBlue KEYWORD2 +CRGB::MediumOrchid KEYWORD2 +CRGB::MediumPurple KEYWORD2 +CRGB::MediumSeaGreen KEYWORD2 +CRGB::MediumSlateBlue KEYWORD2 +CRGB::MediumSpringGreen KEYWORD2 +CRGB::MediumTurquoise KEYWORD2 +CRGB::MediumVioletRed KEYWORD2 +CRGB::MidnightBlue KEYWORD2 +CRGB::MintCream KEYWORD2 +CRGB::MistyRose KEYWORD2 +CRGB::Moccasin KEYWORD2 +CRGB::NavajoWhite KEYWORD2 +CRGB::Navy KEYWORD2 +CRGB::OldLace KEYWORD2 +CRGB::Olive KEYWORD2 +CRGB::OliveDrab KEYWORD2 +CRGB::Orange KEYWORD2 +CRGB::OrangeRed KEYWORD2 +CRGB::Orchid KEYWORD2 +CRGB::PaleGoldenrod KEYWORD2 +CRGB::PaleGreen KEYWORD2 +CRGB::PaleTurquoise KEYWORD2 +CRGB::PaleVioletRed KEYWORD2 +CRGB::PapayaWhip KEYWORD2 +CRGB::PeachPuff KEYWORD2 +CRGB::Peru KEYWORD2 +CRGB::Pink KEYWORD2 +CRGB::Plaid KEYWORD2 +CRGB::Plum KEYWORD2 +CRGB::PowderBlue KEYWORD2 +CRGB::Purple KEYWORD2 +CRGB::Red KEYWORD2 +CRGB::RosyBrown KEYWORD2 +CRGB::RoyalBlue KEYWORD2 +CRGB::SaddleBrown KEYWORD2 +CRGB::Salmon KEYWORD2 +CRGB::SandyBrown KEYWORD2 +CRGB::SeaGreen KEYWORD2 +CRGB::Seashell KEYWORD2 +CRGB::Sienna KEYWORD2 +CRGB::Silver KEYWORD2 +CRGB::SkyBlue KEYWORD2 +CRGB::SlateBlue KEYWORD2 +CRGB::SlateGray KEYWORD2 +CRGB::SlateGrey KEYWORD2 +CRGB::Snow KEYWORD2 +CRGB::SpringGreen KEYWORD2 +CRGB::SteelBlue KEYWORD2 +CRGB::Tan KEYWORD2 +CRGB::Teal KEYWORD2 +CRGB::Thistle KEYWORD2 +CRGB::Tomato KEYWORD2 +CRGB::Turquoise KEYWORD2 +CRGB::Violet KEYWORD2 +CRGB::Wheat KEYWORD2 +CRGB::White KEYWORD2 +CRGB::WhiteSmoke KEYWORD2 +CRGB::Yellow KEYWORD2 +CRGB::YellowGreen KEYWORD2 +CRGB::FairyLight KEYWORD2 +CRGB::FairyLightNCC KEYWORD2 + +# Color Palettes +DEFINE_GRADIENT_PALETTE KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +# Chipsets +APA102 LITERAL1 +APA104 LITERAL1 +APA106 LITERAL1 +DMXSERIAL LITERAL1 +DMXSIMPLE 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 +P9813 LITERAL1 +PIXIE LITERAL1 +PL9823 LITERAL1 +SK6812 LITERAL1 +SK6822 LITERAL1 +SK9822 LITERAL1 +SM16703 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 +RBG LITERAL1 +GRB LITERAL1 +GBR LITERAL1 +BRG LITERAL1 +BGR LITERAL1 + +# hue literals +HUE_RED LITERAL1 +HUE_ORANGE LITERAL1 +HUE_YELLOW LITERAL1 +HUE_GREEN LITERAL1 +HUE_AQUA LITERAL1 +HUE_BLUE LITERAL1 +HUE_PURPLE LITERAL1 +HUE_PINK LITERAL1 + +# Color correction values +TypicalSMD5050 LITERAL1 +TypicalLEDStrip LITERAL1 +Typical8mmPixel LITERAL1 +TypicalPixelString LITERAL1 +UncorrectedColor LITERAL1 +Candle LITERAL1 +Tungsten40W LITERAL1 +Tungsten100W LITERAL1 +Halogen LITERAL1 +CarbonArc LITERAL1 +HighNoonSun LITERAL1 +DirectSunlight LITERAL1 +OvercastSky LITERAL1 +ClearBlueSky LITERAL1 +WarmFluorescent LITERAL1 +StandardFluorescent LITERAL1 +CoolWhiteFluorescent LITERAL1 +FullSpectrumFluorescent LITERAL1 +GrowLightFluorescent LITERAL1 +BlackLightFluorescent LITERAL1 +MercuryVapor LITERAL1 +SodiumVapor LITERAL1 +MetalHalide LITERAL1 +HighPressureSodium LITERAL1 +UncorrectedTemperature LITERAL1 + +# Color util literals +FORWARD_HUES LITERAL1 +BACKWARD_HUES LITERAL1 +SHORTEST_HUES LITERAL1 +LONGEST_HUES LITERAL1 +LINEARBLEND LITERAL1 +NOBLEND LITERAL1 + +# Predefined Color Palettes +Rainbow_gp LITERAL1 +CloudColors_p LITERAL1 +LavaColors_p LITERAL1 +OceanColors_p LITERAL1 +ForestColors_p LITERAL1 +RainbowColors_p LITERAL1 +RainbowStripeColors_p LITERAL1 +PartyColors_p LITERAL1 +HeatColors_p LITERAL1 diff --git a/lib/FastLED/library.json b/lib/FastLED/library.json new file mode 100644 index 00000000..16d52031 --- /dev/null +++ b/lib/FastLED/library.json @@ -0,0 +1,57 @@ +{ + "name": "FastLED", + "description": "FastLED is a library for programming addressable rgb led strips (APA102/Dotstar, WS2812/Neopixel, LPD8806, and a dozen others) acting both as a driver and as a library for color management and fast math.", + "keywords": "led,noise,rgb,math,fast", + "authors": [ + { + "name": "Daniel Garcia", + "url": "https://github.com/focalintent", + "maintainer": true + }, + { + "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.6.0", + "license": "MIT", + "homepage": "http://fastled.io", + "frameworks": "arduino", + "platforms": "atmelavr, atmelsam, freescalekinetis, nordicnrf51, nxplpc, ststm32, teensy, espressif8266, espressif32, nordicnrf52", + "headers": "FastLED.h", + "export": { + "exclude": [ + "docs", + "extras" + ] + }, + "build": { + "srcFilter": [ + "+<*.c>", + "+<*.cpp>", + "+<*.h>", + "+" + ], + "libArchive": false + } +} diff --git a/lib/FastLED/library.properties b/lib/FastLED/library.properties new file mode 100644 index 00000000..b2a2b120 --- /dev/null +++ b/lib/FastLED/library.properties @@ -0,0 +1,10 @@ +name=FastLED +version=3.6.0 +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. +paragraph=Multi-platform library for controlling dozens of different types of LEDs along with optimized math, effect, and noise functions. +category=Display +url=https://github.com/FastLED/FastLED +architectures=* +includes=FastLED.h diff --git a/lib/FastLED/release_notes.md b/lib/FastLED/release_notes.md new file mode 100644 index 00000000..487cf69a --- /dev/null +++ b/lib/FastLED/release_notes.md @@ -0,0 +1,312 @@ +FastLED 3.6.0 +============= +This release incorporates valuable improvements from FastLED contributors, tested and explored by the world-wide FastLED community of artists, creators, and developers. Thank you for all of your time, energy, and help! Here are some of the most significant changes in FastLED 3.6.0: +* Greatly improved support for ESP32 and ESP8266 +* Expanded and improved board support including Teensy4, Adafruit M4 CAN Express and Grand Central M4, RP2040, ATtiny48/88, Arduino MKRZero, and various other AVR and ARM boards +* Added support for DP1903 LEDs +* Added fill_rainbow_circular and fill_palette_circular functions to draw a full rainbow or other color palette on a circular ring of LEDs +* Added a non-wrapping mode for ColorFromPalette, "LINEARBLEND_NOWRAP" +* No more "register" compiler warnings +* Bug fixes and performance improvements, including in lib8tion and noise functions +* We are expanding the FastLED team to help the library grow, evolve, and flourish +* Released May 2023, with deepest thanks to all the FastLED community members around the world! + + +FastLED 3.5.0 +============= +This release incorporates dozens of valuable improvements from FastLED contributors, tested and explored by the world-wide FastLED community of artists, creators, and developers. Thank you for all of your time, energy, and help! Here are some of the most significant changes in FastLED 3.5.0: +* Greatly improved ESP32 and ESP8266 support +* Improved board support for Teensy 4, Adafruit MatrixPortal M4, Arduino Nano Every, Particle Photon, and Seeed Wio Terminal +* Improved and/or sped up: sin8, cos8, blend8, blur2d, scale8, Perlin/simplex noise +* Improved HSV colors are smoother, richer, and brighter in fill_rainbow and elsewhere +* Modernized and cleaned up the FastLED examples +* Added github CI integration to help with automated testing +* Added a Code of Conduct from https://www.contributor-covenant.org/ +* Released January 2022, with many thanks to FastLED contributors and the FastLED community! + + +FastLED 3.4.0 +============= + +* Improved reliability on ESP32 when wifi is active +* Merged in contributed support for Adafruit boards: QT Py SAMD21, Circuit Playground Express, Circuit Playground Bluefruit, and ItsyBitsy nRF52840 Express +* Merged in contributed support for SparkFun Artemis boards +* Merged in contributed support for Arduino Nano Every / Arduino Uno Wifi Rev. 2 +* Merged in contributed support for Seeedstudio Odyssey and XIAO boards +* Merged in contributed support for AVR chips ATmega1284, ATmega4809, and LGT8F328 +* XYMatrix example now supports 90-degree rotated orientation +* Moved source code files into "src" subdirectory +* Many small code cleanups and bug fixes +* Released December 2020, with many thanks to everyone contributing to FastLED! + +We also want to note here that in 2020, Github named FastLED one of the 'Greatest Hits' of Open Source software, and preserved an archived copy of FastLED in the Arctic Code Vault, the Bodleian Library at Oxford University, the Bibliotheca Alexandrina (the Library of Alexandria), and the Stanford University Libraries. https://archiveprogram.github.com/greatest-hits/ + + + +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 +============= + +* typo fix + +FastLED 3.2.5 +============= + +* Fix for SAMD51 based boards (a SAMD21 optimization broke the D51 builds, now D51 is a separate platform) + +FastLED 3.2.4 +============= + +* fix builds for WAV boards + +FastLED 3.2.2 +============= + +* Perf tweak for SAMD21 +* LPD6803 support +* Add atmega328pb support +* Variety of minor bug/correctness/typo fixes +* Added SM16703, GE8822, GS1903 + +FastLED 3.2.1 +============= +* ATmega644P support +* Adafruit Hallowwing (Thanks to Lady Ada) +* Improved STM 32 support +* Some user contributed cleanups +* ESP32 APA102 output fix + +FastLED3.2 +========== +* ESP32 support with improved output and parallel output options (thanks Sam Guyer!) +* 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) +* Added support for ATmega328 non-picopower hardware pins (Thanks to John Whittington) +* Fixes for ESP32 support (Thanks to Daniel Tullemans) +* 'Makefile' compilation fix (Thanks to Nico Hood) + +FastLED 3.1.7 (skipped) +======================= + +FastLED 3.1.6 +============= +* Preliminary support for esp32 +* Variety of random bug fixes +* 6-channel parallel output for the esp8266 +* Race condition fixes for teensy hardware SPI +* Preliminary teensy 3.6 support +* Various fixes falling out from "fixing" scale 8 adjustments +* Add gemma m0 support (thanks @ladyada!) + +FastLED 3.1.5 +============= +* Fix due parallel output build issue + +FastLED 3.1.4 +============= +* fix digispark avr build issue + +FastLED3.1.3 +=============== + +* Add SK6822 timings +* 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 +* Add wino board support +* Fix scale8 (so now, scale8(255,255) == 255, not 254!) +* Add ESP8266 parallel output support + + +FastLED3.1.1 +============ +* Enabled RFDuino/nrf51822 hardware SPI support +* 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 + * https://github.com/FastLED/FastLED/wiki/RGBSet-Reference + + +FastLED3.1.0 +============ +* Added support for the following platforms + * Arduino Zero + * Teensy LC + * RFDuino/nrf51822 + * Spark Core +* Major internal code reoganization +* Started doxygen based documentation +* Lots of bug/performance fixes +* Parallel output on various arm platforms +* lots of new stuff + +FastLED3.0.2 +============ +* possibly fix issues #67 and #90 by fixing gcc 4.8.x support + +FastLED3.0.1 +============ +* fix issue #89 w/power management pin always being on + +FastLED3.0 +========== + +* Added support for the following platforms: + * Arduino due + * Teensy 3.1 +* Added the following LED chipsets: + * USC1903_400 + * GW6205 / GW6205_400 + * APA102 + * APA104 + * LPD1886 + * P9813 + * SmartMatrix +* Added multiple examples: + * ColorPalette - show off the color palette code + * ColorTemperature - show off the color correction code + * Fire2012 + * Fire2012WithPalette + * Multiple led controller examples + * Noise + * NoisePlayground + * NoisePlusPalette + * SmartMatrix - show off SmartMatrix support + * XYMatrix - show how to use a mtrix layout of leds +* Added color correction +* Added dithering +* Added power management support +* Added support for color palettes +* Added easing functions +* Added fast trig functions +* Added simplex noise functions +* Added color utility functions +* Fixed DMXSERIAL/DMXSIMPLE support +* Timing adjustments for existing SPI chipsets +* Cleaned up the code layout to make platform support easier +* Many bug fixes +* A number of performance/memory improvements +* Remove Squant (takes up space!) + +FastLED2 +======== + +## Full release of the library + +## Release Candidate 6 +* Rename library, offically, to FastLED, move to github +* Update keywords with all the new stuffs + +## Release Candidate 5 +* Gemma and Trinket: supported except for global "setBrightness" + +## Release Candidate 4 +* Added NEOPIXEL as a synonym for WS2811 +* Fix WS2811/WS2812B timings, bring it in line to exactly 1.25ns/bit. +* Fix handling of constant color definitions (damn you, gcc!) + +## Release Candidate 3 +* Fixed bug when Clock and Data were on the same port +* Added ability to set pixel color directly from HSV +* Added ability to retrieve current random16 seed + +## Release Candidate 2 +* mostly bug fixes +* Fix SPI macro definitions for latest teensy3 software update +* Teensy 2 compilation fix +* hsv2rgb_rainbow performance fix + +## Release Candidate 1 +* New unified/simplified API for adding/using controllers +* fleshout clockless chip support +* add hsv (spectrum and rainbow style colors) +* 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 +* bug fixes for SM16716 +* performance improvements, lpd8806 exceeds 22Mbit now +* hardware def fixes +* allow alternate rgb color orderings +* high speed math methods +* rich CRGB structure + +## Preview 3 +* True hardware SPI support for teensy (up to 20Mbit output!) +* Minor bug fixes/tweaks + +## Preview 2 +* Rename pin class to FastPin +* Replace latch with select, more accurate description of what it does +* Enforce intra-frame timing for ws2801s +* SM16716 support +* Add #define FAST_SPI_INTERRUPTS_WRITE_PINS to make sure world is ok w/interrupts and SPI +* Add #define FASTLED_FORCE_SOFTWARE_SPI for those times when you absolutely don't want to use hardware SPI, ev +en if you're using the hardware SPI pins +* Add pin definitions for the arduino megas - should fix ws2811 support +* Add pin definitions for the leonardo - should fix spi support and pin mappings +* Add warnings when pin definitions are missing +* Added google+ community for fastspi users - https://plus.google.com/communities/109127054924227823508 +# Add pin definitions for Teensy++ 2.0 + + +## Preview 1 +* Initial release diff --git a/lib/FastLED/src/FastLED.cpp b/lib/FastLED/src/FastLED.cpp new file mode 100644 index 00000000..19dd2411 --- /dev/null +++ b/lib/FastLED/src/FastLED.cpp @@ -0,0 +1,293 @@ +#define FASTLED_INTERNAL +#include "FastLED.h" + +/// @file FastLED.cpp +/// Central source file for FastLED, implements the CFastLED class/object + +#if defined(__SAM3X8E__) +volatile uint32_t fuckit; +#endif + +FASTLED_NAMESPACE_BEGIN + +/// Pointer to the matrix object when using the Smart Matrix Library +/// @see https://github.com/pixelmatix/SmartMatrix +void *pSmartMatrix = NULL; + +CFastLED FastLED; + +CLEDController *CLEDController::m_pHead = NULL; +CLEDController *CLEDController::m_pTail = NULL; +static uint32_t lastshow = 0; + +/// Global frame counter, used for debugging ESP implementations +/// @todo Include in FASTLED_DEBUG_COUNT_FRAME_RETRIES block? +uint32_t _frame_cnt=0; + +/// Global frame retry counter, used for debugging ESP implementations +/// @todo Include in FASTLED_DEBUG_COUNT_FRAME_RETRIES block? +uint32_t _retry_cnt=0; + +// uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28); + +CFastLED::CFastLED() { + // clear out the array of led controllers + // m_nControllers = 0; + m_Scale = 255; + m_nFPS = 0; + m_pPowerFunc = NULL; + m_nPowerData = 0xFFFFFFFF; +} + +CLEDController &CFastLED::addLeds(CLEDController *pLed, + struct CRGB *data, + int nLedsOrOffset, int nLedsIfOffset) { + int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0; + int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset; + + pLed->init(); + pLed->setLeds(data + nOffset, nLeds); + FastLED.setMaxRefreshRate(pLed->getMaxRefreshRate(),true); + return *pLed; +} + +void CFastLED::show(uint8_t scale) { + // guard against showing too rapidly + while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros)); + lastshow = micros(); + + // If we have a function for computing power, use it! + if(m_pPowerFunc) { + scale = (*m_pPowerFunc)(scale, m_nPowerData); + } + + CLEDController *pCur = CLEDController::head(); + while(pCur) { + uint8_t d = pCur->getDither(); + if(m_nFPS < 100) { pCur->setDither(0); } + pCur->showLeds(scale); + pCur->setDither(d); + pCur = pCur->next(); + } + countFPS(); +} + +int CFastLED::count() { + int x = 0; + CLEDController *pCur = CLEDController::head(); + while( pCur) { + ++x; + pCur = pCur->next(); + } + return x; +} + +CLEDController & CFastLED::operator[](int x) { + CLEDController *pCur = CLEDController::head(); + while(x-- && pCur) { + pCur = pCur->next(); + } + if(pCur == NULL) { + return *(CLEDController::head()); + } else { + return *pCur; + } +} + +void CFastLED::showColor(const struct CRGB & color, uint8_t scale) { + while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros)); + lastshow = micros(); + + // If we have a function for computing power, use it! + if(m_pPowerFunc) { + scale = (*m_pPowerFunc)(scale, m_nPowerData); + } + + CLEDController *pCur = CLEDController::head(); + while(pCur) { + uint8_t d = pCur->getDither(); + if(m_nFPS < 100) { pCur->setDither(0); } + pCur->showColor(color, scale); + pCur->setDither(d); + pCur = pCur->next(); + } + countFPS(); +} + +void CFastLED::clear(bool writeData) { + if(writeData) { + showColor(CRGB(0,0,0), 0); + } + clearData(); +} + +void CFastLED::clearData() { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->clearLedData(); + pCur = pCur->next(); + } +} + +void CFastLED::delay(unsigned long ms) { + unsigned long start = millis(); + do { +#ifndef FASTLED_ACCURATE_CLOCK + // make sure to allow at least one ms to pass to ensure the clock moves + // forward + ::delay(1); +#endif + show(); + yield(); + } + while((millis()-start) < ms); +} + +void CFastLED::setTemperature(const struct CRGB & temp) { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->setTemperature(temp); + pCur = pCur->next(); + } +} + +void CFastLED::setCorrection(const struct CRGB & correction) { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->setCorrection(correction); + pCur = pCur->next(); + } +} + +void CFastLED::setDither(uint8_t ditherMode) { + CLEDController *pCur = CLEDController::head(); + while(pCur) { + pCur->setDither(ditherMode); + pCur = pCur->next(); + } +} + +// +// template void transpose8(unsigned char A[8], unsigned char B[8]) { +// uint32_t x, y, t; +// +// // Load the array and pack it into x and y. +// y = *(unsigned int*)(A); +// x = *(unsigned int*)(A+4); +// +// // 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]; +// + // // 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; +// +// B[7*n] = y; y >>= 8; +// B[6*n] = y; y >>= 8; +// B[5*n] = y; y >>= 8; +// B[4*n] = y; +// +// B[3*n] = x; x >>= 8; +// B[2*n] = x; x >>= 8; +// B[n] = x; x >>= 8; +// B[0] = x; +// // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0; +// // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0; +// } +// +// void transposeLines(Lines & out, Lines & in) { +// transpose8<1,2>(in.bytes, out.bytes); +// transpose8<1,2>(in.bytes + 8, out.bytes + 1); +// } + + +/// Unused value +/// @todo Remove? +extern int noise_min; + +/// Unused value +/// @todo Remove? +extern int noise_max; + +void CFastLED::countFPS(int nFrames) { + static int br = 0; + static uint32_t lastframe = 0; // millis(); + + 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(); + } +} + +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; + } +} + +/// Called at program exit when run in a desktop environment. +/// Extra C definition that some environments may need. +/// @returns 0 to indicate success +extern "C" int atexit(void (* /*func*/ )()) { return 0; } + +#ifdef FASTLED_NEEDS_YIELD +extern "C" void yield(void) { } +#endif + +#ifdef NEED_CXX_BITS +namespace __cxxabiv1 +{ + #if !defined(ESP8266) && !defined(ESP32) + extern "C" void __cxa_pure_virtual (void) {} + #endif + + /* guard variables */ + + /* The ABI requires a 64-bit type. */ + __extension__ typedef int __guard __attribute__((mode(__DI__))); + + extern "C" int __cxa_guard_acquire (__guard *) __attribute__((weak)); + extern "C" void __cxa_guard_release (__guard *) __attribute__((weak)); + extern "C" void __cxa_guard_abort (__guard *) __attribute__((weak)); + + extern "C" int __cxa_guard_acquire (__guard *g) + { + return !*(char *)(g); + } + + extern "C" void __cxa_guard_release (__guard *g) + { + *(char *)g = 1; + } + + extern "C" void __cxa_guard_abort (__guard *) + { + + } +} +#endif + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/FastLED.h b/lib/FastLED/src/FastLED.h new file mode 100644 index 00000000..58ffa76b --- /dev/null +++ b/lib/FastLED/src/FastLED.h @@ -0,0 +1,673 @@ +#ifndef __INC_FASTSPI_LED2_H +#define __INC_FASTSPI_LED2_H + +/// @file FastLED.h +/// central include file for FastLED, defines the CFastLED class/object + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) +#define FASTLED_HAS_PRAGMA_MESSAGE +#endif + +/// Current FastLED version number, as an integer. +/// E.g. 3006000 for version "3.6.0", with: +/// * 1 digit for the major version +/// * 3 digits for the minor version +/// * 3 digits for the patch version +#define FASTLED_VERSION 3006000 +#ifndef FASTLED_INTERNAL +# ifdef FASTLED_SHOW_VERSION +# ifdef FASTLED_HAS_PRAGMA_MESSAGE +# pragma message "FastLED version 3.006.000" +# else +# warning FastLED version 3.006.000 (Not really a warning, just telling you here.) +# endif +# endif +#endif + +#ifndef __PROG_TYPES_COMPAT__ +/// avr-libc define to expose __progmem__ typedefs. +/// @note These typedefs are now deprecated! +/// @see https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html +#define __PROG_TYPES_COMPAT__ +#endif + +#ifdef SmartMatrix_h +#include +#endif + +#ifdef DmxSimple_h +#include +#endif + +#ifdef DmxSerial_h +#include +#endif + +#include + +#include "cpp_compat.h" + +#include "fastled_config.h" +#include "led_sysdefs.h" + +// Utility functions +#include "fastled_delay.h" +#include "bitswap.h" + +#include "controller.h" +#include "fastpin.h" +#include "fastspi_types.h" +#include "dmx.h" + +#include "platforms.h" +#include "fastled_progmem.h" + +#include "lib8tion.h" +#include "pixeltypes.h" +#include "hsv2rgb.h" +#include "colorutils.h" +#include "pixelset.h" +#include "colorpalettes.h" + +#include "noise.h" +#include "power_mgt.h" + +#include "fastspi.h" +#include "chipsets.h" + +FASTLED_NAMESPACE_BEGIN + +/// LED chipsets with SPI interface +enum ESPIChipsets { + LPD6803, ///< LPD6803 LED chipset + LPD8806, ///< LPD8806 LED chipset + WS2801, ///< WS2801 LED chipset + WS2803, ///< WS2803 LED chipset + SM16716, ///< SM16716 LED chipset + P9813, ///< P9813 LED chipset + APA102, ///< APA102 LED chipset + SK9822, ///< SK9822 LED chipset + DOTSTAR ///< APA102 LED chipset alias +}; + +/// Smart Matrix Library controller type +/// @see https://github.com/pixelmatix/SmartMatrix +enum ESM { SMART_MATRIX }; + +/// Octo WS2811 LED Library controller types +/// @see https://www.pjrc.com/teensy/td_libs_OctoWS2811.html +/// @see https://github.com/PaulStoffregen/OctoWS2811 +enum OWS2811 { OCTOWS2811,OCTOWS2811_400, OCTOWS2813}; + +/// WS2812Serial Library controller type +/// @see https://www.pjrc.com/non-blocking-ws2812-led-library/ +/// @see https://github.com/PaulStoffregen/WS2812Serial +enum SWS2812 { WS2812SERIAL }; + +#ifdef HAS_PIXIE +template class PIXIE : public PixieController {}; +#endif + +#ifdef FASTLED_HAS_CLOCKLESS +/// @addtogroup Chipsets +/// @{ +/// @addtogroup ClocklessChipsets +/// @{ + +/// LED controller for WS2812 LEDs with GRB color order +/// @see WS2812Controller800Khz +template class NEOPIXEL : public WS2812Controller800Khz {}; +template class SM16703 : public SM16703Controller {}; ///< @copydoc SM16703Controller +template class TM1829 : public TM1829Controller800Khz {}; ///< @copydoc TM1829Controller800Khz +template class TM1812 : public TM1809Controller800Khz {}; ///< TM1812 controller class. @copydetails TM1809Controller800Khz +template class TM1809 : public TM1809Controller800Khz {}; ///< @copydoc TM1809Controller800Khz +template class TM1804 : public TM1809Controller800Khz {}; ///< TM1804 controller class. @copydetails TM1809Controller800Khz +template class TM1803 : public TM1803Controller400Khz {}; ///< @copydoc TM1803Controller400Khz +template class TM1934 : public TM1934Controller {}; ///< @copydoc TM1934Controller800Khz +template class UCS1903 : public UCS1903Controller400Khz {}; ///< @copydoc UCS1903Controller400Khz +template class UCS1903B : public UCS1903BController800Khz {}; ///< @copydoc UCS1903BController800Khz +template class UCS1904 : public UCS1904Controller800Khz {}; ///< @copydoc UCS1904Controller800Khz +template class UCS2903 : public UCS2903Controller {}; ///< @copydoc UCS2903Controller +template class WS2812 : public WS2812Controller800Khz {}; ///< @copydoc WS2812Controller800Khz +template class WS2852 : public WS2812Controller800Khz {}; ///< WS2852 controller class. @copydetails WS2812Controller800Khz +template class WS2812B : public WS2812Controller800Khz {}; ///< WS2812B controller class. @copydetails WS2812Controller800Khz +template class GS1903 : public WS2812Controller800Khz {}; ///< GS1903 controller class. @copydetails WS2812Controller800Khz +template class SK6812 : public SK6812Controller {}; ///< @copydoc SK6812Controller +template class SK6822 : public SK6822Controller {}; ///< SK6822 controller class. @copydetails SK6822Controller +template class APA106 : public SK6822Controller {}; ///< APA106 controller class. @copydetails SK6822Controller +template class PL9823 : public PL9823Controller {}; ///< @copydoc PL9823Controller +template class WS2811 : public WS2811Controller800Khz {}; ///< @copydoc WS2811Controller800Khz +template class WS2813 : public WS2813Controller {}; ///< @copydoc WS2813Controller +template class APA104 : public WS2811Controller800Khz {}; ///< APA104 controller class. @copydetails WS2811Controller800Khz +template class WS2811_400 : public WS2811Controller400Khz {}; ///< @copydoc WS2811Controller400Khz +template class GE8822 : public GE8822Controller800Khz {}; ///< @copydoc GE8822Controller800Khz +template class GW6205 : public GW6205Controller800Khz {}; ///< @copydoc GW6205Controller800Khz +template class GW6205_400 : public GW6205Controller400Khz {}; ///< @copydoc GW6205Controller400Khz +template class LPD1886 : public LPD1886Controller1250Khz {}; ///< @copydoc LPD1886Controller1250Khz +template class LPD1886_8BIT : public LPD1886Controller1250Khz_8bit {}; ///< @copydoc LPD1886Controller1250Khz_8bit +#if defined(DmxSimple_h) || defined(FASTLED_DOXYGEN) +/// @copydoc DMXSimpleController +template class DMXSIMPLE : public DMXSimpleController {}; +#endif +#if defined(DmxSerial_h) || defined(FASTLED_DOXYGEN) +/// @copydoc DMXSerialController +template class DMXSERIAL : public DMXSerialController {}; +#endif +#endif +/// @} ClocklessChipsets +/// @} Chipsets + + +/// Blockless output port enum +enum EBlockChipsets { +#ifdef PORTA_FIRST_PIN + WS2811_PORTA, + WS2813_PORTA, + WS2811_400_PORTA, + TM1803_PORTA, + UCS1903_PORTA, +#endif +#ifdef PORTB_FIRST_PIN + WS2811_PORTB, + WS2813_PORTB, + WS2811_400_PORTB, + TM1803_PORTB, + UCS1903_PORTB, +#endif +#ifdef PORTC_FIRST_PIN + WS2811_PORTC, + WS2813_PORTC, + WS2811_400_PORTC, + TM1803_PORTC, + UCS1903_PORTC, +#endif +#ifdef PORTD_FIRST_PIN + WS2811_PORTD, + WS2813_PORTD, + WS2811_400_PORTD, + TM1803_PORTD, + UCS1903_PORTD, +#endif +#ifdef HAS_PORTDC + WS2811_PORTDC, + WS2813_PORTDC, + WS2811_400_PORTDC, + TM1803_PORTDC, + UCS1903_PORTDC, +#endif +}; + +#if defined(LIB8_ATTINY) +#define NUM_CONTROLLERS 2 +#else +/// Unknown NUM_CONTROLLERS definition. Unused elsewhere in the library? +/// @todo Remove? +#define NUM_CONTROLLERS 8 +#endif + +/// Typedef for a power consumption calculation function. Used within +/// CFastLED for rescaling brightness before sending the LED data to +/// the strip with CFastLED::show(). +/// @param scale the initial brightness scale value +/// @param data max power data, in milliwatts +/// @returns the brightness scale, limited to max power +typedef uint8_t (*power_func)(uint8_t scale, uint32_t data); + +/// High level controller interface for FastLED. +/// This class manages controllers, global settings, and trackings such as brightness +/// and refresh rates, and provides access functions for driving led data to controllers +/// via the show() / showColor() / clear() methods. +/// This is instantiated as a global object with the name FastLED. +/// @nosubgrouping +class CFastLED { + // int m_nControllers; + uint8_t m_Scale; ///< the current global brightness scale setting + uint16_t m_nFPS; ///< tracking for current frames per second (FPS) value + uint32_t m_nMinMicros; ///< minimum µs between frames, used for capping frame rates + uint32_t m_nPowerData; ///< max power use parameter + power_func m_pPowerFunc; ///< function for overriding brightness when using FastLED.show(); + +public: + CFastLED(); + + + /// Add a CLEDController instance to the world. Exposed to the public to allow people to implement their own + /// CLEDController objects or instances. There are two ways to call this method (as well as the other addLeds() + /// variations). The first is with 3 arguments, in which case the arguments are the controller, a pointer to + /// led data, and the number of leds used by this controller. The second is with 4 arguments, in which case + /// the first two arguments are the same, the third argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the fourth argument is the number of leds for this controller object. + /// @param pLed the led controller being added + /// @param data base pointer to an array of CRGB data structures + /// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset number of leds (4 argument version) + /// @returns a reference to the added controller + static CLEDController &addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0); + + /// @name Adding SPI-based controllers + /// Add an SPI based CLEDController instance to the world. + /// + /// There are two ways to call this method (as well as the other addLeds() + /// variations). The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. + /// + /// @param data base pointer to an array of CRGB data structures + /// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset number of leds (4 argument version) + /// @tparam CHIPSET the chipset type + /// @tparam DATA_PIN the optional data pin for the leds (if omitted, will default to the first hardware SPI MOSI pin) + /// @tparam CLOCK_PIN the optional clock pin for the leds (if omitted, will default to the first hardware SPI clock pin) + /// @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 + /// @{ + + /// Add an SPI based CLEDController instance to the world. + 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); } + case WS2801: { static WS2801Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2803: { static WS2803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SM16716: { static SM16716Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case P9813: { static P9813Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case DOTSTAR: + case APA102: { static APA102Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SK9822: { static SK9822Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + } + } + + /// Add an SPI based CLEDController instance to the world. + template static 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); } + case WS2801: { static WS2801Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2803: { static WS2803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SM16716: { static SM16716Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case P9813: { static P9813Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case DOTSTAR: + case APA102: { static APA102Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SK9822: { static SK9822Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + } + } + + /// Add an SPI based CLEDController instance to the world. + template static 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); } + case WS2801: { static WS2801Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case WS2803: { static WS2803Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SM16716: { static SM16716Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case P9813: { static P9813Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case DOTSTAR: + case APA102: { static APA102Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + case SK9822: { static SK9822Controller c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); } + } + } + +#ifdef SPI_DATA + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data, nLedsOrOffset, nLedsIfOffset); + } + + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data, nLedsOrOffset, nLedsIfOffset); + } + + template static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data, nLedsOrOffset, nLedsIfOffset); + } + +#endif + /// @} Adding SPI based controllers + +#ifdef FASTLED_HAS_CLOCKLESS + /// @name Adding 3-wire led controllers + /// Add a clockless (aka 3-wire, also DMX) based CLEDController instance to the world. + /// + /// There are two ways to call this method (as well as the other addLeds() + /// variations). The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. + /// + /// This method also takes 2 to 3 template parameters for identifying the specific chipset, data pin, + /// RGB ordering, and SPI data rate + /// + /// @param data base pointer to an array of CRGB data structures + /// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset number of leds (4 argument version) + /// @tparam CHIPSET the chipset type (required) + /// @tparam DATA_PIN the data pin for the leds (required) + /// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @returns a reference to the added controller + /// @{ + + /// Add a clockless based CLEDController instance to the world. + template class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + /// Add a clockless based CLEDController instance to the world. + template class CHIPSET, uint8_t DATA_PIN> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + /// Add a clockless based CLEDController instance to the world. + template class CHIPSET, uint8_t DATA_PIN> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + 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) + { + switch(CHIPSET) { + case DMX: { static DMXController controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } + } + } + #endif + /// @} Adding 3-wire led controllers +#endif + + /// @name Adding 3rd party library controllers + /// Add a 3rd party library based CLEDController instance to the world. + /// + /// There are two ways to call this method (as well as the other addLeds() + /// variations). The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. This class includes the SmartMatrix + /// and OctoWS2811 based controllers + /// + /// This method also takes 1 to 2 template parameters for identifying the specific chipset and + /// RGB ordering. + /// + /// @param data base pointer to an array of CRGB data structures + /// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset number of leds (4 argument version) + /// @tparam CHIPSET the chipset type (required) + /// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @returns a reference to the added controller + /// @{ + + /// Add a 3rd party library based CLEDController instance to the world. + template class CHIPSET, EOrder RGB_ORDER> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + + /// Add a 3rd party library based CLEDController instance to the world. + template class CHIPSET> + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + static CHIPSET c; + return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); + } + +#ifdef USE_OCTOWS2811 + /// Add a OCTOWS2811 based CLEDController instance to the world. + /// @see https://www.pjrc.com/teensy/td_libs_OctoWS2811.html + /// @see https://github.com/PaulStoffregen/OctoWS2811 + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + switch(CHIPSET) { + case OCTOWS2811: { static COctoWS2811Controller controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } + case OCTOWS2811_400: { static COctoWS2811Controller controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } +#ifdef WS2813_800kHz + case OCTOWS2813: { static COctoWS2811Controller controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } +#endif + } + } + + /// Add a OCTOWS2811 library based CLEDController instance to the world. + /// @see https://www.pjrc.com/teensy/td_libs_OctoWS2811.html + /// @see https://github.com/PaulStoffregen/OctoWS2811 + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + return addLeds(data,nLedsOrOffset,nLedsIfOffset); + } + +#endif + +#ifdef USE_WS2812SERIAL + /// Add a WS2812Serial library based CLEDController instance to the world. + /// @see https://www.pjrc.com/non-blocking-ws2812-led-library/ + /// @see https://github.com/PaulStoffregen/WS2812Serial + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + static CWS2812SerialController controller; + return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); + } +#endif + +#ifdef SmartMatrix_h + /// Add a SmartMatrix library based CLEDController instance to the world. + /// @see https://github.com/pixelmatix/SmartMatrix + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) + { + switch(CHIPSET) { + case SMART_MATRIX: { static CSmartMatrixController controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); } + } + } +#endif + /// @} Adding 3rd party library controllers + + +#ifdef FASTLED_HAS_BLOCKLESS + + /// @name Adding parallel output controllers + /// Add a block based CLEDController instance to the world. + /// + /// There are two ways to call this method (as well as the other addLeds() + /// variations). The first is with 2 arguments, in which case the arguments are a pointer to + /// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case + /// the first argument is the same, the second argument is an offset into the CRGB data where this controller's + /// CRGB data begins, and the third argument is the number of leds for this controller object. + /// + /// This method also takes a 2 to 3 template parameters for identifying the specific chipset and rgb ordering + /// RGB ordering, and SPI data rate + /// + /// @param data base pointer to an array of CRGB data structures + /// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array + /// @param nLedsIfOffset number of leds (4 argument version) + /// @tparam CHIPSET the chipset/port type (required) + /// @tparam NUM_LANES how many parallel lanes of output to write + /// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in) + /// @returns a reference to the added controller + /// @{ + + /// Add a block based parallel output CLEDController instance to the world. + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + switch(CHIPSET) { + #ifdef PORTA_FIRST_PIN + case WS2811_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTA: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef PORTB_FIRST_PIN + case WS2811_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTB: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef PORTC_FIRST_PIN + case WS2811_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTC: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef PORTD_FIRST_PIN + case WS2811_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTD: return addLeds(new InlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + #ifdef HAS_PORTDC + case WS2811_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2811_400_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case WS2813_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case TM1803_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + case UCS1903_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController(), data, nLedsOrOffset, nLedsIfOffset); + #endif + } + } + + /// Add a block based parallel output CLEDController instance to the world. + template + static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) { + return addLeds(data,nLedsOrOffset,nLedsIfOffset); + } + /// @} Adding parallel output controllers +#endif + + /// Set the global brightness scaling + /// @param scale a 0-255 value for how much to scale all leds before writing them out + void setBrightness(uint8_t scale) { m_Scale = scale; } + + /// Get the current global brightness setting + /// @returns the current global brightness value + uint8_t getBrightness() { return m_Scale; } + + /// Set the maximum power to be used, given in volts and milliamps. + /// @param volts how many volts the leds are being driven at (usually 5) + /// @param milliamps the maximum milliamps of power draw you want + inline void setMaxPowerInVoltsAndMilliamps(uint8_t volts, uint32_t milliamps) { setMaxPowerInMilliWatts(volts * milliamps); } + + /// Set the maximum power to be used, given in milliwatts + /// @param milliwatts the max power draw desired, in milliwatts + inline void setMaxPowerInMilliWatts(uint32_t milliwatts) { m_pPowerFunc = &calculate_max_brightness_for_power_mW; m_nPowerData = milliwatts; } + + /// Update all our controllers with the current led colors, using the passed in brightness + /// @param scale the brightness value to use in place of the stored value + void show(uint8_t scale); + + /// Update all our controllers with the current led colors + void show() { show(m_Scale); } + + /// Clear the leds, wiping the local array of data. Optionally you can also + /// send the cleared data to the LEDs. + /// @param writeData whether or not to write out to the leds as well + void clear(bool writeData = false); + + /// Clear out the local data array + void clearData(); + + /// Set all leds on all controllers to the given color/scale. + /// @param color what color to set the leds to + /// @param scale what brightness scale to show at + void showColor(const struct CRGB & color, uint8_t scale); + + /// Set all leds on all controllers to the given color + /// @param color what color to set the leds to + void showColor(const struct CRGB & color) { showColor(color, m_Scale); } + + /// Delay for the given number of milliseconds. Provided to allow the library to be used on platforms + /// that don't have a delay function (to allow code to be more portable). + /// @note This will call show() constantly to drive the dithering engine (and will call show() at least once). + /// @param ms the number of milliseconds to pause for + void delay(unsigned long ms); + + /// Set a global color temperature. Sets the color temperature for all added led strips, + /// overriding whatever previous color temperature those controllers may have had. + /// @param temp A CRGB structure describing the color temperature + void setTemperature(const struct CRGB & temp); + + /// Set a global color correction. Sets the color correction for all added led strips, + /// overriding whatever previous color correction those controllers may have had. + /// @param correction A CRGB structure describin the color correction. + void setCorrection(const struct CRGB & correction); + + /// Set the dithering mode. Sets the dithering mode for all added led strips, overriding + /// whatever previous dithering option those controllers may have had. + /// @param ditherMode what type of dithering to use, either BINARY_DITHER or DISABLE_DITHER + void setDither(uint8_t ditherMode = BINARY_DITHER); + + /// Set the maximum refresh rate. This is global for all leds. Attempts to + /// call show() faster than this rate will simply wait. + /// @note The refresh rate defaults to the slowest refresh rate of all the leds added through addLeds(). + /// If you wish to set/override this rate, be sure to call setMaxRefreshRate() _after_ + /// adding all of your leds. + /// @param refresh maximum refresh rate in hz + /// @param constrain constrain refresh rate to the slowest speed yet set + void setMaxRefreshRate(uint16_t refresh, bool constrain=false); + + /// For debugging, this will keep track of time between calls to countFPS(). Every + /// `nFrames` calls, it will update an internal counter for the current FPS. + /// @todo Make this a rolling counter + /// @param nFrames how many frames to time for determining FPS + void countFPS(int nFrames=25); + + /// Get the number of frames/second being written out + /// @returns the most recently computed FPS value + 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() + int count(); + + /// Get a reference to a registered 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 + 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 + CRGB *leds() { return (*this)[0].leds(); } +}; + +/// Alias of the FastLED instance for legacy purposes +#define FastSPI_LED FastLED +/// Alias of the FastLED instance for legacy purposes +#define FastSPI_LED2 FastLED +#ifndef LEDS +/// Alias of the FastLED instance for legacy purposes +#define LEDS FastLED +#endif + +/// Global LED strip management instance +extern CFastLED FastLED; + +/// If no pin/port mappings are found, sends a warning message to the user +/// during compilation. +/// @see fastpin.h +#ifndef HAS_HARDWARE_PIN_SUPPORT +#warning "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info." +#define NO_HARDWARE_PIN_SUPPORT +#endif + + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/bitswap.cpp b/lib/FastLED/src/bitswap.cpp new file mode 100644 index 00000000..1e3b83db --- /dev/null +++ b/lib/FastLED/src/bitswap.cpp @@ -0,0 +1,31 @@ +/// @file bitswap.cpp +/// Functions for doing a rotation of bits/bytes used by parallel output + +/// Disables pragma messages and warnings +#define FASTLED_INTERNAL + +#include "FastLED.h" + +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/lib/FastLED/src/bitswap.h b/lib/FastLED/src/bitswap.h new file mode 100644 index 00000000..dbded929 --- /dev/null +++ b/lib/FastLED/src/bitswap.h @@ -0,0 +1,292 @@ +#ifndef __INC_BITSWAP_H +#define __INC_BITSWAP_H + +#include "FastLED.h" + +/// @file bitswap.h +/// Functions for doing a rotation of bits/bytes used by parallel output + +FASTLED_NAMESPACE_BEGIN + + +#if defined(FASTLED_ARM) || defined(FASTLED_ESP8266) || defined(FASTLED_DOXYGEN) +/// Structure representing 8 bits of access +typedef union { + uint8_t raw; ///< the entire byte + struct { + uint32_t a0:1; ///< bit 0 (0x01) + uint32_t a1:1; ///< bit 1 (0x02) + uint32_t a2:1; ///< bit 2 (0x04) + uint32_t a3:1; ///< bit 3 (0x08) + uint32_t a4:1; ///< bit 4 (0x10) + uint32_t a5:1; ///< bit 5 (0x20) + uint32_t a6:1; ///< bit 6 (0x40) + uint32_t a7:1; ///< bit 7 (0x80) + }; +} just8bits; + +/// Structure representing 32 bits of access +typedef struct { + uint32_t a0:1; ///< byte 'a', bit 0 (0x00000000) + uint32_t a1:1; ///< byte 'a', bit 1 (0x00000002) + uint32_t a2:1; ///< byte 'a', bit 2 (0x00000004) + uint32_t a3:1; ///< byte 'a', bit 3 (0x00000008) + uint32_t a4:1; ///< byte 'a', bit 4 (0x00000010) + uint32_t a5:1; ///< byte 'a', bit 5 (0x00000020) + uint32_t a6:1; ///< byte 'a', bit 6 (0x00000040) + uint32_t a7:1; ///< byte 'a', bit 7 (0x00000080) + uint32_t b0:1; ///< byte 'b', bit 0 (0x00000100) + uint32_t b1:1; ///< byte 'b', bit 1 (0x00000200) + uint32_t b2:1; ///< byte 'b', bit 2 (0x00000400) + uint32_t b3:1; ///< byte 'b', bit 3 (0x00000800) + uint32_t b4:1; ///< byte 'b', bit 4 (0x00001000) + uint32_t b5:1; ///< byte 'b', bit 5 (0x00002000) + uint32_t b6:1; ///< byte 'b', bit 6 (0x00004000) + uint32_t b7:1; ///< byte 'b', bit 7 (0x00008000) + uint32_t c0:1; ///< byte 'c', bit 0 (0x00010000) + uint32_t c1:1; ///< byte 'c', bit 1 (0x00020000) + uint32_t c2:1; ///< byte 'c', bit 2 (0x00040000) + uint32_t c3:1; ///< byte 'c', bit 3 (0x00080000) + uint32_t c4:1; ///< byte 'c', bit 4 (0x00100000) + uint32_t c5:1; ///< byte 'c', bit 5 (0x00200000) + uint32_t c6:1; ///< byte 'c', bit 6 (0x00400000) + uint32_t c7:1; ///< byte 'c', bit 7 (0x00800000) + uint32_t d0:1; ///< byte 'd', bit 0 (0x01000000) + uint32_t d1:1; ///< byte 'd', bit 1 (0x02000000) + uint32_t d2:1; ///< byte 'd', bit 2 (0x04000000) + uint32_t d3:1; ///< byte 'd', bit 3 (0x08000000) + uint32_t d4:1; ///< byte 'd', bit 4 (0x10000000) + uint32_t d5:1; ///< byte 'd', bit 5 (0x20000000) + uint32_t d6:1; ///< byte 'd', bit 6 (0x40000000) + uint32_t d7:1; ///< byte 'd', bit 7 (0x80000000) +} sub4; + +/// Union containing a full 8 bytes to swap the bit orientation on +typedef union { + uint32_t word[2]; ///< two 32-bit values to load for swapping + uint8_t bytes[8]; ///< eight 8-bit values to load for swapping + struct { + sub4 a; ///< 32-bit access struct for bit swapping, upper four bytes (word[0] or bytes[0-3]) + sub4 b; ///< 32-bit access struct for bit swapping, lower four bytes (word[1] or bytes[4-7]) + }; +} bitswap_type; + + +/// Set `out.X` bits 0, 1, 2, and 3 to bit N +/// of `in.a.a`, `in.a.b`, `in.a.b`, `in.a.c`, and `in.a.d` +/// @param X the sub4 of `out` to set +/// @param N the bit of each byte to retrieve +/// @see bitswap_type +#define SWAPSA(X,N) out. X ## 0 = in.a.a ## N; \ + out. X ## 1 = in.a.b ## N; \ + out. X ## 2 = in.a.c ## N; \ + out. X ## 3 = in.a.d ## N; + +/// Set `out.X` bits 0, 1, 2, and 3 to bit N +/// of `in.b.a`, `in.b.b`, `in.b.b`, `in.b.c`, and `in.b.d` +/// @param X the sub4 of `out` to set +/// @param N the bit of each byte to retrieve +/// @see bitswap_type +#define SWAPSB(X,N) out. X ## 0 = in.b.a ## N; \ + out. X ## 1 = in.b.b ## N; \ + out. X ## 2 = in.b.c ## N; \ + out. X ## 3 = in.b.d ## N; + +/// Set `out.X` bits to bit N of both `in.a` and `in.b` +/// in order +/// @param X the sub4 of `out` to set +/// @param N the bit of each byte to retrieve +/// @see bitswap_type +#define SWAPS(X,N) out. X ## 0 = in.a.a ## N; \ + out. X ## 1 = in.a.b ## N; \ + out. X ## 2 = in.a.c ## N; \ + out. X ## 3 = in.a.d ## N; \ + out. X ## 4 = in.b.a ## N; \ + out. X ## 5 = in.b.b ## N; \ + out. X ## 6 = in.b.c ## N; \ + out. X ## 7 = in.b.d ## N; + + +/// Do an 8-byte by 8-bit rotation +__attribute__((always_inline)) inline void swapbits8(bitswap_type in, bitswap_type & out) { + + // SWAPS(a.a,7); + // SWAPS(a.b,6); + // SWAPS(a.c,5); + // SWAPS(a.d,4); + // SWAPS(b.a,3); + // SWAPS(b.b,2); + // SWAPS(b.c,1); + // SWAPS(b.d,0); + + // SWAPSA(a.a,7); + // SWAPSA(a.b,6); + // SWAPSA(a.c,5); + // SWAPSA(a.d,4); + // + // SWAPSB(a.a,7); + // SWAPSB(a.b,6); + // SWAPSB(a.c,5); + // SWAPSB(a.d,4); + // + // SWAPSA(b.a,3); + // SWAPSA(b.b,2); + // SWAPSA(b.c,1); + // SWAPSA(b.d,0); + // // + // SWAPSB(b.a,3); + // SWAPSB(b.b,2); + // SWAPSB(b.c,1); + // SWAPSB(b.d,0); + + for(int i = 0; i < 8; ++i) { + just8bits work; + work.a3 = in.word[0] >> 31; + work.a2 = in.word[0] >> 23; + work.a1 = in.word[0] >> 15; + work.a0 = in.word[0] >> 7; + in.word[0] <<= 1; + work.a7 = in.word[1] >> 31; + work.a6 = in.word[1] >> 23; + work.a5 = in.word[1] >> 15; + work.a4 = in.word[1] >> 7; + in.word[1] <<= 1; + out.bytes[i] = work.raw; + } +} + +/// 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) { + uint8_t x = A[row]; + + uint8_t bit = (1<>= 1) { + if(x & mask) { + *p++ |= bit; + } else { + *p++ &= ~bit; + } + } + // B[7] |= (x & 0x01) << row; x >>= 1; + // B[6] |= (x & 0x01) << row; x >>= 1; + // B[5] |= (x & 0x01) << row; x >>= 1; + // B[4] |= (x & 0x01) << row; x >>= 1; + // B[3] |= (x & 0x01) << row; x >>= 1; + // B[2] |= (x & 0x01) << row; x >>= 1; + // B[1] |= (x & 0x01) << row; x >>= 1; + // B[0] |= (x & 0x01) << row; x >>= 1; + } +} + +/// Simplified form of bits rotating function. +/// This rotates data into LSB for a faster write (the code using this data can happily walk the array backwards). +/// Based on code found here: https://web.archive.org/web/20190108225554/http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt +void transpose8x1_noinline(unsigned char *A, unsigned char *B); + +/// @copydoc transpose8x1_noinline() +__attribute__((always_inline)) inline void transpose8x1(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; +} + +/// Simplified form of bits rotating function. +/// Based on code found here: https://web.archive.org/web/20190108225554/http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt +__attribute__((always_inline)) inline void transpose8x1_MSB(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; + + B[7] = y; y >>= 8; + B[6] = y; y >>= 8; + B[5] = y; y >>= 8; + B[4] = y; + + B[3] = x; x >>= 8; + B[2] = x; x >>= 8; + B[1] = x; x >>= 8; + B[0] = x; /* */ +} + +/// Templated bit-rotating function. +/// Based on code found here: https://web.archive.org/web/20190108225554/http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt +template +__attribute__((always_inline)) inline void transpose8(unsigned char *A, unsigned char *B) { + uint32_t x, y, t; + + // Load the array and pack it into x and y. + if(m == 1) { + y = *(unsigned int*)(A); + x = *(unsigned int*)(A+4); + } else { + 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]; + } + + // 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; + + B[7*n] = y; y >>= 8; + B[6*n] = y; y >>= 8; + B[5*n] = y; y >>= 8; + B[4*n] = y; + + B[3*n] = x; x >>= 8; + B[2*n] = x; x >>= 8; + B[n] = x; x >>= 8; + B[0] = x; + // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0; + // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0; +} + +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/chipsets.h b/lib/FastLED/src/chipsets.h new file mode 100644 index 00000000..8e54bdd0 --- /dev/null +++ b/lib/FastLED/src/chipsets.h @@ -0,0 +1,750 @@ +#ifndef __INC_CHIPSETS_H +#define __INC_CHIPSETS_H + +#include "FastLED.h" +#include "pixeltypes.h" + +/// @file chipsets.h +/// Contains the bulk of the definitions for the various LED chipsets supported. + +FASTLED_NAMESPACE_BEGIN + +/// @defgroup Chipsets LED Chipset Controllers +/// Implementations of ::CLEDController classes for various led chipsets. +/// +/// @{ + +#if defined(ARDUINO) //&& defined(SoftwareSerial_h) + + +#if defined(SoftwareSerial_h) || defined(__SoftwareSerial_h) +#include + +#define HAS_PIXIE + +/// Adafruit Pixie controller class +/// @tparam DATA_PIN the pin to write data out on +/// @tparam RGB_ORDER the RGB ordering for the LED data +template +class PixieController : public CPixelLEDController { + SoftwareSerial Serial; + CMinWait<2000> mWait; + +public: + PixieController() : Serial(-1, DATA_PIN) {} + +protected: + /// Initialize the controller + virtual void init() { + Serial.begin(115200); + mWait.mark(); + } + + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + while(pixels.has(1)) { + uint8_t r = pixels.loadAndScale0(); + Serial.write(r); + uint8_t g = pixels.loadAndScale1(); + Serial.write(g); + uint8_t b = pixels.loadAndScale2(); + Serial.write(b); + pixels.advanceData(); + pixels.stepDithering(); + } + mWait.mark(); + } + +}; + +// template +// class PixieController : public PixieBaseController { +// public: +// virtual void init() { +// STREAM.begin(115200); +// } +// }; +#endif +#endif + +/// @defgroup ClockedChipsets Clocked Chipsets +/// Nominally SPI based, these chipsets have a data and a clock line. +/// @{ + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LPD8806 controller class - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// LPD8806 controller class. +/// @tparam DATA_PIN the data pin for these LEDs +/// @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 +class LPD8806Controller : public CPixelLEDController { + typedef SPIOutput SPI; + + class LPD8806_ADJUST { + public: + // LPD8806 spec wants the high bit of every rgb data byte sent out to be set. + __attribute__((always_inline)) inline static uint8_t adjust(FASTLED_REGISTER uint8_t data) { return ((data>>1) | 0x80) + ((data && (data<254)) & 0x01); } + __attribute__((always_inline)) inline static void postBlock(int len) { + SPI::writeBytesValueRaw(0, ((len*3+63)>>6)); + } + + }; + + SPI mSPI; + +public: + LPD8806Controller() {} + virtual void init() { + mSPI.init(); + } + +protected: + + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mSPI.template writePixels<0, LPD8806_ADJUST, RGB_ORDER>(pixels); + } +}; + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// WS2801 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// WS2801 controller class. +/// @tparam DATA_PIN the data pin for these LEDs +/// @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 +class WS2801Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + CMinWait<1000> mWaitDelay; + +public: + WS2801Controller() {} + + /// Initialize the controller + virtual void init() { + mSPI.init(); + mWaitDelay.mark(); + } + +protected: + + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mWaitDelay.wait(); + mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels); + mWaitDelay.mark(); + } +}; + +/// WS2803 controller class. +/// @copydetails WS2801Controller +template +class WS2803Controller : public WS2801Controller {}; + +/// LPD6803 controller class (LPD1101). +/// 16 bit (1 bit const "1", 5 bit red, 5 bit green, 5 bit blue). +/// In chip CMODE pin must be set to 1 (inside oscillator mode). +/// @tparam DATA_PIN the data pin for these LEDs +/// @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) +/// @see Datasheet: https://cdn-shop.adafruit.com/datasheets/LPD6803.pdf +template +class LPD6803Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void startBoundary() { mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); mSPI.writeByte(0); } + void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); } + +public: + LPD6803Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + startBoundary(); + while(pixels.has(1)) { + FASTLED_REGISTER uint16_t command; + command = 0x8000; + command |= (pixels.loadAndScale0() & 0xF8) << 7; // red is the high 5 bits + command |= (pixels.loadAndScale1() & 0xF8) << 2; // green is the middle 5 bits + mSPI.writeByte((command >> 8) & 0xFF); + command |= pixels.loadAndScale2() >> 3 ; // blue is the low 5 bits + mSPI.writeByte(command & 0xFF); + + pixels.stepDithering(); + pixels.advanceData(); + } + endBoundary(pixels.size()); + mSPI.waitFully(); + mSPI.release(); + } + +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// APA102 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// APA102 controller class. +/// @tparam DATA_PIN the data pin for these LEDs +/// @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 +class APA102Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } + void endBoundary(int nLeds) { int nDWords = (nLeds/32); do { mSPI.writeByte(0xFF); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nDWords--); } + + inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) { +#ifdef FASTLED_SPI_BYTE_ONLY + mSPI.writeByte(0xE0 | brightness); + mSPI.writeByte(b0); + mSPI.writeByte(b1); + mSPI.writeByte(b2); +#else + uint16_t b = 0xE000 | (brightness << 8) | (uint16_t)b0; + mSPI.writeWord(b); + uint16_t w = b1 << 8; + w |= b2; + mSPI.writeWord(w); +#endif + } + +public: + APA102Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2(); +#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1 + const uint16_t maxBrightness = 0x1F; + uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1; + s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness; + s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness; + s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness; +#else + const uint8_t brightness = 0x1F; +#endif + + startBoundary(); + while (pixels.has(1)) { + writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2)); + pixels.stepDithering(); + pixels.advanceData(); + } + endBoundary(pixels.size()); + + mSPI.waitFully(); + mSPI.release(); + } + +}; + +/// SK9822 controller class. +/// @tparam DATA_PIN the data pin for these LEDs +/// @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 +class SK9822Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void startBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } + void endBoundary(int nLeds) { int nLongWords = (nLeds/32); do { mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); mSPI.writeByte(0x00); } while(nLongWords--); } + + inline void writeLed(uint8_t brightness, uint8_t b0, uint8_t b1, uint8_t b2) __attribute__((always_inline)) { +#ifdef FASTLED_SPI_BYTE_ONLY + mSPI.writeByte(0xE0 | brightness); + mSPI.writeByte(b0); + mSPI.writeByte(b1); + mSPI.writeByte(b2); +#else + uint16_t b = 0xE000 | (brightness << 8) | (uint16_t)b0; + mSPI.writeWord(b); + uint16_t w = b1 << 8; + w |= b2; + mSPI.writeWord(w); +#endif + } + +public: + SK9822Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + uint8_t s0 = pixels.getScale0(), s1 = pixels.getScale1(), s2 = pixels.getScale2(); +#if FASTLED_USE_GLOBAL_BRIGHTNESS == 1 + const uint16_t maxBrightness = 0x1F; + uint16_t brightness = ((((uint16_t)max(max(s0, s1), s2) + 1) * maxBrightness - 1) >> 8) + 1; + s0 = (maxBrightness * s0 + (brightness >> 1)) / brightness; + s1 = (maxBrightness * s1 + (brightness >> 1)) / brightness; + s2 = (maxBrightness * s2 + (brightness >> 1)) / brightness; +#else + const uint8_t brightness = 0x1F; +#endif + + startBoundary(); + while (pixels.has(1)) { + writeLed(brightness, pixels.loadAndScale0(0, s0), pixels.loadAndScale1(0, s1), pixels.loadAndScale2(0, s2)); + pixels.stepDithering(); + pixels.advanceData(); + } + + endBoundary(pixels.size()); + + mSPI.waitFully(); + mSPI.release(); + } + +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// P9813 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// P9813 controller class. +/// @tparam DATA_PIN the data pin for these LEDs +/// @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 +class P9813Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void writeBoundary() { mSPI.writeWord(0); mSPI.writeWord(0); } + + inline void writeLed(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline)) { + FASTLED_REGISTER uint8_t top = 0xC0 | ((~b & 0xC0) >> 2) | ((~g & 0xC0) >> 4) | ((~r & 0xC0) >> 6); + mSPI.writeByte(top); mSPI.writeByte(b); mSPI.writeByte(g); mSPI.writeByte(r); + } + +public: + P9813Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + mSPI.select(); + + writeBoundary(); + while(pixels.has(1)) { + writeLed(pixels.loadAndScale0(), pixels.loadAndScale1(), pixels.loadAndScale2()); + pixels.advanceData(); + pixels.stepDithering(); + } + writeBoundary(); + mSPI.waitFully(); + + mSPI.release(); + } + +}; + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SM16716 definition - takes data/clock/select pin values (N.B. should take an SPI definition?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// SM16716 controller class. +/// @tparam DATA_PIN the data pin for these LEDs +/// @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 +class SM16716Controller : public CPixelLEDController { + typedef SPIOutput SPI; + SPI mSPI; + + void writeHeader() { + // Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes) + mSPI.select(); + 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(); + } + +public: + SM16716Controller() {} + + virtual void init() { + mSPI.init(); + } + +protected: + /// @copydoc CPixelLEDController::showPixels() + 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 + // writeHeader(); + mSPI.template writePixels( pixels ); + writeHeader(); + } + +}; + +/// @} ClockedChipsets + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Clockless template instantiations - see clockless.h for how the timing values are used +// + +#ifdef FASTLED_HAS_CLOCKLESS +/// @defgroup ClocklessChipsets Clockless Chipsets +/// These chipsets have only a single data line. +/// +/// The clockless chipset controllers use the same base class +/// and the same protocol, but with varying timing periods. +/// +/// These controllers have 3 control points in their cycle for each bit: +/// @code +/// 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) +/// @endcode +/// +/// 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). +/// +/// @note The base class, ClocklessController, is platform-specific. +/// @{ + +// 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 defined(__LGT8F__) || (CLOCKLESS_FREQUENCY == 8000000 || CLOCKLESS_FREQUENCY == 16000000 || CLOCKLESS_FREQUENCY == 24000000) || defined(FASTLED_DOXYGEN) // || CLOCKLESS_FREQUENCY == 48000000 || CLOCKLESS_FREQUENCY == 96000000) // 125ns/clock + +/// Frequency multiplier for each clockless data interval. +/// @see Notes in @ref ClocklessChipsets +#define FMUL (CLOCKLESS_FREQUENCY/8000000) + +/// GE8822 controller class. +/// @copydetails WS2812Controller800Khz +template +class GE8822Controller800Khz : public ClocklessController {}; + +/// LPD1886 controller class. +/// @copydetails WS2812Controller800Khz +template +class LPD1886Controller1250Khz : public ClocklessController {}; + +/// LPD1886 controller class. +/// @copydetails WS2812Controller800Khz +template +class LPD1886Controller1250Khz_8bit : public ClocklessController {}; + +/// WS2812 controller class @ 800 KHz. +/// @tparam DATA_PIN the data pin for these LEDs +/// @tparam RGB_ORDER the RGB ordering for these LEDs +template +class WS2812Controller800Khz : public ClocklessController {}; + +/// WS2811 controller class @ 800 KHz. +/// @copydetails WS2812Controller800Khz +template +class WS2811Controller800Khz : public ClocklessController {}; + +/// DP1903 controller class @ 800 KHz. +/// @copydetails WS2812Controller800Khz +template +class DP1903Controller800Khz : public ClocklessController {}; + +/// DP1903 controller class @ 400 KHz. +/// @copydetails WS2812Controller800Khz +template +class DP1903Controller400Khz : public ClocklessController {}; + +/// WS2813 controller class. +/// @copydetails WS2812Controller800Khz +template //not tested +class WS2813Controller : public ClocklessController {}; + +/// WS2811 controller class @ 400 KHz. +/// @copydetails WS2812Controller800Khz +template +class WS2811Controller400Khz : public ClocklessController {}; + +/// SK6822 controller class. +/// @copydetails WS2812Controller800Khz +template +class SK6822Controller : public ClocklessController {}; + +/// SM16703 controller class. +/// @copydetails WS2812Controller800Khz +template +class SM16703Controller : public ClocklessController {}; + +/// SK6812 controller class. +/// @copydetails WS2812Controller800Khz +template +class SK6812Controller : public ClocklessController {}; + +/// UCS1903 controller class @ 400 KHz. +/// @copydetails WS2812Controller800Khz +template +class UCS1903Controller400Khz : public ClocklessController {}; + +/// UCS1903B controller class. +/// @copydetails WS2812Controller800Khz +template +class UCS1903BController800Khz : public ClocklessController {}; + +/// UCS1904 controller class. +/// @copydetails WS2812Controller800Khz +template +class UCS1904Controller800Khz : public ClocklessController {}; + +/// UCS2903 controller class. +/// @copydetails WS2812Controller800Khz +template +class UCS2903Controller : public ClocklessController {}; + +/// TM1809 controller class. +/// @copydetails WS2812Controller800Khz +template +class TM1809Controller800Khz : public ClocklessController {}; + +/// TM1803 controller class. +/// @copydetails WS2812Controller800Khz +template +class TM1803Controller400Khz : public ClocklessController {}; + +/// TM1829 controller class. +/// @copydetails WS2812Controller800Khz +template +class TM1829Controller800Khz : public ClocklessController {}; + +/// GW6205 controller class @ 400 KHz. +/// @copydetails WS2812Controller800Khz +template +class GW6205Controller400Khz : public ClocklessController {}; + +/// UCS1904 controller class @ 800 KHz. +/// @copydetails WS2812Controller800Khz +template +class GW6205Controller800Khz : public ClocklessController {}; + +/// PL9823 controller class. +/// @copydetails WS2812Controller800Khz +template +class PL9823Controller : public ClocklessController {}; + +#else + +/// Calculates the number of cycles for the clockless chipset (which may differ from CPU cycles) +/// @see ::NS() +#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 {}; + +// GW6205@400khz - 800ns, 800ns, 800ns +template +class GW6205Controller400Khz : public ClocklessController {}; + +// GW6205@400khz - 400ns, 400ns, 400ns +template +class GW6205Controller800Khz : public ClocklessController {}; + +// UCS1903 - 500ns, 1500ns, 500ns +template +class UCS1903Controller400Khz : public ClocklessController {}; + +// UCS1903B - 400ns, 450ns, 450ns +template +class UCS1903BController800Khz : public ClocklessController {}; + +// UCS1904 - 400ns, 400ns, 450ns +template +class UCS1904Controller800Khz : public ClocklessController {}; + +// UCS2903 - 250ns, 750ns, 250ns +template +class UCS2903Controller : public ClocklessController {}; + +// TM1809 - 350ns, 350ns, 550ns +template +class TM1809Controller800Khz : public ClocklessController {}; + +/* +// WS2811 - 320ns, 320ns, 640ns +template +class WS2811Controller800Khz : public ClocklessController {}; +*/ + +// WS2811 - 320ns, 320ns, 640ns +template +class WS2811Controller800Khz : public ClocklessController {}; + +// WS2813 - 320ns, 320ns, 640ns +template +class WS2813Controller : public ClocklessController {}; + +// WS2812 - 250ns, 625ns, 375ns +template +class WS2812Controller800Khz : public ClocklessController {}; + +// WS2811@400khz - 800ns, 800ns, 900ns +template +class WS2811Controller400Khz : public ClocklessController {}; + +// 750NS, 750NS, 750NS +template +class TM1803Controller400Khz : public ClocklessController {}; + +template +class TM1829Controller800Khz : public ClocklessController {}; + +template +class TM1829Controller1600Khz : public ClocklessController {}; + +template +class LPD1886Controller1250Khz : public ClocklessController {}; + +template +class LPD1886Controller1250Khz_8bit : public ClocklessController {}; + + +template +class SK6822Controller : public ClocklessController {}; +/* +The original WS2812 datasheets that I had done this off of years ago had +T0H as being 250ns, +TOL 1µs and +T1H being 875ns and +T1L being 375ns give or take. +The important part is to make sure the total timing is 1.25µs per bit which these timings are - the newer datasheets have different timings, +however I'm not going to change the timings in the library until I get back to actively working on it and can do a full round of testing with different timings +(the timings in here are based on hundreds of hours of writing/running tests and checking output w/scopes, etc...) + +As for the three numbers - call them T1, T2, and T3. +T0H is equal to T1, +T0L is equal to T2+T3, +T1H is equal to T1+T2, +T1L is equal to T3. + +T1 is the point in time where the signal goes from high to low for a zero. +T1+T2 is the point in time where the signal always goes to low (if it's already low from being a zero, this doesn't change anything) +T1+T2+T3 is the total amount of time that must pass before starting the next bit. +The timings are defined for how the code uses them. +*/ +// ! value are calculated with this formula +/* +T1 = T0H +T2 = T0L - T1L +T3 = T1L +*/ + +// original values +// class SK6812Controller : public ClocklessController {}; +// new values +// +/* value from K32 library for LED_SK6812W_V3 + T0H = 350 + T1H = 700 + T0L = 800 + T1L = 600 + TRS = 50000 +template +class SK6812Controller : public ClocklessController {}; +*/ + +//template + +// TODO - these need to be verified +// TM1934 - 320ns, 320ns, 640ns +template +class TM1934Controller : public ClocklessController {}; + +template +class SK6812Controller : public ClocklessController {}; + +template +class SM16703Controller : public ClocklessController {}; + +template +class PL9823Controller : public ClocklessController {}; +#endif +/// @} ClocklessChipsets + +#endif +/// @} Chipsets + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/color.h b/lib/FastLED/src/color.h new file mode 100644 index 00000000..1d62b17e --- /dev/null +++ b/lib/FastLED/src/color.h @@ -0,0 +1,101 @@ +#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 +/// @{ + +/// @brief Color correction starting points +typedef enum { + /// Typical values for SMD5050 LEDs + TypicalSMD5050=0xFFB0F0 /* 255, 176, 240 */, + /// @copydoc TypicalSMD5050 + TypicalLEDStrip=0xFFB0F0 /* 255, 176, 240 */, + + /// Typical values for 8 mm "pixels on a string". + /// Also for many through-hole 'T' package LEDs. + Typical8mmPixel=0xFFE08C /* 255, 224, 140 */, + /// @copydoc Typical8mmPixel + TypicalPixelString=0xFFE08C /* 255, 224, 140 */, + + /// Uncorrected color (0xFFFFFF) + UncorrectedColor=0xFFFFFF /* 255, 255, 255 */ + +} LEDColorCorrection; + + +/// @brief Color temperature values +/// @details These color values are separated into two groups: black body radiators +/// and gaseous light sources. +/// +/// Black body radiators emit a (relatively) continuous spectrum, +/// and can be described as having a Kelvin 'temperature'. This includes things +/// like candles, tungsten lightbulbs, and sunlight. +/// +/// 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. +/// +/// @see https://en.wikipedia.org/wiki/Color_temperature +typedef enum { + // Black Body Radiators + // @{ + /// 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 */, + // @} + + // Gaseous Light Sources + // @{ + /// Warm (yellower) flourescent light bulbs + WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */, + /// Standard flourescent light bulbs + StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */, + /// Cool white (bluer) flourescent light bulbs + CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */, + /// Full spectrum flourescent light bulbs + FullSpectrumFluorescent=0xFFF4F2 /* 0 K, 255, 244, 242 */, + /// Grow light flourescent light bulbs + GrowLightFluorescent=0xFFEFF7 /* 0 K, 255, 239, 247 */, + /// Black light flourescent light bulbs + BlackLightFluorescent=0xA700FF /* 0 K, 167, 0, 255 */, + /// Mercury vapor light bulbs + MercuryVapor=0xD8F7FF /* 0 K, 216, 247, 255 */, + /// Sodium vapor light bulbs + SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */, + /// Metal-halide light bulbs + MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */, + /// High-pressure sodium light bulbs + HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */, + // @} + + /// Uncorrected temperature (0xFFFFFF) + UncorrectedTemperature=0xFFFFFF /* 255, 255, 255 */ +} ColorTemperature; + +FASTLED_NAMESPACE_END + +///@} +#endif diff --git a/lib/FastLED/src/colorpalettes.cpp b/lib/FastLED/src/colorpalettes.cpp new file mode 100644 index 00000000..dfa9068c --- /dev/null +++ b/lib/FastLED/src/colorpalettes.cpp @@ -0,0 +1,194 @@ +#ifndef __INC_COLORPALETTES_H +/// Include guard +#define __INC_COLORPALETTES_H + +/// Disables pragma messages and warnings +#define FASTLED_INTERNAL + +#include "FastLED.h" +#include "colorutils.h" +#include "colorpalettes.h" + +FASTLED_USING_NAMESPACE + +/// @file colorpalettes.cpp +/// Definitions for the predefined color palettes supplied by FastLED. +/// @note The documentation is in the source file instead of the header +/// because it allows Doxygen to automatically inline the values that +/// make up each palette. + +/// @addtogroup ColorPalettes +/// @{ + +/// @defgroup PredefinedPalettes Predefined Color Palettes +/// Stock color palettes, only included when used. +/// These palettes are all declared as `PROGMEM`, meaning +/// that they won't take up SRAM on AVR chips until used. +/// Furthermore, the compiler won't even include these +/// in your PROGMEM (flash) storage unless you specifically +/// use each one, so you only "pay for" those you actually use. +/// @{ + +/// Cloudy color palette +extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM = +{ + CRGB::Blue, + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + + CRGB::Blue, + CRGB::DarkBlue, + CRGB::SkyBlue, + CRGB::SkyBlue, + + CRGB::LightBlue, + CRGB::White, + CRGB::LightBlue, + CRGB::SkyBlue +}; + +/// Lava color palette +extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM = +{ + CRGB::Black, + CRGB::Maroon, + CRGB::Black, + CRGB::Maroon, + + CRGB::DarkRed, + CRGB::DarkRed, + CRGB::Maroon, + CRGB::DarkRed, + + CRGB::DarkRed, + CRGB::DarkRed, + CRGB::Red, + CRGB::Orange, + + CRGB::White, + CRGB::Orange, + CRGB::Red, + CRGB::DarkRed +}; + + +/// Ocean colors, blues and whites +extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM = +{ + CRGB::MidnightBlue, + CRGB::DarkBlue, + CRGB::MidnightBlue, + CRGB::Navy, + + CRGB::DarkBlue, + CRGB::MediumBlue, + CRGB::SeaGreen, + CRGB::Teal, + + CRGB::CadetBlue, + CRGB::Blue, + CRGB::DarkCyan, + CRGB::CornflowerBlue, + + CRGB::Aquamarine, + CRGB::SeaGreen, + CRGB::Aqua, + CRGB::LightSkyBlue +}; + +/// Forest colors, greens +extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM = +{ + CRGB::DarkGreen, + CRGB::DarkGreen, + CRGB::DarkOliveGreen, + CRGB::DarkGreen, + + CRGB::Green, + CRGB::ForestGreen, + CRGB::OliveDrab, + CRGB::Green, + + CRGB::SeaGreen, + CRGB::MediumAquamarine, + CRGB::LimeGreen, + CRGB::YellowGreen, + + CRGB::LightGreen, + CRGB::LawnGreen, + CRGB::MediumAquamarine, + CRGB::ForestGreen +}; + +/// HSV Rainbow +extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM = +{ + 0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00, + 0xABAB00, 0x56D500, 0x00FF00, 0x00D52A, + 0x00AB55, 0x0056AA, 0x0000FF, 0x2A00D5, + 0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B +}; + +/// Alias of RainbowStripeColors_p +#define RainbowStripesColors_p RainbowStripeColors_p + +/// HSV Rainbow colors with alternatating stripes of black +extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM = +{ + 0xFF0000, 0x000000, 0xAB5500, 0x000000, + 0xABAB00, 0x000000, 0x00FF00, 0x000000, + 0x00AB55, 0x000000, 0x0000FF, 0x000000, + 0x5500AB, 0x000000, 0xAB0055, 0x000000 +}; + +/// HSV color ramp: blue, purple, pink, red, orange, yellow (and back). +/// Basically, everything but the greens, which tend to make +/// people's skin look unhealthy. This palette is good for +/// lighting at a club or party, where it'll be shining on people. +extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM = +{ + 0x5500AB, 0x84007C, 0xB5004B, 0xE5001B, + 0xE81700, 0xB84700, 0xAB7700, 0xABAB00, + 0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E, + 0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9 +}; + +/// Approximate "black body radiation" palette, akin to +/// the FastLED HeatColor() function. +/// It's recommended that you use values 0-240 rather than +/// the usual 0-255, as the last 15 colors will be +/// "wrapping around" from the hot end to the cold end, +/// which looks wrong. +extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM = +{ + 0x000000, + 0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000, + 0xFF3300, 0xFF6600, 0xFF9900, 0xFFCC00, 0xFFFF00, + 0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF +}; + + +/// Rainbow gradient. Provided for situations where you're going +/// to use a number of other gradient palettes, AND 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 + +/// @} +/// @} + +#endif diff --git a/lib/FastLED/src/colorpalettes.h b/lib/FastLED/src/colorpalettes.h new file mode 100644 index 00000000..576f2fbc --- /dev/null +++ b/lib/FastLED/src/colorpalettes.h @@ -0,0 +1,37 @@ +#ifndef __INC_COLORPALETTES_H +#define __INC_COLORPALETTES_H + +#include "FastLED.h" +#include "colorutils.h" + +/// @file colorpalettes.h +/// Declarations for the predefined color palettes supplied by FastLED. + +// Have Doxygen ignore these declarations +/// @cond + +FASTLED_NAMESPACE_BEGIN + +extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM; +extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM; +extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM; +extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM; + +extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM; + +/// Alias of RainbowStripeColors_p +#define RainbowStripesColors_p RainbowStripeColors_p +extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM; + +extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM; + +extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM; + + +DECLARE_GRADIENT_PALETTE( Rainbow_gp); + +FASTLED_NAMESPACE_END + +/// @endcond + +#endif diff --git a/lib/FastLED/src/colorutils.cpp b/lib/FastLED/src/colorutils.cpp new file mode 100644 index 00000000..088ceb1e --- /dev/null +++ b/lib/FastLED/src/colorutils.cpp @@ -0,0 +1,1284 @@ +#define FASTLED_INTERNAL +#define __PROG_TYPES_COMPAT__ + +/// @file colorutils.cpp +/// Utility functions for color fill, palettes, blending, and more + +#include +#include + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + + + +void fill_solid( struct CRGB * targetArray, int numToFill, + const struct CRGB& color) +{ + for( int i = 0; i < numToFill; ++i) { + targetArray[i] = color; + } +} + +void fill_solid( struct CHSV * targetArray, int numToFill, + const struct CHSV& color) +{ + for( int i = 0; i < numToFill; ++i) { + targetArray[i] = color; + } +} + + +// void fill_solid( struct CRGB* targetArray, int numToFill, +// const struct CHSV& hsvColor) +// { +// fill_solid( targetArray, numToFill, (CRGB) hsvColor); +// } + +void fill_rainbow( struct CRGB * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue ) +{ + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + for( int i = 0; i < numToFill; ++i) { + targetArray[i] = hsv; + hsv.hue += deltahue; + } +} + +void fill_rainbow( struct CHSV * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue ) +{ + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + for( int i = 0; i < numToFill; ++i) { + targetArray[i] = hsv; + hsv.hue += deltahue; + } +} + + +void fill_rainbow_circular(struct CRGB* targetArray, int numToFill, uint8_t initialhue, bool reversed) +{ + if (numToFill == 0) return; // avoiding div/0 + + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + + const uint16_t hueChange = 65535 / (uint16_t)numToFill; // hue change for each LED, * 256 for precision (256 * 256 - 1) + uint16_t hueOffset = 0; // offset for hue value, with precision (*256) + + for (int i = 0; i < numToFill; ++i) { + targetArray[i] = hsv; + if (reversed) hueOffset -= hueChange; + else hueOffset += hueChange; + hsv.hue = initialhue + (uint8_t)(hueOffset >> 8); // assign new hue with precise offset (as 8-bit) + } +} + +void fill_rainbow_circular(struct CHSV* targetArray, int numToFill, uint8_t initialhue, bool reversed) +{ + if (numToFill == 0) return; // avoiding div/0 + + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + + const uint16_t hueChange = 65535 / (uint16_t) numToFill; // hue change for each LED, * 256 for precision (256 * 256 - 1) + uint16_t hueOffset = 0; // offset for hue value, with precision (*256) + + for (int i = 0; i < numToFill; ++i) { + targetArray[i] = hsv; + if (reversed) hueOffset -= hueChange; + else hueOffset += hueChange; + hsv.hue = initialhue + (uint8_t)(hueOffset >> 8); // assign new hue with precise offset (as 8-bit) + } +} + + +void fill_gradient_RGB( CRGB* leds, + uint16_t startpos, CRGB startcolor, + uint16_t endpos, CRGB endcolor ) +{ + // if the points are in the wrong order, straighten them + if( endpos < startpos ) { + uint16_t t = endpos; + CRGB tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + + saccum87 rdistance87; + saccum87 gdistance87; + saccum87 bdistance87; + + rdistance87 = (endcolor.r - startcolor.r) << 7; + gdistance87 = (endcolor.g - startcolor.g) << 7; + bdistance87 = (endcolor.b - startcolor.b) << 7; + + uint16_t pixeldistance = endpos - startpos; + int16_t divisor = pixeldistance ? pixeldistance : 1; + + saccum87 rdelta87 = rdistance87 / divisor; + saccum87 gdelta87 = gdistance87 / divisor; + saccum87 bdelta87 = bdistance87 / divisor; + + rdelta87 *= 2; + gdelta87 *= 2; + bdelta87 *= 2; + + accum88 r88 = startcolor.r << 8; + accum88 g88 = startcolor.g << 8; + accum88 b88 = startcolor.b << 8; + for( uint16_t i = startpos; i <= endpos; ++i) { + leds[i] = CRGB( r88 >> 8, g88 >> 8, b88 >> 8); + r88 += rdelta87; + g88 += gdelta87; + b88 += bdelta87; + } +} + +#if 0 +void fill_gradient( const CHSV& c1, const CHSV& c2) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2); +} + +void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3); +} + +void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4); +} +#endif + + + + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2) +{ + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, last, c2); +} + + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3) +{ + uint16_t half = (numLeds / 2); + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, half, c2); + fill_gradient_RGB( leds, half, c2, last, c3); +} + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) +{ + uint16_t onethird = (numLeds / 3); + uint16_t twothirds = ((numLeds * 2) / 3); + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, onethird, c2); + fill_gradient_RGB( leds, onethird, c2, twothirds, c3); + fill_gradient_RGB( leds, twothirds, c3, last, c4); +} + + + + +void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + for( uint16_t i = 0; i < num_leds; ++i) { + leds[i].nscale8_video( scale); + } +} + +void fade_video(CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8_video( leds, num_leds, 255 - fadeBy); +} + +void fadeLightBy(CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8_video( leds, num_leds, 255 - fadeBy); +} + + +void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8( leds, num_leds, 255 - fadeBy); +} + +void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8( leds, num_leds, 255 - fadeBy); +} + +/// Unused alias of nscale8(CRGB*, uint16_t, uint8_t) +/// @todo Remove this or add a declaration? This is not listed in the colorutils.h header. +void nscale8_raw( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + nscale8( leds, num_leds, scale); +} + +void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + for( uint16_t i = 0; i < num_leds; ++i) { + leds[i].nscale8( scale); + } +} + +void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask) +{ + uint8_t fr, fg, fb; + fr = colormask.r; + fg = colormask.g; + fb = colormask.b; + + 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); + } +} + + +CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ) +{ + if( amountOfOverlay == 0) { + return existing; + } + + if( amountOfOverlay == 255) { + existing = overlay; + return existing; + } + +#if 0 + // Old blend method which unfortunately had some rounding errors + fract8 amountOfKeep = 255 - amountOfOverlay; + + existing.red = scale8_LEAVING_R1_DIRTY( existing.red, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.red, amountOfOverlay); + existing.green = scale8_LEAVING_R1_DIRTY( existing.green, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.green, amountOfOverlay); + existing.blue = scale8_LEAVING_R1_DIRTY( existing.blue, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.blue, amountOfOverlay); + + cleanup_R1(); +#else + // Corrected blend method, with no loss-of-precision rounding errors + existing.red = blend8( existing.red, overlay.red, amountOfOverlay); + existing.green = blend8( existing.green, overlay.green, amountOfOverlay); + existing.blue = blend8( existing.blue, overlay.blue, amountOfOverlay); +#endif + + return existing; +} + + + +void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay) +{ + for( uint16_t i = count; i; --i) { + nblend( *existing, *overlay, amountOfOverlay); + ++existing; + ++overlay; + } +} + +CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ) +{ + CRGB nu(p1); + nblend( nu, p2, amountOfP2); + return nu; +} + +CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 ) +{ + for( uint16_t i = 0; i < count; ++i) { + dest[i] = blend(src1[i], src2[i], amountOfsrc2); + } + return dest; +} + + + +CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGradientDirectionCode directionCode) +{ + if( amountOfOverlay == 0) { + return existing; + } + + if( amountOfOverlay == 255) { + existing = overlay; + return existing; + } + + fract8 amountOfKeep = 255 - amountOfOverlay; + + uint8_t huedelta8 = overlay.hue - existing.hue; + + if( directionCode == SHORTEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 > 127) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == LONGEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 < 128) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == FORWARD_HUES) { + existing.hue = existing.hue + scale8( huedelta8, amountOfOverlay); + } + else /* directionCode == BACKWARD_HUES */ + { + huedelta8 = -huedelta8; + existing.hue = existing.hue - scale8( huedelta8, amountOfOverlay); + } + + existing.sat = scale8_LEAVING_R1_DIRTY( existing.sat, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.sat, amountOfOverlay); + existing.val = scale8_LEAVING_R1_DIRTY( existing.val, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.val, amountOfOverlay); + + cleanup_R1(); + + return existing; +} + + + +void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode ) +{ + if(existing == overlay) return; + for( uint16_t i = count; i; --i) { + nblend( *existing, *overlay, amountOfOverlay, directionCode); + ++existing; + ++overlay; + } +} + +CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectionCode directionCode ) +{ + CHSV nu(p1); + nblend( nu, p2, amountOfP2, directionCode); + return nu; +} + +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) { + dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode); + } + return dest; +} + + + +/// Forward declaration of the function "XY" which must be provided by +/// the application for use in two-dimensional filter functions. +uint16_t XY( uint8_t, uint8_t);// __attribute__ ((weak)); + + +// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. +// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. +// +// 0 = no spread at all +// 64 = moderate spreading +// 172 = maximum smooth, even spreading +// +// 173..255 = wider spreading, but increasing flicker +// +// Total light is NOT entirely conserved, so many repeated +// calls to 'blur' will also result in the light fading, +// eventually all the way to black; this is by design so that +// it can be used to (slowly) clear the LEDs to black. +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) { + CRGB cur = leds[i]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[i-1] += part; + leds[i] = cur; + carryover = part; + } +} + +void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) +{ + blurRows(leds, width, height, blur_amount); + blurColumns(leds, width, height, blur_amount); +} + +void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) +{ +/* for( uint8_t row = 0; row < height; row++) { + CRGB* rowbase = leds + (row * width); + blur1d( rowbase, width, blur_amount); + } +*/ + // blur rows same as columns, for irregular matrix + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + for( uint8_t row = 0; row < height; row++) { + CRGB carryover = CRGB::Black; + for( uint8_t i = 0; i < width; i++) { + CRGB cur = leds[XY(i,row)]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[XY(i-1,row)] += part; + leds[XY(i,row)] = cur; + carryover = part; + } + } +} + +// blurColumns: perform a blur1d on each column of a rectangular matrix +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) { + CRGB carryover = CRGB::Black; + for( uint8_t i = 0; i < height; ++i) { + CRGB cur = leds[XY(col,i)]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[XY(col,i-1)] += part; + leds[XY(col,i)] = cur; + carryover = part; + } + } +} + + + +// CRGB HeatColor( uint8_t temperature) +// +// Approximates a 'black body radiation' spectrum for +// a given 'heat' level. This is useful for animations of 'fire'. +// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot). +// This is NOT a chromatically correct 'black body radiation' +// spectrum, but it's surprisingly close, and it's fast and small. +// +// On AVR/Arduino, this typically takes around 70 bytes of program memory, +// versus 768 bytes for a full 256-entry RGB lookup table. + +CRGB HeatColor( uint8_t temperature) +{ + CRGB heatcolor; + + // Scale 'heat' down from 0-255 to 0-191, + // which can then be easily divided into three + // equal 'thirds' of 64 units each. + uint8_t t192 = scale8_video( temperature, 191); + + // calculate a value that ramps up from + // zero to 255 in each 'third' of the scale. + uint8_t heatramp = t192 & 0x3F; // 0..63 + heatramp <<= 2; // scale up to 0..252 + + // now figure out which third of the spectrum we're in: + if( t192 & 0x80) { + // we're in the hottest third + heatcolor.r = 255; // full red + heatcolor.g = 255; // full green + heatcolor.b = heatramp; // ramp up blue + + } else if( t192 & 0x40 ) { + // we're in the middle third + heatcolor.r = 255; // full red + heatcolor.g = heatramp; // ramp up green + heatcolor.b = 0; // no blue + + } else { + // we're in the coolest third + heatcolor.r = heatramp; // ramp up red + heatcolor.g = 0; // no green + heatcolor.b = 0; // no blue + } + + return heatcolor; +} + + +/// Helper function to divide a number by 16, aka four logical shift right (LSR)'s. +/// On avr-gcc, "u8 >> 4" generates a loop, which is big, and slow. +/// merely forcing it to be four /=2's causes avr-gcc to emit +/// a SWAP instruction followed by an AND 0x0F, which is faster, and smaller. +inline uint8_t lsrX4( uint8_t dividend) __attribute__((always_inline)); +inline uint8_t lsrX4( uint8_t dividend) +{ +#if defined(__AVR__) + dividend /= 2; + dividend /= 2; + dividend /= 2; + dividend /= 2; +#else + dividend >>= 4; +#endif + return dividend; +} + + +CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + index = map8(index, 0, 239); // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + // const CRGB* entry = &(pal[0]) + hi4; + // since hi4 is always 0..15, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi4 X sizeof(CRGB), giving hi4XsizeofCRGB; + uint8_t hi4XsizeofCRGB = hi4 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB); + + uint8_t blend = lo4 && (blendType != NOBLEND); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + + if( blend ) { + + if( hi4 == 15 ) { + entry = &(pal[0]); + } else { + ++entry; + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + // rgb1.nscale8(f1); + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + ++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; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++green1; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++blue1; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + +CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + index = map8(index, 0, 239); // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi4 ); + + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo4 && (blendType != NOBLEND); + + if( blend ) { + + if( hi4 == 15 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi4 ); + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + ++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; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++green1; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++blue1; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + index = map8(index, 0, 247); // Blend range is affected by lo3 blend of values, remap to avoid wrapping + } + + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + // const CRGB* entry = &(pal[0]) + hi5; + // since hi5 is always 0..31, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi5 X sizeof(CRGB), giving hi5XsizeofCRGB; + uint8_t hi5XsizeofCRGB = hi5 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCRGB); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + ++entry; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + + } + + if( brightness != 255) { + if( brightness ) { + ++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; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++green1; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++blue1; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + index = map8(index, 0, 247); // Blend range is affected by lo3 blend of values, remap to avoid wrapping + } + + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi5); + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi5 ); + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + ++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; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++green1; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + ++blue1; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + + +CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) +{ + const CRGB* entry = &(pal[0]) + index; + + uint8_t red = entry->red; + uint8_t green = entry->green; + uint8_t blue = entry->blue; + + if( brightness != 255) { + ++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); + cleanup_R1(); + } + + return CRGB( red, green, blue); +} + + +CHSV ColorFromPalette( const CHSVPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + index = map8(index, 0, 239); // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + // CRGB rgb1 = pal[ hi4]; + const CHSV* entry = &(pal[0]) + hi4; + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo4 && (blendType != NOBLEND); + + if( blend ) { + + if( hi4 == 15 ) { + entry = &(pal[0]); + } else { + ++entry; + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 256 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + + +CHSV ColorFromPalette( const CHSVPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + index = map8(index, 0, 247); // Blend range is affected by lo3 blend of values, remap to avoid wrapping + } + + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + uint8_t hi5XsizeofCHSV = hi5 * sizeof(CHSV); + const CHSV* entry = (CHSV*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCHSV); + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + ++entry; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 256 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + +CHSV ColorFromPalette( const CHSVPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) +{ + CHSV hsv = *( &(pal[0]) + index ); + + if( brightness != 255) { + hsv.value = scale8_video( hsv.value, brightness); + } + + return hsv; +} + + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256) +{ + 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) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); + } +} + + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; ++i) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; ++i) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256) +{ + 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) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + + + +#if 0 +// replaced by PartyColors_p +void SetupPartyColors(CRGBPalette16& pal) +{ + fill_gradient( pal, 0, CHSV( HUE_PURPLE,255,255), 7, CHSV(HUE_YELLOW - 18,255,255), FORWARD_HUES); + fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES); +} +#endif + + +void nblendPaletteTowardPalette( CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges) +{ + uint8_t* p1; + uint8_t* p2; + uint8_t changes = 0; + + p1 = (uint8_t*)current.entries; + p2 = (uint8_t*)target.entries; + + const uint8_t totalChannels = sizeof(CRGBPalette16); + 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 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]; } + } + + // if we've hit the maximum number of changes, exit + if( changes >= maxChanges) { break; } + } +} + + +uint8_t applyGamma_video( uint8_t brightness, float gamma) +{ + float orig; + float adj; + orig = (float)(brightness) / (255.0); + adj = pow( orig, gamma) * (255.0); + uint8_t result = (uint8_t)(adj); + if( (brightness > 0) && (result == 0)) { + result = 1; // never gamma-adjust a positive number down to zero + } + return result; +} + +CRGB applyGamma_video( const CRGB& orig, float gamma) +{ + CRGB adj; + adj.r = applyGamma_video( orig.r, gamma); + adj.g = applyGamma_video( orig.g, gamma); + adj.b = applyGamma_video( orig.b, gamma); + return adj; +} + +CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB) +{ + CRGB adj; + adj.r = applyGamma_video( orig.r, gammaR); + adj.g = applyGamma_video( orig.g, gammaG); + adj.b = applyGamma_video( orig.b, gammaB); + return adj; +} + +CRGB& napplyGamma_video( CRGB& rgb, float gamma) +{ + rgb = applyGamma_video( rgb, gamma); + return rgb; +} + +CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB) +{ + rgb = applyGamma_video( rgb, gammaR, gammaG, gammaB); + return rgb; +} + +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma) +{ + 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) { + rgbarray[i] = applyGamma_video( rgbarray[i], gammaR, gammaG, gammaB); + } +} + + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/colorutils.h b/lib/FastLED/src/colorutils.h new file mode 100644 index 00000000..7ebf4e8e --- /dev/null +++ b/lib/FastLED/src/colorutils.h @@ -0,0 +1,2229 @@ +#ifndef __INC_COLORUTILS_H +#define __INC_COLORUTILS_H + +/// @file colorutils.h +/// Utility functions for color fill, palettes, blending, and more + +#include "FastLED.h" +#include "pixeltypes.h" +#include "fastled_progmem.h" + +FASTLED_NAMESPACE_BEGIN + +/// @defgroup ColorUtils Color Utility Functions +/// A variety of functions for working with color, palettes, and leds +/// @{ + +/// @defgroup ColorFills Color Fill Functions +/// Functions for filling LED arrays with colors and gradients +/// @{ + +/// Fill a range of LEDs with a solid color. +/// @param targetArray a pointer to the LED array to fill +/// @param numToFill the number of LEDs to fill in the array +/// @param color the color to fill with +void fill_solid( struct CRGB * targetArray, int numToFill, + const struct CRGB& color); + +/// @copydoc fill_solid() +void fill_solid( struct CHSV* targetArray, int numToFill, + const struct CHSV& color); + + +/// Fill a range of LEDs with a rainbow of colors. +/// The colors making up the rainbow are at full saturation and full +/// value (brightness). +/// @param targetArray a pointer to the LED array to fill +/// @param numToFill the number of LEDs to fill in the array +/// @param initialhue the starting hue for the rainbow +/// @param deltahue how many hue values to advance for each LED +void fill_rainbow( struct CRGB * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue = 5); + +/// @copydoc fill_rainbow() +void fill_rainbow( struct CHSV * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue = 5); + + +/// Fill a range of LEDs with a rainbow of colors, so that the hues +/// are continuous between the end of the strip and the beginning. +/// The colors making up the rainbow are at full saturation and full +/// value (brightness). +/// @param targetArray a pointer to the LED array to fill +/// @param numToFill the number of LEDs to fill in the array +/// @param initialhue the starting hue for the rainbow +/// @param reversed whether to progress through the rainbow hues backwards +void fill_rainbow_circular(struct CRGB* targetArray, int numToFill, + uint8_t initialhue, bool reversed=false); + +/// @copydoc fill_rainbow_circular() +void fill_rainbow_circular(struct CHSV* targetArray, int numToFill, + uint8_t initialhue, bool reversed=false); + + +/// Hue direction for calculating fill gradients. +/// Since "hue" is a value around a color wheel, there are always two directions +/// to sweep from one hue to another. +typedef enum { + FORWARD_HUES, ///< Hue always goes clockwise around the color wheel + BACKWARD_HUES, ///< Hue always goes counter-clockwise around the color wheel + SHORTEST_HUES, ///< Hue goes whichever way is shortest + LONGEST_HUES ///< Hue goes whichever way is longest +} TGradientDirectionCode; + + +/// ANSI: signed short _Accum. 8 bits int, 7 bits fraction +/// @see accum88 +#define saccum87 int16_t + + +/// Fill a range of LEDs with a smooth HSV gradient between two HSV colors. +/// This function can write the gradient colors either: +/// +/// 1. Into an array of CRGBs (e.g., an leds[] array, or a CRGB palette) +/// 2. Into an array of CHSVs (e.g. a CHSV palette). +/// +/// In the case of writing into a CRGB array, the gradient is +/// computed in HSV space, and then HSV values are converted to RGB +/// as they're written into the CRGB array. +/// @param targetArray a pointer to the color array to fill +/// @param startpos the starting position in the array +/// @param startcolor the starting color for the gradient +/// @param endpos the ending position in the array +/// @param endcolor the end color for the gradient +/// @param directionCode the direction to travel around the color wheel +template +void fill_gradient( T* targetArray, + uint16_t startpos, CHSV startcolor, + uint16_t endpos, CHSV endcolor, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + // if the points are in the wrong order, straighten them + if( endpos < startpos ) { + uint16_t t = endpos; + CHSV tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + + // If we're fading toward black (val=0) or white (sat=0), + // then set the endhue to the starthue. + // This lets us ramp smoothly to black or white, regardless + // of what 'hue' was set in the endcolor (since it doesn't matter) + if( endcolor.value == 0 || endcolor.saturation == 0) { + endcolor.hue = startcolor.hue; + } + + // Similarly, if we're fading in from black (val=0) or white (sat=0) + // then set the starthue to the endhue. + // This lets us ramp smoothly up from black or white, regardless + // of what 'hue' was set in the startcolor (since it doesn't matter) + if( startcolor.value == 0 || startcolor.saturation == 0) { + startcolor.hue = endcolor.hue; + } + + saccum87 huedistance87; + saccum87 satdistance87; + saccum87 valdistance87; + + satdistance87 = (endcolor.sat - startcolor.sat) << 7; + valdistance87 = (endcolor.val - startcolor.val) << 7; + + uint8_t huedelta8 = endcolor.hue - startcolor.hue; + + if( directionCode == SHORTEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 > 127) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == LONGEST_HUES ) { + directionCode = FORWARD_HUES; + if( huedelta8 < 128) { + directionCode = BACKWARD_HUES; + } + } + + if( directionCode == FORWARD_HUES) { + huedistance87 = huedelta8 << 7; + } + else /* directionCode == BACKWARD_HUES */ + { + huedistance87 = (uint8_t)(256 - huedelta8) << 7; + huedistance87 = -huedistance87; + } + + uint16_t pixeldistance = endpos - startpos; + int16_t divisor = pixeldistance ? pixeldistance : 1; + + saccum87 huedelta87 = huedistance87 / divisor; + saccum87 satdelta87 = satdistance87 / divisor; + saccum87 valdelta87 = valdistance87 / divisor; + + huedelta87 *= 2; + satdelta87 *= 2; + valdelta87 *= 2; + + accum88 hue88 = startcolor.hue << 8; + accum88 sat88 = startcolor.sat << 8; + accum88 val88 = startcolor.val << 8; + for( uint16_t i = startpos; i <= endpos; ++i) { + targetArray[i] = CHSV( hue88 >> 8, sat88 >> 8, val88 >> 8); + hue88 += huedelta87; + sat88 += satdelta87; + val88 += valdelta87; + } +} + + +/// Fill a range of LEDs with a smooth HSV gradient between two HSV colors. +/// @see fill_gradient() +/// @param targetArray a pointer to the color array to fill +/// @param numLeds the number of LEDs to fill +/// @param c1 the starting color in the gradient +/// @param c2 the end color for the gradient +/// @param directionCode the direction to travel around the color wheel +template +void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + uint16_t last = numLeds - 1; + fill_gradient( targetArray, 0, c1, last, c2, directionCode); +} + +/// Fill a range of LEDs with a smooth HSV gradient between three HSV colors. +/// @see fill_gradient() +/// @param targetArray a pointer to the color array to fill +/// @param numLeds the number of LEDs to fill +/// @param c1 the starting color in the gradient +/// @param c2 the middle color for the gradient +/// @param c3 the end color for the gradient +/// @param directionCode the direction to travel around the color wheel +template +void fill_gradient( T* targetArray, uint16_t numLeds, + const CHSV& c1, const CHSV& c2, const CHSV& c3, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + uint16_t half = (numLeds / 2); + uint16_t last = numLeds - 1; + fill_gradient( targetArray, 0, c1, half, c2, directionCode); + fill_gradient( targetArray, half, c2, last, c3, directionCode); +} + +/// Fill a range of LEDs with a smooth HSV gradient between four HSV colors. +/// @see fill_gradient() +/// @param targetArray a pointer to the color array to fill +/// @param numLeds the number of LEDs to fill +/// @param c1 the starting color in the gradient +/// @param c2 the first middle color for the gradient +/// @param c3 the second middle color for the gradient +/// @param c4 the end color for the gradient +/// @param directionCode the direction to travel around the color wheel +template +void fill_gradient( T* targetArray, uint16_t numLeds, + const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4, + TGradientDirectionCode directionCode = SHORTEST_HUES ) +{ + uint16_t onethird = (numLeds / 3); + uint16_t twothirds = ((numLeds * 2) / 3); + uint16_t last = numLeds - 1; + fill_gradient( targetArray, 0, c1, onethird, c2, directionCode); + fill_gradient( targetArray, onethird, c2, twothirds, c3, directionCode); + fill_gradient( targetArray, twothirds, c3, last, c4, directionCode); +} + +/// Convenience synonym +#define fill_gradient_HSV fill_gradient + + +/// Fill a range of LEDs with a smooth RGB gradient between two RGB colors. +/// Unlike HSV, there is no "color wheel" in RGB space, and therefore there's only one +/// "direction" for the gradient to go. This means there's no TGradientDirectionCode +/// parameter for direction. +/// @param leds a pointer to the LED array to fill +/// @param startpos the starting position in the array +/// @param startcolor the starting color for the gradient +/// @param endpos the ending position in the array +/// @param endcolor the end color for the gradient +void fill_gradient_RGB( CRGB* leds, + uint16_t startpos, CRGB startcolor, + uint16_t endpos, CRGB endcolor ); + +/// Fill a range of LEDs with a smooth RGB gradient between two RGB colors. +/// @see fill_gradient_RGB() +/// @param leds a pointer to the LED array to fill +/// @param numLeds the number of LEDs to fill +/// @param c1 the starting color in the gradient +/// @param c2 the end color for the gradient +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2); + +/// Fill a range of LEDs with a smooth RGB gradient between three RGB colors. +/// @see fill_gradient_RGB() +/// @param leds a pointer to the LED array to fill +/// @param numLeds the number of LEDs to fill +/// @param c1 the starting color in the gradient +/// @param c2 the middle color for the gradient +/// @param c3 the end color for the gradient +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3); + +/// Fill a range of LEDs with a smooth RGB gradient between four RGB colors. +/// @see fill_gradient_RGB() +/// @param leds a pointer to the LED array to fill +/// @param numLeds the number of LEDs to fill +/// @param c1 the starting color in the gradient +/// @param c2 the first middle color for the gradient +/// @param c3 the second middle color for the gradient +/// @param c4 the end color for the gradient +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4); + +/// @} ColorFills + + +/// @defgroup ColorFades Color Fade Functions +/// Functions for fading LED arrays +/// @{ + +/// Reduce the brightness of an array of pixels all at once. +/// Guaranteed to never fade all the way to black. +/// @param leds a pointer to the LED array to fade +/// @param num_leds the number of LEDs to fade +/// @param fadeBy how much to fade each LED +void fadeLightBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); + +/// @copydoc fadeLightBy() +void fade_video( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); + +/// Scale the brightness of an array of pixels all at once. +/// Guaranteed to never fade all the way to black. +/// @param leds a pointer to the LED array to scale +/// @param num_leds the number of LEDs to scale +/// @param scale how much to scale each LED +void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale); + + +/// Reduce the brightness of an array of pixels all at once. +/// This function will eventually fade all the way to black. +/// @param leds a pointer to the LED array to fade +/// @param num_leds the number of LEDs to fade +/// @param fadeBy how much to fade each LED +void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); + +/// @copydoc fadeToBlackBy() +void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); + + +/// Scale the brightness of an array of pixels all at once. +/// This function will eventually fade all the way to black, even +/// if "scale" is not zero. +/// @param leds a pointer to the LED array to scale +/// @param num_leds the number of LEDs to scale +/// @param scale how much to scale each LED +void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale); + + +/// Reduce the brightness of an array of pixels as thought it were seen through +/// a transparent filter with the specified color. +/// For example, if the colormask if CRGB(200, 100, 50), then the pixels' red will +/// be faded to 200/256ths, their green to 100/256ths, and their blue to 50/256ths. +/// This particular example will give a "hot fade" look, with white fading to yellow, +/// then red, then black. You can also use colormasks like CRGB::Blue to zero out the +/// red and green elements, leaving blue (largely) the same. +/// @param leds a pointer to the LED array to fade +/// @param numLeds the number of LEDs to fade +/// @param colormask the color mask to fade with +void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask); + +/// @} ColorFades + + +/// @defgroup ColorBlends Color Blending Functions +/// Functions for blending colors together +/// @{ + +/// Computes a new color blended some fraction of the way between two other colors. +/// @param p1 the first color to blend +/// @param p2 the second color to blend +/// @param amountOfP2 the fraction of p2 to blend into p1 +CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ); + +/// @copydoc blend(const CRGB&, const CRGB&, fract8) +/// @param directionCode the direction to travel around the color wheel +CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, + TGradientDirectionCode directionCode = SHORTEST_HUES ); + + +/// Computes a new blended array of colors, each some fraction of the way between +/// corresponding elements of two source arrays of colors. +/// Useful for blending palettes. +/// @param src1 the first array of colors to blend +/// @param src2 the second array of colors to blend +/// @param dest the destination array for the colors +/// @param count the number of LEDs to blend +/// @param amountOfsrc2 the fraction of src2 to blend into src1 +CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, + uint16_t count, fract8 amountOfsrc2 ); + +/// @copydoc blend(const CRGB*, const CRGB*, CRGB*, uint16_t, fract8) +/// @param directionCode the direction to travel around the color wheel +CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, + uint16_t count, fract8 amountOfsrc2, + TGradientDirectionCode directionCode = SHORTEST_HUES ); + + +/// Destructively modifies one color, blending in a given fraction of an overlay color +/// @param existing the color to modify +/// @param overlay the color to blend into existing +/// @param amountOfOverlay the fraction of overlay to blend into existing +CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ); + +/// @copydoc nblend(CRGB&, const CRGB&, fract8) +/// @param directionCode the direction to travel around the color wheel +CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, + TGradientDirectionCode directionCode = SHORTEST_HUES ); + + +/// Destructively blends a given fraction of a color array into an existing color array +/// @param existing the color array to modify +/// @param overlay the color array to blend into existing +/// @param count the number of colors to process +/// @param amountOfOverlay the fraction of overlay to blend into existing +void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay); + +/// @copydoc nblend(CRGB*, CRGB*, uint16_t, fract8) +/// @param directionCode the direction to travel around the color wheel +void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, + TGradientDirectionCode directionCode = SHORTEST_HUES); + +/// @} ColorBlends + + +/// @defgroup ColorBlurs Color Blurring Functions +/// Functions for blurring colors +/// @{ + +/// One-dimensional blur filter. +/// Spreads light to 2 line neighbors. +/// * 0 = no spread at all +/// * 64 = moderate spreading +/// * 172 = maximum smooth, even spreading +/// * 173..255 = wider spreading, but increasing flicker +/// +/// Total light is NOT entirely conserved, so many repeated +/// calls to 'blur' will also result in the light fading, +/// eventually all the way to black; this is by design so that +/// it can be used to (slowly) clear the LEDs to black. +/// @param leds a pointer to the LED array to blur +/// @param numLeds the number of LEDs to blur +/// @param blur_amount the amount of blur to apply +void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount); + +/// Two-dimensional blur filter. +/// Spreads light to 8 XY neighbors. +/// * 0 = no spread at all +/// * 64 = moderate spreading +/// * 172 = maximum smooth, even spreading +/// * 173..255 = wider spreading, but increasing flicker +/// +/// Total light is NOT entirely conserved, so many repeated +/// calls to 'blur' will also result in the light fading, +/// eventually all the way to black; this is by design so that +/// it can be used to (slowly) clear the LEDs to black. +/// @param leds a pointer to the LED array to blur +/// @param width the width of the matrix +/// @param height the height of the matrix +/// @param blur_amount the amount of blur to apply +void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + + +/// Perform a blur1d() on every row of a rectangular matrix +/// @see blur1d() +/// @param leds a pointer to the LED array to blur +/// @param width the width of the matrix +/// @param height the height of the matrix +/// @param blur_amount the amount of blur to apply +void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + +/// Perform a blur1d() on every column of a rectangular matrix +/// @copydetails blurRows() +void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + +/// @} ColorBlurs + + +/// @addtogroup ColorFills +/// @{ + +/// Approximates a "black body radiation" spectrum for +/// a given "heat" level. This is useful for animations of "fire". +/// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot). +/// This is NOT a chromatically correct "black body radiation" +/// spectrum, but it's surprisingly close, and it's fast and small. +CRGB HeatColor( uint8_t temperature); + +/// @} ColorFills +/// @} ColorUtils + + +/// @defgroup ColorPalettes Color Palettes +/// Functions and class definitions for color palettes. +/// +/// RGB palettes map an 8-bit value (0-255) to an RGB color. +/// +/// You can create any color palette you wish; a couple of starters +/// are provided: ForestColors_p, CloudColors_p, LavaColors_p, OceanColors_p, +/// RainbowColors_p, and RainbowStripeColors_p. +/// +/// Palettes come in the traditional 256-entry variety, which take +/// up 768 bytes of RAM, and lightweight 16-entry varieties. The 16-entry +/// variety automatically interpolates between its entries to produce +/// a full 256-element color map, but at a cost of only 48 bytes of RAM. +/// +/// Basic operation is like this (using the 16-entry variety): +/// +/// 1. Declare your palette storage: +/// @code{.cpp} +/// CRGBPalette16 myPalette; +/// @endcode +/// +/// 2. Fill `myPalette` with your own 16 colors, or with a preset color scheme. +/// You can specify your 16 colors a variety of ways: +/// @code{.cpp} +/// CRGBPalette16 myPalette( +/// CRGB::Black, +/// CRGB::Black, +/// CRGB::Red, +/// CRGB::Yellow, +/// CRGB::Green, +/// CRGB::Blue, +/// CRGB::Purple, +/// CRGB::Black, +/// +/// 0x100000, +/// 0x200000, +/// 0x400000, +/// 0x800000, +/// +/// CHSV( 30,255,255), +/// CHSV( 50,255,255), +/// CHSV( 70,255,255), +/// CHSV( 90,255,255) +/// ); +/// @endcode +/// +/// Or you can initiaize your palette with a preset color scheme: +/// @code{.cpp} +/// myPalette = RainbowStripesColors_p; +/// @endcode +/// +/// 3. Any time you want to set a pixel to a color from your palette, use +/// `ColorFromPalette()` as shown: +/// +/// @code{.cpp} +/// uint8_t index = /* any value 0-255 */; +/// leds[i] = ColorFromPalette(myPalette, index); +/// @endcode +/// +/// Even though your palette has only 16 explicily defined entries, you +/// can use an "index" from 0-255. The 16 explicit palette entries will +/// be spread evenly across the 0-255 range, and the intermedate values +/// will be RGB-interpolated between adjacent explicit entries. +/// +/// It's easier to use than it sounds. +/// +/// @{ + + +/// @defgroup PaletteClasses Palette Classes +/// Class definitions for color palettes. +/// @todo For documentation purposes it would be nice to reorder these +/// definitions by type and in ascending number of entries. +/// +/// @{ + +class CRGBPalette16; +class CRGBPalette32; +class CRGBPalette256; +class CHSVPalette16; +class CHSVPalette32; +class CHSVPalette256; + +typedef uint32_t TProgmemRGBPalette16[16]; ///< CRGBPalette16 entries stored in PROGMEM memory +typedef uint32_t TProgmemHSVPalette16[16]; ///< CHSVPalette16 entries stored in PROGMEM memory +/// Alias for TProgmemRGBPalette16 +#define TProgmemPalette16 TProgmemRGBPalette16 +typedef uint32_t TProgmemRGBPalette32[32]; ///< CRGBPalette32 entries stored in PROGMEM memory +typedef uint32_t TProgmemHSVPalette32[32]; ///< CHSVPalette32 entries stored in PROGMEM memory +/// Alias for TProgmemRGBPalette32 +#define TProgmemPalette32 TProgmemRGBPalette32 + +/// Byte of an RGB gradient, stored in PROGMEM memory +typedef const uint8_t TProgmemRGBGradientPalette_byte; +/// Pointer to bytes of an RGB gradient, stored in PROGMEM memory +/// @see DEFINE_GRADIENT_PALETTE +/// @see DECLARE_GRADIENT_PALETTE +typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes; +/// Alias of ::TProgmemRGBGradientPalette_bytes +typedef TProgmemRGBGradientPalette_bytes TProgmemRGBGradientPalettePtr; + +/// Struct for digesting gradient pointer data into its components. +/// This is used when loading a gradient stored in PROGMEM or on +/// the heap into a palette. The pointer is dereferenced and interpreted as +/// this struct, so the component parts can be addressed and copied by name. +typedef union { + struct { + uint8_t index; ///< index of the color entry in the gradient + uint8_t r; ///< CRGB::red channel value of the color entry + uint8_t g; ///< CRGB::green channel value of the color entry + uint8_t b; ///< CRGB::blue channel value of the color entry + }; + uint32_t dword; ///< values as a packed 32-bit double word + uint8_t bytes[4]; ///< values as an array +} TRGBGradientPaletteEntryUnion; + +typedef uint8_t TDynamicRGBGradientPalette_byte; ///< Byte of an RGB gradient entry, stored in dynamic (heap) memory +typedef const TDynamicRGBGradientPalette_byte *TDynamicRGBGradientPalette_bytes; ///< Pointer to bytes of an RGB gradient, stored in dynamic (heap) memory +typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr; ///< Alias of ::TDynamicRGBGradientPalette_bytes + +/// @} + + +/// @defgroup PaletteUpscale Palette Upscaling Functions +/// Functions to upscale palettes from one type to another. +/// @{ + +/// Convert a 16-entry palette to a 256-entry palette +/// @param srcpal16 the source palette to upscale +/// @param destpal256 the destination palette for the upscaled data +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256); +/// @copydoc UpscalePalette(const struct CRGBPalette16&, struct CRGBPalette256&) +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256); + +/// Convert a 16-entry palette to a 32-entry palette +/// @param srcpal16 the source palette to upscale +/// @param destpal32 the destination palette for the upscaled data +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32); +/// @copydoc UpscalePalette(const struct CRGBPalette16&, struct CRGBPalette32&) +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32); + +/// Convert a 32-entry palette to a 256-entry palette +/// @param srcpal32 the source palette to upscale +/// @param destpal256 the destination palette for the upscaled data +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256); +/// @copydoc UpscalePalette(const struct CRGBPalette32&, struct CRGBPalette256&) +void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256); + +/// @} PaletteUpscale + + +/// @addtogroup PaletteClasses +/// @{ + +/// HSV color palette with 16 discrete values +class CHSVPalette16 { +public: + CHSV entries[16]; ///< the color entries that make up the palette + + /// @copydoc CHSV::CHSV() + CHSVPalette16() {}; + + /// Create palette from 16 CHSV values + CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) + { + entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; + entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; + entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; + entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; + }; + + /// Copy constructor + CHSVPalette16( const CHSVPalette16& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + + /// @copydoc CHSVPalette16(const CHSVPalette16& rhs) + CHSVPalette16& operator=( const CHSVPalette16& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + /// Create palette from palette stored in PROGMEM + CHSVPalette16( const TProgmemHSVPalette16& rhs) + { + 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; + entries[i].val = xyz.blue; + } + } + + /// @copydoc CHSVPalette16(const TProgmemHSVPalette16&) + CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) + { + 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; + entries[i].val = xyz.blue; + } + return *this; + } + + /// Array access operator to index into the gradient entries + /// @param x the index to retrieve + /// @returns reference to an entry in the palette's color array + /// @note This does not perform any interpolation like ColorFromPalette(), + /// it accesses the underlying entries that make up the gradient. Beware + /// of bounds issues! + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc operator[] + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc operator[] + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// @copydoc operator[] + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CHSV*() + { + return &(entries[0]); + } + + /// Check if two palettes have the same color entries + bool operator==( const CHSVPalette16 &rhs) const + { + 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) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + + /// Check if two palettes do not have the same color entries + bool operator!=( const CHSVPalette16 &rhs) const + { + return !( *this == rhs); + } + + /// Create palette filled with one color + /// @param c1 the color to fill the palette with + CHSVPalette16( const CHSV& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + + /// Create palette with a gradient from one color to another + /// @param c1 the starting color for the gradient + /// @param c2 the end color for the gradient + CHSVPalette16( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 16, c1, c2); + } + + /// Create palette with three-color gradient + /// @param c1 the starting color for the gradient + /// @param c2 the middle color for the gradient + /// @param c3 the end color for the gradient + CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3); + } + + /// Create palette with four-color gradient + /// @param c1 the starting color for the gradient + /// @param c2 the first middle color for the gradient + /// @param c3 the second middle color for the gradient + /// @param c4 the end color for the gradient + CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); + } + +}; + +/// HSV color palette with 256 discrete values +class CHSVPalette256 { +public: + CHSV entries[256]; ///< @copydoc CHSVPalette16::entries + + /// @copydoc CHSVPalette16::CHSVPalette16() + CHSVPalette256() {}; + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&, + /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, + /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, + /// const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CHSVPalette256( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) + { + CHSVPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, + c08,c09,c10,c11,c12,c13,c14,c15); + *this = p16; + }; + + /// Copy constructor + CHSVPalette256( const CHSVPalette256& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + /// @copydoc CHSVPalette256( const CHSVPalette256&) + CHSVPalette256& operator=( const CHSVPalette256& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + /// Create upscaled palette from 16-entry palette + CHSVPalette256( const CHSVPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + /// @copydoc CHSVPalette256( const CHSVPalette16&) + CHSVPalette256& operator=( const CHSVPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + CHSVPalette256( const TProgmemRGBPalette16& rhs) + { + CHSVPalette16 p16(rhs); + *this = p16; + } + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + CHSVPalette256& operator=( const TProgmemRGBPalette16& rhs) + { + CHSVPalette16 p16(rhs); + *this = p16; + return *this; + } + + /// @copydoc CHSVPalette16::operator[] + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + /// @copydoc operator[] + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc operator[] + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + /// @copydoc operator[] + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CHSV*() + { + return &(entries[0]); + } + + /// @copydoc CHSVPalette16::operator== + bool operator==( const CHSVPalette256 &rhs) const + { + 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) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + + /// @copydoc CHSVPalette16::operator!= + bool operator!=( const CHSVPalette256 &rhs) const + { + return !( *this == rhs); + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) + CHSVPalette256( const CHSV& c1) + { + fill_solid( &(entries[0]), 256, c1); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) + CHSVPalette256( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 256, c1, c2); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) + CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); + } +}; + +/// RGB color palette with 16 discrete values +class CRGBPalette16 { +public: + CRGB entries[16]; ///< @copydoc CHSVPalette16::entries + + /// @copydoc CRGB::CRGB() + CRGBPalette16() {}; + + /// Create palette from 16 CRGB values + CRGBPalette16( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, + const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) + { + entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; + entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; + entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; + entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; + }; + + /// Copy constructor + CRGBPalette16( const CRGBPalette16& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + /// Create palette from array of CRGB colors + CRGBPalette16( const CRGB rhs[16]) + { + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); + } + /// @copydoc CRGBPalette16(const CRGBPalette16&) + CRGBPalette16& operator=( const CRGBPalette16& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + /// Create palette from array of CRGB colors + CRGBPalette16& operator=( const CRGB rhs[16]) + { + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + /// Create palette from CHSV palette + CRGBPalette16( const CHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + /// Create palette from array of CHSV colors + CRGBPalette16( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + /// @copydoc CRGBPalette16(const CHSVPalette16&) + CRGBPalette16& operator=( const CHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + /// Create palette from array of CHSV colors + CRGBPalette16& operator=( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + /// Create palette from palette stored in PROGMEM + CRGBPalette16( const TProgmemRGBPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + } + /// @copydoc CRGBPalette16(const TProgmemRGBPalette16&) + CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + return *this; + } + + /// @copydoc CHSVPalette16::operator== + bool operator==( const CRGBPalette16 &rhs) const + { + 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) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + /// @copydoc CHSVPalette16::operator!= + bool operator!=( const CRGBPalette16 &rhs) const + { + return !( *this == rhs); + } + /// @copydoc CHSVPalette16::operator[] + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + /// @copydoc CHSVPalette16::operator[] + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc CHSVPalette16::operator[] + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + /// @copydoc CHSVPalette16::operator[] + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CRGB*() + { + return &(entries[0]); + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) + CRGBPalette16( const CHSV& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) + CRGBPalette16( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 16, c1, c2); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) + CRGBPalette16( const CRGB& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) + CRGBPalette16( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 16, c1, c2); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3, c4); + } + + /// Creates a palette from a gradient palette in PROGMEM. + /// + /// Gradient palettes are loaded into CRGBPalettes in such a way + /// that, if possible, every color represented in the gradient palette + /// is also represented in the CRGBPalette. + /// + /// For example, consider a gradient palette that is all black except + /// for a single, one-element-wide (1/256th!) spike of red in the middle: + /// @code + /// 0, 0,0,0 + /// 124, 0,0,0 + /// 125, 255,0,0 // one 1/256th-palette-wide red stripe + /// 126, 0,0,0 + /// 255, 0,0,0 + /// @endcode + /// A naive conversion of this 256-element palette to a 16-element palette + /// might accidentally completely eliminate the red spike, rendering the + /// palette completely black. + /// + /// However, the conversions provided here would attempt to include a + /// the red stripe in the output, more-or-less as faithfully as possible. + /// So in this case, the resulting CRGBPalette16 palette would have a red + /// stripe in the middle which was 1/16th of a palette wide -- the + /// narrowest possible in a CRGBPalette16. + /// + /// This means that the relative width of stripes in a CRGBPalette16 + /// will be, by definition, different from the widths in the gradient + /// palette. This code attempts to preserve "all the colors", rather than + /// the exact stripe widths at the expense of dropping some colors. + CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + /// @copydoc CRGBPalette16(TProgmemRGBGradientPalette_bytes) + CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); + ++count; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ++progent; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + /// Creates a palette from a gradient palette in dynamic (heap) memory. + /// @copydetails CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) + CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u = *(ent + count); + ++count; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ++ent; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + +}; + + +/// HSV color palette with 32 discrete values +class CHSVPalette32 { +public: + CHSV entries[32]; ///< @copydoc CHSVPalette16::entries + + /// @copydoc CHSVPalette16::CHSVPalette16() + CHSVPalette32() {}; + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&, + /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, + /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, + /// const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CHSVPalette32( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + 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) { + 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; + entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; + } + }; + + /// Copy constructor + CHSVPalette32( const CHSVPalette32& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + /// @copydoc CHSVPalette32( const CHSVPalette32&) + CHSVPalette32& operator=( const CHSVPalette32& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + CHSVPalette32( const TProgmemHSVPalette32& rhs) + { + 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; + entries[i].val = xyz.blue; + } + } + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + CHSVPalette32& operator=( const TProgmemHSVPalette32& rhs) + { + 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; + entries[i].val = xyz.blue; + } + return *this; + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CHSV*() + { + return &(entries[0]); + } + + /// @copydoc CHSVPalette16::operator== + bool operator==( const CHSVPalette32 &rhs) const + { + 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) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + /// @copydoc CHSVPalette16::operator!= + bool operator!=( const CHSVPalette32 &rhs) const + { + return !( *this == rhs); + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) + CHSVPalette32( const CHSV& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) + CHSVPalette32( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 32, c1, c2); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) + CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); + } + +}; + +/// RGB color palette with 32 discrete values +class CRGBPalette32 { +public: + CRGB entries[32]; ///< @copydoc CHSVPalette16::entries + + /// @copydoc CRGB::CRGB() + CRGBPalette32() {}; + + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, const CRGB&) + CRGBPalette32( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + 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) { + 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; + entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; + } + }; + + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGBPalette16&) + CRGBPalette32( const CRGBPalette32& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + /// Create palette from array of CRGB colors + CRGBPalette32( const CRGB rhs[32]) + { + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); + } + /// @copydoc CRGBPalette32(const CRGBPalette32&) + CRGBPalette32& operator=( const CRGBPalette32& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + /// Create palette from array of CRGB colors + CRGBPalette32& operator=( const CRGB rhs[32]) + { + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSVPalette16&) + CRGBPalette32( const CHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + /// Create palette from array of CHSV colors + CRGBPalette32( const CHSV rhs[32]) + { + for( uint8_t i = 0; i < 32; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + /// @copydoc CRGBPalette32(const CHSVPalette32&) + CRGBPalette32& operator=( const CHSVPalette32& rhs) + { + for( uint8_t i = 0; i < 32; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + /// Create palette from array of CHSV colors + CRGBPalette32& operator=( const CHSV rhs[32]) + { + for( uint8_t i = 0; i < 32; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const TProgmemRGBPalette16&) + CRGBPalette32( const TProgmemRGBPalette32& rhs) + { + for( uint8_t i = 0; i < 32; ++i) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + } + /// @copydoc CRGBPalette32(const TProgmemRGBPalette32&) + CRGBPalette32& operator=( const TProgmemRGBPalette32& rhs) + { + for( uint8_t i = 0; i < 32; ++i) { + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); + } + return *this; + } + + /// @copydoc CRGBPalette16::operator== + bool operator==( const CRGBPalette32 &rhs) const + { + 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) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + /// @copydoc CRGBPalette16::operator!= + bool operator!=( const CRGBPalette32 &rhs) const + { + return !( *this == rhs); + } + + /// @copydoc CRGBPalette16::operator[] + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + /// @copydoc CRGBPalette16::operator[] + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc CRGBPalette16::operator[] + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + /// @copydoc CRGBPalette16::operator[] + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CRGB entries making up the palette + operator CRGB*() + { + return &(entries[0]); + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&) + CRGBPalette32( const CHSV& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&) + CRGBPalette32( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 32, c1, c2); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&) + CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&) + CRGBPalette32( const CRGB& c1) + { + fill_solid( &(entries[0]), 32, c1); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&) + CRGBPalette32( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&) + CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&, const CRGB&) + CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3, c4); + } + + + /// Create upscaled palette from 16-entry palette + CRGBPalette32( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + /// @copydoc CRGBPalette32(const CRGBPalette16&) + CRGBPalette32& operator=( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const TProgmemRGBPalette16&) + CRGBPalette32( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + } + /// @copydoc CRGBPalette32(const TProgmemRGBPalette16&) + CRGBPalette32& operator=( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + return *this; + } + + + /// @copydoc CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) + CRGBPalette32( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + /// @copydoc CRGBPalette32(TProgmemRGBGradientPalette_bytes) + CRGBPalette32& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); + ++count; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ++progent; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 8; + iend8 = indexend / 8; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + /// @copydoc CRGBPalette16::loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes) + CRGBPalette32& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u = *(ent + count); + ++count; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ++ent; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 8; + iend8 = indexend / 8; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + +}; + + +/// RGB color palette with 256 discrete values +class CRGBPalette256 { +public: + CRGB entries[256]; ///< @copydoc CHSVPalette16::entries + + /// @copydoc CRGB::CRGB() + CRGBPalette256() {}; + + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, + /// const CRGB&, const CRGB&, const CRGB&, const CRGB&) + CRGBPalette256( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, + const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, + const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, + const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) + { + CRGBPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, + c08,c09,c10,c11,c12,c13,c14,c15); + *this = p16; + }; + + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGBPalette16&) + CRGBPalette256( const CRGBPalette256& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + /// Create palette from array of CRGB colors + CRGBPalette256( const CRGB rhs[256]) + { + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); + } + /// @copydoc CRGBPalette256(const CRGBPalette256&) + CRGBPalette256& operator=( const CRGBPalette256& rhs) + { + memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + /// Create palette from array of CRGB colors + CRGBPalette256& operator=( const CRGB rhs[256]) + { + memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); + return *this; + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSVPalette16&) + CRGBPalette256( const CHSVPalette256& rhs) + { + for( int i = 0; i < 256; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + } + /// Create palette from array of CHSV colors + CRGBPalette256( const CHSV rhs[256]) + { + for( int i = 0; i < 256; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + } + /// @copydoc CRGBPalette256(const CRGBPalette256&) + CRGBPalette256& operator=( const CHSVPalette256& rhs) + { + for( int i = 0; i < 256; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + /// Create palette from array of CHSV colors + CRGBPalette256& operator=( const CHSV rhs[256]) + { + for( int i = 0; i < 256; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + /// @copydoc CRGBPalette32::CRGBPalette32(const CRGBPalette16&) + CRGBPalette256( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + } + /// @copydoc CRGBPalette256(const CRGBPalette16&) + CRGBPalette256& operator=( const CRGBPalette16& rhs16) + { + UpscalePalette( rhs16, *this); + return *this; + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const TProgmemRGBPalette16&) + CRGBPalette256( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + } + /// @copydoc CRGBPalette256(const TProgmemRGBPalette16&) + CRGBPalette256& operator=( const TProgmemRGBPalette16& rhs) + { + CRGBPalette16 p16(rhs); + *this = p16; + return *this; + } + + /// @copydoc CRGBPalette16::operator== + bool operator==( const CRGBPalette256 &rhs) const + { + 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) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + /// @copydoc CRGBPalette16::operator!= + bool operator!=( const CRGBPalette256 &rhs) const + { + return !( *this == rhs); + } + + /// @copydoc CRGBPalette16::operator[] + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + /// @copydoc CRGBPalette16::operator[] + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc CRGBPalette16::operator[] + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + /// @copydoc CRGBPalette16::operator[] + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CRGB*() + { + return &(entries[0]); + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&) + CRGBPalette256( const CHSV& c1) + { + fill_solid( &(entries[0]), 256, c1); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&) + CRGBPalette256( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 256, c1, c2); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&) + CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); + } + + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&) + CRGBPalette256( const CRGB& c1) + { + fill_solid( &(entries[0]), 256, c1); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&) + CRGBPalette256( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB( &(entries[0]), 256, c1, c2); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&) + CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3); + } + /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&, const CRGB&) + CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) + { + fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3, c4); + } + + /// @copydoc CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) + CRGBPalette256( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + /// @copydoc CRGBPalette256(TProgmemRGBGradientPalette_bytes) + CRGBPalette256& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + while( indexstart < 255) { + ++progent; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + /// @copydoc CRGBPalette16::loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes) + CRGBPalette256& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + while( indexstart < 255) { + ++ent; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } +}; + +/// @} PaletteClasses + + +/// @defgroup PaletteColors Palette Color Functions +/// Functions to retrieve smooth color data from palettes +/// @{ + +/// Color interpolation options for palette +typedef enum { + NOBLEND=0, ///< No interpolation between palette entries + LINEARBLEND=1, ///< Linear interpolation between palette entries, with wrap-around from end to the beginning again + LINEARBLEND_NOWRAP=2 ///< Linear interpolation between palette entries, but no wrap-around +} TBlendType; + +/// Get a color from a palette. +/// These are the main functions for getting and using palette colors. Regardless +/// of the number of entries in the base palette, this function will interpolate +/// between entries to turn the discrete colors into a smooth gradient. +/// @param pal the palette to retrieve the color from +/// @param index the position in the palette to retrieve the color for (0-255) +/// @param brightness optional brightness value to scale the resulting color +/// @param blendType whether to take the palette entries directly (NOBLEND) +/// or blend linearly between palette entries (LINEARBLEND) +CRGB ColorFromPalette( const CRGBPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CRGB ColorFromPalette( const CRGBPalette256& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=NOBLEND ); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CHSV ColorFromPalette( const CHSVPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CHSV ColorFromPalette( const CHSVPalette256& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=NOBLEND ); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CRGB ColorFromPalette( const CRGBPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +/// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) +CHSV ColorFromPalette( const CHSVPalette32& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + + +/// Fill a range of LEDs with a sequence of entries from a palette +/// @tparam PALETTE the type of the palette used (auto-deduced) +/// @param L pointer to the LED array to fill +/// @param N number of LEDs to fill in the array +/// @param startIndex the starting color index in the palette +/// @param incIndex how much to increment the palette color index per LED +/// @param pal the color palette to pull colors from +/// @param brightness brightness value used to scale the resulting color +/// @param blendType whether to take the palette entries directly (NOBLEND) +/// or blend linearly between palette entries (LINEARBLEND) +template +void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, + const PALETTE& pal, uint8_t brightness=255, TBlendType blendType=LINEARBLEND) +{ + uint8_t colorIndex = startIndex; + for( uint16_t i = 0; i < N; ++i) { + L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType); + colorIndex += incIndex; + } +} + + +/// Fill a range of LEDs with a sequence of entries from a palette, so that +/// the entire palette smoothly covers the range of LEDs. +/// @tparam PALETTE the type of the palette used (auto-deduced) +/// @param L pointer to the LED array to fill +/// @param N number of LEDs to fill in the array +/// @param startIndex the starting color index in the palette +/// @param pal the color palette to pull colors from +/// @param brightness brightness value used to scale the resulting color +/// @param blendType whether to take the palette entries directly (NOBLEND) +/// or blend linearly between palette entries (LINEARBLEND) +/// @param reversed whether to progress through the palette backwards +template +void fill_palette_circular(CRGB* L, uint16_t N, uint8_t startIndex, + const PALETTE& pal, uint8_t brightness=255, TBlendType blendType=LINEARBLEND, + bool reversed=false) +{ + if (N == 0) return; // avoiding div/0 + + const uint16_t colorChange = 65535 / N; // color change for each LED, * 256 for precision + uint16_t colorIndex = ((uint16_t) startIndex) << 8; // offset for color index, with precision (*256) + + for (uint16_t i = 0; i < N; ++i) { + L[i] = ColorFromPalette(pal, (colorIndex >> 8), brightness, blendType); + if (reversed) colorIndex -= colorChange; + else colorIndex += colorChange; + } +} + + +/// Maps an array of palette color indexes into an array of LED colors. +/// +/// This function provides an easy way to create lightweight color patterns that +/// can be deployed using any palette. +/// +/// @param dataArray the source array, containing color indexes for the palette +/// @param dataCount the number of data elements in the array +/// @param targetColorArray the LED array to store the resulting colors into. Must be +/// at least as long as `dataCount`. +/// @param pal the color palette to pull colors from +/// @param brightness optional brightness value used to scale the resulting color +/// @param opacity optional opacity value for the new color. If this is 255 (default), +/// the new colors will be written to the array directly. Otherwise the existing LED +/// data will be scaled down using `CRGB::nscale8_video()` and then new colors will +/// be added on top. A higher value means that the new colors will be more visible. +/// @param blendType whether to take the palette entries directly (NOBLEND) +/// or blend linearly between palette entries (LINEARBLEND) +template +void map_data_into_colors_through_palette( + uint8_t *dataArray, uint16_t dataCount, + CRGB* targetColorArray, + const PALETTE& pal, + uint8_t brightness=255, + uint8_t opacity=255, + TBlendType blendType=LINEARBLEND) +{ + for( uint16_t i = 0; i < dataCount; ++i) { + uint8_t d = dataArray[i]; + CRGB rgb = ColorFromPalette( pal, d, brightness, blendType); + if( opacity == 255 ) { + targetColorArray[i] = rgb; + } else { + targetColorArray[i].nscale8( 256 - opacity); + rgb.nscale8_video( opacity); + targetColorArray[i] += rgb; + } + } +} + + +/// Alter one palette by making it slightly more like a "target palette". +/// Used for palette cross-fades. +/// +/// It does this by comparing each of the R, G, and B channels +/// of each entry in the current palette to the corresponding +/// entry in the target palette and making small adjustments: +/// * If the CRGB::red channel is too low, it will be increased. +/// * If the CRGB::red channel is too high, it will be slightly reduced. +/// +/// ...and so on and so forth for the CRGB::green and CRGB::blue channels. +/// +/// Additionally, there are two significant visual improvements +/// to this algorithm implemented here. First is this: +/// * When increasing a channel, it is stepped up by ONE. +/// * When decreasing a channel, it is stepped down by TWO. +/// +/// Due to the way the eye perceives light, and the way colors +/// are represented in RGB, this produces a more uniform apparent +/// brightness when cross-fading between most palette colors. +/// +/// The second visual tweak is limiting the number of changes +/// that will be made to the palette at once. If all the palette +/// entries are changed at once, it can give a muddled appearance. +/// However, if only a *few* palette entries are changed at once, +/// you get a visually smoother transition: in the middle of the +/// cross-fade your current palette will actually contain some +/// colors from the old palette, a few blended colors, and some +/// colors from the new palette. +/// +/// @param currentPalette the palette to modify +/// @param targetPalette the palette to move towards +/// @param maxChanges the maximum number of possible palette changes +/// to make to the color channels per call. The limit is 48 (16 color +/// entries times 3 channels each). The default is 24, meaning that +/// only half of the palette entries can be changed per call. +/// @warning The palette passed as `currentPalette` will be modified! Be sure +/// to make a copy beforehand if needed. +/// @todo Shouldn't the `targetPalette` be `const`? +void nblendPaletteTowardPalette( CRGBPalette16& currentPalette, + CRGBPalette16& targetPalette, + uint8_t maxChanges=24); + +/// @} PaletteColors + + +/// Defines a static RGB palette very compactly using a series +/// of connected color gradients. +/// +/// For example, if you want the first 3/4ths of the palette to be a slow +/// gradient ramping from black to red, and then the remaining 1/4 of the +/// palette to be a quicker ramp to white, you specify just three points: the +/// starting black point (at index 0), the red midpoint (at index 192), +/// and the final white point (at index 255). It looks like this: +/// @code +/// index: 0 192 255 +/// |----------r-r-r-rrrrrrrrRrRrRrRrRRRR-|-RRWRWWRWWW-| +/// color: (0,0,0) (255,0,0) (255,255,255) +/// @endcode +/// +/// Here's how you'd define that gradient palette using this macro: +/// @code{.cpp} +/// DEFINE_GRADIENT_PALETTE( black_to_red_to_white_p ) { +/// 0, 0, 0, 0, /* at index 0, black(0,0,0) */ +/// 192, 255, 0, 0, /* at index 192, red(255,0,0) */ +/// 255, 255, 255, 255 /* at index 255, white(255,255,255) */ +/// }; +/// @endcode +/// +/// This format is designed for compact storage. The example palette here +/// takes up just 12 bytes of PROGMEM (flash) storage, and zero bytes +/// of SRAM when not currently in use. +/// +/// To use one of these gradient palettes, simply assign it into a +/// CRGBPalette16 or a CRGBPalette256, like this: +/// @code{.cpp} +/// CRGBPalette16 pal = black_to_red_to_white_p; +/// @endcode +/// +/// When the assignment is made, the gradients are expanded out into +/// either 16 or 256 palette entries, depending on the kind of palette +/// object they're assigned to. +/// +/// @warning The last "index" position **MUST** be 255! Failure to end +/// with index 255 will result in program hangs or crashes. +/// @par +/// @warning At this point, these gradient palette definitions **MUST** +/// be stored in PROGMEM on AVR-based Arduinos. If you use the +/// `DEFINE_GRADIENT_PALETTE` macro, this is taken of automatically. +/// +#define DEFINE_GRADIENT_PALETTE(X) \ + FL_ALIGN_PROGMEM \ + extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM = + +/// Forward-declaration macro for DEFINE_GRADIENT_PALETTE(X) +#define DECLARE_GRADIENT_PALETTE(X) \ + FL_ALIGN_PROGMEM \ + extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM + +/// @} ColorPalettes + + +/// @defgroup GammaFuncs Gamma Adjustment Functions +/// Functions for applying gamma adjustments to LED data. +/// +/// Gamma correction tries to compensate for the non-linear +/// manner in which humans perceive light and color. Gamma +/// correction is applied using the following expression: +/// @code{.cpp} +/// output = pow(input / 255.0, gamma) * 255.0; +/// @endcode +/// +/// Larger gamma values result in darker images that have more contrast. +/// Lower gamma values result in lighter images with less contrast. +/// +/// These functions apply either: +/// * a single gamma adjustment to a single scalar value +/// * a single gamma adjustment to each channel of a CRGB color, or +/// * different gamma adjustments for each channel of a CRGB color +/// +/// Note that the gamma is specified as a traditional floating point value, +/// e.g., "2.5", and as such these functions should not be called in +/// your innermost pixel loops, or in animations that are extremely +/// low on program storage space. Nevertheless, if you need these +/// functions, here they are. +/// +/// Furthermore, bear in mind that CRGB LEDs have only eight bits +/// per channel of color resolution, and that very small, subtle shadings +/// may not be visible. +/// +/// @see @ref Dimming +/// @see https://en.wikipedia.org/wiki/Gamma_correction +/// @{ + +/// Applies a gamma adjustment to a color channel +/// @param brightness the value of the color data +/// @param gamma the gamma value to apply +/// @returns the color data, adjusted for gamma +uint8_t applyGamma_video( uint8_t brightness, float gamma); + +/// Applies a gamma adjustment to a color +/// @param orig the color to apply an adjustment to +/// @param gamma the gamma value to apply +/// @returns copy of the CRGB object with gamma adjustment applied +CRGB applyGamma_video( const CRGB& orig, float gamma); + +/// Applies a gamma adjustment to a color +/// @param orig the color to apply an adjustment to +/// @param gammaR the gamma value to apply to the CRGB::red channel +/// @param gammaG the gamma value to apply to the CRGB::green channel +/// @param gammaB the gamma value to apply to the CRGB::blue channel +/// @returns copy of the CRGB object with gamma adjustment applied +CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB); + + +/// Destructively applies a gamma adjustment to a color +/// @param rgb the color to apply an adjustment to (modified in place) +/// @param gamma the gamma value to apply +CRGB& napplyGamma_video( CRGB& rgb, float gamma); + +/// Destructively applies a gamma adjustment to a color +/// @param rgb the color to apply an adjustment to (modified in place) +/// @param gammaR the gamma value to apply to the CRGB::red channel +/// @param gammaG the gamma value to apply to the CRGB::green channel +/// @param gammaB the gamma value to apply to the CRGB::blue channel +CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB); + +/// Destructively applies a gamma adjustment to a color array +/// @param rgbarray pointer to an LED array to apply an adjustment to (modified in place) +/// @param count the number of LEDs to modify +/// @param gamma the gamma value to apply +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma); + +/// Destructively applies a gamma adjustment to a color array +/// @param rgbarray pointer to an LED array to apply an adjustment to (modified in place) +/// @param count the number of LEDs to modify +/// @param gammaR the gamma value to apply to the CRGB::red channel +/// @param gammaG the gamma value to apply to the CRGB::green channel +/// @param gammaB the gamma value to apply to the CRGB::blue channel +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB); + +/// @} GammaFuncs + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/controller.h b/lib/FastLED/src/controller.h new file mode 100644 index 00000000..e4ee1a1e --- /dev/null +++ b/lib/FastLED/src/controller.h @@ -0,0 +1,655 @@ +#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 + +/// Gets the assigned color channel for a byte's position in the output, +/// using the color order (EOrder) template parameter from the +/// LED controller +/// @param X the byte's position in the output (0-2) +/// @returns the color channel for that byte (0 = red, 1 = green, 2 = blue) +/// @see EOrder +#define RO(X) RGB_BYTE(RGB_ORDER, X) + +/// Gets the assigned color channel for a byte's position in the output, +/// using a passed RGB color order +/// @param RO the RGB color order +/// @param X the byte's position in the output (0-2) +/// @returns the color channel for that byte (0 = red, 1 = green, 2 = blue) +/// @see EOrder +#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3) + +/// Gets the color channel for byte 0. +/// @see RGB_BYTE(RO,X) +#define RGB_BYTE0(RO) ((RO>>6) & 0x3) +/// Gets the color channel for byte 1. +/// @see RGB_BYTE(RO,X) +#define RGB_BYTE1(RO) ((RO>>3) & 0x3) +/// Gets the color channel for byte 2. +/// @see RGB_BYTE(RO,X) +#define RGB_BYTE2(RO) ((RO) & 0x3) + +// operator byte *(struct CRGB[] arr) { return (byte*)arr; } + +/// Disable dithering +#define DISABLE_DITHER 0x00 +/// Enable dithering using binary dithering (only option) +#define BINARY_DITHER 0x01 +/// The dither setting, either DISABLE_DITHER or BINARY_DITHER +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. +/// 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. +/// @note That the methods for eventual checking of background writing of data (I'm looking at you, Teensy 3.0 DMA controller!) +/// are not yet implemented. +class CLEDController { +protected: + friend class CFastLED; + CRGB *m_Data; ///< pointer to the LED data used by this controller + CLEDController *m_pNext; ///< pointer to the next LED controller in the linked list + CRGB m_ColorCorrection; ///< CRGB object representing the color correction to apply to the strip on show() @see setCorrection + CRGB m_ColorTemperature; ///< CRGB object representing the color temperature to apply to the strip on show() @see setTemperature + EDitherMode m_DitherMode; ///< the current dither mode of the controller + int m_nLeds; ///< the number of LEDs in the LED data array + static CLEDController *m_pHead; ///< pointer to the first LED controller in the linked list + static CLEDController *m_pTail; ///< pointer to the last LED controller in the linked list + + /// Set all the LEDs 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. + /// @param nLeds the number of LEDs to clear + virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); } + + /// @copybrief show(const struct CRGB*, int, CRGB) + /// + /// Will scale for color correction and temperature. Can accept LED data not attached to this controller. + /// @param data the LED data to write to the strip + /// @param nLeds the number of LEDs in the data array + /// @param brightness the brightness of the LEDs + /// @see show(const struct CRGB*, int, CRGB) + void show(const struct CRGB *data, int nLeds, uint8_t brightness) { + show(data, nLeds, getAdjustment(brightness)); + } + + /// @copybrief showColor(const struct CRGB&, int, CRGB) + /// + /// Will scale for color correction and temperature. Can accept LED data not attached to this controller. + /// @param data the CRGB color to set the LEDs to + /// @param nLeds the number of LEDs in the data array + /// @param brightness the brightness of the LEDs + /// @see showColor(const struct CRGB&, int, CRGB) + void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) { + showColor(data, nLeds, getAdjustment(brightness)); + } + + /// Write the data to the LEDs managed by this controller + /// @param brightness the brightness of the LEDs + /// @see show(const struct CRGB*, int, uint8_t) + void showLeds(uint8_t brightness=255) { + show(m_Data, m_nLeds, getAdjustment(brightness)); + } + + /// @copybrief showColor(const struct CRGB&, int, CRGB) + /// + /// @param data the CRGB color to set the LEDs to + /// @param brightness the brightness of the LEDs + /// @see showColor(const struct CRGB&, int, CRGB) + void showColor(const struct CRGB & data, uint8_t brightness=255) { + showColor(data, m_nLeds, getAdjustment(brightness)); + } + + /// Get the first LED controller in the linked list of controllers + /// @returns CLEDController::m_pHead + static CLEDController *head() { return m_pHead; } + + /// Get the next controller in the linked list after this one. Will return NULL at the end of the linked list. + /// @returns CLEDController::m_pNext + CLEDController *next() { return m_pNext; } + + /// Set the default array of LEDs to be used by this controller + /// @param data pointer to the LED data + /// @param nLeds the number of LEDs in the LED data + 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? + /// @returns CLEDController::m_nLeds + virtual int size() { return m_nLeds; } + + /// How many Lanes does this controller manage? + /// @returns 1 for a non-Parallel controller + virtual int lanes() { return 1; } + + /// Pointer to the CRGB array for this controller + /// @returns CLEDController::m_Data + CRGB* leds() { return m_Data; } + + /// Reference to the n'th LED managed by the controller + /// @param x the LED number to retrieve + /// @returns reference to CLEDController::m_Data[x] + CRGB &operator[](int x) { return m_Data[x]; } + + /// Set the dithering mode for this controller to use + /// @param ditherMode the dithering mode to set + /// @returns a reference to the controller + inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; } + + /// Get the dithering option currently set for this controller + /// @return the currently set dithering option (CLEDController::m_DitherMode) + inline uint8_t getDither() { return m_DitherMode; } + + /// The color corrction to use for this controller, expressed as a CRGB object + /// @param correction the color correction to set + /// @returns a reference to the controller + CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; } + + /// @copydoc setCorrection() + CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; } + + /// Get the correction value used by this controller + /// @returns the current color correction (CLEDController::m_ColorCorrection) + CRGB getCorrection() { return m_ColorCorrection; } + + /// Set the color temperature, aka white point, for this controller + /// @param temperature the color temperature to set + /// @returns a reference to the controller + CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; } + + /// @copydoc setTemperature() + CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; } + + /// Get the color temperature, aka whipe point, for this controller + /// @returns the current color temperature (CLEDController::m_ColorTemperature) + CRGB getTemperature() { return m_ColorTemperature; } + + /// Get the combined brightness/color adjustment for this controller + /// @param scale the brightness scale to get the correction for + /// @returns a CRGB object representing the total adjustment, including color correction and color temperature + CRGB getAdjustment(uint8_t scale) { + return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature); + } + + /// Calculates the combined color adjustment to the LEDs at a given scale, color correction, and color temperature + /// @param scale the scale value for the RGB data (i.e. brightness) + /// @param colorCorrection color correction to apply + /// @param colorTemperature color temperature to apply + /// @returns a CRGB object representing the adjustment, including color correction and color temperature + 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 + } + + /// Gets the maximum possible refresh rate of the strip + /// @returns the maximum refresh rate, in frames per second (FPS) + 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. +/// @tparam RGB_ORDER the rgb ordering for the LEDs (e.g. what order red, green, and blue data is written out in) +/// @tparam LANES how many parallel lanes of output to write +/// @tparam MASK bitmask for the output lanes +template +struct PixelController { + const uint8_t *mData; ///< pointer to the underlying LED data + int mLen; ///< number of LEDs in the data for one lane + int mLenRemaining; ///< counter for the number of LEDs left to process + uint8_t d[3]; ///< values for the scaled dither signal @see init_binary_dithering() + uint8_t e[3]; ///< values for the scaled dither signal @see init_binary_dithering() + CRGB mScale; ///< the per-channel scale values, provided by a color correction function such as CLEDController::computeAdjustment() + int8_t mAdvance; ///< how many bytes to advance the pointer by each time. For CRGB this is 3. + int mOffsets[LANES]; ///< the number of bytes to offset each lane from the starting pointer @see initOffsets() + + /// Copy constructor + /// @param other the object to copy + 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]; } + } + + /// Initialize the PixelController::mOffsets array based on the length of the strip + /// @param len the number of LEDs in one lane of the strip + 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) ) + +/// Alias for RECOMMENDED_VIRTUAL_BITS +#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS + +#endif + + + /// Set up the values for binary dithering + void init_binary_dithering() { +#if !defined(NO_DITHERING) || (NO_DITHERING != 1) + // 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? + /// @param n the number to check against + /// @returns 'true' if there are more than n pixels left to process + __attribute__((always_inline)) inline bool has(int n) { + return mLenRemaining >= n; + } + + /// Toggle dithering enable + /// If dithering is set to enabled, this will re-init the dithering values + /// (init_binary_dithering()). Otherwise it will clear the stored dithering + /// data. + /// @param dither the dither setting + 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; + } + } + + /// Get the length of the LED strip + /// @returns PixelController::mLen + __attribute__((always_inline)) inline int size() { return mLen; } + + /// Get the number of lanes of the Controller + /// @returns LANES from template + __attribute__((always_inline)) inline int lanes() { return LANES; } + + /// Get the amount to advance the pointer by + /// @returns PixelController::mAdvance + __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 + /// @note If updating here, be sure to update the asm version in clockless_trinket.h! + __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)]; + } + + /// @name Template'd static functions for output + /// These functions are used for retrieving LED data for the LED chipset output controllers. + /// @{ + + /// Read a byte of LED data + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; } + /// Read a byte of LED data for parallel output + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @param lane the parallel output lane to read the byte for + template __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; } + + /// Calculate a dither value using the per-channel dither data + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @param b the color byte to dither + /// @see PixelController::d + template __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; } + /// Calculate a dither value + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param b the color byte to dither + /// @param d dither data + template __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; } + + /// Scale a value using the per-channel scale data + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @param b the color byte to scale + /// @see PixelController::mScale + template __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); } + /// Scale a value + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param b the byte to scale + /// @param scale the scale value + template __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); } + + /// @name Composite shortcut functions for loading, dithering, and scaling + /// These composite functions will load color data, dither it, and scale it + /// all at once so that it's ready for the output controller to send to the + /// LEDs. + /// @{ + + + /// Loads, dithers, and scales a single byte for a given output slot, using class dither and scale values + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale(pc, pc.dither(pc, pc.loadByte(pc))); } + + /// Loads, dithers, and scales a single byte for a given output slot and lane, using class dither and scale values + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @param lane the parallel output lane to read the byte for + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale(pc, pc.dither(pc, pc.loadByte(pc, lane))); } + + /// Loads, dithers, and scales a single byte for a given output slot and lane + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @param lane the parallel output lane to read the byte for + /// @param d the dither data for the byte + /// @param scale the scale data for the byte + 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); } + + /// Loads and scales a single byte for a given output slot and lane + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @param lane the parallel output lane to read the byte for + /// @param scale the scale data for the byte + template __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte(pc, lane), scale); } + + + /// A version of loadAndScale() that advances the output data pointer + /// @param pc reference to the pixel controller + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale(pc); } + + /// A version of loadAndScale() that advances the output data pointer + /// @param pc reference to the pixel controller + /// @param lane the parallel output lane to read the byte for + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale(pc, lane); } + + /// A version of loadAndScale() that advances the output data pointer without dithering + /// @param pc reference to the pixel controller + /// @param lane the parallel output lane to read the byte for + /// @param scale the scale data for the byte + template __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale(pc, lane, scale); } + + /// @} Composite shortcut functions + + + /// @name Data retrieval functions + /// These functions retrieve channel-specific data from the class, + /// arranged in output order. + /// @{ + + /// Gets the dithering data for the provided output slot + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @returns dithering data for the given channel + /// @see PixelController::d + template __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; } + + /// Gets the scale data for the provided output slot + /// @tparam SLOT The data slot in the output stream. This is used to select which byte of the output stream is being processed. + /// @param pc reference to the pixel controller + /// @returns scale data for the given channel + /// @see PixelController::mScale + template __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; } + + /// @} Data retrieval functions + + + /// @} Template'd static functions for output + + // 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); } ///< non-template alias of loadAndScale<0>() + __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane, uint8_t scale) { return loadAndScale<1>(*this, lane, scale); } ///< non-template alias of loadAndScale<1>() + __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane, uint8_t scale) { return loadAndScale<2>(*this, lane, scale); } ///< non-template alias of loadAndScale<2>() + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane, uint8_t scale) { return advanceAndLoadAndScale<0>(*this, lane, scale); } ///< non-template alias of advanceAndLoadAndScale<0>() + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane, uint8_t scale) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane, scale); } ///< stepDithering() and advanceAndLoadAndScale0() + + __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); } ///< @copydoc loadAndScale0(int, uint8_t) + __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); } ///< @copydoc loadAndScale1(int, uint8_t) + __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); } ///< @copydoc loadAndScale2(int, uint8_t) + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); } ///< @copydoc advanceAndLoadAndScale0(int, uint8_t) + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); } ///< @copydoc stepAdvanceAndLoadAndScale0(int, uint8_t) + + __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); } ///< @copydoc loadAndScale0(int, uint8_t) + __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); } ///< @copydoc loadAndScale1(int, uint8_t) + __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); } ///< @copydoc loadAndScale2(int, uint8_t) + __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); } ///< @copydoc advanceAndLoadAndScale0(int, uint8_t) + __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); } ///< @copydoc stepAdvanceAndLoadAndScale0(int, uint8_t) + + __attribute__((always_inline)) inline uint8_t getScale0() { return getscale<0>(*this); } ///< non-template alias of getscale<0>() + __attribute__((always_inline)) inline uint8_t getScale1() { return getscale<1>(*this); } ///< non-template alias of getscale<1>() + __attribute__((always_inline)) inline uint8_t getScale2() { return getscale<2>(*this); } ///< non-template alias of getscale<2>() +}; + +/// Template extension of the CLEDController class +/// @tparam RGB_ORDER the rgb ordering for the LEDs (e.g. what order red, green, and blue data is written out in) +/// @tparam LANES how many parallel lanes of output to write +/// @tparam MASK bitmask for the output lanes +template class CPixelLEDController : public CLEDController { +protected: + /// Send the LED data to the strip + /// @param pixels the PixelController object for the LED data + 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 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) { + 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 < 0 ? -nLeds : nLeds, scale, getDither()); + if(nLeds < 0) { + // nLeds < 0 implies that we want to show them in reverse + pixels.mAdvance = -pixels.mAdvance; + } + showPixels(pixels); + } + +public: + CPixelLEDController() : CLEDController() {} + + /// Get the number of lanes of the Controller + /// @returns LANES from template + int lanes() { return LANES; } +}; + + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/cpp_compat.h b/lib/FastLED/src/cpp_compat.h new file mode 100644 index 00000000..06911625 --- /dev/null +++ b/lib/FastLED/src/cpp_compat.h @@ -0,0 +1,34 @@ +/// @file cpp_compat.h +/// Compatibility functions based on C++ version + +#ifndef __INC_CPP_COMPAT_H +#define __INC_CPP_COMPAT_H + +#include "FastLED.h" + +#if __cplusplus <= 199711L + +/// Compile-time assertion checking, introduced in C++11 +/// @see https://en.cppreference.com/w/cpp/language/static_assert +#define static_assert(expression, message) + +/// Declares that it is possible to evaluate a value at compile time, introduced in C++11 +/// @see https://en.cppreference.com/w/cpp/language/constexpr +#define constexpr const + +#else + +// things that we can turn on if we're in a C++11 environment +#endif + +#if __cplusplus < 201703L +#define FASTLED_REGISTER register +#else + #ifdef FASTLED_REGISTER + #undef FASTLED_REGISTER + #endif +#define FASTLED_REGISTER + +#endif + +#endif diff --git a/lib/FastLED/src/dmx.h b/lib/FastLED/src/dmx.h new file mode 100644 index 00000000..44a18e66 --- /dev/null +++ b/lib/FastLED/src/dmx.h @@ -0,0 +1,90 @@ +/// @file dmx.h +/// Defines the DMX512-based LED controllers. + +#ifndef __INC_DMX_H +#define __INC_DMX_H + +#include "FastLED.h" + +/// @addtogroup Chipsets +/// @{ + +/// @addtogroup ClocklessChipsets +/// @{ + +#if defined(DmxSimple_h) || defined(FASTLED_DOXYGEN) +#include + +/// Flag set when the DmxSimple library is included +#define HAS_DMX_SIMPLE + +FASTLED_NAMESPACE_BEGIN + +/// DMX512 based LED controller class, using the DmxSimple library +/// @tparam DATA_PIN the data pin for the output of the DMX bus +/// @tparam RGB_ORDER the RGB ordering for these LEDs +/// @see https://www.pjrc.com/teensy/td_libs_DmxSimple.html +/// @see https://github.com/PaulStoffregen/DmxSimple +/// @see https://en.wikipedia.org/wiki/DMX512 +template class DMXSimpleController : public CPixelLEDController { +public: + /// Initialize the LED controller + virtual void init() { DmxSimple.usePin(DATA_PIN); } + +protected: + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + int iChannel = 1; + while(pixels.has(1)) { + DmxSimple.write(iChannel++, pixels.loadAndScale0()); + DmxSimple.write(iChannel++, pixels.loadAndScale1()); + DmxSimple.write(iChannel++, pixels.loadAndScale2()); + pixels.advanceData(); + pixels.stepDithering(); + } + } +}; + +FASTLED_NAMESPACE_END + +#endif + +#if defined(DmxSerial_h) || defined(FASTLED_DOXYGEN) +#include + +/// Flag set when the DMXSerial library is included +#define HAS_DMX_SERIAL + +FASTLED_NAMESPACE_BEGIN + +/// DMX512 based LED controller class, using the DMXSerial library +/// @tparam RGB_ORDER the RGB ordering for these LEDs +/// @see http://www.mathertel.de/Arduino/DMXSerial.aspx +/// @see https://github.com/mathertel/DMXSerial +/// @see https://en.wikipedia.org/wiki/DMX512 +template class DMXSerialController : public CPixelLEDController { +public: + /// Initialize the LED controller + virtual void init() { DMXSerial.init(DMXController); } + + /// @copydoc CPixelLEDController::showPixels() + virtual void showPixels(PixelController & pixels) { + int iChannel = 1; + while(pixels.has(1)) { + DMXSerial.write(iChannel++, pixels.loadAndScale0()); + DMXSerial.write(iChannel++, pixels.loadAndScale1()); + DMXSerial.write(iChannel++, pixels.loadAndScale2()); + pixels.advanceData(); + pixels.stepDithering(); + } + } +}; + +FASTLED_NAMESPACE_END + +/// @} DMXControllers +/// @} Chipsets + +#endif + +#endif diff --git a/lib/FastLED/src/fastled_config.h b/lib/FastLED/src/fastled_config.h new file mode 100644 index 00000000..2f90396a --- /dev/null +++ b/lib/FastLED/src/fastled_config.h @@ -0,0 +1,92 @@ +#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 + +/// @def FASTLED_FORCE_SOFTWARE_PINS +/// Use this option only for debugging pin access and forcing software pin access. Forces use of `digitalWrite()` +/// methods for pin access vs. direct hardware port access. +/// @note Software pin access only works in Arduino-based environments. +// #define FASTLED_FORCE_SOFTWARE_PINS + +/// @def FASTLED_FORCE_SOFTWARE_SPI +/// 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 have hardware SPI available. +// #define FASTLED_FORCE_SOFTWARE_SPI + +/// @def FASTLED_ALLOW_INTERRUPTS +/// 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 + +/// @def FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW +/// 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 + +/// @def FASTLED_SCALE8_FIXED +/// Use this to 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 + +/// @def FASTLED_BLEND_FIXED +/// Use this to 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 + +/// @def FASTLED_NOISE_FIXED +/// Use this to toggle whether to use "fixed" FastLED 8-bit 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 + +/// @def FASTLED_INTERRUPT_RETRY_COUNT +/// 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 + +/// @def FASTLED_USE_GLOBAL_BRIGHTNESS +/// Use this toggle to enable global brightness in contollers that support is (e.g. ADA102 and SK9822). +/// It changes how color scaling works and uses global brightness before scaling down color values. +/// This enables much more accurate color control on low brightness settings. +//#define FASTLED_USE_GLOBAL_BRIGHTNESS 1 + + +// The defines are used for Doxygen documentation generation. +// They're commented out above and repeated here so the Doxygen parser +// will be able to find them. They will not affect your own configuration, +// and you do *NOT* need to modify them. +#ifdef FASTLED_DOXYGEN +#define FASTLED_FORCE_SOFTWARE_PINS +#define FASTLED_FORCE_SOFTWARE_SPI +#define FASTLED_ALLOW_INTERRUPTS +#define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 0 +#define FASTLED_INTERRUPT_RETRY_COUNT 2 +#define FASTLED_USE_GLOBAL_BRIGHTNESS 0 +#endif + +#endif diff --git a/lib/FastLED/src/fastled_delay.h b/lib/FastLED/src/fastled_delay.h new file mode 100644 index 00000000..44bd108a --- /dev/null +++ b/lib/FastLED/src/fastled_delay.h @@ -0,0 +1,184 @@ +#ifndef __INC_FL_DELAY_H +#define __INC_FL_DELAY_H + +#include "FastLED.h" + +/// @file fastled_delay.h +/// Utility functions and classes for managing delay cycles + +FASTLED_NAMESPACE_BEGIN + + +#if (!defined(NO_MINIMUM_WAIT) || (NO_MINIMUM_WAIT==0)) + +/// Class to ensure that a minimum amount of time has kicked since the last time run - and delay if not enough time has passed yet. +/// @tparam WAIT The amount of time to wait, in microseconds +template class CMinWait { + /// Timestamp of the last time this was run, in microseconds + uint16_t mLastMicros; + +public: + /// Constructor + CMinWait() { mLastMicros = 0; } + + /// Blocking delay until WAIT time since mark() has passed + void wait() { + uint16_t diff; + do { + diff = (micros() & 0xFFFF) - mLastMicros; + } while(diff < WAIT); + } + + /// Reset the timestamp that marks the start of the wait period + void mark() { mLastMicros = micros() & 0xFFFF; } +}; + +#else + +// if you keep your own FPS (and therefore don't call show() too quickly for pixels to latch), you may not want a minimum wait. +template class CMinWait { +public: + CMinWait() { } + void wait() { } + void mark() {} +}; + +#endif + + +//////////////////////////////////////////////////////////////////////////////////////////// +/// +/// @name Clock cycle counted delay loop +/// +/// @{ + +// Default is now just 'nop', with special case for AVR + +// ESP32 core has it's own definition of NOP, so undef it first +#ifdef ESP32 +#undef NOP +#undef NOP2 +#endif + +#if defined(__AVR__) +# define FL_NOP __asm__ __volatile__ ("cp r0,r0\n"); +# define FL_NOP2 __asm__ __volatile__ ("rjmp .+0"); +#else +/// Single no operation ("no-op") instruction for delay +# define FL_NOP __asm__ __volatile__ ("nop\n"); +/// Double no operation ("no-op") instruction for delay +# define FL_NOP2 __asm__ __volatile__ ("nop\n\t nop\n"); +#endif + +// predeclaration to not upset the compiler + + +/// Delay N clock cycles. +/// @tparam CYCLES the number of clock cycles to delay +/// @note No delay is applied if CYCLES is less than or equal to zero. +template inline void delaycycles(); + +/// A variant of ::delaycycles that will always delay +/// at least one cycle. +template inline void delaycycles_min1() { + delaycycles<1>(); + delaycycles(); +} + + +// TODO: ARM version of _delaycycles_ + +// usable definition +#if defined(FASTLED_AVR) +// worker template - this will nop for LOOP * 3 + PAD cycles total +template inline void _delaycycles_AVR() { + delaycycles(); + // the loop below is 3 cycles * LOOP. the LDI is one cycle, + // the DEC is 1 cycle, the BRNE is 2 cycles if looping back and + // 1 if not (the LDI balances out the BRNE being 1 cycle on exit) + __asm__ __volatile__ ( + " LDI R16, %0\n" + "L_%=: DEC R16\n" + " BRNE L_%=\n" + : /* no outputs */ + : "M" (LOOP) + : "r16" + ); +} + +template __attribute__((always_inline)) inline void delaycycles() { + _delaycycles_AVR(); +} +#else +// template inline void _delaycycles_ARM() { +// delaycycles(); +// // the loop below is 3 cycles * LOOP. the LDI is one cycle, +// // the DEC is 1 cycle, the BRNE is 2 cycles if looping back and +// // 1 if not (the LDI balances out the BRNE being 1 cycle on exit) +// __asm__ __volatile__ ( +// " mov.w r9, %0\n" +// "L_%=: subs.w r9, r9, #1\n" +// " bne.n L_%=\n" +// : /* no outputs */ +// : "M" (LOOP) +// : "r9" +// ); +// } + + +template __attribute__((always_inline)) inline void delaycycles() { + // _delaycycles_ARM(); + FL_NOP; delaycycles(); +} +#endif + +// pre-instantiations for values small enough to not need the loop, as well as sanity holders +// for some negative values. + +// These are hidden from Doxygen because they match the expected behavior of the class. +/// @cond +template<> __attribute__((always_inline)) inline void delaycycles<-10>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-9>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-8>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-7>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-6>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-5>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-4>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-3>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-2>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-1>() {} +template<> __attribute__((always_inline)) inline void delaycycles<0>() {} +template<> __attribute__((always_inline)) inline void delaycycles<1>() {FL_NOP;} +template<> __attribute__((always_inline)) inline void delaycycles<2>() {FL_NOP2;} +template<> __attribute__((always_inline)) inline void delaycycles<3>() {FL_NOP;FL_NOP2;} +template<> __attribute__((always_inline)) inline void delaycycles<4>() {FL_NOP2;FL_NOP2;} +template<> __attribute__((always_inline)) inline void delaycycles<5>() {FL_NOP2;FL_NOP2;FL_NOP;} +/// @endcond + +/// @} + + +/// @name Some timing related macros/definitions +/// @{ + +// Macro to convert from nano-seconds to clocks and clocks to nano-seconds +// #define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L))) + +/// CPU speed, in megahertz (MHz) +#define F_CPU_MHZ (F_CPU / 1000000L) + +// #define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000 + +/// Convert from nanoseconds to number of clock cycles +#define NS(_NS) (((_NS * F_CPU_MHZ) + 999) / 1000) +/// Convert from number of clock cycles to microseconds +#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L) + +/// Macro for making sure there's enough time available +#define NO_TIME(A, B, C) (NS(A) < 3 || NS(B) < 3 || NS(C) < 6) + +/// @} + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/fastled_progmem.h b/lib/FastLED/src/fastled_progmem.h new file mode 100644 index 00000000..4200921e --- /dev/null +++ b/lib/FastLED/src/fastled_progmem.h @@ -0,0 +1,108 @@ +#ifndef __INC_FL_PROGMEM_H +#define __INC_FL_PROGMEM_H + +#include "FastLED.h" + +/// @file fastled_progmem.h +/// Wrapper definitions to allow seamless use of PROGMEM in environments that have it +/// +/// This is a compatibility layer for devices that do or don't +/// have "PROGMEM" and the associated pgm_ accessors. +/// +/// If a platform supports PROGMEM, it should define +/// `FASTLED_USE_PROGMEM` as 1, otherwise FastLED will +/// fall back to NOT using PROGMEM. +/// +/// Whether or not pgmspace.h is \#included is separately +/// controllable by FASTLED_INCLUDE_PGMSPACE, if needed. + +FASTLED_NAMESPACE_BEGIN + + +// This block is used for Doxygen documentation generation, +// so that the Doxygen parser will be able to find the macros +// included without a defined platform +#ifdef FASTLED_DOXYGEN +#define FASTLED_USE_PROGMEM 1 +#define FL_ALIGN_PROGMEM __attribute__ ((aligned (4))) +#endif + +/// @def FASTLED_USE_PROGMEM +/// Determine whether the current platform supports PROGMEM. +/// If FASTLED_USE_PROGMEM is 1, we'll map FL_PROGMEM +/// and the FL_PGM_* accessors to the Arduino equivalents. + + +#if FASTLED_USE_PROGMEM == 1 +#ifndef FASTLED_INCLUDE_PGMSPACE +#define FASTLED_INCLUDE_PGMSPACE 1 +#endif + +#if FASTLED_INCLUDE_PGMSPACE == 1 +#include +#endif + +/// PROGMEM keyword for storage +#define FL_PROGMEM PROGMEM + +/// @name PROGMEM Read Functions +/// Functions for reading data from PROGMEM memory. +/// +/// Note that only the "near" memory wrappers are provided. +/// If you're using "far" memory, you already have +/// portability issues to work through, but you could +/// add more support here if needed. +/// +/// @{ + +/// Read a byte (8-bit) from PROGMEM memory +#define FL_PGM_READ_BYTE_NEAR(x) (pgm_read_byte_near(x)) +/// Read a word (16-bit) from PROGMEM memory +#define FL_PGM_READ_WORD_NEAR(x) (pgm_read_word_near(x)) +/// Read a double word (32-bit) from PROGMEM memory +#define FL_PGM_READ_DWORD_NEAR(x) (pgm_read_dword_near(x)) + +/// @} PROGMEM + +// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 +#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6)) +#ifdef FASTLED_AVR +#ifdef PROGMEM +#undef PROGMEM +#define PROGMEM __attribute__((section(".progmem.data"))) +#endif +#endif +#endif + +#else +// If FASTLED_USE_PROGMEM is 0 or undefined, +// we'll use regular memory (RAM) access. + +//empty PROGMEM simulation +#define FL_PROGMEM +#define FL_PGM_READ_BYTE_NEAR(x) (*((const uint8_t*)(x))) +#define FL_PGM_READ_WORD_NEAR(x) (*((const uint16_t*)(x))) +#define FL_PGM_READ_DWORD_NEAR(x) (*((const uint32_t*)(x))) + +#endif + +/// @def FL_ALIGN_PROGMEM +/// Helps to force 4-byte alignment for platforms with unaligned access +/// +/// On some platforms, most notably ARM M0, unaligned access +/// to 'PROGMEM' for multibyte values (e.g. read dword) is +/// not allowed and causes a crash. This macro can help +/// 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) || defined(ESP8266) +#define FL_ALIGN_PROGMEM __attribute__ ((aligned (4))) +#else +#define FL_ALIGN_PROGMEM +#endif + + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/fastpin.h b/lib/FastLED/src/fastpin.h new file mode 100644 index 00000000..2b0986bb --- /dev/null +++ b/lib/FastLED/src/fastpin.h @@ -0,0 +1,359 @@ +#ifndef __INC_FASTPIN_H +#define __INC_FASTPIN_H + +#include "FastLED.h" + +#include "led_sysdefs.h" +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" + +/// @file fastpin.h +/// Class base definitions for defining fast pin access + +FASTLED_NAMESPACE_BEGIN + +/// Constant for "not a pin" +/// @todo Unused, remove? +#define NO_PIN 255 + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Pin access class - needs to tune for various platforms (naive fallback solution?) +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Abstract class for "selectable" things +class Selectable { +public: + virtual void select() = 0; ///< Select this object + virtual void release() = 0; ///< Release this object + virtual bool isSelected() = 0; ///< Check if this object is currently selected +}; + +#if !defined(FASTLED_NO_PINMAP) + +/// Naive fallback solution for low level pin access +class Pin : public Selectable { + volatile RwReg *mPort; ///< Output register for the pin + volatile RoReg *mInPort; ///< Input register for the pin + RwReg mPinMask; ///< Bitmask for the pin within its register + uint8_t mPin; ///< Arduino digital pin number + + /// Initialize the class by retrieving the register + /// pointers and bitmask. + void _init() { + mPinMask = digitalPinToBitMask(mPin); + mPort = (volatile RwReg*)portOutputRegister(digitalPinToPort(mPin)); + mInPort = (volatile RoReg*)portInputRegister(digitalPinToPort(mPin)); + } + +public: + /// Constructor + /// @param pin Arduino digital pin number + Pin(int pin) : mPin(pin) { _init(); } + + typedef volatile RwReg * port_ptr_t; ///< type for a pin read/write register, volatile + typedef RwReg port_t; ///< type for a pin read/write register, non-volatile + + /// Set the pin mode as `OUTPUT` + inline void setOutput() { pinMode(mPin, OUTPUT); } + + /// Set the pin mode as `INPUT` + inline void setInput() { pinMode(mPin, INPUT); } + + /// Set the pin state to `HIGH` + inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; } + /// Set the pin state to `LOW` + inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; } + + /// Toggle the pin twice to create a short pulse + inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + /// Toggle the pin. + /// If the pin was high, set it low. If was low, set it high. + inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; } + + /// Set the same pin on another port to `HIGH` + /// @param port the port to modify + inline void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; } + /// Set the same pin on another port to `LOW` + /// @param port the port to modify + inline void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; } + /// Set the state of the output register + /// @param val the state to set the output register to + /// @note This function is not limited to the current pin! It modifies the entire register. + inline void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *mPort = val; } + + /// Set the state of a port + /// @param port the port to modify + /// @param val the state to set the port to + inline void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + /// Gets the state of the port with this pin `HIGH` + port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; } + /// Gets the state of the port with this pin `LOW` + port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; } + /// Get the output state of the port + port_ptr_t port() __attribute__ ((always_inline)) { return mPort; } + /// Get the pin mask + port_t mask() __attribute__ ((always_inline)) { return mPinMask; } + + /// @copydoc Pin::hi() + virtual void select() { hi(); } + /// @copydoc Pin::lo() + virtual void release() { lo(); } + /// Checks if the pin is currently `HIGH` + virtual bool isSelected() { return (*mPort & mPinMask) == mPinMask; } +}; + +/// I/O pin initially set to OUTPUT +class OutputPin : public Pin { +public: + /// @copydoc Pin::Pin(int) + OutputPin(int pin) : Pin(pin) { setOutput(); } +}; + +/// I/O pin initially set to INPUT +class InputPin : public Pin { +public: + /// @copydoc Pin::Pin(int) + InputPin(int pin) : Pin(pin) { setInput(); } +}; + +#else +// This is the empty code version of the raw pin class, method bodies should be filled in to Do The Right Thing[tm] when making this +// available on a new platform +class Pin : public Selectable { + volatile RwReg *mPort; + volatile RoReg *mInPort; + RwReg mPinMask; + uint8_t mPin; + + void _init() { + // TODO: fill in init on a new platform + mPinMask = 0; + mPort = NULL; + mInPort = NULL; + } + +public: + Pin(int pin) : mPin(pin) { _init(); } + + void setPin(int pin) { mPin = pin; _init(); } + + typedef volatile RwReg * port_ptr_t; + typedef RwReg port_t; + + inline void setOutput() { /* TODO: Set pin output */ } + inline void setInput() { /* TODO: Set pin input */ } + + inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; } + inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; } + + inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; } + + inline void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; } + inline void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; } + inline void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *mPort = val; } + + inline void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; } + port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; } + port_ptr_t port() __attribute__ ((always_inline)) { return mPort; } + port_t mask() __attribute__ ((always_inline)) { return mPinMask; } + + virtual void select() { hi(); } + virtual void release() { lo(); } + virtual bool isSelected() { return (*mPort & mPinMask) == mPinMask; } +}; + +class OutputPin : public Pin { +public: + OutputPin(int pin) : Pin(pin) { setOutput(); } +}; + +class InputPin : public Pin { +public: + InputPin(int pin) : Pin(pin) { setInput(); } +}; + +#endif + +/// The simplest level of Pin class. This relies on runtime functions during initialization to get the port/pin mask for the pin. Most +/// of the accesses involve references to these static globals that get set up. This won't be the fastest set of pin operations, but it +/// will provide pin level access on pretty much all Arduino environments. In addition, it includes some methods to help optimize access in +/// various ways. Namely, the versions of hi(), lo(), and fastset() that take the port register as a passed in register variable (saving a global +/// dereference), since these functions are aggressively inlined, that can help collapse out a lot of extraneous memory loads/dereferences. +/// +/// In addition, if, while writing a bunch of data to a pin, you know no other pins will be getting written to, you can get/cache a value of +/// the pin's port register and use that to do a full set to the register. This results in one being able to simply do a store to the register, +/// vs. the load, and/or, and store that would be done normally. +/// +/// There are platform specific instantiations of this class that provide direct i/o register access to pins for much higher speed pin twiddling. +/// +/// Note that these classes are all static functions. So the proper usage is Pin<13>::hi(); or such. Instantiating objects is not recommended, +/// as passing Pin objects around will likely -not- have the effect you're expecting. +#ifdef FASTLED_FORCE_SOFTWARE_PINS +template class FastPin { + static RwReg sPinMask; ///< Bitmask for the pin within its register + static volatile RwReg *sPort; ///< Output register for the pin + static volatile RoReg *sInPort; ///< Input register for the pin + static void _init() { +#if !defined(FASTLED_NO_PINMAP) + sPinMask = digitalPinToBitMask(PIN); + sPort = portOutputRegister(digitalPinToPort(PIN)); + sInPort = portInputRegister(digitalPinToPort(PIN)); +#endif + } + +public: + typedef volatile RwReg * port_ptr_t; ///< @copydoc Pin::port_ptr_t + typedef RwReg port_t; ///< @copydoc Pin::port_t + + /// @copydoc Pin::setOutput() + inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); } + /// @copydoc Pin::setInput() + inline static void setInput() { _init(); pinMode(PIN, INPUT); } + + /// @copydoc Pin::hi() + inline static void hi() __attribute__ ((always_inline)) { *sPort |= sPinMask; } + /// @copydoc Pin::lo() + inline static void lo() __attribute__ ((always_inline)) { *sPort &= ~sPinMask; } + + /// @copydoc Pin::strobe() + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + /// @copydoc Pin::toggle() + inline static void toggle() __attribute__ ((always_inline)) { *sInPort = sPinMask; } + + /// @copydoc Pin::hi(FASTLED_REGISTER port_ptr_t) + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port |= sPinMask; } + /// @copydoc Pin::lo(FASTLED_REGISTER port_ptr_t) + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~sPinMask; } + /// @copydoc Pin::set(FASTLED_REGISTER port_t) + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *sPort = val; } + + /// @copydoc Pin::fastset() + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + /// @copydoc Pin::hival() + static port_t hival() __attribute__ ((always_inline)) { return *sPort | sPinMask; } + /// @copydoc Pin::loval() + static port_t loval() __attribute__ ((always_inline)) { return *sPort & ~sPinMask; } + /// @copydoc Pin::port() + static port_ptr_t port() __attribute__ ((always_inline)) { return sPort; } + /// @copydoc Pin::mask() + static port_t mask() __attribute__ ((always_inline)) { return sPinMask; } +}; + +template RwReg FastPin::sPinMask; +template volatile RwReg *FastPin::sPort; +template volatile RoReg *FastPin::sInPort; + +#else + +template class FastPin { + constexpr static bool validpin() { return false; } + + static_assert(validpin(), "Invalid pin specified"); + + static void _init() { } + +public: + typedef volatile RwReg * port_ptr_t; ///< @copydoc Pin::port_ptr_t + typedef RwReg port_t; ///< @copydoc Pin::port_t + + /// @copydoc Pin::setOutput() + inline static void setOutput() { } + /// @copydoc Pin::setInput() + inline static void setInput() { } + + /// @copydoc Pin::hi() + inline static void hi() __attribute__ ((always_inline)) { } + /// @copydoc Pin::lo() + inline static void lo() __attribute__ ((always_inline)) { } + + /// @copydoc Pin::strobe() + inline static void strobe() __attribute__ ((always_inline)) { } + + /// @copydoc Pin::toggle() + inline static void toggle() __attribute__ ((always_inline)) { } + + /// @copydoc Pin::hi(FASTLED_REGISTER port_ptr_t) + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { } + /// @copydoc Pin::lo(FASTLED_REGISTER port_ptr_t) + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { } + /// @copydoc Pin::set(FASTLED_REGISTER port_t) + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { } + + /// @copydoc Pin::fastset() + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { } + + /// @copydoc Pin::hival() + static port_t hival() __attribute__ ((always_inline)) { return 0; } + /// @copydoc Pin::loval() + static port_t loval() __attribute__ ((always_inline)) { return 0;} + /// @copydoc Pin::port() + static port_ptr_t port() __attribute__ ((always_inline)) { return NULL; } + /// @copydoc Pin::mask() + static port_t mask() __attribute__ ((always_inline)) { return 0; } +}; + +#endif + +/// FastPin implementation for bit-banded access. +/// Only for MCUs that support bitbanding. +/// @note This bitband class is optional! +template class FastPinBB : public FastPin {}; + +typedef volatile uint32_t & reg32_t; ///< Reference to a 32-bit register, volatile +typedef volatile uint32_t * ptr_reg32_t; ///< Pointer to a 32-bit register, volatile + +/// Utility template for tracking down information about pins and ports +/// @tparam port the port to check information for +template struct __FL_PORT_INFO { + /// Checks whether a port exists + static bool hasPort() { return 0; } + /// Gets the name of the port, as a C-string + static const char *portName() { return "--"; } + /// Gets the raw address of the port + static const void *portAddr() { return NULL; } +}; + + +/// Macro to create the instantiations for defined ports. +/// We're going to abuse this later for auto discovery of pin/port mappings +/// for new variants. +/// Use this for ports that are numeric in nature, e.g. GPIO0, GPIO1, etc. +/// @param L the number of the port +/// @param BASE the data type for the register +#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(); } }; + +/// Macro to create the instantiations for defined ports. +/// We're going to abuse this later for auto discovery of pin/port mappings +/// for new variants. +/// Use this for ports that are letters. The first parameter will be the +/// letter, the second parameter will be an integer/counter of some kind. +/// This is because attempts to turn macro parameters into character constants +/// break in some compilers. +/// @param L the letter of the port +/// @param LC an integer counter +/// @param BASE the data type for the register +#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 + +#endif // __INC_FASTPIN_H diff --git a/lib/FastLED/src/fastspi.h b/lib/FastLED/src/fastspi.h new file mode 100644 index 00000000..7731c737 --- /dev/null +++ b/lib/FastLED/src/fastspi.h @@ -0,0 +1,181 @@ +/// @file fastspi.h +/// Serial peripheral interface (SPI) definitions per platform + +#ifndef __INC_FASTSPI_H +#define __INC_FASTSPI_H + +#include "FastLED.h" + +#include "controller.h" +#include "lib8tion.h" + +#include "fastspi_bitbang.h" + +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) || (defined(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)) || (defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)) +// just use clocks +#define DATA_RATE_MHZ(X) (1000000 * (X)) +#define DATA_RATE_KHZ(X) (1000 * (X)) +#else +/// Convert data rate from megahertz (MHz) to clock cycles per bit +#define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X) +/// Convert data rate from kilohertz (KHz) to clock cycles per bit +#define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X) +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// External SPI template definition with partial instantiation(s) to map to hardware SPI ports on platforms/builds where the pin +// mappings are known at compile time. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if !defined(FASTLED_ALL_PINS_HARDWARE_SPI) +/// Hardware SPI output +template +class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; +#endif + +/// Software SPI output +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 +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(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) +template +class SPIOutput : public ESP32SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; +#endif + +#if defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) +template +class SPIOutput : public ESP8266SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; +#endif + +#if defined(SPI_DATA) && defined(SPI_CLOCK) + +#if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI) + +template +class SPIOutput : public ARMHardwareSPIOutput {}; + +#if defined(SPI2_DATA) + +template +class SPIOutput : public ARMHardwareSPIOutput {}; + +template +class SPIOutput : public ARMHardwareSPIOutput {}; + +template +class SPIOutput : public ARMHardwareSPIOutput {}; +#endif + +#elif defined(FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI) + +template +class SPIOutput : public Teensy4HardwareSPIOutput {}; + +template +class SPIOutput : public Teensy4HardwareSPIOutput {}; + +template +class SPIOutput : public Teensy4HardwareSPIOutput {}; + +#elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI) + +#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\ + class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40077000> {}; + +DECLARE_SPI0(7,13); +DECLARE_SPI0(8,13); +DECLARE_SPI0(11,13); +DECLARE_SPI0(12,13); +DECLARE_SPI0(7,14); +DECLARE_SPI0(8,14); +DECLARE_SPI0(11,14); +DECLARE_SPI0(12,14); +DECLARE_SPI1(0,20); +DECLARE_SPI1(1,20); +DECLARE_SPI1(21,20); + +#elif defined(__SAM3X8E__) + +template +class SPIOutput : public SAMHardwareSPIOutput {}; + +#elif defined(AVR_HARDWARE_SPI) + +template +class SPIOutput : public AVRHardwareSPIOutput {}; + +#if defined(SPI_UART0_DATA) + +template +class SPIOutput : public AVRUSART0SPIOutput {}; + +#endif + +#if defined(SPI_UART1_DATA) + +template +class SPIOutput : public AVRUSART1SPIOutput {}; + +#endif + +#elif defined(ARDUNIO_CORE_SPI) + +template +class SPIOutput : public ArdunioCoreSPIOutput {}; + +#endif + +#else +# if !defined(FASTLED_INTERNAL) && !defined(FASTLED_ALL_PINS_HARDWARE_SPI) +# ifdef FASTLED_HAS_PRAGMA_MESSAGE +# pragma message "No hardware SPI pins defined. All SPI access will default to bitbanged output" +# else +# warning "No hardware SPI pins defined. All SPI access will default to bitbanged output" +# endif +# endif +#endif + +// #if defined(USART_DATA) && defined(USART_CLOCK) +// template +// class AVRSPIOutput : public AVRUSARTSPIOutput {}; +// #endif + +#else +# if !defined(FASTLED_INTERNAL) && !defined(FASTLED_ALL_PINS_HARDWARE_SPI) +# ifdef FASTLED_HAS_PRAGMA_MESSAGE +# pragma message "Forcing software SPI - no hardware SPI for you!" +# else +# warning "Forcing software SPI - no hardware SPI for you!" +# endif +# endif +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/fastspi_bitbang.h b/lib/FastLED/src/fastspi_bitbang.h new file mode 100644 index 00000000..ef284916 --- /dev/null +++ b/lib/FastLED/src/fastspi_bitbang.h @@ -0,0 +1,419 @@ +/// @file fastspi_bitbang.h +/// Software SPI (aka bit-banging) support + +#ifndef __INC_FASTSPI_BITBANG_H +#define __INC_FASTSPI_BITBANG_H + +#include "FastLED.h" + +#include "fastled_delay.h" + +FASTLED_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Software SPI (aka bit-banging) support +/// Includes aggressive optimizations for when the clock and data pin are on the same port. +/// @tparam DATA_PIN pin number of the SPI data pin. +/// @tparam CLOCK_PIN pin number of the SPI clock pin. +/// @tparam SPI_SPEED speed of the bus. Determines the delay times between pin writes. +/// @note Although this is named with the "AVR" prefix, this will work on any platform. Theoretically. +/// @todo Replace the select pin definition with a set of pins, to allow using mux hardware for routing in the future. +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 + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_ptr_t clock_ptr_t; + + // The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports + // are 8 bits wide while on arm they are 32. + typedef typename FastPin::port_t data_t; + typedef typename FastPin::port_t clock_t; + Selectable *m_pSelect; ///< SPI chip select + +public: + /// Default constructor + AVRSoftwareSPIOutput() { m_pSelect = NULL; } + /// Constructor with selectable for SPI chip select + AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + + /// Set the pointer for the SPI chip select + /// @param pSelect pointer to chip select control + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + /// Set the clock/data pins to output and make sure the chip select is released. + void init() { + // set the pins to output and make sure the select is released (which apparently means hi? This is a bit + // confusing to me) + FastPin::setOutput(); + FastPin::setOutput(); + release(); + } + + /// Stop the SPI output. + /// Pretty much a NOP with software, as there's no registers to kick + static void stop() { } + + /// Wait until the SPI subsystem is ready for more data to write. + /// A NOP when bitbanging. + static void wait() __attribute__((always_inline)) { } + /// @copydoc AVRSoftwareSPIOutput::wait() + static void waitFully() __attribute__((always_inline)) { wait(); } + + /// Write a single byte over SPI without waiting. + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); } + /// Write a single byte over SPI and wait afterwards. + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); } + + /// Write a word (two bytes) over SPI. + static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); } + + /// Write a single byte over SPI. + /// Naive implelentation, simply calls writeBit() on the 8 bits in the byte. + 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); + } + +private: + /// writeByte() implementation with data/clock registers passed in. + static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) { + writeBit<7>(b, clockpin, datapin); + writeBit<6>(b, clockpin, datapin); + writeBit<5>(b, clockpin, datapin); + writeBit<4>(b, clockpin, datapin); + writeBit<3>(b, clockpin, datapin); + writeBit<2>(b, clockpin, datapin); + writeBit<1>(b, clockpin, datapin); + writeBit<0>(b, clockpin, datapin); + } + + /// writeByte() implementation with the data register passed in and prebaked values for data hi w/clock hi and + /// low and data lo w/clock hi and lo. This is to be used when clock and data are on the same GPIO register, + /// can get close to getting a bit out the door in 2 clock cycles! + static void writeByte(uint8_t b, data_ptr_t datapin, + data_t hival, data_t loval, + clock_t hiclock, clock_t loclock) { + writeBit<7>(b, datapin, hival, loval, hiclock, loclock); + writeBit<6>(b, datapin, hival, loval, hiclock, loclock); + writeBit<5>(b, datapin, hival, loval, hiclock, loclock); + writeBit<4>(b, datapin, hival, loval, hiclock, loclock); + writeBit<3>(b, datapin, hival, loval, hiclock, loclock); + writeBit<2>(b, datapin, hival, loval, hiclock, loclock); + writeBit<1>(b, datapin, hival, loval, hiclock, loclock); + writeBit<0>(b, datapin, hival, loval, hiclock, loclock); + } + + /// writeByte() implementation with not just registers passed in, but pre-baked values for said registers for + /// data hi/lo and clock hi/lo values. + /// @note Weird things will happen if this method is called in cases where + /// the data and clock pins are on the same port! Don't do that! + static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin, + data_t hival, data_t loval, + clock_t hiclock, clock_t loclock) { + writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock); + writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock); + } + +public: + +#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 + /// We want to make sure that the clock pulse is held high for a minimum of 35 ns. + #define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1) + + /// Delay for the clock signal 'high' period + #define CLOCK_HI_DELAY do { delaycycles(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0); + /// Delay for the clock signal 'low' period + #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 clock + /// @tparam BIT the bit index in the byte + /// @param b the byte to read the bit from + template __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) { + //cli(); + if(b & (1 << BIT)) { + FastPin::hi(); +#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; +#else + FastPin::hi(); CLOCK_HI_DELAY; + FastPin::lo(); CLOCK_LO_DELAY; +#endif + } else { + FastPin::lo(); + 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; +#else + FastPin::lo(); CLOCK_LO_DELAY; +#endif + } + //sei(); + } + +private: + /// Write the BIT'th bit out via SPI, setting the data pin then strobing the clock, using the passed in pin registers to accelerate access if needed + template __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) { + if(b & (1 << BIT)) { + FastPin::hi(datapin); + FastPin::hi(clockpin); CLOCK_HI_DELAY; + FastPin::lo(clockpin); CLOCK_LO_DELAY; + } else { + FastPin::lo(datapin); + FastPin::hi(clockpin); CLOCK_HI_DELAY; + FastPin::lo(clockpin); CLOCK_LO_DELAY; + } + + } + + /// The version of writeBit() to use when clock and data are on separate pins with precomputed values for setting + /// the clock and data pins + template __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin, + data_t hival, data_t loval, clock_t hiclock, clock_t loclock) { + // // only need to explicitly set clock hi if clock and data are on different ports + if(b & (1 << BIT)) { + FastPin::fastset(datapin, hival); + FastPin::fastset(clockpin, hiclock); CLOCK_HI_DELAY; + FastPin::fastset(clockpin, loclock); CLOCK_LO_DELAY; + } else { + // FL_NOP; + FastPin::fastset(datapin, loval); + FastPin::fastset(clockpin, hiclock); CLOCK_HI_DELAY; + FastPin::fastset(clockpin, loclock); CLOCK_LO_DELAY; + } + } + + /// The version of writeBit() to use when clock and data are on the same port with precomputed values for the various + /// combinations + template __attribute__((always_inline)) inline static void writeBit(uint8_t b, data_ptr_t clockdatapin, + data_t datahiclockhi, data_t dataloclockhi, + data_t datahiclocklo, data_t dataloclocklo) { +#if 0 + writeBit(b); +#else + if(b & (1 << BIT)) { + FastPin::fastset(clockdatapin, datahiclocklo); + FastPin::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY; + FastPin::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY; + } else { + // FL_NOP; + FastPin::fastset(clockdatapin, dataloclocklo); + FastPin::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY; + FastPin::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY; + } +#endif + } + +public: + + /// Select the SPI output (chip select) + /// @todo Research whether this really means 'hi' or 'lo'. + /// @par + /// @todo Move select responsibility out of the SPI classes entirely, + /// make it up to the caller to remember to lock/select the line? + void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin::hi(); } + + /// Release the SPI chip select line + void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin::lo(); } + + /// Write multiple bytes of the given value over SPI. + /// Useful for quickly flushing, say, a line of 0's down the line. + /// @param value the value to write to the bus + /// @param len how many copies of the value to write + void writeBytesValue(uint8_t value, int len) { + select(); + writeBytesValueRaw(value, len); + release(); + } + + /// Write multiple bytes of the given value over SPI, without selecting the interface. + /// @copydetails AVRSoftwareSPIOutput::writeBytesValue(uint8_t, int) + static void writeBytesValueRaw(uint8_t value, int len) { +#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS + // TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need + // to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall + // back to the degenerative code below + while(len--) { + writeByte(value); + } +#else + FASTLED_REGISTER data_ptr_t datapin = FastPin::port(); + + if(FastPin::port() != FastPin::port()) { + // If data and clock are on different ports, then writing a bit will consist of writing the value foor + // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line + FASTLED_REGISTER clock_ptr_t clockpin = FastPin::port(); + FASTLED_REGISTER data_t datahi = FastPin::hival(); + FASTLED_REGISTER data_t datalo = FastPin::loval(); + FASTLED_REGISTER clock_t clockhi = FastPin::hival(); + FASTLED_REGISTER clock_t clocklo = FastPin::loval(); + while(len--) { + writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo); + } + + } else { + // If data and clock are on the same port then we can combine setting the data and clock pins + FASTLED_REGISTER data_t datahi_clockhi = FastPin::hival() | FastPin::mask(); + FASTLED_REGISTER data_t datalo_clockhi = FastPin::loval() | FastPin::mask(); + FASTLED_REGISTER data_t datahi_clocklo = FastPin::hival() & ~FastPin::mask(); + FASTLED_REGISTER data_t datalo_clocklo = FastPin::loval() & ~FastPin::mask(); + + while(len--) { + writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); + } + } +#endif + } + + /// Write an array of data to the SPI interface. + /// @tparam D Per-byte modifier class, e.g. ::DATA_NOP + /// @param data pointer to data to write + /// @param len number of bytes to write + /// @todo Need to type this better so that explicit casts into the call aren't required. + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + select(); +#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS + uint8_t *end = data + len; + while(data != end) { + writeByte(D::adjust(*data++)); + } +#else + FASTLED_REGISTER clock_ptr_t clockpin = FastPin::port(); + FASTLED_REGISTER data_ptr_t datapin = FastPin::port(); + + if(FastPin::port() != FastPin::port()) { + // If data and clock are on different ports, then writing a bit will consist of writing the value foor + // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line + FASTLED_REGISTER data_t datahi = FastPin::hival(); + FASTLED_REGISTER data_t datalo = FastPin::loval(); + FASTLED_REGISTER clock_t clockhi = FastPin::hival(); + FASTLED_REGISTER clock_t clocklo = FastPin::loval(); + uint8_t *end = data + len; + + while(data != end) { + writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo); + } + + } else { + // FastPin::hi(); + // If data and clock are on the same port then we can combine setting the data and clock pins + FASTLED_REGISTER data_t datahi_clockhi = FastPin::hival() | FastPin::mask(); + FASTLED_REGISTER data_t datalo_clockhi = FastPin::loval() | FastPin::mask(); + FASTLED_REGISTER data_t datahi_clocklo = FastPin::hival() & ~FastPin::mask(); + FASTLED_REGISTER data_t datalo_clocklo = FastPin::loval() & ~FastPin::mask(); + + uint8_t *end = data + len; + + while(data != end) { + writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); + } + // FastPin::lo(); + } +#endif + D::postBlock(len); + release(); + } + + /// Write an array of data to the SPI interface. + /// @param data pointer to data to write + /// @param len number of bytes to write + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + + /// Write LED pixel data to the SPI interface. + /// Data is written in groups of three, re-ordered per the RGB_ORDER. + /// @tparam FLAGS Option flags, such as ::FLAG_START_BIT + /// @tparam D Per-byte modifier class, e.g. ::DATA_NOP + /// @tparam RGB_ORDER the rgb ordering for the LED data (e.g. what order red, green, and blue data is written out in) + /// @param pixels a ::PixelController with the LED data and modifier options + template __attribute__((noinline)) void writePixels(PixelController pixels) { + select(); + int len = pixels.mLen; + +#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS + // If interrupts or other things may be generating output while we're working on things, then we need + // to use this block + 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(); + } +#else + // If we can guaruntee that no one else will be writing data while we are running (namely, changing the values of the PORT/PDOR pins) + // then we can use a bunch of optimizations in here + FASTLED_REGISTER data_ptr_t datapin = FastPin::port(); + + if(FastPin::port() != FastPin::port()) { + FASTLED_REGISTER clock_ptr_t clockpin = FastPin::port(); + // If data and clock are on different ports, then writing a bit will consist of writing the value foor + // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line + FASTLED_REGISTER data_t datahi = FastPin::hival(); + FASTLED_REGISTER data_t datalo = FastPin::loval(); + FASTLED_REGISTER clock_t clockhi = FastPin::hival(); + FASTLED_REGISTER clock_t clocklo = FastPin::loval(); + + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo); + } + writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo); + writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo); + writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo); + pixels.advanceData(); + pixels.stepDithering(); + } + + } else { + // If data and clock are on the same port then we can combine setting the data and clock pins + FASTLED_REGISTER data_t datahi_clockhi = FastPin::hival() | FastPin::mask(); + FASTLED_REGISTER data_t datalo_clockhi = FastPin::loval() | FastPin::mask(); + FASTLED_REGISTER data_t datahi_clocklo = FastPin::hival() & ~FastPin::mask(); + FASTLED_REGISTER data_t datalo_clocklo = FastPin::loval() & ~FastPin::mask(); + + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); + } + writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); + writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); + writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); + pixels.advanceData(); + pixels.stepDithering(); + } + } +#endif + D::postBlock(len); + release(); + } +}; + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/fastspi_dma.h b/lib/FastLED/src/fastspi_dma.h new file mode 100644 index 00000000..23aa12b6 --- /dev/null +++ b/lib/FastLED/src/fastspi_dma.h @@ -0,0 +1,3 @@ +/// @file fastspi_dma.h +/// Direct memory access (DMA) functions for SPI interfaces +/// @deprecated This header file is empty. diff --git a/lib/FastLED/src/fastspi_nop.h b/lib/FastLED/src/fastspi_nop.h new file mode 100644 index 00000000..74a69108 --- /dev/null +++ b/lib/FastLED/src/fastspi_nop.h @@ -0,0 +1,71 @@ +/// @file fastspi_nop.h +/// Example of a NOP/stub class to show the SPI methods required by a chipset implementation +/// @note Example for developers. Not a functional part of the library. + +#ifndef __INC_FASTSPI_NOP_H +#define __INC_FASTSPI_NOP_H + +#if FASTLED_DOXYGEN // Guard against the arduino ide idiotically including every header file + +#include "FastLED.h" + +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 +class NOPSPIOutput { + Selectable *m_pSelect; + +public: + /// Default Constructor + NOPSPIOutput() { m_pSelect = NULL; } + + /// Constructor with selectable + NOPSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + + /// set the object representing the selectable + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + /// initialize the SPI subssytem + void init() { /* TODO */ } + + /// latch the CS select + void select() { /* TODO */ } + + /// release the CS select + void release() { /* TODO */ } + + /// wait until all queued up data has been written + void waitFully(); + + /// not the most efficient mechanism in the world - but should be enough for sm16716 and friends + template inline static void writeBit(uint8_t b) { /* TODO */ } + + /// write a byte out via SPI (returns immediately on writing register) + void writeByte(uint8_t b) { /* TODO */ } + /// write a word out via SPI (returns immediately on writing register) + void writeWord(uint16_t w) { /* TODO */ } + + /// 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) { /* TODO */ } + + /// A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytesValue(uint8_t value, int len) { /* TODO */ } + + /// A full cycle of writing a raw block of data out, including select, release, and waiting + void writeBytes(uint8_t *data, int len) { /* TODO */ } + + /// 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) { /* TODO */ } + + /// write out pixel data from the given PixelController object + template void writePixels(PixelController pixels) { /* TODO */ } + +}; + +FASTLED_NAMESPACE_END + +#endif +#endif diff --git a/lib/FastLED/src/fastspi_ref.h b/lib/FastLED/src/fastspi_ref.h new file mode 100644 index 00000000..3dddf3b4 --- /dev/null +++ b/lib/FastLED/src/fastspi_ref.h @@ -0,0 +1,103 @@ +/// @file fastspi_ref.h +/// Example of a hardware SPI support class. +/// @note Example for developers. Not a functional part of the library. + +#ifndef __INC_FASTSPI_ARM_SAM_H +#define __INC_FASTSPI_ARM_SAM_H + +#if FASTLED_DOXYGEN // guard against the arduino ide idiotically including every header file +#include "FastLED.h" + +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 they're not the most efficient to start with +template +class REFHardwareSPIOutput { + Selectable *m_pSelect; + +public: + /// Default Constructor + SAMHardwareSPIOutput() { m_pSelect = NULL; } + + /// Constructor with selectable + SAMHArdwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + + /// set the object representing the selectable + void setSelect(Selectable *pSelect) { /* TODO */ } + + /// initialize the SPI subssytem + void init() { /* TODO */ } + + /// latch the CS select + void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } } + + /// release the CS select + void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } } + + /// wait until all queued up data has been written + static void waitFully() { /* TODO */ } + + /// write a byte out via SPI (returns immediately on writing register) + static void writeByte(uint8_t b) { /* TODO */ } + + /// write a word out via SPI (returns immediately on writing register) + static void writeWord(uint16_t w) { /* TODO */ } + + /// 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(FASTLED_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(FASTLED_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) { /* TODO */ } + + /// 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(); + while(data != end) { + 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(); + data += (3+skip); + } + D::postBlock(len); + release(); + } + +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif + diff --git a/lib/FastLED/src/fastspi_types.h b/lib/FastLED/src/fastspi_types.h new file mode 100644 index 00000000..cfbb3610 --- /dev/null +++ b/lib/FastLED/src/fastspi_types.h @@ -0,0 +1,83 @@ +/// @file fastspi_types.h +/// Data types and constants used by SPI interfaces + +#ifndef __INC_FASTSPI_TYPES_H +#define __INC_FASTSPI_TYPES_H + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +/// @name Byte Re-Order Macros +/// Some helper macros for getting at mis-ordered byte values. +/// @todo Unused. Remove? +/// +/// @{ + +/// Get SPI byte 0 offset +#define SPI_B0 (RGB_BYTE0(RGB_ORDER) + (MASK_SKIP_BITS & SKIP)) +/// Get SPI byte 1 offset +#define SPI_B1 (RGB_BYTE1(RGB_ORDER) + (MASK_SKIP_BITS & SKIP)) +/// Get SPI byte 2 offset +#define SPI_B2 (RGB_BYTE2(RGB_ORDER) + (MASK_SKIP_BITS & SKIP)) +/// Advance SPI data pointer +#define SPI_ADVANCE (3 + (MASK_SKIP_BITS & SKIP)) +/// @} + +/// Dummy class for output controllers that need no data transformations. +/// Some of the SPI controllers will need to perform a transform on each byte before doing +/// anything with it. Creating a class of this form and passing it in as a template parameter to +/// writeBytes()/writeBytes3() will ensure that the body of this method will get called on every +/// byte worked on. +/// @note Recommendation: make the adjust method aggressively inlined. +/// @todo Convinience macro for building these +class DATA_NOP { +public: + /// Hook called to adjust a byte of data before writing it to the output. + /// In this dummy version, no adjustment is made. + static __attribute__((always_inline)) inline uint8_t adjust(FASTLED_REGISTER uint8_t data) { return data; } + + /// @copybrief adjust(register uint8_t) + /// @param data input byte + /// @param scale scale value + /// @returns input byte rescaled using ::scale8(uint8_t, uint8_t) + static __attribute__((always_inline)) inline uint8_t adjust(FASTLED_REGISTER uint8_t data, FASTLED_REGISTER uint8_t scale) { return scale8(data, scale); } + + /// Hook called after a block of data is written to the output. + /// In this dummy version, no action is performed. + static __attribute__((always_inline)) inline void postBlock(int /* len */) { } +}; + +/// Flag for the start of an SPI transaction +#define FLAG_START_BIT 0x80 + +/// Bitmask for the lower 6 bits of a byte +/// @todo Unused. Remove? +#define MASK_SKIP_BITS 0x3F + +/// @name Clock speed dividers +/// @{ + +/// Divisor for clock speed by 2 +#define SPEED_DIV_2 2 +/// Divisor for clock speed by 4 +#define SPEED_DIV_4 4 +/// Divisor for clock speed by 8 +#define SPEED_DIV_8 8 +/// Divisor for clock speed by 16 +#define SPEED_DIV_16 16 +/// Divisor for clock speed by 32 +#define SPEED_DIV_32 32 +/// Divisor for clock speed by 64 +#define SPEED_DIV_64 64 +/// Divisor for clock speed by 128 +#define SPEED_DIV_128 128 +/// @} + +/// Max SPI data rate +/// @todo Unused. Remove? +#define MAX_DATA_RATE 0 + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/hsv2rgb.cpp b/lib/FastLED/src/hsv2rgb.cpp new file mode 100644 index 00000000..0d094fd7 --- /dev/null +++ b/lib/FastLED/src/hsv2rgb.cpp @@ -0,0 +1,689 @@ +/// @file hsv2rgb.cpp +/// Functions to convert from the HSV colorspace to the RGB colorspace + +/// Disables pragma messages and warnings +#define FASTLED_INTERNAL +#include + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +/// HSV to RGB implementation in raw C, platform independent +void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb); +/// HSV to RGB implementation in raw C, for the AVR platform only +void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb); + +#if defined(__AVR__) && !defined( LIB8_ATTINY ) +void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb) +{ + hsv2rgb_raw_avr( hsv, rgb); +} +#else +void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb) +{ + hsv2rgb_raw_C( hsv, rgb); +} +#endif + + +/// Apply dimming compensation to values +#define APPLY_DIMMING(X) (X) + +/// Divide the color wheel into eight sections, 32 elements each +/// @todo Unused. Remove? +#define HSV_SECTION_6 (0x20) + +/// Divide the color wheel into four sections, 64 elements each +/// @todo I believe this is mis-named, and should be HSV_SECTION_4 +#define HSV_SECTION_3 (0x40) + +void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb) +{ + // Convert hue, saturation and brightness ( HSV/HSB ) to RGB + // "Dimming" is used on saturation and brightness to make + // the output more visually linear. + + // Apply dimming curves + uint8_t value = APPLY_DIMMING( hsv.val); + uint8_t saturation = hsv.sat; + + // The brightness floor is minimum number that all of + // R, G, and B will be set to. + uint8_t invsat = APPLY_DIMMING( 255 - saturation); + uint8_t brightness_floor = (value * invsat) / 256; + + // The color amplitude is the maximum amount of R, G, and B + // that will be added on top of the brightness_floor to + // create the specific hue desired. + uint8_t color_amplitude = value - brightness_floor; + + // Figure out which section of the hue wheel we're in, + // and how far offset we are withing that section + uint8_t section = hsv.hue / HSV_SECTION_3; // 0..2 + uint8_t offset = hsv.hue % HSV_SECTION_3; // 0..63 + + uint8_t rampup = offset; // 0..63 + uint8_t rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0 + + // We now scale rampup and rampdown to a 0-255 range -- at least + // in theory, but here's where architecture-specific decsions + // come in to play: + // To scale them up to 0-255, we'd want to multiply by 4. + // But in the very next step, we multiply the ramps by other + // values and then divide the resulting product by 256. + // So which is faster? + // ((ramp * 4) * othervalue) / 256 + // or + // ((ramp ) * othervalue) / 64 + // It depends on your processor architecture. + // On 8-bit AVR, the "/ 256" is just a one-cycle register move, + // but the "/ 64" might be a multicycle shift process. So on AVR + // it's faster do multiply the ramp values by four, and then + // divide by 256. + // On ARM, the "/ 256" and "/ 64" are one cycle each, so it's + // faster to NOT multiply the ramp values by four, and just to + // divide the resulting product by 64 (instead of 256). + // Moral of the story: trust your profiler, not your insticts. + + // Since there's an AVR assembly version elsewhere, we'll + // assume what we're on an architecture where any number of + // bit shifts has roughly the same cost, and we'll remove the + // redundant math at the source level: + + // // scale up to 255 range + // //rampup *= 4; // 0..252 + // //rampdown *= 4; // 0..252 + + // compute color-amplitude-scaled-down versions of rampup and rampdown + uint8_t rampup_amp_adj = (rampup * color_amplitude) / (256 / 4); + uint8_t rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4); + + // add brightness_floor offset to everything + uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor; + uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor; + + + if( section ) { + if( section == 1) { + // section 1: 0x40..0x7F + rgb.r = brightness_floor; + rgb.g = rampdown_adj_with_floor; + rgb.b = rampup_adj_with_floor; + } else { + // section 2; 0x80..0xBF + rgb.r = rampup_adj_with_floor; + rgb.g = brightness_floor; + rgb.b = rampdown_adj_with_floor; + } + } else { + // section 0: 0x00..0x3F + rgb.r = rampdown_adj_with_floor; + rgb.g = rampup_adj_with_floor; + rgb.b = brightness_floor; + } +} + + + +#if defined(__AVR__) && !defined( LIB8_ATTINY ) +void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb) +{ + uint8_t hue, saturation, value; + + hue = hsv.hue; + saturation = hsv.sat; + value = hsv.val; + + // Saturation more useful the other way around + saturation = 255 - saturation; + uint8_t invsat = APPLY_DIMMING( saturation ); + + // Apply dimming curves + value = APPLY_DIMMING( value ); + + // The brightness floor is minimum number that all of + // R, G, and B will be set to, which is value * invsat + uint8_t brightness_floor; + + asm volatile( + "mul %[value], %[invsat] \n" + "mov %[brightness_floor], r1 \n" + : [brightness_floor] "=r" (brightness_floor) + : [value] "r" (value), + [invsat] "r" (invsat) + : "r0", "r1" + ); + + // The color amplitude is the maximum amount of R, G, and B + // that will be added on top of the brightness_floor to + // create the specific hue desired. + uint8_t color_amplitude = value - brightness_floor; + + // Figure how far we are offset into the section of the + // color wheel that we're in + uint8_t offset = hsv.hue & (HSV_SECTION_3 - 1); // 0..63 + uint8_t rampup = offset * 4; // 0..252 + + + // compute color-amplitude-scaled-down versions of rampup and rampdown + uint8_t rampup_amp_adj; + uint8_t rampdown_amp_adj; + + asm volatile( + "mul %[rampup], %[color_amplitude] \n" + "mov %[rampup_amp_adj], r1 \n" + "com %[rampup] \n" + "mul %[rampup], %[color_amplitude] \n" + "mov %[rampdown_amp_adj], r1 \n" + : [rampup_amp_adj] "=&r" (rampup_amp_adj), + [rampdown_amp_adj] "=&r" (rampdown_amp_adj), + [rampup] "+r" (rampup) + : [color_amplitude] "r" (color_amplitude) + : "r0", "r1" + ); + + + // add brightness_floor offset to everything + uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor; + uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor; + + + // keep gcc from using "X" as the index register for storing + // results back in the return structure. AVR's X register can't + // do "std X+q, rnn", but the Y and Z registers can. + // if the pointer to 'rgb' is in X, gcc will add all kinds of crazy + // extra instructions. Simply killing X here seems to help it + // try Y or Z first. + asm volatile( "" : : : "r26", "r27" ); + + + if( hue & 0x80 ) { + // section 2: 0x80..0xBF + rgb.r = rampup_adj_with_floor; + rgb.g = brightness_floor; + rgb.b = rampdown_adj_with_floor; + } else { + if( hue & 0x40) { + // section 1: 0x40..0x7F + rgb.r = brightness_floor; + rgb.g = rampdown_adj_with_floor; + rgb.b = rampup_adj_with_floor; + } else { + // section 0: 0x00..0x3F + rgb.r = rampdown_adj_with_floor; + rgb.g = rampup_adj_with_floor; + rgb.b = brightness_floor; + } + } + + cleanup_R1(); +} +// End of AVR asm implementation + +#endif + +void hsv2rgb_spectrum( const struct CHSV& hsv, CRGB& rgb) +{ + CHSV hsv2(hsv); + hsv2.hue = scale8( hsv2.hue, 191); + hsv2rgb_raw(hsv2, rgb); +} + + +/// Force a variable reference to avoid compiler over-optimization. +/// Sometimes the compiler will do clever things to reduce +/// code size that result in a net slowdown, if it thinks that +/// a variable is not used in a certain location. +/// This macro does its best to convince the compiler that +/// the variable is used in this location, to help control +/// code motion and de-duplication that would result in a slowdown. +#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) ) + + +/// @cond +#define K255 255 +#define K171 171 +#define K170 170 +#define K85 85 +/// @endcond + +void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) +{ + // Yellow has a higher inherent brightness than + // any other color; 'pure' yellow is perceived to + // be 93% as bright as white. In order to make + // yellow appear the correct relative brightness, + // it has to be rendered brighter than all other + // colors. + // Level Y1 is a moderate boost, the default. + // Level Y2 is a strong boost. + const uint8_t Y1 = 1; + const uint8_t Y2 = 0; + + // G2: Whether to divide all greens by two. + // Depends GREATLY on your particular LEDs + const uint8_t G2 = 0; + + // Gscale: what to scale green down by. + // Depends GREATLY on your particular LEDs + const uint8_t Gscale = 0; + + + uint8_t hue = hsv.hue; + uint8_t sat = hsv.sat; + uint8_t val = hsv.val; + + uint8_t offset = hue & 0x1F; // 0..31 + + // offset8 = offset * 8 + uint8_t offset8 = offset; + { +#if defined(__AVR__) + // Left to its own devices, gcc turns "x <<= 3" into a loop + // It's much faster and smaller to just do three single-bit shifts + // So this business is to force that. + offset8 <<= 1; + asm volatile(""); + offset8 <<= 1; + asm volatile(""); + offset8 <<= 1; +#else + // On ARM and other non-AVR platforms, we just shift 3. + offset8 <<= 3; +#endif + } + + uint8_t third = scale8( offset8, (256 / 3)); // max = 85 + + uint8_t r, g, b; + + if( ! (hue & 0x80) ) { + // 0XX + if( ! (hue & 0x40) ) { + // 00X + //section 0-1 + if( ! (hue & 0x20) ) { + // 000 + //case 0: // R -> O + r = K255 - third; + g = third; + b = 0; + FORCE_REFERENCE(b); + } else { + // 001 + //case 1: // O -> Y + if( Y1 ) { + r = K171; + g = K85 + third ; + b = 0; + FORCE_REFERENCE(b); + } + if( Y2 ) { + r = K170 + third; + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + g = K85 + twothirds; + b = 0; + FORCE_REFERENCE(b); + } + } + } else { + //01X + // section 2-3 + if( ! (hue & 0x20) ) { + // 010 + //case 2: // Y -> G + if( Y1 ) { + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + r = K171 - twothirds; + g = K170 + third; + b = 0; + FORCE_REFERENCE(b); + } + if( Y2 ) { + r = K255 - offset8; + g = K255; + b = 0; + FORCE_REFERENCE(b); + } + } else { + // 011 + // case 3: // G -> A + r = 0; + FORCE_REFERENCE(r); + g = K255 - third; + b = third; + } + } + } else { + // section 4-7 + // 1XX + if( ! (hue & 0x40) ) { + // 10X + if( ! ( hue & 0x20) ) { + // 100 + //case 4: // A -> B + r = 0; + FORCE_REFERENCE(r); + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + g = K171 - twothirds; //K170? + b = K85 + twothirds; + + } else { + // 101 + //case 5: // B -> P + r = third; + g = 0; + FORCE_REFERENCE(g); + b = K255 - third; + + } + } else { + if( ! (hue & 0x20) ) { + // 110 + //case 6: // P -- K + r = K85 + third; + g = 0; + FORCE_REFERENCE(g); + b = K171 - third; + + } else { + // 111 + //case 7: // K -> R + r = K170 + third; + g = 0; + FORCE_REFERENCE(g); + b = K85 - third; + + } + } + } + + // This is one of the good places to scale the green down, + // although the client can scale green down as well. + if( G2 ) g = g >> 1; + if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale); + + // Scale down colors if we're desaturated at all + // and add the brightness_floor to r, g, and b. + if( sat != 255 ) { + if( sat == 0) { + r = 255; b = 255; g = 255; + } else { + uint8_t desat = 255 - sat; + desat = scale8_video( desat, desat); + + uint8_t satscale = 255 - desat; + //satscale = sat; // uncomment to revert to pre-2021 saturation behavior + + //nscale8x3_video( r, g, b, sat); +#if (FASTLED_SCALE8_FIXED==1) + r = scale8_LEAVING_R1_DIRTY( r, satscale); + g = scale8_LEAVING_R1_DIRTY( g, satscale); + b = scale8_LEAVING_R1_DIRTY( b, satscale); + cleanup_R1(); +#else + if( r ) r = scale8( r, satscale) + 1; + if( g ) g = scale8( g, satscale) + 1; + if( b ) b = scale8( b, satscale) + 1; +#endif + uint8_t brightness_floor = desat; + r += brightness_floor; + g += brightness_floor; + b += brightness_floor; + } + } + + // Now scale everything down if we're at value < 255. + if( val != 255 ) { + + val = scale8_video_LEAVING_R1_DIRTY( val, val); + if( val == 0 ) { + r=0; g=0; b=0; + } else { + // nscale8x3_video( r, g, b, val); +#if (FASTLED_SCALE8_FIXED==1) + r = scale8_LEAVING_R1_DIRTY( r, val); + g = scale8_LEAVING_R1_DIRTY( g, val); + b = scale8_LEAVING_R1_DIRTY( b, val); + cleanup_R1(); +#else + if( r ) r = scale8( r, val) + 1; + if( g ) g = scale8( g, val) + 1; + if( b ) b = scale8( b, val) + 1; +#endif + } + } + + // Here we have the old AVR "missing std X+n" problem again + // It turns out that fixing it winds up costing more than + // not fixing it. + // To paraphrase Dr Bronner, profile! profile! profile! + //asm volatile( "" : : : "r26", "r27" ); + //asm volatile (" movw r30, r26 \n" : : : "r30", "r31"); + rgb.r = r; + rgb.g = g; + rgb.b = b; +} + + +void hsv2rgb_raw(const struct CHSV * phsv, struct CRGB * prgb, int numLeds) { + 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) { + 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) { + hsv2rgb_spectrum(phsv[i], prgb[i]); + } +} + + +/// Convert a fractional input into a constant +#define FIXFRAC8(N,D) (((N)*256)/(D)) + +// This function is only an approximation, and it is not +// nearly as fast as the normal HSV-to-RGB conversion. +// See extended notes in the .h file. +CHSV rgb2hsv_approximate( const CRGB& rgb) +{ + uint8_t r = rgb.r; + uint8_t g = rgb.g; + uint8_t b = rgb.b; + uint8_t h, s, v; + + // find desaturation + uint8_t desat = 255; + if( r < desat) desat = r; + if( g < desat) desat = g; + if( b < desat) desat = b; + + // remove saturation from all channels + r -= desat; + g -= desat; + b -= desat; + + //Serial.print("desat="); Serial.print(desat); Serial.println(""); + + //uint8_t orig_desat = sqrt16( desat * 256); + //Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println(""); + + // saturation is opposite of desaturation + s = 255 - desat; + //Serial.print("s.1="); Serial.print(s); Serial.println(""); + + if( s != 255 ) { + // undo 'dimming' of saturation + s = 255 - sqrt16( (255-s) * 256); + } + // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5 + // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0))); + //Serial.print("s.2="); Serial.print(s); Serial.println(""); + + + // at least one channel is now zero + // if all three channels are zero, we had a + // shade of gray. + if( (r + g + b) == 0) { + // we pick hue zero for no special reason + return CHSV( 0, 0, 255 - s); + } + + // scale all channels up to compensate for desaturation + if( s < 255) { + if( s == 0) s = 1; + uint32_t scaleup = 65535 / (s); + r = ((uint32_t)(r) * scaleup) / 256; + g = ((uint32_t)(g) * scaleup) / 256; + b = ((uint32_t)(b) * scaleup) / 256; + } + //Serial.print("r.2="); Serial.print(r); Serial.println(""); + //Serial.print("g.2="); Serial.print(g); Serial.println(""); + //Serial.print("b.2="); Serial.print(b); Serial.println(""); + + uint16_t total = r + g + b; + + //Serial.print("total="); Serial.print(total); Serial.println(""); + + // scale all channels up to compensate for low values + if( total < 255) { + if( total == 0) total = 1; + uint32_t scaleup = 65535 / (total); + r = ((uint32_t)(r) * scaleup) / 256; + g = ((uint32_t)(g) * scaleup) / 256; + b = ((uint32_t)(b) * scaleup) / 256; + } + //Serial.print("r.3="); Serial.print(r); Serial.println(""); + //Serial.print("g.3="); Serial.print(g); Serial.println(""); + //Serial.print("b.3="); Serial.print(b); Serial.println(""); + + if( total > 255 ) { + v = 255; + } else { + v = qadd8(desat,total); + // undo 'dimming' of brightness + if( v != 255) v = sqrt16( v * 256); + // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5 + // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0)); + + } + + //Serial.print("v="); Serial.print(v); Serial.println(""); + + +#if 0 + + //#else + if( v != 255) { + // this part could probably use refinement/rethinking, + // (but it doesn't overflow & wrap anymore) + uint16_t s16; + s16 = (s * 256); + s16 /= v; + //Serial.print("s16="); Serial.print(s16); Serial.println(""); + if( s16 < 256) { + s = s16; + } else { + s = 255; // clamp to prevent overflow + } + } +#endif + + //Serial.print("s.3="); Serial.print(s); Serial.println(""); + + + // since this wasn't a pure shade of gray, + // the interesting question is what hue is it + + + + // start with which channel is highest + // (ties don't matter) + uint8_t highest = r; + if( g > highest) highest = g; + if( b > highest) highest = b; + + if( highest == r ) { + // Red is highest. + // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow + if( g == 0 ) { + // if green is zero, we're in Purple/Pink-Red + h = (HUE_PURPLE + HUE_PINK) / 2; + h += scale8( qsub8(r, 128), FIXFRAC8(48,128)); + } else if ( (r - g) > g) { + // if R-G > G then we're in Red-Orange + h = HUE_RED; + h += scale8( g, FIXFRAC8(32,85)); + } else { + // R-G < G, we're in Orange-Yellow + h = HUE_ORANGE; + h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221 + } + + } else if ( highest == g) { + // Green is highest + // Hue could be Yellow-Green, Green-Aqua + if( b == 0) { + // if Blue is zero, we're in Yellow-Green + // G = 171..255 + // R = 171.. 0 + h = HUE_YELLOW; + uint8_t radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31 + uint8_t gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31; + uint8_t rgadj = radj + gadj; + uint8_t hueadv = rgadj / 2; + h += hueadv; + //h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))), + // FIXFRAC8(32,255)); // + } else { + // if Blue is nonzero we're in Green-Aqua + if( (g-b) > b) { + h = HUE_GREEN; + h += scale8( b, FIXFRAC8(32,85)); + } else { + h = HUE_AQUA; + h += scale8( qsub8(b, 85), FIXFRAC8(8,42)); + } + } + + } else /* highest == b */ { + // Blue is highest + // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink + if( r == 0) { + // if red is zero, we're in Aqua/Blue-Blue + h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4); + h += scale8( qsub8(b, 128), FIXFRAC8(24,128)); + } else if ( (b-r) > r) { + // B-R > R, we're in Blue-Purple + h = HUE_BLUE; + h += scale8( r, FIXFRAC8(32,85)); + } else { + // B-R < R, we're in Purple-Pink + h = HUE_PURPLE; + h += scale8( qsub8(r, 85), FIXFRAC8(32,85)); + } + } + + h += 1; + return CHSV( h, s, v); +} + +// Examples that need work: +// 0,192,192 +// 192,64,64 +// 224,32,32 +// 252,0,126 +// 252,252,0 +// 252,252,126 + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/hsv2rgb.h b/lib/FastLED/src/hsv2rgb.h new file mode 100644 index 00000000..2a72ed65 --- /dev/null +++ b/lib/FastLED/src/hsv2rgb.h @@ -0,0 +1,171 @@ +#ifndef __INC_HSV2RGB_H +#define __INC_HSV2RGB_H + +#include "FastLED.h" + +#include "pixeltypes.h" + +/// @file hsv2rgb.h +/// Functions to convert from the HSV colorspace to the RGB colorspace + +/// @defgroup HSV2RGB HSV to RGB Conversion Functions +/// Functions to convert from the HSV colorspace to the RGB colorspace. +/// +/// These basically fall into two groups: spectra, and rainbows. +/// pectra and rainbows are not the same thing. Wikipedia has a good +/// llustration that shows a "spectrum" and a "rainbow" side by side: +/// [![Spectra and Rainbow comparison](http://upload.wikimedia.org/wikipedia/commons/f/f6/Prism_compare_rainbow_01.png)](https://commons.wikimedia.org/wiki/File:Prism_compare_rainbow_01.png) +/// +/// Source: http://en.wikipedia.org/wiki/Rainbow#Number_of_colours_in_spectrum_or_rainbow +/// +/// Among other differences, you'll see that a "rainbow" has much more yellow than +/// a plain spectrum. "Classic" LED color washes are spectrum based, and +/// usually show very little yellow. +/// +/// Take a look Wikipedia's page on HSV color space, with pseudocode for conversion +/// to RGB color space: http://en.wikipedia.org/wiki/HSL_and_HSV +/// +/// Note that their conversion algorithm, which is (naturally) very popular +/// is in the "maximum brightness at any given hue" style, vs. the "uniform +/// brightness for all hues" style. +/// +/// You can't have both; either purple is the same brightness as red, e.g: +/// @code +/// red = 0xFF0000 +/// purple = 0x800080 +/// @endcode +/// +/// Where you have the same "total light" output. OR purple is "as bright +/// as it can be", e.g.: +/// @code +/// red = 0xFF0000 +/// purple = 0xFF00FF +/// @endcode +/// +/// Where purple is much brighter than red. +/// +/// The colorspace conversions here try to keep the apparent brightness +/// constant even as the hue varies. +/// +/// Adafruit's "Wheel" function, discussed [here](http://forums.adafruit.com/viewtopic.php?f=47&t=22483) +/// is also of the "constant apparent brightness" variety. +/// +/// @todo Provide the "maximum brightness no matter what" variation. +/// +/// @see [Some good, clear Arduino C code from Kasper Kamperman](http://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/), +/// which in turn [was based on Windows C code from "nico80"](http://www.codeproject.com/Articles/9207/An-HSB-RGBA-colour-picker) + +/// @{ + +FASTLED_NAMESPACE_BEGIN + + +/// Convert an HSV value to RGB using a visually balanced rainbow. +/// This "rainbow" yields better yellow and orange than a straight +/// mathematical "spectrum". +/// +/// ![FastLED 'Rainbow' Hue Chart](https://raw.githubusercontent.com/FastLED/FastLED/gh-pages/images/HSV-rainbow-with-desc.jpg) +/// +/// @param hsv CHSV struct to convert to RGB. Max hue supported is HUE_MAX_RAINBOW +/// @param rgb CRGB struct to store the result of the conversion (will be modified) +void hsv2rgb_rainbow( const struct CHSV& hsv, struct CRGB& rgb); + +/// @copybrief hsv2rgb_rainbow(const struct CHSV&, struct CRGB&) +/// @see hsv2rgb_rainbow(const struct CHSV&, struct CRGB&) +/// @param phsv CHSV array to convert to RGB. Max hue supported is HUE_MAX_RAINBOW +/// @param prgb CRGB array to store the result of the conversion (will be modified) +/// @param numLeds the number of array values to process +void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds); + +/// Max hue accepted for the hsv2rgb_rainbow() function +#define HUE_MAX_RAINBOW 255 + + +/// Convert an HSV value to RGB using a mathematically straight spectrum. +/// This "spectrum" will have more green and blue than a "rainbow", +/// and less yellow and orange. +/// +/// ![FastLED 'Spectrum' Hue Chart](https://raw.githubusercontent.com/FastLED/FastLED/gh-pages/images/HSV-spectrum-with-desc.jpg) +/// +/// @note This function wraps hsv2rgb_raw() and rescales the hue value to fit +/// the smaller range. +/// +/// @param hsv CHSV struct to convert to RGB. Max hue supported is HUE_MAX_SPECTRUM +/// @param rgb CRGB struct to store the result of the conversion (will be modified) +void hsv2rgb_spectrum( const struct CHSV& hsv, struct CRGB& rgb); + +/// @copybrief hsv2rgb_spectrum(const struct CHSV&, struct CRGB&) +/// @see hsv2rgb_spectrum(const struct CHSV&, struct CRGB&) +/// @param phsv CHSV array to convert to RGB. Max hue supported is HUE_MAX_SPECTRUM +/// @param prgb CRGB array to store the result of the conversion (will be modified) +/// @param numLeds the number of array values to process +void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds); + +/// Max hue accepted for the hsv2rgb_spectrum() function +#define HUE_MAX_SPECTRUM 255 + + + +/// @copybrief hsv2rgb_spectrum(const struct CHSV&, struct CRGB&) +/// @see hsv2rgb_spectrum(const struct CHSV&, struct CRGB&) +/// @note The hue is limited to the range 0-191 (HUE_MAX). This +/// results in a slightly faster conversion speed at the expense +/// of color balance. +/// @param hsv CHSV struct to convert to RGB. Max hue supported is HUE_MAX +/// @param rgb CRGB struct to store the result of the conversion (will be modified) +void hsv2rgb_raw(const struct CHSV& hsv, struct CRGB & rgb); + +/// @copybrief hsv2rgb_raw(const struct CHSV&, struct CRGB&) +/// @see hsv2rgb_raw(const struct CHSV&, struct CRGB&) +/// @param phsv CHSV array to convert to RGB. Max hue supported is HUE_MAX +/// @param prgb CRGB array to store the result of the conversion (will be modified) +/// @param numLeds the number of array values to process +void hsv2rgb_raw(const struct CHSV* phsv, struct CRGB * prgb, int numLeds); + +/// Max hue accepted for the hsv2rgb_raw() function +#define HUE_MAX 191 + + +/// Recover approximate HSV values from RGB. +/// These values are *approximate*, not exact. Why is this "only" an approximation? +/// Because not all RGB colors have HSV equivalents! For example, there +/// is no HSV value that will ever convert to RGB(255,255,0) using +/// the code provided in this library. So if you try to +/// convert RGB(255,255,0) "back" to HSV, you'll necessarily get +/// only an approximation. Emphasis has been placed on getting +/// the "hue" as close as usefully possible, but even that's a bit +/// of a challenge. The 8-bit HSV and 8-bit RGB color spaces +/// are not a "bijection". +/// +/// Nevertheless, this function does a pretty good job, particularly +/// at recovering the 'hue' from fully saturated RGB colors that +/// originally came from HSV rainbow colors. So if you start +/// with CHSV(hue_in,255,255), and convert that to RGB, and then +/// convert it back to HSV using this function, the resulting output +/// hue will either exactly the same, or very close (+/-1). +/// The more desaturated the original RGB color is, the rougher the +/// approximation, and the less accurate the results. +/// @note This function is a long-term work in progress; expect +/// results to change slightly over time as this function is +/// refined and improved. +/// @par +/// @note This function is most accurate when the input is an +/// RGB color that came from a fully-saturated HSV color to start +/// with. E.g. CHSV( hue, 255, 255) -> CRGB -> CHSV will give +/// best results. +/// @par +/// @note This function is not nearly as fast as HSV-to-RGB. +/// It is provided for those situations when the need for this +/// function cannot be avoided, or when extremely high performance +/// is not needed. +/// @see https://en.wikipedia.org/wiki/Bijection +/// @param rgb an RGB value to convert +/// @returns the approximate HSV equivalent of the RGB value +CHSV rgb2hsv_approximate( const CRGB& rgb); + + +FASTLED_NAMESPACE_END + +///@} HSV2RGB + +#endif diff --git a/lib/FastLED/src/led_sysdefs.h b/lib/FastLED/src/led_sysdefs.h new file mode 100644 index 00000000..f18a0994 --- /dev/null +++ b/lib/FastLED/src/led_sysdefs.h @@ -0,0 +1,82 @@ +#ifndef __INC_LED_SYSDEFS_H +#define __INC_LED_SYSDEFS_H + +#include "FastLED.h" + +#include "fastled_config.h" + +/// @file led_sysdefs.h +/// Determines which platform system definitions to include + +#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" +#elif defined(__MK66FX1M0__) || defined(__MK64FX512__) +// Include k66/T3.6 headers +#include "platforms/arm/k66/led_sysdefs_arm_k66.h" +#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__) || defined(STM32F2XX) || defined(STM32F1) +#include "platforms/arm/stm32/led_sysdefs_arm_stm32.h" +#elif defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) +#include "platforms/arm/d21/led_sysdefs_arm_d21.h" +#elif defined(__SAMD51G19A__) || defined(__SAMD51J19A__) || defined(__SAME51J19A__) || defined(__SAMD51P19A__) || defined(__SAMD51P20A__) +#include "platforms/arm/d51/led_sysdefs_arm_d51.h" +#elif defined(ARDUINO_ARCH_RP2040) // not sure a pico-sdk define for this +// RP2040 (Raspberry Pi Pico etc) +#include "platforms/arm/rp2040/led_sysdefs_arm_rp2040.h" +#elif defined(ESP8266) +#include "platforms/esp/8266/led_sysdefs_esp8266.h" +#elif defined(ESP32) +#include "platforms/esp/32/led_sysdefs_esp32.h" +#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 +/// Start of the FastLED namespace +#define FASTLED_NAMESPACE_BEGIN +/// End of the FastLED namespace +#define FASTLED_NAMESPACE_END +/// "Using" directive for the namespace +#define FASTLED_USING_NAMESPACE +#endif + +// Arduino.h needed for convenience functions digitalPinToPort/BitMask/portOutputRegister and the pinMode methods. +#ifdef ARDUINO +#include +#endif + +/// Clock cycles per microsecond. +/// Calculated using the F_CPU preprocessor define +#define CLKS_PER_US (F_CPU/1000000) + +#endif diff --git a/lib/FastLED/src/lib8tion.cpp b/lib/FastLED/src/lib8tion.cpp new file mode 100644 index 00000000..27036717 --- /dev/null +++ b/lib/FastLED/src/lib8tion.cpp @@ -0,0 +1,258 @@ +/// @file lib8tion.cpp +/// Fast, efficient 8-bit math functions specifically +/// designed for high-performance LED programming. + +/// Disables pragma messages and warnings +#define FASTLED_INTERNAL + +#include +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + +/// @copydoc ::rand16seed +#define RAND16_SEED 1337 +uint16_t rand16seed = RAND16_SEED; + + +// memset8, memcpy8, memmove8: +// optimized avr replacements for the standard "C" library +// routines memset, memcpy, and memmove. +// +// There are two techniques that make these routines +// faster than the standard avr-libc routines. +// First, the loops are unrolled 2X, meaning that +// the average loop overhead is cut in half. +// And second, the compare-and-branch at the bottom +// of each loop decrements the low byte of the +// counter, and if the carry is clear, it branches +// back up immediately. Only if the low byte math +// causes carry do we bother to decrement the high +// byte and check that result for carry as well. +// Results for a 100-byte buffer are 20-40% faster +// than standard avr-libc, at a cost of a few extra +// bytes of code. + +#if defined(__AVR__) +extern "C" { +//__attribute__ ((noinline)) +void * memset8 ( void * ptr, uint8_t val, uint16_t num ) +{ + asm volatile( + " movw r26, %[ptr] \n\t" + " sbrs %A[num], 0 \n\t" + " rjmp Lseteven_%= \n\t" + " rjmp Lsetodd_%= \n\t" + "Lsetloop_%=: \n\t" + " st X+, %[val] \n\t" + "Lsetodd_%=: \n\t" + " st X+, %[val] \n\t" + "Lseteven_%=: \n\t" + " subi %A[num], 2 \n\t" + " brcc Lsetloop_%= \n\t" + " sbci %B[num], 0 \n\t" + " brcc Lsetloop_%= \n\t" + : [num] "+r" (num) + : [ptr] "r" (ptr), + [val] "r" (val) + : "memory" + ); + return ptr; +} + + + +//__attribute__ ((noinline)) +void * memcpy8 ( void * dst, const void* src, uint16_t num ) +{ + asm volatile( + " movw r30, %[src] \n\t" + " movw r26, %[dst] \n\t" + " sbrs %A[num], 0 \n\t" + " rjmp Lcpyeven_%= \n\t" + " rjmp Lcpyodd_%= \n\t" + "Lcpyloop_%=: \n\t" + " ld __tmp_reg__, Z+ \n\t" + " st X+, __tmp_reg__ \n\t" + "Lcpyodd_%=: \n\t" + " ld __tmp_reg__, Z+ \n\t" + " st X+, __tmp_reg__ \n\t" + "Lcpyeven_%=: \n\t" + " subi %A[num], 2 \n\t" + " brcc Lcpyloop_%= \n\t" + " sbci %B[num], 0 \n\t" + " brcc Lcpyloop_%= \n\t" + : [num] "+r" (num) + : [src] "r" (src), + [dst] "r" (dst) + : "memory" + ); + return dst; +} + +//__attribute__ ((noinline)) +void * memmove8 ( void * dst, const void* src, uint16_t num ) +{ + if( src > dst) { + // if src > dst then we can use the forward-stepping memcpy8 + return memcpy8( dst, src, num); + } else { + // if src < dst then we have to step backward: + dst = (char*)dst + num; + src = (char*)src + num; + asm volatile( + " movw r30, %[src] \n\t" + " movw r26, %[dst] \n\t" + " sbrs %A[num], 0 \n\t" + " rjmp Lmoveven_%= \n\t" + " rjmp Lmovodd_%= \n\t" + "Lmovloop_%=: \n\t" + " ld __tmp_reg__, -Z \n\t" + " st -X, __tmp_reg__ \n\t" + "Lmovodd_%=: \n\t" + " ld __tmp_reg__, -Z \n\t" + " st -X, __tmp_reg__ \n\t" + "Lmoveven_%=: \n\t" + " subi %A[num], 2 \n\t" + " brcc Lmovloop_%= \n\t" + " sbci %B[num], 0 \n\t" + " brcc Lmovloop_%= \n\t" + : [num] "+r" (num) + : [src] "r" (src), + [dst] "r" (dst) + : "memory" + ); + return dst; + } +} + + +} /* end extern "C" */ + +#endif /* AVR */ + + + + +#if 0 +// TEST / VERIFICATION CODE ONLY BELOW THIS POINT +#include +#include "lib8tion.h" + +void test1abs( int8_t i) +{ + Serial.print("abs("); Serial.print(i); Serial.print(") = "); + int8_t j = abs8(i); + Serial.print(j); Serial.println(" "); +} + +void testabs() +{ + delay(5000); + for( int8_t q = -128; q != 127; ++q) { + test1abs(q); + } + for(;;){}; +} + + +void testmul8() +{ + delay(5000); + byte r, c; + + Serial.println("mul8:"); + for( r = 0; r <= 20; r += 1) { + Serial.print(r); Serial.print(" : "); + for( c = 0; c <= 20; c += 1) { + byte t; + t = mul8( r, c); + Serial.print(t); Serial.print(' '); + } + Serial.println(' '); + } + Serial.println("done."); + for(;;){}; +} + + +void testscale8() +{ + delay(5000); + byte r, c; + + Serial.println("scale8:"); + for( r = 0; r <= 240; r += 10) { + Serial.print(r); Serial.print(" : "); + for( c = 0; c <= 240; c += 10) { + byte t; + t = scale8( r, c); + Serial.print(t); Serial.print(' '); + } + Serial.println(' '); + } + + Serial.println(' '); + Serial.println("scale8_video:"); + + for( r = 0; r <= 100; r += 4) { + Serial.print(r); Serial.print(" : "); + for( c = 0; c <= 100; c += 4) { + byte t; + t = scale8_video( r, c); + Serial.print(t); Serial.print(' '); + } + Serial.println(' '); + } + + Serial.println("done."); + for(;;){}; +} + + + +void testqadd8() +{ + delay(5000); + byte r, c; + for( r = 0; r <= 240; r += 10) { + Serial.print(r); Serial.print(" : "); + for( c = 0; c <= 240; c += 10) { + byte t; + t = qadd8( r, c); + Serial.print(t); Serial.print(' '); + } + Serial.println(' '); + } + Serial.println("done."); + for(;;){}; +} + +void testnscale8x3() +{ + delay(5000); + byte r, g, b, sc; + for( byte z = 0; z < 10; ++z) { + r = random8(); g = random8(); b = random8(); sc = random8(); + + Serial.print("nscale8x3_video( "); + Serial.print(r); Serial.print(", "); + Serial.print(g); Serial.print(", "); + Serial.print(b); Serial.print(", "); + Serial.print(sc); Serial.print(") = [ "); + + nscale8x3_video( r, g, b, sc); + + Serial.print(r); Serial.print(", "); + Serial.print(g); Serial.print(", "); + Serial.print(b); Serial.print("]"); + + Serial.println(' '); + } + Serial.println("done."); + for(;;){}; +} + +#endif + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/lib8tion.h b/lib/FastLED/src/lib8tion.h new file mode 100644 index 00000000..f5916ccd --- /dev/null +++ b/lib/FastLED/src/lib8tion.h @@ -0,0 +1,1441 @@ +#ifndef __INC_LIB8TION_H +#define __INC_LIB8TION_H + +#include "FastLED.h" + +#ifndef __INC_LED_SYSDEFS_H +#error WTH? led_sysdefs needs to be included first +#endif + +/// @file lib8tion.h +/// Fast, efficient 8-bit math functions specifically +/// designed for high-performance LED programming. + + +FASTLED_NAMESPACE_BEGIN + + +#include + +/// Define a LIB8TION member function as static inline with an "unused" attribute +#define LIB8STATIC __attribute__ ((unused)) static inline +/// Define a LIB8TION member function as always static inline +#define LIB8STATIC_ALWAYS_INLINE __attribute__ ((always_inline)) static inline + +#if !defined(__AVR__) +#include +// for memmove, memcpy, and memset if not defined here +#endif // end of !defined(__AVR__) + +#if defined(__arm__) + +#if defined(FASTLED_TEENSY3) +// Can use Cortex M4 DSP instructions +#define QADD8_C 0 +#define QADD7_C 0 +#define QADD8_ARM_DSP_ASM 1 +#define QADD7_ARM_DSP_ASM 1 +#else +// Generic ARM +#define QADD8_C 1 +#define QADD7_C 1 +#endif // end of defined(FASTLED_TEENSY3) + +#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 AVG8R_C 1 +#define AVG7_C 1 +#define AVG16_C 1 +#define AVG16R_C 1 +#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 AVG8R_C 1 +#define AVG7_C 1 +#define AVG16_C 1 +#define AVG16R_C 1 +#define AVG15_C 1 +#define BLEND8_C 1 + +// end of #elif defined(ARDUINO_ARCH_APOLLO3) + +#elif defined(__AVR__) + +// AVR ATmega and friends Arduino + +#define QADD8_C 0 +#define QADD7_C 0 +#define QSUB8_C 0 +#define ABS8_C 0 +#define ADD8_C 0 +#define SUB8_C 0 +#define AVG8_C 0 +#define AVG8R_C 0 +#define AVG7_C 0 +#define AVG16_C 0 +#define AVG16R_C 0 +#define AVG15_C 0 + +#define QADD8_AVRASM 1 +#define QADD7_AVRASM 1 +#define QSUB8_AVRASM 1 +#define ABS8_AVRASM 1 +#define ADD8_AVRASM 1 +#define SUB8_AVRASM 1 +#define AVG8_AVRASM 1 +#define AVG8R_AVRASM 1 +#define AVG7_AVRASM 1 +#define AVG16_AVRASM 1 +#define AVG16R_AVRASM 1 +#define AVG15_AVRASM 1 + +// Note: these require hardware MUL instruction +// -- sorry, ATtiny! +#if !defined(LIB8_ATTINY) +#define SCALE8_C 0 +#define SCALE16BY8_C 0 +#define SCALE16_C 0 +#define MUL8_C 0 +#define QMUL8_C 0 +#define EASE8_C 0 +#define BLEND8_C 0 +#define SCALE8_AVRASM 1 +#define SCALE16BY8_AVRASM 1 +#define SCALE16_AVRASM 1 +#define MUL8_AVRASM 1 +#define QMUL8_AVRASM 1 +#define EASE8_AVRASM 1 +#define CLEANUP_R1_AVRASM 1 +#define BLEND8_AVRASM 1 +#else +// On ATtiny, we just use C implementations +#define SCALE8_C 1 +#define SCALE16BY8_C 1 +#define SCALE16_C 1 +#define MUL8_C 1 +#define QMUL8_C 1 +#define EASE8_C 1 +#define BLEND8_C 1 +#define SCALE8_AVRASM 0 +#define SCALE16BY8_AVRASM 0 +#define SCALE16_AVRASM 0 +#define MUL8_AVRASM 0 +#define QMUL8_AVRASM 0 +#define EASE8_AVRASM 0 +#define BLEND8_AVRASM 0 +#endif // end of !defined(LIB8_ATTINY) + +// end of #elif defined(__AVR__) + +#else + +// Doxygen: ignore these macros +/// @cond + +// unspecified architecture, so +// no ASM, everything in C +#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 AVG8R_C 1 +#define AVG7_C 1 +#define AVG16_C 1 +#define AVG16R_C 1 +#define AVG15_C 1 +#define BLEND8_C 1 + +/// @endcond + +#endif + +/// @defgroup lib8tion Fast Math Functions +/// Fast, efficient 8-bit math functions specifically +/// designed for high-performance LED programming. +/// +/// Because of the AVR (Arduino) and ARM assembly language +/// implementations provided, using these functions often +/// results in smaller and faster code than the equivalent +/// program using plain "C" arithmetic and logic. +/// +/// Included are: +/// +/// - Saturating unsigned 8-bit add and subtract. +/// Instead of wrapping around if an overflow occurs, +/// these routines just 'clamp' the output at a maxumum +/// of 255, or a minimum of 0. Useful for adding pixel +/// values. E.g., qadd8( 200, 100) = 255. +/// @code +/// qadd8( i, j) == MIN( (i + j), 0xFF ) +/// qsub8( i, j) == MAX( (i - j), 0 ) +/// @endcode +/// +/// - Saturating signed 8-bit ("7-bit") add. +/// @code +/// qadd7( i, j) == MIN( (i + j), 0x7F) +/// @endcode +/// +/// - Scaling (down) of unsigned 8- and 16- bit values. +/// Scaledown value is specified in 1/256ths. +/// @code +/// scale8( i, sc) == (i * sc) / 256 +/// scale16by8( i, sc) == (i * sc) / 256 +/// @endcode +/// +/// Example: scaling a 0-255 value down into a +/// range from 0-99: +/// @code +/// downscaled = scale8( originalnumber, 100); +/// @endcode +/// +/// A special version of scale8 is provided for scaling +/// LED brightness values, to make sure that they don't +/// accidentally scale down to total black at low +/// dimming levels, since that would look wrong: +/// @code +/// scale8_video( i, sc) = ((i * sc) / 256) +? 1 +/// @endcode +/// +/// Example: reducing an LED brightness by a +/// dimming factor: +/// @code +/// new_bright = scale8_video( orig_bright, dimming); +/// @endcode +/// +/// - Fast 8- and 16- bit unsigned random numbers. +/// Significantly faster than Arduino random(), but +/// also somewhat less random. You can add entropy. +/// @code +/// random8() == random from 0..255 +/// random8( n) == random from 0..(N-1) +/// random8( n, m) == random from N..(M-1) +/// +/// random16() == random from 0..65535 +/// random16( n) == random from 0..(N-1) +/// random16( n, m) == random from N..(M-1) +/// +/// random16_set_seed( k) == seed = k +/// random16_add_entropy( k) == seed += k +/// @endcode +/// +/// - Absolute value of a signed 8-bit value. +/// @code +/// abs8( i) == abs( i) +/// @endcode +/// +/// - 8-bit math operations which return 8-bit values. +/// These are provided mostly for completeness, +/// not particularly for performance. +/// @code +/// mul8( i, j) == (i * j) & 0xFF +/// add8( i, j) == (i + j) & 0xFF +/// sub8( i, j) == (i - j) & 0xFF +/// @endcode +/// +/// - Fast 16-bit approximations of sin and cos. +/// Input angle is a uint16_t from 0-65535. +/// Output is a signed int16_t from -32767 to 32767. +/// @code +/// sin16( x) == sin( (x/32768.0) * pi) * 32767 +/// cos16( x) == cos( (x/32768.0) * pi) * 32767 +/// @endcode +/// +/// Accurate to more than 99% in all cases. +/// +/// - Fast 8-bit approximations of sin and cos. +/// Input angle is a uint8_t from 0-255. +/// Output is an UNsigned uint8_t from 0 to 255. +/// @code +/// sin8( x) == (sin( (x/128.0) * pi) * 128) + 128 +/// cos8( x) == (cos( (x/128.0) * pi) * 128) + 128 +/// @endcode +/// +/// Accurate to within about 2%. +/// +/// - Fast 8-bit "easing in/out" function. +/// @code +/// ease8InOutCubic(x) == 3(x^2) - 2(x^3) +/// ease8InOutApprox(x) == +/// faster, rougher, approximation of cubic easing +/// ease8InOutQuad(x) == quadratic (vs cubic) easing +/// @endcode +/// +/// - Cubic, Quadratic, and Triangle wave functions. +/// Input is a uint8_t representing phase withing the wave, +/// similar to how sin8 takes an angle 'theta'. +/// Output is a uint8_t representing the amplitude of +/// the wave at that point. +/// @code +/// cubicwave8( x) +/// quadwave8( x) +/// triwave8( x) +/// @endcode +/// +/// - Square root for 16-bit integers. About three times +/// faster and five times smaller than Arduino's built-in +/// generic 32-bit sqrt routine. +/// @code +/// sqrt16( uint16_t x ) == sqrt( x) +/// @endcode +/// +/// - Dimming and brightening functions for 8-bit +/// light values. +/// @code +/// dim8_video( x) == scale8_video( x, x) +/// dim8_raw( x) == scale8( x, x) +/// dim8_lin( x) == (x<128) ? ((x+1)/2) : scale8(x,x) +/// brighten8_video( x) == 255 - dim8_video( 255 - x) +/// brighten8_raw( x) == 255 - dim8_raw( 255 - x) +/// brighten8_lin( x) == 255 - dim8_lin( 255 - x) +/// @endcode +/// +/// The dimming functions in particular are suitable +/// for making LED light output appear more 'linear'. +/// +/// - Linear interpolation between two values, with the +/// fraction between them expressed as an 8- or 16-bit +/// fixed point fraction (fract8 or fract16). +/// @code +/// lerp8by8( fromU8, toU8, fract8 ) +/// lerp16by8( fromU16, toU16, fract8 ) +/// lerp15by8( fromS16, toS16, fract8 ) +/// == from + (( to - from ) * fract8) / 256) +/// lerp16by16( fromU16, toU16, fract16 ) +/// == from + (( to - from ) * fract16) / 65536) +/// map8( in, rangeStart, rangeEnd) +/// == map( in, 0, 255, rangeStart, rangeEnd); +/// @endcode +/// +/// - Optimized memmove, memcpy, and memset, that are +/// faster than standard avr-libc 1.8. +/// @code +/// memmove8( dest, src, bytecount) +/// memcpy8( dest, src, bytecount) +/// memset8( buf, value, bytecount) +/// @endcode +/// +/// - Beat generators which return sine or sawtooth +/// waves in a specified number of Beats Per Minute. +/// Sine wave beat generators can specify a low and +/// high range for the output. Sawtooth wave beat +/// generators always range 0-255 or 0-65535. +/// @code +/// beatsin8( BPM, low8, high8) +/// = (sine(beatphase) * (high8-low8)) + low8 +/// beatsin16( BPM, low16, high16) +/// = (sine(beatphase) * (high16-low16)) + low16 +/// beatsin88( BPM88, low16, high16) +/// = (sine(beatphase) * (high16-low16)) + low16 +/// beat8( BPM) = 8-bit repeating sawtooth wave +/// beat16( BPM) = 16-bit repeating sawtooth wave +/// beat88( BPM88) = 16-bit repeating sawtooth wave +/// @endcode +/// +/// BPM is beats per minute in either simple form +/// e.g. 120, or Q8.8 fixed-point form. +/// BPM88 is beats per minute in ONLY Q8.8 fixed-point +/// form. +/// +/// Lib8tion is pronounced like 'libation': lie-BAY-shun +/// +/// @{ + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup FractionalTypes Fixed-Point Fractional Types. +/// Types for storing fractional data. +/// +/// * ::sfract7 should be interpreted as signed 128ths. +/// * ::fract8 should be interpreted as unsigned 256ths. +/// * ::sfract15 should be interpreted as signed 32768ths. +/// * ::fract16 should be interpreted as unsigned 65536ths. +/// +/// Example: if a fract8 has the value "64", that should be interpreted +/// as 64/256ths, or one-quarter. +/// +/// accumXY types should be interpreted as X bits of integer, +/// and Y bits of fraction. +/// E.g., ::accum88 has 8 bits of int, 8 bits of fraction +/// +/// @{ + +/// ANSI: unsigned short _Fract. +/// Range is 0 to 0.99609375 in steps of 0.00390625. +/// Should be interpreted as unsigned 256ths. +typedef uint8_t fract8; + +/// ANSI: signed short _Fract. +/// Range is -0.9921875 to 0.9921875 in steps of 0.0078125. +/// Should be interpreted as signed 128ths. +typedef int8_t sfract7; + +/// ANSI: unsigned _Fract. +/// Range is 0 to 0.99998474121 in steps of 0.00001525878. +/// Should be interpreted as unsigned 65536ths. +typedef uint16_t fract16; + +/// ANSI: signed _Fract. +/// Range is -0.99996948242 to 0.99996948242 in steps of 0.00003051757. +/// Should be interpreted as signed 32768ths. +typedef int16_t sfract15; + + +typedef uint16_t accum88; ///< ANSI: unsigned short _Accum. 8 bits int, 8 bits fraction +typedef int16_t saccum78; ///< ANSI: signed short _Accum. 7 bits int, 8 bits fraction +typedef uint32_t accum1616; ///< ANSI: signed _Accum. 16 bits int, 16 bits fraction +typedef int32_t saccum1516; ///< ANSI: signed _Accum. 15 bits int, 16 bits fraction +typedef uint16_t accum124; ///< no direct ANSI counterpart. 12 bits int, 4 bits fraction +typedef int32_t saccum114; ///< no direct ANSI counterpart. 1 bit int, 14 bits fraction + + +/// typedef for IEEE754 "binary32" float type internals +/// @see https://en.wikipedia.org/wiki/IEEE_754 +typedef union { + uint32_t i; ///< raw value, as an integer + float f; ///< raw value, as a float + struct { + uint32_t mantissa: 23; ///< 23-bit mantissa + uint32_t exponent: 8; ///< 8-bit exponent + uint32_t signbit: 1; ///< sign bit + }; + struct { + uint32_t mant7 : 7; ///< @todo Doc: what is this for? + uint32_t mant16: 16; ///< @todo Doc: what is this for? + uint32_t exp_ : 8; ///< @todo Doc: what is this for? + uint32_t sb_ : 1; ///< @todo Doc: what is this for? + }; + struct { + uint32_t mant_lo8 : 8; ///< @todo Doc: what is this for? + uint32_t mant_hi16_exp_lo1 : 16; ///< @todo Doc: what is this for? + uint32_t sb_exphi7 : 8; ///< @todo Doc: what is this for? + }; +} IEEE754binary32_t; + +/// @} FractionalTypes + + +#include "lib8tion/math8.h" +#include "lib8tion/scale8.h" +#include "lib8tion/random8.h" +#include "lib8tion/trig8.h" + +/////////////////////////////////////////////////////////////////////// + + + + + + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup FloatConversions Float-to-Fixed and Fixed-to-Float Conversions +/// Functions to convert between floating point and fixed point types. +/// @note Anything involving a "float" on AVR will be slower. +/// @{ + +/// Conversion from 16-bit fixed point (::sfract15) to IEEE754 32-bit float. +LIB8STATIC float sfract15ToFloat( sfract15 y) +{ + return y / 32768.0; +} + +/// Conversion from IEEE754 float in the range (-1,1) to 16-bit fixed point (::sfract15). +/// @note The extremes of one and negative one are NOT representable! The +/// representable range is 0.99996948242 to -0.99996948242, in steps of 0.00003051757. +LIB8STATIC sfract15 floatToSfract15( float f) +{ + return f * 32768.0; +} + +/// @} FloatConversions + + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup FastMemory Fast Memory Functions for AVR +/// Alternatives to memmove, memcpy, and memset that are +/// faster on AVR than standard avr-libc 1.8. +/// @{ + +#if defined(__AVR__) || defined(FASTLED_DOXYGEN) +extern "C" { +void * memmove8( void * dst, const void * src, uint16_t num ); ///< Faster alternative to memmove() on AVR +void * memcpy8 ( void * dst, const void * src, uint16_t num ) __attribute__ ((noinline)); ///< Faster alternative to memcpy() on AVR +void * memset8 ( void * ptr, uint8_t value, uint16_t num ) __attribute__ ((noinline)) ; ///< Faster alternative to memset() on AVR +} +#else +// on non-AVR platforms, these names just call standard libc. +#define memmove8 memmove +#define memcpy8 memcpy +#define memset8 memset +#endif + +/// @} FastMemory + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup LinearInterpolation Linear Interpolation +/// Fast linear interpolation functions, such as could be used for Perlin noise, etc. +/// +/// A note on the structure of the lerp functions: +/// The cases for b>a and b<=a are handled separately for +/// speed. Without knowing the relative order of a and b, +/// the value (a-b) might be overflow the width of a or b, +/// and have to be promoted to a wider, slower type. +/// To avoid that, we separate the two cases, and are able +/// to do all the math in the same width as the arguments, +/// which is much faster and smaller on AVR. +/// @{ + +/// Linear interpolation between two unsigned 8-bit values, +/// with 8-bit fraction +LIB8STATIC uint8_t lerp8by8( uint8_t a, uint8_t b, fract8 frac) +{ + uint8_t result; + if( b > a) { + uint8_t delta = b - a; + uint8_t scaled = scale8( delta, frac); + result = a + scaled; + } else { + uint8_t delta = a - b; + uint8_t scaled = scale8( delta, frac); + result = a - scaled; + } + return result; +} + +/// Linear interpolation between two unsigned 16-bit values, +/// with 16-bit fraction +LIB8STATIC uint16_t lerp16by16( uint16_t a, uint16_t b, fract16 frac) +{ + uint16_t result; + if( b > a ) { + uint16_t delta = b - a; + uint16_t scaled = scale16(delta, frac); + result = a + scaled; + } else { + uint16_t delta = a - b; + uint16_t scaled = scale16( delta, frac); + result = a - scaled; + } + return result; +} + +/// Linear interpolation between two unsigned 16-bit values, +/// with 8-bit fraction +LIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac) +{ + uint16_t result; + if( b > a) { + uint16_t delta = b - a; + uint16_t scaled = scale16by8( delta, frac); + result = a + scaled; + } else { + uint16_t delta = a - b; + uint16_t scaled = scale16by8( delta, frac); + result = a - scaled; + } + return result; +} + +/// Linear interpolation between two signed 15-bit values, +/// with 8-bit fraction +LIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac) +{ + int16_t result; + if( b > a) { + uint16_t delta = b - a; + uint16_t scaled = scale16by8( delta, frac); + result = a + scaled; + } else { + uint16_t delta = a - b; + uint16_t scaled = scale16by8( delta, frac); + result = a - scaled; + } + return result; +} + +/// Linear interpolation between two signed 15-bit values, +/// with 8-bit fraction +LIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac) +{ + int16_t result; + if( b > a) { + uint16_t delta = b - a; + uint16_t scaled = scale16( delta, frac); + result = a + scaled; + } else { + uint16_t delta = a - b; + uint16_t scaled = scale16( delta, frac); + result = a - scaled; + } + return result; +} + +/// Map from one full-range 8-bit value into a narrower +/// range of 8-bit values, possibly a range of hues. +/// +/// E.g. map `myValue` into a hue in the range blue..purple..pink..red +/// @code +/// hue = map8( myValue, HUE_BLUE, HUE_RED); +/// @endcode +/// +/// Combines nicely with the waveform functions (like sin8(), etc) +/// to produce continuous hue gradients back and forth: +/// @code +/// hue = map8( sin8( myValue), HUE_BLUE, HUE_RED); +/// @endcode +/// +/// Mathematically simiar to lerp8by8(), but arguments are more +/// like Arduino's "map"; this function is similar to +/// @code +/// map( in, 0, 255, rangeStart, rangeEnd) +/// @endcode +/// +/// but faster and specifically designed for 8-bit values. +LIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd) +{ + uint8_t rangeWidth = rangeEnd - rangeStart; + uint8_t out = scale8( in, rangeWidth); + out += rangeStart; + return out; +} + +/// @} LinearInterpolation + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup Easing Easing Functions +/// Specify the rate of change of a parameter over time. +/// @see http://easings.net +/// @{ + +/// 8-bit quadratic ease-in / ease-out function. +/// Takes around 13 cycles on AVR. +#if (EASE8_C == 1) || defined(FASTLED_DOXYGEN) +LIB8STATIC uint8_t ease8InOutQuad( uint8_t i) +{ + uint8_t j = i; + if( j & 0x80 ) { + j = 255 - j; + } + uint8_t jj = scale8( j, j); + uint8_t jj2 = jj << 1; + if( i & 0x80 ) { + jj2 = 255 - jj2; + } + return jj2; +} + +#elif EASE8_AVRASM == 1 +// This AVR asm version of ease8InOutQuad preserves one more +// low-bit of precision than the C version, and is also slightly +// smaller and faster. +LIB8STATIC uint8_t ease8InOutQuad(uint8_t val) { + uint8_t j=val; + asm volatile ( + "sbrc %[val], 7 \n" + "com %[j] \n" + "mul %[j], %[j] \n" + "add r0, %[j] \n" + "ldi %[j], 0 \n" + "adc %[j], r1 \n" + "lsl r0 \n" // carry = high bit of low byte of mul product + "rol %[j] \n" // j = (j * 2) + carry // preserve add'l bit of precision + "sbrc %[val], 7 \n" + "com %[j] \n" + "clr __zero_reg__ \n" + : [j] "+&a" (j) + : [val] "a" (val) + : "r0", "r1" + ); + return j; +} + +#else +#error "No implementation for ease8InOutQuad available." +#endif + +/// 16-bit quadratic ease-in / ease-out function. +/// C implementation at this point. +LIB8STATIC uint16_t ease16InOutQuad( uint16_t i) +{ + uint16_t j = i; + if( j & 0x8000 ) { + j = 65535 - j; + } + uint16_t jj = scale16( j, j); + uint16_t jj2 = jj << 1; + if( i & 0x8000 ) { + jj2 = 65535 - jj2; + } + return jj2; +} + + +/// 8-bit cubic ease-in / ease-out function. +/// Takes around 18 cycles on AVR. +LIB8STATIC fract8 ease8InOutCubic( fract8 i) +{ + uint8_t ii = scale8_LEAVING_R1_DIRTY( i, i); + uint8_t iii = scale8_LEAVING_R1_DIRTY( ii, i); + + uint16_t r1 = (3 * (uint16_t)(ii)) - ( 2 * (uint16_t)(iii)); + + /* the code generated for the above *'s automatically + cleans up R1, so there's no need to explicitily call + cleanup_R1(); */ + + uint8_t result = r1; + + // if we got "256", return 255: + if( r1 & 0x100 ) { + result = 255; + } + return result; +} + + +/// Fast, rough 8-bit ease-in/ease-out function. +/// Shaped approximately like ease8InOutCubic(), +/// it's never off by more than a couple of percent +/// from the actual cubic S-curve, and it executes +/// more than twice as fast. Use when the cycles +/// are more important than visual smoothness. +/// Asm version takes around 7 cycles on AVR. +#if (EASE8_C == 1) || defined(FASTLED_DOXYGEN) +LIB8STATIC fract8 ease8InOutApprox( fract8 i) +{ + if( i < 64) { + // start with slope 0.5 + i /= 2; + } else if( i > (255 - 64)) { + // end with slope 0.5 + i = 255 - i; + i /= 2; + i = 255 - i; + } else { + // in the middle, use slope 192/128 = 1.5 + i -= 64; + i += (i / 2); + i += 32; + } + + return i; +} + +#elif EASE8_AVRASM == 1 +LIB8STATIC uint8_t ease8InOutApprox( fract8 i) +{ + // takes around 7 cycles on AVR + asm volatile ( + " subi %[i], 64 \n\t" + " cpi %[i], 128 \n\t" + " brcc Lshift_%= \n\t" + + // middle case + " mov __tmp_reg__, %[i] \n\t" + " lsr __tmp_reg__ \n\t" + " add %[i], __tmp_reg__ \n\t" + " subi %[i], 224 \n\t" + " rjmp Ldone_%= \n\t" + + // start or end case + "Lshift_%=: \n\t" + " lsr %[i] \n\t" + " subi %[i], 96 \n\t" + + "Ldone_%=: \n\t" + + : [i] "+a" (i) + : + : "r0" + ); + return i; +} +#else +#error "No implementation for ease8 available." +#endif + +/// @} Easing + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup WaveformGenerators Waveform Generators +/// General purpose wave generator functions. +/// +/// @{ + + +/// Triangle wave generator. +/// Useful for turning a one-byte ever-increasing value into a +/// one-byte value that oscillates up and down. +/// @code +/// input output +/// 0..127 0..254 (positive slope) +/// 128..255 254..0 (negative slope) +/// @endcode +/// +/// On AVR this function takes just three cycles. +/// +LIB8STATIC uint8_t triwave8(uint8_t in) +{ + if( in & 0x80) { + in = 255 - in; + } + uint8_t out = in << 1; + return out; +} + +/// Quadratic waveform generator. Spends just a little +/// more time at the limits than "sine" does. +/// +/// S-shaped wave generator (like "sine"). Useful +/// for turning a one-byte "counter" value into a +/// one-byte oscillating value that moves smoothly up and down, +/// with an "acceleration" and "deceleration" curve. +/// +/// This is even faster than "sin8()", and has +/// a slightly different curve shape. +LIB8STATIC uint8_t quadwave8(uint8_t in) +{ + return ease8InOutQuad( triwave8( in)); +} + +/// Cubic waveform generator. Spends visibly more time +/// at the limits than "sine" does. +/// @copydetails quadwave8() +LIB8STATIC uint8_t cubicwave8(uint8_t in) +{ + return ease8InOutCubic( triwave8( in)); +} + + +/// Square wave generator. +/// Useful for turning a one-byte ever-increasing value +/// into a one-byte value that is either 0 or 255. +/// The width of the output "pulse" is determined by +/// the pulsewidth argument: +/// @code +/// if pulsewidth is 255, output is always 255. +/// if pulsewidth < 255, then +/// if input < pulsewidth then output is 255 +/// if input >= pulsewidth then output is 0 +/// @endcode +/// +/// The output looking like: +/// +/// @code +/// 255 +--pulsewidth--+ +/// . | | +/// 0 0 +--------(256-pulsewidth)-------- +/// @endcode +/// +/// @param in input value +/// @param pulsewidth width of the output pulse +/// @returns square wave output +LIB8STATIC uint8_t squarewave8( uint8_t in, uint8_t pulsewidth=128) +{ + if( in < pulsewidth || (pulsewidth == 255)) { + return 255; + } else { + return 0; + } +} + +/// @} WaveformGenerators + + + +/// @addtogroup FractionalTypes +/// @{ + +/// Template class for representing fractional ints. +/// @tparam T underlying type for data storage +/// @tparam F number of fractional bits +/// @tparam I number of integer bits +template class q { + T i:I; ///< Integer value of number + T f:F; ///< Fractional value of number +public: + /// Constructor, storing a float as a fractional int + q(float fx) { i = fx; f = (fx-i) * (1<>F); } + /// @copydoc operator*(uint32_t) + uint16_t operator*(uint16_t v) { return (v*i) + ((v*f)>>F); } + /// @copydoc operator*(uint32_t) + int32_t operator*(int32_t v) { return (v*i) + ((v*f)>>F); } + /// @copydoc operator*(uint32_t) + int16_t operator*(int16_t v) { return (v*i) + ((v*f)>>F); } +#if defined(FASTLED_ARM) | defined(FASTLED_RISCV) | defined(FASTLED_APOLLO3) + /// @copydoc operator*(uint32_t) + int operator*(int v) { return (v*i) + ((v*f)>>F); } +#endif +}; + +template static uint32_t operator*(uint32_t v, q & q) { return q * v; } +template static uint16_t operator*(uint16_t v, q & q) { return q * v; } +template static int32_t operator*(int32_t v, q & q) { return q * v; } +template static int16_t operator*(int16_t v, q & q) { return q * v; } +#if defined(FASTLED_ARM) | defined(FASTLED_RISCV) | defined(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; +/// A 6.2 integer (6 bits integer, 2 bits fraction) +typedef q q62; +/// A 8.8 integer (8 bits integer, 8 bits fraction) +typedef q q88; +/// A 12.4 integer (12 bits integer, 4 bits fraction) +typedef q q124; + +/// @} + +/// @} lib8tion (excluding the timekeeping functions from the nested group) + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup Timekeeping Timekeeping Functions +/// Tools for tracking and working with time +/// +/// @{ + +#if ((defined(ARDUINO) || defined(SPARK) || defined(FASTLED_HAS_MILLIS)) && !defined(USE_GET_MILLISECOND_TIMER)) || defined(FASTLED_DOXYGEN) +// Forward declaration of Arduino function 'millis'. +//uint32_t millis(); + +/// The a number of functions need access to a millisecond counter +/// in order to keep time. On Arduino, this is "millis()". +/// On other platforms, you'll need to provide a function with this +/// signature which provides similar functionality: +/// @code{.cpp} +/// uint32_t get_millisecond_timer(); +/// @endcode +/// +/// You can also force use of the get_millisecond_timer() function +/// by \#defining `USE_GET_MILLISECOND_TIMER`. +#define GET_MILLIS millis +#else +uint32_t get_millisecond_timer(); +#define GET_MILLIS get_millisecond_timer +#endif + +/// @} Timekeeping + + +/// @addtogroup lib8tion +/// @{ + + +/////////////////////////////////////////////////////////////////////// +/// +/// @defgroup BeatGenerators Waveform Beat Generators +/// Waveform generators that reset at a given number +/// of "beats per minute" (BPM). +/// +/// The standard "beat" functions generate "sawtooth" waves which rise from +/// 0 up to a max value and then reset, continuously repeating that cycle at +/// the specified frequency (BPM). +/// +/// The "sin" versions function similarly, but create an oscillating sine wave +/// at the specified frequency. +/// +/// BPM can be supplied two ways. The simpler way of specifying BPM is as +/// a simple 8-bit integer from 1-255, (e.g., "120"). +/// The more sophisticated way of specifying BPM allows for fractional +/// "Q8.8" fixed point number (an ::accum88) with an 8-bit integer part and +/// an 8-bit fractional part. The easiest way to construct this is to multiply +/// a floating point BPM value (e.g. 120.3) by 256, (e.g. resulting in 30796 +/// in this case), and pass that as the 16-bit BPM argument. +/// +/// Originally these functions were designed to make an entire animation project pulse. +/// with brightness. For that effect, add this line just above your existing call to +/// "FastLED.show()": +/// @code +/// uint8_t bright = beatsin8( 60 /*BPM*/, 192 /*dimmest*/, 255 /*brightest*/ )); +/// FastLED.setBrightness( bright ); +/// FastLED.show(); +/// @endcode +/// +/// The entire animation will now pulse between brightness 192 and 255 once per second. +/// +/// @warning Any "BPM88" parameter **MUST** always be provided in Q8.8 format! +/// @note The beat generators need access to a millisecond counter +/// to track elapsed time. See ::GET_MILLIS for reference. When using the Arduino +/// `millis()` function, accuracy is a bit better than one part in a thousand. +/// +/// @{ + + +/// Generates a 16-bit "sawtooth" wave at a given BPM, with BPM +/// specified in Q8.8 fixed-point format. +/// @param beats_per_minute_88 the frequency of the wave, in Q8.8 format +/// @param timebase the time offset of the wave from the millis() timer +/// @warning The BPM parameter **MUST** be provided in Q8.8 format! E.g. +/// for 120 BPM it would be 120*256 = 30720. If you just want to specify +/// "120", use beat16() or beat8(). +LIB8STATIC uint16_t beat88( accum88 beats_per_minute_88, uint32_t timebase = 0) +{ + // BPM is 'beats per minute', or 'beats per 60000ms'. + // To avoid using the (slower) division operator, we + // want to convert 'beats per 60000ms' to 'beats per 65536ms', + // and then use a simple, fast bit-shift to divide by 65536. + // + // The ratio 65536:60000 is 279.620266667:256; we'll call it 280:256. + // The conversion is accurate to about 0.05%, more or less, + // e.g. if you ask for "120 BPM", you'll get about "119.93". + return (((GET_MILLIS()) - timebase) * beats_per_minute_88 * 280) >> 16; +} + +/// Generates a 16-bit "sawtooth" wave at a given BPM +/// @param beats_per_minute the frequency of the wave, in decimal +/// @param timebase the time offset of the wave from the millis() timer +LIB8STATIC uint16_t beat16( accum88 beats_per_minute, uint32_t timebase = 0) +{ + // Convert simple 8-bit BPM's to full Q8.8 accum88's if needed + if( beats_per_minute < 256) beats_per_minute <<= 8; + return beat88(beats_per_minute, timebase); +} + +/// Generates an 8-bit "sawtooth" wave at a given BPM +/// @param beats_per_minute the frequency of the wave, in decimal +/// @param timebase the time offset of the wave from the millis() timer +LIB8STATIC uint8_t beat8( accum88 beats_per_minute, uint32_t timebase = 0) +{ + return beat16( beats_per_minute, timebase) >> 8; +} + + +/// Generates a 16-bit sine wave at a given BPM that oscillates within +/// a given range. +/// @param beats_per_minute_88 the frequency of the wave, in Q8.8 format +/// @param lowest the lowest output value of the sine wave +/// @param highest the highest output value of the sine wave +/// @param timebase the time offset of the wave from the millis() timer +/// @param phase_offset phase offset of the wave from the current position +/// @warning The BPM parameter **MUST** be provided in Q8.8 format! E.g. +/// for 120 BPM it would be 120*256 = 30720. If you just want to specify +/// "120", use beatsin16() or beatsin8(). +LIB8STATIC uint16_t beatsin88( accum88 beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535, + uint32_t timebase = 0, uint16_t phase_offset = 0) +{ + uint16_t beat = beat88( beats_per_minute_88, timebase); + uint16_t beatsin = (sin16( beat + phase_offset) + 32768); + uint16_t rangewidth = highest - lowest; + uint16_t scaledbeat = scale16( beatsin, rangewidth); + uint16_t result = lowest + scaledbeat; + return result; +} + +/// Generates a 16-bit sine wave at a given BPM that oscillates within +/// a given range. +/// @param beats_per_minute the frequency of the wave, in decimal +/// @param lowest the lowest output value of the sine wave +/// @param highest the highest output value of the sine wave +/// @param timebase the time offset of the wave from the millis() timer +/// @param phase_offset phase offset of the wave from the current position +LIB8STATIC uint16_t beatsin16( accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535, + uint32_t timebase = 0, uint16_t phase_offset = 0) +{ + uint16_t beat = beat16( beats_per_minute, timebase); + uint16_t beatsin = (sin16( beat + phase_offset) + 32768); + uint16_t rangewidth = highest - lowest; + uint16_t scaledbeat = scale16( beatsin, rangewidth); + uint16_t result = lowest + scaledbeat; + return result; +} + +/// Generates an 8-bit sine wave at a given BPM that oscillates within +/// a given range. +/// @param beats_per_minute the frequency of the wave, in decimal +/// @param lowest the lowest output value of the sine wave +/// @param highest the highest output value of the sine wave +/// @param timebase the time offset of the wave from the millis() timer +/// @param phase_offset phase offset of the wave from the current position +LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, + uint32_t timebase = 0, uint8_t phase_offset = 0) +{ + uint8_t beat = beat8( beats_per_minute, timebase); + uint8_t beatsin = sin8( beat + phase_offset); + uint8_t rangewidth = highest - lowest; + uint8_t scaledbeat = scale8( beatsin, rangewidth); + uint8_t result = lowest + scaledbeat; + return result; +} + +/// @} BeatGenerators + +/// @} lib8tion, to exclude timekeeping functions + + +/////////////////////////////////////////////////////////////////////// +/// +/// @addtogroup Timekeeping +/// @{ + +/// Return the current seconds since boot in a 16-bit value. Used as part of the +/// "every N time-periods" mechanism +LIB8STATIC uint16_t seconds16() +{ + uint32_t ms = GET_MILLIS(); + uint16_t s16; + s16 = ms / 1000; + return s16; +} + +/// Return the current minutes since boot in a 16-bit value. Used as part of the +/// "every N time-periods" mechanism +LIB8STATIC uint16_t minutes16() +{ + uint32_t ms = GET_MILLIS(); + uint16_t m16; + m16 = (ms / (60000L)) & 0xFFFF; + return m16; +} + +/// Return the current hours since boot in an 8-bit value. Used as part of the +/// "every N time-periods" mechanism +LIB8STATIC uint8_t hours8() +{ + uint32_t ms = GET_MILLIS(); + uint8_t h8; + h8 = (ms / (3600000L)) & 0xFF; + return h8; +} + + +/// Helper routine to divide a 32-bit value by 1024, returning +/// only the low 16 bits. +/// You'd think this would be just +/// @code +/// result = (in32 >> 10) & 0xFFFF; +/// @endcode +/// And on ARM, that's what you want and all is well. +/// But on AVR that code turns into a loop that executes +/// a four-byte shift ten times: 40 shifts in all, plus loop +/// overhead. This routine gets exactly the same result with +/// just six shifts (vs 40), and no loop overhead. +/// Used to convert millis to "binary seconds" aka bseconds: +/// one bsecond == 1024 millis. +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) + ); +#else + out16 = (in32 >> 10) & 0xFFFF; +#endif + return out16; +} + +/// Returns the current time-since-boot in +/// "binary seconds", which are actually 1024/1000 of a +/// second long. +LIB8STATIC uint16_t bseconds16() +{ + uint32_t ms = GET_MILLIS(); + uint16_t s16; + s16 = div1024_32_16( ms); + return s16; +} + +/// Preprocessor-based class "template" for ::CEveryNTime, used with `EVERY_N_TIME` timekeepers. +/// Classes to implement ::EVERY_N_MILLIS, ::EVERY_N_SECONDS, +/// ::EVERY_N_MINUTES, ::EVERY_N_HOURS, and ::EVERY_N_BSECONDS. +#if 1 +#define INSTANTIATE_EVERY_N_TIME_PERIODS(NAME,TIMETYPE,TIMEGETTER) \ +class NAME { \ +public: \ + TIMETYPE mPrevTrigger; \ + TIMETYPE mPeriod; \ + \ + NAME() { reset(); mPeriod = 1; }; \ + NAME(TIMETYPE period) { reset(); setPeriod(period); }; \ + void setPeriod( TIMETYPE period) { mPeriod = period; }; \ + TIMETYPE getTime() { return (TIMETYPE)(TIMEGETTER()); }; \ + TIMETYPE getPeriod() { return mPeriod; }; \ + TIMETYPE getElapsed() { return getTime() - mPrevTrigger; } \ + TIMETYPE getRemaining() { return mPeriod - getElapsed(); } \ + TIMETYPE getLastTriggerTime() { return mPrevTrigger; } \ + bool ready() { \ + bool isReady = (getElapsed() >= mPeriod); \ + if( isReady ) { reset(); } \ + return isReady; \ + } \ + void reset() { mPrevTrigger = getTime(); }; \ + void trigger() { mPrevTrigger = getTime() - mPeriod; }; \ + \ + operator bool() { return ready(); } \ +}; + +/// @name CEveryNTime Base Classes +/// These macros define the time interval checking classes +/// used in the `EVERY_N_TIME` time macros. +/// @{ + +#if defined(FASTLED_DOXYGEN) +/// Time interval checking class. +/// Keeps track of a time interval in order to limit how often code +/// is executed. +/// @note TIMETYPE is specific to the initialized class, and is in the +/// units used by the time function. E.g. for ::EVERY_N_MILLIS it's uint32_t +/// and milliseconds, for ::EVERY_N_HOURS it's uint8_t and hours, etc. +/// @warning This specific class isn't actually part of the library! It's created +/// using a preprocessor macro (::INSTANTIATE_EVERY_N_TIME_PERIODS) as +/// a new class for every different time unit. It has been recreated +/// specifically for the documentation, so that the methods can be documented +/// as usual. +/// @see INSTANTIATE_EVERY_N_TIME_PERIODS +class CEveryNTime { +public: + TIMETYPE mPrevTrigger; ///< Timestamp of the last time the class was "ready" + TIMETYPE mPeriod; ///< Timing interval to check + + /// Default constructor + CEveryNTime() { reset(); mPeriod = 1; }; + /// Constructor + /// @param period the time interval between triggers + CEveryNTime(TIMETYPE period) { reset(); setPeriod(period); }; + + /// Set the time interval between triggers + void setPeriod( TIMETYPE period) { mPeriod = period; }; + + /// Get the current time according to the class' timekeeper + TIMETYPE getTime() { return (TIMETYPE)(TIMEGETTER()); }; + + /// Get the time interval between triggers + TIMETYPE getPeriod() { return mPeriod; }; + + /// Get the time elapsed since the last trigger event + TIMETYPE getElapsed() { return getTime() - mPrevTrigger; } + + /// Get the time until the next trigger event + TIMETYPE getRemaining() { return mPeriod - getElapsed(); } + + /// Get the timestamp of the most recent trigger event + TIMETYPE getLastTriggerTime() { return mPrevTrigger; } + + /// Check if the time interval has elapsed + bool ready() { + bool isReady = (getElapsed() >= mPeriod); + if( isReady ) { reset(); } + return isReady; + } + + /// Reset the timestamp to the current time + void reset() { mPrevTrigger = getTime(); }; + + /// Reset the timestamp so it is ready() on next call + void trigger() { mPrevTrigger = getTime() - mPeriod; }; + + /// @copydoc ready() + operator bool() { return ready(); } +}; +#endif // FASTLED_DOXYGEN + +/// Create the CEveryNMillis class for millisecond intervals +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNMillis,uint32_t,GET_MILLIS); + +/// Create the CEveryNSeconds class for second intervals +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNSeconds,uint16_t,seconds16); + +/// Create the CEveryNBSeconds class for bsecond intervals +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNBSeconds,uint16_t,bseconds16); + +/// Create the CEveryNMinutes class for minutes intervals +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNMinutes,uint16_t,minutes16); + +/// Create the CEveryNHours class for hours intervals +INSTANTIATE_EVERY_N_TIME_PERIODS(CEveryNHours,uint8_t,hours8); + +/// Alias for CEveryNMillis +#define CEveryNMilliseconds CEveryNMillis +/// @} CEveryNTime Base Classes + +#else + +// Under C++11 rules, we would be allowed to use not-external +// -linkage-type symbols as template arguments, +// e.g., LIB8STATIC seconds16, and we'd be able to use these +// templates as shown below. +// However, under C++03 rules, we cannot do that, and thus we +// have to resort to the preprocessor to 'instantiate' 'templates', +// as handled above. +template +class CEveryNTimePeriods { +public: + timeType mPrevTrigger; + timeType mPeriod; + + CEveryNTimePeriods() { reset(); mPeriod = 1; }; + CEveryNTimePeriods(timeType period) { reset(); setPeriod(period); }; + void setPeriod( timeType period) { mPeriod = period; }; + timeType getTime() { return (timeType)(timeGetter()); }; + timeType getPeriod() { return mPeriod; }; + timeType getElapsed() { return getTime() - mPrevTrigger; } + timeType getRemaining() { return mPeriod - getElapsed(); } + timeType getLastTriggerTime() { return mPrevTrigger; } + bool ready() { + bool isReady = (getElapsed() >= mPeriod); + if( isReady ) { reset(); } + return isReady; + } + void reset() { mPrevTrigger = getTime(); }; + void trigger() { mPrevTrigger = getTime() - mPeriod; }; + + operator bool() { return ready(); } +}; +typedef CEveryNTimePeriods CEveryNSeconds; +typedef CEveryNTimePeriods CEveryNBSeconds; +typedef CEveryNTimePeriods CEveryNMillis; +typedef CEveryNTimePeriods CEveryNMinutes; +typedef CEveryNTimePeriods CEveryNHours; +#endif + + +/// @name "EVERY_N_TIME" Macros +/// Check whether to excecute a block of code every N amount of time. +/// These are useful for limiting how often code runs. For example, +/// you can use ::fill_rainbow() to fill a strip of LEDs with color, +/// combined with an ::EVERY_N_MILLIS block to limit how fast the colors +/// change: +/// @code{.cpp} +/// static uint8_t hue = 0; +/// fill_rainbow(leds, NUM_LEDS, hue); +/// EVERY_N_MILLIS(20) { hue++; } // advances hue every 20 milliseconds +/// @endcode +/// Note that in order for these to be accurate, the EVERY_N block must +/// be evaluated at a regular basis. +/// @{ + +/// @cond +#define CONCAT_HELPER( x, y ) x##y +#define CONCAT_MACRO( x, y ) CONCAT_HELPER( x, y ) +/// @endcond + + +/// Checks whether to execute a block of code every N milliseconds +/// @see GET_MILLIS +#define EVERY_N_MILLIS(N) EVERY_N_MILLIS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) + +/// Checks whether to execute a block of code every N milliseconds, using a custom instance name +/// @copydetails EVERY_N_MILLIS +#define EVERY_N_MILLIS_I(NAME,N) static CEveryNMillis NAME(N); if( NAME ) + + +/// Checks whether to execute a block of code every N seconds +/// @see seconds16() +#define EVERY_N_SECONDS(N) EVERY_N_SECONDS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) + +/// Checks whether to execute a block of code every N seconds, using a custom instance name +/// @copydetails EVERY_N_SECONDS +#define EVERY_N_SECONDS_I(NAME,N) static CEveryNSeconds NAME(N); if( NAME ) + + +/// Checks whether to execute a block of code every N bseconds +/// @see bseconds16() +#define EVERY_N_BSECONDS(N) EVERY_N_BSECONDS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) + +/// Checks whether to execute a block of code every N bseconds, using a custom instance name +/// @copydetails EVERY_N_BSECONDS +#define EVERY_N_BSECONDS_I(NAME,N) static CEveryNBSeconds NAME(N); if( NAME ) + + +/// Checks whether to execute a block of code every N minutes +/// @see minutes16() +#define EVERY_N_MINUTES(N) EVERY_N_MINUTES_I(CONCAT_MACRO(PER, __COUNTER__ ),N) + +/// Checks whether to execute a block of code every N minutes, using a custom instance name +/// @copydetails EVERY_N_MINUTES +#define EVERY_N_MINUTES_I(NAME,N) static CEveryNMinutes NAME(N); if( NAME ) + + +/// Checks whether to execute a block of code every N hours +/// @see hours8() +#define EVERY_N_HOURS(N) EVERY_N_HOURS_I(CONCAT_MACRO(PER, __COUNTER__ ),N) + +/// Checks whether to execute a block of code every N hours, using a custom instance name +/// @copydetails EVERY_N_HOURS +#define EVERY_N_HOURS_I(NAME,N) static CEveryNHours NAME(N); if( NAME ) + + +/// Alias for ::EVERY_N_MILLIS +#define EVERY_N_MILLISECONDS(N) EVERY_N_MILLIS(N) +/// Alias for ::EVERY_N_MILLIS_I +#define EVERY_N_MILLISECONDS_I(NAME,N) EVERY_N_MILLIS_I(NAME,N) + +/// @} Every_N +/// @} Timekeeping + + +// These defines are used to declare hidden or commented symbols for the +// purposes of Doxygen documentation generation. They do not affect your program. +#ifdef FASTLED_DOXYGEN +/// Set this flag to use the get_millisecond_timer() function in place +/// of the default millis() function. +/// @ingroup Timekeeping +#define USE_GET_MILLISECOND_TIMER +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/lib8tion/math8.h b/lib/FastLED/src/lib8tion/math8.h new file mode 100644 index 00000000..98e0adb2 --- /dev/null +++ b/lib/FastLED/src/lib8tion/math8.h @@ -0,0 +1,710 @@ +#ifndef __INC_LIB8TION_MATH_H +#define __INC_LIB8TION_MATH_H + +#include "scale8.h" + + +/// @file math8.h +/// Fast, efficient 8-bit math functions specifically +/// designed for high-performance LED programming. + +/// @ingroup lib8tion +/// @{ + +/// @defgroup Math Basic Math Operations +/// Fast, efficient 8-bit math functions specifically +/// designed for high-performance LED programming. +/// +/// Because of the AVR (Arduino) and ARM assembly language +/// implementations provided, using these functions often +/// results in smaller and faster code than the equivalent +/// program using plain "C" arithmetic and logic. +/// @{ + +/// Add one byte to another, saturating at 0xFF +/// @param i first byte to add +/// @param j second byte to add +/// @returns the sum of i + j, capped at 0xFF +LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j) +{ +#if QADD8_C == 1 + unsigned int t = i + j; + if( t > 255) t = 255; + 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) + ); + return i; +#elif QADD8_ARM_DSP_ASM == 1 + asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j)); + return i; +#else +#error "No implementation for qadd8 available." +#endif +} + +/// Add one byte to another, saturating at 0x7F and -0x80 +/// @param i first byte to add +/// @param j second byte to add +/// @returns the sum of i + j, capped at 0x7F and -0x80 +LIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j) +{ +#if QADD7_C == 1 + int16_t t = i + j; + if( t > 127) t = 127; + else if( t < -128) t = -128; + return t; +#elif QADD7_AVRASM == 1 + asm volatile( + /* First, add j to i, conditioning the V and C flags */ + "add %0, %1 \n\t" + + /* Now test the V flag. + If V is clear, we branch to end. + If V is set, we go ahead and load 0x7F into i. + */ + "brvc L_%= \n\t" + "ldi %0, 0x7F \n\t" + + /* When both numbers are negative, C is set. + Adding it to make result negative. */ + "adc %0, __zero_reg__\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)); + return i; +#else +#error "No implementation for qadd7 available." +#endif +} + +/// Subtract one byte from another, saturating at 0x00 +/// @param i byte to subtract from +/// @param j byte to subtract +/// @returns i - j with a floor of 0 +LIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j) +{ +#if QSUB8_C == 1 + int t = i - j; + if( t < 0) t = 0; + return t; +#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) + ); + return i; +#else +#error "No implementation for qsub8 available." +#endif +} + +/// Add one byte to another, with 8-bit result +/// @note This does not saturate and may overflow! +/// @param i first byte to add +/// @param j second byte to add +/// @returns the sum of i + j, 8-bit +LIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j) +{ +#if ADD8_C == 1 + int t = i + j; + return t; +#elif ADD8_AVRASM == 1 + // Add j to i, period. + asm volatile( "add %0, %1" : "+a" (i) : "a" (j)); + return i; +#else +#error "No implementation for add8 available." +#endif +} + +/// Add one byte to two bytes, with 16-bit result +/// @note This does not saturate and may overflow! +/// @param i first value to add, 8-bit +/// @param j second value to add, 16-bit +/// @returns the sum of i + j, 16-bit +LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j) +{ +#if ADD8_C == 1 + uint16_t t = i + 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) + ); + return i; +#else +#error "No implementation for add8to16 available." +#endif +} + + +/// Subtract one byte from another, 8-bit result +/// @note This does not saturate and may overflow! +/// @param i byte to subtract from +/// @param j byte to subtract +/// @returns i - j +LIB8STATIC_ALWAYS_INLINE uint8_t sub8( uint8_t i, uint8_t j) +{ +#if SUB8_C == 1 + int t = i - j; + return t; +#elif SUB8_AVRASM == 1 + // Subtract j from i, period. + asm volatile( "sub %0, %1" : "+a" (i) : "a" (j)); + return i; +#else +#error "No implementation for sub8 available." +#endif +} + +/// Calculate an integer average of two unsigned +/// 8-bit integer values (uint8_t), rounded down. +/// Fractional results are rounded down, e.g. avg8(20,41) = 30 +/// @param i first value to average +/// @param j second value to average +/// @returns mean average of i and j, rounded down +LIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j) +{ +#if AVG8_C == 1 + 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) + ); + return i; +#else +#error "No implementation for avg8 available." +#endif +} + +/// Calculate an integer average of two unsigned +/// 16-bit integer values (uint16_t), rounded down. +/// Fractional results are rounded down, e.g. avg16(20,41) = 30 +/// @param i first value to average +/// @param j second value to average +/// @returns mean average of i and j, rounded down +LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j) +{ +#if AVG16_C == 1 + 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) + ); + return i; +#else +#error "No implementation for avg16 available." +#endif +} + +/// Calculate an integer average of two unsigned +/// 8-bit integer values (uint8_t), rounded up. +/// Fractional results are rounded up, e.g. avg8r(20,41) = 31 +/// @param i first value to average +/// @param j second value to average +/// @returns mean average of i and j, rounded up +LIB8STATIC_ALWAYS_INLINE uint8_t avg8r( uint8_t i, uint8_t j) +{ +#if AVG8R_C == 1 + return (i + j + 1) >> 1; +#elif AVG8R_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, old 1st bit now in C */ + "ror %0 \n\t" + /* Add C flag */ + "adc %0, __zero_reg__\n\t" + : "+a" (i) + : "a" (j) + ); + return i; +#else +#error "No implementation for avg8r available." +#endif +} + +/// Calculate an integer average of two unsigned +/// 16-bit integer values (uint16_t), rounded up. +/// Fractional results are rounded up, e.g. avg16r(20,41) = 31 +/// @param i first value to average +/// @param j second value to average +/// @returns mean average of i and j, rounded up +LIB8STATIC_ALWAYS_INLINE uint16_t avg16r( uint16_t i, uint16_t j) +{ +#if AVG16R_C == 1 + return (uint32_t)((uint32_t)(i) + (uint32_t)(j) + 1) >> 1; +#elif AVG16R_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, old 1st bit now in C */ + "ror %A[i] \n\t" + /* Add C flag */ + "adc %A[i], __zero_reg__\n\t" + "adc %B[i], __zero_reg__\n\t" + : [i] "+a" (i) + : [j] "a" (j) + ); + return i; +#else +#error "No implementation for avg16r available." +#endif +} + + +/// Calculate an integer average of two signed 7-bit +/// integers (int8_t). +/// If the first argument is even, result is rounded down. +/// If the first argument is odd, result is rounded up. +/// @param i first value to average +/// @param j second value to average +/// @returns mean average of i and j, rounded +LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j) +{ +#if AVG7_C == 1 + return (i>>1) + (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) + ); + return i; +#else +#error "No implementation for avg7 available." +#endif +} + +/// Calculate an integer average of two signed 15-bit +/// integers (int16_t). +/// If the first argument is even, result is rounded down. +/// If the first argument is odd, result is rounded up. +/// @param i first value to average +/// @param j second value to average +/// @returns mean average of i and j, rounded +LIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j) +{ +#if AVG15_C == 1 + return (i>>1) + (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) + ); + return i; +#else +#error "No implementation for avg15 available." +#endif +} + + +/// Calculate the remainder of one unsigned 8-bit +/// value divided by anoter, aka A % M. +/// Implemented by repeated subtraction, which is +/// very compact, and very fast if A is "probably" +/// less than M. If A is a large multiple of M, +/// the loop has to execute multiple times. However, +/// even in that case, the loop is only two +/// instructions long on AVR, i.e., quick. +/// @param a dividend byte +/// @param m divisor byte +/// @returns remainder of a / m (i.e. a % m) +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) + ); +#else + while( a >= m) a -= m; +#endif + return a; +} + +/// Add two numbers, and calculate the modulo +/// of the sum and a third number, M. +/// In other words, it returns (A+B) % M. +/// It is designed as a compact mechanism for +/// incrementing a "mode" switch and wrapping +/// around back to "mode 0" when the switch +/// goes past the end of the available range. +/// e.g. if you have seven modes, this switches +/// to the next one and wraps around if needed: +/// @code{.cpp} +/// mode = addmod8( mode, 1, 7); +/// @endcode +/// @param a dividend byte +/// @param b value to add to the dividend +/// @param m divisor byte +/// @returns remainder of (a + b) / m +/// @see mod8() for notes on performance. +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) + ); +#else + a += b; + while( a >= m) a -= m; +#endif + return a; +} + +/// Subtract two numbers, and calculate the modulo +/// of the difference and a third number, M. +/// In other words, it returns (A-B) % M. +/// It is designed as a compact mechanism for +/// decrementing a "mode" switch and wrapping +/// around back to "mode 0" when the switch +/// goes past the start of the available range. +/// e.g. if you have seven modes, this switches +/// to the previous one and wraps around if needed: +/// @code{.cpp} +/// mode = submod8( mode, 1, 7); +/// @endcode +/// @param a dividend byte +/// @param b value to subtract from the dividend +/// @param m divisor byte +/// @returns remainder of (a - b) / m +/// @see mod8() for notes on performance. +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) + ); +#else + a -= b; + while( a >= m) a -= m; +#endif + return a; +} + +/// 8x8 bit multiplication, with 8-bit result. +/// @param i first byte to multiply +/// @param j second byte to multiply +/// @returns the product of i * j +/// @note This does not saturate and may overflow! +LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j) +{ +#if MUL8_C == 1 + 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" + ); + return i; +#else +#error "No implementation for mul8 available." +#endif +} + + +/// 8x8 bit multiplication with 8-bit result, saturating at 0xFF. +/// @param i first byte to multiply +/// @param j second byte to multiply +/// @returns the product of i * j, capping at 0xFF +LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j) +{ +#if QMUL8_C == 1 + unsigned p = (unsigned)i * (unsigned)j; + if( p > 255) p = 255; + 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" + /* Extract the LOW 8-bits (r0) */ + " mov %0, r0 \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 to 0xFF */ + " ldi %0, 0xFF \n\t" + "Lnospill_%=: \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." +#endif +} + + +/// Take the absolute value of a signed 8-bit uint8_t. +LIB8STATIC_ALWAYS_INLINE int8_t abs8( int8_t i) +{ +#if ABS8_C == 1 + 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" + + /* Negate the value */ + "neg %0 \n" + + : "+r" (i) : "r" (i) + ); + return i; +#else +#error "No implementation for abs8 available." +#endif +} + +/// Square root for 16-bit integers. +/// About three times faster and five times smaller +/// than Arduino's general `sqrt` on AVR. +LIB8STATIC uint8_t sqrt16(uint16_t x) +{ + if( x <= 1) { + return x; + } + + uint8_t low = 1; // lower bound + uint8_t hi, mid; + + if( x > 7904) { + hi = 255; + } else { + hi = (x >> 5) + 8; // initial estimate for upper bound + } + + do { + mid = (low + hi) >> 1; + if ((uint16_t)(mid * mid) > x) { + hi = mid - 1; + } else { + if( mid == 255) { + return 255; + } + low = mid + 1; + } + } while (hi >= low); + + return low - 1; +} + +/// Blend a variable proportion (0-255) of one byte to another. +/// @param a the starting byte value +/// @param b the byte value to blend toward +/// @param amountOfB the proportion (0-255) of b to blend +/// @returns a byte value between a and b, inclusive +#if (FASTLED_BLEND_FIXED == 1) +LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB) +{ + + // The BLEND_FIXED formula is + // + // result = ( A*(amountOfA) + B*(amountOfB) )/ 256 + // + // …where amountOfA = 255-amountOfB. + // + // This formula will never return 255, which is why the BLEND_FIXED + SCALE8_FIXED version is + // + // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256 + // + // We can rearrange this formula for some great optimisations. + // + // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256 + // = ( A*(255-amountOfB) + A + B*(amountOfB) + B ) / 256 + // = ( A*(256-amountOfB) + B*(amountOfB) + B ) / 256 + // = ( A*256 + B + B*(amountOfB) - A*(amountOfB) ) / 256 // this is the version used in SCALE8_FIXED AVR below + // = ( A*256 + B + (B-A)*(amountOfB) ) / 256 // this is the version used in SCALE8_FIXED C below + + uint16_t partial; + uint8_t result; + +#if BLEND8_C == 1 + +# if (FASTLED_SCALE8_FIXED == 1) + partial = (a << 8) | b; // A*256 + B + + // on many platforms this compiles to a single multiply of (B-A) * amountOfB + partial += (b * amountOfB); + partial -= (a * amountOfB); + +# else + uint8_t amountOfA = 255 - amountOfB; + + // on the other hand, this compiles to two multiplies, and gives the "wrong" answer :] + partial = (a * amountOfA); + partial += (b * amountOfB); +# endif + + result = partial >> 8; + + return result; + +#elif BLEND8_AVRASM == 1 + +# if (FASTLED_SCALE8_FIXED == 1) + + // 1 or 2 cycles depending on how the compiler optimises + partial = (a << 8) | b; + + // 7 cycles + asm volatile ( + " mul %[a], %[amountOfB] \n\t" + " sub %A[partial], r0 \n\t" + " sbc %B[partial], r1 \n\t" + " mul %[b], %[amountOfB] \n\t" + " add %A[partial], r0 \n\t" + " adc %B[partial], r1 \n\t" + " clr __zero_reg__ \n\t" + : [partial] "+r" (partial) + : [amountOfB] "r" (amountOfB), + [a] "r" (a), + [b] "r" (b) + : "r0", "r1" + ); + +# else + + // non-SCALE8-fixed version + + // 7 cycles + asm volatile ( + /* partial = b * amountOfB */ + " mul %[b], %[amountOfB] \n\t" + " movw %A[partial], r0 \n\t" + + /* amountOfB (aka amountOfA) = 255 - amountOfB */ + " com %[amountOfB] \n\t" + + /* partial += a * amountOfB (aka amountOfA) */ + " mul %[a], %[amountOfB] \n\t" + + " add %A[partial], r0 \n\t" + " adc %B[partial], r1 \n\t" + + " clr __zero_reg__ \n\t" + + : [partial] "=r" (partial), + [amountOfB] "+a" (amountOfB) + : [a] "a" (a), + [b] "a" (b) + : "r0", "r1" + ); + +# endif + + result = partial >> 8; + + return result; + +#else +# error "No implementation for blend8 available." +#endif +} + +#else +LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB) +{ + // This version loses precision in the integer math + // and can actually return results outside of the range + // from a to b. Its use is not recommended. + uint8_t result; + uint8_t amountOfA = 255 - amountOfB; + result = scale8_LEAVING_R1_DIRTY( a, amountOfA) + + scale8_LEAVING_R1_DIRTY( b, amountOfB); + cleanup_R1(); + return result; +} +#endif + + +/// @} Math +/// @} lib8tion + +#endif diff --git a/lib/FastLED/src/lib8tion/random8.h b/lib/FastLED/src/lib8tion/random8.h new file mode 100644 index 00000000..1df93413 --- /dev/null +++ b/lib/FastLED/src/lib8tion/random8.h @@ -0,0 +1,118 @@ +#ifndef __INC_LIB8TION_RANDOM_H +#define __INC_LIB8TION_RANDOM_H + +/// @file random8.h +/// Fast, efficient random number generators specifically +/// designed for high-performance LED programming. + +/// @ingroup lib8tion +/// @{ + +/// @defgroup Random Fast Random Number Generators +/// Fast 8-bit and 16-bit unsigned random number generators. +/// Significantly faster than Arduino random(), but +/// also somewhat less random. You can add entropy. +/// +/// Pseudo-random number generation follows the form: +/// @code +/// X(n+1) = (2053 * X(n)) + 13849) +/// @endcode +/// @{ + + +/// Multiplier value for pseudo-random number generation +#define FASTLED_RAND16_2053 ((uint16_t)(2053)) +/// Increment value for pseudo-random number generation +#define FASTLED_RAND16_13849 ((uint16_t)(13849)) + +#if defined(LIB8_ATTINY) +/// Multiplies a value by the pseudo-random multiplier +#define APPLY_FASTLED_RAND16_2053(x) (x << 11) + (x << 2) + x +#else +/// Multiplies a value by the pseudo-random multiplier +#define APPLY_FASTLED_RAND16_2053(x) (x * FASTLED_RAND16_2053) +#endif + +/// Seed for the random number generator functions +extern uint16_t rand16seed; // = RAND16_SEED; + +/// Generate an 8-bit random number +/// @returns random 8-bit number, in the range 0-255 +LIB8STATIC uint8_t random8() +{ + 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)) + + ((uint8_t)(rand16seed >> 8))); +} + +/// Generate a 16-bit random number +/// @returns random 16-bit number, in the range 0-65535 +LIB8STATIC uint16_t random16() +{ + rand16seed = APPLY_FASTLED_RAND16_2053(rand16seed) + FASTLED_RAND16_13849; + return rand16seed; +} + +/// Generate an 8-bit random number between 0 and lim +/// @param lim the upper bound for the result, exclusive +LIB8STATIC uint8_t random8(uint8_t lim) +{ + uint8_t r = random8(); + r = (r*lim) >> 8; + return r; +} + +/// Generate an 8-bit random number in the given range +/// @param min the lower bound for the random number, inclusive +/// @param lim the upper bound for the random number, exclusive +LIB8STATIC uint8_t random8(uint8_t min, uint8_t lim) +{ + uint8_t delta = lim - min; + uint8_t r = random8(delta) + min; + return r; +} + +/// Generate an 16-bit random number between 0 and lim +/// @param lim the upper bound for the result, exclusive +LIB8STATIC uint16_t random16( uint16_t lim) +{ + uint16_t r = random16(); + uint32_t p = (uint32_t)lim * (uint32_t)r; + r = p >> 16; + return r; +} + +/// Generate an 16-bit random number in the given range +/// @param min the lower bound for the random number, inclusive +/// @param lim the upper bound for the random number, exclusive +LIB8STATIC uint16_t random16( uint16_t min, uint16_t lim) +{ + uint16_t delta = lim - min; + uint16_t r = random16( delta) + min; + return r; +} + +/// Set the 16-bit seed used for the random number generator +LIB8STATIC void random16_set_seed( uint16_t seed) +{ + rand16seed = seed; +} + +/// Get the current seed value for the random number generator +LIB8STATIC uint16_t random16_get_seed() +{ + return rand16seed; +} + +/// Add entropy into the random number generator +LIB8STATIC void random16_add_entropy( uint16_t entropy) +{ + rand16seed += entropy; +} + +/// @} Random +/// @} lib8tion + +#endif diff --git a/lib/FastLED/src/lib8tion/scale8.h b/lib/FastLED/src/lib8tion/scale8.h new file mode 100644 index 00000000..75e72fbb --- /dev/null +++ b/lib/FastLED/src/lib8tion/scale8.h @@ -0,0 +1,778 @@ +#ifndef __INC_LIB8TION_SCALE_H +#define __INC_LIB8TION_SCALE_H + +/// @file scale8.h +/// Fast, efficient 8-bit scaling functions specifically +/// designed for high-performance LED programming. + +/// @addtogroup lib8tion +/// @{ + +/// @defgroup Scaling Scaling Functions +/// Fast, efficient 8-bit scaling functions specifically +/// designed for high-performance LED programming. +/// +/// Because of the AVR(Arduino) and ARM assembly language +/// implementations provided, using these functions often +/// results in smaller and faster code than the equivalent +/// program using plain "C" arithmetic and logic. +/// @{ + + +/// Scale one byte by a second one, which is treated as +/// the numerator of a fraction whose denominator is 256. +/// +/// In other words, it computes i * (scale / 256) +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @returns scaled value +/// @note Takes 4 clocks on AVR with MUL, 2 clocks on ARM +LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale) +{ +#if SCALE8_C == 1 +#if (FASTLED_SCALE8_FIXED == 1) + return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8; +#else + return ((uint16_t)i * (uint16_t)(scale) ) >> 8; +#endif +#elif SCALE8_AVRASM == 1 +#if defined(LIB8_ATTINY) +#if (FASTLED_SCALE8_FIXED == 1) + uint8_t work=i; +#else + uint8_t work=0; +#endif + uint8_t cnt=0x80; + asm volatile( +#if (FASTLED_SCALE8_FIXED == 1) + " inc %[scale] \n\t" + " breq DONE_%= \n\t" + " clr %[work] \n\t" +#endif + "LOOP_%=: \n\t" + /*" sbrc %[scale], 0 \n\t" + " add %[work], %[i] \n\t" + " ror %[work] \n\t" + " lsr %[scale] \n\t" + " clc \n\t"*/ + " sbrc %[scale], 0 \n\t" + " add %[work], %[i] \n\t" + " ror %[work] \n\t" + " lsr %[scale] \n\t" + " lsr %[cnt] \n\t" + "brcc LOOP_%= \n\t" + "DONE_%=: \n\t" + : [work] "+r" (work), [cnt] "+r" (cnt) + : [scale] "r" (scale), [i] "r" (i) + : + ); + return work; +#else + 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" + /* 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 */ + ); + /* Return the result */ + return i; +#endif +#else +#error "No implementation for scale8 available." +#endif +} + + +/// The "video" version of scale8() guarantees that the output will +/// be only be zero if one or both of the inputs are zero. +/// If both inputs are non-zero, the output is guaranteed to be non-zero. +/// This makes for better "video"/LED dimming, at the cost of +/// several additional cycles. +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @returns scaled value +/// @see scale8() +LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale) +{ +#if SCALE8_C == 1 || defined(LIB8_ATTINY) + uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0); + // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; + // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale; + return j; +#elif SCALE8_AVRASM == 1 + uint8_t j=0; + asm volatile( + " tst %[i]\n\t" + " breq L_%=\n\t" + " mul %[i], %[scale]\n\t" + " mov %[j], r1\n\t" + " clr __zero_reg__\n\t" + " cpse %[scale], r1\n\t" + " subi %[j], 0xFF\n\t" + "L_%=: \n\t" + : [j] "+a" (j) + : [i] "a" (i), [scale] "a" (scale) + : "r0", "r1" + ); + return j; + // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; + // asm volatile( + // " tst %0 \n" + // " breq L_%= \n" + // " mul %0, %1 \n" + // " mov %0, r1 \n" + // " add %0, %2 \n" + // " clr __zero_reg__ \n" + // "L_%=: \n" + // : "+a" (i) + // : "a" (scale), "a" (nonzeroscale) + // : "r0", "r1"); + // // Return the result + // return i; +#else +#error "No implementation for scale8_video available." +#endif +} + + +/// @defgroup ScalingDirty Scaling Functions that Leave R1 Dirty +/// These functions are more efficient for scaling multiple +/// bytes at once, but require calling cleanup_R1() afterwards. +/// @{ + + +/// This version of scale8() does not clean up the R1 register on AVR. +/// If you are doing several "scale8()'s" in a row, use this, and +/// then explicitly call cleanup_R1(). +/// @warning You **MUST** call cleanup_R1() after using this function! +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @returns scaled value +/// @see scale8() +LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale) +{ +#if SCALE8_C == 1 +#if (FASTLED_SCALE8_FIXED == 1) + return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8; +#else + return ((int)i * (int)(scale) ) >> 8; +#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 */ + ); + // Return the result + return i; +#else +#error "No implementation for scale8_LEAVING_R1_DIRTY available." +#endif +} + +/// In place modifying version of scale8() that does not clean up the R1 register on AVR. +/// If you are doing several "scale8()'s" in a row, use this, and +/// then explicitly call cleanup_R1(). +/// @warning You **MUST** call cleanup_R1() after using this function! +/// @par +/// @warning This function always modifies its arguments in place! +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @see scale8() +LIB8STATIC_ALWAYS_INLINE void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale) +{ +#if SCALE8_C == 1 +#if (FASTLED_SCALE8_FIXED == 1) + i = (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8; +#else + i = ((int)i * (int)(scale) ) >> 8; +#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 */ + ); +#else +#error "No implementation for nscale8_LEAVING_R1_DIRTY available." +#endif +} + + +/// This version of scale8_video() does not clean up the R1 register on AVR. +/// If you are doing several "scale8_video()'s" in a row, use this, and +/// then explicitly call cleanup_R1(). +/// @warning You **MUST** call cleanup_R1() after using this function! +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @returns scaled value +/// @see scale8_video() +LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale) +{ +#if SCALE8_C == 1 || defined(LIB8_ATTINY) + uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0); + // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; + // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale; + return j; +#elif SCALE8_AVRASM == 1 + uint8_t j=0; + asm volatile( + " tst %[i]\n\t" + " breq L_%=\n\t" + " mul %[i], %[scale]\n\t" + " mov %[j], r1\n\t" + " breq L_%=\n\t" + " subi %[j], 0xFF\n\t" + "L_%=: \n\t" + : [j] "+a" (j) + : [i] "a" (i), [scale] "a" (scale) + : "r0", "r1" + ); + return j; + // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; + // asm volatile( + // " tst %0 \n" + // " breq L_%= \n" + // " mul %0, %1 \n" + // " mov %0, r1 \n" + // " add %0, %2 \n" + // " clr __zero_reg__ \n" + // "L_%=: \n" + // : "+a" (i) + // : "a" (scale), "a" (nonzeroscale) + // : "r0", "r1"); + // // Return the result + // return i; +#else +#error "No implementation for scale8_video_LEAVING_R1_DIRTY available." +#endif +} + +/// In place modifying version of scale8_video() that does not clean up the R1 register on AVR. +/// If you are doing several "scale8_video()'s" in a row, use this, and +/// then explicitly call cleanup_R1(). +/// @warning You **MUST** call cleanup_R1() after using this function! +/// @par +/// @warning This function always modifies its arguments in place! +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @see scale8_video() +LIB8STATIC_ALWAYS_INLINE void nscale8_video_LEAVING_R1_DIRTY( uint8_t & i, fract8 scale) +{ +#if SCALE8_C == 1 || defined(LIB8_ATTINY) + i = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0); +#elif SCALE8_AVRASM == 1 + asm volatile( + " tst %[i]\n\t" + " breq L_%=\n\t" + " mul %[i], %[scale]\n\t" + " mov %[i], r1\n\t" + " breq L_%=\n\t" + " subi %[i], 0xFF\n\t" + "L_%=: \n\t" + : [i] "+a" (i) + : [scale] "a" (scale) + : "r0", "r1" + ); +#else +#error "No implementation for scale8_video_LEAVING_R1_DIRTY available." +#endif +} + +/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls +/// @ingroup ScalingDirty +LIB8STATIC_ALWAYS_INLINE void cleanup_R1() +{ +#if CLEANUP_R1_AVRASM == 1 + // Restore r1 to "0"; it's expected to always be that + asm volatile( "clr __zero_reg__ \n\t" : : : "r1" ); +#endif +} + +/// @} ScalingDirty + + +/// Scale three one-byte values by a fourth one, which is treated as +/// the numerator of a fraction whose demominator is 256. +/// +/// In other words, it computes r,g,b * (scale / 256) +/// +/// @warning This function always modifies its arguments in place! +/// @param r first value to scale +/// @param g second value to scale +/// @param b third value to scale +/// @param scale scale factor, in n/256 units +LIB8STATIC void nscale8x3( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale) +{ +#if SCALE8_C == 1 +#if (FASTLED_SCALE8_FIXED == 1) + uint16_t scale_fixed = scale + 1; + r = (((uint16_t)r) * scale_fixed) >> 8; + g = (((uint16_t)g) * scale_fixed) >> 8; + b = (((uint16_t)b) * scale_fixed) >> 8; +#else + r = ((int)r * (int)(scale) ) >> 8; + g = ((int)g * (int)(scale) ) >> 8; + b = ((int)b * (int)(scale) ) >> 8; +#endif +#elif SCALE8_AVRASM == 1 + r = scale8_LEAVING_R1_DIRTY(r, scale); + g = scale8_LEAVING_R1_DIRTY(g, scale); + b = scale8_LEAVING_R1_DIRTY(b, scale); + cleanup_R1(); +#else +#error "No implementation for nscale8x3 available." +#endif +} + +/// Scale three one-byte values by a fourth one, which is treated as +/// the numerator of a fraction whose demominator is 256. +/// +/// In other words, it computes r,g,b * (scale / 256), ensuring +/// that non-zero values passed in remain non-zero, no matter how low the scale +/// argument. +/// +/// @warning This function always modifies its arguments in place! +/// @param r first value to scale +/// @param g second value to scale +/// @param b third value to scale +/// @param scale scale factor, in n/256 units +LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale) +{ +#if SCALE8_C == 1 + uint8_t nonzeroscale = (scale != 0) ? 1 : 0; + r = (r == 0) ? 0 : (((int)r * (int)(scale) ) >> 8) + nonzeroscale; + g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale; + b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale; +#elif SCALE8_AVRASM == 1 + nscale8_video_LEAVING_R1_DIRTY( r, scale); + nscale8_video_LEAVING_R1_DIRTY( g, scale); + nscale8_video_LEAVING_R1_DIRTY( b, scale); + cleanup_R1(); +#else +#error "No implementation for nscale8x3 available." +#endif +} + +/// Scale two one-byte values by a third one, which is treated as +/// the numerator of a fraction whose demominator is 256. +/// +/// In other words, it computes i,j * (scale / 256). +/// +/// @warning This function always modifies its arguments in place! +/// @param i first value to scale +/// @param j second value to scale +/// @param scale scale factor, in n/256 units +LIB8STATIC void nscale8x2( uint8_t& i, uint8_t& j, fract8 scale) +{ +#if SCALE8_C == 1 +#if FASTLED_SCALE8_FIXED == 1 + uint16_t scale_fixed = scale + 1; + i = (((uint16_t)i) * scale_fixed ) >> 8; + j = (((uint16_t)j) * scale_fixed ) >> 8; +#else + i = ((uint16_t)i * (uint16_t)(scale) ) >> 8; + j = ((uint16_t)j * (uint16_t)(scale) ) >> 8; +#endif +#elif SCALE8_AVRASM == 1 + i = scale8_LEAVING_R1_DIRTY(i, scale); + j = scale8_LEAVING_R1_DIRTY(j, scale); + cleanup_R1(); +#else +#error "No implementation for nscale8x2 available." +#endif +} + +/// Scale two one-byte values by a third one, which is treated as +/// the numerator of a fraction whose demominator is 256. +/// +/// In other words, it computes i,j * (scale / 256), ensuring +/// that non-zero values passed in remain non zero, no matter how low the scale +/// argument. +/// +/// @warning This function always modifies its arguments in place! +/// @param i first value to scale +/// @param j second value to scale +/// @param scale scale factor, in n/256 units +LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale) +{ +#if SCALE8_C == 1 + uint8_t nonzeroscale = (scale != 0) ? 1 : 0; + i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale; + j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale; +#elif SCALE8_AVRASM == 1 + nscale8_video_LEAVING_R1_DIRTY( i, scale); + nscale8_video_LEAVING_R1_DIRTY( j, scale); + cleanup_R1(); +#else +#error "No implementation for nscale8x2 available." +#endif +} + + +/// Scale a 16-bit unsigned value by an 8-bit value, which is treated +/// as the numerator of a fraction whose denominator is 256. +/// +/// In other words, it computes i * (scale / 256) +/// @param i input value to scale +/// @param scale scale factor, in n/256 units +/// @returns scaled value +LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale ) +{ +#if SCALE16BY8_C == 1 + uint16_t result; +#if FASTLED_SCALE8_FIXED == 1 + result = (i * (1+((uint16_t)scale))) >> 8; +#else + result = (i * scale) / 256; +#endif + return result; +#elif SCALE16BY8_AVRASM == 1 +#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" + ); + 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" + ); + return result; +#endif +#else + #error "No implementation for scale16by8 available." +#endif +} + +/// Scale a 16-bit unsigned value by an 16-bit value, which is treated +/// as the numerator of a fraction whose denominator is 65536. +/// In other words, it computes i * (scale / 65536) +/// @param i input value to scale +/// @param scale scale factor, in n/65536 units +/// @returns scaled value +LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale ) +{ + #if SCALE16_C == 1 + uint16_t result; +#if FASTLED_SCALE8_FIXED == 1 + result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536; +#else + result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536; +#endif + return result; +#elif SCALE16_AVRASM == 1 +#if FASTLED_SCALE8_FIXED == 1 + // implemented sort of like + // result = ((i * scale) + i ) / 65536 + // + // why not like this, you may ask? + // result = (i * (scale+1)) / 65536 + // the answer is that if scale is 65535, then scale+1 + // 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" +); + +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 = 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" + ); + + 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" + ); + + 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 = result >> 16; + return result; +#endif +#else + #error "No implementation for scale16 available." +#endif +} +/// @} Scaling + + +/// @defgroup Dimming Dimming and Brightening Functions +/// Functions to dim or brighten data. +/// +/// The eye does not respond in a linear way to light. +/// High speed PWM'd LEDs at 50% duty cycle appear far +/// brighter then the "half as bright" you might expect. +/// +/// If you want your midpoint brightness LEDs (128) to +/// appear half as bright as "full" brightness (255), you +/// have to apply a "dimming function". +/// +/// @note These are approximations of gamma correction with +/// a gamma value of 2.0. +/// @see @ref GammaFuncs +/// @{ + +/// Adjust a scaling value for dimming. +/// @see scale8() +LIB8STATIC uint8_t dim8_raw( uint8_t x) +{ + return scale8( x, x); +} + +/// Adjust a scaling value for dimming for video (value will never go below 1) +/// @see scale8_video() +LIB8STATIC uint8_t dim8_video( uint8_t x) +{ + return scale8_video( x, x); +} + +/// Linear version of the dimming function that halves for values < 128 +LIB8STATIC uint8_t dim8_lin( uint8_t x ) +{ + if( x & 0x80 ) { + x = scale8( x, x); + } else { + x += 1; + x /= 2; + } + return x; +} + +/// Brighten a value (inverse of dim8_raw()) +LIB8STATIC uint8_t brighten8_raw( uint8_t x) +{ + uint8_t ix = 255 - x; + return 255 - scale8( ix, ix); +} + +/// Brighten a value (inverse of dim8_video()) +LIB8STATIC uint8_t brighten8_video( uint8_t x) +{ + uint8_t ix = 255 - x; + return 255 - scale8_video( ix, ix); +} + +/// Brighten a value (inverse of dim8_lin()) +LIB8STATIC uint8_t brighten8_lin( uint8_t x ) +{ + uint8_t ix = 255 - x; + if( ix & 0x80 ) { + ix = scale8( ix, ix); + } else { + ix += 1; + ix /= 2; + } + return 255 - ix; +} + +/// @} Dimming +/// @} lib8tion + +#endif diff --git a/lib/FastLED/src/lib8tion/trig8.h b/lib/FastLED/src/lib8tion/trig8.h new file mode 100644 index 00000000..f4df8bc9 --- /dev/null +++ b/lib/FastLED/src/lib8tion/trig8.h @@ -0,0 +1,268 @@ +#ifndef __INC_LIB8TION_TRIG_H +#define __INC_LIB8TION_TRIG_H + +/// @file trig8.h +/// Fast, efficient 8-bit trigonometry functions specifically +/// designed for high-performance LED programming. + +/// @ingroup lib8tion +/// @{ + +/// @defgroup Trig Fast Trigonometry Functions +/// Fast 8-bit and 16-bit approximations of sin(x) and cos(x). +/// +/// Don't use these approximations for calculating the +/// trajectory of a rocket to Mars, but they're great +/// for art projects and LED displays. +/// +/// On Arduino/AVR, the 16-bit approximation is more than +/// 10X faster than floating point sin(x) and cos(x), while +/// the 8-bit approximation is more than 20X faster. +/// @{ + +#if defined(__AVR__) + +/// Platform-independent alias of the fast sin implementation +#define sin16 sin16_avr + +/// Fast 16-bit approximation of sin(x). This approximation never varies more than +/// 0.69% from the floating point value you'd get by doing +/// @code{.cpp} +/// float s = sin(x) * 32767.0; +/// @endcode +/// +/// @param theta input angle from 0-65535 +/// @returns sin of theta, value between -32767 to 32767. +LIB8STATIC int16_t sin16_avr( uint16_t theta ) +{ + static const uint8_t data[] = + { 0, 0, 49, 0, 6393%256, 6393/256, 48, 0, + 12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0, + 23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0, + 30273%256, 30273/256, 14, 0, 32137%256, 32137/256, 4 /*,0*/ }; + + uint16_t offset = (theta & 0x3FFF); + + // AVR doesn't have a multi-bit shift instruction, + // so if we say "offset >>= 3", gcc makes a tiny loop. + // Inserting empty volatile statements between each + // bit shift forces gcc to unroll the loop. + offset >>= 1; // 0..8191 + asm volatile(""); + offset >>= 1; // 0..4095 + asm volatile(""); + offset >>= 1; // 0..2047 + + if( theta & 0x4000 ) offset = 2047 - offset; + + uint8_t sectionX4; + sectionX4 = offset / 256; + sectionX4 *= 4; + + uint8_t m; + + union { + uint16_t b; + struct { + uint8_t blo; + uint8_t bhi; + }; + } u; + + //in effect u.b = blo + (256 * bhi); + u.blo = data[ sectionX4 ]; + u.bhi = data[ sectionX4 + 1]; + m = data[ sectionX4 + 2]; + + uint8_t secoffset8 = (uint8_t)(offset) / 2; + + uint16_t mx = m * secoffset8; + + int16_t y = mx + u.b; + if( theta & 0x8000 ) y = -y; + + return y; +} + +#else + +/// Platform-independent alias of the fast sin implementation +#define sin16 sin16_C + +/// Fast 16-bit approximation of sin(x). This approximation never varies more than +/// 0.69% from the floating point value you'd get by doing +/// @code{.cpp} +/// float s = sin(x) * 32767.0; +/// @endcode +/// +/// @param theta input angle from 0-65535 +/// @returns sin of theta, value between -32767 to 32767. +LIB8STATIC int16_t sin16_C( uint16_t theta ) +{ + static const uint16_t base[] = + { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 }; + static const uint8_t slope[] = + { 49, 48, 44, 38, 31, 23, 14, 4 }; + + uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047 + if( theta & 0x4000 ) offset = 2047 - offset; + + uint8_t section = offset / 256; // 0..7 + uint16_t b = base[section]; + uint8_t m = slope[section]; + + uint8_t secoffset8 = (uint8_t)(offset) / 2; + + uint16_t mx = m * secoffset8; + int16_t y = mx + b; + + if( theta & 0x8000 ) y = -y; + + return y; +} + +#endif + +/// Fast 16-bit approximation of cos(x). This approximation never varies more than +/// 0.69% from the floating point value you'd get by doing +/// @code{.cpp} +/// float s = cos(x) * 32767.0; +/// @endcode +/// +/// @param theta input angle from 0-65535 +/// @returns cos of theta, value between -32767 to 32767. +LIB8STATIC int16_t cos16( uint16_t theta) +{ + return sin16( theta + 16384); +} + +/////////////////////////////////////////////////////////////////////// +// sin8() and cos8() +// Fast 8-bit approximations of sin(x) & cos(x). + +/// Pre-calculated lookup table used in sin8() and cos8() functions +const uint8_t b_m16_interleave[] = { 0, 49, 49, 41, 90, 27, 117, 10 }; + +#if defined(__AVR__) && !defined(LIB8_ATTINY) +/// Platform-independent alias of the fast sin implementation +#define sin8 sin8_avr + +/// Fast 8-bit approximation of sin(x). This approximation never varies more than +/// 2% from the floating point value you'd get by doing +/// @code{.cpp} +/// float s = (sin(x) * 128.0) + 128; +/// @endcode +/// +/// @param theta input angle from 0-255 +/// @returns sin of theta, value between 0 and 255 +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) + ); + + offset &= 0x3F; // 0..63 + + uint8_t secoffset = offset & 0x0F; // 0..15 + if( theta & 0x40) ++secoffset; + + uint8_t m16; uint8_t b; + + uint8_t section = offset >> 4; // 0..3 + uint8_t s2 = section * 2; + + const uint8_t* p = b_m16_interleave; + p += s2; + b = *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) + ); + + int8_t y = mx + b; + if( theta & 0x80 ) y = -y; + + y += 128; + + return y; +} + +#else + +/// Platform-independent alias of the fast sin implementation +#define sin8 sin8_C + +/// Fast 8-bit approximation of sin(x). This approximation never varies more than +/// 2% from the floating point value you'd get by doing +/// @code{.cpp} +/// float s = (sin(x) * 128.0) + 128; +/// @endcode +/// +/// @param theta input angle from 0-255 +/// @returns sin of theta, value between 0 and 255 +LIB8STATIC uint8_t sin8_C( uint8_t theta) +{ + uint8_t offset = theta; + if( theta & 0x40 ) { + offset = (uint8_t)255 - offset; + } + offset &= 0x3F; // 0..63 + + uint8_t secoffset = offset & 0x0F; // 0..15 + 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; + uint8_t m16 = *p; + + uint8_t mx = (m16 * secoffset) >> 4; + + int8_t y = mx + b; + if( theta & 0x80 ) y = -y; + + y += 128; + + return y; +} + +#endif + +/// Fast 8-bit approximation of cos(x). This approximation never varies more than +/// 2% from the floating point value you'd get by doing +/// @code{.cpp} +/// float s = (cos(x) * 128.0) + 128; +/// @endcode +/// +/// @param theta input angle from 0-255 +/// @returns cos of theta, value between 0 and 255 +LIB8STATIC uint8_t cos8( uint8_t theta) +{ + return sin8( theta + 64); +} + +/// @} Trig +/// @} lib8tion + +#endif diff --git a/lib/FastLED/src/noise.cpp b/lib/FastLED/src/noise.cpp new file mode 100644 index 00000000..56944f4d --- /dev/null +++ b/lib/FastLED/src/noise.cpp @@ -0,0 +1,857 @@ +/// @file noise.cpp +/// Functions to generate and fill arrays with noise. + +/// Disables pragma messages and warnings +#define FASTLED_INTERNAL +#include "FastLED.h" +#include + +FASTLED_NAMESPACE_BEGIN + +/// Reads a single byte from the p array +#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}; + + +// Start Doxygen define hiding +/// @cond + +#if FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW == 1 +#define AVG15(U,V) (((U)+(V)) >> 1) +#else +// See if we should use the inlined avg15 for AVR with MUL instruction +#if defined(__AVR__) && (LIB8_ATTINY == 0) +#define AVG15(U,V) (avg15_inline_avr_mul((U),(V))) +// inlined copy of avg15 for AVR with MUL instruction; cloned from math8.h +// Forcing this inline in the 3-D 16bit noise produces a 12% speedup overall, +// at a cost of just +8 bytes of net code size. +static int16_t inline __attribute__((always_inline)) avg15_inline_avr_mul( int16_t i, int16_t j) +{ + 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) ); + return i; +} +#else +#define AVG15(U,V) (avg15((U),(V))) +#endif +#endif + +// See fastled_config.h for notes on this; +// "#define FASTLED_NOISE_FIXED 1" is the correct value +#if FASTLED_NOISE_FIXED == 0 +#define EASE8(x) (FADE(x) ) +#define EASE16(x) (FADE(x) ) +#else +#define EASE8(x) (ease8InOutQuad(x) ) +#define EASE16(x) (ease16InOutQuad(x)) +#endif +// +// #define FADE_12 +#define FADE_16 + +#ifdef FADE_12 +#define FADE logfade12 +#define LERP(a,b,u) lerp15by12(a,b,u) +#else +#define FADE(x) scale16(x,x) +#define LERP(a,b,u) lerp15by16(a,b,u) +#endif + +// end Doxygen define hiding +/// @endcond + +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; + } +#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; } + + 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; } + + 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; } + + return AVG15(u,v); +} + +// selectBasedOnHashBit performs this: +// result = (hash & (1<>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; + + 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; +#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 + } + } +#endif + + if(hash&1) { u = -u; } + if(hash&2) { v = -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; + + 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; } + + 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; + + 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; } + + return avg7(u,v); +} + + +#ifdef FADE_12 +uint16_t logfade12(uint16_t val) { + 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)); + int16_t result; + if( b > a) { + uint16_t delta = b - a; + uint16_t scaled = scale16(delta,frac<<4); + result = a + scaled; + } else { + uint16_t delta = a - b; + uint16_t scaled = scale16(delta,frac<<4); + result = a - scaled; + } + return result; +} +#endif + +static int8_t inline __attribute__((always_inline)) lerp7by8( int8_t a, int8_t b, fract8 frac) +{ + // int8_t delta = b - a; + // int16_t prod = (uint16_t)delta * (uint16_t)frac; + // int8_t scaled = prod >> 8; + // int8_t result = a + scaled; + // return result; + int8_t result; + if( b > a) { + uint8_t delta = b - a; + uint8_t scaled = scale8( delta, frac); + result = a + scaled; + } else { + uint8_t delta = a - b; + uint8_t scaled = scale8( delta, frac); + result = a - scaled; + } + return result; +} + +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); + + int16_t ans = LERP(Y1,Y2,w); + + 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); + + // // 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; + + // 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 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); + + 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); + + 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; +} + +int16_t inoise16_raw(uint32_t x) +{ + // 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); + + // 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; + + u = EASE16(u); + + int16_t ans = LERP(grad16(P(AA), xx), grad16(P(BA), xx - N), u); + + return ans; +} + +uint16_t inoise16(uint32_t x) { + 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); + + int8_t Y1 = lerp7by8(X1,X2,v); + int8_t Y2 = lerp7by8(X3,X4,v); + + int8_t ans = lerp7by8(Y1,Y2,w); + + return ans; +} + +uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z) { + //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 + return ans; +} + +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; + + // 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 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); + + int8_t ans = lerp7by8(X1,X2,v); + + return ans; + // return scale8((70+(ans)),234)<<1; +} + + + +uint8_t inoise8(uint16_t x, uint16_t y) { + //return scale8(69+inoise8_raw(x,y),237)<<1; + int8_t n = inoise8_raw( x, y); // -64..+64 + n+= 64; // 0..128 + uint8_t ans = qadd8( n, n); // 0..255 + return ans; +} + +// output range = -64 .. +64 +int8_t inoise8_raw(uint16_t x) +{ + // Find the unit cube containing the point + uint8_t X = x>>8; + + // 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 + uint8_t u = x; + + // Get a signed version of the above for the grad function + int8_t xx = ((uint8_t)(x)>>1) & 0x7F; + uint8_t N = 0x80; + + u = EASE8( u); + + int8_t ans = lerp7by8(grad8(P(AA), xx), grad8(P(BA), xx - N), u); + + return ans; +} + +uint8_t inoise8(uint16_t x) { + int8_t n = inoise8_raw(x); //-64..+64 + n += 64; // 0..128 + uint8_t ans = qadd8(n,n); // 0..255 + return ans; +} + +// struct q44 { +// uint8_t i:4; +// uint8_t f:4; +// q44(uint8_t _i, uint8_t _f) {i=_i; f=_f; } +// }; + +// uint32_t mul44(uint32_t v, q44 mulby44) { +// return (v *mulby44.i) + ((v * mulby44.f) >> 4); +// } +// +// uint16_t mul44_16(uint16_t v, q44 mulby44) { +// return (v *mulby44.i) + ((v * mulby44.f) >> 4); +// } + +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) { + pData[i] = qadd8(pData[i],inoise8(xx,time)>>o); + } + + _xx <<= 1; + scx <<= 1; + } +} + +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) { + uint32_t accum = (inoise16(xx,time))>>o; + accum += (pData[i]<<8); + if(accum > 65535) { accum = 65535; } + pData[i] = accum>>8; + } + + _xx <<= 1; + scx <<= 1; + } +} + +/// Fill a 2D 8-bit buffer with noise, using inoise8() +/// @param pData the array of data to fill with noise values +/// @param width the width of the 2D buffer +/// @param height the height of the 2D buffer +/// @param octaves the number of octaves to use for noise. More octaves = more noise. +/// @param freq44 starting octave frequency +/// @param amplitude noise amplitude +/// @param skip how many noise maps to skip over, incremented recursively per octave +/// @param x x-axis coordinate on noise map (1D) +/// @param scalex the scale (distance) between x points when filling in noise +/// @param y y-axis coordinate on noise map (2D) +/// @param scaley the scale (distance) between y points when filling in noise +/// @param time the time position for the noise field +/// @todo Why isn't this declared in the header (noise.h)? +void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, q44 freq44, fract8 amplitude, int skip, uint16_t x, int scalex, uint16_t y, int scaley, uint16_t time) { + if(octaves > 1) { + fill_raw_2dnoise8(pData, width, height, octaves-1, freq44, amplitude, skip+1, x*freq44, freq44 * scalex, y*freq44, freq44 * scaley, time); + } else { + // amplitude is always 255 on the lowest level + amplitude=255; + } + + scalex *= skip; + scaley *= skip; + + fract8 invamp = 255-amplitude; + uint16_t xx = x; + 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) { + 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 1) { + fill_raw_2dnoise16(pData, width, height, octaves-1, freq88, amplitude, skip, x *freq88 , scalex *freq88, y * freq88, scaley * freq88, time); + } else { + // amplitude is always 255 on the lowest level + amplitude=65535; + } + + scalex *= skip; + scaley *= skip; + fract16 invamp = 65535-amplitude; + for(int i = 0; i < height; i+=skip, y+=scaley) { + uint16_t *pRow = pData + (i*width); + for(int j = 0,xx=x; j < width; j+=skip, xx+=scalex) { + uint16_t noise_base = inoise16(xx,y,time); + noise_base = (0x8000 & noise_base) ? noise_base - (32767) : 32767 - noise_base; + noise_base = scale16(noise_base<<1, amplitude); + if(skip==1) { + pRow[j] = scale16(pRow[j],invamp) + noise_base; + } else { + for(int ii = i; ii<(i+skip) && ii 1) { + fill_raw_2dnoise16into8(pData, width, height, octaves-1, freq44, amplitude, skip+1, x*freq44, scalex *freq44, y*freq44, scaley * freq44, time); + } else { + // amplitude is always 255 on the lowest level + amplitude=255; + } + + scalex *= skip; + scaley *= skip; + uint32_t xx; + fract8 invamp = 255-amplitude; + for(int i = 0; i < height; i+=skip, y+=scaley) { + uint8_t *pRow = pData + (i*width); + xx = x; + for(int j = 0; j < width; j+=skip, xx+=scalex) { + uint16_t noise_base = inoise16(xx,y,time); + noise_base = (0x8000 & noise_base) ? noise_base - (32767) : 32767 - noise_base; + noise_base = scale8(noise_base>>7,amplitude); + if(skip==1) { + pRow[j] = qadd8(scale8(pRow[j],invamp),noise_base); + } else { + for(int ii = i; ii<(i+skip) && ii 255 ? 255 : LedsRemaining; // limit to 255 max + + uint8_t V[LedsPer]; + uint8_t H[LedsPer]; + + memset(V, 0, LedsPer); + memset(H, 0, LedsPer); + + fill_raw_noise8(V, LedsPer, octaves, x, scale, time); + fill_raw_noise8(H, LedsPer, hue_octaves, hue_x, hue_scale, time); + + for (int i = 0; i < LedsPer; ++i) { + leds[i + j] = CHSV(H[i], 255, V[i]); + } + } +} + +void fill_noise16(CRGB *leds, int num_leds, + uint8_t octaves, uint16_t x, int scale, + uint8_t hue_octaves, uint16_t hue_x, int hue_scale, + uint16_t time, uint8_t hue_shift) { + + if (num_leds <= 0) return; + + for (int j = 0; j < num_leds; j += 255) { + const int LedsRemaining = num_leds - j; + const int LedsPer = LedsRemaining > 255 ? 255 : LedsRemaining; // limit to 255 max + + uint8_t V[LedsPer]; + uint8_t H[LedsPer]; + + memset(V, 0, LedsPer); + memset(H, 0, LedsPer); + + fill_raw_noise16into8(V, LedsPer, octaves, x, scale, time); + fill_raw_noise8(H, LedsPer, hue_octaves, hue_x, hue_scale, time); + + for (int i = 0; i < LedsPer; ++i) { + leds[i + j] = CHSV(H[i] + hue_shift, 255, V[i]); + } + } +} + +void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine, + uint8_t octaves, uint16_t x, int xscale, uint16_t y, int yscale, uint16_t time, + uint8_t hue_octaves, uint16_t hue_x, int hue_xscale, uint16_t hue_y, uint16_t hue_yscale,uint16_t hue_time,bool blend) { + uint8_t V[height][width]; + uint8_t H[height][width]; + + memset(V,0,height*width); + memset(H,0,height*width); + + fill_raw_2dnoise8((uint8_t*)V,width,height,octaves,x,xscale,y,yscale,time); + fill_raw_2dnoise8((uint8_t*)H,width,height,hue_octaves,hue_x,hue_xscale,hue_y,hue_yscale,hue_time); + + int w1 = width-1; + int h1 = height-1; + for(int i = 0; i < height; ++i) { + int wb = i*width; + for(int j = 0; j < width; ++j) { + CRGB led(CHSV(H[h1-i][w1-j],255,V[i][j])); + + int pos = j; + if(serpentine && (i & 0x1)) { + pos = w1-j; + } + + if(blend) { + leds[wb+pos] >>= 1; leds[wb+pos] += (led>>=1); + } else { + leds[wb+pos] = led; + } + } + } +} + +void fill_2dnoise16(CRGB *leds, int width, int height, bool serpentine, + uint8_t octaves, uint32_t x, int xscale, uint32_t y, int yscale, uint32_t time, + uint8_t hue_octaves, uint16_t hue_x, int hue_xscale, uint16_t hue_y, uint16_t hue_yscale,uint16_t hue_time, bool blend, uint16_t hue_shift) { + uint8_t V[height][width]; + uint8_t H[height][width]; + + memset(V,0,height*width); + memset(H,0,height*width); + + fill_raw_2dnoise16into8((uint8_t*)V,width,height,octaves,q44(2,0),171,1,x,xscale,y,yscale,time); + // fill_raw_2dnoise16into8((uint8_t*)V,width,height,octaves,x,xscale,y,yscale,time); + // fill_raw_2dnoise8((uint8_t*)V,width,height,hue_octaves,x,xscale,y,yscale,time); + fill_raw_2dnoise8((uint8_t*)H,width,height,hue_octaves,hue_x,hue_xscale,hue_y,hue_yscale,hue_time); + + + int w1 = width-1; + int h1 = height-1; + hue_shift >>= 8; + + for(int i = 0; i < height; ++i) { + int wb = i*width; + for(int j = 0; j < width; ++j) { + CRGB led(CHSV(hue_shift + (H[h1-i][w1-j]),196,V[i][j])); + + int pos = j; + if(serpentine && (i & 0x1)) { + pos = w1-j; + } + + if(blend) { + leds[wb+pos] >>= 1; leds[wb+pos] += (led>>=1); + } else { + leds[wb+pos] = led; + } + } + } +} + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/noise.h b/lib/FastLED/src/noise.h new file mode 100644 index 00000000..8bc0b00b --- /dev/null +++ b/lib/FastLED/src/noise.h @@ -0,0 +1,228 @@ +#ifndef __INC_NOISE_H +#define __INC_NOISE_H + +#include "FastLED.h" + +/// @file noise.h +/// Functions to generate and fill arrays with noise. + +FASTLED_NAMESPACE_BEGIN + +/// @defgroup Noise Noise Functions +/// Functions to generate and fill arrays with noise. +/// These functions use [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) +/// as the noise generation algorithm. +/// @{ + + +/// @defgroup NoiseGeneration Noise Generation Functions +/// Functions to generate noise. +/// @{ + +/// @name 16-Bit Scaled Noise Functions +/// @{ + +/// @copydoc inoise16(uint32_t, uint32_t) +/// @param z z-axis coordinate on noise map (3D) +extern uint16_t inoise16(uint32_t x, uint32_t y, uint32_t z); + +/// @copydoc inoise16(uint32_t) +/// @param y y-axis coordinate on noise map (2D) +extern uint16_t inoise16(uint32_t x, uint32_t y); + +/// 16-bit, fixed point implementation of Perlin's noise. +/// @see inoise16_raw() +/// @returns scaled noise value as an unsigned integer, 0-65535 +/// @param x x-axis coordinate on noise map (1D) +extern uint16_t inoise16(uint32_t x); + +/// @} 16-Bit Scaled Noise Functions + + +/// @name 16-Bit Raw Noise Functions +/// @{ + +/// @copydoc inoise16_raw(uint32_t, uint32_t) +/// @param z z-axis coordinate on noise map (3D) +extern int16_t inoise16_raw(uint32_t x, uint32_t y, uint32_t z); + +/// @copydoc inoise16_raw(uint32_t) +/// @param y y-axis coordinate on noise map (2D) +extern int16_t inoise16_raw(uint32_t x, uint32_t y); + +/// 16-bit, fixed point implementation of Perlin's noise without scaling. +/// Coordinates are 16.16 fixed point values, 32 bit integers with +/// integral coordinates in the high 16-bits and fractional in the low 16-bits. +/// @returns unscaled noise value as a signed integer, roughly -18k to 18k +/// @param x x-axis coordinate on noise map (1D) +extern int16_t inoise16_raw(uint32_t x); + +/// @} 16-Bit Raw Noise Functions + + +/// @name 8-Bit Scaled Noise Functions +/// @{ + +/// @copydoc inoise8(uint16_t, uint16_t) +/// @param z z-axis coordinate on noise map (3D) +extern uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z); + +/// @copydoc inoise8(uint16_t) +/// @param y y-axis coordinate on noise map (2D) +extern uint8_t inoise8(uint16_t x, uint16_t y); + +/// 8-Bit, fixed point implementation of Perlin's noise. +/// @see inoise8_raw() +/// @returns scaled noise value as an unsigned integer, 0-255 +/// @param x x-axis coordinate on noise map (1D) +extern uint8_t inoise8(uint16_t x); + +/// @} 8-Bit Scaled Noise Functions + + +/// @name 8-Bit Raw Noise Functions +/// @{ + +/// @copydoc inoise8_raw(uint16_t, uint16_t) +/// @param z z-axis coordinate on noise map (3D) +extern int8_t inoise8_raw(uint16_t x, uint16_t y, uint16_t z); + +/// @copydoc inoise8_raw(uint16_t) +/// @param y y-axis coordinate on noise map (2D) +extern int8_t inoise8_raw(uint16_t x, uint16_t y); + +/// 8-bit, fixed point implementation of Perlin's noise without scaling. +/// Coordinates are 8.8 fixed point values, 16-bit integers with +/// integral coordinates in the high 8-bits and fractional in the low 8-bits. +/// @returns unscaled noise value as a signed integer, roughly -70 to 70 +/// @param x x-axis coordinate on noise map (1D) +extern int8_t inoise8_raw(uint16_t x); + +/// @} 8-Bit Raw Noise Functions +/// @} NoiseGeneration + + + +/// @defgroup NoiseFill Noise Fill Functions +/// Functions to fill a buffer with noise data. +/// @{ + +/// @name Raw Fill Functions +/// Fill a 1D or 2D array with generated noise. +/// @{ + +/// Fill a 1D 8-bit buffer with noise, using inoise8() +/// @param pData the array of data to fill with noise values +/// @param num_points the number of points of noise to compute +/// @param octaves the number of octaves to use for noise. More octaves = more noise. +/// @param x x-axis coordinate on noise map (1D) +/// @param scalex the scale (distance) between x points when filling in noise +/// @param time the time position for the noise field +void fill_raw_noise8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint16_t x, int scalex, uint16_t time); + +/// Fill a 1D 8-bit buffer with noise, using inoise16() +/// @copydetails fill_raw_noise8() +void fill_raw_noise16into8(uint8_t *pData, uint8_t num_points, uint8_t octaves, uint32_t x, int scalex, uint32_t time); + +/// Fill a 2D 8-bit buffer with noise, using inoise8() +/// @param pData the array of data to fill with noise values +/// @param width the width of the 2D buffer +/// @param height the height of the 2D buffer +/// @param octaves the number of octaves to use for noise. More octaves = more noise. +/// @param x x-axis coordinate on noise map (1D) +/// @param scalex the scale (distance) between x points when filling in noise +/// @param y y-axis coordinate on noise map (2D) +/// @param scaley the scale (distance) between y points when filling in noise +/// @param time the time position for the noise field +void fill_raw_2dnoise8(uint8_t *pData, int width, int height, uint8_t octaves, uint16_t x, int scalex, uint16_t y, int scaley, uint16_t time); + +/// Fill a 2D 8-bit buffer with noise, using inoise16() +/// @copydetails fill_raw_2dnoise8(uint8_t*, int, int, uint8_t, uint16_t, int, uint16_t, int, uint16_t) +void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time); + +/// Fill a 2D 16-bit buffer with noise, using inoise16() +/// @copydetails fill_raw_2dnoise8(uint8_t*, int, int, uint8_t, uint16_t, int, uint16_t, int, uint16_t) +/// @param freq88 starting octave frequency +/// @param amplitude noise amplitude +/// @param skip how many noise maps to skip over, incremented recursively per octave +void fill_raw_2dnoise16(uint16_t *pData, int width, int height, uint8_t octaves, q88 freq88, fract16 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time); + +/// Fill a 2D 8-bit buffer with noise, using inoise16() +/// @copydetails fill_raw_2dnoise8(uint8_t*, int, int, uint8_t, uint16_t, int, uint16_t, int, uint16_t) +/// @param freq44 starting octave frequency +/// @param amplitude noise amplitude +/// @param skip how many noise maps to skip over, incremented recursively per octave +void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, q44 freq44, fract8 amplitude, int skip, uint32_t x, int scalex, uint32_t y, int scaley, uint32_t time); + +/// @} Raw Fill Functions + + +/// @name Fill Functions +/// Fill an LED array with colors based on noise. +/// Colors are calculated using noisemaps, randomly selecting hue and value +/// (brightness) points with full saturation (255). +/// @{ + +/// Fill an LED array with random colors, using 8-bit noise +/// @param leds pointer to LED array +/// @param num_leds the number of LEDs to fill +/// @param octaves the number of octaves to use for value (brightness) noise +/// @param x x-axis coordinate on noise map for value (brightness) noise +/// @param scale the scale (distance) between x points when filling in value (brightness) noise +/// @param hue_octaves the number of octaves to use for color hue noise +/// @param hue_x x-axis coordinate on noise map for color hue noise +/// @param hue_scale the scale (distance) between x points when filling in color hue noise +/// @param time the time position for the noise field +void fill_noise8(CRGB *leds, int num_leds, + uint8_t octaves, uint16_t x, int scale, + uint8_t hue_octaves, uint16_t hue_x, int hue_scale, + uint16_t time); + +/// Fill an LED array with random colors, using 16-bit noise +/// @copydetails fill_noise8() +/// @param hue_shift how much to shift the final hues by for every LED +void fill_noise16(CRGB *leds, int num_leds, + uint8_t octaves, uint16_t x, int scale, + uint8_t hue_octaves, uint16_t hue_x, int hue_scale, + uint16_t time, uint8_t hue_shift=0); + +/// Fill an LED matrix with random colors, using 8-bit noise +/// @param leds pointer to LED array +/// @param width the width of the LED matrix +/// @param height the height of the LED matrix +/// @param serpentine whether the matrix is laid out in a serpentine pattern (alternating left/right directions per row) +/// +/// @param octaves the number of octaves to use for value (brightness) noise +/// @param x x-axis coordinate on noise map for value (brightness) noise +/// @param xscale the scale (distance) between x points when filling in value (brightness) noise +/// @param y y-axis coordinate on noise map for value (brightness) noise +/// @param yscale the scale (distance) between y points when filling in value (brightness) noise +/// @param time the time position for the value (brightness) noise field +/// +/// @param hue_octaves the number of octaves to use for color hue noise +/// @param hue_x x-axis coordinate on noise map for color hue noise +/// @param hue_xscale the scale (distance) between x points when filling in color hue noise +/// @param hue_y y-axis coordinate on noise map for color hue noise. +/// @param hue_yscale the scale (distance) between y points when filling in color hue noise +/// @param hue_time the time position for the color hue noise field +/// @param blend if true, will blend the newly generated LED values into the array. If false, +/// will overwrite the array values directly. +void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine, + uint8_t octaves, uint16_t x, int xscale, uint16_t y, int yscale, uint16_t time, + uint8_t hue_octaves, uint16_t hue_x, int hue_xscale, uint16_t hue_y, uint16_t hue_yscale,uint16_t hue_time,bool blend); + +/// Fill an LED matrix with random colors, using 16-bit noise +/// @copydetails fill_2dnoise8() +/// @param hue_shift how much to shift the final hues by for every LED +void fill_2dnoise16(CRGB *leds, int width, int height, bool serpentine, + uint8_t octaves, uint32_t x, int xscale, uint32_t y, int yscale, uint32_t time, + uint8_t hue_octaves, uint16_t hue_x, int hue_xscale, uint16_t hue_y, uint16_t hue_yscale,uint16_t hue_time, bool blend, uint16_t hue_shift=0); + +/// @} Fill Functions + +/// @} NoiseFill +/// @} Noise + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/pixelset.h b/lib/FastLED/src/pixelset.h new file mode 100644 index 00000000..e4edcbbc --- /dev/null +++ b/lib/FastLED/src/pixelset.h @@ -0,0 +1,417 @@ +#ifndef __INC_PIXELSET_H +#define __INC_PIXELSET_H + +#include "FastLED.h" + +#ifndef abs +#include +#endif + +/// @file pixelset.h +/// Declares classes for managing logical groups of LEDs + + +/// @defgroup PixelSet Pixel Data Sets +/// @brief Classes for managing logical groups of LEDs +/// @{ + +/// Represents a set of 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 the other @ref PixelTypes as well as functions in colorutils.h. +/// @tparam PIXEL_TYPE the type of LED data referenced in the class, e.g. CRGB. +/// @note 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; ///< direction of the LED data, either 1 or -1. Determines how the pointer is incremented. + const int len; ///< length of the LED data, in PIXEL_TYPE units. More accurately, it's the distance from + /// the start of the CPixelView::leds array to the end of the set (CPixelView::end_pos) + PIXEL_TYPE * const leds; ///< pointer to the LED data + PIXEL_TYPE * const end_pos; ///< pointer to the end position of the LED data + +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 pointer 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 pointer 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, in number of LEDs + 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 that 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 different things? Note that 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 The start point 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 + // @todo 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; + } + + /// Print debug data to serial, disabled for release. + /// Edit this file to re-enable these for debugging purposes. + void dump() const { + /// @code + /// 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(""); + /// @endcode + } + + /// 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 all channels for all of 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 all channels for all of the 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. + /// With CRGB, this brings up each channel to the higher of the two values + /// @see CRGB::operator|= + 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. + /// @copydetails operator|=(const PIXEL_TYPE&) + 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. + /// @copydetails operator|=(const PIXEL_TYPE&) + 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. + /// With CRGB, this brings up each channel down to the lower of the two values + /// @see CRGB::operator&= + 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. + /// @copydetails operator&=(const PIXEL_TYPE&) + 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. + /// @copydetails operator&=(const PIXEL_TYPE&) + inline CPixelView & operator&=(uint8_t d) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { (*pixel) &= d; } return *this; } + + /// @} Modification/Scaling Operators + + + /// 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; } + + + /// @name Color Util Functions + /// @{ + + /// Fill all of the LEDs with a solid color + /// @param color the color to fill with + inline CPixelView & fill_solid(const PIXEL_TYPE & color) { *this = color; return *this; } + /// @copydoc CPixelView::fill_solid(const PIXEL_TYPE&) + inline CPixelView & fill_solid(const CHSV & color) { if(dir>0) { *this = color; return *this; } } + + /// Fill all of the LEDs with a rainbow of colors. + /// @param initialhue the starting hue for the rainbow + /// @param deltahue how many hue values to advance for each LED + /// @see ::fill_rainbow(struct CRGB*, int, uint8_t, uint8_t) + 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; + } + + /// Fill all of the LEDs with a smooth HSV gradient between two HSV colors. + /// @param startcolor the starting color in the gradient + /// @param endcolor the end color for the gradient + /// @param directionCode the direction to travel around the color wheel + /// @see ::fill_gradient(T*, uint16_t, const CHSV&, const CHSV&, TGradientDirectionCode) + 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; + } + + /// Fill all of the LEDs with a smooth HSV gradient between three HSV colors. + /// @param c1 the starting color in the gradient + /// @param c2 the middle color for the gradient + /// @param c3 the end color for the gradient + /// @param directionCode the direction to travel around the color wheel + /// @see ::fill_gradient(T*, uint16_t, const CHSV&, const CHSV&, const CHSV&, TGradientDirectionCode) + 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; + } + + /// Fill all of the LEDs with a smooth HSV gradient between four HSV colors. + /// @param c1 the starting color in the gradient + /// @param c2 the first middle color for the gradient + /// @param c3 the second middle color for the gradient + /// @param c4 the end color for the gradient + /// @param directionCode the direction to travel around the color wheel + /// @see ::fill_gradient(T*, uint16_t, const CHSV&, const CHSV&, const CHSV&, const CHSV&, TGradientDirectionCode) + 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; + } + + /// Fill all of the LEDs with a smooth RGB gradient between two RGB colors. + /// @param startcolor the starting color in the gradient + /// @param endcolor the end color for the gradient + /// @param directionCode the direction to travel around the color wheel + /// @see ::fill_gradient_RGB(CRGB*, uint16_t, const CRGB&, const CRGB&) + 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; + } + + /// Fill all of the LEDs with a smooth RGB gradient between three RGB colors. + /// @param c1 the starting color in the gradient + /// @param c2 the middle color for the gradient + /// @param c3 the end color for the gradient + /// @see ::fill_gradient_RGB(CRGB*, uint16_t, const CRGB&, const CRGB&, const CRGB&) + 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; + } + + /// Fill all of the LEDs with a smooth RGB gradient between four RGB colors. + /// @param c1 the starting color in the gradient + /// @param c2 the first middle color for the gradient + /// @param c3 the second middle color for the gradient + /// @param c4 the end color for the gradient + /// @see ::fill_gradient_RGB(CRGB*, uint16_t, const CRGB&, const CRGB&, const CRGB&, const CRGB&) + 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; + } + + /// Destructively modifies all LEDs, blending in a given fraction of an overlay color + /// @param overlay the color to blend in + /// @param amountOfOverlay the fraction of overlay to blend in + /// @see ::nblend(CRGB&, const CRGB&, fract8) + inline CPixelView & nblend(const PIXEL_TYPE & overlay, fract8 amountOfOverlay) { for(iterator pixel = begin(), _end = end(); pixel != _end; ++pixel) { ::nblend((*pixel), overlay, amountOfOverlay); } return *this; } + + /// Destructively blend another set of LEDs into this one + /// @param rhs the set of LEDs to blend into this set + /// @param amountOfOverlay the fraction of each color in the other set to blend in + /// @see ::nblend(CRGB&, const CRGB&, fract8) + 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; } + + /// One-dimensional blur filter + /// @param blur_amount the amount of blur to apply + /// @note Only bringing in a 1d blur, not sure 2d blur makes sense when looking at sub arrays + /// @see ::blur1d(CRGB*, uint16_t, fract8) + inline CPixelView & blur1d(fract8 blur_amount) { + if(dir >= 0) { + ::blur1d(leds, len, blur_amount); + } else { + ::blur1d(leds + len + 1, -len, blur_amount); + } + return *this; + } + + /// Destructively applies a gamma adjustment to all LEDs + /// @param gamma the gamma value to apply + /// @see ::napplyGamma_video(CRGB&, float) + inline CPixelView & napplyGamma_video(float gamma) { + if(dir >= 0) { + ::napplyGamma_video(leds, len, gamma); + } else { + ::napplyGamma_video(leds + len + 1, -len, gamma); + } + return *this; + } + + /// @copybrief CPixelView::napplyGamma_video(float) + /// @param gammaR the gamma value to apply to the CRGB::red channel + /// @param gammaG the gamma value to apply to the CRGB::green channel + /// @param gammaB the gamma value to apply to the CRGB::blue channel + /// @see ::napplyGamma_video(CRGB&, float, float, float) + 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; + } + + /// @} Color Util Functions + + + /// @name Iterator + /// @{ + + /// Iterator helper class for CPixelView + /// @tparam the type of the LED array data + /// @todo Make this a fully specified/proper iterator + template + class pixelset_iterator_base { + T * leds; ///< pointer to LED array + const int8_t dir; ///< direction of LED array, for incrementing the pointer + + public: + /// Copy constructor + __attribute__((always_inline)) inline pixelset_iterator_base(const pixelset_iterator_base & rhs) : leds(rhs.leds), dir(rhs.dir) {} + + /// Base constructor + /// @tparam the type of the LED array data + /// @param _leds pointer to LED array + /// @param _dir direction of LED array + __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; } ///< Increment LED pointer in data direction + __attribute__((always_inline)) inline pixelset_iterator_base operator++(int) { pixelset_iterator_base tmp(*this); leds += dir; return tmp; } ///< @copydoc operator++() + + __attribute__((always_inline)) inline bool operator==(pixelset_iterator_base & other) const { return leds == other.leds; /* && set==other.set; */ } ///< Check if iterator is at the same position + __attribute__((always_inline)) inline bool operator!=(pixelset_iterator_base & other) const { return leds != other.leds; /* || set != other.set; */ } ///< Check if iterator is not at the same position + + __attribute__((always_inline)) inline PIXEL_TYPE& operator*() const { return *leds; } ///< Dereference operator, to get underlying pointer to the LEDs + }; + + typedef pixelset_iterator_base iterator; ///< Iterator helper type for this class + typedef pixelset_iterator_base const_iterator; ///< Const iterator helper type for this class + + iterator begin() { return iterator(leds, dir); } ///< Makes an iterator instance for the start of the LED set + iterator end() { return iterator(end_pos, dir); } ///< Makes an iterator instance for the end of the LED set + + iterator begin() const { return iterator(leds, dir); } ///< Makes an iterator instance for the start of the LED set, const qualified + iterator end() const { return iterator(end_pos, dir); } ///< Makes an iterator instance for the end of the LED set, const qualified + + const_iterator cbegin() const { return const_iterator(leds, dir); } ///< Makes a const iterator instance for the start of the LED set, const qualified + const_iterator cend() const { return const_iterator(end_pos, dir); } ///< Makes a const iterator instance for the end of the LED set, const qualified + + /// @} Iterator +}; + +/// CPixelView for CRGB arrays +typedef CPixelView CRGBSet; + +/// Retrieve a pointer to a CRGB array, using a CRGBSet and an LED offset +__attribute__((always_inline)) +inline CRGB *operator+(const CRGBSet & pixels, int offset) { return (CRGB*)pixels + offset; } + + +/// A version of CPixelView with an included array of CRGB LEDs +/// @tparam SIZE the number of LEDs to include in the array +template +class CRGBArray : public CPixelView { + CRGB rawleds[SIZE]; ///< the LED data + +public: + CRGBArray() : CPixelView(rawleds, SIZE) {} + using CPixelView::operator=; +}; + +/// @} PixelSet + + +#endif diff --git a/lib/FastLED/src/pixeltypes.h b/lib/FastLED/src/pixeltypes.h new file mode 100644 index 00000000..083cbf38 --- /dev/null +++ b/lib/FastLED/src/pixeltypes.h @@ -0,0 +1,968 @@ +#ifndef __INC_PIXELS_H +#define __INC_PIXELS_H + +#include "FastLED.h" + +#include +#include "lib8tion.h" +#include "color.h" + +/// @file pixeltypes.h +/// Definitions for pixel color data structs + +FASTLED_NAMESPACE_BEGIN + +struct CRGB; +struct CHSV; + +/// @defgroup PixelTypes Pixel Data Types (CRGB/CHSV) +/// @brief Structs that hold pixel color data +/// @{ + +/// Forward declaration of hsv2rgb_rainbow here, +/// to avoid circular dependencies. +extern void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb); + +/// Representation of an HSV pixel (hue, saturation, value (aka brightness)). +struct CHSV { + union { + struct { + union { + /// Color hue. + /// This is an 8-bit value representing an angle around + /// the color wheel. Where 0 is 0°, and 255 is 358°. + uint8_t hue; + uint8_t h; ///< @copydoc hue + }; + union { + /// Color saturation. + /// This is an 8-bit value representing a percentage. + uint8_t saturation; + uint8_t sat; ///< @copydoc saturation + uint8_t s; ///< @copydoc saturation + }; + union { + /// Color value (brightness). + /// This is an 8-bit value representing a percentage. + uint8_t value; + uint8_t val; ///< @copydoc value + uint8_t v; ///< @copydoc value + }; + }; + /// Access the hue, saturation, and value data as an array. + /// Where: + /// * `raw[0]` is the hue + /// * `raw[1]` is the saturation + /// * `raw[2]` is the value + uint8_t raw[3]; + }; + + /// Array access operator to index into the CHSV object + /// @param x the index to retrieve (0-2) + /// @returns the CHSV::raw value for the given index + inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) + { + return raw[x]; + } + + /// @copydoc operator[] + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return raw[x]; + } + + /// Default constructor + /// @warning Default values are UNITIALIZED! + inline CHSV() __attribute__((always_inline)) = default; + + /// Allow construction from hue, saturation, and value + /// @param ih input hue + /// @param is input saturation + /// @param iv input value + inline CHSV( uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) + : h(ih), s(is), v(iv) + { + } + + /// Allow copy construction + inline CHSV(const CHSV& rhs) __attribute__((always_inline)) = default; + + /// Allow copy construction + inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) = default; + + /// Assign new HSV values + /// @param ih input hue + /// @param is input saturation + /// @param iv input value + /// @returns reference to the CHSV object + inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) + { + h = ih; + s = is; + v = iv; + return *this; + } +}; + +/// Pre-defined hue values for CHSV objects +typedef enum { + HUE_RED = 0, ///< Red (0°) + HUE_ORANGE = 32, ///< Orange (45°) + HUE_YELLOW = 64, ///< Yellow (90°) + HUE_GREEN = 96, ///< Green (135°) + HUE_AQUA = 128, ///< Aqua (180°) + HUE_BLUE = 160, ///< Blue (225°) + HUE_PURPLE = 192, ///< Purple (270°) + HUE_PINK = 224 ///< Pink (315°) +} HSVHue; + +/// Representation of an RGB pixel (Red, Green, Blue) +struct CRGB { + union { + struct { + union { + uint8_t r; ///< Red channel value + uint8_t red; ///< @copydoc r + }; + union { + uint8_t g; ///< Green channel value + uint8_t green; ///< @copydoc g + }; + union { + uint8_t b; ///< Blue channel value + uint8_t blue; ///< @copydoc b + }; + }; + /// Access the red, green, and blue data as an array. + /// Where: + /// * `raw[0]` is the red value + /// * `raw[1]` is the green value + /// * `raw[2]` is the blue value + uint8_t raw[3]; + }; + + /// Array access operator to index into the CRGB object + /// @param x the index to retrieve (0-2) + /// @returns the CRGB::raw value for the given index + inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) + { + return raw[x]; + } + + /// Array access operator to index into the CRGB object + /// @param x the index to retrieve (0-2) + /// @returns the CRGB::raw value for the given index + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return raw[x]; + } + + /// Default constructor + /// @warning Default values are UNITIALIZED! + inline CRGB() __attribute__((always_inline)) = default; + + /// Allow construction from red, green, and blue + /// @param ir input red value + /// @param ig input green value + /// @param ib input blue value + inline CRGB( uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline)) + : r(ir), g(ig), b(ib) + { + } + + /// Allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code + /// @param colorcode a packed 24 bit color code + inline CRGB( uint32_t colorcode) __attribute__((always_inline)) + : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) + { + } + + /// Allow construction from a LEDColorCorrection enum + /// @param colorcode an LEDColorCorrect enumeration value + inline CRGB( LEDColorCorrection colorcode) __attribute__((always_inline)) + : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) + { + + } + + /// Allow construction from a ColorTemperature enum + /// @param colorcode an ColorTemperature enumeration value + inline CRGB( ColorTemperature colorcode) __attribute__((always_inline)) + : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) + { + + } + + /// Allow copy construction + inline CRGB(const CRGB& rhs) __attribute__((always_inline)) = default; + + /// Allow construction from a CHSV color + inline CRGB(const CHSV& rhs) __attribute__((always_inline)) + { + hsv2rgb_rainbow( rhs, *this); + } + + /// Allow assignment from one RGB struct to another + inline CRGB& operator= (const CRGB& rhs) __attribute__((always_inline)) = default; + + /// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code + /// @param colorcode a packed 24 bit color code + inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline)) + { + r = (colorcode >> 16) & 0xFF; + g = (colorcode >> 8) & 0xFF; + b = (colorcode >> 0) & 0xFF; + return *this; + } + + /// Allow assignment from red, green, and blue + /// @param nr new red value + /// @param ng new green value + /// @param nb new blue value + inline CRGB& setRGB (uint8_t nr, uint8_t ng, uint8_t nb) __attribute__((always_inline)) + { + r = nr; + g = ng; + b = nb; + return *this; + } + + /// Allow assignment from hue, saturation, and value + /// @param hue color hue + /// @param sat color saturation + /// @param val color value (brightness) + inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline)) + { + hsv2rgb_rainbow( CHSV(hue, sat, val), *this); + return *this; + } + + /// Allow assignment from just a hue. + /// Saturation and value (brightness) are set automatically to max. + /// @param hue color hue + inline CRGB& setHue (uint8_t hue) __attribute__((always_inline)) + { + hsv2rgb_rainbow( CHSV(hue, 255, 255), *this); + return *this; + } + + /// Allow assignment from HSV color + inline CRGB& operator= (const CHSV& rhs) __attribute__((always_inline)) + { + hsv2rgb_rainbow( rhs, *this); + return *this; + } + + /// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code + /// @param colorcode a packed 24 bit color code + inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) + { + r = (colorcode >> 16) & 0xFF; + g = (colorcode >> 8) & 0xFF; + b = (colorcode >> 0) & 0xFF; + return *this; + } + + + /// Add one CRGB to another, saturating at 0xFF for each channel + inline CRGB& operator+= (const CRGB& rhs ) + { + r = qadd8( r, rhs.r); + g = qadd8( g, rhs.g); + b = qadd8( b, rhs.b); + return *this; + } + + /// Add a constant to each channel, saturating at 0xFF. + /// @note This is NOT an operator+= overload because the compiler + /// can't usefully decide when it's being passed a 32-bit + /// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue) + inline CRGB& addToRGB (uint8_t d ) + { + r = qadd8( r, d); + g = qadd8( g, d); + b = qadd8( b, d); + return *this; + } + + /// Subtract one CRGB from another, saturating at 0x00 for each channel + inline CRGB& operator-= (const CRGB& rhs ) + { + r = qsub8( r, rhs.r); + g = qsub8( g, rhs.g); + b = qsub8( b, rhs.b); + return *this; + } + + /// Subtract a constant from each channel, saturating at 0x00. + /// @note This is NOT an operator+= overload because the compiler + /// can't usefully decide when it's being passed a 32-bit + /// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue) + inline CRGB& subtractFromRGB(uint8_t d ) + { + r = qsub8( r, d); + g = qsub8( g, d); + b = qsub8( b, d); + return *this; + } + + /// Subtract a constant of '1' from each channel, saturating at 0x00 + inline CRGB& operator-- () __attribute__((always_inline)) + { + subtractFromRGB(1); + return *this; + } + + /// @copydoc operator-- + inline CRGB operator-- (int ) __attribute__((always_inline)) + { + CRGB retval(*this); + --(*this); + return retval; + } + + /// Add a constant of '1' from each channel, saturating at 0xFF + inline CRGB& operator++ () __attribute__((always_inline)) + { + addToRGB(1); + return *this; + } + + /// @copydoc operator++ + inline CRGB operator++ (int ) __attribute__((always_inline)) + { + CRGB retval(*this); + ++(*this); + return retval; + } + + /// Divide each of the channels by a constant + inline CRGB& operator/= (uint8_t d ) + { + r /= d; + g /= d; + b /= d; + return *this; + } + + /// Right shift each of the channels by a constant + inline CRGB& operator>>= (uint8_t d) + { + r >>= d; + g >>= d; + b >>= d; + return *this; + } + + /// Multiply each of the channels by a constant, + /// saturating each channel at 0xFF. + inline CRGB& operator*= (uint8_t d ) + { + r = qmul8( r, d); + g = qmul8( g, d); + b = qmul8( b, d); + return *this; + } + + /// Scale down a RGB to N/256ths of it's current brightness using + /// "video" dimming rules. "Video" dimming rules means that unless the scale factor + /// is ZERO each channel is guaranteed NOT to dim down to zero. If it's already + /// nonzero, it'll stay nonzero, even if that means the hue shifts a little + /// at low brightness levels. + /// @see nscale8x3_video + inline CRGB& nscale8_video (uint8_t scaledown ) + { + nscale8x3_video( r, g, b, scaledown); + return *this; + } + + /// %= is a synonym for nscale8_video(). Think of it is scaling down + /// by "a percentage" + inline CRGB& operator%= (uint8_t scaledown ) + { + nscale8x3_video( r, g, b, scaledown); + return *this; + } + + /// fadeLightBy is a synonym for nscale8_video(), as a fade instead of a scale + /// @param fadefactor the amount to fade, sent to nscale8_video() as (255 - fadefactor) + inline CRGB& fadeLightBy (uint8_t fadefactor ) + { + nscale8x3_video( r, g, b, 255 - fadefactor); + return *this; + } + + /// Scale down a RGB to N/256ths of its current brightness, using + /// "plain math" dimming rules. "Plain math" dimming rules means that the low light + /// levels may dim all the way to 100% black. + /// @see nscale8x3 + inline CRGB& nscale8 (uint8_t scaledown ) + { + nscale8x3( r, g, b, scaledown); + return *this; + } + + /// Scale down a RGB to N/256ths of its current brightness, using + /// "plain math" dimming rules. "Plain math" dimming rules means that the low light + /// levels may dim all the way to 100% black. + /// @see ::scale8 + inline CRGB& nscale8 (const CRGB & scaledown ) + { + r = ::scale8(r, scaledown.r); + g = ::scale8(g, scaledown.g); + b = ::scale8(b, scaledown.b); + return *this; + } + + /// Return a CRGB object that is a scaled down version of this object + inline CRGB scale8 (uint8_t scaledown ) const + { + CRGB out = *this; + nscale8x3( out.r, out.g, out.b, scaledown); + return out; + } + + /// Return a CRGB object that is a scaled down version of this object + inline CRGB scale8 (const CRGB & scaledown ) const + { + CRGB out; + out.r = ::scale8(r, scaledown.r); + out.g = ::scale8(g, scaledown.g); + out.b = ::scale8(b, scaledown.b); + return out; + } + + /// fadeToBlackBy is a synonym for nscale8(), as a fade instead of a scale + /// @param fadefactor the amount to fade, sent to nscale8() as (255 - fadefactor) + inline CRGB& fadeToBlackBy (uint8_t fadefactor ) + { + nscale8x3( r, g, b, 255 - fadefactor); + return *this; + } + + /// "or" operator brings each channel up to the higher of the two values + inline CRGB& operator|= (const CRGB& rhs ) + { + if( rhs.r > r) r = rhs.r; + if( rhs.g > g) g = rhs.g; + if( rhs.b > b) b = rhs.b; + return *this; + } + + /// @copydoc operator|= + inline CRGB& operator|= (uint8_t d ) + { + if( d > r) r = d; + if( d > g) g = d; + if( d > b) b = d; + return *this; + } + + /// "and" operator brings each channel down to the lower of the two values + inline CRGB& operator&= (const CRGB& rhs ) + { + if( rhs.r < r) r = rhs.r; + if( rhs.g < g) g = rhs.g; + if( rhs.b < b) b = rhs.b; + return *this; + } + + /// @copydoc operator&= + inline CRGB& operator&= (uint8_t d ) + { + if( d < r) r = d; + if( d < g) g = d; + if( d < b) b = d; + return *this; + } + + /// This allows testing a CRGB for zero-ness + inline explicit operator bool() const __attribute__((always_inline)) + { + return r || g || b; + } + + /// Converts a CRGB to a 32-bit color having an alpha of 255. + inline explicit operator uint32_t() const + { + return uint32_t{0xff000000} | + (uint32_t{r} << 16) | + (uint32_t{g} << 8) | + uint32_t{b}; + } + + /// Invert each channel + inline CRGB operator- () const + { + CRGB retval; + retval.r = 255 - r; + retval.g = 255 - g; + retval.b = 255 - b; + return retval; + } + +#if (defined SmartMatrix_h || defined SmartMatrix3_h) + /// Convert to an rgb24 object, used with the SmartMatrix library + /// @see https://github.com/pixelmatix/SmartMatrix + operator rgb24() const { + rgb24 ret; + ret.red = r; + ret.green = g; + ret.blue = b; + return ret; + } +#endif + + /// Get the "luma" of a CRGB object. In other words, roughly how much + /// light the CRGB pixel is putting out (from 0 to 255). + inline uint8_t getLuma ( ) const { + //Y' = 0.2126 R' + 0.7152 G' + 0.0722 B' + // 54 183 18 (!) + + uint8_t luma = scale8_LEAVING_R1_DIRTY( r, 54) + \ + scale8_LEAVING_R1_DIRTY( g, 183) + \ + scale8_LEAVING_R1_DIRTY( b, 18); + cleanup_R1(); + return luma; + } + + /// Get the average of the R, G, and B values + inline uint8_t getAverageLight( ) const { +#if FASTLED_SCALE8_FIXED == 1 + const uint8_t eightyfive = 85; +#else + const uint8_t eightyfive = 86; +#endif + uint8_t avg = scale8_LEAVING_R1_DIRTY( r, eightyfive) + \ + scale8_LEAVING_R1_DIRTY( g, eightyfive) + \ + scale8_LEAVING_R1_DIRTY( b, eightyfive); + cleanup_R1(); + return avg; + } + + /// Maximize the brightness of this CRGB object. + /// This makes the individual color channels as bright as possible + /// while keeping the same value differences between channels. + /// @note This does not keep the same ratios between channels, + /// just the same difference in absolute values. + inline void maximizeBrightness( uint8_t limit = 255 ) { + 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; + red = (red * factor) / 256; + green = (green * factor) / 256; + blue = (blue * factor) / 256; + } + } + + /// 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; + + ret.r = lerp8by8(r,other.r,frac); + ret.g = lerp8by8(g,other.g,frac); + ret.b = lerp8by8(b,other.b,frac); + + return ret; + } + + /// @copydoc lerp8 + inline CRGB lerp16( const CRGB& other, fract16 frac) const + { + 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; + + return ret; + } + + /// Returns 0 or 1, depending on the lowest bit of the sum of the color components. + inline uint8_t getParity() + { + uint8_t sum = r + g + b; + return (sum & 0x01); + } + + /// Adjusts the color in the smallest way possible + /// so that the parity of the coloris now the desired value. + /// This allows you to "hide" one bit of information in the color. + /// + /// Ideally, we find one color channel which already + /// has data in it, and modify just that channel by one. + /// We don't want to light up a channel that's black + /// if we can avoid it, and if the pixel is 'grayscale', + /// (meaning that R==G==B), we modify all three channels + /// at once, to preserve the neutral hue. + /// + /// There's no such thing as a free lunch; in many cases + /// this "hidden bit" may actually be visible, but this + /// code makes reasonable efforts to hide it as much + /// as is reasonably possible. + /// + /// Also, an effort is made to make it such that + /// repeatedly setting the parity to different values + /// will not cause the color to "drift". Toggling + /// the parity twice should generally result in the + /// original color again. + /// + inline void setParity( uint8_t parity) + { + uint8_t curparity = getParity(); + + if( parity == curparity) return; + + if( parity ) { + // going 'up' + if( (b > 0) && (b < 255)) { + if( r == g && g == b) { + ++r; + ++g; + } + ++b; + } else if( (r > 0) && (r < 255)) { + ++r; + } else if( (g > 0) && (g < 255)) { + ++g; + } else { + if( r == g && g == b) { + r ^= 0x01; + g ^= 0x01; + } + b ^= 0x01; + } + } else { + // going 'down' + if( b > 1) { + if( r == g && g == b) { + --r; + --g; + } + --b; + } else if( g > 1) { + --g; + } else if( r > 1) { + --r; + } else { + if( r == g && g == b) { + r ^= 0x01; + g ^= 0x01; + } + b ^= 0x01; + } + } + } + + /// Predefined RGB colors + typedef enum { + AliceBlue=0xF0F8FF, ///< @htmlcolorblock{F0F8FF} + Amethyst=0x9966CC, ///< @htmlcolorblock{9966CC} + AntiqueWhite=0xFAEBD7, ///< @htmlcolorblock{FAEBD7} + Aqua=0x00FFFF, ///< @htmlcolorblock{00FFFF} + Aquamarine=0x7FFFD4, ///< @htmlcolorblock{7FFFD4} + Azure=0xF0FFFF, ///< @htmlcolorblock{F0FFFF} + Beige=0xF5F5DC, ///< @htmlcolorblock{F5F5DC} + Bisque=0xFFE4C4, ///< @htmlcolorblock{FFE4C4} + Black=0x000000, ///< @htmlcolorblock{000000} + BlanchedAlmond=0xFFEBCD, ///< @htmlcolorblock{FFEBCD} + Blue=0x0000FF, ///< @htmlcolorblock{0000FF} + BlueViolet=0x8A2BE2, ///< @htmlcolorblock{8A2BE2} + Brown=0xA52A2A, ///< @htmlcolorblock{A52A2A} + BurlyWood=0xDEB887, ///< @htmlcolorblock{DEB887} + CadetBlue=0x5F9EA0, ///< @htmlcolorblock{5F9EA0} + Chartreuse=0x7FFF00, ///< @htmlcolorblock{7FFF00} + Chocolate=0xD2691E, ///< @htmlcolorblock{D2691E} + Coral=0xFF7F50, ///< @htmlcolorblock{FF7F50} + CornflowerBlue=0x6495ED, ///< @htmlcolorblock{6495ED} + Cornsilk=0xFFF8DC, ///< @htmlcolorblock{FFF8DC} + Crimson=0xDC143C, ///< @htmlcolorblock{DC143C} + Cyan=0x00FFFF, ///< @htmlcolorblock{00FFFF} + DarkBlue=0x00008B, ///< @htmlcolorblock{00008B} + DarkCyan=0x008B8B, ///< @htmlcolorblock{008B8B} + DarkGoldenrod=0xB8860B, ///< @htmlcolorblock{B8860B} + DarkGray=0xA9A9A9, ///< @htmlcolorblock{A9A9A9} + DarkGrey=0xA9A9A9, ///< @htmlcolorblock{A9A9A9} + DarkGreen=0x006400, ///< @htmlcolorblock{006400} + DarkKhaki=0xBDB76B, ///< @htmlcolorblock{BDB76B} + DarkMagenta=0x8B008B, ///< @htmlcolorblock{8B008B} + DarkOliveGreen=0x556B2F, ///< @htmlcolorblock{556B2F} + DarkOrange=0xFF8C00, ///< @htmlcolorblock{FF8C00} + DarkOrchid=0x9932CC, ///< @htmlcolorblock{9932CC} + DarkRed=0x8B0000, ///< @htmlcolorblock{8B0000} + DarkSalmon=0xE9967A, ///< @htmlcolorblock{E9967A} + DarkSeaGreen=0x8FBC8F, ///< @htmlcolorblock{8FBC8F} + DarkSlateBlue=0x483D8B, ///< @htmlcolorblock{483D8B} + DarkSlateGray=0x2F4F4F, ///< @htmlcolorblock{2F4F4F} + DarkSlateGrey=0x2F4F4F, ///< @htmlcolorblock{2F4F4F} + DarkTurquoise=0x00CED1, ///< @htmlcolorblock{00CED1} + DarkViolet=0x9400D3, ///< @htmlcolorblock{9400D3} + DeepPink=0xFF1493, ///< @htmlcolorblock{FF1493} + DeepSkyBlue=0x00BFFF, ///< @htmlcolorblock{00BFFF} + DimGray=0x696969, ///< @htmlcolorblock{696969} + DimGrey=0x696969, ///< @htmlcolorblock{696969} + DodgerBlue=0x1E90FF, ///< @htmlcolorblock{1E90FF} + FireBrick=0xB22222, ///< @htmlcolorblock{B22222} + FloralWhite=0xFFFAF0, ///< @htmlcolorblock{FFFAF0} + ForestGreen=0x228B22, ///< @htmlcolorblock{228B22} + Fuchsia=0xFF00FF, ///< @htmlcolorblock{FF00FF} + Gainsboro=0xDCDCDC, ///< @htmlcolorblock{DCDCDC} + GhostWhite=0xF8F8FF, ///< @htmlcolorblock{F8F8FF} + Gold=0xFFD700, ///< @htmlcolorblock{FFD700} + Goldenrod=0xDAA520, ///< @htmlcolorblock{DAA520} + Gray=0x808080, ///< @htmlcolorblock{808080} + Grey=0x808080, ///< @htmlcolorblock{808080} + Green=0x008000, ///< @htmlcolorblock{008000} + GreenYellow=0xADFF2F, ///< @htmlcolorblock{ADFF2F} + Honeydew=0xF0FFF0, ///< @htmlcolorblock{F0FFF0} + HotPink=0xFF69B4, ///< @htmlcolorblock{FF69B4} + IndianRed=0xCD5C5C, ///< @htmlcolorblock{CD5C5C} + Indigo=0x4B0082, ///< @htmlcolorblock{4B0082} + Ivory=0xFFFFF0, ///< @htmlcolorblock{FFFFF0} + Khaki=0xF0E68C, ///< @htmlcolorblock{F0E68C} + Lavender=0xE6E6FA, ///< @htmlcolorblock{E6E6FA} + LavenderBlush=0xFFF0F5, ///< @htmlcolorblock{FFF0F5} + LawnGreen=0x7CFC00, ///< @htmlcolorblock{7CFC00} + LemonChiffon=0xFFFACD, ///< @htmlcolorblock{FFFACD} + LightBlue=0xADD8E6, ///< @htmlcolorblock{ADD8E6} + LightCoral=0xF08080, ///< @htmlcolorblock{F08080} + LightCyan=0xE0FFFF, ///< @htmlcolorblock{E0FFFF} + LightGoldenrodYellow=0xFAFAD2, ///< @htmlcolorblock{FAFAD2} + LightGreen=0x90EE90, ///< @htmlcolorblock{90EE90} + LightGrey=0xD3D3D3, ///< @htmlcolorblock{D3D3D3} + LightPink=0xFFB6C1, ///< @htmlcolorblock{FFB6C1} + LightSalmon=0xFFA07A, ///< @htmlcolorblock{FFA07A} + LightSeaGreen=0x20B2AA, ///< @htmlcolorblock{20B2AA} + LightSkyBlue=0x87CEFA, ///< @htmlcolorblock{87CEFA} + LightSlateGray=0x778899, ///< @htmlcolorblock{778899} + LightSlateGrey=0x778899, ///< @htmlcolorblock{778899} + LightSteelBlue=0xB0C4DE, ///< @htmlcolorblock{B0C4DE} + LightYellow=0xFFFFE0, ///< @htmlcolorblock{FFFFE0} + Lime=0x00FF00, ///< @htmlcolorblock{00FF00} + LimeGreen=0x32CD32, ///< @htmlcolorblock{32CD32} + Linen=0xFAF0E6, ///< @htmlcolorblock{FAF0E6} + Magenta=0xFF00FF, ///< @htmlcolorblock{FF00FF} + Maroon=0x800000, ///< @htmlcolorblock{800000} + MediumAquamarine=0x66CDAA, ///< @htmlcolorblock{66CDAA} + MediumBlue=0x0000CD, ///< @htmlcolorblock{0000CD} + MediumOrchid=0xBA55D3, ///< @htmlcolorblock{BA55D3} + MediumPurple=0x9370DB, ///< @htmlcolorblock{9370DB} + MediumSeaGreen=0x3CB371, ///< @htmlcolorblock{3CB371} + MediumSlateBlue=0x7B68EE, ///< @htmlcolorblock{7B68EE} + MediumSpringGreen=0x00FA9A, ///< @htmlcolorblock{00FA9A} + MediumTurquoise=0x48D1CC, ///< @htmlcolorblock{48D1CC} + MediumVioletRed=0xC71585, ///< @htmlcolorblock{C71585} + MidnightBlue=0x191970, ///< @htmlcolorblock{191970} + MintCream=0xF5FFFA, ///< @htmlcolorblock{F5FFFA} + MistyRose=0xFFE4E1, ///< @htmlcolorblock{FFE4E1} + Moccasin=0xFFE4B5, ///< @htmlcolorblock{FFE4B5} + NavajoWhite=0xFFDEAD, ///< @htmlcolorblock{FFDEAD} + Navy=0x000080, ///< @htmlcolorblock{000080} + OldLace=0xFDF5E6, ///< @htmlcolorblock{FDF5E6} + Olive=0x808000, ///< @htmlcolorblock{808000} + OliveDrab=0x6B8E23, ///< @htmlcolorblock{6B8E23} + Orange=0xFFA500, ///< @htmlcolorblock{FFA500} + OrangeRed=0xFF4500, ///< @htmlcolorblock{FF4500} + Orchid=0xDA70D6, ///< @htmlcolorblock{DA70D6} + PaleGoldenrod=0xEEE8AA, ///< @htmlcolorblock{EEE8AA} + PaleGreen=0x98FB98, ///< @htmlcolorblock{98FB98} + PaleTurquoise=0xAFEEEE, ///< @htmlcolorblock{AFEEEE} + PaleVioletRed=0xDB7093, ///< @htmlcolorblock{DB7093} + PapayaWhip=0xFFEFD5, ///< @htmlcolorblock{FFEFD5} + PeachPuff=0xFFDAB9, ///< @htmlcolorblock{FFDAB9} + Peru=0xCD853F, ///< @htmlcolorblock{CD853F} + Pink=0xFFC0CB, ///< @htmlcolorblock{FFC0CB} + Plaid=0xCC5533, ///< @htmlcolorblock{CC5533} + Plum=0xDDA0DD, ///< @htmlcolorblock{DDA0DD} + PowderBlue=0xB0E0E6, ///< @htmlcolorblock{B0E0E6} + Purple=0x800080, ///< @htmlcolorblock{800080} + Red=0xFF0000, ///< @htmlcolorblock{FF0000} + RosyBrown=0xBC8F8F, ///< @htmlcolorblock{BC8F8F} + RoyalBlue=0x4169E1, ///< @htmlcolorblock{4169E1} + SaddleBrown=0x8B4513, ///< @htmlcolorblock{8B4513} + Salmon=0xFA8072, ///< @htmlcolorblock{FA8072} + SandyBrown=0xF4A460, ///< @htmlcolorblock{F4A460} + SeaGreen=0x2E8B57, ///< @htmlcolorblock{2E8B57} + Seashell=0xFFF5EE, ///< @htmlcolorblock{FFF5EE} + Sienna=0xA0522D, ///< @htmlcolorblock{A0522D} + Silver=0xC0C0C0, ///< @htmlcolorblock{C0C0C0} + SkyBlue=0x87CEEB, ///< @htmlcolorblock{87CEEB} + SlateBlue=0x6A5ACD, ///< @htmlcolorblock{6A5ACD} + SlateGray=0x708090, ///< @htmlcolorblock{708090} + SlateGrey=0x708090, ///< @htmlcolorblock{708090} + Snow=0xFFFAFA, ///< @htmlcolorblock{FFFAFA} + SpringGreen=0x00FF7F, ///< @htmlcolorblock{00FF7F} + SteelBlue=0x4682B4, ///< @htmlcolorblock{4682B4} + Tan=0xD2B48C, ///< @htmlcolorblock{D2B48C} + Teal=0x008080, ///< @htmlcolorblock{008080} + Thistle=0xD8BFD8, ///< @htmlcolorblock{D8BFD8} + Tomato=0xFF6347, ///< @htmlcolorblock{FF6347} + Turquoise=0x40E0D0, ///< @htmlcolorblock{40E0D0} + Violet=0xEE82EE, ///< @htmlcolorblock{EE82EE} + Wheat=0xF5DEB3, ///< @htmlcolorblock{F5DEB3} + White=0xFFFFFF, ///< @htmlcolorblock{FFFFFF} + WhiteSmoke=0xF5F5F5, ///< @htmlcolorblock{F5F5F5} + Yellow=0xFFFF00, ///< @htmlcolorblock{FFFF00} + YellowGreen=0x9ACD32, ///< @htmlcolorblock{9ACD32} + + // LED RGB color that roughly approximates + // the color of incandescent fairy lights, + // assuming that you're using FastLED + // color correction on your LEDs (recommended). + FairyLight=0xFFE42D, ///< @htmlcolorblock{FFE42D} + + // If you are using no color correction, use this + FairyLightNCC=0xFF9D2A ///< @htmlcolorblock{FFE42D} + + } HTMLColorCode; +}; + + +/// Check if two CRGB objects have the same color data +inline __attribute__((always_inline)) bool operator== (const CRGB& lhs, const CRGB& rhs) +{ + return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b); +} + +/// Check if two CRGB objects do *not* have the same color data +inline __attribute__((always_inline)) bool operator!= (const CRGB& lhs, const CRGB& rhs) +{ + return !(lhs == rhs); +} + +/// Check if two CHSV objects have the same color data +inline __attribute__((always_inline)) bool operator== (const CHSV& lhs, const CHSV& rhs) +{ + return (lhs.h == rhs.h) && (lhs.s == rhs.s) && (lhs.v == rhs.v); +} + +/// Check if two CHSV objects do *not* have the same color data +inline __attribute__((always_inline)) bool operator!= (const CHSV& lhs, const CHSV& rhs) +{ + return !(lhs == rhs); +} + +/// Check if the sum of the color channels in one CRGB object is less than another +inline __attribute__((always_inline)) bool operator< (const CRGB& lhs, const CRGB& rhs) +{ + uint16_t sl, sr; + sl = lhs.r + lhs.g + lhs.b; + sr = rhs.r + rhs.g + rhs.b; + return sl < sr; +} + +/// Check if the sum of the color channels in one CRGB object is greater than another +inline __attribute__((always_inline)) bool operator> (const CRGB& lhs, const CRGB& rhs) +{ + uint16_t sl, sr; + sl = lhs.r + lhs.g + lhs.b; + sr = rhs.r + rhs.g + rhs.b; + return sl > sr; +} + +/// Check if the sum of the color channels in one CRGB object is greater than or equal to another +inline __attribute__((always_inline)) bool operator>= (const CRGB& lhs, const CRGB& rhs) +{ + uint16_t sl, sr; + sl = lhs.r + lhs.g + lhs.b; + sr = rhs.r + rhs.g + rhs.b; + return sl >= sr; +} + +/// Check if the sum of the color channels in one CRGB object is less than or equal to another +inline __attribute__((always_inline)) bool operator<= (const CRGB& lhs, const CRGB& rhs) +{ + uint16_t sl, sr; + sl = lhs.r + lhs.g + lhs.b; + sr = rhs.r + rhs.g + rhs.b; + return sl <= sr; +} + + +/// @copydoc CRGB::operator+= +__attribute__((always_inline)) +inline CRGB operator+( const CRGB& p1, const CRGB& p2) +{ + return CRGB( qadd8( p1.r, p2.r), + qadd8( p1.g, p2.g), + qadd8( p1.b, p2.b)); +} + +/// @copydoc CRGB::operator-= +__attribute__((always_inline)) +inline CRGB operator-( const CRGB& p1, const CRGB& p2) +{ + return CRGB( qsub8( p1.r, p2.r), + qsub8( p1.g, p2.g), + qsub8( p1.b, p2.b)); +} + +/// @copydoc CRGB::operator*= +__attribute__((always_inline)) +inline CRGB operator*( const CRGB& p1, uint8_t d) +{ + return CRGB( qmul8( p1.r, d), + qmul8( p1.g, d), + qmul8( p1.b, d)); +} + +/// @copydoc CRGB::operator/= +__attribute__((always_inline)) +inline CRGB operator/( const CRGB& p1, uint8_t d) +{ + return CRGB( p1.r/d, p1.g/d, p1.b/d); +} + + +/// Combine two CRGB objects, taking the smallest value of each channel +__attribute__((always_inline)) +inline CRGB operator&( const CRGB& p1, const CRGB& p2) +{ + return CRGB( p1.r < p2.r ? p1.r : p2.r, + p1.g < p2.g ? p1.g : p2.g, + p1.b < p2.b ? p1.b : p2.b); +} + +/// Combine two CRGB objects, taking the largest value of each channel +__attribute__((always_inline)) +inline CRGB operator|( const CRGB& p1, const CRGB& p2) +{ + return CRGB( p1.r > p2.r ? p1.r : p2.r, + p1.g > p2.g ? p1.g : p2.g, + p1.b > p2.b ? p1.b : p2.b); +} + +/// Scale using CRGB::nscale8_video() +__attribute__((always_inline)) +inline CRGB operator%( const CRGB& p1, uint8_t d) +{ + CRGB retval( p1); + retval.nscale8_video( d); + return retval; +} + + + +/// RGB color channel orderings, used when instantiating controllers to determine +/// what order the controller should send data out in. The default ordering +/// is RGB. +/// Within this enum, the red channel is 0, the green channel is 1, and the +/// blue chanel is 2. +enum EOrder { + RGB=0012, ///< Red, Green, Blue (0012) + RBG=0021, ///< Red, Blue, Green (0021) + GRB=0102, ///< Green, Red, Blue (0102) + GBR=0120, ///< Green, Blue, Red (0120) + BRG=0201, ///< Blue, Red, Green (0201) + BGR=0210 ///< Blue, Green, Red (0210) +}; + +FASTLED_NAMESPACE_END +///@} + +#endif diff --git a/lib/FastLED/src/platforms.cpp b/lib/FastLED/src/platforms.cpp new file mode 100644 index 00000000..db6b179c --- /dev/null +++ b/lib/FastLED/src/platforms.cpp @@ -0,0 +1,44 @@ +/// @file platforms.cpp +/// Platform-specific functions and variables + +/// Disables pragma messages and warnings +#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/lib/FastLED/src/platforms.h b/lib/FastLED/src/platforms.h new file mode 100644 index 00000000..0aca821d --- /dev/null +++ b/lib/FastLED/src/platforms.h @@ -0,0 +1,50 @@ +#ifndef __INC_PLATFORMS_H +#define __INC_PLATFORMS_H + +#include "FastLED.h" + +#include "fastled_config.h" + +/// @file platforms.h +/// Determines which platforms headers to include + +#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" +#elif defined(__MK66FX1M0__) || defined(__MK64FX512__) +// Include k66/T3.6 headers +#include "platforms/arm/k66/fastled_arm_k66.h" +#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" +#elif defined(STM32F10X_MD) || defined(__STM32F1__) || defined(STM32F2XX) || defined(STM32F1) +#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__) || defined(__SAMD51J19A__) || defined(__SAME51J19A__) || defined(__SAMD51P19A__) +#include "platforms/arm/d51/fastled_arm_d51.h" +#elif defined(ARDUINO_ARCH_RP2040) // not sure a pico-sdk define for this +// RP2040 (Raspberry Pi Pico etc) +#include "platforms/arm/rp2040/fastled_arm_rp2040.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" +#endif + +#endif diff --git a/lib/FastLED/src/platforms/apollo3/clockless_apollo3.h b/lib/FastLED/src/platforms/apollo3/clockless_apollo3.h new file mode 100644 index 00000000..ca241532 --- /dev/null +++ b/lib/FastLED/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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER uint8_t & b) { + // SysTick counts down (not up) and is 24-bit + for(FASTLED_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(); + FASTLED_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 + FASTLED_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/lib/FastLED/src/platforms/apollo3/fastled_apollo3.h b/lib/FastLED/src/platforms/apollo3/fastled_apollo3.h new file mode 100644 index 00000000..4c727dd0 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/apollo3/fastpin_apollo3.h b/lib/FastLED/src/platforms/apollo3/fastpin_apollo3.h new file mode 100644 index 00000000..79db204b --- /dev/null +++ b/lib/FastLED/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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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/lib/FastLED/src/platforms/apollo3/fastspi_apollo3.h b/lib/FastLED/src/platforms/apollo3/fastspi_apollo3.h new file mode 100644 index 00000000..c752d8bb --- /dev/null +++ b/lib/FastLED/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(FASTLED_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(FASTLED_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/lib/FastLED/src/platforms/apollo3/led_sysdefs_apollo3.h b/lib/FastLED/src/platforms/apollo3/led_sysdefs_apollo3.h new file mode 100644 index 00000000..be74e24d --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/common/m0clockless.h b/lib/FastLED/src/platforms/arm/common/m0clockless.h new file mode 100644 index 00000000..a759eba9 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/common/m0clockless.h @@ -0,0 +1,389 @@ +#ifndef __INC_M0_CLOCKLESS_H +#define __INC_M0_CLOCKLESS_H + +struct M0ClocklessData { + uint8_t d[3]; + uint8_t e[3]; + uint8_t adj; + uint8_t pad; + uint32_t s[3]; +}; + + +templateint +showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, uint32_t num_leds, struct M0ClocklessData *pData) { + // Lo register variables + FASTLED_REGISTER uint32_t scratch=0; + FASTLED_REGISTER struct M0ClocklessData *base = pData; + FASTLED_REGISTER volatile uint32_t *port = _port; + FASTLED_REGISTER uint32_t d=0; + FASTLED_REGISTER uint32_t counter=num_leds; + FASTLED_REGISTER uint32_t bn=0; + FASTLED_REGISTER uint32_t b=0; + FASTLED_REGISTER uint32_t bitmask = _bitmask; + + // high register variable + FASTLED_REGISTER const uint8_t *leds = _leds; +#if (FASTLED_SCALE8_FIXED == 1) + ++pData->s[0]; + ++pData->s[1]; + ++pData->s[2]; +#endif + asm __volatile__ ( + /////////////////////////////////////////////////////////////////////////// + // + // asm macro definitions - used to assemble the clockless output + // + ".ifnotdef fl_delay_def;" +#ifdef FASTLED_ARM_M0_PLUS + " .set fl_is_m0p, 1;" + " .macro m0pad;" + " nop;" + " .endm;" +#else + " .set fl_is_m0p, 0;" + " .macro m0pad;" + " .endm;" +#endif + " .set fl_delay_def, 1;" + " .set fl_delay_mod, 4;" + " .if fl_is_m0p == 1;" + " .set fl_delay_mod, 3;" + " .endif;" + " .macro fl_delay dtime, reg=r0;" + " .if (\\dtime > 0);" + " .set dcycle, (\\dtime / fl_delay_mod);" + " .set dwork, (dcycle * fl_delay_mod);" + " .set drem, (\\dtime - dwork);" + " .rept (drem);" + " nop;" + " .endr;" + " .if dcycle > 0;" + " mov \\reg, #dcycle;" + " delayloop_\\@:;" + " sub \\reg, #1;" + " bne delayloop_\\@;" + " .if fl_is_m0p == 0;" + " nop;" + " .endif;" + " .endif;" + " .endif;" + " .endm;" + + " .macro mod_delay dtime,b1,b2,reg;" + " .set adj, (\\b1 + \\b2);" + " .if adj < \\dtime;" + " .set dtime2, (\\dtime - adj);" + " fl_delay dtime2, \\reg;" + " .endif;" + " .endm;" + + // check the bit and drop the line low if it isn't set + " .macro qlo4 b,bitmask,port,loff ;" + " lsl \\b, #1 ;" + " bcs skip_\\@ ;" + " str \\bitmask, [\\port, \\loff] ;" + " skip_\\@: ;" + " m0pad;" + " .endm ;" + + // set the pin hi or low (determined by the offset passed in ) + " .macro qset2 bitmask,port,loff;" + " str \\bitmask, [\\port, \\loff];" + " m0pad;" + " .endm;" + + // Load up the next led byte to work with, put it in bn + " .macro loadleds3 leds, bn, rled, scratch;" + " mov \\scratch, \\leds;" + " ldrb \\bn, [\\scratch, \\rled];" + " .endm;" + + // check whether or not we should dither + " .macro loaddither7 bn,d,base,rdither;" + " ldrb \\d, [\\base, \\rdither];" + " lsl \\d, #24;" //; shift high for the qadd w/bn + " lsl \\bn, #24;" //; shift high for the qadd w/d + " bne chkskip_\\@;" //; if bn==0, clear d;" + " eor \\d, \\d;" //; clear d;" + " m0pad;" + " chkskip_\\@:;" + " .endm;" + + // Do the qadd8 for dithering -- there's two versions of this. The m0 version + // takes advantage of the 3 cycle branch to do two things after the branch, + // while keeping timing constant. The m0+, however, branches in 2 cycles, so + // we have to work around that a bit more. This is one of the few times + // where the m0 will actually be _more_ efficient than the m0+ + " .macro dither5 bn,d;" + " .syntax unified;" + " .if fl_is_m0p == 0;" + " adds \\bn, \\d;" // do the add + " bcc dither5_1_\\@;" + " mvns \\bn, \\bn;" // set the low 24bits ot 1's + " lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits + " dither5_1_\\@:;" + " nop;" // nop to keep timing in line + " .else;" + " adds \\bn, \\d;" // do the add" + " bcc dither5_2_\\@;" + " mvns \\bn, \\bn;" // set the low 24bits ot 1's + " dither5_2_\\@:;" + " bcc dither5_3_\\@;" + " lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits + " dither5_3_\\@:;" + " .endif;" + " .syntax divided;" + " .endm;" + + // Do our scaling + " .macro scale4 bn, base, scale, scratch;" + " ldr \\scratch, [\\base, \\scale];" + " lsr \\bn, \\bn, #24;" // bring bn back down to its low 8 bits + " mul \\bn, \\scratch;" // do the multiply + " .endm;" + + // swap bn into b + " .macro swapbbn1 b,bn;" + " lsl \\b, \\bn, #16;" // put the 8 bits we want for output high + " .endm;" + + // adjust the dithering value for the next time around (load e from memory + // to do the math) + " .macro adjdither7 base,d,rled,eoffset,scratch;" + " ldrb \\d, [\\base, \\rled];" + " ldrb \\scratch,[\\base,\\eoffset];" // load e + " .syntax unified;" + " subs \\d, \\scratch, \\d;" // d=e-d + " .syntax divided;" + " strb \\d, [\\base, \\rled];" // save d + " .endm;" + + // increment the led pointer (base+6 has what we're incrementing by) + " .macro incleds3 leds, base, scratch;" + " ldrb \\scratch, [\\base, #6];" // load incremen + " add \\leds, \\leds, \\scratch;" // update leds pointer + " .endm;" + + // compare and loop + " .macro cmploop5 counter,label;" + " .syntax unified;" + " subs \\counter, #1;" + " .syntax divided;" + " beq done_\\@;" + " m0pad;" + " b \\label;" + " done_\\@:;" + " .endm;" + + " .endif;" + ); + +#define M0_ASM_ARGS : \ + [leds] "+h" (leds), \ + [counter] "+l" (counter), \ + [scratch] "+l" (scratch), \ + [d] "+l" (d), \ + [bn] "+l" (bn), \ + [b] "+l" (b) \ + : \ + [port] "l" (port), \ + [base] "l" (base), \ + [bitmask] "l" (bitmask), \ + [hi_off] "I" (HI_OFFSET), \ + [lo_off] "I" (LO_OFFSET), \ + [led0] "I" (RO(0)), \ + [led1] "I" (RO(1)), \ + [led2] "I" (RO(2)), \ + [e0] "I" (3+RO(0)), \ + [e1] "I" (3+RO(1)), \ + [e2] "I" (3+RO(2)), \ + [scale0] "I" (4*(2+RO(0))), \ + [scale1] "I" (4*(2+RO(1))), \ + [scale2] "I" (4*(2+RO(2))), \ + [T1] "I" (T1), \ + [T2] "I" (T2), \ + [T3] "I" (T3) \ + : + + ///////////////////////////////////////////////////////////////////////// + // now for some convinience macros to make building our lines a bit cleaner +#define LOOP " loop_%=:" +#define HI2 " qset2 %[bitmask], %[port], %[hi_off];" +#define _D1 " mod_delay %c[T1],2,0,%[scratch];" +#define QLO4 " qlo4 %[b],%[bitmask],%[port], %[lo_off];" +#define LOADLEDS3(X) " loadleds3 %[leds], %[bn], %[led" #X "] ,%[scratch];" +#define _D2(ADJ) " mod_delay %c[T2],4," #ADJ ",%[scratch];" +#define LO2 " qset2 %[bitmask], %[port], %[lo_off];" +#define _D3(ADJ) " mod_delay %c[T3],2," #ADJ ",%[scratch];" +#define LOADDITHER7(X) " loaddither7 %[bn], %[d], %[base], %[led" #X "];" +#define DITHER5 " dither5 %[bn], %[d];" +#define SCALE4(X) " scale4 %[bn], %[base], %[scale" #X "], %[scratch];" +#define SWAPBBN1 " swapbbn1 %[b], %[bn];" +#define ADJDITHER7(X) " adjdither7 %[base],%[d],%[led" #X "],%[e" #X "],%[scratch];" +#define INCLEDS3 " incleds3 %[leds],%[base],%[scratch];" +#define CMPLOOP5 " cmploop5 %[counter], loop_%=;" +#define NOTHING "" + +#if (defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1)) + // We're allowing interrupts and have hardware timer support defined - + // track the loop outside the asm code, to allow inserting the interrupt + // overrun checks. + asm __volatile__ ( + // pre-load byte 0 + LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 + M0_ASM_ARGS); + + do { + asm __volatile__ ( + // Write out byte 0, prepping byte 1 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 1, prepping byte 2 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 2, prepping byte 0 + HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) + + M0_ASM_ARGS + ); + SEI_CHK; INNER_SEI; --counter; CLI_CHK; + } while(counter); +#elif (FASTLED_ALLOW_INTERRUPTS == 1) + // We're allowing interrupts - track the loop outside the asm code, and + // re-enable interrupts in between each iteration. + asm __volatile__ ( + // pre-load byte 0 + LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 + M0_ASM_ARGS); + + do { + asm __volatile__ ( + // Write out byte 0, prepping byte 1 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 1, prepping byte 2 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 2, prepping byte 0 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) + + M0_ASM_ARGS + ); + + uint32_t ticksBeforeInterrupts = SysTick->VAL; + sei(); + --counter; + cli(); + + // If more than 45 uSecs have elapsed, give up on this frame and start over. + // Note: this isn't completely correct. It's possible that more than one + // millisecond will elapse, and so SysTick->VAL will lap + // ticksBeforeInterrupts. + // Note: ticksBeforeInterrupts DECREASES + const uint32_t kTicksPerMs = VARIANT_MCK / 1000; + const uint32_t kTicksPerUs = kTicksPerMs / 1000; + const uint32_t kTicksIn45us = kTicksPerUs * 45; + + const uint32_t currentTicks = SysTick->VAL; + + if (ticksBeforeInterrupts < currentTicks) { + // Timer started over + if ((ticksBeforeInterrupts + (kTicksPerMs - currentTicks)) > kTicksIn45us) { + return 0; + } + } else { + if ((ticksBeforeInterrupts - currentTicks) > kTicksIn45us) { + return 0; + } + } + } while(counter); +#else + // We're not allowing interrupts - run the entire loop in asm to keep things + // as tight as possible. In an ideal world, we should be pushing out ws281x + // leds (or other 3-wire leds) with zero gaps between pixels. + asm __volatile__ ( + // pre-load byte 0 + LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 + + // loop over writing out the data + LOOP + // Write out byte 0, prepping byte 1 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 1, prepping byte 2 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 2, prepping byte 0 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) CMPLOOP5 + + M0_ASM_ARGS + ); +#endif + return num_leds; +} + +#endif diff --git a/lib/FastLED/src/platforms/arm/d21/clockless_arm_d21.h b/lib/FastLED/src/platforms/arm/d21/clockless_arm_d21.h new file mode 100644 index 00000000..16526ed6 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/d21/fastled_arm_d21.h b/lib/FastLED/src/platforms/arm/d21/fastled_arm_d21.h new file mode 100644 index 00000000..98412749 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d21/fastled_arm_d21.h @@ -0,0 +1,7 @@ +#ifndef __INC_FASTLED_ARM_D21_H +#define __INC_FASTLED_ARM_D21_H + +#include "fastpin_arm_d21.h" +#include "clockless_arm_d21.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/d21/fastpin_arm_d21.h b/lib/FastLED/src/platforms/arm/d21/fastpin_arm_d21.h new file mode 100644 index 00000000..5b732a8a --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d21/fastpin_arm_d21.h @@ -0,0 +1,273 @@ +#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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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_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) || defined(ARDUINO_SAMD_MKRZERO) + +#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 + +#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 + +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + + +#endif // __INC_FASTPIN_ARM_SAM_H diff --git a/lib/FastLED/src/platforms/arm/d21/led_sysdefs_arm_d21.h b/lib/FastLED/src/platforms/arm/d21/led_sysdefs_arm_d21.h new file mode 100644 index 00000000..a48db10a --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d21/led_sysdefs_arm_d21.h @@ -0,0 +1,26 @@ +#ifndef __INC_LED_SYSDEFS_ARM_D21_H +#define __INC_LED_SYSDEFS_ARM_D21_H + + +#define FASTLED_ARM +#define FASTLED_ARM_M0_PLUS + +#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 + +// reusing/abusing cli/sei defs for due +#define cli() __disable_irq(); +#define sei() __enable_irq(); + + +#endif diff --git a/lib/FastLED/src/platforms/arm/d51/README.txt b/lib/FastLED/src/platforms/arm/d51/README.txt new file mode 100644 index 00000000..036a02a6 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d51/README.txt @@ -0,0 +1,7 @@ +FastLED updates for adafruit FEATHER M4 and fixes to ITSBITSY M4 compiles + SAMD51 + +Tested on + - FEATHER M4 with DOTSTAR and neopixel strips + - Seeed Wio Terminal and WS2812B and APA102 LED strips using either SPI or GPIO pins + diff --git a/lib/FastLED/src/platforms/arm/d51/clockless_arm_d51.h b/lib/FastLED/src/platforms/arm/d51/clockless_arm_d51.h new file mode 100644 index 00000000..2772d8f5 --- /dev/null +++ b/lib/FastLED/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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) { + for(FASTLED_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; + + FASTLED_REGISTER data_ptr_t port = FastPin::port(); + FASTLED_REGISTER data_t hi = *port | FastPin::mask(); + FASTLED_REGISTER data_t lo = *port & ~FastPin::mask(); + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + FASTLED_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/lib/FastLED/src/platforms/arm/d51/fastled_arm_d51.h b/lib/FastLED/src/platforms/arm/d51/fastled_arm_d51.h new file mode 100644 index 00000000..912a9018 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d51/fastled_arm_d51.h @@ -0,0 +1,8 @@ +#ifndef __INC_FASTLED_ARM_D51_H +#define __INC_FASTLED_ARM_D51_H + +#include "fastpin_arm_d51.h" +#include "../../fastspi_ardunio_core.h" +#include "clockless_arm_d51.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/d51/fastpin_arm_d51.h b/lib/FastLED/src/platforms/arm/d51/fastpin_arm_d51.h new file mode 100644 index 00000000..2cddd9f9 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d51/fastpin_arm_d51.h @@ -0,0 +1,237 @@ +#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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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_CAN) + +#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( 7, 3, 1); _FL_DEFPIN( 8, 2, 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 + +#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 + +#elif defined(SEEED_WIO_TERMINAL) + +#define MAX_PIN 9 +// D0/A0-D8/A8 +_FL_DEFPIN( 0, 8, 1); _FL_DEFPIN( 1, 9, 1); _FL_DEFPIN( 2, 7, 0); _FL_DEFPIN( 3, 4, 1); +_FL_DEFPIN( 4, 5, 1); _FL_DEFPIN( 5, 6, 1); _FL_DEFPIN( 6, 4, 0); _FL_DEFPIN( 7, 7, 1); +_FL_DEFPIN( 8, 6, 0); +// SDA/SCL +_FL_DEFPIN(12, 17, 0); _FL_DEFPIN(13, 16, 0); +// match GPIO pin nubers 9..11 MISO/MOSI/SCK +_FL_DEFPIN(PIN_SPI_MISO, 0, 1); _FL_DEFPIN(PIN_SPI_MOSI, 2, 1); _FL_DEFPIN(PIN_SPI_SCK, 3, 1); + +#define SPI_DATA PIN_SPI_MOSI +#define SPI_CLOCK PIN_SPI_SCK + +#define ARDUNIO_CORE_SPI +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ADAFRUIT_MATRIXPORTAL_M4_EXPRESS) + +#define MAX_PIN 21 +// 0/1 - SERCOM/UART (Serial1) +_FL_DEFPIN( 0, 1, 0); _FL_DEFPIN( 1, 0, 0); +// 2..3 buttons +_FL_DEFPIN( 2, 22, 1); _FL_DEFPIN( 3, 23, 1); +// 4 neopixel +_FL_DEFPIN( 4, 23, 0); +// SDA/SCL +_FL_DEFPIN( 5, 31, 1); _FL_DEFPIN( 6, 30, 1); +// 7..12 RGBRGB pins +_FL_DEFPIN( 7, 0, 1); _FL_DEFPIN( 8, 1, 1); _FL_DEFPIN( 9, 2, 1); _FL_DEFPIN(10, 3, 1); +_FL_DEFPIN(11, 4, 1); _FL_DEFPIN(12, 5, 1); +// 13 LED +_FL_DEFPIN(13, 14, 0); +// 14..21 Control pins +_FL_DEFPIN(14, 6, 1); _FL_DEFPIN(15, 14, 1); _FL_DEFPIN(16, 12, 1); _FL_DEFPIN(17, 7, 1); +_FL_DEFPIN(18, 8, 1); _FL_DEFPIN(19, 9, 1); _FL_DEFPIN(20, 15, 1); _FL_DEFPIN(21, 13, 1); +// 22..26 Analog pins +_FL_DEFPIN(22, 2, 1); _FL_DEFPIN(23, 5, 1); _FL_DEFPIN(24, 4, 1); _FL_DEFPIN(25, 6, 1); +_FL_DEFPIN(26, 7, 1); +// 34..36 ESP SPI +_FL_DEFPIN(34, 16, 0); _FL_DEFPIN(35, 17, 0); _FL_DEFPIN(36, 19, 0); +// 48..50 external SPI #2 on sercom 0 +_FL_DEFPIN(48, 5, 0); _FL_DEFPIN(49, 4, 0); _FL_DEFPIN(50, 7, 0); + +#define SPI_DATA 4 +#define SPI_CLOCK 7 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#elif defined(ADAFRUIT_GRAND_CENTRAL_M4) + +#define MAX_PIN 54 +// D0..D7 +_FL_DEFPIN( 0, 25, 1); _FL_DEFPIN( 1, 24, 1); _FL_DEFPIN( 2, 18, 2); _FL_DEFPIN( 3, 19, 2); +_FL_DEFPIN( 4, 20, 2); _FL_DEFPIN( 5, 21, 2); _FL_DEFPIN( 6, 20, 3); _FL_DEFPIN( 7, 21, 3); +// D8..D13 +_FL_DEFPIN( 8, 18, 1); _FL_DEFPIN( 9, 2, 1); _FL_DEFPIN(10, 22, 1); +_FL_DEFPIN(11, 23, 1); _FL_DEFPIN(12, 0, 1); _FL_DEFPIN(13, 1, 0); +// D14..D21 +_FL_DEFPIN(14, 16, 1); _FL_DEFPIN(15, 17, 1); _FL_DEFPIN(16, 22, 2); _FL_DEFPIN(17, 23, 2); +_FL_DEFPIN(18, 12, 1); _FL_DEFPIN(19, 13, 1); _FL_DEFPIN(20, 20, 1); _FL_DEFPIN(21, 21, 1); +// D22..D53 +_FL_DEFPIN(22, 12, 3); _FL_DEFPIN(23, 15, 0); _FL_DEFPIN(24, 17, 2); _FL_DEFPIN(25, 16, 2); +_FL_DEFPIN(26, 12, 0); _FL_DEFPIN(27, 13, 0); _FL_DEFPIN(28, 14, 0); _FL_DEFPIN(29, 19, 1); +_FL_DEFPIN(30, 23, 0); _FL_DEFPIN(31, 22, 0); _FL_DEFPIN(32, 21, 0); _FL_DEFPIN(33, 20, 0); +_FL_DEFPIN(34, 19, 0); _FL_DEFPIN(35, 18, 0); _FL_DEFPIN(36, 17, 0); _FL_DEFPIN(37, 16, 0); +_FL_DEFPIN(38, 15, 1); _FL_DEFPIN(39, 14, 1); _FL_DEFPIN(40, 13, 2); _FL_DEFPIN(41, 12, 2); +_FL_DEFPIN(42, 15, 2); _FL_DEFPIN(43, 14, 2); _FL_DEFPIN(44, 11, 2); _FL_DEFPIN(45, 10, 2); +_FL_DEFPIN(46, 6, 2); _FL_DEFPIN(47, 7, 2); _FL_DEFPIN(48, 4, 2); _FL_DEFPIN(49, 5, 2); +_FL_DEFPIN(50, 11, 3); _FL_DEFPIN(51, 8, 3); _FL_DEFPIN(52, 9, 3); _FL_DEFPIN(53, 10, 3); + +#define SPI_DATA 51 +#define SPI_CLOCK 52 + +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#endif + + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + + +#endif // __INC_FASTPIN_ARM_D51_H diff --git a/lib/FastLED/src/platforms/arm/d51/led_sysdefs_arm_d51.h b/lib/FastLED/src/platforms/arm/d51/led_sysdefs_arm_d51.h new file mode 100644 index 00000000..77726fa2 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/d51/led_sysdefs_arm_d51.h @@ -0,0 +1,25 @@ +#ifndef __INC_LED_SYSDEFS_ARM_D51_H +#define __INC_LED_SYSDEFS_ARM_D51_H + + +#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 + +// reusing/abusing cli/sei defs for due +#define cli() __disable_irq(); +#define sei() __enable_irq(); + + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/clockless_arm_k20.h b/lib/FastLED/src/platforms/arm/k20/clockless_arm_k20.h new file mode 100644 index 00000000..e35e74db --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/clockless_arm_k20.h @@ -0,0 +1,124 @@ +#ifndef __INC_CLOCKLESS_ARM_K20_H +#define __INC_CLOCKLESS_ARM_K20_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY3) + +#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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) { + for(FASTLED_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; + + FASTLED_REGISTER data_ptr_t port = FastPin::port(); + FASTLED_REGISTER data_t hi = *port | FastPin::mask(); + FASTLED_REGISTER data_t lo = *port & ~FastPin::mask(); + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + FASTLED_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; + } +}; +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/clockless_block_arm_k20.h b/lib/FastLED/src/platforms/arm/k20/clockless_block_arm_k20.h new file mode 100644 index 00000000..750e4e7e --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/clockless_block_arm_k20.h @@ -0,0 +1,332 @@ +#ifndef __INC_BLOCK_CLOCKLESS_ARM_K20_H +#define __INC_BLOCK_CLOCKLESS_ARM_K20_H + +// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY3) +#define FASTLED_HAS_BLOCKLESS 1 + +#define PORTC_FIRST_PIN 15 +#define PORTD_FIRST_PIN 2 +#define HAS_PORTDC 1 + +#define PORT_MASK (((1< + +FASTLED_NAMESPACE_BEGIN + +template +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 showPixels(PixelController & pixels) { + mWait.wait(); + uint32_t clocks = showRGBInternal(pixels); + #if FASTLED_ALLOW_INTERRUPTS == 0 + // Adjust the timer + long microsTaken = CLKS_TO_MICROS(clocks); + MS_COUNTER += (1 + (microsTaken / 1000)); + #endif + + mWait.mark(); + } + + virtual void init() { + if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC + switch(USED_LANES) { + case 12: FastPin<30>::setOutput(); + case 11: FastPin<29>::setOutput(); + case 10: FastPin<27>::setOutput(); + case 9: FastPin<28>::setOutput(); + case 8: FastPin<12>::setOutput(); + case 7: FastPin<11>::setOutput(); + case 6: FastPin<13>::setOutput(); + case 5: FastPin<10>::setOutput(); + case 4: FastPin<9>::setOutput(); + case 3: FastPin<23>::setOutput(); + case 2: FastPin<22>::setOutput(); + case 1: FastPin<15>::setOutput(); + } + } else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD + switch(USED_LANES) { + case 8: FastPin<5>::setOutput(); + case 7: FastPin<21>::setOutput(); + case 6: FastPin<20>::setOutput(); + case 5: FastPin<6>::setOutput(); + case 4: FastPin<8>::setOutput(); + case 3: FastPin<7>::setOutput(); + case 2: FastPin<14>::setOutput(); + case 1: FastPin<2>::setOutput(); + } + } + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + typedef union { + uint8_t bytes[12]; + uint16_t shorts[6]; + uint32_t raw[3]; + } Lines; + + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + FASTLED_REGISTER Lines b2; + if(USED_LANES>8) { + transpose8<1,2>(b.bytes,b2.bytes); + transpose8<1,2>(b.bytes+8,b2.bytes+1); + } else { + transpose8x1(b.bytes,b2.bytes); + } + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_REGISTER uint8_t scale = pixels.template getscale(pixels); + + for(FASTLED_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; + + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + if(USED_LANES>8) { + *FastPin::cport() = ((~b2.shorts[i]) & PORT_MASK); + } else { + *FastPin::cport() = ((~b2.bytes[7-i]) & PORT_MASK); + } + + while((next_mark - ARM_DWT_CYCCNT) > (T3)); + *FastPin::cport() = PORT_MASK; + + b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale(pixels,i+(USED_LANES/2),d,scale); + } + + // if folks use an odd numnber of lanes, get the last byte's value here + if(USED_LANES & 0x01) { + b.bytes[USED_LANES-1] = pixels.template loadAndScale(pixels,USED_LANES-1,d,scale); + } + + for(FASTLED_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; + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + if(USED_LANES>8) { + *FastPin::cport() = ((~b2.shorts[i]) & PORT_MASK); + } else { + // b2.bytes[0] = 0; + *FastPin::cport() = ((~b2.bytes[7-i]) & PORT_MASK); + } + + while((next_mark - ARM_DWT_CYCCNT) > (T3)); + *FastPin::cport() = PORT_MASK; + + } + } + + + + // 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 &allpixels) { + // Get access to the clock + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + ARM_DWT_CYCCNT = 0; + + // Setup the pixel controller and load/scale the first byte + allpixels.preStepFirstByteDithering(); + FASTLED_REGISTER Lines b0; + + allpixels.preStepFirstByteDithering(); + for(int i = 0; i < USED_LANES; ++i) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + + cli(); + uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + + while(allpixels.has(1)) { + #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-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; } + } + #endif + allpixels.stepDithering(); + + // 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 + }; + + return ARM_DWT_CYCCNT; + } +}; + +#define PMASK ((1<<(LANES))-1) +#define PMASK_HI (PMASK>>8 & 0xFF) +#define PMASK_LO (PMASK & 0xFF) + +template +class SixteenWayInlineBlockClocklessController : 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() { + static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!"); + // FastPin<30>::setOutput(); + // FastPin<29>::setOutput(); + // FastPin<27>::setOutput(); + // FastPin<28>::setOutput(); + switch(LANES) { + case 16: FastPin<12>::setOutput(); + case 15: FastPin<11>::setOutput(); + case 14: FastPin<13>::setOutput(); + case 13: FastPin<10>::setOutput(); + case 12: FastPin<9>::setOutput(); + case 11: FastPin<23>::setOutput(); + case 10: FastPin<22>::setOutput(); + case 9: FastPin<15>::setOutput(); + + case 8: FastPin<5>::setOutput(); + case 7: FastPin<21>::setOutput(); + case 6: FastPin<20>::setOutput(); + case 5: FastPin<6>::setOutput(); + case 4: FastPin<8>::setOutput(); + case 3: FastPin<7>::setOutput(); + case 2: FastPin<14>::setOutput(); + case 1: FastPin<2>::setOutput(); + } + } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + uint32_t clocks = showRGBInternal(pixels); + #if FASTLED_ALLOW_INTERRUPTS == 0 + // Adjust the timer + long microsTaken = CLKS_TO_MICROS(clocks); + MS_COUNTER += (1 + (microsTaken / 1000)); + #endif + + mWait.mark(); + } + + typedef union { + uint8_t bytes[16]; + uint16_t shorts[8]; + uint32_t raw[4]; + } Lines; + + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + FASTLED_REGISTER Lines b2; + transpose8x1(b.bytes,b2.bytes); + transpose8x1(b.bytes+8,b2.bytes+8); + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_REGISTER uint8_t scale = pixels.template getscale(pixels); + + for(FASTLED_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; + *FastPin::sport() = PMASK_HI; + + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6)); + *FastPin::cport() = ((~b2.bytes[7-i]) & PMASK_LO); + *FastPin::cport() = ((~b2.bytes[15-i]) & PMASK_HI); + + while((next_mark - ARM_DWT_CYCCNT) > (T3)); + *FastPin::cport() = PMASK_LO; + *FastPin::cport() = PMASK_HI; + + b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + if(LANES==16 || (LANES>8 && ((i+8) < LANES))) { + b.bytes[i+8] = pixels.template loadAndScale(pixels,i+8,d,scale); + } + } + } + + + + // 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 &allpixels) { + // Get access to the clock + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + ARM_DWT_CYCCNT = 0; + + // Setup the pixel controller and load/scale the first byte + allpixels.preStepFirstByteDithering(); + FASTLED_REGISTER Lines b0; + + allpixels.preStepFirstByteDithering(); + for(int i = 0; i < LANES; ++i) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + + cli(); + uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + + while(allpixels.has(1)) { + allpixels.stepDithering(); + #if 0 && (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 ARM_DWT_CYCCNT; } + } + #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 0 && (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + sei(); + + return ARM_DWT_CYCCNT; + } +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/fastled_arm_k20.h b/lib/FastLED/src/platforms/arm/k20/fastled_arm_k20.h new file mode 100644 index 00000000..06c5c8e8 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/fastled_arm_k20.h @@ -0,0 +1,13 @@ +#ifndef __INC_FASTLED_ARM_K20_H +#define __INC_FASTLED_ARM_K20_H + +// Include the k20 headers +#include "fastpin_arm_k20.h" +#include "fastspi_arm_k20.h" +#include "octows2811_controller.h" +#include "ws2812serial_controller.h" +#include "smartmatrix_t3.h" +#include "clockless_arm_k20.h" +#include "clockless_block_arm_k20.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/fastpin_arm_k20.h b/lib/FastLED/src/platforms/arm/k20/fastpin_arm_k20.h new file mode 100644 index 00000000..8198b8c0 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/fastpin_arm_k20.h @@ -0,0 +1,120 @@ +#ifndef __FASTPIN_ARM_K20_H +#define __FASTPIN_ARM_K20_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 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 +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)) { _PSOR::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC +/// does a poor job of optimizing around these accesses so they are not being used just yet. +template class _ARMPIN_BITBAND { +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)) { *_PDOR::template rx<_BIT>() = 1; } + inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return 1; } + inline static port_t loval() __attribute__ ((always_inline)) { return 0; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); } + inline static port_t mask() __attribute__ ((always_inline)) { return 1; } +}; + +// Macros for k20 pin access/definition +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (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; } \ + template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; +#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 _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; \ + template<> class FastPinBB : public _ARMPIN_BITBAND {}; + +// Actual pin definitions +_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4); + +#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY) + +#define MAX_PIN 33 +_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 +#define SPI1 (*(SPI_t *)0x4002D000) + +#define SPI2_DATA 7 +#define SPI2_CLOCK 14 + +#define FASTLED_TEENSY3 +#define ARM_HARDWARE_SPI +#define HAS_HARDWARE_PIN_SUPPORT +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_ARM_K20 diff --git a/lib/FastLED/src/platforms/arm/k20/fastspi_arm_k20.h b/lib/FastLED/src/platforms/arm/k20/fastspi_arm_k20.h new file mode 100644 index 00000000..b5211493 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/fastspi_arm_k20.h @@ -0,0 +1,466 @@ +#ifndef __INC_FASTSPI_ARM_H +#define __INC_FASTSPI_ARM_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY) + +// Version 1.20 renamed SPI_t to KINETISK_SPI_t +#if TEENSYDUINO >= 120 +#define SPI_t KINETISK_SPI_t +#endif + +#ifndef KINETISK_SPI0 +#define KINETISK_SPI0 SPI0 +#endif + +#ifndef SPI_PUSHR_CONT +#define SPI_PUSHR_CONT SPIX.PUSHR_CONT +#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X) +#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ +#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT +#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X) +#endif + +// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now, +// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit +// set (the zero-th bit). Unclear if I will want this to change at some point. +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; } +}; + +#define MAX(A, B) (( (A) > (B) ) ? (A) : (B)) + +#define USE_CONT 0 +// intra-frame backup data +struct SPIState { + uint32_t _ctar0,_ctar1; + uint32_t pins[4]; +}; + +// extern SPIState gState; + + +// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world. +template void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) { + switch(VAL) { + // Handle the dbl clock cases + case 0: case 1: + case 2: preScalar = 0; scalar = 0; dbl = 1; break; + case 3: preScalar = 1; scalar = 0; dbl = 1; break; + case 5: preScalar = 2; scalar = 0; dbl = 1; break; + case 7: preScalar = 3; scalar = 0; dbl = 1; break; + + // Handle the scalar value 6 cases (since it's not a power of two, it won't get caught + // below) + case 9: preScalar = 1; scalar = 2; dbl = 1; break; + case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break; + + case 15: preScalar = 2; scalar = 2; dbl = 1; break; + case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break; + + case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break; + case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break; + default: { + int p2 = BitWork::highestBit(); + int p3 = BitWork::highestBit(); + int p5 = BitWork::highestBit(); + int p7 = BitWork::highestBit(); + + int w2 = 2 * (1 << p2); + int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0; + int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0; + int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0; + + int maxval = MAX(MAX(w2, w3), MAX(w5, w7)); + + if(w2 == maxval) { preScalar = 0; scalar = p2; } + else if(w3 == maxval) { preScalar = 1; scalar = p3; } + else if(w5 == maxval) { preScalar = 2; scalar = p5; } + else if(w7 == maxval) { preScalar = 3; scalar = p7; } + + dbl = 0; + if(scalar == 0) { dbl = 1; } + else if(scalar < 3) { --scalar; } + } + } + return; +} + +#define SPIX (*(SPI_t*)pSPIX) + +template +class ARMHardwareSPIOutput { + Selectable *m_pSelect; + SPIState gState; + + // Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa), + // and likewise enabling pin 14 disables pin 13 (and vice versa) + inline void enable_pins(void) __attribute__((always_inline)) { + //serial_print("enable_pins\n"); + switch(_DATA_PIN) { + case 7: + CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + case 11: + CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + } + + switch(_CLOCK_PIN) { + case 13: + CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + case 14: + CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + } + } + + // Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using + inline void disable_pins(void) __attribute__((always_inline)) { + switch(_DATA_PIN) { + case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break; + case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break; + } + + switch(_CLOCK_PIN) { + case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break; + case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break; + } + } + + static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) { + if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return; + uint32_t mcr = SPIX.MCR; + if(mcr & SPI_MCR_MDIS) { + SPIX.CTAR0 = ctar0; + SPIX.CTAR1 = ctar1; + } else { + SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT; + SPIX.CTAR0 = ctar0; + SPIX.CTAR1 = ctar1; + SPIX.MCR = mcr; + } + } + + static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) { + if (SPIX.CTAR0 == ctar) return; + uint32_t mcr = SPIX.MCR; + if (mcr & SPI_MCR_MDIS) { + SPIX.CTAR0 = ctar; + } else { + SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT; + SPIX.CTAR0 = ctar; + + SPIX.MCR = mcr; + } + } + + static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) { + if (SPIX.CTAR1 == ctar) return; + uint32_t mcr = SPIX.MCR; + if (mcr & SPI_MCR_MDIS) { + SPIX.CTAR1 = ctar; + } else { + SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT; + SPIX.CTAR1 = ctar; + SPIX.MCR = mcr; + + } + } + + void setSPIRate() { + // Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits + uint32_t _PBR = 0; + uint32_t _BR = 0; + uint32_t _CSSCK = 0; + uint32_t _DBR = 0; + + // if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256 + // else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128 + // else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64 + // else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32 + // else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16 + // else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8 + // else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7 + // else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5 + // else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4 + // else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3 + // else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2 + + getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR); + _CSSCK = _BR; + + uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK); + uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK); + + #if USE_CONT == 1 + ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL; + ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL; + #endif + + if(_DBR) { + ctar0 |= SPI_CTAR_DBR; + ctar1 |= SPI_CTAR_DBR; + } + + update_ctars(ctar0,ctar1); + } + + void inline save_spi_state() __attribute__ ((always_inline)) { + // save ctar data + gState._ctar0 = SPIX.CTAR0; + gState._ctar1 = SPIX.CTAR1; + + // save data for the not-us pins + gState.pins[0] = CORE_PIN7_CONFIG; + gState.pins[1] = CORE_PIN11_CONFIG; + gState.pins[2] = CORE_PIN13_CONFIG; + gState.pins[3] = CORE_PIN14_CONFIG; + } + + void inline restore_spi_state() __attribute__ ((always_inline)) { + // restore ctar data + update_ctars(gState._ctar0,gState._ctar1); + + // restore data for the not-us pins (not necessary because disable_pins will do this) + // CORE_PIN7_CONFIG = gState.pins[0]; + // CORE_PIN11_CONFIG = gState.pins[1]; + // CORE_PIN13_CONFIG = gState.pins[2]; + // CORE_PIN14_CONFIG = gState.pins[3]; + } + + +public: + ARMHardwareSPIOutput() { m_pSelect = NULL; } + ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + // set the pins to output + FastPin<_DATA_PIN>::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); + + // Enable SPI0 clock + uint32_t sim6 = SIM_SCGC6; + if((SPI_t*)pSPIX == &KINETISK_SPI0) { + if (!(sim6 & SIM_SCGC6_SPI0)) { + //serial_print("init1\n"); + SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0; + SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1); + } + } else if((SPI_t*)pSPIX == &SPI1) { + if (!(sim6 & SIM_SCGC6_SPI1)) { + //serial_print("init1\n"); + SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1; + SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1); + } + } + + // Configure SPI as the master and enable + SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE); + SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT); + + // pin/spi configuration happens on select + } + + static void waitFully() __attribute__((always_inline)) { + // Wait for the last byte to get shifted into the register + bool empty = false; + + do { + cli(); + if ((SPIX.SR & 0xF000) > 0) { + // reset the TCF flag + SPIX.SR |= SPI_SR_TCF; + } else { + empty = true; + } + sei(); + } while (!empty); + + // wait for the TCF flag to get set + while (!(SPIX.SR & SPI_SR_TCF)); + SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF); + } + + static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); } + static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); } + static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); } + + enum ECont { CONT, NOCONT }; + enum EWait { PRE, POST, NONE }; + enum ELast { NOTLAST, LAST }; + + #if USE_CONT == 1 + #define CM CONT + #else + #define CM NOCONT + #endif + #define WM PRE + + template class Write { + public: + static void writeWord(uint16_t w) __attribute__((always_inline)) { + 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); + SPIX.SR |= SPI_SR_TCF; + sei(); + if(WAIT_STATE == POST) { wait(); } + } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { + 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); + SPIX.SR |= SPI_SR_TCF; + sei(); + if(WAIT_STATE == POST) { wait(); } + } + }; + + static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); } + static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; sei(); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); } + + static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); } + static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei();} + + static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); } + static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); wait(); } + static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); } + + // not the most efficient mechanism in the world - but should be enough for sm16716 and friends + template inline static void writeBit(uint8_t b) { + uint32_t ctar1_save = SPIX.CTAR1; + + // Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit + uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0); + update_ctar1(ctar1); + + writeWord( (b & (1 << BIT)) != 0); + + update_ctar1(ctar1_save); + } + + void inline select() __attribute__((always_inline)) { + save_spi_state(); + if(m_pSelect != NULL) { m_pSelect->select(); } + setSPIRate(); + enable_pins(); + } + + void inline release() __attribute__((always_inline)) { + disable_pins(); + if(m_pSelect != NULL) { m_pSelect->release(); } + restore_spi_state(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { Write::writeByte(value); } + } + + void writeBytesValue(uint8_t value, int len) { + select(); + while(len--) { + writeByte(value); + } + waitFully(); + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_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(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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; + + // Setup the pixel controller + if((FLAGS & FLAG_START_BIT) == 0) { + //If no start bit stupiditiy, write out as many 16-bit blocks as we can + while(pixels.has(2)) { + // Load and write out the first two bytes + if(WM == NONE) { wait1(); } + Write::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1())); + + // Load and write out the next two bytes (step dithering, advance data in between since we + // cross pixels here) + Write::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0())); + + // Load and write out the next two bytes + Write::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2())); + pixels.stepDithering(); + pixels.advanceData(); + } + + if(pixels.has(1)) { + if(WM == NONE) { wait1(); } + // write out the rest as alternating 16/8-bit blocks (likely to be just one) + Write::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1())); + Write::writeByte(D::adjust(pixels.loadAndScale2())); + } + + D::postBlock(len); + waitFully(); + } else if(FLAGS & FLAG_START_BIT) { + uint32_t ctar1_save = SPIX.CTAR1; + + // Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit + uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8); + update_ctar1(ctar1); + + while(pixels.has(1)) { + writeWord( 0x100 | D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + waitFully(); + + // restore ctar1 + update_ctar1(ctar1_save); + } + release(); + } +}; +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/led_sysdefs_arm_k20.h b/lib/FastLED/src/platforms/arm/k20/led_sysdefs_arm_k20.h new file mode 100644 index 00000000..0dcb626a --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/led_sysdefs_arm_k20.h @@ -0,0 +1,46 @@ +#ifndef __INC_LED_SYSDEFS_ARM_K20_H +#define __INC_LED_SYSDEFS_ARM_K20_H + +#define FASTLED_TEENSY3 +#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 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) */ +#endif + +extern volatile uint32_t systick_millis_count; +# define MS_COUNTER systick_millis_count + + +// Default to using PROGMEM, since TEENSY3 provides it +// even though all it does is ignore it. Just being +// conservative here in case TEENSY3 changes. +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/octows2811_controller.h b/lib/FastLED/src/platforms/arm/k20/octows2811_controller.h new file mode 100644 index 00000000..f365e61f --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k20/octows2811_controller.h @@ -0,0 +1,66 @@ +#ifndef __INC_OCTOWS2811_CONTROLLER_H +#define __INC_OCTOWS2811_CONTROLLER_H + +#ifdef USE_OCTOWS2811 + +// #include "OctoWS2811.h" + +FASTLED_NAMESPACE_BEGIN + +template +class COctoWS2811Controller : public CPixelLEDController { + OctoWS2811 *pocto; + uint8_t *drawbuffer,*framebuffer; + + void _init(int nLeds) { + if(pocto == NULL) { + drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3); + framebuffer = (uint8_t*)malloc(nLeds * 8 * 3); + + // byte ordering is handled in show by the pixel controller + int config = WS2811_RGB; + config |= CHIP; + + pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config); + + pocto->begin(); + } + } +public: + COctoWS2811Controller() { pocto = NULL; } + virtual int size() { return CLEDController::size() * 8; } + + virtual void init() { /* do nothing yet */ } + + typedef union { + uint8_t bytes[8]; + uint32_t raw[2]; + } Lines; + + virtual void showPixels(PixelController & pixels) { + _init(pixels.size()); + + uint8_t *pData = drawbuffer; + while(pixels.has(1)) { + Lines b; + + 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); } + transpose8x1_MSB(b.bytes,pData); pData += 8; + for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale2(i); } + transpose8x1_MSB(b.bytes,pData); pData += 8; + pixels.stepDithering(); + pixels.advanceData(); + } + + pocto->show(); + } + +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif diff --git a/lib/FastLED/src/platforms/arm/k20/smartmatrix_t3.h b/lib/FastLED/src/platforms/arm/k20/smartmatrix_t3.h new file mode 100644 index 00000000..c9747f0b --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/k20/ws2812serial_controller.h b/lib/FastLED/src/platforms/arm/k20/ws2812serial_controller.h new file mode 100644 index 00000000..a761dd49 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/k66/clockless_arm_k66.h b/lib/FastLED/src/platforms/arm/k66/clockless_arm_k66.h new file mode 100644 index 00000000..892c7bae --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k66/clockless_arm_k66.h @@ -0,0 +1,124 @@ +#ifndef __INC_CLOCKLESS_ARM_K66_H +#define __INC_CLOCKLESS_ARM_K66_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY3) + +#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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) { + for(FASTLED_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; + + FASTLED_REGISTER data_ptr_t port = FastPin::port(); + FASTLED_REGISTER data_t hi = *port | FastPin::mask(); + FASTLED_REGISTER data_t lo = *port & ~FastPin::mask(); + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + FASTLED_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; + } +}; +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/arm/k66/clockless_block_arm_k66.h b/lib/FastLED/src/platforms/arm/k66/clockless_block_arm_k66.h new file mode 100644 index 00000000..3a43c61a --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k66/clockless_block_arm_k66.h @@ -0,0 +1,347 @@ +#ifndef __INC_BLOCK_CLOCKLESS_ARM_K66_H +#define __INC_BLOCK_CLOCKLESS_ARM_K66_H + +// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY3) +#define FASTLED_HAS_BLOCKLESS 1 + +#define PORTB_FIRST_PIN 0 +#define PORTC_FIRST_PIN 15 +#define PORTD_FIRST_PIN 2 +#define HAS_PORTDC 1 + +#define LANE_MASK (((1< + +FASTLED_NAMESPACE_BEGIN + +template +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 showPixels(PixelController & pixels) { + mWait.wait(); + uint32_t clocks = showRGBInternal(pixels); + #if FASTLED_ALLOW_INTERRUPTS == 0 + // Adjust the timer + long microsTaken = CLKS_TO_MICROS(clocks); + MS_COUNTER += (1 + (microsTaken / 1000)); + #endif + + mWait.mark(); + } + + virtual void init() { + if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC + switch(USED_LANES) { + case 12: FastPin<30>::setOutput(); + case 11: FastPin<29>::setOutput(); + case 10: FastPin<27>::setOutput(); + case 9: FastPin<28>::setOutput(); + case 8: FastPin<12>::setOutput(); + case 7: FastPin<11>::setOutput(); + case 6: FastPin<13>::setOutput(); + case 5: FastPin<10>::setOutput(); + case 4: FastPin<9>::setOutput(); + case 3: FastPin<23>::setOutput(); + case 2: FastPin<22>::setOutput(); + case 1: FastPin<15>::setOutput(); + } + } else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD + switch(USED_LANES) { + case 8: FastPin<5>::setOutput(); + case 7: FastPin<21>::setOutput(); + case 6: FastPin<20>::setOutput(); + case 5: FastPin<6>::setOutput(); + case 4: FastPin<8>::setOutput(); + case 3: FastPin<7>::setOutput(); + case 2: FastPin<14>::setOutput(); + case 1: FastPin<2>::setOutput(); + } + } else if (FIRST_PIN == PORTB_FIRST_PIN) { // PORTB + switch (USED_LANES) { + case 8: FastPin<45>::setOutput(); + case 7: FastPin<44>::setOutput(); + case 6: FastPin<46>::setOutput(); + case 5: FastPin<43>::setOutput(); + case 4: FastPin<30>::setOutput(); + case 3: FastPin<29>::setOutput(); + case 2: FastPin<1>::setOutput(); + case 1: FastPin<0>::setOutput(); + } + } + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + typedef union { + uint8_t bytes[12]; + uint16_t shorts[6]; + uint32_t raw[3]; + } Lines; + + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + FASTLED_REGISTER Lines b2; + if(USED_LANES>8) { + transpose8<1,2>(b.bytes,b2.bytes); + transpose8<1,2>(b.bytes+8,b2.bytes+1); + } else { + transpose8x1(b.bytes,b2.bytes); + } + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_REGISTER uint8_t scale = pixels.template getscale(pixels); + + for(FASTLED_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; + + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + if(USED_LANES>8) { + *FastPin::cport() = ((~b2.shorts[i]) & PORT_MASK); + } else { + *FastPin::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK); + } + + while((next_mark - ARM_DWT_CYCCNT) > (T3)); + *FastPin::cport() = PORT_MASK; + + b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale(pixels,i+(USED_LANES/2),d,scale); + } + + // if folks use an odd numnber of lanes, get the last byte's value here + if(USED_LANES & 0x01) { + b.bytes[USED_LANES-1] = pixels.template loadAndScale(pixels,USED_LANES-1,d,scale); + } + + for(FASTLED_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; + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); + if(USED_LANES>8) { + *FastPin::cport() = ((~b2.shorts[i]) & PORT_MASK); + } else { + // b2.bytes[0] = 0; + *FastPin::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK); + } + + while((next_mark - ARM_DWT_CYCCNT) > (T3)); + *FastPin::cport() = PORT_MASK; + + } + } + + + + // 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 &allpixels) { + // Get access to the clock + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + ARM_DWT_CYCCNT = 0; + + // Setup the pixel controller and load/scale the first byte + allpixels.preStepFirstByteDithering(); + FASTLED_REGISTER Lines b0; + + allpixels.preStepFirstByteDithering(); + for(int i = 0; i < USED_LANES; ++i) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + + cli(); + uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + + while(allpixels.has(1)) { + #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-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; } + } + #endif + allpixels.stepDithering(); + + // 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 + }; + + return ARM_DWT_CYCCNT; + } +}; + +#define PMASK ((1<<(LANES))-1) +#define PMASK_HI (PMASK>>8 & 0xFF) +#define PMASK_LO (PMASK & 0xFF) + +template +class SixteenWayInlineBlockClocklessController : 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() { + static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!"); + // FastPin<30>::setOutput(); + // FastPin<29>::setOutput(); + // FastPin<27>::setOutput(); + // FastPin<28>::setOutput(); + switch(LANES) { + case 16: FastPin<12>::setOutput(); + case 15: FastPin<11>::setOutput(); + case 14: FastPin<13>::setOutput(); + case 13: FastPin<10>::setOutput(); + case 12: FastPin<9>::setOutput(); + case 11: FastPin<23>::setOutput(); + case 10: FastPin<22>::setOutput(); + case 9: FastPin<15>::setOutput(); + + case 8: FastPin<5>::setOutput(); + case 7: FastPin<21>::setOutput(); + case 6: FastPin<20>::setOutput(); + case 5: FastPin<6>::setOutput(); + case 4: FastPin<8>::setOutput(); + case 3: FastPin<7>::setOutput(); + case 2: FastPin<14>::setOutput(); + case 1: FastPin<2>::setOutput(); + } + } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + uint32_t clocks = showRGBInternal(pixels); + #if FASTLED_ALLOW_INTERRUPTS == 0 + // Adjust the timer + long microsTaken = CLKS_TO_MICROS(clocks); + MS_COUNTER += (1 + (microsTaken / 1000)); + #endif + + mWait.mark(); + } + + typedef union { + uint8_t bytes[16]; + uint16_t shorts[8]; + uint32_t raw[4]; + } Lines; + + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + FASTLED_REGISTER Lines b2; + transpose8x1(b.bytes,b2.bytes); + transpose8x1(b.bytes+8,b2.bytes+8); + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_REGISTER uint8_t scale = pixels.template getscale(pixels); + + for(FASTLED_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; + *FastPin::sport() = PMASK_HI; + + while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6)); + *FastPin::cport() = ((~b2.bytes[7-i]) & PMASK_LO); + *FastPin::cport() = ((~b2.bytes[15-i]) & PMASK_HI); + + while((next_mark - ARM_DWT_CYCCNT) > (T3)); + *FastPin::cport() = PMASK_LO; + *FastPin::cport() = PMASK_HI; + + b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + if(LANES==16 || (LANES>8 && ((i+8) < LANES))) { + b.bytes[i+8] = pixels.template loadAndScale(pixels,i+8,d,scale); + } + } + } + + + // 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 &allpixels) { + // Get access to the clock + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + ARM_DWT_CYCCNT = 0; + + // Setup the pixel controller and load/scale the first byte + allpixels.preStepFirstByteDithering(); + FASTLED_REGISTER Lines b0; + + allpixels.preStepFirstByteDithering(); + for(int i = 0; i < LANES; ++i) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + + cli(); + uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); + + while(allpixels.has(1)) { + allpixels.stepDithering(); + #if 0 && (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 ARM_DWT_CYCCNT; } + } + #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 0 && (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + sei(); + + return ARM_DWT_CYCCNT; + } +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif diff --git a/lib/FastLED/src/platforms/arm/k66/fastled_arm_k66.h b/lib/FastLED/src/platforms/arm/k66/fastled_arm_k66.h new file mode 100644 index 00000000..2113e10e --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k66/fastled_arm_k66.h @@ -0,0 +1,14 @@ +#ifndef __INC_FASTLED_ARM_K66_H +#define __INC_FASTLED_ARM_K66_H + +// Include the k66 headers +#include "fastpin_arm_k66.h" +#include "fastspi_arm_k66.h" +#include "../k20/octows2811_controller.h" +#include "../k20/ws2812serial_controller.h" +#include "../k20/smartmatrix_t3.h" +#include "clockless_arm_k66.h" +#include "clockless_block_arm_k66.h" + +#endif + diff --git a/lib/FastLED/src/platforms/arm/k66/fastpin_arm_k66.h b/lib/FastLED/src/platforms/arm/k66/fastpin_arm_k66.h new file mode 100644 index 00000000..4d017784 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k66/fastpin_arm_k66.h @@ -0,0 +1,128 @@ +#ifndef __FASTPIN_ARM_K66_H +#define __FASTPIN_ARM_K66_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 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 +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)) { _PSOR::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC +/// does a poor job of optimizing around these accesses so they are not being used just yet. +template class _ARMPIN_BITBAND { +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)) { *_PDOR::template rx<_BIT>() = 1; } + inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return 1; } + inline static port_t loval() __attribute__ ((always_inline)) { return 0; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); } + inline static port_t mask() __attribute__ ((always_inline)) { return 1; } +}; + +// Macros for k20 pin access/definition +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (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; } \ + template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; +#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 _FL_DEFPIN(PIN, BIT, L) template<> 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) + +#define MAX_PIN 63 +_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); + + + +#define SPI_DATA 11 +#define SPI_CLOCK 13 + +#define SPI2_DATA 7 +#define SPI2_CLOCK 14 + +#define FASTLED_TEENSY3 +#define ARM_HARDWARE_SPI +#define HAS_HARDWARE_PIN_SUPPORT +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_ARM_K66 diff --git a/lib/FastLED/src/platforms/arm/k66/fastspi_arm_k66.h b/lib/FastLED/src/platforms/arm/k66/fastspi_arm_k66.h new file mode 100644 index 00000000..55d5a64e --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k66/fastspi_arm_k66.h @@ -0,0 +1,470 @@ +#ifndef __INC_FASTSPI_ARM_H +#define __INC_FASTSPI_ARM_H + +// +// copied from k20 code +// changed SPI1 define to KINETISK_SPI1 +// TODO: add third alternative MOSI pin (28) and CLOCK pin (27) +// TODO: add alternative pins for SPI1 +// TODO: add SPI2 output +// + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY) + +// Version 1.20 renamed SPI_t to KINETISK_SPI_t +#if TEENSYDUINO >= 120 +#define SPI_t KINETISK_SPI_t +#endif + +#ifndef KINETISK_SPI0 +#define KINETISK_SPI0 SPI0 +#endif + +#ifndef SPI_PUSHR_CONT +#define SPI_PUSHR_CONT SPIX.PUSHR_CONT +#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X) +#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ +#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT +#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X) +#endif + +// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now, +// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit +// set (the zero-th bit). Unclear if I will want this to change at some point. +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; } +}; + +#define MAX(A, B) (( (A) > (B) ) ? (A) : (B)) + +#define USE_CONT 0 +// intra-frame backup data +struct SPIState { + uint32_t _ctar0,_ctar1; + uint32_t pins[4]; +}; + +// extern SPIState gState; + + +// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world. +template void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) { + switch(VAL) { + // Handle the dbl clock cases + case 0: case 1: + case 2: preScalar = 0; scalar = 0; dbl = 1; break; + case 3: preScalar = 1; scalar = 0; dbl = 1; break; + case 5: preScalar = 2; scalar = 0; dbl = 1; break; + case 7: preScalar = 3; scalar = 0; dbl = 1; break; + + // Handle the scalar value 6 cases (since it's not a power of two, it won't get caught + // below) + case 9: preScalar = 1; scalar = 2; dbl = 1; break; + case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break; + + case 15: preScalar = 2; scalar = 2; dbl = 1; break; + case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break; + + case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break; + case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break; + default: { + int p2 = BitWork::highestBit(); + int p3 = BitWork::highestBit(); + int p5 = BitWork::highestBit(); + int p7 = BitWork::highestBit(); + + int w2 = 2 * (1 << p2); + int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0; + int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0; + int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0; + + int maxval = MAX(MAX(w2, w3), MAX(w5, w7)); + + if(w2 == maxval) { preScalar = 0; scalar = p2; } + else if(w3 == maxval) { preScalar = 1; scalar = p3; } + else if(w5 == maxval) { preScalar = 2; scalar = p5; } + else if(w7 == maxval) { preScalar = 3; scalar = p7; } + + dbl = 0; + if(scalar == 0) { dbl = 1; } + else if(scalar < 3) { --scalar; } + } + } + return; +} + +#define SPIX (*(SPI_t*)pSPIX) + +template +class ARMHardwareSPIOutput { + Selectable *m_pSelect; + SPIState gState; + + // Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa), + // and likewise enabling pin 14 disables pin 13 (and vice versa) + inline void enable_pins(void) __attribute__((always_inline)) { + //serial_print("enable_pins\n"); + switch(_DATA_PIN) { + case 7: + CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + case 11: + CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + } + + switch(_CLOCK_PIN) { + case 13: + CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + case 14: + CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); + CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + break; + } + } + + // Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using + inline void disable_pins(void) __attribute__((always_inline)) { + switch(_DATA_PIN) { + case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break; + case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break; + } + + switch(_CLOCK_PIN) { + case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break; + case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break; + } + } + + static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) { + if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return; + uint32_t mcr = SPIX.MCR; + if(mcr & SPI_MCR_MDIS) { + SPIX.CTAR0 = ctar0; + SPIX.CTAR1 = ctar1; + } else { + SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT; + SPIX.CTAR0 = ctar0; + SPIX.CTAR1 = ctar1; + SPIX.MCR = mcr; + } + } + + static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) { + if (SPIX.CTAR0 == ctar) return; + uint32_t mcr = SPIX.MCR; + if (mcr & SPI_MCR_MDIS) { + SPIX.CTAR0 = ctar; + } else { + SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT; + SPIX.CTAR0 = ctar; + + SPIX.MCR = mcr; + } + } + + static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) { + if (SPIX.CTAR1 == ctar) return; + uint32_t mcr = SPIX.MCR; + if (mcr & SPI_MCR_MDIS) { + SPIX.CTAR1 = ctar; + } else { + SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT; + SPIX.CTAR1 = ctar; + SPIX.MCR = mcr; + + } + } + + void setSPIRate() { + // Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits + uint32_t _PBR = 0; + uint32_t _BR = 0; + uint32_t _CSSCK = 0; + uint32_t _DBR = 0; + + // if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256 + // else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128 + // else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64 + // else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32 + // else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16 + // else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8 + // else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7 + // else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5 + // else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4 + // else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3 + // else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2 + + getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR); + _CSSCK = _BR; + + uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK); + uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK); + + #if USE_CONT == 1 + ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL; + ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL; + #endif + + if(_DBR) { + ctar0 |= SPI_CTAR_DBR; + ctar1 |= SPI_CTAR_DBR; + } + + update_ctars(ctar0,ctar1); + } + + void inline save_spi_state() __attribute__ ((always_inline)) { + // save ctar data + gState._ctar0 = SPIX.CTAR0; + gState._ctar1 = SPIX.CTAR1; + + // save data for the not-us pins + gState.pins[0] = CORE_PIN7_CONFIG; + gState.pins[1] = CORE_PIN11_CONFIG; + gState.pins[2] = CORE_PIN13_CONFIG; + gState.pins[3] = CORE_PIN14_CONFIG; + } + + void inline restore_spi_state() __attribute__ ((always_inline)) { + // restore ctar data + update_ctars(gState._ctar0,gState._ctar1); + + // restore data for the not-us pins (not necessary because disable_pins will do this) + // CORE_PIN7_CONFIG = gState.pins[0]; + // CORE_PIN11_CONFIG = gState.pins[1]; + // CORE_PIN13_CONFIG = gState.pins[2]; + // CORE_PIN14_CONFIG = gState.pins[3]; + } + +public: + ARMHardwareSPIOutput() { m_pSelect = NULL; } + ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + + void init() { + // set the pins to output + FastPin<_DATA_PIN>::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); + + // Enable SPI0 clock + uint32_t sim6 = SIM_SCGC6; + if((SPI_t*)pSPIX == &KINETISK_SPI0) { + if (!(sim6 & SIM_SCGC6_SPI0)) { + //serial_print("init1\n"); + SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0; + SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1); + } + } else if((SPI_t*)pSPIX == &KINETISK_SPI1) { + if (!(sim6 & SIM_SCGC6_SPI1)) { + //serial_print("init1\n"); + SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1; + SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1); + } + } + + // Configure SPI as the master and enable + SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE); + SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT); + + // pin/spi configuration happens on select + } + + static void waitFully() __attribute__((always_inline)) { + // Wait for the last byte to get shifted into the register + bool empty = false; + + do { + cli(); + if ((SPIX.SR & 0xF000) > 0) { + // reset the TCF flag + SPIX.SR |= SPI_SR_TCF; + } else { + empty = true; + } + sei(); + } while (!empty); + + // wait for the TCF flag to get set + while (!(SPIX.SR & SPI_SR_TCF)); + SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF); + } + + static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); } + static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); } + static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); } + + enum ECont { CONT, NOCONT }; + enum EWait { PRE, POST, NONE }; + enum ELast { NOTLAST, LAST }; + + #if USE_CONT == 1 + #define CM CONT + #else + #define CM NOCONT + #endif + #define WM PRE + + template class Write { + public: + 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); + SPIX.SR |= SPI_SR_TCF; + if(WAIT_STATE == POST) { wait(); } + } + + 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); + SPIX.SR |= SPI_SR_TCF; + if(WAIT_STATE == POST) { wait(); } + } + }; + + static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;} + static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;} + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;} + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;} + + static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;} + static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;} + + static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;} + static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;wait(); } + static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;} + + // not the most efficient mechanism in the world - but should be enough for sm16716 and friends + template inline static void writeBit(uint8_t b) { + uint32_t ctar1_save = SPIX.CTAR1; + + // Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit + uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0); + update_ctar1(ctar1); + + writeWord( (b & (1 << BIT)) != 0); + + update_ctar1(ctar1_save); + } + + void inline select() __attribute__((always_inline)) { + save_spi_state(); + if(m_pSelect != NULL) { m_pSelect->select(); } + setSPIRate(); + enable_pins(); + } + + void inline release() __attribute__((always_inline)) { + disable_pins(); + if(m_pSelect != NULL) { m_pSelect->release(); } + restore_spi_state(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { Write::writeByte(value); } + } + + void writeBytesValue(uint8_t value, int len) { + select(); + while(len--) { + writeByte(value); + } + waitFully(); + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_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(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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; + + // Setup the pixel controller + if((FLAGS & FLAG_START_BIT) == 0) { + //If no start bit stupiditiy, write out as many 16-bit blocks as we can + while(pixels.has(2)) { + // Load and write out the first two bytes + if(WM == NONE) { wait1(); } + Write::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1())); + + // Load and write out the next two bytes (step dithering, advance data in between since we + // cross pixels here) + Write::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0())); + + // Load and write out the next two bytes + Write::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2())); + pixels.stepDithering(); + pixels.advanceData(); + } + + if(pixels.has(1)) { + if(WM == NONE) { wait1(); } + // write out the rest as alternating 16/8-bit blocks (likely to be just one) + Write::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1())); + Write::writeByte(D::adjust(pixels.loadAndScale2())); + } + + D::postBlock(len); + waitFully(); + } else if(FLAGS & FLAG_START_BIT) { + uint32_t ctar1_save = SPIX.CTAR1; + + // Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit + uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8); + update_ctar1(ctar1); + + while(pixels.has(1)) { + writeWord( 0x100 | D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + waitFully(); + + // restore ctar1 + update_ctar1(ctar1_save); + } + release(); + } +}; +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/arm/k66/led_sysdefs_arm_k66.h b/lib/FastLED/src/platforms/arm/k66/led_sysdefs_arm_k66.h new file mode 100644 index 00000000..0b0c701c --- /dev/null +++ b/lib/FastLED/src/platforms/arm/k66/led_sysdefs_arm_k66.h @@ -0,0 +1,46 @@ +#ifndef __INC_LED_SYSDEFS_ARM_K66_H +#define __INC_LED_SYSDEFS_ARM_K66_H + +#define FASTLED_TEENSY3 +#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 == 192000000) +#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 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) */ +#endif + +extern volatile uint32_t systick_millis_count; +# define MS_COUNTER systick_millis_count + + +// Default to using PROGMEM, since TEENSY3 provides it +// even though all it does is ignore it. Just being +// conservative here in case TEENSY3 changes. +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + + +#endif diff --git a/lib/FastLED/src/platforms/arm/kl26/clockless_arm_kl26.h b/lib/FastLED/src/platforms/arm/kl26/clockless_arm_kl26.h new file mode 100644 index 00000000..29a61fba --- /dev/null +++ b/lib/FastLED/src/platforms/arm/kl26/clockless_arm_kl26.h @@ -0,0 +1,65 @@ +#ifndef __INC_CLOCKLESS_ARM_KL26 +#define __INC_CLOCKLESS_ARM_KL26 + +#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(); + uint32_t clocks = showRGBInternal(pixels); + if(!clocks) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + clocks = showRGBInternal(pixels); + } + long microsTaken = CLKS_TO_MICROS(clocks * ((T1 + T2 + T3) * 24)); + MS_COUNTER += (microsTaken / 1000); + 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<4,8,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin::mask(), pixels.mData, pixels.mLen, &data); + // return 0; // 0x00FFFFFF - _VAL; + } + + +}; + +FASTLED_NAMESPACE_END + + +#endif // __INC_CLOCKLESS_ARM_KL26 diff --git a/lib/FastLED/src/platforms/arm/kl26/fastled_arm_kl26.h b/lib/FastLED/src/platforms/arm/kl26/fastled_arm_kl26.h new file mode 100644 index 00000000..a0ef0ff7 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/kl26/fastled_arm_kl26.h @@ -0,0 +1,10 @@ +#ifndef __INC_FASTLED_ARM_KL26_H +#define __INC_FASTLED_ARM_KL26_H + +// Include the k20 headers +#include "fastpin_arm_kl26.h" +#include "fastspi_arm_kl26.h" +#include "clockless_arm_kl26.h" +#include "../k20/ws2812serial_controller.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/kl26/fastpin_arm_kl26.h b/lib/FastLED/src/platforms/arm/kl26/fastpin_arm_kl26.h new file mode 100644 index 00000000..e9743c4e --- /dev/null +++ b/lib/FastLED/src/platforms/arm/kl26/fastpin_arm_kl26.h @@ -0,0 +1,88 @@ +#ifndef __FASTPIN_ARM_KL26_H +#define __FASTPIN_ARM_KL26_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 teensy LC 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; + + 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)) { _PSOR::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +// Macros for kl26 pin access/definition +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (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; } \ +template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; +#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 _FL_DEFPIN(PIN, BIT, L) template<> 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) + +#define MAX_PIN 26 +_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 +// #define SPI1 (*(SPI_t *)0x4002D000) + +#define SPI2_DATA 0 +#define SPI2_CLOCK 20 + +#define HAS_HARDWARE_PIN_SUPPORT +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_ARM_K20 diff --git a/lib/FastLED/src/platforms/arm/kl26/fastspi_arm_kl26.h b/lib/FastLED/src/platforms/arm/kl26/fastspi_arm_kl26.h new file mode 100644 index 00000000..efe18f15 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/kl26/fastspi_arm_kl26.h @@ -0,0 +1,252 @@ +#ifndef __INC_FASTSPI_ARM_KL26_H +#define __INC_FASTSPI_ARM_KL26_h + +FASTLED_NAMESPACE_BEGIN + +template void getScalars(uint8_t & sppr, uint8_t & spr) { + if(VAL > 4096) { sppr=7; spr=8; } + else if(VAL > 3584) { sppr=6; spr=8; } + else if(VAL > 3072) { sppr=5; spr=8; } + else if(VAL > 2560) { sppr=4; spr=8; } + else if(VAL > 2048) { sppr=7; spr=7; } + else if(VAL > 2048) { sppr=3; spr=8; } + else if(VAL > 1792) { sppr=6; spr=7; } + else if(VAL > 1536) { sppr=5; spr=7; } + else if(VAL > 1536) { sppr=2; spr=8; } + else if(VAL > 1280) { sppr=4; spr=7; } + else if(VAL > 1024) { sppr=7; spr=6; } + else if(VAL > 1024) { sppr=3; spr=7; } + else if(VAL > 1024) { sppr=1; spr=8; } + else if(VAL > 896) { sppr=6; spr=6; } + else if(VAL > 768) { sppr=5; spr=6; } + else if(VAL > 768) { sppr=2; spr=7; } + else if(VAL > 640) { sppr=4; spr=6; } + else if(VAL > 512) { sppr=7; spr=5; } + else if(VAL > 512) { sppr=3; spr=6; } + else if(VAL > 512) { sppr=1; spr=7; } + else if(VAL > 512) { sppr=0; spr=8; } + else if(VAL > 448) { sppr=6; spr=5; } + else if(VAL > 384) { sppr=5; spr=5; } + else if(VAL > 384) { sppr=2; spr=6; } + else if(VAL > 320) { sppr=4; spr=5; } + else if(VAL > 256) { sppr=7; spr=4; } + else if(VAL > 256) { sppr=3; spr=5; } + else if(VAL > 256) { sppr=1; spr=6; } + else if(VAL > 256) { sppr=0; spr=7; } + else if(VAL > 224) { sppr=6; spr=4; } + else if(VAL > 192) { sppr=5; spr=4; } + else if(VAL > 192) { sppr=2; spr=5; } + else if(VAL > 160) { sppr=4; spr=4; } + else if(VAL > 128) { sppr=7; spr=3; } + else if(VAL > 128) { sppr=3; spr=4; } + else if(VAL > 128) { sppr=1; spr=5; } + else if(VAL > 128) { sppr=0; spr=6; } + else if(VAL > 112) { sppr=6; spr=3; } + else if(VAL > 96) { sppr=5; spr=3; } + else if(VAL > 96) { sppr=2; spr=4; } + else if(VAL > 80) { sppr=4; spr=3; } + else if(VAL > 64) { sppr=7; spr=2; } + else if(VAL > 64) { sppr=3; spr=3; } + else if(VAL > 64) { sppr=1; spr=4; } + else if(VAL > 64) { sppr=0; spr=5; } + else if(VAL > 56) { sppr=6; spr=2; } + else if(VAL > 48) { sppr=5; spr=2; } + else if(VAL > 48) { sppr=2; spr=3; } + else if(VAL > 40) { sppr=4; spr=2; } + else if(VAL > 32) { sppr=7; spr=1; } + else if(VAL > 32) { sppr=3; spr=2; } + else if(VAL > 32) { sppr=1; spr=3; } + else if(VAL > 32) { sppr=0; spr=4; } + else if(VAL > 28) { sppr=6; spr=1; } + else if(VAL > 24) { sppr=5; spr=1; } + else if(VAL > 24) { sppr=2; spr=2; } + else if(VAL > 20) { sppr=4; spr=1; } + else if(VAL > 16) { sppr=7; spr=0; } + else if(VAL > 16) { sppr=3; spr=1; } + else if(VAL > 16) { sppr=1; spr=2; } + else if(VAL > 16) { sppr=0; spr=3; } + else if(VAL > 14) { sppr=6; spr=0; } + else if(VAL > 12) { sppr=5; spr=0; } + else if(VAL > 12) { sppr=2; spr=1; } + else if(VAL > 10) { sppr=4; spr=0; } + else if(VAL > 8) { sppr=3; spr=0; } + else if(VAL > 8) { sppr=1; spr=1; } + else if(VAL > 8) { sppr=0; spr=2; } + else if(VAL > 6) { sppr=2; spr=0; } + else if(VAL > 4) { sppr=1; spr=0; } + else if(VAL > 4) { sppr=0; spr=1; } + else /* if(VAL > 2) */ { sppr=0; spr=0; } +} + + +#define SPIX (*(KINETISL_SPI_t*)pSPIX) +#define ARM_HARDWARE_SPI + +template +class ARMHardwareSPIOutput { + Selectable *m_pSelect; + + static inline void enable_pins(void) __attribute__((always_inline)) { + switch(_DATA_PIN) { + case 0: CORE_PIN0_CONFIG = PORT_PCR_MUX(2); break; + case 1: CORE_PIN1_CONFIG = PORT_PCR_MUX(5); break; + case 7: CORE_PIN7_CONFIG = PORT_PCR_MUX(2); break; + case 8: CORE_PIN8_CONFIG = PORT_PCR_MUX(5); break; + case 11: CORE_PIN11_CONFIG = PORT_PCR_MUX(2); break; + case 12: CORE_PIN12_CONFIG = PORT_PCR_MUX(5); break; + case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); break; + } + + switch(_CLOCK_PIN) { + case 13: CORE_PIN13_CONFIG = PORT_PCR_MUX(2); break; + case 14: CORE_PIN14_CONFIG = PORT_PCR_MUX(2); break; + case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); break; + } + } + + static inline void disable_pins(void) __attribute((always_inline)) { + switch(_DATA_PIN) { + case 0: CORE_PIN0_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 1: CORE_PIN1_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 8: CORE_PIN8_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 12: CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 21: CORE_PIN21_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + } + + switch(_CLOCK_PIN) { + case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + case 20: CORE_PIN20_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break; + } + } + + void setSPIRate() { + uint8_t sppr, spr; + getScalars<_SPI_CLOCK_DIVIDER>(sppr, spr); + + // Set the speed + SPIX.BR = SPI_BR_SPPR(sppr) | SPI_BR_SPR(spr); + + // Also, force 8 bit transfers (don't want to juggle 8/16 since that flushes the world) + SPIX.C2 = 0; + SPIX.C1 |= SPI_C1_SPE; + } + +public: + ARMHardwareSPIOutput() { m_pSelect = NULL; } + ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + + // set the object representing the selectable + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + // initialize the SPI subssytem + void init() { + FastPin<_DATA_PIN>::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); + + // Enable the SPI clocks + uint32_t sim4 = SIM_SCGC4; + if ((pSPIX == 0x40076000) && !(sim4 & SIM_SCGC4_SPI0)) { + SIM_SCGC4 = sim4 | SIM_SCGC4_SPI0; + } + + if ( (pSPIX == 0x40077000) && !(sim4 & SIM_SCGC4_SPI1)) { + SIM_SCGC4 = sim4 | SIM_SCGC4_SPI1; + } + + SPIX.C1 = SPI_C1_MSTR | SPI_C1_SPE; + SPIX.C2 = 0; + SPIX.BR = SPI_BR_SPPR(1) | SPI_BR_SPR(0); + } + + // latch the CS select + void inline select() __attribute__((always_inline)) { + if(m_pSelect != NULL) { m_pSelect->select(); } + setSPIRate(); + enable_pins(); + } + + + // release the CS select + void inline release() __attribute__((always_inline)) { + disable_pins(); + if(m_pSelect != NULL) { m_pSelect->release(); } + } + + // Wait for the world to be clear + static void wait() __attribute__((always_inline)) { while(!(SPIX.S & SPI_S_SPTEF)); } + + // wait until all queued up data has been written + void waitFully() { wait(); } + + // not the most efficient mechanism in the world - but should be enough for sm16716 and friends + template inline static void writeBit(uint8_t b) { /* TODO */ } + + // write a byte out via SPI (returns immediately on writing register) + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.DL = b; } + // 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) { + setSPIRate(); + 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(FASTLED_REGISTER uint8_t *data, int len) { + setSPIRate(); + 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(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + + template void writePixels(PixelController pixels) { + int len = pixels.mLen; + + select(); + 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())); + } else { + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + } + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + release(); + } + +}; + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/arm/kl26/led_sysdefs_arm_kl26.h b/lib/FastLED/src/platforms/arm/kl26/led_sysdefs_arm_kl26.h new file mode 100644 index 00000000..575e6399 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/kl26/led_sysdefs_arm_kl26.h @@ -0,0 +1,47 @@ +#ifndef __INC_LED_SYSDEFS_ARM_KL26_H +#define __INC_LED_SYSDEFS_ARM_KL26_H + +#define FASTLED_TEENSYLC +#define FASTLED_ARM +#define FASTLED_ARM_M0_PLUS + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 1 +#endif + +#define FASTLED_SPI_BYTE_ONLY + +// 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 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) */ +#endif + +extern volatile uint32_t systick_millis_count; +# define MS_COUNTER systick_millis_count + +// Default to using PROGMEM since TEENSYLC provides it +// even though all it does is ignore it. Just being +// conservative here in case TEENSYLC changes. +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + +#endif diff --git a/lib/FastLED/src/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h b/lib/FastLED/src/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h new file mode 100644 index 00000000..1dbca06e --- /dev/null +++ b/lib/FastLED/src/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h @@ -0,0 +1,214 @@ +#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]; + uint32_t MS_COUNTER; + 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; + MS_COUNTER = 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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_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]); + + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_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/lib/FastLED/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h b/lib/FastLED/src/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h new file mode 100644 index 00000000..bb3d9e1a --- /dev/null +++ b/lib/FastLED/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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER uint32_t & b) { + for(FASTLED_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[2]); + FastPin::lo(); + } else { + while((next_mark - ARM_DWT_CYCCNT) > off[1]); + FastPin::lo(); + } + b <<= 1; + } + + 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[2]); + FastPin::lo(); + } else { + while((next_mark - ARM_DWT_CYCCNT) > off[1]); + 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(); + FASTLED_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)*1000); + + 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/lib/FastLED/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h b/lib/FastLED/src/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h new file mode 100644 index 00000000..0cd53602 --- /dev/null +++ b/lib/FastLED/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 "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/lib/FastLED/src/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h b/lib/FastLED/src/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h new file mode 100644 index 00000000..cd32ba40 --- /dev/null +++ b/lib/FastLED/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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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/lib/FastLED/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h b/lib/FastLED/src/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h new file mode 100644 index 00000000..796793f5 --- /dev/null +++ b/lib/FastLED/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 Teensy4HardwareSPIOutput { + 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: + Teensy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;} + Teensy4HardwareSPIOutput(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(FASTLED_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(FASTLED_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/lib/FastLED/src/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h b/lib/FastLED/src/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h new file mode 100644 index 00000000..ac490825 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/mxrt1062/octows2811_controller.h b/lib/FastLED/src/platforms/arm/mxrt1062/octows2811_controller.h new file mode 100644 index 00000000..a7c2f3e4 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/mxrt1062/octows2811_controller.h @@ -0,0 +1,64 @@ +#ifndef __INC_OCTOWS2811_CONTROLLER_H +#define __INC_OCTOWS2811_CONTROLLER_H + +#ifdef USE_OCTOWS2811 + +// #include "OctoWS2811.h" + +FASTLED_NAMESPACE_BEGIN + +template +class COctoWS2811Controller : public CPixelLEDController { + OctoWS2811 *pocto; + uint8_t *drawbuffer,*framebuffer; + + void _init(int nLeds) { + if(pocto == NULL) { + drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3); + framebuffer = (uint8_t*)malloc(nLeds * 8 * 3); + + // byte ordering is handled in show by the pixel controller + int config = WS2811_RGB; + config |= CHIP; + + pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config); + + pocto->begin(); + } + } +public: + COctoWS2811Controller() { pocto = NULL; } + virtual int size() { return CLEDController::size() * 8; } + + virtual void init() { /* do nothing yet */ } + + virtual void showPixels(PixelController &pixels) { + uint32_t size = pixels.size(); + uint32_t sizeTimes8 = 8U * size; + _init(size); + + uint32_t index = 0; + while (pixels.has(1)) { + for (int lane = 0; lane < 8; lane++) { + uint8_t r = pixels.loadAndScale0(lane); + uint8_t g = pixels.loadAndScale1(lane); + uint8_t b = pixels.loadAndScale2(lane); + pocto->setPixel(index, r, g, b); + index += size; + } + index -= sizeTimes8; + index++; + pixels.stepDithering(); + pixels.advanceData(); + } + + pocto->show(); + } + +}; + +FASTLED_NAMESPACE_END + +#endif + +#endif diff --git a/lib/FastLED/src/platforms/arm/nrf51/clockless_arm_nrf51.h b/lib/FastLED/src/platforms/arm/nrf51/clockless_arm_nrf51.h new file mode 100644 index 00000000..c607e61e --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf51/fastled_arm_nrf51.h b/lib/FastLED/src/platforms/arm/nrf51/fastled_arm_nrf51.h new file mode 100644 index 00000000..88344a35 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/nrf51/fastled_arm_nrf51.h @@ -0,0 +1,9 @@ +#ifndef __INC_FASTLED_ARM_NRF51_H +#define __INC_FASTLED_ARM_NRF51_H + +// Include the k20 headers +#include "fastpin_arm_nrf51.h" +#include "fastspi_arm_nrf51.h" +#include "clockless_arm_nrf51.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/nrf51/fastpin_arm_nrf51.h b/lib/FastLED/src/platforms/arm/nrf51/fastpin_arm_nrf51.h new file mode 100644 index 00000000..112f1967 --- /dev/null +++ b/lib/FastLED/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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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/lib/FastLED/src/platforms/arm/nrf51/fastspi_arm_nrf51.h b/lib/FastLED/src/platforms/arm/nrf51/fastspi_arm_nrf51.h new file mode 100644 index 00000000..6826ebcb --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h b/lib/FastLED/src/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h new file mode 100644 index 00000000..b63dfd32 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/nrf51/led_sysdefs_arm_nrf51.h @@ -0,0 +1,46 @@ +#ifndef __LED_SYSDEFS_ARM_NRF51 +#define __LED_SYSDEFS_ARM_NRF51 + +#ifndef NRF51 +#define NRF51 +#endif + +#define LED_TIMER NRF_TIMER1 +#define FASTLED_NO_PINMAP +#define FASTLED_HAS_CLOCKLESS + +#define FASTLED_SPI_BYTE_ONLY + +#define FASTLED_ARM +#define FASTLED_ARM_M0 + +#ifndef F_CPU +#define F_CPU 16000000 +#endif + +#include +#include +#include + +typedef volatile uint32_t RoReg; +typedef volatile uint32_t RwReg; +typedef uint32_t prog_uint32_t; +typedef uint8_t boolean; + +#define PROGMEM +#define NO_PROGMEM +#define NEED_CXX_BITS + +// 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 +#endif + +#define cli() __disable_irq(); +#define sei() __enable_irq(); + +#endif diff --git a/lib/FastLED/src/platforms/arm/nrf52/arbiter_nrf52.h b/lib/FastLED/src/platforms/arm/nrf52/arbiter_nrf52.h new file mode 100644 index 00000000..8972d2d2 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf52/clockless_arm_nrf52.h b/lib/FastLED/src/platforms/arm/nrf52/clockless_arm_nrf52.h new file mode 100644 index 00000000..1dd3cd94 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf52/fastled_arm_nrf52.h b/lib/FastLED/src/platforms/arm/nrf52/fastled_arm_nrf52.h new file mode 100644 index 00000000..45300306 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf52/fastpin_arm_nrf52.h b/lib/FastLED/src/platforms/arm/nrf52/fastpin_arm_nrf52.h new file mode 100644 index 00000000..f7346a17 --- /dev/null +++ b/lib/FastLED/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 (FASTLED_REGISTER port_ptr_t port) { hi(); } // sets _MASK in the SET OUTPUT register (output set high) + FASTLED_NRF52_INLINE_ATTRIBUTE static void lo (FASTLED_REGISTER port_ptr_t port) { lo(); } // sets _MASK in the CLEAR OUTPUT register (output set low) + FASTLED_NRF52_INLINE_ATTRIBUTE static void set(FASTLED_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(FASTLED_REGISTER port_ptr_t port, FASTLED_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/lib/FastLED/src/platforms/arm/nrf52/fastpin_arm_nrf52_variants.h b/lib/FastLED/src/platforms/arm/nrf52/fastpin_arm_nrf52_variants.h new file mode 100644 index 00000000..9020655c --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf52/fastspi_arm_nrf52.h b/lib/FastLED/src/platforms/arm/nrf52/fastspi_arm_nrf52.h new file mode 100644 index 00000000..89d006e3 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/nrf52/led_sysdefs_arm_nrf52.h b/lib/FastLED/src/platforms/arm/nrf52/led_sysdefs_arm_nrf52.h new file mode 100644 index 00000000..3a7ea582 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/rp2040/clockless_arm_rp2040.h b/lib/FastLED/src/platforms/arm/rp2040/clockless_arm_rp2040.h new file mode 100644 index 00000000..44e70f85 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/rp2040/clockless_arm_rp2040.h @@ -0,0 +1,323 @@ +#ifndef __INC_CLOCKLESS_ARM_RP2040 +#define __INC_CLOCKLESS_ARM_RP2040 + +#include "hardware/structs/sio.h" +#include "../common/m0clockless.h" + +#if FASTLED_RP2040_CLOCKLESS_PIO +#include "hardware/clocks.h" +#include "hardware/dma.h" +// compiler throws a warning about comparison that is always true +// silence that so users don't see it +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" +#include "hardware/pio.h" +#pragma GCC diagnostic pop + +#include "pio_gen.h" +#endif + +/* + * This clockless implementation uses RP2040's PIO feature to perform + * non-blocking transfers to LEDs with very little memory overhead. + * (allocates one buffer of equal size to the data to be sent) + * + * The SDK-provided claims system is used so that resources can used without + * interfering with other code that behaves well and uses claims. + * + * Resource usage is 4 instructions of program memory on the first PIO instance + * with an unclaimed state machine, said unclaimed PIO state machine, and one + * DMA channel per instance of ClocklessController. + * Additionally, one interrupt handler for DMA_IRQ_0 (configurable as shared or + * exclusive via FASTLED_RP2040_CLOCKLESS_IRQ_SHARED) is used regardless of how + * many instances are created. + * + * The DMA handler is likely the only significant risk in terms of conflicts, + * and users can adapt other code to use DMA_IRQ_1 and/or adopt shared handlers + * to avoid this becoming an issue. + */ + +FASTLED_NAMESPACE_BEGIN +#define FASTLED_HAS_CLOCKLESS 1 + +#if FASTLED_RP2040_CLOCKLESS_PIO +static CMinWait<0> *dma_chan_waits[NUM_DMA_CHANNELS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static inline void __isr clockless_dma_complete_handler() { + for (unsigned int i = 0; i < NUM_DMA_CHANNELS; i++) { + // if dma triggered for this channel and it's been used (has a CMinWait) + if ((dma_hw->ints0 & (1 << i)) && dma_chan_waits[i]) { + dma_hw->ints0 = (1 << i); // clear/ack IRQ + dma_chan_waits[i]->mark(); // mark the wait + return; + } + } +} +static bool clockless_isr_installed = false; +#endif + +template +class ClocklessController : public CPixelLEDController { +#if FASTLED_RP2040_CLOCKLESS_PIO + int dma_channel = -1; + void *dma_buf = nullptr; + size_t dma_buf_size = 0; + + float pio_clock_multiplier; + int T1_mult, T2_mult, T3_mult; + + // increase wait time by time taken to send 4 words (to flush PIO TX buffer) + CMinWait mWait; + + // start a DMA transfer to the PIO state machine from addr (transfer count 32 bit words) + static void do_dma_transfer(int channel, const void *addr, uint count) { + dma_channel_set_read_addr(channel, addr, false); + dma_channel_set_trans_count(channel, count, true); + } + + // writes bits to an in-memory buffer (to DMA from) + // pico has enough memory to not really care about using a buffer for DMA + template __attribute__ ((always_inline)) inline static int writeBitsToBuf(int32_t *out_buf, unsigned int bitpos, uint8_t b) { + // not really optimised and I haven't checked output assembly, but this should take ~50 cycles worst case + // (and on average substantially fewer -- LEDs without XTRA0 should never trigger the second half of the function) + + // position of word that takes highest bits (first word used) + int wordpos_1 = bitpos >> 5; // bitpos / 32; + + // number of bits from the byte that fit into first word + int bitcnt_1 = 32 - (bitpos & 0b11111); // bitpos % 32; + // shift required to place byte within the word + int bitshift_1 = bitcnt_1 - 8; + // mask for output bits that are taken from input + // int32_t bitmask_1 = 0xFF << bitshift_1; + int32_t bitmask_1 = ((1 << BITS) - 1) << (bitshift_1 - (BITS-8)); + + out_buf[wordpos_1] = (out_buf[wordpos_1] & ~bitmask_1) | ((b << bitshift_1) & bitmask_1); + + if (bitcnt_1 >= BITS) return BITS; // fast case for entire byte fitting in word + + // number of bits from the byte to place into second word + int bitcnt_2 = 8 - bitcnt_1; + // shift required to place byte within the word + int bitshift_2 = 32 - bitcnt_2; + // mask for output bits that are taken from input + // int32_t bitmask_2 = ((1 << bitcnt_2) - 1) << bitshift_2; + int32_t bitmask_2 = ((1 << (bitcnt_2 + (BITS-8))) - 1) << (bitshift_2 - (BITS-8)); // fixed XTRA0 + + out_buf[wordpos_1 + 1] = (out_buf[wordpos_1 + 1] & ~bitmask_2) | ((b << bitshift_2) & bitmask_2); + + return BITS; + } +#else + CMinWait mWait; +#endif +public: + virtual void init() { +#if FASTLED_RP2040_CLOCKLESS_PIO + if (dma_channel != -1) return; // maybe init was called twice somehow? not sure if possible +#endif + + // start by configuring pin as output for blocking fallback + FastPin::setOutput(); + +#if FASTLED_RP2040_CLOCKLESS_PIO + // convert from input timebase to one that the PIO program can handle + int max_t = T1 > T2 ? T1 : T2; + max_t = T3 > max_t ? T3 : max_t; + + if (max_t > CLOCKLESS_PIO_MAX_TIME_PERIOD) { + pio_clock_multiplier = (float)CLOCKLESS_PIO_MAX_TIME_PERIOD / max_t; + T1_mult = pio_clock_multiplier * T1; + T2_mult = pio_clock_multiplier * T2; + T3_mult = pio_clock_multiplier * T3; + } + else { + pio_clock_multiplier = 1.f; + T1_mult = T1; + T2_mult = T2; + T3_mult = T3; + } + + PIO pio; + int sm; + int offset = -1; + + // find an unclaimed PIO state machine and upload the clockless program if possible + // there's two PIO instances, each with four state machines, so this should usually work out fine + const PIO pios[NUM_PIOS] = { pio0, pio1 }; + // iterate over PIO instances + for (unsigned int i = 0; i < NUM_PIOS; i++) { + pio = pios[i]; + sm = pio_claim_unused_sm(pio, false); // claim a state machine + if (sm == -1) continue; // skip this PIO if no unused sm + + offset = add_clockless_pio_program(pio, T1_mult, T2_mult, T3_mult); + if (offset == -1) { + pio_sm_unclaim(pio, sm); // unclaim the state machine and skip this PIO + continue; // if program couldn't be added + } + + break; // found pio and sm that work + } + if (offset == -1) return; // couldn't find good pio and sm + + + // claim an unused DMA channel (there's 12 in total,, so this should also usually work out fine) + dma_channel = dma_claim_unused_channel(false); + if (dma_channel == -1) return; // no free DMA channel + + + // setup PIO state machine + pio_gpio_init(pio, DATA_PIN); + pio_sm_set_consecutive_pindirs(pio, sm, DATA_PIN, 1, true); + + pio_sm_config c = clockless_pio_program_get_default_config(offset); + sm_config_set_set_pins(&c, DATA_PIN, 1); + sm_config_set_out_pins(&c, DATA_PIN, 1); + sm_config_set_out_shift(&c, false, true, 32); + + // uncommenting this makes the FIFO 8 words long, + // which seems like it won't actually benefit us + // sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + float div = clock_get_hz(clk_sys) / (pio_clock_multiplier * CLOCKLESS_FREQUENCY); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); + + + // setup DMA + dma_channel_config channel_config = dma_channel_get_default_config(dma_channel); + channel_config_set_dreq(&channel_config, pio_get_dreq(pio, sm, true)); + dma_channel_configure(dma_channel, + &channel_config, + &pio->txf[sm], + NULL, // address set when making transfer + 1, // count set when making transfer + false); // don't trigger now + + + // setup DMA complete interrupt handler to update mWait time after transfer + + // store a pointer to mWait of this instance to a global array for the interrupt handler + // kinda dirty hack here to cast to CMinWait<0>*, but only mark is used, which isn't affected by the template var WAIT + dma_chan_waits[dma_channel] = (CMinWait<0>*)&mWait; + + if (!clockless_isr_installed) { +#if FASTLED_RP2040_CLOCKLESS_IRQ_SHARED + irq_add_shared_handler(DMA_IRQ_0, clockless_dma_complete_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); +#else + irq_set_exclusive_handler(DMA_IRQ_0, clockless_dma_complete_handler); +#endif + irq_set_enabled(DMA_IRQ_0, true); + clockless_isr_installed = true; + } + dma_channel_set_irq0_enabled(dma_channel, true); +#endif // FASTLED_RP2040_CLOCKLESS_PIO + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + virtual void showPixels(PixelController & pixels) { +#if FASTLED_RP2040_CLOCKLESS_PIO + if (dma_channel == -1) { // setup failed, so fall back to a blocking implementation + showRGBBlocking(pixels); + return; + } + + // wait for past transfer to finish + // call when previous pixels are done will run without blocking, + // call when previous pixels are still being transmitted should block until complete + + // a potential improvement here would be to prepare data for the output before waiting, + // but that would require a smarter DMA buffer system + // (currently, the gap between LEDs is greater than 50us due to the time taken) + if (dma_channel_is_busy(dma_channel)) { + dma_channel_wait_for_finish_blocking(dma_channel); + } + mWait.wait(); + + showRGBInternal(pixels); +#else + mWait.wait(); + showRGBBlocking(pixels); + mWait.mark(); +#endif + } + +#if FASTLED_RP2040_CLOCKLESS_PIO + void showRGBInternal(PixelController pixels) { + size_t req_buf_size = (pixels.mLen * 3 * (8+XTRA0) + 31) / 32; + + // (re)allocate DMA buffer if not large enough to hold req_buf_size 32-bit words + // pico has enough memory to not really care about using a buffer for DMA + // just give up on failure + if (dma_buf_size < req_buf_size) { + if (dma_buf != nullptr) + free(dma_buf); + + dma_buf = malloc(req_buf_size * 4); + if (dma_buf == nullptr) { + dma_buf_size = 0; + return; + } + dma_buf_size = req_buf_size; + + // fill with zeroes to ensure XTRA0s are really zero without needing extra work + memset(dma_buf, 0, dma_buf_size * 4); + } + + unsigned int bitpos = 0; + + pixels.preStepFirstByteDithering(); + uint8_t b = pixels.loadAndScale0(); + + while(pixels.has(1)) { + pixels.stepDithering(); + + // Write first byte, read next byte + bitpos += writeBitsToBuf<8+XTRA0>((int32_t*)(dma_buf), bitpos, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + bitpos += writeBitsToBuf<8+XTRA0>((int32_t*)(dma_buf), bitpos, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + bitpos += writeBitsToBuf<8+XTRA0>((int32_t*)(dma_buf), bitpos, b); + b = pixels.advanceAndLoadAndScale0(); + }; + + do_dma_transfer(dma_channel, dma_buf, req_buf_size); + } +#endif // FASTLED_RP2040_CLOCKLESS_PIO + + void showRGBBlocking(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; + + typedef FastPin pin; + volatile uint32_t *portBase = &sio_hw->gpio_out; + const int portSetOff = (uint32_t)&sio_hw->gpio_set - (uint32_t)&sio_hw->gpio_out; + const int portClrOff = (uint32_t)&sio_hw->gpio_clr - (uint32_t)&sio_hw->gpio_out; + + cli(); + showLedData(portBase, pin::mask(), pixels.mData, pixels.mLen, &data); + sei(); + } + +}; + +FASTLED_NAMESPACE_END + + +#endif // __INC_CLOCKLESS_ARM_RP2040 diff --git a/lib/FastLED/src/platforms/arm/rp2040/fastled_arm_rp2040.h b/lib/FastLED/src/platforms/arm/rp2040/fastled_arm_rp2040.h new file mode 100644 index 00000000..60767e26 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/rp2040/fastled_arm_rp2040.h @@ -0,0 +1,8 @@ +#ifndef __INC_FASTLED_ARM_RP2040_H +#define __INC_FASTLED_ARM_RP2040_H + +// Include the rp2040 headers +#include "fastpin_arm_rp2040.h" +#include "clockless_arm_rp2040.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/rp2040/fastpin_arm_rp2040.h b/lib/FastLED/src/platforms/arm/rp2040/fastpin_arm_rp2040.h new file mode 100644 index 00000000..ccd1711a --- /dev/null +++ b/lib/FastLED/src/platforms/arm/rp2040/fastpin_arm_rp2040.h @@ -0,0 +1,77 @@ +#ifndef __FASTPIN_ARM_RP2040_H +#define __FASTPIN_ARM_RP2040_H + +#include "pico.h" +#include "hardware/gpio.h" +#include "hardware/structs/sio.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 + +// warning: set and fastset are not thread-safe! use with caution! +template class _RP2040PIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { gpio_set_function(PIN, GPIO_FUNC_SIO); sio_hw->gpio_oe_set = _MASK; } + inline static void setInput() { gpio_set_function(PIN, GPIO_FUNC_SIO); sio_hw->gpio_oe_clr = _MASK; } + + inline static void hi() __attribute__ ((always_inline)) { sio_hw->gpio_set = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { sio_hw->gpio_clr = _MASK; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { sio_hw->gpio_out = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { sio_hw->gpio_togl = _MASK; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return sio_hw->gpio_out | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return sio_hw->gpio_out & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &sio_hw->gpio_out; } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &sio_hw->gpio_set; } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &sio_hw->gpio_clr; } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +#define _FL_DEFPIN(PIN) template<> class FastPin : public _RP2040PIN {}; + +#define MAX_PIN 29 +_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); + +#ifdef PICO_DEFAULT_SPI_TX_PIN +#define SPI_DATA PICO_DEFAULT_SPI_TX_PIN +#else +#define SPI_DATA 19 +#endif + +#ifdef PICO_DEFAULT_SPI_SCK_PIN +#define SPI_CLOCK PICO_DEFAULT_SPI_SCK_PIN +#else +#define SPI_CLOCK 18 +#endif + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif // FASTLED_FORCE_SOFTWARE_PINS + + +FASTLED_NAMESPACE_END + +#endif // __FASTPIN_ARM_RP2040_H diff --git a/lib/FastLED/src/platforms/arm/rp2040/led_sysdefs_arm_rp2040.h b/lib/FastLED/src/platforms/arm/rp2040/led_sysdefs_arm_rp2040.h new file mode 100644 index 00000000..94e3baf8 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/rp2040/led_sysdefs_arm_rp2040.h @@ -0,0 +1,90 @@ +#ifndef __INC_LED_SYSDEFS_ARM_RP2040_H +#define __INC_LED_SYSDEFS_ARM_RP2040_H + +#include "hardware/sync.h" + +#define FASTLED_ARM +#define FASTLED_ARM_M0_PLUS + +// TODO: PORT SPI TO HW +//#define FASTLED_SPI_BYTE_ONLY +#define FASTLED_FORCE_SOFTWARE_SPI +// Force FAST_SPI_INTERRUPTS_WRITE_PINS on becuase two cores running +// simultaneously could lead to data races on GPIO. +// This could potentially be optimised by adding a mask to FastPin's set and +// fastset, but for now it's probably safe to call that out of scope. +#ifndef FAST_SPI_INTERRUPTS_WRITE_PINS +#define FAST_SPI_INTERRUPTS_WRITE_PINS 1 +#endif + +#define FASTLED_NO_PINMAP + +typedef volatile uint32_t RoReg; +typedef volatile uint32_t RwReg; + +// #define F_CPU clock_get_hz(clk_sys) // can't use runtime function call +// is the boot-time value in another var already for any platforms? +// it doesn't seem to be, so hardcode the sdk default of 125 MHz +#ifndef F_CPU +#ifdef VARIANT_MCK +#define F_CPU VARIANT_MCK +#else +#define F_CPU 125000000 +#endif +#endif + +#ifndef VARIANT_MCK +#define VARIANT_MCK F_CPU +#endif + +// 8MHz for PIO +// #define CLOCKLESS_FREQUENCY 8000000 +#define CLOCKLESS_FREQUENCY F_CPU + +// Default to allowing interrupts +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 1 +#endif + +// not sure if this is wanted? but it probably is +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + +// Default to no PROGMEM +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 0 +#endif + +// Default to non-blocking PIO-based implemnetation +#ifndef FASTLED_RP2040_CLOCKLESS_PIO +#define FASTLED_RP2040_CLOCKLESS_PIO 1 +#endif + +// Default to shared interrupt handler for clockless DMA +#ifndef FASTLED_RP2040_CLOCKLESS_IRQ_SHARED +#define FASTLED_RP2040_CLOCKLESS_IRQ_SHARED 1 +#endif + +// SPI pin defs for old SDK ver +#ifndef PICO_DEFAULT_SPI +#define PICO_DEFAULT_SPI 0 +#endif +#ifndef PICO_DEFAULT_SPI_SCK_PIN +#define PICO_DEFAULT_SPI_SCK_PIN 18 +#endif +#ifndef PICO_DEFAULT_SPI_TX_PIN +#define PICO_DEFAULT_SPI_TX_PIN 19 +#endif +#ifndef PICO_DEFAULT_SPI_RX_PIN +#define PICO_DEFAULT_SPI_RX_PIN 16 +#endif +#ifndef PICO_DEFAULT_SPI_CSN_PIN +#define PICO_DEFAULT_SPI_CSN_PIN 17 +#endif + +static uint32_t saved_interrupt_status; +#define cli() (saved_interrupt_status = save_and_disable_interrupts()) +#define sei() (restore_interrupts(saved_interrupt_status)) + +#endif // __INC_LED_SYSDEFS_ARM_RP2040_H diff --git a/lib/FastLED/src/platforms/arm/rp2040/pio_asm.h b/lib/FastLED/src/platforms/arm/rp2040/pio_asm.h new file mode 100644 index 00000000..01def8cf --- /dev/null +++ b/lib/FastLED/src/platforms/arm/rp2040/pio_asm.h @@ -0,0 +1,98 @@ +#ifndef _PIO_ASM_H +#define _PIO_ASM_H + +/* + * PIO "assembly" macro header, written by somewhatlurker + * Released to public domain/CC0 license. + * Comes with no guarantees of correctness. + */ + +typedef uint16_t pio_instr; + +#define PIO_INSTR_JMP (0b000 << 13) +#define PIO_INSTR_WAIT (0b001 << 13) +#define PIO_INSTR_IN (0b010 << 13) +#define PIO_INSTR_OUT (0b011 << 13) +#define PIO_INSTR_PUSH (0b100 << 13) +#define PIO_INSTR_PULL ((0b100 << 13) | (0b1 << 7)) +#define PIO_INSTR_MOV (0b101 << 13) +#define PIO_INSTR_IRQ (0b110 << 13) +#define PIO_INSTR_SET (0b111 << 13) + +#define PIO_DELAY(x, sideset_count) (((x) & ((1 << (5 - sideset_count)) - 1)) << 8) +#define PIO_SIDESET(x, sideset_count) (((x) & ((1 << (sideset_count)) - 1)) << (13 - sideset_count)) +#define PIO_SIDESET_ENABLE_BIT (0b1 << 12) + +#define PIO_JMP_CND_ALWAYS (0b000 << 5) +#define PIO_JMP_CND_NOT_X (0b001 << 5) +#define PIO_JMP_CND_X_DEC (0b010 << 5) +#define PIO_JMP_CND_NOT_Y (0b011 << 5) +#define PIO_JMP_CND_Y_DEC (0b100 << 5) +#define PIO_JMP_CND_X_NE_Y (0b101 << 5) +#define PIO_JMP_CND_PIN (0b110 << 5) +#define PIO_JMP_CND_NOT_OSRE (0b111 << 5) +#define PIO_JMP_ADR(x) ((x) & 0b11111) + +#define PIO_WAIT_POLARITY_1 (0b1 << 7) +#define PIO_WAIT_POLARITY_0 (0b0 << 7) +#define PIO_WAIT_SRC_GPIO (0b00 << 5) +#define PIO_WAIT_SRC_PIN (0b01 << 5) +#define PIO_WAIT_SRC_IRQ (0b10 << 5) +#define PIO_WAIT_IDX(x) ((x) & 0b11111) + +#define PIO_IN_SRC_PINS (0b000 << 5) +#define PIO_IN_SRC_X (0b001 << 5) +#define PIO_IN_SRC_Y (0b010 << 5) +#define PIO_IN_SRC_NULL (0b011 << 5) +#define PIO_IN_SRC_ISR (0b110 << 5) +#define PIO_IN_SRC_OSR (0b111 << 5) +#define PIO_IN_CNT(x) ((x) & 0b11111) + +#define PIO_OUT_DST_PINS (0b000 << 5) +#define PIO_OUT_DST_X (0b001 << 5) +#define PIO_OUT_DST_Y (0b010 << 5) +#define PIO_OUT_DST_NULL (0b011 << 5) +#define PIO_OUT_DST_PINDIRS (0b100 << 5) +#define PIO_OUT_DST_PC (0b101 << 5) +#define PIO_OUT_DST_ISR (0b110 << 5) +#define PIO_OUT_DST_EXEC (0b111 << 5) +#define PIO_OUT_CNT(x) ((x) & 0b11111) + +#define PIO_PUSH_IFFULL (0b1 << 6) +#define PIO_PUSH_BLOCK (0b1 << 5) + +#define PIO_PULL_IFEMPTY (0b1 << 6) +#define PIO_PULL_BLOCK (0b1 << 5) + +#define PIO_MOV_DST_PINS (0b000 << 5) +#define PIO_MOV_DST_X (0b001 << 5) +#define PIO_MOV_DST_Y (0b010 << 5) +#define PIO_MOV_DST_EXEC (0b100 << 5) +#define PIO_MOV_DST_PC (0b101 << 5) +#define PIO_MOV_DST_ISR (0b110 << 5) +#define PIO_MOV_DST_OSR (0b111 << 5) +#define PIO_MOV_OP_NONE (0b00 << 3) +#define PIO_MOV_OP_INVERT (0b00 << 3) +#define PIO_MOV_OP_REVERSE (0b00 << 3) +#define PIO_MOV_SRC_PINS (0b000) +#define PIO_MOV_SRC_X (0b001) +#define PIO_MOV_SRC_Y (0b010) +#define PIO_MOV_SRC_NULL (0b011) +#define PIO_MOV_SRC_STATUS (0b101) +#define PIO_MOV_SRC_ISR (0b110) +#define PIO_MOV_SRC_OSR (0b111) + +#define PIO_IRQ_CLEAR (0b1 << 6) +#define PIO_IRQ_WAIT (0b1 << 5) +#define PIO_IRQ_IDX(x) ((x) & 0b111) +#define PIO_IRQ_IDX_REL (0b1 << 4) + +#define PIO_SET_DST_PINS (0b000 << 5) +#define PIO_SET_DST_X (0b001 << 5) +#define PIO_SET_DST_Y (0b010 << 5) +#define PIO_SET_DST_PINDIRS (0b100 << 5) +#define PIO_SET_DATA(x) ((x) & 0b11111) + +#define PIO_NOP (PIO_INSTR_MOV | PIO_MOV_DST_Y | PIO_MOV_SRC_Y) + +#endif // _PIO_ASM_H diff --git a/lib/FastLED/src/platforms/arm/rp2040/pio_gen.h b/lib/FastLED/src/platforms/arm/rp2040/pio_gen.h new file mode 100644 index 00000000..98aa9fec --- /dev/null +++ b/lib/FastLED/src/platforms/arm/rp2040/pio_gen.h @@ -0,0 +1,57 @@ +#ifndef _PIO_GEN_H +#define _PIO_GEN_H + +#include "pio_asm.h" +#include "hardware/pio.h" + +/* + * This file contains code to manage the PIO program for clockless LEDs. + * + * A PIO program is "assembled" from compiler macros so that T1, T2, T3 can be + * set from other code. + * Otherwise, this is quite similar to what would be output by pioasm, with the + * additional step of adding the program to a state machine integrated. + */ + +#define CLOCKLESS_PIO_SIDESET_COUNT 0 + +#define CLOCKLESS_PIO_WRAP_TARGET 0 +#define CLOCKLESS_PIO_WRAP 3 + +// we have 4 bits to store delay in instruction encoding with one sideset bit, but we can accept up to 16 because 1 is always subtracted first +#define CLOCKLESS_PIO_MAX_TIME_PERIOD (1 << (5 - CLOCKLESS_PIO_SIDESET_COUNT)) + +static inline int add_clockless_pio_program(PIO pio, int T1, int T2, int T3) { + pio_instr clockless_pio_instr[] = { + // wrap_target + // out x, 1; read next bit to x + (pio_instr)(PIO_INSTR_OUT | PIO_OUT_DST_X | PIO_OUT_CNT(1)), + // set pins, 1 [T1 - 1]; set output high for T1 + (pio_instr)(PIO_INSTR_SET | PIO_SET_DST_PINS | PIO_SET_DATA(1) | PIO_DELAY(T1 - 1, CLOCKLESS_PIO_SIDESET_COUNT)), + // mov pins, x [T2 - 1]; set output to X for T2 + (pio_instr)(PIO_INSTR_MOV | PIO_MOV_DST_PINS | PIO_MOV_SRC_X | PIO_DELAY(T2 - 1, CLOCKLESS_PIO_SIDESET_COUNT)), + // set pins, 0 [T3 - 2] // set output low for T3 (minus two because we'll also read next bit using one instruction during this time) + (pio_instr)(PIO_INSTR_SET | PIO_SET_DST_PINS | PIO_SET_DATA(0) | PIO_DELAY(T3 - 2, CLOCKLESS_PIO_SIDESET_COUNT)), + // wrap + }; + + struct pio_program clockless_pio_program = { + .instructions = clockless_pio_instr, + .length = sizeof(clockless_pio_instr) / sizeof(clockless_pio_instr[0]), + .origin = -1, + }; + + if (!pio_can_add_program(pio, &clockless_pio_program)) + return -1; + + return (int)pio_add_program(pio, &clockless_pio_program); +} + +static inline pio_sm_config clockless_pio_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + CLOCKLESS_PIO_WRAP_TARGET, offset + CLOCKLESS_PIO_WRAP); + sm_config_set_sideset(&c, CLOCKLESS_PIO_SIDESET_COUNT, false, false); + return c; +} + +#endif // _PIO_GEN_H diff --git a/lib/FastLED/src/platforms/arm/sam/clockless_arm_sam.h b/lib/FastLED/src/platforms/arm/sam/clockless_arm_sam.h new file mode 100644 index 00000000..31151d78 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/sam/clockless_arm_sam.h @@ -0,0 +1,122 @@ +#ifndef __INC_CLOCKLESS_ARM_SAM_H +#define __INC_CLOCKLESS_ARM_SAM_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the sam family of arm chips, like that used in the due and rfduino +// See clockless.h for detailed info on how the template parameters are used. + +#if defined(__SAM3X8E__) + + +#define TADJUST 0 +#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) ) + +#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; } + +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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER uint8_t & b) { + // Make sure we don't slot into a wrapping spot, this will delay up to 12.5µs for WS2812 + // bool bShift=0; + // while(VAL < (TOTAL*10)) { bShift=true; } + // if(bShift) { next_mark = (VAL-TOTAL); }; + + for(FASTLED_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); + *port = 1; + + // how long we want to wait next depends on whether or not our bit is set to 1 or 0 + if(b&0x80) { + // we're a 1, wait until there's less than T3 clocks left + while((next_mark - DUE_TIMER_VAL) > (T3)); + } else { + // we're a 0, wait until there's less than (T2+T3+slop) clocks left in this bit + while((next_mark - DUE_TIMER_VAL) > (T2+T3+6+TADJUST+TADJUST)); + } + *port=0; + b <<= 1; + } + } + +#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) ) + // 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 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); + + FASTLED_REGISTER data_ptr_t port asm("r7") = FastPinBB::port(); FORCE_REFERENCE(port); + *port = 0; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + uint8_t b = pixels.loadAndScale0(); + + uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL)); + while(pixels.has(1)) { + pixels.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 0; + } + } + #endif + + writeBits<8+XTRA0>(next_mark, port, b); + + b = pixels.loadAndScale1(); + writeBits<8+XTRA0>(next_mark, port,b); + + b = pixels.loadAndScale2(); + writeBits<8+XTRA0>(next_mark, port,b); + + b = pixels.advanceAndLoadAndScale0(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + + TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); + return DUE_TIMER_VAL; + } +}; + +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/arm/sam/clockless_block_arm_sam.h b/lib/FastLED/src/platforms/arm/sam/clockless_block_arm_sam.h new file mode 100644 index 00000000..e8f7ea50 --- /dev/null +++ b/lib/FastLED/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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, Lines & b3, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + Lines b2; + transpose8x1(b.bytes,b2.bytes); + + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_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/lib/FastLED/src/platforms/arm/sam/fastled_arm_sam.h b/lib/FastLED/src/platforms/arm/sam/fastled_arm_sam.h new file mode 100644 index 00000000..3567bb62 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/sam/fastled_arm_sam.h @@ -0,0 +1,10 @@ +#ifndef __INC_FASTLED_ARM_SAM_H +#define __INC_FASTLED_ARM_SAM_H + +// Include the sam headers +#include "fastpin_arm_sam.h" +#include "fastspi_arm_sam.h" +#include "clockless_arm_sam.h" +#include "clockless_block_arm_sam.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/sam/fastpin_arm_sam.h b/lib/FastLED/src/platforms/arm/sam/fastpin_arm_sam.h new file mode 100644 index 00000000..b1c4ade6 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/sam/fastpin_arm_sam.h @@ -0,0 +1,137 @@ +#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 sloightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + + +/// Template definition for arduino due 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 register, set output register, clear output register, set data direction register +template class _DUEPIN { +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)) { _PSOR::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _PDOR::r() ^= _MASK; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + + +/// Template definition for DUE style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC +/// does a poor job of optimizing around these accesses so they are not being used just yet. +template class _DUEPIN_BITBAND { +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)) { *_PDOR::template rx<_BIT>() = 1; } + inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; } + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() ^= 1; } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return 1; } + inline static port_t loval() __attribute__ ((always_inline)) { return 0; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); } + inline static port_t mask() __attribute__ ((always_inline)) { return 1; } +}; + +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (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; } \ + template static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; +#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 _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _DUEPIN {}; \ + template<> class FastPinBB : public _DUEPIN_BITBAND {}; + +_FL_IO(A,0); +_FL_IO(B,1); +_FL_IO(C,2); +_FL_IO(D,3); + +#if defined(__SAM3X8E__) + +#define MAX_PIN 78 +_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 +_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 +#define ARM_HARDWARE_SPI +#define HAS_HARDWARE_PIN_SUPPORT + +#endif + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + + +#endif // __INC_FASTPIN_ARM_SAM_H diff --git a/lib/FastLED/src/platforms/arm/sam/fastspi_arm_sam.h b/lib/FastLED/src/platforms/arm/sam/fastspi_arm_sam.h new file mode 100644 index 00000000..0edbad58 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/sam/fastspi_arm_sam.h @@ -0,0 +1,163 @@ +#ifndef __INC_FASTSPI_ARM_SAM_H +#define __INC_FASTSPI_ARM_SAM_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(__SAM3X8E__) +#define m_SPI ((Spi*)SPI0) + +template +class SAMHardwareSPIOutput { + Selectable *m_pSelect; + + static inline void waitForEmpty() { while ((m_SPI->SPI_SR & SPI_SR_TDRE) == 0); } + + void enableConfig() { m_SPI->SPI_WPMR &= ~SPI_WPMR_WPEN; } + void disableConfig() { m_SPI->SPI_WPMR |= SPI_WPMR_WPEN; } + + void enableSPI() { m_SPI->SPI_CR = SPI_CR_SPIEN; } + void disableSPI() { m_SPI->SPI_CR = SPI_CR_SPIDIS; } + void resetSPI() { m_SPI->SPI_CR = SPI_CR_SWRST; } + + static inline void readyTransferBits(FASTLED_REGISTER uint32_t bits) { + bits -= 8; + // don't change the number of transfer bits while data is still being transferred from TDR to the shift register + waitForEmpty(); + m_SPI->SPI_CSR[0] = SPI_CSR_NCPHA | SPI_CSR_CSAAT | (bits << SPI_CSR_BITS_Pos) | SPI_CSR_DLYBCT(1) | SPI_CSR_SCBR(_SPI_CLOCK_DIVIDER); + } + + template static inline void writeBits(uint16_t w) { + waitForEmpty(); + m_SPI->SPI_TDR = (uint32_t)w | SPI_PCS(0); + } + +public: + SAMHardwareSPIOutput() { m_pSelect = NULL; } + SAMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + + // set the object representing the selectable + void setSelect(Selectable *pSelect) { /* TODO */ } + + // initialize the SPI subssytem + void init() { + // m_SPI = SPI0; + + // set the output pins master out, master in, clock. Note doing this here because I still don't + // know how I want to expose this type of functionality in FastPin. + PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN>::mask(), PIO_DEFAULT); + PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN-1>::mask(), PIO_DEFAULT); + PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_CLOCK_PIN>::mask(), PIO_DEFAULT); + + release(); + + // Configure the SPI clock, divider between 1-255 + // SCBR = _SPI_CLOCK_DIVIDER + pmc_enable_periph_clk(ID_SPI0); + disableSPI(); + + // reset twice (what the sam code does, not sure why?) + resetSPI(); + resetSPI(); + + // Configure SPI as master, enable + // Bits we want in MR: master, disable mode fault detection, variable peripheral select + m_SPI->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS; + + enableSPI(); + + // Send everything out in 8 bit chunks, other sizes appear to work, poorly... + readyTransferBits(8); + } + + // latch the CS select + void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } } + + // release the CS select + void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } } + + // wait until all queued up data has been written + void waitFully() { while((m_SPI->SPI_SR & SPI_SR_TXEMPTY) == 0); } + + // write a byte out via SPI (returns immediately on writing register) + static void writeByte(uint8_t b) { + writeBits<8>(b); + } + + // write a word out via SPI (returns immediately on writing register) + static void writeWord(uint16_t w) { + writeBits<16>(w); + } + + // 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(); + } + + template void writeBytes(FASTLED_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(); + } + + void writeBytes(FASTLED_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 + // not the most efficient mechanism in the world - but should be enough for sm16716 and friends + template inline void writeBit(uint8_t b) { + // need to wait for all exisiting data to go out the door, first + waitFully(); + disableSPI(); + if(b & (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + FastPin<_CLOCK_PIN>::lo(); + enableSPI(); + } + + // 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; + + if(FLAGS & FLAG_START_BIT) { + while(pixels.has(1)) { + writeBits<9>((1<<8) | D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + pixels.advanceData(); + pixels.stepDithering(); + } + } else { + while(pixels.has(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/lib/FastLED/src/platforms/arm/sam/led_sysdefs_arm_sam.h b/lib/FastLED/src/platforms/arm/sam/led_sysdefs_arm_sam.h new file mode 100644 index 00000000..a4828648 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/sam/led_sysdefs_arm_sam.h @@ -0,0 +1,39 @@ +#ifndef __INC_LED_SYSDEFS_ARM_SAM_H +#define __INC_LED_SYSDEFS_ARM_SAM_H + + +#define FASTLED_ARM + +// Setup DUE timer defines/channels/etc... +#ifndef DUE_TIMER_CHANNEL +#define DUE_TIMER_GROUP 0 +#endif + +#ifndef DUE_TIMER_CHANNEL +#define DUE_TIMER_CHANNEL 0 +#endif + +#define DUE_TIMER ((DUE_TIMER_GROUP==0) ? TC0 : ((DUE_TIMER_GROUP==1) ? TC1 : TC2)) +#define DUE_TIMER_ID (ID_TC0 + (DUE_TIMER_GROUP*3) + DUE_TIMER_CHANNEL) +#define DUE_TIMER_VAL (DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_CV << 1) +#define DUE_TIMER_RUNNING ((DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_SR & TC_SR_CLKSTA) != 0) + +#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 + +// reusing/abusing cli/sei defs for due +#define cli() __disable_irq(); __disable_fault_irq(); +#define sei() __enable_irq(); __enable_fault_irq(); + + +#endif diff --git a/lib/FastLED/src/platforms/arm/stm32/clockless_arm_stm32.h b/lib/FastLED/src/platforms/arm/stm32/clockless_arm_stm32.h new file mode 100644 index 00000000..40bab2e3 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/stm32/clockless_arm_stm32.h @@ -0,0 +1,133 @@ +#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 + +#if defined(STM32F2XX) +// The photon runs faster than the others +#define ADJ 8 +#else +#define ADJ 20 +#endif + +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(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) { + for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) { + while(_CYCCNT < (T1+T2+T3-ADJ)); + FastPin::fastset(port, hi); + _CYCCNT = 4; + if(b&0x80) { + while(_CYCCNT < (T1+T2-ADJ)); + FastPin::fastset(port, lo); + } else { + while(_CYCCNT < (T1-ADJ/2)); + FastPin::fastset(port, lo); + } + b <<= 1; + } + + while(_CYCCNT < (T1+T2+T3-ADJ)); + FastPin::fastset(port, hi); + _CYCCNT = 4; + + if(b&0x80) { + while(_CYCCNT < (T1+T2-ADJ)); + FastPin::fastset(port, lo); + } else { + while(_CYCCNT < (T1-ADJ/2)); + 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; + + FASTLED_REGISTER data_ptr_t port = FastPin::port(); + FASTLED_REGISTER data_t hi = *port | FastPin::mask();; + FASTLED_REGISTER data_t lo = *port & ~FastPin::mask();; + *port = lo; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + FASTLED_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/lib/FastLED/src/platforms/arm/stm32/cm3_regs.h b/lib/FastLED/src/platforms/arm/stm32/cm3_regs.h new file mode 100644 index 00000000..7bb7f759 --- /dev/null +++ b/lib/FastLED/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/lib/FastLED/src/platforms/arm/stm32/fastled_arm_stm32.h b/lib/FastLED/src/platforms/arm/stm32/fastled_arm_stm32.h new file mode 100644 index 00000000..3f86a873 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/stm32/fastled_arm_stm32.h @@ -0,0 +1,9 @@ +#ifndef __INC_FASTLED_ARM_SAM_H +#define __INC_FASTLED_ARM_SAM_H + +// Include the sam headers +#include "fastpin_arm_stm32.h" +// #include "fastspi_arm_stm32.h" +#include "clockless_arm_stm32.h" + +#endif diff --git a/lib/FastLED/src/platforms/arm/stm32/fastpin_arm_stm32.h b/lib/FastLED/src/platforms/arm/stm32/fastpin_arm_stm32.h new file mode 100644 index 00000000..4f9316e4 --- /dev/null +++ b/lib/FastLED/src/platforms/arm/stm32/fastpin_arm_stm32.h @@ -0,0 +1,282 @@ +#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 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; } + +#if defined(STM32F2XX) + inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRRL = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRRH = _MASK; } +#else + 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); } +#endif + inline static void set(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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; } + +#if defined(STM32F2XX) + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRRL; } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRRH; } +#else + 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; } +#endif + + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + + +#define _R(T) struct __gen_struct_ ## T +#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin : public _ARMPIN {}; + +#if defined(STM32F10X_MD) +#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 _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)); + +#elif defined(STM32F2XX) +#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); + +#elif defined (STM32F1) +// stm32duino +#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); + + +#else +#error "Platform not supported" +#endif + +#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(STM32F2XX) // Photon Particle + +// https://github.com/focalintent/FastLED-Sparkcore/blob/master/firmware/fastpin_arm_stm32.h +#define MAX_PIN 20 +_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(10, 5, C); +_FL_DEFPIN(11, 3, C); +_FL_DEFPIN(12, 2, C); +_FL_DEFPIN(13, 5, A); +_FL_DEFPIN(14, 6, A); +_FL_DEFPIN(15, 7, A); +_FL_DEFPIN(16, 4, A); +_FL_DEFPIN(17, 0, A); +_FL_DEFPIN(18, 10, A); +_FL_DEFPIN(19, 9, A); +_FL_DEFPIN(20, 7, C); + +#define SPI_DATA 15 +#define SPI_CLOCK 13 + +#define HAS_HARDWARE_PIN_SUPPORT + +#elif 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 + +#elif 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 + +#elif defined(ARDUINO_GENERIC_F103C8TX) // stm32duino generic STM32F103C8TX +#define MAX_PIN 36 + +// PA0-PA15 +_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, 8, A); +_FL_DEFPIN(9, 9, A); +_FL_DEFPIN(10, 10, A); +_FL_DEFPIN(11, 11, A); +_FL_DEFPIN(12, 12, A); +_FL_DEFPIN(13, 13, A); +_FL_DEFPIN(14, 14, A); +_FL_DEFPIN(15, 15, A); + +// PB0-PB15 +_FL_DEFPIN(16, 0, B); +_FL_DEFPIN(17, 1, B); +_FL_DEFPIN(18, 2, B); +_FL_DEFPIN(19, 3, B); +_FL_DEFPIN(20, 4, B); +_FL_DEFPIN(21, 5, B); +_FL_DEFPIN(22, 6, B); +_FL_DEFPIN(23, 7, B); +_FL_DEFPIN(24, 8, B); +_FL_DEFPIN(25, 9, B); +_FL_DEFPIN(26, 10, B); +_FL_DEFPIN(27, 11, B); +_FL_DEFPIN(28, 12, B); +_FL_DEFPIN(29, 13, B); +_FL_DEFPIN(30, 14, B); +_FL_DEFPIN(31, 15, B); + +// PC13-PC15 +_FL_DEFPIN(32, 13, C); +_FL_DEFPIN(33, 14, C); +_FL_DEFPIN(34, 15, C); + +// PD0-PD1 +_FL_DEFPIN(35, 0, D); +_FL_DEFPIN(36, 1, D); + +// SPI2 MOSI +#define SPI_DATA PB15 +// SPI2 SCK +#define SPI_CLOCK PB13 + +#define HAS_HARDWARE_PIN_SUPPORT + +#endif // STM32F1 + +#endif // FASTLED_FORCE_SOFTWARE_PINS + +FASTLED_NAMESPACE_END + +#endif // __INC_FASTPIN_ARM_STM32 diff --git a/lib/FastLED/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h b/lib/FastLED/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h new file mode 100644 index 00000000..edd409bd --- /dev/null +++ b/lib/FastLED/src/platforms/arm/stm32/led_sysdefs_arm_stm32.h @@ -0,0 +1,84 @@ +#ifndef __INC_LED_SYSDEFS_ARM_SAM_H +#define __INC_LED_SYSDEFS_ARM_SAM_H + +#if defined(STM32F10X_MD) || defined(STM32F2XX) + +#include + +#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(); + +#elif defined (__STM32F1__) + +#include "cm3_regs.h" + +#define cli() nvic_globalirq_disable() +#define sei() nvic_globalirq_enable() + +#elif defined(STM32F1) +// stm32duino + +#define cli() noInterrupts() +#define sei() interrupts() + +#else +#error "Platform not supported" +#endif + +#define FASTLED_ARM + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 1 +#endif + +// Default to allowing interrupts +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 0 +#endif + +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + +// pgmspace definitions +#define PROGMEM + +#if !defined(STM32F1) +// The stm32duino core already defines these +#define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#endif + +// Default to NOT using PROGMEM here +#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 + +#if defined(STM32F2XX) +#define F_CPU 120000000 +#elif defined(STM32F1) +// F_CPU is already defined on stm32duino, but it's not constant. +#undef F_CPU +#define F_CPU 72000000 +#else +#define F_CPU 72000000 +#endif + +#if defined(STM32F2XX) +// Photon doesn't provide yield +#define FASTLED_NEEDS_YIELD +extern "C" void yield(); +#endif + +#endif // defined(STM32F10X_MD) || defined(STM32F2XX) diff --git a/lib/FastLED/src/platforms/avr/clockless_trinket.h b/lib/FastLED/src/platforms/avr/clockless_trinket.h new file mode 100644 index 00000000..deae9979 --- /dev/null +++ b/lib/FastLED/src/platforms/avr/clockless_trinket.h @@ -0,0 +1,495 @@ +#ifndef __INC_CLOCKLESS_TRINKET_H +#define __INC_CLOCKLESS_TRINKET_H + +#include "../../controller.h" +#include "../../lib8tion.h" +#include // for cli/se definitions + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_AVR) + +// Scaling macro choice +#ifndef TRINKET_SCALE +#define TRINKET_SCALE 1 +// whether or not to use dithering +#define DITHER 1 +#endif + +#if (F_CPU==8000000) +#define FASTLED_SLOW_CLOCK_ADJUST // asm __volatile__ ("mov r0,r0\n\t"); +#else +#define FASTLED_SLOW_CLOCK_ADJUST +#endif + +#define US_PER_TICK (64 / (F_CPU/1000000)) + +// Variations on the functions in delay.h - w/a loop var passed in to preserve registers across calls by the optimizer/compiler +template inline void _dc(FASTLED_REGISTER uint8_t & loopvar); + +template __attribute__((always_inline)) inline void _dc_AVR(FASTLED_REGISTER uint8_t & loopvar) { + _dc(loopvar); + // The convolution in here is to ensure that the state of the carry flag coming into the delay loop is preserved + asm __volatile__ ( "BRCS L_PC%=\n\t" + " LDI %[loopvar], %[_LOOP]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\tBREQ L_DONE%=\n\t" + "L_PC%=: LDI %[loopvar], %[_LOOP]\n\tLL_%=: DEC %[loopvar]\n\t BRNE LL_%=\n\tBSET 0\n\t" + "L_DONE%=:\n\t" + : + [loopvar] "+a" (loopvar) : [_LOOP] "M" (_LOOP) : ); +} + +template __attribute__((always_inline)) inline void _dc(FASTLED_REGISTER uint8_t & loopvar) { + _dc_AVR(loopvar); +} +template<> __attribute__((always_inline)) inline void _dc<-6>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc<-5>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc<-4>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc<-3>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc<-2>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc<-1>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc< 0>(FASTLED_REGISTER uint8_t & ) {} +template<> __attribute__((always_inline)) inline void _dc< 1>(FASTLED_REGISTER uint8_t & ) {asm __volatile__("mov r0,r0":::);} +#if defined(__LGT8F__) +template<> __attribute__((always_inline)) inline void _dc< 2>(FASTLED_REGISTER uint8_t & loopvar) { _dc<1>(loopvar); _dc<1>(loopvar); } +#else +template<> __attribute__((always_inline)) inline void _dc< 2>(FASTLED_REGISTER uint8_t & ) {asm __volatile__("rjmp .+0":::);} +#endif +template<> __attribute__((always_inline)) inline void _dc< 3>(FASTLED_REGISTER uint8_t & loopvar) { _dc<2>(loopvar); _dc<1>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc< 4>(FASTLED_REGISTER uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc< 5>(FASTLED_REGISTER uint8_t & loopvar) { _dc<2>(loopvar); _dc<3>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc< 6>(FASTLED_REGISTER uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); _dc<2>(loopvar);} +template<> __attribute__((always_inline)) inline void _dc< 7>(FASTLED_REGISTER uint8_t & loopvar) { _dc<4>(loopvar); _dc<3>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc< 8>(FASTLED_REGISTER uint8_t & loopvar) { _dc<4>(loopvar); _dc<4>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc< 9>(FASTLED_REGISTER uint8_t & loopvar) { _dc<5>(loopvar); _dc<4>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<10>(FASTLED_REGISTER uint8_t & loopvar) { _dc<6>(loopvar); _dc<4>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<11>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<1>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<12>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<2>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<13>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<3>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<14>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<4>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<15>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<5>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<16>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<6>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<17>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<7>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<18>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<8>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<19>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<9>(loopvar); } +template<> __attribute__((always_inline)) inline void _dc<20>(FASTLED_REGISTER uint8_t & loopvar) { _dc<10>(loopvar); _dc<10>(loopvar); } + +#define DINTPIN(T,ADJ,PINADJ) (T-(PINADJ+ADJ)>0) ? _dc(loopvar) : _dc<0>(loopvar); +#define DINT(T,ADJ) if(AVR_PIN_CYCLES(DATA_PIN)==1) { DINTPIN(T,ADJ,1) } else { DINTPIN(T,ADJ,2); } +#define _D1(ADJ) DINT(T1,ADJ) +#define _D2(ADJ) DINT(T2,ADJ) +#define _D3(ADJ) DINT(T3,ADJ) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// 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 point 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 ((FASTLED_ALLOW_INTERRUPTS == 0) && defined(NO_CORRECTION) && (NO_CORRECTION == 1) && !(defined(NO_CLOCK_CORRECTION))) +// we hit this if you were trying to turn off clock correction without also trying to enable interrupts. +# pragma message "In older versions of FastLED defining NO_CORRECTION 1 would mistakenly turn off color correction as well as clock correction." +# pragma message "define NO_CLOCK_CORRECTION 1 to fix this warning." +# define NO_CLOCK_CORRECTION 1 +#endif + +#if (!defined(NO_CLOCK_CORRECTION) || (NO_CLOCK_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0) +static uint8_t gTimeErrorAccum256ths; +#endif + +#define FASTLED_HAS_CLOCKLESS 1 + +template +class ClocklessController : public CPixelLEDController { + static_assert(T1 >= 2 && T2 >= 2 && T3 >= 3, "Not enough cycles - use a higher clock speed"); + + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + CMinWait mWait; + +public: + virtual void init() { + FastPin::setOutput(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + virtual void showPixels(PixelController & pixels) { + + mWait.wait(); + cli(); + + if(pixels.mLen > 0) { + showRGBInternal(pixels); + } + + // Adjust the timer +#if (!defined(NO_CLOCK_CORRECTION) || (NO_CLOCK_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0) + uint32_t microsTaken = (uint32_t)pixels.size() * (uint32_t)CLKS_TO_MICROS(24 * (T1 + T2 + T3)); + + // adust for approximate observed actal runtime (as of January 2015) + // roughly 9.6 cycles per pixel, which is 0.6us/pixel at 16MHz + // microsTaken += nLeds * 0.6 * CLKS_TO_MICROS(16); + microsTaken += scale16by8(pixels.size(),(0.6 * 256) + 1) * CLKS_TO_MICROS(16); + + // if less than 1000us, there is NO timer impact, + // this is because the ONE interrupt that might come in while interrupts + // are disabled is queued up, and it will be serviced as soon as + // interrupts are re-enabled. + // This actually should technically also account for the runtime of the + // interrupt handler itself, but we're just not going to worry about that. + if( microsTaken > 1000) { + + // Since up to one timer tick will be queued, we don't need + // to adjust the MS_COUNTER for that one. + microsTaken -= 1000; + + // Now convert microseconds to 256ths of a second, approximately like this: + // 250ths = (us/4) + // 256ths = 250ths * (263/256); + uint16_t x256ths = microsTaken >> 2; + x256ths += scale16by8(x256ths,7); + + x256ths += gTimeErrorAccum256ths; + MS_COUNTER += (x256ths >> 8); + gTimeErrorAccum256ths = x256ths & 0xFF; + } + +#if 0 + // For pixel counts of 30 and under at 16Mhz, no correction is necessary. + // For pixel counts of 15 and under at 8Mhz, no correction is necessary. + // + // This code, below, is smaller, and quicker clock correction, which drifts much + // more significantly, but is a few bytes smaller. Presented here for consideration + // as an alternate on the ATtiny, which can't have more than about 150 pixels MAX + // anyway, meaning that microsTaken will never be more than about 4,500, which fits in + // a 16-bit variable. The difference between /1000 and /1024 only starts showing + // up in the range of about 100 pixels, so many ATtiny projects won't even + // see a clock difference due to the approximation there. + uint16_t microsTaken = (uint32_t)nLeds * (uint32_t)CLKS_TO_MICROS((24) * (T1 + T2 + T3)); + MS_COUNTER += (microsTaken >> 10); +#endif + +#endif + + sei(); + mWait.mark(); + } +#define USE_ASM_MACROS + +#if defined(__AVR_ATmega4809__) +// Not used - place holder so existing ASM_VARS macro can remain the same +#define ASM_VAR_PORT "r" (*FastPin::port()) +#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), \ + [data] "+z" (data), \ + [b1] "+a" (b1), \ + [d0] "+r" (d0), \ + [d1] "+r" (d1), \ + [d2] "+r" (d2), \ + [loopvar] "+a" (loopvar), \ + [scale_base] "+a" (scale_base) \ + : /* use variables */ \ + [ADV] "r" (advanceBy), \ + [b0] "a" (b0), \ + [hi] "r" (hi), \ + [lo] "r" (lo), \ + [s0] "r" (s0), \ + [s1] "r" (s1), \ + [s2] "r" (s2), \ + [e0] "r" (e0), \ + [e1] "r" (e1), \ + [e2] "r" (e2), \ + [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 */ + +#if defined(__AVR_ATmega4809__) + +// 1 cycle, write hi to the port +#define HI1 do {*FastPin::port()=hi;} while(0); +// 1 cycle, write lo to the port +#define LO1 do {*FastPin::port()=lo;} while(0); + +#else + +// Note: the code in the else in HI1/LO1 will be turned into an sts (2 cycle, 2 word) +// 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; } +// 1 cycle, write lo to the port +#define LO1 if((int)(FastPin::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[lo]" ASM_VARS ); } else { *FastPin::port()=lo; } + +#endif + +// 2 cycles, sbrs on flipping the line to lo if we're pushing out a 0 +#define QLO2(B, N) asm __volatile__("sbrs %[" #B "], " #N ASM_VARS ); LO1; +// load a byte from ram into the given var with the given offset +#define LD2(B,O) asm __volatile__("ldd %[" #B "], Z + %[" #O "]\n\t" ASM_VARS ); +// 4 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var, clear carry +#define LDSCL4(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]\n\tclc\n\t" ASM_VARS ); + +#if (DITHER==1) +// apply dithering value before we do anything with scale_base +#define PRESCALE4(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\tbrcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\t" ASM_VARS); + +// Do the add for the prescale +#define PRESCALEA2(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\t" ASM_VARS); + +// Do the clamp for the prescale, clear carry when we're done - NOTE: Must ensure carry flag state is preserved! +#define PRESCALEB4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tneg %[" #D "]\n\tCLC" ASM_VARS); + +// Clamp for prescale, increment data, since we won't ever wrap 65k, this also effectively clears carry for us +#define PSBIDATA4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tadd %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" ASM_VARS); + +#else +#define PRESCALE4(D) _dc<4>(loopvar); +#define PRESCALEA2(D) _dc<2>(loopvar); +#define PRESCALEB4(D) _dc<4>(loopvar); +#define PSBIDATA4(D) asm __volatile__( "add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\trjmp .+0\n\t" ASM_VARS ); +#endif + +// 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space) +#define _SCALE02(B, N) "sbrc %[s0], " #N "\n\tadd %[" #B "], %[scale_base]\n\t" +#define _SCALE12(B, N) "sbrc %[s1], " #N "\n\tadd %[" #B "], %[scale_base]\n\t" +#define _SCALE22(B, N) "sbrc %[s2], " #N "\n\tadd %[" #B "], %[scale_base]\n\t" +#define SCALE02(B,N) asm __volatile__( _SCALE02(B,N) ASM_VARS ); +#define SCALE12(B,N) asm __volatile__( _SCALE12(B,N) ASM_VARS ); +#define SCALE22(B,N) asm __volatile__( _SCALE22(B,N) ASM_VARS ); + +// 1 cycle - rotate right, pulling in from carry +#define _ROR1(B) "ror %[" #B "]\n\t" +#define ROR1(B) asm __volatile__( _ROR1(B) ASM_VARS); + +// 1 cycle, clear the carry bit +#define _CLC1 "clc\n\t" +#define CLC1 asm __volatile__( _CLC1 ASM_VARS ); + +// 2 cycles, rortate right, pulling in from carry then clear the carry bit +#define RORCLC2(B) asm __volatile__( _ROR1(B) _CLC1 ASM_VARS ); + +// 4 cycles, rotate, clear carry, scale next bit +#define RORSC04(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE02(B, N) ASM_VARS ); +#define RORSC14(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE12(B, N) ASM_VARS ); +#define RORSC24(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE22(B, N) ASM_VARS ); + +// 4 cycles, scale bit, rotate, clear carry +#define SCROR04(B, N) asm __volatile__( _SCALE02(B,N) _ROR1(B) _CLC1 ASM_VARS ); +#define SCROR14(B, N) asm __volatile__( _SCALE12(B,N) _ROR1(B) _CLC1 ASM_VARS ); +#define SCROR24(B, N) asm __volatile__( _SCALE22(B,N) _ROR1(B) _CLC1 ASM_VARS ); + +///////////////////////////////////////////////////////////////////////////////////// +// Loop life cycle + +// dither adjustment macro - should be kept in sync w/what's in stepDithering +// #define ADJDITHER2(D, E) D = E - D; +#define _NEGD1(D) "neg %[" #D "]\n\t" +#define _ADJD1(D,E) "add %[" #D "], %[" #E "]\n\t" +#define ADJDITHER2(D, E) asm __volatile__ ( _NEGD1(D) _ADJD1(D, E) ASM_VARS); +#define ADDDE1(D, E) asm __volatile__ ( _ADJD1(D, E) ASM_VARS ); + +// #define xstr(a) str(a) +// #define str(a) #a +// #define ADJDITHER2(D,E) asm __volatile__("subi %[" #D "], " xstr(DUSE) "\n\tand %[" #D "], %[" #E "]\n\t" ASM_VARS); + +// define the beginning of the loop +#define LOOP asm __volatile__("1:" ASM_VARS ); +// define the end of the loop +#define DONE asm __volatile__("2:" ASM_VARS ); + +// 2 cycles - increment the data pointer +#define IDATA2 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" ASM_VARS ); +#define IDATACLC3 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" _CLC1 ASM_VARS ); + +// 1 cycle mov +#define _MOV1(B1, B2) "mov %[" #B1 "], %[" #B2 "]\n\t" + +#define MOV1(B1, B2) asm __volatile__( _MOV1(B1,B2) ASM_VARS ); + +// 3 cycle mov - skip if scale fix is happening +#if (FASTLED_SCALE8_FIXED == 1) +#define _MOV_FIX03(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s0], __zero_reg__\n\t" _MOV1(B1, B2) +#define _MOV_FIX13(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s1], __zero_reg__\n\t" _MOV1(B1, B2) +#define _MOV_FIX23(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s2], __zero_reg__\n\t" _MOV1(B1, B2) +#else +// if we haven't fixed scale8, just do the move and nop the 2 cycles that would be used to +// do the fixed adjustment +#define _MOV_FIX03(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t" +#define _MOV_FIX13(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t" +#define _MOV_FIX23(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t" +#endif + +// 3 cycle mov + negate D for dither adjustment +#define MOV_NEGD04(B1, B2, D) asm __volatile( _MOV_FIX03(B1, B2) _NEGD1(D) ASM_VARS ); +#define MOV_ADDDE04(B1, B2, D, E) asm __volatile( _MOV_FIX03(B1, B2) _ADJD1(D, E) ASM_VARS ); +#define MOV_NEGD14(B1, B2, D) asm __volatile( _MOV_FIX13(B1, B2) _NEGD1(D) ASM_VARS ); +#define MOV_ADDDE14(B1, B2, D, E) asm __volatile( _MOV_FIX13(B1, B2) _ADJD1(D, E) ASM_VARS ); +#define MOV_NEGD24(B1, B2, D) asm __volatile( _MOV_FIX23(B1, B2) _NEGD1(D) ASM_VARS ); + +// 2 cycles - decrement the counter +#define DCOUNT2 asm __volatile__("sbiw %[count], 1" ASM_VARS ); +// 2 cycles - jump to the beginning of the loop +#define JMPLOOP2 asm __volatile__("rjmp 1b" ASM_VARS ); +// 2 cycles - jump out of the loop +#define BRLOOP1 asm __volatile__("brne 3\n\trjmp 2f\n\t3:" ASM_VARS ); + +// 5 cycles 2 sbiw, 3 for the breq/rjmp +#define ENDLOOP5 asm __volatile__("sbiw %[count], 1\n\tbreq L_%=\n\trjmp 1b\n\tL_%=:\n\t" ASM_VARS); + +// NOP using the variables, forcing a move +#define DNOP asm __volatile__("mov r0,r0" ASM_VARS); + +#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) { + uint8_t *data = (uint8_t*)pixels.mData; + data_ptr_t port = FastPin::port(); + data_t mask = FastPin::mask(); + uint8_t scale_base = 0; + + // FASTLED_REGISTER uint8_t *end = data + nLeds; + data_t hi = *port | mask; + data_t lo = *port & ~mask; + *port = lo; + + // the byte currently being written out + uint8_t b0 = 0; + // the byte currently being worked on to write the next out + uint8_t b1 = 0; + + // Setup the pixel controller + pixels.preStepFirstByteDithering(); + + // pull the dithering/adjustment values out of the pixels object for direct asm access + + // even though advanceBy is only an int8, we cast it to int16 for sign extension in case it's negative. + int16_t advanceBy = pixels.advanceBy(); + uint16_t count = pixels.mLen; + + uint8_t s0 = pixels.mScale.raw[RO(0)]; + uint8_t s1 = pixels.mScale.raw[RO(1)]; + uint8_t s2 = pixels.mScale.raw[RO(2)]; +#if (FASTLED_SCALE8_FIXED==1) + s0++; s1++; s2++; +#endif + uint8_t d0 = pixels.d[RO(0)]; + uint8_t d1 = pixels.d[RO(1)]; + uint8_t d2 = pixels.d[RO(2)]; + uint8_t e0 = pixels.e[RO(0)]; + uint8_t e1 = pixels.e[RO(1)]; + uint8_t e2 = pixels.e[RO(2)]; + + uint8_t loopvar=0; + + // This has to be done in asm to keep gcc from messing up the asm code further down + b0 = data[RO(0)]; + { + LDSCL4(b0,O0) PRESCALEA2(d0) + PRESCALEB4(d0) SCALE02(b0,0) + RORSC04(b0,1) ROR1(b0) CLC1 + SCROR04(b0,2) SCALE02(b0,3) + RORSC04(b0,4) ROR1(b0) CLC1 + SCROR04(b0,5) SCALE02(b0,6) + RORSC04(b0,7) ROR1(b0) CLC1 + MOV_ADDDE04(b1,b0,d0,e0) + MOV1(b0,b1) + } + + { + // while(--count) + { + // Loop beginning + DNOP; + LOOP; + + // Sum of the clock counts across each row should be 10 for 8Mhz, WS2811 + // The values in the D1/D2/D3 indicate how many cycles the previous column takes + // to allow things to line back up. + // + // While writing out byte 0, we're loading up byte 1, applying the dithering adjustment, + // then scaling it using 8 cycles of shift/add interleaved in between writing the bits + // out. When doing byte 1, we're doing the above for byte 2. When we're doing byte 2, + // we're cycling back around and doing the above for byte 0. + + // Inline scaling - RGB ordering + // DNOP + HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O1) _D2(4) LO1 PRESCALEA2(d1) _D3(2) + HI1 _D1(1) QLO2(b0, 6) PRESCALEB4(d1) _D2(4) LO1 SCALE12(b1,0) _D3(2) + HI1 _D1(1) QLO2(b0, 5) RORSC14(b1,1) _D2(4) LO1 RORCLC2(b1) _D3(2) + HI1 _D1(1) QLO2(b0, 4) SCROR14(b1,2) _D2(4) LO1 SCALE12(b1,3) _D3(2) + HI1 _D1(1) QLO2(b0, 3) RORSC14(b1,4) _D2(4) LO1 RORCLC2(b1) _D3(2) + HI1 _D1(1) QLO2(b0, 2) SCROR14(b1,5) _D2(4) LO1 SCALE12(b1,6) _D3(2) + 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) /* fall through */ + case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) /* fall through */ + case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) /* fall through */ + case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) + } + MOV_ADDDE14(b0,b1,d1,e1) _D2(4) LO1 _D3(0) + + HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O2) _D2(4) LO1 PRESCALEA2(d2) _D3(2) + HI1 _D1(1) QLO2(b0, 6) PSBIDATA4(d2) _D2(4) LO1 SCALE22(b1,0) _D3(2) + HI1 _D1(1) QLO2(b0, 5) RORSC24(b1,1) _D2(4) LO1 RORCLC2(b1) _D3(2) + HI1 _D1(1) QLO2(b0, 4) SCROR24(b1,2) _D2(4) LO1 SCALE22(b1,3) _D3(2) + HI1 _D1(1) QLO2(b0, 3) RORSC24(b1,4) _D2(4) LO1 RORCLC2(b1) _D3(2) + HI1 _D1(1) QLO2(b0, 2) SCROR24(b1,5) _D2(4) LO1 SCALE22(b1,6) _D3(2) + 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) /* fall through */ + case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) /* fall through */ + case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) /* fall through */ + case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) + } + + // Because Prescale on the middle byte also increments the data counter, + // we have to do both halves of updating d2 here - negating it (in the + // MOV_NEGD24 macro) and then adding E back into it + MOV_NEGD24(b0,b1,d2) _D2(4) LO1 ADDDE1(d2,e2) _D3(1) + HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O0) _D2(4) LO1 PRESCALEA2(d0) _D3(2) + HI1 _D1(1) QLO2(b0, 6) PRESCALEB4(d0) _D2(4) LO1 SCALE02(b1,0) _D3(2) + HI1 _D1(1) QLO2(b0, 5) RORSC04(b1,1) _D2(4) LO1 RORCLC2(b1) _D3(2) + HI1 _D1(1) QLO2(b0, 4) SCROR04(b1,2) _D2(4) LO1 SCALE02(b1,3) _D3(2) + HI1 _D1(1) QLO2(b0, 3) RORSC04(b1,4) _D2(4) LO1 RORCLC2(b1) _D3(2) + HI1 _D1(1) QLO2(b0, 2) SCROR04(b1,5) _D2(4) LO1 SCALE02(b1,6) _D3(2) + 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) /* fall through */ + case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) /* fall through */ + case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) /* fall through */ + case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0) + } + MOV_ADDDE04(b0,b1,d0,e0) _D2(4) LO1 _D3(5) + ENDLOOP5 + } + DONE; + } + + } + +}; + +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/platforms/avr/fastled_avr.h b/lib/FastLED/src/platforms/avr/fastled_avr.h new file mode 100644 index 00000000..47236f44 --- /dev/null +++ b/lib/FastLED/src/platforms/avr/fastled_avr.h @@ -0,0 +1,13 @@ +#ifndef __INC_FASTLED_AVR_H +#define __INC_FASTLED_AVR_H + +#include "fastpin_avr.h" +#include "fastspi_avr.h" +#include "clockless_trinket.h" + +// Default to using PROGMEM +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + +#endif diff --git a/lib/FastLED/src/platforms/avr/fastpin_avr.h b/lib/FastLED/src/platforms/avr/fastpin_avr.h new file mode 100644 index 00000000..122bc2c4 --- /dev/null +++ b/lib/FastLED/src/platforms/avr/fastpin_avr.h @@ -0,0 +1,434 @@ +#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(FASTLED_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(FASTLED_REGISTER port_ptr_t /*port*/) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t /*port*/) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t /*port*/, FASTLED_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) + +// 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< {}; + +#elif defined(__AVR_ATmega4809__) + +// Leverage VPORTs instead of PORTs for faster access +#define _FL_IO(L,C) _RD8(VPORT ## L ## _DIR); _RD8(VPORT ## L ## _OUT); _RD8(VPORT ## L ## _IN); _FL_DEFINE_PORT3(L, C, _R(VPORT ## 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_ATtiny48__) || defined(__AVR_ATtiny88__) + +#define MAX_PIN 27 +_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, 7, B); _FL_DEFPIN(15, 2, A); +_FL_DEFPIN(16, 3, A); _FL_DEFPIN(17, 0, A); _FL_DEFPIN(18, 1, A); _FL_DEFPIN(19, 0, C); +_FL_DEFPIN(20, 1, C); _FL_DEFPIN(21, 2, C); _FL_DEFPIN(22, 3, C); _FL_DEFPIN(23, 4, C); +_FL_DEFPIN(24, 5, C); _FL_DEFPIN(25, 7, C); + +#define SPI_DATA 11 +#define SPI_CLOCK 13 +#define SPI_SELECT 10 +#define AVR_HARDWARE_SPI 1 +#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 11 +#define SPI_CLOCK 13 +#define SPI_SELECT 8 +#define AVR_HARDWARE_SPI 1 +#define HAS_HARDWARE_PIN_SUPPORT 1 + +#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); + +#define SPI_DATA 11 +#define SPI_CLOCK 13 +#define SPI_SELECT 8 +#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/lib/FastLED/src/platforms/avr/fastspi_avr.h b/lib/FastLED/src/platforms/avr/fastspi_avr.h new file mode 100644 index 00000000..31d896c0 --- /dev/null +++ b/lib/FastLED/src/platforms/avr/fastspi_avr.h @@ -0,0 +1,842 @@ +#ifndef __INC_FASTSPI_AVR_H +#define __INC_FASTSPI_AVR_H + +FASTLED_NAMESPACE_BEGIN + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware SPI support using USART registers and friends +// +// TODO: Complete/test implementation - right now this doesn't work +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// uno/mini/duemilanove +#if defined(AVR_HARDWARE_SPI) + +#if defined(UBRR1) + +#ifndef UCPHA1 +#define UCPHA1 1 +#endif + +template +class AVRUSART1SPIOutput { + Selectable *m_pSelect; + +public: + AVRUSART1SPIOutput() { m_pSelect = NULL; } + AVRUSART1SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + UBRR1 = 0; + + /* Set MSPI mode of operation and SPI data mode 0. */ + UCSR1C = (1<::setOutput(); + FastPin<_DATA_PIN>::setOutput(); + + // must be done last, see page 206 + setSPIRate(); + } + + void setSPIRate() { + if(_SPI_CLOCK_DIVIDER > 2) { + UBRR1 = (_SPI_CLOCK_DIVIDER/2)-1; + } else { + UBRR1 = 0; + } + } + + + static void stop() { + // TODO: stop the uart spi output + } + + static bool shouldWait(bool wait = false) __attribute__((always_inline)) { + static bool sWait=false; + if(sWait) { + sWait = wait; return true; + } else { + sWait = wait; return false; + } + // return true; + } + static void wait() __attribute__((always_inline)) { + if(shouldWait()) { + while(!(UCSR1A & (1<>8); writeByte(w&0xFF); } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR1=b; shouldWait(true); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR1=b; shouldWait(true); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR1=b; shouldWait(true); } + + + template inline static void writeBit(uint8_t b) { + if(b && (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + FastPin<_CLOCK_PIN>::lo(); + } + + void enable_pins() { } + void disable_pins() { } + + void select() { + if(m_pSelect != NULL) { + m_pSelect->select(); + } + enable_pins(); + setSPIRate(); + } + + void release() { + if(m_pSelect != NULL) { + m_pSelect->release(); + } + disable_pins(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { + writeByte(value); + } + } + + void writeBytesValue(uint8_t value, int len) { + //setSPIRate(); + select(); + while(len--) { + writeByte(value); + } + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + //setSPIRate(); + uint8_t *end = data + len; + select(); + while(data != end) { + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); + } + release(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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) { + //setSPIRate(); + int len = pixels.mLen; + + select(); + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + writeBytePostWait(D::adjust(pixels.loadAndScale0())); + writeBytePostWait(D::adjust(pixels.loadAndScale1())); + writeBytePostWait(D::adjust(pixels.loadAndScale2())); + } else { + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + } + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + release(); + } +}; +#endif + +#if defined(UBRR0) +template +class AVRUSART0SPIOutput { + Selectable *m_pSelect; + +public: + AVRUSART0SPIOutput() { m_pSelect = NULL; } + AVRUSART0SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + UBRR0 = 0; + + /* Set MSPI mode of operation and SPI data mode 0. */ + UCSR0C = (1<::setOutput(); + FastPin<_DATA_PIN>::setOutput(); + + // must be done last, see page 206 + setSPIRate(); + } + + void setSPIRate() { + if(_SPI_CLOCK_DIVIDER > 2) { + UBRR0 = (_SPI_CLOCK_DIVIDER/2)-1; + } else { + UBRR0 = 0; + } + } + + static void stop() { + // TODO: stop the uart spi output + } + + static bool shouldWait(bool wait = false) __attribute__((always_inline)) { + static bool sWait=false; + if(sWait) { + sWait = wait; return true; + } else { + sWait = wait; return false; + } + // return true; + } + static void wait() __attribute__((always_inline)) { + if(shouldWait()) { + while(!(UCSR0A & (1<>8); writeByte(w&0xFF); } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR0=b; shouldWait(true); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR0=b; shouldWait(true); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR0=b; shouldWait(true); } + + + template inline static void writeBit(uint8_t b) { + if(b && (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + FastPin<_CLOCK_PIN>::lo(); + } + + void enable_pins() { } + void disable_pins() { } + + void select() { + if(m_pSelect != NULL) { + m_pSelect->select(); + } + enable_pins(); + setSPIRate(); + } + + void release() { + if(m_pSelect != NULL) { + m_pSelect->release(); + } + disable_pins(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { + writeByte(value); + } + } + + void writeBytesValue(uint8_t value, int len) { + //setSPIRate(); + select(); + while(len--) { + writeByte(value); + } + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + //setSPIRate(); + uint8_t *end = data + len; + select(); + while(data != end) { + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); + } + release(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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) { + //setSPIRate(); + int len = pixels.mLen; + + select(); + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + writeBytePostWait(D::adjust(pixels.loadAndScale0())); + writeBytePostWait(D::adjust(pixels.loadAndScale1())); + writeBytePostWait(D::adjust(pixels.loadAndScale2())); + } else { + 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 + +#if defined(SPI0_CTRLA) + +template +class AVRHardwareSPIOutput { + Selectable *m_pSelect; + +public: + AVRHardwareSPIOutput() { m_pSelect = NULL; } + AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + FastPin<_CLOCK_PIN>::setOutput(); + FastPin<_DATA_PIN>::setOutput(); + + // Arduino Nano Every documentation lists SPI pins in ALT2 portmux position + PORTMUX_TWISPIROUTEA = PORTMUX_SPI01_bm; + + // Set SPI master mode and clock scaler. + SPI0_CTRLA = SPI_MASTER_bm; + if(_SPI_CLOCK_DIVIDER >= 128) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 64) { SPI0_CTRLA |= SPI_PRESC1_bm; } + else if (_SPI_CLOCK_DIVIDER >= 32) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 16) { SPI0_CTRLA |= SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 8) { SPI0_CTRLA |= SPI_PRESC0_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 4) { /* default rate */ } + else { SPI0_CTRLA |= SPI_CLK2X_bm; } + + // Set mode 0 and disable slave select. + SPI0_CTRLB = SPI_SSD_bm; + + // Enable SPI. + SPI0_CTRLA |= SPI_ENABLE_bm; + } + + void setSPIRate() { + SPI0_CTRLA &= ~ ( (1<= 128) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 64) { SPI0_CTRLA |= SPI_PRESC1_bm; } + else if (_SPI_CLOCK_DIVIDER >= 32) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 16) { SPI0_CTRLA |= SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 8) { SPI0_CTRLA |= SPI_PRESC0_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 4) { /* default rate */ } + else { SPI0_CTRLA |= SPI_CLK2X_bm; } + } + + static void stop() { + SPI0_CTRLA &= ~(SPI_ENABLE_bm); + } + + static bool shouldWait(bool wait = false) __attribute__((always_inline)) { + static bool sWait=false; + if(sWait) { + sWait = wait; return true; + } else { + sWait = wait; return false; + } + } + static void wait() __attribute__((always_inline)) { + if(shouldWait()) { + while(!(SPI0_INTFLAGS & SPI_IF_bm)); + } + } + static void waitFully() __attribute__((always_inline)) { wait(); } + + static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPI0_DATA=b; shouldWait(true); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPI0_DATA=b; shouldWait(true); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPI0_DATA=b; shouldWait(true); } + + + template inline static void writeBit(uint8_t b) { + if(b && (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + FastPin<_CLOCK_PIN>::lo(); + } + + void enable_pins() { } + void disable_pins() { } + + void select() { + if(m_pSelect != NULL) { + m_pSelect->select(); + } + enable_pins(); + setSPIRate(); + } + + void release() { + if(m_pSelect != NULL) { + m_pSelect->release(); + } + disable_pins(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { + writeByte(value); + } + } + + void writeBytesValue(uint8_t value, int len) { + //setSPIRate(); + select(); + while(len--) { + writeByte(value); + } + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + //setSPIRate(); + uint8_t *end = data + len; + select(); + while(data != end) { + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); + } + release(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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) { + //setSPIRate(); + int len = pixels.mLen; + + select(); + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + writeBytePostWait(D::adjust(pixels.loadAndScale0())); + writeBytePostWait(D::adjust(pixels.loadAndScale1())); + writeBytePostWait(D::adjust(pixels.loadAndScale2())); + } else { + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + } + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + release(); + } +}; + +#endif + +#if defined(SPSR) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware SPI support using SPDR registers and friends +// +// Technically speaking, this uses the AVR SPI registers. This will work on the Teensy 3.0 because Paul made a set of compatability +// classes that map the AVR SPI registers to ARM's, however this caps the performance of output. +// +// TODO: implement ARMHardwareSPIOutput +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +class AVRHardwareSPIOutput { + Selectable *m_pSelect; + bool mWait; + +public: + AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;} + AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void setSPIRate() { + SPCR &= ~ ( (1<= 128) { SPCR |= (1<= 64) { SPCR |= (1<= 32) { SPCR |= (1<= 16) { SPCR |= (1<= 8) { SPCR |= (1<= 4) { /* do nothing - default rate */ } + else { b2x = true; } + + if(b2x) { SPSR |= (1<::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); +#ifdef SPI_SELECT + // Make sure the slave select line is set to output, or arduino will block us + FastPin::setOutput(); + FastPin::lo(); +#endif + + SPCR |= ((1<= 128) { SPCR |= (1<= 64) { SPCR |= (1<= 32) { SPCR |= (1<= 16) { SPCR |= (1<= 8) { SPCR |= (1<= 4) { /* do nothing - default rate */ } + else { b2x = true; } + + if(b2x) { SPSR |= (1<>8); writeByte(w&0xFF); } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPDR=b; shouldWait(true); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); } + + template inline static void writeBit(uint8_t b) { + SPCR &= ~(1 << SPE); + if(b & (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + FastPin<_CLOCK_PIN>::lo(); + SPCR |= 1 << SPE; + shouldWait(false); + } + + void enable_pins() { + SPCR |= ((1<select(); } + enable_pins(); + setSPIRate(); + } + + void release() { + if(m_pSelect != NULL) { m_pSelect->release(); } + disable_pins(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { writeByte(value); } + } + + void writeBytesValue(uint8_t value, int len) { + //setSPIRate(); + select(); + while(len--) { + writeByte(value); + } + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + //setSPIRate(); + uint8_t *end = data + len; + select(); + while(data != end) { + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); + } + release(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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) { + //setSPIRate(); + int len = pixels.mLen; + + select(); + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + writeBytePostWait(D::adjust(pixels.loadAndScale0())); + writeBytePostWait(D::adjust(pixels.loadAndScale1())); + writeBytePostWait(D::adjust(pixels.loadAndScale2())); + } else { + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + } + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + waitFully(); + release(); + } +}; +#elif defined(SPSR0) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware SPI support using SPDR0 registers and friends +// +// Technically speaking, this uses the AVR SPI registers. This will work on the Teensy 3.0 because Paul made a set of compatability +// classes that map the AVR SPI registers to ARM's, however this caps the performance of output. +// +// TODO: implement ARMHardwareSPIOutput +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +class AVRHardwareSPIOutput { + Selectable *m_pSelect; + bool mWait; + +public: + AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;} + AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void setSPIRate() { + SPCR0 &= ~ ( (1<= 128) { SPCR0 |= (1<= 64) { SPCR0 |= (1<= 32) { SPCR0 |= (1<= 16) { SPCR0 |= (1<= 8) { SPCR0 |= (1<= 4) { /* do nothing - default rate */ } + else { b2x = true; } + + if(b2x) { SPSR0 |= (1<::setOutput(); + FastPin<_CLOCK_PIN>::setOutput(); +#ifdef SPI_SELECT + // Make sure the slave select line is set to output, or arduino will block us + FastPin::setOutput(); + FastPin::lo(); +#endif + + SPCR0 |= ((1<= 128) { SPCR0 |= (1<= 64) { SPCR0 |= (1<= 32) { SPCR0 |= (1<= 16) { SPCR0 |= (1<= 8) { SPCR0 |= (1<= 4) { /* do nothing - default rate */ } + else { b2x = true; } + + if(b2x) { SPSR0 |= (1<>8); writeByte(w&0xFF); } + + static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPDR0=b; shouldWait(true); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPDR0=b; shouldWait(true); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR0=b; shouldWait(true); } + + template inline static void writeBit(uint8_t b) { + SPCR0 &= ~(1 << SPE); + if(b & (1 << BIT)) { + FastPin<_DATA_PIN>::hi(); + } else { + FastPin<_DATA_PIN>::lo(); + } + + FastPin<_CLOCK_PIN>::hi(); + FastPin<_CLOCK_PIN>::lo(); + SPCR0 |= 1 << SPE; + shouldWait(false); + } + + void enable_pins() { + SPCR0 |= ((1<select(); } + enable_pins(); + setSPIRate(); + } + + void release() { + if(m_pSelect != NULL) { m_pSelect->release(); } + disable_pins(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { writeByte(value); } + } + + void writeBytesValue(uint8_t value, int len) { + //setSPIRate(); + select(); + while(len--) { + writeByte(value); + } + release(); + } + + // Write a block of n uint8_ts out + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + //setSPIRate(); + uint8_t *end = data + len; + select(); + while(data != end) { + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); + } + release(); + } + + void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes(data, len); } + + // 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) { + //setSPIRate(); + int len = pixels.mLen; + + select(); + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + writeBytePostWait(D::adjust(pixels.loadAndScale0())); + writeBytePostWait(D::adjust(pixels.loadAndScale1())); + writeBytePostWait(D::adjust(pixels.loadAndScale2())); + } else { + 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 + +#else +// #define FASTLED_FORCE_SOFTWARE_SPI +#endif + +FASTLED_NAMESPACE_END; + + +#endif diff --git a/lib/FastLED/src/platforms/avr/led_sysdefs_avr.h b/lib/FastLED/src/platforms/avr/led_sysdefs_avr.h new file mode 100644 index 00000000..5ac651bf --- /dev/null +++ b/lib/FastLED/src/platforms/avr/led_sysdefs_avr.h @@ -0,0 +1,70 @@ +#ifndef __INC_LED_SYSDEFS_AVR_H +#define __INC_LED_SYSDEFS_AVR_H + +#define FASTLED_AVR + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 2 +#endif + +#define FASTLED_SPI_BYTE_ONLY + +#include +#include // for cli/se definitions + +// Define the register types +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) */ + + +// Default to disallowing interrupts (may want to gate this on teensy2 vs. other arm platforms, since the +// teensy2 has a good, fast millis interrupt implementation) +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 0 +#endif + +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + + +// Default to using PROGMEM here +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + +#if defined(ARDUINO_AVR_DIGISPARK) || defined(ARDUINO_AVR_DIGISPARKPRO) +#ifndef NO_CORRECTION +#define NO_CORRECTION 1 +#endif +#endif + +extern "C" { +# if defined(CORE_TEENSY) || defined(TEENSYDUINO) +extern volatile unsigned long timer0_millis_count; +# define MS_COUNTER 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 +# endif +}; + +// special defs for the tiny environments +#if defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny48__) || defined(__AVR_ATtiny88__) || defined(__AVR_ATtinyX41__) || defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny441__) +#define LIB8_ATTINY 1 +#define FASTLED_NEEDS_YIELD +#endif + +#if defined(ARDUINO) && (ARDUINO > 150) && !defined(IS_BEAN) && !defined (ARDUINO_AVR_DIGISPARK) && !defined (LIB8_TINY) && !defined (ARDUINO_AVR_LARDU_328E) +// don't need YIELD defined by the library +#else +#define FASTLED_NEEDS_YIELD +extern "C" void yield(); +#endif +#endif diff --git a/lib/FastLED/src/platforms/esp/32/clockless_block_esp32.h b/lib/FastLED/src/platforms/esp/32/clockless_block_esp32.h new file mode 100644 index 00000000..97d715c0 --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/clockless_block_esp32.h @@ -0,0 +1,172 @@ +#ifndef __INC_CLOCKLESS_BLOCK_ESP8266_H +#define __INC_CLOCKLESS_BLOCK_ESP8266_H + +#define FASTLED_HAS_BLOCKLESS 1 + +#define PORT_MASK (((1< +class InlineBlockClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + // Verify that the pin is valid + static_assert(FastPin::validpin(), "Invalid pin specified"); + + data_t mPinMask; + data_ptr_t mPort; + CMinWait mWait; + +public: + virtual int size() { return CLEDController::size() * LANES; } + + virtual void showPixels(PixelController & pixels) { + // mWait.wait(); + /*uint32_t clocks = */ + int cnt=FASTLED_INTERRUPT_RETRY_COUNT; + while(!showRGBInternal(pixels) && cnt--) { + ets_intr_unlock(); +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_retry_cnt; +#endif + delayMicroseconds(WAIT_TIME * 10); + ets_intr_lock(); + } + // #if FASTLED_ALLOW_INTTERUPTS == 0 + // Adjust the timer + // long microsTaken = CLKS_TO_MICROS(clocks); + // MS_COUNTER += (1 + (microsTaken / 1000)); + // #endif + + // mWait.mark(); + } + + template static void initPin() { + if(PIN >= REAL_FIRST_PIN && PIN <= LAST_PIN) { + _ESPPIN::setOutput(); + // FastPin::setOutput(); + } + } + + virtual void init() { + // Only supportd on pins 12-15 + // SZG: This probably won't work (check pins definitions in fastpin_esp32) + initPin<12>(); + initPin<13>(); + initPin<14>(); + initPin<15>(); + mPinMask = FastPin::mask(); + mPort = FastPin::port(); + + // Serial.print("Mask is "); Serial.println(PORT_MASK); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + typedef union { + uint8_t bytes[8]; + uint16_t shorts[4]; + uint32_t raw[2]; + } Lines; + +#define ESP_ADJUST 0 // (2*(F_CPU/24000000)) +#define ESP_ADJUST2 0 + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & last_mark, FASTLED_REGISTER Lines & b, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + Lines b2 = b; + transpose8x1_noinline(b.bytes,b2.bytes); + + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_REGISTER uint8_t scale = pixels.template getscale(pixels); + + for(FASTLED_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; + + uint32_t nword = ((uint32_t)(~b2.bytes[7-i]) & PORT_MASK) << REAL_FIRST_PIN; + while((__clock_cycles() - last_mark) < (T1-6)); + *FastPin::cport() = nword; + + while((__clock_cycles() - last_mark) < (T1+T2)); + *FastPin::cport() = PORT_MASK << REAL_FIRST_PIN; + + b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + } + + for(FASTLED_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; + + uint32_t nword = ((uint32_t)(~b2.bytes[7-i]) & PORT_MASK) << REAL_FIRST_PIN; + while((__clock_cycles() - last_mark) < (T1-6)); + *FastPin::cport() = nword; + + while((__clock_cycles() - last_mark) < (T1+T2)); + *FastPin::cport() = PORT_MASK << REAL_FIRST_PIN; + } + } + + // 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 &allpixels) { + + // Setup the pixel controller and load/scale the first byte + Lines b0; + + for(int i = 0; i < USED_LANES; ++i) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + allpixels.preStepFirstByteDithering(); + + ets_intr_lock(); + uint32_t _start = __clock_cycles(); + uint32_t last_mark = _start; + + while(allpixels.has(1)) { + // Write first byte, read next byte + writeBits<8+XTRA0,1>(last_mark, b0, allpixels); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0,2>(last_mark, b0, allpixels); + allpixels.advanceData(); + + // Write third byte + writeBits<8+XTRA0,0>(last_mark, b0, allpixels); + +#if (FASTLED_ALLOW_INTERRUPTS == 1) + ets_intr_unlock(); +#endif + + allpixels.stepDithering(); + +#if (FASTLED_ALLOW_INTERRUPTS == 1) + ets_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))) { ets_intr_unlock(); return 0; } + } +#endif + }; + + ets_intr_unlock(); +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_frame_cnt; +#endif + return __clock_cycles() - _start; + } +}; + +FASTLED_NAMESPACE_END +#endif diff --git a/lib/FastLED/src/platforms/esp/32/clockless_i2s_esp32.h b/lib/FastLED/src/platforms/esp/32/clockless_i2s_esp32.h new file mode 100644 index 00000000..845d625b --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/clockless_i2s_esp32.h @@ -0,0 +1,781 @@ +/* + * I2S Driver + * + * Copyright (c) 2019 Yves Bazin + * Copyright (c) 2019 Samuel Z. Guyer + * Derived from lots of code examples from other people. + * + * The I2S implementation can drive up to 24 strips in parallel, but + * with the following limitation: all the strips must have the same + * timing (i.e., they must all use the same chip). + * + * To enable the I2S driver, add the following line *before* including + * FastLED.h (no other changes are necessary): + * + * #define FASTLED_ESP32_I2S true + * + * The overall strategy is to use the parallel mode of the I2S "audio" + * peripheral to send up to 24 bits in parallel to 24 different pins. + * Unlike the RMT peripheral the I2S system cannot send bits of + * different lengths. Instead, we set the I2S data clock fairly high + * and then encode a signal as a series of bits. + * + * For example, with a clock divider of 10 the data clock will be + * 8MHz, so each bit is 125ns. The WS2812 expects a "1" bit to be + * encoded as a HIGH signal for around 875ns, followed by LOW for + * 375ns. Sending the following pattern results in the right shape + * signal: + * + * 1111111000 WS2812 "1" bit encoded as 10 125ns pulses + * + * The I2S peripheral expects the bits for all 24 outputs to be packed + * into a single 32-bit word. The complete signal is a series of these + * 32-bit values -- one for each bit for each strip. The pixel data, + * however, is stored "serially" as a series of RGB values separately + * for each strip. To prepare the data we need to do three things: (1) + * take 1 pixel from each strip, and (2) tranpose the bits so that + * they are in the parallel form, (3) translate each data bit into the + * bit pattern that encodes the signal for that bit. This code is in + * the fillBuffer() method: + * + * 1. Read 1 pixel from each strip into an array; store this data by + * color channel (e.g., all the red bytes, then all the green + * bytes, then all the blue bytes). For three color channels, the + * array is 3 X 24 X 8 bits. + * + * 2. Tranpose the array so that it is 3 X 8 X 24 bits. The hardware + * wants the data in 32-bit chunks, so the actual form is 3 X 8 X + * 32, with the low 8 bits unused. + * + * 3. Take each group of 24 parallel bits and "expand" them into a + * pattern according to the encoding. For example, with a 8MHz + * data clock, each data bit turns into 10 I2s pulses, so 24 + * parallel data bits turn into 10 X 24 pulses. + * + * We send data to the I2S peripheral using the DMA interface. We use + * two DMA buffers, so that we can fill one buffer while the other + * buffer is being sent. Each DMA buffer holds the fully-expanded + * pulse pattern for one pixel on up to 24 strips. The exact amount of + * memory required depends on the number of color channels and the + * number of pulses used to encode each bit. + * + * We get an interrupt each time a buffer is sent; we then fill that + * buffer while the next one is being sent. The DMA interface allows + * us to configure the buffers as a circularly linked list, so that it + * can automatically start on the next buffer. + */ +/* + * 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 + +#ifndef FASTLED_INTERNAL +#pragma message "NOTE: ESP32 support using I2S parallel driver. All strips must use the same chipset" +#endif + +FASTLED_NAMESPACE_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_heap_caps.h" +#include "soc/soc.h" +#include "soc/gpio_sig_map.h" +#include "soc/i2s_reg.h" +#include "soc/i2s_struct.h" +#include "soc/io_mux_reg.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "rom/lldesc.h" +#include "esp_system.h" // Load ESP_IDF_VERSION_MAJOR if exists +// ESP_IDF_VERSION_MAJOR is defined in ESP-IDF v3.3 or later +#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR > 3 +#include "esp_intr_alloc.h" +#else +#include "esp_intr.h" +#endif +#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 +#define NUM_COLOR_CHANNELS 3 + +// -- Choose which I2S device to use +#ifndef I2S_DEVICE +#define I2S_DEVICE 0 +#endif + +// -- Max number of controllers we can support +#ifndef FASTLED_I2S_MAX_CONTROLLERS +#define FASTLED_I2S_MAX_CONTROLLERS 24 +#endif + +// -- I2S clock +#define I2S_BASE_CLK (80000000L) +#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s looses some bits +#define I2S_MAX_PULSE_PER_BIT 20 //put it higher to get more accuracy but it could decrease the refresh rate without real improvement +// -- Convert ESP32 cycles back into nanoseconds +#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ) + +// -- Array of all controllers +static CLEDController * gControllers[FASTLED_I2S_MAX_CONTROLLERS]; +static int gNumControllers = 0; +static int gNumStarted = 0; + +// -- Global semaphore for the whole show process +// Semaphore is not given until all data has been sent +static xSemaphoreHandle gTX_sem = NULL; + +// -- One-time I2S initialization +static bool gInitialized = false; + +// -- Interrupt handler +static intr_handle_t gI2S_intr_handle = NULL; + +// -- A pointer to the memory-mapped structure: I2S0 or I2S1 +static i2s_dev_t * i2s; + +// -- I2S goes to these pins until we remap them using the GPIO matrix +static int i2s_base_pin_index; + +// --- I2S DMA buffers +struct DMABuffer { + lldesc_t descriptor; + uint8_t * buffer; +}; + +#define NUM_DMA_BUFFERS 2 +static DMABuffer * dmaBuffers[NUM_DMA_BUFFERS]; + +// -- Bit patterns +// For now, we require all strips to be the same chipset, so these +// are global variables. + +static int gPulsesPerBit = 0; +static uint32_t gOneBit[40] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static uint32_t gZeroBit[40] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +// -- Counters to track progress +static int gCurBuffer = 0; +static bool gDoneFilling = false; +static int ones_for_one; +static int ones_for_zero; + +// -- Temp buffers for pixels and bits being formatted for DMA +static uint8_t gPixelRow[NUM_COLOR_CHANNELS][32]; +static uint8_t gPixelBits[NUM_COLOR_CHANNELS][8][4]; +static int CLOCK_DIVIDER_N; +static int CLOCK_DIVIDER_A; +static int CLOCK_DIVIDER_B; + +template +class ClocklessController : public CPixelLEDController +{ + // -- Store the GPIO pin + gpio_num_t mPin; + + // -- Verify that the pin is valid + static_assert(FastPin::validpin(), "Invalid pin specified"); + + // -- 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/lib/FastLED/src/platforms/esp/32/clockless_rmt_esp32.cpp b/lib/FastLED/src/platforms/esp/32/clockless_rmt_esp32.cpp new file mode 100644 index 00000000..57c39a71 --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/clockless_rmt_esp32.cpp @@ -0,0 +1,559 @@ + +#ifdef ESP32 + +#ifndef FASTLED_ESP32_I2S + +#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; + +// -- Stored values for FASTLED_RMT_MAX_CHANNELS and FASTLED_RMT_MEM_BLOCKS +int ESP32RMTController::gMaxChannel; +int ESP32RMTController::gMemBlocks; + + +ESP32RMTController::ESP32RMTController(int DATA_PIN, int T1, int T2, int T3, int maxChannel, int memBlocks) + : mPixelData(0), + mSize(0), + mCur(0), + mBufSize(0), + mWhichHalf(0), + mBuffer(0), + mBufferSize(0), + mCurPulse(0) +{ + // -- Store the max channel and mem blocks parameters + gMaxChannel = maxChannel; + gMemBlocks = memBlocks; + + // -- 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. +uint8_t * ESP32RMTController::getPixelBuffer(int size_in_bytes) +{ + // -- Free the old buffer if it will be too small + if (mPixelData != 0 and mBufSize < size_in_bytes) { + free(mPixelData); + mPixelData = 0; + } + + if (mPixelData == 0) { + mBufSize = size_in_bytes; + mPixelData = (uint8_t *) malloc(mBufSize); + } + + mSize = size_in_bytes; + + return mPixelData; +} + +// -- Initialize RMT subsystem +// This only needs to be done once +void ESP32RMTController::init(gpio_num_t pin) +{ + if (gInitialized) return; + esp_err_t espErr = ESP_OK; + + for (int i = 0; i < gMaxChannel; i += gMemBlocks) { + gOnChannel[i] = NULL; + + // -- RMT configuration for transmission + rmt_config_t rmt_tx; + memset(&rmt_tx, 0, sizeof(rmt_config_t)); + rmt_tx.channel = rmt_channel_t(i); + rmt_tx.rmt_mode = RMT_MODE_TX; + rmt_tx.gpio_num = pin; + rmt_tx.mem_block_num = gMemBlocks; + 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 + espErr = rmt_config(&rmt_tx); + FASTLED_DEBUG("rmt_config result: %d", espErr); + + 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) + espErr = rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, PULSES_PER_FILL); + FASTLED_DEBUG("rmt_set_tx_thr_intr_en result: %d", espErr); + } + } + + // -- 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; + (void)espErr; +} + +// -- Show this string of pixels +// This is the main entry point for the pixel controller +void IRAM_ATTR ESP32RMTController::showPixels() +{ + if (gNumStarted == 0) { + // -- First controller: make sure everything is set up + ESP32RMTController::init(mPin); + +#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 < gMaxChannel && 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 += gMemBlocks; + } + + // -- 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 IRAM_ATTR 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 IRAM_ATTR ESP32RMTController::startOnChannel(int channel) +{ + esp_err_t espErr = ESP_OK; + // -- 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 +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) + espErr = rmt_set_gpio(mRMT_channel, RMT_MODE_TX, mPin, false); + FASTLED_DEBUG("rmt_set_gpio result: %d", espErr); +#else + espErr = rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin); + FASTLED_DEBUG("rrmt_set_pin result: %d", espErr); +#endif + + 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 + espErr = rmt_set_tx_intr_en(mRMT_channel, true); + FASTLED_DEBUG("rmt_set_tx_intr_en result: %d", espErr); + + // -- Kick off the transmission + tx_start(); + } + (void)espErr; +} + +// -- Start RMT transmission +// Setting this RMT flag is what actually kicks off the peripheral +void IRAM_ATTR ESP32RMTController::tx_start() +{ + // rmt_tx_start(mRMT_channel, true); + // Inline the code for rmt_tx_start, so it can be placed in IRAM +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + // rmt_ll_tx_reset_pointer(&RMT, mRMT_channel) + RMT.tx_conf[mRMT_channel].mem_rd_rst = 1; + RMT.tx_conf[mRMT_channel].mem_rd_rst = 0; + RMT.tx_conf[mRMT_channel].mem_rst = 1; + RMT.tx_conf[mRMT_channel].mem_rst = 0; + // rmt_ll_clear_tx_end_interrupt(&RMT, mRMT_channel) + RMT.int_clr.val = (1 << (mRMT_channel)); + // rmt_ll_enable_tx_end_interrupt(&RMT, mRMT_channel, true) + RMT.int_ena.val |= (1 << mRMT_channel); + // rmt_ll_tx_start(&RMT, mRMT_channel) + RMT.tx_conf[mRMT_channel].conf_update = 1; + RMT.tx_conf[mRMT_channel].tx_start = 1; +#elif CONFIG_IDF_TARGET_ESP32S3 + // rmt_ll_tx_reset_pointer(&RMT, mRMT_channel) + RMT.chnconf0[mRMT_channel].mem_rd_rst_n = 1; + RMT.chnconf0[mRMT_channel].mem_rd_rst_n = 0; + RMT.chnconf0[mRMT_channel].apb_mem_rst_n = 1; + RMT.chnconf0[mRMT_channel].apb_mem_rst_n = 0; + // rmt_ll_clear_tx_end_interrupt(&RMT, mRMT_channel) + RMT.int_clr.val = (1 << (mRMT_channel)); + // rmt_ll_enable_tx_end_interrupt(&RMT, mRMT_channel, true) + RMT.int_ena.val |= (1 << mRMT_channel); + // rmt_ll_tx_start(&RMT, mRMT_channel) + RMT.chnconf0[mRMT_channel].conf_update_n = 1; + RMT.chnconf0[mRMT_channel].tx_start_n = 1; +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + // rmt_ll_tx_reset_pointer(&RMT, mRMT_channel) + RMT.conf_ch[mRMT_channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[mRMT_channel].conf1.mem_rd_rst = 0; + // rmt_ll_clear_tx_end_interrupt(&RMT, mRMT_channel) + RMT.int_clr.val = (1 << (mRMT_channel * 3)); + // rmt_ll_enable_tx_end_interrupt(&RMT, mRMT_channel, true) + RMT.int_ena.val &= ~(1 << (mRMT_channel * 3)); + RMT.int_ena.val |= (1 << (mRMT_channel * 3)); + // rmt_ll_tx_start(&RMT, mRMT_channel) + RMT.conf_ch[mRMT_channel].conf1.tx_start = 1; +#else + #error Not yet implemented for unknown ESP32 target +#endif + 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 IRAM_ATTR 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); + + // Inline the code for rmt_set_tx_intr_en(channel, false) and rmt_tx_stop, so it can be placed in IRAM +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << channel); + // rmt_ll_tx_stop(&RMT, channel) + RMT.tx_conf[channel].tx_stop = 1; + RMT.tx_conf[channel].conf_update = 1; + // rmt_ll_tx_reset_pointer(&RMT, channel) + RMT.tx_conf[channel].mem_rd_rst = 1; + RMT.tx_conf[channel].mem_rd_rst = 0; + RMT.tx_conf[channel].mem_rst = 1; + RMT.tx_conf[channel].mem_rst = 0; +#elif CONFIG_IDF_TARGET_ESP32S3 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << channel); + // rmt_ll_tx_stop(&RMT, channel) + RMT.chnconf0[channel].tx_stop_n = 1; + RMT.chnconf0[channel].conf_update_n = 1; + // rmt_ll_tx_reset_pointer(&RMT, channel) + RMT.chnconf0[channel].mem_rd_rst_n = 1; + RMT.chnconf0[channel].mem_rd_rst_n = 0; + RMT.chnconf0[channel].apb_mem_rst_n = 1; + RMT.chnconf0[channel].apb_mem_rst_n = 0; +#elif CONFIG_IDF_TARGET_ESP32S2 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << (channel * 3)); + // rmt_ll_tx_stop(&RMT, channel) + RMT.conf_ch[channel].conf1.tx_stop = 1; + // rmt_ll_tx_reset_pointer(&RMT, channel) + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_rd_rst = 0; +#elif CONFIG_IDF_TARGET_ESP32 + // rmt_ll_enable_tx_end_interrupt(&RMT, channel) + RMT.int_ena.val &= ~(1 << (channel * 3)); + // rmt_ll_tx_stop(&RMT, channel) + RMT.conf_ch[channel].conf1.tx_start = 0; + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_rd_rst = 0; + // rmt_ll_tx_reset_pointer(&RMT, channel) + // RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + // RMT.conf_ch[channel].conf1.mem_rd_rst = 0; +#else + #error Not yet implemented for unknown ESP32 target +#endif + + 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; + + for (channel = 0; channel < gMaxChannel; channel++) { + #if CONFIG_IDF_TARGET_ESP32S2 + int tx_done_bit = channel * 3; + int tx_next_bit = channel + 12; + #elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + int tx_done_bit = channel; + int tx_next_bit = channel + 8; + #elif CONFIG_IDF_TARGET_ESP32 + int tx_done_bit = channel * 3; + int tx_next_bit = channel + 24; + #else + #error Not yet implemented for unknown ESP32 target + #endif + + 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) { + int32_t delta = (now - mLastFill); + if (delta > (int32_t)mMaxCyclesPerFill) { + // Serial.print(delta); + // Serial.print(" BAIL "); + // Serial.println(mCur); + // rmt_tx_stop(mRMT_channel); + // Inline the code for rmt_tx_stop, so it can be placed in IRAM + /** -- Go back to the original strategy of just setting mCur = mSize + and letting the regular 'stop' process happen + * mRMT_mem_start = 0; + RMT.int_ena.val &= ~(1 << (mRMT_channel * 3)); + RMT.conf_ch[mRMT_channel].conf1.tx_start = 0; + RMT.conf_ch[mRMT_channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[mRMT_channel].conf1.mem_rd_rst = 0; + */ + mCur = mSize; + } + } + } + mLastFill = now; + + // -- Get the zero and one values into local variables + FASTLED_REGISTER uint32_t one_val = mOne.val; + FASTLED_REGISTER uint32_t zero_val = mZero.val; + + // -- Use locals for speed + volatile FASTLED_REGISTER uint32_t * pItem = mRMT_mem_ptr; + + for (FASTLED_REGISTER int i = 0; i < PULSES_PER_FILL/8; i++) { + if (mCur < mSize) { + + // -- Get the next four bytes of pixel data + FASTLED_REGISTER uint32_t pixeldata = mPixelData[mCur] << 24; + 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 (FASTLED_REGISTER uint32_t j = 0; j < 8; 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 by filling the + // rest of the buffer with zeros + *pItem++ = 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 (FASTLED_REGISTER uint32_t j = 0; j < 8; j++) { + mBuffer[mCurPulse] = (byteval & 0x80000000L) ? mOne : mZero; + byteval <<= 1; + mCurPulse++; + } +} + +#endif // ! FASTLED_ESP32_I2S + +#endif // ESP32 diff --git a/lib/FastLED/src/platforms/esp/32/clockless_rmt_esp32.h b/lib/FastLED/src/platforms/esp/32/clockless_rmt_esp32.h new file mode 100644 index 00000000..19afe3da --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/clockless_rmt_esp32.h @@ -0,0 +1,435 @@ +/* + * 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. + * + * #define FASTLED_RMT_SERIAL_DEBUG 1 + * + * NEW (Oct 2021): If set enabled (Set to 1), output errorcodes to + * Serial for debugging if not ESP_OK. Might be useful to find + * bugs or problems with GPIO PINS. + * + * + * 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" +// ESP_IDF_VERSION_MAJOR is defined in ESP-IDF v3.3 or later +#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR > 3 +#include "esp_intr_alloc.h" +#else +#include "esp_intr.h" +#endif +#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; +#ifdef FASTLED_XTENSA + __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc)); +#else + cyc = cpu_hal_get_cycle_count(); +#endif + 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 + +#ifndef FASTLED_RMT_SERIAL_DEBUG +#define FASTLED_RMT_SERIAL_DEBUG 1 +#endif + +#if FASTLED_RMT_SERIAL_DEBUG == 1 +#define FASTLED_DEBUG(format, errcode, ...) if (errcode != ESP_OK) { Serial.printf(PSTR("FASTLED: " format "\n"), errcode, ##__VA_ARGS__); } +#else +#define FASTLED_DEBUG(format, errcode, ...) (void) errcode; +#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 + +// 64 for ESP32, ESP32S2 +// 48 for ESP32S3, ESP32C3, ESP32H2 +#ifndef FASTLED_RMT_MEM_WORDS_PER_CHANNEL +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) +#define FASTLED_RMT_MEM_WORDS_PER_CHANNEL SOC_RMT_MEM_WORDS_PER_CHANNEL +#else +// ESP32 value (only chip variant supported on older IDF) +#define FASTLED_RMT_MEM_WORDS_PER_CHANNEL 64 +#endif +#endif + +#define MAX_PULSES (FASTLED_RMT_MEM_WORDS_PER_CHANNEL * FASTLED_RMT_MEM_BLOCKS) +#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 +// RMT Clock is typically APB CLK, which is 80MHz on most devices, but 40MHz on ESP32-H2 +#define F_CPU_RMT ( APB_CLK_FREQ ) +#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(500000) + +// -- 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 + +// -- Max RMT TX channel +#ifndef FASTLED_RMT_MAX_CHANNELS +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) +// 8 for (ESP32) 4 for (ESP32S2, ESP32S3) 2 for (ESP32C3, ESP32H2) +#define FASTLED_RMT_MAX_CHANNELS SOC_RMT_TX_CANDIDATES_PER_GROUP +#else +#ifdef CONFIG_IDF_TARGET_ESP32S2 +#define FASTLED_RMT_MAX_CHANNELS 4 +#else +#define FASTLED_RMT_MAX_CHANNELS 8 +#endif +#endif +#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 + uint8_t * mPixelData; + int mSize; + int mCur; + int mBufSize; + + // -- 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; + + // -- These values need to be real variables, so we can access them + // in the cpp file + static int gMaxChannel; + static int gMemBlocks; + +public: + + // -- Constructor + // Mainly just stores the template parameters from the LEDController as + // member variables. + ESP32RMTController(int DATA_PIN, int T1, int T2, int T3, int maxChannel, int memBlocks); + + // -- Get max cycles per fill + uint32_t IRAM_ATTR getMaxCyclesPerFill() const { return mMaxCyclesPerFill; } + + // -- Get or create the pixel data buffer + uint8_t * getPixelBuffer(int size_in_bytes); + + // -- Initialize RMT subsystem + // This only needs to be done once. The particular pin is not important, + // because we need to configure the RMT channels on the fly. + static void init(gpio_num_t pin); + + // -- 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; + + // -- Verify that the pin is valid + static_assert(FastPin::validpin(), "Invalid pin specified"); + +public: + + ClocklessController() + : mRMTController(DATA_PIN, T1, T2, T3, FASTLED_RMT_MAX_CHANNELS, FASTLED_RMT_MEM_BLOCKS) + {} + + void init() + { + } + + 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; + uint8_t * pData = mRMTController.getPixelBuffer(size_in_bytes); + + // -- This might be faster + while (pixels.has(1)) { + *pData++ = pixels.loadAndScale0(); + *pData++ = pixels.loadAndScale1(); + *pData++ = pixels.loadAndScale2(); + pixels.advanceData(); + pixels.stepDithering(); + } + } + + // -- 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/lib/FastLED/src/platforms/esp/32/fastled_esp32.h b/lib/FastLED/src/platforms/esp/32/fastled_esp32.h new file mode 100755 index 00000000..6e9a1c59 --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/fastled_esp32.h @@ -0,0 +1,15 @@ +#pragma once + +#include "fastpin_esp32.h" + +#ifdef FASTLED_ALL_PINS_HARDWARE_SPI +#include "fastspi_esp32.h" +#endif + +#ifdef FASTLED_ESP32_I2S +#include "clockless_i2s_esp32.h" +#else +#include "clockless_rmt_esp32.h" +#endif + +// #include "clockless_block_esp32.h" diff --git a/lib/FastLED/src/platforms/esp/32/fastpin_esp32.h b/lib/FastLED/src/platforms/esp/32/fastpin_esp32.h new file mode 100644 index 00000000..5249408b --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/fastpin_esp32.h @@ -0,0 +1,159 @@ +#pragma once + +FASTLED_NAMESPACE_BEGIN + +template class _ESPPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + static constexpr bool validpin() { return VALIDPIN; } + +#ifndef GPIO_OUT1_REG + static constexpr uint32_t GPIO_REG = GPIO_OUT_REG; + static constexpr uint32_t GPIO_BIT_SET_REG = GPIO_OUT_W1TS_REG; + static constexpr uint32_t GPIO_BIT_CLEAR_REG = GPIO_OUT_W1TC_REG; + #else + static constexpr uint32_t GPIO_REG = PIN < 32 ? GPIO_OUT_REG : GPIO_OUT1_REG; + static constexpr uint32_t GPIO_BIT_SET_REG = PIN < 32 ? GPIO_OUT_W1TS_REG : GPIO_OUT1_W1TS_REG; + static constexpr uint32_t GPIO_BIT_CLEAR_REG = PIN < 32 ? GPIO_OUT_W1TC_REG : GPIO_OUT1_W1TC_REG; + #endif + + inline static void setOutput() { + static_assert(validpin(), "Invalid pin specified"); + pinMode(PIN, OUTPUT); + } + inline static void setInput() { pinMode(PIN, INPUT); } + + inline static void hi() __attribute__ ((always_inline)) { + *sport() = MASK; + } + + inline static void lo() __attribute__ ((always_inline)) { + *cport() = MASK; + } + + inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { + *port() = val; + } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { + *port() ^= MASK; + } + + inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { + return (*port()) | MASK; + } + + inline static port_t loval() __attribute__ ((always_inline)) { + return (*port()) & ~MASK; + } + + inline static port_ptr_t port() __attribute__ ((always_inline)) { + return (port_ptr_t)GPIO_REG; + } + + inline static port_ptr_t sport() __attribute__ ((always_inline)) { + return (port_ptr_t)GPIO_BIT_SET_REG; + } + + inline static port_ptr_t cport() __attribute__ ((always_inline)) { + return (port_ptr_t)GPIO_BIT_CLEAR_REG; + } + + inline static port_t mask() __attribute__ ((always_inline)) { return MASK; } + + inline static bool isset() __attribute__ ((always_inline)) { + return (*port()) & MASK; + } +}; + +#ifndef FASTLED_UNUSABLE_PIN_MASK + +#define _FL_BIT(B) (1ULL << B) + +#if CONFIG_IDF_TARGET_ESP32 +// 40 GPIO pins. ESPIDF defined 24, 28-31 as invalid and 34-39 as readonly +// GPIO 6-11 used by default for SPI flash. GPIO 20 is invalid. +// NOTE: GPIO 1 & 3 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(6) | _FL_BIT(7) | _FL_BIT(8) | _FL_BIT(9) | _FL_BIT(10) | _FL_BIT(20)) + +#elif CONFIG_IDF_TARGET_ESP32C3 +// 22 GPIO pins. ESPIDF defines all pins as valid +// GPIO 11-17 used by default for SPI flash +// NOTE: GPIO 20-21 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(11) | _FL_BIT(12) | _FL_BIT(13) | _FL_BIT(14) | _FL_BIT(15) | _FL_BIT(16) | _FL_BIT(17)) + +#elif CONFIG_IDF_TARGET_ESP32S2 +// 48 GPIO pins. ESPIDF defines 22-25, 47 as invalid and 46-47 as readonly.s +// GPIO 27-32 used by default for SPI flash. +// NOTE: GPIO 37 & 38 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(27) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31) | _FL_BIT(32)) + +#elif CONFIG_IDF_TARGET_ESP32S3 +// 49 GPIO pins. ESPIDF defineds 22-25 as invalid. +// GPIO 27-32 used by default for SPI flash. +// NOTE: GPIO 43 & 44 commonly used for UART and may cause flashes when uploading. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(27) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31) | _FL_BIT(32)) + +#elif CONFIG_IDF_TARGET_ESP32H2 +// 22 GPIO pins. ESPIDF defines all pins as valid. +// ESP32-H2 datasheet not yet available, when it is, mask the pins commonly used by SPI flash. +#warning ESP32-H2 chip flash configuration not yet known. Only pins defined by ESP-IDF will be masked. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL) + +#else +#warning Unknown ESP32 chip variant. Only pins defined by ESP-IDF will be masked. +#define FASTLED_UNUSABLE_PIN_MASK (0ULL) +#endif + +#endif + + + +// SOC GPIO mask was not added until version IDF version 4.3. Prior to this only ESP32 chip was supported, so only +// the value for ESP32 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 3, 0) && !defined(SOC_GPIO_VALID_OUTPUT_GPIO_MASK) +// 0~39 except from 24, 28~31 are valid +#define SOC_GPIO_VALID_GPIO_MASK (0xFFFFFFFFFFULL & ~(0ULL | _FL_BIT(24) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30) | _FL_BIT(31))) +// GPIO >= 34 are input only +#define SOC_GPIO_VALID_OUTPUT_GPIO_MASK (SOC_GPIO_VALID_GPIO_MASK & ~(0ULL | _FL_BIT(34) | _FL_BIT(35) | _FL_BIT(36) | _FL_BIT(37) | _FL_BIT(38) | _FL_BIT(39))) + +#endif + + +// Define mask of valid pins. Start with the list of valid output pins from ESPIDF and remove unusable pins +#define _FL_VALID_PIN_MASK (uint64_t(SOC_GPIO_VALID_OUTPUT_GPIO_MASK) & ~FASTLED_UNUSABLE_PIN_MASK) + +#define _FL_PIN_VALID(PIN) ((_FL_VALID_PIN_MASK & (1ULL << PIN)) != 0) + +#define _FL_DEFPIN(PIN) template <> class FastPin : public _ESPPIN {}; + +// Define all possible pins. If the pin is not valid for a particular ESP32 variant, the pin number +// will be shifted into the 192-255 range, in effect rendering it unusable. +_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); +_FL_DEFPIN(32); _FL_DEFPIN(33); _FL_DEFPIN(34); _FL_DEFPIN(35); +_FL_DEFPIN(36); _FL_DEFPIN(37); _FL_DEFPIN(38); _FL_DEFPIN(39); +_FL_DEFPIN(40); _FL_DEFPIN(41); _FL_DEFPIN(42); _FL_DEFPIN(43); +_FL_DEFPIN(44); _FL_DEFPIN(45); _FL_DEFPIN(46); _FL_DEFPIN(47); +_FL_DEFPIN(48); _FL_DEFPIN(49); _FL_DEFPIN(50); _FL_DEFPIN(51); +_FL_DEFPIN(52); _FL_DEFPIN(53); _FL_DEFPIN(54); _FL_DEFPIN(55); +_FL_DEFPIN(56); _FL_DEFPIN(57); _FL_DEFPIN(58); _FL_DEFPIN(59); +_FL_DEFPIN(60); _FL_DEFPIN(61); _FL_DEFPIN(62); _FL_DEFPIN(63); + +#define HAS_HARDWARE_PIN_SUPPORT + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/platforms/esp/32/fastspi_esp32.h b/lib/FastLED/src/platforms/esp/32/fastspi_esp32.h new file mode 100644 index 00000000..62fb59a1 --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/fastspi_esp32.h @@ -0,0 +1,180 @@ +#pragma once +#pragma message "ESP32 Hardware SPI support added" + +FASTLED_NAMESPACE_BEGIN + +/* + * ESP32 Hardware SPI Driver + * + * Copyright (c) 2020 Nick Wallace + * Derived from code for ESP8266 hardware SPI by Benoit Anastay. + * + * This hardware SPI implementation can drive clocked LEDs from either the + * VSPI or HSPI bus (aka SPI2 & SPI3). No support is provided for SPI1, because it is + * shared among devices and the cache for data (code) in the Flash as well as the PSRAM. + * + * To enable the hardware SPI driver, add the following line *before* including + * FastLED.h: + * + * #define FASTLED_ALL_PINS_HARDWARE_SPI + * + * This driver uses the VSPI bus by default (GPIO 18, 19, 23, & 5). To use the + * HSPI bus (GPIO 14, 12, 13, & 15) add the following line *before* including + * FastLED.h: + * + * #define FASTLED_ESP32_SPI_BUS HSPI + * + */ +/* + * 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. + */ + +#include + +#ifndef FASTLED_ESP32_SPI_BUS + #define FASTLED_ESP32_SPI_BUS VSPI +#endif + +static SPIClass ledSPI(FASTLED_ESP32_SPI_BUS); + + +#if FASTLED_ESP32_SPI_BUS == VSPI + static uint8_t spiClk = 18; + static uint8_t spiMiso = 19; + static uint8_t spiMosi = 23; + static uint8_t spiCs = 5; +#elif FASTLED_ESP32_SPI_BUS == HSPI + static uint8_t spiClk = 14; + static uint8_t spiMiso = 12; + static uint8_t spiMosi = 13; + static uint8_t spiCs = 15; +#elif FASTLED_ESP32_SPI_BUS == FSPI // ESP32S2 can re-route to arbitrary pins + #define spiMosi DATA_PIN + #define spiClk CLOCK_PIN + #define spiMiso -1 + #define spiCs -1 +#endif + +template +class ESP32SPIOutput { + Selectable *m_pSelect; + +public: + // Verify that the pins are valid + static_assert(FastPin::validpin(), "Invalid data pin specified"); + static_assert(FastPin::validpin(), "Invalid clock pin specified"); + + ESP32SPIOutput() { m_pSelect = NULL; } + ESP32SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + // set the pins to output and make sure the select is released (which apparently means hi? This is a bit + // confusing to me) + ledSPI.begin(spiClk, spiMiso, spiMosi, spiCs); + release(); + } + + // stop the SPI output. Pretty much a NOP with software, as there's no registers to kick + static void stop() { } + + // wait until the SPI subsystem is ready for more data to write. A NOP when bitbanging + static void wait() __attribute__((always_inline)) { } + static void waitFully() __attribute__((always_inline)) { wait(); } + + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); } + + static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); } + + // naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte. + static void writeByte(uint8_t b) { + ledSPI.transfer(b); + } + +public: + + // select the SPI output (TODO: research whether this really means hi or lo. Alt TODO: move select responsibility out of the SPI classes + // entirely, make it up to the caller to remember to lock/select the line?) + void select() { + ledSPI.beginTransaction(SPISettings(SPI_SPEED, MSBFIRST, SPI_MODE0)); + if(m_pSelect != NULL) { m_pSelect->select(); } + } + + // release the SPI line + void release() { + if(m_pSelect != NULL) { m_pSelect->release(); } + ledSPI.endTransaction(); + } + + // Write out len bytes of the given value out over ledSPI. Useful for quickly flushing, say, a line of 0's down the line. + void writeBytesValue(uint8_t value, int len) { + select(); + writeBytesValueRaw(value, len); + release(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { + ledSPI.transfer(value); + } + } + + // write a block of len uint8_ts out. Need to type this better so that explicit casts into the call aren't required. + // note that this template version takes a class parameter for a per-byte modifier to the data. + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + select(); + uint8_t *end = data + len; + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + release(); + } + + // default version of writing a block of data out to the SPI port, with no data modifications being made + void writeBytes(FASTLED_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) { + ledSPI.transfer(b); + } + + // 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 of each grouping, as well as a class specifying a per + // byte of data modification to be made. (See DATA_NOP above) + template __attribute__((noinline)) 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(); + } +}; + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/platforms/esp/32/led_sysdefs_esp32.h b/lib/FastLED/src/platforms/esp/32/led_sysdefs_esp32.h new file mode 100644 index 00000000..cf1aa4dc --- /dev/null +++ b/lib/FastLED/src/platforms/esp/32/led_sysdefs_esp32.h @@ -0,0 +1,46 @@ +#pragma once +#include "esp32-hal.h" +#ifndef ESP32 +#define ESP32 +#endif + +#define FASTLED_ESP32 + +#if CONFIG_IDF_TARGET_ARCH_RISCV +#define FASTLED_RISCV +#else +#define FASTLED_XTENSA +#endif + +// Handling for older versions of ESP32 Arduino core +#if !defined(ESP_IDF_VERSION) +// Older versions of ESP_IDF only supported ESP32 +#define CONFIG_IDF_TARGET_ESP32 1 +// Define missing version macros. Hard code older version 3.0 since actual version is unknown +#define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +#define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(3, 0, 0) +#endif + +// Use system millis timer +#define FASTLED_HAS_MILLIS + +typedef volatile uint32_t RoReg; +typedef volatile uint32_t RwReg; +typedef unsigned long 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 +# define FASTLED_ESP32_RAW_PIN_ORDER + diff --git a/lib/FastLED/src/platforms/esp/8266/clockless_block_esp8266.h b/lib/FastLED/src/platforms/esp/8266/clockless_block_esp8266.h new file mode 100644 index 00000000..9cbee78c --- /dev/null +++ b/lib/FastLED/src/platforms/esp/8266/clockless_block_esp8266.h @@ -0,0 +1,160 @@ +#ifndef __INC_CLOCKLESS_BLOCK_ESP8266_H +#define __INC_CLOCKLESS_BLOCK_ESP8266_H + +#define FASTLED_HAS_BLOCKLESS 1 + +#define FIX_BITS(bits) (((bits & 0x0fL) << 12) | (bits & 0x30)) + +#define MIN(X,Y) (((X)<(Y)) ? (X):(Y)) +#define USED_LANES (MIN(LANES, 6)) +#define PORT_MASK (((1 << USED_LANES)-1) & 0x0000FFFFL) +#define PIN_MASK FIX_BITS(PORT_MASK) + +FASTLED_NAMESPACE_BEGIN + +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES +extern uint32_t _frame_cnt; +extern uint32_t _retry_cnt; +#endif + +template +class InlineBlockClocklessController : public CPixelLEDController { + typedef typename FastPin::port_ptr_t data_ptr_t; + typedef typename FastPin::port_t data_t; + + CMinWait mWait; + +public: + virtual int size() { return CLEDController::size() * LANES; } + + virtual void showPixels(PixelController & pixels) { + mWait.wait(); + /*uint32_t clocks = */ + int cnt=FASTLED_INTERRUPT_RETRY_COUNT; + while(!showRGBInternal(pixels) && cnt--) { + os_intr_unlock(); + #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_retry_cnt; + #endif + delayMicroseconds(WAIT_TIME * 10); + os_intr_lock(); + } + // #if FASTLED_ALLOW_INTTERUPTS == 0 + // Adjust the timer + // long microsTaken = CLKS_TO_MICROS(clocks); + // MS_COUNTER += (1 + (microsTaken / 1000)); + // #endif + + mWait.mark(); + } + + template static void initPin() { + _ESPPIN::setOutput(); + } + + 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; } + + typedef union { + uint8_t bytes[8]; + uint16_t shorts[4]; + uint32_t raw[2]; + } Lines; + +#define ESP_ADJUST 0 // (2*(F_CPU/24000000)) +#define ESP_ADJUST2 0 + template __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & last_mark, FASTLED_REGISTER Lines & b, PixelController &pixels) { // , FASTLED_REGISTER uint32_t & b2) { + Lines b2 = b; + transpose8x1_noinline(b.bytes,b2.bytes); + + FASTLED_REGISTER uint8_t d = pixels.template getd(pixels); + FASTLED_REGISTER uint8_t scale = pixels.template getscale(pixels); + + for(FASTLED_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; + + uint32_t nword = (uint32_t)(~b2.bytes[7-i]); + while((__clock_cycles() - last_mark) < (T1-6)); + *FastPin::cport() = FIX_BITS(nword); + + while((__clock_cycles() - last_mark) < (T1+T2)); + *FastPin::cport() = PIN_MASK; + + b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); + } + + for(FASTLED_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; + + uint32_t nword = (uint32_t)(~b2.bytes[7-i]); + while((__clock_cycles() - last_mark) < (T1-6)); + *FastPin::cport() = FIX_BITS(nword); + + while((__clock_cycles() - last_mark) < (T1+T2)); + *FastPin::cport() = PIN_MASK; + } + } + + // 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 IRAM_ATTR showRGBInternal(PixelController &allpixels) { + + // Setup the pixel controller and load/scale the first byte + Lines b0; + + for(int i = 0; i < USED_LANES; ++i) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + allpixels.preStepFirstByteDithering(); + + os_intr_lock(); + uint32_t _start = __clock_cycles(); + uint32_t last_mark = _start; + + while(allpixels.has(1)) { + // Write first byte, read next byte + writeBits<8+XTRA0,1>(last_mark, b0, allpixels); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0,2>(last_mark, b0, allpixels); + allpixels.advanceData(); + + // Write third byte + writeBits<8+XTRA0,0>(last_mark, b0, allpixels); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + os_intr_unlock(); + #endif + + allpixels.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))) { os_intr_unlock(); return 0; } + } + #endif + }; + + os_intr_unlock(); + #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_frame_cnt; + #endif + return __clock_cycles() - _start; + } +}; + +FASTLED_NAMESPACE_END +#endif diff --git a/lib/FastLED/src/platforms/esp/8266/clockless_esp8266.h b/lib/FastLED/src/platforms/esp/8266/clockless_esp8266.h new file mode 100644 index 00000000..d44ff87e --- /dev/null +++ b/lib/FastLED/src/platforms/esp/8266/clockless_esp8266.h @@ -0,0 +1,160 @@ +#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 + delayMicroseconds(WAIT_TIME); + } + mWait.mark(); + } + +#define _ESP_ADJ (0) +#define _ESP_ADJ2 (0) + + template __attribute__ ((always_inline)) inline static bool writeBits(FASTLED_REGISTER uint32_t & last_mark, FASTLED_REGISTER uint32_t b) { + b <<= 24; b = ~b; + for(FASTLED_REGISTER uint32_t i = BITS; i > 0; --i) { + while((__clock_cycles() - last_mark) < (T1+T2+T3)) { + // the current compiler does like just a ; to end of THIS while loop + // although this change make no sense because below are more of the same while loops without + // compiler warning. + ; + } + 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(); + + // even with interrupts disabled, the NMI interupt seems to cause + // timing issues here. abort the frame if one bit took to long. if the + // last of the 24 bits has been sent already, it is too late + // this fixes the flickering first pixel that started to occur with + // framework version 3.0.0 + if ((__clock_cycles() - last_mark) >= (T1 + T2 + T3 - 5)) { + return true; + } + } + return false; + } + + + static uint32_t IRAM_ATTR showRGBInternal(PixelController pixels) { + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + FASTLED_REGISTER uint32_t b = pixels.loadAndScale0(); + pixels.preStepFirstByteDithering(); + uint32_t start; + + // This function has multiple exits, so we'll use an object + // with a destructor that releases the interrupt lock, regardless + // of how we exit the function. It also has methods for manually + // unlocking and relocking interrupts temporarily. + struct InterruptLock { + InterruptLock() { + os_intr_lock(); + } + ~InterruptLock() { + os_intr_unlock(); + } + void Unlock() { + os_intr_unlock(); + } + void Lock() { + os_intr_lock(); + } + }; + + { // Start of interrupt-locked block + InterruptLock intlock; + + start = __clock_cycles(); + uint32_t last_mark = start; + while(pixels.has(1)) { + // Write first byte, read next byte + if (writeBits<8+XTRA0>(last_mark, b)) { + return 0; + } + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + if (writeBits<8+XTRA0>(last_mark, b)) { + return 0; + } + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + if (writeBits<8+XTRA0>(last_mark, b)) { + return 0; + } + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + intlock.Unlock(); + #endif + + b = pixels.advanceAndLoadAndScale0(); + pixels.stepDithering(); + + #if (FASTLED_ALLOW_INTERRUPTS == 1) + intlock.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))) { + return 0; + } + } + #endif + }; + } // End of interrupt-locked block + + #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + ++_frame_cnt; + #endif + return __clock_cycles() - start; + } +}; + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/platforms/esp/8266/fastled_esp8266.h b/lib/FastLED/src/platforms/esp/8266/fastled_esp8266.h new file mode 100644 index 00000000..cb444336 --- /dev/null +++ b/lib/FastLED/src/platforms/esp/8266/fastled_esp8266.h @@ -0,0 +1,10 @@ +#pragma once + +#include "fastpin_esp8266.h" + +#ifdef FASTLED_ALL_PINS_HARDWARE_SPI +#include "fastspi_esp8266.h" +#endif + +#include "clockless_esp8266.h" +#include "clockless_block_esp8266.h" diff --git a/lib/FastLED/src/platforms/esp/8266/fastpin_esp8266.h b/lib/FastLED/src/platforms/esp/8266/fastpin_esp8266.h new file mode 100644 index 00000000..b9e1360c --- /dev/null +++ b/lib/FastLED/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(FASTLED_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(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_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/lib/FastLED/src/platforms/esp/8266/fastspi_esp8266.h b/lib/FastLED/src/platforms/esp/8266/fastspi_esp8266.h new file mode 100644 index 00000000..b1cf9630 --- /dev/null +++ b/lib/FastLED/src/platforms/esp/8266/fastspi_esp8266.h @@ -0,0 +1,145 @@ +#pragma once +#pragma message "ESP8266 Hardware SPI support added" + +#include + +FASTLED_NAMESPACE_BEGIN + +/* + * ESP8266 Hardware SPI Driver + * + * Copyright (c) 2022 Benoit Anastay + * Rewrote based on Nick Wallace, ESP32 integration. + * + * + * To enable the hardware SPI driver, add the following line *before* including + * FastLED.h: + * + * #define FASTLED_ALL_PINS_HARDWARE_SPI + * + * This driver uses the SPI bus (GPIO D5 & D7). + * + */ +/* + * 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. + */ + +template +class ESP8266SPIOutput { + Selectable *m_pSelect; + +public: + ESP8266SPIOutput() { m_pSelect = NULL; } + ESP8266SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + // set the pins to output and make sure the select is released (which apparently means hi? This is a bit + // confusing to me) + SPI.begin(); + release(); + } + + // stop the SPI output. Pretty much a NOP with software, as there's no registers to kick + static void stop() { } + + // wait until the SPI subsystem is ready for more data to write. A NOP when bitbanging + static void wait() __attribute__((always_inline)) { } + static void waitFully() __attribute__((always_inline)) { wait(); } + + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); } + + static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); } + + // naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte. + static void writeByte(uint8_t b) { + SPI.transfer(b); + } + +public: + + // select the SPI output (TODO: research whether this really means hi or lo. Alt TODO: move select responsibility out of the SPI classes + // entirely, make it up to the caller to remember to lock/select the line?) + void select() { + SPI.beginTransaction(SPISettings(3200000, MSBFIRST, SPI_MODE0)); + if(m_pSelect != NULL) { m_pSelect->select(); } + } + + // release the SPI line + void release() { + if(m_pSelect != NULL) { m_pSelect->release(); } + SPI.endTransaction(); + } + + // Write out len bytes of the given value out over SPI. Useful for quickly flushing, say, a line of 0's down the line. + void writeBytesValue(uint8_t value, int len) { + select(); + writeBytesValueRaw(value, len); + release(); + } + + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { + SPI.transfer(value); + } + } + + // write a block of len uint8_ts out. Need to type this better so that explicit casts into the call aren't required. + // note that this template version takes a class parameter for a per-byte modifier to the data. + template void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { + select(); + uint8_t *end = data + len; + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + release(); + } + + // default version of writing a block of data out to the SPI port, with no data modifications being made + void writeBytes(FASTLED_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) { + SPI.transfer(b); + } + + // 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 of each grouping, as well as a class specifying a per + // byte of data modification to be made. (See DATA_NOP above) + template __attribute__((noinline)) 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(); + } +}; + +FASTLED_NAMESPACE_END \ No newline at end of file diff --git a/lib/FastLED/src/platforms/esp/8266/led_sysdefs_esp8266.h b/lib/FastLED/src/platforms/esp/8266/led_sysdefs_esp8266.h new file mode 100644 index 00000000..668a006b --- /dev/null +++ b/lib/FastLED/src/platforms/esp/8266/led_sysdefs_esp8266.h @@ -0,0 +1,37 @@ +#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 + diff --git a/lib/FastLED/src/platforms/fastspi_ardunio_core.h b/lib/FastLED/src/platforms/fastspi_ardunio_core.h new file mode 100644 index 00000000..9e0921bd --- /dev/null +++ b/lib/FastLED/src/platforms/fastspi_ardunio_core.h @@ -0,0 +1,103 @@ +#ifndef __INC_FASTSPI_ARDUNIO_CORE_H +#define __INC_FASTSPI_ARDUNIO_CORE_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(ARDUNIO_CORE_SPI) +#include + +template +class ArdunioCoreSPIOutput { + +public: + ArdunioCoreSPIOutput() {} + + // 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)); + } + + // release the CS select + void inline release() __attribute__((always_inline)) { + _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)) { + _SPIObject.transfer(b); + } + + // write a word out via SPI (returns immediately on writing register) + void inline writeWord(uint16_t w) __attribute__((always_inline)) { + _SPIObject.transfer16(w); + } + + // 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(FASTLED_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(FASTLED_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) { + // todo + } + + // 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); + release(); + } + +}; + + +#endif + +FASTLED_NAMESPACE_END +#endif diff --git a/lib/FastLED/src/power_mgt.cpp b/lib/FastLED/src/power_mgt.cpp new file mode 100644 index 00000000..0bd648fa --- /dev/null +++ b/lib/FastLED/src/power_mgt.cpp @@ -0,0 +1,195 @@ +/// @file power_mgt.cpp +/// Functions to limit the power used by FastLED + +/// Disables pragma messages and warnings +#define FASTLED_INTERNAL +#include "FastLED.h" +#include "power_mgt.h" + +FASTLED_NAMESPACE_BEGIN + +// POWER MANAGEMENT + +/// @name Power Usage Values +/// These power usage values are approximate, and your exact readings +/// will be slightly (10%?) different from these. +/// +/// They were arrived at by actually measuing the power draw of a number +/// of different LED strips, and a bunch of closed-loop-feedback testing +/// to make sure that if we USE these values, we stay at or under +/// the target power consumption. +/// Actual power consumption is much, much more complicated and has +/// to include things like voltage drop, etc., etc. +/// However, this is good enough for most cases, and almost certainly better +/// than no power management at all. +/// +/// You're welcome to adjust these values as needed; there may eventually be an API +/// for changing these on the fly, but it saves codespace and RAM to have them +/// be compile-time constants. +/// @{ +static const uint8_t gRed_mW = 16 * 5; ///< 16mA @ 5v = 80mW +static const uint8_t gGreen_mW = 11 * 5; ///< 11mA @ 5v = 55mW +static const uint8_t gBlue_mW = 15 * 5; ///< 15mA @ 5v = 75mW +static const uint8_t gDark_mW = 1 * 5; ///< 1mA @ 5v = 5mW +/// @} + +// Alternate calibration by RAtkins via pre-PSU wattage measurments; +// these are all probably about 20%-25% too high due to PSU heat losses, +// but if you're measuring wattage on the PSU input side, this may +// be a better set of calibrations. (WS2812B) +// static const uint8_t gRed_mW = 100; +// static const uint8_t gGreen_mW = 48; +// static const uint8_t gBlue_mW = 100; +// static const uint8_t gDark_mW = 12; + + +/// Debug Option: Set to 1 to enable the power limiting LED +/// @see set_max_power_indicator_LED() +#define POWER_LED 1 + +/// Debug Option: Set to enable Serial debug statements for power limit functions +#define POWER_DEBUG_PRINT 0 + + +// Power consumed by the MCU +static const uint8_t gMCU_mW = 25 * 5; // 25mA @ 5v = 125 mW + +static uint8_t gMaxPowerIndicatorLEDPinNumber = 0; // default = Arduino onboard LED pin. set to zero to skip this. + + +uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds ) //25354 +{ + uint32_t red32 = 0, green32 = 0, blue32 = 0; + const CRGB* firstled = &(ledbuffer[0]); + uint8_t* p = (uint8_t*)(firstled); + + uint16_t count = numLeds; + + // This loop might benefit from an AVR assembly version -MEK + while( count) { + red32 += *p++; + green32 += *p++; + blue32 += *p++; + --count; + } + + red32 *= gRed_mW; + green32 *= gGreen_mW; + blue32 *= gBlue_mW; + + red32 >>= 8; + green32 >>= 8; + blue32 >>= 8; + + uint32_t total = red32 + green32 + blue32 + (gDark_mW * numLeds); + + return total; +} + + +uint8_t calculate_max_brightness_for_power_vmA(const CRGB* ledbuffer, uint16_t numLeds, uint8_t target_brightness, uint32_t max_power_V, uint32_t max_power_mA) { + return calculate_max_brightness_for_power_mW(ledbuffer, numLeds, target_brightness, max_power_V * max_power_mA); +} + +uint8_t calculate_max_brightness_for_power_mW(const CRGB* ledbuffer, uint16_t numLeds, uint8_t target_brightness, uint32_t max_power_mW) { + uint32_t total_mW = calculate_unscaled_power_mW( ledbuffer, numLeds); + + uint32_t requested_power_mW = ((uint32_t)total_mW * target_brightness) / 256; + + 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)); + } + + return recommended_brightness; +} + +// sets brightness to +// - no more than target_brightness +// - no more than max_mW milliwatts +uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32_t max_power_mW) +{ + uint32_t total_mW = gMCU_mW; + + CLEDController *pCur = CLEDController::head(); + while(pCur) { + total_mW += calculate_unscaled_power_mW( pCur->leds(), pCur->size()); + pCur = pCur->next(); + } + +#if POWER_DEBUG_PRINT == 1 + Serial.print("power demand at full brightness mW = "); + Serial.println( total_mW); +#endif + + uint32_t requested_power_mW = ((uint32_t)total_mW * target_brightness) / 256; +#if POWER_DEBUG_PRINT == 1 + if( target_brightness != 255 ) { + Serial.print("power demand at scaled brightness mW = "); + Serial.println( requested_power_mW); + } + Serial.print("power limit mW = "); + Serial.println( max_power_mW); +#endif + + if( requested_power_mW < max_power_mW) { +#if POWER_LED > 0 + if( gMaxPowerIndicatorLEDPinNumber ) { + Pin(gMaxPowerIndicatorLEDPinNumber).lo(); // turn the LED off + } +#endif +#if POWER_DEBUG_PRINT == 1 + Serial.print("demand is under the limit"); +#endif + return target_brightness; + } + + uint8_t recommended_brightness = (uint32_t)((uint8_t)(target_brightness) * (uint32_t)(max_power_mW)) / ((uint32_t)(requested_power_mW)); +#if POWER_DEBUG_PRINT == 1 + Serial.print("recommended brightness # = "); + Serial.println( recommended_brightness); + + uint32_t resultant_power_mW = (total_mW * recommended_brightness) / 256; + Serial.print("resultant power demand mW = "); + Serial.println( resultant_power_mW); + + Serial.println(); +#endif + +#if POWER_LED > 0 + if( gMaxPowerIndicatorLEDPinNumber ) { + Pin(gMaxPowerIndicatorLEDPinNumber).hi(); // turn the LED on + } +#endif + + return recommended_brightness; +} + + +void set_max_power_indicator_LED( uint8_t pinNumber) +{ + gMaxPowerIndicatorLEDPinNumber = pinNumber; +} + +void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps) +{ + FastLED.setMaxPowerInVoltsAndMilliamps(volts, milliamps); +} + +void set_max_power_in_milliwatts( uint32_t 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(); +} + +void delay_at_max_brightness_for_power( uint16_t ms) +{ + FastLED.delay(ms); +} + +FASTLED_NAMESPACE_END diff --git a/lib/FastLED/src/power_mgt.h b/lib/FastLED/src/power_mgt.h new file mode 100644 index 00000000..f2edf123 --- /dev/null +++ b/lib/FastLED/src/power_mgt.h @@ -0,0 +1,109 @@ +#ifndef POWER_MGT_H +#define POWER_MGT_H + +#include "FastLED.h" + +#include "pixeltypes.h" + +/// @file power_mgt.h +/// Functions to limit the power used by FastLED + +FASTLED_NAMESPACE_BEGIN + +/// @defgroup Power Power Management Functions +/// Functions to limit the amount of power used by FastLED +/// @{ + + +/// @name Power Control Setup Functions +/// Functions to initialize the power control system +/// @{ + +/// Set the maximum power used in milliamps for a given voltage +/// @deprecated Use CFastLED::setMaxPowerInVoltsAndMilliamps() +void set_max_power_in_volts_and_milliamps( uint8_t volts, uint32_t milliamps); + +/// Set the maximum power used in watts +/// @deprecated Use CFastLED::setMaxPowerInMilliWatts +void set_max_power_in_milliwatts( uint32_t powerInmW); + +/// Select a pin with an LED that will be flashed to indicate that power management +/// is pulling down the brightness +/// @param pinNumber output pin. Zero is "no indicator LED". +void set_max_power_indicator_LED( uint8_t pinNumber); // zero = no indicator LED + +/// @} PowerSetup + + +/// @name Power Control 'show()' and 'delay()' Functions +/// Power-limiting replacements of `show()` and `delay()`. +/// These are drop-in replacements for CFastLED::show() and CFastLED::delay(). +/// In order to use these, you have to actually replace your calls to +/// CFastLED::show() and CFastLED::delay() with these two functions. +/// @deprecated These functions are deprecated as of [6ebcb64](https://github.com/FastLED/FastLED/commit/6ebcb6436273cc9a9dc91733af8dfd1fedde6d60), +/// circa 2015. Do not use them for new programs. +/// +/// @{ + +/// Similar to CFastLED::show(), but pre-adjusts brightness to keep +/// below the power threshold. +/// @deprecated This is now a part of CFastLED::show() +void show_at_max_brightness_for_power(); +/// Similar to CFastLED::delay(), but pre-adjusts brightness to keep below the power +/// threshold. +/// @deprecated This is now a part of CFastLED::delay() +void delay_at_max_brightness_for_power( uint16_t ms); + +/// @} PowerShowDelay + + +/// @name Power Control Internal Helper Functions +/// Internal helper functions for power control. +/// @{ + +/// Determines how many milliwatts the current LED data would draw +/// at max brightness (255) +/// @param ledbuffer the LED data to check +/// @param numLeds the number of LEDs in the data array +/// @returns the number of milliwatts the LED data would consume at max brightness +uint32_t calculate_unscaled_power_mW( const CRGB* ledbuffer, uint16_t numLeds); + +/// Determines the highest brightness level you can use and still stay under +/// the specified power budget for a given set of LEDs. +/// @param ledbuffer the LED data to check +/// @param numLeds the number of LEDs in the data array +/// @param target_brightness the brightness you'd ideally like to use +/// @param max_power_mW the max power draw desired, in milliwatts +/// @returns a limited brightness value. No higher than the target brightness, +/// but may be lower depending on the power limit. +uint8_t calculate_max_brightness_for_power_mW(const CRGB* ledbuffer, uint16_t numLeds, uint8_t target_brightness, uint32_t max_power_mW); + +/// @copybrief calculate_max_brightness_for_power_mW() +/// @param ledbuffer the LED data to check +/// @param numLeds the number of LEDs in the data array +/// @param target_brightness the brightness you'd ideally like to use +/// @param max_power_V the max power in volts +/// @param max_power_mA the max power in milliamps +/// @returns a limited brightness value. No higher than the target brightness, +/// but may be lower depending on the power limit. +uint8_t calculate_max_brightness_for_power_vmA(const CRGB* ledbuffer, uint16_t numLeds, uint8_t target_brightness, uint32_t max_power_V, uint32_t max_power_mA); + +/// Determines the highest brightness level you can use and still stay under +/// the specified power budget for all sets of LEDs. +/// Unlike the other internal power functions which use a pointer to a +/// specific set of LED data, this function uses the ::CFastLED linked list +/// of LED controllers and their attached data. +/// @param target_brightness the brightness you'd ideally like to use +/// @param max_power_mW the max power draw desired, in milliwatts +/// @returns a limited brightness value. No higher than the target brightness, +/// but may be lower depending on the power limit. +uint8_t calculate_max_brightness_for_power_mW( uint8_t target_brightness, uint32_t max_power_mW); + +/// @} PowerInternal + + +/// @} Power + +FASTLED_NAMESPACE_END + +#endif diff --git a/lib/FastLED/src/wiring.cpp b/lib/FastLED/src/wiring.cpp new file mode 100644 index 00000000..5d699d2c --- /dev/null +++ b/lib/FastLED/src/wiring.cpp @@ -0,0 +1,242 @@ +#define FASTLED_INTERNAL +#include "FastLED.h" + +/// @file wiring.cpp +/// Re-implementation of Arduino core functions +/// @deprecated No longer used + +FASTLED_USING_NAMESPACE + +#if 0 + +#if defined(FASTLED_AVR) && !defined(TEENSYDUINO) && !defined(LIB8_ATTINY) +extern "C" { +// the prescaler is set so that timer0 ticks every 64 clock cycles, and the +// the overflow handler is called every 256 ticks. +#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) + +typedef union { unsigned long _long; uint8_t raw[4]; } tBytesForLong; +// tBytesForLong FastLED_timer0_overflow_count; +volatile unsigned long FastLED_timer0_overflow_count=0; +volatile unsigned long FastLED_timer0_millis = 0; + +LIB8STATIC void __attribute__((always_inline)) fastinc32 (volatile uint32_t & _long) { + uint8_t b = ++((tBytesForLong&)_long).raw[0]; + if(!b) { + b = ++((tBytesForLong&)_long).raw[1]; + if(!b) { + b = ++((tBytesForLong&)_long).raw[2]; + if(!b) { + ++((tBytesForLong&)_long).raw[3]; + } + } + } +} + +#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) +ISR(TIM0_OVF_vect) +#else +ISR(TIMER0_OVF_vect) +#endif +{ + fastinc32(FastLED_timer0_overflow_count); + // FastLED_timer0_overflow_count++; +} + +// there are 1024 microseconds per overflow counter tick. +unsigned long millis() +{ + unsigned long m; + uint8_t oldSREG = SREG; + + // disable interrupts while we read FastLED_timer0_millis or we might get an + // inconsistent value (e.g. in the middle of a write to FastLED_timer0_millis) + cli(); + m = FastLED_timer0_overflow_count; //._long; + SREG = oldSREG; + + return (m*(MICROSECONDS_PER_TIMER0_OVERFLOW/8))/(1000/8); +} + +unsigned long micros() { + unsigned long m; + uint8_t oldSREG = SREG, t; + + cli(); + m = FastLED_timer0_overflow_count; // ._long; +#if defined(TCNT0) + t = TCNT0; +#elif defined(TCNT0L) + t = TCNT0L; +#else + #error TIMER 0 not defined +#endif + + +#ifdef TIFR0 + if ((TIFR0 & _BV(TOV0)) && (t < 255)) + ++m; +#else + if ((TIFR & _BV(TOV0)) && (t < 255)) + ++m; +#endif + + SREG = oldSREG; + + return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond()); +} + +void delay(unsigned long ms) +{ + uint16_t start = (uint16_t)micros(); + + while (ms > 0) { + if (((uint16_t)micros() - start) >= 1000) { + --ms; + start += 1000; + } + } +} + +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +void init() +{ + // this needs to be called before setup() or some functions won't + // work there + sei(); + + // on the ATmega168, timer 0 is also used for fast hardware pwm + // (using phase-correct PWM would mean that timer 0 overflowed half as often + // resulting in different millis() behavior on the ATmega8 and ATmega168) +#if defined(TCCR0A) && defined(WGM01) + sbi(TCCR0A, WGM01); + sbi(TCCR0A, WGM00); +#endif + + // set timer 0 prescale factor to 64 +#if defined(__AVR_ATmega128__) + // CPU specific: different values for the ATmega128 + sbi(TCCR0, CS02); +#elif defined(TCCR0) && defined(CS01) && defined(CS00) + // this combination is for the standard atmega8 + sbi(TCCR0, CS01); + sbi(TCCR0, CS00); +#elif defined(TCCR0B) && defined(CS01) && defined(CS00) + // this combination is for the standard 168/328/1280/2560 + sbi(TCCR0B, CS01); + sbi(TCCR0B, CS00); +#elif defined(TCCR0A) && defined(CS01) && defined(CS00) + // this combination is for the __AVR_ATmega645__ series + sbi(TCCR0A, CS01); + sbi(TCCR0A, CS00); +#else + #error Timer 0 prescale factor 64 not set correctly +#endif + + // enable timer 0 overflow interrupt +#if defined(TIMSK) && defined(TOIE0) + sbi(TIMSK, TOIE0); +#elif defined(TIMSK0) && defined(TOIE0) + sbi(TIMSK0, TOIE0); +#else + #error Timer 0 overflow interrupt not set correctly +#endif + + // timers 1 and 2 are used for phase-correct hardware pwm + // this is better for motors as it ensures an even waveform + // note, however, that fast pwm mode can achieve a frequency of up + // 8 MHz (with a 16 MHz clock) at 50% duty cycle + +#if defined(TCCR1B) && defined(CS11) && defined(CS10) + TCCR1B = 0; + + // set timer 1 prescale factor to 64 + sbi(TCCR1B, CS11); +#if F_CPU >= 8000000L + sbi(TCCR1B, CS10); +#endif +#elif defined(TCCR1) && defined(CS11) && defined(CS10) + sbi(TCCR1, CS11); +#if F_CPU >= 8000000L + sbi(TCCR1, CS10); +#endif +#endif + // put timer 1 in 8-bit phase correct pwm mode +#if defined(TCCR1A) && defined(WGM10) + sbi(TCCR1A, WGM10); +#elif defined(TCCR1) + #warning this needs to be finished +#endif + + // set timer 2 prescale factor to 64 +#if defined(TCCR2) && defined(CS22) + sbi(TCCR2, CS22); +#elif defined(TCCR2B) && defined(CS22) + sbi(TCCR2B, CS22); +#else + #warning Timer 2 not finished (may not be present on this CPU) +#endif + + // configure timer 2 for phase correct pwm (8-bit) +#if defined(TCCR2) && defined(WGM20) + sbi(TCCR2, WGM20); +#elif defined(TCCR2A) && defined(WGM20) + sbi(TCCR2A, WGM20); +#else + #warning Timer 2 not finished (may not be present on this CPU) +#endif + +#if defined(TCCR3B) && defined(CS31) && defined(WGM30) + sbi(TCCR3B, CS31); // set timer 3 prescale factor to 64 + sbi(TCCR3B, CS30); + sbi(TCCR3A, WGM30); // put timer 3 in 8-bit phase correct pwm mode +#endif + +#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */ + sbi(TCCR4B, CS42); // set timer4 prescale factor to 64 + sbi(TCCR4B, CS41); + sbi(TCCR4B, CS40); + sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode + sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A + sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D +#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */ +#if defined(TCCR4B) && defined(CS41) && defined(WGM40) + sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64 + sbi(TCCR4B, CS40); + sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode +#endif +#endif /* end timer4 block for ATMEGA1280/2560 and similar */ + +#if defined(TCCR5B) && defined(CS51) && defined(WGM50) + sbi(TCCR5B, CS51); // set timer 5 prescale factor to 64 + sbi(TCCR5B, CS50); + sbi(TCCR5A, WGM50); // put timer 5 in 8-bit phase correct pwm mode +#endif + +#if defined(ADCSRA) + // set a2d prescale factor to 128 + // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range. + // XXX: this will not work properly for other clock speeds, and + // this code should use F_CPU to determine the prescale factor. + sbi(ADCSRA, ADPS2); + sbi(ADCSRA, ADPS1); + sbi(ADCSRA, ADPS0); + + // enable a2d conversions + sbi(ADCSRA, ADEN); +#endif + + // the bootloader connects pins 0 and 1 to the USART; disconnect them + // here so they can be used as normal digital i/o; they will be + // reconnected in Serial.begin() +#if defined(UCSRB) + UCSRB = 0; +#elif defined(UCSR0B) + UCSR0B = 0; +#endif +} +}; +#endif + +#endif + diff --git a/lib/FastLED/workspace.code-workspace b/lib/FastLED/workspace.code-workspace new file mode 100644 index 00000000..ef9f5d27 --- /dev/null +++ b/lib/FastLED/workspace.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": "." + } + ] +} \ No newline at end of file diff --git a/lib/NeoPixelBus/.gitattributes b/lib/NeoPixelBus/.gitattributes new file mode 100644 index 00000000..412eeda7 --- /dev/null +++ b/lib/NeoPixelBus/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/lib/NeoPixelBus/.github/FUNDING.yml b/lib/NeoPixelBus/.github/FUNDING.yml new file mode 100644 index 00000000..459096e6 --- /dev/null +++ b/lib/NeoPixelBus/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [makuna] +custom: ["https://paypal.me/MakunaGithub"] diff --git a/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/all-others.md b/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/all-others.md new file mode 100644 index 00000000..0c394e63 --- /dev/null +++ b/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/all-others.md @@ -0,0 +1,15 @@ +--- +name: All others +about: If your issue doesn't fit the other two, this will guide you to support. +title: "[DELETE ME]" +labels: NOT AN ISSUE +assignees: '' + +--- + +### STOP +If you are seeking support, then please use the Discussions feature or gitter channel by following one of these links. +[NeoPixelBus Discussions](https://github.com/Makuna/NeoPixelBus/discussions) +[NeoPixelBus Gitter Channel](https://gitter.im/Makuna/NeoPixelBus) + +If you submit issues that are not traceable bugs or feature requests, it will just get closed. diff --git a/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/bug_report.md b/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..b042e6b6 --- /dev/null +++ b/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,37 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**NOTE: If you are seeking help or have questions, this is NOT the place to do it.** For questions and support, jump on Gitter and ask away. +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/NeoPixelBus?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Development environment (please complete the following information):** + - OS: [e.g. Win10] + - Build Environment [e.g. Arduino IDE v.1.8.10] + - Board target [e.g. AVR Mege 2560, v 1.10.31 ] - you will need to check the board manager for this. + - Library version [e.g. v2.3.4] - you will need to check the library manager for this. If you used github to clone it, mention this and include the reference. + +**Minimal Sketch that reproduced the problem:** +If an example will demonstrate the problem, list which example you used. +If its your sketch, take the time to remove unneeded parts and minimize it to the smallest sketch that reproduces the problem. Then include the sketch here using `code` markup. Start with a line with three single left quotes and then end with a line with three single left quotes. + +**Additional context** +Add any other context about the problem here. diff --git a/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/feature_request.md b/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..c57bd0e6 --- /dev/null +++ b/lib/NeoPixelBus/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**NOTE: If you are seeking help or have questions, this is NOT the place to do it.** For questions and support, jump on Gitter and ask away. +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/NeoPixelBus?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/lib/NeoPixelBus/.piopm b/lib/NeoPixelBus/.piopm new file mode 100644 index 00000000..464e5aac --- /dev/null +++ b/lib/NeoPixelBus/.piopm @@ -0,0 +1 @@ +{"type": "library", "name": "NeoPixelBus", "version": "2.7.5", "spec": {"owner": "makuna", "id": 547, "name": "NeoPixelBus", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/lib/NeoPixelBus/COPYING b/lib/NeoPixelBus/COPYING new file mode 100644 index 00000000..153d416d --- /dev/null +++ b/lib/NeoPixelBus/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/lib/NeoPixelBus/ReadMe.md b/lib/NeoPixelBus/ReadMe.md new file mode 100644 index 00000000..aa61e1ce --- /dev/null +++ b/lib/NeoPixelBus/ReadMe.md @@ -0,0 +1,38 @@ +# NeoPixelBus + +[![Donate](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6AA97KE54UJR4) + +Arduino NeoPixel library + +A library to control one wire protocol RGB and RGBW leds like APA106, SK6812, WS2811, WS2812 and WS2813 that are commonly refered to as NeoPixels and two wire protocol RGB like Lpd8806, APA102 and SK9822 commonly refered to as DotStars. +Supports most Arduino platforms. + +Please read this best practices link before connecting your NeoPixels, it will save you a lot of time and effort. +[Adafruit NeoPixel Best Practices](https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices) + +For quick questions and support: +* [Try the new Github Discussions](https://github.com/Makuna/NeoPixelBus/discussions) +* [Discord NeoPixelBus Invitation](https://discord.gg/c6FrysvZyV) or if you are already a member of [Discord Server NeoPixelBus](https://discord.com/channels/789177382221119519/789177382221119521) +* Or jump on Gitter +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Makuna/NeoPixelBus?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +For bugs, make sure there isn't an active issue and then create one. + +## Why this library and not FastLED or some other library? +See [Why this Library in the Wiki](https://github.com/Makuna/NeoPixelBus/wiki/Library-Comparisons). + +## Documentation +[See Wiki](https://github.com/Makuna/NeoPixelBus/wiki) + +## Installing This Library (preferred, you just want to use it) +Open the Library Manager and search for "NeoPixelBus by Makuna" and install + +## Installing This Library From GitHub (advanced, you want to contribute) +Create a directory in your Arduino\Library folder named "NeoPixelBus" +Clone (Git) this project into that folder. +It should now show up in the import list when you restart Arduino IDE. + + + + + diff --git a/lib/NeoPixelBus/examples/DotStarTest/DotStarTest.ino b/lib/NeoPixelBus/examples/DotStarTest/DotStarTest.ino new file mode 100644 index 00000000..45201ab1 --- /dev/null +++ b/lib/NeoPixelBus/examples/DotStarTest/DotStarTest.ino @@ -0,0 +1,88 @@ +// DotStarTest +// This example will cycle between showing four pixels as Red, Green, Blue, White +// and then showing those pixels as Black. +// +// There is serial output of the current state so you can confirm and follow along +// + +#include + +const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure + +// make sure to set this to the correct pins +const uint8_t DotClockPin = 2; +const uint8_t DotDataPin = 3; + +#define colorSaturation 128 + +// for software bit bang +NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); + +// for hardware SPI (best performance but must use hardware pins) +//NeoPixelBus strip(PixelCount); + +// DotStars that support RGB color and a overall luminance/brightness value +// NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); +// DotStars that support RGBW color with a seperate white element +//NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); + +RgbColor red(colorSaturation, 0, 0); +RgbColor green(0, colorSaturation, 0); +RgbColor blue(0, 0, colorSaturation); +RgbColor white(colorSaturation); +RgbColor black(0); + +// for use with RGB DotStars when using the luminance/brightness global value +// note that its range is only 0 - 31 (31 is full bright) and +// also note that it is not useful for POV displays as it will cause more flicker +RgbwColor redL(colorSaturation, 0, 0, 31); // use white value to store luminance +RgbwColor greenL(0, colorSaturation, 0, 31); // use white value to store luminance +RgbwColor blueL(0, 0, colorSaturation, 31); // use white value to store luminance +RgbwColor whiteL(255, 255, 255, colorSaturation / 8); // luminance is only 0-31 + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // this resets all the neopixels to an off state + strip.Begin(); + strip.ClearTo(black); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + + +void loop() +{ + delay(5000); + + Serial.println("Colors R, G, B, W..."); + + // set the colors, + strip.SetPixelColor(0, red); + strip.SetPixelColor(1, green); + strip.SetPixelColor(2, blue); + strip.SetPixelColor(3, white); + strip.Show(); + + + delay(5000); + + Serial.println("Off ..."); + + // turn off the pixels + strip.SetPixelColor(0, black); + strip.SetPixelColor(1, black); + strip.SetPixelColor(2, black); + strip.SetPixelColor(3, black); + strip.Show(); + +} + diff --git a/lib/NeoPixelBus/examples/ESP32/DotStarTest_Esp32Advanced/DotStarTest_Esp32Advanced.ino b/lib/NeoPixelBus/examples/ESP32/DotStarTest_Esp32Advanced/DotStarTest_Esp32Advanced.ino new file mode 100644 index 00000000..cd31da2b --- /dev/null +++ b/lib/NeoPixelBus/examples/ESP32/DotStarTest_Esp32Advanced/DotStarTest_Esp32Advanced.ino @@ -0,0 +1,159 @@ +// DotStarTest_Esp32Advanced - This example only works on the ESP32 +// This example will cycle between showing four pixels as Red, Green, Blue, White +// and then showing those pixels as Black. This example uses Hardware SPI on the ESP32 +// with options to use alternate pins for SPI and drive two SPI ports using the alternate +// additional SPI hardware available on the ESP32. +// +// There is serial output of the current state so you can confirm and follow along +// + +#include + +#define USE_DEFAULT_SPI_PORT 1 +#define USE_ALTERNATE_SPI_PORT 1 + +#if (USE_DEFAULT_SPI_PORT == 1) + const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure + + // It's usually better to use alternate pins. If set to false, strip will use GPIO 18 for Clock, GPIO 23 for Data, and pin 5 will output a chip select signal + const bool useSpiAlternatePins = true; + + // If you set useSpiAlternatePins true, then these pins will be used instead. Any output-capable GPIO can be used. + const uint8_t DotClockPin = 18; + const uint8_t DotDataPin = 23; + const int8_t DotChipSelectPin = -1; // -1 means the chip select signal won't be output, freeing up one pin compared to useSpiAlternatePins=false + + // for software bit bang (only use if neither SPI peripheral is available) + //NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); + + // for hardware SPI (best performance) with default SPI peripheral + NeoPixelBus strip(PixelCount); + + // DotStarSpiMethod defaults to 10MHz clock speed. For other speeds, replace "DotStarSpiMethod" with another method specifying speed, e.g. "DotStarSpi2MhzMethod" (see wiki for more details) + + // to change the SPI clock speed during runtime, use the "Hz" clock setting, e.g. DotStarSpiHzMethod, which default to 10MHz but supports updating during runtime + //NeoPixelBus strip(PixelCount); + //#define SET_CLOCK_SPEED_DURING_RUNTIME // only define if using "Hz" clock method + +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + const uint16_t PixelCount2 = 4; // this example assumes 4 pixels, making it smaller will cause a failure + + // It's usually better to use alternate pins. If set to false, strip2 will use GPIO 14 for Clock, GPIO 13 for Data, and pin 15 will output a chip select signal + const bool useSpiAlternatePins2 = true; + + // If you set useSpiAlternatePins2 true, then these pins will be used instead. Any output-capable GPIO can be used. + const uint8_t DotClockPin2 = 14; + const uint8_t DotDataPin2 = 13; + const int8_t DotChipSelectPin2 = -1; // -1 means the chip select signal won't be output, freeing up one pin compared to useSpiAlternatePins2=false + + // for hardware SPI (best performance) with alternate SPI peripheral + NeoPixelBus strip2(PixelCount2); + + // DotStarHspiMethod defaults to 10MHz clock speed. For other speeds, replace "DotStarSpiMethod" with another method specifying speed, e.g. "DotStarHspi2MhzMethod" (see wiki for more details) +#endif + +#define colorSaturation 128 + +// Note that both DotStarSpiMethod and DotStarHspiMethod can be used with DotStarLbgrFeature and DotStarWbgrFeature but to keep things simple those are excluded from this example, see DotStarTest for more details + +RgbColor red(colorSaturation, 0, 0); +RgbColor green(0, colorSaturation, 0); +RgbColor blue(0, 0, colorSaturation); +RgbColor white(colorSaturation); +RgbColor black(0); + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + +#if (USE_DEFAULT_SPI_PORT == 1) + if (useSpiAlternatePins) + { + strip.Begin(DotClockPin, DotDataPin, DotDataPin, DotChipSelectPin); + } + else + { + strip.Begin(); + } + + strip.ClearTo(black); // this resets all the DotStars to an off state + strip.Show(); +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + if (useSpiAlternatePins2) + { + strip2.Begin(DotClockPin2, DotDataPin2, DotDataPin2, DotChipSelectPin2); + } + else + { + strip2.Begin(); + } + + strip2.ClearTo(black); // this resets all the DotStars to an off state + strip2.Show(); +#endif + + Serial.println(); + Serial.println("Running..."); +} + + +void loop() +{ + delay(5000); + +#if (USE_DEFAULT_SPI_PORT == 1) + Serial.println("Default SPI Colors R, G, B, W..."); + // set the colors, + strip.SetPixelColor(0, red); + strip.SetPixelColor(1, green); + strip.SetPixelColor(2, blue); + strip.SetPixelColor(3, white); + strip.Show(); +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + Serial.println("Alt SPI Colors W, B, G, R..."); + // set the colors, + strip2.SetPixelColor(0, white); + strip2.SetPixelColor(1, blue); + strip2.SetPixelColor(2, green); + strip2.SetPixelColor(3, red); + strip2.Show(); +#endif + + delay(5000); + + Serial.println("Off ..."); + +#if (USE_DEFAULT_SPI_PORT == 1) + #ifdef SET_CLOCK_SPEED_DURING_RUNTIME + uint32_t clockspeed = 5000000UL; + strip.SetMethodSettings(NeoSpiSettings(clockspeed)); + #endif + + // turn off the pixels + strip.SetPixelColor(0, black); + strip.SetPixelColor(1, black); + strip.SetPixelColor(2, black); + strip.SetPixelColor(3, black); + strip.Show(); +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + // turn off the pixels + strip2.SetPixelColor(0, black); + strip2.SetPixelColor(1, black); + strip2.SetPixelColor(2, black); + strip2.SetPixelColor(3, black); + strip2.Show(); +#endif +} diff --git a/lib/NeoPixelBus/examples/ESP32/DotStarTest_Esp32DmaSpi/DotStarTest_Esp32DmaSpi.ino b/lib/NeoPixelBus/examples/ESP32/DotStarTest_Esp32DmaSpi/DotStarTest_Esp32DmaSpi.ino new file mode 100644 index 00000000..41ffdbac --- /dev/null +++ b/lib/NeoPixelBus/examples/ESP32/DotStarTest_Esp32DmaSpi/DotStarTest_Esp32DmaSpi.ino @@ -0,0 +1,166 @@ +// DotStarTest_Esp32Advanced - This example only works on the ESP32 +// This example will cycle between showing four pixels as Red, Green, Blue, White +// and then showing those pixels as Black. This example uses DMA to drive Hardware SPI on the ESP32 +// with options to use alternate pins for SPI and drive two SPI ports +// +// There is serial output of the current state so you can confirm and follow along +// + +// +// ESP32 SPI Buses and up to how many bits they support: +// ESP32: Spi1 (4 bit) | Spi2 (4 bit) | Spi3 (4 bit) +// ESP32-S2: | Spi2 (8 bit) | Spi3 +// ESP32-S3: | Spi2 (8 bit) | Spi3 (4 bit) +// ESP32-C3: | Spi2 (4 bit) | +// +// If a DotStarEsp32DmaSpi method is used without specifying the bus number +// then Spi2 will be used by default +// +// In this demo if an alternate SPI bus is chosen then Spi3 will be used + +#include + +#define USE_DEFAULT_SPI_PORT 1 + +// C3 only has a single Spi bus +#if CONFIG_IDF_TARGET_ESP32C3 +#define USE_ALTERNATE_SPI_PORT 0 +#else +#define USE_ALTERNATE_SPI_PORT 1 +#endif + +#if (USE_DEFAULT_SPI_PORT == 1) + const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure + + // It's usually better to use alternate pins. If set to false, strip will use GPIO 18 for Clock, GPIO 23 for Data, and pin 5 will output a chip select signal + const bool useSpiAlternatePins = true; + + // If you set useSpiAlternatePins true, then these pins will be used instead. Any output-capable GPIO can be used. + const uint8_t DotClockPin = 18; + const uint8_t DotDataPin = 23; + const int8_t DotChipSelectPin = -1; // -1 means the chip select signal won't be output, freeing up one pin compared to useSpiAlternatePins=false + + // for software bit bang (only use if neither SPI peripheral is available) + //NeoPixelBus strip(PixelCount, DotClockPin, DotDataPin); + + // for hardware SPI (best performance) with default SPI peripheral + NeoPixelBus strip(PixelCount); + + // DotStarEsp32DmaVspiMethod defaults to 10MHz clock speed. For other speeds, replace "DotStarSpiMethod" with another method specifying speed, e.g. "DotStarSpi2MhzMethod" (see wiki for more details) + // See DotStarTest_Esp32Advanced example for how to set clock speed at runtime +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + const uint16_t PixelCount2 = 4; // this example assumes 4 pixels, making it smaller will cause a failure + + // It's usually better to use alternate pins. If set to false, strip2 will use GPIO 14 for Clock, GPIO 13 for Data, and pin 15 will output a chip select signal + const bool useSpiAlternatePins2 = true; + + // If you set useSpiAlternatePins2 true, then these pins will be used instead. Any output-capable GPIO can be used. + const uint8_t DotClockPin2 = 14; + const uint8_t DotDataPin2 = 13; + const int8_t DotChipSelectPin2 = -1; // -1 means the chip select signal won't be output, freeing up one pin compared to useSpiAlternatePins2=false + + // for hardware SPI (best performance) with alternate SPI peripheral + NeoPixelBus strip2(PixelCount2); + + // DotStarEsp32DmaHspiMethod defaults to 10MHz clock speed. For other speeds, replace "DotStarSpiMethod" with another method specifying speed, e.g. "DotStarHspi2MhzMethod" (see wiki for more details) +#endif + +#define colorSaturation 128 + +// Note that both DotStarEsp32DmaSpiMethod and DotStarEsp32DmaSpi1Method can be used with DotStarLbgrFeature and DotStarWbgrFeature but to keep things simple those are excluded from this example, see DotStarTest for more details + +RgbColor red(colorSaturation, 0, 0); +RgbColor green(0, colorSaturation, 0); +RgbColor blue(0, 0, colorSaturation); +RgbColor white(colorSaturation); +RgbColor black(0); + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + +#if (USE_DEFAULT_SPI_PORT == 1) + if (useSpiAlternatePins) + { + strip.Begin(DotClockPin, DotDataPin, DotDataPin, DotChipSelectPin); + } + else + { + strip.Begin(); + } + + strip.ClearTo(black); // this resets all the DotStars to an off state + strip.Show(); +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + if (useSpiAlternatePins2) + { + strip2.Begin(DotClockPin2, DotDataPin2, DotDataPin2, DotChipSelectPin2); + } + else + { + strip2.Begin(); + } + + strip2.ClearTo(black); // this resets all the DotStars to an off state + strip2.Show(); +#endif + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(500); + +#if (USE_DEFAULT_SPI_PORT == 1) + Serial.println("Default SPI Colors R, G, B, W..."); + // set the colors, + strip.SetPixelColor(0, red); + strip.SetPixelColor(1, green); + strip.SetPixelColor(2, blue); + strip.SetPixelColor(3, white); + strip.Show(); +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + Serial.println("Alt SPI Colors W, B, G, R..."); + // set the colors, + strip2.SetPixelColor(0, white); + strip2.SetPixelColor(1, blue); + strip2.SetPixelColor(2, green); + strip2.SetPixelColor(3, red); + strip2.Show(); +#endif + + delay(500); + + Serial.println("Off ..."); + +#if (USE_DEFAULT_SPI_PORT == 1) + // turn off the pixels + strip.SetPixelColor(0, black); + strip.SetPixelColor(1, black); + strip.SetPixelColor(2, black); + strip.SetPixelColor(3, black); + strip.Show(); +#endif + +#if (USE_ALTERNATE_SPI_PORT == 1) + // turn off the pixels + strip2.SetPixelColor(0, black); + strip2.SetPixelColor(1, black); + strip2.SetPixelColor(2, black); + strip2.SetPixelColor(3, black); + strip2.Show(); +#endif +} diff --git a/lib/NeoPixelBus/examples/ESP32/NeoPixel_ESP32_I2sParallel/NeoPixel_ESP32_I2sParallel.ino b/lib/NeoPixelBus/examples/ESP32/NeoPixel_ESP32_I2sParallel/NeoPixel_ESP32_I2sParallel.ino new file mode 100644 index 00000000..72d55823 --- /dev/null +++ b/lib/NeoPixelBus/examples/ESP32/NeoPixel_ESP32_I2sParallel/NeoPixel_ESP32_I2sParallel.ino @@ -0,0 +1,54 @@ +// +// NeoPixel_ESP32_I2sParallel - +// This sketch demonstrates the use of the I2S Parallel method allowing upto 8 hardware updated channels +// This example only works on the ESP32 +// +// The key part of the method name is Esp32I2s1X8, +// E2p32 (platform specific method), +// I2s Channel 1 (most commonly available), +// X8 (8 parallel channel mode) +// +// In this example, it demonstrates different ColorFeatures, Method specification, and count per strip +// +#include + +// Demonstrating the use of the first four channels, but the method used allows for eight +NeoPixelBus strip1(120, 15); // note: older WS2811 and longer strip +NeoPixelBus strip2(100, 2); // note: modern WS2812 with letter like WS2812b +NeoPixelBus strip3(100, 4); // note: inverted +NeoPixelBus strip4(50, 16); // note: RGBW and Sk6812 and smaller strip + +void setup() { + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // must call begin on all the strips + strip1.Begin(); + strip2.Begin(); + strip3.Begin(); + strip4.Begin(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() { + delay(1000); + + // draw on the strips + strip1.SetPixelColor(0, RgbColor(255, 0, 0)); // red + strip2.SetPixelColor(0, RgbColor(0, 127, 0)); // green + strip3.SetPixelColor(0, RgbColor(0, 0, 53)); // blue + strip4.SetPixelColor(0, RgbwColor(0, 0, 128, 255)); // white channel with a little blue + + // show them, + // only on the last show, no matter the order, will the data be sent + strip1.Show(); + strip2.Show(); + strip3.Show(); + strip4.Show(); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/examples/NeoPixelBusLg/NeoPixelBusLg.ino b/lib/NeoPixelBus/examples/NeoPixelBusLg/NeoPixelBusLg.ino new file mode 100644 index 00000000..55240cfb --- /dev/null +++ b/lib/NeoPixelBus/examples/NeoPixelBusLg/NeoPixelBusLg.ino @@ -0,0 +1,108 @@ +// NeoPixelBusLg +// +// This example demonstrates the use of the NeoPixelBusLg +// with integrated luminance and gamma support +// +// There is serial output of the current state so you can +// confirm and follow along +// + +#include // instead of NeoPixelBus.h + +const uint16_t PixelCount = 96; // set to the number of pixels in your strip +const uint8_t PixelPin = 14; // make sure to set this to the correct pin, ignored for Esp8266 + +RgbColor red(255, 0, 0); +RgbColor green(0, 255, 0); +RgbColor blue(0, 0, 255); +RgbColor black(0, 0, 0); +RgbColor white(255, 255, 255); + +// Make sure to provide the correct color order feature +// for your NeoPixels +NeoPixelBusLg strip(PixelCount, PixelPin); + +// If speed is an issue and memory is not, then you can use the gamma table variant +// which is much faster but uses 256 bytes of RAM +// NeoPixelBusLg strip(PixelCount, PixelPin); + +// If you want to turn gamma correction off, then you can use the null gamma method +// NeoPixelBusLg strip(PixelCount, PixelPin); + +// If you use a LED driver between the NeoPixel chip and the LEDs that require the PWM range inverted +// NeoPixelBusLg> strip(PixelCount, PixelPin); + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // this resets all the neopixels to an off state + strip.Begin(); + strip.SetLuminance(128); // (0-255) - initially at half brightness + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + static const uint8_t c_MinBrightness = 0; + static const uint8_t c_MaxBrightness = 255; + + static int8_t direction = -1; // start with dimming + + uint8_t luminance = strip.GetLuminance(); + + Serial.print(direction); + Serial.print(" "); + Serial.println(luminance); + + delay(200); + + // swap direction of luminance when limits are reached + // + if (direction < 0 && luminance <= c_MinBrightness) + { + direction = 1; + } + else if (direction > 0 && luminance >= c_MaxBrightness) + { + direction = -1; + } + else + { + luminance += direction; + } + + strip.SetLuminance(luminance); + + // draw something + // + uint16_t half = strip.PixelCount() / 2; + DrawGradient(green, black, 0, half - 1); + DrawGradient(black, red, half, strip.PixelCount() - 1); + + // show the results + strip.Show(); +} + +void DrawGradient(RgbColor startColor, + RgbColor finishColor, + uint16_t startIndex, + uint16_t finishIndex) +{ + uint16_t delta = finishIndex - startIndex; + + for (uint16_t index = startIndex; index < finishIndex; index++) + { + float progress = static_cast(index - startIndex) / delta; + RgbColor color = RgbColor::LinearBlend(startColor, finishColor, progress); + strip.SetPixelColor(index, color); + } +} \ No newline at end of file diff --git a/lib/NeoPixelBus/examples/NeoPixelTest/NeoPixelTest.ino b/lib/NeoPixelBus/examples/NeoPixelTest/NeoPixelTest.ino new file mode 100644 index 00000000..dd63558f --- /dev/null +++ b/lib/NeoPixelBus/examples/NeoPixelTest/NeoPixelTest.ino @@ -0,0 +1,139 @@ +// NeoPixelTest +// This example will cycle between showing four pixels as Red, Green, Blue, White +// and then showing those pixels as Black. +// +// Included but commented out are examples of configuring a NeoPixelBus for +// different color order including an extra white channel, different data speeds, and +// for Esp8266 different methods to send the data. +// NOTE: You will need to make sure to pick the one for your platform +// +// +// There is serial output of the current state so you can confirm and follow along. +// + +#include + +const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +#define colorSaturation 128 + +// three element pixels, in different order and speeds +NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +// There are other Esp8266 alternative methods that provide more pin options, but also have +// other side effects. +// For details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods. + +// You can also use one of these for Esp8266, +// each having their own restrictions. +// +// These two are the same as above as the DMA method is the default. +// NOTE: These will ignore the PIN and use GPI03 pin. +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +// Uart method is good for the Esp-01 or other pin restricted modules. +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods. +// NOTE: These will ignore the PIN and use GPI02 pin. +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +// The bitbang method is really only good if you are not using WiFi features of the ESP. +// It works with all but pin 16. +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +// four element pixels, RGBW +//NeoPixelBus strip(PixelCount, PixelPin); + +RgbColor red(colorSaturation, 0, 0); +RgbColor green(0, colorSaturation, 0); +RgbColor blue(0, 0, colorSaturation); +RgbColor white(colorSaturation); +RgbColor black(0); + +HslColor hslRed(red); +HslColor hslGreen(green); +HslColor hslBlue(blue); +HslColor hslWhite(white); +HslColor hslBlack(black); + + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // this resets all the neopixels to an off state + strip.Begin(); + strip.Show(); + + + Serial.println(); + Serial.println("Running..."); +} + + +void loop() +{ + delay(5000); + + Serial.println("Colors R, G, B, W..."); + + // set the colors, + // if they don't match in order, you need to use NeoGrbFeature feature + strip.SetPixelColor(0, red); + strip.SetPixelColor(1, green); + strip.SetPixelColor(2, blue); + strip.SetPixelColor(3, white); + // the following line demonstrates rgbw color support + // if the NeoPixels are rgbw types the following line will compile + // if the NeoPixels are anything else, the following line will give an error + //strip.SetPixelColor(3, RgbwColor(colorSaturation)); + strip.Show(); + + + delay(5000); + + Serial.println("Off ..."); + + // turn off the pixels + strip.SetPixelColor(0, black); + strip.SetPixelColor(1, black); + strip.SetPixelColor(2, black); + strip.SetPixelColor(3, black); + strip.Show(); + + delay(5000); + + Serial.println("HSL Colors R, G, B, W..."); + + // set the colors, + // if they don't match in order, you may need to use NeoGrbFeature feature + strip.SetPixelColor(0, hslRed); + strip.SetPixelColor(1, hslGreen); + strip.SetPixelColor(2, hslBlue); + strip.SetPixelColor(3, hslWhite); + strip.Show(); + + + delay(5000); + + Serial.println("Off again..."); + + // turn off the pixels + strip.SetPixelColor(0, hslBlack); + strip.SetPixelColor(1, hslBlack); + strip.SetPixelColor(2, hslBlack); + strip.SetPixelColor(3, hslBlack); + strip.Show(); + +} + diff --git a/lib/NeoPixelBus/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino b/lib/NeoPixelBus/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino new file mode 100644 index 00000000..04c24b2b --- /dev/null +++ b/lib/NeoPixelBus/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino @@ -0,0 +1,222 @@ +// NeoPixelAnimation +// This example will randomly pick a new color for each pixel and animate +// the current color to the new color over a random small amount of time, using +// a randomly selected animation curve. +// It will repeat this process once all pixels have finished the animation +// +// This will demonstrate the use of the NeoPixelAnimator extended time feature. +// This feature allows for different time scales to be used, allowing slow extended +// animations to be created. +// +// This will demonstrate the use of the NeoEase animation ease methods; that provide +// simulated acceleration to the animations. +// +// It also includes platform specific code for Esp8266 that demonstrates easy +// animation state and function definition inline. This is not available on AVR +// Arduinos; but the AVR compatible code is also included for comparison. +// +// The example includes some serial output that you can follow along with as it +// does the animation. +// + +#include +#include + +const uint16_t PixelCount = 4; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +NeoPixelBus strip(PixelCount, PixelPin); +// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +// There are other Esp8266 alternative methods that provide more pin options, but also have +// other side effects. +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods + +// NeoPixel animation time management object +NeoPixelAnimator animations(PixelCount, NEO_CENTISECONDS); + +// create with enough animations to have one per pixel, depending on the animation +// effect, you may need more or less. +// +// since the normal animation time range is only about 65 seconds, by passing timescale value +// to the NeoPixelAnimator constructor we can increase the time range, but we also increase +// the time between the animation updates. +// NEO_CENTISECONDS will update the animations every 100th of a second rather than the default +// of a 1000th of a second, but the time range will now extend from about 65 seconds to about +// 10.9 minutes. But you must remember that the values passed to StartAnimations are now +// in centiseconds. +// +// Possible values from 1 to 32768, and there some helpful constants defined as... +// NEO_MILLISECONDS 1 // ~65 seconds max duration, ms updates +// NEO_CENTISECONDS 10 // ~10.9 minutes max duration, centisecond updates +// NEO_DECISECONDS 100 // ~1.8 hours max duration, decisecond updates +// NEO_SECONDS 1000 // ~18.2 hours max duration, second updates +// NEO_DECASECONDS 10000 // ~7.5 days, 10 second updates +// + +#if defined(NEOPIXEBUS_NO_STL) +// for AVR, you need to manage the state due to lack of STL/compiler support +// for Esp8266 you can define the function using a lambda and state is created for you +// see below for an example +struct MyAnimationState +{ + RgbColor StartingColor; // the color the animation starts at + RgbColor EndingColor; // the color the animation will end at + AnimEaseFunction Easeing; // the acceleration curve it will use +}; + +MyAnimationState animationState[PixelCount]; +// one entry per pixel to match the animation timing manager + +void AnimUpdate(const AnimationParam& param) +{ + // first apply an easing (curve) to the animation + // this simulates acceleration to the effect + float progress = animationState[param.index].Easeing(param.progress); + + // this gets called for each animation on every time step + // progress will start at 0.0 and end at 1.0 + // we use the blend function on the RgbColor to mix + // color based on the progress given to us in the animation + RgbColor updatedColor = RgbColor::LinearBlend( + animationState[param.index].StartingColor, + animationState[param.index].EndingColor, + progress); + // apply the color to the strip + strip.SetPixelColor(param.index, updatedColor); +} +#endif + +void SetRandomSeed() +{ + uint32_t seed; + + // random works best with a seed that can use 31 bits + // analogRead on a unconnected pin tends toward less than four bits + seed = analogRead(0); + delay(1); + + for (int shifts = 3; shifts < 31; shifts += 3) + { + seed ^= analogRead(0) << shifts; + delay(1); + } + + // Serial.println(seed); + randomSeed(seed); +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + strip.Begin(); + strip.Show(); + + SetRandomSeed(); + + // just pick some colors + for (uint16_t pixel = 0; pixel < PixelCount; pixel++) + { + RgbColor color = RgbColor(random(255), random(255), random(255)); + strip.SetPixelColor(pixel, color); + } + + Serial.println(); + Serial.println("Running..."); +} + + +void SetupAnimationSet() +{ + // setup some animations + for (uint16_t pixel = 0; pixel < PixelCount; pixel++) + { + const uint8_t peak = 128; + + // pick a random duration of the animation for this pixel + // since values are centiseconds, the range is 1 - 4 seconds + uint16_t time = random(100, 400); + + // each animation starts with the color that was present + RgbColor originalColor = strip.GetPixelColor(pixel); + // and ends with a random color + RgbColor targetColor = RgbColor(random(peak), random(peak), random(peak)); + // with the random ease function + AnimEaseFunction easing; + + switch (random(3)) + { + case 0: + easing = NeoEase::CubicIn; + break; + case 1: + easing = NeoEase::CubicOut; + break; + case 2: + easing = NeoEase::QuadraticInOut; + break; + } + +#if defined(NEOPIXEBUS_NO_STL) + // each animation starts with the color that was present + animationState[pixel].StartingColor = originalColor; + // and ends with a random color + animationState[pixel].EndingColor = targetColor; + // using the specific curve + animationState[pixel].Easeing = easing; + + // now use the animation state we just calculated and start the animation + // which will continue to run and call the update function until it completes + animations.StartAnimation(pixel, time, AnimUpdate); +#else + // we must supply a function that will define the animation, in this example + // we are using "lambda expression" to define the function inline, which gives + // us an easy way to "capture" the originalColor and targetColor for the call back. + // + // this function will get called back when ever the animation needs to change + // the state of the pixel, it will provide a animation progress value + // from 0.0 (start of animation) to 1.0 (end of animation) + // + // we use this progress value to define how we want to animate in this case + // we call RgbColor::LinearBlend which will return a color blended between + // the values given, by the amount passed, hich is also a float value from 0.0-1.0. + // then we set the color. + // + // There is no need for the MyAnimationState struct as the compiler takes care + // of those details for us + AnimUpdateCallback animUpdate = [=](const AnimationParam& param) + { + // progress will start at 0.0 and end at 1.0 + // we convert to the curve we want + float progress = easing(param.progress); + + // use the curve value to apply to the animation + RgbColor updatedColor = RgbColor::LinearBlend(originalColor, targetColor, progress); + strip.SetPixelColor(pixel, updatedColor); + }; + + // now use the animation properties we just calculated and start the animation + // which will continue to run and call the update function until it completes + animations.StartAnimation(pixel, time, animUpdate); +#endif + } +} + +void loop() +{ + if (animations.IsAnimating()) + { + // the normal loop just needs these two to run the active animations + animations.UpdateAnimations(); + strip.Show(); + } + else + { + Serial.println(); + Serial.println("Setup Next Set..."); + // example function that sets up some animations + SetupAnimationSet(); + } +} + diff --git a/lib/NeoPixelBus/examples/animations/NeoPixelCylon/NeoPixelCylon.ino b/lib/NeoPixelBus/examples/animations/NeoPixelCylon/NeoPixelCylon.ino new file mode 100644 index 00000000..15b36d0b --- /dev/null +++ b/lib/NeoPixelBus/examples/animations/NeoPixelCylon/NeoPixelCylon.ino @@ -0,0 +1,123 @@ +// NeoPixelCylon +// This example will move a Cylon Red Eye back and forth across the +// the full collection of pixels on the strip. +// +// This will demonstrate the use of the NeoEase animation ease methods; that provide +// simulated acceleration to the animations. +// +// + +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const RgbColor CylonEyeColor(HtmlColor(0x7f0000)); + +NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); + +NeoPixelAnimator animations(2); // only ever need 2 animations + +uint16_t lastPixel = 0; // track the eye position +int8_t moveDir = 1; // track the direction of movement + +// uncomment one of the lines below to see the effects of +// changing the ease function on the movement animation +AnimEaseFunction moveEase = +// NeoEase::Linear; +// NeoEase::QuadraticInOut; +// NeoEase::CubicInOut; + NeoEase::QuarticInOut; +// NeoEase::QuinticInOut; +// NeoEase::SinusoidalInOut; +// NeoEase::ExponentialInOut; +// NeoEase::CircularInOut; + +void FadeAll(uint8_t darkenBy) +{ + RgbColor color; + for (uint16_t indexPixel = 0; indexPixel < strip.PixelCount(); indexPixel++) + { + color = strip.GetPixelColor(indexPixel); + color.Darken(darkenBy); + strip.SetPixelColor(indexPixel, color); + } +} + +void FadeAnimUpdate(const AnimationParam& param) +{ + if (param.state == AnimationState_Completed) + { + FadeAll(10); + animations.RestartAnimation(param.index); + } +} + +void MoveAnimUpdate(const AnimationParam& param) +{ + // apply the movement animation curve + float progress = moveEase(param.progress); + + // use the curved progress to calculate the pixel to effect + uint16_t nextPixel; + if (moveDir > 0) + { + nextPixel = progress * PixelCount; + } + else + { + nextPixel = (1.0f - progress) * PixelCount; + } + + // if progress moves fast enough, we may move more than + // one pixel, so we update all between the calculated and + // the last + if (lastPixel != nextPixel) + { + for (uint16_t i = lastPixel + moveDir; i != nextPixel; i += moveDir) + { + strip.SetPixelColor(i, CylonEyeColor); + } + } + strip.SetPixelColor(nextPixel, CylonEyeColor); + + lastPixel = nextPixel; + + if (param.state == AnimationState_Completed) + { + // reverse direction of movement + moveDir *= -1; + + // done, time to restart this position tracking animation/timer + animations.RestartAnimation(param.index); + } +} + +void SetupAnimations() +{ + // fade all pixels providing a tail that is longer the faster + // the pixel moves. + animations.StartAnimation(0, 5, FadeAnimUpdate); + + // take several seconds to move eye fron one side to the other + animations.StartAnimation(1, 2000, MoveAnimUpdate); +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + SetupAnimations(); +} + +void loop() +{ + // this is all that is needed to keep it running + // and avoiding using delay() is always a good thing for + // any timing related routines + animations.UpdateAnimations(); + strip.Show(); +} diff --git a/lib/NeoPixelBus/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino b/lib/NeoPixelBus/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino new file mode 100644 index 00000000..ac18de5f --- /dev/null +++ b/lib/NeoPixelBus/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino @@ -0,0 +1,130 @@ +// NeoPixelFunFadeInOut +// This example will randomly pick a color and fade all pixels to that color, then +// it will fade them to black and restart over +// +// This example demonstrates the use of a single animation channel to animate all +// the pixels at once. +// +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const uint8_t AnimationChannels = 1; // we only need one as all the pixels are animated at once + +NeoPixelBus strip(PixelCount, PixelPin); +// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +// There are other Esp8266 alternative methods that provide more pin options, but also have +// other side effects. +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods + +NeoPixelAnimator animations(AnimationChannels); // NeoPixel animation management object + +boolean fadeToColor = true; // general purpose variable used to store effect state + + +// what is stored for state is specific to the need, in this case, the colors. +// basically what ever you need inside the animation update function +struct MyAnimationState +{ + RgbColor StartingColor; + RgbColor EndingColor; +}; + +// one entry per pixel to match the animation timing manager +MyAnimationState animationState[AnimationChannels]; + +void SetRandomSeed() +{ + uint32_t seed; + + // random works best with a seed that can use 31 bits + // analogRead on a unconnected pin tends toward less than four bits + seed = analogRead(0); + delay(1); + + for (int shifts = 3; shifts < 31; shifts += 3) + { + seed ^= analogRead(0) << shifts; + delay(1); + } + + randomSeed(seed); +} + +// simple blend function +void BlendAnimUpdate(const AnimationParam& param) +{ + // this gets called for each animation on every time step + // progress will start at 0.0 and end at 1.0 + // we use the blend function on the RgbColor to mix + // color based on the progress given to us in the animation + RgbColor updatedColor = RgbColor::LinearBlend( + animationState[param.index].StartingColor, + animationState[param.index].EndingColor, + param.progress); + + // apply the color to the strip + for (uint16_t pixel = 0; pixel < PixelCount; pixel++) + { + strip.SetPixelColor(pixel, updatedColor); + } +} + +void FadeInFadeOutRinseRepeat(float luminance) +{ + if (fadeToColor) + { + // Fade upto a random color + // we use HslColor object as it allows us to easily pick a hue + // with the same saturation and luminance so the colors picked + // will have similiar overall brightness + RgbColor target = HslColor(random(360) / 360.0f, 1.0f, luminance); + uint16_t time = random(800, 2000); + + animationState[0].StartingColor = strip.GetPixelColor(0); + animationState[0].EndingColor = target; + + animations.StartAnimation(0, time, BlendAnimUpdate); + } + else + { + // fade to black + uint16_t time = random(600, 700); + + animationState[0].StartingColor = strip.GetPixelColor(0); + animationState[0].EndingColor = RgbColor(0); + + animations.StartAnimation(0, time, BlendAnimUpdate); + } + + // toggle to the next effect state + fadeToColor = !fadeToColor; +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + SetRandomSeed(); +} + +void loop() +{ + if (animations.IsAnimating()) + { + // the normal loop just needs these two to run the active animations + animations.UpdateAnimations(); + strip.Show(); + } + else + { + // no animation runnning, start some + // + FadeInFadeOutRinseRepeat(0.2f); // 0.0 = black, 0.25 is normal, 0.5 is bright + } +} + + + diff --git a/lib/NeoPixelBus/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino b/lib/NeoPixelBus/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino new file mode 100644 index 00000000..30a0e202 --- /dev/null +++ b/lib/NeoPixelBus/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino @@ -0,0 +1,139 @@ +// NeoPixelFunLoop +// This example will move a trail of light around a series of pixels. +// A ring formation of pixels looks best. +// The trail will have a slowly fading tail. +// +// This will demonstrate the use of the NeoPixelAnimator. +// It shows the advanced use an animation to control the modification and +// starting of other animations. +// It also shows the normal use of animating colors. +// It also demonstrates the ability to share an animation channel rather than +// hard code them to pixels. +// + +#include +#include + + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint16_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const uint16_t AnimCount = PixelCount / 5 * 2 + 1; // we only need enough animations for the tail and one extra + +const uint16_t PixelFadeDuration = 300; // third of a second +// one second divide by the number of pixels = loop once a second +const uint16_t NextPixelMoveDuration = 1000 / PixelCount; // how fast we move through the pixels + +NeoGamma colorGamma; // for any fade animations, best to correct gamma + +NeoPixelBus strip(PixelCount, PixelPin); +// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +// There are other Esp8266 alternative methods that provide more pin options, but also have +// other side effects. +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods + +// what is stored for state is specific to the need, in this case, the colors and +// the pixel to animate; +// basically what ever you need inside the animation update function +struct MyAnimationState +{ + RgbColor StartingColor; + RgbColor EndingColor; + uint16_t IndexPixel; // which pixel this animation is effecting +}; + +NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object +MyAnimationState animationState[AnimCount]; +uint16_t frontPixel = 0; // the front of the loop +RgbColor frontColor; // the color at the front of the loop + +void SetRandomSeed() +{ + uint32_t seed; + + // random works best with a seed that can use 31 bits + // analogRead on a unconnected pin tends toward less than four bits + seed = analogRead(0); + delay(1); + + for (int shifts = 3; shifts < 31; shifts += 3) + { + seed ^= analogRead(0) << shifts; + delay(1); + } + + // Serial.println(seed); + randomSeed(seed); +} + +void FadeOutAnimUpdate(const AnimationParam& param) +{ + // this gets called for each animation on every time step + // progress will start at 0.0 and end at 1.0 + // we use the blend function on the RgbColor to mix + // color based on the progress given to us in the animation + RgbColor updatedColor = RgbColor::LinearBlend( + animationState[param.index].StartingColor, + animationState[param.index].EndingColor, + param.progress); + // apply the color to the strip + strip.SetPixelColor(animationState[param.index].IndexPixel, + colorGamma.Correct(updatedColor)); +} + +void LoopAnimUpdate(const AnimationParam& param) +{ + // wait for this animation to complete, + // we are using it as a timer of sorts + if (param.state == AnimationState_Completed) + { + // done, time to restart this position tracking animation/timer + animations.RestartAnimation(param.index); + + // pick the next pixel inline to start animating + // + frontPixel = (frontPixel + 1) % PixelCount; // increment and wrap + if (frontPixel == 0) + { + // we looped, lets pick a new front color + frontColor = HslColor(random(360) / 360.0f, 1.0f, 0.25f); + } + + uint16_t indexAnim; + // do we have an animation available to use to animate the next front pixel? + // if you see skipping, then either you are going to fast or need to increase + // the number of animation channels + if (animations.NextAvailableAnimation(&indexAnim, 1)) + { + animationState[indexAnim].StartingColor = frontColor; + animationState[indexAnim].EndingColor = RgbColor(0, 0, 0); + animationState[indexAnim].IndexPixel = frontPixel; + + animations.StartAnimation(indexAnim, PixelFadeDuration, FadeOutAnimUpdate); + } + } +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + SetRandomSeed(); + + // we use the index 0 animation to time how often we move to the next + // pixel in the strip + animations.StartAnimation(0, NextPixelMoveDuration, LoopAnimUpdate); +} + + +void loop() +{ + // this is all that is needed to keep it running + // and avoiding using delay() is always a good thing for + // any timing related routines + animations.UpdateAnimations(); + strip.Show(); +} + + + diff --git a/lib/NeoPixelBus/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino b/lib/NeoPixelBus/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino new file mode 100644 index 00000000..89e8347a --- /dev/null +++ b/lib/NeoPixelBus/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino @@ -0,0 +1,114 @@ +// NeoPixelFunRandomChange +// This example will randomly select a number pixels and then +// start an animation to blend them from their current color to +// randomly selected a color +// + +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +NeoPixelBus strip(PixelCount, PixelPin); +// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +// There are other Esp8266 alternative methods that provide more pin options, but also have +// other side effects. +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods + +NeoPixelAnimator animations(PixelCount); // NeoPixel animation management object + +// what is stored for state is specific to the need, in this case, the colors. +// Basically what ever you need inside the animation update function +struct MyAnimationState +{ + RgbColor StartingColor; + RgbColor EndingColor; +}; + +// one entry per pixel to match the animation timing manager +MyAnimationState animationState[PixelCount]; + +void SetRandomSeed() +{ + uint32_t seed; + + // random works best with a seed that can use 31 bits + // analogRead on a unconnected pin tends toward less than four bits + seed = analogRead(0); + delay(1); + + for (int shifts = 3; shifts < 31; shifts += 3) + { + seed ^= analogRead(0) << shifts; + delay(1); + } + + // Serial.println(seed); + randomSeed(seed); +} + +// simple blend function +void BlendAnimUpdate(const AnimationParam& param) +{ + // this gets called for each animation on every time step + // progress will start at 0.0 and end at 1.0 + // we use the blend function on the RgbColor to mix + // color based on the progress given to us in the animation + RgbColor updatedColor = RgbColor::LinearBlend( + animationState[param.index].StartingColor, + animationState[param.index].EndingColor, + param.progress); + // apply the color to the strip + strip.SetPixelColor(param.index, updatedColor); +} + +void PickRandom(float luminance) +{ + // pick random count of pixels to animate + uint16_t count = random(PixelCount); + while (count > 0) + { + // pick a random pixel + uint16_t pixel = random(PixelCount); + + // pick random time and random color + // we use HslColor object as it allows us to easily pick a color + // with the same saturation and luminance + uint16_t time = random(100, 400); + animationState[pixel].StartingColor = strip.GetPixelColor(pixel); + animationState[pixel].EndingColor = HslColor(random(360) / 360.0f, 1.0f, luminance); + + animations.StartAnimation(pixel, time, BlendAnimUpdate); + + count--; + } +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + SetRandomSeed(); +} + + +void loop() +{ + if (animations.IsAnimating()) + { + // the normal loop just needs these two to run the active animations + animations.UpdateAnimations(); + strip.Show(); + } + else + { + // no animations runnning, start some + // + PickRandom(0.2f); // 0.0 = black, 0.25 is normal, 0.5 is bright + } +} + + + diff --git a/lib/NeoPixelBus/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino b/lib/NeoPixelBus/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino new file mode 100644 index 00000000..b796091c --- /dev/null +++ b/lib/NeoPixelBus/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino @@ -0,0 +1,97 @@ +// NeoPixelFunLoop +// This example will move a trail of light around a series of pixels. +// A ring formation of pixels looks best. +// The trail will have a slowly fading tail. +// +// This will demonstrate the use of the RotateRight method. +// + +#include +#include + + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint16_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const uint16_t AnimCount = 1; // we only need one +const uint16_t TailLength = 6; // length of the tail, must be shorter than PixelCount +const float MaxLightness = 0.4f; // max lightness at the head of the tail (0.5f is full bright) + +NeoGamma colorGamma; // for any fade animations, best to correct gamma + +NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); + +NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object + +void SetRandomSeed() +{ + uint32_t seed; + + // random works best with a seed that can use 31 bits + // analogRead on a unconnected pin tends toward less than four bits + seed = analogRead(0); + delay(1); + + for (int shifts = 3; shifts < 31; shifts += 3) + { + seed ^= analogRead(0) << shifts; + delay(1); + } + + // Serial.println(seed); + randomSeed(seed); +} + +void LoopAnimUpdate(const AnimationParam& param) +{ + // wait for this animation to complete, + // we are using it as a timer of sorts + if (param.state == AnimationState_Completed) + { + // done, time to restart this position tracking animation/timer + animations.RestartAnimation(param.index); + + // rotate the complete strip one pixel to the right on every update + strip.RotateRight(1); + } +} + +void DrawTailPixels() +{ + // using Hsl as it makes it easy to pick from similiar saturated colors + float hue = random(360) / 360.0f; + for (uint16_t index = 0; index < strip.PixelCount() && index <= TailLength; index++) + { + float lightness = index * MaxLightness / TailLength; + RgbColor color = HslColor(hue, 1.0f, lightness); + + strip.SetPixelColor(index, colorGamma.Correct(color)); + } +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + SetRandomSeed(); + + // Draw the tail that will be rotated through all the rest of the pixels + DrawTailPixels(); + + // we use the index 0 animation to time how often we rotate all the pixels + animations.StartAnimation(0, 66, LoopAnimUpdate); +} + + +void loop() +{ + // this is all that is needed to keep it running + // and avoiding using delay() is always a good thing for + // any timing related routines + animations.UpdateAnimations(); + strip.Show(); +} + + diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino new file mode 100644 index 00000000..34111e83 --- /dev/null +++ b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino @@ -0,0 +1,98 @@ +// NeoPixelBuffer +// This example will animate pixels using a bitmap stored on a SD card +// +// +// This will demonstrate the use of the NeoBitmapFile object +// NOTE: The images provided in the example directory should be copied to +// the root of the SD card so the below code will find it. +// NOTE: This sample and the included images were built for a 144 pixel strip so +// running this with a smaller string may not look as interesting. Try providing +// your own 24 bit bitmap for better results. + +#include +#include +#include +#include + +const int chipSelect = D8; // make sure to set this to your SD carder reader CS + +//typedef NeoGrbFeature MyPixelColorFeature; +typedef NeoGrbwFeature MyPixelColorFeature; + +const uint16_t PixelCount = 144; // the sample images are meant for 144 pixels +const uint16_t PixelPin = 2; +const uint16_t AnimCount = 1; // we only need one + +NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); +NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object + +// our NeoBitmapFile will use the same color feature as NeoPixelBus and +// we want it to use the SD File object +NeoBitmapFile image; + +uint16_t animState; + +void LoopAnimUpdate(const AnimationParam& param) +{ + // wait for this animation to complete, + // we are using it as a timer of sorts + if (param.state == AnimationState_Completed) + { + // done, time to restart this position tracking animation/timer + animations.RestartAnimation(param.index); + + // draw the complete row at animState to the complete strip + image.Blt(strip, 0, 0, animState, image.Width()); + animState = (animState + 1) % image.Height(); // increment and wrap + } +} + +void setup() { + Serial.begin(115200); + while (!Serial); // wait for serial attach + + strip.Begin(); + strip.Show(); + + Serial.print("Initializing SD card..."); + + // see if the card is present and can be initialized: + if (!SD.begin(chipSelect)) + { + Serial.println("Card failed, or not present"); + // don't do anything more: + return; + } + Serial.println("card initialized."); + + // open the file + File bitmapFile = SD.open("strings.bmp"); + if (!bitmapFile) + { + Serial.println("File open fail, or not present"); + // don't do anything more: + return; + } + + // initialize the image with the file + if (!image.Begin(bitmapFile)) + { + Serial.println("File format fail, not a supported bitmap"); + // don't do anything more: + return; + } + + animState = 0; + // we use the index 0 animation to time how often we rotate all the pixels + animations.StartAnimation(0, 30, LoopAnimUpdate); +} + +void loop() { + // this is all that is needed to keep it running + // and avoiding using delay() is always a good thing for + // any timing related routines + animations.UpdateAnimations(); + strip.Show(); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/Strings.bmp b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/Strings.bmp new file mode 100644 index 00000000..0ee3be6f Binary files /dev/null and b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/Strings.bmp differ diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/StringsW.bmp b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/StringsW.bmp new file mode 100644 index 00000000..8e45e137 Binary files /dev/null and b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBitmap/StringsW.bmp differ diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn new file mode 100644 index 00000000..e90048c3 Binary files /dev/null and b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn differ diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h new file mode 100644 index 00000000..852c2097 --- /dev/null +++ b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h @@ -0,0 +1,30 @@ +// To recreate the data below, use the Paint.Net plugin "Arduino Progmem NeoPixel FileType" +// to save as/export the included Cylon.pdn +// Paint.Net - http://www.getpaint.net/download.html#download +// Plugin - http://forums.getpaint.net/index.php?/topic/107921-arduino-neopixel-sketch-exporter/ +// This uses Flatten, GRB, Hexadecimal +// + +const uint16_t myImageWidth = 16; +const uint16_t myImageHeight = 20; +const uint8_t PROGMEM myImage[] = { // (16 x 20) GRB in Hexadecimal + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h new file mode 100644 index 00000000..9314f2fb --- /dev/null +++ b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h @@ -0,0 +1,30 @@ +// To recreate the data below, use the Paint.Net plugin "Arduino Progmem NeoPixel FileType" +// to save as/export the included Cylon.pdn +// Paint.Net - http://www.getpaint.net/download.html#download +// Plugin - http://forums.getpaint.net/index.php?/topic/107921-arduino-neopixel-sketch-exporter/ +// This uses Flatten, GRBW, Hexadecimal +// + +const uint16_t myImageWidth = 16; +const uint16_t myImageHeight = 20; +const uint8_t PROGMEM myImage[] = { // (16 x 20) GRBW in Hexadecimal + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino new file mode 100644 index 00000000..1ba86644 --- /dev/null +++ b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino @@ -0,0 +1,74 @@ +// NeoPixelBufferCylon +// This example will move a Cylon Red Eye back and forth across the +// the full collection of pixels on the strip. +// +// This will demonstrate the use of the NeoVerticalSpriteSheet +// + +#include +#include + +// The actual image is contained in the data structure in one of the Cylon*.h files +// You will need to use the one that has the same color feature as your NeoPixelBus +// There are two provided, but you can create your own easily enough using +// free versions of Paint.Net and the plugin +#include "CylonGrb.h" +typedef NeoGrbFeature MyPixelColorFeature; + +// #include "CylonGrbw.h" +// typedef NeoGrbwFeature MyPixelColorFeature; + +const uint16_t PixelCount = 16; // the sample images are meant for 16 pixels +const uint16_t PixelPin = 2; +const uint16_t AnimCount = 1; // we only need one + +NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); +NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object + +// sprite sheet stored in progmem using the same pixel feature as the NeoPixelBus +NeoVerticalSpriteSheet> spriteSheet( + myImageWidth, // image width and sprite width since its vertical sprite sheet + myImageHeight, // image height + 1, // sprite is only one pixel high + myImage); + +uint16_t indexSprite; + +void LoopAnimUpdate(const AnimationParam& param) +{ + // wait for this animation to complete, + // we are using it as a timer of sorts + if (param.state == AnimationState_Completed) + { + // done, time to restart this position tracking animation/timer + animations.RestartAnimation(param.index); + + // draw the next frame in the sprite + spriteSheet.Blt(strip, 0, indexSprite); + indexSprite = (indexSprite + 1) % myImageHeight; // increment and wrap + } +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + indexSprite = 0; + + // we use the index 0 animation to time how often we rotate all the pixels + animations.StartAnimation(0, 60, LoopAnimUpdate); +} + + +void loop() +{ + // this is all that is needed to keep it running + // and avoiding using delay() is always a good thing for + // any timing related routines + animations.UpdateAnimations(); + strip.Show(); +} + diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino new file mode 100644 index 00000000..a435167f --- /dev/null +++ b/lib/NeoPixelBus/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino @@ -0,0 +1,167 @@ +// NeoPixelBufferShader +// This example will provide a shader class to the NeoPixelBuffer that will dim and brighten +// the pixels that are in the buffer (a device dependent bitmap) +// + +#include + +const uint16_t PixelCount = 64; // set this to the size of your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +// three element GRB pixels, change to your needs +NeoPixelBus strip(PixelCount, PixelPin); + +// the buffer object, +// defined to use memory with the same feature as the strip +// initialized with the same number of pixels as our strip +NeoBuffer> image(8,8,NULL); + +const RgbColor BrightRed(255, 0, 0); +const RgbColor BrightGreen(0, 255, 0); +const RgbColor BrightBlue(0, 0, 255); + +const RgbColor BrightYellow(255, 255, 0); +const RgbColor BrightCyan(0, 255, 255); +const RgbColor BrightPurple(255, 0, 255); + +const RgbColor DarkRed(32, 0, 0); +const RgbColor DarkGreen(0, 32, 0); +const RgbColor DarkBlue(0, 0, 32); + +const RgbColor DarkYellow(32, 32, 0); +const RgbColor DarkCyan(0, 32, 32); +const RgbColor DarkPurple(32, 0, 32); + +const RgbColor White(255); +const RgbColor Black(0); + +// define a custom shader object that provides brightness support +// based upon the NeoShaderBase +template class BrightnessShader : public NeoShaderBase +{ +public: + BrightnessShader(): + NeoShaderBase(), + _brightness(255) // default to full bright + {} + + // required for a shader object, it will be called for + // every pixel + void Apply(uint16_t index, uint8_t* pDest, const uint8_t* pSrc) + { + // we don't care what the index is so we ignore it + // + // to apply our brightness shader, + // use the source color, modify, and apply to the destination + + // for every byte in the pixel, + // scale the source value by the brightness and + // store it in the destination byte + const uint8_t* pSrcEnd = pSrc + T_COLOR_FEATURE::PixelSize; + while (pSrc != pSrcEnd) + { + *pDest++ = (*pSrc++ * (uint16_t(_brightness) + 1)) >> 8; + } + } + + // provide an accessor to set brightness + void setBrightness(uint8_t brightness) + { + _brightness = brightness; + Dirty(); // must call dirty when a property changes + } + + // provide an accessor to get brightness + uint8_t getBrightness() + { + return _brightness; + } + +private: + uint8_t _brightness; +}; + +// create an instance of our shader object with the same feature as our buffer +BrightnessShader shader; + +// some dimming tracking variables and settings +int8_t delta; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // this resets all the neopixels to an off state + strip.Begin(); + strip.Show(); + + // dibs do not default to any color, + // so clear it to black if you aren't going to draw + // into every pixel + image.ClearTo(Black); + + // draw a pattern into the image + uint8_t x = 0; + uint8_t y = 0; + image.SetPixelColor(x++, y, DarkRed); + image.SetPixelColor(x++, y, DarkYellow); + image.SetPixelColor(x++, y, DarkGreen); + image.SetPixelColor(x++, y, DarkCyan); + image.SetPixelColor(x++, y, DarkBlue); + image.SetPixelColor(x++, y, DarkPurple); + + x = 0; + y = 1; + image.SetPixelColor(x++, y, BrightRed); + image.SetPixelColor(x++, y, BrightYellow); + image.SetPixelColor(x++, y, BrightGreen); + image.SetPixelColor(x++, y, BrightCyan); + image.SetPixelColor(x++, y, BrightBlue); + image.SetPixelColor(x++, y, BrightPurple); + + Serial.println(); + Serial.println("Running..."); + + delta = -1; // start by dimming downward +} + +void loop() +{ + // we increment by delta every 30ms + delay(30); + + // update the brightness in shader + // + uint8_t brightness = shader.getBrightness(); + // check if we flip directions + if (brightness == 0) + { + delta = 1; // increment + } + else if (brightness == 255) + { + delta = -1; // decrement + } + // modify and apply + brightness += delta; + shader.setBrightness(brightness); + + Serial.println(brightness); + + + // render the image using the shader and then call Show() + // these two should be called together in order + // + + // need to provide the type of color feature for the strip and + // the type of our custom shader + image.Render>(strip, shader); + strip.Show(); + +} + diff --git a/lib/NeoPixelBus/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino b/lib/NeoPixelBus/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino new file mode 100644 index 00000000..4988aecd --- /dev/null +++ b/lib/NeoPixelBus/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino @@ -0,0 +1,158 @@ +// NeoPixelDibTest +// This example will provide a shader class to the NeoPixelDib that will dim and brighten +// the pixels that are in the Dib (Device Independant Bitmap) +// + +#include + +const uint16_t PixelCount = 64; // set this to the size of your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +// three element GRB pixels, change to your needs +NeoPixelBus strip(PixelCount, PixelPin); + +// the DIB object, using RgbColor and initialized with the same number of pixels as our strip +NeoDib image(PixelCount); + +const RgbColor BrightRed(255, 0, 0); +const RgbColor BrightGreen(0, 255, 0); +const RgbColor BrightBlue(0, 0, 255); + +const RgbColor BrightYellow(255, 255, 0); +const RgbColor BrightCyan(0, 255, 255); +const RgbColor BrightPurple(255, 0, 255); + +const RgbColor DarkRed(32, 0, 0); +const RgbColor DarkGreen(0, 32, 0); +const RgbColor DarkBlue(0, 0, 32); + +const RgbColor DarkYellow(32, 32, 0); +const RgbColor DarkCyan(0, 32, 32); +const RgbColor DarkPurple(32, 0, 32); + +const RgbColor White(255); +const RgbColor Black(0); + +// define a custom shader object that provides brightness support +// based upon the NeoShaderBase +class BrightnessShader : public NeoShaderBase +{ +public: + BrightnessShader(): + NeoShaderBase(), + _brightness(255) // default to full bright + {} + + // required for a shader object, it will be called for + // every pixel + RgbColor Apply(uint16_t index, RgbColor original) + { + // we don't care what the index is so we ignore it + // + // to apply our brightness shader, modify the original color and return the color we want + // blend from black (_brightness == 0.0) to the original color (_brightness == 1.0) + + return RgbColor::LinearBlend(Black, original, (float)_brightness / 255.0f); + } + + // provide an accessor to set brightness + void setBrightness(uint8_t brightness) + { + _brightness = brightness; + Dirty(); // must call dirty when a property changes + } + + // provide an accessor to get brightness + uint8_t getBrightness() + { + return _brightness; + } + +private: + uint8_t _brightness; +}; + +// create an instance of our shader object +BrightnessShader shader; + +// some dimming tracking variables and settings +int8_t delta; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + Serial.flush(); + + // this resets all the neopixels to an off state + strip.Begin(); + strip.Show(); + + // dibs do not default to any color, + // so clear it to black if you aren't going to draw + // into every pixel + image.ClearTo(Black); + + // draw a pattern into the image + uint8_t index = 0; + image.SetPixelColor(index++, DarkRed); + image.SetPixelColor(index++, DarkYellow); + image.SetPixelColor(index++, DarkGreen); + image.SetPixelColor(index++, DarkCyan); + image.SetPixelColor(index++, DarkBlue); + image.SetPixelColor(index++, DarkPurple); + + image.SetPixelColor(index++, Black); + image.SetPixelColor(index++, Black); + + image.SetPixelColor(index++, BrightRed); + image.SetPixelColor(index++, BrightYellow); + image.SetPixelColor(index++, BrightGreen); + image.SetPixelColor(index++, BrightCyan); + image.SetPixelColor(index++, BrightBlue); + image.SetPixelColor(index++, BrightPurple); + + Serial.println(); + Serial.println("Running..."); + + delta = -1; // start by dimming downward +} + +void loop() +{ + // we increment by delta every 30ms + delay(30); + + // update the brightness in shader + // + uint8_t brightness = shader.getBrightness(); + // check if we flip directions + if (brightness == 0) + { + delta = 1; // increment + } + else if (brightness == 255) + { + delta = -1; // decrement + } + // modify and apply + brightness += delta; + shader.setBrightness(brightness); + + Serial.println(brightness); + + + // render the image using the shader and then call Show() + // these two should be called together in order + // + + // need to provide the type of color feature for the strip and + // the type of our custom shader + image.Render(strip, shader); + strip.Show(); + +} + diff --git a/lib/NeoPixelBus/examples/gamma/NeoPixelGamma/NeoPixelGamma.ino b/lib/NeoPixelBus/examples/gamma/NeoPixelGamma/NeoPixelGamma.ino new file mode 100644 index 00000000..4c859c92 --- /dev/null +++ b/lib/NeoPixelBus/examples/gamma/NeoPixelGamma/NeoPixelGamma.ino @@ -0,0 +1,95 @@ +// NeoPixelGamma +// This example will display a timed series of color gradients with gamma correction +// and then without. +// If the last pixel is on, then the colors being shown are color corrected. +// It will show Red grandiant, Green grandiant, Blue grandiant, a White grandiant, and +// then repeat. +// +// This will demonstrate the use of the NeoGamma class +// +// + +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); + +// uncomment only one of these to compare memory use or speed +// +// NeoGamma colorGamma; +NeoGamma colorGamma; + +void DrawPixels(bool corrected, HslColor startColor, HslColor stopColor) +{ + for (uint16_t index = 0; index < strip.PixelCount() - 1; index++) + { + float progress = index / static_cast(strip.PixelCount() - 2); + RgbColor color = HslColor::LinearBlend(startColor, stopColor, progress); + if (corrected) + { + color = colorGamma.Correct(color); + } + strip.SetPixelColor(index, color); + } + + // use the last pixel to indicate if we are showing corrected colors or not + if (corrected) + { + strip.SetPixelColor(strip.PixelCount() - 1, RgbColor(64)); + } + else + { + strip.SetPixelColor(strip.PixelCount() - 1, RgbColor(0)); + } + + strip.Show(); +} + +void setup() +{ + strip.Begin(); + strip.Show(); +} + +void loop() +{ + HslColor startColor; + HslColor stopColor; + + // red color + startColor = HslColor(0.0f, 1.0f, 0.0f); + stopColor = HslColor(0.0f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // green color + startColor = HslColor(0.33f, 1.0f, 0.0f); + stopColor = HslColor(0.33f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // blue color + startColor = HslColor(0.66f, 1.0f, 0.0f); + stopColor = HslColor(0.66f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // white color + startColor = HslColor(0.0f, 0.0f, 0.0f); + stopColor = HslColor(0.0f, 0.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/examples/gamma/NeoPixelGammaDynamic/NeoPixelGammaDynamic.ino b/lib/NeoPixelBus/examples/gamma/NeoPixelGammaDynamic/NeoPixelGammaDynamic.ino new file mode 100644 index 00000000..977d6780 --- /dev/null +++ b/lib/NeoPixelBus/examples/gamma/NeoPixelGammaDynamic/NeoPixelGammaDynamic.ino @@ -0,0 +1,103 @@ +// NeoPixelGammaDynamic +// This example will display a timed series of color gradients with gamma correction +// and then without. +// If the last pixel is on, then the colors being shown are color corrected. +// It will show Red grandiant, Green grandiant, Blue grandiant, a White grandiant, and +// then repeat. +// +// This will demonstrate the use of the NeoGamma class with the +// NeoGammaDynamicTableMethod being used for a custom gamma table +// +// + +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +// for esp8266 the pin is ignored unless it is the bitbang method +NeoPixelBus strip(PixelCount, PixelPin); + +NeoGamma colorGamma; + +void DrawPixels(bool corrected, HslColor startColor, HslColor stopColor) +{ + for (uint16_t index = 0; index < strip.PixelCount() - 1; index++) + { + float progress = index / static_cast(strip.PixelCount() - 2); + RgbColor color = HslColor::LinearBlend(startColor, stopColor, progress); + if (corrected) + { + color = colorGamma.Correct(color); + } + strip.SetPixelColor(index, color); + } + + // use the last pixel to indicate if we are showing corrected colors or not + if (corrected) + { + strip.SetPixelColor(strip.PixelCount() - 1, RgbColor(64)); + } + else + { + strip.SetPixelColor(strip.PixelCount() - 1, RgbColor(0)); + } + + strip.Show(); +} + + +float GammaCalc(float unitValue) +{ + // we will use CieLab gamma equation for our custom table + return NeoEase::GammaCieLab(unitValue); +} + +void setup() +{ + // initialize the internal gamma table using our GammaCalc function + // if you plan on using 16bit element colors, add a second optional arg as `true` + NeoGammaDynamicTableMethod::Initialize(GammaCalc); // , true); + + strip.Begin(); + strip.Show(); +} + +void loop() +{ + HslColor startColor; + HslColor stopColor; + + // red color + startColor = HslColor(0.0f, 1.0f, 0.0f); + stopColor = HslColor(0.0f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // green color + startColor = HslColor(0.33f, 1.0f, 0.0f); + stopColor = HslColor(0.33f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // blue color + startColor = HslColor(0.66f, 1.0f, 0.0f); + stopColor = HslColor(0.66f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // white color + startColor = HslColor(0.0f, 0.0f, 0.0f); + stopColor = HslColor(0.0f, 0.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino b/lib/NeoPixelBus/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino new file mode 100644 index 00000000..05a160c0 --- /dev/null +++ b/lib/NeoPixelBus/examples/sevensegment/NeoSegmentBus/NeoSegmentBus.ino @@ -0,0 +1,36 @@ +// NeoSegmentBus +// This example will demonstrate using the NeoSegmentBus which provides support for a +// seven segment LED digit driven by three WS2811; connected in series with other digits +// +// See https://shop.idlehandsdev.com/products/addressable-7-segment-display for a hardware example +// +// This example will print the string "3.14" and then rotate it through the available digits +// + +#include + +const uint16_t DigitCount = 4; // Max Digits, not segments, not pixels +const uint8_t BusPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +#define brightness 128 + +NeoPixelSegmentBus strip(DigitCount, BusPin); + +void setup() +{ + strip.Begin(); + strip.Show(); // clears all digits by default + + delay(500); + strip.SetString(0, "3.14", brightness); + strip.Show(); +} + +void loop() +{ + delay(2000); + + strip.RotateRight(1); // reads right to left, so it is reversed + strip.Show(); +} + diff --git a/lib/NeoPixelBus/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino b/lib/NeoPixelBus/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino new file mode 100644 index 00000000..fd74cad3 --- /dev/null +++ b/lib/NeoPixelBus/examples/sevensegment/NeoSegmentFade/NeoSegmentFade.ino @@ -0,0 +1,144 @@ +// NeoSegmentBus +// This example will demonstrate using the NeoSegmentBus which provides support for a +// seven segment LED digit driven by three WS2811; connected in series with other digits +// +// See https://shop.idlehandsdev.com/products/addressable-7-segment-display for a hardware example +// +// This example will print current seconds since start of the Arduino +// with a digit animating a circling path for each second +// + +#include +#include + +const uint16_t DigitCount = 5; // Max Digits, not segments, not pixels +const uint8_t BusPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const uint16_t CycleDigit = 0; +const uint16_t SecondsDigit = 1; + +#define brightness 128 + +NeoPixelSegmentBus strip(DigitCount, BusPin); + +enum Animation +{ + Animation_Cycle, // animation for the cycle indicator + Animation_Fade, // animation for fade of seconds + Animation_COUNT +}; + +NeoPixelAnimator animations(Animation_COUNT); + +void CycleAnimation(const AnimationParam& param) +{ + // calculate which segment should be on using the animation progress + uint8_t bitfield = 1 << ( static_cast(param.progress * LedSegment_G) % LedSegment_G); + // instant a digit with that segment on + SevenSegDigit digit(bitfield, brightness); + // apply it to the strip + strip.SetPixelColor(CycleDigit, digit); +} + +// for the animation of fading the new number in, we use +// two digit DIBs (Device Independant Bitmaps) of SevenSegDigit to blend with; +// each sized one less than the strip due to the first is a used for the cycle +// animation. +typedef NeoDib SevenSegDib; + +SevenSegDib StartingDigits(DigitCount - 1); +SevenSegDib EndingDigits(DigitCount - 1); + +// shader class that will do the "string" blending +// +class DigitBlendShader +{ +public: + // this shader always renders and doesn't track a dirty state + bool IsDirty() const + { + return true; + } + + void ResetDirty() + { + } + + SevenSegDigit Apply(uint16_t indexDigit, SevenSegDigit digit) + { + // since we call EndingDigits.Render below, the digit argument is + // from the EndingDigits so no need to call GetPixelColor to get it + // create a digit that is a blend between the last seconds + // value and the next seconds value using the BlendAmount + SevenSegDigit blendDigit = SevenSegDigit::LinearBlend( + StartingDigits.GetPixelColor(indexDigit), + digit, + BlendAmount); + + return blendDigit; + } + + float BlendAmount; +}; + +// the instance of our shader class +DigitBlendShader blendShader; + +void FadeAnimation(const AnimationParam& param) +{ + // set the shader property BlendAmount to the animation progress + blendShader.BlendAmount = param.progress; + // apply it to the strip at the SecondsDigit location + EndingDigits.Render(strip, + blendShader, + SecondsDigit); +} + +uint32_t lastSeconds; + +void setup() +{ + lastSeconds = millis() / 1000; + + strip.Begin(); + strip.Show(); + + // init animation Dibs as cleared + StartingDigits.ClearTo(0); + EndingDigits.ClearTo(0); +} + +void loop() +{ + uint32_t seconds = millis() / 1000; + + // when the seconds change, start animations for the update + // + if (seconds != lastSeconds) + { + // copy last animation ending digits as starting digits + StartingDigits = EndingDigits; + + // format and display new value in ending digits dib + String display(seconds); + SevenSegDigit::SetString(EndingDigits, + 0, + display.c_str(), + brightness); + + // start the seconds fade animation + animations.StartAnimation(Animation_Fade, 500, FadeAnimation); + + // start the cycle animation for the next second + animations.StartAnimation(Animation_Cycle, 1000, CycleAnimation); + + lastSeconds = seconds; + } + + if (animations.IsAnimating()) + { + // the normal loop just needs these two to run the active animations + animations.UpdateAnimations(); + strip.Show(); + } +} + diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino new file mode 100644 index 00000000..4854b00a --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino @@ -0,0 +1,101 @@ +//---------------------------------------------------------------------- +// NeoPixelMosaicDump +// This will dump to the serial output a grid map of the defined mosaic +// The output is displayed as row column labeled grid with the NeoPixelBus +// index of the pixel at the intersection of the row and column. +// +// To help with physical layout, there maybe included a symbol following the index +// < means the index is the input index for the panel, the first on the panel +// > means the index is the output index for the panel, the last on the panel +// +// This is useful in visualising the mosaic layout of your panels to +// confirm you have them correctly wired together for this mosaic pattern +// +// It does not require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts +// rotation is ignored for mosaic as it applies a rotation for you +// that is specific to the location of the panel within the mosaic +// to reduce connection lengths + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; + +// make sure to set these panel and tile layout to match your sizes +const uint8_t PanelWidth = 8; // a 8 pixel x 8 pixel matrix of leds on the panel +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +NeoMosaic mosaic( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +void DumpMosaic() +{ + int16_t totalWidth = static_cast(mosaic.getWidth()); + int16_t totalHeight = static_cast(mosaic.getHeight()); + + Serial.println(); + + Serial.print("\t\t"); + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print(x); + Serial.print("\t"); + } + Serial.println(); + + Serial.print("\t---"); + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print("--------"); + } + Serial.println(); + + for (int16_t y = 0; y < totalHeight; y++) + { + Serial.print(" "); + Serial.print(y); + Serial.print("\t|\t"); + + for (int16_t x = 0; x < totalWidth; x++) + { + NeoTopologyHint hint = mosaic.TopologyHint(x, y); + + Serial.print(mosaic.Map(x, y)); + if (hint == NeoTopologyHint_FirstOnPanel) + { + Serial.print("<"); + } + else if (hint == NeoTopologyHint_LastOnPanel) + { + Serial.print(">"); + } + Serial.print("\t"); + } + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + DumpMosaic(); +} + +void loop() +{ + +} + diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino new file mode 100644 index 00000000..700a1bfe --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino @@ -0,0 +1,97 @@ +//---------------------------------------------------------------------- +// NeoPixelTopologyTest +// This will display specific colors in specific locations on the led panels +// +// This is useful in confirming the layout of your panels +// +// It does require that you have the actual panels connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts +// rotation is ignored for mosaic as it applies a rotation for you +// that is specific to the location of the panel within the mosaic +// to reduce connection lengths + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +const uint16_t PixelCount = PanelWidth * PanelHeight * TileWidth * TileHeight; +const uint8_t PixelPin = 2; + +NeoMosaic mosaic( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); + +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor white(128); +// if using NeoRgbwFeature above, use this white instead to use +// the correct white element of the LED +//RgbwColor white(128); +RgbColor black(0); + +const uint16_t left = 0; +const uint16_t right = PanelWidth - 1; +const uint16_t top = 0; +const uint16_t bottom = PanelHeight - 1; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("Upper left is white."); + Serial.println("Upper right is Red."); + Serial.println("Lower right is Green"); + Serial.println("Lower Left is Blue"); + + // use the topo to map the 2d cordinate to the pixel + // and use that to SetPixelColor + strip.SetPixelColor(mosaic.Map(left, top), white); + strip.SetPixelColor(mosaic.Map(right, top), red); + strip.SetPixelColor(mosaic.Map(right, bottom), green); + strip.SetPixelColor(mosaic.Map(left, bottom), blue); + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} + diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelRingDynamicTopologyTest/NeoPixelRingDynamicTopologyTest.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelRingDynamicTopologyTest/NeoPixelRingDynamicTopologyTest.ino new file mode 100644 index 00000000..6e4753a1 --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelRingDynamicTopologyTest/NeoPixelRingDynamicTopologyTest.ino @@ -0,0 +1,122 @@ +//---------------------------------------------------------------------- +// NeoPixelRingTopologyTest +// This will display specific colors in specific locations on the led rings +// +// This is useful in confirming the layout of your rings +// +// It does require that you have the actual series of rings connected +//---------------------------------------------------------------------- + +#include + +const uint8_t PixelCount = 119; +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +// define the layout of your series of rings +// +// This example is using all of Adafruits rings and a Jewel in the center. +// The center is the input and all the rings are connected in series going outward +// +// Rings: +// 0 - 1 (virtual ring, the center of the jewel) +// 1 - 6 (virtual ring, the outer ring of the jewel) +// 2 - 12 count ring +// 3 - 16 count ring +// 4 - 24 count ring +// 5 - 60 count ring comprised of four arc segments +// +// The values below in Rings[] are the index of the first pixel in each ring. +// An extra value is appended for a virtual ring start that also +// represents the total count of pixels in the complete series and this extra +// value is required. +// +class MyRingsLayout +{ +public: + void Begin() { + // this is where you load your dynamic rings layout and init Rings and RingCount + // this example will just set these to static numbers to simulate a dynamic layout + RingCount = 6; + Rings = new uint16_t[RingCount]; + + Rings[0] = 1; + Rings[1] = 6; + Rings[2] = 12; + Rings[3] = 16; + Rings[4] = 24; + Rings[5] = 60; // don't forget the final count of pixels as the last item + } + +protected: + uint16_t* Rings; + uint8_t RingCount; + + uint8_t _ringCount() const + { + return RingCount; + } +}; + +// use the MyRingsLayout to declare the topo object +// +NeoRingTopology topo; + +// declare our strip +// +NeoPixelBus strip(PixelCount, PixelPin); + +// define some handy colors +// +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor black(0); + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + topo.Begin(); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("First pixel in each ring is Red."); + Serial.println("Middle pixel in each ring is Green."); + Serial.println("Last Pixel in each ring is Blue."); + + + // use the topo to map the 2d polar cordinate to the pixel + // and use that to SetPixelColor + for (uint16_t ring = 0; ring < topo.getCountOfRings(); ring++) + { + // first pixel in each ring is red + strip.SetPixelColor(topo.Map(ring, 0), red); + // last pixel in each ring is blue + strip.SetPixelColor(topo.Map(ring, topo.getPixelCountAtRing(ring) - 1), blue); + // middle pixel in each ring is green + strip.SetPixelColor(topo.Map(ring, topo.getPixelCountAtRing(ring) / 2), green); + } + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino new file mode 100644 index 00000000..47922a7f --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino @@ -0,0 +1,104 @@ +//---------------------------------------------------------------------- +// NeoPixelRingTopologyTest +// This will display specific colors in specific locations on the led rings +// +// This is useful in confirming the layout of your rings +// +// It does require that you have the actual series of rings connected +//---------------------------------------------------------------------- + +#include + +const uint8_t PixelCount = 119; +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +// define the layout of your series of rings +// +// This example is using all of Adafruits rings and a Jewel in the center. +// The center is the input and all the rings are connected in series going outward +// +// Rings: +// 0 - 1 (virtual ring, the center of the jewel) +// 1 - 6 (virtual ring, the outer ring of the jewel) +// 2 - 12 count ring +// 3 - 16 count ring +// 4 - 24 count ring +// 5 - 60 count ring comprised of four arc segments +// +// The values below in Rings[] are the index of the first pixel in each ring. +// An extra value is appended for a virtual ring start that also +// represents the total count of pixels in the complete series and this extra +// value is required. +// +class MyRingsLayout +{ +protected: + const uint16_t Rings[7] = {0, 1, 7, 19, 35, 59, PixelCount}; + + uint8_t _ringCount() const + { + return sizeof(Rings) / sizeof(Rings[0]); + } +}; + +// use the MyRingsLayout to declare the topo object +// +NeoRingTopology topo; + +// declare our strip +// +NeoPixelBus strip(PixelCount, PixelPin); + +// define some handy colors +// +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor black(0); + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("First pixel in each ring is Red."); + Serial.println("Middle pixel in each ring is Green."); + Serial.println("Last Pixel in each ring is Blue."); + + + // use the topo to map the 2d polar cordinate to the pixel + // and use that to SetPixelColor + for (uint16_t ring = 0; ring < topo.getCountOfRings(); ring++) + { + // first pixel in each ring is red + strip.SetPixelColor(topo.Map(ring, 0), red); + // last pixel in each ring is blue + strip.SetPixelColor(topo.Map(ring, topo.getPixelCountAtRing(ring) - 1), blue); + // middle pixel in each ring is green + strip.SetPixelColor(topo.Map(ring, topo.getPixelCountAtRing(ring) / 2), green); + } + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino new file mode 100644 index 00000000..c95a0d2f --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino @@ -0,0 +1,105 @@ +//---------------------------------------------------------------------- +// NeoPixelTileDump +// This will dump to the serial output a grid map of the defined tiles +// The output is displayed as row column labeled grid with the NeoPixelBus +// index of the pixel at the intersection of the row and column +// +// To help with physical layout, there maybe included a symbol following the index +// < means the index is the input index for the panel, the first on the panel +// > means the index is the output index for the panel, the last on the panel +// +// This is useful in visualising the tile layout of your panels to +// confirm you have them correctly wired together for the defined pattern +// +// It does not require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// change this to be one of the layouts which will define the layout +// of the panels themselves +typedef ColumnMajorLayout MyTilesLayout; + +// make sure to set these panel and tile layout to match your sizes +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +NeoTiles tiles( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +void DumpTopo() +{ + int16_t totalWidth = static_cast(tiles.getWidth()); + int16_t totalHeight = static_cast(tiles.getHeight()); + + Serial.println(); + + Serial.print("\t\t"); + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print(x); + Serial.print("\t"); + } + Serial.println(); + + Serial.print("\t---"); + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print("--------"); + } + Serial.println(); + + for (int16_t y = 0; y < totalHeight; y++) + { + Serial.print(" "); + Serial.print(y); + Serial.print("\t|\t"); + + for (int16_t x = 0; x < totalWidth; x++) + { + NeoTopologyHint hint = tiles.TopologyHint(x, y); + + Serial.print(tiles.Map(x, y)); + if (hint == NeoTopologyHint_FirstOnPanel) + { + Serial.print("<"); + } + else if (hint == NeoTopologyHint_LastOnPanel) + { + Serial.print(">"); + } + Serial.print("\t"); + } + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + DumpTopo(); +} + +void loop() +{ + +} + diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino new file mode 100644 index 00000000..f86e603b --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino @@ -0,0 +1,103 @@ +//---------------------------------------------------------------------- +// NeoPixelTilesTest +// This will display specific colors in specific locations on the led panels +// +// This is useful in confirming the layout of your panels +// +// It does require that you have the actual panels connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// change this to be one of the layouts which will define the layout +// of the panels themselves +typedef ColumnMajorLayout MyTilesLayout; + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +const uint16_t PixelCount = PanelWidth * PanelHeight * TileWidth * TileHeight; +const uint8_t PixelPin = 2; + +NeoTiles tiles( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); + +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor white(128); +// if using NeoRgbwFeature above, use this white instead to use +// the correct white element of the LED +//RgbwColor white(128); +RgbColor black(0); + +const uint16_t left = 0; +const uint16_t right = PanelWidth - 1; +const uint16_t top = 0; +const uint16_t bottom = PanelHeight - 1; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("Upper left is white."); + Serial.println("Upper right is Red."); + Serial.println("Lower right is Green"); + Serial.println("Lower Left is Blue"); + + // use the topo to map the 2d cordinate to the pixel + // and use that to SetPixelColor + strip.SetPixelColor(tiles.Map(left, top), white); + strip.SetPixelColor(tiles.Map(right, top), red); + strip.SetPixelColor(tiles.Map(right, bottom), green); + strip.SetPixelColor(tiles.Map(left, bottom), blue); + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} + diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino new file mode 100644 index 00000000..d0e5548f --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------- +// NeoPixelTopologyDump +// This will dump to the serial output a grid map of the defined topology +// The output is displayed as row column labeled grid with the NeoPixelBus +// index of the pixel at the intersection of the row and column +// +// This is useful in visualising the layout of your panel so you can +// confirm you have the correct pattern +// +// It does not require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; + +NeoTopology topo(PanelWidth, PanelHeight); + +void DumpTopo() +{ + int16_t totalWidth = static_cast(topo.getWidth()); + int16_t totalHeight = static_cast(topo.getHeight()); + + Serial.println(); + + Serial.print("\t\t"); + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print(x); + Serial.print("\t"); + } + Serial.println(); + + Serial.print("\t--"); + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print("--------"); + } + Serial.println(); + + for (int16_t y = 0; y < totalHeight; y++) + { + Serial.print(" "); + Serial.print(y); + Serial.print("\t|\t"); + + for (int16_t x = 0; x < totalWidth; x++) + { + Serial.print(topo.Map(x, y)); + Serial.print("\t"); + } + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + DumpTopo(); +} + +void loop() +{ + +} + diff --git a/lib/NeoPixelBus/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino b/lib/NeoPixelBus/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino new file mode 100644 index 00000000..6ff06286 --- /dev/null +++ b/lib/NeoPixelBus/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino @@ -0,0 +1,92 @@ +//---------------------------------------------------------------------- +// NeoPixelTopologyTest +// This will display specific colors in specific locations on the led panel +// +// This is useful in confirming the layout of your panel +// +// It does require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint16_t PixelCount = PanelWidth * PanelHeight; +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +NeoTopology topo(PanelWidth, PanelHeight); + +NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +// for esp8266 omit the pin +//NeoPixelBus strip(PixelCount); + +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor white(128); +// if using NeoRgbwFeature above, use this white instead to use +// the correct white element of the LED +//RgbwColor white(128); +RgbColor black(0); + +const uint16_t left = 0; +const uint16_t right = PanelWidth - 1; +const uint16_t top = 0; +const uint16_t bottom = PanelHeight - 1; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("Upper left is white."); + Serial.println("Upper right is Red."); + Serial.println("Lower right is Green"); + Serial.println("Lower Left is Blue"); + + // use the topo to map the 2d cordinate to the pixel + // and use that to SetPixelColor + strip.SetPixelColor(topo.Map(left, top), white); + strip.SetPixelColor(topo.Map(right, top), red); + strip.SetPixelColor(topo.Map(right, bottom), green); + strip.SetPixelColor(topo.Map(left, bottom), blue); + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} + diff --git a/lib/NeoPixelBus/keywords.txt b/lib/NeoPixelBus/keywords.txt new file mode 100644 index 00000000..b38278ba --- /dev/null +++ b/lib/NeoPixelBus/keywords.txt @@ -0,0 +1,872 @@ +####################################### +# Syntax Coloring Map NeoPixelBus +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +NeoPixelBus KEYWORD1 +NeoPixelBusLg KEYWORD1 +NeoPixelSegmentBus KEYWORD1 +RgbwColor KEYWORD1 +RgbColor KEYWORD1 +Rgb16Color KEYWORD1 +Rgb48Color KEYWORD1 +Rgbw64Color KEYWORD1 +HslColor KEYWORD1 +HsbColor KEYWORD1 +HtmlColor KEYWORD1 +NeoNoSettings KEYWORD1 +NeoTm1814Settings KEYWORD1 +NeoTm1914Settings KEYWORD1 +NeoSm16803pbSettings KEYWORD1 +NeoSm16823eSettings KEYWORD1 +NeoSm16804ebSettings KEYWORD1 +NeoSm16824eSettings KEYWORD1 +NeoSpiSettings KEYWORD1 +NeoGrbFeature KEYWORD1 +NeoGrbwFeature KEYWORD1 +NeoRgbwFeature KEYWORD1 +NeoRgbFeature KEYWORD1 +NeoBrgFeature KEYWORD1 +NeoRbgFeature KEYWORD1 +NeoBgrFeature KEYWORD1 +NeoRgbw64Feature KEYWORD1 +NeoRgb48Feature KEYWORD1 +NeoGrb48Feature KEYWORD1 +NeoRgbUcs8903Feature KEYWORD1 +NeoRgbwUcs8904Feature KEYWORD1 +NeoGrb48Ws2816Feature KEYWORD1 +NeoWrgbTm1814Feature KEYWORD1 +NeoRgbTm1914Feature KEYWORD1 +NeoGrbTm1914Feature KEYWORD1 +NeoRgbSm16803pbColorFeature KEYWORD1 +NeoRgbSm16823eColorFeature KEYWORD1 +NeoRgbwSm16804ebColorFeature KEYWORD1 +NeoRgbwSm16824eColorFeature KEYWORD1 +DotStarBgrFeature KEYWORD1 +DotStarLbgrFeature KEYWORD1 +Lpd6803GrbFeature KEYWORD1 +Lpd6803GbrFeature KEYWORD1 +Lpd6803BrgFeature KEYWORD1 +Lpd6803RgbFeature KEYWORD1 +Lpd8806GrbFeature KEYWORD1 +Lpd8806BrgFeature KEYWORD1 +P9813BgrFeature KEYWORD1 +SevenSegmentFeature KEYWORD1 +Neo800KbpsMethod KEYWORD1 +Neo400KbpsMethod KEYWORD1 +NeoWs2813Method KEYWORD1 +NeoWs2812xMethod KEYWORD1 +NeoWs2812Method KEYWORD1 +NeoWs2811Method KEYWORD1 +NeoWs2816Method KEYWORD1 +NeoSk6812Method KEYWORD1 +NeoTm1814Method KEYWORD1 +NeoTm1914Method KEYWORD1 +NeoTm1829Method KEYWORD1 +NeoTx1812Method KEYWORD1 +NeoLc8812Method KEYWORD1 +NeoApa106Method KEYWORD1 +NeoIntertekMethod KEYWORD1 +Neo800KbpsInvertedMethod KEYWORD1 +Neo400KbpsInvertedMethod KEYWORD1 +NeoWs2813InvertedMethod KEYWORD1 +NeoWs2812xInvertedMethod KEYWORD1 +NeoWs2812InvertedMethod KEYWORD1 +NeoWs2811InvertedMethod KEYWORD1 +NeoWs2816InvertedMethod KEYWORD1 +NeoSk6812InvertedMethod KEYWORD1 +NeoTm1814InvertedMethod KEYWORD1 +NeoTm1914InvertedMethod KEYWORD1 +NeoTm1829InvertedMethod KEYWORD1 +NeoTx1812InvertedMethod KEYWORD1 +NeoLc8812InvertedMethod KEYWORD1 +NeoApa106InvertedMethod KEYWORD1 +NeoInvertedIntertekMethod KEYWORD1 +NeoEsp8266DmaWs2812xMethod KEYWORD1 +NeoEsp8266DmaWs2816Method KEYWORD1 +NeoEsp8266DmaSk6812Method KEYWORD1 +NeoEsp8266DmaTm1814Method KEYWORD1 +NeoEsp8266DmaTm1914Method KEYWORD1 +NeoEsp8266DmaTm1829Method KEYWORD1 +NeoEsp8266DmaApa106Method KEYWORD1 +NeoEsp8266Dma800KbpsMethod KEYWORD1 +NeoEsp8266Dma400KbpsMethod KEYWORD1 +NeoEsp8266DmaInvertedWs2812xMethod KEYWORD1 +NeoEsp8266DmaInvertedWs2816Method KEYWORD1 +NeoEsp8266DmaInvertedSk6812Method KEYWORD1 +NeoEsp8266DmaInvertedTm1814Method KEYWORD1 +NeoEsp8266DmaInvertedTm1914Method KEYWORD1 +NeoEsp8266DmaInvertedTm1829Method KEYWORD1 +NeoEsp8266DmaInvertedApa106Method KEYWORD1 +NeoEsp8266DmaInverted800KbpsMethod KEYWORD1 +NeoEsp8266DmaInverted400KbpsMethod KEYWORD1 +NeoEsp8266Dmx512Method KEYWORD1 +NeoEsp8266Ws2821Method KEYWORD1 +NeoEsp8266Dmx512InvertedMethod KEYWORD1 +NeoEsp8266Ws2821InvertedMethod KEYWORD1 +NeoEsp8266Uart0Ws2813Method KEYWORD1 +NeoEsp8266Uart0Ws2812xMethod KEYWORD1 +NeoEsp8266Uart0Ws2812Method KEYWORD1 +NeoEsp8266Uart0Ws2811Method KEYWORD1 +NeoEsp8266Uart0Ws2816Method KEYWORD1 +NeoEsp8266Uart0Sk6812Method KEYWORD1 +NeoEsp8266Uart0Tm1814Method KEYWORD1 +NeoEsp8266Uart0Tm1914Method KEYWORD1 +NeoEsp8266Uart0Tm1829Method KEYWORD1 +NeoEsp8266Uart0Lc8812Method KEYWORD1 +NeoEsp8266Uart0Apa106Method KEYWORD1 +NeoEsp8266Uart0800KbpsMethod KEYWORD1 +NeoEsp8266Uart0400KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2813Method KEYWORD1 +NeoEsp8266AsyncUart0Ws2812xMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2812Method KEYWORD1 +NeoEsp8266AsyncUart0Ws2811Method KEYWORD1 +NeoEsp8266AsyncUart0Ws2816Method KEYWORD1 +NeoEsp8266AsyncUart0Sk6812Method KEYWORD1 +NeoEsp8266AsyncUart0Tm1814Method KEYWORD1 +NeoEsp8266AsyncUart0Tm1914Method KEYWORD1 +NeoEsp8266AsyncUart0Tm1829Method KEYWORD1 +NeoEsp8266AsyncUart0Lc8812Method KEYWORD1 +NeoEsp8266AsyncUart0Apa106Method KEYWORD1 +NeoEsp8266AsyncUart0800KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart0400KbpsMethod KEYWORD1 +NeoEsp8266Uart1Ws2813Method KEYWORD1 +NeoEsp8266Uart1Ws2812xMethod KEYWORD1 +NeoEsp8266Uart1Ws2812Method KEYWORD1 +NeoEsp8266Uart1Ws2811Method KEYWORD1 +NeoEsp8266Uart1Ws2816Method KEYWORD1 +NeoEsp8266Uart1Sk6812Method KEYWORD1 +NeoEsp8266Uart1Tm1814 KEYWORD1 +NeoEsp8266Uart1Tm1914 KEYWORD1 +NeoEsp8266Uart1Tm1829 KEYWORD1 +NeoEsp8266Uart1Lc8812Method KEYWORD1 +NeoEsp8266Uart1Apa106Method KEYWORD1 +NeoEsp8266Uart1800KbpsMethod KEYWORD1 +NeoEsp8266Uart1400KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2813Method KEYWORD1 +NeoEsp8266AsyncUart1Ws2812xMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2812Method KEYWORD1 +NeoEsp8266AsyncUart1Ws2811Method KEYWORD1 +NeoEsp8266AsyncUart1Ws2816Method KEYWORD1 +NeoEsp8266AsyncUart1Sk6812Method KEYWORD1 +NeoEsp8266AsyncUart1Tm1814 KEYWORD1 +NeoEsp8266AsyncUart1Tm1914 KEYWORD1 +NeoEsp8266AsyncUart1Tm1829 KEYWORD1 +NeoEsp8266AsyncUart1Lc8812Method KEYWORD1 +NeoEsp8266AsyncUart1Apa106Method KEYWORD1 +NeoEsp8266AsyncUart1800KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart1400KbpsMethod KEYWORD1 +NeoEsp8266Uart0Ws2813InvertedMethod KEYWORD1 +NeoEsp8266Uart0Ws2812xInvertedMethod KEYWORD1 +NeoEsp8266Uart0Ws2812InvertedMethod KEYWORD1 +NeoEsp8266Uart0Ws2811InvertedMethod KEYWORD1 +NeoEsp8266Uart0Ws2816InvertedMethod KEYWORD1 +NeoEsp8266Uart0Sk6812InvertedMethod KEYWORD1 +NeoEsp8266Uart0Tm1814InvertedMethod KEYWORD1 +NeoEsp8266Uart0Tm1914InvertedMethod KEYWORD1 +NeoEsp8266Uart0Tm1829InvertedMethod KEYWORD1 +NeoEsp8266Uart0Lc8812InvertedMethod KEYWORD1 +NeoEsp8266Uart0Apa106InvertedMethod KEYWORD1 +NeoEsp8266Uart0800KbpsInvertedMethod KEYWORD1 +NeoEsp8266Uart0400KbpsInvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2813InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2812xInvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2812InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2811InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2816InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Sk6812InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Tm1814InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Tm1914InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Tm1829InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Lc8812InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0Apa106InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0800KbpsInvertedMethod KEYWORD1 +NeoEsp8266AsyncUart0400KbpsInvertedMethod KEYWORD1 +NeoEsp8266Uart1Ws2813InvertedMethod KEYWORD1 +NeoEsp8266Uart1Ws2812xInvertedMethod KEYWORD1 +NeoEsp8266Uart1Ws2812InvertedMethod KEYWORD1 +NeoEsp8266Uart1Ws2811InvertedMethod KEYWORD1 +NeoEsp8266Uart1Ws2816InvertedMethod KEYWORD1 +NeoEsp8266Uart1Sk6812InvertedMethod KEYWORD1 +NeoEsp8266Uart1Tm1814InvertedMethod KEYWORD1 +NeoEsp8266Uart1Tm1914InvertedMethod KEYWORD1 +NeoEsp8266Uart1Tm1829InvertedMethod KEYWORD1 +NeoEsp8266Uart1Lc8812InvertedMethod KEYWORD1 +NeoEsp8266Uart1Apa106InvertedMethod KEYWORD1 +NeoEsp8266Uart1800KbpsInvertedMethod KEYWORD1 +NeoEsp8266Uart1400KbpsInvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2813InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2812xInvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2812InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2811InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2816InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Sk6812InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Tm1814InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Tm1914InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Tm1829InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Lc8812InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1Apa106InvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1800KbpsInvertedMethod KEYWORD1 +NeoEsp8266AsyncUart1400KbpsInvertedMethod KEYWORD1 +NeoEsp8266BitBangWs2813Method KEYWORD1 +NeoEsp8266BitBangWs2812xMethod KEYWORD1 +NeoEsp8266BitBangWs2812Method KEYWORD1 +NeoEsp8266BitBangWs2811Method KEYWORD1 +NeoEsp8266BitBangWs2816Method KEYWORD1 +NeoEsp8266BitBangSk6812Method KEYWORD1 +NeoEsp8266BitBangTm1814Method KEYWORD1 +NeoEsp8266BitBangTm1914Method KEYWORD1 +NeoEsp8266BitBangTm1829Method KEYWORD1 +NeoEsp8266BitBangLc8812Method KEYWORD1 +NeoEsp8266BitBangApa106Method KEYWORD1 +NeoEsp8266BitBang800KbpsMethod KEYWORD1 +NeoEsp8266BitBang400KbpsMethod KEYWORD1 +NeoEsp8266BitBangWs2813InvertedMethod KEYWORD1 +NeoEsp8266BitBangWs2812xInvertedMethod KEYWORD1 +NeoEsp8266BitBangWs2812InvertedMethod KEYWORD1 +NeoEsp8266BitBangWs2811InvertedMethod KEYWORD1 +NeoEsp8266BitBangWs2816InvertedMethod KEYWORD1 +NeoEsp8266BitBangSk6812InvertedMethod KEYWORD1 +NeoEsp8266BitBangTm1814InvertedMethod KEYWORD1 +NeoEsp8266BitBangTm1914InvertedMethod KEYWORD1 +NeoEsp8266BitBangTm1829InvertedMethod KEYWORD1 +NeoEsp8266BitBangLc8812InvertedMethod KEYWORD1 +NeoEsp8266BitBangApa106InvertedMethod KEYWORD1 +NeoEsp8266BitBang800KbpsInvertedMethod KEYWORD1 +NeoEsp8266BitBang400KbpsInvertedMethod KEYWORD1 +NeoEsp32I2sNWs2812xMethod KEYWORD1 +NeoEsp32I2sNWs2816Method KEYWORD1 +NeoEsp32I2sNSk6812Method KEYWORD1 +NeoEsp32I2sNTm1814Method KEYWORD1 +NeoEsp32I2sNTm1914Method KEYWORD1 +NeoEsp32I2sN800KbpsMethod KEYWORD1 +NeoEsp32I2sN400KbpsMethod KEYWORD1 +NeoEsp32I2sNApa106Method KEYWORD1 +NeoEsp32I2s0Ws2812xMethod KEYWORD1 +NeoEsp32I2s0Ws2816Method KEYWORD1 +NeoEsp32I2s0Sk6812Method KEYWORD1 +NeoEsp32I2s0Tm1814Method KEYWORD1 +NeoEsp32I2s0Tm1914Method KEYWORD1 +NeoEsp32I2s0Tm1829Method KEYWORD1 +NeoEsp32I2s0800KbpsMethod KEYWORD1 +NeoEsp32I2s0400KbpsMethod KEYWORD1 +NeoEsp32I2s0Apa106Method KEYWORD1 +NeoEsp32I2s1Ws2812xMethod KEYWORD1 +NeoEsp32I2s1Ws2816Method KEYWORD1 +NeoEsp32I2s1Sk6812Method KEYWORD1 +NeoEsp32I2s1Tm1814Method KEYWORD1 +NeoEsp32I2s1Tm1914Method KEYWORD1 +NeoEsp32I2s1Tm1829Method KEYWORD1 +NeoEsp32I2s1800KbpsMethod KEYWORD1 +NeoEsp32I2s1400KbpsMethod KEYWORD1 +NeoEsp32I2s1Apa106Method KEYWORD1 +NeoEsp32I2sNWs2812xInvertedMethod KEYWORD1 +NeoEsp32I2sNWs2816InvertedMethod KEYWORD1 +NeoEsp32I2sNSk6812InvertedMethod KEYWORD1 +NeoEsp32I2sNTm1814InvertedMethod KEYWORD1 +NeoEsp32I2sNTm1914InvertedMethod KEYWORD1 +NeoEsp32I2sN800KbpsInvertedMethod KEYWORD1 +NeoEsp32I2sN400KbpsInvertedMethod KEYWORD1 +NeoEsp32I2sNApa106InvertedMethod KEYWORD1 +NeoEsp32I2s0Ws2812xInvertedMethod KEYWORD1 +NeoEsp32I2s0Ws2816InvertedMethod KEYWORD1 +NeoEsp32I2s0Sk6812InvertedMethod KEYWORD1 +NeoEsp32I2s0Tm1814InvertedMethod KEYWORD1 +NeoEsp32I2s0Tm1914InvertedMethod KEYWORD1 +NeoEsp32I2s0Tm1829InvertedMethod KEYWORD1 +NeoEsp32I2s0800KbpsInvertedMethod KEYWORD1 +NeoEsp32I2s0400KbpsInvertedMethod KEYWORD1 +NeoEsp32I2s0Apa106InvertedMethod KEYWORD1 +NeoEsp32I2s1Ws2812xInvertedMethod KEYWORD1 +NeoEsp32I2s1Ws2816InvertedMethod KEYWORD1 +NeoEsp32I2s1Sk6812InvertedMethod KEYWORD1 +NeoEsp32I2s1Tm1814InvertedMethod KEYWORD1 +NeoEsp32I2s1Tm1914InvertedMethod KEYWORD1 +NeoEsp32I2s1Tm1829InvertedMethod KEYWORD1 +NeoEsp32I2s1800KbpsInvertedMethod KEYWORD1 +NeoEsp32I2s1400KbpsInvertedMethod KEYWORD1 +NeoEsp32I2s1Apa106InvertedMethod KEYWORD1 +NeoEsp32I2s0X8Ws2812xMethod KEYWORD1 +NeoEsp32I2s0X8800KbpsMethod KEYWORD1 +NeoEsp32I2s0X8400KbpsMethod KEYWORD1 +NeoEsp32I2s0X8Ws2813Method KEYWORD1 +NeoEsp32I2s0X8Ws2812dMethod KEYWORD1 +NeoEsp32I2s0X8Ws2811Method KEYWORD1 +NeoEsp32I2s0X8Ws2816Method KEYWORD1 +NeoEsp32I2s0X8Ws2812Method KEYWORD1 +NeoEsp32I2s0X8Sk6812Method KEYWORD1 +NeoEsp32I2s0X8Tm1814Method KEYWORD1 +NeoEsp32I2s0X8Tm1829Method KEYWORD1 +NeoEsp32I2s0X8Tm1914Method KEYWORD1 +NeoEsp32I2s0X8Lc8812Method KEYWORD1 +NeoEsp32I2s0X8Apa106Method KEYWORD1 +NeoEsp32I2s0X16Ws2812xMethod KEYWORD1 +NeoEsp32I2s0X16800KbpsMethod KEYWORD1 +NeoEsp32I2s0X16400KbpsMethod KEYWORD1 +NeoEsp32I2s0X16Ws2813Method KEYWORD1 +NeoEsp32I2s0X16Ws2812dMethod KEYWORD1 +NeoEsp32I2s0X16Ws2811Method KEYWORD1 +NeoEsp32I2s0X16Ws2816Method KEYWORD1 +NeoEsp32I2s0X16Ws2812Method KEYWORD1 +NeoEsp32I2s0X16Sk6812Method KEYWORD1 +NeoEsp32I2s0X16Tm1814Method KEYWORD1 +NeoEsp32I2s0X16Tm1829Method KEYWORD1 +NeoEsp32I2s0X16Tm1914Method KEYWORD1 +NeoEsp32I2s0X16Lc8812Method KEYWORD1 +NeoEsp32I2s0X16Apa106Method KEYWORD1 +NeoEsp32I2s1X8Ws2812xMethod KEYWORD1 +NeoEsp32I2s1X8800KbpsMethod KEYWORD1 +NeoEsp32I2s1X8400KbpsMethod KEYWORD1 +NeoEsp32I2s1X8Ws2813Method KEYWORD1 +NeoEsp32I2s1X8Ws2812dMethod KEYWORD1 +NeoEsp32I2s1X8Ws2811Method KEYWORD1 +NeoEsp32I2s1X8Ws2816Method KEYWORD1 +NeoEsp32I2s1X8Ws2812Method KEYWORD1 +NeoEsp32I2s1X8Sk6812Method KEYWORD1 +NeoEsp32I2s1X8Tm1814Method KEYWORD1 +NeoEsp32I2s1X8Tm1829Method KEYWORD1 +NeoEsp32I2s1X8Tm1914Method KEYWORD1 +NeoEsp32I2s1X8Lc8812Method KEYWORD1 +NeoEsp32I2s1X8Apa106Method KEYWORD1 +NeoEsp32I2s1X16Ws2812xMethod KEYWORD1 +NeoEsp32I2s1X16800KbpsMethod KEYWORD1 +NeoEsp32I2s1X16400KbpsMethod KEYWORD1 +NeoEsp32I2s1X16Ws2813Method KEYWORD1 +NeoEsp32I2s1X16Ws2812dMethod KEYWORD1 +NeoEsp32I2s1X16Ws2811Method KEYWORD1 +NeoEsp32I2s1X16Ws2816Method KEYWORD1 +NeoEsp32I2s1X16Ws2812Method KEYWORD1 +NeoEsp32I2s1X16Sk6812Method KEYWORD1 +NeoEsp32I2s1X16Tm1814Method KEYWORD1 +NeoEsp32I2s1X16Tm1829Method KEYWORD1 +NeoEsp32I2s1X16Tm1914Method KEYWORD1 +NeoEsp32I2s1X16Lc8812Method KEYWORD1 +NeoEsp32I2s1X16Apa106Method KEYWORD1 +NeoEsp32RmtNWs2811Method KEYWORD1 +NeoEsp32RmtNWs2812xMethod KEYWORD1 +NeoEsp32RmtNWs2816Method KEYWORD1 +NeoEsp32RmtNSk6812Method KEYWORD1 +NeoEsp32RmtNTm1814Method KEYWORD1 +NeoEsp32RmtNTm1914Method KEYWORD1 +NeoEsp32RmtNTm1829Method KEYWORD1 +NeoEsp32RmtNApa106Method KEYWORD1 +NeoEsp32RmtN800KbpsMethod KEYWORD1 +NeoEsp32RmtN400KbpsMethod KEYWORD1 +NeoEsp32Rmt0Ws2811Method KEYWORD1 +NeoEsp32Rmt0Ws2812xMethod KEYWORD1 +NeoEsp32Rmt0Ws2816Method KEYWORD1 +NeoEsp32Rmt0Sk6812Method KEYWORD1 +NeoEsp32Rmt0Tm1814Method KEYWORD1 +NeoEsp32Rmt0Tm1914Method KEYWORD1 +NeoEsp32Rmt0Tm1829Method KEYWORD1 +NeoEsp32Rmt0Apa106Method KEYWORD1 +NeoEsp32Rmt0800KbpsMethod KEYWORD1 +NeoEsp32Rmt0400KbpsMethod KEYWORD1 +NeoEsp32Rmt1Ws2811Method KEYWORD1 +NeoEsp32Rmt1Ws2812xMethod KEYWORD1 +NeoEsp32Rmt1Ws2816Method KEYWORD1 +NeoEsp32Rmt1Sk6812Method KEYWORD1 +NeoEsp32Rmt1Tm1814Method KEYWORD1 +NeoEsp32Rmt1Tm1914Method KEYWORD1 +NeoEsp32Rmt1Tm1829Method KEYWORD1 +NeoEsp32Rmt1Apa106Method KEYWORD1 +NeoEsp32Rmt1800KbpsMethod KEYWORD1 +NeoEsp32Rmt1400KbpsMethod KEYWORD1 +NeoEsp32Rmt2Ws2811Method KEYWORD1 +NeoEsp32Rmt2Ws2812xMethod KEYWORD1 +NeoEsp32Rmt2Ws2816Method KEYWORD1 +NeoEsp32Rmt2Sk6812Method KEYWORD1 +NeoEsp32Rmt2Tm1814Method KEYWORD1 +NeoEsp32Rmt2Tm1829Method KEYWORD1 +NeoEsp32Rmt2Tm1914Method KEYWORD1 +NeoEsp32Rmt2Apa106Method KEYWORD1 +NeoEsp32Rmt2800KbpsMethod KEYWORD1 +NeoEsp32Rmt2400KbpsMethod KEYWORD1 +NeoEsp32Rmt3Ws2811Method KEYWORD1 +NeoEsp32Rmt3Ws2812xMethod KEYWORD1 +NeoEsp32Rmt3Ws2816Method KEYWORD1 +NeoEsp32Rmt3Sk6812Method KEYWORD1 +NeoEsp32Rmt3Tm1814Method KEYWORD1 +NeoEsp32Rmt3Tm1914Method KEYWORD1 +NeoEsp32Rmt3Tm1829Method KEYWORD1 +NeoEsp32Rmt3Apa106Method KEYWORD1 +NeoEsp32Rmt3800KbpsMethod KEYWORD1 +NeoEsp32Rmt3400KbpsMethod KEYWORD1 +NeoEsp32Rmt4Ws2811Method KEYWORD1 +NeoEsp32Rmt4Ws2812xMethod KEYWORD1 +NeoEsp32Rmt4Ws2816Method KEYWORD1 +NeoEsp32Rmt4Sk6812Method KEYWORD1 +NeoEsp32Rmt4Tm1814Method KEYWORD1 +NeoEsp32Rmt4Tm1914Method KEYWORD1 +NeoEsp32Rmt4Tm1829Method KEYWORD1 +NeoEsp32Rmt4Apa106Method KEYWORD1 +NeoEsp32Rmt4800KbpsMethod KEYWORD1 +NeoEsp32Rmt4400KbpsMethod KEYWORD1 +NeoEsp32Rmt5Ws2811Method KEYWORD1 +NeoEsp32Rmt5Ws2812xMethod KEYWORD1 +NeoEsp32Rmt5Ws2816Method KEYWORD1 +NeoEsp32Rmt5Sk6812Method KEYWORD1 +NeoEsp32Rmt5Tm1814Method KEYWORD1 +NeoEsp32Rmt5Tm1914Method KEYWORD1 +NeoEsp32Rmt5Tm1829Method KEYWORD1 +NeoEsp32Rmt5Apa106Method KEYWORD1 +NeoEsp32Rmt5800KbpsMethod KEYWORD1 +NeoEsp32Rmt5400KbpsMethod KEYWORD1 +NeoEsp32Rmt6Ws2811Method KEYWORD1 +NeoEsp32Rmt6Ws2812xMethod KEYWORD1 +NeoEsp32Rmt6Ws2816Method KEYWORD1 +NeoEsp32Rmt6Sk6812Method KEYWORD1 +NeoEsp32Rmt6Tm1814Method KEYWORD1 +NeoEsp32Rmt6Tm1914Method KEYWORD1 +NeoEsp32Rmt6Tm1829Method KEYWORD1 +NeoEsp32Rmt6Apa106Method KEYWORD1 +NeoEsp32Rmt6800KbpsMethod KEYWORD1 +NeoEsp32Rmt6400KbpsMethod KEYWORD1 +NeoEsp32Rmt7Ws2811Method KEYWORD1 +NeoEsp32Rmt7Ws2812xMethod KEYWORD1 +NeoEsp32Rmt7Ws2816Method KEYWORD1 +NeoEsp32Rmt7Sk6812Method KEYWORD1 +NeoEsp32Rmt7Tm1814Method KEYWORD1 +NeoEsp32Rmt7Tm1914Method KEYWORD1 +NeoEsp32Rmt7Tm1829Method KEYWORD1 +NeoEsp32Rmt7Apa106Method KEYWORD1 +NeoEsp32Rmt7800KbpsMethod KEYWORD1 +NeoEsp32Rmt7400KbpsMethod KEYWORD1 +NeoEsp32RmtNWs2811InvertedMethod KEYWORD1 +NeoEsp32RmtNWs2812xInvertedMethod KEYWORD1 +NeoEsp32RmtNWs2816InvertedMethod KEYWORD1 +NeoEsp32RmtNSk6812InvertedMethod KEYWORD1 +NeoEsp32RmtNTm1814InvertedMethod KEYWORD1 +NeoEsp32RmtNTm1914InvertedMethod KEYWORD1 +NeoEsp32RmtNTm1829InvertedMethod KEYWORD1 +NeoEsp32RmtNApa106InvertedMethod KEYWORD1 +NeoEsp32RmtN800KbpsInvertedMethod KEYWORD1 +NeoEsp32RmtN400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt0Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt0Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt0Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt0Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt0Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt0Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt0Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt0Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt0800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt0400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt1Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt1Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt1Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt1Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt1Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt1Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt1Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt1Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt1800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt1400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt2Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt2Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt2Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt2Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt2Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt2Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt2Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt2Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt2800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt2400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt3Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt3Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt3Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt3Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt3Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt3Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt3Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt3Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt3800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt3400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt4Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt4Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt4Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt4Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt4Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt4Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt4Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt4Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt4800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt4400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt5Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt5Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt5Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt5Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt5Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt5Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt5Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt5Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt5800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt5400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt6Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt6Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt6Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt6Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt6Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt6Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt6Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt6Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt6800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt6400KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt7Ws2811InvertedMethod KEYWORD1 +NeoEsp32Rmt7Ws2812xInvertedMethod KEYWORD1 +NeoEsp32Rmt7Ws2816InvertedMethod KEYWORD1 +NeoEsp32Rmt7Sk6812InvertedMethod KEYWORD1 +NeoEsp32Rmt7Tm1814InvertedMethod KEYWORD1 +NeoEsp32Rmt7Tm1914InvertedMethod KEYWORD1 +NeoEsp32Rmt7Tm1829InvertedMethod KEYWORD1 +NeoEsp32Rmt7Apa106InvertedMethod KEYWORD1 +NeoEsp32Rmt7800KbpsInvertedMethod KEYWORD1 +NeoEsp32Rmt7400KbpsInvertedMethod KEYWORD1 +NeoEsp32BitBangWs2813Method KEYWORD1 +NeoEsp32BitBangWs2812xMethod KEYWORD1 +NeoEsp32BitBangWs2812Method KEYWORD1 +NeoEsp32BitBangWs2811Method KEYWORD1 +NeoEsp32BitBangWs2816Method KEYWORD1 +NeoEsp32BitBangSk6812Method KEYWORD1 +NeoEsp32BitBangTm1814Method KEYWORD1 +NeoEsp32BitBangTm1914Method KEYWORD1 +NeoEsp32BitBangTm1829Method KEYWORD1 +NeoEsp32BitBangLc8812Method KEYWORD1 +NeoEsp32BitBangApa106Method KEYWORD1 +NeoEsp32BitBang800KbpsMethod KEYWORD1 +NeoEsp32BitBang400KbpsMethod KEYWORD1 +NeoEsp32BitBangWs2813InvertedMethod KEYWORD1 +NeoEsp32BitBangWs2812xInvertedMethod KEYWORD1 +NeoEsp32BitBangWs2812InvertedMethod KEYWORD1 +NeoEsp32BitBangWs2811InvertedMethod KEYWORD1 +NeoEsp32BitBangWs2816InvertedMethod KEYWORD1 +NeoEsp32BitBangSk6812InvertedMethod KEYWORD1 +NeoEsp32BitBangTm1814InvertedMethod KEYWORD1 +NeoEsp32BitBangTm1914InvertedMethod KEYWORD1 +NeoEsp32BitBangTm1829InvertedMethod KEYWORD1 +NeoEsp32BitBangLc8812InvertedMethod KEYWORD1 +NeoEsp32BitBangApa106InvertedMethod KEYWORD1 +NeoEsp32BitBang800KbpsInvertedMethod KEYWORD1 +NeoEsp32BitBang400KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwmNWs2812xMethod KEYWORD1 +NeoNrf52xPwmNWs2816Method KEYWORD1 +NeoNrf52xPwmNSk6812Method KEYWORD1 +NeoNrf52xPwmNTm1814Method KEYWORD1 +NeoNrf52xPwmNTm1914Method KEYWORD1 +NeoNrf52xPwmNTm1829Method KEYWORD1 +NeoNrf52xPwmNTx1812Method KEYWORD1 +NeoNrf52xPwmN800KbpsMethod KEYWORD1 +NeoNrf52xPwmN400KbpsMethod KEYWORD1 +NeoNrf52xPwmNApa106Method KEYWORD1 +NeoNrf52xPwm0Ws2812xMethod KEYWORD1 +NeoNrf52xPwm0Ws2816Method KEYWORD1 +NeoNrf52xPwm0Sk6812Method KEYWORD1 +NeoNrf52xPwm0Tm1814Method KEYWORD1 +NeoNrf52xPwm0Tm1914Method KEYWORD1 +NeoNrf52xPwm0Tm1829Method KEYWORD1 +NeoNrf52xPwm0Tx1812Method KEYWORD1 +NeoNrf52xPwm0800KbpsMethod KEYWORD1 +NeoNrf52xPwm0400KbpsMethod KEYWORD1 +NeoNrf52xPwm0Apa106Method KEYWORD1 +NeoNrf52xPwm1Ws2812xMethod KEYWORD1 +NeoNrf52xPwm1Ws2816Method KEYWORD1 +NeoNrf52xPwm1Sk6812Method KEYWORD1 +NeoNrf52xPwm1Tm1814Method KEYWORD1 +NeoNrf52xPwm1Tm1914Method KEYWORD1 +NeoNrf52xPwm1Tm1829Method KEYWORD1 +NeoNrf52xPwm1Tx1812Method KEYWORD1 +NeoNrf52xPwm1800KbpsMethod KEYWORD1 +NeoNrf52xPwm1400KbpsMethod KEYWORD1 +NeoNrf52xPwm1Apa106Method KEYWORD1 +NeoNrf52xPwm2Ws2812xMethod KEYWORD1 +NeoNrf52xPwm2Ws2816Method KEYWORD1 +NeoNrf52xPwm2Sk6812Method KEYWORD1 +NeoNrf52xPwm2Tm1814Method KEYWORD1 +NeoNrf52xPwm2Tm1914Method KEYWORD1 +NeoNrf52xPwm2Tm1829Method KEYWORD1 +NeoNrf52xPwm2Tx1812Method KEYWORD1 +NeoNrf52xPwm2800KbpsMethod KEYWORD1 +NeoNrf52xPwm2400KbpsMethod KEYWORD1 +NeoNrf52xPwm2Apa106Method KEYWORD1 +NeoNrf52xPwm3Ws2812xMethod KEYWORD1 +NeoNrf52xPwm3Ws2816Method KEYWORD1 +NeoNrf52xPwm3Sk6812Method KEYWORD1 +NeoNrf52xPwm3Tm1814Method KEYWORD1 +NeoNrf52xPwm3Tm1914Method KEYWORD1 +NeoNrf52xPwm3Tm1829Method KEYWORD1 +NeoNrf52xPwm3Tx1812Method KEYWORD1 +NeoNrf52xPwm3800KbpsMethod KEYWORD1 +NeoNrf52xPwm3400KbpsMethod KEYWORD1 +NeoNrf52xPwm3Apa106Method KEYWORD1 +NeoNrf52xPwmNWs2812xInvertedMethod KEYWORD1 +NeoNrf52xPwmNWs2816InvertedMethod KEYWORD1 +NeoNrf52xPwmNSk6812InvertedMethod KEYWORD1 +NeoNrf52xPwmNTm1814InvertedMethod KEYWORD1 +NeoNrf52xPwmNTm1914InvertedMethod KEYWORD1 +NeoNrf52xPwmNTm1829InvertedMethod KEYWORD1 +NeoNrf52xPwmNTx1812InvertedMethod KEYWORD1 +NeoNrf52xPwmN800KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwmN400KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwmNApa106InvertedMethod KEYWORD1 +NeoNrf52xPwm0Ws2812xInvertedMethod KEYWORD1 +NeoNrf52xPwm0Ws2816InvertedMethod KEYWORD1 +NeoNrf52xPwm0Sk6812InvertedMethod KEYWORD1 +NeoNrf52xPwm0Tm1814InvertedMethod KEYWORD1 +NeoNrf52xPwm0Tm1914InvertedMethod KEYWORD1 +NeoNrf52xPwm0Tm1829InvertedMethod KEYWORD1 +NeoNrf52xPwm0Tx1812InvertedMethod KEYWORD1 +NeoNrf52xPwm0800KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm0400KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm0Apa106InvertedMethod KEYWORD1 +NeoNrf52xPwm1Ws2812xInvertedMethod KEYWORD1 +NeoNrf52xPwm1Ws2816InvertedMethod KEYWORD1 +NeoNrf52xPwm1Sk6812InvertedMethod KEYWORD1 +NeoNrf52xPwm1Tm1814InvertedMethod KEYWORD1 +NeoNrf52xPwm1Tm1914InvertedMethod KEYWORD1 +NeoNrf52xPwm1Tm1829InvertedMethod KEYWORD1 +NeoNrf52xPwm1Tx1812InvertedMethod KEYWORD1 +NeoNrf52xPwm1800KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm1400KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm1Apa106InvertedMethod KEYWORD1 +NeoNrf52xPwm2Ws2812xInvertedMethod KEYWORD1 +NeoNrf52xPwm2Ws2816InvertedMethod KEYWORD1 +NeoNrf52xPwm2Sk6812InvertedMethod KEYWORD1 +NeoNrf52xPwm2Tm1814InvertedMethod KEYWORD1 +NeoNrf52xPwm2Tm1914InvertedMethod KEYWORD1 +NeoNrf52xPwm2Tm1829InvertedMethod KEYWORD1 +NeoNrf52xPwm2Tx1812InvertedMethod KEYWORD1 +NeoNrf52xPwm2800KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm2400KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm2Apa106InvertedMethod KEYWORD1 +NeoNrf52xPwm3Ws2812xInvertedMethod KEYWORD1 +NeoNrf52xPwm3Ws2816InvertedMethod KEYWORD1 +NeoNrf52xPwm3Sk6812InvertedMethod KEYWORD1 +NeoNrf52xPwm3Tm1814InvertedMethod KEYWORD1 +NeoNrf52xPwm3Tm1914InvertedMethod KEYWORD1 +NeoNrf52xPwm3Tm1829InvertedMethod KEYWORD1 +NeoNrf52xPwm3Tx1812InvertedMethod KEYWORD1 +NeoNrf52xPwm3800KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm3400KbpsInvertedMethod KEYWORD1 +NeoNrf52xPwm3Apa106InvertedMethod KEYWORD1 +DotStarMethod KEYWORD1 +DotStarSpiMethod KEYWORD1 +DotStarSpi20MhzMethod KEYWORD1 +DotStarSpi10MhzMethod KEYWORD1 +DotStarSpi2MhzMethod KEYWORD1 +DotStarSpi1MhzMethod KEYWORD1 +DotStarSpi500KhzMethod KEYWORD1 +DotStarSpiHzMethod KEYWORD1 +Ws2801Method KEYWORD1 +Ws2801SpiMethod KEYWORD1 +Ws2801Spi20MhzMethod KEYWORD1 +Ws2801Spi10MhzMethod KEYWORD1 +Ws2801Spi2MhzMethod KEYWORD1 +Ws2801Spi1MhzMethod KEYWORD1 +Ws2801Spi500KhzMethod KEYWORD1 +Ws2801SpiHzMethod KEYWORD1 +Lpd6803SpiMethod KEYWORD1 +Lpd6803Method KEYWORD1 +Lpd6803Spi20MhzMethod KEYWORD1 +Lpd6803Spi10MhzMethod KEYWORD1 +Lpd6803Spi2MhzMethod KEYWORD1 +Lpd6803Spi1MhzMethod KEYWORD1 +Lpd6803Spi500KhzMethod KEYWORD1 +Lpd6803SpiHzMethod KEYWORD1 +Lpd8806Method KEYWORD1 +Lpd8806SpiMethod KEYWORD1 +Lpd8806Spi20MhzMethod KEYWORD1 +Lpd8806Spi10MhzMethod KEYWORD1 +Lpd8806Spi2MhzMethod KEYWORD1 +Lpd8806Spi1MhzMethod KEYWORD1 +Lpd8806Spi500KhzMethod KEYWORD1 +Lpd8806SpiHzMethod KEYWORD1 +P9813Method KEYWORD1 +P9813SpiMethod KEYWORD1 +P9813Spi20MhzMethod KEYWORD1 +P9813Spi10MhzMethod KEYWORD1 +P9813Spi2MhzMethod KEYWORD1 +P9813Spi1MhzMethod KEYWORD1 +P9813Spi500KhzMethod KEYWORD1 +P9813SpiHzMethod KEYWORD1 +Tlc5947Method KEYWORD1 +Tlc5947Method16Bit KEYWORD1 +Tlc5947Spi30MhzMethod KEYWORD1 +Tlc5947Spi30MhzMethod16Bit KEYWORD1 +Tlc5947Spi15MhzMethod KEYWORD1 +Tlc5947Spi15MhzMethod16Bit KEYWORD1 +Tlc5947SpiMethod KEYWORD1 +Tlc5947SpiMethod16Bit KEYWORD1 +Sm16716Method KEYWORD1 +NeoPixelAnimator KEYWORD1 +AnimUpdateCallback KEYWORD1 +AnimationParam KEYWORD1 +NeoEase KEYWORD1 +AnimEaseFunction KEYWORD1 +RowMajorLayout KEYWORD1 +RowMajor90Layout KEYWORD1 +RowMajor180Layout KEYWORD1 +RowMajor270Layout KEYWORD1 +RowMajorAlternatingLayout KEYWORD1 +RowMajorAlternating90Layout KEYWORD1 +RowMajorAlternating180Layout KEYWORD1 +RowMajorAlternating270Layout KEYWORD1 +ColumnMajorLayout KEYWORD1 +ColumnMajor90Layout KEYWORD1 +ColumnMajor180Layout KEYWORD1 +ColumnMajor270Layout KEYWORD1 +ColumnMajorAlternatingLayout KEYWORD1 +ColumnMajorAlternating90Layout KEYWORD1 +ColumnMajorAlternating180Layout KEYWORD1 +ColumnMajorAlternating270Layout KEYWORD1 +NeoTopology KEYWORD1 +NeoRingTopology KEYWORD1 +NeoTiles KEYWORD1 +NeoMosaic KEYWORD1 +NeoGammaCieLabEquationMethod KEYWORD1 +NeoGammaEquationMethod KEYWORD1 +NeoGammaTableMethod KEYWORD1 +NeoGamma KEYWORD1 +NeoHueBlendShortestDistance KEYWORD1 +NeoHueBlendLongestDistance KEYWORD1 +NeoHueBlendClockwiseDirection KEYWORD1 +NeoHueBlendCounterClockwiseDirection KEYWORD1 +NeoBufferContext KEYWORD1 +LayoutMapCallback KEYWORD1 +NeoBufferMethod KEYWORD1 +NeoBufferProgmemMethod KEYWORD1 +NeoBuffer KEYWORD1 +NeoVerticalSpriteSheet KEYWORD1 +NeoBitmapFile KEYWORD1 +HtmlShortColorNames KEYWORD1 +HtmlColorNames KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +Begin KEYWORD2 +Show KEYWORD2 +CanShow KEYWORD2 +ClearTo KEYWORD2 +RotateLeft KEYWORD2 +ShiftLeft KEYWORD2 +RotateRight KEYWORD2 +ShiftRight KEYWORD2 +IsDirty KEYWORD2 +Dirty KEYWORD2 +ResetDirty KEYWORD2 +Pixels KEYWORD2 +PixelSize KEYWORD2 +PixelsSize KEYWORD2 +PixelCount KEYWORD2 +SetPixelColor KEYWORD2 +GetPixelColor KEYWORD2 +SwapPixelColor KEYWORD2 +SetLuminance KEYWORD2 +GetLuminance KEYWORD2 +ApplyPostAdjustments KEYWORD2 +SetString KEYWORD2 +CalculateBrightness KEYWORD2 +Dim KEYWORD2 +Brighten KEYWORD2 +Darken KEYWORD2 +Lighten KEYWORD2 +SetPixelSettings KEYWORD2 +SetMethodSettings KEYWORD2 +LinearBlend KEYWORD2 +BilinearBlend KEYWORD2 +IsAnimating KEYWORD2 +NextAvailableAnimation KEYWORD2 +StartAnimation KEYWORD2 +StopAnimation KEYWORD2 +RestartAnimation KEYWORD2 +IsAnimationActive KEYWORD2 +AnimationDuration KEYWORD2 +ChangeAnimationDuration KEYWORD2 +UpdateAnimations KEYWORD2 +IsPaused KEYWORD2 +Pause KEYWORD2 +Resume KEYWORD2 +getTimeScale KEYWORD2 +setTimeScale KEYWORD2 +QuadraticIn KEYWORD2 +QuadraticOut KEYWORD2 +QuadraticInOut KEYWORD2 +QuadraticCenter KEYWORD2 +CubicIn KEYWORD2 +CubicOut KEYWORD2 +CubicInOut KEYWORD2 +CubicCenter KEYWORD2 +QuarticIn KEYWORD2 +QuarticOut KEYWORD2 +QuarticInOut KEYWORD2 +QuarticCenter KEYWORD2 +QuinticIn KEYWORD2 +QuinticOut KEYWORD2 +QuinticInOut KEYWORD2 +QuinticCenter KEYWORD2 +SinusoidalIn KEYWORD2 +SinusoidalOut KEYWORD2 +SinusoidalInOut KEYWORD2 +SinusoidalCenter KEYWORD2 +ExponentialIn KEYWORD2 +ExponentialOut KEYWORD2 +ExponentialInOut KEYWORD2 +ExponentialCenter KEYWORD2 +CircularIn KEYWORD2 +CircularOut KEYWORD2 +CircularInOut KEYWORD2 +CircularCenter KEYWORD2 +Gamma KEYWORD2 +GammaCieLab KEYWORD2 +Map KEYWORD2 +MapProbe KEYWORD2 +getWidth KEYWORD2 +getHeight KEYWORD2 +RingPixelShift KEYWORD2 +RingPixelRotate KEYWORD2 +getCountOfRings KEYWORD2 +getPixelCountAtRing KEYWORD2 +getPixelCount KEYWORD2 +TopologyHint KEYWORD2 +Correct KEYWORD2 +SpriteWidth KEYWORD2 +SpriteHeight KEYWORD2 +SpriteCount KEYWORD2 +Blt KEYWORD2 +Width KEYWORD2 +Height KEYWORD2 +Parse KEYWORD2 +ToString KEYWORD2 +ToNumericalString KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### + +NEO_MILLISECONDS LITERAL1 +NEO_CENTISECONDS LITERAL1 +NEO_DECISECONDS LITERAL1 +NEO_SECONDS LITERAL1 +NEO_DECASECONDS LITERAL1 +AnimationState_Started LITERAL1 +AnimationState_Progress LITERAL1 +AnimationState_Completed LITERAL1 +NeoTopologyHint_FirstOnPanel LITERAL1 +NeoTopologyHint_InPanel LITERAL1 +NeoTopologyHint_LastOnPanel LITERAL1 +NeoTopologyHint_OutOfBounds LITERAL1 +PixelIndex_OutOfBounds LITERAL1 +NeoBusChannel_0 LITERAL1 +NeoBusChannel_1 LITERAL1 +NeoBusChannel_2 LITERAL1 +NeoBusChannel_3 LITERAL1 +NeoBusChannel_4 LITERAL1 +NeoBusChannel_5 LITERAL1 +NeoBusChannel_6 LITERAL1 +NeoBusChannel_7 LITERAL1 + diff --git a/lib/NeoPixelBus/library.json b/lib/NeoPixelBus/library.json new file mode 100644 index 00000000..50bde0f0 --- /dev/null +++ b/lib/NeoPixelBus/library.json @@ -0,0 +1,19 @@ +{ + "name": "NeoPixelBus", + "keywords": "NeoPixel, WS2811, WS2812, WS2813, WS2821, SK6812, DotStar, APA102, SK9822, APA106, LPD8806, LPD6803, P9813, TM1829, TM1814, TM1914, TX1812, WS2801, SM16803, SM16823, SM16804, SM16824, SM16716, DMX512, RGB, RGBW", + "description": "A library that makes controlling NeoPixels (WS2812x and many others) and DotStars (SK6812 and many others) easy. Supports most Arduino platforms, including async hardware support for Esp8266, Esp32, and Nrf52 (Nano 33 BLE). Support for RGBW pixels and 7 Segment LED direct driven. Includes seperate RgbColor, RgbwColor, Rgb16Color, Rgb48Color, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. For all platforms; there are two methods of sending DotStar data, hardware SPI and software SPI.", + "homepage": "https://github.com/Makuna/NeoPixelBus/wiki", + "repository": { + "type": "git", + "url": "https://github.com/Makuna/NeoPixelBus" + }, + "version": "2.7.5", + "frameworks": "arduino", + "platforms": "*", + "dependencies": [ + { + "name": "SPI" + } + ] +} + diff --git a/lib/NeoPixelBus/library.properties b/lib/NeoPixelBus/library.properties new file mode 100644 index 00000000..d062c18d --- /dev/null +++ b/lib/NeoPixelBus/library.properties @@ -0,0 +1,9 @@ +name=NeoPixelBus by Makuna +version=2.7.5 +author=Michael C. Miller (makuna@live.com) +maintainer=Michael C. Miller (makuna@live.com) +sentence=A library that makes controlling NeoPixels (WS2812x and many others) and DotStars (SK6812 and many others) easy. +paragraph=Supports most Arduino platforms, including async hardware support for Esp8266, Esp32, and Nrf52 (Nano 33 BLE). Support for RGBW pixels and 7 Segment LED direct driven. Includes seperate RgbColor, RgbwColor, Rgb16Color, Rgb48Color, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. Supports Matrix layout of pixels. Includes Gamma corretion object. For all platforms; there are two methods of sending DotStar data, hardware SPI and software SPI. +category=Display +url=https://github.com/Makuna/NeoPixelBus/wiki +architectures=* \ No newline at end of file diff --git a/lib/NeoPixelBus/src/NeoPixelAnimator.h b/lib/NeoPixelBus/src/NeoPixelAnimator.h new file mode 100644 index 00000000..b1681647 --- /dev/null +++ b/lib/NeoPixelBus/src/NeoPixelAnimator.h @@ -0,0 +1,180 @@ +/*------------------------------------------------------------------------- +NeoPixelAnimator provides animation timing support. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include +#include "internal/animations/NeoEase.h" + +enum AnimationState +{ + AnimationState_Started, + AnimationState_Progress, + AnimationState_Completed +}; + +struct AnimationParam +{ + float progress; + uint16_t index; + AnimationState state; +}; + +#if defined(NEOPIXEBUS_NO_STL) + +typedef void(*AnimUpdateCallback)(const AnimationParam& param); + +#else + +#undef max +#undef min +#include +typedef std::function AnimUpdateCallback; + +#endif + + +#define NEO_MILLISECONDS 1 // ~65 seconds max duration, ms updates +#define NEO_CENTISECONDS 10 // ~10.9 minutes max duration, centisecond updates +#define NEO_DECISECONDS 100 // ~1.8 hours max duration, decisecond updates +#define NEO_SECONDS 1000 // ~18.2 hours max duration, second updates +#define NEO_DECASECONDS 10000 // ~7.5 days, 10 second updates + +class NeoPixelAnimator +{ +public: + NeoPixelAnimator(uint16_t countAnimations, uint16_t timeScale = NEO_MILLISECONDS); + ~NeoPixelAnimator(); + + bool IsAnimating() const + { + return _activeAnimations > 0; + } + + + bool NextAvailableAnimation(uint16_t* indexAvailable, uint16_t indexStart = 0); + + void StartAnimation(uint16_t indexAnimation, uint16_t duration, AnimUpdateCallback animUpdate); + void StopAnimation(uint16_t indexAnimation); + void StopAll(); + + void RestartAnimation(uint16_t indexAnimation) + { + if (indexAnimation >= _countAnimations || _animations[indexAnimation]._duration == 0) + { + return; + } + + StartAnimation(indexAnimation, _animations[indexAnimation]._duration, (_animations[indexAnimation]._fnCallback)); + } + + bool IsAnimationActive(uint16_t indexAnimation) const + { + if (indexAnimation >= _countAnimations) + { + return false; + } + return (IsAnimating() && _animations[indexAnimation]._remaining != 0); + } + + uint16_t AnimationDuration(uint16_t indexAnimation) + { + if (indexAnimation >= _countAnimations) + { + return 0; + } + return _animations[indexAnimation]._duration; + } + + void ChangeAnimationDuration(uint16_t indexAnimation, uint16_t newDuration); + + void UpdateAnimations(); + + bool IsPaused() + { + return (!_isRunning); + } + + void Pause() + { + _isRunning = false; + } + + void Resume() + { + _isRunning = true; + _animationLastTick = millis(); + } + + uint16_t getTimeScale() + { + return _timeScale; + } + + void setTimeScale(uint16_t timeScale) + { + _timeScale = (timeScale < 1) ? (1) : (timeScale > 32768) ? 32768 : timeScale; + } + +private: + struct AnimationContext + { + AnimationContext() : + _duration(0), + _remaining(0), + _fnCallback(NULL) + {} + + void StartAnimation(uint16_t duration, AnimUpdateCallback animUpdate) + { + _duration = duration; + _remaining = duration; + _fnCallback = animUpdate; + } + + void StopAnimation() + { + _remaining = 0; + } + + float CurrentProgress() + { + return (float)(_duration - _remaining) / (float)_duration; + } + + uint16_t _duration; + uint16_t _remaining; + + AnimUpdateCallback _fnCallback; + }; + + uint16_t _countAnimations; + AnimationContext* _animations; + uint32_t _animationLastTick; + uint16_t _activeAnimations; + uint16_t _timeScale; + bool _isRunning; +}; diff --git a/lib/NeoPixelBus/src/NeoPixelBrightnessBus.h b/lib/NeoPixelBus/src/NeoPixelBrightnessBus.h new file mode 100644 index 00000000..2d82b567 --- /dev/null +++ b/lib/NeoPixelBus/src/NeoPixelBrightnessBus.h @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- +NeoPixelBus library wrapper template class that provides overall brightness control + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include "NeoPixelBus.h" + + +template class [[deprecated("Use NeoPixelBusLg instead.")]] NeoPixelBrightnessBus : + public NeoPixelBus +{ +private: + + void ScaleColor(uint16_t scale, typename T_COLOR_FEATURE::ColorObject* color) + { + // This is the similiar as calling Dim on the color object + // there is an assumption that all color elements are byte aligned + // so if any future color object gets introduced that is not it will + // cause a problem + uint8_t* ptr = (uint8_t*)color; + uint8_t* ptrEnd = ptr + sizeof(typename T_COLOR_FEATURE::ColorObject); + + while (ptr != ptrEnd) + { + uint16_t value = *ptr; + *ptr++ = (value * scale) >> 8; + } + } + + void ConvertColor(typename T_COLOR_FEATURE::ColorObject* color) + { + // This is the same as calling Dim on the color object + uint16_t scale = _brightness + 1; + ScaleColor(scale, color); + } + + void RecoverColor(typename T_COLOR_FEATURE::ColorObject* color) const + { + // this is the same as calling Brighton on the color object + // there is an assumption that all color elements are byte aligned + // so if any future color object gets introduced that is not it will + // cause a problem + uint8_t* ptr = (uint8_t*)color; + uint8_t* ptrEnd = ptr + sizeof(typename T_COLOR_FEATURE::ColorObject); + uint16_t scale = _brightness + 1; + + while (ptr != ptrEnd) + { + uint16_t value = *ptr; + *ptr++ = (value << 8) / scale; + } + } + +public: + NeoPixelBrightnessBus(uint16_t countPixels, uint8_t pin) : + NeoPixelBus(countPixels, pin), + _brightness(255) + { + } + + NeoPixelBrightnessBus(uint16_t countPixels, uint8_t pin, NeoBusChannel channel) : + NeoPixelBus(countPixels, pin, channel), + _brightness(255) + { + } + + NeoPixelBrightnessBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData) : + NeoPixelBus(countPixels, pinClock, pinData), + _brightness(255) + { + } + + NeoPixelBrightnessBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable = NOT_A_PIN) : + NeoPixelBus(countPixels, pinClock, pinData, pinLatch, pinOutputEnable), + _brightness(255) + { + } + + NeoPixelBrightnessBus(uint16_t countPixels) : + NeoPixelBus(countPixels), + _brightness(255) + { + } + + void SetBrightness(uint8_t brightness) + { + // Only update if there is a change + if (brightness != _brightness) + { + uint16_t scale = ((static_cast(brightness) + 1) << 8) / (static_cast(_brightness) + 1); + + // scale existing pixels + // + for (uint16_t indexPixel = 0; indexPixel < NeoPixelBus::PixelCount(); indexPixel++) + { + typename T_COLOR_FEATURE::ColorObject color = NeoPixelBus::GetPixelColor(indexPixel); + ScaleColor(scale, &color); + NeoPixelBus::SetPixelColor(indexPixel, color); + } + + _brightness = brightness; + this->Dirty(); + } + } + + uint8_t GetBrightness() const + { + return _brightness; + } + + void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color) + { + ConvertColor(&color); + NeoPixelBus::SetPixelColor(indexPixel, color); + } + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(uint16_t indexPixel) const + { + typename T_COLOR_FEATURE::ColorObject color = NeoPixelBus::GetPixelColor(indexPixel); + RecoverColor(&color); + return color; + } + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color) + { + ConvertColor(&color); + NeoPixelBus::ClearTo(color); + }; + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color, uint16_t first, uint16_t last) + { + ConvertColor(&color); + NeoPixelBus::ClearTo(color, first, last); + } + + +protected: + uint8_t _brightness; +}; + + diff --git a/lib/NeoPixelBus/src/NeoPixelBus.h b/lib/NeoPixelBus/src/NeoPixelBus.h new file mode 100644 index 00000000..bf6344fd --- /dev/null +++ b/lib/NeoPixelBus/src/NeoPixelBus.h @@ -0,0 +1,429 @@ +/*------------------------------------------------------------------------- +NeoPixel library + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#include + +// standard neo definitions +// +const uint8_t NEO_DIRTY = 0x80; // a change was made to pixel data that requires a show +const uint16_t PixelIndex_OutOfBounds = 0xffff; + +#include "internal/NeoUtil.h" +#include "internal/animations/NeoEase.h" +#include "internal/NeoSettings.h" +#include "internal/NeoColors.h" +#include "internal/NeoColorFeatures.h" +#include "internal/NeoTopologies.h" +#include "internal/NeoBuffers.h" +#include "internal/NeoBusChannel.h" +#include "internal/NeoMethods.h" + + +template class NeoPixelBus +{ +public: + // Constructor: number of LEDs, pin number + // NOTE: Pin Number maybe ignored due to hardware limitations of the method. + + NeoPixelBus(uint16_t countPixels, uint8_t pin) : + _countPixels(countPixels), + _state(0), + _method(pin, countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize) + { + } + + NeoPixelBus(uint16_t countPixels, uint8_t pin, NeoBusChannel channel) : + _countPixels(countPixels), + _state(0), + _method(pin, countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize, channel) + { + } + + NeoPixelBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData) : + _countPixels(countPixels), + _state(0), + _method(pinClock, pinData, countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize) + { + } + + NeoPixelBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable = NOT_A_PIN) : + _countPixels(countPixels), + _state(0), + _method(pinClock, pinData, pinLatch, pinOutputEnable, countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize) + { + } + + NeoPixelBus(uint16_t countPixels) : + _countPixels(countPixels), + _state(0), + _method(countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize) + { + } + + ~NeoPixelBus() + { + } + + operator NeoBufferContext() + { + Dirty(); // we assume you are playing with bits + return NeoBufferContext(_pixels(), PixelsSize()); + } + + void Begin() + { + _method.Initialize(); + ClearTo(0); + } + + // used by DotStarSpiMethod/DotStarEsp32DmaSpiMethod if pins can be configured + void Begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _method.Initialize(sck, miso, mosi, ss); + ClearTo(0); + } + + // used by DotStarEsp32DmaSpiMethod if pins can be configured - reordered and extended version supporting quad SPI + void Begin(int8_t sck, int8_t dat0, int8_t dat1, int8_t dat2, int8_t dat3, int8_t ss) + { + _method.Initialize(sck, dat0, dat1, dat2, dat3, ss); + ClearTo(0); + } + + // used by DotStarEsp32DmaSpiMethod if pins can be configured - reordered and extended version supporting oct SPI + void Begin(int8_t sck, int8_t dat0, int8_t dat1, int8_t dat2, int8_t dat3, int8_t dat4, int8_t dat5, int8_t dat6, int8_t dat7, int8_t ss) + { + _method.Initialize(sck, dat0, dat1, dat2, dat3, dat4, dat5, dat6, dat7, ss); + ClearTo(0); + } + + void Show(bool maintainBufferConsistency = true) + { + if (!IsDirty() && !_method.AlwaysUpdate()) + { + return; + } + + _method.Update(maintainBufferConsistency); + + ResetDirty(); + } + + inline bool CanShow() const + { + return _method.IsReadyToUpdate(); + }; + + bool IsDirty() const + { + return (_state & NEO_DIRTY); + }; + + void Dirty() + { + _state |= NEO_DIRTY; + }; + + void ResetDirty() + { + _state &= ~NEO_DIRTY; + }; + + uint8_t* Pixels() + { + return _pixels(); + }; + + size_t PixelsSize() const + { + return _method.getDataSize() - T_COLOR_FEATURE::SettingsSize; + }; + + size_t PixelSize() const + { + return T_COLOR_FEATURE::PixelSize; + }; + + uint16_t PixelCount() const + { + return _countPixels; + }; + + void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color) + { + if (indexPixel < _countPixels) + { + T_COLOR_FEATURE::applyPixelColor(_pixels(), indexPixel, color); + Dirty(); + } + }; + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(uint16_t indexPixel) const + { + if (indexPixel < _countPixels) + { + return T_COLOR_FEATURE::retrievePixelColor(_pixels(), indexPixel); + } + else + { + // Pixel # is out of bounds, this will get converted to a + // color object type initialized to 0 (black) + return 0; + } + }; + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color) + { + uint8_t temp[T_COLOR_FEATURE::PixelSize]; + uint8_t* pixels = _pixels(); + + T_COLOR_FEATURE::applyPixelColor(temp, 0, color); + + T_COLOR_FEATURE::replicatePixel(pixels, temp, _countPixels); + + Dirty(); + }; + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color, uint16_t first, uint16_t last) + { + if (first < _countPixels && + last < _countPixels && + first <= last) + { + uint8_t temp[T_COLOR_FEATURE::PixelSize]; + uint8_t* pixels = _pixels(); + uint8_t* pFront = T_COLOR_FEATURE::getPixelAddress(pixels, first); + + T_COLOR_FEATURE::applyPixelColor(temp, 0, color); + + T_COLOR_FEATURE::replicatePixel(pFront, temp, last - first + 1); + + Dirty(); + } + } + + void RotateLeft(uint16_t rotationCount) + { + if ((_countPixels - 1) >= rotationCount) + { + _rotateLeft(rotationCount, 0, _countPixels - 1); + } + } + + void RotateLeft(uint16_t rotationCount, uint16_t first, uint16_t last) + { + if (first < _countPixels && + last < _countPixels && + first < last && + (last - first) >= rotationCount) + { + _rotateLeft(rotationCount, first, last); + } + } + + void ShiftLeft(uint16_t shiftCount) + { + if ((_countPixels - 1) >= shiftCount) + { + _shiftLeft(shiftCount, 0, _countPixels - 1); + Dirty(); + } + } + + void ShiftLeft(uint16_t shiftCount, uint16_t first, uint16_t last) + { + if (first < _countPixels && + last < _countPixels && + first < last && + (last - first) >= shiftCount) + { + _shiftLeft(shiftCount, first, last); + Dirty(); + } + } + + void RotateRight(uint16_t rotationCount) + { + if ((_countPixels - 1) >= rotationCount) + { + _rotateRight(rotationCount, 0, _countPixels - 1); + } + } + + void RotateRight(uint16_t rotationCount, uint16_t first, uint16_t last) + { + if (first < _countPixels && + last < _countPixels && + first < last && + (last - first) >= rotationCount) + { + _rotateRight(rotationCount, first, last); + } + } + + void ShiftRight(uint16_t shiftCount) + { + if ((_countPixels - 1) >= shiftCount) + { + _shiftRight(shiftCount, 0, _countPixels - 1); + Dirty(); + } + } + + void ShiftRight(uint16_t shiftCount, uint16_t first, uint16_t last) + { + if (first < _countPixels && + last < _countPixels && + first < last && + (last - first) >= shiftCount) + { + _shiftRight(shiftCount, first, last); + Dirty(); + } + } + + void SwapPixelColor(uint16_t indexPixelOne, uint16_t indexPixelTwo) + { + auto colorOne = GetPixelColor(indexPixelOne); + auto colorTwo = GetPixelColor(indexPixelTwo); + + SetPixelColor(indexPixelOne, colorTwo); + SetPixelColor(indexPixelTwo, colorOne); + }; + + void SetPixelSettings(const typename T_COLOR_FEATURE::SettingsObject& settings) + { + T_COLOR_FEATURE::applySettings(_method.getData(), _method.getDataSize(), settings); + Dirty(); + }; + + void SetMethodSettings(const typename T_METHOD::SettingsObject& settings) + { + _method.applySettings(settings); + Dirty(); + }; + + uint32_t CalcTotalMilliAmpere(const typename T_COLOR_FEATURE::ColorObject::SettingsObject& settings) + { + uint32_t total = 0; // in 1/10th milliamps + + for (uint16_t index = 0; index < _countPixels; index++) + { + auto color = GetPixelColor(index); + total += color.CalcTotalTenthMilliAmpere(settings); + } + + return total / 10; // return millamps + } + +protected: + const uint16_t _countPixels; // Number of RGB LEDs in strip + + uint8_t _state; // internal state + T_METHOD _method; + + uint8_t* _pixels() + { + // get pixels data within the data stream + return T_COLOR_FEATURE::pixels(_method.getData(), _method.getDataSize()); + } + + const uint8_t* _pixels() const + { + // get pixels data within the data stream + return T_COLOR_FEATURE::pixels(_method.getData(), _method.getDataSize()); + } + + void _rotateLeft(uint16_t rotationCount, uint16_t first, uint16_t last) + { + // store in temp + uint8_t temp[rotationCount * T_COLOR_FEATURE::PixelSize]; + uint8_t* pixels = _pixels(); + + uint8_t* pFront = T_COLOR_FEATURE::getPixelAddress(pixels, first); + + T_COLOR_FEATURE::movePixelsInc(temp, pFront, rotationCount); + + // shift data + _shiftLeft(rotationCount, first, last); + + // move temp back + pFront = T_COLOR_FEATURE::getPixelAddress(pixels, last - (rotationCount - 1)); + T_COLOR_FEATURE::movePixelsInc(pFront, temp, rotationCount); + + Dirty(); + } + + void _shiftLeft(uint16_t shiftCount, uint16_t first, uint16_t last) + { + uint16_t front = first + shiftCount; + uint16_t count = last - front + 1; + + uint8_t* pixels = _pixels(); + uint8_t* pFirst = T_COLOR_FEATURE::getPixelAddress(pixels, first); + uint8_t* pFront = T_COLOR_FEATURE::getPixelAddress(pixels, front); + + T_COLOR_FEATURE::movePixelsInc(pFirst, pFront, count); + + // intentional no dirty + } + + void _rotateRight(uint16_t rotationCount, uint16_t first, uint16_t last) + { + // store in temp + uint8_t temp[rotationCount * T_COLOR_FEATURE::PixelSize]; + uint8_t* pixels = _pixels(); + + uint8_t* pFront = T_COLOR_FEATURE::getPixelAddress(pixels, last - (rotationCount - 1)); + + T_COLOR_FEATURE::movePixelsDec(temp, pFront, rotationCount); + + // shift data + _shiftRight(rotationCount, first, last); + + // move temp back + pFront = T_COLOR_FEATURE::getPixelAddress(pixels, first); + T_COLOR_FEATURE::movePixelsDec(pFront, temp, rotationCount); + + Dirty(); + } + + void _shiftRight(uint16_t shiftCount, uint16_t first, uint16_t last) + { + uint16_t front = first + shiftCount; + uint16_t count = last - front + 1; + + uint8_t* pixels = _pixels(); + uint8_t* pFirst = T_COLOR_FEATURE::getPixelAddress(pixels, first); + uint8_t* pFront = T_COLOR_FEATURE::getPixelAddress(pixels, front); + + T_COLOR_FEATURE::movePixelsDec(pFront, pFirst, count); + // intentional no dirty + } +}; + + diff --git a/lib/NeoPixelBus/src/NeoPixelBusLg.h b/lib/NeoPixelBus/src/NeoPixelBusLg.h new file mode 100644 index 00000000..f951efb6 --- /dev/null +++ b/lib/NeoPixelBus/src/NeoPixelBusLg.h @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------- +NeoPixelBus library wrapper template class that provides luminance and gamma control + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include "NeoPixelBus.h" + +// +// +// T_GAMMA - +// NeoGammaEquationMethod +// NeoGammaCieLabEquationMethod +// NeoGammaTableMethod +// NeoGammaNullMethod +// NeoGammaInvert + +template class NeoPixelBusLg : + public NeoPixelBus +{ +public: + class LuminanceShader + { + public: + LuminanceShader(uint8_t luminance = 255) : + _luminance(luminance) + { + } + + // our shader is always dirty, but these are needed for standard + // shader support + bool IsDirty() const + { + return true; + }; + + void Dirty() + { + }; + + void ResetDirty() + { + }; + + typename T_COLOR_FEATURE::ColorObject Apply(uint16_t, const typename T_COLOR_FEATURE::ColorObject& original) + { + // dim and then return gamma adjusted + typename T_COLOR_FEATURE::ColorObject color = original.Dim(_luminance); + return NeoGamma::Correct(color); + } + + protected: + uint8_t _luminance; + + void setLuminance(uint8_t luminance) + { + _luminance = luminance; + } + + uint8_t getLuminance() const + { + return _luminance; + } + + friend class NeoPixelBusLg; + }; + + // Exposed Shader instance for use with NeoDib.Render like + // + // image.Render(strip, strip.Shader); + // where MyBusType is defined like + // typedef NeoPixelBusLg MyBusType; + // + LuminanceShader Shader; + +public: + NeoPixelBusLg(uint16_t countPixels, uint8_t pin) : + NeoPixelBus(countPixels, pin), + Shader() + { + } + + NeoPixelBusLg(uint16_t countPixels, uint8_t pin, NeoBusChannel channel) : + NeoPixelBus(countPixels, pin, channel), + Shader() + { + } + + NeoPixelBusLg(uint16_t countPixels, uint8_t pinClock, uint8_t pinData) : + NeoPixelBus(countPixels, pinClock, pinData), + Shader() + { + } + + NeoPixelBusLg(uint16_t countPixels, uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable = NOT_A_PIN) : + NeoPixelBus(countPixels, pinClock, pinData, pinLatch, pinOutputEnable), + Shader() + { + } + + NeoPixelBusLg(uint16_t countPixels) : + NeoPixelBus(countPixels), + Shader() + { + } + + void SetLuminance(uint8_t luminance) + { + // does NOT affect current pixel data as there is no safe way + // to reconstruct the original color values after being + // modified with both luminance and gamma without storing them + Shader.setLuminance(luminance); + } + + uint8_t GetLuminance() const + { + return Shader.getLuminance(); + } + + void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color) + { + color = Shader.Apply(indexPixel, color); + NeoPixelBus::SetPixelColor(indexPixel, color); + } + + /* + GetPixelColor is not overloaded as the original will be used + to just return the fully adjusted color value directly with + no reverse conversion since it is fraught with inaccuracy + */ + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color) + { + color = Shader.Apply(0, color); + NeoPixelBus::ClearTo(color); + }; + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color, uint16_t first, uint16_t last) + { + color = Shader.Apply(0, color); + NeoPixelBus::ClearTo(color, first, last); + } + + // if the Pixels buffer is manipulated directly, then this can be called + // to apply the luminance and gamma correction to those changes + void ApplyPostAdjustments() + { + if (this->IsDirty()) + { + for (uint16_t indexPixel = 0; indexPixel < NeoPixelBus::PixelCount(); indexPixel++) + { + typename T_COLOR_FEATURE::ColorObject color = NeoPixelBus::GetPixelColor(indexPixel); + color = Shader.Apply(indexPixel, color); + NeoPixelBus::SetPixelColor(indexPixel, color); + } + this->Dirty(); + } + } +}; + + diff --git a/lib/NeoPixelBus/src/NeoPixelSegmentBus.h b/lib/NeoPixelBus/src/NeoPixelSegmentBus.h new file mode 100644 index 00000000..ebe16ce7 --- /dev/null +++ b/lib/NeoPixelBus/src/NeoPixelSegmentBus.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------- +NeoPixelBus library wrapper template class that provides enhanced methods +for writing to segment based strips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include "NeoPixelBus.h" + +template class NeoPixelSegmentBus : + public NeoPixelBus +{ +public: + NeoPixelSegmentBus(uint16_t countPixels, uint8_t pin) : + NeoPixelBus(countPixels, pin) + { + } + + NeoPixelSegmentBus(uint16_t countPixels) : + NeoPixelBus(countPixels) + { + } + + void SetString(uint16_t indexDigit, + const char* str, + uint8_t brightness, + uint8_t defaultBrightness = 0) + { + T_COLOR_FEATURE::ColorObject::SetString(*this, + indexDigit, + str, + brightness, + defaultBrightness); + } + + void SetString(uint16_t indexDigit, + const String& str, + uint8_t brightness, + uint8_t defaultBrightness = 0) + { + SetString(indexDigit, str.c_str(), brightness, defaultBrightness); + } +}; + + diff --git a/lib/NeoPixelBus/src/internal/NeoBuffers.h b/lib/NeoPixelBus/src/internal/NeoBuffers.h new file mode 100644 index 00000000..a3eacc49 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoBuffers.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- +NeoBuffers includes all the classes that describe pixel buffers + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#include "buffers/LayoutMapCallback.h" +#include "buffers/NeoShaderNop.h" +#include "buffers/NeoShaderBase.h" +#include "buffers/NeoBufferContext.h" + +#include "buffers/NeoBuffer.h" +#include "buffers/NeoBufferMethods.h" +#include "buffers/NeoBufferProgmemMethod.h" + +#include "buffers/NeoDib.h" +#include "buffers/NeoBitmapFile.h" +#include "buffers/NeoVerticalSpriteSheet.h" + diff --git a/lib/NeoPixelBus/src/internal/NeoBusChannel.h b/lib/NeoPixelBus/src/internal/NeoBusChannel.h new file mode 100644 index 00000000..ea3ef515 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoBusChannel.h @@ -0,0 +1,44 @@ +#pragma once + +// For those platforms/methods that support dynamic channel setting +// +// ESP32 - 8 TX channels +// ESP32S2 - 4 TX channels +// ESP32C3 - 2 TX channels +// ESP32S3 - 4 TX channels +// NRF52840 - 3 or 4 channels (some variants only have 3) + +enum NeoBusChannel +{ + NeoBusChannel_0, + NeoBusChannel_1, + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) + + NeoBusChannel_2, + +// NRF52x has only 3 or 4 channels of PWM +#if defined(ARDUINO_ARCH_NRF52840) + +#if defined(NRF_PWM3) + NeoBusChannel_3, +#endif + +// ESP32 has either 8, 4, or 2 channels (S2 has only 4, C3 only 2) +#elif defined(ARDUINO_ARCH_ESP32) + + NeoBusChannel_3, + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + NeoBusChannel_4, + NeoBusChannel_5, + NeoBusChannel_6, + NeoBusChannel_7, +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) + +#endif // ARDUINO_ARCH_ESP32 + + NeoBusChannel_COUNT +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/NeoColorFeatures.h b/lib/NeoPixelBus/src/internal/NeoColorFeatures.h new file mode 100644 index 00000000..12ea4e9c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoColorFeatures.h @@ -0,0 +1,86 @@ +/*------------------------------------------------------------------------- +NeoColorFeatures includes all the feature classes that describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// NeoPixel Features +// +#include "features/Neo2ByteElements.h" +#include "features/Neo3ByteElements.h" +#include "features/Neo4ByteElements.h" +#include "features/Neo6ByteElements.h" +#include "features/Neo6Byte4xxElements.h" +#include "features/Neo8ByteElements.h" +#include "features/NeoBgrFeature.h" +#include "features/NeoBrgFeature.h" +#include "features/NeoGrb48Feature.h" +#include "features/NeoGrbFeature.h" +#include "features/NeoGrbwFeature.h" +#include "features/NeoRbgFeature.h" +#include "features/NeoRgb48Feature.h" +#include "features/NeoRgbFeature.h" +#include "features/NeoRgbw64Feature.h" +#include "features/NeoRgbwFeature.h" +#include "features/NeoRgbwxxFeature.h" +#include "features/NeoSm168xxColorFeatures.h" +#include "features/NeoTm1814ColorFeatures.h" +#include "features/NeoTm1914ColorFeatures.h" + +typedef NeoRgb48Feature NeoRgbUcs8903Feature; +typedef NeoRgbw64Feature NeoRgbwUcs8904Feature; +typedef NeoGrb48Feature NeoGrbWs2816Feature; + +// DotStart Features +// +#include "features/DotStar3Elements.h" +#include "features/DotStar4Elements.h" +#include "features/DotStarBgrFeature.h" +#include "features/DotStarBrgFeature.h" +#include "features/DotStarGbrFeature.h" +#include "features/DotStarGrbFeature.h" +#include "features/DotStarLbgrFeature.h" +#include "features/DotStarLbrgFeature.h" +#include "features/DotStarLgbrFeature.h" +#include "features/DotStarLgrbFeature.h" +#include "features/DotStarLrbgFeature.h" +#include "features/DotStarLrgbFeature.h" +#include "features/DotStarRbgFeature.h" +#include "features/DotStarRgbFeature.h" +#include "features/Lpd8806BrgFeature.h" +#include "features/Lpd8806GrbFeature.h" +#include "features/Lpd6803BrgFeature.h" +#include "features/Lpd6803GrbFeature.h" +#include "features/Lpd6803GbrFeature.h" +#include "features/Lpd6803RgbFeature.h" +#include "features/P9813BgrFeature.h" + +// 7 Segment Features +// +#include "features/Neo9ByteElements.h" +#include "features/NeoAbcdefgpsSegmentFeature.h" +#include "features/NeoBacedfpgsSegmentFeature.h" + +typedef NeoAbcdefgpsSegmentFeature SevenSegmentFeature; // Abcdefg order is default diff --git a/lib/NeoPixelBus/src/internal/NeoColors.h b/lib/NeoPixelBus/src/internal/NeoColors.h new file mode 100644 index 00000000..72ccba45 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoColors.h @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- +NeoColors includes all the color classes that describe color and +modify colors for NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#include "colors/NeoHueBlend.h" + +#include "colors/RgbColorBase.h" + +#include "colors/RgbColor.h" +#include "colors/Rgb16Color.h" +#include "colors/Rgb48Color.h" + +#include "colors/HslColor.h" +#include "colors/HsbColor.h" +#include "colors/HtmlColor.h" + +#include "colors/RgbwColor.h" +#include "colors/Rgbw64Color.h" + +#include "colors/SegmentDigit.h" + +#include "colors/NeoGamma.h" +#include "colors/NeoGammaEquationMethod.h" +#include "colors/NeoGammaCieLabEquationMethod.h" +#include "colors/NeoGammaTableMethod.h" +#include "colors/NeoGammaDynamicTableMethod.h" +#include "colors/NeoGammaNullMethod.h" +#include "colors/NeoGammaInvertMethod.h" diff --git a/lib/NeoPixelBus/src/internal/NeoMethods.h b/lib/NeoPixelBus/src/internal/NeoMethods.h new file mode 100644 index 00000000..d60260fc --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoMethods.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- +NeoMethods includes all the classes that describe pulse/data sending methods using +bitbang, SPI, or other platform specific hardware peripherl support. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// Generic Two Wire (clk and data) methods +// +#include "methods/DotStarGenericMethod.h" +#include "methods/Lpd8806GenericMethod.h" +#include "methods/Lpd6803GenericMethod.h" +#include "methods/Ws2801GenericMethod.h" +#include "methods/P9813GenericMethod.h" +#include "methods/Tlc5947GenericMethod.h" +#include "methods/Sm16716GenericMethod.h" + +// Platform specific and One Wire (data) methods +// +#if defined(ARDUINO_ARCH_ESP8266) + +#include "methods/NeoEsp8266DmaMethod.h" +#include "methods/NeoEsp8266I2sDmx512Method.h" +#include "methods/NeoEsp8266UartMethod.h" +#include "methods/NeoEspBitBangMethod.h" + +#elif defined(ARDUINO_ARCH_ESP32) + +#include "methods/NeoEsp32I2sMethod.h" +#include "methods/NeoEsp32RmtMethod.h" +#include "methods/NeoEspBitBangMethod.h" +#include "methods/DotStarEsp32DmaSpiMethod.h" +#include "methods/NeoEsp32I2sXMethod.h" + +#elif defined(ARDUINO_ARCH_NRF52840) // must be before __arm__ + +#include "methods/NeoNrf52xMethod.h" + +#elif defined(__arm__) // must be before ARDUINO_ARCH_AVR due to Teensy incorrectly having it set + +#include "methods/NeoArmMethod.h" + +#elif defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) + +#include "methods/NeoAvrMethod.h" + +#else +#error "Platform Currently Not Supported, please add an Issue at Github/Makuna/NeoPixelBus" +#endif diff --git a/lib/NeoPixelBus/src/internal/NeoSettings.h b/lib/NeoPixelBus/src/internal/NeoSettings.h new file mode 100644 index 00000000..008dd29b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoSettings.h @@ -0,0 +1,62 @@ +/*------------------------------------------------------------------------- +NeoSettings provides settings classes to describe settings + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoNoSettings +{ +}; + +class NeoRgbCurrentSettings +{ +public: + NeoRgbCurrentSettings(uint16_t red, uint16_t green, uint16_t blue) : + RedTenthMilliAmpere(red), + GreenTenthMilliAmpere(green), + BlueTenthMilliAmpere(blue) + { + } + + const uint16_t RedTenthMilliAmpere; // in 1/10th ma + const uint16_t GreenTenthMilliAmpere; // in 1/10th ma + const uint16_t BlueTenthMilliAmpere; // in 1/10th ma +}; + +class NeoRgbwCurrentSettings +{ +public: + NeoRgbwCurrentSettings(uint16_t red, uint16_t green, uint16_t blue, uint16_t white) : + RedTenthMilliAmpere(red), + GreenTenthMilliAmpere(green), + BlueTenthMilliAmpere(blue), + WhiteTenthMilliAmpere(white) + { + } + + const uint16_t RedTenthMilliAmpere; // in 1/10th ma + const uint16_t GreenTenthMilliAmpere; // in 1/10th ma + const uint16_t BlueTenthMilliAmpere; // in 1/10th ma + const uint16_t WhiteTenthMilliAmpere; // in 1/10th ma +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/NeoTopologies.h b/lib/NeoPixelBus/src/internal/NeoTopologies.h new file mode 100644 index 00000000..ce41f3e4 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoTopologies.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- +NeoTopologies includes all the classes that describe pixel cordinate mapping +from 2d spaces to 1d strips that NeoPixelBus uses. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#include "topologies/ColumnMajorAlternatingLayout.h" +#include "topologies/ColumnMajorLayout.h" +#include "topologies/RowMajorAlternatingLayout.h" +#include "topologies/RowMajorLayout.h" + +#include "topologies/NeoTopology.h" +#include "topologies/NeoRingTopology.h" +#include "topologies/NeoTiles.h" +#include "topologies/NeoMosaic.h" + + diff --git a/lib/NeoPixelBus/src/internal/NeoUtil.h b/lib/NeoPixelBus/src/internal/NeoUtil.h new file mode 100644 index 00000000..d7ed30e6 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/NeoUtil.h @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// some platforms do not come with STL or properly defined one, specifically functional +// if you see... +// undefined reference to `std::__throw_bad_function_call()' +// ...then you can either add the platform symbol to the list so NEOPIXEBUS_NO_STL gets defined or +// go to boards.txt and enable c++ by adding (teensy31.build.flags.libs=-lstdc++) and set to "smallest code" option in Arduino +// +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) || defined(STM32L432xx) || defined(STM32L476xx) || defined(ARDUINO_ARCH_SAM) +#define NEOPIXEBUS_NO_STL 1 +#endif + +// some platforms do not define this standard progmem type for some reason +// +#ifndef PGM_VOID_P +#define PGM_VOID_P const void * +#endif + +#ifndef countof +#define countof(array) (sizeof(array)/sizeof(array[0])) +#endif + +class NeoUtil +{ +private: + static constexpr uint8_t Reverse8BitsLookup[16] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; + +public: + inline static uint8_t Reverse8Bits(uint8_t n) + { + return (Reverse8BitsLookup[n & 0b1111] << 4) | Reverse8BitsLookup[n >> 4]; + } + + inline static size_t RoundUp(size_t numToRound, size_t multiple) + { + return ((numToRound + multiple - 1) / multiple) * multiple; + } + + // alternatives that proved to be slower but left for more periodic testing + /* + // marginally slower than the table + static uint8_t Reverse8Bits(uint8_t b) + { + b = (b & 0b11110000) >> 4 | (b & 0b00001111) << 4; + b = (b & 0b11001100) >> 2 | (b & 0b00110011) << 2; + b = (b & 0b10101010) >> 1 | (b & 0b01010101) << 1; + return b; + } + */ + + /* WAY TO SLOW + static uint8_t Reverse8Bits(uint8_t b) + { + return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023; + } + */ +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/animations/NeoEase.h b/lib/NeoPixelBus/src/internal/animations/NeoEase.h new file mode 100644 index 00000000..52c0012b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/animations/NeoEase.h @@ -0,0 +1,325 @@ +/*------------------------------------------------------------------------- +NeoEase provides animation curve equations for animation support. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#if defined(NEOPIXEBUS_NO_STL) + +typedef float(*AnimEaseFunction)(float unitValue); + +#else + +#undef max +#undef min +#include +typedef std::function AnimEaseFunction; + +#endif + +class NeoEase +{ +public: + static float Linear(float unitValue) + { + return unitValue; + } + + static float QuadraticIn(float unitValue) + { + return unitValue * unitValue; + } + + static float QuadraticOut(float unitValue) + { + return (-unitValue * (unitValue - 2.0f)); + } + + static float QuadraticInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * unitValue * unitValue); + } + else + { + unitValue -= 1.0f; + return (-0.5f * (unitValue * (unitValue - 2.0f) - 1.0f)); + } + } + + static float QuadraticCenter(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (-0.5f * (unitValue * unitValue - 2.0f)); + } + else + { + unitValue -= 1.0f; + return (0.5f * (unitValue * unitValue + 1.0f)); + } + } + + static float CubicIn(float unitValue) + { + return (unitValue * unitValue * unitValue); + } + + static float CubicOut(float unitValue) + { + unitValue -= 1.0f; + return (unitValue * unitValue * unitValue + 1.0f); + } + + static float CubicInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * unitValue * unitValue * unitValue); + } + else + { + unitValue -= 2.0f; + return (0.5f * (unitValue * unitValue * unitValue + 2.0f)); + } + } + + static float CubicCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + return (0.5f * (unitValue * unitValue * unitValue + 1.0f)); + } + + static float QuarticIn(float unitValue) + { + return (unitValue * unitValue * unitValue * unitValue); + } + + static float QuarticOut(float unitValue) + { + unitValue -= 1.0f; + return -(unitValue * unitValue * unitValue * unitValue - 1.0f); + } + + static float QuarticInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * unitValue * unitValue * unitValue * unitValue); + } + else + { + unitValue -= 2.0f; + return (-0.5f * (unitValue * unitValue * unitValue * unitValue - 2.0f)); + } + } + + static float QuarticCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + if (unitValue < 0.0f) + { + return (-0.5f * (unitValue * unitValue * unitValue * unitValue - 1.0f)); + } + else + { + return (0.5f * (unitValue * unitValue * unitValue * unitValue + 1.0f)); + } + } + + static float QuinticIn(float unitValue) + { + return (unitValue * unitValue * unitValue * unitValue * unitValue); + } + + static float QuinticOut(float unitValue) + { + unitValue -= 1.0f; + return (unitValue * unitValue * unitValue * unitValue * unitValue + 1.0f); + } + + static float QuinticInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * unitValue * unitValue * unitValue * unitValue * unitValue); + } + else + { + unitValue -= 2.0f; + return (0.5f * (unitValue * unitValue * unitValue * unitValue * unitValue + 2.0f)); + } + } + + static float QuinticCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + return (0.5f * (unitValue * unitValue * unitValue * unitValue * unitValue + 1.0f)); + } + + static float SinusoidalIn(float unitValue) + { + return (-cos(unitValue * HALF_PI) + 1.0f); + } + + static float SinusoidalOut(float unitValue) + { + return (sin(unitValue * HALF_PI)); + } + + static float SinusoidalInOut(float unitValue) + { + return -0.5f * (cos(PI * unitValue) - 1.0f); + } + + static float SinusoidalCenter(float unitValue) + { + if (unitValue < 0.5f) + { + return (0.5f * sin(PI * unitValue)); + } + else + { + return (-0.5f * (cos(PI * (unitValue-0.5f)) + 1.0f)); + } + + } + + static float ExponentialIn(float unitValue) + { + return (pow(2, 10.0f * (unitValue - 1.0f))); + } + + static float ExponentialOut(float unitValue) + { + return (-pow(2, -10.0f * unitValue) + 1.0f); + } + + static float ExponentialInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * pow(2, 10.0f * (unitValue - 1.0f))); + } + else + { + unitValue -= 1.0f; + return (0.5f * (-pow(2, -10.0f * unitValue) + 2.0f)); + } + } + + static float ExponentialCenter(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * (-pow(2, -10.0f * unitValue) + 1.0f)); + } + else + { + unitValue -= 2.0f; + return (0.5f * (pow(2, 10.0f * unitValue) + 1.0f)); + } + } + + static float CircularIn(float unitValue) + { + if (unitValue == 1.0f) + { + return 1.0f; + } + else + { + return (-(sqrt(1.0f - unitValue * unitValue) - 1.0f)); + } + } + + static float CircularOut(float unitValue) + { + unitValue -= 1.0f; + return (sqrt(1.0f - unitValue * unitValue)); + } + + static float CircularInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (-0.5f * (sqrt(1.0f - unitValue * unitValue) - 1.0f)); + } + else + { + unitValue -= 2.0f; + return (0.5f * (sqrt(1.0f - unitValue * unitValue) + 1.0f)); + } + } + + static float CircularCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + + if (unitValue < 0.0f) + { + return (0.5f * sqrt(1.0f - unitValue * unitValue)); + } + else if (unitValue > 0.0f) + { + unitValue -= 2.0f; + return (-0.5f * (sqrt(1.0f - unitValue * unitValue) - 1.0f ) + 0.5f); + } + else + { + return 1.0f; + } + } + + static float Gamma(float unitValue) + { + return pow(unitValue, 1.0f / 0.45f); + } + + static float GammaCieLab(float unitValue) + { + if (unitValue <= 0.08f) + { + return unitValue / 9.033f; + } + else + { + return pow((unitValue + 0.16f) / 1.16f, 3.0f); + } + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/animations/NeoPixelAnimator.cpp b/lib/NeoPixelBus/src/internal/animations/NeoPixelAnimator.cpp new file mode 100644 index 00000000..b97565fa --- /dev/null +++ b/lib/NeoPixelBus/src/internal/animations/NeoPixelAnimator.cpp @@ -0,0 +1,198 @@ +/*------------------------------------------------------------------------- +NeoPixelAnimator provides animation timing support. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "NeoPixelBus.h" +#include "NeoPixelAnimator.h" + +NeoPixelAnimator::NeoPixelAnimator(uint16_t countAnimations, uint16_t timeScale) : + _countAnimations(countAnimations), + _animationLastTick(0), + _activeAnimations(0), + _isRunning(true) +{ + setTimeScale(timeScale); + _animations = new AnimationContext[_countAnimations]; +} + +NeoPixelAnimator::~NeoPixelAnimator() +{ + delete[] _animations; +} + +bool NeoPixelAnimator::NextAvailableAnimation(uint16_t* indexAvailable, uint16_t indexStart) +{ + if (indexStart >= _countAnimations) + { + // last one + indexStart = _countAnimations - 1; + } + + uint16_t next = indexStart; + + do + { + if (!IsAnimationActive(next)) + { + if (indexAvailable) + { + *indexAvailable = next; + } + return true; + } + next = (next + 1) % _countAnimations; + } while (next != indexStart); + return false; +} + +void NeoPixelAnimator::StartAnimation(uint16_t indexAnimation, + uint16_t duration, + AnimUpdateCallback animUpdate) +{ + if (indexAnimation >= _countAnimations || animUpdate == NULL) + { + return; + } + + if (_activeAnimations == 0) + { + _animationLastTick = millis(); + } + + StopAnimation(indexAnimation); + + // all animations must have at least non zero duration, otherwise + // they are considered stopped + if (duration == 0) + { + duration = 1; + } + + _animations[indexAnimation].StartAnimation(duration, animUpdate); + + _activeAnimations++; +} + +void NeoPixelAnimator::StopAnimation(uint16_t indexAnimation) +{ + if (indexAnimation >= _countAnimations) + { + return; + } + + if (IsAnimationActive(indexAnimation)) + { + _activeAnimations--; + _animations[indexAnimation].StopAnimation(); + } +} + +void NeoPixelAnimator::StopAll() +{ + for (uint16_t indexAnimation = 0; indexAnimation < _countAnimations; ++indexAnimation) + { + _animations[indexAnimation].StopAnimation(); + } + _activeAnimations = 0; +} + + +void NeoPixelAnimator::UpdateAnimations() +{ + if (_isRunning) + { + uint32_t currentTick = millis(); + uint32_t delta = currentTick - _animationLastTick; + + if (delta >= _timeScale) + { + AnimationContext* pAnim; + + delta /= _timeScale; // scale delta into animation time + + for (uint16_t iAnim = 0; iAnim < _countAnimations; iAnim++) + { + pAnim = &_animations[iAnim]; + AnimUpdateCallback fnUpdate = pAnim->_fnCallback; + AnimationParam param; + + param.index = iAnim; + + if (pAnim->_remaining > delta) + { + param.state = (pAnim->_remaining == pAnim->_duration) ? AnimationState_Started : AnimationState_Progress; + param.progress = pAnim->CurrentProgress(); + + fnUpdate(param); + + pAnim->_remaining -= delta; + } + else if (pAnim->_remaining > 0) + { + param.state = AnimationState_Completed; + param.progress = 1.0f; + + _activeAnimations--; + pAnim->StopAnimation(); + + fnUpdate(param); + } + } + + _animationLastTick = currentTick; + } + } +} + +void NeoPixelAnimator::ChangeAnimationDuration(uint16_t indexAnimation, uint16_t newDuration) +{ + if (indexAnimation >= _countAnimations) + { + return; + } + + AnimationContext* pAnim = &_animations[indexAnimation]; + + // calc the current animation progress + float progress = pAnim->CurrentProgress(); + + // keep progress in range just in case + if (progress < 0.0f) + { + progress = 0.0f; + } + else if (progress > 1.0f) + { + progress = 1.0f; + } + + // change the duration + pAnim->_duration = newDuration; + + // _remaining time must also be reset after a duration change; + // use the progress to recalculate it + pAnim->_remaining = uint16_t(pAnim->_duration * (1.0f - progress)); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/LayoutMapCallback.h b/lib/NeoPixelBus/src/internal/buffers/LayoutMapCallback.h new file mode 100644 index 00000000..d822f188 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/LayoutMapCallback.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- +LayoutMapCallback + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + + +#if defined(NEOPIXEBUS_NO_STL) + +typedef uint16_t(*LayoutMapCallback)(int16_t x, int16_t y); + +#else + +#undef max +#undef min +#include +typedef std::function LayoutMapCallback; + +#endif \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoBitmapFile.h b/lib/NeoPixelBus/src/internal/buffers/NeoBitmapFile.h new file mode 100644 index 00000000..d66316a8 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoBitmapFile.h @@ -0,0 +1,391 @@ +/*------------------------------------------------------------------------- +NeoBitmapFile + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +const uint16_t c_BitmapFileId = 0x4d42; // "BM" + +#pragma pack(push, 2) +struct BitmapFileHeader +{ + uint16_t FileId; // only c_BitmapFileId is supported + uint32_t FileSize; + uint16_t Reserved0; + uint16_t Reserved1; + uint32_t PixelAddress; +}; + +struct BitmapInfoHeader +{ + uint32_t Size; + int32_t Width; + int32_t Height; + uint16_t Planes; // only support 1 + uint16_t BitsPerPixel; // only support 24 and 32 + uint32_t Compression; // only support BI_Rgb + uint32_t RawDateSize; // can be zero + int32_t XPpm; + int32_t YPpm; + uint32_t PaletteLength; + uint32_t ImportantColorCount; +}; +#pragma pack(pop) + +enum BmpCompression +{ + BI_Rgb, + BI_Rle8, + BI_Rle4, + BI_Bitfields, + BI_Jpeg, + BI_Png, + BI_AlphaBitfields, + BI_Cmyk = 11, + BI_CmykRle8, + BI_CmykRle4 +}; + +// T_COLOR_FEATURE - one of the Features +// T_FILE_METHOD - any standard File object following Arduino File methods/members +// +template class NeoBitmapFile +{ +public: + NeoBitmapFile() : + _fileAddressPixels(0), + _width(0), + _height(0), + _sizeRow(0), + _bytesPerPixel(0), + _bottomToTop(true) + { + } + + ~NeoBitmapFile() + { + _file.close(); + } + + bool Begin(T_FILE_METHOD file) + { + if (_file) + { + _file.close(); + } + + if (!file || !file.seek(0)) + { + goto error; + } + + _file = file; + + BitmapFileHeader bmpHeader; + BitmapInfoHeader bmpInfoHeader; + size_t result; + + result = _file.read((uint8_t*)(&bmpHeader), sizeof(bmpHeader)); + + if (result != sizeof(bmpHeader) || + bmpHeader.FileId != c_BitmapFileId || + bmpHeader.FileSize != _file.size()) + { + goto error; + } + + result = _file.read((uint8_t*)(&bmpInfoHeader), sizeof(bmpInfoHeader)); + + if (result != sizeof(bmpInfoHeader) || + result != bmpInfoHeader.Size || + 1 != bmpInfoHeader.Planes || + BI_Rgb != bmpInfoHeader.Compression) + { + goto error; + } + + if (!(24 == bmpInfoHeader.BitsPerPixel || + 32 == bmpInfoHeader.BitsPerPixel)) + { + goto error; + } + + // save the interesting information + _width = abs(bmpInfoHeader.Width); + _height = abs(bmpInfoHeader.Height); + _fileAddressPixels = bmpHeader.PixelAddress; + // negative height means rows are top to bottom + _bottomToTop = (bmpInfoHeader.Height > 0); + // rows are 32 bit aligned so they may have padding on each row + _sizeRow = (bmpInfoHeader.BitsPerPixel * _width + 31) / 32 * 4; + _bytesPerPixel = bmpInfoHeader.BitsPerPixel / 8; + + return true; + + error: + _fileAddressPixels = 0; + _width = 0; + _height = 0; + _sizeRow = 0; + _bytesPerPixel = 0; + + _file.close(); + return false; + }; + + size_t PixelSize() const + { + return T_COLOR_FEATURE::PixelSize; + }; + + uint16_t PixelCount() const + { + return _width * _height; + }; + + uint16_t Width() const + { + return _width; + }; + + uint16_t Height() const + { + return _height; + }; + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(int16_t x, int16_t y) + { + if (x < 0 || x >= _width || y < 0 || y >= _height) + { + // Pixel # is out of bounds, this will get converted to a + // color object type initialized to 0 (black) + return 0; + } + + typename T_COLOR_FEATURE::ColorObject color; + if (!seek(x, y) || !readPixel(&color)) + { + return 0; + } + + return color; + }; + + + template void Render(NeoBufferContext destBuffer, + T_SHADER& shader, + uint16_t indexPixel, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc) + { + const uint16_t destPixelCount = destBuffer.PixelCount(); + typename T_COLOR_FEATURE::ColorObject color(0); + xSrc = constrainX(xSrc); + ySrc = constrainY(ySrc); + + if (seek(xSrc, ySrc)) + { + for (int16_t x = 0; x < wSrc && indexPixel < destPixelCount; x++, indexPixel++) + { + if (static_cast(xSrc) < _width) + { + if (readPixel(&color)) + { + color = shader.Apply(indexPixel, color); + xSrc++; + } + } + + T_COLOR_FEATURE::applyPixelColor(destBuffer.Pixels, indexPixel, color); + } + } + } + + void Blt(NeoBufferContext destBuffer, + uint16_t indexPixel, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc) + { + NeoShaderNop shaderNop; + + Render>(destBuffer, shaderNop, indexPixel, xSrc, ySrc, wSrc); + }; + + template void Render(NeoBufferContext destBuffer, + T_SHADER& shader, + int16_t xDest, + int16_t yDest, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc, + int16_t hSrc, + LayoutMapCallback layoutMap) + { + const uint16_t destPixelCount = destBuffer.PixelCount(); + typename T_COLOR_FEATURE::ColorObject color(0); + + for (int16_t y = 0; y < hSrc; y++) + { + int16_t xFile = constrainX(xSrc); + int16_t yFile = constrainY(ySrc + y); + + if (seek(xFile, yFile)) + { + for (int16_t x = 0; x < wSrc; x++) + { + uint16_t indexDest = layoutMap(xDest + x, yDest + y); + + if (static_cast(xFile) < _width) + { + if (readPixel(&color)) + { + color = shader.Apply(indexDest, color); + xFile++; + } + } + + if (indexDest < destPixelCount) + { + T_COLOR_FEATURE::applyPixelColor(destBuffer.Pixels, indexDest, color); + } + } + } + } + }; + + void Blt(NeoBufferContext destBuffer, + int16_t xDest, + int16_t yDest, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc, + int16_t hSrc, + LayoutMapCallback layoutMap) + { + NeoShaderNop shaderNop; + + Render>(destBuffer, + shaderNop, + xDest, + yDest, + xSrc, + ySrc, + wSrc, + hSrc, + layoutMap); + }; + + +private: + T_FILE_METHOD _file; + uint32_t _fileAddressPixels; + uint16_t _width; + uint16_t _height; + uint32_t _sizeRow; + uint8_t _bytesPerPixel; + bool _bottomToTop; + + int16_t constrainX(int16_t x) const + { + if (x < 0) + { + x = 0; + } + else if (static_cast(x) >= _width) + { + x = _width - 1; + } + return x; + }; + + int16_t constrainY(int16_t y) const + { + if (y < 0) + { + y = 0; + } + else if (static_cast(y) >= _height) + { + y = _height - 1; + } + return y; + }; + + bool seek(int16_t x, int16_t y) + { + if (_bottomToTop) + { + y = (_height - 1) - y; + } + + uint32_t pos = y * _sizeRow + x * _bytesPerPixel; + pos += _fileAddressPixels; + + return _file.seek(pos); + }; + + bool readPixel(RgbColor* color) + { + uint8_t bgr[4]; + int result; + + result = _file.read(bgr, _bytesPerPixel); + + if (result != _bytesPerPixel) + { + *color = 0; + return false; + } + + color->B = bgr[0]; + color->G = bgr[1]; + color->R = bgr[2]; + + return true; + }; + + bool readPixel(RgbwColor* color) + { + uint8_t bgr[4]; + int result; + + bgr[3] = 0; // init white channel as read maybe only 3 bytes + result = _file.read(bgr, _bytesPerPixel); + + if (result != _bytesPerPixel) + { + *color = 0; + return false; + } + + color->B = bgr[0]; + color->G = bgr[1]; + color->R = bgr[2]; + color->W = bgr[3]; + + return true; + }; +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoBuffer.h b/lib/NeoPixelBus/src/internal/buffers/NeoBuffer.h new file mode 100644 index 00000000..c2501c79 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoBuffer.h @@ -0,0 +1,176 @@ +/*------------------------------------------------------------------------- +NeoBuffer + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// T_BUFFER_METHOD - one of +// NeoBufferMethod +// NeoBufferProgmemMethod +// +template class NeoBuffer +{ +public: + NeoBuffer(uint16_t width, + uint16_t height, + PGM_VOID_P pixels) : + _method(width, height, pixels) + { + } + + operator NeoBufferContext() + { + return _method; + } + + uint16_t PixelCount() const + { + return _method.PixelCount(); + }; + + uint16_t Width() const + { + return _method.Width(); + }; + + uint16_t Height() const + { + return _method.Height(); + }; + + void SetPixelColor( + int16_t x, + int16_t y, + typename T_BUFFER_METHOD::ColorObject color) + { + _method.SetPixelColor(pixelIndex(x, y), color); + }; + + typename T_BUFFER_METHOD::ColorObject GetPixelColor( + int16_t x, + int16_t y) const + { + return _method.GetPixelColor(pixelIndex(x, y)); + }; + + void ClearTo(typename T_BUFFER_METHOD::ColorObject color) + { + _method.ClearTo(color); + }; + + void Blt(NeoBufferContext destBuffer, + uint16_t indexPixel) + { + uint16_t destPixelCount = destBuffer.PixelCount(); + // validate indexPixel + if (indexPixel >= destPixelCount) + { + return; + } + + // calc how many we can copy + uint16_t copyCount = destPixelCount - indexPixel; + uint16_t srcPixelCount = PixelCount(); + if (copyCount > srcPixelCount) + { + copyCount = srcPixelCount; + } + + uint8_t* pDest = T_BUFFER_METHOD::ColorFeature::getPixelAddress(destBuffer.Pixels, indexPixel); + _method.CopyPixels(pDest, _method.Pixels(), copyCount); + } + + void Blt(NeoBufferContext destBuffer, + int16_t xDest, + int16_t yDest, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc, + int16_t hSrc, + LayoutMapCallback layoutMap) + { + uint16_t destPixelCount = destBuffer.PixelCount(); + + for (int16_t y = 0; y < hSrc; y++) + { + for (int16_t x = 0; x < wSrc; x++) + { + uint16_t indexDest = layoutMap(xDest + x, yDest + y); + + if (indexDest < destPixelCount) + { + const uint8_t* pSrc = T_BUFFER_METHOD::ColorFeature::getPixelAddress(_method.Pixels(), pixelIndex(xSrc + x, ySrc + y)); + uint8_t* pDest = T_BUFFER_METHOD::ColorFeature::getPixelAddress(destBuffer.Pixels, indexDest); + + _method.CopyPixels(pDest, pSrc, 1); + } + } + } + } + + void Blt(NeoBufferContext destBuffer, + int16_t xDest, + int16_t yDest, + LayoutMapCallback layoutMap) + { + Blt(destBuffer, xDest, yDest, 0, 0, Width(), Height(), layoutMap); + } + + template void Render(NeoBufferContext destBuffer, T_SHADER& shader) + { + uint16_t countPixels = destBuffer.PixelCount(); + + if (countPixels > _method.PixelCount()) + { + countPixels = _method.PixelCount(); + } + + for (uint16_t indexPixel = 0; indexPixel < countPixels; indexPixel++) + { + const uint8_t* pSrc = T_BUFFER_METHOD::ColorFeature::getPixelAddress(_method.Pixels(), indexPixel); + uint8_t* pDest = T_BUFFER_METHOD::ColorFeature::getPixelAddress(destBuffer.Pixels, indexPixel); + + shader.Apply(indexPixel, pDest, pSrc); + } + } + +private: + T_BUFFER_METHOD _method; + + uint16_t pixelIndex( + int16_t x, + int16_t y) const + { + uint16_t result = PixelIndex_OutOfBounds; + + if (x >= 0 && + static_cast(x) < Width() && + y >= 0 && + static_cast(y) < Height()) + { + result = x + y * Width(); + } + return result; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoBufferContext.h b/lib/NeoPixelBus/src/internal/buffers/NeoBufferContext.h new file mode 100644 index 00000000..d1cb0b81 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoBufferContext.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- +NeoBufferContext + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// This is used to allow a template classes that share common buffer concept to +// be able to pass that common information to functions +// The template classes just need to expose a conversion operator to this type +template struct NeoBufferContext +{ + NeoBufferContext(uint8_t* pixels, + size_t sizePixels) : + Pixels(pixels), + SizePixels(sizePixels) + { + } + + uint16_t PixelCount() const + { + return SizePixels / T_COLOR_FEATURE::PixelSize; + }; + + uint8_t* Pixels; + const size_t SizePixels; + +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoBufferMethods.h b/lib/NeoPixelBus/src/internal/buffers/NeoBufferMethods.h new file mode 100644 index 00000000..169e1fff --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoBufferMethods.h @@ -0,0 +1,152 @@ +/*------------------------------------------------------------------------- +NeoBufferMethod + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +template class NeoBufferMethod +{ +public: + NeoBufferMethod(uint16_t width, uint16_t height, PGM_VOID_P pixels = NULL) : + _width(width), + _height(height) + { + _pixels = (uint8_t*)malloc(PixelsSize()); + if (pixels) + { + // copy from progmem to initialize + T_COLOR_FEATURE::movePixelsInc_P(_pixels, pixels, PixelCount()); + } + } + + ~NeoBufferMethod() + { + free(_pixels); + } + + operator NeoBufferContext() + { + return NeoBufferContext(Pixels(), PixelsSize()); + } + + uint8_t* Pixels() const + { + return _pixels; + }; + + size_t PixelsSize() const + { + return PixelSize() * PixelCount(); + }; + + size_t PixelSize() const + { + return T_COLOR_FEATURE::PixelSize; + }; + + uint16_t PixelCount() const + { + return _width * _height; + }; + + uint16_t Width() const + { + return _width; + }; + + uint16_t Height() const + { + return _height; + }; + + void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color) + { + if (indexPixel < PixelCount()) + { + T_COLOR_FEATURE::applyPixelColor(_pixels, indexPixel, color); + } + }; + + void SetPixelColor(int16_t x, int16_t y, typename T_COLOR_FEATURE::ColorObject color) + { + if (x < 0 || x >= _width || y < 0 || y >= _height) + { + return; + } + + uint16_t indexPixel = x + y * _width; + T_COLOR_FEATURE::applyPixelColor(_pixels, indexPixel, color); + }; + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(uint16_t indexPixel) const + { + if (indexPixel >= PixelCount()) + { + // Pixel # is out of bounds, this will get converted to a + // color object type initialized to 0 (black) + return 0; + } + + return T_COLOR_FEATURE::retrievePixelColor(_pixels, indexPixel); + }; + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(int16_t x, int16_t y) const + { + if (x < 0 || x >= _width || y < 0 || y >= _height) + { + // Pixel # is out of bounds, this will get converted to a + // color object type initialized to 0 (black) + return 0; + } + + uint16_t indexPixel = x + y * _width; + return T_COLOR_FEATURE::retrievePixelColor(_pixels, indexPixel); + }; + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color) + { + uint8_t temp[T_COLOR_FEATURE::PixelSize]; + + T_COLOR_FEATURE::applyPixelColor(temp, 0, color); + + T_COLOR_FEATURE::replicatePixel(_pixels, temp, PixelCount()); + }; + + void CopyPixels(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + T_COLOR_FEATURE::movePixelsInc(pPixelDest, pPixelSrc, count); + } + + typedef typename T_COLOR_FEATURE::ColorObject ColorObject; + typedef T_COLOR_FEATURE ColorFeature; + +private: + const uint16_t _width; + const uint16_t _height; + uint8_t* _pixels; +}; + + + diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoBufferProgmemMethod.h b/lib/NeoPixelBus/src/internal/buffers/NeoBufferProgmemMethod.h new file mode 100644 index 00000000..c46848b8 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoBufferProgmemMethod.h @@ -0,0 +1,126 @@ +/*------------------------------------------------------------------------- +NeoBufferProgmemMethod + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +template class NeoBufferProgmemMethod +{ +public: + NeoBufferProgmemMethod(uint16_t width, uint16_t height, PGM_VOID_P pixels) : + _width(width), + _height(height), + _pixels(pixels) + { + } + + operator NeoBufferContext() + { + return NeoBufferContext(Pixels(), PixelsSize()); + } + + uint8_t* Pixels() const + { + return (uint8_t*)_pixels; + }; + + size_t PixelsSize() const + { + return PixelSize() * PixelCount(); + }; + + size_t PixelSize() const + { + return T_COLOR_FEATURE::PixelSize; + }; + + uint16_t PixelCount() const + { + return _width * _height; + }; + + uint16_t Width() const + { + return _width; + }; + + uint16_t Height() const + { + return _height; + }; + + void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color) + { + // PROGMEM is read only, this will do nothing + }; + + void SetPixelColor(uint16_t x, uint16_t y, typename T_COLOR_FEATURE::ColorObject color) + { + // PROGMEM is read only, this will do nothing + }; + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(uint16_t indexPixel) const + { + if (indexPixel >= PixelCount()) + { + // Pixel # is out of bounds, this will get converted to a + // color object type initialized to 0 (black) + return 0; + } + + return T_COLOR_FEATURE::retrievePixelColor_P(_pixels, indexPixel); + }; + + typename T_COLOR_FEATURE::ColorObject GetPixelColor(int16_t x, int16_t y) const + { + if (x < 0 || x >= _width || y < 0 || y >= _height) + { + // Pixel # is out of bounds, this will get converted to a + // color object type initialized to 0 (black) + return 0; + } + + uint16_t indexPixel = x + y * _width; + return T_COLOR_FEATURE::retrievePixelColor_P(_pixels, indexPixel); + }; + + void ClearTo(typename T_COLOR_FEATURE::ColorObject color) + { + // PROGMEM is read only, this will do nothing + }; + + void CopyPixels(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + T_COLOR_FEATURE::movePixelsInc_P(pPixelDest, pPixelSrc, count); + } + + typedef typename T_COLOR_FEATURE::ColorObject ColorObject; + typedef T_COLOR_FEATURE ColorFeature; + +private: + const uint16_t _width; + const uint16_t _height; + PGM_VOID_P _pixels; +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoDib.h b/lib/NeoPixelBus/src/internal/buffers/NeoDib.h new file mode 100644 index 00000000..f3d162fc --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoDib.h @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- +NeoDib - Device Independant Bitmap, interal data stored in RGB/RGBW format +rather than the ColorFeature format + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// T_COLOR_OBJECT - one of the color objects +// RgbColor +// RgbwColor +// Rgb16Color +// Rgb48Color +// Rgbw64Color +// SevenSegDigit +// +template class NeoDib +{ +public: + NeoDib(uint16_t countPixels) : + _countPixels(countPixels), + _state(0) + { + _pixels = (T_COLOR_OBJECT*)malloc(PixelsSize()); + ResetDirty(); + } + + ~NeoDib() + { + free((uint8_t*)_pixels); + } + + NeoDib& operator=(const NeoDib& other) + { + // check for self-assignment + if (&other == this) + { + return *this; + } + + uint16_t copyCount = other.PixelCount() < PixelCount() ? other.PixelCount() : PixelCount(); + + for (uint16_t pixel = 0; pixel < copyCount; pixel++) + { + _pixels[pixel] = other.Pixels()[pixel]; + } + + Dirty(); + return *this; + } + + T_COLOR_OBJECT* Pixels() const + { + return _pixels; + }; + + uint16_t PixelCount() const + { + return _countPixels; + }; + + size_t PixelsSize() const + { + return _countPixels * PixelSize(); + }; + + size_t PixelSize() const + { + return sizeof(T_COLOR_OBJECT); + }; + + void SetPixelColor( + uint16_t indexPixel, + T_COLOR_OBJECT color) + { + if (indexPixel < PixelCount()) + { + _pixels[indexPixel] = color; + Dirty(); + } + }; + + T_COLOR_OBJECT GetPixelColor( + uint16_t indexPixel) const + { + if (indexPixel >= PixelCount()) + { + return 0; + } + return _pixels[indexPixel]; + }; + + void ClearTo(T_COLOR_OBJECT color) + { + for (uint16_t pixel = 0; pixel < PixelCount(); pixel++) + { + _pixels[pixel] = color; + } + Dirty(); + }; + + template + void Render(NeoBufferContext destBuffer, T_SHADER& shader, uint16_t destIndexPixel = 0) + { + if (IsDirty() || shader.IsDirty()) + { + uint16_t countPixels = destBuffer.PixelCount(); + + if (countPixels > _countPixels) + { + countPixels = _countPixels; + } + + for (uint16_t indexPixel = 0; indexPixel < countPixels; indexPixel++) + { + T_COLOR_OBJECT color = shader.Apply(indexPixel, _pixels[indexPixel]); + T_COLOR_FEATURE::applyPixelColor(destBuffer.Pixels, destIndexPixel + indexPixel, color); + } + + shader.ResetDirty(); + ResetDirty(); + } + } + + bool IsDirty() const + { + return (_state & NEO_DIRTY); + }; + + void Dirty() + { + _state |= NEO_DIRTY; + }; + + void ResetDirty() + { + _state &= ~NEO_DIRTY; + }; + +private: + const uint16_t _countPixels; // Number of RGB LEDs in strip + T_COLOR_OBJECT* _pixels; + uint8_t _state; // internal state +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoShaderBase.h b/lib/NeoPixelBus/src/internal/buffers/NeoShaderBase.h new file mode 100644 index 00000000..399c6f9b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoShaderBase.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- +NeoShaderBase + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoShaderBase +{ +public: + NeoShaderBase() : + _state(0) + { + } + + bool IsDirty() const + { + return (_state & NEO_DIRTY); + }; + + void Dirty() + { + _state |= NEO_DIRTY; + }; + + void ResetDirty() + { + _state &= ~NEO_DIRTY; + }; + +protected: + uint8_t _state; // internal state +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoShaderNop.h b/lib/NeoPixelBus/src/internal/buffers/NeoShaderNop.h new file mode 100644 index 00000000..c7ca2e02 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoShaderNop.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- +NeoShaderNop + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +template class NeoShaderNop +{ +public: + NeoShaderNop() + { + } + + bool IsDirty() const + { + return true; + }; + + void Dirty() + { + }; + + void ResetDirty() + { + }; + + T_COLOR_OBJECT Apply(uint16_t, T_COLOR_OBJECT color) + { + return color; + }; +}; + diff --git a/lib/NeoPixelBus/src/internal/buffers/NeoVerticalSpriteSheet.h b/lib/NeoPixelBus/src/internal/buffers/NeoVerticalSpriteSheet.h new file mode 100644 index 00000000..bc1885f1 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/buffers/NeoVerticalSpriteSheet.h @@ -0,0 +1,166 @@ +/*------------------------------------------------------------------------- +NeoVerticalSpriteSheet + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// T_BUFFER_METHOD - one of +// NeoBufferMethod +// NeoBufferProgmemMethod +// +template class NeoVerticalSpriteSheet +{ +public: + NeoVerticalSpriteSheet(uint16_t width, + uint16_t height, + uint16_t spriteHeight, + PGM_VOID_P pixels) : + _method(width, height, pixels), + _spriteHeight(spriteHeight), + _spriteCount(height / spriteHeight) + { + } + + operator NeoBufferContext() + { + return _method; + } + + uint16_t SpriteWidth() const + { + return _method.Width(); + }; + + uint16_t SpriteHeight() const + { + return _spriteHeight; + }; + + uint16_t SpriteCount() const + { + return _spriteCount; + } + + void SetPixelColor(uint16_t indexSprite, + int16_t x, + int16_t y, + typename T_BUFFER_METHOD::ColorObject color) + { + _method.SetPixelColor(pixelIndex(indexSprite, x, y), color); + }; + + typename T_BUFFER_METHOD::ColorObject GetPixelColor(uint16_t indexSprite, + int16_t x, + int16_t y) const + { + return _method.GetPixelColor(pixelIndex(indexSprite, x, y)); + }; + + void ClearTo(typename T_BUFFER_METHOD::ColorObject color) + { + _method.ClearTo(color); + }; + + void Blt(NeoBufferContext destBuffer, + uint16_t indexPixel, + uint16_t indexSprite) + { + uint16_t destPixelCount = destBuffer.PixelCount(); + // validate indexPixel + if (indexPixel >= destPixelCount) + { + return; + } + + // validate indexSprite + if (indexSprite >= _spriteCount) + { + return; + } + // calc how many we can copy + uint16_t copyCount = destPixelCount - indexPixel; + + if (copyCount > SpriteWidth()) + { + copyCount = SpriteWidth(); + } + + uint8_t* pDest = T_BUFFER_METHOD::ColorFeature::getPixelAddress(destBuffer.Pixels, indexPixel); + const uint8_t* pSrc = T_BUFFER_METHOD::ColorFeature::getPixelAddress(_method.Pixels(), pixelIndex(indexSprite, 0, 0)); + _method.CopyPixels(pDest, pSrc, copyCount); + } + + void Blt(NeoBufferContext destBuffer, + int16_t x, + int16_t y, + uint16_t indexSprite, + LayoutMapCallback layoutMap) + { + if (indexSprite >= _spriteCount) + { + return; + } + uint16_t destPixelCount = destBuffer.PixelCount(); + + for (int16_t srcY = 0; srcY < SpriteHeight(); srcY++) + { + for (int16_t srcX = 0; srcX < SpriteWidth(); srcX++) + { + uint16_t indexDest = layoutMap(srcX + x, srcY + y); + + if (indexDest < destPixelCount) + { + const uint8_t* pSrc = T_BUFFER_METHOD::ColorFeature::getPixelAddress(_method.Pixels(), pixelIndex(indexSprite, srcX, srcY)); + uint8_t* pDest = T_BUFFER_METHOD::ColorFeature::getPixelAddress(destBuffer.Pixels, indexDest); + + _method.CopyPixels(pDest, pSrc, 1); + } + } + } + + } + +private: + T_BUFFER_METHOD _method; + + const uint16_t _spriteHeight; + const uint16_t _spriteCount; + + uint16_t pixelIndex(uint16_t indexSprite, + int16_t x, + int16_t y) const + { + uint16_t result = PixelIndex_OutOfBounds; + + if (indexSprite < _spriteCount && + x >= 0 && + static_cast(x) < SpriteWidth() && + y >= 0 && + static_cast(y) < SpriteHeight()) + { + result = x + y * SpriteWidth() + indexSprite * _spriteHeight * SpriteWidth(); + } + return result; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/HsbColor.cpp b/lib/NeoPixelBus/src/internal/colors/HsbColor.cpp new file mode 100644 index 00000000..b45f5113 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HsbColor.cpp @@ -0,0 +1,86 @@ +/*------------------------------------------------------------------------- +HsbColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "Rgb48Color.h" +#include "HsbColor.h" + +void HsbColor::_RgbToHsb(float r, float g, float b, HsbColor* color) +{ + float max = (r > g && r > b) ? r : (g > b) ? g : b; + float min = (r < g&& r < b) ? r : (g < b) ? g : b; + + float d = max - min; + + float h = 0.0; + float v = max; + float s = (v == 0.0f) ? 0 : (d / v); + + if (d != 0.0f) + { + if (r == max) + { + h = (g - b) / d + (g < b ? 6.0f : 0.0f); + } + else if (g == max) + { + h = (b - r) / d + 2.0f; + } + else + { + h = (r - g) / d + 4.0f; + } + h /= 6.0f; + } + + + color->H = h; + color->S = s; + color->B = v; +} + +HsbColor::HsbColor(const RgbColor& color) +{ + // convert colors to float between (0.0 - 1.0) + float r = color.R / 255.0f; + float g = color.G / 255.0f; + float b = color.B / 255.0f; + + _RgbToHsb(r, g, b, this); +} + +HsbColor::HsbColor(const Rgb48Color& color) +{ + // convert colors to float between (0.0 - 1.0) + float r = color.R / 65535.0f; + float g = color.G / 65535.0f; + float b = color.B / 65535.0f; + + _RgbToHsb(r, g, b, this); +} diff --git a/lib/NeoPixelBus/src/internal/colors/HsbColor.h b/lib/NeoPixelBus/src/internal/colors/HsbColor.h new file mode 100644 index 00000000..bf9466bd --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HsbColor.h @@ -0,0 +1,119 @@ + +/*------------------------------------------------------------------------- +HsbColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// ------------------------------------------------------------------------ +// HsbColor represents a color object that is represented by Hue, Saturation, Brightness +// component values. It contains helpful color routines to manipulate the +// color. +// ------------------------------------------------------------------------ +struct HsbColor +{ + // ------------------------------------------------------------------------ + // Construct a HsbColor using H, S, B values (0.0 - 1.0) + // ------------------------------------------------------------------------ + HsbColor(float h, float s, float b) : + H(h), S(s), B(b) + { + }; + + // ------------------------------------------------------------------------ + // Construct a HsbColor using RgbColor + // ------------------------------------------------------------------------ + HsbColor(const RgbColor& color); + + // ------------------------------------------------------------------------ + // Construct a HsbColor using Rgb48Color + // ------------------------------------------------------------------------ + HsbColor(const Rgb48Color& color); + + // ------------------------------------------------------------------------ + // Construct a HsbColor that will have its values set in latter operations + // CAUTION: The H,S,B members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + HsbColor() + { + }; + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + template static HsbColor LinearBlend(const HsbColor& left, + const HsbColor& right, + float progress) + { + return HsbColor(T_NEOHUEBLEND::HueBlend(left.H, right.H, progress), + left.S + ((right.S - left.S) * progress), + left.B + ((right.B - left.B) * progress)); + } + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + template static HsbColor BilinearBlend(const HsbColor& c00, + const HsbColor& c01, + const HsbColor& c10, + const HsbColor& c11, + float x, + float y) + { + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return HsbColor( + T_NEOHUEBLEND::HueBlend( + T_NEOHUEBLEND::HueBlend(c00.H, c10.H, x), + T_NEOHUEBLEND::HueBlend(c01.H, c11.H, x), + y), + c00.S * v00 + c10.S * v10 + c01.S * v01 + c11.S * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11); + }; + + // ------------------------------------------------------------------------ + // Hue, Saturation, Brightness color members + // ------------------------------------------------------------------------ + + float H; + float S; + float B; + +private: + static void _RgbToHsb(float r, float g, float b, HsbColor* color); +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/HslColor.cpp b/lib/NeoPixelBus/src/internal/colors/HslColor.cpp new file mode 100644 index 00000000..bd6c3f43 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HslColor.cpp @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- +HslColor provides a color object that can be directly consumed by NeoPixelBus + + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "Rgb48Color.h" +#include "HslColor.h" + +void HslColor::_RgbToHsl(float r, float g, float b, HslColor* color) +{ + float max = (r > g && r > b) ? r : (g > b) ? g : b; + float min = (r < g && r < b) ? r : (g < b) ? g : b; + + float h, s, l; + l = (max + min) / 2.0f; + + if (max == min) + { + h = s = 0.0f; + } + else + { + float d = max - min; + s = (l > 0.5f) ? d / (2.0f - (max + min)) : d / (max + min); + + if (r > g && r > b) + { + h = (g - b) / d + (g < b ? 6.0f : 0.0f); + } + else if (g > b) + { + h = (b - r) / d + 2.0f; + } + else + { + h = (r - g) / d + 4.0f; + } + h /= 6.0f; + } + + color->H = h; + color->S = s; + color->L = l; +} + +HslColor::HslColor(const RgbColor& color) +{ + // convert colors to float between (0.0 - 1.0) + float r = color.R / 255.0f; + float g = color.G / 255.0f; + float b = color.B / 255.0f; + + _RgbToHsl(r, g, b, this); +} + +HslColor::HslColor(const Rgb48Color& color) +{ + // convert colors to float between (0.0 - 1.0) + float r = color.R / 65535.0f; + float g = color.G / 65535.0f; + float b = color.B / 65535.0f; + + _RgbToHsl(r, g, b, this); +} diff --git a/lib/NeoPixelBus/src/internal/colors/HslColor.h b/lib/NeoPixelBus/src/internal/colors/HslColor.h new file mode 100644 index 00000000..44f9061e --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HslColor.h @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------- +HslColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// ------------------------------------------------------------------------ +// HslColor represents a color object that is represented by Hue, Saturation, Lightness +// component values. It contains helpful color routines to manipulate the +// color. +// ------------------------------------------------------------------------ +struct HslColor +{ + + // ------------------------------------------------------------------------ + // Construct a HslColor using H, S, L values (0.0 - 1.0) + // L should be limited to between (0.0 - 0.5) + // ------------------------------------------------------------------------ + HslColor(float h, float s, float l) : + H(h), S(s), L(l) + { + }; + + // ------------------------------------------------------------------------ + // Construct a HslColor using RgbColor + // ------------------------------------------------------------------------ + HslColor(const RgbColor& color); + + // ------------------------------------------------------------------------ + // Construct a HslColor using Rgb48Color + // ------------------------------------------------------------------------ + HslColor(const Rgb48Color& color); + + // ------------------------------------------------------------------------ + // Construct a HslColor that will have its values set in latter operations + // CAUTION: The H,S,L members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + HslColor() + { + }; + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + template static HslColor LinearBlend(const HslColor& left, + const HslColor& right, + float progress) + { + return HslColor(T_NEOHUEBLEND::HueBlend(left.H, right.H, progress), + left.S + ((right.S - left.S) * progress), + left.L + ((right.L - left.L) * progress)); + }; + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + template static HslColor BilinearBlend(const HslColor& c00, + const HslColor& c01, + const HslColor& c10, + const HslColor& c11, + float x, + float y) + { + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return HslColor( + T_NEOHUEBLEND::HueBlend( + T_NEOHUEBLEND::HueBlend(c00.H, c10.H, x), + T_NEOHUEBLEND::HueBlend(c01.H, c11.H, x), + y), + c00.S * v00 + c10.S * v10 + c01.S * v01 + c11.S * v11, + c00.L * v00 + c10.L * v10 + c01.L * v01 + c11.L * v11); + }; + + // ------------------------------------------------------------------------ + // Hue, Saturation, Lightness color members + // ------------------------------------------------------------------------ + float H; + float S; + float L; + +private: + static void _RgbToHsl(float r, float g, float b, HslColor* color); + +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/HtmlColor.cpp b/lib/NeoPixelBus/src/internal/colors/HtmlColor.cpp new file mode 100644 index 00000000..9e9af5f9 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HtmlColor.cpp @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- +This file contains the HtmlColor implementation + +Written by Unai Uribarri + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "HtmlColor.h" + +static inline char hexdigit(uint8_t v) +{ + return v + (v < 10 ? '0' : 'a' - 10); +} + + +size_t HtmlColor::ToNumericalString(char* buf, size_t bufSize) const +{ + size_t bufLen = bufSize - 1; + + if (bufLen-- > 0) + { + if (bufLen > 0) + { + buf[0] = '#'; + } + + uint32_t color = Color; + for (uint8_t indexDigit = 6; indexDigit > 0; indexDigit--) + { + if (bufLen > indexDigit) + { + buf[indexDigit] = hexdigit(color & 0x0000000f); + } + color >>= 4; + } + + buf[(bufLen < 7 ? bufLen : 7)] = 0; + } + return 7; +} diff --git a/lib/NeoPixelBus/src/internal/colors/HtmlColor.h b/lib/NeoPixelBus/src/internal/colors/HtmlColor.h new file mode 100644 index 00000000..3e63421e --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HtmlColor.h @@ -0,0 +1,333 @@ +/*------------------------------------------------------------------------- +HtmlColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#define MAX_HTML_COLOR_NAME_LEN 21 + +#ifndef pgm_read_ptr +// ESP8266 doesn't define this macro +#define pgm_read_ptr(addr) (*reinterpret_cast(addr)) +#endif + +// ------------------------------------------------------------------------ +// HtmlColorPair represents an association between a name and a HTML color code +// ------------------------------------------------------------------------ +struct HtmlColorPair +{ + PGM_P Name; + uint32_t Color; +}; + +// ------------------------------------------------------------------------ +// HtmlShortColorNames is a template class used for Parse and ToString +// ------------------------------------------------------------------------ +class HtmlShortColorNames +{ +public: + static const HtmlColorPair* Pair(uint8_t index); + static uint8_t Count(); +}; + +// ------------------------------------------------------------------------ +// HtmlColorNames is a template class used for Parse and ToString +// ------------------------------------------------------------------------ +class HtmlColorNames +{ +public: + static const HtmlColorPair* Pair(uint8_t index); + static uint8_t Count(); +}; + +// ------------------------------------------------------------------------ +// HtmlColor represents a color object that is represented by a single uint32 +// value. It contains minimal routines and used primarily as a helper to +// initialize other color objects +// ------------------------------------------------------------------------ +struct HtmlColor +{ + // ------------------------------------------------------------------------ + // Construct a HtmlColor using a single value (0-0xffffff) + // This works well for hexidecimal color definitions + // 0xff0000 = red, 0x00ff00 = green, and 0x0000ff = blue + // ------------------------------------------------------------------------ + HtmlColor(uint32_t color) : + Color(color) + { + }; + + // ------------------------------------------------------------------------ + // Construct a HtmlColor using RgbColor + // ------------------------------------------------------------------------ + HtmlColor(const RgbColor& color) + { + Color = static_cast(color.R) << 16 | static_cast(color.G) << 8 | static_cast(color.B); + } + + // ------------------------------------------------------------------------ + // Construct a HtmlColor that will have its values set in latter operations + // CAUTION: The Color member is not initialized and may not be consistent + // ------------------------------------------------------------------------ + HtmlColor() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const HtmlColor& other) const + { + return (Color == other.Color); + }; + + bool operator!=(const HtmlColor& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // Parse a HTML4/CSS3 color name + // T_HTMLCOLORNAMES - template class that defines the collection of names + // name - the color name + // nameSize - the max size of name to check + // + // returns - zero if failed, or the number of chars parsed + // + // It will stop parsing name when a null terminator is reached, + // nameSize is reached, no match is found in the name/color pair table, or + // a non-alphanumeric is read like seperators or whitespace. + // + // It also accepts 3 or 6 digit hexadecimal notation (#rgb or #rrggbb), + // but it doesn't accept RGB, RGBA nor HSL values. + // + // See https://www.w3.org/TR/css3-color/#SRGB + // + // name must point to the first non-whitespace character to be parsed + // parsing will stop at the first non-alpha numeric + // + // Name MUST NOT be a PROGMEM pointer + // + // examples: + // Parse(buff, buffSize); + // Parse(buff, buffSize); + // ------------------------------------------------------------------------ + + template size_t Parse(const char* name, size_t nameSize) + { + if (nameSize > 0) + { + if (name[0] == '#') + { + // Parse an hexadecimal notation "#rrbbgg" or "#rgb" + // + uint8_t temp[6]; // stores preconverted chars to hex values + uint8_t tempSize = 0; + + for (uint8_t indexChar = 1; indexChar < nameSize && indexChar < 8; ++indexChar) + { + char c = name[indexChar]; + if (c >= '0' && c <= '9') + { + c -= '0'; + } + else + { + // Convert a letter to lower case (only for ASCII letters) + // It's faster & smaller than tolower() + c |= 32; + if (c >= 'a' && c <= 'f') + { + c = c - 'a' + 10; + } + else + { + // we found an non hexidecimal char + // which could be null, deliminator, or other spacing + break; + } + } + + temp[tempSize] = c; + tempSize++; + } + + if (tempSize != 3 && tempSize != 6) + { + // invalid count of numerical chars + return 0; + } + else + { + uint32_t color = 0; + for (uint8_t indexChar = 0; indexChar < tempSize; ++indexChar) + { + color = color * 16 + temp[indexChar]; + if (tempSize == 3) + { + // 3 digit hexadecimal notation can be supported easily + // duplicating digits. + color = color * 16 + temp[indexChar]; + } + } + + Color = color; + return tempSize; + } + } + else + { + // parse a standard name for the color + // + + // the normal list is small enough a binary search isn't interesting, + for (uint8_t indexName = 0; indexName < T_HTMLCOLORNAMES::Count(); ++indexName) + { + const HtmlColorPair* colorPair = T_HTMLCOLORNAMES::Pair(indexName); + PGM_P searchName = reinterpret_cast(pgm_read_ptr(&(colorPair->Name))); + size_t str1Size = nameSize; + const char* str1 = name; + const char* str2P = searchName; + + uint16_t result; + + while (str1Size > 0) + { + char ch1 = tolower(*str1++); + char ch2 = tolower(pgm_read_byte(str2P++)); + result = ch1 - ch2; + if (result != 0 || ch2 == '\0') + { + if (ch2 == '\0' && !isalnum(ch1)) + { + // the string continues but is not part of a + // valid color name, + // ends in white space, deliminator, etc + result = 0; + } + break; + } + result = -1; // have not reached the end of searchName; + str1Size--; + } + + if (result == 0) + { + Color = pgm_read_dword(&colorPair->Color); + return nameSize - str1Size; + } + } + } + } + + return 0; + } + + template size_t Parse(const char* name) + { + return Parse(name, MAX_HTML_COLOR_NAME_LEN + 1); + } + + template size_t Parse(String const &name) + { + return Parse(name.c_str(), name.length() + 1); + } + + // ------------------------------------------------------------------------ + // Converts this color code to its HTML4/CSS3 name + // T_HTMLCOLORNAMES - template class that defines the collection of names + // buf - buffer to write the string + // bufSize - actual size of buf array + // + // It returns the number of chars required not including the NUL terminator. + // + // If there is not enough space in the buffer, it will write as many + // characters as allowed and will always finish the buffer with a NUL char + // + // examples: + // ToString(buf, bufSize); + // ToString(buf, bufSize); + // ------------------------------------------------------------------------ + + template size_t ToString(char* buf, size_t bufSize) const + { + // search for a color value/name pairs first + for (uint8_t indexName = 0; indexName < T_HTMLCOLORNAMES::Count(); ++indexName) + { + const HtmlColorPair* colorPair = T_HTMLCOLORNAMES::Pair(indexName); + if (pgm_read_dword(&colorPair->Color) == Color) + { + PGM_P name = (PGM_P)pgm_read_ptr(&colorPair->Name); + strncpy_P(buf, name, bufSize); + return strlen_P(name); + } + } + + // no color name pair match, convert using numerical format + return ToNumericalString(buf, bufSize); + } + + // ------------------------------------------------------------------------ + // Converts this color code to its HTML4/CSS3 numerical name + // + // buf - buffer to write the string + // bufSize - actual size of buf array + // + // It returns the number of chars required not including the NUL terminator. + // + // If there is not enough space in the buffer, it will write as many + // characters as allowed and will always finish the buffer with a NUL char + // ------------------------------------------------------------------------ + + size_t ToNumericalString(char* buf, size_t bufSize) const; + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + + static HtmlColor BilinearBlend(const HtmlColor& c00, + const HtmlColor& c01, + const HtmlColor& c10, + const HtmlColor& c11, + float x, + float y) + { + return RgbColor::BilinearBlend(c00, c01, c10, c11, x, y); + } + + // ------------------------------------------------------------------------ + // Color member (0-0xffffff) where + // 0xff0000 is red + // 0x00ff00 is green + // 0x0000ff is blue + // ------------------------------------------------------------------------ + uint32_t Color; +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/HtmlColorNameStrings.cpp b/lib/NeoPixelBus/src/internal/colors/HtmlColorNameStrings.cpp new file mode 100644 index 00000000..6c71b33c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HtmlColorNameStrings.cpp @@ -0,0 +1,179 @@ +/*------------------------------------------------------------------------- +HtmlColorNameStrings provides the implemenation of the color string names + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "HtmlColorNameStrings.h" + +/* HTML4 color names */ +const char c_HtmlNameAqua[] PROGMEM = "aqua"; +const char c_HtmlNameBlack[] PROGMEM = "black"; +const char c_HtmlNameBlue[] PROGMEM = "blue"; +const char c_HtmlNameFuchsia[] PROGMEM = "fuchsia"; +const char c_HtmlNameGray[] PROGMEM = "gray"; +const char c_HtmlNameGreen[] PROGMEM = "green"; +const char c_HtmlNameLime[] PROGMEM = "lime"; +const char c_HtmlNameMaroon[] PROGMEM = "maroon"; +const char c_HtmlNameNavy[] PROGMEM = "navy"; +const char c_HtmlNameOlive[] PROGMEM = "olive"; +const char c_HtmlNameOrange[] PROGMEM = "orange"; +const char c_HtmlNamePurple[] PROGMEM = "purple"; +const char c_HtmlNameRed[] PROGMEM = "red"; +const char c_HtmlNameSilver[] PROGMEM = "silver"; +const char c_HtmlNameTeal[] PROGMEM = "teal"; +const char c_HtmlNameWhite[] PROGMEM = "white"; +const char c_HtmlNameYellow[] PROGMEM = "yellow"; + +/* Additional CSS3 color names */ +const char c_HtmlNameAliceBlue[] PROGMEM = "aliceblue"; +const char c_HtmlNameAntiqueWhite[] PROGMEM = "antiquewhite"; +const char c_HtmlNameAquamarine[] PROGMEM = "aquamarine"; +const char c_HtmlNameAzure[] PROGMEM = "azure"; +const char c_HtmlNameBeige[] PROGMEM = "beige"; +const char c_HtmlNameBisque[] PROGMEM = "bisque"; +const char c_HtmlNameBlanchedAlmond[] PROGMEM = "blanchedalmond"; +const char c_HtmlNameBlueViolet[] PROGMEM = "blueviolet"; +const char c_HtmlNameBrown[] PROGMEM = "brown"; +const char c_HtmlNameBurlyWood[] PROGMEM = "burlywood"; +const char c_HtmlNameCadetBlue[] PROGMEM = "cadetblue"; +const char c_HtmlNameChartreuse[] PROGMEM = "chartreuse"; +const char c_HtmlNameChocolate[] PROGMEM = "chocolate"; +const char c_HtmlNameCoral[] PROGMEM = "coral"; +const char c_HtmlNameCornflowerBlue[] PROGMEM = "cornflowerblue"; +const char c_HtmlNameCornsilk[] PROGMEM = "cornsilk"; +const char c_HtmlNameCrimson[] PROGMEM = "crimson"; +const char c_HtmlNameCyan[] PROGMEM = "cyan"; +const char c_HtmlNameDarkBlue[] PROGMEM = "darkblue"; +const char c_HtmlNameDarkCyan[] PROGMEM = "darkcyan"; +const char c_HtmlNameDarkGoldenrod[] PROGMEM = "darkgoldenrod"; +const char c_HtmlNameDarkGray[] PROGMEM = "darkgray"; +const char c_HtmlNameDarkGreen[] PROGMEM = "darkgreen"; +const char c_HtmlNameDarkGrey[] PROGMEM = "darkgrey"; +const char c_HtmlNameDarkKhaki[] PROGMEM = "darkkhaki"; +const char c_HtmlNameDarkMagenta[] PROGMEM = "darkmagenta"; +const char c_HtmlNameDarkOliveGreen[] PROGMEM = "darkolivegreen"; +const char c_HtmlNameDarkOrange[] PROGMEM = "darkorange"; +const char c_HtmlNameDarkOrchid[] PROGMEM = "darkorchid"; +const char c_HtmlNameDarkRed[] PROGMEM = "darkred"; +const char c_HtmlNameDarkSalmon[] PROGMEM = "darksalmon"; +const char c_HtmlNameDarkSeaGreen[] PROGMEM = "darkseagreen"; +const char c_HtmlNameDarkSlateBlue[] PROGMEM = "darkslateblue"; +const char c_HtmlNameDarkSlateGray[] PROGMEM = "darkslategray"; +const char c_HtmlNameDarkSlateGrey[] PROGMEM = "darkslategrey"; +const char c_HtmlNameDarkTurquoise[] PROGMEM = "darkturquoise"; +const char c_HtmlNameDarkViolet[] PROGMEM = "darkviolet"; +const char c_HtmlNameDeepPink[] PROGMEM = "deeppink"; +const char c_HtmlNameDeepSkyBlue[] PROGMEM = "deepskyblue"; +const char c_HtmlNameDimGray[] PROGMEM = "dimgray"; +const char c_HtmlNameDimGrey[] PROGMEM = "dimgrey"; +const char c_HtmlNameDodgerBlue[] PROGMEM = "dodgerblue"; +const char c_HtmlNameFirebrick[] PROGMEM = "firebrick"; +const char c_HtmlNameFloralWhite[] PROGMEM = "floralwhite"; +const char c_HtmlNameForestGreen[] PROGMEM = "forestgreen"; +const char c_HtmlNameGainsboro[] PROGMEM = "gainsboro"; +const char c_HtmlNameGhostWhite[] PROGMEM = "ghostwhite"; +const char c_HtmlNameGold[] PROGMEM = "gold"; +const char c_HtmlNameGoldenrod[] PROGMEM = "goldenrod"; +const char c_HtmlNameGreenYellow[] PROGMEM = "greenyellow"; +const char c_HtmlNameGrey[] PROGMEM = "grey"; +const char c_HtmlNameHoneydew[] PROGMEM = "honeydew"; +const char c_HtmlNameHotPink[] PROGMEM = "hotpink"; +const char c_HtmlNameIndianRed[] PROGMEM = "indianred"; +const char c_HtmlNameIndigo[] PROGMEM = "indigo"; +const char c_HtmlNameIvory[] PROGMEM = "ivory"; +const char c_HtmlNameKhaki[] PROGMEM = "khaki"; +const char c_HtmlNameLavender[] PROGMEM = "lavender"; +const char c_HtmlNameLavenderBlush[] PROGMEM = "lavenderblush"; +const char c_HtmlNameLawnGreen[] PROGMEM = "lawngreen"; +const char c_HtmlNameLemonChiffon[] PROGMEM = "lemonchiffon"; +const char c_HtmlNameLightBlue[] PROGMEM = "lightblue"; +const char c_HtmlNameLightCoral[] PROGMEM = "lightcoral"; +const char c_HtmlNameLightCyan[] PROGMEM = "lightcyan"; +const char c_HtmlNameLightGoldenrodYellow[] PROGMEM = "lightgoldenrodyellow"; +const char c_HtmlNameLightGray[] PROGMEM = "lightgray"; +const char c_HtmlNameLightGreen[] PROGMEM = "lightgreen"; +const char c_HtmlNameLightGrey[] PROGMEM = "lightgrey"; +const char c_HtmlNameLightPink[] PROGMEM = "lightpink"; +const char c_HtmlNameLightSalmon[] PROGMEM = "lightsalmon"; +const char c_HtmlNameLightSeaGreen[] PROGMEM = "lightseagreen"; +const char c_HtmlNameLightSkyBlue[] PROGMEM = "lightskyblue"; +const char c_HtmlNameLightSlateGray[] PROGMEM = "lightslategray"; +const char c_HtmlNameLightSlateGrey[] PROGMEM = "lightslategrey"; +const char c_HtmlNameLightSteelBlue[] PROGMEM = "lightsteelblue"; +const char c_HtmlNameLightYellow[] PROGMEM = "lightyellow"; +const char c_HtmlNameLimeGreen[] PROGMEM = "limegreen"; +const char c_HtmlNameLinen[] PROGMEM = "linen"; +const char c_HtmlNameMagenta[] PROGMEM = "magenta"; +const char c_HtmlNameMediumAquamarine[] PROGMEM = "mediumaquamarine"; +const char c_HtmlNameMediumBlue[] PROGMEM = "mediumblue"; +const char c_HtmlNameMediumOrchid[] PROGMEM = "mediumorchid"; +const char c_HtmlNameMediumPurple[] PROGMEM = "mediumpurple"; +const char c_HtmlNameMediumSeagreen[] PROGMEM = "mediumseagreen"; +const char c_HtmlNameMediumSlateBlue[] PROGMEM = "mediumslateblue"; +const char c_HtmlNameMediumSpringGreen[] PROGMEM = "mediumspringgreen"; +const char c_HtmlNameMediumTurquoise[] PROGMEM = "mediumturquoise"; +const char c_HtmlNameMediumVioletRed[] PROGMEM = "mediumvioletred"; +const char c_HtmlNameMidnightBlue[] PROGMEM = "midnightblue"; +const char c_HtmlNameMintCream[] PROGMEM = "mintcream"; +const char c_HtmlNameMistyRose[] PROGMEM = "mistyrose"; +const char c_HtmlNameMoccasin[] PROGMEM = "moccasin"; +const char c_HtmlNameNavajoWhite[] PROGMEM = "navajowhite"; +const char c_HtmlNameOldLace[] PROGMEM = "oldlace"; +const char c_HtmlNameOliveDrab[] PROGMEM = "olivedrab"; +const char c_HtmlNameOrangeRed[] PROGMEM = "orangered"; +const char c_HtmlNameOrchid[] PROGMEM = "orchid"; +const char c_HtmlNamePaleGoldenrod[] PROGMEM = "palegoldenrod"; +const char c_HtmlNamePaleGreen[] PROGMEM = "palegreen"; +const char c_HtmlNamePaleTurquoise[] PROGMEM = "paleturquoise"; +const char c_HtmlNamePaleVioletRed[] PROGMEM = "palevioletred"; +const char c_HtmlNamePapayaWhip[] PROGMEM = "papayawhip"; +const char c_HtmlNamePeachPuff[] PROGMEM = "peachpuff"; +const char c_HtmlNamePeru[] PROGMEM = "peru"; +const char c_HtmlNamePink[] PROGMEM = "pink"; +const char c_HtmlNamePlum[] PROGMEM = "plum"; +const char c_HtmlNamePowderBlue[] PROGMEM = "powderblue"; +const char c_HtmlNameRosyBrown[] PROGMEM = "rosybrown"; +const char c_HtmlNameRoyalBlue[] PROGMEM = "royalblue"; +const char c_HtmlNameSaddleBrown[] PROGMEM = "saddlebrown"; +const char c_HtmlNameSalmon[] PROGMEM = "salmon"; +const char c_HtmlNameSandyBrown[] PROGMEM = "sandybrown"; +const char c_HtmlNameSeaGreen[] PROGMEM = "seagreen"; +const char c_HtmlNameSeaShell[] PROGMEM = "seashell"; +const char c_HtmlNameSienna[] PROGMEM = "sienna"; +const char c_HtmlNameSkyBlue[] PROGMEM = "skyblue"; +const char c_HtmlNameSlateBlue[] PROGMEM = "slateblue"; +const char c_HtmlNameSlateGray[] PROGMEM = "slategray"; +const char c_HtmlNameSlateGrey[] PROGMEM = "slategrey"; +const char c_HtmlNameSnow[] PROGMEM = "snow"; +const char c_HtmlNameSpringGreen[] PROGMEM = "springgreen"; +const char c_HtmlNameSteelBlue[] PROGMEM = "steelblue"; +const char c_HtmlNameTan[] PROGMEM = "tan"; +const char c_HtmlNameThistle[] PROGMEM = "thistle"; +const char c_HtmlNameTomato[] PROGMEM = "tomato"; +const char c_HtmlNameTurquoise[] PROGMEM = "turquoise"; +const char c_HtmlNameViolet[] PROGMEM = "violet"; +const char c_HtmlNameWheat[] PROGMEM = "wheat"; +const char c_HtmlNameWhiteSmoke[] PROGMEM = "whitesmoke"; +const char c_HtmlNameYellowGreen[] PROGMEM = "yellowgreen"; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/HtmlColorNameStrings.h b/lib/NeoPixelBus/src/internal/colors/HtmlColorNameStrings.h new file mode 100644 index 00000000..dcd30c62 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HtmlColorNameStrings.h @@ -0,0 +1,178 @@ +/*------------------------------------------------------------------------- +HtmlColorNameStrings provides the declaration of the color string names + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +/* HTML4 color names */ +extern const char c_HtmlNameAqua[] PROGMEM; +extern const char c_HtmlNameBlack[] PROGMEM; +extern const char c_HtmlNameBlue[] PROGMEM; +extern const char c_HtmlNameFuchsia[] PROGMEM; +extern const char c_HtmlNameGray[] PROGMEM; +extern const char c_HtmlNameGreen[] PROGMEM; +extern const char c_HtmlNameLime[] PROGMEM; +extern const char c_HtmlNameMaroon[] PROGMEM; +extern const char c_HtmlNameNavy[] PROGMEM; +extern const char c_HtmlNameOlive[] PROGMEM; +extern const char c_HtmlNameOrange[] PROGMEM; +extern const char c_HtmlNamePurple[] PROGMEM; +extern const char c_HtmlNameRed[] PROGMEM; +extern const char c_HtmlNameSilver[] PROGMEM; +extern const char c_HtmlNameTeal[] PROGMEM; +extern const char c_HtmlNameWhite[] PROGMEM; +extern const char c_HtmlNameYellow[] PROGMEM; + +/* Additional CSS3 color names */ +extern const char c_HtmlNameAliceBlue[] PROGMEM; +extern const char c_HtmlNameAntiqueWhite[] PROGMEM; +extern const char c_HtmlNameAquamarine[] PROGMEM; +extern const char c_HtmlNameAzure[] PROGMEM; +extern const char c_HtmlNameBeige[] PROGMEM; +extern const char c_HtmlNameBisque[] PROGMEM; +extern const char c_HtmlNameBlanchedAlmond[] PROGMEM; +extern const char c_HtmlNameBlueViolet[] PROGMEM; +extern const char c_HtmlNameBrown[] PROGMEM; +extern const char c_HtmlNameBurlyWood[] PROGMEM; +extern const char c_HtmlNameCadetBlue[] PROGMEM; +extern const char c_HtmlNameChartreuse[] PROGMEM; +extern const char c_HtmlNameChocolate[] PROGMEM; +extern const char c_HtmlNameCoral[] PROGMEM; +extern const char c_HtmlNameCornflowerBlue[] PROGMEM; +extern const char c_HtmlNameCornsilk[] PROGMEM; +extern const char c_HtmlNameCrimson[] PROGMEM; +extern const char c_HtmlNameCyan[] PROGMEM; +extern const char c_HtmlNameDarkBlue[] PROGMEM; +extern const char c_HtmlNameDarkCyan[] PROGMEM; +extern const char c_HtmlNameDarkGoldenrod[] PROGMEM; +extern const char c_HtmlNameDarkGray[] PROGMEM; +extern const char c_HtmlNameDarkGreen[] PROGMEM; +extern const char c_HtmlNameDarkGrey[] PROGMEM; +extern const char c_HtmlNameDarkKhaki[] PROGMEM; +extern const char c_HtmlNameDarkMagenta[] PROGMEM; +extern const char c_HtmlNameDarkOliveGreen[] PROGMEM; +extern const char c_HtmlNameDarkOrange[] PROGMEM; +extern const char c_HtmlNameDarkOrchid[] PROGMEM; +extern const char c_HtmlNameDarkRed[] PROGMEM; +extern const char c_HtmlNameDarkSalmon[] PROGMEM; +extern const char c_HtmlNameDarkSeaGreen[] PROGMEM; +extern const char c_HtmlNameDarkSlateBlue[] PROGMEM; +extern const char c_HtmlNameDarkSlateGray[] PROGMEM; +extern const char c_HtmlNameDarkSlateGrey[] PROGMEM; +extern const char c_HtmlNameDarkTurquoise[] PROGMEM; +extern const char c_HtmlNameDarkViolet[] PROGMEM; +extern const char c_HtmlNameDeepPink[] PROGMEM; +extern const char c_HtmlNameDeepSkyBlue[] PROGMEM; +extern const char c_HtmlNameDimGray[] PROGMEM; +extern const char c_HtmlNameDimGrey[] PROGMEM; +extern const char c_HtmlNameDodgerBlue[] PROGMEM; +extern const char c_HtmlNameFirebrick[] PROGMEM; +extern const char c_HtmlNameFloralWhite[] PROGMEM; +extern const char c_HtmlNameForestGreen[] PROGMEM; +extern const char c_HtmlNameGainsboro[] PROGMEM; +extern const char c_HtmlNameGhostWhite[] PROGMEM; +extern const char c_HtmlNameGold[] PROGMEM; +extern const char c_HtmlNameGoldenrod[] PROGMEM; +extern const char c_HtmlNameGreenYellow[] PROGMEM; +extern const char c_HtmlNameGrey[] PROGMEM; +extern const char c_HtmlNameHoneydew[] PROGMEM; +extern const char c_HtmlNameHotPink[] PROGMEM; +extern const char c_HtmlNameIndianRed[] PROGMEM; +extern const char c_HtmlNameIndigo[] PROGMEM; +extern const char c_HtmlNameIvory[] PROGMEM; +extern const char c_HtmlNameKhaki[] PROGMEM; +extern const char c_HtmlNameLavender[] PROGMEM; +extern const char c_HtmlNameLavenderBlush[] PROGMEM; +extern const char c_HtmlNameLawnGreen[] PROGMEM; +extern const char c_HtmlNameLemonChiffon[] PROGMEM; +extern const char c_HtmlNameLightBlue[] PROGMEM; +extern const char c_HtmlNameLightCoral[] PROGMEM; +extern const char c_HtmlNameLightCyan[] PROGMEM; +extern const char c_HtmlNameLightGoldenrodYellow[] PROGMEM; +extern const char c_HtmlNameLightGray[] PROGMEM; +extern const char c_HtmlNameLightGreen[] PROGMEM; +extern const char c_HtmlNameLightGrey[] PROGMEM; +extern const char c_HtmlNameLightPink[] PROGMEM; +extern const char c_HtmlNameLightSalmon[] PROGMEM; +extern const char c_HtmlNameLightSeaGreen[] PROGMEM; +extern const char c_HtmlNameLightSkyBlue[] PROGMEM; +extern const char c_HtmlNameLightSlateGray[] PROGMEM; +extern const char c_HtmlNameLightSlateGrey[] PROGMEM; +extern const char c_HtmlNameLightSteelBlue[] PROGMEM; +extern const char c_HtmlNameLightYellow[] PROGMEM; +extern const char c_HtmlNameLimeGreen[] PROGMEM; +extern const char c_HtmlNameLinen[] PROGMEM; +extern const char c_HtmlNameMagenta[] PROGMEM; +extern const char c_HtmlNameMediumAquamarine[] PROGMEM; +extern const char c_HtmlNameMediumBlue[] PROGMEM; +extern const char c_HtmlNameMediumOrchid[] PROGMEM; +extern const char c_HtmlNameMediumPurple[] PROGMEM; +extern const char c_HtmlNameMediumSeagreen[] PROGMEM; +extern const char c_HtmlNameMediumSlateBlue[] PROGMEM; +extern const char c_HtmlNameMediumSpringGreen[] PROGMEM; +extern const char c_HtmlNameMediumTurquoise[] PROGMEM; +extern const char c_HtmlNameMediumVioletRed[] PROGMEM; +extern const char c_HtmlNameMidnightBlue[] PROGMEM; +extern const char c_HtmlNameMintCream[] PROGMEM; +extern const char c_HtmlNameMistyRose[] PROGMEM; +extern const char c_HtmlNameMoccasin[] PROGMEM; +extern const char c_HtmlNameNavajoWhite[] PROGMEM; +extern const char c_HtmlNameOldLace[] PROGMEM; +extern const char c_HtmlNameOliveDrab[] PROGMEM; +extern const char c_HtmlNameOrangeRed[] PROGMEM; +extern const char c_HtmlNameOrchid[] PROGMEM; +extern const char c_HtmlNamePaleGoldenrod[] PROGMEM; +extern const char c_HtmlNamePaleGreen[] PROGMEM; +extern const char c_HtmlNamePaleTurquoise[] PROGMEM; +extern const char c_HtmlNamePaleVioletRed[] PROGMEM; +extern const char c_HtmlNamePapayaWhip[] PROGMEM; +extern const char c_HtmlNamePeachPuff[] PROGMEM; +extern const char c_HtmlNamePeru[] PROGMEM; +extern const char c_HtmlNamePink[] PROGMEM; +extern const char c_HtmlNamePlum[] PROGMEM; +extern const char c_HtmlNamePowderBlue[] PROGMEM; +extern const char c_HtmlNameRosyBrown[] PROGMEM; +extern const char c_HtmlNameRoyalBlue[] PROGMEM; +extern const char c_HtmlNameSaddleBrown[] PROGMEM; +extern const char c_HtmlNameSalmon[] PROGMEM; +extern const char c_HtmlNameSandyBrown[] PROGMEM; +extern const char c_HtmlNameSeaGreen[] PROGMEM; +extern const char c_HtmlNameSeaShell[] PROGMEM; +extern const char c_HtmlNameSienna[] PROGMEM; +extern const char c_HtmlNameSkyBlue[] PROGMEM; +extern const char c_HtmlNameSlateBlue[] PROGMEM; +extern const char c_HtmlNameSlateGray[] PROGMEM; +extern const char c_HtmlNameSlateGrey[] PROGMEM; +extern const char c_HtmlNameSnow[] PROGMEM; +extern const char c_HtmlNameSpringGreen[] PROGMEM; +extern const char c_HtmlNameSteelBlue[] PROGMEM; +extern const char c_HtmlNameTan[] PROGMEM; +extern const char c_HtmlNameThistle[] PROGMEM; +extern const char c_HtmlNameTomato[] PROGMEM; +extern const char c_HtmlNameTurquoise[] PROGMEM; +extern const char c_HtmlNameViolet[] PROGMEM; +extern const char c_HtmlNameWheat[] PROGMEM; +extern const char c_HtmlNameWhiteSmoke[] PROGMEM; +extern const char c_HtmlNameYellowGreen[] PROGMEM; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/HtmlColorNames.cpp b/lib/NeoPixelBus/src/internal/colors/HtmlColorNames.cpp new file mode 100644 index 00000000..ce6fd630 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HtmlColorNames.cpp @@ -0,0 +1,194 @@ +/*------------------------------------------------------------------------- +HtmlColorNames provides a template class for access to the full name table + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + + +#include +#include "../NeoUtil.h" +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "HtmlColor.h" +#include "HtmlColorNameStrings.h" + +static const HtmlColorPair c_ColorNames[] PROGMEM = { + { c_HtmlNameAliceBlue, 0xf0f8ff }, + { c_HtmlNameAntiqueWhite, 0xfaebd7 }, + { c_HtmlNameAqua, 0xffff }, + { c_HtmlNameAquamarine, 0x7fffd4 }, + { c_HtmlNameAzure, 0xf0ffff }, + { c_HtmlNameBeige, 0xf5f5dc }, + { c_HtmlNameBisque, 0xffe4c4 }, + { c_HtmlNameBlack, 0x0 }, + { c_HtmlNameBlanchedAlmond, 0xffebcd }, + { c_HtmlNameBlue, 0xff }, + { c_HtmlNameBlueViolet, 0x8a2be2 }, + { c_HtmlNameBrown, 0xa52a2a }, + { c_HtmlNameBurlyWood, 0xdeb887 }, + { c_HtmlNameCadetBlue, 0x5f9ea0 }, + { c_HtmlNameChartreuse, 0x7fff00 }, + { c_HtmlNameChocolate, 0xd2691e }, + { c_HtmlNameCoral, 0xff7f50 }, + { c_HtmlNameCornflowerBlue, 0x6495ed }, + { c_HtmlNameCornsilk, 0xfff8dc }, + { c_HtmlNameCrimson, 0xdc143c }, + { c_HtmlNameCyan, 0xffff }, + { c_HtmlNameDarkBlue, 0x8b }, + { c_HtmlNameDarkCyan, 0x8b8b }, + { c_HtmlNameDarkGoldenrod, 0xb8860b }, + { c_HtmlNameDarkGray, 0xa9a9a9 }, + { c_HtmlNameDarkGreen, 0x6400 }, + { c_HtmlNameDarkGrey, 0xa9a9a9 }, + { c_HtmlNameDarkKhaki, 0xbdb76b }, + { c_HtmlNameDarkMagenta, 0x8b008b }, + { c_HtmlNameDarkOliveGreen, 0x556b2f }, + { c_HtmlNameDarkOrange, 0xff8c00 }, + { c_HtmlNameDarkOrchid, 0x9932cc }, + { c_HtmlNameDarkRed, 0x8b0000 }, + { c_HtmlNameDarkSalmon, 0xe9967a }, + { c_HtmlNameDarkSeaGreen, 0x8fbc8f }, + { c_HtmlNameDarkSlateBlue, 0x483d8b }, + { c_HtmlNameDarkSlateGray, 0x2f4f4f }, + { c_HtmlNameDarkSlateGrey, 0x2f4f4f }, + { c_HtmlNameDarkTurquoise, 0xced1 }, + { c_HtmlNameDarkViolet, 0x9400d3 }, + { c_HtmlNameDeepPink, 0xff1493 }, + { c_HtmlNameDeepSkyBlue, 0xbfff }, + { c_HtmlNameDimGray, 0x696969 }, + { c_HtmlNameDimGrey, 0x696969 }, + { c_HtmlNameDodgerBlue, 0x1e90ff }, + { c_HtmlNameFirebrick, 0xb22222 }, + { c_HtmlNameFloralWhite, 0xfffaf0 }, + { c_HtmlNameForestGreen, 0x228b22 }, + { c_HtmlNameFuchsia, 0xff00ff }, + { c_HtmlNameGainsboro, 0xdcdcdc }, + { c_HtmlNameGhostWhite, 0xf8f8ff }, + { c_HtmlNameGold, 0xffd700 }, + { c_HtmlNameGoldenrod, 0xdaa520 }, + { c_HtmlNameGray, 0x808080 }, + { c_HtmlNameGreen, 0x8000 }, + { c_HtmlNameGreenYellow, 0xadff2f }, + { c_HtmlNameGrey, 0x808080 }, + { c_HtmlNameHoneydew, 0xf0fff0 }, + { c_HtmlNameHotPink, 0xff69b4 }, + { c_HtmlNameIndianRed, 0xcd5c5c }, + { c_HtmlNameIndigo, 0x4b0082 }, + { c_HtmlNameIvory, 0xfffff0 }, + { c_HtmlNameKhaki, 0xf0e68c }, + { c_HtmlNameLavender, 0xe6e6fa }, + { c_HtmlNameLavenderBlush, 0xfff0f5 }, + { c_HtmlNameLawnGreen, 0x7cfc00 }, + { c_HtmlNameLemonChiffon, 0xfffacd }, + { c_HtmlNameLightBlue, 0xadd8e6 }, + { c_HtmlNameLightCoral, 0xf08080 }, + { c_HtmlNameLightCyan, 0xe0ffff }, + { c_HtmlNameLightGoldenrodYellow, 0xfafad2 }, + { c_HtmlNameLightGray, 0xd3d3d3 }, + { c_HtmlNameLightGreen, 0x90ee90 }, + { c_HtmlNameLightGrey, 0xd3d3d3 }, + { c_HtmlNameLightPink, 0xffb6c1 }, + { c_HtmlNameLightSalmon, 0xffa07a }, + { c_HtmlNameLightSeaGreen, 0x20b2aa }, + { c_HtmlNameLightSkyBlue, 0x87cefa }, + { c_HtmlNameLightSlateGray, 0x778899 }, + { c_HtmlNameLightSlateGrey, 0x778899 }, + { c_HtmlNameLightSteelBlue, 0xb0c4de }, + { c_HtmlNameLightYellow, 0xffffe0 }, + { c_HtmlNameLime, 0xff00 }, + { c_HtmlNameLimeGreen, 0x32cd32 }, + { c_HtmlNameLinen, 0xfaf0e6 }, + { c_HtmlNameMagenta, 0xff00ff }, + { c_HtmlNameMaroon, 0x800000 }, + { c_HtmlNameMediumAquamarine, 0x66cdaa }, + { c_HtmlNameMediumBlue, 0xcd }, + { c_HtmlNameMediumOrchid, 0xba55d3 }, + { c_HtmlNameMediumPurple, 0x9370d8 }, + { c_HtmlNameMediumSeagreen, 0x3cb371 }, + { c_HtmlNameMediumSlateBlue, 0x7b68ee }, + { c_HtmlNameMediumSpringGreen, 0xfa9a }, + { c_HtmlNameMediumTurquoise, 0x48d1cc }, + { c_HtmlNameMediumVioletRed, 0xc71585 }, + { c_HtmlNameMidnightBlue, 0x191970 }, + { c_HtmlNameMintCream, 0xf5fffa }, + { c_HtmlNameMistyRose, 0xffe4e1 }, + { c_HtmlNameMoccasin, 0xffe4b5 }, + { c_HtmlNameNavajoWhite, 0xffdead }, + { c_HtmlNameNavy, 0x80 }, + { c_HtmlNameOldLace, 0xfdf5e6 }, + { c_HtmlNameOlive, 0x808000 }, + { c_HtmlNameOliveDrab, 0x6b8e23 }, + { c_HtmlNameOrange, 0xffa500 }, + { c_HtmlNameOrangeRed, 0xff4500 }, + { c_HtmlNameOrchid, 0xda70d6 }, + { c_HtmlNamePaleGoldenrod, 0xeee8aa }, + { c_HtmlNamePaleGreen, 0x98fb98 }, + { c_HtmlNamePaleTurquoise, 0xafeeee }, + { c_HtmlNamePaleVioletRed, 0xd87093 }, + { c_HtmlNamePapayaWhip, 0xffefd5 }, + { c_HtmlNamePeachPuff, 0xffdab9 }, + { c_HtmlNamePeru, 0xcd853f }, + { c_HtmlNamePink, 0xffc0cb }, + { c_HtmlNamePlum, 0xdda0dd }, + { c_HtmlNamePowderBlue, 0xb0e0e6 }, + { c_HtmlNamePurple, 0x800080 }, + { c_HtmlNameRed, 0xff0000 }, + { c_HtmlNameRosyBrown, 0xbc8f8f }, + { c_HtmlNameRoyalBlue, 0x4169e1 }, + { c_HtmlNameSaddleBrown, 0x8b4513 }, + { c_HtmlNameSalmon, 0xfa8072 }, + { c_HtmlNameSandyBrown, 0xf4a460 }, + { c_HtmlNameSeaGreen, 0x2e8b57 }, + { c_HtmlNameSeaShell, 0xfff5ee }, + { c_HtmlNameSienna, 0xa0522d }, + { c_HtmlNameSilver, 0xc0c0c0 }, + { c_HtmlNameSkyBlue, 0x87ceeb }, + { c_HtmlNameSlateBlue, 0x6a5acd }, + { c_HtmlNameSlateGray, 0x708090 }, + { c_HtmlNameSlateGrey, 0x708090 }, + { c_HtmlNameSnow, 0xfffafa }, + { c_HtmlNameSpringGreen, 0xff7f }, + { c_HtmlNameSteelBlue, 0x4682b4 }, + { c_HtmlNameTan, 0xd2b48c }, + { c_HtmlNameTeal, 0x8080 }, + { c_HtmlNameThistle, 0xd8bfd8 }, + { c_HtmlNameTomato, 0xff6347 }, + { c_HtmlNameTurquoise, 0x40e0d0 }, + { c_HtmlNameViolet, 0xee82ee }, + { c_HtmlNameWheat, 0xf5deb3 }, + { c_HtmlNameWhite, 0xffffff }, + { c_HtmlNameWhiteSmoke, 0xf5f5f5 }, + { c_HtmlNameYellow, 0xffff00 }, + { c_HtmlNameYellowGreen, 0x9acd32 }, +}; + +const HtmlColorPair* HtmlColorNames::Pair(uint8_t index) +{ + return &c_ColorNames[index]; +} + +uint8_t HtmlColorNames::Count() +{ + return countof(c_ColorNames); +} diff --git a/lib/NeoPixelBus/src/internal/colors/HtmlColorShortNames.cpp b/lib/NeoPixelBus/src/internal/colors/HtmlColorShortNames.cpp new file mode 100644 index 00000000..14aaf268 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/HtmlColorShortNames.cpp @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------- +HtmlShortColorNames provides a template class for access to the short name table + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + + +#include +#include "../NeoUtil.h" +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "HtmlColor.h" +#include "HtmlColorNameStrings.h" + +static const HtmlColorPair c_ShortColorNames[] PROGMEM = { + { c_HtmlNameAqua, 0xffff }, + { c_HtmlNameBlack, 0x0 }, + { c_HtmlNameBlue, 0xff }, + { c_HtmlNameFuchsia, 0xff00ff }, + { c_HtmlNameGray, 0x808080 }, + { c_HtmlNameGreen, 0x8000 }, + { c_HtmlNameLime, 0xff00 }, + { c_HtmlNameMaroon, 0x800000 }, + { c_HtmlNameNavy, 0x80 }, + { c_HtmlNameOlive, 0x808000 }, + { c_HtmlNameOrange, 0xffa500 }, + { c_HtmlNamePurple, 0x800080 }, + { c_HtmlNameRed, 0xff0000 }, + { c_HtmlNameSilver, 0xc0c0c0 }, + { c_HtmlNameTeal, 0x8080 }, + { c_HtmlNameWhite, 0xffffff }, + { c_HtmlNameYellow, 0xffff00 }, +}; + + +const HtmlColorPair* HtmlShortColorNames::Pair(uint8_t index) +{ + return &c_ShortColorNames[index]; +} + +uint8_t HtmlShortColorNames::Count() +{ + return countof(c_ShortColorNames); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGamma.h b/lib/NeoPixelBus/src/internal/colors/NeoGamma.h new file mode 100644 index 00000000..d017fa27 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGamma.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- +NeoGamma class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// use one of the gamma method classes as a converter for this template class +// T_METHOD - +// NeoGammaEquationMethod +// NeoGammaCieLabEquationMethod +// NeoGammaTableMethod +// NeoGammaNullMethod +// NeoGammaInvert +// +template class NeoGamma +{ +public: + static RgbColor Correct(const RgbColor& original) + { + return RgbColor(T_METHOD::Correct(original.R), + T_METHOD::Correct(original.G), + T_METHOD::Correct(original.B)); + } + + static RgbwColor Correct(const RgbwColor& original) + { + return RgbwColor(T_METHOD::Correct(original.R), + T_METHOD::Correct(original.G), + T_METHOD::Correct(original.B), + T_METHOD::Correct(original.W) ); + } + + static Rgb48Color Correct(const Rgb48Color& original) + { + return Rgb48Color(T_METHOD::Correct(original.R), + T_METHOD::Correct(original.G), + T_METHOD::Correct(original.B)); + } + + static Rgbw64Color Correct(const Rgbw64Color& original) + { + return Rgbw64Color(T_METHOD::Correct(original.R), + T_METHOD::Correct(original.G), + T_METHOD::Correct(original.B), + T_METHOD::Correct(original.W)); + } +}; + + + diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaCieLabEquationMethod.h b/lib/NeoPixelBus/src/internal/colors/NeoGammaCieLabEquationMethod.h new file mode 100644 index 00000000..6603677e --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaCieLabEquationMethod.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- +NeoGammaCieLabEquationMethod class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// Alternative equation to provide at least one official model for specific LEDs +class NeoGammaCieLabEquationMethod +{ +public: + static uint8_t Correct(uint8_t value) + { + return static_cast(255.0f * NeoEase::GammaCieLab(value / 255.0f) + 0.5f); + } + static uint16_t Correct(uint16_t value) + { + return static_cast(65535.0f * NeoEase::GammaCieLab(value / 65535.0f) + 0.5f); + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaDynamicTableMethod.cpp b/lib/NeoPixelBus/src/internal/colors/NeoGammaDynamicTableMethod.cpp new file mode 100644 index 00000000..383b39fb --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaDynamicTableMethod.cpp @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- +NeoGamma classes are used to correct RGB colors for human eye gamma levels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoUtil.h" +#include "NeoGammaDynamicTableMethod.h" + + +uint8_t NeoGammaDynamicTableMethod::_table[256] = { 0 }; +NeoGammaDynamicTableMethod::NeoGamma16LowHint* NeoGammaDynamicTableMethod::_hints = nullptr; +uint8_t NeoGammaDynamicTableMethod::_hintsCount = 0; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaDynamicTableMethod.h b/lib/NeoPixelBus/src/internal/colors/NeoGammaDynamicTableMethod.h new file mode 100644 index 00000000..2ccc9fed --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaDynamicTableMethod.h @@ -0,0 +1,226 @@ +/*------------------------------------------------------------------------- +NeoGammaDynamicTableMethod class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +#if defined(NEOPIXEBUS_NO_STL) + +typedef float(*GammaCalcFunction)(float unitValue); + +#else + +#undef max +#undef min +#include +typedef std::function GammaCalcFunction; + +#endif + +class NeoGammaDynamicTableMethod +{ +protected: + struct NeoGamma16LowHint + { + uint8_t pos; + uint8_t count; + }; + +public: + static uint8_t Correct(uint8_t value) + { + return _table[value]; + } + + static uint16_t Correct(uint16_t value) + { + // since a single monolithic table would be an unreasonable memory usage + // this will use a hybrid of two tables, the base 255 table for the hibyte + // and a smaller table with hints on how to use the table for certain values + // and the left over values some simple calculations to approximate the final + // 16 bit value as compared to the equation + uint8_t hi = (value >> 8); + uint16_t lo = (value & 0x00ff); + uint8_t hiResult = _table[hi]; + uint16_t lowResult = 0; + + if (hi < _hintsCount) + { + // use _hints table to calculate a reasonable lowbyte + lowResult = (lo + _hints[hi].pos * 256) / _hints[hi].count; + } + else if (hi == 255) + { + // last entry is always linear + lowResult = lo; + } + else + { + // check the _table for duplicate or jumps to adjust the range of lowbyte + if (hiResult == _table[hi - 1]) + { + // this result is an upper duplicate + lowResult = (lo >> 1) | 0x80; // lo / 2 + 128 + } + else + { + uint8_t delta = _table[hi + 1] - hiResult; + + if (delta == 0) + { + // this result is a lower duplicate + lowResult = (lo >> 1); // lo / 2 + } + else if (delta == 1) + { + // this result is incremental and thus linear + lowResult = lo; + } + else + { + // this result jumps by more than one, so need to spread across + lowResult = delta * lo; + } + } + + } + + return (static_cast(hiResult) << 8) + lowResult; + } + + static void Initialize(GammaCalcFunction calc, bool optimize16Bit = false) + { + if (_hints) + { + delete [] _hints; + _hints = nullptr; + } + + // first, iterate and fill 8 bit table + for (uint16_t entry = 0; entry < 256; entry++) + { + _table[entry] = static_cast(255.0f * calc(entry / 255.0f) + 0.5f); + } + + // no optimization, so no 16 bit hints table + if (optimize16Bit) + { + NeoGamma16LowHint hints[256]; + + // now walk table creating an optimized hint table for 16bit + // approximation + // There is an assumption that lower values have series of duplicates and + // upper values at worst have two values the same + // + uint16_t entryStart = 0; + uint16_t entryEnd = 0; + uint16_t entryLastTriplet = 0; + + while (entryStart < 255) + { + uint8_t value = _table[entryStart]; + + while (value == _table[entryEnd] && entryEnd < 255) + { + entryEnd++; + } + + if (entryEnd == entryStart) + { + // no more duplicates, no need to continue + break; + } + + uint8_t pos = 0; + uint8_t count = entryEnd - entryStart; + + if (count >= 3) + { + // remember the last triplet + series + // as we don't need hints after this + // there can be paired duplicates after and before this + entryLastTriplet = entryEnd; + } + + // fill hints with known duplicate value + while (entryStart != entryEnd) + { + hints[entryStart].count = count; + hints[entryStart].pos = pos; + entryStart++; + pos++; + } + } + // create static hint table and copy temp table to it + _hintsCount = entryLastTriplet; // only need to last triplet + _hints = new NeoGamma16LowHint[_hintsCount]; + memcpy(_hints, hints, sizeof(NeoGamma16LowHint) * _hintsCount); + } + } + + // SerialDumpTables is used if you want to generate your own static gamma table class + // rather than use this dynamically generated table. Just capture the serial output + // and use as your initializers for your tables + static void SerialDumpTables() + { + Serial.println(); + Serial.println("8 bit:"); + for (uint16_t entry = 0; entry < 256; entry++) + { + if (entry % 16 == 0) + { + Serial.println(); + } + Serial.print(_table[entry]); + Serial.print(", "); + } + + Serial.println(); + Serial.println(); + Serial.print("16 bit: hintsCount = "); + Serial.println(_hintsCount); + if (_hints) + { + for (uint8_t hint = 0; hint < _hintsCount; hint++) + { + if (hint % 16 == 0) + { + Serial.println(); + } + Serial.print("{"); + Serial.print(_hints[hint].pos); + Serial.print(","); + Serial.print(_hints[hint].count); + Serial.print("}, "); + } + } + Serial.println(); + } + +private: + static uint8_t _table[256]; + static NeoGamma16LowHint* _hints; + static uint8_t _hintsCount; +}; diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaEquationMethod.h b/lib/NeoPixelBus/src/internal/colors/NeoGammaEquationMethod.h new file mode 100644 index 00000000..19ce6879 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaEquationMethod.h @@ -0,0 +1,44 @@ +/*------------------------------------------------------------------------- +NeoGammaEquationMethod class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +// NeoGammaEquationMethod uses no memory but is slower than NeoGammaTableMethod +class NeoGammaEquationMethod +{ +public: + static uint8_t Correct(uint8_t value) + { + return static_cast(255.0f * NeoEase::Gamma(value / 255.0f) + 0.5f); + } + static uint16_t Correct(uint16_t value) + { + return static_cast(65535.0f * NeoEase::Gamma(value / 65535.0f) + 0.5f); + } +}; + + diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaInvertMethod.h b/lib/NeoPixelBus/src/internal/colors/NeoGammaInvertMethod.h new file mode 100644 index 00000000..f28bdef2 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaInvertMethod.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- +NeoGammaInvertMethod class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// use one of the gamma method classes as a base converter for this template class +// T_METHOD - +// NeoGammaEquationMethod +// NeoGammaCieLabEquationMethod +// NeoGammaTableMethod +// NeoGammaNullMethod +// +template class NeoGammaInvertMethod +{ +public: + static uint8_t Correct(uint8_t value) + { + return ~T_METHOD::Correct(value); + } + + static uint16_t Correct(uint16_t value) + { + return ~T_METHOD::Correct(value); + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaNullMethod.h b/lib/NeoPixelBus/src/internal/colors/NeoGammaNullMethod.h new file mode 100644 index 00000000..d1d4436b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaNullMethod.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- +NeoGammaNullMethod class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// no gamma correction at all +class NeoGammaNullMethod +{ +public: + static uint8_t Correct(uint8_t value) + { + return value; + } + + static uint16_t Correct(uint16_t value) + { + return value; + } +}; diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaTableMethod.cpp b/lib/NeoPixelBus/src/internal/colors/NeoGammaTableMethod.cpp new file mode 100644 index 00000000..f1d87825 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaTableMethod.cpp @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- +NeoGamma classes are used to correct RGB colors for human eye gamma levels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoUtil.h" +#include "NeoGammaTableMethod.h" + +const uint8_t NeoGammaTableMethod::_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, // 32 + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, // 48 + 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, // 64 + 12, 12, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, // 80 + 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, // 96 + 29, 30, 30, 31, 32, 33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, // 112 + 41, 42, 43, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, 54, // 128 + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, // 144 + 71, 73, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84, 85, 87, 88, 89, // 160 + 90, 91, 93, 94, 95, 97, 98, 99, 101, 102, 103, 105, 106, 107, 109, 110, // 176 + 111, 113, 114, 116, 117, 119, 120, 122, 123, 125, 126, 128, 129, 131, 132, 134, // 192 + 135, 137, 138, 140, 142, 143, 145, 146, 148, 150, 151, 153, 155, 156, 158, 160, // 208 + 162, 163, 165, 167, 168, 170, 172, 174, 176, 177, 179, 181, 183, 185, 187, 189, // 224 + 190, 192, 194, 196, 198, 200, 202, 203, 206, 207, 210, 212, 214, 216, 218, 220, // 240 + 222, 224, 226, 228, 230, 232, 234, 237, 239, 241, 243, 245, 247, 250, 251, 255 // 256 +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/NeoGammaTableMethod.h b/lib/NeoPixelBus/src/internal/colors/NeoGammaTableMethod.h new file mode 100644 index 00000000..4874a440 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoGammaTableMethod.h @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- +NeoGammaTableMethod class is used to correct RGB colors for human eye gamma levels equally +across all color channels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// NeoGammaTableMethod uses 256 bytes of memory, but is significantly faster +class NeoGammaTableMethod +{ +protected: + struct NeoGamma16LowHint + { + uint8_t pos; + uint8_t count; + }; + +public: + static uint8_t Correct(uint8_t value) + { + return _table[value]; + } + + static uint16_t Correct(uint16_t value) + { + // since a single monolithic table would be an unreasonable memory usage + // this will use a hybrid of two tables, the base 255 table for the hibyte + // and a smaller table with hints on how to use the table for certain values + // and the left over values some simple calculations to approximate the final + // 16 bit value as compared to the equation + static const NeoGamma16LowHint _hints[] = { + {0,16}, {1,16}, {2,16}, {3,16}, {4,16}, {5,16}, {6,16}, {7,16}, {8,16}, {9,16}, {10,16}, {11,16}, {12,16}, {13,16}, {14,16}, {15,16}, + {0,10}, {1,10}, {2,10}, {3,10}, {4,10}, {5,10}, {6,10}, {7,10}, {8,10}, {9,10}, {0,6}, {1,6}, {2,6}, {3,6}, {4,6}, {5,6}, + {0,6}, {1,6}, {2,6}, {3,6}, {4,6}, {5,6}, {0,4}, {1,4}, {2,4}, {3,4}, {0,4}, {1,4}, {2,4}, {3,4}, {0,3}, {1,3}, + {2,3}, {0,4}, {1,4}, {2,4}, {3,4}, {0,3}, {1,3}, {2,3}, {0,3}, {1,3}, {2,3}, {0,2}, {1,2}, {0,3}, {1,3}, {2,3}, + {0,2}, {1,2}, {0,2}, {1,2}, {0,3}, {1,3}, {2,3}, {0,2}, {1,2} + }; + + uint8_t hi = (value >> 8); + uint16_t lo = (value & 0x00ff); + uint8_t hiResult = _table[hi]; + uint16_t lowResult = 0; + + if (hi < countof(_hints)) + { + // use _hints table to calculate a reasonable lowbyte + lowResult = (lo + _hints[hi].pos * 256) / _hints[hi].count; + } + else if (hi == 255) + { + // last entry is always linear + lowResult = lo; + } + else + { + // check the _table for duplicate or jumps to adjust the range of lowbyte + if (hiResult == _table[hi - 1]) + { + // this result is an upper duplicate + lowResult = (lo >> 1) | 0x80; // lo / 2 + 128 + } + else + { + uint8_t delta = _table[hi + 1] - hiResult; + + if (delta == 0) + { + // this result is a lower duplicate + lowResult = (lo >> 1); // lo / 2 + } + else if (delta == 1) + { + // this result is incremental and thus linear + lowResult = lo; + } + else + { + // this result jumps by more than one, so need to spread across + lowResult = delta * lo; + } + } + + } + + return (static_cast(hiResult) << 8) + lowResult; + } + + +private: + static const uint8_t _table[256]; + +}; diff --git a/lib/NeoPixelBus/src/internal/colors/NeoHueBlend.h b/lib/NeoPixelBus/src/internal/colors/NeoHueBlend.h new file mode 100644 index 00000000..d77a58fb --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/NeoHueBlend.h @@ -0,0 +1,118 @@ +/*------------------------------------------------------------------------- +NeoHueBlend provides method objects that can be directly consumed by +blend template functions in HslColor and HsbColor + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoHueBlendBase +{ +protected: + static float FixWrap(float value) + { + if (value < 0.0f) + { + value += 1.0f; + } + else if (value > 1.0f) + { + value -= 1.0f; + } + return value; + } +}; + +class NeoHueBlendShortestDistance : NeoHueBlendBase +{ +public: + static float HueBlend(float left, float right, float progress) + { + float delta = right - left; + float base = left; + if (delta > 0.5f) + { + base = right; + delta = 1.0f - delta; + progress = 1.0f - progress; + } + else if (delta < -0.5f) + { + delta = 1.0f + delta; + } + return FixWrap(base + (delta) * progress); + }; +}; + +class NeoHueBlendLongestDistance : NeoHueBlendBase +{ +public: + static float HueBlend(float left, float right, float progress) + { + float delta = right - left; + float base = left; + if (delta < 0.5f && delta >= 0.0f) + { + base = right; + delta = 1.0f - delta; + progress = 1.0f - progress; + } + else if (delta > -0.5f && delta < 0.0f) + { + delta = 1.0f + delta; + } + return FixWrap(base + delta * progress); + }; +}; + +class NeoHueBlendClockwiseDirection : NeoHueBlendBase +{ +public: + static float HueBlend(float left, float right, float progress) + { + float delta = right - left; + float base = left; + if (delta < 0.0f) + { + delta = 1.0f + delta; + } + + return FixWrap(base + delta * progress); + }; +}; + +class NeoHueBlendCounterClockwiseDirection : NeoHueBlendBase +{ +public: + static float HueBlend(float left, float right, float progress) + { + float delta = right - left; + float base = left; + if (delta > 0.0f) + { + delta = delta - 1.0f; + } + + return FixWrap(base + delta * progress); + }; +}; diff --git a/lib/NeoPixelBus/src/internal/colors/Rgb16Color.h b/lib/NeoPixelBus/src/internal/colors/Rgb16Color.h new file mode 100644 index 00000000..aa08164b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/Rgb16Color.h @@ -0,0 +1,315 @@ +/*------------------------------------------------------------------------- +Rgb16Color provides a color object that stores in only 16 bits, aka 565 format + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// ------------------------------------------------------------------------ +// Rgb16Color represents a color object that is represented by Red, Green, Blue +// component values stored in a single 16 bit value using 565 model. +// It contains helpful color routines to manipulate the color. +// ------------------------------------------------------------------------ +struct Rgb16Color : RgbColorBase +{ + typedef NeoRgbCurrentSettings SettingsObject; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using R, G, B values (0-255) + // ------------------------------------------------------------------------ + Rgb16Color(uint8_t r, uint8_t g, uint8_t b) + { + setR(r); + setG(g); + setB(b); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using a single brightness value (0-255) + // This works well for creating gray tone colors + // (0) = black, (255) = white, (128) = gray + // ------------------------------------------------------------------------ + Rgb16Color(uint8_t brightness) + { + setR(brightness); + setG(brightness); + setB(brightness); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using a single 16bit encoded value + // CAUTION: the color value must follow a 5/6/5 encoding model otherwise an + // incorrect color will be generated + // ------------------------------------------------------------------------ + Rgb16Color(uint16_t color) : + Color565(color) + { + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using RgbCOlor + // ------------------------------------------------------------------------ + Rgb16Color(const RgbColor& color) + { + setR(color.R); + setG(color.G); + setB(color.B); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using HtmlColor + // ------------------------------------------------------------------------ + Rgb16Color(const HtmlColor& color) + { + RgbColor converted = color; + + setR(converted.R); + setG(converted.G); + setB(converted.B); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using HslColor + // ------------------------------------------------------------------------ + Rgb16Color(const HslColor& color) + { + RgbColor converted = color; + + setR(converted.R); + setG(converted.G); + setB(converted.B); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color using HsbColor + // ------------------------------------------------------------------------ + Rgb16Color(const HsbColor& color) + { + RgbColor converted = color; + + setR(converted.R); + setG(converted.G); + setB(converted.B); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb16Color that will have its values set in latter operations + // CAUTION: The Color565 member is not initialized and may not be consistent + // ------------------------------------------------------------------------ + Rgb16Color() + { + }; + + // ------------------------------------------------------------------------ + // properties + // ------------------------------------------------------------------------ + void setR(uint8_t r) + { + Color565 &= 0x07ff; + Color565 |= ((r & 0xf8) << 8); + }; + uint8_t getR() const + { + return (Color565 & 0xf800) >> 8; + }; + + void setG(uint8_t g) + { + Color565 &= 0xf81f; + Color565 |= ((g & 0xfe) << 3); + }; + uint8_t getG() const + { + return (Color565 & 0x07e0) >> 3; + }; + + void setB(uint8_t b) + { + Color565 &= 0xffe0; + Color565 |= ((b & 0xf8) >> 3); + }; + uint8_t getB() const + { + return (Color565 & 0x001f) << 3; + }; + + // ------------------------------------------------------------------------ + // operator [] - readonly + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t operator[](size_t idx) const + { + switch (idx) + { + case 0: + return getR(); + case 1: + return getG(); + default: + return getB(); + } + } + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const Rgb16Color& other) const + { + return (Color565 == other.Color565); + }; + + bool operator!=(const Rgb16Color& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint8_t CalculateBrightness() const + { + RgbColor converted = *this; + return converted.CalculateBrightness(); + }; + + // ------------------------------------------------------------------------ + // Dim will return a new color that is blended to black with the given ratio + // ratio - (0-255) where 255 will return the original color and 0 will return black + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + Rgb16Color Dim(uint8_t ratio) const + { + RgbColor converted = *this; + RgbColor result = converted.Dim(ratio); + + return Rgb16Color(result.R, result.G, result.B); + }; + + // ------------------------------------------------------------------------ + // Brighten will return a new color that is blended to white with the given ratio + // ratio - (0-255) where 255 will return the original color and 0 will return white + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + Rgb16Color Brighten(uint8_t ratio) const + { + RgbColor converted = *this; + RgbColor result = converted.Brighten(ratio); + + return Rgb16Color(result.R, result.G, result.B); + }; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-255) the amount to dim the color + // ------------------------------------------------------------------------ + void Darken(uint8_t delta) + { + RgbColor converted = *this; + + converted.Darken(delta); + setR(converted.R); + setG(converted.G); + setB(converted.B); + }; + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-255) the amount to lighten the color + // ------------------------------------------------------------------------ + void Lighten(uint8_t delta) + { + RgbColor converted = *this; + + converted.Lighten(delta); + setR(converted.R); + setG(converted.G); + setB(converted.B); + }; + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static Rgb16Color LinearBlend(const Rgb16Color& left, const Rgb16Color& right, float progress) + { + RgbColor result = RgbColor::LinearBlend(left, right, progress); + + return Rgb16Color(result.R, result.G, result.B); + }; + // progress - (0 - 255) value where 0 will return left and 255 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static Rgb16Color LinearBlend(const Rgb16Color& left, const Rgb16Color& right, uint8_t progress) + { + RgbColor result = RgbColor::LinearBlend(left, right, progress); + + return Rgb16Color(result.R, result.G, result.B); + }; + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static Rgb16Color BilinearBlend(const Rgb16Color& c00, + const Rgb16Color& c01, + const Rgb16Color& c10, + const Rgb16Color& c11, + float x, + float y) + { + RgbColor result = RgbColor::BilinearBlend(c00, c01, c10, c11, x, y); + + return Rgb16Color(result.R, result.G, result.B); + }; + + uint32_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) + { + auto total = 0; + + total += getR() * settings.RedTenthMilliAmpere / 255; + total += getG() * settings.GreenTenthMilliAmpere / 255; + total += getB() * settings.BlueTenthMilliAmpere / 255; + + return total; + }; + + uint16_t Color565; + + const static uint8_t Max = 255; + const static size_t Count = 3; // three elements in [] +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/Rgb48Color.cpp b/lib/NeoPixelBus/src/internal/colors/Rgb48Color.cpp new file mode 100644 index 00000000..d33b355d --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/Rgb48Color.cpp @@ -0,0 +1,178 @@ +/*------------------------------------------------------------------------- +Rgb48Color provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "Rgb48Color.h" +#include "HslColor.h" +#include "HsbColor.h" +#include "HtmlColor.h" + +#include "RgbwColor.h" +#include "Rgbw64Color.h" + +Rgb48Color::Rgb48Color(const Rgbw64Color& color) : + R(color.R), + G(color.G), + B(color.B) +{ +} + +Rgb48Color::Rgb48Color(const HslColor& color) +{ + float r; + float g; + float b; + + _HslToRgb(color, &r, &g, &b); + + R = static_cast(r * Max); + G = static_cast(g * Max); + B = static_cast(b * Max); +} + +Rgb48Color::Rgb48Color(const HsbColor& color) +{ + float r; + float g; + float b; + + _HsbToRgb(color, &r, &g, &b); + + R = static_cast(r * Max); + G = static_cast(g * Max); + B = static_cast(b * Max); +} + +uint16_t Rgb48Color::CalculateBrightness() const +{ + return static_cast((static_cast(R) + static_cast(G) + static_cast(B)) / 3); +} + +Rgb48Color Rgb48Color::Dim(uint16_t ratio) const +{ + // specifically avoids float math + return Rgb48Color(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio)); +} + +Rgb48Color Rgb48Color::Brighten(uint16_t ratio) const +{ + // specifically avoids float math + return Rgb48Color(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio)); +} + +void Rgb48Color::Darken(uint16_t delta) +{ + if (R > delta) + { + R -= delta; + } + else + { + R = 0; + } + + if (G > delta) + { + G -= delta; + } + else + { + G = 0; + } + + if (B > delta) + { + B -= delta; + } + else + { + B = 0; + } +} + +void Rgb48Color::Lighten(uint16_t delta) +{ + if (R < Max - delta) + { + R += delta; + } + else + { + R = Max; + } + + if (G < Max - delta) + { + G += delta; + } + else + { + G = Max; + } + + if (B < Max - delta) + { + B += delta; + } + else + { + B = Max; + } +} + +Rgb48Color Rgb48Color::LinearBlend(const Rgb48Color& left, const Rgb48Color& right, float progress) +{ + return Rgb48Color( left.R + ((static_cast(right.R) - left.R) * progress), + left.G + ((static_cast(right.G) - left.G) * progress), + left.B + ((static_cast(right.B) - left.B) * progress)); +} +Rgb48Color Rgb48Color::LinearBlend(const Rgb48Color& left, const Rgb48Color& right, uint8_t progress) +{ + return Rgb48Color(left.R + (((static_cast(right.R) - left.R) * static_cast(progress) + 1) >> 8), + left.G + (((static_cast(right.G) - left.G) * static_cast(progress) + 1) >> 8), + left.B + (((static_cast(right.B) - left.B) * static_cast(progress) + 1) >> 8)); +} + +Rgb48Color Rgb48Color::BilinearBlend(const Rgb48Color& c00, + const Rgb48Color& c01, + const Rgb48Color& c10, + const Rgb48Color& c11, + float x, + float y) +{ + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return Rgb48Color( + c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11, + c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/Rgb48Color.h b/lib/NeoPixelBus/src/internal/colors/Rgb48Color.h new file mode 100644 index 00000000..05986db3 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/Rgb48Color.h @@ -0,0 +1,290 @@ +/*------------------------------------------------------------------------- +Rgb48Color provides a color object that contains 16bit color elements + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +struct Rgbw64Color; // forward declared + +// ------------------------------------------------------------------------ +// Rgb48Color represents a color object that is represented by Red, Green, Blue +// component values. It contains helpful color routines to manipulate the +// color. +// ------------------------------------------------------------------------ +struct Rgb48Color : RgbColorBase +{ + typedef NeoRgbCurrentSettings SettingsObject; + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color using R, G, B values (0-65535) + // ------------------------------------------------------------------------ + Rgb48Color(uint16_t r, uint16_t g, uint16_t b) : + R(r), G(g), B(b) + { + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color using a single brightness value (0-65535) + // This works well for creating gray tone colors + // (0) = black, (65535) = white, (32768) = gray + // ------------------------------------------------------------------------ + Rgb48Color(uint16_t brightness) : + R(brightness), G(brightness), B(brightness) + { + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color using RgbColor + // ------------------------------------------------------------------------ + Rgb48Color(const RgbColor& color) + { + // x16 = map(x8, 0, 255, 0, 65535); // refactors to just * 257 + R = (uint16_t)color.R * 257; // 257 = MAXUINT16/MAXUINT8 = 65535/255 + G = (uint16_t)color.G * 257; + B = (uint16_t)color.B * 257; + }; + + // ------------------------------------------------------------------------ + // explicitly Construct a Rgb48Color using RgbwColor + // ------------------------------------------------------------------------ + explicit Rgb48Color(const RgbwColor& color) + { + *this = RgbColor(color); + } + + // ------------------------------------------------------------------------ + // explicitly Construct a Rgb48Color using Rgbw64Color + // ------------------------------------------------------------------------ + explicit Rgb48Color(const Rgbw64Color& color); + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color using HtmlColor + // ------------------------------------------------------------------------ + Rgb48Color(const HtmlColor& color) + { + *this = RgbColor(color); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color using HslColor + // ------------------------------------------------------------------------ + Rgb48Color(const HslColor& color); + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color using HsbColor + // ------------------------------------------------------------------------ + Rgb48Color(const HsbColor& color); + + // ------------------------------------------------------------------------ + // Construct a Rgb48Color that will have its values set in latter operations + // CAUTION: The R,G,B members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + Rgb48Color() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const Rgb48Color& other) const + { + return (R == other.R && G == other.G && B == other.B); + }; + + bool operator!=(const Rgb48Color& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // CompareTo method + // compares against another color with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - this is less than other + // positive - this is greater than other + // ------------------------------------------------------------------------ + int32_t CompareTo(const Rgb48Color& other, uint16_t epsilon = 256) + { + return _Compare(*this, other, epsilon); + } + + // ------------------------------------------------------------------------ + // Compare method + // compares two colors with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - left is less than right + // positive - left is greater than right + // ------------------------------------------------------------------------ + static int32_t Compare(const Rgb48Color& left, const Rgb48Color& right, uint16_t epsilon = 256) + { + return _Compare(left, right, epsilon); + } + + // ------------------------------------------------------------------------ + // operator [] - readonly + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint16_t operator[](size_t idx) const + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + default: + return B; + } + } + + // ------------------------------------------------------------------------ + // operator [] - read write + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint16_t& operator[](size_t idx) + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + default: + return B; + } + } + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint16_t CalculateBrightness() const; + + // ------------------------------------------------------------------------ + // Dim will return a new color that is blended to black with the given ratio + // ratio - (0-65535) where 65535 will return the original color and 0 will return black + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + Rgb48Color Dim(uint16_t ratio) const; + + // ------------------------------------------------------------------------ + // Brighten will return a new color that is blended to white with the given ratio + // ratio - (0-65535) where 65535 will return the original color and 0 will return white + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + Rgb48Color Brighten(uint16_t ratio) const; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-65535) the amount to dim the color + // ------------------------------------------------------------------------ + void Darken(uint16_t delta); + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-65535) the amount to lighten the color + // ------------------------------------------------------------------------ + void Lighten(uint16_t delta); + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static Rgb48Color LinearBlend(const Rgb48Color& left, const Rgb48Color& right, float progress); + // progress - (0 - 255) value where 0 will return left and 255 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static Rgb48Color LinearBlend(const Rgb48Color& left, const Rgb48Color& right, uint8_t progress); + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static Rgb48Color BilinearBlend(const Rgb48Color& c00, + const Rgb48Color& c01, + const Rgb48Color& c10, + const Rgb48Color& c11, + float x, + float y); + + uint32_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) + { + auto total = 0; + + total += R * settings.RedTenthMilliAmpere / Max; + total += G * settings.GreenTenthMilliAmpere / Max; + total += B * settings.BlueTenthMilliAmpere / Max; + + return total; + } + + // ------------------------------------------------------------------------ + // Red, Green, Blue color members (0-65535) where + // (0,0,0) is black and (65535,65535,65535) is white + // ------------------------------------------------------------------------ + uint16_t R; + uint16_t G; + uint16_t B; + + const static uint16_t Max = 65535; + const static size_t Count = 3; // three elements in [] + +private: + inline static uint16_t _elementDim(uint16_t value, uint16_t ratio) + { + return (static_cast(value) * (static_cast(ratio) + 1)) >> 16; + } + + inline static uint16_t _elementBrighten(uint16_t value, uint16_t ratio) + { + uint32_t element = ((static_cast(value) + 1) << 16) / (static_cast(ratio) + 1); + + if (element > Max) + { + element = Max; + } + else + { + element -= 1; + } + return element; + } +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/RgbColor.cpp b/lib/NeoPixelBus/src/internal/colors/RgbColor.cpp new file mode 100644 index 00000000..81d0ba56 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/RgbColor.cpp @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------- +RgbColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "Rgb16Color.h" +#include "Rgb48Color.h" +#include "HslColor.h" +#include "HsbColor.h" +#include "HtmlColor.h" + +#include "RgbwColor.h" + +RgbColor::RgbColor(const RgbwColor& color) : + R(color.R), + G(color.G), + B(color.B) +{ +}; + +RgbColor::RgbColor(const Rgb16Color& color) +{ + R = color.getR(); + G = color.getG(); + B = color.getB(); +} + +RgbColor::RgbColor(const HtmlColor& color) +{ + uint32_t temp = color.Color; + + B = (temp & 0xff); + temp = temp >> 8; + G = (temp & 0xff); + temp = temp >> 8; + R = (temp & 0xff); +}; + +RgbColor::RgbColor(const HslColor& color) +{ + float r; + float g; + float b; + + _HslToRgb(color, &r, &g, &b); + + R = static_cast(r * Max); + G = static_cast(g * Max); + B = static_cast(b * Max); +} + +RgbColor::RgbColor(const HsbColor& color) +{ + float r; + float g; + float b; + + _HsbToRgb(color, &r, &g, &b); + + R = static_cast(r * Max); + G = static_cast(g * Max); + B = static_cast(b * Max); +} + +uint8_t RgbColor::CalculateBrightness() const +{ + return static_cast((static_cast(R) + static_cast(G) + static_cast(B)) / 3); +} + +RgbColor RgbColor::Dim(uint8_t ratio) const +{ + // specifically avoids float math + return RgbColor(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio)); +} + +RgbColor RgbColor::Brighten(uint8_t ratio) const +{ + // specifically avoids float math + return RgbColor(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio)); +} + +void RgbColor::Darken(uint8_t delta) +{ + if (R > delta) + { + R -= delta; + } + else + { + R = 0; + } + + if (G > delta) + { + G -= delta; + } + else + { + G = 0; + } + + if (B > delta) + { + B -= delta; + } + else + { + B = 0; + } +} + +void RgbColor::Lighten(uint8_t delta) +{ + if (R < Max - delta) + { + R += delta; + } + else + { + R = Max; + } + + if (G < Max - delta) + { + G += delta; + } + else + { + G = Max; + } + + if (B < Max - delta) + { + B += delta; + } + else + { + B = Max; + } +} + +RgbColor RgbColor::LinearBlend(const RgbColor& left, const RgbColor& right, float progress) +{ + return RgbColor( left.R + ((static_cast(right.R) - left.R) * progress), + left.G + ((static_cast(right.G) - left.G) * progress), + left.B + ((static_cast(right.B) - left.B) * progress)); +} + +RgbColor RgbColor::LinearBlend(const RgbColor& left, const RgbColor& right, uint8_t progress) +{ + return RgbColor(left.R + (((static_cast(right.R) - left.R) * static_cast(progress) + 1) >> 8), + left.G + (((static_cast(right.G) - left.G) * static_cast(progress) + 1) >> 8), + left.B + (((static_cast(right.B) - left.B) * static_cast(progress) + 1) >> 8)); +} + +RgbColor RgbColor::BilinearBlend(const RgbColor& c00, + const RgbColor& c01, + const RgbColor& c10, + const RgbColor& c11, + float x, + float y) +{ + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return RgbColor( + c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11, + c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/RgbColor.h b/lib/NeoPixelBus/src/internal/colors/RgbColor.h new file mode 100644 index 00000000..2b34d804 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/RgbColor.h @@ -0,0 +1,274 @@ +/*------------------------------------------------------------------------- +RgbColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +struct RgbwColor; + +// ------------------------------------------------------------------------ +// RgbColor represents a color object that is represented by Red, Green, Blue +// component values. It contains helpful color routines to manipulate the +// color. +// ------------------------------------------------------------------------ +struct RgbColor : RgbColorBase +{ + typedef NeoRgbCurrentSettings SettingsObject; + + // ------------------------------------------------------------------------ + // Construct a RgbColor using R, G, B values (0-255) + // ------------------------------------------------------------------------ + RgbColor(uint8_t r, uint8_t g, uint8_t b) : + R(r), G(g), B(b) + { + }; + + // ------------------------------------------------------------------------ + // Construct a RgbColor using a single brightness value (0-255) + // This works well for creating gray tone colors + // (0) = black, (255) = white, (128) = gray + // ------------------------------------------------------------------------ + RgbColor(uint8_t brightness) : + R(brightness), G(brightness), B(brightness) + { + }; + + // ------------------------------------------------------------------------ + // explicitly Construct a RgbColor using RgbwColor + // ------------------------------------------------------------------------ + explicit RgbColor(const RgbwColor& color); + + // ------------------------------------------------------------------------ + // Construct a RgbColor using Rgb16Color + // ------------------------------------------------------------------------ + RgbColor(const Rgb16Color& color); + + // ------------------------------------------------------------------------ + // Construct a RgbColor using HtmlColor + // ------------------------------------------------------------------------ + RgbColor(const HtmlColor& color); + + // ------------------------------------------------------------------------ + // Construct a RgbColor using HslColor + // ------------------------------------------------------------------------ + RgbColor(const HslColor& color); + + // ------------------------------------------------------------------------ + // Construct a RgbColor using HsbColor + // ------------------------------------------------------------------------ + RgbColor(const HsbColor& color); + + + // ------------------------------------------------------------------------ + // Construct a RgbColor that will have its values set in latter operations + // CAUTION: The R,G,B members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + RgbColor() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const RgbColor& other) const + { + return (R == other.R && G == other.G && B == other.B); + }; + + bool operator!=(const RgbColor& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // CompareTo method + // compares against another color with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - this is less than other + // positive - this is greater than other + // ------------------------------------------------------------------------ + int16_t CompareTo(const RgbColor& other, uint8_t epsilon = 1) + { + return _Compare(*this, other, epsilon); + } + + // ------------------------------------------------------------------------ + // Compare method + // compares two colors with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - left is less than right + // positive - left is greater than right + // ------------------------------------------------------------------------ + static int16_t Compare(const RgbColor& left, const RgbColor& right, uint8_t epsilon = 1) + { + return _Compare(left, right, epsilon); + } + + // ------------------------------------------------------------------------ + // operator [] - readonly + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t operator[](size_t idx) const + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + default: + return B; + } + } + + // ------------------------------------------------------------------------ + // operator [] - read write + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t& operator[](size_t idx) + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + default: + return B; + } + } + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint8_t CalculateBrightness() const; + + // ------------------------------------------------------------------------ + // Dim will return a new color that is blended to black with the given ratio + // ratio - (0-255) where 255 will return the original color and 0 will return black + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + RgbColor Dim(uint8_t ratio) const; + + // ------------------------------------------------------------------------ + // Brighten will return a new color that is blended to white with the given ratio + // ratio - (0-255) where 255 will return the original color and 0 will return white + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + RgbColor Brighten(uint8_t ratio) const; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-255) the amount to dim the color + // ------------------------------------------------------------------------ + void Darken(uint8_t delta); + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-255) the amount to lighten the color + // ------------------------------------------------------------------------ + void Lighten(uint8_t delta); + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static RgbColor LinearBlend(const RgbColor& left, const RgbColor& right, float progress); + // progress - (0 - 255) value where 0 will return left and 255 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static RgbColor LinearBlend(const RgbColor& left, const RgbColor& right, uint8_t progress); + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static RgbColor BilinearBlend(const RgbColor& c00, + const RgbColor& c01, + const RgbColor& c10, + const RgbColor& c11, + float x, + float y); + + uint32_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) + { + auto total = 0; + + total += R * settings.RedTenthMilliAmpere / Max; + total += G * settings.GreenTenthMilliAmpere / Max; + total += B * settings.BlueTenthMilliAmpere / Max; + + return total; + } + + // ------------------------------------------------------------------------ + // Red, Green, Blue color members (0-255) where + // (0,0,0) is black and (255,255,255) is white + // ------------------------------------------------------------------------ + uint8_t R; + uint8_t G; + uint8_t B; + + const static uint8_t Max = 255; + const static size_t Count = 3; // three elements in [] + +private: + inline static uint8_t _elementDim(uint8_t value, uint8_t ratio) + { + return (static_cast(value) * (static_cast(ratio) + 1)) >> 8; + } + + inline static uint8_t _elementBrighten(uint8_t value, uint8_t ratio) + { + uint16_t element = ((static_cast(value) + 1) << 8) / (static_cast(ratio) + 1); + + if (element > Max) + { + element = Max; + } + else + { + element -= 1; + } + return element; + } +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/RgbColorBase.cpp b/lib/NeoPixelBus/src/internal/colors/RgbColorBase.cpp new file mode 100644 index 00000000..92c43f9d --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/RgbColorBase.cpp @@ -0,0 +1,136 @@ +/*------------------------------------------------------------------------- +RgbColorBase provides a RGB color object common support + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "Rgb48Color.h" +#include "HslColor.h" +#include "HsbColor.h" +#include "HtmlColor.h" + +float RgbColorBase::_CalcColor(float p, float q, float t) +{ + if (t < 0.0f) + t += 1.0f; + if (t > 1.0f) + t -= 1.0f; + + if (t < 1.0f / 6.0f) + return p + (q - p) * 6.0f * t; + + if (t < 0.5f) + return q; + + if (t < 2.0f / 3.0f) + return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f); + + return p; +} + +void RgbColorBase::_HslToRgb(const HslColor& color, float* r, float* g, float* b) +{ + float h = color.H; + float s = color.S; + float l = color.L; + + + if (color.S == 0.0f || color.L == 0.0f) + { + *r = *g = *b = l; // achromatic or black + } + else + { + float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s); + float p = 2.0f * l - q; + *r = _CalcColor(p, q, h + 1.0f / 3.0f); + *g = _CalcColor(p, q, h); + *b = _CalcColor(p, q, h - 1.0f / 3.0f); + } +} + +void RgbColorBase::_HsbToRgb(const HsbColor& color, float* r, float* g, float* b) +{ + float h = color.H; + float s = color.S; + float v = color.B; + + if (color.S == 0.0f) + { + *r = *g = *b = v; // achromatic or black + } + else + { + if (h < 0.0f) + { + h += 1.0f; + } + else if (h >= 1.0f) + { + h -= 1.0f; + } + h *= 6.0f; + int i = (int)h; + float f = h - i; + float q = v * (1.0f - s * f); + float p = v * (1.0f - s); + float t = v * (1.0f - s * (1.0f - f)); + switch (i) + { + case 0: + *r = v; + *g = t; + *b = p; + break; + case 1: + *r = q; + *g = v; + *b = p; + break; + case 2: + *r = p; + *g = v; + *b = t; + break; + case 3: + *r = p; + *g = q; + *b = v; + break; + case 4: + *r = t; + *g = p; + *b = v; + break; + default: + *r = v; + *g = p; + *b = q; + break; + } + } +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/RgbColorBase.h b/lib/NeoPixelBus/src/internal/colors/RgbColorBase.h new file mode 100644 index 00000000..aef9d9a8 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/RgbColorBase.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- +RgbColorBase provides a RGB color object common support + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +struct HslColor; +struct HsbColor; +struct HtmlColor; +struct Rgb16Color; + +struct RgbColorBase +{ + +protected: + static float _CalcColor(float p, float q, float t); + + static void _HslToRgb(const HslColor& color, float* r, float* g, float* b); + + static void _HsbToRgb(const HsbColor& color, float* r, float* g, float* b); + + template static T_RESULT _Compare( + const T_COLOR& left, + const T_COLOR& right, + T_RESULT epsilon) + { + T_RESULT result = 0; + T_RESULT resultAbs = 0; + + for (size_t elem = 0; elem < T_COLOR::Count; elem++) + { + T_RESULT delta = static_cast(left[elem]) - right[elem]; + T_RESULT deltaAbs = abs(delta); + + if (deltaAbs > resultAbs) + { + resultAbs = deltaAbs; + result = delta; + } + } + + if (resultAbs > epsilon) + { + return result; + } + return 0; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/Rgbw64Color.cpp b/lib/NeoPixelBus/src/internal/colors/Rgbw64Color.cpp new file mode 100644 index 00000000..e90f7af7 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/Rgbw64Color.cpp @@ -0,0 +1,190 @@ +/*------------------------------------------------------------------------- +Rgbw64Color provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "RgbwColor.h" +#include "Rgb48Color.h" +#include "HslColor.h" +#include "HsbColor.h" +#include "Rgbw64Color.h" +#include "HtmlColor.h" + +Rgbw64Color::Rgbw64Color(const HslColor& color) +{ + Rgb48Color rgbColor(color); + *this = rgbColor; +} + +Rgbw64Color::Rgbw64Color(const HsbColor& color) +{ + Rgb48Color rgbColor(color); + *this = rgbColor; +} + +uint16_t Rgbw64Color::CalculateBrightness() const +{ + uint16_t colorB = static_cast((static_cast(R) + static_cast(G) + static_cast(B)) / 3); + if (W > colorB) + { + return W; + } + else + { + return colorB; + } +} + +Rgbw64Color Rgbw64Color::Dim(uint16_t ratio) const +{ + // specifically avoids float math + return Rgbw64Color(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio), _elementDim(W, ratio)); +} + +Rgbw64Color Rgbw64Color::Brighten(uint16_t ratio) const +{ + // specifically avoids float math + return Rgbw64Color(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio), _elementBrighten(W, ratio)); +} + +void Rgbw64Color::Darken(uint16_t delta) +{ + if (R > delta) + { + R -= delta; + } + else + { + R = 0; + } + + if (G > delta) + { + G -= delta; + } + else + { + G = 0; + } + + if (B > delta) + { + B -= delta; + } + else + { + B = 0; + } + + if (W > delta) + { + W -= delta; + } + else + { + W = 0; + } +} + +void Rgbw64Color::Lighten(uint16_t delta) +{ + if (IsColorLess()) + { + if (W < Max - delta) + { + W += delta; + } + else + { + W = Max; + } + } + else + { + if (R < Max - delta) + { + R += delta; + } + else + { + R = Max; + } + + if (G < Max - delta) + { + G += delta; + } + else + { + G = Max; + } + + if (B < Max - delta) + { + B += delta; + } + else + { + B = Max; + } + } +} + +Rgbw64Color Rgbw64Color::LinearBlend(const Rgbw64Color& left, const Rgbw64Color& right, float progress) +{ + return Rgbw64Color( left.R + ((static_cast(right.R) - left.R) * progress), + left.G + ((static_cast(right.G) - left.G) * progress), + left.B + ((static_cast(right.B) - left.B) * progress), + left.W + ((static_cast(right.W) - left.W) * progress) ); +} +Rgbw64Color Rgbw64Color::LinearBlend(const Rgbw64Color& left, const Rgbw64Color& right, uint8_t progress) +{ + return Rgbw64Color(left.R + (((static_cast(right.R) - left.R) * static_cast(progress) + 1) >> 8), + left.G + (((static_cast(right.G) - left.G) * static_cast(progress) + 1) >> 8), + left.B + (((static_cast(right.B) - left.B) * static_cast(progress) + 1) >> 8), + left.W + (((static_cast(right.W) - left.W) * static_cast(progress) + 1) >> 8)); +} + +Rgbw64Color Rgbw64Color::BilinearBlend(const Rgbw64Color& c00, + const Rgbw64Color& c01, + const Rgbw64Color& c10, + const Rgbw64Color& c11, + float x, + float y) +{ + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return Rgbw64Color( + c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11, + c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11, + c00.W * v00 + c10.W * v10 + c01.W * v01 + c11.W * v11 ); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/Rgbw64Color.h b/lib/NeoPixelBus/src/internal/colors/Rgbw64Color.h new file mode 100644 index 00000000..38509ff7 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/Rgbw64Color.h @@ -0,0 +1,324 @@ +/*------------------------------------------------------------------------- +Rgbw64Color provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +struct RgbColor; +struct HslColor; +struct HsbColor; + +// ------------------------------------------------------------------------ +// Rgbw64Color represents a color object that is represented by Red, Green, Blue +// component values and an extra White component. It contains helpful color +// routines to manipulate the color. +// ------------------------------------------------------------------------ +struct Rgbw64Color : RgbColorBase +{ + typedef NeoRgbwCurrentSettings SettingsObject; + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using R, G, B, W values (0-65535) + // ------------------------------------------------------------------------ + Rgbw64Color(uint16_t r, uint16_t g, uint16_t b, uint16_t w = 0) : + R(r), G(g), B(b), W(w) + { + }; + + // ------------------------------------------------------------------------ + // Construct a RgbColor using a single brightness value (0-65535) + // This works well for creating gray tone colors + // (0) = black, (65535) = white, (32768) = gray + // ------------------------------------------------------------------------ + Rgbw64Color(uint16_t brightness) : + R(0), G(0), B(0), W(brightness) + { + }; + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using RgbColor + // ------------------------------------------------------------------------ + Rgbw64Color(const RgbColor& color) + { + *this = Rgb48Color(color); + }; + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using Rgb48Color + // ------------------------------------------------------------------------ + Rgbw64Color(const Rgb48Color& color) : + R(color.R), + G(color.G), + B(color.B), + W(0) + { + }; + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using RgbwColor + // ------------------------------------------------------------------------ + Rgbw64Color(const RgbwColor& color) + { + // x16 = map(x8, 0, 255, 0, 65535); // refactors to just * 257 + R = (uint16_t)color.R * 257; // 257 = MAXUINT16/MAXUINT8 = 65535/255 + G = (uint16_t)color.G * 257; + B = (uint16_t)color.B * 257; + W = (uint16_t)color.W * 257; + }; + + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using HtmlColor + // ------------------------------------------------------------------------ + Rgbw64Color(const HtmlColor& color) + { + *this = RgbwColor(color); + } + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using HslColor + // ------------------------------------------------------------------------ + Rgbw64Color(const HslColor& color); + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color using HsbColor + // ------------------------------------------------------------------------ + Rgbw64Color(const HsbColor& color); + + // ------------------------------------------------------------------------ + // Construct a Rgbw64Color that will have its values set in latter operations + // CAUTION: The R,G,B, W members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + Rgbw64Color() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const Rgbw64Color& other) const + { + return (R == other.R && G == other.G && B == other.B && W == other.W); + }; + + bool operator!=(const Rgbw64Color& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // CompareTo method + // compares against another color with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - this is less than other + // positive - this is greater than other + // ------------------------------------------------------------------------ + int32_t CompareTo(const Rgbw64Color& other, uint16_t epsilon = 256) + { + return _Compare(*this, other, epsilon); + } + + // ------------------------------------------------------------------------ + // Compare method + // compares two colors with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - left is less than right + // positive - left is greater than right + // ------------------------------------------------------------------------ + static int32_t Compare(const Rgbw64Color& left, const Rgbw64Color& right, uint16_t epsilon = 256) + { + return _Compare(left, right, epsilon); + } + + // ------------------------------------------------------------------------ + // operator [] - readonly + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint16_t operator[](size_t idx) const + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + case 2: + return B; + default: + return W; + } + } + + // ------------------------------------------------------------------------ + // operator [] - read write + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint16_t& operator[](size_t idx) + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + case 2: + return B; + default: + return W; + } + } + + // ------------------------------------------------------------------------ + // Returns if the color is grey, all values are equal other than white + // ------------------------------------------------------------------------ + bool IsMonotone() const + { + return (R == B && R == G); + }; + + // ------------------------------------------------------------------------ + // Returns if the color components are all zero, the white component maybe + // anything + // ------------------------------------------------------------------------ + bool IsColorLess() const + { + return (R == 0 && B == 0 && G == 0); + }; + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint16_t CalculateBrightness() const; + + // ------------------------------------------------------------------------ + // Dim will return a new color that is blended to black with the given ratio + // ratio - (0-65535) where 65535 will return the original color and 0 will return black + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + Rgbw64Color Dim(uint16_t ratio) const; + + // ------------------------------------------------------------------------ + // Brighten will return a new color that is blended to white with the given ratio + // ratio - (0-65535) where 65535 will return the original color and 0 will return white + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + Rgbw64Color Brighten(uint16_t ratio) const; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-65535) the amount to dim the color + // ------------------------------------------------------------------------ + void Darken(uint16_t delta); + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-65535) the amount to lighten the color + // ------------------------------------------------------------------------ + void Lighten(uint16_t delta); + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static Rgbw64Color LinearBlend(const Rgbw64Color& left, const Rgbw64Color& right, float progress); + // progress - (0 - 255) value where 0 will return left and 255 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static Rgbw64Color LinearBlend(const Rgbw64Color& left, const Rgbw64Color& right, uint8_t progress); + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static Rgbw64Color BilinearBlend(const Rgbw64Color& c00, + const Rgbw64Color& c01, + const Rgbw64Color& c10, + const Rgbw64Color& c11, + float x, + float y); + + uint16_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) + { + auto total = 0; + + total += R * settings.RedTenthMilliAmpere / Max; + total += G * settings.GreenTenthMilliAmpere / Max; + total += B * settings.BlueTenthMilliAmpere / Max; + total += W * settings.WhiteTenthMilliAmpere / Max; + + return total; + } + + // ------------------------------------------------------------------------ + // Red, Green, Blue, White color members (0-65535) where + // (0,0,0,0) is black and (65535,65535,65535, 0) and (0,0,0,65535) is white + // Note (65535,65535,65535,65535) is extreme bright white + // ------------------------------------------------------------------------ + uint16_t R; + uint16_t G; + uint16_t B; + uint16_t W; + + const static uint16_t Max = 65535; + const static size_t Count = 4; // four elements in [] + +private: + inline static uint16_t _elementDim(uint16_t value, uint16_t ratio) + { + return (static_cast(value) * (static_cast(ratio) + 1)) >> 16; + } + + inline static uint16_t _elementBrighten(uint16_t value, uint16_t ratio) + { + uint32_t element = ((static_cast(value) + 1) << 16) / (static_cast(ratio) + 1); + + if (element > Max) + { + element = Max; + } + else + { + element -= 1; + } + return element; + } +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/RgbwColor.cpp b/lib/NeoPixelBus/src/internal/colors/RgbwColor.cpp new file mode 100644 index 00000000..5391583d --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/RgbwColor.cpp @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------- +RgbwColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "RgbColorBase.h" +#include "RgbColor.h" +#include "Rgb48Color.h" +#include "HslColor.h" +#include "HsbColor.h" +#include "RgbwColor.h" +#include "HtmlColor.h" + +RgbwColor::RgbwColor(const HtmlColor& color) +{ + uint32_t temp = color.Color; + B = (temp & 0xff); + temp = temp >> 8; + G = (temp & 0xff); + temp = temp >> 8; + R = (temp & 0xff); + temp = temp >> 8; + W = (temp & 0xff); +}; + +RgbwColor::RgbwColor(const HslColor& color) +{ + RgbColor rgbColor(color); + *this = rgbColor; +} + +RgbwColor::RgbwColor(const HsbColor& color) +{ + RgbColor rgbColor(color); + *this = rgbColor; +} + +uint8_t RgbwColor::CalculateBrightness() const +{ + uint8_t colorB = static_cast((static_cast(R) + static_cast(G) + static_cast(B)) / 3); + if (W > colorB) + { + return W; + } + else + { + return colorB; + } +} + +RgbwColor RgbwColor::Dim(uint8_t ratio) const +{ + // specifically avoids float math + return RgbwColor(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio), _elementDim(W, ratio)); +} + +RgbwColor RgbwColor::Brighten(uint8_t ratio) const +{ + // specifically avoids float math + return RgbwColor(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio), _elementBrighten(W, ratio)); +} + +void RgbwColor::Darken(uint8_t delta) +{ + if (R > delta) + { + R -= delta; + } + else + { + R = 0; + } + + if (G > delta) + { + G -= delta; + } + else + { + G = 0; + } + + if (B > delta) + { + B -= delta; + } + else + { + B = 0; + } + + if (W > delta) + { + W -= delta; + } + else + { + W = 0; + } +} + +void RgbwColor::Lighten(uint8_t delta) +{ + if (IsColorLess()) + { + if (W < 255 - delta) + { + W += delta; + } + else + { + W = 255; + } + } + else + { + if (R < 255 - delta) + { + R += delta; + } + else + { + R = 255; + } + + if (G < 255 - delta) + { + G += delta; + } + else + { + G = 255; + } + + if (B < 255 - delta) + { + B += delta; + } + else + { + B = 255; + } + } +} + +RgbwColor RgbwColor::LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress) +{ + return RgbwColor( left.R + ((static_cast(right.R) - left.R) * progress), + left.G + ((static_cast(right.G) - left.G) * progress), + left.B + ((static_cast(right.B) - left.B) * progress), + left.W + ((static_cast(right.W) - left.W) * progress) ); +} +RgbwColor RgbwColor::LinearBlend(const RgbwColor& left, const RgbwColor& right, uint8_t progress) +{ + return RgbwColor(left.R + (((static_cast(right.R) - left.R) * static_cast(progress) + 1) >> 8), + left.G + (((static_cast(right.G) - left.G) * static_cast(progress) + 1) >> 8), + left.B + (((static_cast(right.B) - left.B) * static_cast(progress) + 1) >> 8), + left.W + (((static_cast(right.W) - left.W) * static_cast(progress) + 1) >> 8)); +} + +RgbwColor RgbwColor::BilinearBlend(const RgbwColor& c00, + const RgbwColor& c01, + const RgbwColor& c10, + const RgbwColor& c11, + float x, + float y) +{ + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return RgbwColor( + c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11, + c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11, + c00.W * v00 + c10.W * v10 + c01.W * v01 + c11.W * v11 ); +} \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/colors/RgbwColor.h b/lib/NeoPixelBus/src/internal/colors/RgbwColor.h new file mode 100644 index 00000000..6154769f --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/RgbwColor.h @@ -0,0 +1,300 @@ +/*------------------------------------------------------------------------- +RgbwColor provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +struct RgbColor; +struct HslColor; +struct HsbColor; + +// ------------------------------------------------------------------------ +// RgbwColor represents a color object that is represented by Red, Green, Blue +// component values and an extra White component. It contains helpful color +// routines to manipulate the color. +// ------------------------------------------------------------------------ +struct RgbwColor : RgbColorBase +{ + typedef NeoRgbwCurrentSettings SettingsObject; + + // ------------------------------------------------------------------------ + // Construct a RgbwColor using R, G, B, W values (0-255) + // ------------------------------------------------------------------------ + RgbwColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) : + R(r), G(g), B(b), W(w) + { + }; + + // ------------------------------------------------------------------------ + // Construct a RgbColor using a single brightness value (0-255) + // This works well for creating gray tone colors + // (0) = black, (255) = white, (128) = gray + // ------------------------------------------------------------------------ + RgbwColor(uint8_t brightness) : + R(0), G(0), B(0), W(brightness) + { + }; + + // ------------------------------------------------------------------------ + // Construct a RgbwColor using RgbColor + // ------------------------------------------------------------------------ + RgbwColor(const RgbColor& color) : + R(color.R), + G(color.G), + B(color.B), + W(0) + { + }; + + // ------------------------------------------------------------------------ + // Construct a RgbwColor using HtmlColor + // ------------------------------------------------------------------------ + RgbwColor(const HtmlColor& color); + + // ------------------------------------------------------------------------ + // Construct a RgbwColor using HslColor + // ------------------------------------------------------------------------ + RgbwColor(const HslColor& color); + + // ------------------------------------------------------------------------ + // Construct a RgbwColor using HsbColor + // ------------------------------------------------------------------------ + RgbwColor(const HsbColor& color); + + // ------------------------------------------------------------------------ + // Construct a RgbwColor that will have its values set in latter operations + // CAUTION: The R,G,B, W members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + RgbwColor() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const RgbwColor& other) const + { + return (R == other.R && G == other.G && B == other.B && W == other.W); + }; + + bool operator!=(const RgbwColor& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // CompareTo method + // compares against another color with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - this is less than other + // positive - this is greater than other + // ------------------------------------------------------------------------ + int16_t CompareTo(const RgbwColor& other, uint8_t epsilon = 1) + { + return _Compare(*this, other, epsilon); + } + + // ------------------------------------------------------------------------ + // Compare method + // compares two colors with the given epsilon (delta allowed) + // returns the greatest difference of a set of elements, + // 0 = equal within epsilon delta + // negative - left is less than right + // positive - left is greater than right + // ------------------------------------------------------------------------ + static int16_t Compare(const RgbwColor& left, const RgbwColor& right, uint8_t epsilon = 1) + { + return _Compare(left, right, epsilon); + } + + // ------------------------------------------------------------------------ + // operator [] - readonly + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t operator[](size_t idx) const + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + case 2: + return B; + default: + return W; + } + } + + // ------------------------------------------------------------------------ + // operator [] - read write + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t& operator[](size_t idx) + { + switch (idx) + { + case 0: + return R; + case 1: + return G; + case 2: + return B; + default: + return W; + } + } + + // ------------------------------------------------------------------------ + // Returns if the color is grey, all values are equal other than white + // ------------------------------------------------------------------------ + bool IsMonotone() const + { + return (R == B && R == G); + }; + + // ------------------------------------------------------------------------ + // Returns if the color components are all zero, the white component maybe + // anything + // ------------------------------------------------------------------------ + bool IsColorLess() const + { + return (R == 0 && B == 0 && G == 0); + }; + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint8_t CalculateBrightness() const; + + // ------------------------------------------------------------------------ + // Dim will return a new color that is blended to black with the given ratio + // ratio - (0-255) where 255 will return the original color and 0 will return black + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + RgbwColor Dim(uint8_t ratio) const; + + // ------------------------------------------------------------------------ + // Brighten will return a new color that is blended to white with the given ratio + // ratio - (0-255) where 255 will return the original color and 0 will return white + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + RgbwColor Brighten(uint8_t ratio) const; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-255) the amount to dim the color + // ------------------------------------------------------------------------ + void Darken(uint8_t delta); + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-255) the amount to lighten the color + // ------------------------------------------------------------------------ + void Lighten(uint8_t delta); + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the color to start the blend at + // right - the color to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static RgbwColor LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress); + // progress - (0 - 255) value where 0 will return left and 255 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static RgbwColor LinearBlend(const RgbwColor& left, const RgbwColor& right, uint8_t progress); + + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static RgbwColor BilinearBlend(const RgbwColor& c00, + const RgbwColor& c01, + const RgbwColor& c10, + const RgbwColor& c11, + float x, + float y); + + uint16_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) + { + auto total = 0; + + total += R * settings.RedTenthMilliAmpere / Max; + total += G * settings.GreenTenthMilliAmpere / Max; + total += B * settings.BlueTenthMilliAmpere / Max; + total += W * settings.WhiteTenthMilliAmpere / Max; + + return total; + } + + // ------------------------------------------------------------------------ + // Red, Green, Blue, White color members (0-255) where + // (0,0,0,0) is black and (255,255,255, 0) and (0,0,0,255) is white + // Note (255,255,255,255) is extreme bright white + // ------------------------------------------------------------------------ + uint8_t R; + uint8_t G; + uint8_t B; + uint8_t W; + + const static uint8_t Max = 255; + const static size_t Count = 4; // four elements in [] + +private: + inline static uint8_t _elementDim(uint8_t value, uint8_t ratio) + { + return (static_cast(value) * (static_cast(ratio) + 1)) >> 8; + } + + inline static uint8_t _elementBrighten(uint8_t value, uint8_t ratio) + { + uint16_t element = ((static_cast(value) + 1) << 8) / (static_cast(ratio) + 1); + + if (element > Max) + { + element = Max; + } + else + { + element -= 1; + } + return element; + } +}; + diff --git a/lib/NeoPixelBus/src/internal/colors/SegmentDigit.cpp b/lib/NeoPixelBus/src/internal/colors/SegmentDigit.cpp new file mode 100644 index 00000000..2984cf85 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/SegmentDigit.cpp @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------- +SegmentDigit provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "SegmentDigit.h" + +// +// https://en.wikichip.org/wiki/seven-segment_display/representing_letters +// +const uint8_t SevenSegDigit::DecodeNumbers[] = { + // 0 1 2 3 4 5 6 7 8 9 + 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; + +const uint8_t SevenSegDigit::DecodeAlphaCaps[] = { + // A B C D E F G + 0x77, 0x7F, 0x39, 0x00, 0x79, 0x71, 0x3D, + // H I J K L M N + 0x76, 0x30, 0x1E, 0x00, 0x38, 0x00, 0x00, + // O P Q R S + 0x3F, 0x73, 0x00, 0x00, 0x6D, + // T U V W X Y Z + 0x00, 0x3E, 0x00, 0x00, 0x76, 0x00, 0x00 }; + +const uint8_t SevenSegDigit::DecodeAlpha[] = { + // a b c d e f g + 0x00, 0x7C, 0x58, 0x5E, 0x00, 0x00, 0x00, + // h i j k l m n + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, + // o p q r s + 0x5C, 0x00, 0x67, 0x50, 0x00, + // t u v w x y z + 0x78, 0x1C, 0x00, 0x00, 0x00, 0x6E, 0x00 }; + +const uint8_t SevenSegDigit::DecodeSpecial[] = { + // , - . / + 0x80, 0x40, 0x80, 0x40 }; + +void SevenSegDigit::init(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness) +{ + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + Segment[iSegment] = (bitmask & 0x01) ? brightness : defaultBrightness; + bitmask >>= 1; + } +} + +SevenSegDigit::SevenSegDigit(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness) +{ + init(bitmask, brightness, defaultBrightness); +}; + +SevenSegDigit::SevenSegDigit(char letter, uint8_t brightness, uint8_t defaultBrightness, bool maintainCase) +{ + if (letter >= '0' && letter <= '9') + { + init(DecodeNumbers[letter - '0'], brightness, defaultBrightness); + } + else if (letter >= 'a' && letter <= 'z') + { + uint8_t index = letter - 'a'; + uint8_t bitmask = DecodeAlpha[index]; + if (!bitmask && !maintainCase) + { + bitmask = DecodeAlphaCaps[index]; + } + init(bitmask, brightness, defaultBrightness); + } + else if (letter >= 'A' && letter <= 'Z') + { + uint8_t index = letter - 'A'; + uint8_t bitmask = DecodeAlphaCaps[index]; + if (!bitmask && !maintainCase) + { + bitmask = DecodeAlpha[index]; + } + init(bitmask, brightness, defaultBrightness); + } + else if (letter >= ',' && letter <= '/') + { + init(DecodeSpecial[letter - ','], brightness, defaultBrightness); + } + else + { + memset(Segment, defaultBrightness, sizeof(Segment)); + } +}; + +uint8_t SevenSegDigit::CalculateBrightness() const +{ + uint16_t sum = 0; + + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + sum += Segment[iSegment]; + } + + return static_cast(sum / Count); +} + +SevenSegDigit SevenSegDigit::Dim(uint8_t ratio) const +{ + SevenSegDigit result; + + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + result.Segment[iSegment] = _elementDim(Segment[iSegment], ratio); + } + return result; +} + +SevenSegDigit SevenSegDigit::Brighten(uint8_t ratio) const +{ + SevenSegDigit result; + + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + result.Segment[iSegment] = _elementBrighten(Segment[iSegment], ratio); + } + return result; +} + +void SevenSegDigit::Darken(uint8_t delta) +{ + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + uint8_t element = Segment[iSegment]; + if (element > delta) + { + element -= delta; + } + else + { + element = 0; + } + Segment[iSegment] = element; + } +} + +void SevenSegDigit::Lighten(uint8_t delta) +{ + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + uint8_t element = Segment[iSegment]; + if (element < 255 - delta) + { + element += delta; + } + else + { + element = 255; + } + Segment[iSegment] = element; + } +} + +SevenSegDigit SevenSegDigit::LinearBlend(const SevenSegDigit& left, const SevenSegDigit& right, float progress) +{ + SevenSegDigit result; + + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + result.Segment[iSegment] = left.Segment[iSegment] + ((static_cast(right.Segment[iSegment]) - left.Segment[iSegment]) * progress); + } + return result; +} + +SevenSegDigit SevenSegDigit::LinearBlend(const SevenSegDigit& left, const SevenSegDigit& right, uint8_t progress) +{ + SevenSegDigit result; + + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + result.Segment[iSegment] = left.Segment[iSegment] + + (((static_cast(right.Segment[iSegment]) - left.Segment[iSegment]) * static_cast(progress) + 1) >> 8); + } + return result; +} + diff --git a/lib/NeoPixelBus/src/internal/colors/SegmentDigit.h b/lib/NeoPixelBus/src/internal/colors/SegmentDigit.h new file mode 100644 index 00000000..abf8649c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/colors/SegmentDigit.h @@ -0,0 +1,325 @@ +/*------------------------------------------------------------------------- +SegmentDigit provides a color object that can be directly consumed by NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +enum LedSegment +{ + LedSegment_A, + LedSegment_B, + LedSegment_C, + LedSegment_D, + LedSegment_E, + LedSegment_F, + LedSegment_G, + LedSegment_Decimal, // maybe jumpered to alternate custom segment + LedSegment_Custom, // generally not used but maybe connected to a custom segment + LedSegment_COUNT +}; + +class NeoSevenSegCurrentSettings +{ +public: + NeoSevenSegCurrentSettings(uint16_t segments, uint16_t decimal, uint16_t special = 0) : + SegmentTenthMilliAmpere(segments), + DecimalTenthMilliAmpere(decimal), + SpecialTenthMilliAmpere(special) + { + } + + uint16_t SegmentTenthMilliAmpere; // in 1/10th ma + uint16_t DecimalTenthMilliAmpere; // in 1/10th ma + uint16_t SpecialTenthMilliAmpere; // in 1/10th ma +}; + +// ------------------------------------------------------------------------ +// SevenSegDigit represents a color object that is represented by the segments +// of a 7 segment LED display digit. It contains helpful routines to manipulate +// and set the elements. +// +// The order represents the physical LED location starting at A, through to G, then +// ending at the decimal point +// "abcdefg." +// ------------------------------------------------------------------------ +struct SevenSegDigit +{ + typedef NeoSevenSegCurrentSettings SettingsObject; + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit using + // the default brightness to apply to all segments + // ------------------------------------------------------------------------ + SevenSegDigit(uint8_t defaultBrightness) + { + memset(Segment, defaultBrightness, sizeof(Segment)); + } + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit using + // a bitmask for the segment (bit order is ".gfedcba") + // the brightness to apply to them, (0-255) + // the default brightness to apply to those not set in the bitmask (0-255) + // ------------------------------------------------------------------------ + SevenSegDigit(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness = 0); + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit using + // a char that will get mapped to the segments, + // the brightness to apply to them, (0-255) + // the default brightness to apply to those not set in the bitmask (0-255) + // ------------------------------------------------------------------------ + SevenSegDigit(char letter, uint8_t brightness, uint8_t defaultBrightness = 0, bool maintainCase = false); + + // ------------------------------------------------------------------------ + // Construct a SevenSegDigit that will have its values set in latter operations + // CAUTION: The members are not initialized and may not be consistent + // ------------------------------------------------------------------------ + SevenSegDigit() + { + }; + + // ------------------------------------------------------------------------ + // Comparison operators + // ------------------------------------------------------------------------ + bool operator==(const SevenSegDigit& other) const + { + for (uint8_t iSegment = 0; iSegment < Count; iSegment++) + { + if (Segment[iSegment] != other.Segment[iSegment]) + { + return false; + } + } + return true; + }; + + bool operator!=(const SevenSegDigit& other) const + { + return !(*this == other); + }; + + // ------------------------------------------------------------------------ + // operator [] - readonly + // access elements in order of the Segments + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t operator[](size_t idx) const + { + return Segment[idx]; + } + + // ------------------------------------------------------------------------ + // operator [] - read write + // access elements in order by index rather than R,G,B + // see static Count for the number of elements + // ------------------------------------------------------------------------ + uint8_t& operator[](size_t idx) + { + return Segment[idx]; + } + + // ------------------------------------------------------------------------ + // CalculateBrightness will calculate the overall brightness + // NOTE: This is a simple linear brightness + // ------------------------------------------------------------------------ + uint8_t CalculateBrightness() const; + + // ------------------------------------------------------------------------ + // Dim will return a new SevenSegDigit that is blended to off with the given ratio + // ratio - (0-255) where 255 will return the original brightness and 0 will return off + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + SevenSegDigit Dim(uint8_t ratio) const; + + // ------------------------------------------------------------------------ + // Brighten will return a new SevenSegDigit that is blended to full bright with the given ratio + // ratio - (0-255) where 255 will return the original brightness and 0 will return full brightness + // + // NOTE: This is a simple linear blend + // ------------------------------------------------------------------------ + SevenSegDigit Brighten(uint8_t ratio) const; + + // ------------------------------------------------------------------------ + // Darken will adjust the color by the given delta toward black + // NOTE: This is a simple linear change + // delta - (0-255) the amount to dim the segment + // ------------------------------------------------------------------------ + void Darken(uint8_t delta); + + // ------------------------------------------------------------------------ + // Lighten will adjust the color by the given delta toward white + // NOTE: This is a simple linear change + // delta - (0-255) the amount to lighten the segment + // ------------------------------------------------------------------------ + void Lighten(uint8_t delta); + + // ------------------------------------------------------------------------ + // LinearBlend between two colors by the amount defined by progress variable + // left - the segment to start the blend at + // right - the segment to end the blend at + // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right + // and a value between will blend the brightness of each element + // weighted linearly between them + // ------------------------------------------------------------------------ + static SevenSegDigit LinearBlend(const SevenSegDigit& left, const SevenSegDigit& right, float progress); + // progress - (0 - 255) value where 0 will return left and 255 will return right + // and a value between will blend the color weighted linearly between them + // ------------------------------------------------------------------------ + static SevenSegDigit LinearBlend(const SevenSegDigit& left, const SevenSegDigit& right, uint8_t progress); + + + uint32_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) + { + auto total = 0; + + for (uint8_t segment = LedSegment_A; segment < Count - 2; segment++) + { + total += Segment[segment] * settings.SegmentTenthMilliAmpere / Max; + } + + total += Segment[Count - 2] * settings.DecimalTenthMilliAmpere / Max; + total += Segment[Count - 1] * settings.SpecialTenthMilliAmpere / Max; + + return total; + } + + template + static void SetString(T_SET_TARGET& target, + uint16_t indexDigit, + const char* str, + uint8_t brightness, + uint8_t defaultBrightness = 0) + { + if (str == nullptr) + { + return; + } + + const char* pFirst = str; + const char* pIter = str; + + // digits are right to left + // so find the end and start there + while (*pIter != '\0') + { + pIter++; + } + pIter--; + + + while (pIter >= pFirst) + { + bool decimal = false; + bool special = false; + char value = *pIter--; + + // must always be merged by previous char + // (the one to the right) + // so if repeated ignore it + // + if (value == ':' || value == ';') + { + continue; + } + + // check if merging a decimal with the next char is required + // (the one to the left) + // + if (pIter >= pFirst && (value == '.' || value == ',')) + { + // merge a decimal as long as they aren't the same + if (*(pIter) != value) + { + decimal = true; + value = *pIter--; // use the next char + } + } + + // check next char for colon + // + if (pIter >= pFirst && (*pIter == ':' || *pIter == ';')) + { + // the colon is custom extension using the decimal AND the special + // channels + special = true; + decimal = true; + pIter--; // skip colon + } + + SevenSegDigit digit(value, brightness, defaultBrightness); + if (decimal) + { + digit.Segment[LedSegment_Decimal] = brightness; + } + if (special) + { + digit.Segment[LedSegment_Custom] = brightness; + } + target.SetPixelColor(indexDigit, digit); + indexDigit++; + } + } + + // ------------------------------------------------------------------------ + // segment members (0-255) where each represents the segment location + // and the value defines the brightnes (0) is off and (255) is full brightness + // ------------------------------------------------------------------------ + static const uint8_t Count = 9; + uint8_t Segment[Count]; + + const static uint8_t Max = 255; + + // segment decode maps from ascii relative first char in map to a bitmask of segments + // + static const uint8_t DecodeNumbers[10]; // 0-9 + static const uint8_t DecodeAlphaCaps[26]; // A-Z + static const uint8_t DecodeAlpha[26]; // a-z + static const uint8_t DecodeSpecial[4]; // , - . / + +protected: + void init(uint8_t bitmask, uint8_t brightness, uint8_t defaultBrightness); + + inline static uint8_t _elementDim(uint8_t value, uint8_t ratio) + { + return (static_cast(value) * (static_cast(ratio) + 1)) >> 8; + } + + inline static uint8_t _elementBrighten(uint8_t value, uint8_t ratio) + { + uint16_t element = ((static_cast(value) + 1) << 8) / (static_cast(ratio) + 1); + + if (element > Max) + { + element = Max; + } + else + { + element -= 1; + } + return element; + } +}; + diff --git a/lib/NeoPixelBus/src/internal/features/DotStar3Elements.h b/lib/NeoPixelBus/src/internal/features/DotStar3Elements.h new file mode 100644 index 00000000..83728f3c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStar3Elements.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- +DotStar3Elements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStar3ElementsNoSettings : public Neo4ByteRgbElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/DotStar4Elements.h b/lib/NeoPixelBus/src/internal/features/DotStar4Elements.h new file mode 100644 index 00000000..09c2653e --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStar4Elements.h @@ -0,0 +1,117 @@ +/*------------------------------------------------------------------------- +DotStar4Elements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class DotStar4Elements +{ +public: + static const size_t PixelSize = 4; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = pPixelSrc[0]; + *pPixelDest++ = pPixelSrc[1]; + *pPixelDest++ = pPixelSrc[2]; + *pPixelDest++ = pPixelSrc[3]; + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = *pPixelSrc++; + *pPixelDest++ = *pPixelSrc++; + *pPixelDest++ = *pPixelSrc++; + *pPixelDest++ = *pPixelSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + const uint8_t* pSrc = (const uint8_t*)pPixelSrc; + while (pPixelDest < pEnd) + { + *pPixelDest++ = pgm_read_byte(pSrc++); + *pPixelDest++ = pgm_read_byte(pSrc++); + *pPixelDest++ = pgm_read_byte(pSrc++); + *pPixelDest++ = pgm_read_byte(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pDestBack = pPixelDest + (count * PixelSize); + const uint8_t* pSrcBack = pPixelSrc + (count * PixelSize); + while (pDestBack > pPixelDest) + { + *--pDestBack = *--pSrcBack; + *--pDestBack = *--pSrcBack; + *--pDestBack = *--pSrcBack; + *--pDestBack = *--pSrcBack; + } + } + + typedef RgbwColor ColorObject; +}; + + +class DotStar4ElementsNoSettings : public DotStar4Elements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/DotStarBgrFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarBgrFeature.h new file mode 100644 index 00000000..30cf0d74 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarBgrFeature.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- +DotStarBgrFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class DotStarBgrFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarBrgFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarBrgFeature.h new file mode 100644 index 00000000..9edde6a1 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarBrgFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarBrgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarBrgFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.B; + *p++ = color.R; + *p = color.G; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.B = *p++; + color.R = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarGbrFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarGbrFeature.h new file mode 100644 index 00000000..f280a76b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarGbrFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarGbrFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarGbrFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.G; + *p++ = color.B; + *p = color.R; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.G = *p++; + color.B = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarGrbFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarGrbFeature.h new file mode 100644 index 00000000..6cdb266c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarGrbFeature.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- +DotStarGrbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class DotStarGrbFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.G; + *p++ = color.R; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.G = *p++; + color.R = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarLbgrFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarLbgrFeature.h new file mode 100644 index 00000000..d5cfac21 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarLbgrFeature.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- +DotStarLbgrFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class DotStarLbgrFeature : public DotStar4ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarLbrgFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarLbrgFeature.h new file mode 100644 index 00000000..8addf5d7 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarLbrgFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarLbrgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarLbrgFeature : public DotStar4ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.B; + *p++ = color.R; + *p = color.G; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.B = *p++; + color.R = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarLgbrFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarLgbrFeature.h new file mode 100644 index 00000000..0d0958a8 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarLgbrFeature.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- +DotStarLgbrFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class DotStarLgbrFeature : public DotStar4ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.G; + *p++ = color.B; + *p = color.R; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.G = *p++; + color.B = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarLgrbFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarLgrbFeature.h new file mode 100644 index 00000000..5579e39c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarLgrbFeature.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- +DotStarLgrbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class DotStarLgrbFeature : public DotStar4ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.G; + *p++ = color.R; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.G = *p++; + color.R = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarLrbgFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarLrbgFeature.h new file mode 100644 index 00000000..414f1f3d --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarLrbgFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarLrbgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarLrbgFeature : public DotStar4ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.R; + *p++ = color.B; + *p = color.G; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.R = *p++; + color.B = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarLrgbFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarLrgbFeature.h new file mode 100644 index 00000000..bcd95ee3 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarLrgbFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarLrgbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarLrgbFeature : public DotStar4ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarRbgFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarRbgFeature.h new file mode 100644 index 00000000..239d6149 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarRbgFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarRbgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarRbgFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.R; + *p++ = color.B; + *p = color.G; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.R = *p++; + color.B = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/DotStarRgbFeature.h b/lib/NeoPixelBus/src/internal/features/DotStarRgbFeature.h new file mode 100644 index 00000000..2630f13f --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/DotStarRgbFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +DotStarRgbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStars + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class DotStarRgbFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/Lpd6803BrgFeature.h b/lib/NeoPixelBus/src/internal/features/Lpd6803BrgFeature.h new file mode 100644 index 00000000..da602878 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Lpd6803BrgFeature.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- +Lpd6803BrgFeature provides feature class to describe color order and +color depth for NeoPixelBus template class when used with DotStar like chips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class Lpd6803BrgFeature : public Neo2ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint16_t color555; + + encodePixel(color.B, color.R, color.G, &color555); + *p++ = color555 >> 8; + *p = color555 & 0xff; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + uint16_t color555; + + color555 = ((*p++) << 8); + color555 |= (*p); + + decodePixel(color555, &color.B, &color.R, &color.G); + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + uint16_t color555; + + color555 = (pgm_read_byte(p++) << 8); + color555 |= pgm_read_byte(p); + + decodePixel(color555, &color.B, &color.R, &color.G); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/Lpd6803GbrFeature.h b/lib/NeoPixelBus/src/internal/features/Lpd6803GbrFeature.h new file mode 100644 index 00000000..81e25dcb --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Lpd6803GbrFeature.h @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------- +Lpd6803GbrFeature provides feature class to describe color order and +color depth for NeoPixelBus template class when used with DotStar like chips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class Lpd6803GbrFeature : public Neo2ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint16_t color555; + + encodePixel(color.G, color.B, color.R, &color555); + *p++ = color555 >> 8; + *p = color555 & 0xff; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + uint16_t color555; + + color555 = ((*p++) << 8); + color555 |= (*p); + + decodePixel(color555, &color.G, &color.B, &color.R); + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + uint16_t color555; + + color555 = (pgm_read_byte(p++) << 8); + color555 |= pgm_read_byte(p); + + decodePixel(color555, &color.G, &color.B, &color.R); + + return color; + } +}; + diff --git a/lib/NeoPixelBus/src/internal/features/Lpd6803GrbFeature.h b/lib/NeoPixelBus/src/internal/features/Lpd6803GrbFeature.h new file mode 100644 index 00000000..9799040e --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Lpd6803GrbFeature.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- +Lpd6803GrbFeature provides feature class to describe color order and +color depth for NeoPixelBus template class when used with DotStar like chips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class Lpd6803GrbFeature : public Neo2ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint16_t color555; + + encodePixel(color.G, color.R, color.B, &color555); + *p++ = color555 >> 8; + *p = color555 & 0xff; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + uint16_t color555; + + color555 = ((*p++) << 8); + color555 |= (*p); + + decodePixel(color555, &color.G, &color.R, &color.B); + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + uint16_t color555; + + color555 = (pgm_read_byte(p++) << 8); + color555 |= pgm_read_byte(p); + + decodePixel(color555, &color.G, &color.R, &color.B); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/Lpd6803RgbFeature.h b/lib/NeoPixelBus/src/internal/features/Lpd6803RgbFeature.h new file mode 100644 index 00000000..92bea7e8 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Lpd6803RgbFeature.h @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------- +Lpd6803RgbFeature provides feature class to describe color order and +color depth for NeoPixelBus template class when used with DotStar like chips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Lpd6803RgbFeature : public Neo2ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint16_t color555; + + encodePixel(color.R, color.G, color.B, &color555); + *p++ = color555 >> 8; + *p = color555 & 0xff; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + uint16_t color555; + + color555 = ((*p++) << 8); + color555 |= (*p); + + decodePixel(color555, &color.R, &color.G, &color.B); + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + uint16_t color555; + + color555 = (pgm_read_byte(p++) << 8); + color555 |= pgm_read_byte(p); + + decodePixel(color555, &color.R, &color.G, &color.B); + + return color; + } +}; + + diff --git a/lib/NeoPixelBus/src/internal/features/Lpd8806BrgFeature.h b/lib/NeoPixelBus/src/internal/features/Lpd8806BrgFeature.h new file mode 100644 index 00000000..4d3f1fb3 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Lpd8806BrgFeature.h @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- +Lpd8806BrgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStar like chips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Lpd8806BrgFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = (color.B >> 1) | 0x80; + *p++ = (color.R >> 1) | 0x80; + *p = (color.G >> 1) | 0x80; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.B = (*p++) << 1; + color.R = (*p++) << 1; + color.G = (*p) << 1; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.B = (pgm_read_byte(p++)) << 1; + color.R = (pgm_read_byte(p++)) << 1; + color.G = (pgm_read_byte(p)) << 1; + + return color; + } + +}; + diff --git a/lib/NeoPixelBus/src/internal/features/Lpd8806GrbFeature.h b/lib/NeoPixelBus/src/internal/features/Lpd8806GrbFeature.h new file mode 100644 index 00000000..36ffb6ce --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Lpd8806GrbFeature.h @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------- +Lpd8806GrbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with DotStar like chips + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Lpd8806GrbFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = (color.G >> 1) | 0x80; + *p++ = (color.R >> 1) | 0x80; + *p = (color.B >> 1) | 0x80; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.G = (*p++) << 1; + color.R = (*p++) << 1; + color.B = (*p) << 1; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.G = (pgm_read_byte(p++)) << 1; + color.R = (pgm_read_byte(p++)) << 1; + color.B = (pgm_read_byte(p)) << 1; + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/Neo2ByteElements.h b/lib/NeoPixelBus/src/internal/features/Neo2ByteElements.h new file mode 100644 index 00000000..68ba226b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo2ByteElements.h @@ -0,0 +1,122 @@ +/*------------------------------------------------------------------------- +Neo2ByteElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Neo2ByteElements +{ +public: + static const size_t PixelSize = 2; // 1 bit + 555 encoded elements + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + for (uint8_t iElement = 0; iElement < PixelSize; iElement++) + { + *pPixelDest++ = pPixelSrc[iElement]; + } + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = *pPixelSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + const uint8_t* pSrc = (const uint8_t*)pPixelSrc; + while (pPixelDest < pEnd) + { + *pPixelDest++ = pgm_read_byte(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pDestBack = pPixelDest + (count * PixelSize); + const uint8_t* pSrcBack = pPixelSrc + (count * PixelSize); + while (pDestBack > pPixelDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef RgbColor ColorObject; + +protected: + static void encodePixel(uint8_t c1, uint8_t c2, uint8_t c3, uint16_t* color555) + { + *color555 = (0x8000 | + ((c1 & 0xf8) << 7) | + ((c2 & 0xf8) << 2) | + ((c3 & 0xf8) >> 3)); + } + + static void decodePixel(uint16_t color555, uint8_t* c1, uint8_t* c2, uint8_t* c3) + { + *c1 = (color555 >> 7) & 0xf8; + *c2 = (color555 >> 2) & 0xf8; + *c3 = (color555 << 3) & 0xf8; + } +}; + +class Neo2ByteElementsNoSettings : public Neo2ByteElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/Neo3ByteElements.h b/lib/NeoPixelBus/src/internal/features/Neo3ByteElements.h new file mode 100644 index 00000000..a2a9984a --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo3ByteElements.h @@ -0,0 +1,110 @@ +/*------------------------------------------------------------------------- +Neo3ByteElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Neo3ByteElements +{ +public: + static const size_t PixelSize = 3; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + const uint8_t* pEnd = pPixelDest + (count * PixelSize); + + while (pPixelDest < pEnd) + { + for (uint8_t iElement = 0; iElement < PixelSize; iElement++) + { + *pPixelDest++ = pPixelSrc[iElement]; + } + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + const uint8_t* pEnd = pPixelDest + (count * PixelSize); + + while (pPixelDest < pEnd) + { + *pPixelDest++ = *pPixelSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + const uint8_t* pEnd = pPixelDest + (count * PixelSize); + const uint8_t* pSrc = reinterpret_cast(pPixelSrc); + + while (pPixelDest < pEnd) + { + *pPixelDest++ = pgm_read_byte(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pDestBack = pPixelDest + (count * PixelSize); + const uint8_t* pSrcBack = pPixelSrc + (count * PixelSize); + + while (pDestBack > pPixelDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef RgbColor ColorObject; +}; + +class Neo3ByteElementsNoSettings : public Neo3ByteElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/Neo4ByteElements.h b/lib/NeoPixelBus/src/internal/features/Neo4ByteElements.h new file mode 100644 index 00000000..1f78b189 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo4ByteElements.h @@ -0,0 +1,125 @@ +/*------------------------------------------------------------------------- +Neo4ByteElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Neo4ByteElementsBase +{ +public: + static const size_t PixelSize = 4; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + const uint32_t* pEnd = pDest + count; // * PixelSize / sizeof(*pDest); + + while (pDest < pEnd) + { + *pDest++ = *pSrc; + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + const uint32_t* pEnd = pDest + count; // * PixelSize / sizeof(*pDest); + + while (pDest < pEnd) + { + *pDest++ = *pSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + const uint32_t* pEnd = pDest + count; // * PixelSize / sizeof(*pDest); + + while (pDest < pEnd) + { + *pDest++ = pgm_read_dword(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + uint32_t* pDestBack = pDest + count; // * PixelSize / sizeof(*pDest); + const uint32_t* pSrcBack = pSrc + count; // * PixelSize / sizeof(*pSrc); + + while (pDestBack > pDest) + { + *--pDestBack = *--pSrcBack; + } + } + +}; + +class Neo4ByteElements : public Neo4ByteElementsBase +{ +public: + typedef RgbwColor ColorObject; +}; + +class Neo4ByteRgbElements : public Neo4ByteElementsBase +{ +public: + typedef RgbColor ColorObject; +}; + +class Neo4ByteElementsNoSettings : public Neo4ByteElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/Neo6Byte4xxElements.h b/lib/NeoPixelBus/src/internal/features/Neo6Byte4xxElements.h new file mode 100644 index 00000000..14961aa2 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo6Byte4xxElements.h @@ -0,0 +1,116 @@ +/*------------------------------------------------------------------------- +Neo6Byte4xxElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes. While it takes 6 bytes, it +only uses four and ignores the last two + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class Neo6Byte4xxElements +{ +public: + static const size_t PixelSize = 6; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + const uint16_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = *pSrc; + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + const uint16_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = *pSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + const uint16_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = pgm_read_word(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + uint16_t* pDestBack = pDest + (count * PixelSize / sizeof(*pDest)); + const uint16_t* pSrcBack = pSrc + (count * PixelSize / sizeof(*pSrc)); + + while (pDestBack > pDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef RgbwColor ColorObject; +}; + +class Neo6Byte4xxElementsNoSettings : public Neo6Byte4xxElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/Neo6ByteElements.h b/lib/NeoPixelBus/src/internal/features/Neo6ByteElements.h new file mode 100644 index 00000000..6a793fa6 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo6ByteElements.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- +Neo6ByteElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class Neo6ByteElements +{ +public: + static const size_t PixelSize = 6; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + const uint16_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = *pSrc; + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + const uint16_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = *pSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + const uint16_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = pgm_read_word(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint16_t* pDest = reinterpret_cast(pPixelDest); + const uint16_t* pSrc = reinterpret_cast(pPixelSrc); + uint16_t* pDestBack = pDest + (count * PixelSize / sizeof(*pDest)); + const uint16_t* pSrcBack = pSrc + (count * PixelSize / sizeof(*pSrc)); + + while (pDestBack > pDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef Rgb48Color ColorObject; +}; + +class Neo6ByteElementsNoSettings : public Neo6ByteElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/Neo8ByteElements.h b/lib/NeoPixelBus/src/internal/features/Neo8ByteElements.h new file mode 100644 index 00000000..041c1cb6 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo8ByteElements.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- +Neo8ByteElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class Neo8ByteElements +{ +public: + static const size_t PixelSize = 8; + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + const uint32_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = *pSrc; + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + const uint32_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = *pSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + const uint32_t* pEnd = pDest + (count * PixelSize / sizeof(*pDest)); + + while (pDest < pEnd) + { + *pDest++ = pgm_read_dword(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint32_t* pDest = reinterpret_cast(pPixelDest); + const uint32_t* pSrc = reinterpret_cast(pPixelSrc); + uint32_t* pDestBack = pDest + (count * PixelSize / sizeof(*pDest)); + const uint32_t* pSrcBack = pSrc + (count * PixelSize / sizeof(*pSrc)); + + while (pDestBack > pDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef Rgbw64Color ColorObject; +}; + +class Neo8ByteElementsNoSettings : public Neo8ByteElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/Neo9ByteElements.h b/lib/NeoPixelBus/src/internal/features/Neo9ByteElements.h new file mode 100644 index 00000000..72984226 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/Neo9ByteElements.h @@ -0,0 +1,106 @@ +/*------------------------------------------------------------------------- +Neo9ByteElements provides feature base classes to describe color elements +for NeoPixelBus Color Feature template classes + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class Neo9ByteElements +{ +public: + static const size_t PixelSize = 9; // three 3 element + + static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + static const uint8_t* getPixelAddress(const uint8_t* pPixels, uint16_t indexPixel) + { + return pPixels + indexPixel * PixelSize; + } + + static void replicatePixel(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + for (uint8_t iElement = 0; iElement < PixelSize; iElement++) + { + *pPixelDest++ = pPixelSrc[iElement]; + } + } + } + + static void movePixelsInc(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + while (pPixelDest < pEnd) + { + *pPixelDest++ = *pPixelSrc++; + } + } + + static void movePixelsInc_P(uint8_t* pPixelDest, PGM_VOID_P pPixelSrc, uint16_t count) + { + uint8_t* pEnd = pPixelDest + (count * PixelSize); + const uint8_t* pSrc = (const uint8_t*)pPixelSrc; + while (pPixelDest < pEnd) + { + *pPixelDest++ = pgm_read_byte(pSrc++); + } + } + + static void movePixelsDec(uint8_t* pPixelDest, const uint8_t* pPixelSrc, uint16_t count) + { + uint8_t* pDestBack = pPixelDest + (count * PixelSize); + const uint8_t* pSrcBack = pPixelSrc + (count * PixelSize); + while (pDestBack > pPixelDest) + { + *--pDestBack = *--pSrcBack; + } + } + + typedef SevenSegDigit ColorObject; +}; + +class Neo9ByteElementsNoSettings : public Neo9ByteElements +{ +public: + typedef NeoNoSettings SettingsObject; + static const size_t SettingsSize = 0; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoAbcdefgpsSegmentFeature.h b/lib/NeoPixelBus/src/internal/features/NeoAbcdefgpsSegmentFeature.h new file mode 100644 index 00000000..e4af4774 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoAbcdefgpsSegmentFeature.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- +NeoAbcdefgpsSegmentFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with seven segment display + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// Abcdefgps byte order +class NeoAbcdefgpsSegmentFeature : public Neo9ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint8_t commonSize = (PixelSize < color.Count) ? PixelSize : color.Count; + for (uint8_t iSegment = 0; iSegment < commonSize; iSegment++) + { + *p++ = color.Segment[iSegment]; + } + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + uint8_t commonSize = (PixelSize < color.Count) ? PixelSize : color.Count; + + for (uint8_t iSegment = 0; iSegment < commonSize; iSegment++) + { + color.Segment[iSegment] = *p++; + } + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + uint8_t commonSize = (PixelSize < color.Count) ? PixelSize : color.Count; + + for (uint8_t iSegment = 0; iSegment < commonSize; iSegment++) + { + color.Segment[iSegment] = pgm_read_byte(p++); + } + + return color; + } + +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/NeoBacedfpgsSegmentFeature.h b/lib/NeoPixelBus/src/internal/features/NeoBacedfpgsSegmentFeature.h new file mode 100644 index 00000000..092b94aa --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoBacedfpgsSegmentFeature.h @@ -0,0 +1,91 @@ +/*------------------------------------------------------------------------- +NeoBacedfpgsSegmentFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with seven segment display + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// BACEDF.G+ byte order +class NeoBacedfpgsSegmentFeature : public Neo9ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + // Segment Digit is Abcdefgps order + *p++ = color.Segment[LedSegment_B]; + *p++ = color.Segment[LedSegment_A]; + *p++ = color.Segment[LedSegment_C]; + + *p++ = color.Segment[LedSegment_E]; + *p++ = color.Segment[LedSegment_D]; + *p++ = color.Segment[LedSegment_F]; + + *p++ = color.Segment[LedSegment_Decimal]; + *p++ = color.Segment[LedSegment_G]; + *p++ = color.Segment[LedSegment_Custom]; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.Segment[LedSegment_B] = *p++; + color.Segment[LedSegment_A] = *p++; + color.Segment[LedSegment_C] = *p++; + + color.Segment[LedSegment_E] = *p++; + color.Segment[LedSegment_D] = *p++; + color.Segment[LedSegment_F] = *p++; + + color.Segment[LedSegment_Decimal] = *p++; + color.Segment[LedSegment_G] = *p++; + color.Segment[LedSegment_Custom] = *p++; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.Segment[LedSegment_B] = pgm_read_byte(p++); + color.Segment[LedSegment_A] = pgm_read_byte(p++); + color.Segment[LedSegment_C] = pgm_read_byte(p++); + + color.Segment[LedSegment_E] = pgm_read_byte(p++); + color.Segment[LedSegment_D] = pgm_read_byte(p++); + color.Segment[LedSegment_F] = pgm_read_byte(p++); + + color.Segment[LedSegment_Decimal] = pgm_read_byte(p++); + color.Segment[LedSegment_G] = pgm_read_byte(p++); + color.Segment[LedSegment_Custom] = pgm_read_byte(p++); + + return color; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoBgrFeature.h b/lib/NeoPixelBus/src/internal/features/NeoBgrFeature.h new file mode 100644 index 00000000..04ae35d7 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoBgrFeature.h @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- +NeoBgrFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class NeoBgrFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoBrgFeature.h b/lib/NeoPixelBus/src/internal/features/NeoBrgFeature.h new file mode 100644 index 00000000..374b4c63 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoBrgFeature.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- +NeoBrgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoBrgFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.B; + *p++ = color.R; + *p = color.G; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.B = *p++; + color.R = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoGrb48Feature.h b/lib/NeoPixelBus/src/internal/features/NeoGrb48Feature.h new file mode 100644 index 00000000..f53bba25 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoGrb48Feature.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- +NeoGrb48Feature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoGrb48Feature : public Neo6ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint16_t* p = reinterpret_cast(getPixelAddress(pPixels, indexPixel)); + + *p++ = color.G; + *p++ = color.R; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint16_t* p = reinterpret_cast(getPixelAddress(pPixels, indexPixel)); + + color.G = *p++; + color.R = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint16_t* p = reinterpret_cast(getPixelAddress(reinterpret_cast(pPixels), indexPixel)); + + color.G = pgm_read_word(p++); + color.R = pgm_read_word(p++); + color.B = pgm_read_word(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoGrbFeature.h b/lib/NeoPixelBus/src/internal/features/NeoGrbFeature.h new file mode 100644 index 00000000..c246e555 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoGrbFeature.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- +NeoGrbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoGrbFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.G; + *p++ = color.R; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.G = *p++; + color.R = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/NeoGrbwFeature.h b/lib/NeoPixelBus/src/internal/features/NeoGrbwFeature.h new file mode 100644 index 00000000..e6aea516 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoGrbwFeature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +NeoGrbwFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoGrbwFeature : public Neo4ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.G; + *p++ = color.R; + *p++ = color.B; + *p = color.W; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.G = *p++; + color.R = *p++; + color.B = *p++; + color.W = *p; + + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.W = pgm_read_byte(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoRbgFeature.h b/lib/NeoPixelBus/src/internal/features/NeoRbgFeature.h new file mode 100644 index 00000000..7fde3249 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoRbgFeature.h @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- +NeoRbgFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class NeoRbgFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.B; + *p = color.G; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.B = *p++; + color.G = *p; + + return color; + } + + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/NeoRgb48Feature.h b/lib/NeoPixelBus/src/internal/features/NeoRgb48Feature.h new file mode 100644 index 00000000..e9c7b632 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoRgb48Feature.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- +NeoRgb48Feature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoRgb48Feature : public Neo6ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint16_t* p = reinterpret_cast(getPixelAddress(pPixels, indexPixel)); + + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint16_t* p = reinterpret_cast(getPixelAddress(pPixels, indexPixel)); + + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint16_t* p = reinterpret_cast(getPixelAddress(reinterpret_cast(pPixels), indexPixel)); + + color.R = pgm_read_word(p++); + color.G = pgm_read_word(p++); + color.B = pgm_read_word(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoRgbFeature.h b/lib/NeoPixelBus/src/internal/features/NeoRgbFeature.h new file mode 100644 index 00000000..134af526 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoRgbFeature.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- +NeoRgbFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoRgbFeature : public Neo3ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoRgbw64Feature.h b/lib/NeoPixelBus/src/internal/features/NeoRgbw64Feature.h new file mode 100644 index 00000000..b09369e2 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoRgbw64Feature.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- +NeoRgbw64Feature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class NeoRgbw64Feature : public Neo8ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint16_t* p = reinterpret_cast(getPixelAddress(pPixels, indexPixel)); + + *p++ = color.R; + *p++ = color.G; + *p++ = color.B; + *p = color.W; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint16_t* p = reinterpret_cast(getPixelAddress(pPixels, indexPixel)); + + color.R = *p++; + color.G = *p++; + color.B = *p++; + color.W = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint16_t* p = reinterpret_cast(getPixelAddress(reinterpret_cast(pPixels), indexPixel)); + + color.R = pgm_read_word(p++); + color.G = pgm_read_word(p++); + color.B = pgm_read_word(p++); + color.W = pgm_read_word(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoRgbwFeature.h b/lib/NeoPixelBus/src/internal/features/NeoRgbwFeature.h new file mode 100644 index 00000000..7ab746be --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoRgbwFeature.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------- +NeoRgbwFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoRgbwFeature : public Neo4ByteElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.G; + *p++ = color.B; + *p = color.W; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.G = *p++; + color.B = *p++; + color.W = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.W = pgm_read_byte(p); + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoRgbwxxFeature.h b/lib/NeoPixelBus/src/internal/features/NeoRgbwxxFeature.h new file mode 100644 index 00000000..cd167abd --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoRgbwxxFeature.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- +NeoRgbwxxFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoRgbwxxFeature : public Neo6Byte4xxElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.G; + *p++ = color.B; + *p++ = color.W; + // zero the xx, this maybe unnecessary though, but its thorough + *p++ = 0; + *p = 0; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.G = *p++; + color.B = *p++; + color.W = *p; + // ignore the xx + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(reinterpret_cast(pPixels), indexPixel); + + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.W = pgm_read_byte(p); + // ignore the xx + + return color; + } +}; diff --git a/lib/NeoPixelBus/src/internal/features/NeoSm168xxColorFeatures.h b/lib/NeoPixelBus/src/internal/features/NeoSm168xxColorFeatures.h new file mode 100644 index 00000000..98dd482b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoSm168xxColorFeatures.h @@ -0,0 +1,326 @@ +/*------------------------------------------------------------------------- +NeoSm168xxColorFeatures provides feature classes to describe color order and +color depth for NeoPixelBus template class specific to the SM168xx chips/leds + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once +/* +3 channel RGB +SM16803P 1.8~60mA << need spec sheet to get accurate implementation +SM16813PB 1.8~19mA +SM16823E 60~350mA +4 channel RGBW +SM16804PB 1.5~60mA << need spec sheet to get accurate implementation +SM16804EB 1.8~19mA +SM16824E 60~350mA +*/ + +class NeoSm168x3SettingsBase : public NeoRgbCurrentSettings +{ +public: + NeoSm168x3SettingsBase(uint8_t redGain, + uint8_t greenGain, + uint8_t blueGain, + uint16_t redCurrent, + uint16_t greenCurrent, + uint16_t blueCurrent) : + NeoRgbCurrentSettings(redCurrent, greenCurrent, blueCurrent), + RedGain(redGain & 0x0f), + GreenGain(greenGain & 0x0f), + BlueGain(blueGain & 0x0f) {} + + const uint8_t RedGain : 4; + const uint8_t GreenGain : 4; + const uint8_t BlueGain : 4; +}; + +class NeoSm16803pbSettings : public NeoSm168x3SettingsBase +{ +public: + NeoSm16803pbSettings(uint8_t redGain, uint8_t greenGain, uint8_t blueGain) : + NeoSm168x3SettingsBase(redGain, + greenGain, + blueGain, + CurrentLookup[redGain], + CurrentLookup[greenGain], + CurrentLookup[blueGain]) + { + } + + void Encode(uint8_t* encoded) const + { + // 0RGB 4 bits each + encoded[0] = RedGain; + encoded[1] = GreenGain << 4 | BlueGain; + } + +protected: + static constexpr uint8_t CurrentLookup[16] = { + 18, 30, 41, 53, 64, 76, 87, 99, + 110, 133, 145, 156, 168, 179, 190}; +}; + +class NeoSm16823eSettings : public NeoSm168x3SettingsBase +{ +public: + NeoSm16823eSettings(uint8_t redGain, uint8_t greenGain, uint8_t blueGain, uint16_t resisterOhms) : + NeoSm168x3SettingsBase(redGain, + greenGain, + blueGain, + calcCurrent(resisterOhms, redGain), + calcCurrent(resisterOhms, greenGain), + calcCurrent(resisterOhms, blueGain)), + extROhms(resisterOhms) + { + } + + void Encode(uint8_t* encoded) const + { + // RGB0 4 bits each + encoded[0] = RedGain << 4 | GreenGain; + encoded[1] = BlueGain << 4; + } + +protected: + const uint16_t extROhms; + + static uint16_t calcCurrent(const uint16_t ohms, const uint8_t gain) + { + uint16_t mA = (967 * (240 + (gain * 32)) / ohms); // from spec sheet, gain 0-15 instead + return mA * 10; // return tenths of mA + } +}; + +// RGBW versions + +class NeoSm168x4SettingsBase : public NeoRgbwCurrentSettings +{ +public: + NeoSm168x4SettingsBase(uint8_t redGain, + uint8_t greenGain, + uint8_t blueGain, + uint8_t whiteGain, + uint16_t redCurrent, + uint16_t greenCurrent, + uint16_t blueCurrent, + uint16_t whiteCurrent) : + NeoRgbwCurrentSettings(redCurrent, greenCurrent, blueCurrent, whiteCurrent), + RedGain(redGain & 0x0f), + GreenGain(greenGain & 0x0f), + BlueGain(blueGain & 0x0f), + WhiteGain(whiteGain & 0x0f) {} + + const uint8_t RedGain : 4; + const uint8_t GreenGain : 4; + const uint8_t BlueGain : 4; + const uint8_t WhiteGain : 4; +}; + +class NeoSm16804ebSettings : public NeoSm168x4SettingsBase +{ +public: + NeoSm16804ebSettings(uint8_t redGain, uint8_t greenGain, uint8_t blueGain, uint8_t whiteGain) : + NeoSm168x4SettingsBase(redGain, + greenGain, + blueGain, + whiteGain, + CurrentLookup[redGain], + CurrentLookup[greenGain], + CurrentLookup[blueGain], + CurrentLookup[whiteGain]) + { + } + + void Encode(uint8_t* encoded) const + { + // RGBW 4 bits each + encoded[0] = RedGain << 4 | GreenGain; + encoded[1] = BlueGain << 4 | WhiteGain; + } + +protected: + static constexpr uint8_t CurrentLookup[16] = { + 18, 30, 41, 53, 64, 76, 87, 99, + 110, 133, 145, 156, 168, 179, 190 }; +}; + +class NeoSm16824eSettings : public NeoSm168x4SettingsBase +{ +public: + NeoSm16824eSettings(uint8_t redGain, uint8_t greenGain, uint8_t blueGain, uint8_t whiteGain, uint16_t resisterOhms) : + NeoSm168x4SettingsBase(redGain, + greenGain, + blueGain, + whiteGain, + calcCurrent(resisterOhms, redGain), + calcCurrent(resisterOhms, greenGain), + calcCurrent(resisterOhms, blueGain), + calcCurrent(resisterOhms, whiteGain)), + extROhms(resisterOhms) + { + } + + void Encode(uint8_t* encoded) const + { + // RGBW 4 bits each + encoded[0] = RedGain << 4 | GreenGain; + encoded[1] = BlueGain << 4 | WhiteGain; + } + +protected: + const uint16_t extROhms; + + static uint16_t calcCurrent(const uint16_t ohms, const uint8_t gain) + { + uint16_t mA = (1100 * (240 + (gain * 32)) / ohms); // from spec sheet, gain 0-15 instead + return mA * 10; // return tenths of mA + } + +}; + +template class NeoRgbwSm168x4Elements : public Neo4ByteElements +{ +public: + typedef T_SETTINGS SettingsObject; + static const size_t SettingsSize = 2; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + // settings are at the end of the data stream + uint8_t* pDest = pData + sizeData - SettingsSize; + + settings.Encode(pDest); + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.G; + *p++ = color.B; + *p = color.W; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.G = *p++; + color.B = *p++; + color.W = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.W = pgm_read_byte(p); + + return color; + } +}; + +template class NeoRgbSm168x3Elements : public Neo3ByteElements +{ +public: + typedef T_SETTINGS SettingsObject; + static const size_t SettingsSize = 2; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + // settings are at the end of the data stream + uint8_t* pDest = pData + sizeData - SettingsSize; + + settings.Encode(pDest); + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + return pData; + } + + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } +}; + +typedef NeoRgbSm168x3Elements NeoRgbSm16803pbColorFeature; +typedef NeoRgbSm168x3Elements NeoRgbSm16823eColorFeature; +typedef NeoRgbwSm168x4Elements NeoRgbwSm16804ebColorFeature; +typedef NeoRgbwSm168x4Elements NeoRgbwSm16824eColorFeature; + + diff --git a/lib/NeoPixelBus/src/internal/features/NeoTm1814ColorFeatures.h b/lib/NeoPixelBus/src/internal/features/NeoTm1814ColorFeatures.h new file mode 100644 index 00000000..674c4d14 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoTm1814ColorFeatures.h @@ -0,0 +1,137 @@ +/*------------------------------------------------------------------------- +NeoTm1814ColorFeatures provides feature classes to describe color order and +color depth for NeoPixelBus template class specific to the TM1814 chip + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class NeoTm1814Settings : public NeoRgbwCurrentSettings +{ +public: + NeoTm1814Settings(uint16_t red, uint16_t green, uint16_t blue, uint16_t white) : + NeoRgbwCurrentSettings(red, green, blue, white) + { + } + + const static uint16_t MinCurrent = 65; + const static uint16_t MaxCurrent = 380; + + static uint16_t LimitCurrent(uint16_t value) + { + if (value < MinCurrent) + { + value = MinCurrent; + } + else if (value > MaxCurrent) + { + value = MaxCurrent; + } + return value; + } +}; + +class Neo4ByteElementsTm1814Settings : public Neo4ByteElements +{ +private: + const static uint16_t EncodeDivisor = 5; + +public: + typedef NeoTm1814Settings SettingsObject; + static const size_t SettingsSize = 8; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + // settings are at the front of the data stream + uint8_t* pSet = pData; + + // C1 + *pSet++ = (SettingsObject::LimitCurrent(settings.WhiteTenthMilliAmpere) - SettingsObject::MinCurrent) / EncodeDivisor; + *pSet++ = (SettingsObject::LimitCurrent(settings.RedTenthMilliAmpere) - SettingsObject::MinCurrent) / EncodeDivisor; + *pSet++ = (SettingsObject::LimitCurrent(settings.GreenTenthMilliAmpere) - SettingsObject::MinCurrent) / EncodeDivisor; + *pSet++ = (SettingsObject::LimitCurrent(settings.BlueTenthMilliAmpere) - SettingsObject::MinCurrent) / EncodeDivisor; + + uint8_t* pC1 = pData; + + // C2 + for (uint8_t elem = 0; elem < 4; elem++) + { + *pSet++ = ~(*pC1++); + } + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + // settings are at the front of the data stream + return pData + SettingsSize; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + // settings are at the front of the data stream + return pData + SettingsSize; + } +}; + + +class NeoWrgbTm1814Feature : public Neo4ByteElementsTm1814Settings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.W; + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = *p++; + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; + diff --git a/lib/NeoPixelBus/src/internal/features/NeoTm1914ColorFeatures.h b/lib/NeoPixelBus/src/internal/features/NeoTm1914ColorFeatures.h new file mode 100644 index 00000000..5ea14bd5 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/NeoTm1914ColorFeatures.h @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------- +NeoTm1914ColorFeatures provides feature classes to describe color order and +color depth for NeoPixelBus template class specific to the TM1914 chip + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +enum NeoTm1914_Mode +{ + NeoTm1914_Mode_DinFdinAutoSwitch, // Switches between DIN and FDIN on any signal pause > 300ms + NeoTm1914_Mode_DinOnly, // DIN input pin used exclusively + NeoTm1914_Mode_FdinOnly // FDIN input pin used exclusively +}; + +class NeoTm1914Settings +{ +public: + NeoTm1914Settings(NeoTm1914_Mode mode = NeoTm1914_Mode_DinOnly) : + Mode(mode) + { + } + + NeoTm1914_Mode Mode; +}; + +class Neo3ByteElementsTm1914Settings : public Neo3ByteElements +{ +public: + typedef NeoTm1914Settings SettingsObject; + static const size_t SettingsSize = 6; + + static void applySettings([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData, [[maybe_unused]] const SettingsObject& settings) + { + // settings are at the front of the data stream + uint8_t* pSet = pData; + uint8_t mode = 0xff; + + // C1 - the mode + *pSet++ = 0xff; + *pSet++ = 0xff; + + switch (settings.Mode) + { + case NeoTm1914_Mode_DinFdinAutoSwitch: + mode = 0xff; + break; + + case NeoTm1914_Mode_FdinOnly: + mode = 0xfa; + break; + + case NeoTm1914_Mode_DinOnly: + default: + mode = 0xf5; + break; + } + *pSet++ = mode; + + // C2 - ones compliment of the above + uint8_t* pC1 = pData; + for (uint8_t elem = 0; elem < 3; elem++) + { + *pSet++ = ~(*pC1++); + } + } + + static uint8_t* pixels([[maybe_unused]] uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + // settings are at the front of the data stream + return pData + SettingsSize; + } + + static const uint8_t* pixels([[maybe_unused]] const uint8_t* pData, [[maybe_unused]] size_t sizeData) + { + // settings are at the front of the data stream + return pData + SettingsSize; + } +}; + + +class NeoRgbTm1914Feature : public Neo3ByteElementsTm1914Settings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; + + +class NeoGrbTm1914Feature : public Neo3ByteElementsTm1914Settings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = color.G; + *p++ = color.R; + *p = color.B; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.G = *p++; + color.R = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/features/P9813BgrFeature.h b/lib/NeoPixelBus/src/internal/features/P9813BgrFeature.h new file mode 100644 index 00000000..728c5bc3 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/features/P9813BgrFeature.h @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------- +P9813BgrFeature provides feature classes to describe color order and +color depth for NeoPixelBus template class when used with P9813s + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class P9813BgrFeature : public DotStar3ElementsNoSettings +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xC0 | ((~color.B & 0xC0) >> 2) | ((~color.G & 0xC0) >> 4) | ((~color.R & 0xC0) >> 6); + *p++ = color.B; + *p++ = color.G; + *p = color.R; + } + + static ColorObject retrievePixelColor(const uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.B = *p++; + color.G = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; + + + + + + diff --git a/lib/NeoPixelBus/src/internal/methods/DotStarEsp32DmaSpiMethod.h b/lib/NeoPixelBus/src/internal/methods/DotStarEsp32DmaSpiMethod.h new file mode 100644 index 00000000..4fef576a --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/DotStarEsp32DmaSpiMethod.h @@ -0,0 +1,332 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using Esp32, DMA and SPI (APA102). + +Written by Michael C. Miller. +DotStarEsp32DmaSpiMethod written by Louis Beaudoin (Pixelvation) + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include "driver/spi_master.h" + +// API and type use require newer IDF versions +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 1) + +template class DotStarEsp32DmaSpiMethodBase +{ +public: + typedef typename T_SPISPEED::SettingsObject SettingsObject; + + DotStarEsp32DmaSpiMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeStartFrame(4 * T_SPIBUS::ParallelBits), + _sizePixelData(pixelCount * elementSize + settingsSize), + _sizeEndFrame((pixelCount + 15) / 16 * T_SPIBUS::ParallelBits) // 16 = div 2 (bit for every two pixels) div 8 (bits to bytes) + { + _spiBufferSize = _sizeStartFrame + _sizePixelData + _sizeEndFrame; + + // must have a 4 byte aligned buffer for i2s + uint32_t alignment = _spiBufferSize % 4; + if (alignment) + { + _spiBufferSize += 4 - alignment; + } + + _data = static_cast(malloc(_spiBufferSize)); + _dmadata = static_cast(heap_caps_malloc(_spiBufferSize, MALLOC_CAP_DMA)); + + // data cleared later in NeoPixelBus::Begin() + } + + // Support constructor specifying pins by ignoring pins + DotStarEsp32DmaSpiMethodBase(uint8_t, uint8_t, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + DotStarEsp32DmaSpiMethodBase(pixelCount, elementSize, settingsSize) + { + } + + ~DotStarEsp32DmaSpiMethodBase() + { + if (_spiHandle) + { + deinitSpiDevice(); + esp_err_t ret = spi_bus_free(T_SPIBUS::SpiHostDevice); + ESP_ERROR_CHECK(ret); + } + free(_data); + heap_caps_free(_dmadata); + _spiHandle = NULL; + } + + bool IsReadyToUpdate() const + { + spi_transaction_t t; + spi_transaction_t* tptr = &t; + + esp_err_t ret = spi_device_get_trans_result(_spiHandle, &tptr, 0); + + // we are ready if prev result is back (ESP_OK) or if we got a timeout and + // transaction length of 0 (we didn't start a transaction) + return (ret == ESP_OK || (ret == ESP_ERR_TIMEOUT && 0 == _spiTransaction.length)); + } + + void Initialize(int8_t sck, int8_t dat0, int8_t dat1, int8_t dat2, int8_t dat3, int8_t dat4, int8_t dat5, int8_t dat6, int8_t dat7, int8_t ss) + { + memset(_data, 0x00, _sizeStartFrame); + memset(_data + _sizeStartFrame + _sizePixelData, 0x00, _spiBufferSize - (_sizeStartFrame + _sizePixelData)); + + _ssPin = ss; + + esp_err_t ret; + spi_bus_config_t buscfg = { 0 }; + + buscfg.sclk_io_num = sck; + buscfg.data0_io_num = dat0; + buscfg.data1_io_num = dat1; + buscfg.data2_io_num = dat2; + buscfg.data3_io_num = dat3; + buscfg.data4_io_num = dat4; + buscfg.data5_io_num = dat5; + buscfg.data6_io_num = dat6; + buscfg.data7_io_num = dat7; + buscfg.max_transfer_sz = _spiBufferSize; + if (T_SPIBUS::ParallelBits == 8) + { + buscfg.flags = SPICOMMON_BUSFLAG_OCTAL; + } + + //Initialize the SPI bus + ret = spi_bus_initialize(T_SPIBUS::SpiHostDevice, &buscfg, SPI_DMA_CH_AUTO); + ESP_ERROR_CHECK(ret); + + _spiTransaction = { 0 }; + initSpiDevice(); + } + + void Initialize(int8_t sck, int8_t dat0, int8_t dat1, int8_t dat2, int8_t dat3, int8_t ss) + { + Initialize(sck, dat0, dat1, dat2, dat3, -1, -1, -1, -1, ss); + } + + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + Initialize(sck, mosi, miso, -1, -1, ss); + } + + // If pins aren't specified, initialize bus with just the default SCK and MOSI pins for the SPI peripheral (no SS, no >1-bit pins) + void Initialize() + { +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + if (T_SPIBUS::SpiHostDevice == VSPI_HOST) + { + Initialize(SCK, -1, MOSI, -1, -1, -1); + } + else + { + Initialize(14, -1, 13, -1, -1, -1); + } +#else + Initialize(SCK, -1, MOSI, -1, -1, -1); +#endif + } + + void Update(bool) + { + while(!IsReadyToUpdate()) + { + portYIELD(); + } + + memcpy(_dmadata, _data, _spiBufferSize); + + _spiTransaction = { 0 }; + _spiTransaction.length = (_spiBufferSize) * 8; // in bits not bytes! + + if (T_SPIBUS::ParallelBits == 1) + { + _spiTransaction.flags = 0; + } + if (T_SPIBUS::ParallelBits == 2) + { + _spiTransaction.flags = SPI_TRANS_MODE_DIO; + } + if (T_SPIBUS::ParallelBits == 4) + { + _spiTransaction.flags = SPI_TRANS_MODE_QIO; + } + if (T_SPIBUS::ParallelBits == 8) + { + _spiTransaction.flags = SPI_TRANS_MODE_OCT; + } + _spiTransaction.tx_buffer = _dmadata; + + esp_err_t ret = spi_device_queue_trans(_spiHandle, &_spiTransaction, 0); //Transmit! + ESP_ERROR_CHECK(ret); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data + _sizeStartFrame; + }; + + size_t getDataSize() const + { + return _sizePixelData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _speed.applySettings(settings); + if (_spiHandle) + { + deinitSpiDevice(); + initSpiDevice(); + } + } + +private: + void initSpiDevice() + { + spi_device_interface_config_t devcfg = {0}; + + devcfg.clock_speed_hz = _speed.Clock; + devcfg.mode = 0; //SPI mode 0 + devcfg.spics_io_num = _ssPin; //CS pin + devcfg.queue_size = 1; + if (T_SPIBUS::ParallelBits == 1) + { + devcfg.flags = 0; + } + if (T_SPIBUS::ParallelBits >= 2) + { + devcfg.flags = SPI_DEVICE_HALFDUPLEX; + } + + //Allocate the LEDs on the SPI bus + esp_err_t ret = spi_bus_add_device(T_SPIBUS::SpiHostDevice, &devcfg, &_spiHandle); + ESP_ERROR_CHECK(ret); + } + + void deinitSpiDevice() + { + while(!IsReadyToUpdate()); + esp_err_t ret = spi_bus_remove_device(_spiHandle); + ESP_ERROR_CHECK(ret); + } + + const size_t _sizeStartFrame; + const size_t _sizePixelData; // Size of '_data' buffer below, minus (_sizeStartFrame + _sizeEndFrame) + const size_t _sizeEndFrame; + + size_t _spiBufferSize; + uint8_t* _data; // Holds start/end frames and LED color values + uint8_t* _dmadata; // Holds start/end frames and LED color values + spi_device_handle_t _spiHandle = NULL; + spi_transaction_t _spiTransaction; + T_SPISPEED _speed; + int8_t _ssPin; +}; + + +// Unfortunately we have a bit of a mess with SPI bus names across different version of the ESP32 +// e.g ESP32 has SPI, HSPI, and VSPI (1, 2, and 3), ESP32-S2 has SPI, FSPI, and HSPI (1, 2, and 3) +// and the S3 and C3 dropped the silly names entirely and just uses SPI1, SPI2, and SPI3. +// +// SPI1 can be only be used by ESP32 and supports up to 4 bit +// SPI2 supports up to 4 bit output across all of those devices (!) and supports 8 bit on S2 and S3 +// SPI3 supports up to 4 bit output on ESP32 and S3, and 1 bit only on the S2 + +enum spi_bus_width_t { + WIDTH1 = 1, + WIDTH2 = 2, + WIDTH4 = 4, + WIDTH8 = 8, +}; + +template +struct Esp32SpiBus +{ + const static spi_host_device_t SpiHostDevice = bus; + const static int ParallelBits = bits; +}; + +// Define all valid ESP32 SPI Buses with a default speed + +// SPI1 -- ESP32 Only +#if defined(CONFIG_IDF_TARGET_ESP32) +typedef Esp32SpiBus Esp32Spi1Bus; +typedef Esp32SpiBus Esp32Spi12BitBus; +typedef Esp32SpiBus Esp32Spi14BitBus; + +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi1Method; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi12BitMethod; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi14BitMethod; +#endif + +// SPI2 +typedef Esp32SpiBus Esp32Spi2Bus; +typedef Esp32SpiBus Esp32Spi22BitBus; +typedef Esp32SpiBus Esp32Spi24BitBus; +#if SOC_SPI_SUPPORT_OCT +typedef Esp32SpiBus Esp32Spi28BitBus; +#endif + +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi2Method; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi22BitMethod; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi24BitMethod; +#if SOC_SPI_SUPPORT_OCT +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi28BitMethod; +#endif + + +// SPI3 +#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)) +typedef Esp32SpiBus Esp32Spi3Bus; +typedef Esp32SpiBus Esp32Spi32BitBus; +typedef Esp32SpiBus Esp32Spi34BitBus; + +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi3Method; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi32BitMethod; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi34BitMethod; +#endif + +#if defined(CONFIG_IDF_TARGET_ESP32S2) +typedef Esp32SpiBus Esp32Spi3Bus; +typedef DotStarEsp32DmaSpiMethodBase DotStarEsp32DmaSpi3Method; +#endif + +// Default SpiDma methods if we don't care about bus. It's nice that every single ESP32 out there +// supports up to 4 bits on SPI2 + +typedef DotStarEsp32DmaSpi2Method DotStarEsp32DmaSpiMethod; +typedef DotStarEsp32DmaSpi22BitMethod DotStarEsp32DmaSpi2BitMethod; +typedef DotStarEsp32DmaSpi24BitMethod DotStarEsp32DmaSpi4BitMethod; +#if SOC_SPI_SUPPORT_OCT +typedef DotStarEsp32DmaSpi28BitMethod DotStarEsp32DmaSpi8BitMethod; +#endif + +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 1) \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/DotStarGenericMethod.h b/lib/NeoPixelBus/src/internal/methods/DotStarGenericMethod.h new file mode 100644 index 00000000..5470864a --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/DotStarGenericMethod.h @@ -0,0 +1,176 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using general Pins (APA102). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + + +template class DotStarMethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + DotStarMethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _sizeEndFrame((pixelCount + 15) / 16), // 16 = div 2 (bit for every two pixels) div 8 (bits to bytes) + _wire(pinClock, pinData) + { + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + DotStarMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + DotStarMethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize) + { + } +#endif + + ~DotStarMethodBase() + { + free(_data); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _wire.begin(sck, miso, mosi, ss); + } +#endif + + void Initialize() + { + _wire.begin(); + } + + void Update(bool) + { + const uint8_t startFrame[4] = { 0x00 }; + const uint8_t resetFrame[4] = { 0x00 }; + + _wire.beginTransaction(); + + // start frame + _wire.transmitBytes(startFrame, sizeof(startFrame)); + + // data + _wire.transmitBytes(_data, _sizeData); + + // reset frame + _wire.transmitBytes(resetFrame, sizeof(resetFrame)); + + // end frame + + // one bit for every two pixels with no less than 1 byte + for (size_t endFrameByte = 0; endFrameByte < _sizeEndFrame; endFrameByte++) + { + _wire.transmitByte(0x00); + } + + _wire.endTransaction(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + const size_t _sizeEndFrame; + + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values +}; + +typedef DotStarMethodBase DotStarMethod; + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) +#include "TwoWireSpiImple.h" +typedef DotStarMethodBase> DotStarSpi40MhzMethod; +typedef DotStarMethodBase> DotStarSpi20MhzMethod; +typedef DotStarMethodBase> DotStarSpi10MhzMethod; +typedef DotStarMethodBase> DotStarSpi5MhzMethod; +typedef DotStarMethodBase> DotStarSpi2MhzMethod; +typedef DotStarMethodBase> DotStarSpi1MhzMethod; +typedef DotStarMethodBase> DotStarSpi500KhzMethod; +typedef DotStarMethodBase> DotStarSpiHzMethod; + +typedef DotStarSpi10MhzMethod DotStarSpiMethod; +#endif + +#if defined(ARDUINO_ARCH_ESP32) +// Give option to use Vspi alias of Spi class if wanting to specify which SPI peripheral is used on the ESP32 +typedef DotStarMethodBase> DotStarEsp32Vspi40MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Vspi20MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Vspi10MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Vspi5MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Vspi2MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Vspi1MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Vspi500KhzMethod; +typedef DotStarMethodBase> DotStarEsp32VspiHzMethod; + +typedef DotStarSpi10MhzMethod DotStarEsp32VspiMethod; + +#include "TwoWireHspiImple.h" +typedef DotStarMethodBase> DotStarEsp32Hspi40MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Hspi20MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Hspi10MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Hspi5MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Hspi2MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Hspi1MhzMethod; +typedef DotStarMethodBase> DotStarEsp32Hspi500KhzMethod; +typedef DotStarMethodBase> DotStarEsp32HspiHzMethod; + +typedef DotStarEsp32Hspi10MhzMethod DotStarEsp32HspiMethod; +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/Esp32_i2s.c b/lib/NeoPixelBus/src/internal/methods/Esp32_i2s.c new file mode 100644 index 00000000..5893d19d --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Esp32_i2s.c @@ -0,0 +1,671 @@ +// WARNING: This file contains code that is more than likely already +// exposed from the Esp32 Arduino API. It will be removed once integration is complete. +// +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(ARDUINO_ARCH_ESP32) + +#include "sdkconfig.h" // this sets useful config symbols, like CONFIG_IDF_TARGET_ESP32C3 + +// ESP32 C3 & S3 I2S is not supported yet due to significant changes to interface +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + + + +#include +#include +#include "stdlib.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + + +#if ESP_IDF_VERSION_MAJOR>=4 +#include "esp_intr_alloc.h" +#else +#include "esp_intr.h" +#endif + +#include "rom/lldesc.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/i2s_struct.h" +#if defined(CONFIG_IDF_TARGET_ESP32) +/* included here for ESP-IDF v4.x compatibility */ +#include "soc/dport_reg.h" +#endif +#include "soc/sens_reg.h" +#include "driver/gpio.h" +#include "driver/i2s.h" + +#if !defined(CONFIG_IDF_TARGET_ESP32S3) +#include "driver/dac.h" +#endif + +#include "Esp32_i2s.h" +#include "esp32-hal.h" + +esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample); +esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, bool parallel_mode, size_t bytes_per_sample); + +#define MATRIX_DETACH_OUT_SIG 0x100 + +#if ESP_IDF_VERSION_MAJOR<=4 +#define I2S_BASE_CLK (160000000L) +#endif + +#define I2S_DMA_BLOCK_COUNT_DEFAULT 0 +// 20 bytes gives us enough time if we use single stage idle +// But it can't be longer due to non-parrallel mode and 50us reset time +// there just isn't enough silence at the end to fill more than 20 bytes +#define I2S_DMA_SILENCE_SIZE 20 // 4 byte increments +#define I2S_DMA_SILENCE_BLOCK_COUNT_FRONT 2 // two front +#define I2S_DMA_SILENCE_BLOCK_COUNT_BACK 1 // one back, required for non parallel + +typedef struct +{ + i2s_dev_t* bus; + int8_t ws; + int8_t bck; + int8_t out; + int8_t in; + + intr_handle_t isr_handle; + lldesc_t* dma_items; + size_t dma_count; + + volatile uint32_t is_sending_data; +} i2s_bus_t; + +// is_sending_data values +#define I2s_Is_Idle 0 +#define I2s_Is_Pending 1 +#define I2s_Is_Sending 2 + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +// (I2S_NUM_MAX == 2) +static i2s_bus_t I2S[I2S_NUM_MAX] = +{ + {&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle}, + {&I2S1, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle} +}; +#else +static i2s_bus_t I2S[I2S_NUM_MAX] = +{ + {&I2S0, -1, -1, -1, -1, NULL, NULL, I2S_DMA_BLOCK_COUNT_DEFAULT, I2s_Is_Idle} +}; +#endif + +void IRAM_ATTR i2sDmaISR(void* arg); + +inline void dmaItemInit(lldesc_t* item, uint8_t* posData, size_t sizeData, lldesc_t* itemNext) +{ + item->eof = 0; + item->owner = 1; + item->sosf = 0; + item->offset = 0; + item->buf = posData; + item->size = sizeData; + item->length = sizeData; + item->qe.stqe_next = itemNext; +} + +bool i2sInitDmaItems(uint8_t bus_num, uint8_t* data, size_t dataSize) +{ + if (bus_num >= I2S_NUM_MAX) + { + return false; + } + + size_t dmaCount = I2S[bus_num].dma_count; + + if (I2S[bus_num].dma_items == NULL) + { + I2S[bus_num].dma_items = (lldesc_t*)heap_caps_malloc(dmaCount * sizeof(lldesc_t), MALLOC_CAP_DMA); + if (I2S[bus_num].dma_items == NULL) + { + log_e("MEM ERROR!"); + return false; + } + } + + lldesc_t* itemFirst = &I2S[bus_num].dma_items[0]; + lldesc_t* item = itemFirst; +// lldesc_t* itemsEnd = itemFirst + I2S[bus_num].dma_count; + lldesc_t* itemNext = item + 1; + size_t dataLeft = dataSize; + uint8_t* pos = data; + // at the end of the data is the encoded silence reset + uint8_t* posSilence = data + dataSize - I2S_DMA_SILENCE_SIZE; + + // front two are silent items used for looping to micmic single fire + // default to looping + dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemNext); + dmaItemInit(itemNext, posSilence, I2S_DMA_SILENCE_SIZE, item); + item = itemNext; + itemNext++; + + // init blocks with avialable data + // + while (dataLeft) + { + item = itemNext; + itemNext++; + + size_t blockSize = dataLeft; + if (blockSize > I2S_DMA_MAX_DATA_LEN) + { + blockSize = I2S_DMA_MAX_DATA_LEN; + } + dataLeft -= blockSize; + + dmaItemInit(item, pos, blockSize, itemNext); + + pos += blockSize; + } + + // last data item is EOF to manage send state using EOF ISR + item->eof = 1; + + // last block, the back silent item, loops to front + item = itemNext; + dmaItemInit(item, posSilence, I2S_DMA_SILENCE_SIZE, itemFirst); + + return true; +} + +bool i2sDeinitDmaItems(uint8_t bus_num) +{ + if (bus_num >= I2S_NUM_MAX) + { + return false; + } + + heap_caps_free(I2S[bus_num].dma_items); + I2S[bus_num].dma_items = NULL; + + return true; +} + +// normal 4, 10, 63, 12, 16 + +esp_err_t i2sSetClock(uint8_t bus_num, + uint8_t div_num, // 4 13 + uint8_t div_b, // 10 20 + uint8_t div_a, // 63 63 + uint8_t bck, // 12 60 or 7 + uint8_t bits) // 16 8 +{ + if (bus_num >= I2S_NUM_MAX || div_a > 63 || div_b > 63 || bck > 63) + { + return ESP_FAIL; + } + + //log_i("i2sSetClock bus %u, clkm_div_num %u, clk_div_a %u, clk_div_b %u, bck_div_num %u, bits_mod %u", + // bus_num, + // div_num, + // div_a, + // div_b, + // bck, + // bits); + + i2s_dev_t* i2s = I2S[bus_num].bus; + + typeof(i2s->clkm_conf) clkm_conf; + + clkm_conf.val = 0; + +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + clkm_conf.clk_sel = 2; // APPL = 1 APB = 2 + clkm_conf.clk_en = 1; // examples of i2s show this being set if sel is set to 2 +#else + clkm_conf.clka_en = 0; +#endif + + clkm_conf.clkm_div_a = div_a; + clkm_conf.clkm_div_b = div_b; + clkm_conf.clkm_div_num = div_num; + i2s->clkm_conf.val = clkm_conf.val; + + typeof(i2s->sample_rate_conf) sample_rate_conf; + sample_rate_conf.val = 0; + sample_rate_conf.tx_bck_div_num = bck; + sample_rate_conf.rx_bck_div_num = bck; + sample_rate_conf.tx_bits_mod = bits; + sample_rate_conf.rx_bits_mod = bits; + i2s->sample_rate_conf.val = sample_rate_conf.val; + + return ESP_OK; +} + +void i2sSetPins(uint8_t bus_num, + int8_t out, + int8_t parallel, + int8_t busSampleSize, + bool invert) +{ + if (bus_num >= I2S_NUM_MAX) + { + return; + } + + if (out >= 0) + { + uint32_t i2sSignal; + + pinMode(out, OUTPUT); + +#if defined(CONFIG_IDF_TARGET_ESP32S2) + + // S2 only has one bus + // + // in parallel mode + // 8bit mode : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX + // 16bit mode : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX + // 24bit mode : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX + if (parallel == -1) + { + i2sSignal = I2S0O_DATA_OUT23_IDX; + } + else if (busSampleSize == 1) + { + i2sSignal = I2S0O_DATA_OUT16_IDX + parallel; + } + else if (busSampleSize == 2) + { + i2sSignal = I2S0O_DATA_OUT8_IDX + parallel; + } + else + { + i2sSignal = I2S0O_DATA_OUT0_IDX + parallel; + } + +#else + if (bus_num == 0) + { + // in parallel mode + // 0-7 bits : I2S0O_DATA_OUT16_IDX ~I2S0O_DATA_OUT23_IDX + // 8-15 bits : I2S0O_DATA_OUT8_IDX ~I2S0O_DATA_OUT23_IDX + // 16-23 bits : I2S0O_DATA_OUT0_IDX ~I2S0O_DATA_OUT23_IDX + if (parallel == -1) + { + i2sSignal = I2S0O_DATA_OUT23_IDX; + } + else if (parallel < 8) + { + i2sSignal = I2S0O_DATA_OUT16_IDX + parallel; + } + else if (parallel < 16) + { + i2sSignal = I2S0O_DATA_OUT8_IDX + parallel - 8; + } + else + { + i2sSignal = I2S0O_DATA_OUT0_IDX + parallel - 16; + } + } + else + { + if (parallel == -1) + { + i2sSignal = I2S1O_DATA_OUT23_IDX; + } + else + { + i2sSignal = I2S1O_DATA_OUT0_IDX + parallel; + } + } +#endif + //log_i("i2sSetPins bus %u, i2sSignal %u, pin %u, mux %u", + // bus_num, + // i2sSignal, + // out, + // parallel); + gpio_matrix_out(out, i2sSignal, invert, false); + } +} + +void i2sSetClkWsPins(uint8_t bus_num, + int8_t outClk, + bool invertClk, + int8_t outWs, + bool invertWs) +{ + + if (bus_num >= I2S_NUM_MAX) + { + return; + } + + uint32_t i2sSignalClk = I2S0O_BCK_OUT_IDX; + uint32_t i2sSignalWs = I2S0O_WS_OUT_IDX; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) + if (bus_num == 1) + { + i2sSignalClk = I2S1O_BCK_OUT_IDX; + i2sSignalWs = I2S1O_WS_OUT_IDX; + } +#endif + + if (outClk >= 0) + { + pinMode(outClk, OUTPUT); + gpio_matrix_out(outClk, i2sSignalClk, invertClk, false); + } + + if (outWs >= 0) + { + pinMode(outWs, OUTPUT); + gpio_matrix_out(outWs, i2sSignalWs, invertWs, false); + } +} + +bool i2sWriteDone(uint8_t bus_num) +{ + if (bus_num >= I2S_NUM_MAX) + { + return false; + } + + return (I2S[bus_num].is_sending_data == I2s_Is_Idle); +} + +void i2sInit(uint8_t bus_num, + bool parallel_mode, + size_t bytes_per_sample, + uint32_t sample_rate, + i2s_tx_chan_mod_t chan_mod, + i2s_tx_fifo_mod_t fifo_mod, + size_t dma_count, + uint8_t* data, + size_t dataSize) +{ + if (bus_num >= I2S_NUM_MAX) + { + return; + } + + I2S[bus_num].dma_count = dma_count + + I2S_DMA_SILENCE_BLOCK_COUNT_FRONT + + I2S_DMA_SILENCE_BLOCK_COUNT_BACK; + + if (!i2sInitDmaItems(bus_num, data, dataSize)) + { + return; + } + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +// (I2S_NUM_MAX == 2) + if (bus_num) + { + periph_module_enable(PERIPH_I2S1_MODULE); + } + else +#endif + { + periph_module_enable(PERIPH_I2S0_MODULE); + } + + esp_intr_disable(I2S[bus_num].isr_handle); + i2s_dev_t* i2s = I2S[bus_num].bus; + i2s->out_link.stop = 1; + i2s->conf.tx_start = 0; + i2s->int_ena.val = 0; + i2s->int_clr.val = 0xFFFFFFFF; + i2s->fifo_conf.dscr_en = 0; + + // reset i2s + i2s->conf.tx_reset = 1; + i2s->conf.tx_reset = 0; + i2s->conf.rx_reset = 1; + i2s->conf.rx_reset = 0; + + // reset 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; + + // reset 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; + + + // set parallel (LCD) mode + { + typeof(i2s->conf2) conf2; + conf2.val = 0; + conf2.lcd_en = parallel_mode; + conf2.lcd_tx_wrx2_en = 0; // parallel_mode; // ((parallel_mode) && (bytes_per_sample == 2)); + i2s->conf2.val = conf2.val; + } + + // Enable and configure DMA + { + typeof(i2s->lc_conf) lc_conf; + lc_conf.val = 0; + lc_conf.out_eof_mode = 1; + i2s->lc_conf.val = lc_conf.val; + } + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + i2s->pdm_conf.pcm2pdm_conv_en = 0; + i2s->pdm_conf.pdm2pcm_conv_en = 0; +#endif + // SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S); + + { + typeof(i2s->fifo_conf) fifo_conf; + + fifo_conf.val = 0; + fifo_conf.rx_fifo_mod_force_en = 1; + fifo_conf.tx_fifo_mod_force_en = 1; + fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel + fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel + fifo_conf.rx_data_num = 32; //Thresholds. + fifo_conf.tx_data_num = 32; + + i2s->fifo_conf.val = fifo_conf.val; + } + + // $REVIEW old code didn't set this + { + typeof(i2s->conf1) conf1; + conf1.val = 0; + conf1.tx_stop_en = 0; + conf1.tx_pcm_bypass = 1; + i2s->conf1.val = conf1.val; + } + + { + typeof(i2s->conf_chan) conf_chan; + conf_chan.val = 0; + conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left + conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left + i2s->conf_chan.val = conf_chan.val; + } + + { + typeof(i2s->conf) conf; + conf.val = 0; + conf.tx_msb_shift = !parallel_mode; // 0:DAC/PCM, 1:I2S + conf.tx_right_first = 0; // parallel_mode?; + i2s->conf.val = conf.val; + } + + i2s->timing.val = 0; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + i2s->pdm_conf.rx_pdm_en = 0; + i2s->pdm_conf.tx_pdm_en = 0; +#endif + + + i2sSetSampleRate(bus_num, sample_rate, parallel_mode, bytes_per_sample); + + /* */ + //Reset FIFO/DMA -> needed? Doesn't dma_reset/fifo_reset do this? + i2s->lc_conf.in_rst=1; i2s->lc_conf.out_rst=1; i2s->lc_conf.ahbm_rst=1; i2s->lc_conf.ahbm_fifo_rst=1; + i2s->lc_conf.in_rst=0; i2s->lc_conf.out_rst=0; i2s->lc_conf.ahbm_rst=0; i2s->lc_conf.ahbm_fifo_rst=0; + i2s->conf.tx_reset=1; i2s->conf.tx_fifo_reset=1; i2s->conf.rx_fifo_reset=1; + i2s->conf.tx_reset=0; i2s->conf.tx_fifo_reset=0; i2s->conf.rx_fifo_reset=0; + /* */ + + // enable intr in cpu // + int i2sIntSource; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +// (I2S_NUM_MAX == 2) + if (bus_num == 1) { + i2sIntSource = ETS_I2S1_INTR_SOURCE; + } + else +#endif + { + i2sIntSource = ETS_I2S0_INTR_SOURCE; + } + + esp_intr_alloc(i2sIntSource, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle); + // enable send intr + i2s->int_ena.out_eof = 1; + i2s->int_ena.out_dscr_err = 1; + +/* ??? */ + // Enable and configure DMA + { + typeof(i2s->lc_conf) lc_conf; + lc_conf.val = 0; + lc_conf.out_data_burst_en = 1; + lc_conf.indscr_burst_en = 1; + i2s->lc_conf.val = lc_conf.val; + } +/* */ + i2s->fifo_conf.dscr_en = 1;// enable dma + i2s->out_link.start = 0; + i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma + i2s->out_link.start = 1; // starts dma + i2s->conf.tx_start = 1;// Start I2s module + + esp_intr_enable(I2S[bus_num].isr_handle); +} + +void i2sDeinit(uint8_t bus_num) +{ + i2sDeinitDmaItems(bus_num); +} + +esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, bool parallel_mode, size_t bytes_per_sample) +{ + if (bus_num >= I2S_NUM_MAX) + { + return ESP_FAIL; + } + + uint8_t bck = 12; + + // parallel mode needs a higher sample rate + // + if (parallel_mode) + { +#if defined(CONFIG_IDF_TARGET_ESP32S2) + rate *= bytes_per_sample; + bck *= bytes_per_sample; + + //rate /= bytes_per_sample; + //bck /= bytes_per_sample; +#else + rate *= bytes_per_sample; +#endif + } + + // 160,000,000L / (100,000 * 384) + double clkmdiv = (double)I2S_BASE_CLK / ((rate * 384) + 1); + if (clkmdiv > 256.0) + { + log_e("rate is too low"); + return ESP_FAIL; + } + else if (clkmdiv < 2.0) + { + log_e("rate is too fast, clkmdiv = %f (%u, %u, %u)", + clkmdiv, + rate, + parallel_mode, + bytes_per_sample); + return ESP_FAIL; + } + + // calc integer and franctional for more precise timing + // + uint8_t clkmInteger = clkmdiv; + uint8_t clkmFraction = (clkmdiv - clkmInteger) * 63.0; + + i2sSetClock(bus_num, clkmInteger, clkmFraction, 63, bck, bytes_per_sample * 8); + + return ESP_OK; +} + +void IRAM_ATTR i2sDmaISR(void* arg) +{ + i2s_bus_t* i2s = (i2s_bus_t*)(arg); + + if (i2s->bus->int_st.out_eof) + { + // lldesc_t* item = (lldesc_t*)(i2s->bus->out_eof_des_addr); + if (i2s->is_sending_data != I2s_Is_Idle) + { + // the second item (last of the two front silent items) is + // silent looping item + lldesc_t* itemLoop = &i2s->dma_items[0]; + lldesc_t* itemLoopBreaker = itemLoop + 1; + // set to loop on silent items + itemLoopBreaker->qe.stqe_next = itemLoop; + + i2s->is_sending_data = I2s_Is_Idle; + } + } + + i2s->bus->int_clr.val = i2s->bus->int_st.val; +} + +bool i2sWrite(uint8_t bus_num) +{ + if (bus_num >= I2S_NUM_MAX) + { + return false; + } + + // the second item (last of the two front silent items) is + // silent looping item + lldesc_t* itemLoopBreaker = &I2S[bus_num].dma_items[1]; + lldesc_t* itemLoopNext = itemLoopBreaker + 1; + + // set to NOT loop on silent items + itemLoopBreaker->qe.stqe_next = itemLoopNext; + + I2S[bus_num].is_sending_data = I2s_Is_Sending; + + return true; +} + +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#endif // defined(ARDUINO_ARCH_ESP32) + diff --git a/lib/NeoPixelBus/src/internal/methods/Esp32_i2s.h b/lib/NeoPixelBus/src/internal/methods/Esp32_i2s.h new file mode 100644 index 00000000..9f961d34 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Esp32_i2s.h @@ -0,0 +1,49 @@ +#pragma once + +// ESP32C3/S3 I2S is not supported yet due to significant changes to interface +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +#define I2S_DMA_MAX_DATA_LEN 4092// maximum bytes in one dma item + +typedef enum { + I2S_CHAN_STEREO, I2S_CHAN_RIGHT_TO_LEFT, I2S_CHAN_LEFT_TO_RIGHT, I2S_CHAN_RIGHT_ONLY, I2S_CHAN_LEFT_ONLY +} i2s_tx_chan_mod_t; + +typedef enum { + I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE +} i2s_tx_fifo_mod_t; + +void i2sInit(uint8_t bus_num, + bool parallel_mode, + size_t bytes_per_sample, + uint32_t sample_rate, + i2s_tx_chan_mod_t chan_mod, + i2s_tx_fifo_mod_t fifo_mod, + size_t dma_count, + uint8_t* data, + size_t dataSize); +void i2sDeinit(uint8_t bus_num); +void i2sSetPins(uint8_t bus_num, + int8_t out, + int8_t parallel, + int8_t busSampleSize, + bool invert); +void i2sSetClkWsPins(uint8_t bus_num, + int8_t outClk, + bool invertClk, + int8_t outWs, + bool invertWs); +bool i2sWrite(uint8_t bus_num); +bool i2sWriteDone(uint8_t bus_num); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/Lpd6803GenericMethod.h b/lib/NeoPixelBus/src/internal/methods/Lpd6803GenericMethod.h new file mode 100644 index 00000000..d3f0408d --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Lpd6803GenericMethod.h @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for LPD6803 using general Pins + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + + +template class Lpd6803MethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + Lpd6803MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _sizeFrame((pixelCount + 7) / 8), // bit for every pixel at least + _wire(pinClock, pinData) + { + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + Lpd6803MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Lpd6803MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize) + { + } +#endif + + + ~Lpd6803MethodBase() + { + free(_data); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _wire.begin(sck, miso, mosi, ss); + } +#endif + + void Initialize() + { + _wire.begin(); + } + + void Update(bool) + { + const uint8_t startFrame[4] = { 0x00 }; + + _wire.beginTransaction(); + + // start frame + _wire.transmitBytes(startFrame, sizeof(startFrame)); + + // data + _wire.transmitBytes(_data, _sizeData); + + // end frame + // one bit for every pixel with no less than 1 byte + for (size_t frameByte = 0; frameByte < _sizeFrame; frameByte++) + { + _wire.transmitByte(0x00); + } + + _wire.endTransaction(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + const size_t _sizeFrame; + + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values +}; + +typedef Lpd6803MethodBase Lpd6803Method; + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) +#include "TwoWireSpiImple.h" +typedef Lpd6803MethodBase> Lpd6803Spi20MhzMethod; +typedef Lpd6803MethodBase> Lpd6803Spi10MhzMethod; +typedef Lpd6803MethodBase> Lpd6803Spi5MhzMethod; +typedef Lpd6803MethodBase> Lpd6803Spi2MhzMethod; +typedef Lpd6803MethodBase> Lpd6803Spi1MhzMethod; +typedef Lpd6803MethodBase> Lpd6803Spi500KhzMethod; + +typedef Lpd6803MethodBase> Lpd6803SpiHzMethod; + +typedef Lpd6803Spi10MhzMethod Lpd6803SpiMethod; +#endif + + + diff --git a/lib/NeoPixelBus/src/internal/methods/Lpd8806GenericMethod.h b/lib/NeoPixelBus/src/internal/methods/Lpd8806GenericMethod.h new file mode 100644 index 00000000..c4afa610 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Lpd8806GenericMethod.h @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for LPD8806 using general Pins + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + + +template class Lpd8806MethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + Lpd8806MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _sizeFrame((pixelCount + 31) / 32), + _wire(pinClock, pinData) + { + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + Lpd8806MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Lpd8806MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize) + { + } +#endif + + + ~Lpd8806MethodBase() + { + free(_data); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _wire.begin(sck, miso, mosi, ss); + } +#endif + + void Initialize() + { + _wire.begin(); + } + + void Update(bool) + { + _wire.beginTransaction(); + + // start frame + for (size_t frameByte = 0; frameByte < _sizeFrame; frameByte++) + { + _wire.transmitByte(0x00); + } + + // data + _wire.transmitBytes(_data, _sizeData); + + // end frame + for (size_t frameByte = 0; frameByte < _sizeFrame; frameByte++) + { + _wire.transmitByte(0xff); + } + + _wire.endTransaction(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + const size_t _sizeFrame; + + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values +}; + +typedef Lpd8806MethodBase Lpd8806Method; + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) +#include "TwoWireSpiImple.h" +typedef Lpd8806MethodBase> Lpd8806Spi20MhzMethod; +typedef Lpd8806MethodBase> Lpd8806Spi10MhzMethod; +typedef Lpd8806MethodBase> Lpd8806Spi5MhzMethod; +typedef Lpd8806MethodBase> Lpd8806Spi2MhzMethod; +typedef Lpd8806MethodBase> Lpd8806Spi1MhzMethod; +typedef Lpd8806MethodBase> Lpd8806Spi500KhzMethod; + +typedef Lpd8806MethodBase> Lpd8806SpiHzMethod; + +typedef Lpd8806Spi10MhzMethod Lpd8806SpiMethod; +#endif + + + diff --git a/lib/NeoPixelBus/src/internal/methods/NeoArmMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoArmMethod.h new file mode 100644 index 00000000..c68324c6 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoArmMethod.h @@ -0,0 +1,873 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for ARM MCUs. +Teensy 3.0, 3.1, LC, Arduino Due + +Written by Michael C. Miller. +Some work taken from the Adafruit NeoPixel library. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. +The contents of this file were taken from the Adafruit NeoPixel library +and modified only to fit within individual calling functions. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#if defined(__arm__) && !defined(ARDUINO_ARCH_NRF52840) + +template class NeoArmMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoArmMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin) + { + pinMode(pin, OUTPUT); + + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + + ~NeoArmMethodBase() + { + pinMode(_pin, INPUT); + + free(_data); + } + + bool IsReadyToUpdate() const + { + uint32_t delta = micros() - _endTime; + + return (delta >= T_SPEED::ResetTimeUs); + } + + void Initialize() + { + digitalWrite(_pin, LOW); + + _endTime = micros(); + } + + void Update(bool) + { + // Data latch = 50+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while (!IsReadyToUpdate()) + { + yield(); // allows for system yield if needed + } + + noInterrupts(); // Need 100% focus on instruction timing + + T_SPEED::send_pixels(_data, _sizeData, _pin); + + interrupts(); + + // save EOD time for latch on next call + _endTime = micros(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + + uint32_t _endTime; // Latch timing reference + uint8_t* _data; // Holds LED color values + uint8_t _pin; // output pin number +}; + +// Teensy 3.0 or 3.1 (3.2) or 3.5 or 3.6 +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + +class NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t CyclesT0h = (F_CPU / 4000000); + static const uint32_t CyclesT1h = (F_CPU / 1250000); + static const uint32_t Cycles = (F_CPU / 800000); +}; + +class NeoArmMk20dxSpeedPropsWs2812x : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmMk20dxSpeedPropsSk6812 : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoArmMk20dxSpeedPropsTm1814 : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmMk20dxSpeedPropsTm1829 : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmMk20dxSpeedProps800Kbps : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; +}; + +class NeoArmMk20dxSpeedProps400Kbps +{ +public: + static const uint32_t CyclesT0h = (F_CPU / 2000000); + static const uint32_t CyclesT1h = (F_CPU / 833333); + static const uint32_t Cycles = (F_CPU / 400000); + static const uint32_t ResetTimeUs = 50; +}; + +class NeoArmMk20dxSpeedPropsApa106 +{ +public: + static const uint32_t CyclesT0h = (F_CPU / 4000000); + static const uint32_t CyclesT1h = (F_CPU / 913750); + static const uint32_t Cycles = (F_CPU / 584800); + static const uint32_t ResetTimeUs = 50; +}; + +template class NeoArmMk20dxSpeedBase +{ +public: + static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; + + static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + { + uint8_t* p = pixels; + uint8_t* end = p + sizePixels; + uint8_t pix; + uint8_t mask; + + volatile uint8_t* set = portSetRegister(pin); + volatile uint8_t* clr = portClearRegister(pin); + + uint32_t cyc; + + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + + cyc = ARM_DWT_CYCCNT + T_SPEEDPROPS::Cycles; + while (p < end) + { + pix = *p++; + for (mask = 0x80; mask; mask >>= 1) + { + while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::Cycles); + + cyc = ARM_DWT_CYCCNT; + *set = 1; + if (pix & mask) + { + while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::CyclesT1h); + } + else + { + while (ARM_DWT_CYCCNT - cyc < T_SPEEDPROPS::CyclesT0h); + } + *clr = 1; + } + } + } +}; + +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; +typedef NeoArmMethodBase> NeoArmTm1814InvertedMethod; +typedef NeoArmMethodBase> NeoArmTm1829InvertedMethod; +typedef NeoArmMethodBase> NeoArmApa106Method; +typedef NeoArmMethodBase> NeoArm800KbpsMethod; +typedef NeoArmMethodBase> NeoArm400KbpsMethod; + +typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod; + +#elif defined(__MKL26Z64__) // Teensy-LC + +#if F_CPU == 48000000 + +class NeoArmMk26z64Speed800KbpsBase +{ +public: + static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + { + uint8_t* p = pixels; + uint8_t pix; + uint8_t count; + uint8_t dly; + uint8_t bitmask = digitalPinToBitMask(pin); + volatile uint8_t* reg = portSetRegister(pin); + uint32_t num = sizePixels; + + asm volatile( + "L%=_begin:" "\n\t" + "ldrb %[pix], [%[p], #0]" "\n\t" + "lsl %[pix], #24" "\n\t" + "movs %[count], #7" "\n\t" + "L%=_loop:" "\n\t" + "lsl %[pix], #1" "\n\t" + "bcs L%=_loop_one" "\n\t" + "L%=_loop_zero:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_loop_delay_T0H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T0H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_loop_delay_T0L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T0L" "\n\t" + "b L%=_next" "\n\t" + "L%=_loop_one:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_loop_delay_T1H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T1H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_loop_delay_T1L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T1L" "\n\t" + "nop" "\n\t" + "L%=_next:" "\n\t" + "sub %[count], #1" "\n\t" + "bne L%=_loop" "\n\t" + "lsl %[pix], #1" "\n\t" + "bcs L%=_last_one" "\n\t" + "L%=_last_zero:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_last_delay_T0H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T0H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #10" "\n\t" + "L%=_last_delay_T0L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T0L" "\n\t" + "b L%=_repeat" "\n\t" + "L%=_last_one:" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_last_delay_T1H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T1H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #1" "\n\t" + "L%=_last_delay_T1L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T1L" "\n\t" + "nop" "\n\t" + "L%=_repeat:" "\n\t" + "add %[p], #1" "\n\t" + "sub %[num], #1" "\n\t" + "bne L%=_begin" "\n\t" + "L%=_done:" "\n\t" + : [p] "+r" (p), + [pix] "=&r" (pix), + [count] "=&r" (count), + [dly] "=&r" (dly), + [num] "+r" (num) + : [bitmask] "r" (bitmask), + [reg] "r" (reg) + ); + } +}; + +class NeoArmMk26z64SpeedWs2812x : public NeoArmMk26z64Speed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 300; +}; + +class NeoArmMk26z64SpeedSk6812 : public NeoArmMk26z64Speed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 80; +}; + +class NeoArmMk26z64SpeedTm1814 : public NeoArmMk26z64Speed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 200; +}; + +class NeoArmMk26z64SpeedTm1829 : public NeoArmMk26z64Speed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 200; +}; + +class NeoArmMk26z64Speed800Kbps : public NeoArmMk26z64Speed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 50; +}; + +typedef NeoArmMethodBase NeoArmWs2812xMethod; +typedef NeoArmMethodBase NeoArmSk6812Method; +typedef NeoArmMethodBase NeoArmTm1814InvertedMethod; +typedef NeoArmMethodBase NeoArmTm1829InvertedMethod; +typedef NeoArmMethodBase NeoArm800KbpsMethod; +typedef NeoArm800KbpsMethod NeoArmApa106Method; +typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod; + +#else +#error "Teensy-LC: Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" +#endif // F_CPU == 48000000 + +#elif defined(__SAMD21G18A__) // Arduino Zero + + +class NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static void BitPreWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;"); + } + static void BitT1hWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } + static void BitT0lWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } + static void BitPostWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;"); + } +}; + +class NeoArmSamd21g18aSpeedPropsWs2812x : public NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmSamd21g18aSpeedPropsSk6812 : public NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoArmSamd21g18aSpeedPropsTm1814 : public NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmSamd21g18aSpeedPropsTm1829 : public NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmSamd21g18aSpeedProps800Kbps : public NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; +}; + + +class NeoArmSamd21g18aSpeedProps400Kbps +{ +public: + static void BitPreWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); + } + static void BitT1hWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;"); + } + static void BitT0lWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;"); + } + static void BitPostWait() + { + asm("nop; nop; nop; nop; nop; nop; nop;"); + } + static const uint32_t ResetTimeUs = 50; +}; + +template class NeoArmSamd21g18aSpeedBase +{ +public: + static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; + + static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + { + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + uint8_t* ptr = pixels; + uint8_t* end = ptr + sizePixels;; + uint8_t p = *ptr++; + uint8_t bitMask = 0x80; + uint8_t portNum = g_APinDescription[pin].ulPort; + uint32_t pinMask = 1ul << g_APinDescription[pin].ulPin; + + volatile uint32_t* set = &(PORT->Group[portNum].OUTSET.reg); + volatile uint32_t* clr = &(PORT->Group[portNum].OUTCLR.reg); + + for (;;) + { + *set = pinMask; + T_SPEEDPROPS::BitPreWait(); + + if (p & bitMask) + { + T_SPEEDPROPS::BitT1hWait(); + *clr = pinMask; + } + else + { + *clr = pinMask; + T_SPEEDPROPS::BitT0lWait(); + } + if (bitMask >>= 1) + { + T_SPEEDPROPS::BitPostWait(); + } + else + { + if (ptr >= end) + { + break; + } + p = *ptr++; + bitMask = 0x80; + } + } + } +}; + +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; +typedef NeoArmMethodBase> NeoArmTm1814InvertedMethod; +typedef NeoArmMethodBase> NeoArmTm1829InvertedMethod; +typedef NeoArmMethodBase> NeoArm800KbpsMethod; +typedef NeoArmMethodBase> NeoArm400KbpsMethod; +typedef NeoArm400KbpsMethod NeoArmApa106Method; +typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod; + +#elif defined(ARDUINO_STM32_FEATHER) || defined(ARDUINO_ARCH_STM32L4) || defined(ARDUINO_ARCH_STM32F4) || defined(ARDUINO_ARCH_STM32F1)// FEATHER WICED (120MHz) + +class NeoArmStm32SpeedProps800KbpsBase +{ +public: + static void BitT1hWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop;"); + } + static void BitT1lWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop;"); + } + static void BitT0hWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop;"); + } + static void BitT0lWait() + { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } +}; + +class NeoArmStm32SpeedPropsWs2812x : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmStm32SpeedPropsSk6812 : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoArmStm32SpeedPropsTm1814 : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmStm32SpeedPropsTm1829 : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmStm32SpeedProps800Kbps : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; +}; + +/* TODO - not found in Adafruit library +class NeoArmStm32SpeedProps400Kbps +{ +static void BitT1hWait() +{ +} +static void BitT1lWait() +{ +} +static void BitT0hWait() +{ +} +static void BitT0lWait() +{ +} +}; +*/ + +template class NeoArmStm32SpeedBase +{ +public: + static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; + + static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + { + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + + uint8_t* ptr = pixels; + uint8_t* end = ptr + sizePixels; + uint8_t p = *ptr++; + uint8_t bitMask = 0x80; + +#if defined(ARDUINO_STM32_FEATHER) + uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit); + + volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); + volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); + +#elif defined(ARDUINO_ARCH_STM32F4) + uint32_t pinMask = BIT(pin & 0x0f); + + volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); + volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); + +#elif defined(ARDUINO_ARCH_STM32F1) + + uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit); + + volatile uint32_t* set = &(PIN_MAP[pin].gpio_device->regs->BRR); + volatile uint32_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRR); + +#elif defined(ARDUINO_ARCH_STM32L4) + + uint32_t pinMask = g_APinDescription[pin].bit; + + GPIO_TypeDef* GPIO = static_cast(g_APinDescription[pin].GPIO); + + volatile uint32_t* set = &(GPIO->BRR); + volatile uint32_t* clr = &(GPIO->BSRR); + +#endif + for (;;) + { + if (p & bitMask) + { + // ONE + // High 800ns + *set = pinMask; + T_SPEEDPROPS::BitT1hWait(); + // Low 450ns + *clr = pinMask; + T_SPEEDPROPS::BitT1lWait(); + } + else + { + // ZERO + // High 400ns + *set = pinMask; + T_SPEEDPROPS::BitT0hWait(); + // Low 850ns + *clr = pinMask; + T_SPEEDPROPS::BitT0lWait(); + } + if (bitMask >>= 1) + { + // Move on to the next pixel + asm("nop;"); + } + else + { + if (ptr >= end) + { + break; + } + + p = *ptr++; + bitMask = 0x80; + } + } + } +}; + +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; +typedef NeoArmMethodBase> NeoArmTm1814InvertedMethod; +typedef NeoArmMethodBase> NeoArmTm1829InvertedMethod; +typedef NeoArmMethodBase> NeoArm800KbpsMethod; +typedef NeoArm800KbpsMethod NeoArmApa106Method; +typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod; + +#else // Other ARM architecture -- Presumed Arduino Due + + +#define ARM_OTHER_SCALE VARIANT_MCK / 2UL / 1000000UL +#define ARM_OTHER_INST (2UL * F_CPU / VARIANT_MCK) + +class NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t CyclesT0h = static_cast((0.40 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); + static const uint32_t CyclesT1h = static_cast((0.80 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); + static const uint32_t Cycles = static_cast((1.25 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); +}; + +class NeoArmOtherSpeedPropsWs2812x : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmOtherSpeedPropsSk6812 : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoArmOtherSpeedPropsTm1814 : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmOtherSpeedPropsTm1829 : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoArmOtherSpeedProps800Kbps : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; +}; + +class NeoArmOtherSpeedProps400Kbps +{ +public: + static const uint32_t CyclesT0h = static_cast((0.50 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); + static const uint32_t CyclesT1h = static_cast((1.20 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); + static const uint32_t Cycles = static_cast((2.50 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); + static const uint32_t ResetTimeUs = 50; +}; + +template class NeoArmOtherSpeedBase +{ +public: + static const uint32_t ResetTimeUs = T_SPEEDPROPS::ResetTimeUs; + + static void send_pixels(uint8_t* pixels, size_t sizePixels, uint8_t pin) + { + uint32_t pinMask; + uint32_t t; + Pio* port; + volatile WoReg* portSet; + volatile WoReg* portClear; + volatile WoReg* timeValue; + volatile WoReg* timeReset; + uint8_t* p; + uint8_t* end; + uint8_t pix; + uint8_t mask; + + pmc_set_writeprotect(false); + pmc_enable_periph_clk(static_cast(TC3_IRQn)); + + TC_Configure(TC1, 0, + TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1); + TC_Start(TC1, 0); + + pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into + port = g_APinDescription[pin].pPort; // declarations above. Want to + portSet = &(port->PIO_SODR); // burn a few cycles after + portClear = &(port->PIO_CODR); // starting timer to minimize + timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. + timeReset = &(TC1->TC_CHANNEL[0].TC_CCR); + p = pixels; + end = p + sizePixels; + pix = *p++; + mask = 0x80; + + for (;;) + { + if (pix & mask) + { + t = T_SPEEDPROPS::CyclesT1h; + } + else + { + t = T_SPEEDPROPS::CyclesT0h; + } + + // wait for the end of the previous cycle + while (*timeValue < T_SPEEDPROPS::Cycles); + + *portSet = pinMask; + *timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG; + + while (*timeValue < t); + + *portClear = pinMask; + if (!(mask >>= 1)) + { + // This 'inside-out' loop logic utilizes + if (p >= end) + { + break; // idle time to minimize inter-byte delays. + } + pix = *p++; + mask = 0x80; + } + } + + // not really needed as the wait for latch does this and + // while (*timeValue < T_SPEEDPROPS::Cycles); // Wait for last bit + + TC_Stop(TC1, 0); + } +}; + +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; +typedef NeoArmMethodBase> NeoArmTm1814InvertedMethod; +typedef NeoArmMethodBase> NeoArmTm1829InvertedMethod; +typedef NeoArmMethodBase> NeoArm800KbpsMethod; +typedef NeoArmMethodBase> NeoArm400KbpsMethod; +typedef NeoArm400KbpsMethod NeoArmApa106Method; +typedef NeoArmTm1814InvertedMethod NeoArmTm1914InvertedMethod; + +#endif + + +// Arm doesn't have alternatives methods yet, so only one to make the default +typedef NeoArmWs2812xMethod NeoWs2813Method; +typedef NeoArmWs2812xMethod NeoWs2812xMethod; +typedef NeoArmWs2812xMethod NeoWs2811Method; +typedef NeoArmWs2812xMethod NeoWs2816Method; +typedef NeoArmSk6812Method NeoSk6812Method; +typedef NeoArmSk6812Method NeoLc8812Method; +typedef NeoArm800KbpsMethod NeoWs2812Method; +typedef NeoArmApa106Method NeoApa106Method; +typedef NeoArmWs2812xMethod Neo800KbpsMethod; +#ifdef NeoArm400KbpsMethod // this is needed due to missing 400Kbps for some platforms +typedef NeoArm400KbpsMethod Neo400KbpsMethod; +#endif +// there is no non-invert methods for arm, but the norm for TM1814 is inverted, so +typedef NeoArmTm1814InvertedMethod NeoTm1814InvertedMethod; +typedef NeoArmTm1914InvertedMethod NeoTm1914InvertedMethod; +typedef NeoArmTm1829InvertedMethod NeoTm1829InvertedMethod; + +#endif // defined(__arm__) + diff --git a/lib/NeoPixelBus/src/internal/methods/NeoAvrMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoAvrMethod.h new file mode 100644 index 00000000..8dc0e9b2 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoAvrMethod.h @@ -0,0 +1,252 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Atmel AVR. + +Written by Michael C. Miller. +Some work taken from the Adafruit NeoPixel library. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) + +extern "C" +{ + void send_data_8mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask); + void send_data_8mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask); + void send_data_8mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask); + void send_data_12mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask); + void send_data_12mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask); + void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask); + void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask); + void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask); + void send_data_32mhz(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask, const uint8_t cycleTiming); +} + +class NeoAvrSpeed800KbpsBase +{ +public: + static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask) + { +#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU +#ifdef PORTD // PORTD isn't present on ATtiny85, etc. + if (port == &PORTD) + send_data_8mhz_800_PortD(data, sizeData, pinMask); + else if (port == &PORTB) +#endif // PORTD + send_data_8mhz_800_PortB(data, sizeData, pinMask); + +#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU +#ifdef PORTD // PORTD + if (port == &PORTD) + send_data_12mhz_800_PortD(data, sizeData, pinMask); + else if (port == &PORTB) +#endif // PORTD + send_data_12mhz_800_PortB(data, sizeData, pinMask); + +#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU + send_data_16mhz_800(data, sizeData, port, pinMask); +#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU + send_data_32mhz(data, sizeData, port, pinMask, 3); +#else +#error "CPU SPEED NOT SUPPORTED" +#endif + } + +}; + +class NeoAvrSpeedWs2812x : public NeoAvrSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoAvrSpeedSk6812 : public NeoAvrSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoAvrSpeedTm1814 : public NeoAvrSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoAvrSpeedTm1829 : public NeoAvrSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + +class NeoAvrSpeed800Kbps: public NeoAvrSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; +}; + +class NeoAvrSpeed400Kbps +{ +public: + static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask) + { +#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU + send_data_8mhz_400(data, sizeData, port, pinMask); + +#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU + send_data_12mhz_400(data, sizeData, port, pinMask); + +#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU + send_data_16mhz_400(data, sizeData, port, pinMask); +#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU + send_data_32mhz(data, sizeData, port, pinMask, 7); +#else +#error "CPU SPEED NOT SUPPORTED" +#endif + } + static const uint32_t ResetTimeUs = 50; +}; + +template class NeoAvrMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoAvrMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin), + _port(NULL), + _pinMask(0) + { + pinMode(pin, OUTPUT); + + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + + _port = portOutputRegister(digitalPinToPort(pin)); + _pinMask = digitalPinToBitMask(pin); + } + + ~NeoAvrMethodBase() + { + pinMode(_pin, INPUT); + + free(_data); + } + + bool IsReadyToUpdate() const + { + uint32_t delta = micros() - _endTime; + + return (delta >= T_SPEED::ResetTimeUs); + } + + void Initialize() + { + digitalWrite(_pin, LOW); + + _endTime = micros(); + } + + void Update(bool) + { + // Data latch = 50+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while (!IsReadyToUpdate()) + { +#if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA) + yield(); // allows for system yield if needed +#endif + } + + noInterrupts(); // Need 100% focus on instruction timing + + T_SPEED::send_data(_data, _sizeData, _port, _pinMask); + + interrupts(); + + // save EOD time for latch on next call + _endTime = micros(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // size of _data below + const uint8_t _pin; // output pin number + + uint32_t _endTime; // Latch timing reference + uint8_t* _data; // Holds data stream which include LED color values and other settings as needed + + volatile uint8_t* _port; // Output PORT register + uint8_t _pinMask; // Output PORT bitmask +}; + + +typedef NeoAvrMethodBase NeoAvrWs2812xMethod; +typedef NeoAvrMethodBase NeoAvrSk6812Method; +typedef NeoAvrMethodBase NeoAvrTm1814InvertedMethod; +typedef NeoAvrMethodBase NeoAvrTm1829InvertedMethod; +typedef NeoAvrMethodBase NeoAvr800KbpsMethod; +typedef NeoAvrMethodBase NeoAvr400KbpsMethod; +typedef NeoAvrTm1814InvertedMethod NeoAvrTm1914InvertedMethod; + +// AVR doesn't have alternatives yet, so there is just the default +typedef NeoAvrWs2812xMethod NeoWs2813Method; +typedef NeoAvrWs2812xMethod NeoWs2812xMethod; +typedef NeoAvr800KbpsMethod NeoWs2812Method; +typedef NeoAvrWs2812xMethod NeoWs2811Method; +typedef NeoAvrWs2812xMethod NeoWs2816Method; +typedef NeoAvrSk6812Method NeoSk6812Method; +typedef NeoAvrSk6812Method NeoLc8812Method; +typedef NeoAvr400KbpsMethod NeoApa106Method; +typedef NeoAvrWs2812xMethod Neo800KbpsMethod; +typedef NeoAvr400KbpsMethod Neo400KbpsMethod; + +// there is no non-invert methods for avr, but the norm for TM1814 is inverted, so +typedef NeoAvrTm1814InvertedMethod NeoTm1814InvertedMethod; +typedef NeoAvrTm1914InvertedMethod NeoTm1914InvertedMethod; +typedef NeoAvrTm1829InvertedMethod NeoTm1829InvertedMethod; +#endif + diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp32I2sMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp32I2sMethod.h new file mode 100644 index 00000000..95311dd6 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp32I2sMethod.h @@ -0,0 +1,408 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// ESP32 C3 & S3 I2S is not supported yet due to significant changes to interface +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + + +extern "C" +{ +#include "Esp32_i2s.h" +} + +const uint16_t c_dmaBytesPerPixelBytes = 4; + +class NeoEsp32I2sSpeedWs2812x +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 300; +}; + +class NeoEsp32I2sSpeedSk6812 +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 80; // ! original 80 +}; + +class NeoEsp32I2sSpeedTm1814 +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 200; +}; + +class NeoEsp32I2sSpeedTm1914 +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 200; +}; + +// ! VALUES ARE NOT CONFIRMED +// TODO - these need to be verified +// TM1934 - 320ns, 320ns, 640ns +class NeoEsp32I2sSpeedTM1934 +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 200; +}; + +class NeoEsp32I2sSpeedTm1829 +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 200; +}; + +class NeoEsp32I2sSpeed800Kbps +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sSpeed400Kbps +{ +public: + const static uint32_t I2sSampleRate = 50000; + const static uint16_t ByteSendTimeUs = 20; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sSpeedApa106 +{ +public: + const static uint32_t I2sSampleRate = 76000; + const static uint16_t ByteSendTimeUs = 14; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sBusZero +{ +public: + NeoEsp32I2sBusZero() {}; + + const static uint8_t I2sBusNumber = 0; +}; + +class NeoEsp32I2sBusOne +{ +public: + NeoEsp32I2sBusOne() {}; + + const static uint8_t I2sBusNumber = 1; +}; + +// dynamic channel support +class NeoEsp32I2sBusN +{ +public: + NeoEsp32I2sBusN(NeoBusChannel channel) : + I2sBusNumber(static_cast(channel)) + { + } + NeoEsp32I2sBusN() = delete; // no default constructor + + const uint8_t I2sBusNumber; +}; + +class NeoEsp32I2sNotInverted +{ +public: + const static bool Inverted = false; +}; + +class NeoEsp32I2sInverted +{ +public: + const static bool Inverted = true; +}; + +template class NeoEsp32I2sMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin) + { + construct(pixelCount, elementSize, settingsSize); + } + + NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin), + _bus(channel) + { + construct(pixelCount, elementSize, settingsSize); + } + + ~NeoEsp32I2sMethodBase() + { + while (!IsReadyToUpdate()) + { + yield(); + } + + i2sSetPins(_bus.I2sBusNumber, -1, -1, -1, false); + i2sDeinit(_bus.I2sBusNumber); + free(_data); + heap_caps_free(_i2sBuffer); + } + + bool IsReadyToUpdate() const + { + return (i2sWriteDone(_bus.I2sBusNumber)); + } + + void Initialize() + { + size_t dmaBlockCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN; + + i2sInit(_bus.I2sBusNumber, + false, + 2, // bytes per sample + T_SPEED::I2sSampleRate, + I2S_CHAN_STEREO, + I2S_FIFO_16BIT_DUAL, + dmaBlockCount, + _i2sBuffer, + _i2sBufferSize); + i2sSetPins(_bus.I2sBusNumber, _pin, -1, -1, T_INVERT::Inverted); + } + + void Update(bool) + { + // wait for not actively sending data + while (!IsReadyToUpdate()) + { + yield(); + } + + FillBuffers(); + + i2sWrite(_bus.I2sBusNumber); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // Size of '_data' buffer + const uint8_t _pin; // output pin number + const T_BUS _bus; // holds instance for multi bus support + + uint8_t* _data; // Holds LED color values + + size_t _i2sBufferSize; // total size of _i2sBuffer + uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc + + void construct(uint16_t pixelCount, size_t elementSize, size_t settingsSize) + { + // DMA is too fast to support a single pixel and maintain consistency + if (pixelCount < 2) + { + pixelCount = 2; + } + + uint16_t dmaSettingsSize = c_dmaBytesPerPixelBytes * settingsSize; + uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize; + uint16_t resetSize = c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs; + + _i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize + resetSize; + + // must have a 4 byte aligned buffer for i2s + uint32_t alignment = _i2sBufferSize % 4; + if (alignment) + { + _i2sBufferSize += 4 - alignment; + } + + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + + _i2sBuffer = static_cast(heap_caps_malloc(_i2sBufferSize, MALLOC_CAP_DMA)); + // no need to initialize all of it, but since it contains + // "reset" bits that don't latter get overwritten we just clear it all + memset(_i2sBuffer, 0x00, _i2sBufferSize); + } + + void FillBuffers() + { + const uint16_t bitpatterns[16] = + { + 0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, + 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, + 0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, + 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, + }; + + uint16_t* pDma = reinterpret_cast(_i2sBuffer); + uint8_t* pEnd = _data + _sizeData; + for (uint8_t* pPixel = _data; pPixel < pEnd; pPixel++) + { + *(pDma++) = bitpatterns[((*pPixel) & 0x0f)]; + *(pDma++) = bitpatterns[((*pPixel) >> 4) & 0x0f]; + } + } +}; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Ws2812xMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Sk6812Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Tm1814Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Tm1829Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Tm1914Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0TM1934Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0400KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Apa106Method; + + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Ws2812xInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Sk6812InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Tm1814InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Tm1914InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0TM1934InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Tm1829InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0800KbpsInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0400KbpsInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Apa106InvertedMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) +// (I2S_NUM_MAX == 2) + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Ws2812xMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Sk6812Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Tm1814Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1TM1934Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Tm1829Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Tm1914Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1400KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Apa106Method; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Ws2812xInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Sk6812InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Tm1814InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1TM1934InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Tm1829InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Tm1914InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1800KbpsInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1400KbpsInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Apa106InvertedMethod; + + +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNWs2812xMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNSk6812Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTm1814Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTM1934Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTm1829Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTm1914Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sN800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sN400KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNApa106Method; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNWs2812xInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNSk6812InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTm1814InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTM1934InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTm1829InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNTm1914InvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sN800KbpsInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sN400KbpsInvertedMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2sNApa106InvertedMethod; + +#endif + +#if !defined(NEOPIXEL_ESP32_RMT_DEFAULT) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +// I2s Bus 1 method is the default method for Esp32 +// Esp32 S2 & C3 & S3 will use RMT as the default allways +typedef NeoEsp32I2s1Ws2812xMethod NeoWs2813Method; +typedef NeoEsp32I2s1Ws2812xMethod NeoWs2812xMethod; +typedef NeoEsp32I2s1800KbpsMethod NeoWs2812Method; +typedef NeoEsp32I2s1Ws2812xMethod NeoWs2811Method; +typedef NeoEsp32I2s1Ws2812xMethod NeoWs2816Method; +typedef NeoEsp32I2s1Sk6812Method NeoSk6812Method; +typedef NeoEsp32I2s1Tm1814Method NeoTm1814Method; +typedef NeoEsp32I2s1TM1934Method NeoTM1934Method; +typedef NeoEsp32I2s1Tm1829Method NeoTm1829Method; +typedef NeoEsp32I2s1Tm1914Method NeoTm1914Method; +typedef NeoEsp32I2s1Sk6812Method NeoLc8812Method; +typedef NeoEsp32I2s1Apa106Method NeoApa106Method; + +typedef NeoEsp32I2s1Ws2812xMethod Neo800KbpsMethod; +typedef NeoEsp32I2s1400KbpsMethod Neo400KbpsMethod; + +typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2813InvertedMethod; +typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2812xInvertedMethod; +typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2811InvertedMethod; +typedef NeoEsp32I2s1Ws2812xInvertedMethod NeoWs2816InvertedMethod; +typedef NeoEsp32I2s1800KbpsInvertedMethod NeoWs2812InvertedMethod; +typedef NeoEsp32I2s1Sk6812InvertedMethod NeoSk6812InvertedMethod; +typedef NeoEsp32I2s1Tm1814InvertedMethod NeoTm1814InvertedMethod; +typedef NeoEsp32I2s1TM1934InvertedMethod NeoTM1934InvertedMethod; +typedef NeoEsp32I2s1Tm1829InvertedMethod NeoTm1829InvertedMethod; +typedef NeoEsp32I2s1Tm1914InvertedMethod NeoTm1914InvertedMethod; +typedef NeoEsp32I2s1Sk6812InvertedMethod NeoLc8812InvertedMethod; +typedef NeoEsp32I2s1Apa106InvertedMethod NeoApa106InvertedMethod; + +typedef NeoEsp32I2s1Ws2812xInvertedMethod Neo800KbpsInvertedMethod; +typedef NeoEsp32I2s1400KbpsInvertedMethod Neo400KbpsInvertedMethod; + +#endif // !defined(NEOPIXEL_ESP32_RMT_DEFAULT) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp32I2sXMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp32I2sXMethod.h new file mode 100644 index 00000000..2a5a91c0 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp32I2sXMethod.h @@ -0,0 +1,757 @@ +#pragma once + +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +// ESP32C3/S3 I2S is not supported yet due to significant changes to interface +#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +extern "C" +{ +#include "Esp32_i2s.h" +} + +#pragma once + +// ESP32 Endian Map +// uint16_t +// 1234 +// 3412 +// uint32_t +// 12345678 +// 78563412 +// uint64_t +// 0123456789abcdef +// efcdab8967452301 + +// +// true size of mux channel, 8 bit +// +class NeoEspI2sMuxBusSize8Bit +{ +public: + NeoEspI2sMuxBusSize8Bit() {}; + + const static size_t MuxBusDataSize = 1; + + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId) + { +#if defined(CONFIG_IDF_TARGET_ESP32S2) + // 1234 - order + // 3412 = actual due to endianness + // 00000001 + const uint32_t EncodedZeroBit = 0x00000100; + // 00010101 + const uint32_t EncodedOneBit = 0x01000101; +#else + // 8 channel bits layout for DMA 32bit value + // note, right to left + // mux bus bit/id 76543210 76543210 76543210 76543210 + // encode bit # 3 2 1 0 + // value zero 0 0 0 1 + // value one 0 1 1 1 + // + // due to indianness between peripheral and cpu, bytes within the words are swapped in the const + // 1234 - order + // 3412 = actual due to endianness + // 00000001 + const uint32_t EncodedZeroBit = 0x00010000; + // 00010101 + const uint32_t EncodedOneBit = 0x01010001; +#endif + + uint32_t* pDma = reinterpret_cast(dmaBuffer); + const uint8_t* pEnd = data + sizeData; + + for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++) + { + uint8_t value = *pPixel; + + for (uint8_t bit = 0; bit < 8; bit++) + { + uint32_t dma = *(pDma); + + dma |= (((value & 0x80) ? EncodedOneBit : EncodedZeroBit) << (muxId)); + *(pDma++) = dma; + value <<= 1; + } + } + } +}; + +// +// true size of mux channel, 16 bit +// +class NeoEspI2sMuxBusSize16Bit +{ +public: + NeoEspI2sMuxBusSize16Bit() {}; + + const static size_t MuxBusDataSize = 2; + + static void EncodeIntoDma(uint8_t* dmaBuffer, const uint8_t* data, size_t sizeData, uint8_t muxId) + { +#if defined(CONFIG_IDF_TARGET_ESP32S2) + // 1234 5678 - order + // 3412 7856 = actual due to endianness + // not swap 0000000000000001 + const uint64_t EncodedZeroBit64 = 0x0000000000010000; + // no swap 0000000100010001 + const uint64_t EncodedOneBit64 = 0x0001000000010001; + // can be shifted by 8! + Fillx16(dmaBuffer, + data, + sizeData, + muxId, + EncodedZeroBit64, + EncodedOneBit64); +#else + + // 16 channel bits layout for DMA 64bit value + // note, right to left, destination is 32bit chunks + // due to indianness between peripheral and cpu, + // bytes within the words are swapped and words within dwords + // in the literal constants + // { } { } + // 0123 4567 89ab cdef - order of bytes in literal constant + // efcd ab89 6745 2301 - order of memory on ESP32 due to Endianness + // 6745 2301 efcd ab89 - 32bit dest means only map using 32bits so swap upper and lower + // + // Due to final bit locations, can't shift encoded one bit + // either left more than 7 or right more than 7 so we have to + // split the updates and use different encodings + if (muxId < 8) + { + // endian + dest swap 0000000000000001 + const uint64_t EncodedZeroBit64 = 0x0000000001000000; + // endian + dest swap 0000000100010001 + const uint64_t EncodedOneBit64 = 0x0100000001000100; + // cant be shifted by 8! + Fillx16(dmaBuffer, + data, + sizeData, + muxId, + EncodedZeroBit64, + EncodedOneBit64); + } + else + { + // endian + dest swap 0000000000000001 + // then pre shift by 8 0000000000000100 + const uint64_t EncodedZeroBit64 = 0x0000000000010000; + // endian + dest swap 0000000100010001 + // then pre shift by 8 0000010001000100 + const uint64_t EncodedOneBit64 = 0x0001000000010001; + Fillx16(dmaBuffer, + data, + sizeData, + muxId - 8, // preshifted + EncodedZeroBit64, + EncodedOneBit64); + } +#endif + } + +protected: + static void Fillx16(uint8_t* dmaBuffer, + const uint8_t* data, + size_t sizeData, + uint8_t muxShift, + const uint64_t EncodedZeroBit64, + const uint64_t EncodedOneBit64) + { + uint64_t* pDma64 = reinterpret_cast(dmaBuffer); + const uint8_t* pEnd = data + sizeData; + + for (const uint8_t* pPixel = data; pPixel < pEnd; pPixel++) + { + uint8_t value = *pPixel; + + for (uint8_t bit = 0; bit < 8; bit++) + { + uint64_t dma64 = *(pDma64); + + dma64 |= (((value & 0x80) ? EncodedOneBit64 : EncodedZeroBit64) << (muxShift)); + *(pDma64++) = dma64; + value <<= 1; + } + } + } +}; + +// +// tracks mux channels used and if updated +// +// T_FLAG - type used to store bit flags, UINT8_t for 8 channels, UINT16_t for 16 +// T_MUXSIZE - true size of mux channel = NeoEspI2sMuxBusSize8Bit or NeoEspI2sMuxBusSize16Bit +// +template class NeoEspI2sMuxMap : public T_MUXSIZE +{ +public: + const static uint8_t InvalidMuxId = -1; + const static size_t BusMaxCount = sizeof(T_FLAG) * 8; + + size_t MaxBusDataSize; // max size of stream data from any single mux bus + T_FLAG UpdateMap; // bitmap flags of mux buses to track update state + T_FLAG UpdateMapMask; // mask to used bits in s_UpdateMap + T_FLAG BusCount; // count of mux buses + + // as a static instance, all members get initialized to zero + // and the constructor is called at inconsistent time to other globals + // so its not useful to have or rely on, + // but without it presence they get zeroed far too late + NeoEspI2sMuxMap() + // //: + // //MaxBusDataSize(0), + // //UpdateMap(0), + // //UpdateMapMask(0), + // //BusCount(0) + { + } + + uint8_t RegisterNewMuxBus(const size_t dataSize) + { + // find first available bus id + uint8_t muxId = 0; + while (muxId < BusMaxCount) + { + T_FLAG muxIdField = (1 << muxId); + if ((UpdateMapMask & muxIdField) == 0) + { + // complete registration + BusCount++; + UpdateMapMask |= muxIdField; + if (dataSize > MaxBusDataSize) + { + MaxBusDataSize = dataSize; + } + break; + } + muxId++; + } + if (muxId == BusMaxCount) + { + log_e("exceded channel limit of %u on bus", BusMaxCount); + } + return muxId; + } + + + bool DeregisterMuxBus(uint8_t muxId) + { + T_FLAG muxIdField = (1 << muxId); + if (UpdateMapMask & muxIdField) + { + // complete deregistration + BusCount--; + UpdateMapMask &= ~muxIdField; + if (UpdateMapMask == 0) + { + return true; + } + } + return false; + } + + bool IsAllMuxBusesUpdated() + { + return (UpdateMap == UpdateMapMask); + } + + bool IsNoMuxBusesUpdate() + { + return (UpdateMap == 0); + } + + void MarkMuxBusUpdated(uint8_t muxId) + { + UpdateMap |= (1 << muxId); + } + + void ResetMuxBusesUpdated() + { + UpdateMap = 0; + } + + void Reset() + { + MaxBusDataSize = 0; + UpdateMap = 0; + UpdateMapMask = 0; + BusCount = 0; + } +}; + +// +// Implementation of a Double Buffered version of a I2sContext +// Manages the underlying I2S details including the buffer(s) +// This creates a front buffer that can be filled while actively sending +// the back buffer, thus improving async operation of the i2s DMA. +// Note that the back buffer must be DMA memory, a limited resource, so +// the front buffer uses normal memory and copies rather than swap pointers +// +// T_MUXMAP - NeoEspI2sMuxMap - tracking class for mux state +// +template class NeoEspI2sDblBuffContext +{ +public: + const static size_t DmaBitsPerPixelBit = 4; + + size_t I2sBufferSize; // total size of I2sBuffer + uint8_t* I2sBuffer; // holds the DMA buffer that is referenced by I2sBufDesc + uint8_t* I2sEditBuffer; // hold a editable buffer that is copied to I2sBuffer + T_MUXMAP MuxMap; + + // as a static instance, all members get initialized to zero + // and the constructor is called at inconsistent time to other globals + // so its not useful to have or rely on, + // but without it presence they get zeroed far too late + NeoEspI2sDblBuffContext() + // //: + // //I2sBufferSize(0), + // //I2sBuffer(nullptr), + // //I2sEditBuffer(nullptr), + // //MuxMap() + { + } + + void Construct(const uint8_t busNumber, uint32_t i2sSampleRate) + { + // construct only once on first time called + if (I2sBuffer == nullptr) + { + // MuxMap.MaxBusDataSize = max size in bytes of a single channel + // DmaBitsPerPixelBit = how many dma bits/byte are needed for each source (pixel) bit/byte + // T_MUXMAP::MuxBusDataSize = the true size of data for selected mux mode (not exposed size as i2s0 only supports 16bit mode) + I2sBufferSize = MuxMap.MaxBusDataSize * 8 * DmaBitsPerPixelBit * T_MUXMAP::MuxBusDataSize; + + // must have a 4 byte aligned buffer for i2s + uint32_t alignment = I2sBufferSize % 4; + if (alignment) + { + I2sBufferSize += 4 - alignment; + } + + size_t dmaBlockCount = (I2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN; + + I2sBuffer = static_cast(heap_caps_malloc(I2sBufferSize, MALLOC_CAP_DMA)); + if (I2sBuffer == nullptr) + { + log_e("send buffer memory allocation failure (size %u)", + I2sBufferSize); + } + memset(I2sBuffer, 0x00, I2sBufferSize); + + I2sEditBuffer = static_cast(malloc(I2sBufferSize)); + if (I2sEditBuffer == nullptr) + { + log_e("edit buffer memory allocation failure (size %u)", + I2sBufferSize); + } + memset(I2sEditBuffer, 0x00, I2sBufferSize); + + i2sInit(busNumber, + true, + T_MUXMAP::MuxBusDataSize, + i2sSampleRate, +#if defined(CONFIG_IDF_TARGET_ESP32S2) +// using these modes on ESP32S2 actually allows it to function +// in both x8 and x16 + I2S_CHAN_STEREO, + I2S_FIFO_16BIT_DUAL, +#else +// but they won't work on ESP32 in parallel mode, but these will + I2S_CHAN_RIGHT_TO_LEFT, + I2S_FIFO_16BIT_SINGLE, +#endif + dmaBlockCount, + I2sBuffer, + I2sBufferSize); + } + } + + void Destruct(const uint8_t busNumber) + { + if (I2sBuffer == nullptr) + { + return; + } + + i2sSetPins(busNumber, -1, -1, -1, false); + i2sDeinit(busNumber); + + free(I2sEditBuffer); + heap_caps_free(I2sBuffer); + + I2sBufferSize = 0; + I2sBuffer = nullptr; + I2sEditBuffer = nullptr; + + MuxMap.Reset(); + } +}; + +// +// Implementation of the low level interface into i2s mux bus +// +// T_BUSCONTEXT - the context to use, currently only NeoEspI2sDblBuffContext but there is +// a plan to provide one that doesn't implement the front buffer but would be less +// async as it would have to wait until the last frame was completely sent before +// updating and new data +// T_BUS - the bus id, NeoEsp32I2sBusZero, NeoEsp32I2sBusOne +// +template class NeoEsp32I2sMuxBus +{ +public: + NeoEsp32I2sMuxBus() : + _muxId(s_context.MuxMap.InvalidMuxId) + { + } + + void RegisterNewMuxBus(size_t dataSize) + { + _muxId = s_context.MuxMap.RegisterNewMuxBus(dataSize); + } + + void Initialize(uint8_t pin, uint32_t i2sSampleRate, bool invert) + { + s_context.Construct(T_BUS::I2sBusNumber, i2sSampleRate); + i2sSetPins(T_BUS::I2sBusNumber, pin, _muxId, s_context.MuxMap.MuxBusDataSize, invert); + } + + void DeregisterMuxBus() + { + if (s_context.MuxMap.DeregisterMuxBus(_muxId)) + { + s_context.Destruct(T_BUS::I2sBusNumber); + } + // disconnect muxed pin? + _muxId = s_context.MuxMap.InvalidMuxId; + } + + void StartWrite() + { + if (s_context.MuxMap.IsAllMuxBusesUpdated()) + { + s_context.MuxMap.ResetMuxBusesUpdated(); + + // wait for not actively sending data + while (!IsWriteDone()) + { + yield(); + } + // copy edit buffer to sending buffer + memcpy(s_context.I2sBuffer, s_context.I2sEditBuffer, s_context.I2sBufferSize); + i2sWrite(T_BUS::I2sBusNumber); + } + } + + bool IsWriteDone() + { + return i2sWriteDone(T_BUS::I2sBusNumber); + } + + void FillBuffers(const uint8_t* data, size_t sizeData) + { + if (s_context.MuxMap.IsNoMuxBusesUpdate()) + { + // clear all the data in preperation for each mux channel to add + memset(s_context.I2sEditBuffer, 0x00, s_context.I2sBufferSize); + } + + s_context.MuxMap.EncodeIntoDma(s_context.I2sEditBuffer, + data, + sizeData, + _muxId ); + + s_context.MuxMap.MarkMuxBusUpdated(_muxId); + } + + void MarkUpdated() + { + s_context.MuxMap.MarkMuxBusUpdated(_muxId); + } + +private: + static T_BUSCONTEXT s_context; + uint8_t _muxId; +}; + +template T_BUSCONTEXT NeoEsp32I2sMuxBus::s_context = T_BUSCONTEXT(); + +// +// wrapping layer of the i2s mux bus as a NeoMethod +// +// T_SPEED - NeoEsp32I2sSpeed* (ex NeoEsp32I2sSpeedWs2812x) used to define output signal form +// T_BUS - NeoEsp32I2sMuxBus, the bus to use +// T_INVERT - NeoEsp32I2sNotInverted or NeoEsp32I2sInverted, will invert output signal +// +template class NeoEsp32I2sXMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp32I2sXMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin), + _bus() + { + _bus.RegisterNewMuxBus(_sizeData + T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs); + } + + ~NeoEsp32I2sXMethodBase() + { + while (!_bus.IsWriteDone()) + { + yield(); + } + + _bus.DeregisterMuxBus(); + + free(_data); + } + + bool IsReadyToUpdate() const + { + return _bus.IsWriteDone(); + } + + void Initialize() + { + _bus.Initialize(_pin, T_SPEED::I2sSampleRate, T_INVERT::Inverted); + + _data = static_cast(malloc(_sizeData)); + if (_data == nullptr) + { + log_e("front buffer memory allocation failure"); + } + // data cleared later in Begin() + } + + void Update(bool) + { + _bus.FillBuffers(_data, _sizeData); + _bus.StartWrite(); // only triggers actual write after all mux busses have updated + } + + bool AlwaysUpdate() + { + // this method requires update to be called even if no changes to method buffer + // as edit buffer is always cleared and then copied to send buffer and all + // mux bus needs to included + return true; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // Size of '_data' buffer + const uint8_t _pin; // output pin number + + T_BUS _bus; // holds instance for mux bus support + uint8_t* _data; // Holds LED color values +}; + +#if defined(CONFIG_IDF_TARGET_ESP32S2) + +typedef NeoEsp32I2sMuxBus>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus; +typedef NeoEsp32I2sMuxBus>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus; + +#else + +typedef NeoEsp32I2sMuxBus>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux8Bus; +typedef NeoEsp32I2sMuxBus>, NeoEsp32I2sBusZero> NeoEsp32I2s0Mux16Bus; + + +typedef NeoEsp32I2sMuxBus>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux8Bus; +typedef NeoEsp32I2sMuxBus>, NeoEsp32I2sBusOne> NeoEsp32I2s1Mux16Bus; + +#endif + +// NORMAL +// + +// I2s0x8 +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Ws2812xMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Sk6812Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Tm1814Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Tm1829Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Tm1914Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8800KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8400KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Apa106Method; + +typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2813Method; +typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2812dMethod; +typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2811Method; +typedef NeoEsp32I2s0X8Ws2812xMethod NeoEsp32I2s0X8Ws2816Method; +typedef NeoEsp32I2s0X8800KbpsMethod NeoEsp32I2s0X8Ws2812Method; +typedef NeoEsp32I2s0X8Sk6812Method NeoEsp32I2s0X8Lc8812Method; + +// I2s0x16 +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Ws2812xMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Sk6812Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Tm1814Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Tm1829Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Tm1914Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16800KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16400KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Apa106Method; + +typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2813Method; +typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2812dMethod; +typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2811Method; +typedef NeoEsp32I2s0X16Ws2812xMethod NeoEsp32I2s0X16Ws2816Method; +typedef NeoEsp32I2s0X16800KbpsMethod NeoEsp32I2s0X16Ws2812Method; +typedef NeoEsp32I2s0X16Sk6812Method NeoEsp32I2s0X16Lc8812Method; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) + +// I2s1x8 +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Ws2812xMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Sk6812Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Tm1814Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Tm1829Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Tm1914Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8800KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8400KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Apa106Method; + +typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2813Method; +typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2812dMethod; +typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2811Method; +typedef NeoEsp32I2s1X8Ws2812xMethod NeoEsp32I2s1X8Ws2816Method; +typedef NeoEsp32I2s1X8800KbpsMethod NeoEsp32I2s1X8Ws2812Method; +typedef NeoEsp32I2s1X8Sk6812Method NeoEsp32I2s1X8Lc8812Method; + +// I2s1x16 +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Ws2812xMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Sk6812Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Tm1814Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Tm1829Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Tm1914Method; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16800KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16400KbpsMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Apa106Method; + +typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2813Method; +typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2812dMethod; +typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2811Method; +typedef NeoEsp32I2s1X16Ws2812xMethod NeoEsp32I2s1X16Ws2816Method; +typedef NeoEsp32I2s1X16800KbpsMethod NeoEsp32I2s1X16Ws2812Method; +typedef NeoEsp32I2s1X16Sk6812Method NeoEsp32I2s1X16Lc8812Method; + + +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) + +// INVERTED +// +// I2s0x8 INVERTED +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Ws2812xInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Sk6812InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Tm1814InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Tm1829InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Tm1914InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8800KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8400KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X8Apa106InvertedMethod; + +typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2813InvertedMethod; +typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2812xInvertedMethod; +typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2811InvertedMethod; +typedef NeoEsp32I2s0X8Ws2812xInvertedMethod NeoEsp32I2s0X8Ws2816InvertedMethod; +typedef NeoEsp32I2s0X8800KbpsInvertedMethod NeoEsp32I2s0X8Ws2812InvertedMethod; +typedef NeoEsp32I2s0X8Sk6812InvertedMethod NeoEsp32I2s0X8Lc8812InvertedMethod; + + +// I2s0x16 INVERTED +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Ws2812xInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Sk6812InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Tm1814InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Tm1829InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Tm1914InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16800KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16400KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s0X16Apa106InvertedMethod; + +typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2813InvertedMethod; +typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2812xInvertedMethod; +typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2811InvertedMethod; +typedef NeoEsp32I2s0X16Ws2812xInvertedMethod NeoEsp32I2s0X16Ws2816InvertedMethod; +typedef NeoEsp32I2s0X16800KbpsInvertedMethod NeoEsp32I2s0X16Ws2812InvertedMethod; +typedef NeoEsp32I2s0X16Sk6812InvertedMethod NeoEsp32I2s0X16Lc8812InvertedMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) + +// I2s1x8 INVERTED +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Ws2812xInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Sk6812InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Tm1814InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Tm1829InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Tm1914InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8800KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8400KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X8Apa106InvertedMethod; + +typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2813InvertedMethod; +typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2812xInvertedMethod; +typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2811InvertedMethod; +typedef NeoEsp32I2s1X8Ws2812xInvertedMethod NeoEsp32I2s1X8Ws2816InvertedMethod; +typedef NeoEsp32I2s1X8800KbpsInvertedMethod NeoEsp32I2s1X8Ws2812InvertedMethod; +typedef NeoEsp32I2s1X8Sk6812InvertedMethod NeoEsp32I2s1X8Lc8812InvertedMethod; + +// I2s1x16 INVERTED +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Ws2812xInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Sk6812InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Tm1814InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Tm1829InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Tm1914InvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16800KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16400KbpsInvertedMethod; +typedef NeoEsp32I2sXMethodBase NeoEsp32I2s1X16Apa106InvertedMethod; + +typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2813InvertedMethod; +typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2812xInvertedMethod; +typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2811InvertedMethod; +typedef NeoEsp32I2s1X16Ws2812xInvertedMethod NeoEsp32I2s1X16Ws2816InvertedMethod; +typedef NeoEsp32I2s1X16800KbpsInvertedMethod NeoEsp32I2s1X16Ws2812InvertedMethod; +typedef NeoEsp32I2s1X16Sk6812InvertedMethod NeoEsp32I2s1X16Lc8812InvertedMethod; + +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) + +#endif // defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp32RmtMethod.cpp b/lib/NeoPixelBus/src/internal/methods/NeoEsp32RmtMethod.cpp new file mode 100644 index 00000000..5d62b4bb --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp32RmtMethod.cpp @@ -0,0 +1,342 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +A BIG thanks to Andreas Merkle for the investigation and implementation of +a workaround to the GCC bug that drops method attributes from template methods + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoSettings.h" +#include "../NeoBusChannel.h" +#include "NeoEsp32RmtMethod.h" + +#ifdef ARDUINO_ARCH_ESP32 + + +// translate NeoPixelBuffer into RMT buffer +// this is done on the fly so we don't require a send buffer in raw RMT format +// which would be 32x larger than the primary buffer +// +// NOTE: This was moved from the template below to here to workaround a GCC bug +// That bug is that the IRAM_ATTR attribute (any attributes) is lost on template classes. +// +// Further, it was removed from the header to support current Esp32 release +// which will need to be removed when the latest GitHub branchis released +// due to this method will not get inlined this way +// +void NeoEsp32RmtSpeed::_translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num, + const uint32_t rmtBit0, + const uint32_t rmtBit1, + const uint16_t rmtDurationReset) +{ + if (src == NULL || dest == NULL) + { + *translated_size = 0; + *item_num = 0; + return; + } + + size_t size = 0; + size_t num = 0; + const uint8_t* psrc = static_cast(src); + rmt_item32_t* pdest = dest; + + for (;;) + { + uint8_t data = *psrc; + + for (uint8_t bit = 0; bit < 8; bit++) + { + pdest->val = (data & 0x80) ? rmtBit1 : rmtBit0; + pdest++; + data <<= 1; + } + num += 8; + size++; + + // if this is the last byte we need to adjust the length of the last pulse + if (size >= src_size) + { + // extend the last bits LOW value to include the full reset signal length + pdest--; + pdest->duration1 = rmtDurationReset; + // and stop updating data to send + break; + } + + if (num >= wanted_num) + { + // stop updating data to send + break; + } + + psrc++; + } + + *translated_size = size; + *item_num = num; +} + + +// these are required due to the linker error with ISRs +// dangerous relocation: l32r: literal placed after use +// https://stackoverflow.com/questions/19532826/what-does-a-dangerous-relocation-error-mean +// +void NeoEsp32RmtSpeedWs2811::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedWs2812x::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedSk6812::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedTm1814::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedTm1829::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedTm1914::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeed800Kbps::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeed400Kbps::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedApa106::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtSpeedTx1812::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedWs2811::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedWs2812x::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedSk6812::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedTm1814::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedTm1829::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedTm1914::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedTM1934::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeed800Kbps::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeed400Kbps::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedApa106::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} + +void NeoEsp32RmtInvertedSpeedTx1812::Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) +{ + _translate(src, dest, src_size, wanted_num, translated_size, item_num, + RmtBit0, RmtBit1, RmtDurationReset); +} +#endif \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp32RmtMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp32RmtMethod.h new file mode 100644 index 00000000..d6f4de1f --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp32RmtMethod.h @@ -0,0 +1,997 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +A BIG thanks to Andreas Merkle for the investigation and implementation of +a workaround to the GCC bug that drops method attributes from template methods + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP32 + +/* General Reference documentation for the APIs used in this implementation +LOW LEVEL: (what is actually used) +DOCS: https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html +EXAMPLE: https://github.com/espressif/esp-idf/blob/826ff7186ae07dc81e960a8ea09ebfc5304bfb3b/examples/peripherals/rmt_tx/main/rmt_tx_main.c + +HIGHER LEVEL: +NO TRANSLATE SUPPORT so this was not used +NOTE: https://github.com/espressif/arduino-esp32/commit/50d142950d229b8fabca9b749dc4a5f2533bc426 +Esp32-hal-rmt.h +Esp32-hal-rmt.c +*/ + +extern "C" +{ +#include +} + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) +#define NEOPIXELBUS_RMT_INT_FLAGS (ESP_INTR_FLAG_LOWMED) +#else +#define NEOPIXELBUS_RMT_INT_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1) +#endif + +class NeoEsp32RmtSpeed +{ +public: + // ClkDiv of 2 provides for good resolution and plenty of reset resolution; but + // a ClkDiv of 1 will provide enough space for the longest reset and does show + // little better pulse accuracy + const static uint8_t RmtClockDivider = 2; + + inline constexpr static uint32_t FromNs(uint32_t ns) + { + return ns / NsPerRmtTick; + } + +protected: + const static uint32_t RmtCpu = 80000000L; // 80 mhz RMT clock + const static uint32_t NsPerSecond = 1000000000L; + const static uint32_t RmtTicksPerSecond = (RmtCpu / RmtClockDivider); + const static uint32_t NsPerRmtTick = (NsPerSecond / RmtTicksPerSecond); // about 25 + + static void IRAM_ATTR _translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num, + const uint32_t rmtBit0, + const uint32_t rmtBit1, + const uint16_t rmtDurationReset); + +}; + +class NeoEsp32RmtSpeedBase : public NeoEsp32RmtSpeed +{ +public: + // this is used rather than the rmt_item32_t as you can't correctly initialize + // it as a static constexpr within the template + inline constexpr static uint32_t Item32Val(uint16_t nsHigh, uint16_t nsLow) + { + return (FromNs(nsLow) << 16) | (1 << 15) | (FromNs(nsHigh)); + } + + const static rmt_idle_level_t IdleLevel = RMT_IDLE_LEVEL_LOW; +}; + +class NeoEsp32RmtInvertedSpeedBase : public NeoEsp32RmtSpeed +{ +public: + // this is used rather than the rmt_item32_t as you can't correctly initialize + // it as a static constexpr within the template + inline constexpr static uint32_t Item32Val(uint16_t nsHigh, uint16_t nsLow) + { + return (FromNs(nsLow) << 16) | (1 << 31) | (FromNs(nsHigh)); + } + + const static rmt_idle_level_t IdleLevel = RMT_IDLE_LEVEL_HIGH; +}; + +// ! VALUES ARE NOT CONFIRMED +// TODO - these need to be verified +// TM1934 - 320ns, 320ns, 640ns +class NeoEsp32RmtSpeedTM1934 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 950); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(900, 350); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeedWs2811 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 950); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(900, 350); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeedWs2812x : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeedSk6812 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// normal is inverted signal +class NeoEsp32RmtSpeedTm1814 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// normal is inverted signal +class NeoEsp32RmtSpeedTm1829 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 900); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 400); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// normal is inverted signal +class NeoEsp32RmtSpeedTm1914 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeed800Kbps : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeed400Kbps : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(800, 1700); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1600, 900); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeedApa106 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(350, 1350); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1350, 350); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtSpeedTx1812 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 600); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(600, 300); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeedWs2811 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 950); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(900, 350); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeedWs2812x : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(300000); // 300us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeedSk6812 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// normal is inverted signal +class NeoEsp32RmtInvertedSpeedTm1814 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// normal is inverted signal +class NeoEsp32RmtInvertedSpeedTm1829 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 900); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 400); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// normal is inverted signal +class NeoEsp32RmtInvertedSpeedTm1914 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +// ! VALUES ARE NOT CONFIRMED +// TODO - these need to be verified +// TM1934 - 320ns, 320ns, 640ns +class NeoEsp32RmtInvertedSpeedTM1934 : public NeoEsp32RmtSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(360, 890); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(720, 530); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(200000); // 200us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeed800Kbps : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(400, 850); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(800, 450); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeed400Kbps : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(800, 1700); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1600, 900); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeedApa106 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(350, 1350); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(1350, 350); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(50000); // 50us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtInvertedSpeedTx1812 : public NeoEsp32RmtInvertedSpeedBase +{ +public: + const static DRAM_ATTR uint32_t RmtBit0 = Item32Val(300, 600); + const static DRAM_ATTR uint32_t RmtBit1 = Item32Val(600, 300); + const static DRAM_ATTR uint16_t RmtDurationReset = FromNs(80000); // 80us + + static void IRAM_ATTR Translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num); +}; + +class NeoEsp32RmtChannel0 +{ +public: + NeoEsp32RmtChannel0() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_0; +}; + +class NeoEsp32RmtChannel1 +{ +public: + NeoEsp32RmtChannel1() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_1; +}; + +class NeoEsp32RmtChannel2 +{ +public: + NeoEsp32RmtChannel2() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_2; +}; + +class NeoEsp32RmtChannel3 +{ +public: + NeoEsp32RmtChannel3() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_3; +}; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +class NeoEsp32RmtChannel4 +{ +public: + NeoEsp32RmtChannel4() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_4; +}; + +class NeoEsp32RmtChannel5 +{ +public: + NeoEsp32RmtChannel5() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_5; +}; + +class NeoEsp32RmtChannel6 +{ +public: + NeoEsp32RmtChannel6() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_6; +}; + +class NeoEsp32RmtChannel7 +{ +public: + NeoEsp32RmtChannel7() {}; + + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_7; +}; + +#endif + +// dynamic channel support +class NeoEsp32RmtChannelN +{ +public: + NeoEsp32RmtChannelN(NeoBusChannel channel) : + RmtChannelNumber(static_cast(channel)) + { + } + NeoEsp32RmtChannelN() = delete; // no default constructor + + const rmt_channel_t RmtChannelNumber; +}; + +template class NeoEsp32RmtMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp32RmtMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin) + { + construct(); + } + + NeoEsp32RmtMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) : + _sizeData(pixelCount* elementSize + settingsSize), + _pin(pin), + _channel(channel) + { + construct(); + } + + ~NeoEsp32RmtMethodBase() + { + // wait until the last send finishes before destructing everything + // arbitrary time out of 10 seconds + ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_wait_tx_done(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)); + + ESP_ERROR_CHECK(rmt_driver_uninstall(_channel.RmtChannelNumber)); + + gpio_matrix_out(_pin, 0x100, false, false); + pinMode(_pin, INPUT); + + free(_dataEditing); + free(_dataSending); + } + + + bool IsReadyToUpdate() const + { + return (ESP_OK == rmt_wait_tx_done(_channel.RmtChannelNumber, 0)); + } + + void Initialize() + { + rmt_config_t config = {}; + + config.rmt_mode = RMT_MODE_TX; + config.channel = _channel.RmtChannelNumber; + config.gpio_num = static_cast(_pin); + config.mem_block_num = 1; + config.tx_config.loop_en = false; + + config.tx_config.idle_output_en = true; + config.tx_config.idle_level = T_SPEED::IdleLevel; + + config.tx_config.carrier_en = false; + config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; + + config.clk_div = T_SPEED::RmtClockDivider; + + ESP_ERROR_CHECK(rmt_config(&config)); + ESP_ERROR_CHECK(rmt_driver_install(_channel.RmtChannelNumber, 0, NEOPIXELBUS_RMT_INT_FLAGS)); + ESP_ERROR_CHECK(rmt_translator_init(_channel.RmtChannelNumber, T_SPEED::Translate)); + } + + void Update(bool maintainBufferConsistency) + { + // wait for not actively sending data + // this will time out at 10 seconds, an arbitrarily long period of time + // and do nothing if this happens + if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_wait_tx_done(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS))) + { + // now start the RMT transmit with the editing buffer before we swap + ESP_ERROR_CHECK_WITHOUT_ABORT(rmt_write_sample(_channel.RmtChannelNumber, _dataEditing, _sizeData, false)); + + if (maintainBufferConsistency) + { + // copy editing to sending, + // this maintains the contract that "colors present before will + // be the same after", otherwise GetPixelColor will be inconsistent + memcpy(_dataSending, _dataEditing, _sizeData); + } + + // swap so the user can modify without affecting the async operation + std::swap(_dataSending, _dataEditing); + } + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _dataEditing; + }; + + size_t getDataSize() const + { + return _sizeData; + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // Size of '_data*' buffers + const uint8_t _pin; // output pin number + const T_CHANNEL _channel; // holds instance for multi channel support + + // Holds data stream which include LED color values and other settings as needed + uint8_t* _dataEditing; // exposed for get and set + uint8_t* _dataSending; // used for async send using RMT + + + void construct() + { + _dataEditing = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + + _dataSending = static_cast(malloc(_sizeData)); + // no need to initialize it, it gets overwritten on every send + } +}; + +// normal +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNWs2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNWs2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNWs2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNSk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNApa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtN800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtN400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1400KbpsMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3400KbpsMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7TM1934Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2811Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2816Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tm1814Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tm1829Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tm1914Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tx1812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7400KbpsMethod; + +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) + +// inverted +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNWs2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNWs2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNWs2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNSk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNApa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtNTx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtN800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32RmtN400KbpsInvertedMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0400KbpsInvertedMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1400KbpsInvertedMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2400KbpsInvertedMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3400KbpsInvertedMethod; + +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4400KbpsInvertedMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5400KbpsInvertedMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6400KbpsInvertedMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7TM1934InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2811InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2812xInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2816InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Sk6812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tm1814InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tm1829InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tm1914InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Apa106InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Tx1812InvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7800KbpsInvertedMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7400KbpsInvertedMethod; + +#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) +#endif // !defined(CONFIG_IDF_TARGET_ESP32C3) + + +#if defined(NEOPIXEL_ESP32_RMT_DEFAULT) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + +// Normally I2s method is the default, defining NEOPIXEL_ESP32_RMT_DEFAULT +// will switch to use RMT as the default method +// The ESP32S2 & ESP32C3 will always defualt to RMT + +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + +// RMT channel 1 method is the default method for Esp32S2 & Esp32C3 +typedef NeoEsp32Rmt1TM1934Method NeoTM1934Method; +typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2813Method; +typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2812xMethod; +typedef NeoEsp32Rmt1800KbpsMethod NeoWs2812Method; +typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2811Method; +typedef NeoEsp32Rmt1Ws2812xMethod NeoWs2816Method; +typedef NeoEsp32Rmt1Sk6812Method NeoSk6812Method; +typedef NeoEsp32Rmt1Tm1814Method NeoTm1814Method; +typedef NeoEsp32Rmt1Tm1829Method NeoTm1829Method; +typedef NeoEsp32Rmt1Tm1914Method NeoTm1914Method; +typedef NeoEsp32Rmt1Sk6812Method NeoLc8812Method; +typedef NeoEsp32Rmt1Apa106Method NeoApa106Method; +typedef NeoEsp32Rmt1Tx1812Method NeoTx1812Method; + +typedef NeoEsp32Rmt1Ws2812xMethod Neo800KbpsMethod; +typedef NeoEsp32Rmt1400KbpsMethod Neo400KbpsMethod; + +typedef NeoEsp32Rmt1TM1934InvertedMethod NeoTM1934InvertedMethod; +typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2813InvertedMethod; +typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2812xInvertedMethod; +typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2811InvertedMethod; +typedef NeoEsp32Rmt1800KbpsInvertedMethod NeoWs2812InvertedMethod; +typedef NeoEsp32Rmt1Ws2812xInvertedMethod NeoWs2816InvertedMethod; +typedef NeoEsp32Rmt1Sk6812InvertedMethod NeoSk6812InvertedMethod; +typedef NeoEsp32Rmt1Tm1814InvertedMethod NeoTm1814InvertedMethod; +typedef NeoEsp32Rmt1Tm1829InvertedMethod NeoTm1829InvertedMethod; +typedef NeoEsp32Rmt1Tm1914InvertedMethod NeoTm1914InvertedMethod; +typedef NeoEsp32Rmt1Sk6812InvertedMethod NeoLc8812InvertedMethod; +typedef NeoEsp32Rmt1Apa106InvertedMethod NeoApa106InvertedMethod; +typedef NeoEsp32Rmt1Tx1812InvertedMethod NeoTx1812InvertedMethod; + +typedef NeoEsp32Rmt1Ws2812xInvertedMethod Neo800KbpsInvertedMethod; +typedef NeoEsp32Rmt1400KbpsInvertedMethod Neo400KbpsInvertedMethod; + +#else // defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + +// RMT channel 6 method is the default method for Esp32 +typedef NeoEsp32Rmt6TM1934Method NeoTM1934Method; +typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2813Method; +typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2812xMethod; +typedef NeoEsp32Rmt6800KbpsMethod NeoWs2812Method; +typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2811Method; +typedef NeoEsp32Rmt6Ws2812xMethod NeoWs2816Method; +typedef NeoEsp32Rmt6Sk6812Method NeoSk6812Method; +typedef NeoEsp32Rmt6Tm1814Method NeoTm1814Method; +typedef NeoEsp32Rmt6Tm1829Method NeoTm1829Method; +typedef NeoEsp32Rmt6Tm1914Method NeoTm1914Method; +typedef NeoEsp32Rmt6Sk6812Method NeoLc8812Method; +typedef NeoEsp32Rmt6Apa106Method NeoApa106Method; +typedef NeoEsp32Rmt6Tx1812Method NeoTx1812Method; + +typedef NeoEsp32Rmt6Ws2812xMethod Neo800KbpsMethod; +typedef NeoEsp32Rmt6400KbpsMethod Neo400KbpsMethod; + +typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2813InvertedMethod; +typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2812xInvertedMethod; +typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2811InvertedMethod; +typedef NeoEsp32Rmt6800KbpsInvertedMethod NeoWs2812InvertedMethod; +typedef NeoEsp32Rmt6Ws2812xInvertedMethod NeoWs2816InvertedMethod; +typedef NeoEsp32Rmt6Sk6812InvertedMethod NeoSk6812InvertedMethod; +typedef NeoEsp32Rmt6Tm1814InvertedMethod NeoTm1814InvertedMethod; +typedef NeoEsp32Rmt6Tm1829InvertedMethod NeoTm1829InvertedMethod; +typedef NeoEsp32Rmt6Tm1914InvertedMethod NeoTm1914InvertedMethod; +typedef NeoEsp32Rmt6Sk6812InvertedMethod NeoLc8812InvertedMethod; +typedef NeoEsp32Rmt6Apa106InvertedMethod NeoApa106InvertedMethod; +typedef NeoEsp32Rmt6Tx1812InvertedMethod NeoTx1812InvertedMethod; + +typedef NeoEsp32Rmt6Ws2812xInvertedMethod Neo800KbpsInvertedMethod; +typedef NeoEsp32Rmt6400KbpsInvertedMethod Neo400KbpsInvertedMethod; + +#endif // defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + +#endif // defined(NEOPIXEL_ESP32_RMT_DEFAULT) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp8266DmaMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266DmaMethod.h new file mode 100644 index 00000000..4aa0302c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266DmaMethod.h @@ -0,0 +1,435 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266. + + +Written by Michael C. Miller. +Thanks to g3gg0.de for porting the initial DMA support which lead to this. +Thanks to github/cnlohr for the original work on DMA support, which opend +all our minds to a better way (located at https://github.com/cnlohr/esp8266ws2812i2s). + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP8266 +#include "NeoEsp8266I2sMethodCore.h" + +class NeoEsp8266DmaSpeedBase +{ +public: + static const uint8_t IdleLevel = 0; + static uint16_t Convert(uint8_t value) + { + const uint16_t bitpatterns[16] = + { + 0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, + 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, + 0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, + 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, + }; + + return bitpatterns[value]; + } +}; + +class NeoEsp8266DmaInvertedSpeedBase +{ +public: + static const uint8_t IdleLevel = 1; + static uint16_t Convert(uint8_t value) + { + const uint16_t bitpatterns[16] = + { + 0b0111011101110111, 0b0111011101110001, 0b0111011100010111, 0b0111011100010001, + 0b0111000101110111, 0b0111000101110001, 0b0111000100010111, 0b0111000100010001, + 0b0001011101110111, 0b0001011101110001, 0b0001011100010111, 0b0001011100010001, + 0b0001000101110111, 0b0001000101110001, 0b0001000100010111, 0b0001000100010001, + }; + + return bitpatterns[value]; + } +}; + +class NeoEsp8266DmaSpeed800KbpsBase : public NeoEsp8266DmaSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 5; // 0-63 + const static uint32_t I2sBaseClockDivisor = 10; // 0-63 + const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed +}; + +class NeoEsp8266DmaSpeedWs2812x : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 300; +}; + +class NeoEsp8266DmaSpeedSk6812 : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 80; +}; + +class NeoEsp8266DmaInvertedSpeedTm1814 : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 200; +}; + +class NeoEsp8266DmaInvertedSpeedTm1829 : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 200; +}; + +class NeoEsp8266DmaSpeed800Kbps : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 50; +}; + +class NeoEsp8266DmaSpeed400Kbps : public NeoEsp8266DmaSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 10; // 0-63 + const static uint32_t I2sBaseClockDivisor = 10; // 0-63 + const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed + const static uint32_t ResetTimeUs = 50; +}; + +class NeoEsp8266DmaSpeedApa106 : public NeoEsp8266DmaSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 4; // 0-63 + const static uint32_t I2sBaseClockDivisor = 17; // 0-63 + const static uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element + const static uint32_t ResetTimeUs = 50; +}; + +class NeoEsp8266DmaSpeedIntertek : public NeoEsp8266DmaSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 5; // 0-63 + const static uint32_t I2sBaseClockDivisor = 10; // 0-63 + const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element + const static uint32_t ResetTimeUs = 12470; + const static uint32_t InterPixelTimeUs = 20; +}; + +class NeoEsp8266DmaInvertedSpeed800KbpsBase : public NeoEsp8266DmaInvertedSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 5; // 0-63 + const static uint32_t I2sBaseClockDivisor = 10; // 0-63 + const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed +}; + +class NeoEsp8266DmaInvertedSpeedWs2812x : public NeoEsp8266DmaInvertedSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 300; +}; + +class NeoEsp8266DmaInvertedSpeedSk6812 : public NeoEsp8266DmaInvertedSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 80; +}; + +class NeoEsp8266DmaSpeedTm1814 : public NeoEsp8266DmaInvertedSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 200; +}; + +class NeoEsp8266DmaSpeedTm1829 : public NeoEsp8266DmaInvertedSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 200; +}; + +class NeoEsp8266DmaInvertedSpeed800Kbps : public NeoEsp8266DmaInvertedSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 50; +}; + +class NeoEsp8266DmaInvertedSpeed400Kbps : public NeoEsp8266DmaInvertedSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 10; // 0-63 + const static uint32_t I2sBaseClockDivisor = 10; // 0-63 + const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed + const static uint32_t ResetTimeUs = 50; +}; + +class NeoEsp8266DmaInvertedSpeedApa106 : public NeoEsp8266DmaInvertedSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 4; // 0-63 + const static uint32_t I2sBaseClockDivisor = 17; // 0-63 + const static uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element + const static uint32_t ResetTimeUs = 50; +}; + +class NeoEsp8266DmaInvertedSpeedIntertek : public NeoEsp8266DmaInvertedSpeedBase +{ +public: + const static uint32_t I2sClockDivisor = 5; // 0-63 + const static uint32_t I2sBaseClockDivisor = 10; // 0-63 + const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed + const static uint32_t ResetTimeUs = 12470; + const static uint32_t InterPixelTimeUs = 20; +}; + +template class NeoEsp8266DmaEncode : public T_SPEED +{ +public: + static size_t SpacingPixelSize(size_t sizePixel) + { + return sizePixel; + } + + static void FillBuffers(uint8_t* i2sBuffer, + const uint8_t* data, + size_t sizeData, + [[maybe_unused]] size_t sizePixel) + { + uint16_t* pDma = (uint16_t*)i2sBuffer; + const uint8_t* pEnd = data + sizeData; + for (const uint8_t* pData = data; pData < pEnd; pData++) + { + *(pDma++) = T_SPEED::Convert(((*pData) & 0x0f)); + *(pDma++) = T_SPEED::Convert(((*pData) >> 4) & 0x0f); + } + } +}; + +template class NeoEsp8266DmaPixelSpacingEncode : public T_SPEED +{ +public: + static size_t SpacingPixelSize(size_t sizePixel) + { + return sizePixel + T_SPEED::InterPixelTimeUs / T_SPEED::ByteSendTimeUs; + } + + static void FillBuffers(uint8_t* i2sBuffer, + const uint8_t* data, + size_t sizeData, + size_t sizePixel) + { + uint16_t* pDma = (uint16_t*)i2sBuffer; + const uint8_t* pEnd = data + sizeData; + uint8_t element = 0; + for (const uint8_t* pData = data; pData < pEnd; pData++) + { + *(pDma++) = T_SPEED::Convert(((*pData) & 0x0f)); + *(pDma++) = T_SPEED::Convert(((*pData) >> 4) & 0x0f); + + element++; + if (element == sizePixel) + { + element = 0; + + for (uint8_t padding = 0; + padding < (T_SPEED::InterPixelTimeUs / T_SPEED::ByteSendTimeUs); + padding++) + { + *(pDma++) = T_SPEED::IdleLevel * 0xffff; + *(pDma++) = T_SPEED::IdleLevel * 0xffff; + } + } + } + } +}; + +template class NeoEsp8266DmaMethodBase : NeoEsp8266I2sMethodCore +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizePixel(elementSize), + _sizeData(pixelCount * elementSize + settingsSize) + { + size_t dmaPixelSize = DmaBytesPerPixelBytes * T_ENCODER::SpacingPixelSize(_sizePixel); + size_t dmaSettingsSize = DmaBytesPerPixelBytes * settingsSize; + + size_t i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize; + // size is rounded up to nearest c_I2sByteBoundarySize + i2sBufferSize = NeoUtil::RoundUp(i2sBufferSize, c_I2sByteBoundarySize); + + // calculate a buffer size that takes reset amount of time + size_t i2sResetSize = T_ENCODER::ResetTimeUs * DmaBytesPerPixelBytes / T_ENCODER::ByteSendTimeUs; + // size is rounded up to nearest c_I2sByteBoundarySize + i2sResetSize = NeoUtil::RoundUp(i2sResetSize, c_I2sByteBoundarySize); + size_t is2BufMaxBlockSize = (c_maxDmaBlockSize / dmaPixelSize) * dmaPixelSize; + + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + + AllocateI2s(i2sBufferSize, i2sResetSize, is2BufMaxBlockSize, T_ENCODER::IdleLevel); + } + + NeoEsp8266DmaMethodBase([[maybe_unused]] uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + NeoEsp8266DmaMethodBase(pixelCount, elementSize, settingsSize) + { + } + + ~NeoEsp8266DmaMethodBase() + { + uint8_t waits = 1; + while (!IsReadyToUpdate()) + { + waits = 2; + yield(); + } + + // wait for any pending sends to complete + // due to internal i2s caching/send delays, this can more that once the data size + uint32_t time = micros(); + while ((micros() - time) < ((getPixelTime() + T_ENCODER::ResetTimeUs) * waits)) + { + yield(); + } + + FreeI2s(); + + free(_data); + } + + bool IsReadyToUpdate() const + { + return IsIdle(); + } + + void Initialize() + { + InitializeI2s(T_ENCODER::I2sClockDivisor, T_ENCODER::I2sBaseClockDivisor); + } + + void IRAM_ATTR Update(bool) + { + // wait for not actively sending data + while (!IsReadyToUpdate()) + { + yield(); + } + T_ENCODER::FillBuffers(_i2sBuffer, _data, _sizeData, _sizePixel); + + WriteI2s(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + // due to encoding required for i2s, we need 4 bytes to encode the pulses + static const uint16_t DmaBytesPerPixelBytes = 4; + + const size_t _sizePixel; // size of a pixel in _data + const size_t _sizeData; // Size of '_data' buffer + uint8_t* _data; // Holds LED color values + + uint32_t getPixelTime() const + { + return (T_ENCODER::ByteSendTimeUs * GetSendSize() / DmaBytesPerPixelBytes); + }; + +}; + + + +// normal +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaWs2812xMethod; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaSk6812Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaTm1814Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaTm1829Method; +typedef NeoEsp8266DmaTm1814Method NeoEsp8266DmaTm1914Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266Dma800KbpsMethod; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266Dma400KbpsMethod; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaApa106Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaIntertekMethod; + + +// inverted +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInvertedWs2812xMethod; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInvertedSk6812Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInvertedTm1814Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInvertedTm1829Method; +typedef NeoEsp8266DmaInvertedTm1814Method NeoEsp8266DmaInvertedTm1914Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInverted800KbpsMethod; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInverted400KbpsMethod; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInvertedApa106Method; +typedef NeoEsp8266DmaMethodBase> NeoEsp8266DmaInvertedIntertekMethod; + +// Dma method is the default method for Esp8266 +typedef NeoEsp8266DmaWs2812xMethod NeoWs2813Method; +typedef NeoEsp8266DmaWs2812xMethod NeoWs2812xMethod; +typedef NeoEsp8266Dma800KbpsMethod NeoWs2812Method; +typedef NeoEsp8266DmaWs2812xMethod NeoWs2811Method; +typedef NeoEsp8266DmaWs2812xMethod NeoWs2816Method; +typedef NeoEsp8266DmaSk6812Method NeoSk6812Method; +typedef NeoEsp8266DmaTm1814Method NeoTm1814Method; +typedef NeoEsp8266DmaTm1829Method NeoTm1829Method; +typedef NeoEsp8266DmaTm1914Method NeoTm1914Method; +typedef NeoEsp8266DmaSk6812Method NeoLc8812Method; +typedef NeoEsp8266DmaApa106Method NeoApa106Method; +typedef NeoEsp8266DmaIntertekMethod NeoIntertekMethod; + +typedef NeoEsp8266DmaWs2812xMethod Neo800KbpsMethod; +typedef NeoEsp8266Dma400KbpsMethod Neo400KbpsMethod; + +// inverted +typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2813InvertedMethod; +typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2812xInvertedMethod; +typedef NeoEsp8266DmaInverted800KbpsMethod NeoWs2812InvertedMethod; +typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2811InvertedMethod; +typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2816InvertedMethod; +typedef NeoEsp8266DmaInvertedSk6812Method NeoSk6812InvertedMethod; +typedef NeoEsp8266DmaInvertedTm1814Method NeoTm1814InvertedMethod; +typedef NeoEsp8266DmaInvertedTm1829Method NeoTm1829InvertedMethod; +typedef NeoEsp8266DmaInvertedTm1914Method NeoTm1914InvertedMethod; +typedef NeoEsp8266DmaInvertedSk6812Method NeoLc8812InvertedMethod; +typedef NeoEsp8266DmaInvertedApa106Method NeoApa106InvertedMethod; +typedef NeoEsp8266DmaInvertedIntertekMethod NeoInvertedIntertekMethod; + +typedef NeoEsp8266DmaInvertedWs2812xMethod Neo800KbpsInvertedMethod; +typedef NeoEsp8266DmaInverted400KbpsMethod Neo400KbpsInvertedMethod; +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sDmx512Method.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sDmx512Method.h new file mode 100644 index 00000000..531a7de3 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sDmx512Method.h @@ -0,0 +1,352 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP8266 +#include "NeoEsp8266I2sMethodCore.h" + + +class NeoEsp8266I2sDmx512SpeedBase +{ +public: + // 4 us bit send, 250Kbps + static const uint32_t I2sClockDivisor = 20; // 0-63 + static const uint32_t I2sBaseClockDivisor = 32; // 0-63 + static const uint32_t ByteSendTimeUs = 44; // us it takes to send a single pixel element of 11 bits + static const uint32_t BreakMabUs = 96; // Break min 92, Mab min 12 + static const size_t BreakMabSize = 4; // roundupby((BreakMabUs/4us)/8,4) count of bytes needed for the Break+Mab timing + static const uint32_t MtbpUs = 11; // Mtbp, min 0, buy we use at least one byte of space (8*1.35) + static const size_t MtbpSize = 1; // (MtbpUs/1.35)/8 count of bytes needed for the Mtbp timing + // DMX requires the first slot to be zero + static const size_t HeaderSize = 1; +}; + +class NeoEsp8266I2sDmx512Speed : public NeoEsp8266I2sDmx512SpeedBase +{ +public: + static const uint8_t MtbpLevel = 0x1; // high + static const uint8_t StartBit = 0b00000000; + static const uint8_t StopBits = 0b00000011; + static const uint32_t Break = 0x00000000; // Break + static const uint32_t BreakMab = 0x00000007; // Break + Mab + + static uint8_t Convert(uint8_t value) + { + // DMX requires LSB order + return NeoUtil::Reverse8Bits( value ); + } +}; + +class NeoEsp8266I2sDmx512InvertedSpeed : public NeoEsp8266I2sDmx512SpeedBase +{ +public: + static const uint8_t MtbpLevel = 0x00; // low + static const uint8_t StartBit = 0b00000001; + static const uint8_t StopBits = 0b00000000; + static const uint32_t Break = 0xffffffff; // Break + static const uint32_t BreakMab = 0xfffffff8; // Break + Mab + + static uint8_t Convert(uint8_t value) + { + // DMX requires LSB order + return NeoUtil::Reverse8Bits( ~value ); + } +}; + + +class NeoEsp8266I2sWs2821SpeedBase +{ +public: + // 1.35 us bit send, 750Kbps + static const uint32_t I2sClockDivisor = 27; // 0-63 + static const uint32_t I2sBaseClockDivisor = 8; // 0-63 + static const uint32_t ByteSendTimeUs = 15; // us it takes to send a single pixel element of 11 bits + static const uint32_t BreakMabUs = 92; // Break min 88, Mab min 4 + static const size_t BreakMabSize = 12; // roundupby((BreakMabUs/1.35)/8,4) count of bytes needed for the Break+Mab timing + static const uint32_t MtbpUs = 88; // Mtbp, min 88 + static const size_t MtbpSize = 9; // (MtbpUs/1.35)/8 count of bytes needed for the Mtbp timing + + // DMX/WS2821 requires the first slot to be zero + static const size_t HeaderSize = 1; +}; + +class NeoEsp8266I2sWs2821Speed : public NeoEsp8266I2sWs2821SpeedBase +{ +public: + static const uint8_t MtbpLevel = 0x1; // high + static const uint8_t StartBit = 0b00000000; + static const uint8_t StopBits = 0b00000011; + static const uint32_t Break = 0x00000000; // Break + static const uint32_t BreakMab = 0x00000007; // Break + Mab (4~12us/1.35us) + + static uint8_t Convert(uint8_t value) + { + // DMX requires LSB order + return NeoUtil::Reverse8Bits(value); + } +}; + +class NeoEsp8266I2sWs2821InvertedSpeed : public NeoEsp8266I2sWs2821SpeedBase +{ +public: + static const uint8_t MtbpLevel = 0x00; // low + static const uint8_t StartBit = 0b00000001; + static const uint8_t StopBits = 0b00000000; + static const uint32_t Break = 0xffffffff; // Break + static const uint32_t BreakMab = 0xfffffff8; // Break + Mab + + static uint8_t Convert(uint8_t value) + { + // DMX requires LSB order + return NeoUtil::Reverse8Bits(~value); + } +}; + +template class NeoEsp8266I2sDmx512MethodBase : NeoEsp8266I2sMethodCore +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp8266I2sDmx512MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize + T_SPEED::HeaderSize) + { + size_t dmaPixelBits = I2sBitsPerPixelBytes * elementSize; + size_t dmaSettingsBits = I2sBitsPerPixelBytes * (settingsSize + T_SPEED::HeaderSize); + + // bits + half rounding byte of bits / bits per byte + size_t i2sBufferSize = (pixelCount * dmaPixelBits + dmaSettingsBits + 4) / 8; + + i2sBufferSize = i2sBufferSize + T_SPEED::BreakMabSize; + + // size is rounded up to nearest c_I2sByteBoundarySize + i2sBufferSize = NeoUtil::RoundUp(i2sBufferSize, c_I2sByteBoundarySize); + + // size of a looping silent space rounded up to nearest c_I2sByteBoundarySize + size_t i2sResetSize = NeoUtil::RoundUp(T_SPEED::MtbpSize, c_I2sByteBoundarySize); + + // protocol limits use of full block size to c_I2sByteBoundarySize + size_t is2BufMaxBlockSize = (c_maxDmaBlockSize / c_I2sByteBoundarySize) * c_I2sByteBoundarySize; + + _data = static_cast(malloc(_sizeData)); + // first "slot" cleared due to protocol requiring it to be zero + memset(_data, 0x00, 1); + + AllocateI2s(i2sBufferSize, i2sResetSize, is2BufMaxBlockSize, T_SPEED::MtbpLevel); + } + + NeoEsp8266I2sDmx512MethodBase([[maybe_unused]] uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + NeoEsp8266I2sDmx512MethodBase(pixelCount, elementSize, settingsSize) + { + } + + ~NeoEsp8266I2sDmx512MethodBase() + { + uint8_t waits = 1; + while (!IsReadyToUpdate()) + { + waits = 2; + yield(); + } + + // wait for any pending sends to complete + // due to internal i2s caching/send delays, this can more that once the data size + uint32_t time = micros(); + while ((micros() - time) < ((getPixelTime() + T_SPEED::MtbpUs) * waits)) + { + yield(); + } + + FreeI2s(); + + free(_data); + } + + bool IsReadyToUpdate() const + { + return IsIdle(); + } + + void Initialize() + { + InitializeI2s(T_SPEED::I2sClockDivisor, T_SPEED::I2sBaseClockDivisor); + } + + void IRAM_ATTR Update(bool) + { + // wait for not actively sending data + while (!IsReadyToUpdate()) + { + yield(); + } + FillBuffers(); + + WriteI2s(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data + T_SPEED::HeaderSize; + }; + + size_t getDataSize() const + { + return _sizeData - T_SPEED::HeaderSize; + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + // given 11 sending bits per pixel byte, + static const uint16_t I2sBitsPerPixelBytes = 11; + + const size_t _sizeData; // Size of '_data' buffer + uint8_t* _data; // Holds LED color values + + // encodes the data with start and stop bits + // input buffer is bytes + // output stream is uint31_t + static void Encoder(const uint8_t* pSrc, const uint8_t* pSrcEnd, + uint32_t* pOutput, const uint32_t* pOutputEnd) + { + static const uint32_t Mtbp = 0xffffffff * T_SPEED::MtbpLevel; + const uint8_t* pData = pSrc; + + int8_t outputBit = 32; + uint32_t output = 0; + + // DATA stream, one start, two stop + while (pData < pSrcEnd) + { + uint8_t data = T_SPEED::Convert( *(pData++) ); + + if (outputBit > 10) + { + // simple + outputBit -= 1; + output |= T_SPEED::StartBit << outputBit; + + outputBit -= 8; + output |= data << outputBit; + + outputBit -= 2; + output |= T_SPEED::StopBits << outputBit; + } + else + { + // split across an output uint32_t + // handle start bit + if (outputBit < 1) + { + *(pOutput++) = output; + output = 0; + outputBit += 32; + } + outputBit -= 1; + output |= (T_SPEED::StartBit << outputBit); + + // handle data bits + if (outputBit < 8) + { + output |= data >> (8 - outputBit); + + *(pOutput++) = output; + output = 0; + outputBit += 32; + } + outputBit -= 8; + output |= data << outputBit; + + // handle stop bits + if (outputBit < 2) + { + output |= T_SPEED::StopBits >> (2 - outputBit); + + *(pOutput++) = output; + output = 0; + outputBit += 32; + } + outputBit -= 2; + output |= T_SPEED::StopBits << outputBit; + } + } + if (outputBit > 0) + { + // padd last output uint32_t with Mtbp + output |= Mtbp >> (32 - outputBit); + *(pOutput++) = output; + } + // fill the rest of the output with Mtbp + while (pOutput < pOutputEnd) + { + *(pOutput++) = Mtbp; + } + } + + + void FillBuffers() + { + uint32_t* pDma32 = reinterpret_cast(_i2sBuffer); + const uint32_t* pDma32End = reinterpret_cast(_i2sBuffer + _i2sBufferSize); + + // first insert Break space as needed + for (size_t count = 1; + count < (T_SPEED::BreakMabSize/sizeof(T_SPEED::Break)); + count++) + { + *(pDma32++) = T_SPEED::Break; + } + // then tail of break with mab + *(pDma32++) = T_SPEED::BreakMab; + + Encoder(_data, _data + _sizeData, pDma32, pDma32End); + } + + uint32_t getPixelTime() const + { + return (T_SPEED::ByteSendTimeUs * this->_sizeData); + }; + +}; + + +// normal +typedef NeoEsp8266I2sDmx512MethodBase NeoEsp8266Dmx512Method; +typedef NeoEsp8266I2sDmx512MethodBase NeoEsp8266Ws2821Method; + +// inverted +typedef NeoEsp8266I2sDmx512MethodBase NeoEsp8266Dmx512InvertedMethod; +typedef NeoEsp8266I2sDmx512MethodBase NeoEsp8266Ws2821InvertedMethod; + +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sMethodCore.cpp b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sMethodCore.cpp new file mode 100644 index 00000000..e47e0bfa --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sMethodCore.cpp @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#include +#include "../NeoUtil.h" +#include "NeoEsp8266I2sMethodCore.h" + +#ifdef ARDUINO_ARCH_ESP8266 + +NeoEsp8266I2sMethodCore* NeoEsp8266I2sMethodCore::s_this; + +#endif \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sMethodCore.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sMethodCore.h new file mode 100644 index 00000000..db67126e --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266I2sMethodCore.h @@ -0,0 +1,362 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once +#ifdef ARDUINO_ARCH_ESP8266 + +#include "Arduino.h" + +extern "C" +{ +#include "osapi.h" +#include "ets_sys.h" + +#include "i2s_reg.h" + +#ifdef ARDUINO_ESP8266_MAJOR //this define was added in ESP8266 Arduino Core version v3.0.1 +#include "core_esp8266_i2s.h" //for Arduino core >= 3.0.1 +#else +#include "i2s.h" //for Arduino core <= 3.0.0 +#endif + +#include "eagle_soc.h" +#include "esp8266_peri.h" +#include "slc_register.h" + +#include "osapi.h" +#include "ets_sys.h" +#include "user_interface.h" + +#if !defined(__CORE_ESP8266_VERSION_H) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) + void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata); +#endif +} + +struct slc_queue_item +{ + uint32 blocksize : 12; + uint32 datalen : 12; + uint32 unused : 5; + uint32 sub_sof : 1; + uint32 eof : 1; + uint32 owner : 1; + uint8* buf_ptr; + struct slc_queue_item* next_link_ptr; +}; + +enum NeoDmaState +{ + NeoDmaState_Idle, + NeoDmaState_Pending, + NeoDmaState_Sending +}; + +const uint16_t c_maxDmaBlockSize = 4095; + +const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted to this + +class NeoEsp8266I2sMethodCore +{ +private: + static const uint8_t c_StateBlockCount = 2; + static const size_t c_StateDataSize = 4; // mulitples of c_I2sByteBoundarySize + + // i2s sends 4 byte elements, + static const uint16_t c_I2sByteBoundarySize = 4; + +protected: + static NeoEsp8266I2sMethodCore* s_this; // for the ISR + + volatile NeoDmaState _dmaState; + + slc_queue_item* _i2sBufDesc; // dma block descriptors + uint16_t _i2sBufDescCount; // count of block descriptors in _i2sBufDesc + + size_t _i2sBufferSize; // total size of _i2sBuffer + uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc + + size_t _i2sIdleDataTotalSize; // total size of represented zeroes, mulitple uses of _i2sIdleData + size_t _i2sIdleDataSize; // size of _i2sIdleData + uint8_t* _i2sIdleData; + + uint16_t _is2BufMaxBlockSize; // max size based on size of a pixel of a single block + + size_t GetSendSize() const + { + return _i2sBufferSize + _i2sIdleDataTotalSize; + } + + // This routine is called as soon as the DMA routine has something to tell us. All we + // handle here is the RX_EOF_INT status, which indicate the DMA has sent a buffer whose + // descriptor has the 'EOF' field set to 1. + // in the case of this code, the second to last state descriptor + static void IRAM_ATTR i2s_slc_isr(void) + { + ETS_SLC_INTR_DISABLE(); + + uint32_t slc_intr_status = SLCIS; + + SLCIC = 0xFFFFFFFF; + + if ((slc_intr_status & SLCIRXEOF) && s_this) + { + if (s_this->_dmaState != NeoDmaState_Idle) + { + // first two items are the state blocks + slc_queue_item* itemLoop = s_this->_i2sBufDesc; + slc_queue_item* itemLoopBreaker = itemLoop + 1; + // set to loop on idle items + itemLoopBreaker->next_link_ptr = itemLoop; + + s_this->_dmaState = NeoDmaState_Idle; + } + } + + ETS_SLC_INTR_ENABLE(); + } + + NeoEsp8266I2sMethodCore() + { }; + + void AllocateI2s(const size_t i2sBufferSize, // expected multiples of c_I2sByteBoundarySize + const size_t i2sZeroesSize, // expected multiples of c_I2sByteBoundarySize + const size_t is2BufMaxBlockSize, + const uint8_t idleLevel) + { + _i2sBufferSize = i2sBufferSize; + _i2sIdleDataTotalSize = i2sZeroesSize; + _i2sIdleDataSize = _i2sIdleDataTotalSize; + + size_t countIdleQueueItems = 1; + if (_i2sIdleDataSize > 256) + { + // reuse a single idle data buffer of 256 with multiple dma slc_queue_items + countIdleQueueItems = _i2sIdleDataSize / 256 + 1; + _i2sIdleDataSize = 256; + } + else + { + _i2sIdleDataSize = NeoUtil::RoundUp(_i2sIdleDataSize, c_I2sByteBoundarySize); + } + _is2BufMaxBlockSize = is2BufMaxBlockSize; + + _i2sBuffer = static_cast(malloc(_i2sBufferSize)); + // no need to initialize it, it gets overwritten on every send + _i2sIdleData = static_cast(malloc(_i2sIdleDataSize)); + memset(_i2sIdleData, idleLevel * 0xff, _i2sIdleDataSize); + + _i2sBufDescCount = (_i2sBufferSize / _is2BufMaxBlockSize) + 1 + + countIdleQueueItems + + c_StateBlockCount; // need more for state/latch blocks + + _i2sBufDesc = (slc_queue_item*)malloc(_i2sBufDescCount * sizeof(slc_queue_item)); + + s_this = this; // store this for the ISR + } + + void FreeI2s() + { + StopI2s(); + + s_this = nullptr; + pinMode(c_I2sPin, INPUT); + + free(_i2sBuffer); + free(_i2sBufDesc); + free(_i2sIdleData); + } + + bool IsIdle() const + { + return (_dmaState == NeoDmaState_Idle); + } + + + void DmaItemInit(slc_queue_item* item, uint8_t* data, size_t sizeData, slc_queue_item* itemNext) + { + item->owner = 1; + item->eof = 0; // no need to trigger interrupt generally + item->sub_sof = 0; + item->datalen = sizeData; + item->blocksize = sizeData; + item->buf_ptr = data; + item->unused = 0; + item->next_link_ptr = itemNext; + } + + void InitializeI2s(const uint32_t i2sClockDivisor, const uint32_t i2sBaseClockDivisor) + { + StopI2s(); + + pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA + + uint8_t* is2Buffer = _i2sBuffer; + uint8_t* is2BufferEnd = _i2sBuffer + _i2sBufferSize; + uint32_t is2BufferSize; + uint16_t indexDesc = 0; + + // prepare the two state/latch descriptors + uint16_t stateDataSize = min(c_StateDataSize, _i2sIdleDataSize); + while (indexDesc < c_StateBlockCount) + { + DmaItemInit(&_i2sBufDesc[indexDesc], _i2sIdleData, stateDataSize, &(_i2sBufDesc[indexDesc + 1])); + + indexDesc++; + } + + // prepare main data block decriptors that point into our one static dma buffer + is2BufferSize = _i2sBufferSize; + while (is2Buffer < is2BufferEnd) + { + uint32_t blockSize = (is2BufferSize > _is2BufMaxBlockSize) ? _is2BufMaxBlockSize : is2BufferSize; + + DmaItemInit(&_i2sBufDesc[indexDesc], is2Buffer, blockSize, &(_i2sBufDesc[indexDesc + 1])); + + is2Buffer += blockSize; + is2BufferSize -= blockSize; + indexDesc++; + } + + // last data item triggers EOF ISR + _i2sBufDesc[indexDesc - 1].eof = 1; + + // prepare idle block decriptors that point into our one idle dma buffer + is2BufferSize = _i2sIdleDataTotalSize; + while (indexDesc < _i2sBufDescCount) + { + uint32_t blockSize = (is2BufferSize > _i2sIdleDataSize) ? _i2sIdleDataSize : is2BufferSize; + + DmaItemInit(&_i2sBufDesc[indexDesc], _i2sIdleData, blockSize, &(_i2sBufDesc[indexDesc + 1])); + + is2Buffer += blockSize; + is2BufferSize -= blockSize; + indexDesc++; + } + + // the last item will loop to the first item + _i2sBufDesc[indexDesc - 1].next_link_ptr = reinterpret_cast(&(_i2sBufDesc[0])); + + // the last state block will loop to the first state block by defualt + _i2sBufDesc[c_StateBlockCount - 1].next_link_ptr = reinterpret_cast(&(_i2sBufDesc[0])); + + // setup the rest of i2s DMA + // + ETS_SLC_INTR_DISABLE(); + + // start off in idel state as that is what it will be all setup to be + // for the interrupt + _dmaState = NeoDmaState_Idle; + + SLCC0 |= SLCRXLR | SLCTXLR; + SLCC0 &= ~(SLCRXLR | SLCTXLR); + SLCIC = 0xFFFFFFFF; + + // Configure DMA + SLCC0 &= ~(SLCMM << SLCM); // clear DMA MODE + SLCC0 |= (1 << SLCM); // set DMA MODE to 1 + SLCRXDC |= SLCBINR | SLCBTNR; // enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE + SLCRXDC &= ~(SLCBRXFE | SLCBRXEM | SLCBRXFM); // disable RX_FILL, RX_EOF_MODE and RX_FILL_MODE + + // Feed DMA the 1st buffer desc addr + // To send data to the I2S subsystem, counter-intuitively we use the RXLINK part, not the TXLINK as you might + // expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw + // an error at us otherwise. Just feed it any random descriptor. + SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address + // set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid + SLCTXL |= (uint32) & (_i2sBufDesc[_i2sBufDescCount - 1]) << SLCTXLA; + SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address + // set RX descriptor address. use first of the data addresses + SLCRXL |= (uint32) & (_i2sBufDesc[0]) << SLCRXLA; + + ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL); + SLCIE = SLCIRXEOF; // Enable only for RX EOF interrupt + + ETS_SLC_INTR_ENABLE(); + + //Start transmission + SLCTXL |= SLCTXLS; + SLCRXL |= SLCRXLS; + + I2S_CLK_ENABLE(); + I2SIC = 0x3F; + I2SIE = 0; + + //Reset I2S + I2SC &= ~(I2SRST); + I2SC |= I2SRST; + I2SC &= ~(I2SRST); + + // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) + I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); + I2SFC |= I2SDE; //Enable DMA + // Set RX/TX CHAN_MOD=0 + I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); + + // set the rate + uint32_t i2s_clock_div = i2sClockDivisor & I2SCDM; + uint8_t i2s_bck_div = i2sBaseClockDivisor & I2SBDM; + + //!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right + I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); + I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (i2s_bck_div << I2SBD) | (i2s_clock_div << I2SCD); + + I2SC |= I2STXS; // Start transmission + } + + void WriteI2s() + { + // first two items are the state blocks + slc_queue_item* itemLoopBreaker = &(_i2sBufDesc[1]); + slc_queue_item* itemData = itemLoopBreaker + 1; + + // set to NOT loop on idle items + itemLoopBreaker->next_link_ptr = itemData; + + _dmaState = NeoDmaState_Sending; + } + + void StopI2s() + { + ETS_SLC_INTR_DISABLE(); + + // Disable any I2S send or receive + I2SC &= ~(I2STXS | I2SRXS); + + // Reset I2S + I2SC &= ~(I2SRST); + I2SC |= I2SRST; + I2SC &= ~(I2SRST); + + SLCIC = 0xFFFFFFFF; + SLCIE = 0; + SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address + SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address + + pinMode(c_I2sPin, INPUT); + } +}; + +#endif // ARDUINO_ARCH_ESP8266 \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp8266UartMethod.cpp b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266UartMethod.cpp new file mode 100644 index 00000000..ee74d472 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266UartMethod.cpp @@ -0,0 +1,176 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 UART hardware + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#ifdef ARDUINO_ARCH_ESP8266 + +#include +#include "../NeoSettings.h" +#include "NeoEsp8266UartMethod.h" +#include +extern "C" +{ + #include +} + +const volatile uint8_t* IRAM_ATTR NeoEsp8266UartContext::FillUartFifo(uint8_t uartNum, + const volatile uint8_t* start, + const volatile uint8_t* end) +{ + // Remember: UARTs send less significant bit (LSB) first so + // pushing ABCDEF byte will generate a 0FEDCBA1 signal, + // including a LOW(0) start & a HIGH(1) stop bits. + // Also, we have configured UART to invert logic levels, so: + const uint8_t _uartData[4] = { + 0b110111, // On wire: 1 000 100 0 [Neopixel reads 00] + 0b000111, // On wire: 1 000 111 0 [Neopixel reads 01] + 0b110100, // On wire: 1 110 100 0 [Neopixel reads 10] + 0b000100, // On wire: 1 110 111 0 [NeoPixel reads 11] + }; + uint8_t avail = (UART_TX_FIFO_SIZE - GetTxFifoLength(uartNum)) / 4; + if (end - start > avail) + { + end = start + avail; + } + while (start < end) + { + uint8_t subpix = *start++; + Enqueue(uartNum, _uartData[(subpix >> 6) & 0x3]); + Enqueue(uartNum, _uartData[(subpix >> 4) & 0x3]); + Enqueue(uartNum, _uartData[(subpix >> 2) & 0x3]); + Enqueue(uartNum, _uartData[subpix & 0x3]); + } + return start; +} + +volatile NeoEsp8266UartInterruptContext* NeoEsp8266UartInterruptContext::s_uartInteruptContext[] = { nullptr, nullptr }; + +void NeoEsp8266UartInterruptContext::StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end) +{ + // send the pixels asynchronously + _asyncBuff = start; + _asyncBuffEnd = end; + + // enable the transmit interrupt + USIE(uartNum) |= (1 << UIFE); +} + +void NeoEsp8266UartInterruptContext::Attach(uint8_t uartNum) +{ + // Disable all interrupts + ETS_UART_INTR_DISABLE(); + + // Clear the RX & TX FIFOS + const uint32_t fifoResetFlags = (1 << UCTXRST) | (1 << UCRXRST); + USC0(uartNum) |= fifoResetFlags; + USC0(uartNum) &= ~(fifoResetFlags); + + // attach the ISR if needed + if (s_uartInteruptContext[0] == nullptr && + s_uartInteruptContext[1] == nullptr) + { + ETS_UART_INTR_ATTACH(Isr, s_uartInteruptContext); + } + + // attach the context + s_uartInteruptContext[uartNum] = this; + + // Set tx fifo trigger. 80 bytes gives us 200 microsecs to refill the FIFO + USC1(uartNum) = (80 << UCFET); + + // Disable RX & TX interrupts. It maybe still enabled by uart.c in the SDK + USIE(uartNum) &= ~((1 << UIFF) | (1 << UIFE)); + + // Clear all pending interrupts in UART1 + USIC(uartNum) = 0xffff; + + // Reenable interrupts + ETS_UART_INTR_ENABLE(); +} + +void NeoEsp8266UartInterruptContext::Detach(uint8_t uartNum) +{ + // Disable interrupts + ETS_UART_INTR_DISABLE(); + + if (s_uartInteruptContext[uartNum] != nullptr) + { + // turn off uart + USC1(uartNum) = 0; + USIC(uartNum) = 0xffff; + USIE(uartNum) = 0; + + s_uartInteruptContext[uartNum] = nullptr; + + if (s_uartInteruptContext[0] == nullptr && + s_uartInteruptContext[1] == nullptr) + { + // detach our ISR + ETS_UART_INTR_ATTACH(NULL, NULL); + + // return so we don't enable interrupts since there is no ISR anymore + return; + } + } + + // Reenable interrupts + ETS_UART_INTR_ENABLE(); +} + +// The xtos_1int handler calls with param1 as the arg, param2 as a pointer +// to an exception frame in memory. +void IRAM_ATTR NeoEsp8266UartInterruptContext::Isr(void* param, [[maybe_unused]] void* exceptionFrame) +{ + // make sure this is for us + if (param == s_uartInteruptContext) + { + // Interrupt handler is shared between UART0 & UART1 + // so we need to test for both + for (uint8_t uartNum = 0; uartNum < 2; uartNum++) + { + if (USIS(uartNum) && s_uartInteruptContext[uartNum] != nullptr) + { + // Fill the FIFO with new data + s_uartInteruptContext[uartNum]->_asyncBuff = FillUartFifo( + uartNum, + s_uartInteruptContext[uartNum]->_asyncBuff, + s_uartInteruptContext[uartNum]->_asyncBuffEnd); + + // Disable TX interrupt when done + if (s_uartInteruptContext[uartNum]->_asyncBuff == s_uartInteruptContext[uartNum]->_asyncBuffEnd) + { + // clear the TX FIFO Empty + USIE(uartNum) &= ~(1 << UIFE); + } + + // Clear all interrupts flags (just in case) + USIC(uartNum) = 0xffff; + } + } + } +} + +#endif + diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEsp8266UartMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266UartMethod.h new file mode 100644 index 00000000..3dd30a18 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEsp8266UartMethod.h @@ -0,0 +1,564 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 UART hardware + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP8266 + +// this template method class is used to track the data being sent on the uart +// when using the default serial ISR installed by the core +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class NeoEsp8266UartContext +{ +public: + // Gets the number of bytes waiting in the TX FIFO + static inline uint8_t IRAM_ATTR GetTxFifoLength(uint8_t uartNum) + { + return (USS(uartNum) >> USTXC) & 0xff; + } + // Append a byte to the TX FIFO + static inline void IRAM_ATTR Enqueue(uint8_t uartNum, uint8_t value) + { + USF(uartNum) = value; + } + + static const volatile uint8_t* IRAM_ATTR FillUartFifo(uint8_t uartNum, + const volatile uint8_t* start, + const volatile uint8_t* end); +}; + +// this template method class is used to track the data being sent on the uart +// when using our own UART ISR +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class NeoEsp8266UartInterruptContext : NeoEsp8266UartContext +{ +public: + NeoEsp8266UartInterruptContext() : + _asyncBuff(nullptr), + _asyncBuffEnd(nullptr) + { + } + + bool IsSending() + { + return (_asyncBuff != _asyncBuffEnd); + } + + void StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end); + void Attach(uint8_t uartNum); + void Detach(uint8_t uartNum); + +private: + volatile const uint8_t* _asyncBuff; + volatile const uint8_t* _asyncBuffEnd; + volatile static NeoEsp8266UartInterruptContext* s_uartInteruptContext[2]; + + static void IRAM_ATTR Isr(void* param, void* exceptionFrame); +}; + +// this template feature class is used a base for all others and contains +// common methods +// +class UartFeatureBase +{ +protected: + static void ConfigUart(uint8_t uartNum, bool invert) + { + // clear all invert bits + USC0(uartNum) &= ~((1 << UCDTRI) | (1 << UCRTSI) | (1 << UCTXI) | (1 << UCDSRI) | (1 << UCCTSI) | (1 << UCRXI)); + + if (!invert) + { + // For normal operations, + // Invert the TX voltage associated with logic level so: + // - A logic level 0 will generate a Vcc signal + // - A logic level 1 will generate a Gnd signal + USC0(uartNum) |= (1 << UCTXI); + } + } +}; + +// this template feature class is used to define the specifics for uart0 +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class UartFeature0 : UartFeatureBase +{ +public: + static const uint32_t Index = 0; + static void Init(uint32_t baud, bool invert) + { + // Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1) + Serial.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY); + ConfigUart(Index, invert); + } +}; + +// this template feature class is used to define the specifics for uart1 +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class UartFeature1 : UartFeatureBase +{ +public: + static const uint32_t Index = 1; + static void Init(uint32_t baud, bool invert) + { + // Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1) + Serial1.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY); + ConfigUart(Index, invert); + } +}; + +// this template method class is used a base for all others and contains +// common properties and methods +// +// used by NeoEsp8266Uart and NeoEsp8266AsyncUart +// +class NeoEsp8266UartBase +{ +protected: + const size_t _sizeData; // Size of '_data' buffer below + uint8_t* _data; // Holds LED color values + uint32_t _startTime; // Microsecond count when last update started + + NeoEsp8266UartBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize) + { + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + + ~NeoEsp8266UartBase() + { + free(_data); + } + +}; + +// this template method class is used to glue uart feature and context for +// synchronous uart method +// +// used by NeoEsp8266UartMethodBase +// T_UARTFEATURE - (UartFeature0 | UartFeature1) +// T_UARTCONTEXT - (NeoEsp8266UartContext | NeoEsp8266UartInterruptContext) +// +template class NeoEsp8266Uart : public NeoEsp8266UartBase +{ +protected: + NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + NeoEsp8266UartBase(pixelCount, elementSize, settingsSize) + { + } + + ~NeoEsp8266Uart() + { + // Wait until the TX fifo is empty. This way we avoid broken frames + // when destroying & creating a NeoPixelBus to change its length. + while (T_UARTCONTEXT::GetTxFifoLength(T_UARTFEATURE::Index) > 0) + { + yield(); + } + } + + void InitializeUart(uint32_t uartBaud, bool invert) + { + T_UARTFEATURE::Init(uartBaud, invert); + } + + void UpdateUart(bool) + { + // Since the UART can finish sending queued bytes in the FIFO in + // the background, instead of waiting for the FIFO to flush + // we annotate the start time of the frame so we can calculate + // when it will finish. + _startTime = micros(); + + // Then keep filling the FIFO until done + const uint8_t* ptr = _data; + const uint8_t* end = ptr + _sizeData; + while (ptr != end) + { + ptr = const_cast(T_UARTCONTEXT::FillUartFifo(T_UARTFEATURE::Index, ptr, end)); + } + } +}; + +// this template method class is used to glue uart feature and context for +// asynchronously uart method +// +// This UART controller uses two buffers that are swapped in every call to +// NeoPixelBus.Show(). One buffer contains the data that is being sent +// asynchronosly and another buffer contains the data that will be send +// in the next call to NeoPixelBus.Show(). +// +// Therefore, the result of NeoPixelBus.Pixels() is invalidated after +// every call to NeoPixelBus.Show() and must not be cached. +// +// used by NeoEsp8266UartMethodBase +// T_UARTFEATURE - (UartFeature0 | UartFeature1) +// T_UARTCONTEXT - (NeoEsp8266UartContext | NeoEsp8266UartInterruptContext) +// +template class NeoEsp8266AsyncUart : public NeoEsp8266UartBase +{ +protected: + NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + NeoEsp8266UartBase(pixelCount, elementSize, settingsSize) + { + _dataSending = static_cast(malloc(_sizeData)); + } + + ~NeoEsp8266AsyncUart() + { + // Remember: the UART interrupt can be sending data from _dataSending in the background + while (_context.IsSending()) + { + yield(); + } + // detach context, which will disable intr, may disable ISR + _context.Detach(T_UARTFEATURE::Index); + + free(_dataSending); + } + + void IRAM_ATTR InitializeUart(uint32_t uartBaud, bool invert) + { + T_UARTFEATURE::Init(uartBaud, invert); + + // attach the context, which will enable the ISR + _context.Attach(T_UARTFEATURE::Index); + } + + void UpdateUart(bool maintainBufferConsistency) + { + // Instruct ESP8266 hardware uart to send the pixels asynchronously + _context.StartSending(T_UARTFEATURE::Index, + _data, + _data + _sizeData); + + // Annotate when we started to send bytes, so we can calculate when we are ready to send again + _startTime = micros(); + + if (maintainBufferConsistency) + { + // copy editing to sending, + // this maintains the contract that "colors present before will + // be the same after", otherwise GetPixelColor will be inconsistent + memcpy(_dataSending, _data, _sizeData); + } + + // swap so the user can modify without affecting the async operation + std::swap(_dataSending, _data); + } + +private: + T_UARTCONTEXT _context; + + uint8_t* _dataSending; // Holds a copy of LED color values taken when UpdateUart began +}; + +class NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed + static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte +}; + +// NeoEsp8266UartSpeedWs2813 contains the timing constants used to get NeoPixelBus running with the Ws2813 +class NeoEsp8266UartSpeedWs2812x : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; // us between data send bursts to reset for next update +}; + +class NeoEsp8266UartSpeedSk6812 : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; // us between data send bursts to reset for next update +}; + +class NeoEsp8266UartSpeedTm1814 : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; // us between data send bursts to reset for next update +}; + +class NeoEsp8266UartSpeedTm1829 : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 200; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartSpeed800Kbps contains the timing constant used to get NeoPixelBus running at 800Khz +class NeoEsp8266UartSpeed800Kbps : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartSpeed400Kbps contains the timing constant used to get NeoPixelBus running at 400Khz +class NeoEsp8266UartSpeed400Kbps +{ +public: + static const uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed + static const uint32_t UartBaud = 1600000; // 400mhz, 4 serial bytes per NeoByte + static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartSpeedApa106 contains the timing constant used to get NeoPixelBus running for Apa106 +// Pulse cycle = 1.71 = 1.368 longer than normal, 0.731 slower, NeoEsp8266UartSpeedApa1066 +class NeoEsp8266UartSpeedApa106 +{ +public: + static const uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element at 400khz speed + static const uint32_t UartBaud = 2339181; // APA106 pulse cycle of 1.71us, 4 serial bytes per NeoByte + static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update +}; + +class NeoEsp8266UartNotInverted +{ +public: + const static bool Inverted = false; +}; + +class NeoEsp8266UartInverted +{ +public: + const static bool Inverted = true; +}; + +// NeoEsp8266UartMethodBase is a light shell arround NeoEsp8266Uart or NeoEsp8266AsyncUart that +// implements the methods needed to operate as a NeoPixelBus method. +// +// T_SPEED - (NeoEsp8266UartSpeed*) +// T_BASE - (NeoEsp8266Uart | NeoEsp8266AsyncUart) +// T_INVERT - (NeoEsp8266UartNotInverted | NeoEsp8266UartInverted) +// +template +class NeoEsp8266UartMethodBase: public T_BASE +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEsp8266UartMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) + : T_BASE(pixelCount, elementSize, settingsSize) + { + } + NeoEsp8266UartMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) + : T_BASE(pixelCount, elementSize, settingsSize) + { + } + + bool IsReadyToUpdate() const + { + uint32_t delta = micros() - this->_startTime; + return delta >= getPixelTime() + T_SPEED::ResetTimeUs; + } + + void Initialize() + { + this->InitializeUart(T_SPEED::UartBaud, T_INVERT::Inverted); + + // Inverting logic levels can generate a phantom bit in the led strip bus + // We need to delay 50+ microseconds the output stream to force a data + // latch and discard this bit. Otherwise, that bit would be prepended to + // the first frame corrupting it. + this->_startTime = micros() - getPixelTime(); + } + + void Update(bool maintainBufferConsistency) + { + // Data latch = 50+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while (!this->IsReadyToUpdate()) + { + yield(); + } + this->UpdateUart(maintainBufferConsistency); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return this->_data; + }; + + size_t getDataSize() const + { + return this->_sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + uint32_t getPixelTime() const + { + return (T_SPEED::ByteSendTimeUs * this->_sizeData); + }; +}; + +// uart 0 +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Ws2812xMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Sk6812Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0Tm1814Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0Tm1829Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Apa106Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0800KbpsMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0400KbpsMethod; + +typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2813Method; +typedef NeoEsp8266Uart0800KbpsMethod NeoEsp8266Uart0Ws2812Method; +typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2811Method; +typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2816Method; +typedef NeoEsp8266Uart0Tm1814Method NeoEsp8266Uart0Tm1914Method; +typedef NeoEsp8266Uart0Sk6812Method NeoEsp8266Uart0Lc8812Method; + +// uart 1 +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Ws2812xMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Sk6812Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1Tm1814Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1Tm1829Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Apa106Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1800KbpsMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1400KbpsMethod; + +typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2813Method; +typedef NeoEsp8266Uart1800KbpsMethod NeoEsp8266Uart1Ws2812Method; +typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2811Method; +typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2816Method; +typedef NeoEsp8266Uart1Tm1814Method NeoEsp8266Uart1Tm1914Method; +typedef NeoEsp8266Uart1Sk6812Method NeoEsp8266Uart1Lc8812Method; + +// uart 0 async +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Ws2812xMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Sk6812Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Tm1814Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Tm1829Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Apa106Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0800KbpsMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0400KbpsMethod; + +typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2813Method; +typedef NeoEsp8266AsyncUart0800KbpsMethod NeoEsp8266AsyncUart0Ws2812Method; +typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2811Method; +typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2816Method; +typedef NeoEsp8266AsyncUart0Tm1814Method NeoEsp8266AsyncUart0Tm1914Method; +typedef NeoEsp8266AsyncUart0Sk6812Method NeoEsp8266AsyncUart0Lc8812Method; + +// uart 1 async +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Ws2812xMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Sk6812Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Tm1814Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Tm1829Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Apa106Method; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1800KbpsMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1400KbpsMethod; + +typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2813Method; +typedef NeoEsp8266AsyncUart1800KbpsMethod NeoEsp8266AsyncUart1Ws2812Method; +typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2811Method; +typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2816Method; +typedef NeoEsp8266AsyncUart1Tm1814Method NeoEsp8266AsyncUart1Tm1914Method; +typedef NeoEsp8266AsyncUart1Sk6812Method NeoEsp8266AsyncUart1Lc8812Method; + +// inverted +// +// uart 0 +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0Ws2812xInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0Sk6812InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Tm1814InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart0Tm1829InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0Apa106InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0800KbpsInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart0400KbpsInvertedMethod; + +typedef NeoEsp8266Uart0Ws2812xInvertedMethod NeoEsp8266Uart0Ws2813InvertedMethod; +typedef NeoEsp8266Uart0800KbpsInvertedMethod NeoEsp8266Uart0Ws2812InvertedMethod; +typedef NeoEsp8266Uart0Ws2812xInvertedMethod NeoEsp8266Uart0Ws2811InvertedMethod; +typedef NeoEsp8266Uart0Ws2812xInvertedMethod NeoEsp8266Uart0Ws2816InvertedMethod; +typedef NeoEsp8266Uart0Tm1814InvertedMethod NeoEsp8266Uart0Tm1914InvertedMethod; +typedef NeoEsp8266Uart0Sk6812InvertedMethod NeoEsp8266Uart0Lc8812InvertedMethod; + +// uart 1 +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1Ws2812xInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1Sk6812InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Tm1814InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266Uart1Tm1829InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1Apa106InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1800KbpsInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266Uart1400KbpsInvertedMethod; + +typedef NeoEsp8266Uart1Ws2812xInvertedMethod NeoEsp8266Uart1Ws2813InvertedMethod; +typedef NeoEsp8266Uart1800KbpsInvertedMethod NeoEsp8266Uart1Ws2812InvertedMethod; +typedef NeoEsp8266Uart1Ws2812xInvertedMethod NeoEsp8266Uart1Ws2811InvertedMethod; +typedef NeoEsp8266Uart1Ws2812xInvertedMethod NeoEsp8266Uart1Ws2816InvertedMethod; +typedef NeoEsp8266Uart1Tm1814InvertedMethod NeoEsp8266Uart1Tm1914InvertedMethod; +typedef NeoEsp8266Uart1Sk6812InvertedMethod NeoEsp8266Uart1Lc8812InvertedMethod; + +// uart 0 async +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Ws2812xInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Sk6812InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Tm1814InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart0Tm1829InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0Apa106InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0800KbpsInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart0400KbpsInvertedMethod; + +typedef NeoEsp8266AsyncUart0Ws2812xInvertedMethod NeoEsp8266AsyncUart0Ws2813InvertedMethod; +typedef NeoEsp8266AsyncUart0800KbpsInvertedMethod NeoEsp8266AsyncUart0Ws2812InvertedMethod; +typedef NeoEsp8266AsyncUart0Ws2812xInvertedMethod NeoEsp8266AsyncUart0Ws2811InvertedMethod; +typedef NeoEsp8266AsyncUart0Ws2812xInvertedMethod NeoEsp8266AsyncUart0Ws2816InvertedMethod; +typedef NeoEsp8266AsyncUart0Tm1814InvertedMethod NeoEsp8266AsyncUart0Tm1914InvertedMethod; +typedef NeoEsp8266AsyncUart0Sk6812InvertedMethod NeoEsp8266AsyncUart0Lc8812InvertedMethod; + +// uart 1 async +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Ws2812xInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Sk6812InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Tm1814InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartNotInverted> NeoEsp8266AsyncUart1Tm1829InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1Apa106InvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1800KbpsInvertedMethod; +typedef NeoEsp8266UartMethodBase, NeoEsp8266UartInverted> NeoEsp8266AsyncUart1400KbpsInvertedMethod; + +typedef NeoEsp8266AsyncUart1Ws2812xInvertedMethod NeoEsp8266AsyncUart1Ws2813InvertedMethod; +typedef NeoEsp8266AsyncUart1800KbpsInvertedMethod NeoEsp8266AsyncUart1Ws2812InvertedMethod; +typedef NeoEsp8266AsyncUart1Ws2812xInvertedMethod NeoEsp8266AsyncUart1Ws2811InvertedMethod; +typedef NeoEsp8266AsyncUart1Ws2812xInvertedMethod NeoEsp8266AsyncUart1Ws2816InvertedMethod; +typedef NeoEsp8266AsyncUart1Tm1814InvertedMethod NeoEsp8266AsyncUart1Tm1914InvertedMethod; +typedef NeoEsp8266AsyncUart1Sk6812InvertedMethod NeoEsp8266AsyncUart1Lc8812InvertedMethod; +#endif + diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEspBitBangMethod.cpp b/lib/NeoPixelBus/src/internal/methods/NeoEspBitBangMethod.cpp new file mode 100644 index 00000000..93ae066f --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEspBitBangMethod.cpp @@ -0,0 +1,156 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 and Esp32 + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + +#include + +static inline uint32_t getCycleCount(void) +{ + uint32_t ccount; + +#if defined(CONFIG_IDF_TARGET_ESP32C3) + __asm__ __volatile__("csrr %0,0x7e2":"=r" (ccount)); + //ccount = esp_cpu_get_ccount(); +#else + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); +#endif + return ccount; +} + +void IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels, + const uint8_t* end, + uint8_t pin, + uint32_t t0h, + uint32_t t1h, + uint32_t period, + size_t sizePixel, + uint32_t tSpacing, + bool invert) +{ + uint32_t setValue = _BV(pin); + uint32_t clearValue = _BV(pin); + uint8_t mask = 0x80; + uint8_t subpix = *pixels++; + uint8_t element = 0; + uint32_t cyclesStart = 0; // trigger emediately + uint32_t cyclesNext = 0; + +#if defined(ARDUINO_ARCH_ESP32) +#if defined(CONFIG_IDF_TARGET_ESP32C3) + volatile uint32_t* setRegister = &GPIO.out_w1ts.val; + volatile uint32_t* clearRegister = &GPIO.out_w1tc.val; + setValue = _BV(pin); + clearValue = _BV(pin); +#else + volatile uint32_t* setRegister = &GPIO.out_w1ts; + volatile uint32_t* clearRegister = &GPIO.out_w1tc; +#endif // defined(CONFIG_IDF_TARGET_ESP32C3) +#else + uint32_t setRegister = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TS_ADDRESS; + uint32_t clearRegister = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TC_ADDRESS; + if (pin == 16) + { + setRegister = RTC_GPIO_OUT; + clearRegister = RTC_GPIO_OUT; + // reading AND writing RTC_GPIO_OUT is too slow inside the loop so + // we only do writing in the loop + clearValue = (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe); + setValue = clearValue | 1; + } +#endif // defined(ARDUINO_ARCH_ESP32) + + if (invert) + { + std::swap(setRegister, clearRegister); + std::swap(setValue, clearValue); + } + + for (;;) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = t0h; + if (subpix & mask) + { + cyclesBit = t1h; + } + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + while (((cyclesStart = getCycleCount()) - cyclesNext) < period); + + // set pin state +#if defined(ARDUINO_ARCH_ESP32) + *setRegister = setValue; +#else + WRITE_PERI_REG(setRegister, setValue); +#endif + + // wait for the LOW + while ((getCycleCount() - cyclesStart) < cyclesBit); + + // reset pin start +#if defined(ARDUINO_ARCH_ESP32) + *clearRegister = clearValue; +#else + WRITE_PERI_REG(clearRegister, clearValue); +#endif + + cyclesNext = cyclesStart; + + // next bit + mask >>= 1; + if (mask == 0) + { + // no more bits to send in this byte + // check for another byte + if (pixels >= end) + { + // no more bytes to send so stop + break; + } + // reset mask to first bit and get the next byte + mask = 0x80; + subpix = *pixels++; + + // if pixel spacing is needed + if (tSpacing) + { + element++; + if (element == sizePixel) + { + element = 0; + + // wait for pixel spacing + while ((getCycleCount() - cyclesNext) < tSpacing); + } + } + } + } +} + + +#endif // defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) diff --git a/lib/NeoPixelBus/src/internal/methods/NeoEspBitBangMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoEspBitBangMethod.h new file mode 100644 index 00000000..42f10b13 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoEspBitBangMethod.h @@ -0,0 +1,378 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 and Esp32 + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + +#if defined(ARDUINO_ARCH_ESP8266) +#include +#endif +#if defined(CONFIG_IDF_TARGET_ESP32C3) +#define CYCLES_LOOPTEST (1) // adjustment due to loop exit test instruction cycles +#elif defined(CONFIG_IDF_TARGET_ESP32S3) +#define CYCLES_LOOPTEST (2) // adjustment due to loop exit test instruction cycles +#else +#define CYCLES_LOOPTEST (4) // adjustment due to loop exit test instruction cycles +#endif + +extern void neoEspBitBangWriteSpacingPixels(const uint8_t* pixels, + const uint8_t* end, + uint8_t pin, + uint32_t t0h, + uint32_t t1h, + uint32_t period, + size_t sizePixel, + uint32_t tSpacing, + bool invert); + + +class NeoEspNotInverted +{ +public: + const static uint8_t IdleLevel = LOW; +}; + +class NeoEspInverted +{ +public: + const static uint8_t IdleLevel = HIGH; +}; + +// ! VALUES ARE NOT CONFIRMED +// TODO - these need to be verified +// TM1934 - 320ns, 320ns, 640ns +class NeoEspBitBangSpeedTM1934 +{ +public: + const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us + const static uint32_t T1H = (F_CPU / 1052632 - CYCLES_LOOPTEST); // 0.95us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 300; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeedWs2811 +{ +public: + const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us + const static uint32_t T1H = (F_CPU / 1052632 - CYCLES_LOOPTEST); // 0.95us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 300; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeedWs2812x +{ +public: + const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us + const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 300; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeedSk6812 +{ +public: + const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us + const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 80; + const static uint32_t TInterPixel = 0; +}; + +// Tm1814 normal is inverted signal +class NeoEspBitBangSpeedTm1814 +{ +public: + const static uint32_t T0H = (F_CPU / 2916666 - CYCLES_LOOPTEST); // 0.35us + const static uint32_t T1H = (F_CPU / 1666666 - CYCLES_LOOPTEST); // 0.75us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 200; + const static uint32_t TInterPixel = 0; +}; + +// Tm1829 normal is inverted signal +class NeoEspBitBangSpeedTm1829 +{ +public: + const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us + const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 200; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeed800Kbps +{ +public: + const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us + const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + static const uint32_t ResetTimeUs = 50; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeed400Kbps +{ +public: + const static uint32_t T0H = (F_CPU / 2000000 - CYCLES_LOOPTEST); + const static uint32_t T1H = (F_CPU / 833333 - CYCLES_LOOPTEST); + const static uint32_t Period = (F_CPU / 400000 - CYCLES_LOOPTEST); + + static const uint32_t ResetTimeUs = 50; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeedApa106 +{ +public: + const static uint32_t T0H = (F_CPU / 2857143 - CYCLES_LOOPTEST); // 0.35us + const static uint32_t T1H = (F_CPU / 740741 - CYCLES_LOOPTEST); // 1.35 + const static uint32_t Period = (F_CPU / 606061 - CYCLES_LOOPTEST); // 1.65us + + static const uint32_t ResetTimeUs = 50; + const static uint32_t TInterPixel = 0; +}; + +class NeoEspBitBangSpeedIntertek +{ +public: + const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us + const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit + + const static uint32_t ResetTimeUs = 12470; + const static uint32_t TInterPixel = (F_CPU / 50000); // 20us +}; + + +template class NeoEspBitBangEncode : public T_SPEED, public T_INVERTED +{ +public: + static void WritePixels(uint8_t pin, + const uint8_t* data, + size_t sizeData, + size_t sizePixel) + { + neoEspBitBangWriteSpacingPixels(data, + data + sizeData, + pin, + T_SPEED::T0H, + T_SPEED::T1H, + T_SPEED::Period, + sizePixel, + T_SPEED::TInterPixel, + T_INVERTED::IdleLevel); + } +}; + +template class NeoEspBitBangMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoEspBitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizePixel(elementSize), + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin) + { + pinMode(pin, OUTPUT); + + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + + ~NeoEspBitBangMethodBase() + { + pinMode(_pin, INPUT); + + free(_data); + } + + bool IsReadyToUpdate() const + { + uint32_t delta = micros() - _endTime; + + return (delta >= T_ENCODER::ResetTimeUs); + } + + void Initialize() + { + digitalWrite(_pin, T_ENCODER::IdleLevel); + + _endTime = micros(); + } + + void Update(bool) + { + // Data latch = 50+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while (!IsReadyToUpdate()) + { + yield(); // allows for system yield if needed + } + + // Need 100% focus on instruction timing +#if defined(ARDUINO_ARCH_ESP32) + // delay(1); // required ? + portMUX_TYPE updateMux = portMUX_INITIALIZER_UNLOCKED; + + portENTER_CRITICAL(&updateMux); +#else + noInterrupts(); +#endif + + T_ENCODER::WritePixels(_pin, + _data, + _sizeData, + _sizePixel); + +#if defined(ARDUINO_ARCH_ESP32) + portEXIT_CRITICAL(&updateMux); +#else + interrupts(); +#endif + + // save EOD time for latch on next call + _endTime = micros(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizePixel; // size of a pixel in _data + const size_t _sizeData; // Size of '_data' buffer below + const uint8_t _pin; // output pin number + + uint32_t _endTime; // Latch timing reference + uint8_t* _data; // Holds LED color values +}; + + +#if defined(ARDUINO_ARCH_ESP32) + +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangWs2811Method; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangWs2812xMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangSk6812Method; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangTm1814Method; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangTm1829Method; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBang800KbpsMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBang400KbpsMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangApa106Method; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangIntertekMethod; + +typedef NeoEsp32BitBangWs2812xMethod NeoEsp32BitBangWs2813Method; +typedef NeoEsp32BitBang800KbpsMethod NeoEsp32BitBangWs2812Method; +typedef NeoEsp32BitBangWs2812xMethod NeoEsp32BitBangWs2816Method; +typedef NeoEsp32BitBangTm1814Method NeoEsp32BitBangTm1914Method; +typedef NeoEsp32BitBangSk6812Method NeoEsp32BitBangLc8812Method; + +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangWs2811InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangWs2812xInvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangSk6812InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangTm1814InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangTm1829InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBang800KbpsInvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBang400KbpsInvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangApa106InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp32BitBangIntertekInvertedMethod; + +typedef NeoEsp32BitBangWs2812xInvertedMethod NeoEsp32BitBangWs2813InvertedMethod; +typedef NeoEsp32BitBang800KbpsInvertedMethod NeoEsp32BitBangWs2812InvertedMethod; +typedef NeoEsp32BitBangWs2812xInvertedMethod NeoEsp32BitBangWs2816InvertedMethod; +typedef NeoEsp32BitBangTm1814InvertedMethod NeoEsp32BitBangTm1914InvertedMethod; +typedef NeoEsp32BitBangSk6812InvertedMethod NeoEsp32BitBangLc8812InvertedMethod; + +#else // defined(ARDUINO_ARCH_ESP8266) + +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangWs2811Method; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangWs2812xMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangSk6812Method; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangTm1814Method; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangTm1829Method; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBang800KbpsMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBang400KbpsMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangApa106Method; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangIntertekMethod; + +typedef NeoEsp8266BitBangWs2812xMethod NeoEsp8266BitBangWs2813Method; +typedef NeoEsp8266BitBang800KbpsMethod NeoEsp8266BitBangWs2812Method; +typedef NeoEsp8266BitBangWs2812xMethod NeoEsp8266BitBangWs2816Method; +typedef NeoEsp8266BitBangTm1814Method NeoEsp8266BitBangTm1914Method; +typedef NeoEsp8266BitBangSk6812Method NeoEsp8266BitBangLc8812Method; + +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangWs2811InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangWs2812xInvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangSk6812InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangTm1814InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangTm1829InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBang800KbpsInvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBang400KbpsInvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangApa106InvertedMethod; +typedef NeoEspBitBangMethodBase> NeoEsp8266BitBangIntertekInvertedMethod; + +typedef NeoEsp8266BitBangWs2812xInvertedMethod NeoEsp8266BitBangWs2813InvertedMethod; +typedef NeoEsp8266BitBang800KbpsInvertedMethod NeoEsp8266BitBangWs2812InvertedMethod; +typedef NeoEsp8266BitBangWs2812xInvertedMethod NeoEsp8266BitBangWs2816InvertedMethod; +typedef NeoEsp8266BitBangTm1814InvertedMethod NeoEsp8266BitBangTm1914InvertedMethod; +typedef NeoEsp8266BitBangSk6812InvertedMethod NeoEsp8266BitBangLc8812InvertedMethod; + +#endif // defined(ARDUINO_ARCH_ESP32) + +// ESP bitbang doesn't have defaults and should avoided except for testing + +#endif // defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) diff --git a/lib/NeoPixelBus/src/internal/methods/NeoNrf52xMethod.h b/lib/NeoPixelBus/src/internal/methods/NeoNrf52xMethod.h new file mode 100644 index 00000000..5a509ba1 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoNrf52xMethod.h @@ -0,0 +1,683 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper classes for Nrf52* MCUs. +Nano 33 BLE + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. +The contents of this file were taken from the Adafruit NeoPixel library +and modified only to fit within individual calling functions. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#if defined(ARDUINO_ARCH_NRF52840) + +const uint16_t c_dmaBytesPerDataByte = 8 * sizeof(nrf_pwm_values_common_t); // bits * bytes to represent pulse + +// for Bit* variables +// count 1 = 0.0625us, so max count (32768) is 2048us + +class NeoNrf52xPwmSpeedWs2811 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 14 | 0x8000; // ~0.9us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint16_t CountReset = 240; // 300 us / 1.25us + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmSpeedWs2812x +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.4us + const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 240 ; // 300us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmSpeedSk6812 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.4us + const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 64; // 80us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmSpeedTm1814 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 12; // ~0.7us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 160; // 200us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmSpeedTm1829 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 160; // 200us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmSpeedTm1914 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 12; // ~0.7us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 160; // 200us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmSpeed800Kbps +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.4us + const static nrf_pwm_values_common_t Bit1 = 13 | 0x8000; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 40; // 50us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmSpeed400Kbps +{ +public: + const static uint32_t CountTop = 40UL; // 2.5us + const static nrf_pwm_values_common_t Bit0 = 13 | 0x8000; // ~0.8us + const static nrf_pwm_values_common_t Bit1 = 26 | 0x8000; // ~1.6us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint16_t CountReset = 20; // 50 us / 2.5us + const static PinStatus IdleLevel = LOW; +}; + +// count 1 = 0.0625us, so max count (32768) is 2048us +class NeoNrf52xPwmSpeedApa106 +{ +public: + const static uint32_t CountTop = 26UL; // ~1.525us (target is 1.65us) + const static nrf_pwm_values_common_t Bit0 = 6 | 0x8000; // ~0.375us (target is 0.35) + const static nrf_pwm_values_common_t Bit1 = 21 | 0x8000; // ~1.3125us (target is 1.350) + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 40; // 50us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmSpeedTx1812 +{ +public: + const static uint32_t CountTop = 14UL; // ~0.875us (target is 0.9us) + const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3125us (target is 0.3) + const static nrf_pwm_values_common_t Bit1 = 10 | 0x8000; // ~0.625us (target is 0.6) + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 228; // 200us / 0.875us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmInvertedSpeedWs2811 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 14; // ~0.9us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint16_t CountReset = 240; // 300 us / 1.25us + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmInvertedSpeedWs2812x +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 6; // ~0.4us + const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 240; // 300us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmInvertedSpeedSk6812 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 6; // ~0.4us + const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 64; // 80us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmInvertedSpeedTm1814 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 12 | 0x8000; // ~0.7us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 160; // 200us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmInvertedSpeedTm1829 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 160; // 200us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmInvertedSpeedTm1914 +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 5 | 0x8000; // ~0.3us + const static nrf_pwm_values_common_t Bit1 = 12 | 0x8000; // ~0.7us + const static nrf_pwm_values_common_t BitReset = 0x8000; // LOW + const static uint32_t CountReset = 160; // 200us / 1.25us pulse width + const static PinStatus IdleLevel = LOW; +}; + +class NeoNrf52xPwmInvertedSpeed800Kbps +{ +public: + const static uint32_t CountTop = 20UL; // 1.25us + const static nrf_pwm_values_common_t Bit0 = 6; // ~0.4us + const static nrf_pwm_values_common_t Bit1 = 13; // ~0.8us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 40; // 50us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmInvertedSpeed400Kbps +{ +public: + const static uint32_t CountTop = 40UL; // 2.5us + const static nrf_pwm_values_common_t Bit0 = 13; // ~0.8us + const static nrf_pwm_values_common_t Bit1 = 26; // ~1.6us + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint16_t CountReset = 20; // 50 us / 2.5us + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmInvertedSpeedApa106 +{ +public: + const static uint32_t CountTop = 26UL; // ~1.525us (target is 1.65us) + const static nrf_pwm_values_common_t Bit0 = 6; // ~0.375us (target is 0.35) + const static nrf_pwm_values_common_t Bit1 = 21; // ~1.3125us (target is 1.350) + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 40; // 50us / 1.25us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwmInvertedSpeedTx1812 +{ +public: + const static uint32_t CountTop = 14UL; // ~0.875us (target is 0.9us) + const static nrf_pwm_values_common_t Bit0 = 5; // ~0.3125us (target is 0.3) + const static nrf_pwm_values_common_t Bit1 = 10; // ~0.625us (target is 0.6) + const static nrf_pwm_values_common_t BitReset = 0x0000; // HIGH + const static uint32_t CountReset = 228; // 200us / 0.875us pulse width + const static PinStatus IdleLevel = HIGH; +}; + +class NeoNrf52xPwm0 +{ +public: + NeoNrf52xPwm0() {}; + + inline static NRF_PWM_Type* Pwm() + { + return NRF_PWM0; + } +}; + +class NeoNrf52xPwm1 +{ +public: + NeoNrf52xPwm1() {}; + + inline static NRF_PWM_Type* Pwm() + { + return NRF_PWM1; + } +}; + +class NeoNrf52xPwm2 +{ +public: + NeoNrf52xPwm2() {}; + + inline static NRF_PWM_Type* Pwm() + { + return NRF_PWM2; + } +}; + +#if defined(NRF_PWM3) +class NeoNrf52xPwm3 +{ +public: + NeoNrf52xPwm3() {}; + + inline static NRF_PWM_Type* Pwm() + { + return NRF_PWM3; + } +}; +#endif + +// dynamic channel support +class NeoNrf52xPwmN +{ +public: + NeoNrf52xPwmN(NeoBusChannel channel) + { + NRF_PWM_Type* PWM[] = { + NRF_PWM0, + NRF_PWM1, + NRF_PWM2 +#ifdef NRF_PWM3 + ,NRF_PWM3 +#endif + }; + _pwm = PWM[channel]; + } + + inline NRF_PWM_Type* Pwm() const + { + return _pwm; + } + +protected: + NRF_PWM_Type* _pwm; + + NeoNrf52xPwmN() {}; +}; + +template class NeoNrf52xMethodBase +{ +public: + typedef NeoNoSettings SettingsObject; + + NeoNrf52xMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _pin(pin) + { + construct(); + } + + NeoNrf52xMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) : + _sizeData(pixelCount* elementSize + settingsSize), + _pin(pin), + _bus(channel) + { + construct(); + } + + ~NeoNrf52xMethodBase() + { + while (!IsReadyToUpdate()) + { + yield(); + } + + dmaDeinit(); + + pinMode(_pin, INPUT); + + free(_data); + free(_dmaBuffer); + } + + bool IsReadyToUpdate() const + { + return (_bus.Pwm()->EVENTS_STOPPED); + } + + void Initialize() + { + digitalWrite(_pin, T_SPEED::IdleLevel); + + dmaInit(); + + // must force a first update so the EVENTS_SEQEND gets set as + // you can't set it manually + FillBuffer(); + dmaStart(); + } + + void Update(bool) + { + // Data latch = 50+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while (!IsReadyToUpdate()) + { + yield(); // allows for system yield if needed + } + + FillBuffer(); + dmaStart(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + const uint8_t _pin; // output pin number + const T_BUS _bus; // holds instance for multi channel support + + uint8_t* _data; // Holds LED color values + size_t _dmaBufferSize; // total size of _dmaBuffer + nrf_pwm_values_common_t* _dmaBuffer; // Holds pixel data in native format for PWM hardware + + void construct() + { + pinMode(_pin, OUTPUT); + + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + + _dmaBufferSize = c_dmaBytesPerDataByte * _sizeData + sizeof(nrf_pwm_values_common_t); + _dmaBuffer = static_cast(malloc(_dmaBufferSize)); + } + + void dmaInit() + { + // only use channel zero + _bus.Pwm()->PSEL.OUT[0] = digitalPinToPinName(_pin); + _bus.Pwm()->PSEL.OUT[1] = NC; + _bus.Pwm()->PSEL.OUT[2] = NC; + _bus.Pwm()->PSEL.OUT[3] = NC; + + _bus.Pwm()->ENABLE = 1; + _bus.Pwm()->MODE = NRF_PWM_MODE_UP; + _bus.Pwm()->PRESCALER = NRF_PWM_CLK_16MHz; + _bus.Pwm()->COUNTERTOP = T_SPEED::CountTop; + _bus.Pwm()->LOOP = 1; // single fire so events get set + _bus.Pwm()->DECODER = NRF_PWM_LOAD_COMMON; + + // sequence zero is the primary data with a BitReset entry on the end for + // the delay repeating + _bus.Pwm()->SEQ[0].PTR = reinterpret_cast(_dmaBuffer); + _bus.Pwm()->SEQ[0].CNT = _dmaBufferSize / sizeof(nrf_pwm_values_common_t); + _bus.Pwm()->SEQ[0].REFRESH = 0; // ignored + _bus.Pwm()->SEQ[0].ENDDELAY = T_SPEED::CountReset; // ignored still? + + // sequence one is pointing to the BitReset entry at the end of the primary data + _bus.Pwm()->SEQ[1].PTR = reinterpret_cast(_dmaBuffer + (_bus.Pwm()->SEQ[0].CNT - 1)); + _bus.Pwm()->SEQ[1].CNT = 1; + _bus.Pwm()->SEQ[1].REFRESH = 0; // ignored + _bus.Pwm()->SEQ[1].ENDDELAY = 0; // ignored + + // stop when the loop finishes + _bus.Pwm()->SHORTS = PWM_SHORTS_LOOPSDONE_STOP_Msk; + _bus.Pwm()->INTEN = 0; + + dmaResetEvents(); + } + + void dmaDeinit() + { + _bus.Pwm()->ENABLE = 0; + _bus.Pwm()->PSEL.OUT[0] = NC; + } + + void FillBuffer() + { + nrf_pwm_values_common_t* pDma = _dmaBuffer; + nrf_pwm_values_common_t* pDmaEnd = _dmaBuffer + (_dmaBufferSize / sizeof(nrf_pwm_values_common_t)); + uint8_t* pEnd = _data + _sizeData; + + for (uint8_t* pData = _data; pData < pEnd; pData++) + { + uint8_t data = *pData; + + for (uint8_t bit = 0; bit < 8; bit++) + { + *(pDma++) = (data & 0x80) ? T_SPEED::Bit1 : T_SPEED::Bit0; + data <<= 1; + } + } + + // fill the rest with BitReset as it will get repeated when delaying or + // at the end before being stopped + while (pDma < pDmaEnd) + { + *(pDma++) = T_SPEED::BitReset; + } + } + + void dmaResetEvents() + { + _bus.Pwm()->EVENTS_LOOPSDONE = 0; + _bus.Pwm()->EVENTS_SEQEND[0] = 0; + _bus.Pwm()->EVENTS_SEQEND[1] = 0; + _bus.Pwm()->EVENTS_STOPPED = 0; + } + + void dmaStart() + { + dmaResetEvents(); + _bus.Pwm()->TASKS_SEQSTART[0] = 1; + } +}; + +// normal +typedef NeoNrf52xMethodBase NeoNrf52xPwmNWs2811Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNWs2812xMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNWs2816Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNSk6812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTm1814Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTm1829Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTm1914Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTx1812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNApa106Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwmN800KbpsMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmN400KbpsMethod; + +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Ws2811Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Ws2812xMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Ws2816Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Sk6812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tm1814Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tm1829Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tm1914Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tx1812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Apa106Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0800KbpsMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0400KbpsMethod; + +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Ws2811Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Ws2812xMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Ws2816Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Sk6812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tm1814Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tm1829Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tm1914Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tx1812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Apa106Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1800KbpsMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1400KbpsMethod; + +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Ws2811Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Ws2812xMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Ws2816Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Sk6812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tm1814Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tm1829Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tm1914Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tx1812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Apa106Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2800KbpsMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2400KbpsMethod; + +#if defined(NRF_PWM3) +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Ws2811Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Ws2812xMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Ws2816Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Sk6812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tm1814Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tm1829Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tm1914Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tx1812Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Apa106Method; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3800KbpsMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3400KbpsMethod; +#endif + +// inverted +typedef NeoNrf52xMethodBase NeoNrf52xPwmNWs2811InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNWs2812xInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNWs2816InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNSk6812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTm1814InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTm1829InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTm1914InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNTx1812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmNApa106InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmN800KbpsInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwmN400KbpsInvertedMethod; + +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Ws2811InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Ws2812xInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Ws2816InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Sk6812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tm1814InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tm1829InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tm1914InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Tx1812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0Apa106InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0800KbpsInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm0400KbpsInvertedMethod; + +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Ws2811InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Ws2812xInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Ws2816InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Sk6812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tm1814InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tm1829InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tm1914InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Tx1812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1Apa106InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1800KbpsInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm1400KbpsInvertedMethod; + +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Ws2811InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Ws2812xInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Ws2816InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Sk6812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tm1814InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tm1829InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tm1914InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Tx1812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2Apa106InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2800KbpsInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm2400KbpsInvertedMethod; + +#if defined(NRF_PWM3) +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Ws2811InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Ws2812xInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Ws2816InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Sk6812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tm1814InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tm1829InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tm1914InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Tx1812InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3Apa106InvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3800KbpsInvertedMethod; +typedef NeoNrf52xMethodBase NeoNrf52xPwm3400KbpsInvertedMethod; +#endif + +// defaults +typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2813Method; +typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2812xMethod; +typedef NeoNrf52xPwm2800KbpsMethod NeoWs2812Method; +typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2811Method; +typedef NeoNrf52xPwm2Ws2812xMethod NeoWs2816Method; +typedef NeoNrf52xPwm2Sk6812Method NeoSk6812Method; +typedef NeoNrf52xPwm2Tm1814Method NeoTm1814Method; +typedef NeoNrf52xPwm2Tm1829Method NeoTm1829Method; +typedef NeoNrf52xPwm2Tm1914Method NeoTm1914Method; +typedef NeoNrf52xPwm2Tx1812Method NeoTx1812Method; +typedef NeoNrf52xPwm2Sk6812Method NeoLc8812Method; +typedef NeoNrf52xPwm2Apa106Method NeoApa106Method; + +typedef NeoNrf52xPwm2Ws2812xMethod Neo800KbpsMethod; +typedef NeoNrf52xPwm2400KbpsMethod Neo400KbpsMethod; + +typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2813InvertedMethod; +typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2812xInvertedMethod; +typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2811InvertedMethod; +typedef NeoNrf52xPwm2800KbpsInvertedMethod NeoWs2812InvertedMethod; +typedef NeoNrf52xPwm2Ws2812xInvertedMethod NeoWs2816InvertedMethod; +typedef NeoNrf52xPwm2Sk6812InvertedMethod NeoSk6812InvertedMethod; +typedef NeoNrf52xPwm2Tm1814InvertedMethod NeoTm1814InvertedMethod; +typedef NeoNrf52xPwm2Tm1829InvertedMethod NeoTm1829InvertedMethod; +typedef NeoNrf52xPwm2Tm1914InvertedMethod NeoTm1914InvertedMethod; +typedef NeoNrf52xPwm2Tx1812InvertedMethod NeoTx1812InvertedMethod; +typedef NeoNrf52xPwm2Sk6812InvertedMethod NeoLc8812InvertedMethod; +typedef NeoNrf52xPwm2Apa106InvertedMethod NeoApa106InvertedMethod; + +typedef NeoNrf52xPwm2Ws2812xInvertedMethod Neo800KbpsInvertedMethod; +typedef NeoNrf52xPwm2400KbpsInvertedMethod Neo400KbpsInvertedMethod; + +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/NeoPixelAvr.c b/lib/NeoPixelBus/src/internal/methods/NeoPixelAvr.c new file mode 100644 index 00000000..bf602b99 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/NeoPixelAvr.c @@ -0,0 +1,717 @@ +/*------------------------------------------------------------------------- +Arduino library to control a wide variety of WS2811- and WS2812-based RGB +LED devices such as Adafruit FLORA RGB Smart Pixels and NeoPixel strips. +Currently handles 400 and 800 KHz bitstreams on 8, 12 and 16 MHz ATmega +MCUs, with LEDs wired for various color orders. 8 MHz MCUs provide +output on PORTB and PORTD, while 16 MHz chips can handle most output pins +(possible exception with upper PORT registers on the Arduino Mega). + +Written by Phil Burgess / Paint Your Dragon for Adafruit Industries, +contributions by PJRC, Michael Miller and other members of the open +source community. + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing products +from Adafruit! + +------------------------------------------------------------------------- +The contents of this file were taken from the Adafruit NeoPixel library +and modified only to fit within individual calling functions. + +NeoPixel is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixel is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if (defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)) && !defined(__arm__) + +#include + +// Hand-tuned assembly code issues data to the LED drivers at a specific +// rate. There's separate code for different CPU speeds (8, 12, 16 MHz) +// for both the WS2811 (400 KHz) and WS2812 (800 KHz) drivers. The +// datastream timing for the LED drivers allows a little wiggle room each +// way (listed in the datasheets), so the conditions for compiling each +// case are set up for a range of frequencies rather than just the exact +// 8, 12 or 16 MHz values, permitting use with some close-but-not-spot-on +// devices (e.g. 16.5 MHz DigiSpark). The ranges were arrived at based +// on the datasheet figures and have not been extensively tested outside +// the canonical 8/12/16 MHz speeds; there's no guarantee these will work +// close to the extremes (or possibly they could be pushed further). +// Keep in mind only one CPU speed case actually gets compiled; the +// resulting program isn't as massive as it might look from source here. + +#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU + +#ifdef PORTD // PORTD isn't present on ATtiny85, etc. +void send_data_8mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask) +{ + volatile size_t i = sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + volatile uint8_t n1; + volatile uint8_t n2 = 0; // First, next bits out + + // Squeezing an 800 KHz stream out of an 8 MHz chip requires code + // specific to each PORT register. At present this is only written + // to work with pins on PORTD or PORTB, the most likely use case -- + // this covers all the pins on the Adafruit Flora and the bulk of + // digital pins on the Arduino Pro 8 MHz (keep in mind, this code + // doesn't even get compiled for 16 MHz boards like the Uno, Mega, + // Leonardo, etc., so don't bother extending this out of hand). + // Additional PORTs could be added if you really need them, just + // duplicate the else and loop and change the PORT. Each add'l + // PORT will require about 150(ish) bytes of program space. + + // 10 instruction clocks per bit: HHxxxxxLLL + // OUT instructions: ^ ^ ^ (T=0,2,7) + + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; + n1 = lo; + + if (b & 0x80) + { + n1 = hi; + } + + // Dirty trick: RJMPs proceeding to the next instruction are used + // to delay two clock cycles in one instruction word (rather than + // using two NOPs). This was necessary in order to squeeze the + // loop down to exactly 64 words -- the maximum possible for a + // relative branch. + + asm volatile( + "headD:" "\n\t" // Clk Pseudocode + // Bit 7: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 6: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 5: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 4: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 3: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 2: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 1: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet) + // Bit 0: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "brne headD" "\n" // 2 while(i) (Z flag set above) + : [byte] "+r" (b), + [n1] "+r" (n1), + [n2] "+r" (n2), + [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTD)), + [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo) ); +} +#endif + +void send_data_8mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask) +{ + volatile size_t i = sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + volatile uint8_t n1; + volatile uint8_t n2 = 0; // First, next bits out + + // Same as above, just switched to PORTB and stripped of comments. + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; + n1 = lo; + if (b & 0x80) + { + n1 = hi; + } + + asm volatile( + "headB:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headB" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); +} + +void send_data_8mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask) +{ + volatile size_t i = sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + // Timing is more relaxed; unrolling the inner loop for each bit is + // not necessary. Still using the peculiar RJMPs as 2X NOPs, not out + // of need but just to trim the code size down a little. + // This 400-KHz-datastream-on-8-MHz-CPU code is not quite identical + // to the 800-on-16 code later -- the hi/lo timing between WS2811 and + // WS2812 is not simply a 2:1 scale! + + // 20 inst. clocks per bit: HHHHxxxxxxLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,4,10) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) + "dec %[bit]" "\n\t" // 1 bit-- (T = 8) + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 10) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) + "nop" "\n\t" // 1 nop (T = 13) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [hi] "r" (hi), + [lo] "r" (lo), + [ptr] "e" (ptr)); +} + +#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU + +#ifdef PORTD // PORTD isn't present on ATtiny85, etc. +void send_data_12mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask) +{ + volatile size_t i = sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + // In the 12 MHz case, an optimized 800 KHz datastream (no dead time + // between bytes) requires a PORT-specific loop similar to the 8 MHz + // code (but a little more relaxed in this case). + + // 15 instruction clocks per bit: HHHHxxxxxxLLLLL + // OUT instructions: ^ ^ ^ (T=0,4,10) + + volatile uint8_t next; + + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; + next = lo; + if (b & 0x80) next = hi; + + // Don't "optimize" the OUT calls into the bitTime subroutine; + // we're exploiting the RCALL and RET as 3- and 4-cycle NOPs! + asm volatile( + "headD:" "\n\t" // (T = 0) + "out %[port], %[hi]" "\n\t" // (T = 1) + "rcall bitTimeD" "\n\t" // Bit 7 (T = 15) + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 6 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 5 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 4 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 3 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 2 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 1 + // Bit 0: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1) + "rjmp .+0" "\n\t" // 2 nop nop (T = 3) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5) + "out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9) + "nop" "\n\t" // 1 (T = 10) + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 13) + "brne headD" "\n\t" // 2 if(i != 0) -> (next byte) + "rjmp doneD" "\n\t" + "bitTimeD:" "\n\t" // nop nop nop (T = 4) + "out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5) + "mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7) + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9) + "nop" "\n\t" // 1 (T = 10) + "out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11) + "ret" "\n\t" // 4 nop nop nop nop (T = 15) + "doneD:" "\n" + : [byte] "+r" (b), + [next] "+r" (next), + [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTD)), + [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); +} +#endif + +void send_data_12mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask) +{ + volatile uint16_t i = (uint16_t)sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + volatile uint8_t next; + + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; + next = lo; + if (b & 0x80) + { + next = hi; + } + + // Same as above, just set for PORTB & stripped of comments + asm volatile( + "headB:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headB" "\n\t" + "rjmp doneB" "\n\t" + "bitTimeB:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneB:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); +} + +void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask) +{ + volatile uint16_t i = (uint16_t)sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + // 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,6,15) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head30:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" "\n\t" // 2 nop nop (T = 6) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8) + "rjmp .+0" "\n\t" // 2 nop nop (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "nop" "\n\t" // 1 nop (T = 15) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17) + "rjmp .+0" "\n\t" // 2 nop nop (T = 19) + "dec %[bit]" "\n\t" // 1 bit-- (T = 20) + "breq nextbyte30" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22) + "rjmp .+0" "\n\t" // 2 nop nop (T = 24) + "rjmp .+0" "\n\t" // 2 nop nop (T = 26) + "rjmp .+0" "\n\t" // 2 nop nop (T = 28) + "rjmp head30" "\n\t" // 2 -> head30 (next bit out) + "nextbyte30:" "\n\t" // (T = 22) + "nop" "\n\t" // 1 nop (T = 23) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 28) + "brne head30" "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [hi] "r" (hi), + [lo] "r" (lo), + [ptr] "e" (ptr)); +} + +#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU + +void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask) +{ + volatile uint16_t i = (uint16_t)sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + // WS2811 and WS2812 have different hi/lo duty cycles; this is + // similar but NOT an exact copy of the prior 400-on-8 code. + + // 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL + // ST instructions: ^ ^ ^ (T=0,5,13) + + volatile uint8_t next; + volatile uint8_t bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "dec %[bit]" "\n\t" // 1 bit-- (T = 5) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8) + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "nop" "\n\t" // 1 nop (T = 13) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) + "nop" "\n\t" // 1 nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 10) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) + "nop" "\n\t" // 1 nop (T = 16) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); +} + +void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask) +{ + volatile size_t i = sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + // The 400 KHz clock on 16 MHz MCU is the most 'relaxed' version. + + // 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,8,20) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head40:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" "\n\t" // 2 nop nop (T = 6) + "rjmp .+0" "\n\t" // 2 nop nop (T = 8) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp .+0" "\n\t" // 2 nop nop (T = 20) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22) + "nop" "\n\t" // 1 nop (T = 23) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24) + "dec %[bit]" "\n\t" // 1 bit-- (T = 25) + "breq nextbyte40" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27) + "nop" "\n\t" // 1 nop (T = 28) + "rjmp .+0" "\n\t" // 2 nop nop (T = 30) + "rjmp .+0" "\n\t" // 2 nop nop (T = 32) + "rjmp .+0" "\n\t" // 2 nop nop (T = 34) + "rjmp .+0" "\n\t" // 2 nop nop (T = 36) + "rjmp .+0" "\n\t" // 2 nop nop (T = 38) + "rjmp head40" "\n\t" // 2 -> head40 (next bit out) + "nextbyte40:" "\n\t" // (T = 27) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30) + "rjmp .+0" "\n\t" // 2 nop nop (T = 32) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34) + "rjmp .+0" "\n\t" // 2 nop nop (T = 36) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38) + "brne head40" "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); +} + +#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU + +void send_data_32mhz(uint8_t* data, + size_t sizeData, + volatile uint8_t* port, + uint8_t pinMask, + const uint8_t cycleTiming) +{ + volatile uint16_t i = (uint16_t)sizeData; // Loop counter + volatile uint8_t* ptr = data; // Pointer to next byte + volatile uint8_t b = *ptr++; // Current byte value + volatile uint8_t hi; // PORT w/output bit set high + volatile uint8_t lo; // PORT w/output bit set low + + volatile uint8_t next; + volatile uint8_t bit; + volatile uint8_t cycle; + volatile uint8_t cycleCount; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + cycleCount = cycleTiming; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi + "mov %[cycle], %[cycleCount]" "\n\t" // 0-1 cycle = shortCycle + "cycleLoop1:" "\n\t" + "dec %[cycle]" "\n\t" // 1 cycle-- + "brne cycleLoop1" "\n\t" // 2 if(cycle != 0) -> (cycleLoop1) + "st %a[port], %[next]" "\n\t" // 2 PORT = next + "mov %[cycle], %[cycleCount]" "\n\t" // 0-1 cycle = shortCycle + "cycleLoop2:" "\n\t" + "dec %[cycle]" "\n\t" // 1 cycle-- + "brne cycleLoop2" "\n\t" // 2 if(cycle != 0) -> (cycleLoop2) + "rjmp .+0" "\n\t" // 2 nop nop (timing tuning) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo + "mov %[next] , %[lo]" "\n\t" // 1 next = lo + "mov %[cycle], %[cycleCount]" "\n\t" // 0-1 cycle = shortCycle + "cycleLoop3:" "\n\t" + "dec %[cycle]" "\n\t" // 1 cycle-- + "brne cycleLoop3" "\n\t" // 2 if(cycle != 0) -> (cycleLoop3) + "dec %[bit]" "\n\t" // 1 bit-- + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 15) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 15) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 16) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 18) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + // outputs + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [cycle] "+r" (cycle), + [count] "+w" (i) + // inputs + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo), + [cycleCount] "r" (cycleCount)); +} + +#else +#error "CPU SPEED NOT SUPPORTED" +#endif + +#endif diff --git a/lib/NeoPixelBus/src/internal/methods/P9813GenericMethod.h b/lib/NeoPixelBus/src/internal/methods/P9813GenericMethod.h new file mode 100644 index 00000000..bd9369db --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/P9813GenericMethod.h @@ -0,0 +1,145 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for P9813s using general Pins (APA102). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + + +template class P9813MethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + P9813MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _sizeEndFrame((pixelCount + 15) / 16), // 16 = div 2 (bit for every two pixels) div 8 (bits to bytes) + _wire(pinClock, pinData) + { + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + P9813MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + P9813MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize) + { + } +#endif + + ~P9813MethodBase() + { + free(_data); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _wire.begin(sck, miso, mosi, ss); + } +#endif + + void Initialize() + { + _wire.begin(); + } + + void Update(bool) + { + const uint8_t startFrame[4] = { 0x00 }; + const uint8_t endFrame[4] = { 0x00 }; + + _wire.beginTransaction(); + + // start frame + _wire.transmitBytes(startFrame, sizeof(startFrame)); + + // data + _wire.transmitBytes(_data, _sizeData); + + // end frame + _wire.transmitBytes(endFrame, sizeof(endFrame)); + + _wire.endTransaction(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + const size_t _sizeEndFrame; + + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values +}; + +typedef P9813MethodBase P9813Method; + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) +#include "TwoWireSpiImple.h" +typedef P9813MethodBase> P9813Spi20MhzMethod; +typedef P9813MethodBase> P9813Spi10MhzMethod; +typedef P9813MethodBase> P9813Spi5MhzMethod; +typedef P9813MethodBase> P9813Spi2MhzMethod; +typedef P9813MethodBase> P9813Spi1MhzMethod; +typedef P9813MethodBase> P9813Spi500KhzMethod; + +typedef P9813MethodBase> P9813SpiHzMethod; + +typedef P9813Spi10MhzMethod P9813SpiMethod; +#endif + + + diff --git a/lib/NeoPixelBus/src/internal/methods/Sm16716GenericMethod.h b/lib/NeoPixelBus/src/internal/methods/Sm16716GenericMethod.h new file mode 100644 index 00000000..882b9a74 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Sm16716GenericMethod.h @@ -0,0 +1,138 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for SM16716 using general Pins + +Written by Michael C. Miller. +Contributed by Ivo H (ivoh95) + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + + +template class Sm16716MethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + Sm16716MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount* elementSize + settingsSize), + _sizeFrame(6), // 48 bits + _wire(pinClock, pinData) + { + _data = static_cast(malloc(_sizeData)); + memset(_data, 0, _sizeData); + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + Sm16716MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Sm16716MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize) + { + } +#endif + + ~Sm16716MethodBase() + { + free(_data); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + +#if defined(ARDUINO_ARCH_ESP32) + // can't support hardware SPI due to weird extra bits + //void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + //{ + // _wire.begin(sck, miso, mosi, ss); + //} +#endif + + void Initialize() + { + _wire.begin(); + } + + void Update(bool) + { + _wire.beginTransaction(); + + // start frame + for (size_t frameBytes = 0; frameBytes < _sizeFrame; frameBytes++) + { + _wire.transmitByte(0x00); + } + _wire.transmitBit(LOW); + _wire.transmitBit(LOW); // two extra 0s to make the 50 0 header + _wire.transmitBit(HIGH); // one to start the led frame + + for (size_t pixel = 0; pixel < (_sizeData / 3); pixel++) + { + _wire.transmitByte(_data[pixel]); + _wire.transmitByte(_data[pixel + 1]); + _wire.transmitByte(_data[pixel + 2]); + _wire.transmitBit(HIGH); //show the color and start the next frame + } + + _wire.endTransaction(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + const size_t _sizeFrame; + + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values +}; + +// can ONLY support our bitbang for wire due to requirement for custom transmitBit method +// to handle not byte oriented data stream +// +typedef Sm16716MethodBase Sm16716Method; diff --git a/lib/NeoPixelBus/src/internal/methods/Tlc5947GenericMethod.h b/lib/NeoPixelBus/src/internal/methods/Tlc5947GenericMethod.h new file mode 100644 index 00000000..001b3881 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Tlc5947GenericMethod.h @@ -0,0 +1,220 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Tlc5947 24 channel PWM controller using general Pins. + +Written by Michael C. Miller. +Written by Dennis Kasprzyk. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + +#define TLC5947_MODULE_PWM_CHANNEL_COUNT 24 + +class Tlc5947Converter8Bit +{ +public: + static const size_t sizeChannel = 1; + static void ConvertFrame(uint8_t* sendBufferPtr, uint8_t* channelPtr) + { + // Write 2 channels into 3 bytes scaling 8-bit to 12-bit per channel + for (int indexChannel = 0; indexChannel < TLC5947_MODULE_PWM_CHANNEL_COUNT; indexChannel += 2) + { + uint8_t ch1 = *channelPtr--; + uint8_t ch2 = *channelPtr--; + + *sendBufferPtr++ = ch1; + *sendBufferPtr++ = (ch1 & 0xf0) | (ch2 >> 4); + *sendBufferPtr++ = ((ch2 << 4) & 0xf0) | (ch2 >> 4); + } + } +}; + +class Tlc5947Converter16Bit +{ +public: + static const size_t sizeChannel = 2; + static void ConvertFrame(uint8_t* sendBufferPtr, uint8_t* sourceBufferPtr) + { + uint16_t* channelPtr = (uint16_t*)sourceBufferPtr; + + // Write 2 channels into 3 bytes using upper 12-bit of each channel + for (int indexChannel = 0; indexChannel < TLC5947_MODULE_PWM_CHANNEL_COUNT; indexChannel += 2) + { + uint8_t ch1 = *channelPtr--; + uint8_t ch2 = *channelPtr--; + + *sendBufferPtr++ = ch1 >> 8; + *sendBufferPtr++ = (ch1 & 0xf0) | (ch2 >> 12); + *sendBufferPtr++ = ch2 >> 4; + } + } +}; + + +template class Tlc5947MethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + // 24 channel * 12 bit + static const size_t sizeSendBuffer = 36; + + Tlc5947MethodBase(uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _countModule((pixelCount * elementSize + TLC5947_MODULE_PWM_CHANNEL_COUNT - 1) / TLC5947_MODULE_PWM_CHANNEL_COUNT), + _sizeData(_countModule * TLC5947_MODULE_PWM_CHANNEL_COUNT + settingsSize), + _wire(pinClock, pinData), + _pinLatch(pinLatch), + _pinOutputEnable(pinOutputEnable) + { + _data = static_cast(malloc(_sizeData)); + pinMode(pinLatch, OUTPUT); + pinMode(pinOutputEnable, OUTPUT); + digitalWrite(pinOutputEnable, HIGH); + } + + Tlc5947MethodBase(uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Tlc5947MethodBase(pinClock, pinData, pinLatch, -1, pixelCount, elementSize, settingsSize) + { + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + Tlc5947MethodBase(uint8_t pinLatch, uint8_t pinOutputEnable, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Tlc5947MethodBase(SCK, MOSI, pinLatch, pinOutputEnable, pixelCount, elementSize, settingsSize) + { + } + + Tlc5947MethodBase(uint8_t pinLatch, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Tlc5947MethodBase(SCK, MOSI, pinLatch, -1, pixelCount, elementSize, settingsSize) + { + } +#endif + + ~Tlc5947MethodBase() + { + free(_data); + pinMode(_pinLatch, INPUT); + pinMode(_pinOutputEnable, INPUT); + } + + bool IsReadyToUpdate() const + { + return true; // dot stars don't have a required delay + } + +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _wire.begin(sck, miso, mosi, ss); + } +#endif + + void Initialize() + { + _wire.begin(); + memset(_data, 0, _sizeData); + } + + void Update(bool) + { + + digitalWrite(_pinOutputEnable, HIGH); + + digitalWrite(_pinLatch, LOW); + _wire.beginTransaction(); + + // We need to write the channels in reverse order. Get a Pointer to the last channel. + uint8_t* lastChannelPtr = _data + ((_countModule * TLC5947_MODULE_PWM_CHANNEL_COUNT - 1) * T_BITCONVERT::sizeChannel); + for (uint16_t countSend = 0; countSend < _countModule; countSend++) + { + // We pass a pointer to the last channel and ConvertFrame reads the channels backwards + T_BITCONVERT::ConvertFrame(_sendBuffer, lastChannelPtr); + _wire.transmitBytes(_sendBuffer, sizeSendBuffer); + lastChannelPtr -= TLC5947_MODULE_PWM_CHANNEL_COUNT * T_BITCONVERT::sizeChannel; + } + + _wire.endTransaction(); + digitalWrite(_pinLatch, HIGH); + digitalWrite(_pinLatch, LOW); + digitalWrite(_pinOutputEnable, LOW); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const uint16_t _countModule; // Number of tlc5947 modules + const size_t _sizeData; // Size of '_data' buffer below + + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values + uint8_t _sendBuffer[sizeSendBuffer]; // Holds channel values for one module + uint8_t _pinLatch; + uint8_t _pinOutputEnable; +}; + +typedef Tlc5947MethodBase Tlc5947Method; +typedef Tlc5947MethodBase Tlc5947Method16Bit; + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) +#include "TwoWireSpiImple.h" + +// for standalone +typedef Tlc5947MethodBase> Tlc5947Spi30MhzMethod; +typedef Tlc5947MethodBase> Tlc5947Spi30MhzMethod16Bit; + +// for cascaded devices +typedef Tlc5947MethodBase> Tlc5947Spi15MhzMethod; +typedef Tlc5947MethodBase> Tlc5947Spi15MhzMethod16Bit; + +typedef Tlc5947MethodBase> Tlc5947SpiMethod; +typedef Tlc5947MethodBase> Tlc5947SpiMethod16Bit; + + +#endif + + + diff --git a/lib/NeoPixelBus/src/internal/methods/TwoWireBitBangImple.h b/lib/NeoPixelBus/src/internal/methods/TwoWireBitBangImple.h new file mode 100644 index 00000000..1c8627a4 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/TwoWireBitBangImple.h @@ -0,0 +1,110 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using general Pins (APA102/LPD8806). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + + +class TwoWireBitBangImple +{ +public: + typedef NeoNoSettings SettingsObject; + + TwoWireBitBangImple(uint8_t pinClock, uint8_t pinData) : + _pinClock(pinClock), + _pinData(pinData) + { + pinMode(pinClock, OUTPUT); + pinMode(pinData, OUTPUT); + } + + ~TwoWireBitBangImple() + { + pinMode(_pinClock, INPUT); + pinMode(_pinData, INPUT); + } + + void begin() + { + digitalWrite(_pinClock, LOW); + digitalWrite(_pinData, LOW); + } + + void beginTransaction() + { + + } + + void endTransaction() + { + digitalWrite(_pinData, LOW); + } + + void transmitBit(uint8_t bit) + { + // set data bit on pin + digitalWrite(_pinData, bit); + + // set clock high as data is ready + digitalWrite(_pinClock, HIGH); + + // set clock low as data pin is changed + digitalWrite(_pinClock, LOW); + } + + void transmitByte(uint8_t data) + { + for (int bit = 7; bit >= 0; bit--) + { + // set data bit on pin + digitalWrite(_pinData, (data & 0x80) == 0x80 ? HIGH : LOW); + + // set clock high as data is ready + digitalWrite(_pinClock, HIGH); + + data <<= 1; + + // set clock low as data pin is changed + digitalWrite(_pinClock, LOW); + } + } + + void transmitBytes(const uint8_t* data, size_t dataSize) + { + const uint8_t* endData = data + dataSize; + while (data < endData) + { + transmitByte(*data++); + } + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const uint8_t _pinClock; // output pin number for clock line + const uint8_t _pinData; // output pin number for data line +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/TwoWireBitBangImpleAvr.h b/lib/NeoPixelBus/src/internal/methods/TwoWireBitBangImpleAvr.h new file mode 100644 index 00000000..e3b9e320 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/TwoWireBitBangImpleAvr.h @@ -0,0 +1,140 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using general Pins (APA102/LPD8806). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + + +class TwoWireBitBangImple +{ +public: + typedef NeoNoSettings SettingsObject; + + TwoWireBitBangImple(uint8_t pinClock, uint8_t pinData) : + _pinClock(pinClock), + _pinData(pinData) + { + pinMode(pinClock, OUTPUT); + pinMode(pinData, OUTPUT); + + _portClock = portOutputRegister(digitalPinToPort(_pinClock)); + _pinMaskClock = digitalPinToBitMask(_pinClock); + _portData = portOutputRegister(digitalPinToPort(_pinData)); + _pinMaskData = digitalPinToBitMask(_pinData); + } + + ~TwoWireBitBangImple() + { + pinMode(_pinClock, INPUT); + pinMode(_pinData, INPUT); + } + + void begin() + { + digitalWrite(_pinClock, LOW); + digitalWrite(_pinData, LOW); + } + + void beginTransaction() + { + + } + + void endTransaction() + { + digitalWrite(_pinData, LOW); + } + + void transmitBit(uint8_t bit) + { + // set data bit on pin + // digitalWrite(_pinData, bit); // HIGH : LOW + if (bit) + { + *_portData |= _pinMaskData; + } + else + { + *_portData &= ~_pinMaskData; + } + + // set clock high as data is ready + // digitalWrite(_pinClock, HIGH); + *_portClock |= _pinMaskClock; + + // set clock low as data pin is changed + // digitalWrite(_pinClock, LOW); + *_portClock &= ~_pinMaskClock; + } + + void transmitByte(uint8_t data) + { + for (int bit = 7; bit >= 0; bit--) + { + // set data bit on pin + // digitalWrite(_pinData, (data & 0x80) == 0x80 ? HIGH : LOW); + if (data & 0x80) + { + *_portData |= _pinMaskData; + } + else + { + *_portData &= ~_pinMaskData; + } + + // set clock high as data is ready + // digitalWrite(_pinClock, HIGH); + *_portClock |= _pinMaskClock; + + data <<= 1; + + // set clock low as data pin is changed + // digitalWrite(_pinClock, LOW); + *_portClock &= ~_pinMaskClock; + } + } + + void transmitBytes(const uint8_t* data, size_t dataSize) + { + const uint8_t* endData = data + dataSize; + while (data < endData) + { + transmitByte(*data++); + } + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + } + +private: + const uint8_t _pinClock; // output pin number for clock line + const uint8_t _pinData; // output pin number for data line + + volatile uint8_t* _portData; // Output PORT register + uint8_t _pinMaskData; // Output PORT bitmask + volatile uint8_t* _portClock; // Output PORT register + uint8_t _pinMaskClock; // Output PORT bitmask +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/TwoWireHspiImple.h b/lib/NeoPixelBus/src/internal/methods/TwoWireHspiImple.h new file mode 100644 index 00000000..1c774be3 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/TwoWireHspiImple.h @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using ESP32's alternate SPI (HSPI) (APA102/LPD8806). + +Written by Michael C. Miller. +Minor changes adapting TwoWireSpiImple to support HSPI by Louis Beaudoin (Pixelvation) + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include + +template class TwoWireHspiImple +{ +public: + typedef typename T_SPISPEED::SettingsObject SettingsObject; + + TwoWireHspiImple(uint8_t, uint8_t) // clock and data pins ignored for hardware SPI + { + _hspi = new SPIClass(HSPI); + } + + ~TwoWireHspiImple() + { + _hspi->end(); + delete _hspi; + } + +#if defined(ARDUINO_ARCH_ESP32) + // for cases where hardware SPI can have pins changed + void begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _hspi->begin(sck, miso, mosi, ss); + } +#endif + + void begin() + { + _hspi->begin(); + } + + void beginTransaction() + { + _hspi->beginTransaction(SPISettings(_speed.Clock, MSBFIRST, SPI_MODE0)); + } + + void endTransaction() + { + _hspi->endTransaction(); + } + + void transmitByte(uint8_t data) + { + _hspi->transfer(data); + } + + void transmitBytes(const uint8_t* data, size_t dataSize) + { + // ESPs have a method to write without inplace overwriting the send buffer + // since we don't care what gets received, use it for performance + // FIX: but for what ever reason on Esp32, its not const + _hspi->writeBytes(const_cast(data), dataSize); + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _speed.applySettings(settings); + } + +private: + SPIClass * _hspi = NULL; + T_SPISPEED _speed; +}; diff --git a/lib/NeoPixelBus/src/internal/methods/TwoWireSpiImple.h b/lib/NeoPixelBus/src/internal/methods/TwoWireSpiImple.h new file mode 100644 index 00000000..546b6c84 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/TwoWireSpiImple.h @@ -0,0 +1,225 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for DotStars using SPI (APA102/LPD8806). + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#include + +class SpiSpeed40Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed40Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 40000000L; +}; + +class SpiSpeed30Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed30Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 30000000L; +}; + +class SpiSpeed20Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed20Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 20000000L; +}; + +class SpiSpeed15Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed15Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 15000000L; +}; + +class SpiSpeed10Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed10Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 10000000L; +}; + +class SpiSpeed5Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed5Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 5000000L; +}; + +class SpiSpeed2Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed2Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 2000000L; +}; + +class SpiSpeed1Mhz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed1Mhz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 1000000L; +}; + +class SpiSpeed500Khz +{ +public: + typedef NeoNoSettings SettingsObject; + SpiSpeed500Khz() {}; + + static void applySettings([[maybe_unused]] const SettingsObject& settings) {} + + static const uint32_t Clock = 500000L; +}; + +class NeoSpiSettings +{ +public: + NeoSpiSettings(uint32_t clock) : + Clock(clock) + { + } + uint32_t Clock; +}; + +class SpiSpeedHz +{ +public: + typedef NeoSpiSettings SettingsObject; + + SpiSpeedHz() : + Clock(10000000) + {}; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + Clock = settings.Clock; + } + + uint32_t Clock; +}; + +template class TwoWireSpiImple +{ +public: + typedef typename T_SPISPEED::SettingsObject SettingsObject; + + TwoWireSpiImple(uint8_t, uint8_t) // clock and data pins ignored for hardware SPI + { + } + + ~TwoWireSpiImple() + { + SPI.end(); + } + +#if defined(ARDUINO_ARCH_ESP32) + // for cases where hardware SPI can have pins changed + void begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + SPI.begin(sck, miso, mosi, ss); + } +#endif + + void begin() + { + SPI.begin(); + } + + void beginTransaction() + { + SPI.beginTransaction(SPISettings(_speed.Clock, MSBFIRST, SPI_MODE0)); + } + + void endTransaction() + { + SPI.endTransaction(); + } + + void transmitByte(uint8_t data) + { + SPI.transfer(data); + } + + void transmitBytes(const uint8_t* data, size_t dataSize) + { +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESPs have a method to write without inplace overwriting the send buffer + // since we don't care what gets received, use it for performance + // FIX: but for what ever reason on Esp32, its not const + SPI.writeBytes(const_cast(data), dataSize); + +#else + // default ARDUINO transfer inplace overwrites the send buffer + // which is bad in this case, so we have to send one byte at a time + const uint8_t* endData = data + dataSize; + while (data < endData) + { + SPI.transfer(*data++); + } +#endif + } + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _speed.applySettings(settings); + } + +private: + T_SPISPEED _speed; +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/methods/Ws2801GenericMethod.h b/lib/NeoPixelBus/src/internal/methods/Ws2801GenericMethod.h new file mode 100644 index 00000000..4eb89578 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/methods/Ws2801GenericMethod.h @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for WS2801 + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set +#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__) +#include "TwoWireBitBangImpleAvr.h" +#else +#include "TwoWireBitBangImple.h" +#endif + + +template class Ws2801MethodBase +{ +public: + typedef typename T_TWOWIRE::SettingsObject SettingsObject; + + Ws2801MethodBase(uint8_t pinClock, uint8_t pinData, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + _sizeData(pixelCount * elementSize + settingsSize), + _wire(pinClock, pinData) + { + _data = static_cast(malloc(_sizeData)); + // data cleared later in Begin() + } + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) + Ws2801MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) : + Ws2801MethodBase(SCK, MOSI, pixelCount, elementSize, settingsSize) + { + } +#endif + + ~Ws2801MethodBase() + { + free(_data); + } + + bool IsReadyToUpdate() const + { + uint32_t delta = micros() - _endTime; + + return (delta >= 500); + } + +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _wire.begin(sck, miso, mosi, ss); + + _endTime = micros(); + } +#endif + + void Initialize() + { + _wire.begin(); + + _endTime = micros(); + } + + void Update(bool) + { + while (!IsReadyToUpdate()) + { +#if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA) + yield(); // allows for system yield if needed +#endif + } + + _wire.beginTransaction(); + + // data + _wire.transmitBytes(_data, _sizeData); + + _wire.endTransaction(); + + // save EOD time for latch on next call + _endTime = micros(); + } + + bool AlwaysUpdate() + { + // this method requires update to be called only if changes to buffer + return false; + } + + uint8_t* getData() const + { + return _data; + }; + + size_t getDataSize() const + { + return _sizeData; + }; + + void applySettings([[maybe_unused]] const SettingsObject& settings) + { + _wire.applySettings(settings); + } + +private: + const size_t _sizeData; // Size of '_data' buffer below + + uint32_t _endTime; // Latch timing reference + T_TWOWIRE _wire; + uint8_t* _data; // Holds LED color values +}; + +typedef Ws2801MethodBase Ws2801Method; + +#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny) +#include "TwoWireSpiImple.h" +typedef Ws2801MethodBase> Ws2801Spi20MhzMethod; +typedef Ws2801MethodBase> Ws2801Spi10MhzMethod; +typedef Ws2801MethodBase> Ws2801Spi5MhzMethod; +typedef Ws2801MethodBase> Ws2801Spi2MhzMethod; +typedef Ws2801MethodBase> Ws2801Spi1MhzMethod; +typedef Ws2801MethodBase> Ws2801Spi500KhzMethod; + +typedef Ws2801MethodBase> Ws2801SpiHzMethod; + +typedef Ws2801Spi10MhzMethod Ws2801SpiMethod; +#endif + + + diff --git a/lib/NeoPixelBus/src/internal/topologies/ColumnMajorAlternatingLayout.h b/lib/NeoPixelBus/src/internal/topologies/ColumnMajorAlternatingLayout.h new file mode 100644 index 00000000..ef2da56b --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/ColumnMajorAlternatingLayout.h @@ -0,0 +1,144 @@ +/*------------------------------------------------------------------------- +ColumnMajorAlternatingLayout provides a collection of class objects that are used with NeoTopology +object. +They define the specific layout of pixels and do the math to change the 2d +cordinate space to 1d cordinate space + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class ColumnMajorAlternatingLayout; +class ColumnMajorAlternating180Layout; + +class ColumnMajorAlternatingTilePreference +{ +public: + typedef ColumnMajorAlternatingLayout EvenRowEvenColumnLayout; + typedef ColumnMajorAlternatingLayout EvenRowOddColumnLayout; + typedef ColumnMajorAlternating180Layout OddRowEvenColumnLayout; + typedef ColumnMajorAlternating180Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 07 08 15 +// 01 06 09 14 +// 02 05 10 13 +// 03 04 11 12 +// +class ColumnMajorAlternatingLayout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t index = x * height; + + if (x & 0x0001) + { + index += ((height - 1) - y); + } + else + { + index += y; + } + return index; + } +}; + +// layout example of 4x4 +// 03 02 01 00 +// 04 05 06 07 +// 11 10 09 08 +// 12 13 14 15 +// +class ColumnMajorAlternating90Layout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) + { + uint16_t index = y * width; + + if (y & 0x0001) + { + index += x; + } + else + { + index += ((width - 1) - x); + } + return index; + } +}; + +// layout example of 4x4 +// 12 11 04 03 +// 13 10 05 02 +// 14 09 06 01 +// 15 08 07 00 +// +class ColumnMajorAlternating180Layout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t mx = ((width - 1) - x); + uint16_t index = mx * height; + + if (mx & 0x0001) + { + index += y; + } + else + { + index += ((height - 1) - y); + } + return index; + } +}; + +// layout example of 4x4 +// 15 14 13 12 +// 08 09 10 11 +// 07 06 05 04 +// 00 01 02 03 +// +class ColumnMajorAlternating270Layout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t my = ((height - 1) - y); + uint16_t index = my * width; + + if (my & 0x0001) + { + index += ((width - 1) - x); + } + else + { + index += x; + } + return index; + } +}; diff --git a/lib/NeoPixelBus/src/internal/topologies/ColumnMajorLayout.h b/lib/NeoPixelBus/src/internal/topologies/ColumnMajorLayout.h new file mode 100644 index 00000000..242fd00c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/ColumnMajorLayout.h @@ -0,0 +1,104 @@ +/*------------------------------------------------------------------------- +ColumnMajorLayout provides a collection of class objects that are used with NeoTopology +object. +They define the specific layout of pixels and do the math to change the 2d +cordinate space to 1d cordinate space + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class ColumnMajorLayout; +class ColumnMajor90Layout; +class ColumnMajor180Layout; +class ColumnMajor270Layout; + +class ColumnMajorTilePreference +{ +public: + typedef ColumnMajorLayout EvenRowEvenColumnLayout; + typedef ColumnMajor270Layout EvenRowOddColumnLayout; + typedef ColumnMajor90Layout OddRowEvenColumnLayout; + typedef ColumnMajor180Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 04 08 12 +// 01 05 09 13 +// 02 06 10 14 +// 03 07 11 15 +// +class ColumnMajorLayout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) + { + return x * height + y; + } +}; + +// layout example of 4x4 +// 03 02 01 00 +// 07 06 05 04 +// 11 10 09 08 +// 15 14 13 12 +// +class ColumnMajor90Layout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) + { + return (width - 1 - x) + y * width; + } +}; + +// layout example of 4x4 +// 15 11 07 03 +// 14 10 06 02 +// 13 09 05 01 +// 12 08 04 00 +// +class ColumnMajor180Layout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) * height + (height - 1 - y); + } +}; + +// layout example of 4x4 +// 12 13 14 15 +// 08 09 10 11 +// 04 05 06 07 +// 00 01 02 03 +// +class ColumnMajor270Layout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return x + (height - 1 - y) * width; + } +}; diff --git a/lib/NeoPixelBus/src/internal/topologies/NeoMosaic.h b/lib/NeoPixelBus/src/internal/topologies/NeoMosaic.h new file mode 100644 index 00000000..c42ba0d4 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/NeoMosaic.h @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------- +NeoMosaic provides a mapping feature of a 2d cordinate to linear 1d cordinate +It is used to map tiles of matricies of NeoPixels to a index on the NeoPixelBus +where the the matricies use a set of prefered topology and the tiles of +those matricies use the RowMajorAlternating layout + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +//----------------------------------------------------------------------------- +// class NeoMosaic +// Complex Tile layout class that reduces distance of the interconnects between +// the tiles by using different rotations of the layout at specific locations +// +// T_LAYOUT = the layout used for matrix panel (rotation is ignored) +// One of the following classes and their rotated variants +// RowMajorLayout +// ColumnMajorLayout +// RowMajorAlternatingLayout +// ColumnMajorAlternatingLayout +// +// +// NOTE: The tiles in the mosaic are always laid out using RowMajorAlternating +// +//----------------------------------------------------------------------------- + +template class NeoMosaic +{ +public: + NeoMosaic(uint16_t topoWidth, uint16_t topoHeight, + uint16_t mosaicWidth, uint16_t mosaicHeight) : + _topoWidth(topoWidth), + _topoHeight(topoHeight), + _mosaicWidth(mosaicWidth), + _mosaicHeight(mosaicHeight) + { + } + + uint16_t Map(int16_t x, int16_t y) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x >= static_cast(totalWidth)) + { + x = totalWidth - 1; + } + else if (x < 0) + { + x = 0; + } + + if (y >= static_cast(totalHeight)) + { + y = totalHeight - 1; + } + else if (y < 0) + { + y = 0; + } + + uint16_t localIndex; + uint16_t tileOffset; + + calculate(x, y, &localIndex, &tileOffset); + + return localIndex + tileOffset; + } + + uint16_t MapProbe(int16_t x, int16_t y) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x < 0 || x >= totalWidth || y < 0 || y >= totalHeight) + { + return totalWidth * totalHeight; // count, out of bounds + } + + uint16_t localIndex; + uint16_t tileOffset; + + calculate(x, y, &localIndex, &tileOffset); + + return localIndex + tileOffset; + } + + NeoTopologyHint TopologyHint(int16_t x, int16_t y) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x < 0 || x >= static_cast(totalWidth) || + y < 0 || y >= static_cast(totalHeight)) + { + return NeoTopologyHint_OutOfBounds; + } + + uint16_t localIndex; + uint16_t tileOffset; + NeoTopologyHint result; + + calculate(x, y, &localIndex, &tileOffset); + + if (localIndex == 0) + { + result = NeoTopologyHint_FirstOnPanel; + } + else if (localIndex == (_topoWidth * _topoHeight - 1)) + { + result = NeoTopologyHint_LastOnPanel; + } + else + { + result = NeoTopologyHint_InPanel; + } + + return result; + } + + uint16_t getWidth() const + { + return _topoWidth * _mosaicWidth; + } + + uint16_t getHeight() const + { + return _topoHeight * _mosaicHeight; + } + +private: + const uint16_t _topoWidth; + const uint16_t _topoHeight; + const uint16_t _mosaicWidth; + const uint16_t _mosaicHeight; + + void calculate(uint16_t x, uint16_t y, uint16_t* pLocalIndex, uint16_t* pTileOffset) const + { + uint16_t tileX = x / _topoWidth; + uint16_t topoX = x % _topoWidth; + + uint16_t tileY = y / _topoHeight; + uint16_t topoY = y % _topoHeight; + + *pTileOffset = RowMajorAlternatingLayout::Map(_mosaicWidth, + _mosaicHeight, + tileX, + tileY) * _topoWidth * _topoHeight; + + if (tileX & 0x0001) + { + // odd columns + if (tileY & 0x0001) + { + *pLocalIndex = T_LAYOUT::OddRowOddColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + else + { + *pLocalIndex = T_LAYOUT::EvenRowOddColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + } + else + { + // even columns + if (tileY & 0x0001) + { + *pLocalIndex = T_LAYOUT::OddRowEvenColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + else + { + *pLocalIndex = T_LAYOUT::EvenRowEvenColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + } + } +}; \ No newline at end of file diff --git a/lib/NeoPixelBus/src/internal/topologies/NeoRingTopology.h b/lib/NeoPixelBus/src/internal/topologies/NeoRingTopology.h new file mode 100644 index 00000000..970de02c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/NeoRingTopology.h @@ -0,0 +1,130 @@ +/*------------------------------------------------------------------------- +NeoRingTopology provides a mapping feature of a 2d polar cordinate to a +linear 1d cordinate. +It is used to map a series of concentric rings of NeoPixels to a index on +the NeoPixelBus. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// NeoRingTopology - +// +// T_LAYOUT - a user provided class that contains the following members with +// the Rings[] initialized with the starting index of each ring and +// one extra entry for the total count (example below). +// Other methods and members can exist to intialize a dynamic layout as needed. +// +// class RingsLayout +// { +// protected: +// const uint16_t Rings[] = { 0, 1, 7, 19, 35, 59, PixelCount }; +// +// uint8_t _ringCount() const +// { +// return sizeof(Rings) / sizeof(Rings[0]); +// } +// }; +// + +template class NeoRingTopology : public T_LAYOUT +{ +public: + NeoRingTopology() + { + } + + uint16_t Map(uint8_t ring, uint16_t pixel) const + { + if (pixel >= getPixelCountAtRing(ring)) + { + return 0; // invalid ring and/or pixel argument, always return a valid value, the first one + } + + return _map(ring, pixel); + } + + uint16_t MapProbe(uint8_t ring, uint16_t pixel) const + { + if (pixel >= getPixelCountAtRing(ring)) + { + return getPixelCount(); // total count, out of bounds + } + + return _map(ring, pixel); + } + + uint16_t RingPixelShift(uint8_t ring, uint16_t pixel, int16_t shift) + { + int32_t ringPixel = pixel; + ringPixel += shift; + + if (ringPixel < 0) + { + ringPixel = 0; + } + else + { + uint16_t count = getPixelCountAtRing(ring); + if (ringPixel >= count) + { + ringPixel = count - 1; + } + } + return ringPixel; + } + + uint16_t RingPixelRotate(uint8_t ring, uint16_t pixel, int16_t rotate) + { + int32_t ringPixel = pixel; + ringPixel += rotate; + return ringPixel % getPixelCountAtRing(ring); + } + + uint8_t getCountOfRings() const + { + return T_LAYOUT::_ringCount() - 1; // minus one as the Rings includes the extra value + } + + uint16_t getPixelCountAtRing(uint8_t ring) const + { + if (ring >= getCountOfRings()) + { + return 0; // invalid, no pixels + } + + return T_LAYOUT::Rings[ring + 1] - T_LAYOUT::Rings[ring]; // using the extra value for count calc + } + + uint16_t getPixelCount() const + { + return T_LAYOUT::Rings[T_LAYOUT::_ringCount() - 1]; // the last entry is the total count + } + +private: + uint16_t _map(uint8_t ring, uint16_t pixel) const + { + return T_LAYOUT::Rings[ring] + pixel; + } + +}; diff --git a/lib/NeoPixelBus/src/internal/topologies/NeoTiles.h b/lib/NeoPixelBus/src/internal/topologies/NeoTiles.h new file mode 100644 index 00000000..d614e4a6 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/NeoTiles.h @@ -0,0 +1,162 @@ +/*------------------------------------------------------------------------- +NeoTiles provides a mapping feature of a 2d cordinate to linear 1d cordinate +It is used to map tiles of matricies of NeoPixels to a index on the NeoPixelBus +where the the matricies use one topology and the tiles of those matricies can +use another + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +//----------------------------------------------------------------------------- +// class NeoTiles +// Simple template Tile layout class +// T_MATRIX_LAYOUT = the layout used on the pixel matrix panel (a tile) +// T_TILE_LAYOUT = the layout used for the tiles. +// one of the following classes and their rotated variants +// RowMajorLayout +// ColumnMajorLayout +// RowMajorAlternatingLayout +// ColumnMajorAlternatingLayout +//----------------------------------------------------------------------------- +template class NeoTiles +{ +public: + NeoTiles(uint16_t topoWidth, uint16_t topoHeight, + uint16_t tilesWidth, uint16_t tilesHeight) : + _topo(topoWidth, topoHeight), + _width(tilesWidth), + _height(tilesHeight) + { + } + + uint16_t Map(int16_t x, int16_t y) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x >= static_cast(totalWidth)) + { + x = totalWidth - 1; + } + else if (x < 0) + { + x = 0; + } + + if (y >= static_cast(totalHeight)) + { + y = totalHeight - 1; + } + else if (y < 0) + { + y = 0; + } + + uint16_t localIndex; + uint16_t tileOffset; + + calculate(x, y, &localIndex, &tileOffset); + + return localIndex + tileOffset; + } + + uint16_t MapProbe(int16_t x, int16_t y) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x < 0 || x >= totalWidth || y < 0 || y >= totalHeight) + { + return totalWidth * totalHeight; // count, out of bounds + } + + uint16_t localIndex; + uint16_t tileOffset; + + calculate(x, y, &localIndex, &tileOffset); + + return localIndex + tileOffset; + } + + NeoTopologyHint TopologyHint(int16_t x, int16_t y) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x < 0 || x >= static_cast(totalWidth) || + y < 0 || y >= static_cast(totalHeight)) + { + return NeoTopologyHint_OutOfBounds; + } + + uint16_t localIndex; + uint16_t tileOffset; + NeoTopologyHint result; + + calculate(x, y, &localIndex, &tileOffset); + + if (localIndex == 0) + { + result = NeoTopologyHint_FirstOnPanel; + } + else if (localIndex == (_topo.getWidth() * _topo.getHeight() - 1)) + { + result = NeoTopologyHint_LastOnPanel; + } + else + { + result = NeoTopologyHint_InPanel; + } + + return result; + } + + uint16_t getWidth() const + { + return _width * _topo.getWidth(); + } + + uint16_t getHeight() const + { + return _height * _topo.getHeight(); + } + +private: + const NeoTopology _topo; + const uint16_t _width; + const uint16_t _height; + + void calculate(uint16_t x, uint16_t y, uint16_t* pLocalIndex, uint16_t* pTileOffset) const + { + uint16_t tileX = x / _topo.getWidth(); + uint16_t topoX = x % _topo.getWidth(); + + uint16_t tileY = y / _topo.getHeight(); + uint16_t topoY = y % _topo.getHeight(); + + *pTileOffset = T_TILE_LAYOUT::Map(_width, _height, tileX, tileY) * _topo.getWidth() * _topo.getHeight(); + *pLocalIndex = _topo.Map(topoX, topoY); + } +}; + diff --git a/lib/NeoPixelBus/src/internal/topologies/NeoTopology.h b/lib/NeoPixelBus/src/internal/topologies/NeoTopology.h new file mode 100644 index 00000000..0909764c --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/NeoTopology.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------- +NeoTopology provides a mapping feature of a 2d cordinate to linear 1d cordinate +It is used to map a matrix of NeoPixels to a index on the NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +enum NeoTopologyHint +{ + NeoTopologyHint_FirstOnPanel, + NeoTopologyHint_InPanel, + NeoTopologyHint_LastOnPanel, + NeoTopologyHint_OutOfBounds +}; + +// NeoTopology - +// +// T_LAYOUT - the following classes and their rotated variants +// RowMajorLayout +// ColumnMajorLayout +// RowMajorAlternatingLayout +// ColumnMajorAlternatingLayout +// +template class NeoTopology +{ +public: + NeoTopology(uint16_t width, uint16_t height) : + _width(width), + _height(height) + { + + } + + uint16_t Map(int16_t x, int16_t y) const + { + if (x >= static_cast(_width)) + { + x = _width - 1; + } + else if (x < 0) + { + x = 0; + } + if (y >= static_cast(_height)) + { + y = _height - 1; + } + else if (y < 0) + { + y = 0; + } + return T_LAYOUT::Map(_width, _height, x, y); + } + + uint16_t MapProbe(int16_t x, int16_t y) const + { + if (x < 0 || x >= _width || y < 0 || y >= _height) + { + return _width * _height; // count, out of bounds + } + return T_LAYOUT::Map(_width, _height, x, y); + } + + uint16_t getWidth() const + { + return _width; + } + + uint16_t getHeight() const + { + return _height; + } + +private: + const uint16_t _width; + const uint16_t _height; +}; diff --git a/lib/NeoPixelBus/src/internal/topologies/RowMajorAlternatingLayout.h b/lib/NeoPixelBus/src/internal/topologies/RowMajorAlternatingLayout.h new file mode 100644 index 00000000..09a23c71 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/RowMajorAlternatingLayout.h @@ -0,0 +1,144 @@ +/*------------------------------------------------------------------------- +RowMajorAlternatingLayout provides a collection of class objects that are used with NeoTopology +object. +They define the specific layout of pixels and do the math to change the 2d +cordinate space to 1d cordinate space + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + + +class RowMajorAlternating270Layout; +class RowMajorAlternating90Layout; + +class RowMajorAlternatingTilePreference +{ +public: + typedef RowMajorAlternating270Layout EvenRowEvenColumnLayout; + typedef RowMajorAlternating270Layout EvenRowOddColumnLayout; + typedef RowMajorAlternating90Layout OddRowEvenColumnLayout; + typedef RowMajorAlternating90Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 01 02 03 +// 07 06 05 04 +// 08 09 10 11 +// 15 14 13 12 +// +class RowMajorAlternatingLayout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) + { + uint16_t index = y * width; + + if (y & 0x0001) + { + index += ((width - 1) - x); + } + else + { + index += x; + } + return index; + } +}; + +// layout example of 4x4 +// 15 08 07 00 +// 14 09 06 01 +// 13 10 05 02 +// 12 11 04 03 +// +class RowMajorAlternating90Layout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t mx = ((width - 1) - x); + uint16_t index = mx * height; + + if (mx & 0x0001) + { + index += ((height - 1) - y); + } + else + { + index += y; + } + return index; + } +}; + +// layout example of 4x4 +// 12 13 14 15 +// 11 10 09 08 +// 04 05 06 07 +// 03 02 01 00 +// +class RowMajorAlternating180Layout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t my = ((height - 1) - y); + uint16_t index = my * width; + + if (my & 0x0001) + { + index += x; + } + else + { + index += ((width - 1) - x); + } + return index; + } +}; + +// layout example of 4x4 +// 03 04 11 12 +// 02 05 10 13 +// 01 06 09 14 +// 00 07 08 15 +// +class RowMajorAlternating270Layout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t index = x * height; + + if (x & 0x0001) + { + index += y; + } + else + { + index += ((height - 1) - y); + } + return index; + } +}; diff --git a/lib/NeoPixelBus/src/internal/topologies/RowMajorLayout.h b/lib/NeoPixelBus/src/internal/topologies/RowMajorLayout.h new file mode 100644 index 00000000..90a6f653 --- /dev/null +++ b/lib/NeoPixelBus/src/internal/topologies/RowMajorLayout.h @@ -0,0 +1,103 @@ +/*------------------------------------------------------------------------- +RowMajorLayout provides a collection of class objects that are used with NeoTopology +object. +They define the specific layout of pixels and do the math to change the 2d +cordinate space to 1d cordinate space + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +class RowMajorLayout; +class RowMajor90Layout; +class RowMajor180Layout; +class RowMajor270Layout; + +class RowMajorTilePreference +{ +public: + typedef RowMajorLayout EvenRowEvenColumnLayout; + typedef RowMajor270Layout EvenRowOddColumnLayout; + typedef RowMajor90Layout OddRowEvenColumnLayout; + typedef RowMajor180Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 01 02 03 +// 04 05 06 07 +// 08 09 10 11 +// 12 13 14 15 +// +class RowMajorLayout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) + { + return x + y * width; + } +}; + +// layout example of 4x4 +// 12 08 04 00 +// 13 09 05 01 +// 14 10 06 02 +// 15 11 07 03 +// +class RowMajor90Layout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) * height + y; + } +}; + +// layout example of 4x4 +// 15 14 13 12 +// 11 10 09 08 +// 07 06 05 04 +// 03 02 01 00 +// +class RowMajor180Layout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) + (height - 1 - y) * width; + } +}; + +// layout example of 4x4 +// 03 07 11 15 +// 02 06 10 14 +// 01 05 09 13 +// 00 04 08 12 +// +class RowMajor270Layout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) + { + return x * height + (height - 1 - y); + } +}; diff --git a/platformio.ini b/platformio.ini index cbe13c0d..e457b4fa 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,14 @@ ; PlatformIO Project Configuration File ; Please visit documentation: https://docs.platformio.org/page/projectconf.html + +# If Changing Web UI +# In order to conserve space, the Web UI interface is represented as a series of wled00/html_*.h files which contain C/C++ strings with specific parts of the Web UI. +# These files are automatically created from source files available in wled00/data folder. To generate files, install Node.js 20 or higher globally. After that, recreate html_*.h files by running in the repo directory: +# > npm install +# > npm run build + + [platformio] # ------------------------------------------------------------------------------ # ENVIRONMENTS @@ -11,7 +19,7 @@ # CI binaries ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment -default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi +; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi # Release binaries ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB @@ -43,6 +51,8 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_1 ; default_envs = esp32c3dev ; default_envs = lolin_s2_mini ; default_envs = esp32s3dev_16MB_PSRAM_opi +default_envs = ClemS-ESP32-to-5-LEDs + src_dir = ./wled00 data_dir = ./wled00/data @@ -122,7 +132,7 @@ build_flags = -DMQTT_MAX_PACKET_SIZE=1024 -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC - -D CORE_DEBUG_LEVEL=0 + -D CORE_DEBUG_LEVEL=2 -D NDEBUG -Wno-attributes ;; silence warnings about unknown attribute 'maybe_unused' in NeoPixelBus #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) @@ -133,7 +143,7 @@ build_flags = -D DECODE_SAMSUNG=true -D DECODE_LG=true ;-Dregister= # remove warnings in C++17 due to use of deprecated register keyword by the FastLED library ;; warning: this breaks framework code on ESP32-C3 and ESP32-S2 - -DWLED_USE_MY_CONFIG + ; -DWLED_USE_MY_CONFIG ; -D USERMOD_SENSORSTOMQTT #For ADS1115 sensor uncomment following ; -D USERMOD_ADS1115 @@ -164,9 +174,9 @@ framework = arduino board_build.flash_mode = dout monitor_speed = 115200 # slow upload speed (comment this out with a ';' when building for development use) -upload_speed = 115200 +upload_speed = 230400 # fast upload speed (remove ';' when building for development use) -; upload_speed = 921600 +;upload_speed = 921600 # ------------------------------------------------------------------------------ # LIBRARIES: required dependencies @@ -177,18 +187,19 @@ upload_speed = 115200 # ------------------------------------------------------------------------------ lib_compat_mode = strict lib_deps = - fastled/FastLED @ 3.6.0 +# library are modified +; fastled/FastLED @ 3.6.0 +; makuna/NeoPixelBus @ 2.7.5 IRremoteESP8266 @ 2.8.2 - makuna/NeoPixelBus @ 2.7.5 https://github.com/Aircoookie/ESPAsyncWebServer.git @ 2.2.1 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line - #TFT_eSPI + ; TFT_eSPI #For compatible OLED display uncomment following - #U8g2 #@ ~2.33.15 + ; U8g2 #@ ~2.33.15 #For Dallas sensor uncomment following - #OneWire @ ~2.3.7 + OneWire @ ~2.3.7 #For BME280 sensor uncomment following - #BME280 @ ~3.0.0 + ; BME280 @ ~3.0.0 ; adafruit/Adafruit BMP280 Library @ 2.1.0 ; adafruit/Adafruit CCS811 Library @ 1.0.4 ; adafruit/Adafruit Si7021 Library @ 1.4.0 @@ -828,3 +839,28 @@ lib_deps = ${esp32.lib_deps} TFT_eSPI @ ^2.3.70 board_build.partitions = ${esp32.default_partitions} + + +# ------------------------------------------------------------------------------ +# ClemS-ESP32-to-5-LEDs +# ------------------------------------------------------------------------------ +[env:ClemS-ESP32-to-5-LEDs] +board = esp32dev +platform = ${esp32.platform} +platform_packages = ${esp32.platform_packages} +upload_speed = 460800 ; 230400 +build_flags = ${common.build_flags_esp32} + -D WLED_RELEASE_NAME=ClemS_ESP32_LEDs +; -D WLED_DISABLE_BROWNOUT_DET + -D WLED_DISABLE_INFRARED + -D USERMOD_RTC +; -D SPI_FREQUENCY=40000000 + -D USER_SETUP_LOADED + -D LEDPIN=25 + -D DEFAULT_LED_COUNT=60 + +monitor_filters = esp32_exception_decoder +lib_deps = + ${esp32.lib_deps} + +board_build.partitions = ${esp32.default_partitions} \ No newline at end of file diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index bff98629..5d9198bb 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -143,7 +143,7 @@ class Bus { virtual bool hasRGB(void) { return Bus::hasRGB(_type); } static bool hasRGB(uint8_t type) { - if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF) return false; + if ((type >= TYPE_TM1934_WWA && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF) return false; return true; } virtual bool hasWhite(void) { return Bus::hasWhite(_type); } @@ -156,7 +156,9 @@ class Bus { virtual bool hasCCT(void) { return Bus::hasCCT(_type); } static bool hasCCT(uint8_t type) { if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA || - type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH) return true; + type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH + || type == TYPE_TM1934_WWA + ) return true; return false; } static void setCCT(uint16_t cct) { diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index 45ace99d..e7851564 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -101,6 +101,13 @@ #define I_32_I1_UCS_4 62 //Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) +// ! add new bus types here ! +//TM1934 (Warm White, Middle White, Cold White) +#define I_32_RN_TM3_4 63 +#define I_32_U1_TM3_4 64 +#define I_32_DM_TM3_4 65 +#define I_32_BB_TM3_4 66 + //APA102 #define I_HS_DOT_3 39 //hardware SPI #define I_SS_DOT_3 40 //soft SPI @@ -166,6 +173,15 @@ /*** ESP32 Neopixel methods ***/ #ifdef ARDUINO_ARCH_ESP32 + +//TM1934 (Warm White, Middle White, Cold White) +#define B_32_RN_NEO_3 NeoPixelBusLg +#ifndef WLED_NO_I2S0_PIXELBUS +#define B_32_I0_NEO_3 NeoPixelBusLg +#endif +#ifndef WLED_NO_I2S1_PIXELBUS +#define B_32_I1_NEO_3 NeoPixelBusLg +#endif //RGB #define B_32_RN_NEO_3 NeoPixelBusLg #ifndef WLED_NO_I2S0_PIXELBUS @@ -707,6 +723,8 @@ class PolyBus { case 3: col.G = r; col.R = b; col.B = g; break; //3 = RBG case 4: col.G = b; col.R = g; col.B = r; break; //4 = BGR case 5: col.G = g; col.R = b; col.B = r; break; //5 = GBR + // TODO add for tri-white LEDs + // case 6: col.CW = r; col.MW = g; col.WW = b; break; //6 = Cold white, Middle white, Warm white } // upper nibble contains W swap information switch (co >> 4) { @@ -1154,7 +1172,9 @@ class PolyBus { #ifdef ESP8266 uint8_t offset = pins[0] -1; //for driver: 0 = uart0, 1 = uart1, 2 = dma, 3 = bitbang if (offset > 3) offset = 3; + // ! add Led type here ! switch (busType) { + case TYPE_TM1934: case TYPE_WS2812_1CH_X3: case TYPE_WS2812_2CH_X3: case TYPE_WS2812_RGB: @@ -1192,7 +1212,11 @@ class PolyBus { if (num > 9) return I_NONE; if (num > 7) offset = num -7; #endif + // ! add Led type here ! switch (busType) { + case TYPE_TM1934: + case TYPE_TM1934_WWA: + return I_32_RN_NEO_3 + offset; case TYPE_WS2812_1CH_X3: case TYPE_WS2812_2CH_X3: case TYPE_WS2812_RGB: diff --git a/wled00/const.h b/wled00/const.h index 388b64c8..0a47fe6e 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -227,6 +227,8 @@ #define TYPE_NONE 0 //light is not configured #define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light //Digital types (data pin only) (16-31) +#define TYPE_TM1934_WWA 16 //amber + warm + cold white +#define TYPE_TM1934 17 //white-only chips (3 channels per IC) #define TYPE_WS2812_1CH 18 //white-only chips (1 channel per IC) (unused) #define TYPE_WS2812_1CH_X3 19 //white-only chips (3 channels per IC) #define TYPE_WS2812_2CH_X3 20 //CCT chips (1st IC controls WW + CW of 1st zone and CW of 2nd zone, 2nd IC controls WW of 2nd zone and WW + CW of 3rd zone) diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 5bddafe5..245ee5cd 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -341,6 +341,8 @@ ${i+1}: