initial commit

This commit is contained in:
2026-02-10 22:32:16 +01:00
commit b908c942d9
215 changed files with 41366 additions and 0 deletions

5
Code/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
Code/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

14
Code/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "PlatformIO",
"task": "Build",
"problemMatcher": [
"$platformio"
],
"group": "build",
"label": "PlatformIO: Build"
}
]
}

View File

@@ -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)

20
Code/lib/FastLED/LICENSE Normal file
View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,99 @@
FastLED
===========
[![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)](https://github.com/FastLED/FastLED/actions/workflows/build.yml)
[![Documentation](https://img.shields.io/badge/Docs-Doxygen-blue.svg)](http://fastled.io/docs)
[![Reddit](https://img.shields.io/badge/reddit-/r/FastLED-orange.svg?logo=reddit)](https://www.reddit.com/r/FastLED/)
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.
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 Started
Install the library using either [the .zip file from the latest release](https://github.com/FastLED/FastLED/releases/latest/) or by searching for "FastLED" in the libraries manager of the Arduino IDE. [See the Arduino documentation on how to install libraries for more information.](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries)
How quickly can you get up and running with the library? Here's a simple blink program:
```cpp
#include <FastLED.h>
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
void setup() { FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS); }
void loop() {
leds[0] = CRGB::White; FastLED.show(); delay(30);
leds[0] = CRGB::Black; FastLED.show(); delay(30);
}
```
## Help and Support
If you need help with using the library, please consider visiting the Reddit community at https://reddit.com/r/FastLED. There are thousands of knowledgeable FastLED users in that group and a plethora of solutions in the post history.
If you are looking for documentation on how something in the library works, please see the Doxygen documentation online at http://fastled.io/docs.
If you run into bugs with the library, or if you'd like to request support for a particular platform or LED chipset, please submit an issue at http://fastled.io/issues.
## 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
* APA102HD - Same as APA102 but with a high definition gamma correction function applied at the driver level.
* 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
### Porting FastLED to a new platform
Information on porting FastLED can be found in the file
[PORTING.md](./PORTING.md).
## 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

View File

@@ -0,0 +1,6 @@
#!/bin/bash
# cd to the directory of the script
cd "$(dirname "$0")"
python ci-compile.py

View File

@@ -0,0 +1,148 @@
"""
Runs the compilation process for all examples on all boards in parallel.
Build artifacts are recycled within a board group so that subsequent ino
files are built faster.
"""
import os
import sys
import subprocess
import concurrent.futures
import time
from pathlib import Path
from threading import Lock
ERROR_HAPPENED = False
FIRST_JOB_LOCK = Lock()
IS_GITHUB = "GITHUB_WORKFLOW" in os.environ
EXAMPLES = [
"Apa102HD",
"Blink",
"ColorPalette",
"ColorTemperature",
"Cylon",
"DemoReel100",
"Fire2012",
"FirstLight",
"Multiple/MultipleStripsInOneArray",
"Multiple/ArrayOfLedArrays",
"Noise",
"NoisePlayground",
"NoisePlusPalette",
"Pacifica",
"Pride2015",
"RGBCalibrate",
"RGBSetDemo",
"TwinkleFox",
"XYMatrix",
]
BOARDS = ["uno", "esp32dev", "esp01", "yun", "digix", "teensy30"]
PRINT_LOCK = Lock()
def locked_print(*args, **kwargs):
"""Print with a lock to prevent garbled output for multiple threads."""
with PRINT_LOCK:
print(*args, **kwargs)
def compile_for_board_and_example(board: str, example: str):
"""Compile the given example for the given board."""
builddir = Path(".build") / board
builddir = builddir.absolute()
builddir.mkdir(parents=True, exist_ok=True)
srcdir = builddir / "src"
# Remove the previous *.ino file if it exists, everything else is recycled
# to speed up the next build.
if srcdir.exists():
subprocess.run(["rm", "-rf", str(srcdir)], check=True)
locked_print(f"*** Building example {example} for board {board} ***")
result = subprocess.run(
[
"pio",
"ci",
"--board",
board,
"--lib=ci",
"--lib=src",
"--keep-build-dir",
f"--build-dir={builddir}",
f"examples/{example}/*ino",
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
)
locked_print(result.stdout)
if result.returncode != 0:
locked_print(f"*** Error compiling example {example} for board {board} ***")
return False
locked_print(f"*** Finished building example {example} for board {board} ***")
return True
# Function to process task queues for each board
def process_queue(board: str, examples: list[str]):
"""Process the task queue for the given board."""
global ERROR_HAPPENED # pylint: disable=global-statement
is_first = True
for example in examples:
if ERROR_HAPPENED:
return True
locked_print(f"\n*** Building examples for board {board} ***")
if is_first and IS_GITHUB:
with FIRST_JOB_LOCK:
# Github runners are memory limited and the first job is the most
# memory intensive since all the artifacts are being generated in parallel.
success = compile_for_board_and_example(board, example)
else:
success = compile_for_board_and_example(board, example)
is_first = False
if not success:
ERROR_HAPPENED = True
return False
return True
def main() -> int:
"""Main function."""
start_time = time.time()
# Set the working directory to the script's parent directory.
script_dir = Path(__file__).parent.resolve()
os.chdir(script_dir.parent)
os.environ["PLATFORMIO_EXTRA_SCRIPTS"] = "pre:lib/ci/ci-flags.py"
task_queues = {board: EXAMPLES.copy() for board in BOARDS}
num_cpus = min(os.cpu_count(), len(BOARDS))
# Run the compilation process
with concurrent.futures.ThreadPoolExecutor(max_workers=num_cpus) as executor:
future_to_board = {
executor.submit(process_queue, board, task_queues[board]): board
for board in BOARDS
}
for future in concurrent.futures.as_completed(future_to_board):
board = future_to_board[future]
if not future.result():
locked_print(f"Compilation failed for board {board}. Stopping.")
break
total_time = (time.time() - start_time) / 60
if ERROR_HAPPENED:
locked_print("\nDone. Errors happened during compilation.")
return 1
locked_print(
f"\nDone. Built all projects for all boards in {total_time:.2f} minutes."
)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,3 @@
Import("env")
env.Append(CXXFLAGS=["-Wno-register"])

View File

@@ -0,0 +1 @@
platformio==6.1.11

View File

@@ -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

View File

@@ -0,0 +1,2 @@
COMPONENT_ADD_INCLUDEDIRS := ./src src/platforms/esp/32
COMPONENT_SRCDIRS := ./src src/platforms/esp/32

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,235 @@
<doxygenlayout version="1.0">
<!-- Generated by doxygen 1.9.3 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro=""/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="concepts" visible="yes" title="">
</tab>
<tab type="interfaces" visible="yes" title="">
<tab type="interfacelist" visible="yes" title="" intro=""/>
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="structs" visible="yes" title="">
<tab type="structlist" visible="yes" title="" intro=""/>
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
</tab>
<tab type="exceptions" visible="yes" title="">
<tab type="exceptionlist" visible="yes" title="" intro=""/>
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
<!-- Layout definition for a class page -->
<class>
<detaileddescription title=""/>
<includes visible="$SHOW_HEADERFILE"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<memberdecl>
<nestedclasses visible="yes" title=""/>
<publictypes title=""/>
<services title=""/>
<interfaces title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
<publicstaticmethods title=""/>
<publicattributes title=""/>
<publicstaticattributes title=""/>
<protectedtypes title=""/>
<protectedslots title=""/>
<protectedmethods title=""/>
<protectedstaticmethods title=""/>
<protectedattributes title=""/>
<protectedstaticattributes title=""/>
<packagetypes title=""/>
<packagemethods title=""/>
<packagestaticmethods title=""/>
<packageattributes title=""/>
<packagestaticattributes title=""/>
<properties title=""/>
<events title=""/>
<privatetypes title=""/>
<privateslots title=""/>
<privatemethods title=""/>
<privatestaticmethods title=""/>
<privateattributes title=""/>
<privatestaticattributes title=""/>
<friends title=""/>
<related title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<services title=""/>
<interfaces title=""/>
<constructors title=""/>
<functions title=""/>
<related title=""/>
<variables title=""/>
<properties title=""/>
<events title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<detaileddescription title=""/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<concepts visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a concept page -->
<concept>
<detaileddescription title=""/>
<includes visible="$SHOW_HEADERFILE"/>
<definition visible="yes" title=""/>
<authorsection visible="yes"/>
</concept>
<!-- Layout definition for a file page -->
<file>
<detaileddescription title=""/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
<sourcelink visible="yes"/>
<memberdecl>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<concepts visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<detaileddescription title=""/>
<groupgraph visible="$GROUP_GRAPHS"/>
<memberdecl>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<concepts visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
<membergroups visible="yes"/>
</memberdecl>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
</directory>
</doxygenlayout>

View File

@@ -0,0 +1,11 @@
/**
FastLED Doxygen Stylesheet
https://github.com/FastLED/FastLED/
*/
/** Hide "Detailed Description" sub-header when the
* detailed description is at the top of a page.
*/
.contents a#details + h2.groupheader {
display:none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -0,0 +1,18 @@
<!-- HTML footer for doxygen 1.9.3-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion </li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
$generatedby&#160;<a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen"/></a> $doxygenversion
</small></address>
<!--END !GENERATE_TREEVIEW-->
<!-- ... -->
</body>
</html>

View File

@@ -0,0 +1,76 @@
<!-- HTML header for doxygen 1.9.3-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<script type="text/javascript">var page_layout=1;</script>
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
<link href="$relpath^fastled_logo.png" type="image/svg+xml" rel="shortcut icon" />
$extrastylesheet
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript">DoxygenAwesomeDarkModeToggle.init()</script>
</head>
<body>
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN FULL_SIDEBAR-->
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
<!--END FULL_SIDEBAR-->
<!--END DISABLE_INDEX-->
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign">
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber">&#160;$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td>
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<!--BEGIN !FULL_SIDEBAR-->
<td>$searchbox</td>
<!--END !FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
<!--BEGIN SEARCHENGINE-->
<!--BEGIN FULL_SIDEBAR-->
<tr><td colspan="2">$searchbox</td></tr>
<!--END FULL_SIDEBAR-->
<!--END SEARCHENGINE-->
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@@ -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 <FastLED.h>
// 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();
}

View File

@@ -0,0 +1,87 @@
/// @file Apa102HD.ino
/// @brief Example showing how to use the APA102HD gamma correction.
///
/// In this example we compare two strips of LEDs.
/// One strip is in HD mode, the other is in software gamma mode.
///
/// Each strip is a linear ramp of brightnesses, from 0 to 255.
/// Showcasing all the different brightnesses.
///
/// Why do we love gamma correction? Gamma correction more closely
/// matches how humans see light. Led values are measured in fractions
/// of max power output (1/255, 2/255, etc.), while humans see light
/// in a logarithmic way. Gamma correction converts to this eye friendly
/// curve. Gamma correction wants a LED with a high bit depth. The APA102
/// gives us the standard 3 components (red, green, blue) with 8 bits each, it
/// *also* has a 5 bit brightness component. This gives us a total of 13 bits,
/// which allows us to achieve a higher dynamic range. This means deeper fades.
///
/// Example:
/// CRGB leds[NUM_LEDS] = {0};
/// void setup() {
/// FastLED.addLeds<
/// APA102HD, // <--- This selects HD mode.
/// STRIP_0_DATA_PIN,
/// STRIP_0_CLOCK_PIN,
/// RGB
/// >(leds, NUM_LEDS);
/// }
#include <Arduino.h>
#include <FastLED.h>
#include <lib8tion.h>
#define NUM_LEDS 20
// uint8_t DATA_PIN, uint8_t CLOCK_PIN,
#define STRIP_0_DATA_PIN 1
#define STRIP_0_CLOCK_PIN 2
#define STRIP_1_DATA_PIN 3
#define STRIP_1_CLOCK_PIN 4
CRGB leds_hd[NUM_LEDS] = {0}; // HD mode implies gamma.
CRGB leds[NUM_LEDS] = {0}; // Software gamma mode.
// This is the regular gamma correction function that we used to have
// to do. It's used here to showcase the difference between APA102HD
// mode which does the gamma correction for you.
CRGB software_gamma(const CRGB& in) {
CRGB out;
// dim8_raw are the old gamma correction functions.
out.r = dim8_raw(in.r);
out.g = dim8_raw(in.g);
out.b = dim8_raw(in.b);
return out;
}
void setup() {
delay(500); // power-up safety delay
// Two strips of LEDs, one in HD mode, one in software gamma mode.
FastLED.addLeds<APA102HD, STRIP_0_DATA_PIN, STRIP_0_CLOCK_PIN, RGB>(leds_hd, NUM_LEDS);
FastLED.addLeds<APA102, STRIP_1_DATA_PIN, STRIP_1_CLOCK_PIN, RGB>(leds, NUM_LEDS);
}
uint8_t wrap_8bit(int i) {
// Module % operator here wraps a large "i" so that it is
// always in [0, 255] range when returned. For example, if
// "i" is 256, then this will return 0. If "i" is 257
// then this will return 1. No matter how big the "i" is, the
// output range will always be [0, 255]
return i % 256;
}
void loop() {
// Draw a a linear ramp of brightnesses to showcase the difference between
// the HD and non-HD mode.
for (int i = 0; i < NUM_LEDS; i++) {
uint8_t brightness = map(i, 0, NUM_LEDS - 1, 0, 255);
CRGB c(brightness, brightness, brightness); // Just make a shade of white.
leds_hd[i] = c; // The APA102HD leds do their own gamma correction.
CRGB c_gamma_corrected = software_gamma(c);
leds[i] = c_gamma_corrected; // Set the software gamma corrected
// values to the other strip.
}
FastLED.show(); // All leds are now written out.
delay(8); // Wait 8 milliseconds until the next frame.
}

View File

@@ -0,0 +1,73 @@
/// @file Blink.ino
/// @brief Blink the first LED of an LED strip
/// @example Blink.ino
#include <FastLED.h>
// 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<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
// FastLED.addLeds<SM16703, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1829, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1812, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1904, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS2903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2852, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<GS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SK6812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<APA106, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<PL9823, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2813, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<APA104, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GE8822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD1886, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD1886_8BIT, DATA_PIN, RGB>(leds, NUM_LEDS);
// ## Clocked (SPI) types ##
// FastLED.addLeds<LPD6803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<SK9822, DATA_PIN, CLOCK_PIN, RGB>(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);
}

View File

@@ -0,0 +1,192 @@
/// @file ColorPalette.ino
/// @brief Demonstrates how to use @ref ColorPalettes
/// @example ColorPalette.ino
#include <FastLED.h>
#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<LED_TYPE, LED_PIN, COLOR_ORDER>(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.

View File

@@ -0,0 +1,89 @@
/// @file ColorTemperature.ino
/// @brief Demonstrates how to use @ref ColorTemperature based color correction
/// @example ColorTemperature.ino
#include <FastLED.h>
#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<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalSMD5050 );
FastLED.setBrightness( BRIGHTNESS );
}

View File

@@ -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 <FastLED.h>
// 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<WS2812,DATA_PIN,RGB>(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);
}
}

View File

@@ -0,0 +1,127 @@
/// @file DemoReel100.ino
/// @brief FastLED "100 lines of code" demo reel, showing off some effects
/// @example DemoReel100.ino
#include <FastLED.h>
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<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
//FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(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;
}
}

View File

@@ -0,0 +1,109 @@
/// @file Fire2012.ino
/// @brief Simple one-dimensional fire animation
/// @example Fire2012.ino
#include <FastLED.h>
#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<CHIPSET, LED_PIN, COLOR_ORDER>(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;
}
}

View File

@@ -0,0 +1,168 @@
/// @file Fire2012WithPalette.ino
/// @brief Simple one-dimensional fire animation with a programmable color palette
/// @example Fire2012WithPalette.ino
#include <FastLED.h>
#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<CHIPSET, LED_PIN, COLOR_ORDER>(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;
}
}

View File

@@ -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 <FastLED.h>
///////////////////////////////////////////////////////////////////////////////////////////
//
// 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<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
// FastLED.addLeds<SM16703, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1829, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1812, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1904, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS2903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2852, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<GS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SK6812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<APA106, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<PL9823, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2813, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<APA104, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GE8822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD1886, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD1886_8BIT, DATA_PIN, RGB>(leds, NUM_LEDS);
// ## Clocked (SPI) types ##
// FastLED.addLeds<LPD6803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<SK9822, DATA_PIN, CLOCK_PIN, RGB>(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;
}
}

View File

@@ -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 <FastLED.h>
#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<NEOPIXEL, 2>(leds[0], NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 3
FastLED.addLeds<NEOPIXEL, 3>(leds[1], NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 4
FastLED.addLeds<NEOPIXEL, 4>(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);
}
}
}

View File

@@ -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 <FastLED.h>
#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<NEOPIXEL, 4>(leds, NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 5
FastLED.addLeds<NEOPIXEL, 5>(leds, NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 6
FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 7
FastLED.addLeds<NEOPIXEL, 7>(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);
}
}

View File

@@ -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 <FastLED.h>
#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<NEOPIXEL, 10>(redLeds, NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 11
FastLED.addLeds<NEOPIXEL, 11>(greenLeds, NUM_LEDS_PER_STRIP);
// tell FastLED there's 60 NEOPIXEL leds on pin 12
FastLED.addLeds<NEOPIXEL, 12>(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);
}
}

View File

@@ -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 <FastLED.h>
#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<NEOPIXEL, 2>(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<NEOPIXEL, 3>(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<NEOPIXEL, 4>(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);
}
}

View File

@@ -0,0 +1,41 @@
/// @file OctoWS2811Demo.ino
/// @brief Demonstrates how to use OctoWS2811 output
/// @example OctoWS2811Demo.ino
#define USE_OCTOWS2811
#include <OctoWS2811.h>
#include <FastLED.h>
#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<OCTOWS2811>(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);
}

View File

@@ -0,0 +1,60 @@
/// @file ParallelOutputDemo.ino
/// @brief Demonstrates how to write to multiple strips simultaneously
/// @example ParallelOutputDemo.ino
#include <FastLED.h>
#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<WS2811, 1, 16> outputs;
void setup() {
delay(5000);
Serial.begin(57600);
Serial.println("Starting...");
// FastLED.addLeds<WS2811_PORTA,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP);
// FastLED.addLeds<WS2811_PORTB,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP);
// FastLED.addLeds<WS2811_PORTD,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip);
FastLED.addLeds<WS2811_PORTDC,NUM_STRIPS>(leds, NUM_LEDS_PER_STRIP);
// Teensy 4 parallel output example
// FastLED.addLeds<NUM_STRIPS, WS2811, 1>(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);
}

View File

@@ -0,0 +1,118 @@
/// @file Noise.ino
/// @brief Demonstrates how to use noise generation on a 2D LED matrix
/// @example Noise.ino
#include <FastLED.h>
//
// 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<WS2811,2,RGB>(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);
}

View File

@@ -0,0 +1,79 @@
/// @file NoisePlayground.ino
/// @brief Demonstrates how to use noise generation on a 2D LED matrix
/// @example NoisePlayground.ino
#include <FastLED.h>
// 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<WS2811,2,GRB>(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();
}

View File

@@ -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 <FastLED.h>
#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<LED_TYPE,LED_PIN,COLOR_ORDER>(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;
}

View File

@@ -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.h>
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<LED_TYPE,DATA_PIN,COLOR_ORDER>(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);
}
}

View File

@@ -0,0 +1,202 @@
/// @file Pintest.ino
/// @brief Checks available pin outputs (for debugging)
/// @example Pintest.ino
#include <FastLED.h>
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*)&REG_PIO_A_ODSR) { return "REG_PIO_A_ODSR"; }
#endif
#ifdef REG_PIO_B_ODSR
if(portPtr == (void*)&REG_PIO_B_ODSR) { return "REG_PIO_B_ODSR"; }
#endif
#ifdef REG_PIO_C_ODSR
if(portPtr == (void*)&REG_PIO_C_ODSR) { return "REG_PIO_C_ODSR"; }
#endif
#ifdef REG_PIO_D_ODSR
if(portPtr == (void*)&REG_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<uint8_t PIN> void CheckPin()
{
CheckPin<PIN - 1>();
void *systemThinksPortIs = (void*)portOutputRegister(digitalPinToPort(PIN));
RwReg systemThinksMaskIs = digitalPinToBitMask(PIN);
Serial.print("Pin "); Serial.print(PIN); Serial.print(": Port ");
if(systemThinksPortIs == (void*)FastPin<PIN>::port()) {
Serial.print("valid & mask ");
} else {
Serial.print("invalid, is "); Serial.print(getPort((void*)FastPin<PIN>::port())); Serial.print(" should be ");
Serial.print(getPort((void*)systemThinksPortIs));
Serial.print(" & mask ");
}
if(systemThinksMaskIs == FastPin<PIN>::mask()) {
Serial.println("valid.");
} else {
Serial.print("invalid, is "); Serial.print(FastPin<PIN>::mask()); Serial.print(" should be "); Serial.println(systemThinksMaskIs);
}
}
template<> void CheckPin<255> () {}
template<uint8_t _PORT> 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<uint8_t PIN> void PrintPins() {
PrintPins<PIN - 1>();
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<MAX_PIN>();
#endif
Serial.println("-----");
#ifdef NUM_DIGITAL_PINS
PrintPins<NUM_DIGITAL_PINS>();
#endif
Serial.println("------");
delay(100000);
}

View File

@@ -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 <OctoWS2811.h>
#include <FastLED.h>
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>
// 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<OCTOWS2811>(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;
}
}

View File

@@ -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<LED_TYPE,DATA_PIN,COLOR_ORDER>(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);
}
}

View File

@@ -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<SM16703, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1829, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1812, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1809, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1804, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<TM1803, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1903B, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS1904, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<UCS2903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2852, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<GS1903, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SK6812, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<APA106, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<PL9823, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SK6822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2813, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<APA104, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2811_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GE8822, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<GW6205_400, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD1886, DATA_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<LPD1886_8BIT, DATA_PIN, RGB>(leds, NUM_LEDS);
// ## Clocked (SPI) types ##
// FastLED.addLeds<LPD6803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
// FastLED.addLeds<WS2801, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<WS2803, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<SM16716, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
// FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<DOTSTAR, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS); // BGR ordering is typical
// FastLED.addLeds<SK9822, DATA_PIN, CLOCK_PIN, RGB>(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);
}

View File

@@ -0,0 +1,26 @@
/// @file RGBSetDemo.ino
/// @brief Demonstrates how to create an LED group with CRGBArray
/// @example RGBSetDemo.ino
#include <FastLED.h>
#define NUM_LEDS 40
CRGBArray<NUM_LEDS> leds;
void setup() { FastLED.addLeds<NEOPIXEL,2>(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);
}
}

View File

@@ -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 <SmartMatrix.h>
#include <FastLED.h>
#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<SMART_MATRIX>(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);
}

View File

@@ -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<NUM_LEDS> 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<LED_TYPE,DATA_PIN,COLOR_ORDER>(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]);
}

View File

@@ -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 <FastLED.h>
#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<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
FastLED.setBrightness( BRIGHTNESS );
}

View File

@@ -0,0 +1,40 @@
KBD = $C000 ;Read keydown
KBDSTRB = $C010 ;Reset keybd
SPKR = $C030 ;Toggle speaker
; TTL digital output pins on
; 16-pin DIP game connector
SETAN0 = $C058
CLRAN0 = $C059
SETAN1 = $C05A
CLRAN1 = $C05B
SETAN2 = $C05C
CLRAN2 = $C05D
SETAN3 = $C05E
CLRAN3 = $C05F
PIN12ON = CLRAN3
PIN12OFF= SETAN3
PIN13ON = CLRAN2
PIN13OFF= SETAN2
PIN14ON = CLRAN1
PIN14OFF= SETAN1
PIN15ON = CLRAN0
PIN15OFF= SETAN0
;Special for pin 5, except on //gs
C040STROBE = $C040
PIN5STROBE = C040STROBE
SolidApple = $C062 ; read SW1 or SA
OpenApple = $C061 ; read SW0 or OA
VBL = $C019 ; vertical blanking
WAIT = $FCA8 ; wait a little while
CROUT = $FD8E ; print a CR
PRBYTE = $FDDA ; print a hex byte

View File

@@ -0,0 +1,633 @@
/////////////////////////////////
//
// FastLED6502
// by Mark Kriegsman
//
// Device driver and animation
// library for connecting addressable
// LED strips to an Apple II.
// The full "FastLED" library is
// available for Arduino and related
// microcontrollers.
//
/////////////////////////////////
//
// HOST COMPATIBILITY:
// Apples with 16-pin DIP "game ports"
// are fully supported; Apples with only
// 9-pin "game ports" are NOT supported.
//
// Apple ][ fully supported
// Apple ][+ fully supported
// Apple //e fully supported
//
// Apple //c and //c+ NOT supported
// as they lack the required 16-pin
// DIP game port for digital I/O.
//
// Apple //gs:
// motherboard game port IS supported
// back panel connector NOT supported
// See Notes section below.
//
// C64, PET, VIC-20, Atari400/800,
// NES, SYM, KIM-1, and other 6502
// systems are NOT supported at this
// time, but porting should be
// relatively easy for someone familiar
// with each target platform.
//
//
// LED STRIP COMPATIBILITY:
// In general, "four-wire" (clocked)
// LED strips can be supported, but
// "three-wire" (clockless) LED strips
// cannot be supported.
//
// APA102 tested & working
// Adafruit DotStar tested & working
// LPD8806 should work (untested)
// WS2801 should work (untested)
//
// WS2811/WS2812/NeoPixel can NOT work
// due to timing incompatibilities,
// namely that the 1MHz Apple is far
// too slow to drive them correctly.
//
//
// USAGE - HARDWARE:
// Connect an external power source to
// +5 and GND on the LED strip.
// Connect GND on the LED strip to GND
// on the game port connector (pin 8)
// Connect DATA IN on the LED strip to
// pin 12, 13, or 14 on the game port.
// Connect CLOCK IN on the LED strip to
// pin 5 (preferred), 12, 13, or 14 on
// the game port. Note: Apple //gs users
// cannot use pin 5.
//
//
// USAGE - SOFTWARE:
// At build time provide definitions for
// CHIPSET, DATA_PIN, CLOCK_PIN,
// NUM_LEDS, & BRIGHTNESS.
// Inside "Setup":
// Store LED count into
// FastLED_NumPixels,
// Store brightness into
// FastLED_Brightness
// Inside "Loop":
// Update the leds array, found in
// ledsR, ledsG, and ledsB
// Call FastLED_Show
// Jump back to top of "Loop"
//
//
// REFERENCE:
// FastLED_Show:
// display led array on LED strip
// FastLED_FillBlack:
// fill led array to black
// FastLED_FillSolid_RGB_AXY:
// fill led array with RGB color
// FastLED_FillSolid_Hue_X:
// fill led array with solid HSV Hue
// FastLED_Random8:
// return a pseudorandom number in A
// FastLED_FillRainbow_XY:
// fill led array with HSV rainbow
// starting at hue X, incrementing by
// huedelta Y each pixel.
// FastLED_SetHue_XY:
// set pixel Y to HSV hue X
// FastLED_Beat8:
// takes a speed increment in A, and
// returns a triangle wave in A.
//
//
// NOTES:
// For speed and size, there IS some
// self-modifying code in this
// library, so it cannot be burned
// into ROM without modification.
// Brightness control only works on
// APA102 at this point.
// Pin 15 is currently used as a
// 'frame start' signal for protocol
// diagnostics - makes frames more
// visible with an oscilloscope.
// If Pin 5 is specified for the CLOCK
// output pin, FastLED6502 will
// automatically use the high speed
// C040STROBE signal for clock. Note
// that the Apple //gs lacks this
// signal, even on the motherboard
// game port.
// The Apple joystick/mouse port on the
// rear of the //c, //c+, and //gs
// can NOT be used for LED connections
// because it lacks the necessary
// digital output pins.
// This library can drive 100 LED pixels
// at more than 30 frames per second.
//
//
// VERSION HISTORY
// 2015-02-07 - first version, by MEK
// assembled with xa65
// www.floodgap.com/retrotech/xa/
/////////////////////////////////
//
// ENTRY POINT
//
FastLED_Entry
jsr FastLED_FillBlack
jmp Setup
/////////////////////////////////
//
// FASTLED6502 GLOBALS
//
FastLED_NumPixels .byt NUM_LEDS
FastLED_Brightness .byt BRIGHTNESS
FastLED_RandomState .byt 17
FastLED_BeatState .byt 0
/////////////////////////////////
//
// API FUNCTIONS
//
FastLED_FillBlack
lda FastLED_NumPixels
pha
lda #255
sta FastLED_NumPixels
lda #0
tax
tay
jsr FastLED_FillSolid_RGB_AXY
jsr FastLED_Show
pla
sta FastLED_NumPixels
rts
FastLED_FillRainbow_XY
sty rbHueDelta
ldy #0
FR1
lda FastLED_RainbowR,x
sta ledsR,y
lda FastLED_RainbowG,x
sta ledsG,y
lda FastLED_RainbowB,x
sta ledsB,y
txa
clc
adc rbHueDelta
tax
iny
cpy FastLED_NumPixels
bne FR1
rts
rbHueDelta .byt 0
FastLED_SetHue_XY
lda FastLED_RainbowR,x
sta ledsR,y
lda FastLED_RainbowG,x
sta ledsG,y
lda FastLED_RainbowB,x
sta ledsB,y
rts
FastLED_FillSolid_RGB_AXY
sta ledsR
stx ledsG
sty ledsB
ldy #0
FillSolidRGBAXY1
lda ledsR
sta ledsR,y
lda ledsG
sta ledsG,y
lda ledsB
sta ledsB,y
iny
cpy FastLED_NumPixels
bne FillSolidRGBAXY1
rts
FastLED_FillSolid_Hue_X
ldy #0
FillSolidHX1
lda FastLED_RainbowR,x
sta ledsR,y
lda FastLED_RainbowG,x
sta ledsG,y
lda FastLED_RainbowB,x
sta ledsB,y
iny
cpy FastLED_NumPixels
bne FillSolidHX1
rts
; NOTE: USES SELF-MODIFYING CODE
FastLED_Random8
inc Random8GetLo
bne Random8Get
inc Random8GetHi
bne Random8Get
lda #$F8
sta Random8GetHi
lda #03
sta Random8GetLo
Random8Get
Random8GetLo = Random8Get + 1
Random8GetHi = Random8Get + 2
lda $F803
adc FastLED_RandomState
sta FastLED_RandomState
rts
FastLED_Beat8
clc
adc FastLED_BeatState
sta FastLED_BeatState
bit FastLED_BeatState
bmi FastLED_Beat8Neg
asl
rts
FastLED_Beat8Neg
lda #$ff
sec
sbc FastLED_BeatState
sbc FastLED_BeatState
rts
FastLED_Show
jmp CHIPSET
/////////////////////////////////
//
// HARDWARE INTERFACING
//
PINOFF_BASE = PIN15OFF
PINON_BASE = PIN15ON
#define PINON(P) PINON_BASE+((15-P)*2)
#define PINOFF(P) PINOFF_BASE+((15-P)*2)
DATAOFF = PINOFF(DATA_PIN)
DATAON = PINON(DATA_PIN)
CLKOFF = PINOFF(CLOCK_PIN)
CLKON = PINON(CLOCK_PIN)
// Special handling if CLOCK_PIN
// is 5: the C040STROBE line.
#if CLOCK_PIN = 5
#define CLOCK_ON bit PIN5STROBE
#define CLOCK_OFF
#else
#define CLOCK_ON bit CLKON
#define CLOCK_OFF bit CLKOFF
#endif
FRAMEON = PINON(15)
FRAMEOFF = PINOFF(15)
/////////////////////////////////
APA102
bit FRAMEON
jsr FastLED_Send00
jsr FastLED_Send00
jsr FastLED_Send00
jsr FastLED_Send00
lda FastLED_Brightness
lsr
lsr
lsr
ora #$E0
tax
ldy FastLED_NumPixels
APA102PX
txa
jsr FastLED_SendA
lda ledsB,y
jsr FastLED_SendA
lda ledsG,y
jsr FastLED_SendA
lda ledsR,y
jsr FastLED_SendA
dey
bne APA102PX
lda FastLED_NumPixels
lsr
lsr
lsr
lsr
lsr
lsr
tay
iny
APA102CL
jsr FastLED_SendFF
jsr FastLED_Send00
jsr FastLED_Send00
jsr FastLED_Send00
dey
bne APA102CL
bit FRAMEOFF
rts
/////////////////////////////////
LPD8806
bit FRAMEON
ldy FastLED_NumPixels
LPD8806PX
lda ledsG,y
lsr
ora #$80
jsr FastLED_SendA
lda ledsR,y
lsr
ora #$80
jsr FastLED_SendA
lda ledsB,y
lsr
ora #$80
jsr FastLED_SendA
dey
bne LPD8806PX
bit FRAMEOFF
rts
/////////////////////////////////
WS2801
bit FRAMEON
ldy FastLED_NumPixels
WS2801PX
lda ledsG,y
jsr FastLED_SendA
lda ledsR,y
jsr FastLED_SendA
lda ledsB,y
jsr FastLED_SendA
dey
bne WS2801PX
bit FRAMEOFF
rts
/////////////////////////////////
FastLED_SendFF
bit DATAON
jmp FastLED_SendXX
;
FastLED_Send00
bit DATAOFF
;
FastLED_SendXX
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
CLOCK_ON
CLOCK_OFF
rts
FastLED_SendA
cmp #0
beq FastLED_Send00
cmp #$FF
beq FastLED_SendFF
asl
bcc S0x0
S0x1 bit DATAON
bcs S0xK
S0x0 bit DATAOFF
S0xK CLOCK_ON
CLOCK_OFF
asl
bcc S1x0
S1x1 bit DATAON
bcs S1xK
S1x0 bit DATAOFF
S1xK CLOCK_ON
CLOCK_OFF
asl
bcc S2x0
S2x1 bit DATAON
bcs S2xK
S2x0 bit DATAOFF
S2xK CLOCK_ON
CLOCK_OFF
asl
bcc S3x0
S3x1 bit DATAON
bcs S3xK
S3x0 bit DATAOFF
S3xK CLOCK_ON
CLOCK_OFF
asl
bcc S4x0
S4x1 bit DATAON
bcs S4xK
S4x0 bit DATAOFF
S4xK CLOCK_ON
CLOCK_OFF
asl
bcc S5x0
S5x1 bit DATAON
bcs S5xK
S5x0 bit DATAOFF
S5xK CLOCK_ON
CLOCK_OFF
asl
bcc S6x0
S6x1 bit DATAON
bcs S6xK
S6x0 bit DATAOFF
S6xK CLOCK_ON
CLOCK_OFF
asl
bcc S7x0
S7x1 bit DATAON
bcs S7xK
S7x0 bit DATAOFF
S7xK CLOCK_ON
CLOCK_OFF
rts
/////////////////////////////////
//
// Force page allignment for speed
// for leds array and Rainbow table
//
.dsb 256-(* & $FF),0
/////////////////////////////////
//
// LED ARRAY
//
ledsR .dsb 256,0
ledsG .dsb 256,0
ledsB .dsb 256,0
/////////////////////////////////
//
// HSV RAINBOW DEFINITION
//
// Generated directly from FastLED.
//
FastLED_RainbowR
.byt $FF,$FD,$FA,$F8,$F5,$F2,$F0,$ED
.byt $EA,$E8,$E5,$E2,$E0,$DD,$DA,$D8
.byt $D5,$D2,$D0,$CD,$CA,$C8,$C5,$C2
.byt $C0,$BD,$BA,$B8,$B5,$B2,$B0,$AD
.byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
.byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
.byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
.byt $AB,$AB,$AB,$AB,$AB,$AB,$AB,$AB
.byt $AB,$A6,$A1,$9C,$96,$91,$8C,$86
.byt $81,$7C,$76,$71,$6C,$66,$61,$5C
.byt $56,$51,$4C,$47,$41,$3C,$37,$31
.byt $2C,$27,$21,$1C,$17,$11,$0C,$07
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$02,$05,$07,$0A,$0D,$0F,$12
.byt $15,$17,$1A,$1D,$1F,$22,$25,$27
.byt $2A,$2D,$2F,$32,$35,$37,$3A,$3D
.byt $3F,$42,$45,$47,$4A,$4D,$4F,$52
.byt $55,$57,$5A,$5C,$5F,$62,$64,$67
.byt $6A,$6C,$6F,$72,$74,$77,$7A,$7C
.byt $7F,$82,$84,$87,$8A,$8C,$8F,$92
.byt $94,$97,$9A,$9C,$9F,$A2,$A4,$A7
.byt $AB,$AD,$B0,$B2,$B5,$B8,$BA,$BD
.byt $C0,$C2,$C5,$C8,$CA,$CD,$D0,$D2
.byt $D5,$D8,$DA,$DD,$E0,$E2,$E5,$E8
.byt $EA,$ED,$F0,$F2,$F5,$F8,$FA,$FD
FastLED_RainbowG
.byt $00,$02,$05,$07,$0A,$0D,$0F,$12
.byt $15,$17,$1A,$1D,$1F,$22,$25,$27
.byt $2A,$2D,$2F,$32,$35,$37,$3A,$3D
.byt $3F,$42,$45,$47,$4A,$4D,$4F,$52
.byt $55,$57,$5A,$5C,$5F,$62,$64,$67
.byt $6A,$6C,$6F,$72,$74,$77,$7A,$7C
.byt $7F,$82,$84,$87,$8A,$8C,$8F,$92
.byt $94,$97,$9A,$9C,$9F,$A2,$A4,$A7
.byt $AB,$AD,$B0,$B2,$B5,$B8,$BA,$BD
.byt $C0,$C2,$C5,$C8,$CA,$CD,$D0,$D2
.byt $D5,$D8,$DA,$DD,$E0,$E2,$E5,$E8
.byt $EA,$ED,$F0,$F2,$F5,$F8,$FA,$FD
.byt $FF,$FD,$FA,$F8,$F5,$F2,$F0,$ED
.byt $EA,$E8,$E5,$E2,$E0,$DD,$DA,$D8
.byt $D5,$D2,$D0,$CD,$CA,$C8,$C5,$C2
.byt $C0,$BD,$BA,$B8,$B5,$B2,$B0,$AD
.byt $AB,$A6,$A1,$9C,$96,$91,$8C,$86
.byt $81,$7C,$76,$71,$6C,$66,$61,$5C
.byt $56,$51,$4C,$47,$41,$3C,$37,$31
.byt $2C,$27,$21,$1C,$17,$11,$0C,$07
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
FastLED_RainbowB
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$00,$00,$00,$00,$00,$00,$00
.byt $00,$02,$05,$07,$0A,$0D,$0F,$12
.byt $15,$17,$1A,$1D,$1F,$22,$25,$27
.byt $2A,$2D,$2F,$32,$35,$37,$3A,$3D
.byt $3F,$42,$45,$47,$4A,$4D,$4F,$52
.byt $55,$5A,$5F,$64,$6A,$6F,$74,$7A
.byt $7F,$84,$8A,$8F,$94,$9A,$9F,$A4
.byt $AA,$AF,$B4,$B9,$BF,$C4,$C9,$CF
.byt $D4,$D9,$DF,$E4,$E9,$EF,$F4,$F9
.byt $FF,$FD,$FA,$F8,$F5,$F2,$F0,$ED
.byt $EA,$E8,$E5,$E2,$E0,$DD,$DA,$D8
.byt $D5,$D2,$D0,$CD,$CA,$C8,$C5,$C2
.byt $C0,$BD,$BA,$B8,$B5,$B2,$B0,$AD
.byt $AB,$A9,$A6,$A4,$A1,$9E,$9C,$99
.byt $96,$94,$91,$8E,$8C,$89,$86,$84
.byt $81,$7E,$7C,$79,$76,$74,$71,$6E
.byt $6C,$69,$66,$64,$61,$5E,$5C,$59
.byt $55,$53,$50,$4E,$4B,$48,$46,$43
.byt $40,$3E,$3B,$38,$36,$33,$30,$2E
.byt $2B,$28,$26,$23,$20,$1E,$1B,$18
.byt $16,$13,$10,$0E,$0B,$08,$06,$03

Binary file not shown.

View File

@@ -0,0 +1,89 @@
; "Rainbow with glitter" demo
; for "FastLED6502"
;
; Runs on an Apple ][, ][+, //e, or //gs
;
; Supports APA102, Adafruit DotStar,
; LPD8806, and WS2801 LED strips.
;
; LED strip connects to game port pins,
; see FastLED6502.s65 for details.
;
; Mark Kriegsman, February 2015
#define NUM_LEDS 100
#define BRIGHTNESS 64
#define CHIPSET APA102
#define DATA_PIN 14
#define CLOCK_PIN 5
* = $6000
#include "FastLED6502.s65"
#include "AppleII.s65"
gHue .byt 0
gHueDelta .byt 17
gHueSpeed .byt 7
Setup
lda #0
sta gHue
Loop
lda gHue
clc
adc gHueSpeed
sta gHue
ldx gHue
ldy gHueDelta
; Fill RGB array with HSV rainbow
jsr FastLED_FillRainbow_XY
; Use master brightness control
lda #BRIGHTNESS
sta FastLED_Brightness
CheckOpenApple
bit OpenApple
bpl CheckSolidApple
; Add glitter if requested
jsr AddGlitter
CheckSolidApple
bit SolidApple
bpl DoDisplay
; Pulse brightness if requested
jsr PulseBrightness
DoDisplay
; This is where the magic happens
jsr FastLED_Show
jmp Loop
AddGlitter
ldy #3
MaybeAdd1Glitter
jsr FastLED_Random8
cmp FastLED_NumPixels
bcs SkipThis1Glitter
tax
lda #$FF
sta ledsR,x
sta ledsG,x
sta ledsB,x
SkipThis1Glitter
dey
bne MaybeAdd1Glitter
rts
PulseBrightness
lda #13
jsr FastLED_Beat8
clc
adc #12
bcc PulseBright1
lda #$FF
PulseBright1
sta FastLED_Brightness
rts

View File

@@ -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

View File

@@ -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.7.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>",
"+<platforms/esp/32/*.cpp>"
],
"libArchive": false
}
}

View File

@@ -0,0 +1,10 @@
name=FastLED
version=3.7.0
author=Daniel Garcia
maintainer=Daniel Garcia <dgarcia@fastled.io>
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

View File

@@ -0,0 +1,322 @@
FastLED 3.7.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.7.0:
* Support for ESP-IDF version 5.x on ESP32 and ESP8266
* Improved support for new boards including UNO r4, Adafruit Grand Central Metro M4, SparkFun Thing Plus, RP2040, Portenta C33, and others. We also added a pointer to the PORTING.md document to help streamline additional porting; if youre porting to a new microcontroller, PORTING.md is the place to start.
* New gamma correction capability for APA102 and SK9822 LEDs
* Bug fixes and performances improvements, including faster smaller code on AVR, fewer compiler warnings, and faster build times
* Released May 2024, with heartfelt thanks to all the FastLED community members around the world!
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

View File

@@ -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<int m, int n> 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

View File

@@ -0,0 +1,688 @@
#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. 3007000 for version "3.7.0", with:
/// * 1 digit for the major version
/// * 3 digits for the minor version
/// * 3 digits for the patch version
#define FASTLED_VERSION 3007000
#ifndef FASTLED_INTERNAL
# ifdef FASTLED_SHOW_VERSION
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
# pragma message "FastLED version 3.007.000"
# else
# warning FastLED version 3.007.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 <SmartMatrix.h>
#endif
#ifdef DmxSimple_h
#include <DmxSimple.h>
#endif
#ifdef DmxSerial_h
#include <DMXSerial.h>
#endif
#ifdef USE_OCTOWS2811
#include <OctoWS2811.h>
#endif
#include <stdint.h>
#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
SK9822HD, ///< SK9822 LED chipset with 5-bit gamma correction
DOTSTAR, ///< APA102 LED chipset alias
DOTSTARHD, ///< APA102HD LED chipset alias
APA102HD, ///< APA102 LED chipset with 5-bit gamma correction
};
/// 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<uint8_t DATA_PIN, EOrder RGB_ORDER> class PIXIE : public PixieController<DATA_PIN, RGB_ORDER> {};
#endif
#ifdef FASTLED_HAS_CLOCKLESS
/// @addtogroup Chipsets
/// @{
/// @addtogroup ClocklessChipsets
/// @{
/// LED controller for WS2812 LEDs with GRB color order
/// @see WS2812Controller800Khz
template<uint8_t DATA_PIN> class NEOPIXEL : public WS2812Controller800Khz<DATA_PIN, GRB> {};
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SM16703 : public SM16703Controller<DATA_PIN, RGB_ORDER> {}; ///< @copydoc SM16703Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1829 : public TM1829Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc TM1829Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1812 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< TM1812 controller class. @copydetails TM1809Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1809 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc TM1809Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1804 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< TM1804 controller class. @copydetails TM1809Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class TM1803 : public TM1803Controller400Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc TM1803Controller400Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1903 : public UCS1903Controller400Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc UCS1903Controller400Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1903B : public UCS1903BController800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc UCS1903BController800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS1904 : public UCS1904Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc UCS1904Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class UCS2903 : public UCS2903Controller<DATA_PIN, RGB_ORDER> {}; ///< @copydoc UCS2903Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc WS2812Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2852 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< WS2852 controller class. @copydetails WS2812Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2812B : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< WS2812B controller class. @copydetails WS2812Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GS1903 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< GS1903 controller class. @copydetails WS2812Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SK6812 : public SK6812Controller<DATA_PIN, RGB_ORDER> {}; ///< @copydoc SK6812Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class SK6822 : public SK6822Controller<DATA_PIN, RGB_ORDER> {}; ///< SK6822 controller class. @copydetails SK6822Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class APA106 : public SK6822Controller<DATA_PIN, RGB_ORDER> {}; ///< APA106 controller class. @copydetails SK6822Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class PL9823 : public PL9823Controller<DATA_PIN, RGB_ORDER> {}; ///< @copydoc PL9823Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2811 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc WS2811Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2813 : public WS2813Controller<DATA_PIN, RGB_ORDER> {}; ///< @copydoc WS2813Controller
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class APA104 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< APA104 controller class. @copydetails WS2811Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class WS2811_400 : public WS2811Controller400Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc WS2811Controller400Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GE8822 : public GE8822Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc GE8822Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GW6205 : public GW6205Controller800Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc GW6205Controller800Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class GW6205_400 : public GW6205Controller400Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc GW6205Controller400Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class LPD1886 : public LPD1886Controller1250Khz<DATA_PIN, RGB_ORDER> {}; ///< @copydoc LPD1886Controller1250Khz
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class LPD1886_8BIT : public LPD1886Controller1250Khz_8bit<DATA_PIN, RGB_ORDER> {}; ///< @copydoc LPD1886Controller1250Khz_8bit
#if defined(DmxSimple_h) || defined(FASTLED_DOXYGEN)
/// @copydoc DMXSimpleController
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class DMXSIMPLE : public DMXSimpleController<DATA_PIN, RGB_ORDER> {};
#endif
#if defined(DmxSerial_h) || defined(FASTLED_DOXYGEN)
/// @copydoc DMXSerialController
template<EOrder RGB_ORDER> class DMXSERIAL : public DMXSerialController<RGB_ORDER> {};
#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<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint32_t SPI_DATA_RATE > CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD6803: { static LPD6803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTARHD:
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_DATA_RATE> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
/// Add an SPI based CLEDController instance to the world.
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD6803: { static LPD6803Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTARHD:
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
/// Add an SPI based CLEDController instance to the world.
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
case LPD6803: { static LPD6803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case LPD8806: { static LPD8806Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2801: { static WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case WS2803: { static WS2803Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SM16716: { static SM16716Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case P9813: { static P9813Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTAR:
case APA102: { static APA102Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case DOTSTARHD:
case APA102HD: { static APA102ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822: { static SK9822Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
case SK9822HD: { static SK9822ControllerHD<DATA_PIN, CLOCK_PIN, RGB_ORDER> c; return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset); }
}
}
#ifdef SPI_DATA
template<ESPIChipsets CHIPSET> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB>(data, nLedsOrOffset, nLedsIfOffset);
}
template<ESPIChipsets CHIPSET, EOrder RGB_ORDER> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER>(data, nLedsOrOffset, nLedsIfOffset);
}
template<ESPIChipsets CHIPSET, EOrder RGB_ORDER, uint32_t SPI_DATA_RATE> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER, SPI_DATA_RATE>(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<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<DATA_PIN, RGB_ORDER> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
/// Add a clockless based CLEDController instance to the world.
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<DATA_PIN, RGB> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
/// Add a clockless based CLEDController instance to the world.
template<template<uint8_t DATA_PIN> class CHIPSET, uint8_t DATA_PIN>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<DATA_PIN> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
#if defined(__FASTLED_HAS_FIBCC) && (__FASTLED_HAS_FIBCC == 1)
template<uint8_t NUM_LANES, template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
static CLEDController &addLeds(struct CRGB *data, int nLeds) {
static __FIBCC<CHIPSET, DATA_PIN, NUM_LANES, RGB_ORDER> c;
return addLeds(&c, data, nLeds);
}
#endif
#ifdef FASTSPI_USE_DMX_SIMPLE
template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
switch(CHIPSET) {
case DMX: { static DMXController<DATA_PIN> 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<template<EOrder RGB_ORDER> class CHIPSET, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<RGB_ORDER> c;
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
}
/// Add a 3rd party library based CLEDController instance to the world.
template<template<EOrder RGB_ORDER> class CHIPSET>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
static CHIPSET<RGB> 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<OWS2811 CHIPSET, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
switch(CHIPSET) {
case OCTOWS2811: { static COctoWS2811Controller<RGB_ORDER,WS2811_800kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
case OCTOWS2811_400: { static COctoWS2811Controller<RGB_ORDER,WS2811_400kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
#ifdef WS2813_800kHz
case OCTOWS2813: { static COctoWS2811Controller<RGB_ORDER,WS2813_800kHz> 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<OWS2811 CHIPSET>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
return addLeds<CHIPSET,GRB>(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<SWS2812 CHIPSET, int DATA_PIN, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
{
static CWS2812SerialController<DATA_PIN,RGB_ORDER> 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<ESM CHIPSET>
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<EBlockChipsets CHIPSET, int NUM_LANES, EOrder RGB_ORDER>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
switch(CHIPSET) {
#ifdef PORTA_FIRST_PIN
case WS2811_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef PORTB_FIRST_PIN
case WS2811_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef PORTC_FIRST_PIN
case WS2811_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef PORTD_FIRST_PIN
case WS2811_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
#ifdef HAS_PORTDC
case WS2811_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES,NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2811_400_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES,NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case WS2813_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
case TM1803_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
case UCS1903_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
#endif
}
}
/// Add a block based parallel output CLEDController instance to the world.
template<EBlockChipsets CHIPSET, int NUM_LANES>
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
return addLeds<CHIPSET,NUM_LANES,GRB>(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

View File

@@ -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;
}

View File

@@ -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<<row);
unsigned char *p = B;
for(uint32_t mask = 1<<7 ; mask ; mask >>= 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<int m, int n>
__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

View File

@@ -0,0 +1,782 @@
#ifndef __INC_CHIPSETS_H
#define __INC_CHIPSETS_H
#include "FastLED.h"
#include "pixeltypes.h"
#include "five_bit_hd_gamma.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 <SoftwareSerial.h>
#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<uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class PixieController : public CPixelLEDController<RGB_ORDER> {
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<RGB_ORDER> & 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<SoftwareSerial & STREAM, EOrder RGB_ORDER = RGB>
// class PixieController : public PixieBaseController<STREAM, RGB_ORDER> {
// 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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12) >
class LPD8806Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(1)>
class WS2801Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & pixels) {
mWaitDelay.wait();
mSPI.template writePixels<0, DATA_NOP, RGB_ORDER>(pixels);
mWaitDelay.mark();
}
};
/// WS2803 controller class.
/// @copydetails WS2801Controller
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(25)>
class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {};
/// 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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)>
class LPD6803Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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 <
uint8_t DATA_PIN, uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(12),
FiveBitGammaCorrectionMode GAMMA_CORRECTION_MODE = kFiveBitGammaCorrectionMode_Null,
uint32_t START_FRAME = 0x00000000,
uint32_t END_FRAME = 0xFF000000
>
class APA102Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI;
SPI mSPI;
void startBoundary() {
mSPI.writeWord(START_FRAME >> 16);
mSPI.writeWord(START_FRAME & 0xFFFF);
}
void endBoundary(int nLeds) {
int nDWords = (nLeds/32);
const uint8_t b0 = uint8_t(END_FRAME >> 24 & 0x000000ff);
const uint8_t b1 = uint8_t(END_FRAME >> 16 & 0x000000ff);
const uint8_t b2 = uint8_t(END_FRAME >> 8 & 0x000000ff);
const uint8_t b3 = uint8_t(END_FRAME >> 0 & 0x000000ff);
do {
mSPI.writeByte(b0);
mSPI.writeByte(b1);
mSPI.writeByte(b2);
mSPI.writeByte(b3);
} 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
}
inline void write2Bytes(uint8_t b1, uint8_t b2) __attribute__((always_inline)) {
#ifdef FASTLED_SPI_BYTE_ONLY
mSPI.writeByte(b1);
mSPI.writeByte(b2);
#else
mSPI.writeWord(uint16_t(b1) << 8 | b2);
#endif
}
public:
APA102Controller() {}
virtual void init() {
mSPI.init();
}
protected:
/// @copydoc CPixelLEDController::showPixels()
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
switch (GAMMA_CORRECTION_MODE) {
case kFiveBitGammaCorrectionMode_Null: {
showPixelsDefault(pixels);
break;
}
case kFiveBitGammaCorrectionMode_BitShift: {
showPixelsGammaBitShift(pixels);
break;
}
}
}
private:
static inline void getGlobalBrightnessAndScalingFactors(
PixelController<RGB_ORDER>& pixels,
uint8_t* out_s0, uint8_t* out_s1, uint8_t* out_s2, uint8_t* out_brightness) {
uint8_t s0 = pixels.getScale0();
uint8_t s1 = pixels.getScale1();
uint8_t 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
*out_s0 = s0;
*out_s1 = s1;
*out_s2 = s2;
*out_brightness = static_cast<uint8_t>(brightness);
}
// Legacy showPixels implementation.
inline void showPixelsDefault(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t s0, s1, s2, global_brightness;
getGlobalBrightnessAndScalingFactors(pixels, &s0, &s1, &s2, &global_brightness);
startBoundary();
while (pixels.has(1)) {
uint8_t r = pixels.loadAndScale0(0, s0);
uint8_t g = pixels.loadAndScale1(0, s1);
uint8_t b = pixels.loadAndScale2(0, s2);
writeLed(global_brightness, r, g, b);
pixels.stepDithering();
pixels.advanceData();
}
endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
inline void showPixelsGammaBitShift(PixelController<RGB_ORDER> & pixels) {
mSPI.select();
uint8_t r_scale = pixels.getScale0();
uint8_t g_scale = pixels.getScale1();
uint8_t b_scale = pixels.getScale2();
startBoundary();
while (pixels.has(1)) {
// Load raw uncorrected r,g,b values.
uint8_t r = pixels.loadAndScale0(0, 0xFF);
uint8_t g = pixels.loadAndScale1(0, 0xFF);
uint8_t b = pixels.loadAndScale2(0, 0xFF);
uint8_t brightness = 0;
if ((r | g | b) != 0) {
// Compute the gamma correction for non black pixels.
five_bit_hd_gamma_bitshift(
r, g, b,
r_scale, g_scale, b_scale, // Post-gamma scale.
&r, &g, &b, &brightness);
}
writeLed(brightness, r, g, b);
pixels.stepDithering();
pixels.advanceData();
}
endBoundary(pixels.size());
mSPI.waitFully();
mSPI.release();
}
};
template <
uint8_t DATA_PIN,
uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
>
class APA102ControllerHD : public APA102Controller<
DATA_PIN,
CLOCK_PIN,
RGB_ORDER,
SPI_SPEED,
kFiveBitGammaCorrectionMode_BitShift,
uint32_t(0x00000000),
uint32_t(0x00000000)> {
public:
APA102ControllerHD() = default;
APA102ControllerHD(const APA102ControllerHD&) = delete;
};
/// SK9822 controller class. It's exactly the same as the APA102Controller protocol but with a different END_FRAME and default SPI_SPEED.
/// @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 <
uint8_t DATA_PIN,
uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
>
class SK9822Controller : public APA102Controller<
DATA_PIN,
CLOCK_PIN,
RGB_ORDER,
SPI_SPEED,
kFiveBitGammaCorrectionMode_Null,
0x00000000,
0x00000000
> {
};
/// SK9822 controller class. It's exactly the same as the APA102Controller protocol but with a different END_FRAME and default SPI_SPEED.
/// @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 <
uint8_t DATA_PIN,
uint8_t CLOCK_PIN,
EOrder RGB_ORDER = RGB,
uint32_t SPI_SPEED = DATA_RATE_MHZ(24)
>
class SK9822ControllerHD : public APA102Controller<
DATA_PIN,
CLOCK_PIN,
RGB_ORDER,
SPI_SPEED,
kFiveBitGammaCorrectionMode_BitShift,
0x00000000,
0x00000000
> {
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(10)>
class P9813Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(16)>
class SM16716Controller : public CPixelLEDController<RGB_ORDER> {
typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> 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<RGB_ORDER> & 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<FLAG_START_BIT, DATA_NOP, RGB_ORDER>( 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 <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GE8822Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER, 4> {};
/// LPD1886 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER, 4> {};
/// LPD1886 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz_8bit : public ClocklessController<DATA_PIN, 2 * FMUL, 3 * FMUL, 2 * FMUL, RGB_ORDER> {};
/// WS2812 controller class @ 800 KHz.
/// @tparam DATA_PIN the data pin for these LEDs
/// @tparam RGB_ORDER the RGB ordering for these LEDs
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2812Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// WS2811 controller class @ 800 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// DP1903 controller class @ 800 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class DP1903Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 8 * FMUL, 2 * FMUL, RGB_ORDER> {};
/// DP1903 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class DP1903Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 16 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// WS2813 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> //not tested
class WS2813Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// WS2811 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 10 * FMUL, 6 * FMUL, RGB_ORDER> {};
/// SK6822 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6822Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// SM16703 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16703Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 4 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// SK6812 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6812Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS1903 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, 4 * FMUL, 12 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS1903B controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS1904 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, 3 * FMUL, 3 * FMUL, 4 * FMUL, RGB_ORDER> {};
/// UCS2903 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS2903Controller : public ClocklessController<DATA_PIN, 2 * FMUL, 6 * FMUL, 2 * FMUL, RGB_ORDER> {};
/// TM1809 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER> {};
/// TM1803 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1803Controller400Khz : public ClocklessController<DATA_PIN, 6 * FMUL, 9 * FMUL, 6 * FMUL, RGB_ORDER> {};
/// TM1829 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 5 * FMUL, 3 * FMUL, RGB_ORDER, 0, true, 500> {};
/// GW6205 controller class @ 400 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller400Khz : public ClocklessController<DATA_PIN, 6 * FMUL, 7 * FMUL, 6 * FMUL, RGB_ORDER, 4> {};
/// UCS1904 controller class @ 800 KHz.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller800Khz : public ClocklessController<DATA_PIN, 2 * FMUL, 4 * FMUL, 4 * FMUL, RGB_ORDER, 4> {};
/// PL9823 controller class.
/// @copydetails WS2812Controller800Khz
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class PL9823Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 8 * FMUL, 3 * FMUL, RGB_ORDER> {};
#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 <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GE8822Controller800Khz : public ClocklessController<DATA_PIN, C_NS(350), C_NS(660), C_NS(350), RGB_ORDER, 4> {};
// GW6205@400khz - 800ns, 800ns, 800ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller400Khz : public ClocklessController<DATA_PIN, C_NS(800), C_NS(800), C_NS(800), RGB_ORDER, 4> {};
// GW6205@400khz - 400ns, 400ns, 400ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class GW6205Controller800Khz : public ClocklessController<DATA_PIN, C_NS(400), C_NS(400), C_NS(400), RGB_ORDER, 4> {};
// UCS1903 - 500ns, 1500ns, 500ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903Controller400Khz : public ClocklessController<DATA_PIN, C_NS(500), C_NS(1500), C_NS(500), RGB_ORDER> {};
// UCS1903B - 400ns, 450ns, 450ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1903BController800Khz : public ClocklessController<DATA_PIN, C_NS(400), C_NS(450), C_NS(450), RGB_ORDER> {};
// UCS1904 - 400ns, 400ns, 450ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS1904Controller800Khz : public ClocklessController<DATA_PIN, C_NS(400), C_NS(400), C_NS(450), RGB_ORDER> {};
// UCS2903 - 250ns, 750ns, 250ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class UCS2903Controller : public ClocklessController<DATA_PIN, C_NS(250), C_NS(750), C_NS(250), RGB_ORDER> {};
// TM1809 - 350ns, 350ns, 550ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1809Controller800Khz : public ClocklessController<DATA_PIN, C_NS(350), C_NS(350), C_NS(450), RGB_ORDER> {};
// WS2811 - 320ns, 320ns, 640ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller800Khz : public ClocklessController<DATA_PIN, C_NS(320), C_NS(320), C_NS(640), RGB_ORDER> {};
// WS2813 - 320ns, 320ns, 640ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2813Controller : public ClocklessController<DATA_PIN, C_NS(320), C_NS(320), C_NS(640), RGB_ORDER> {};
// WS2812 - 250ns, 625ns, 375ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2812Controller800Khz : public ClocklessController<DATA_PIN, C_NS(250), C_NS(625), C_NS(375), RGB_ORDER> {};
// WS2811@400khz - 800ns, 800ns, 900ns
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class WS2811Controller400Khz : public ClocklessController<DATA_PIN, C_NS(800), C_NS(800), C_NS(900), RGB_ORDER> {};
// 750NS, 750NS, 750NS
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1803Controller400Khz : public ClocklessController<DATA_PIN, C_NS(700), C_NS(1100), C_NS(700), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller800Khz : public ClocklessController<DATA_PIN, C_NS(340), C_NS(340), C_NS(550), RGB_ORDER, 0, true, 500> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class TM1829Controller1600Khz : public ClocklessController<DATA_PIN, C_NS(100), C_NS(300), C_NS(200), RGB_ORDER, 0, true, 500> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz : public ClocklessController<DATA_PIN, C_NS(200), C_NS(400), C_NS(200), RGB_ORDER, 4> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class LPD1886Controller1250Khz_8bit : public ClocklessController<DATA_PIN, C_NS(200), C_NS(400), C_NS(200), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6822Controller : public ClocklessController<DATA_PIN, C_NS(375), C_NS(1000), C_NS(375), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SK6812Controller : public ClocklessController<DATA_PIN, C_NS(300), C_NS(300), C_NS(600), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class SM16703Controller : public ClocklessController<DATA_PIN, C_NS(300), C_NS(600), C_NS(300), RGB_ORDER> {};
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB>
class PL9823Controller : public ClocklessController<DATA_PIN, C_NS(350), C_NS(1010), C_NS(350), RGB_ORDER> {};
#endif
/// @} ClocklessChipsets
#endif
/// @} Chipsets
FASTLED_NAMESPACE_END
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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 <stddef.h>
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<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF>
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<<i) & MASK) { nOffset += (len * mAdvance); }
}
}
/// Constructor
/// @param d pointer to LED data
/// @param len length of the LED data
/// @param s LED scale values, as CRGB struct
/// @param dither dither setting for the LEDs
/// @param advance whether the pointer (d) should advance per LED
/// @param skip if the pointer is advancing, how many bytes to skip in addition to 3
PixelController(const uint8_t *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER, bool advance=true, uint8_t skip=0) : mData(d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mData += skip;
mAdvance = (advance) ? 3+skip : 0;
initOffsets(len);
}
/// Constructor
/// @param d pointer to LED data
/// @param len length of the LED data
/// @param s LED scale values, as CRGB struct
/// @param dither dither setting for the LEDs
PixelController(const CRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 3;
initOffsets(len);
}
/// Constructor
/// @param d pointer to LED data
/// @param len length of the LED data
/// @param s LED scale values, as CRGB struct
/// @param dither dither setting for the LEDs
PixelController(const CRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mLenRemaining(len), mScale(s) {
enable_dithering(dither);
mAdvance = 0;
initOffsets(len);
}
#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
/// Predicted max update rate, in Hertz
#define MAX_LIKELY_UPDATE_RATE_HZ 400
/// Minimum acceptable dithering rate, in Hertz
#define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
/// The number of updates in a single dither cycle
#define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
/// Set "virtual bits" of dithering to the highest level
/// that is not likely to cause excessive flickering at
/// low brightness levels + low update rates.
/// These pre-set values are a little ambitious, since
/// a 400Hz update rate for WS2811-family LEDs is only
/// possible with 85 pixels or fewer.
/// Once we have a "number of milliseconds since last update"
/// value available here, we can quickly calculate the correct
/// number of "virtual bits" on the fly with a couple of "if"
/// statements -- no division required. At this point,
/// the division is done at compile time, so there's no runtime
/// cost, but the values are still hard-coded.
/// @todo Can these macros be replaced with constants scoped to PixelController::init_binary_dithering()?
#define RECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>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<int SLOT> __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<int SLOT> __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<int SLOT> __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<int SLOT> __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<int SLOT> __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<int SLOT> __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<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(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<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(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<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(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<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte<SLOT>(pc, lane), scale); }
/// A version of loadAndScale() that advances the output data pointer
/// @param pc reference to the pixel controller
template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(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<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(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<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale<SLOT>(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<int SLOT> __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<int SLOT> __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<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> 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<RGB_ORDER,LANES,MASK> & 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<RGB_ORDER, LANES, MASK> 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<RGB_ORDER, LANES, MASK> 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

View File

@@ -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

View File

@@ -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 <DmxSimple.h>
/// 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 <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXSimpleController : public CPixelLEDController<RGB_ORDER> {
public:
/// Initialize the LED controller
virtual void init() { DmxSimple.usePin(DATA_PIN); }
protected:
/// @copydoc CPixelLEDController::showPixels()
virtual void showPixels(PixelController<RGB_ORDER> & 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 <DMXSerial.h>
/// 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 <EOrder RGB_ORDER = RGB> class DMXSerialController : public CPixelLEDController<RGB_ORDER> {
public:
/// Initialize the LED controller
virtual void init() { DMXSerial.init(DMXController); }
/// @copydoc CPixelLEDController::showPixels()
virtual void showPixels(PixelController<RGB_ORDER> & 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

View File

@@ -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

View File

@@ -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<int WAIT> 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<int WAIT> 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<int CYCLES> inline void delaycycles();
/// A variant of ::delaycycles that will always delay
/// at least one cycle.
template<int CYCLES> inline void delaycycles_min1() {
delaycycles<1>();
delaycycles<CYCLES-1>();
}
// TODO: ARM version of _delaycycles_
// usable definition
#if defined(FASTLED_AVR)
// worker template - this will nop for LOOP * 3 + PAD cycles total
template<int LOOP, int PAD> inline void _delaycycles_AVR() {
delaycycles<PAD>();
// 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<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
_delaycycles_AVR<CYCLES / 3, CYCLES % 3>();
}
#else
// template<int LOOP, int PAD> inline void _delaycycles_ARM() {
// delaycycles<PAD>();
// // 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<int CYCLES> __attribute__((always_inline)) inline void delaycycles() {
// _delaycycles_ARM<CYCLES / 3, CYCLES % 3>();
FL_NOP; delaycycles<CYCLES-1>();
}
#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

View File

@@ -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 <avr/pgmspace.h>
#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

View File

@@ -0,0 +1,359 @@
#ifndef __INC_FASTPIN_H
#define __INC_FASTPIN_H
#include "FastLED.h"
#include "led_sysdefs.h"
#include <stddef.h>
#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<uint8_t PIN> 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<uint8_t PIN> RwReg FastPin<PIN>::sPinMask;
template<uint8_t PIN> volatile RwReg *FastPin<PIN>::sPort;
template<uint8_t PIN> volatile RoReg *FastPin<PIN>::sInPort;
#else
template<uint8_t PIN> 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<uint8_t PIN> class FastPinBB : public FastPin<PIN> {};
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<uint8_t port> 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<L> { \
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<LC> { \
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

View File

@@ -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<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
/// Software SPI output
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
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<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public NRF51SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(NRF52_SERIES) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(FASTLED_APOLLO3) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public APOLLO3HardwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public ESP32SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif
#if defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
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<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
#if defined(SPI2_DATA)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
#endif
#elif defined(FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, SPI, 0> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI1_DATA, SPI1_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI1_DATA, SPI1_CLOCK, SPI_SPEED, SPI1, 1> {};
template<uint32_t SPI_SPEED>
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, SPI2, 2> {};
#elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI)
#define DECLARE_SPI0(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\
class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40076000> {};
#define DECLARE_SPI1(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\
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<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public SAMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
#elif defined(AVR_HARDWARE_SPI)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
#if defined(SPI_UART0_DATA)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> : public AVRUSART0SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> {};
#endif
#if defined(SPI_UART1_DATA)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> : public AVRUSART1SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> {};
#endif
#elif defined(ARDUNIO_CORE_SPI)
template<uint32_t SPI_SPEED>
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ArdunioCoreSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, SPI> {};
#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<uint32_t SPI_SPEED>
// class AVRSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> : public AVRUSARTSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> {};
// #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

View File

@@ -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 <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
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<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<CLOCK_PIN>::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<DATA_PIN>::port_t data_t;
typedef typename FastPin<CLOCK_PIN>::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<DATA_PIN>::setOutput();
FastPin<CLOCK_PIN>::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<MIN_DELAY>(); 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 <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
//cli();
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::hi();
#ifdef ESP32
// try to ensure we never have adjacent write opcodes to the same register
FastPin<CLOCK_PIN>::lo();
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::toggle(); CLOCK_LO_DELAY;
#else
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
#endif
} else {
FastPin<DATA_PIN>::lo();
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
#ifdef ESP32
// try to ensure we never have adjacent write opcodes to the same register
FastPin<CLOCK_PIN>::toggle(); CLOCK_HI_DELAY;
#else
FastPin<CLOCK_PIN>::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 <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::hi(datapin);
FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
} else {
FastPin<DATA_PIN>::lo(datapin);
FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::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 <uint8_t BIT> __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<DATA_PIN>::fastset(datapin, hival);
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
} else {
// FL_NOP;
FastPin<DATA_PIN>::fastset(datapin, loval);
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
FastPin<CLOCK_PIN>::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 <uint8_t BIT> __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<BIT>(b);
#else
if(b & (1 << BIT)) {
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
} else {
// FL_NOP;
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
FastPin<DATA_PIN>::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<SELECT_PIN>::hi(); }
/// Release the SPI chip select line
void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::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<DATA_PIN>::port();
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::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<CLOCK_PIN>::port();
FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::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<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::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 <class D> 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<CLOCK_PIN>::port();
FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::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<DATA_PIN>::hival();
FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
uint8_t *end = data + len;
while(data != end) {
writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
}
} else {
// FastPin<CLOCK_PIN>::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<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
uint8_t *end = data + len;
while(data != end) {
writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
}
// FastPin<CLOCK_PIN>::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_NOP>(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 <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> 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<DATA_PIN>::port();
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::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<DATA_PIN>::hival();
FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::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<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::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

View File

@@ -0,0 +1,3 @@
/// @file fastspi_dma.h
/// Direct memory access (DMA) functions for SPI interfaces
/// @deprecated This header file is empty.

View File

@@ -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 <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
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 <uint8_t BIT> 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 <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
/// write out pixel data from the given PixelController object
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) { /* TODO */ }
};
FASTLED_NAMESPACE_END
#endif
#endif

View File

@@ -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 <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
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 <class D> 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_NOP>(data, len); }
/// write a single bit out, which bit from the passed in byte is determined by template parameter
template <uint8_t BIT> 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 <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> 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

View File

@@ -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

View File

@@ -0,0 +1,189 @@
#include "FastLED.h"
#include "five_bit_hd_gamma.h"
#include "fastled_progmem.h"
#include "lib8tion/scale8.h"
// Author: Zach Vorhies
#ifndef FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP
#define FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP 0
#endif
FASTLED_NAMESPACE_BEGIN
#ifndef FASTLED_FIVE_BIT_HD_GAMMA_BITSHIFT_FUNCTION_OVERRIDE
#ifndef FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_2_8
// Fast a memory efficient gamma=2 function.
void five_bit_hd_gamma_function(
uint8_t r8, uint8_t g8, uint8_t b8,
uint16_t* r16, uint16_t* g16, uint16_t* b16) {
*r16 = uint16_t(r8) * r8;
*g16 = uint16_t(g8) * g8;
*b16 = uint16_t(b8) * b8;
}
#else
// Using look up table for gamma16 correction at power of 2.8
static const uint16_t PROGMEM _gamma_2_8[256] = {
0, 0, 0, 1, 1, 2, 4, 6, 8, 11, 14,
18, 23, 29, 35, 41, 49, 57, 67, 77, 88, 99,
112, 126, 141, 156, 173, 191, 210, 230, 251, 274, 297,
322, 348, 375, 404, 433, 464, 497, 531, 566, 602, 640,
680, 721, 763, 807, 853, 899, 948, 998, 1050, 1103, 1158,
1215, 1273, 1333, 1394, 1458, 1523, 1590, 1658, 1729, 1801, 1875,
1951, 2029, 2109, 2190, 2274, 2359, 2446, 2536, 2627, 2720, 2816,
2913, 3012, 3114, 3217, 3323, 3431, 3541, 3653, 3767, 3883, 4001,
4122, 4245, 4370, 4498, 4627, 4759, 4893, 5030, 5169, 5310, 5453,
5599, 5747, 5898, 6051, 6206, 6364, 6525, 6688, 6853, 7021, 7191,
7364, 7539, 7717, 7897, 8080, 8266, 8454, 8645, 8838, 9034, 9233,
9434, 9638, 9845, 10055, 10267, 10482, 10699, 10920, 11143, 11369, 11598,
11829, 12064, 12301, 12541, 12784, 13030, 13279, 13530, 13785, 14042, 14303,
14566, 14832, 15102, 15374, 15649, 15928, 16209, 16493, 16781, 17071, 17365,
17661, 17961, 18264, 18570, 18879, 19191, 19507, 19825, 20147, 20472, 20800,
21131, 21466, 21804, 22145, 22489, 22837, 23188, 23542, 23899, 24260, 24625,
24992, 25363, 25737, 26115, 26496, 26880, 27268, 27659, 28054, 28452, 28854,
29259, 29667, 30079, 30495, 30914, 31337, 31763, 32192, 32626, 33062, 33503,
33947, 34394, 34846, 35300, 35759, 36221, 36687, 37156, 37629, 38106, 38586,
39071, 39558, 40050, 40545, 41045, 41547, 42054, 42565, 43079, 43597, 44119,
44644, 45174, 45707, 46245, 46786, 47331, 47880, 48432, 48989, 49550, 50114,
50683, 51255, 51832, 52412, 52996, 53585, 54177, 54773, 55374, 55978, 56587,
57199, 57816, 58436, 59061, 59690, 60323, 60960, 61601, 62246, 62896, 63549,
64207, 64869, 65535};
void five_bit_hd_gamma_function(uint8_t r8, uint8_t g8,
uint8_t b8, uint16_t *r16,
uint16_t *g16,
uint16_t *b16) {
*r16 = _gamma_2_8[r8];
*g16 = _gamma_2_8[g8];
*b16 = _gamma_2_8[b8];
}
#endif // FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_2_8
#endif // FASTLED_FIVE_BIT_HD_GAMMA_BITSHIFT_FUNCTION_OVERRIDE
#ifndef FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
void five_bit_hd_gamma_bitshift(
uint8_t r8, uint8_t g8, uint8_t b8,
uint8_t r8_scale, uint8_t g8_scale, uint8_t b8_scale,
uint8_t* out_r8,
uint8_t* out_g8,
uint8_t* out_b8,
uint8_t* out_power_5bit) {
// Step 1: Gamma Correction
uint16_t r16, g16, b16;
five_bit_hd_gamma_function(r8, g8, b8, &r16, &g16, &b16);
// Step 2: Post gamma correction scale.
if (r8_scale != 0xff || g8_scale != 0xff || b8_scale != 0xff) {
r16 = scale16by8(r16, r8_scale);
g16 = scale16by8(g16, g8_scale);
b16 = scale16by8(b16, b8_scale);
}
// Step 3: Initialize 5-bit brightness.
// Note: we only get 5 levels of brightness
uint8_t v8 = 31;
uint16_t numerator = 1;
uint16_t denominator = 1;
const uint32_t r16_const = r16;
const uint32_t g16_const = g16;
const uint32_t b16_const = b16;
// Step 4: Bit Shifting Loop, can probably replaced with a
// single pass bit-twiddling hack.
do {
// Note that to avoid slow divisions, we multiply the max_value
// by the denominator.
uint32_t max_value = 0xfffful * 15;
if (r16_const * 31 > max_value) {
break;
}
if (g16_const * 31 > max_value) {
break;
}
if (b16_const * 31 > max_value) {
break;
}
numerator = 31;
denominator = 15;
v8 = 15;
max_value = 0xfffful * 15 * 7;
if (r16_const * 31 * 15 > max_value) {
break;
}
if (g16_const * 31 * 15 > max_value) {
break;
}
if (b16_const * 31 * 15 > max_value) {
break;
}
numerator = 31 * 15;
denominator = 15 * 7;
v8 = 7;
max_value = 0xfffful * 15 * 7 * 3;
if (r16_const * 31 * 15 * 7 > max_value) {
break;
}
if (g16_const * 31 * 15 * 7 > max_value) {
break;
}
if (b16_const * 31 * 15 * 7 > max_value) {
break;
}
numerator = 31 * 15 * 7;
denominator = 15 * 7 * 3;
v8 = 3;
max_value = 0xfffful * 15 * 7 * 3;
if (r16_const * 31 * 15 * 7 * 3 > max_value) {
break;
}
if (g16_const * 31 * 15 * 7 * 3 > max_value) {
break;
}
if (b16_const * 31 * 15 * 7 * 3 > max_value) {
break;
}
numerator = 31 * 15 * 7 * 3;
v8 = 1;
} while(false);
r16 = uint16_t(r16_const * numerator / denominator);
g16 = uint16_t(g16_const * numerator / denominator);
b16 = uint16_t(b16_const * numerator / denominator);
// Step 5: Conversion Back to 8-bit.
uint8_t r8_final = (r8 == 255 && uint8_t(r16 >> 8) >= 254) ? 255 : uint8_t(r16 >> 8);
uint8_t g8_final = (g8 == 255 && uint8_t(g16 >> 8) >= 254) ? 255 : uint8_t(g16 >> 8);
uint8_t b8_final = (b8 == 255 && uint8_t(b16 >> 8) >= 254) ? 255 : uint8_t(b16 >> 8);
#if FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP > 0
if (v8 == 1) {
// Linear tuning for the lowest possible brightness. x=y until
// the intersection point at 9.
if (r8 < FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP && r16 > 0) {
r8_final = r8;
}
if (g8 < FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP && g16 > 0) {
g8_final = g8;
}
if (b8 < FASTLED_FIVE_BIT_HD_GAMMA_LOW_END_LINEAR_RAMP && b16 > 0) {
b8_final = b8;
}
}
#endif
// Step 6: Output
*out_r8 = r8_final;
*out_g8 = g8_final;
*out_b8 = b8_final;
*out_power_5bit = v8;
}
#endif // FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
FASTLED_NAMESPACE_END

View File

@@ -0,0 +1,77 @@
#ifndef _FIVE_BIT_HD_GAMMA_H_
#define _FIVE_BIT_HD_GAMMA_H_
#include "FastLED.h"
FASTLED_NAMESPACE_BEGIN
enum FiveBitGammaCorrectionMode {
kFiveBitGammaCorrectionMode_Null = 0,
kFiveBitGammaCorrectionMode_BitShift = 1
};
// Applies gamma correction for the RGBV(8, 8, 8, 5) color space, where
// the last byte is the brightness byte at 5 bits.
// To override this five_bit_hd_gamma_bitshift you'll need to define
// FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE in your build settings
// then define the function anywhere in your project.
// Example:
// FASTLED_NAMESPACE_BEGIN
// void five_bit_hd_gamma_bitshift(
// uint8_t r8, uint8_t g8, uint8_t b8,
// uint8_t r8_scale, uint8_t g8_scale, uint8_t b8_scale,
// uint8_t* out_r8,
// uint8_t* out_g8,
// uint8_t* out_b8,
// uint8_t* out_power_5bit) {
// cout << "hello world\n";
// }
// FASTLED_NAMESPACE_END
#ifdef FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
// This function is located somewhere else in your project, so it's declared
// extern here.
extern void five_bit_hd_gamma_bitshift(
uint8_t r8, uint8_t g8, uint8_t b8,
uint8_t r8_scale, uint8_t g8_scale, uint8_t b8_scale,
uint8_t* out_r8,
uint8_t* out_g8,
uint8_t* out_b8,
uint8_t* out_power_5bit);
#else
void five_bit_hd_gamma_bitshift(
uint8_t r8, uint8_t g8, uint8_t b8,
uint8_t r8_scale, uint8_t g8_scale, uint8_t b8_scale,
uint8_t* out_r8,
uint8_t* out_g8,
uint8_t* out_b8,
uint8_t* out_power_5bit);
#endif // FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
// Simple gamma correction function that converts from
// 8-bit color component and converts it to gamma corrected 16-bit
// color component. Fast and no memory overhead!
// To override this function you'll need to define FASTLED_FIVE_BIT_HD_GAMMA_BITSHIFT_FUNCTION_OVERRIDE
// in your build settings and then define your own version anywhere in your project.
// Example:
// FASTLED_NAMESPACE_BEGIN
// void five_bit_hd_gamma_function(
// uint8_t r8, uint8_t g8, uint8_t b8,
// uint16_t* r16, uint16_t* g16, uint16_t* b16) {
// cout << "hello world\n";
// }
// FASTLED_NAMESPACE_END
#ifdef FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_OVERRIDE
// This function is located somewhere else in your project, so it's declared
// extern here.
extern void five_bit_hd_gamma_function(
uint8_t r8, uint8_t g8, uint8_t b8,
uint16_t* r16, uint16_t* g16, uint16_t* b16);
#else
void five_bit_hd_gamma_function(
uint8_t r8, uint8_t g8, uint8_t b8,
uint16_t* r16, uint16_t* g16, uint16_t* b16);
#endif // FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_OVERRIDE
FASTLED_NAMESPACE_END
#endif // _FIVE_BIT_HD_GAMMA_H_

View File

@@ -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 <stdint.h>
#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

View File

@@ -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)
///
/// <sup>Source: http://en.wikipedia.org/wiki/Rainbow#Number_of_colours_in_spectrum_or_rainbow</sup>
///
/// 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

View File

@@ -0,0 +1,84 @@
#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"
#elif defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(ARDUINO_ARCH_RENESAS_PORTENTA)
#include "platforms/arm/renesas/led_sysdef_arm_renesas.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 <Arduino.h>
#endif
/// Clock cycles per microsecond.
/// Calculated using the F_CPU preprocessor define
#define CLKS_PER_US (F_CPU/1000000)
#endif

View File

@@ -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 <stdint.h>
#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 <Arduino.h>
#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

File diff suppressed because it is too large Load Diff

View File

@@ -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_%=: "
: "+d" (i) // r16-r31, restricted by ldi
: "r" (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_%=: "
: "+d" (i) // r16-r31, restricted by ldi
: "r" (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_%=: "
: "+d" (i) // r16-r31, restricted by ldi
: "r" (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" : "+r" (i) : "r" (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] "+r" (j)
: [i] "r" (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" : "+r" (i) : "r" (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"
: "+r" (i)
: "r" (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] "+r" (i)
: [j] "r" (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"
: "+r" (i)
: "r" (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] "+r" (i)
: [j] "r" (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"
: "+r" (i)
: "r" (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] "+r" (i)
: [j] "r" (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"
: "+r" (i)
: "r" (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"
: "+d" (i) // r16-r31, restricted by ldi
: "r" (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] "+r" (amountOfB)
: [a] "r" (a),
[b] "r" (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

View File

@@ -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

View File

@@ -0,0 +1,781 @@
#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"
: "+d" (i) /* writes to i; r16-r31, restricted by ldi */
: "r" (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] "+d" (j) // r16-r31, restricted by subi
: [i] "r" (i), [scale] "r" (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" */
: "+d" (i) /* writes to i; r16-r31, restricted by ldi */
: "r" (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" */
: "+d" (i) /* writes to i; r16-r31, restricted by ldi */
: "r" (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] "+d" (j) // r16-r31, restricted by subi
: [i] "r" (i), [scale] "r" (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] "+d" (i) // r16-r31, restricted by subi
: [scale] "r" (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 (scale == 0) {
return 0; // Fixes non zero output when scale == 0 and FASTLED_SCALE8_FIXED==1
}
#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

View File

@@ -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

View File

@@ -0,0 +1,864 @@
/// @file noise.cpp
/// Functions to generate and fill arrays with noise.
/// Disables pragma messages and warnings
#define FASTLED_INTERNAL
#include "FastLED.h"
#include <string.h>
// Compiler throws a warning about stack usage possibly being unbounded even
// though bounds are checked, silence that so users don't see it
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-usage="
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] "+r" (i)
: [j] "r" (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<<bitnumber)) ? a : b
// but with an AVR asm version that's smaller and quicker than C
// (and probably not worth including in lib8tion)
static int8_t inline __attribute__((always_inline)) selectBasedOnHashBit(uint8_t hash, uint8_t bitnumber, int8_t a, int8_t b) {
int8_t result;
#if !defined(__AVR__)
result = (hash & (1<<bitnumber)) ? a : b;
#else
asm volatile(
"mov %[result],%[a] \n\t"
"sbrs %[hash],%[bitnumber] \n\t"
"mov %[result],%[b] \n\t"
: [result] "=r" (result)
: [hash] "r" (hash),
[bitnumber] "M" (bitnumber),
[a] "r" (a),
[b] "r" (b)
);
#endif
return result;
}
static int8_t inline __attribute__((always_inline)) grad8(uint8_t hash, int8_t x, int8_t y, int8_t z) {
#if 0
switch(hash & 0xF) {
case 0: return (( x) + ( y))>>1;
case 1: return ((-x) + ( y))>>1;
case 2: return (( x) + (-y))>>1;
case 3: return ((-x) + (-y))>>1;
case 4: return (( x) + ( z))>>1;
case 5: return ((-x) + ( z))>>1;
case 6: return (( x) + (-z))>>1;
case 7: return ((-x) + (-z))>>1;
case 8: return (( y) + ( z))>>1;
case 9: return ((-y) + ( z))>>1;
case 10: return (( y) + (-z))>>1;
case 11: return ((-y) + (-z))>>1;
case 12: return (( y) + ( x))>>1;
case 13: return ((-y) + ( z))>>1;
case 14: return (( y) + (-x))>>1;
case 15: return ((-y) + (-z))>>1;
}
#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, int16_t scalex, uint16_t y, int16_t 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<height; ++ii) {
uint8_t *pRow = pData + (ii*width);
for(int jj=j; jj<(j+skip) && jj<width; ++jj) {
pRow[jj] = scale8(pRow[jj],invamp) + noise_base;
}
}
}
}
}
}
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_raw_2dnoise8(pData, width, height, octaves, q44(2,0), 128, 1, x, scalex, y, scaley, time);
}
void fill_raw_2dnoise16(uint16_t *pData, int width, int height, uint8_t octaves, q88 freq88, fract16 amplitude, int skip, uint32_t x, int32_t scalex, uint32_t y, int32_t scaley, uint32_t time) {
if(octaves > 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<height; ++ii) {
uint16_t *pRow = pData + (ii*width);
for(int jj=j; jj<(j+skip) && jj<width; ++jj) {
pRow[jj] = scale16(pRow[jj],invamp) + noise_base;
}
}
}
}
}
}
/// Unused
/// @todo Remove?
int32_t nmin=11111110;
/// Unused
/// @todo Remove?
int32_t nmax=0;
void fill_raw_2dnoise16into8(uint8_t *pData, int width, int height, uint8_t octaves, q44 freq44, fract8 amplitude, int skip, uint32_t x, int32_t scalex, uint32_t y, int32_t scaley, uint32_t time) {
if(octaves > 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<height; ++ii) {
uint8_t *pRow = pData + (ii*width);
for(int jj=j; jj<(j+skip) && jj<width; ++jj) {
pRow[jj] = scale8(pRow[jj],invamp) + noise_base;
}
}
}
}
}
}
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_raw_2dnoise16into8(pData, width, height, octaves, q44(2,0), 171, 1, x, scalex, y, scaley, time);
}
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) {
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_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
#pragma GCC diagnostic pop

View File

@@ -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, int16_t scalex, uint16_t y, int16_t 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, int32_t scalex, uint32_t y, int32_t 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, int32_t scalex, uint32_t y, int32_t 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, int32_t scalex, uint32_t y, int32_t 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

View File

@@ -0,0 +1,417 @@
#ifndef __INC_PIXELSET_H
#define __INC_PIXELSET_H
#include "FastLED.h"
#ifndef abs
#include <stdlib.h>
#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 PIXEL_TYPE>
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 T>
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<PIXEL_TYPE> iterator; ///< Iterator helper type for this class
typedef pixelset_iterator_base<const PIXEL_TYPE> 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<CRGB> 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<CRGB> with an included array of CRGB LEDs
/// @tparam SIZE the number of LEDs to include in the array
template<int SIZE>
class CRGBArray : public CPixelView<CRGB> {
CRGB rawleds[SIZE]; ///< the LED data
public:
CRGBArray() : CPixelView<CRGB>(rawleds, SIZE) {}
using CPixelView::operator=;
};
/// @} PixelSet
#endif

View File

@@ -0,0 +1,966 @@
#ifndef __INC_PIXELS_H
#define __INC_PIXELS_H
#include "FastLED.h"
#include <stdint.h>
#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
constexpr 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
constexpr 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
constexpr 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
constexpr 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

View File

@@ -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

View File

@@ -0,0 +1,52 @@
#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__) || defined(__SAMD51P20A__)
#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"
#elif defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(ARDUINO_ARCH_RENESAS_PORTENTA)
#include "platforms/arm/renesas/fastled_arm_renesas.h"
#else
// AVR platforms
#include "platforms/avr/fastled_avr.h"
#endif
#endif

View File

@@ -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 <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;
CMinWait<WAIT_TIME> mWait;
public:
virtual void init() {
// Initialize everything
// Configure DATA_PIN for FastGPIO (settings are in fastpin_apollo3.h)
FastPin<DATA_PIN>::setOutput();
FastPin<DATA_PIN>::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<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}
template<int BITS> __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<DATA_PIN>::hi();
if(b&0x80) {
// "1 code" = longer pulse width
while((__am_hal_systick_count() - next_mark) > (T3+(3*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
} else {
// "0 code" = shorter pulse width
while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::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<DATA_PIN>::hi();
if(b&0x80) {
// "1 code" = longer pulse width
while((__am_hal_systick_count() - next_mark) > (T3+(2*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
} else {
// "0 code" = shorter pulse width
while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::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<RGB_ORDER> 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

View File

@@ -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

View File

@@ -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<uint8_t PIN, uint8_t PAD> 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<PIN> : public _APOLLO3PIN<PIN, PAD> {};
// 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

View File

@@ -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 <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
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 <class D> 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_NOP>(data, len); }
// write a single bit out, which bit from the passed in byte is determined by template parameter
template <uint8_t BIT> 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 <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> 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

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More