initial commit
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# FastLED Platform: adafruit
|
||||
|
||||
Adafruit_NeoPixel integration providing a FastLED‑compatible clockless controller implemented on top of Adafruit’s driver.
|
||||
|
||||
## Files (quick pass)
|
||||
- `clockless.h`: Adapter layer. Exposes a `ClocklessController` template that marshals FastLED pixel data to an `Adafruit_NeoPixel` instance. Requires `Adafruit_NeoPixel.h` to be available; timing (T1/T2/T3) is managed by the Adafruit library.
|
||||
|
||||
## Usage and detection
|
||||
- Controlled by `FASTLED_USE_ADAFRUIT_NEOPIXEL` (defaults to undefined unless building docs). If enabled but `Adafruit_NeoPixel.h` is missing, the adapter disables itself with an error.
|
||||
- Color order: FastLED’s `PixelController` applies RGB ordering; the adapter always feeds RGB into Adafruit’s API.
|
||||
- Timings: `T1/T2/T3` template params are ignored here; Adafruit’s driver handles signal generation and timing per platform.
|
||||
|
||||
## Notes
|
||||
- This path is useful when Adafruit’s platform backends (e.g., some boards/cores) are preferred or more stable for your setup.
|
||||
- Performance characteristics and memory usage follow Adafruit_NeoPixel; expect different throughput vs native FastLED clockless drivers.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_ADAFRUIT_NEOPIXEL`**: Default undefined (unless building docs via `FASTLED_DOXYGEN`). When defined, enables the Adafruit adapter; requires `Adafruit_NeoPixel.h`.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
## Compatibility and color order
|
||||
|
||||
Supported color orders: FastLED’s `PixelController` handles byte reordering before passing data to Adafruit_NeoPixel. Typical orders like GRB/RGB/BRG are supported transparently.
|
||||
|
||||
Constraints vs native FastLED timing:
|
||||
|
||||
- Timing is wholly delegated to Adafruit_NeoPixel. The `T1/T2/T3` template parameters are ignored; use Adafruit’s platform timings.
|
||||
- Throughput and CPU usage may differ from FastLED’s native clockless or RMT/I2S backends. If you need multi‑strip parallelism or strict ISR windows, consider native drivers instead.
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
#include "fl/has_include.h"
|
||||
|
||||
#if FL_HAS_INCLUDE(<Adafruit_NeoPixel.h>)
|
||||
#include "platforms/adafruit/clockless_real.hpp"
|
||||
#else
|
||||
#include "platforms/adafruit/clockless_fake.hpp"
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
/// @file clockless.h
|
||||
/// Adafruit_NeoPixel-based clockless controller implementation
|
||||
///
|
||||
/// This header provides a FastLED-compatible clockless controller that uses
|
||||
/// the Adafruit_NeoPixel library as the underlying driver. This allows users
|
||||
/// to leverage the proven reliability and platform-specific optimizations
|
||||
/// of the Adafruit library within the FastLED ecosystem.
|
||||
///
|
||||
/// Requirements:
|
||||
/// - Adafruit_NeoPixel library must be included before FastLED
|
||||
/// - The controller is only available when Adafruit_NeoPixel.h is detected
|
||||
|
||||
|
||||
#include "fl/memory.h"
|
||||
#include "fl/unique_ptr.h"
|
||||
#include "eorder.h"
|
||||
#include "pixel_controller.h"
|
||||
#include "platforms/adafruit/driver.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
class PixelIterator;
|
||||
|
||||
/// WS2812/NeoPixel clockless controller using Adafruit_NeoPixel library as the underlying driver
|
||||
///
|
||||
/// This controller provides a simple interface for WS2812/NeoPixel LEDs using the proven
|
||||
/// Adafruit_NeoPixel library for platform-specific optimizations and reliable output.
|
||||
/// Supports RGB color order conversion and handles initialization, pixel conversion,
|
||||
/// and output automatically.
|
||||
///
|
||||
/// @tparam DATA_PIN the data pin for the LED strip
|
||||
/// @tparam RGB_ORDER the RGB ordering for the LEDs (affects input processing, output is always RGB)
|
||||
/// @see https://github.com/adafruit/Adafruit_NeoPixel
|
||||
template <int DATA_PIN, EOrder RGB_ORDER = GRB>
|
||||
class AdafruitWS2812Controller : public CPixelLEDController<RGB_ORDER> {
|
||||
private:
|
||||
fl::unique_ptr<fl::IAdafruitNeoPixelDriver> mDriver;
|
||||
|
||||
public:
|
||||
/// Constructor - creates uninitialized controller
|
||||
AdafruitWS2812Controller(){}
|
||||
|
||||
/// Destructor - automatic cleanup
|
||||
virtual ~AdafruitWS2812Controller() = default;
|
||||
|
||||
/// Initialize the controller
|
||||
virtual void init() override {
|
||||
// Driver will be initialized when showPixels is first called
|
||||
}
|
||||
|
||||
/// Output pixels to the LED strip
|
||||
/// Converts FastLED pixel data to Adafruit format and displays
|
||||
/// @param pixels the pixel controller containing LED data
|
||||
virtual void showPixels(PixelController<RGB_ORDER> &pixels) override {
|
||||
// Initialize driver if needed
|
||||
if (!mDriver) {
|
||||
mDriver = fl::IAdafruitNeoPixelDriver::create();
|
||||
mDriver->init(DATA_PIN);
|
||||
}
|
||||
// Convert to PixelIterator and send to driver
|
||||
auto pixelIterator = pixels.as_iterator(this->getRgbw());
|
||||
mDriver->showPixels(pixelIterator);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Get the driver instance (for derived classes)
|
||||
fl::IAdafruitNeoPixelDriver& getDriver() {
|
||||
return *mDriver;
|
||||
}
|
||||
};
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,37 @@
|
||||
/// @file clockless.cpp
|
||||
/// Implementation of IAdafruitNeoPixelDriver
|
||||
///
|
||||
/// This file contains the actual Adafruit_NeoPixel integration, keeping the
|
||||
/// dependency isolated from header files to avoid PlatformIO LDF issues.
|
||||
|
||||
|
||||
#include "platforms/adafruit/driver.h"
|
||||
#include "fl/unused.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// Concrete implementation of IAdafruitNeoPixelDriver
|
||||
class AdafruitNeoPixelDriverFake : public IAdafruitNeoPixelDriver {
|
||||
public:
|
||||
AdafruitNeoPixelDriverFake() {}
|
||||
|
||||
~AdafruitNeoPixelDriverFake() override = default;
|
||||
|
||||
void init(int dataPin) override {
|
||||
FL_UNUSED(dataPin);
|
||||
FL_WARN("Please install adafruit neopixel package to use this api bridge.");
|
||||
}
|
||||
|
||||
void showPixels(PixelIterator& pixelIterator) override {
|
||||
FL_UNUSED(pixelIterator);
|
||||
FL_WARN("Please install adafruit neopixel package to use this api bridge.");
|
||||
}
|
||||
};
|
||||
|
||||
// Static factory method implementation
|
||||
fl::unique_ptr<IAdafruitNeoPixelDriver> IAdafruitNeoPixelDriver::create() {
|
||||
return fl::unique_ptr<IAdafruitNeoPixelDriver>(new AdafruitNeoPixelDriverFake());
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,93 @@
|
||||
/// @file clockless.cpp
|
||||
/// Implementation of IAdafruitNeoPixelDriver
|
||||
///
|
||||
/// This file contains the actual Adafruit_NeoPixel integration, keeping the
|
||||
/// dependency isolated from header files to avoid PlatformIO LDF issues.
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
|
||||
|
||||
#include "fl/unique_ptr.h"
|
||||
#include "fl/memory.h"
|
||||
#include "pixel_iterator.h"
|
||||
#include "platforms/adafruit/driver.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// Concrete implementation of IAdafruitNeoPixelDriver
|
||||
class AdafruitNeoPixelDriverImpl : public IAdafruitNeoPixelDriver {
|
||||
private:
|
||||
unique_ptr<Adafruit_NeoPixel> mNeoPixel;
|
||||
bool mInitialized;
|
||||
int mDataPin;
|
||||
|
||||
public:
|
||||
AdafruitNeoPixelDriverImpl()
|
||||
: mNeoPixel(nullptr), mInitialized(false), mDataPin(-1) {}
|
||||
|
||||
~AdafruitNeoPixelDriverImpl() override = default;
|
||||
|
||||
void init(int dataPin) override {
|
||||
if (!mInitialized) {
|
||||
mDataPin = dataPin;
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void showPixels(PixelIterator& pixelIterator) override {
|
||||
if (!mInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get pixel count from iterator
|
||||
int numPixels = pixelIterator.size();
|
||||
auto rgbw = pixelIterator.get_rgbw();
|
||||
|
||||
// Determine the appropriate NeoPixel type based on RGBW mode
|
||||
uint16_t neoPixelType = NEO_KHZ800;
|
||||
if (rgbw.active()) {
|
||||
neoPixelType |= NEO_RGBW; // RGBW mode
|
||||
} else {
|
||||
neoPixelType |= NEO_RGB; // RGB mode
|
||||
}
|
||||
|
||||
// Create or recreate NeoPixel instance if needed
|
||||
if (!mNeoPixel || mNeoPixel->numPixels() != numPixels) {
|
||||
if (mNeoPixel) {
|
||||
mNeoPixel.reset();
|
||||
}
|
||||
mNeoPixel = fl::make_unique<Adafruit_NeoPixel>(
|
||||
numPixels, mDataPin, neoPixelType);
|
||||
mNeoPixel->begin();
|
||||
}
|
||||
|
||||
// Convert pixel data using PixelIterator and send to Adafruit_NeoPixel
|
||||
if (rgbw.active()) {
|
||||
// RGBW mode
|
||||
for (int i = 0; pixelIterator.has(1); ++i) {
|
||||
fl::u8 r, g, b, w;
|
||||
pixelIterator.loadAndScaleRGBW(&r, &g, &b, &w);
|
||||
mNeoPixel->setPixelColor(i, r, g, b, w);
|
||||
pixelIterator.advanceData();
|
||||
}
|
||||
} else {
|
||||
// RGB mode
|
||||
for (int i = 0; pixelIterator.has(1); ++i) {
|
||||
fl::u8 r, g, b;
|
||||
pixelIterator.loadAndScaleRGB(&r, &g, &b);
|
||||
mNeoPixel->setPixelColor(i, r, g, b);
|
||||
pixelIterator.advanceData();
|
||||
}
|
||||
}
|
||||
|
||||
// Output to LEDs
|
||||
mNeoPixel->show();
|
||||
}
|
||||
};
|
||||
|
||||
// Static factory method implementation
|
||||
fl::unique_ptr<IAdafruitNeoPixelDriver> IAdafruitNeoPixelDriver::create() {
|
||||
return fl::make_unique<AdafruitNeoPixelDriverImpl>();
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#include "fl/unique_ptr.h"
|
||||
#include "pixel_iterator.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
class PixelIterator;
|
||||
|
||||
/// Interface for Adafruit NeoPixel driver - implementation in clockless.cpp
|
||||
class IAdafruitNeoPixelDriver {
|
||||
public:
|
||||
/// Static factory method to create driver implementation
|
||||
static unique_ptr<IAdafruitNeoPixelDriver> create();
|
||||
|
||||
virtual ~IAdafruitNeoPixelDriver() = default;
|
||||
|
||||
/// Initialize the driver with data pin and RGBW mode
|
||||
virtual void init(int dataPin) = 0;
|
||||
|
||||
/// Output pixels to the LED strip
|
||||
virtual void showPixels(PixelIterator& pixelIterator) = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,26 @@
|
||||
# FastLED Platform: apollo3
|
||||
|
||||
Ambiq Apollo3 platform support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_apollo3.h`: Aggregator; includes `fastpin_apollo3.h`, `fastspi_apollo3.h`, `clockless_apollo3.h`.
|
||||
- `fastpin_apollo3.h`: Pin helpers using Ambiq fastgpio; board‑specific `(pin,pad)` mappings (e.g., SFE Edge/Thing Plus/ATP, Artemis Nano, LoRa Thing Plus expLoRaBLE). Defines `HAS_HARDWARE_PIN_SUPPORT` when applicable.
|
||||
- `fastspi_apollo3.h`: Bit‑banged SPI via fastgpio (all pins usable). Provides `APOLLO3HardwareSPIOutput` with `writeBytes`, `writePixels`, and bit‑level toggling.
|
||||
- `clockless_apollo3.h`: Clockless WS281x controller using SysTick for timing on Apollo3 Blue; sets `FASTLED_HAS_CLOCKLESS`.
|
||||
|
||||
## Supported boards and toolchains
|
||||
|
||||
Known‑good Arduino cores/boards:
|
||||
|
||||
- SparkFun Apollo3 (Artemis) core and boards: Edge, Thing Plus, ATP, Artemis Nano, LoRa Thing Plus (expLoRaBLE)
|
||||
- Ambiq SDK‑based environments via the SparkFun core
|
||||
|
||||
Toolchain notes:
|
||||
|
||||
- Uses Ambiq's fastgpio for high‑rate toggling where available; otherwise falls back to standard GPIO which is significantly slower.
|
||||
- Ensure your core exposes `am_hal_gpio_*` APIs used by `fastpin_apollo3.h`. When missing, hardware pin specialization will be disabled.
|
||||
|
||||
Timing caveats:
|
||||
|
||||
- `clockless_apollo3.h` relies on SysTick for delay loops; large interrupt windows can corrupt WS281x signaling. Prefer short ISRs during frame output.
|
||||
- For best stability, keep Wi‑Fi/BLE stacks idle during `show()` and avoid heavy serial printing.
|
||||
@@ -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 = 280>
|
||||
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
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
void apollo3_compile_tests() {
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "FASTLED_USE_PROGMEM should be 0 for Apollo3"
|
||||
#endif
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 1
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 1 for Apollo3"
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1
|
||||
#error "FASTLED_ALLOW_INTERRUPTS should be 1 for Apollo3"
|
||||
#endif
|
||||
|
||||
#ifndef F_CPU
|
||||
#error "F_CPU should be defined for Apollo3"
|
||||
#endif
|
||||
|
||||
// Check that Apollo3-specific features are available
|
||||
#ifndef APOLLO3
|
||||
#warning "APOLLO3 macro not defined - this may indicate platform detection issues"
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,173 @@
|
||||
#ifndef __INC_FASTPIN_APOLLO3_H
|
||||
#define __INC_FASTPIN_APOLLO3_H
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include "fl/namespace.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) || defined(ARDUINO_APOLLO3_SFE_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) || defined(ARDUINO_APOLLO3_SFE_ARTEMIS_ATP)
|
||||
|
||||
#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
|
||||
|
||||
#elif defined(ARDUINO_LoRa_THING_PLUS_expLoRaBLE) || defined(ARDUINO_AM_AP3_THING_PLUS_expLoRaBLE)
|
||||
|
||||
#define MAX_PIN 47
|
||||
|
||||
// Provided by the community:
|
||||
// https://www.reddit.com/r/FastLED/comments/1i30ycy/ambiq_apollo3_commit_specifically_the_spe_lora/
|
||||
// Special Thanks to reddit.com/u/Aromatic-Effort-9414 for providing these pin definitions
|
||||
_FL_DEFPIN(0, 19); _FL_DEFPIN(1, 18); _FL_DEFPIN(2, 41); _FL_DEFPIN(3, 31); _FL_DEFPIN(4, 10);
|
||||
_FL_DEFPIN(5, 30); _FL_DEFPIN(6, 37); _FL_DEFPIN(7, 24); _FL_DEFPIN(8, 46); _FL_DEFPIN(9, 33);
|
||||
_FL_DEFPIN(10, 4); _FL_DEFPIN(11, 28); _FL_DEFPIN(12, 25); _FL_DEFPIN(13, 27); _FL_DEFPIN(14, 6);
|
||||
_FL_DEFPIN(15, 5); _FL_DEFPIN(16, 9); _FL_DEFPIN(17, 8); _FL_DEFPIN(18, 26); _FL_DEFPIN(19, 13);
|
||||
_FL_DEFPIN(20, 12); _FL_DEFPIN(21, 32); _FL_DEFPIN(22, 35); _FL_DEFPIN(23, 34); _FL_DEFPIN(24, 11);
|
||||
_FL_DEFPIN(25, 36); _FL_DEFPIN(26, 38); _FL_DEFPIN(27, 39); _FL_DEFPIN(28, 40); _FL_DEFPIN(29, 42);
|
||||
_FL_DEFPIN(30, 43); _FL_DEFPIN(31, 44); _FL_DEFPIN(32, 47);
|
||||
|
||||
#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
|
||||
@@ -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, void* context = NULL) {
|
||||
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
|
||||
@@ -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
|
||||
@@ -0,0 +1,235 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/audio_input.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/assert.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/span.h"
|
||||
#include "fl/shared_ptr.h"
|
||||
#include "fl/has_include.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
#if !FL_HAS_INCLUDE(<Arduino.h>)
|
||||
#error "This implementation requires Arduino.h - compile with Arduino framework"
|
||||
#endif
|
||||
|
||||
|
||||
#include <Arduino.h> // ok include
|
||||
|
||||
// Check for Arduino I2S library availability and completeness
|
||||
#if defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__)
|
||||
#define ARDUINO_I2S_FULLY_SUPPORTED 0
|
||||
#define ARDUINO_I2S_BROKEN_REASON "I2S not supported on SAMD21"
|
||||
#elif FL_HAS_INCLUDE(<I2S.h>)
|
||||
#include <I2S.h>
|
||||
|
||||
// Define ARDUINO_I2S_FULLY_SUPPORTED only when ALL I2S components are present and functional
|
||||
#if defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA) || \
|
||||
defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RENESAS_UNO) || \
|
||||
defined(_RENESAS_RA_) || defined(ARDUINO_FSP)
|
||||
// Known broken: Renesas RA platforms with incomplete FSP I2S support
|
||||
#define ARDUINO_I2S_FULLY_SUPPORTED 0
|
||||
#define ARDUINO_I2S_BROKEN_REASON "Renesas FSP missing r_i2s_api.h header"
|
||||
#elif !defined(I2S_PHILIPS_MODE) || !defined(I2S_LEFT_JUSTIFIED_MODE) || !defined(I2S_RIGHT_JUSTIFIED_MODE)
|
||||
// Missing essential I2S mode constants - incomplete library implementation
|
||||
#define ARDUINO_I2S_FULLY_SUPPORTED 0
|
||||
#define ARDUINO_I2S_BROKEN_REASON "Missing I2S mode constants (incomplete library)"
|
||||
#else
|
||||
// All required I2S components are present and platform is not known-broken
|
||||
#define ARDUINO_I2S_FULLY_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#else
|
||||
// No I2S.h header found - cannot support I2S audio input
|
||||
#define ARDUINO_I2S_FULLY_SUPPORTED 0
|
||||
#define ARDUINO_I2S_BROKEN_REASON "I2S.h header not available"
|
||||
#endif
|
||||
|
||||
// Legacy compatibility define
|
||||
#define ARDUINO_I2S_SUPPORTED ARDUINO_I2S_FULLY_SUPPORTED
|
||||
|
||||
namespace fl {
|
||||
|
||||
#if ARDUINO_I2S_FULLY_SUPPORTED
|
||||
|
||||
class Arduino_I2S_Audio : public IAudioInput {
|
||||
public:
|
||||
Arduino_I2S_Audio(const AudioConfigI2S &config)
|
||||
: mConfig(config), mHasError(false), mTotalSamplesRead(0), mInitialized(false) {}
|
||||
|
||||
~Arduino_I2S_Audio() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void start() override {
|
||||
if (mInitialized) {
|
||||
FL_WARN("Arduino I2S is already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize Arduino I2S library using standard API
|
||||
// WARNING: Arduino I2S uses board-specific pins, not the pins from mConfig!
|
||||
// For Arduino Zero: WS=0, CLK=1, SD=9
|
||||
// For MKR boards: WS=3, CLK=2, SD=A6
|
||||
// Pin configuration in mConfig is ignored on Arduino platforms
|
||||
int i2s_mode = convertCommFormatToMode(mConfig.mCommFormat);
|
||||
bool success = I2S.begin(
|
||||
i2s_mode,
|
||||
static_cast<long>(mConfig.mSampleRate),
|
||||
static_cast<int>(mConfig.mBitResolution)
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
mHasError = true;
|
||||
mErrorMessage = "Failed to initialize Arduino I2S";
|
||||
FL_WARN(mErrorMessage.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
mTotalSamplesRead = 0;
|
||||
FL_WARN("Arduino I2S audio input started successfully");
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
if (!mInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
I2S.end();
|
||||
mInitialized = false;
|
||||
mTotalSamplesRead = 0;
|
||||
FL_WARN("Arduino I2S audio input stopped");
|
||||
}
|
||||
|
||||
bool error(fl::string *msg = nullptr) override {
|
||||
if (msg && mHasError) {
|
||||
*msg = mErrorMessage;
|
||||
}
|
||||
return mHasError;
|
||||
}
|
||||
|
||||
AudioSample read() override {
|
||||
if (!mInitialized) {
|
||||
FL_WARN("Arduino I2S is not initialized");
|
||||
return AudioSample(); // Invalid sample
|
||||
}
|
||||
|
||||
fl::i16 buffer[I2S_AUDIO_BUFFER_LEN];
|
||||
size_t samples_read = 0;
|
||||
|
||||
// Read samples using standard Arduino I2S API
|
||||
int available = I2S.available();
|
||||
if (available <= 0) {
|
||||
return AudioSample(); // No data available
|
||||
}
|
||||
|
||||
size_t bytes_to_read = fl::min(static_cast<size_t>(available),
|
||||
sizeof(buffer));
|
||||
|
||||
int bytes_read = I2S.read(buffer, bytes_to_read);
|
||||
if (bytes_read <= 0) {
|
||||
return AudioSample(); // No data read
|
||||
}
|
||||
|
||||
samples_read = bytes_read / sizeof(fl::i16);
|
||||
|
||||
if (samples_read == 0) {
|
||||
return AudioSample(); // No samples read
|
||||
}
|
||||
|
||||
// Handle channel selection for mono output
|
||||
if (mConfig.mAudioChannel == AudioChannel::Left || mConfig.mAudioChannel == AudioChannel::Right) {
|
||||
// For stereo input with single channel selection, extract the desired channel
|
||||
size_t mono_samples = samples_read / 2;
|
||||
size_t channel_offset = (mConfig.mAudioChannel == AudioChannel::Right) ? 1 : 0;
|
||||
|
||||
for (size_t i = 0; i < mono_samples; i++) {
|
||||
buffer[i] = buffer[i * 2 + channel_offset];
|
||||
}
|
||||
samples_read = mono_samples;
|
||||
}
|
||||
|
||||
// Apply inversion if requested
|
||||
if (mConfig.mInvert) {
|
||||
for (size_t i = 0; i < samples_read; i++) {
|
||||
buffer[i] = -buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate timestamp based on sample rate and total samples read
|
||||
fl::u32 timestamp_ms = static_cast<fl::u32>((mTotalSamplesRead * 1000ULL) / mConfig.mSampleRate);
|
||||
|
||||
// Update total samples counter
|
||||
mTotalSamplesRead += samples_read;
|
||||
|
||||
fl::span<const fl::i16> data(buffer, samples_read);
|
||||
return AudioSample(data, timestamp_ms);
|
||||
}
|
||||
|
||||
private:
|
||||
AudioConfigI2S mConfig;
|
||||
bool mHasError;
|
||||
fl::string mErrorMessage;
|
||||
bool mInitialized;
|
||||
fl::u64 mTotalSamplesRead;
|
||||
|
||||
int convertCommFormatToMode(I2SCommFormat format) {
|
||||
// Map FastLED I2S formats to Arduino I2S modes
|
||||
switch (format) {
|
||||
case I2SCommFormat::Philips:
|
||||
return I2S_PHILIPS_MODE;
|
||||
case I2SCommFormat::MSB:
|
||||
return I2S_LEFT_JUSTIFIED_MODE;
|
||||
case I2SCommFormat::PCMShort:
|
||||
case I2SCommFormat::PCMLong:
|
||||
return I2S_RIGHT_JUSTIFIED_MODE;
|
||||
default:
|
||||
return I2S_PHILIPS_MODE; // Default to Philips standard
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Platform-specific audio input creation function for Arduino
|
||||
fl::shared_ptr<IAudioInput> arduino_create_audio_input(const AudioConfig& config, fl::string* error_message = nullptr) {
|
||||
if (config.is<AudioConfigI2S>()) {
|
||||
FL_WARN("Creating Arduino I2S audio source");
|
||||
AudioConfigI2S i2s_config = config.get<AudioConfigI2S>();
|
||||
return fl::make_shared<Arduino_I2S_Audio>(i2s_config);
|
||||
} else if (config.is<AudioConfigPdm>()) {
|
||||
const char* ERROR_MESSAGE = "PDM audio not supported in Arduino I2S implementation";
|
||||
FL_WARN(ERROR_MESSAGE);
|
||||
if (error_message) {
|
||||
*error_message = ERROR_MESSAGE;
|
||||
}
|
||||
return fl::shared_ptr<IAudioInput>(); // Return null
|
||||
}
|
||||
|
||||
const char* ERROR_MESSAGE = "Unsupported audio configuration for Arduino";
|
||||
FL_WARN(ERROR_MESSAGE);
|
||||
if (error_message) {
|
||||
*error_message = ERROR_MESSAGE;
|
||||
}
|
||||
return fl::shared_ptr<IAudioInput>(); // Return null
|
||||
}
|
||||
|
||||
#else // !ARDUINO_I2S_FULLY_SUPPORTED
|
||||
|
||||
// Null audio implementation fallback for platforms without complete I2S support
|
||||
fl::shared_ptr<IAudioInput> arduino_create_audio_input(const AudioConfig& config, fl::string* error_message = nullptr) {
|
||||
FL_UNUSED(config);
|
||||
#ifdef ARDUINO_I2S_BROKEN_REASON
|
||||
const char* ERROR_MESSAGE = "Arduino I2S not supported: " ARDUINO_I2S_BROKEN_REASON;
|
||||
#else
|
||||
const char* ERROR_MESSAGE = "Arduino I2S library not available - please install I2S library";
|
||||
#endif
|
||||
FL_WARN(ERROR_MESSAGE);
|
||||
if (error_message) {
|
||||
*error_message = ERROR_MESSAGE;
|
||||
}
|
||||
return fl::shared_ptr<IAudioInput>(); // Return null
|
||||
}
|
||||
|
||||
#endif // ARDUINO_I2S_FULLY_SUPPORTED
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,35 @@
|
||||
# FastLED Platform: arm
|
||||
|
||||
ARM support for a wide range of MCUs (Teensy 3.x/4.x, SAMD21/SAMD51, RP2040, STM32, Renesas UNO R4, nRF5x, etc.).
|
||||
|
||||
## Top-level files (quick pass)
|
||||
- **arm_compile.hpp**: Hierarchical include hook for ARM (e.g., Teensy 4.x `__IMXRT1062__`). Source inclusion is handled by CMake globs.
|
||||
- **compile_test.hpp**: Compile-time assertions for platform flags on ARM families (PROGMEM usage, interrupts policy, F_CPU, memory hints).
|
||||
- **int.h**: ARM-friendly integer and pointer typedefs in `fl::`.
|
||||
|
||||
## Subplatform directories
|
||||
- **common/**: Shared helpers for Cortex-M0/M0+; `m0clockless.h` provides SysTick fallback and clockless ASM macros used by SAMD21, etc.
|
||||
- **d21/**: SAMD21 family (e.g., Arduino Zero-class). Includes `clockless_arm_d21.h` and pin helpers.
|
||||
- **d51/**: SAMD51 family (e.g., Feather M4, ItsyBitsy M4, Wio Terminal). Clockless + SPI core integration.
|
||||
- **giga/**: Arduino GIGA R1 (STM32H747). LED sysdefs and clockless/SPI wiring for this target.
|
||||
- **k20/**: Teensy 3.x (MK20DX). Clockless + block clockless, `fastspi_arm_k20.h`, and integrations: `octows2811_controller.h`, `ws2812serial_controller.h`, `smartmatrix_t3.h`.
|
||||
- **k66/**: Teensy 3.6 (K66). Similar to k20 with clockless and block variants; reuses some k20 controllers.
|
||||
- **kl26/**: Teensy LC (KL26Z64). Clockless and `ws2812serial_controller` support.
|
||||
- **mxrt1062/**: Teensy 4.x (i.MX RT1062). Clockless and block clockless, plus OctoWS2811 and SmartMatrix integrations.
|
||||
- **nrf51/**: Nordic nRF51. Clockless and pin/SPI wiring for this series.
|
||||
- **nrf52/**: Nordic nRF52. Adds `arbiter_nrf52.h` (PWM resource arbitration), clockless, pin/SPI, and sysdefs.
|
||||
- **renesas/**: Renesas RA4M1 (Arduino UNO R4). Clockless + SPI core integration and sysdefs.
|
||||
- **rp2040/**: Raspberry Pi Pico (RP2040). Clockless via PIO; `pio_gen.h` builds a PIO program from T1/T2/T3 at runtime.
|
||||
- **sam/**: Arduino Due (SAM3X). Clockless and block clockless, pin/SPI wiring for SAM.
|
||||
- **stm32/**: STM32 (e.g., F1). Pin helpers and clockless driver for STM32 family.
|
||||
|
||||
## Quick guidance per subplatform
|
||||
|
||||
- d21 (SAMD21): `FASTLED_USE_PROGMEM=0`; clockless via `arm/common/m0clockless.h`. Keep ISRs short; SysTick‑based timing.
|
||||
- d51 (SAMD51): Higher clocks; enable interrupts cautiously. Prefer minimal critical sections.
|
||||
- k20/k66 (Teensy 3.x): DWT cycle counter timing; long ISRs can force retries. OctoWS2811/SmartMatrix available.
|
||||
- mxrt1062 (Teensy 4.x): Very high frequency; mind DWT and interrupt thresholds. Parallel output offload recommended for large installations.
|
||||
- rp2040: PIO‑driven; ensure T1/T2/T3 match LED timing; regenerate PIO program when timings change.
|
||||
- nrf51/nrf52: Budget interrupt windows conservatively. On nrf52, arbitrate PWM instances via `arbiter_nrf52.h`.
|
||||
- renesas (UNO R4): Ensure sysdefs set `FASTLED_USE_PROGMEM=0`; verify cli/sei equivalents and F_CPU.
|
||||
- stm32/giga: Use ARM IRQ wrappers in sysdefs; direct register access varies by core.
|
||||
@@ -0,0 +1,12 @@
|
||||
// Hierarchical include file for platforms/arm/ directory
|
||||
#pragma once
|
||||
|
||||
#ifdef FASTLED_ALL_SRC
|
||||
|
||||
#if defined(__IMXRT1062__)
|
||||
// ARM PLATFORM IMPLEMENTATIONS (Teensy 4.x only)
|
||||
// Note: .cpp files are now automatically included via CMake globbing
|
||||
// No manual includes needed
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_ALL_SRC
|
||||
@@ -0,0 +1,8 @@
|
||||
# FastLED Platform: arm/common
|
||||
|
||||
Shared helpers for ARM Cortex-M0/M0+ clockless output.
|
||||
|
||||
- `m0clockless.h`: SysTick fallback and macro/ASM infrastructure used by M0/M0+ clockless drivers (e.g., SAMD21). Provides delay macros, bit-write helpers, and frame loop structure for tight WS281x timing.
|
||||
|
||||
Notes:
|
||||
- Designed to be included by target-specific clockless headers (e.g., `clockless_arm_d21.h`).
|
||||
@@ -0,0 +1,423 @@
|
||||
#ifndef __INC_M0_CLOCKLESS_H
|
||||
#define __INC_M0_CLOCKLESS_H
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Some platforms have a missing definition for SysTick, in that
|
||||
// case fill that in now.
|
||||
// BEGIN SysTick DEFINITION
|
||||
#ifndef SysTick
|
||||
// Define the SysTick base address
|
||||
#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */
|
||||
#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */
|
||||
#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */
|
||||
|
||||
|
||||
// Define the SysTick structure
|
||||
typedef struct {
|
||||
volatile uint32_t CTRL;
|
||||
volatile uint32_t LOAD;
|
||||
volatile uint32_t VAL;
|
||||
volatile const uint32_t CALIB;
|
||||
} SysTick_Type;
|
||||
|
||||
#endif
|
||||
// END SysTick DEFINITION
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct M0ClocklessData {
|
||||
uint8_t d[3];
|
||||
uint8_t e[3];
|
||||
uint8_t adj;
|
||||
uint8_t pad;
|
||||
uint32_t s[3];
|
||||
};
|
||||
|
||||
|
||||
template<int HI_OFFSET, int LO_OFFSET, int T1, int T2, int T3, EOrder RGB_ORDER, int WAIT_TIME>int
|
||||
showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, uint32_t num_leds, struct M0ClocklessData *pData) {
|
||||
// Lo register variables
|
||||
FASTLED_REGISTER uint32_t scratch=0;
|
||||
FASTLED_REGISTER struct M0ClocklessData *base = pData;
|
||||
FASTLED_REGISTER volatile uint32_t *port = _port;
|
||||
FASTLED_REGISTER uint32_t d=0;
|
||||
FASTLED_REGISTER uint32_t counter=num_leds;
|
||||
FASTLED_REGISTER uint32_t bn=0;
|
||||
FASTLED_REGISTER uint32_t b=0;
|
||||
FASTLED_REGISTER uint32_t bitmask = _bitmask;
|
||||
|
||||
// high register variable
|
||||
FASTLED_REGISTER const uint8_t *leds = _leds;
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
++pData->s[0];
|
||||
++pData->s[1];
|
||||
++pData->s[2];
|
||||
#endif
|
||||
asm __volatile__ (
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// asm macro definitions - used to assemble the clockless output
|
||||
//
|
||||
".ifnotdef fl_delay_def;"
|
||||
#ifdef FASTLED_ARM_M0_PLUS
|
||||
" .set fl_is_m0p, 1;"
|
||||
" .macro m0pad;"
|
||||
" nop;"
|
||||
" .endm;"
|
||||
#else
|
||||
" .set fl_is_m0p, 0;"
|
||||
" .macro m0pad;"
|
||||
" .endm;"
|
||||
#endif
|
||||
" .set fl_delay_def, 1;"
|
||||
" .set fl_delay_mod, 4;"
|
||||
" .if fl_is_m0p == 1;"
|
||||
" .set fl_delay_mod, 3;"
|
||||
" .endif;"
|
||||
" .macro fl_delay dtime, reg=r0;"
|
||||
" .if (\\dtime > 0);"
|
||||
" .set dcycle, (\\dtime / fl_delay_mod);"
|
||||
" .set dwork, (dcycle * fl_delay_mod);"
|
||||
" .set drem, (\\dtime - dwork);"
|
||||
" .rept (drem);"
|
||||
" nop;"
|
||||
" .endr;"
|
||||
" .if dcycle > 0;"
|
||||
" mov \\reg, #dcycle;"
|
||||
" delayloop_\\@:;"
|
||||
" sub \\reg, #1;"
|
||||
" bne delayloop_\\@;"
|
||||
" .if fl_is_m0p == 0;"
|
||||
" nop;"
|
||||
" .endif;"
|
||||
" .endif;"
|
||||
" .endif;"
|
||||
" .endm;"
|
||||
|
||||
" .macro mod_delay dtime,b1,b2,reg;"
|
||||
" .set adj, (\\b1 + \\b2);"
|
||||
" .if adj < \\dtime;"
|
||||
" .set dtime2, (\\dtime - adj);"
|
||||
" fl_delay dtime2, \\reg;"
|
||||
" .endif;"
|
||||
" .endm;"
|
||||
|
||||
// check the bit and drop the line low if it isn't set
|
||||
" .macro qlo4 b,bitmask,port,loff ;"
|
||||
" lsl \\b, #1 ;"
|
||||
" bcs skip_\\@ ;"
|
||||
" str \\bitmask, [\\port, \\loff] ;"
|
||||
" skip_\\@: ;"
|
||||
" m0pad;"
|
||||
" .endm ;"
|
||||
|
||||
// set the pin hi or low (determined by the offset passed in )
|
||||
" .macro qset2 bitmask,port,loff;"
|
||||
" str \\bitmask, [\\port, \\loff];"
|
||||
" m0pad;"
|
||||
" .endm;"
|
||||
|
||||
// Load up the next led byte to work with, put it in bn
|
||||
" .macro loadleds3 leds, bn, rled, scratch;"
|
||||
" mov \\scratch, \\leds;"
|
||||
" ldrb \\bn, [\\scratch, \\rled];"
|
||||
" .endm;"
|
||||
|
||||
// check whether or not we should dither
|
||||
" .macro loaddither7 bn,d,base,rdither;"
|
||||
" ldrb \\d, [\\base, \\rdither];"
|
||||
" lsl \\d, #24;" //; shift high for the qadd w/bn
|
||||
" lsl \\bn, #24;" //; shift high for the qadd w/d
|
||||
" bne chkskip_\\@;" //; if bn==0, clear d;"
|
||||
" eor \\d, \\d;" //; clear d;"
|
||||
" m0pad;"
|
||||
" chkskip_\\@:;"
|
||||
" .endm;"
|
||||
|
||||
// Do the qadd8 for dithering -- there's two versions of this. The m0 version
|
||||
// takes advantage of the 3 cycle branch to do two things after the branch,
|
||||
// while keeping timing constant. The m0+, however, branches in 2 cycles, so
|
||||
// we have to work around that a bit more. This is one of the few times
|
||||
// where the m0 will actually be _more_ efficient than the m0+
|
||||
" .macro dither5 bn,d;"
|
||||
" .syntax unified;"
|
||||
" .if fl_is_m0p == 0;"
|
||||
" adds \\bn, \\d;" // do the add
|
||||
" bcc dither5_1_\\@;"
|
||||
" mvns \\bn, \\bn;" // set the low 24bits ot 1's
|
||||
" lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
|
||||
" dither5_1_\\@:;"
|
||||
" nop;" // nop to keep timing in line
|
||||
" .else;"
|
||||
" adds \\bn, \\d;" // do the add"
|
||||
" bcc dither5_2_\\@;"
|
||||
" mvns \\bn, \\bn;" // set the low 24bits ot 1's
|
||||
" dither5_2_\\@:;"
|
||||
" bcc dither5_3_\\@;"
|
||||
" lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
|
||||
" dither5_3_\\@:;"
|
||||
" .endif;"
|
||||
" .syntax divided;"
|
||||
" .endm;"
|
||||
|
||||
// Do our scaling
|
||||
" .macro scale4 bn, base, scale, scratch;"
|
||||
" ldr \\scratch, [\\base, \\scale];"
|
||||
" lsr \\bn, \\bn, #24;" // bring bn back down to its low 8 bits
|
||||
" mul \\bn, \\scratch;" // do the multiply
|
||||
" .endm;"
|
||||
|
||||
// swap bn into b
|
||||
" .macro swapbbn1 b,bn;"
|
||||
" lsl \\b, \\bn, #16;" // put the 8 bits we want for output high
|
||||
" .endm;"
|
||||
|
||||
// adjust the dithering value for the next time around (load e from memory
|
||||
// to do the math)
|
||||
" .macro adjdither7 base,d,rled,eoffset,scratch;"
|
||||
" ldrb \\d, [\\base, \\rled];"
|
||||
" ldrb \\scratch,[\\base,\\eoffset];" // load e
|
||||
" .syntax unified;"
|
||||
" subs \\d, \\scratch, \\d;" // d=e-d
|
||||
" .syntax divided;"
|
||||
" strb \\d, [\\base, \\rled];" // save d
|
||||
" .endm;"
|
||||
|
||||
// increment the led pointer (base+6 has what we're incrementing by)
|
||||
" .macro incleds3 leds, base, scratch;"
|
||||
" ldrb \\scratch, [\\base, #6];" // load incremen
|
||||
" add \\leds, \\leds, \\scratch;" // update leds pointer
|
||||
" .endm;"
|
||||
|
||||
// compare and loop
|
||||
" .macro cmploop5 counter,label;"
|
||||
" .syntax unified;"
|
||||
" subs \\counter, #1;"
|
||||
" .syntax divided;"
|
||||
" beq done_\\@;"
|
||||
" m0pad;"
|
||||
" b \\label;"
|
||||
" done_\\@:;"
|
||||
" .endm;"
|
||||
|
||||
" .endif;"
|
||||
);
|
||||
|
||||
#define M0_ASM_ARGS : \
|
||||
[leds] "+h" (leds), \
|
||||
[counter] "+l" (counter), \
|
||||
[scratch] "+l" (scratch), \
|
||||
[d] "+l" (d), \
|
||||
[bn] "+l" (bn), \
|
||||
[b] "+l" (b) \
|
||||
: \
|
||||
[port] "l" (port), \
|
||||
[base] "l" (base), \
|
||||
[bitmask] "l" (bitmask), \
|
||||
[hi_off] "I" (HI_OFFSET), \
|
||||
[lo_off] "I" (LO_OFFSET), \
|
||||
[led0] "I" (RO(0)), \
|
||||
[led1] "I" (RO(1)), \
|
||||
[led2] "I" (RO(2)), \
|
||||
[e0] "I" (3+RO(0)), \
|
||||
[e1] "I" (3+RO(1)), \
|
||||
[e2] "I" (3+RO(2)), \
|
||||
[scale0] "I" (4*(2+RO(0))), \
|
||||
[scale1] "I" (4*(2+RO(1))), \
|
||||
[scale2] "I" (4*(2+RO(2))), \
|
||||
[T1] "I" (T1), \
|
||||
[T2] "I" (T2), \
|
||||
[T3] "I" (T3) \
|
||||
:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// now for some convinience macros to make building our lines a bit cleaner
|
||||
#define LOOP " loop_%=:"
|
||||
#define HI2 " qset2 %[bitmask], %[port], %[hi_off];"
|
||||
#define _D1 " mod_delay %c[T1],2,0,%[scratch];"
|
||||
#define QLO4 " qlo4 %[b],%[bitmask],%[port], %[lo_off];"
|
||||
#define LOADLEDS3(X) " loadleds3 %[leds], %[bn], %[led" #X "] ,%[scratch];"
|
||||
#define _D2(ADJ) " mod_delay %c[T2],4," #ADJ ",%[scratch];"
|
||||
#define LO2 " qset2 %[bitmask], %[port], %[lo_off];"
|
||||
#define _D3(ADJ) " mod_delay %c[T3],2," #ADJ ",%[scratch];"
|
||||
#define LOADDITHER7(X) " loaddither7 %[bn], %[d], %[base], %[led" #X "];"
|
||||
#define DITHER5 " dither5 %[bn], %[d];"
|
||||
#define SCALE4(X) " scale4 %[bn], %[base], %[scale" #X "], %[scratch];"
|
||||
#define SWAPBBN1 " swapbbn1 %[b], %[bn];"
|
||||
#define ADJDITHER7(X) " adjdither7 %[base],%[d],%[led" #X "],%[e" #X "],%[scratch];"
|
||||
#define INCLEDS3 " incleds3 %[leds],%[base],%[scratch];"
|
||||
#define CMPLOOP5 " cmploop5 %[counter], loop_%=;"
|
||||
#define NOTHING ""
|
||||
|
||||
#if (defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1))
|
||||
// We're allowing interrupts and have hardware timer support defined -
|
||||
// track the loop outside the asm code, to allow inserting the interrupt
|
||||
// overrun checks.
|
||||
asm __volatile__ (
|
||||
// pre-load byte 0
|
||||
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
|
||||
M0_ASM_ARGS);
|
||||
|
||||
do {
|
||||
asm __volatile__ (
|
||||
// Write out byte 0, prepping byte 1
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 1, prepping byte 2
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 2, prepping byte 0
|
||||
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5)
|
||||
|
||||
M0_ASM_ARGS
|
||||
);
|
||||
SEI_CHK; INNER_SEI; --counter; CLI_CHK;
|
||||
} while(counter);
|
||||
#elif (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
// We're allowing interrupts - track the loop outside the asm code, and
|
||||
// re-enable interrupts in between each iteration.
|
||||
asm __volatile__ (
|
||||
// pre-load byte 0
|
||||
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
|
||||
M0_ASM_ARGS);
|
||||
|
||||
do {
|
||||
asm __volatile__ (
|
||||
// Write out byte 0, prepping byte 1
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 1, prepping byte 2
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 2, prepping byte 0
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5)
|
||||
|
||||
M0_ASM_ARGS
|
||||
);
|
||||
|
||||
uint32_t ticksBeforeInterrupts = SysTick->VAL;
|
||||
sei();
|
||||
--counter;
|
||||
cli();
|
||||
|
||||
// If more than 45 uSecs have elapsed, give up on this frame and start over.
|
||||
// Note: this isn't completely correct. It's possible that more than one
|
||||
// millisecond will elapse, and so SysTick->VAL will lap
|
||||
// ticksBeforeInterrupts.
|
||||
// Note: ticksBeforeInterrupts DECREASES
|
||||
const uint32_t kTicksPerMs = VARIANT_MCK / 1000;
|
||||
const uint32_t kTicksPerUs = kTicksPerMs / 1000;
|
||||
const uint32_t kTicksIn45us = kTicksPerUs * 45;
|
||||
|
||||
const uint32_t currentTicks = SysTick->VAL;
|
||||
|
||||
if (ticksBeforeInterrupts < currentTicks) {
|
||||
// Timer started over
|
||||
if ((ticksBeforeInterrupts + (kTicksPerMs - currentTicks)) > kTicksIn45us) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if ((ticksBeforeInterrupts - currentTicks) > kTicksIn45us) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} while(counter);
|
||||
#else
|
||||
// We're not allowing interrupts - run the entire loop in asm to keep things
|
||||
// as tight as possible. In an ideal world, we should be pushing out ws281x
|
||||
// leds (or other 3-wire leds) with zero gaps between pixels.
|
||||
asm __volatile__ (
|
||||
// pre-load byte 0
|
||||
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
|
||||
|
||||
// loop over writing out the data
|
||||
LOOP
|
||||
// Write out byte 0, prepping byte 1
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 1, prepping byte 2
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 2, prepping byte 0
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) CMPLOOP5
|
||||
|
||||
M0_ASM_ARGS
|
||||
);
|
||||
#endif
|
||||
return num_leds;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
|
||||
namespace fl {
|
||||
static void arm_compile_tests() {
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM should be defined for ARM platforms"
|
||||
#endif
|
||||
|
||||
#if FASTLED_USE_PROGMEM != 0 && FASTLED_USE_PROGMEM != 1
|
||||
#error "FASTLED_USE_PROGMEM should be either 0 or 1 for ARM platforms"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_TEENSYLC) || defined(ARDUINO_TEENSY30) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(STM32F1)
|
||||
// Teensy LC, Teensy 3.0, Teensy 3.1/3.2, Renesas UNO, and STM32F1 have limited memory
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 0
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 0 for Teensy LC, Teensy 3.0, Teensy 3.1/3.2, Renesas UNO, and STM32F1"
|
||||
#endif
|
||||
#else
|
||||
// Most other ARM platforms have lots of memory
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 1
|
||||
#error "SKETCH_HAS_LOTS_OF_MEMORY should be 1 for most ARM platforms"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1 && FASTLED_ALLOW_INTERRUPTS != 0
|
||||
#error "FASTLED_ALLOW_INTERRUPTS should be either 0 or 1 for ARM platforms"
|
||||
#endif
|
||||
|
||||
// Check that F_CPU is defined
|
||||
#ifndef F_CPU
|
||||
#error "F_CPU should be defined for ARM platforms"
|
||||
#endif
|
||||
|
||||
// Specific ARM variant checks
|
||||
#if defined(ARDUINO_ARCH_STM32) || defined(STM32F1)
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 0
|
||||
#error "STM32 platforms should have FASTLED_ALLOW_INTERRUPTS set to 0"
|
||||
#endif
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "STM32 platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_RASPBERRY_PI_PICO)
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "RP2040 platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#if FASTLED_ALLOW_INTERRUPTS != 1
|
||||
#error "RP2040 platforms should have FASTLED_ALLOW_INTERRUPTS set to 1"
|
||||
#endif
|
||||
#ifdef FASTLED_FORCE_SOFTWARE_SPI
|
||||
// RP2040 forces software SPI - this is expected
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__)
|
||||
// Teensy platforms that use PROGMEM
|
||||
#if FASTLED_USE_PROGMEM != 1
|
||||
#error "Teensy K20/K66/MXRT1062 platforms should have FASTLED_USE_PROGMEM set to 1"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_SAM_DUE)
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "SAMD/SAM platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_SERIES) || defined(ARDUINO_ARCH_NRF52)
|
||||
#if FASTLED_USE_PROGMEM != 0
|
||||
#error "NRF52 platforms should have FASTLED_USE_PROGMEM set to 0"
|
||||
#endif
|
||||
#ifndef CLOCKLESS_FREQUENCY
|
||||
#error "NRF52 should have CLOCKLESS_FREQUENCY defined"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// STM32F1 specific compile-time size validation
|
||||
#if defined(STM32F1) || defined(__STM32F1__)
|
||||
// Static assert to ensure we're aware of memory constraints
|
||||
// STM32F103C8 has only 64KB flash and 20KB RAM
|
||||
static_assert(sizeof(void*) == 4, "STM32F1 should be 32-bit platform");
|
||||
|
||||
// Compile-time check for sketch memory usage awareness
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY != 0
|
||||
// This helps catch cases where large data structures might be used
|
||||
#pragma message "STM32F1 Warning: Large memory structures may not fit in 20KB RAM"
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,23 @@
|
||||
# FastLED Platform: ARM SAMD21 (d21)
|
||||
|
||||
SAMD21 (Arduino Zero-class) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_d21.h`: Aggregator for SAMD21; includes pin and clockless headers.
|
||||
- `fastpin_arm_d21.h`: Pin helpers for direct GPIO access on SAMD21.
|
||||
- `led_sysdefs_arm_d21.h`: SAMD21 system defines (interrupt policy, PROGMEM policy, etc.).
|
||||
- `clockless_arm_d21.h`: Clockless WS281x driver built on `arm/common/m0clockless.h` (SysTick + inline ASM macros).
|
||||
|
||||
Notes:
|
||||
- Clockless timing uses the M0/M0+ delay macros; ensure `F_CPU` and interrupt settings match board configuration.
|
||||
|
||||
### Compile-time expectations
|
||||
- `FASTLED_USE_PROGMEM`: 0 (per ARM SAMD guidance)
|
||||
- `FASTLED_ALLOW_INTERRUPTS`: driver tolerates short windows; long ISRs may force a retry/punt
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0`.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK` when `1`.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_D21
|
||||
#define __INC_CLOCKLESS_ARM_D21
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "eorder.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
#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 = 280>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
if (pixels.size() == 0) {
|
||||
return 1; // nonzero means success
|
||||
}
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mColorAdjustment.premixed[0];
|
||||
data.s[1] = pixels.mColorAdjustment.premixed[1];
|
||||
data.s[2] = pixels.mColorAdjustment.premixed[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
return showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_CLOCKLESS_ARM_D21
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef __INC_FASTLED_ARM_D21_H
|
||||
#define __INC_FASTLED_ARM_D21_H
|
||||
|
||||
#include "fastpin_arm_d21.h"
|
||||
#include "clockless_arm_d21.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,289 @@
|
||||
#ifndef __INC_FASTPIN_ARM_SAM_H
|
||||
#define __INC_FASTPIN_ARM_SAM_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
#if 0
|
||||
inline static void setOutput() {
|
||||
if(_BIT<8) {
|
||||
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
|
||||
} else {
|
||||
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
|
||||
}
|
||||
}
|
||||
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
#endif
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTSET.reg = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTCLR.reg = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUT.reg = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTTGL.reg = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUT.reg; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTSET.reg; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTCLR.reg; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE volatile PortGroup * r() { return T; } };
|
||||
|
||||
#define _FL_IO(L) _RD32(GPIO ## L)
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, L> {};
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS)
|
||||
|
||||
#define MAX_PIN 17
|
||||
_FL_DEFPIN( 8,23,1);
|
||||
_FL_DEFPIN( 0, 9,1); _FL_DEFPIN( 1, 8,1); _FL_DEFPIN( 2, 2,1); _FL_DEFPIN( 3, 3,1);
|
||||
_FL_DEFPIN( 6, 5,0); _FL_DEFPIN( 9, 6,0); _FL_DEFPIN(10, 7,0); _FL_DEFPIN(12, 2,0);
|
||||
_FL_DEFPIN(A6, 9,1); _FL_DEFPIN(A7, 8,1); _FL_DEFPIN(A5, 2,1); _FL_DEFPIN(A4, 3,1);
|
||||
_FL_DEFPIN(A1, 5,0); _FL_DEFPIN(A2, 6,0); _FL_DEFPIN(A3, 7,0); _FL_DEFPIN(A0, 2,0);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
|
||||
#elif defined(ADAFRUIT_HALLOWING)
|
||||
|
||||
#define MAX_PIN 20
|
||||
// 0 & 1
|
||||
_FL_DEFPIN( 0, 9, 0); _FL_DEFPIN( 1, 10, 0);
|
||||
// 2, 3, 4
|
||||
_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 11, 0); _FL_DEFPIN( 4, 8, 0);
|
||||
// 5, 6, 7
|
||||
_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 18, 0); _FL_DEFPIN( 7, 0, 0);
|
||||
// 8, 9, 10
|
||||
_FL_DEFPIN( 8, 12, 0); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0);
|
||||
// 11, 12, 13
|
||||
_FL_DEFPIN(11, 21, 0); _FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
|
||||
// 14, 15, 16 (A0 - A2)
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 8, 1); _FL_DEFPIN(16, 9, 1);
|
||||
// 17, 18, 19 (A3 - A5)
|
||||
_FL_DEFPIN(17, 4, 0); _FL_DEFPIN(18, 5, 0); _FL_DEFPIN(19, 6, 0);
|
||||
|
||||
#define SPI_DATA PIN_SPI_MOSI
|
||||
#define SPI_CLOCK PIN_SPI_SCK
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(SEEED_XIAO_M0)
|
||||
|
||||
#define MAX_PIN 10
|
||||
_FL_DEFPIN( 0, 2,0); _FL_DEFPIN( 1, 4,0); _FL_DEFPIN( 2,10,0); _FL_DEFPIN( 3,11,0);
|
||||
_FL_DEFPIN( 4, 8,0); _FL_DEFPIN( 5, 9,0); _FL_DEFPIN( 6, 8,1); _FL_DEFPIN( 7, 9,1);
|
||||
_FL_DEFPIN( 8, 7,0); _FL_DEFPIN( 9, 5,0); _FL_DEFPIN(10, 6,0);
|
||||
|
||||
#define SPI_DATA 9
|
||||
#define SPI_CLOCK 8
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SEEED_ZERO)
|
||||
|
||||
#define MAX_PIN 24
|
||||
|
||||
_FL_DEFPIN( 0,11,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,14,0); _FL_DEFPIN( 3,9,0);
|
||||
_FL_DEFPIN( 4,8,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0);
|
||||
_FL_DEFPIN( 8,6,0); _FL_DEFPIN( 9,7,0); _FL_DEFPIN( 10,18,0); _FL_DEFPIN( 11,16,0);
|
||||
_FL_DEFPIN( 12,19,0); _FL_DEFPIN( 13,17,0); _FL_DEFPIN( 14,2,0); _FL_DEFPIN( 15,8,1);
|
||||
_FL_DEFPIN( 16,9,1); _FL_DEFPIN( 17,4,0); _FL_DEFPIN( 18,5,0); _FL_DEFPIN( 19,2,1);
|
||||
_FL_DEFPIN( 20,22,0); _FL_DEFPIN( 21,23,0); _FL_DEFPIN( 22,12,0);
|
||||
_FL_DEFPIN( 23,10,1);//MOSI
|
||||
_FL_DEFPIN( 24,11,1);//SCK
|
||||
|
||||
#define SPI_DATA 23
|
||||
#define SPI_CLOCK 24
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SODAQ_AUTONOMO)
|
||||
|
||||
#define MAX_PIN 56
|
||||
_FL_DEFPIN( 0, 9,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,11,0); _FL_DEFPIN( 3,10,1);
|
||||
_FL_DEFPIN( 4,11,1); _FL_DEFPIN( 5,12,1); _FL_DEFPIN( 6,13,1); _FL_DEFPIN( 7,14,1);
|
||||
_FL_DEFPIN( 8,15,1); _FL_DEFPIN( 9,14,0); _FL_DEFPIN(10,15,0); _FL_DEFPIN(11,16,0);
|
||||
_FL_DEFPIN(12,17,0); _FL_DEFPIN(13,18,0); _FL_DEFPIN(14,19,0); _FL_DEFPIN(15,16,1);
|
||||
_FL_DEFPIN(16, 8,0); _FL_DEFPIN(17,28,0); _FL_DEFPIN(18,17,1); _FL_DEFPIN(19, 2,0);
|
||||
_FL_DEFPIN(20, 6,0); _FL_DEFPIN(21, 5,0); _FL_DEFPIN(22, 4,0); _FL_DEFPIN(23, 9,1);
|
||||
_FL_DEFPIN(24, 8,1); _FL_DEFPIN(25, 7,1); _FL_DEFPIN(26, 6,1); _FL_DEFPIN(27, 5,1);
|
||||
_FL_DEFPIN(28, 4,1); _FL_DEFPIN(29, 7,0); _FL_DEFPIN(30, 3,1); _FL_DEFPIN(31, 2,1);
|
||||
_FL_DEFPIN(32, 1,1); _FL_DEFPIN(33, 0,1); _FL_DEFPIN(34, 3,0); _FL_DEFPIN(35, 3,0);
|
||||
_FL_DEFPIN(36,30,1); _FL_DEFPIN(37,31,1); _FL_DEFPIN(38,22,1); _FL_DEFPIN(39,23,1);
|
||||
_FL_DEFPIN(40,12,0); _FL_DEFPIN(41,13,0); _FL_DEFPIN(42,22,0); _FL_DEFPIN(43,23,0);
|
||||
_FL_DEFPIN(44,20,0); _FL_DEFPIN(45,21,0); _FL_DEFPIN(46,27,0); _FL_DEFPIN(47,24,0);
|
||||
_FL_DEFPIN(48,25,0); _FL_DEFPIN(49,13,1); _FL_DEFPIN(50,14,1); _FL_DEFPIN(51,17,0);
|
||||
_FL_DEFPIN(52,18,0); _FL_DEFPIN(53,12,1); _FL_DEFPIN(54,13,1); _FL_DEFPIN(55,14,1);
|
||||
_FL_DEFPIN(56,15,1);
|
||||
|
||||
#define SPI_DATA 44
|
||||
#define SPI_CLOCK 45
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_WINO)
|
||||
|
||||
#define MAX_PIN 22
|
||||
_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 16, 0); _FL_DEFPIN( 3, 17, 0);
|
||||
_FL_DEFPIN( 4, 18, 0); _FL_DEFPIN( 5, 19, 0); _FL_DEFPIN( 6, 24, 0); _FL_DEFPIN( 7, 25, 0);
|
||||
_FL_DEFPIN( 8, 27, 0); _FL_DEFPIN( 9, 28, 0); _FL_DEFPIN( 10, 30, 0); _FL_DEFPIN( 11, 31, 0);
|
||||
_FL_DEFPIN( 12, 15, 0); _FL_DEFPIN( 13, 14, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 3, 0);
|
||||
_FL_DEFPIN( 16, 4, 0); _FL_DEFPIN( 17, 5, 0); _FL_DEFPIN( 18, 6, 0); _FL_DEFPIN( 19, 7, 0);
|
||||
_FL_DEFPIN( 20, 8, 0); _FL_DEFPIN( 21, 9, 0); _FL_DEFPIN( 22, 10, 0); _FL_DEFPIN( 23, 11, 0);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_MKRZERO)
|
||||
|
||||
#define MAX_PIN 22
|
||||
_FL_DEFPIN( 0, 22, 0); _FL_DEFPIN( 1, 23, 0); _FL_DEFPIN( 2, 10, 0); _FL_DEFPIN( 3, 11, 0);
|
||||
_FL_DEFPIN( 4, 10, 1); _FL_DEFPIN( 5, 11, 1); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
|
||||
_FL_DEFPIN( 8, 16, 0); _FL_DEFPIN( 9, 17, 0); _FL_DEFPIN( 10, 19, 0); _FL_DEFPIN( 11, 8, 0);
|
||||
_FL_DEFPIN( 12, 9, 0); _FL_DEFPIN( 13, 23, 1); _FL_DEFPIN( 14, 22, 1); _FL_DEFPIN( 15, 2, 0);
|
||||
_FL_DEFPIN( 16, 2, 1); _FL_DEFPIN( 17, 3, 1); _FL_DEFPIN( 18, 4, 0); _FL_DEFPIN( 19, 5, 0);
|
||||
_FL_DEFPIN( 20, 6, 0); _FL_DEFPIN( 21, 7, 0);
|
||||
|
||||
#define SPI_DATA 8
|
||||
#define SPI_CLOCK 9
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_NANO_33_IOT)
|
||||
|
||||
#define MAX_PIN 26
|
||||
_FL_DEFPIN( 0, 23, 1); _FL_DEFPIN( 1, 22, 1); _FL_DEFPIN( 2, 10, 1); _FL_DEFPIN( 3, 11, 1);
|
||||
_FL_DEFPIN( 4, 7, 0); _FL_DEFPIN( 5, 5, 0); _FL_DEFPIN( 6, 4, 0); _FL_DEFPIN( 7, 6, 0);
|
||||
_FL_DEFPIN( 8, 18, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN( 10, 21, 0); _FL_DEFPIN( 11, 16, 0);
|
||||
_FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 2, 1);
|
||||
_FL_DEFPIN( 16, 11, 1); _FL_DEFPIN( 17, 10, 0); _FL_DEFPIN( 18, 8, 1); _FL_DEFPIN( 19, 9, 1);
|
||||
_FL_DEFPIN( 20, 9, 0); _FL_DEFPIN( 21, 3, 1); _FL_DEFPIN( 22, 12, 0); _FL_DEFPIN( 23, 13, 0);
|
||||
_FL_DEFPIN( 24, 14, 0); _FL_DEFPIN( 25, 15, 0);
|
||||
|
||||
#define SPI_DATA 22
|
||||
#define SPI_CLOCK 25
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_GEMMA_M0)
|
||||
|
||||
#define MAX_PIN 4
|
||||
_FL_DEFPIN( 0, 4, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 5, 0);
|
||||
_FL_DEFPIN( 3, 0, 0); _FL_DEFPIN( 4, 1, 0);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_TRINKET_M0)
|
||||
|
||||
#define MAX_PIN 7
|
||||
_FL_DEFPIN( 0, 8, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 9, 0);
|
||||
_FL_DEFPIN( 3, 7, 0); _FL_DEFPIN( 4, 6, 0); _FL_DEFPIN( 7, 0, 0); _FL_DEFPIN( 8, 1, 0);
|
||||
|
||||
#define SPI_DATA 4
|
||||
#define SPI_CLOCK 3
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_QTPY_M0)
|
||||
|
||||
#define MAX_PIN 10
|
||||
_FL_DEFPIN( 0, 2, 0); _FL_DEFPIN( 1, 3, 0); _FL_DEFPIN( 2, 4, 0); _FL_DEFPIN( 3, 5, 0);
|
||||
_FL_DEFPIN( 4, 16, 0); _FL_DEFPIN( 5, 17, 0); _FL_DEFPIN( 6, 6, 0); _FL_DEFPIN( 7, 7, 0);
|
||||
_FL_DEFPIN( 8, 11, 0); _FL_DEFPIN( 9, 9, 0); _FL_DEFPIN( 10, 10, 0);
|
||||
|
||||
#define SPI_DATA 10
|
||||
#define SPI_CLOCK 8
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
|
||||
#elif defined(ADAFRUIT_ITSYBITSY_M0)
|
||||
|
||||
#define MAX_PIN 16
|
||||
_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 9, 0); _FL_DEFPIN( 4, 8, 0);
|
||||
_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
|
||||
_FL_DEFPIN( 8, 6, 0); _FL_DEFPIN( 9, 7, 0); _FL_DEFPIN( 10, 18, 0);
|
||||
_FL_DEFPIN( 11, 16, 0); _FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0);
|
||||
_FL_DEFPIN( 29, 10, 0); // MOSI
|
||||
_FL_DEFPIN( 30, 11, 0); // SCK
|
||||
_FL_DEFPIN( 40, 0, 0); //APA102 Clock
|
||||
_FL_DEFPIN( 41, 0, 1) //APA102 Data
|
||||
|
||||
#define SPI_DATA 29
|
||||
#define SPI_CLOCK 30
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_PIXELTRINKEY_M0)
|
||||
|
||||
#define MAX_PIN 5
|
||||
_FL_DEFPIN( 0, 2, 0); // D0
|
||||
_FL_DEFPIN( 1, 1, 0); // D1 (Internal NeoPixel)
|
||||
_FL_DEFPIN( 2, 4, 0); // D2 (MOSI)
|
||||
_FL_DEFPIN( 3, 5, 0); // D3 (SCK)
|
||||
_FL_DEFPIN( 4, 6, 0); // D4 (MISO)
|
||||
|
||||
#define SPI_DATA 2
|
||||
#define SPI_CLOCK 3
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_ZERO)
|
||||
|
||||
#define MAX_PIN 42
|
||||
_FL_DEFPIN( 0,10,0); _FL_DEFPIN( 1,11,0); _FL_DEFPIN( 2, 8,0); _FL_DEFPIN( 3, 9,0);
|
||||
_FL_DEFPIN( 4,14,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0);
|
||||
_FL_DEFPIN( 8, 6,0); _FL_DEFPIN( 9, 7,0); _FL_DEFPIN(10,18,0); _FL_DEFPIN(11,16,0);
|
||||
_FL_DEFPIN(12,19,0); _FL_DEFPIN(13,17,0); _FL_DEFPIN(14, 2,0); _FL_DEFPIN(15, 8,1);
|
||||
_FL_DEFPIN(16, 9,1); _FL_DEFPIN(17, 4,0); _FL_DEFPIN(18, 5,0); _FL_DEFPIN(19, 2,1);
|
||||
_FL_DEFPIN(20,22,0); _FL_DEFPIN(21,23,0); _FL_DEFPIN(22,12,0); _FL_DEFPIN(23,11,1);
|
||||
_FL_DEFPIN(24,10,1); _FL_DEFPIN(25, 3,1); _FL_DEFPIN(26,27,0); _FL_DEFPIN(27,28,0);
|
||||
_FL_DEFPIN(28,24,0); _FL_DEFPIN(29,25,0); _FL_DEFPIN(30,22,1); _FL_DEFPIN(31,23,1);
|
||||
_FL_DEFPIN(32,22,0); _FL_DEFPIN(33,23,0); _FL_DEFPIN(34,19,0); _FL_DEFPIN(35,16,0);
|
||||
_FL_DEFPIN(36,18,0); _FL_DEFPIN(37,17,0); _FL_DEFPIN(38,13,0); _FL_DEFPIN(39,21,0);
|
||||
_FL_DEFPIN(40, 6,0); _FL_DEFPIN(41, 7,0); _FL_DEFPIN(42, 3,0);
|
||||
|
||||
#define SPI_DATA 24
|
||||
#define SPI_CLOCK 23
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_SAM_H
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_D21_H
|
||||
#define __INC_LED_SYSDEFS_ARM_D21_H
|
||||
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
#define FASTLED_ARM_M0_PLUS
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
# FastLED Platform: ARM SAMD51 (d51)
|
||||
|
||||
SAMD51 (Feather/Itsy M4, Wio Terminal) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_d51.h`: Aggregator; includes `fastpin_arm_d51.h`, `clockless_arm_d51.h`, and SPI core where applicable.
|
||||
- `fastpin_arm_d51.h`: Pin helpers for SAMD51.
|
||||
- `led_sysdefs_arm_d51.h`: System defines for SAMD51.
|
||||
- `clockless_arm_d51.h`: Clockless WS281x driver for SAMD51.
|
||||
- `README.txt`: Historical notes on tested boards.
|
||||
|
||||
Notes:
|
||||
- Higher clock speeds and interrupt policy can affect jitter; prefer short critical sections.
|
||||
- Typical settings: `FASTLED_USE_PROGMEM=0`; consider enabling interrupts with careful ISR timing.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0`.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK` when `1`.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,7 @@
|
||||
FastLED updates for adafruit FEATHER M4 and fixes to ITSBITSY M4 compiles
|
||||
SAMD51
|
||||
|
||||
Tested on
|
||||
- FEATHER M4 with DOTSTAR and neopixel strips
|
||||
- Seeed Wio Terminal and WS2812B and APA102 LED strips using either SPI or GPIO pins
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_D51
|
||||
#define __INC_CLOCKLESS_ARM_D51
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for SAMD51
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control
|
||||
#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
|
||||
#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register
|
||||
#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
|
||||
#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register
|
||||
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
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 data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
FASTLED_REGISTER data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
FASTLED_REGISTER data_t hi = *port | FastPin<DATA_PIN>::mask();
|
||||
FASTLED_REGISTER data_t lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef __INC_FASTLED_ARM_D51_H
|
||||
#define __INC_FASTLED_ARM_D51_H
|
||||
|
||||
#include "fastpin_arm_d51.h"
|
||||
#include "../../fastspi_ardunio_core.h"
|
||||
#include "clockless_arm_d51.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,241 @@
|
||||
#ifndef __INC_FASTPIN_ARM_D51_H
|
||||
#define __INC_FASTPIN_ARM_D51_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
#if 0
|
||||
inline static void setOutput() {
|
||||
if(_BIT<8) {
|
||||
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
|
||||
} else {
|
||||
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
|
||||
}
|
||||
}
|
||||
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
#endif
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTSET.reg = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTCLR.reg = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { PORT->Group[_GRP].OUT.reg = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTTGL.reg = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUT.reg; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTSET.reg; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTCLR.reg; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE volatile PortGroup * r() { return T; } };
|
||||
|
||||
#define _FL_IO(L) _RD32(GPIO ## L)
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1ul << BIT, L> {};
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(ADAFRUIT_ITSYBITSY_M4_EXPRESS)
|
||||
|
||||
#define MAX_PIN 19
|
||||
// D0-D13, including D6+D8 (DotStar CLK + DATA)
|
||||
_FL_DEFPIN( 0, 16, 0); _FL_DEFPIN( 1, 17, 0); _FL_DEFPIN( 2, 7, 0); _FL_DEFPIN( 3, 22, 1);
|
||||
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 2, 1); _FL_DEFPIN( 7, 18, 0);
|
||||
_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
|
||||
_FL_DEFPIN(12, 23, 0); _FL_DEFPIN(13, 22, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
|
||||
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
|
||||
|
||||
// 23..25 MISO/SCK/MOSI
|
||||
_FL_DEFPIN(23, 23, 1); _FL_DEFPIN(24, 1, 0); _FL_DEFPIN(25, 0, 0);
|
||||
|
||||
#define SPI_DATA 25
|
||||
#define SPI_CLOCK 24
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
// Actual pin definitions
|
||||
#elif defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE)
|
||||
|
||||
#define MAX_PIN 20
|
||||
// D0-D13, including D6+D8 (DotStar CLK + DATA)
|
||||
_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 17, 1); _FL_DEFPIN( 3, 16, 1);
|
||||
_FL_DEFPIN( 4, 13, 1); _FL_DEFPIN( 5, 14, 1); _FL_DEFPIN( 6, 15, 1); _FL_DEFPIN( 7, 12, 1);
|
||||
_FL_DEFPIN( 8, 21, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN(10, 18, 0); _FL_DEFPIN(11, 19, 0);
|
||||
_FL_DEFPIN(12, 17, 0); _FL_DEFPIN(13, 16, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 6, 0); _FL_DEFPIN(17, 0, 1);
|
||||
_FL_DEFPIN(18, 8, 1); _FL_DEFPIN(19, 9, 1);
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(22, 2, 1); _FL_DEFPIN(23, 3, 1);
|
||||
|
||||
// 23..25 MISO/SCK/MOSI
|
||||
_FL_DEFPIN(24, 14, 0); _FL_DEFPIN(25, 13, 0); _FL_DEFPIN(26, 12, 0);
|
||||
|
||||
#define SPI_DATA 26
|
||||
#define SPI_CLOCK 25
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_FEATHER_M4_CAN)
|
||||
|
||||
#define MAX_PIN 19
|
||||
// D0-D13, including D8 (neopixel) no pins 2 3
|
||||
_FL_DEFPIN( 0, 17, 1); _FL_DEFPIN( 1, 16, 1);
|
||||
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 16, 0); _FL_DEFPIN( 6, 18, 0);
|
||||
_FL_DEFPIN( 7, 3, 1); _FL_DEFPIN( 8, 2, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
|
||||
_FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
|
||||
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
|
||||
// 23..25 MISO/MOSI/SCK
|
||||
_FL_DEFPIN(23, 22, 1); _FL_DEFPIN(24, 23, 1); _FL_DEFPIN(25, 17, 0);
|
||||
|
||||
#define SPI_DATA 24
|
||||
#define SPI_CLOCK 25
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_FEATHER_M4_EXPRESS)
|
||||
|
||||
#define MAX_PIN 19
|
||||
// D0-D13, including D8 (neopixel) no pins 2 3
|
||||
_FL_DEFPIN( 0, 17, 1); _FL_DEFPIN( 1, 16, 1);
|
||||
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 16, 0); _FL_DEFPIN( 6, 18, 0);
|
||||
_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
|
||||
_FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
|
||||
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
|
||||
// 23..25 MISO/MOSI/SCK
|
||||
_FL_DEFPIN(23, 22, 1); _FL_DEFPIN(24, 23, 1); _FL_DEFPIN(25, 17, 0);
|
||||
|
||||
#define SPI_DATA 24
|
||||
#define SPI_CLOCK 25
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(SEEED_WIO_TERMINAL)
|
||||
|
||||
#define MAX_PIN 9
|
||||
// D0/A0-D8/A8
|
||||
_FL_DEFPIN( 0, 8, 1); _FL_DEFPIN( 1, 9, 1); _FL_DEFPIN( 2, 7, 0); _FL_DEFPIN( 3, 4, 1);
|
||||
_FL_DEFPIN( 4, 5, 1); _FL_DEFPIN( 5, 6, 1); _FL_DEFPIN( 6, 4, 0); _FL_DEFPIN( 7, 7, 1);
|
||||
_FL_DEFPIN( 8, 6, 0);
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(12, 17, 0); _FL_DEFPIN(13, 16, 0);
|
||||
// match GPIO pin nubers 9..11 MISO/MOSI/SCK
|
||||
_FL_DEFPIN(PIN_SPI_MISO, 0, 1); _FL_DEFPIN(PIN_SPI_MOSI, 2, 1); _FL_DEFPIN(PIN_SPI_SCK, 3, 1);
|
||||
|
||||
#define SPI_DATA PIN_SPI_MOSI
|
||||
#define SPI_CLOCK PIN_SPI_SCK
|
||||
|
||||
#define ARDUNIO_CORE_SPI
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_MATRIXPORTAL_M4_EXPRESS)
|
||||
|
||||
#define MAX_PIN 21
|
||||
// 0/1 - SERCOM/UART (Serial1)
|
||||
_FL_DEFPIN( 0, 1, 0); _FL_DEFPIN( 1, 0, 0);
|
||||
// 2..3 buttons
|
||||
_FL_DEFPIN( 2, 22, 1); _FL_DEFPIN( 3, 23, 1);
|
||||
// 4 neopixel
|
||||
_FL_DEFPIN( 4, 23, 0);
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN( 5, 31, 1); _FL_DEFPIN( 6, 30, 1);
|
||||
// 7..12 RGBRGB pins
|
||||
_FL_DEFPIN( 7, 0, 1); _FL_DEFPIN( 8, 1, 1); _FL_DEFPIN( 9, 2, 1); _FL_DEFPIN(10, 3, 1);
|
||||
_FL_DEFPIN(11, 4, 1); _FL_DEFPIN(12, 5, 1);
|
||||
// 13 LED
|
||||
_FL_DEFPIN(13, 14, 0);
|
||||
// 14..21 Control pins
|
||||
_FL_DEFPIN(14, 6, 1); _FL_DEFPIN(15, 14, 1); _FL_DEFPIN(16, 12, 1); _FL_DEFPIN(17, 7, 1);
|
||||
_FL_DEFPIN(18, 8, 1); _FL_DEFPIN(19, 9, 1); _FL_DEFPIN(20, 15, 1); _FL_DEFPIN(21, 13, 1);
|
||||
// 22..26 Analog pins
|
||||
_FL_DEFPIN(22, 2, 1); _FL_DEFPIN(23, 5, 1); _FL_DEFPIN(24, 4, 1); _FL_DEFPIN(25, 6, 1);
|
||||
_FL_DEFPIN(26, 7, 1);
|
||||
// 34..36 ESP SPI
|
||||
_FL_DEFPIN(34, 16, 0); _FL_DEFPIN(35, 17, 0); _FL_DEFPIN(36, 19, 0);
|
||||
// 48..50 external SPI #2 on sercom 0
|
||||
_FL_DEFPIN(48, 5, 0); _FL_DEFPIN(49, 4, 0); _FL_DEFPIN(50, 7, 0);
|
||||
|
||||
#define SPI_DATA 4
|
||||
#define SPI_CLOCK 7
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_GRAND_CENTRAL_M4)
|
||||
|
||||
#define MAX_PIN 62
|
||||
// D0..D7
|
||||
_FL_DEFPIN( 0, 25, 1); _FL_DEFPIN( 1, 24, 1); _FL_DEFPIN( 2, 18, 2); _FL_DEFPIN( 3, 19, 2);
|
||||
_FL_DEFPIN( 4, 20, 2); _FL_DEFPIN( 5, 21, 2); _FL_DEFPIN( 6, 20, 3); _FL_DEFPIN( 7, 21, 3);
|
||||
// D8..D13
|
||||
_FL_DEFPIN( 8, 18, 1); _FL_DEFPIN( 9, 2, 1); _FL_DEFPIN(10, 22, 1);
|
||||
_FL_DEFPIN(11, 23, 1); _FL_DEFPIN(12, 0, 1); _FL_DEFPIN(13, 1, 0);
|
||||
// D14..D21
|
||||
_FL_DEFPIN(14, 16, 1); _FL_DEFPIN(15, 17, 1); _FL_DEFPIN(16, 22, 2); _FL_DEFPIN(17, 23, 2);
|
||||
_FL_DEFPIN(18, 12, 1); _FL_DEFPIN(19, 13, 1); _FL_DEFPIN(20, 20, 1); _FL_DEFPIN(21, 21, 1);
|
||||
// D22..D53
|
||||
_FL_DEFPIN(22, 12, 3); _FL_DEFPIN(23, 15, 0); _FL_DEFPIN(24, 17, 2); _FL_DEFPIN(25, 16, 2);
|
||||
_FL_DEFPIN(26, 12, 0); _FL_DEFPIN(27, 13, 0); _FL_DEFPIN(28, 14, 0); _FL_DEFPIN(29, 19, 1);
|
||||
_FL_DEFPIN(30, 23, 0); _FL_DEFPIN(31, 22, 0); _FL_DEFPIN(32, 21, 0); _FL_DEFPIN(33, 20, 0);
|
||||
_FL_DEFPIN(34, 19, 0); _FL_DEFPIN(35, 18, 0); _FL_DEFPIN(36, 17, 0); _FL_DEFPIN(37, 16, 0);
|
||||
_FL_DEFPIN(38, 15, 1); _FL_DEFPIN(39, 14, 1); _FL_DEFPIN(40, 13, 2); _FL_DEFPIN(41, 12, 2);
|
||||
_FL_DEFPIN(42, 15, 2); _FL_DEFPIN(43, 14, 2); _FL_DEFPIN(44, 11, 2); _FL_DEFPIN(45, 10, 2);
|
||||
_FL_DEFPIN(46, 6, 2); _FL_DEFPIN(47, 7, 2); _FL_DEFPIN(48, 4, 2); _FL_DEFPIN(49, 5, 2);
|
||||
_FL_DEFPIN(50, 11, 3); _FL_DEFPIN(51, 8, 3); _FL_DEFPIN(52, 9, 3); _FL_DEFPIN(53, 10, 3);
|
||||
_FL_DEFPIN(54, 5, 1); _FL_DEFPIN(55, 6, 1); _FL_DEFPIN(56, 7, 1); _FL_DEFPIN(57, 8, 1);
|
||||
_FL_DEFPIN(58, 9, 1); _FL_DEFPIN(59, 4, 0); _FL_DEFPIN(60, 6, 0); _FL_DEFPIN(61, 7, 0);
|
||||
|
||||
#define SPI_DATA 51
|
||||
#define SPI_CLOCK 52
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_D51_H
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_D51_H
|
||||
#define __INC_LED_SYSDEFS_ARM_D51_H
|
||||
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
# FastLED Platform: Arduino GIGA (STM32H747)
|
||||
|
||||
Support for Arduino GIGA R1 based on STM32H747.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_giga.h`: Aggregator; includes `fastpin_arm_giga.h`, `clockless_arm_giga.h`.
|
||||
- `fastpin_arm_giga.h`: Pin helpers for GIGA.
|
||||
- `led_sysdef_arm_giga.h`: System defines (interrupts, PROGMEM policy, F_CPU, cli/sei aliases).
|
||||
- `clockless_arm_giga.h`: Clockless WS281x driver for GIGA.
|
||||
- `armpin.h`: ARM-style pin template utilities.
|
||||
|
||||
Notes:
|
||||
- Uses ARM irq enable/disable wrappers for timing-critical sections.
|
||||
- `led_sysdef_arm_giga.h` sets `FASTLED_USE_PROGMEM=0`, enables interrupts, and defines `F_CPU` (e.g., 480MHz) for timing math.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0`.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK` when `1`.
|
||||
- **`FASTLED_NO_PINMAP`**: Indicates pin maps are not stored in PROGMEM.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, _R(GPIO ## L)> {};
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, typename _GPIO> class _ARMPIN {
|
||||
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = (_MASK<<16); }
|
||||
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _GPIO::r()->ODR = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { if(_GPIO::r()->ODR & _MASK) { lo(); } else { hi(); } }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO::r()->ODR | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO::r()->ODR & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO::r()->ODR; }
|
||||
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR = (_MASK<<16); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR = _MASK; }
|
||||
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_GIGA
|
||||
#define __INC_CLOCKLESS_ARM_GIGA
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for GIGA M7
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control
|
||||
#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
|
||||
#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register
|
||||
#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
|
||||
#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register
|
||||
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
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 data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
FASTLED_REGISTER data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
FASTLED_REGISTER data_t hi = *port | FastPin<DATA_PIN>::mask();
|
||||
FASTLED_REGISTER data_t lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef __INC_FASTLED_ARM_GIGA_H
|
||||
#define __INC_FASTLED_ARM_GIGA_H
|
||||
|
||||
#include "fastpin_arm_giga.h"
|
||||
#include "../../fastspi_ardunio_core.h"
|
||||
#include "clockless_arm_giga.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,208 @@
|
||||
#ifndef __FASTPIN_ARM_GIGA_H
|
||||
#define __FASTPIN_ARM_GIGA_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "armpin.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(ARDUINO_GIGA) || defined(ARDUINO_GIGA_M7)
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE volatile GPIO_TypeDef * r() { return T; } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L);
|
||||
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
|
||||
_FL_IO(A,0);
|
||||
_FL_IO(B,1);
|
||||
_FL_IO(C,2);
|
||||
_FL_IO(D,3);
|
||||
_FL_IO(E,4);
|
||||
_FL_IO(F,5);
|
||||
_FL_IO(G,6);
|
||||
_FL_IO(H,7);
|
||||
_FL_IO(I,8);
|
||||
_FL_IO(J,9);
|
||||
_FL_IO(K,10);
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(ARDUINO_GIGA) || defined(ARDUINO_GIGA_M7)
|
||||
#define MAX_PIN 102
|
||||
|
||||
// PA0-PA15
|
||||
_FL_DEFPIN(83, 0, A);
|
||||
_FL_DEFPIN(66, 1, A);
|
||||
_FL_DEFPIN(3, 2, A);
|
||||
_FL_DEFPIN(2, 3, A);
|
||||
_FL_DEFPIN(84, 4, A);
|
||||
_FL_DEFPIN(85, 5, A);
|
||||
_FL_DEFPIN(56, 6, A);
|
||||
_FL_DEFPIN(5, 7, A);
|
||||
//_FL_DEFPIN(UART7_RX, 8, A);
|
||||
_FL_DEFPIN(1, 9, A);
|
||||
//_FL_DEFPIN(BT_ON, 10, A);
|
||||
//_FL_DEFPIN(USB-OTG-FS_DM, 11, A);
|
||||
//_FL_DEFPIN(USB-OTG-FS_DP, 12, A);
|
||||
//_FL_DEFPIN(SWDIO, 13, A);
|
||||
//_FL_DEFPIN(SWCLK, 14, A);
|
||||
//_FL_DEFPIN(U9 Power Switch, 15, A);
|
||||
|
||||
// PB0-PB13
|
||||
_FL_DEFPIN(78, 0, B);
|
||||
_FL_DEFPIN(79, 1, B);
|
||||
_FL_DEFPIN(47, 2, B);
|
||||
_FL_DEFPIN(91, 3, B);
|
||||
_FL_DEFPIN(7, 4, B);
|
||||
_FL_DEFPIN(93, 5, B);
|
||||
_FL_DEFPIN(101, 6, B);
|
||||
_FL_DEFPIN(0, 7, B);
|
||||
_FL_DEFPIN(8, 8, B);
|
||||
_FL_DEFPIN(9, 9, B);
|
||||
//_FL_DEFPIN(WL_ON, 10, B);
|
||||
_FL_DEFPIN(20, 11, B);
|
||||
_FL_DEFPIN(74, 12, B);
|
||||
_FL_DEFPIN(94, 13, B);
|
||||
|
||||
// PC0-PC15
|
||||
_FL_DEFPIN(82, 0, C);
|
||||
_FL_DEFPIN(73, 1, C);
|
||||
_FL_DEFPIN(81, 2, C);
|
||||
_FL_DEFPIN(80, 3, C);
|
||||
_FL_DEFPIN(76, 4, C);
|
||||
_FL_DEFPIN(77, 5, C);
|
||||
_FL_DEFPIN(68, 6, C);
|
||||
_FL_DEFPIN(15, 7, C);
|
||||
//_FL_DEFPIN(D0, 8, C);
|
||||
//_FL_DEFPIN(D1, 9, C);
|
||||
//_FL_DEFPIN(D2, 10, C);
|
||||
//_FL_DEFPIN(D3, 11, C);
|
||||
//_FL_DEFPIN(CLK, 12, C);
|
||||
//_FL_DEFPIN(BOOT0_BUTTON, 13, C);
|
||||
|
||||
// PD0-PD13
|
||||
//_FL_DEFPIN(FMC_D3, 0, D);
|
||||
//_FL_DEFPIN(FMC_D2, 1, D);
|
||||
//_FL_DEFPIN(SDMMC1_CMD, 2, D);
|
||||
_FL_DEFPIN(75, 3, D);
|
||||
_FL_DEFPIN(67, 4, D);
|
||||
_FL_DEFPIN(18, 5, D);
|
||||
_FL_DEFPIN(19, 6, D);
|
||||
_FL_DEFPIN(90, 7, D);
|
||||
//_FL_DEFPIN(FMC_DQ13, 8, D);
|
||||
//_FL_DEFPIN(FMC_DQ14, 9, D);
|
||||
//_FL_DEFPIN(FMC_DQ15, 10, D);
|
||||
//_FL_DEFPIN(SI/IO0, 11, D);
|
||||
//_FL_DEFPIN(SO/IO1, 12, D);
|
||||
_FL_DEFPIN(6, 13, D);
|
||||
//_FL_DEFPIN(FMC_D0, 14, D);
|
||||
//_FL_DEFPIN(FMC_D1, 14, D);
|
||||
|
||||
// PE0-PE15
|
||||
//_FL_DEFPIN(FMC_DQML, 0, E);
|
||||
//_FL_DEFPIN(FMC_DQMH, 1, E);
|
||||
//_FL_DEFPIN(QUADSPI_BK2-IO1, 2, E);
|
||||
_FL_DEFPIN(88, 3, E);
|
||||
_FL_DEFPIN(49, 4, E);
|
||||
_FL_DEFPIN(51, 5, E);
|
||||
_FL_DEFPIN(40, 6, E);
|
||||
//_FL_DEFPIN(FMC_DQ4, 7, E);
|
||||
//_FL_DEFPIN(FMC_DQ5, 8, E);
|
||||
//_FL_DEFPIN(FMC_DQ6, 9, E);
|
||||
//_FL_DEFPIN(FMC_DQ7, 10, E);
|
||||
//_FL_DEFPIN(FMC_DQ8, 11, E);
|
||||
//_FL_DEFPIN(FMC_DQ9, 12, E);
|
||||
//_FL_DEFPIN(FMC_DQ10, 13, E);
|
||||
//_FL_DEFPIN(FMC_DQ11, 14, E);
|
||||
//_FL_DEFPIN(FMC_DQ2, 15, E);
|
||||
|
||||
// PG0-PG15
|
||||
//_FL_DEFPIN(FMC_A10, 0, G);
|
||||
//_FL_DEFPIN(FMC_A11, 1, G);
|
||||
//_FL_DEFPIN(FMC_A12, 2, G);
|
||||
//_FL_DEFPIN(BT_WAKE_H, 3, G);
|
||||
//_FL_DEFPIN(FMC_BA0, 4, G);
|
||||
//_FL_DEFPIN(FMC_BA1, 5, G);
|
||||
//_FL_DEFPIN(CS, 6, G);
|
||||
_FL_DEFPIN(53, 7, G);
|
||||
_FL_DEFPIN(89, 9, G);
|
||||
_FL_DEFPIN(44, 10, G);
|
||||
_FL_DEFPIN(62, 11, G);
|
||||
_FL_DEFPIN(24, 12, G);
|
||||
_FL_DEFPIN(14, 14, G);
|
||||
//_FL_DEFPIN(FMC_SDNCAS, 15, G);
|
||||
|
||||
// PH3-PH15
|
||||
//_FL_DEFPIN(FMC_SDCKE0, 2, H);
|
||||
//_FL_DEFPIN(FMC_SDNCS, 3, H);
|
||||
_FL_DEFPIN(21, 4, H);
|
||||
//_FL_DEFPIN(FMC_SDNWE, 5, H);
|
||||
_FL_DEFPIN(13, 6, H);
|
||||
//_FL_DEFPIN(BT_WAKE_D, 7, H);
|
||||
_FL_DEFPIN(55, 8, H);
|
||||
_FL_DEFPIN(65, 9, H);
|
||||
_FL_DEFPIN(64, 10, H);
|
||||
_FL_DEFPIN(63, 11, H);
|
||||
_FL_DEFPIN(102, 12, H);
|
||||
_FL_DEFPIN(16, 13, H);
|
||||
_FL_DEFPIN(61, 14, H);
|
||||
_FL_DEFPIN(46, 15, H);
|
||||
|
||||
// PI0-PI15
|
||||
_FL_DEFPIN(69, 0, I);
|
||||
_FL_DEFPIN(70, 1, I);
|
||||
_FL_DEFPIN(71, 2, I);
|
||||
_FL_DEFPIN(72, 3, I);
|
||||
_FL_DEFPIN(60, 4, I);
|
||||
_FL_DEFPIN(54, 5, I);
|
||||
_FL_DEFPIN(59, 6, I);
|
||||
_FL_DEFPIN(58, 7, I);
|
||||
_FL_DEFPIN(17, 9, I);
|
||||
_FL_DEFPIN(43, 10, I);
|
||||
_FL_DEFPIN(50, 11, I);
|
||||
_FL_DEFPIN(86, 12, I);
|
||||
_FL_DEFPIN(45, 13, I);
|
||||
_FL_DEFPIN(39, 14, I);
|
||||
_FL_DEFPIN(42, 15, I);
|
||||
|
||||
// PJ0-PJ15
|
||||
_FL_DEFPIN(25, 0, J);
|
||||
_FL_DEFPIN(27, 1, J);
|
||||
_FL_DEFPIN(29, 2, J);
|
||||
_FL_DEFPIN(31, 3, J);
|
||||
_FL_DEFPIN(33, 4, J);
|
||||
_FL_DEFPIN(35, 5, J);
|
||||
_FL_DEFPIN(37, 6, J);
|
||||
_FL_DEFPIN(38, 7, J);
|
||||
_FL_DEFPIN(4, 8, J);
|
||||
_FL_DEFPIN(57, 9, J);
|
||||
_FL_DEFPIN(11, 10, J);
|
||||
_FL_DEFPIN(12, 11, J);
|
||||
_FL_DEFPIN(22, 12, J);
|
||||
_FL_DEFPIN(87, 13, J);
|
||||
_FL_DEFPIN(26, 14, J);
|
||||
_FL_DEFPIN(28, 15, J);
|
||||
|
||||
// PK0-PK7
|
||||
_FL_DEFPIN(48, 0, K);
|
||||
_FL_DEFPIN(10, 1, K);
|
||||
_FL_DEFPIN(52, 2, K);
|
||||
_FL_DEFPIN(30, 3, K);
|
||||
_FL_DEFPIN(32, 4, K);
|
||||
_FL_DEFPIN(34, 5, K);
|
||||
_FL_DEFPIN(36, 6, K);
|
||||
_FL_DEFPIN(41, 7, K);
|
||||
|
||||
// SPI2 MOSI
|
||||
#define SPI_DATA 90
|
||||
// SPI2 SCK
|
||||
#define SPI_CLOCK 91
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif // ARDUINO_GIGA || ARDUINO_GIGA_M7
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_STM32
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_GIGA_H
|
||||
#define __INC_LED_SYSDEFS_ARM_GIGA_H
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
#define FASTLED_NO_PINMAP
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
|
||||
#define F_CPU 480000000
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace fl {
|
||||
// ARM platforms (32-bit): short is 16-bit, long is 32-bit
|
||||
// uint32_t resolves to 'unsigned long' on most ARM toolchains
|
||||
//
|
||||
// Supported platforms:
|
||||
// - Arduino Due (SAM3X8E Cortex-M3)
|
||||
// - Teensy 3.0 / 3.1 (MK20DX128 / MK20DX256)
|
||||
// - Teensy LC (MKL26Z64 Cortex-M0+)
|
||||
// - Teensy 4.0 / 4.1 (iMXRT1062 Cortex-M7)
|
||||
// - Arduino UNO R4 WiFi (Renesas RA4M1)
|
||||
// - STM32F1 (Maple Mini and similar)
|
||||
// - Arduino GIGA R1 (STM32H747)
|
||||
// - Nordic nRF52 family (nRF52832, nRF52840, etc.)
|
||||
typedef int16_t i16;
|
||||
typedef uint16_t u16;
|
||||
typedef int32_t i32;
|
||||
typedef uint32_t u32;
|
||||
typedef int64_t i64;
|
||||
typedef uint64_t u64;
|
||||
// size_t is unsigned long on ARM (32-bit)
|
||||
typedef size_t size;
|
||||
// uintptr_t is unsigned long on ARM (32-bit pointers)
|
||||
typedef uintptr_t uptr;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
/// @file is_arm.h
|
||||
/// ARM platform detection header
|
||||
///
|
||||
/// This header detects ARM-based platforms by checking compiler-defined macros
|
||||
/// and defines FASTLED_ARM when an ARM platform is detected.
|
||||
///
|
||||
/// Used by platforms/int.h for platform dispatching and by ARM platform headers
|
||||
/// for validation that ARM detection has occurred.
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#if defined(__SAM3X8E__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__IMXRT1062__) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(STM32F1) || defined(STM32F4) || defined(ARDUINO_GIGA) || defined(ARDUINO_GIGA_M7) || defined(NRF52_SERIES) || defined(ARDUINO_ARCH_NRF52) || defined(NRF52840_XXAA) || defined(ARDUINO_NRF52840_FEATHER_SENSE) || defined(ARDUINO_ARCH_APOLLO3) || defined(FASTLED_APOLLO3) || defined(ARDUINO_ARCH_RP2040) || defined(TARGET_RP2040) || defined(PICO_32BIT) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ARCH_SILABS) || defined(__SAMD21G18A__) || defined(__SAMD21J18A__) || defined(__SAMD21E17A__) || defined(__SAMD21E18A__) || defined(__SAMD51G19A__) || defined(__SAMD51J19A__) || defined(__SAME51J19A__) || defined(__SAMD51P19A__) || defined(__SAMD51P20A__)
|
||||
#define FASTLED_ARM
|
||||
#endif
|
||||
#endif // FASTLED_ARM
|
||||
@@ -0,0 +1,28 @@
|
||||
# FastLED Platform: Teensy 3.x (K20)
|
||||
|
||||
Teensy 3.0/3.1/3.2 support (MK20DX family).
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_k20.h`: Aggregator; includes pin/SPI/clockless and helper controllers.
|
||||
- `fastpin_arm_k20.h`: Pin helpers.
|
||||
- `fastspi_arm_k20.h`: SPI output backend.
|
||||
- `clockless_arm_k20.h`: Single-lane clockless driver using DWT cycle counter.
|
||||
- `clockless_block_arm_k20.h`: Block/multi-lane variant.
|
||||
- `clockless_objectfled.*`: ObjectFLED experimental clockless implementation.
|
||||
- `octows2811_controller.h`: OctoWS2811 parallel output integration.
|
||||
- `ws2812serial_controller.h`: UART-style serial WS2812 controller.
|
||||
- `smartmatrix_t3.h`: SmartMatrix support for Teensy 3.x.
|
||||
- `led_sysdefs_arm_k20.h`: System defines for Teensy 3.x.
|
||||
|
||||
Notes:
|
||||
- DWT cycle counter is used for precise timing; interrupt windows are checked to punt frames when overrun.
|
||||
- Consider `FASTLED_ALLOW_INTERRUPTS=1` for responsiveness; long ISRs risk jitter and retries.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `1` on some Teensy3 cores.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK`.
|
||||
- **ObjectFLED**
|
||||
- **`FASTLED_OBJECTFLED_LATCH_DELAY`**: WS2812 latch delay microseconds for ObjectFLED path (default `300`).
|
||||
|
||||
Place defines before including `FastLED.h`.
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_K20_H
|
||||
#define __INC_CLOCKLESS_ARM_K20_H
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
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 data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
FASTLED_REGISTER data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
FASTLED_REGISTER data_t hi = *port | FastPin<DATA_PIN>::mask();
|
||||
FASTLED_REGISTER data_t lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,333 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_ARM_K20_H
|
||||
#define __INC_BLOCK_CLOCKLESS_ARM_K20_H
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define PORTC_FIRST_PIN 15
|
||||
#define PORTD_FIRST_PIN 2
|
||||
#define HAS_PORTDC 1
|
||||
|
||||
#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
|
||||
|
||||
#define USED_LANES ((FIRST_PIN==2) ? MIN(LANES,8) : MIN(LANES,12))
|
||||
|
||||
#include <kinetis.h>
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
|
||||
switch(USED_LANES) {
|
||||
case 12: FastPin<30>::setOutput();
|
||||
case 11: FastPin<29>::setOutput();
|
||||
case 10: FastPin<27>::setOutput();
|
||||
case 9: FastPin<28>::setOutput();
|
||||
case 8: FastPin<12>::setOutput();
|
||||
case 7: FastPin<11>::setOutput();
|
||||
case 6: FastPin<13>::setOutput();
|
||||
case 5: FastPin<10>::setOutput();
|
||||
case 4: FastPin<9>::setOutput();
|
||||
case 3: FastPin<23>::setOutput();
|
||||
case 2: FastPin<22>::setOutput();
|
||||
case 1: FastPin<15>::setOutput();
|
||||
}
|
||||
} else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
|
||||
switch(USED_LANES) {
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
}
|
||||
mPinMask = FastPin<FIRST_PIN>::mask();
|
||||
mPort = FastPin<FIRST_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[12];
|
||||
uint16_t shorts[6];
|
||||
uint32_t raw[3];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController<RGB_ORDER, LANES, PORT_MASK> &pixels) { // , FASTLED_REGISTER uint32_t & b2) {
|
||||
FASTLED_REGISTER Lines b2;
|
||||
if(USED_LANES>8) {
|
||||
transpose8<1,2>(b.bytes,b2.bytes);
|
||||
transpose8<1,2>(b.bytes+8,b2.bytes+1);
|
||||
} else {
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
}
|
||||
FASTLED_REGISTER uint8_t d = pixels.template getd<PX>(pixels);
|
||||
FASTLED_REGISTER uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(FASTLED_REGISTER uint32_t i = 0; i < (USED_LANES/2); ++i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(USED_LANES/2),d,scale);
|
||||
}
|
||||
|
||||
// if folks use an odd numnber of lanes, get the last byte's value here
|
||||
if(USED_LANES & 0x01) {
|
||||
b.bytes[USED_LANES-1] = pixels.template loadAndScale<PX>(pixels,USED_LANES-1,d,scale);
|
||||
}
|
||||
|
||||
for(FASTLED_REGISTER uint32_t i = USED_LANES/2; i < 8; ++i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
// b2.bytes[0] = 0;
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < USED_LANES; ++i) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
allpixels.stepDithering();
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
#define PMASK ((1<<(LANES))-1)
|
||||
#define PMASK_HI (PMASK>>8 & 0xFF)
|
||||
#define PMASK_LO (PMASK & 0xFF)
|
||||
|
||||
template <uint8_t LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
class SixteenWayInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PMASK> {
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!");
|
||||
// FastPin<30>::setOutput();
|
||||
// FastPin<29>::setOutput();
|
||||
// FastPin<27>::setOutput();
|
||||
// FastPin<28>::setOutput();
|
||||
switch(LANES) {
|
||||
case 16: FastPin<12>::setOutput();
|
||||
case 15: FastPin<11>::setOutput();
|
||||
case 14: FastPin<13>::setOutput();
|
||||
case 13: FastPin<10>::setOutput();
|
||||
case 12: FastPin<9>::setOutput();
|
||||
case 11: FastPin<23>::setOutput();
|
||||
case 10: FastPin<22>::setOutput();
|
||||
case 9: FastPin<15>::setOutput();
|
||||
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PMASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[16];
|
||||
uint16_t shorts[8];
|
||||
uint32_t raw[4];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController<RGB_ORDER,LANES, PMASK> &pixels) { // , FASTLED_REGISTER uint32_t & b2) {
|
||||
FASTLED_REGISTER Lines b2;
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
transpose8x1(b.bytes+8,b2.bytes+8);
|
||||
FASTLED_REGISTER uint8_t d = pixels.template getd<PX>(pixels);
|
||||
FASTLED_REGISTER uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(FASTLED_REGISTER uint32_t i = 0; (i < LANES) && (i < 8); ++i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
if(LANES==16 || (LANES>8 && ((i+8) < LANES))) {
|
||||
b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, PMASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < LANES; ++i) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
allpixels.stepDithering();
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
sei();
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
#if defined(__IMXRT1062__) // Teensy 4.0/4.1 only.
|
||||
|
||||
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "third_party/object_fled/src/ObjectFLED.h"
|
||||
|
||||
#include "crgb.h"
|
||||
#include "eorder.h"
|
||||
#include "fl/map.h"
|
||||
#include "fl/singleton.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/rectangular_draw_buffer.h"
|
||||
#include "pixel_iterator.h"
|
||||
#include "cpixel_ledcontroller.h"
|
||||
|
||||
#include "clockless_objectfled.h"
|
||||
|
||||
namespace { // anonymous namespace
|
||||
|
||||
typedef fl::FixedVector<uint8_t, 50> PinList50;
|
||||
|
||||
|
||||
static float gOverclock = 1.0f;
|
||||
static float gPrevOverclock = 1.0f;
|
||||
static int gLatchDelayUs = -1;
|
||||
|
||||
|
||||
// Maps multiple pins and CRGB strips to a single ObjectFLED object.
|
||||
class ObjectFLEDGroup {
|
||||
public:
|
||||
|
||||
|
||||
fl::unique_ptr<fl::ObjectFLED> mObjectFLED;
|
||||
fl::RectangularDrawBuffer mRectDrawBuffer;
|
||||
bool mDrawn = false;
|
||||
|
||||
|
||||
static ObjectFLEDGroup &getInstance() {
|
||||
return fl::Singleton<ObjectFLEDGroup>::instance();
|
||||
}
|
||||
|
||||
ObjectFLEDGroup() = default;
|
||||
~ObjectFLEDGroup() { mObjectFLED.reset(); }
|
||||
|
||||
void onQueuingStart() {
|
||||
mRectDrawBuffer.onQueuingStart();
|
||||
mDrawn = false;
|
||||
}
|
||||
|
||||
void onQueuingDone() {
|
||||
mRectDrawBuffer.onQueuingDone();
|
||||
}
|
||||
|
||||
void addObject(uint8_t pin, uint16_t numLeds, bool is_rgbw) {
|
||||
mRectDrawBuffer.queue(fl::DrawItem(pin, numLeds, is_rgbw));
|
||||
}
|
||||
|
||||
void showPixelsOnceThisFrame() {
|
||||
if (mDrawn) {
|
||||
return;
|
||||
}
|
||||
mDrawn = true;
|
||||
if (mRectDrawBuffer.mAllLedsBufferUint8Size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool draw_list_changed = mRectDrawBuffer.mDrawListChangedThisFrame;
|
||||
bool needs_validation = draw_list_changed || !mObjectFLED.get() || gOverclock != gPrevOverclock;
|
||||
if (needs_validation) {
|
||||
gPrevOverclock = gOverclock;
|
||||
mObjectFLED.reset();
|
||||
PinList50 pinList;
|
||||
for (auto it = mRectDrawBuffer.mDrawList.begin(); it != mRectDrawBuffer.mDrawList.end(); ++it) {
|
||||
pinList.push_back(it->mPin);
|
||||
}
|
||||
int totalLeds = mRectDrawBuffer.getTotalBytes() / 3; // Always work in RGB, even when in RGBW mode.
|
||||
#ifdef FASTLED_DEBUG_OBJECTFLED
|
||||
FASTLED_WARN("ObjectFLEDGroup::showPixelsOnceThisFrame: totalLeds = " << totalLeds);
|
||||
#endif
|
||||
mObjectFLED.reset(new fl::ObjectFLED(totalLeds, mRectDrawBuffer.mAllLedsBufferUint8.get(),
|
||||
CORDER_RGB, pinList.size(),
|
||||
pinList.data()));
|
||||
if (gLatchDelayUs >= 0) {
|
||||
mObjectFLED->begin(gOverclock, gLatchDelayUs);
|
||||
} else {
|
||||
mObjectFLED->begin(gOverclock);
|
||||
}
|
||||
}
|
||||
mObjectFLED->show();
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
void ObjectFled::SetOverclock(float overclock) {
|
||||
gOverclock = overclock;
|
||||
}
|
||||
|
||||
void ObjectFled::SetLatchDelay(uint16_t latch_delay_us) {
|
||||
gLatchDelayUs = latch_delay_us;
|
||||
}
|
||||
|
||||
void ObjectFled::beginShowLeds(int datapin, int nleds) {
|
||||
ObjectFLEDGroup &group = ObjectFLEDGroup::getInstance();
|
||||
group.onQueuingStart();
|
||||
group.addObject(datapin, nleds, false);
|
||||
}
|
||||
|
||||
void ObjectFled::showPixels(uint8_t data_pin, PixelIterator& pixel_iterator) {
|
||||
ObjectFLEDGroup &group = ObjectFLEDGroup::getInstance();
|
||||
group.onQueuingDone();
|
||||
const Rgbw rgbw = pixel_iterator.get_rgbw();
|
||||
|
||||
fl::span<uint8_t> strip_pixels = group.mRectDrawBuffer.getLedsBufferBytesForPin(data_pin, true);
|
||||
if (rgbw.active()) {
|
||||
uint8_t r, g, b, w;
|
||||
while (pixel_iterator.has(1)) {
|
||||
FASTLED_ASSERT(strip_pixels.size() >= 4, "ObjectFled::showPixels: buffer overflow");
|
||||
pixel_iterator.loadAndScaleRGBW(&r, &g, &b, &w);
|
||||
strip_pixels[0] = r;
|
||||
strip_pixels[1] = g;
|
||||
strip_pixels[2] = b;
|
||||
strip_pixels[3] = w;
|
||||
strip_pixels.pop_front();
|
||||
strip_pixels.pop_front();
|
||||
strip_pixels.pop_front();
|
||||
strip_pixels.pop_front();
|
||||
pixel_iterator.advanceData();
|
||||
pixel_iterator.stepDithering();
|
||||
}
|
||||
} else {
|
||||
uint8_t r, g, b;
|
||||
while (pixel_iterator.has(1)) {
|
||||
FASTLED_ASSERT(strip_pixels.size() >= 3, "ObjectFled::showPixels: buffer overflow");
|
||||
pixel_iterator.loadAndScaleRGB(&r, &g, &b);
|
||||
strip_pixels[0] = r;
|
||||
strip_pixels[1] = g;
|
||||
strip_pixels[2] = b;
|
||||
strip_pixels.pop_front();
|
||||
strip_pixels.pop_front();
|
||||
strip_pixels.pop_front();
|
||||
pixel_iterator.advanceData();
|
||||
pixel_iterator.stepDithering();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ObjectFled::endShowLeds() {
|
||||
// First one to call this draws everything, every other call this frame
|
||||
// is ignored.
|
||||
ObjectFLEDGroup::getInstance().showPixelsOnceThisFrame();
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
|
||||
#endif // defined(__IMXRT1062__)
|
||||
@@ -0,0 +1,85 @@
|
||||
/// FastLED mapping of the ObjectFLED driver for Teensy 4.0/4.1.
|
||||
///
|
||||
/// This driver will support upto 42 parallel strips of WS2812 LEDS! ~7x that of OctoWS2811!
|
||||
/// BasicTest example to demonstrate massive parallel output with FastLED using
|
||||
/// ObjectFLED for Teensy 4.0/4.1.
|
||||
///
|
||||
/// This mode will support upto 42 parallel strips of WS2812 LEDS! ~7x that of OctoWS2811!
|
||||
///
|
||||
/// The theoritical limit of Teensy 4.0, if frames per second is not a concern, is
|
||||
/// more than 200k pixels. However, realistically, to run 42 strips at 550 pixels
|
||||
/// each at 60fps, is 23k pixels.
|
||||
///
|
||||
/// @author Kurt Funderburg
|
||||
/// @reddit: reddit.com/u/Tiny_Structure_7
|
||||
/// The FastLED code was written by Zach Vorhies
|
||||
/// @author Kurt Funderburg
|
||||
/// @reddit: reddit.com/u/Tiny_Structure_7
|
||||
/// @author: Zach Vorhies (FastLED code)
|
||||
/// @reddit: reddit.com/u/ZachVorhies
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cpixel_ledcontroller.h"
|
||||
#include "pixel_iterator.h"
|
||||
#include "fl/vector.h"
|
||||
|
||||
#ifndef FASTLED_OBJECTFLED_LATCH_DELAY
|
||||
#define FASTLED_OBJECTFLED_LATCH_DELAY 300 // WS2812-5VB
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
class ObjectFled {
|
||||
public:
|
||||
static void SetOverclock(float overclock);
|
||||
static void SetLatchDelay(uint16_t latchDelayUs);
|
||||
void beginShowLeds(int data_pin, int nleds);
|
||||
void showPixels(uint8_t data_pin, PixelIterator& pixel_iterator);
|
||||
void endShowLeds();
|
||||
};
|
||||
|
||||
// TODO: RGBW support, should be pretty easy except the fact that ObjectFLED
|
||||
// either supports RGBW on all pixels strips, or none.
|
||||
template <int DATA_PIN, EOrder RGB_ORDER = RGB>
|
||||
class ClocklessController_ObjectFLED_WS2812
|
||||
: public CPixelLEDController<RGB_ORDER> {
|
||||
private:
|
||||
typedef CPixelLEDController<RGB_ORDER> Base;
|
||||
ObjectFled mObjectFled;
|
||||
|
||||
public:
|
||||
ClocklessController_ObjectFLED_WS2812(float overclock = 1.0f, int latchDelayUs = FASTLED_OBJECTFLED_LATCH_DELAY): Base() {
|
||||
// Warning - overwrites previous overclock value.
|
||||
// Warning latchDelayUs is GLOBAL!
|
||||
ObjectFled::SetOverclock(overclock);
|
||||
if (latchDelayUs >= 0) {
|
||||
ObjectFled::SetLatchDelay(latchDelayUs);
|
||||
}
|
||||
}
|
||||
void init() override {}
|
||||
virtual uint16_t getMaxRefreshRate() const { return 800; }
|
||||
|
||||
protected:
|
||||
// Wait until the last draw is complete, if necessary.
|
||||
virtual void *beginShowLeds(int nleds) override {
|
||||
void *data = Base::beginShowLeds(nleds);
|
||||
mObjectFled.beginShowLeds(DATA_PIN, nleds);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Prepares data for the draw.
|
||||
virtual void showPixels(PixelController<RGB_ORDER> &pixels) override {
|
||||
auto pixel_iterator = pixels.as_iterator(this->getRgbw());
|
||||
mObjectFled.showPixels(DATA_PIN, pixel_iterator);
|
||||
}
|
||||
|
||||
// Send the data to the strip
|
||||
virtual void endShowLeds(void *data) override {
|
||||
Base::endShowLeds(data);
|
||||
mObjectFled.endShowLeds();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef __INC_FASTLED_ARM_K20_H
|
||||
#define __INC_FASTLED_ARM_K20_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_k20.h"
|
||||
#include "fastspi_arm_k20.h"
|
||||
#include "octows2811_controller.h"
|
||||
#include "ws2812serial_controller.h"
|
||||
#include "smartmatrix_t3.h"
|
||||
#include "clockless_arm_k20.h"
|
||||
#include "clockless_block_arm_k20.h"
|
||||
#include "clockless_objectfled.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
#ifndef __FASTPIN_ARM_K20_H
|
||||
#define __FASTPIN_ARM_K20_H
|
||||
#include "fl/stdint.h"
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
|
||||
/// does a poor job of optimizing around these accesses so they are not being used just yet.
|
||||
template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
|
||||
};
|
||||
|
||||
// Macros for k20 pin access/definition
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE reg32_t r() { return (reg32_t)T; } \
|
||||
template<int BIT> static FASTLED_FORCE_INLINE ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; \
|
||||
template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
|
||||
|
||||
// Actual pin definitions
|
||||
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
#define MAX_PIN 33
|
||||
_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 12, A);
|
||||
_FL_DEFPIN(4, 13, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D);
|
||||
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
|
||||
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
|
||||
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
|
||||
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
|
||||
_FL_DEFPIN(24, 5, A); _FL_DEFPIN(25, 19, B); _FL_DEFPIN(26, 1, E); _FL_DEFPIN(27, 9, C);
|
||||
_FL_DEFPIN(28, 8, C); _FL_DEFPIN(29, 10, C); _FL_DEFPIN(30, 11, C); _FL_DEFPIN(31, 0, E);
|
||||
_FL_DEFPIN(32, 18, B); _FL_DEFPIN(33, 4, A);
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
#define SPI1 (*(SPI_t *)0x4002D000)
|
||||
|
||||
#define SPI2_DATA 7
|
||||
#define SPI2_CLOCK 14
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_K20
|
||||
@@ -0,0 +1,466 @@
|
||||
#ifndef __INC_FASTSPI_ARM_H
|
||||
#define __INC_FASTSPI_ARM_H
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
// Version 1.20 renamed SPI_t to KINETISK_SPI_t
|
||||
#if TEENSYDUINO >= 120
|
||||
#define SPI_t KINETISK_SPI_t
|
||||
#endif
|
||||
|
||||
#ifndef KINETISK_SPI0
|
||||
#define KINETISK_SPI0 SPI0
|
||||
#endif
|
||||
|
||||
#ifndef SPI_PUSHR_CONT
|
||||
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
|
||||
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
|
||||
#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ
|
||||
#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT
|
||||
#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X)
|
||||
#endif
|
||||
|
||||
// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now,
|
||||
// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit
|
||||
// set (the zero-th bit). Unclear if I will want this to change at some point.
|
||||
template<int VAL, int BIT> class BitWork {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
|
||||
};
|
||||
|
||||
template<int VAL> class BitWork<VAL, 0> {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return 0; }
|
||||
};
|
||||
|
||||
#define USE_CONT 0
|
||||
// intra-frame backup data
|
||||
struct SPIState {
|
||||
uint32_t _ctar0,_ctar1;
|
||||
uint32_t pins[4];
|
||||
};
|
||||
|
||||
// extern SPIState gState;
|
||||
|
||||
|
||||
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
|
||||
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
|
||||
switch(VAL) {
|
||||
// Handle the dbl clock cases
|
||||
case 0: case 1:
|
||||
case 2: preScalar = 0; scalar = 0; dbl = 1; break;
|
||||
case 3: preScalar = 1; scalar = 0; dbl = 1; break;
|
||||
case 5: preScalar = 2; scalar = 0; dbl = 1; break;
|
||||
case 7: preScalar = 3; scalar = 0; dbl = 1; break;
|
||||
|
||||
// Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
|
||||
// below)
|
||||
case 9: preScalar = 1; scalar = 2; dbl = 1; break;
|
||||
case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 15: preScalar = 2; scalar = 2; dbl = 1; break;
|
||||
case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
|
||||
case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
|
||||
default: {
|
||||
int p2 = BitWork<VAL/2, 15>::highestBit();
|
||||
int p3 = BitWork<VAL/3, 15>::highestBit();
|
||||
int p5 = BitWork<VAL/5, 15>::highestBit();
|
||||
int p7 = BitWork<VAL/7, 15>::highestBit();
|
||||
|
||||
int w2 = 2 * (1 << p2);
|
||||
int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
|
||||
int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
|
||||
int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
|
||||
|
||||
int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
|
||||
|
||||
if(w2 == maxval) { preScalar = 0; scalar = p2; }
|
||||
else if(w3 == maxval) { preScalar = 1; scalar = p3; }
|
||||
else if(w5 == maxval) { preScalar = 2; scalar = p5; }
|
||||
else if(w7 == maxval) { preScalar = 3; scalar = p7; }
|
||||
|
||||
dbl = 0;
|
||||
if(scalar == 0) { dbl = 1; }
|
||||
else if(scalar < 3) { --scalar; }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#define SPIX (*(SPI_t*)pSPIX)
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
|
||||
class ARMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
SPIState gState;
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
|
||||
// and likewise enabling pin 14 disables pin 13 (and vice versa)
|
||||
inline void enable_pins(void) __attribute__((always_inline)) {
|
||||
//serial_print("enable_pins\n");
|
||||
switch(_DATA_PIN) {
|
||||
case 7:
|
||||
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 11:
|
||||
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13:
|
||||
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 14:
|
||||
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
|
||||
inline void disable_pins(void) __attribute__((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
|
||||
if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if(mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR0 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar;
|
||||
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR1 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR1 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR1 = ctar;
|
||||
SPIX.MCR = mcr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
|
||||
uint32_t _PBR = 0;
|
||||
uint32_t _BR = 0;
|
||||
uint32_t _CSSCK = 0;
|
||||
uint32_t _DBR = 0;
|
||||
|
||||
// if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
|
||||
// else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
|
||||
|
||||
getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
|
||||
_CSSCK = _BR;
|
||||
|
||||
uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
|
||||
#if USE_CONT == 1
|
||||
ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
#endif
|
||||
|
||||
if(_DBR) {
|
||||
ctar0 |= SPI_CTAR_DBR;
|
||||
ctar1 |= SPI_CTAR_DBR;
|
||||
}
|
||||
|
||||
update_ctars(ctar0,ctar1);
|
||||
}
|
||||
|
||||
void inline save_spi_state() __attribute__ ((always_inline)) {
|
||||
// save ctar data
|
||||
gState._ctar0 = SPIX.CTAR0;
|
||||
gState._ctar1 = SPIX.CTAR1;
|
||||
|
||||
// save data for the not-us pins
|
||||
gState.pins[0] = CORE_PIN7_CONFIG;
|
||||
gState.pins[1] = CORE_PIN11_CONFIG;
|
||||
gState.pins[2] = CORE_PIN13_CONFIG;
|
||||
gState.pins[3] = CORE_PIN14_CONFIG;
|
||||
}
|
||||
|
||||
void inline restore_spi_state() __attribute__ ((always_inline)) {
|
||||
// restore ctar data
|
||||
update_ctars(gState._ctar0,gState._ctar1);
|
||||
|
||||
// restore data for the not-us pins (not necessary because disable_pins will do this)
|
||||
// CORE_PIN7_CONFIG = gState.pins[0];
|
||||
// CORE_PIN11_CONFIG = gState.pins[1];
|
||||
// CORE_PIN13_CONFIG = gState.pins[2];
|
||||
// CORE_PIN14_CONFIG = gState.pins[3];
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
ARMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
void init() {
|
||||
// set the pins to output
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// Enable SPI0 clock
|
||||
uint32_t sim6 = SIM_SCGC6;
|
||||
if((SPI_t*)pSPIX == &KINETISK_SPI0) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI0)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
} else if((SPI_t*)pSPIX == &SPI1) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI1)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure SPI as the master and enable
|
||||
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
|
||||
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
|
||||
|
||||
// pin/spi configuration happens on select
|
||||
}
|
||||
|
||||
static void waitFully() __attribute__((always_inline)) {
|
||||
// Wait for the last byte to get shifted into the register
|
||||
bool empty = false;
|
||||
|
||||
do {
|
||||
cli();
|
||||
if ((SPIX.SR & 0xF000) > 0) {
|
||||
// reset the TCF flag
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
} else {
|
||||
empty = true;
|
||||
}
|
||||
sei();
|
||||
} while (!empty);
|
||||
|
||||
// wait for the TCF flag to get set
|
||||
while (!(SPIX.SR & SPI_SR_TCF));
|
||||
SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
|
||||
}
|
||||
|
||||
static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); }
|
||||
static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); }
|
||||
static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); }
|
||||
|
||||
enum ECont { CONT, NOCONT };
|
||||
enum EWait { PRE, POST, NONE };
|
||||
enum ELast { NOTLAST, LAST };
|
||||
|
||||
#if USE_CONT == 1
|
||||
#define CM CONT
|
||||
#else
|
||||
#define CM NOCONT
|
||||
#endif
|
||||
#define WM PRE
|
||||
|
||||
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
|
||||
public:
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
cli();
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
sei();
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
cli();
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(0) | (b & 0xFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
sei();
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
};
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; sei(); wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
|
||||
static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei();}
|
||||
|
||||
static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); wait(); }
|
||||
static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
writeWord( (b & (1 << BIT)) != 0);
|
||||
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
save_spi_state();
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
setSPIRate();
|
||||
enable_pins();
|
||||
}
|
||||
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
disable_pins();
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
restore_spi_state();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <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();
|
||||
}
|
||||
|
||||
void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
// Setup the pixel controller
|
||||
if((FLAGS & FLAG_START_BIT) == 0) {
|
||||
//If no start bit stupiditiy, write out as many 16-bit blocks as we can
|
||||
while(pixels.has(2)) {
|
||||
// Load and write out the first two bytes
|
||||
if(WM == NONE) { wait1(); }
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
|
||||
// Load and write out the next two bytes (step dithering, advance data in between since we
|
||||
// cross pixels here)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0()));
|
||||
|
||||
// Load and write out the next two bytes
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2()));
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
if(pixels.has(1)) {
|
||||
if(WM == NONE) { wait1(); }
|
||||
// write out the rest as alternating 16/8-bit blocks (likely to be just one)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
} else if(FLAGS & FLAG_START_BIT) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
writeWord( 0x100 | D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
|
||||
// restore ctar1
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
release();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_K20_H
|
||||
#define __INC_LED_SYSDEFS_ARM_K20_H
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 96000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
extern volatile uint32_t systick_millis_count;
|
||||
# define MS_COUNTER systick_millis_count
|
||||
|
||||
|
||||
// Default to using PROGMEM, since TEENSY3 provides it
|
||||
// even though all it does is ignore it. Just being
|
||||
// conservative here in case TEENSY3 changes.
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef __INC_OCTOWS2811_CONTROLLER_H
|
||||
#define __INC_OCTOWS2811_CONTROLLER_H
|
||||
|
||||
#ifdef USE_OCTOWS2811
|
||||
|
||||
#include "OctoWS2811.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template<EOrder RGB_ORDER = GRB, uint8_t CHIP = WS2811_800kHz>
|
||||
class COctoWS2811Controller : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
|
||||
OctoWS2811 *pocto;
|
||||
uint8_t *drawbuffer,*framebuffer;
|
||||
|
||||
void _init(int nLeds) {
|
||||
if(pocto == NULL) {
|
||||
drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3);
|
||||
framebuffer = (uint8_t*)malloc(nLeds * 8 * 3);
|
||||
|
||||
// byte ordering is handled in show by the pixel controller
|
||||
int config = WS2811_RGB;
|
||||
config |= CHIP;
|
||||
|
||||
pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config);
|
||||
|
||||
pocto->begin();
|
||||
}
|
||||
}
|
||||
public:
|
||||
COctoWS2811Controller() { pocto = NULL; }
|
||||
virtual int size() { return CLEDController::size() * 8; }
|
||||
|
||||
virtual void init() { /* do nothing yet */ }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[8];
|
||||
uint32_t raw[2];
|
||||
} Lines;
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> & pixels) {
|
||||
_init(pixels.size());
|
||||
|
||||
uint8_t *pData = drawbuffer;
|
||||
while(pixels.has(1)) {
|
||||
Lines b;
|
||||
|
||||
for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale0(i); }
|
||||
transpose8x1_MSB(b.bytes,pData); pData += 8;
|
||||
for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale1(i); }
|
||||
transpose8x1_MSB(b.bytes,pData); pData += 8;
|
||||
for(int i = 0; i < 8; ++i) { b.bytes[i] = pixels.loadAndScale2(i); }
|
||||
transpose8x1_MSB(b.bytes,pData); pData += 8;
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
pocto->show();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef __INC_SMARTMATRIX_T3_H
|
||||
#define __INC_SMARTMATRIX_T3_H
|
||||
|
||||
#ifdef SmartMatrix_h
|
||||
#include <SmartMatrix.h>
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
extern SmartMatrix *pSmartMatrix;
|
||||
|
||||
// note - dmx simple must be included before FastSPI for this code to be enabled
|
||||
class CSmartMatrixController : public CPixelLEDController<RGB_ORDER> {
|
||||
SmartMatrix matrix;
|
||||
|
||||
public:
|
||||
// initialize the LED controller
|
||||
virtual void init() {
|
||||
// Initialize 32x32 LED Matrix
|
||||
matrix.begin();
|
||||
matrix.setBrightness(255);
|
||||
matrix.setColorCorrection(ccNone);
|
||||
|
||||
// Clear screen
|
||||
clearLeds(0);
|
||||
matrix.swapBuffers();
|
||||
pSmartMatrix = &matrix;
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
if(SMART_MATRIX_CAN_TRIPLE_BUFFER) {
|
||||
rgb24 *md = matrix.getRealBackBuffer();
|
||||
} else {
|
||||
rgb24 *md = matrix.backBuffer();
|
||||
}
|
||||
while(pixels.has(1)) {
|
||||
md->red = pixels.loadAndScale0();
|
||||
md->green = pixels.loadAndScale1();
|
||||
md->blue = pixels.loadAndScale2();
|
||||
md++;
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
matrix.swapBuffers();
|
||||
if(SMART_MATRIX_CAN_TRIPLE_BUFFER && pixels.advanceBy() > 0) {
|
||||
matrix.setBackBuffer(pixels.mData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef __INC_WS2812SERIAL_CONTROLLER_H
|
||||
#define __INC_WS2812SERIAL_CONTROLLER_H
|
||||
|
||||
#ifdef USE_WS2812SERIAL
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template<int DATA_PIN, EOrder RGB_ORDER>
|
||||
class CWS2812SerialController : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
|
||||
WS2812Serial *pserial;
|
||||
uint8_t *drawbuffer,*framebuffer;
|
||||
|
||||
void _init(int nLeds) {
|
||||
if (pserial == NULL) {
|
||||
drawbuffer = (uint8_t*)malloc(nLeds * 3);
|
||||
framebuffer = (uint8_t*)malloc(nLeds * 12);
|
||||
pserial = new WS2812Serial(nLeds, framebuffer, drawbuffer, DATA_PIN, WS2812_RGB);
|
||||
pserial->begin();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CWS2812SerialController() { pserial = NULL; }
|
||||
|
||||
virtual void init() { /* do nothing yet */ }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> & pixels) {
|
||||
_init(pixels.size());
|
||||
|
||||
uint8_t *p = drawbuffer;
|
||||
|
||||
while(pixels.has(1)) {
|
||||
*p++ = pixels.loadAndScale0();
|
||||
*p++ = pixels.loadAndScale1();
|
||||
*p++ = pixels.loadAndScale2();
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
pserial->show();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // USE_WS2812SERIAL
|
||||
#endif // __INC_WS2812SERIAL_CONTROLLER_H
|
||||
@@ -0,0 +1,22 @@
|
||||
# FastLED Platform: Teensy 3.6 (K66)
|
||||
|
||||
Teensy 3.6 (MK66FX1M0) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_k66.h`: Aggregator; includes pin/SPI/clockless and reuses some K20 helpers.
|
||||
- `fastpin_arm_k66.h`: Pin helpers.
|
||||
- `fastspi_arm_k66.h`: SPI output backend.
|
||||
- `clockless_arm_k66.h`: Single-lane clockless driver (DWT counter).
|
||||
- `clockless_block_arm_k66.h`: Block/multi-lane variant.
|
||||
- `led_sysdefs_arm_k66.h`: System defines for K66.
|
||||
|
||||
Notes:
|
||||
- Similar to K20 timing model with higher clocks; observe interrupt thresholds for consistent output.
|
||||
- DWT cycle counter is used by clockless; ensure it is enabled early for precise timing.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `1` on some Teensy3 cores.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK`.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_K66_H
|
||||
#define __INC_CLOCKLESS_ARM_K66_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
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 data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
FASTLED_REGISTER data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
FASTLED_REGISTER data_t hi = *port | FastPin<DATA_PIN>::mask();
|
||||
FASTLED_REGISTER data_t lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,347 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_ARM_K66_H
|
||||
#define __INC_BLOCK_CLOCKLESS_ARM_K66_H
|
||||
|
||||
// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define PORTB_FIRST_PIN 0
|
||||
#define PORTC_FIRST_PIN 15
|
||||
#define PORTD_FIRST_PIN 2
|
||||
#define HAS_PORTDC 1
|
||||
|
||||
#define LANE_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
|
||||
#define PORT_SHIFT(P) ((P) << ((FIRST_PIN==0) ? 16 : 0))
|
||||
#define PORT_MASK PORT_SHIFT(LANE_MASK)
|
||||
|
||||
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
|
||||
#define USED_LANES ((FIRST_PIN!=15) ? MIN(LANES,8) : MIN(LANES,12))
|
||||
|
||||
#include <kinetis.h>
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, LANE_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, LANE_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
|
||||
switch(USED_LANES) {
|
||||
case 12: FastPin<30>::setOutput();
|
||||
case 11: FastPin<29>::setOutput();
|
||||
case 10: FastPin<27>::setOutput();
|
||||
case 9: FastPin<28>::setOutput();
|
||||
case 8: FastPin<12>::setOutput();
|
||||
case 7: FastPin<11>::setOutput();
|
||||
case 6: FastPin<13>::setOutput();
|
||||
case 5: FastPin<10>::setOutput();
|
||||
case 4: FastPin<9>::setOutput();
|
||||
case 3: FastPin<23>::setOutput();
|
||||
case 2: FastPin<22>::setOutput();
|
||||
case 1: FastPin<15>::setOutput();
|
||||
}
|
||||
} else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
|
||||
switch(USED_LANES) {
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
} else if (FIRST_PIN == PORTB_FIRST_PIN) { // PORTB
|
||||
switch (USED_LANES) {
|
||||
case 8: FastPin<45>::setOutput();
|
||||
case 7: FastPin<44>::setOutput();
|
||||
case 6: FastPin<46>::setOutput();
|
||||
case 5: FastPin<43>::setOutput();
|
||||
case 4: FastPin<30>::setOutput();
|
||||
case 3: FastPin<29>::setOutput();
|
||||
case 2: FastPin<1>::setOutput();
|
||||
case 1: FastPin<0>::setOutput();
|
||||
}
|
||||
}
|
||||
mPinMask = FastPin<FIRST_PIN>::mask();
|
||||
mPort = FastPin<FIRST_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[12];
|
||||
uint16_t shorts[6];
|
||||
uint32_t raw[3];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController<RGB_ORDER, LANES, LANE_MASK> &pixels) { // , FASTLED_REGISTER uint32_t & b2) {
|
||||
FASTLED_REGISTER Lines b2;
|
||||
if(USED_LANES>8) {
|
||||
transpose8<1,2>(b.bytes,b2.bytes);
|
||||
transpose8<1,2>(b.bytes+8,b2.bytes+1);
|
||||
} else {
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
}
|
||||
FASTLED_REGISTER uint8_t d = pixels.template getd<PX>(pixels);
|
||||
FASTLED_REGISTER uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(FASTLED_REGISTER uint32_t i = 0; i < (USED_LANES/2); ++i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
*FastPin<FIRST_PIN>::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(USED_LANES/2),d,scale);
|
||||
}
|
||||
|
||||
// if folks use an odd numnber of lanes, get the last byte's value here
|
||||
if(USED_LANES & 0x01) {
|
||||
b.bytes[USED_LANES-1] = pixels.template loadAndScale<PX>(pixels,USED_LANES-1,d,scale);
|
||||
}
|
||||
|
||||
for(FASTLED_REGISTER uint32_t i = USED_LANES/2; i < 8; ++i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
// b2.bytes[0] = 0;
|
||||
*FastPin<FIRST_PIN>::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, LANE_MASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < USED_LANES; ++i) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
allpixels.stepDithering();
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
#define PMASK ((1<<(LANES))-1)
|
||||
#define PMASK_HI (PMASK>>8 & 0xFF)
|
||||
#define PMASK_LO (PMASK & 0xFF)
|
||||
|
||||
template <uint8_t LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
class SixteenWayInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PMASK> {
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!");
|
||||
// FastPin<30>::setOutput();
|
||||
// FastPin<29>::setOutput();
|
||||
// FastPin<27>::setOutput();
|
||||
// FastPin<28>::setOutput();
|
||||
switch(LANES) {
|
||||
case 16: FastPin<12>::setOutput();
|
||||
case 15: FastPin<11>::setOutput();
|
||||
case 14: FastPin<13>::setOutput();
|
||||
case 13: FastPin<10>::setOutput();
|
||||
case 12: FastPin<9>::setOutput();
|
||||
case 11: FastPin<23>::setOutput();
|
||||
case 10: FastPin<22>::setOutput();
|
||||
case 9: FastPin<15>::setOutput();
|
||||
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PMASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[16];
|
||||
uint16_t shorts[8];
|
||||
uint32_t raw[4];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER Lines & b, PixelController<RGB_ORDER,LANES, PMASK> &pixels) { // , FASTLED_REGISTER uint32_t & b2) {
|
||||
FASTLED_REGISTER Lines b2;
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
transpose8x1(b.bytes+8,b2.bytes+8);
|
||||
FASTLED_REGISTER uint8_t d = pixels.template getd<PX>(pixels);
|
||||
FASTLED_REGISTER uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(FASTLED_REGISTER uint32_t i = 0; (i < LANES) && (i < 8); ++i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
if(LANES==16 || (LANES>8 && ((i+8) < LANES))) {
|
||||
b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, PMASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < LANES; ++i) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
allpixels.stepDithering();
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
sei();
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef __INC_FASTLED_ARM_K66_H
|
||||
#define __INC_FASTLED_ARM_K66_H
|
||||
|
||||
// Include the k66 headers
|
||||
#include "fastpin_arm_k66.h"
|
||||
#include "fastspi_arm_k66.h"
|
||||
#include "../k20/octows2811_controller.h"
|
||||
#include "../k20/ws2812serial_controller.h"
|
||||
#include "../k20/smartmatrix_t3.h"
|
||||
#include "clockless_arm_k66.h"
|
||||
#include "clockless_block_arm_k66.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
#ifndef __FASTPIN_ARM_K66_H
|
||||
#define __FASTPIN_ARM_K66_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
|
||||
/// does a poor job of optimizing around these accesses so they are not being used just yet.
|
||||
template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
|
||||
};
|
||||
|
||||
// Macros for k20 pin access/definition
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE reg32_t r() { return T; } \
|
||||
template<int BIT> static FASTLED_FORCE_INLINE ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; \
|
||||
template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
|
||||
|
||||
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
#define MAX_PIN 63
|
||||
_FL_DEFPIN( 0, 16, B); _FL_DEFPIN( 1, 17, B); _FL_DEFPIN( 2, 0, D); _FL_DEFPIN( 3, 12, A);
|
||||
_FL_DEFPIN( 4, 13, A); _FL_DEFPIN( 5, 7, D); _FL_DEFPIN( 6, 4, D); _FL_DEFPIN( 7, 2, D);
|
||||
_FL_DEFPIN( 8, 3, D); _FL_DEFPIN( 9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
|
||||
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
|
||||
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
|
||||
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
|
||||
_FL_DEFPIN(24, 26, E); _FL_DEFPIN(25, 5, A); _FL_DEFPIN(26, 14, A); _FL_DEFPIN(27, 15, A);
|
||||
_FL_DEFPIN(28, 16, A); _FL_DEFPIN(29, 18, B); _FL_DEFPIN(30, 19, B); _FL_DEFPIN(31, 10, B);
|
||||
_FL_DEFPIN(32, 11, B); _FL_DEFPIN(33, 24, E); _FL_DEFPIN(34, 25, E); _FL_DEFPIN(35, 8, C);
|
||||
_FL_DEFPIN(36, 9, C); _FL_DEFPIN(37, 10, C); _FL_DEFPIN(38, 11, C); _FL_DEFPIN(39, 17, A);
|
||||
_FL_DEFPIN(40, 28, A); _FL_DEFPIN(41, 29, A); _FL_DEFPIN(42, 26, A); _FL_DEFPIN(43, 20, B);
|
||||
_FL_DEFPIN(44, 22, B); _FL_DEFPIN(45, 23, B); _FL_DEFPIN(46, 21, B); _FL_DEFPIN(47, 8, D);
|
||||
_FL_DEFPIN(48, 9, D); _FL_DEFPIN(49, 4, B); _FL_DEFPIN(50, 5, B); _FL_DEFPIN(51, 14, D);
|
||||
_FL_DEFPIN(52, 13, D); _FL_DEFPIN(53, 12, D); _FL_DEFPIN(54, 15, D); _FL_DEFPIN(55, 11, D);
|
||||
_FL_DEFPIN(56, 10, E); _FL_DEFPIN(57, 11, E); _FL_DEFPIN(58, 0, E); _FL_DEFPIN(59, 1, E);
|
||||
_FL_DEFPIN(60, 2, E); _FL_DEFPIN(61, 3, E); _FL_DEFPIN(62, 4, E); _FL_DEFPIN(63, 5, E);
|
||||
|
||||
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
|
||||
#define SPI2_DATA 7
|
||||
#define SPI2_CLOCK 14
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_K66
|
||||
@@ -0,0 +1,470 @@
|
||||
#ifndef __INC_FASTSPI_ARM_H
|
||||
#define __INC_FASTSPI_ARM_H
|
||||
|
||||
//
|
||||
// copied from k20 code
|
||||
// changed SPI1 define to KINETISK_SPI1
|
||||
// TODO: add third alternative MOSI pin (28) and CLOCK pin (27)
|
||||
// TODO: add alternative pins for SPI1
|
||||
// TODO: add SPI2 output
|
||||
//
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
// Version 1.20 renamed SPI_t to KINETISK_SPI_t
|
||||
#if TEENSYDUINO >= 120
|
||||
#define SPI_t KINETISK_SPI_t
|
||||
#endif
|
||||
|
||||
#ifndef KINETISK_SPI0
|
||||
#define KINETISK_SPI0 SPI0
|
||||
#endif
|
||||
|
||||
#ifndef SPI_PUSHR_CONT
|
||||
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
|
||||
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
|
||||
#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ
|
||||
#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT
|
||||
#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X)
|
||||
#endif
|
||||
|
||||
// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now,
|
||||
// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit
|
||||
// set (the zero-th bit). Unclear if I will want this to change at some point.
|
||||
template<int VAL, int BIT> class BitWork {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
|
||||
};
|
||||
|
||||
template<int VAL> class BitWork<VAL, 0> {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return 0; }
|
||||
};
|
||||
|
||||
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
|
||||
|
||||
#define USE_CONT 0
|
||||
// intra-frame backup data
|
||||
struct SPIState {
|
||||
uint32_t _ctar0,_ctar1;
|
||||
uint32_t pins[4];
|
||||
};
|
||||
|
||||
// extern SPIState gState;
|
||||
|
||||
|
||||
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
|
||||
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
|
||||
switch(VAL) {
|
||||
// Handle the dbl clock cases
|
||||
case 0: case 1:
|
||||
case 2: preScalar = 0; scalar = 0; dbl = 1; break;
|
||||
case 3: preScalar = 1; scalar = 0; dbl = 1; break;
|
||||
case 5: preScalar = 2; scalar = 0; dbl = 1; break;
|
||||
case 7: preScalar = 3; scalar = 0; dbl = 1; break;
|
||||
|
||||
// Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
|
||||
// below)
|
||||
case 9: preScalar = 1; scalar = 2; dbl = 1; break;
|
||||
case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 15: preScalar = 2; scalar = 2; dbl = 1; break;
|
||||
case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
|
||||
case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
|
||||
default: {
|
||||
int p2 = BitWork<VAL/2, 15>::highestBit();
|
||||
int p3 = BitWork<VAL/3, 15>::highestBit();
|
||||
int p5 = BitWork<VAL/5, 15>::highestBit();
|
||||
int p7 = BitWork<VAL/7, 15>::highestBit();
|
||||
|
||||
int w2 = 2 * (1 << p2);
|
||||
int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
|
||||
int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
|
||||
int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
|
||||
|
||||
int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
|
||||
|
||||
if(w2 == maxval) { preScalar = 0; scalar = p2; }
|
||||
else if(w3 == maxval) { preScalar = 1; scalar = p3; }
|
||||
else if(w5 == maxval) { preScalar = 2; scalar = p5; }
|
||||
else if(w7 == maxval) { preScalar = 3; scalar = p7; }
|
||||
|
||||
dbl = 0;
|
||||
if(scalar == 0) { dbl = 1; }
|
||||
else if(scalar < 3) { --scalar; }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#define SPIX (*(SPI_t*)pSPIX)
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
|
||||
class ARMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
SPIState gState;
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
|
||||
// and likewise enabling pin 14 disables pin 13 (and vice versa)
|
||||
inline void enable_pins(void) __attribute__((always_inline)) {
|
||||
//serial_print("enable_pins\n");
|
||||
switch(_DATA_PIN) {
|
||||
case 7:
|
||||
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 11:
|
||||
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13:
|
||||
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 14:
|
||||
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
|
||||
inline void disable_pins(void) __attribute__((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
|
||||
if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if(mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR0 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar;
|
||||
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR1 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR1 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR1 = ctar;
|
||||
SPIX.MCR = mcr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
|
||||
uint32_t _PBR = 0;
|
||||
uint32_t _BR = 0;
|
||||
uint32_t _CSSCK = 0;
|
||||
uint32_t _DBR = 0;
|
||||
|
||||
// if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
|
||||
// else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
|
||||
|
||||
getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
|
||||
_CSSCK = _BR;
|
||||
|
||||
uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
|
||||
#if USE_CONT == 1
|
||||
ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
#endif
|
||||
|
||||
if(_DBR) {
|
||||
ctar0 |= SPI_CTAR_DBR;
|
||||
ctar1 |= SPI_CTAR_DBR;
|
||||
}
|
||||
|
||||
update_ctars(ctar0,ctar1);
|
||||
}
|
||||
|
||||
void inline save_spi_state() __attribute__ ((always_inline)) {
|
||||
// save ctar data
|
||||
gState._ctar0 = SPIX.CTAR0;
|
||||
gState._ctar1 = SPIX.CTAR1;
|
||||
|
||||
// save data for the not-us pins
|
||||
gState.pins[0] = CORE_PIN7_CONFIG;
|
||||
gState.pins[1] = CORE_PIN11_CONFIG;
|
||||
gState.pins[2] = CORE_PIN13_CONFIG;
|
||||
gState.pins[3] = CORE_PIN14_CONFIG;
|
||||
}
|
||||
|
||||
void inline restore_spi_state() __attribute__ ((always_inline)) {
|
||||
// restore ctar data
|
||||
update_ctars(gState._ctar0,gState._ctar1);
|
||||
|
||||
// restore data for the not-us pins (not necessary because disable_pins will do this)
|
||||
// CORE_PIN7_CONFIG = gState.pins[0];
|
||||
// CORE_PIN11_CONFIG = gState.pins[1];
|
||||
// CORE_PIN13_CONFIG = gState.pins[2];
|
||||
// CORE_PIN14_CONFIG = gState.pins[3];
|
||||
}
|
||||
|
||||
public:
|
||||
ARMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
|
||||
void init() {
|
||||
// set the pins to output
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// Enable SPI0 clock
|
||||
uint32_t sim6 = SIM_SCGC6;
|
||||
if((SPI_t*)pSPIX == &KINETISK_SPI0) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI0)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
} else if((SPI_t*)pSPIX == &KINETISK_SPI1) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI1)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure SPI as the master and enable
|
||||
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
|
||||
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
|
||||
|
||||
// pin/spi configuration happens on select
|
||||
}
|
||||
|
||||
static void waitFully() __attribute__((always_inline)) {
|
||||
// Wait for the last byte to get shifted into the register
|
||||
bool empty = false;
|
||||
|
||||
do {
|
||||
cli();
|
||||
if ((SPIX.SR & 0xF000) > 0) {
|
||||
// reset the TCF flag
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
} else {
|
||||
empty = true;
|
||||
}
|
||||
sei();
|
||||
} while (!empty);
|
||||
|
||||
// wait for the TCF flag to get set
|
||||
while (!(SPIX.SR & SPI_SR_TCF));
|
||||
SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
|
||||
}
|
||||
|
||||
static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); }
|
||||
static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); }
|
||||
static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); }
|
||||
|
||||
enum ECont { CONT, NOCONT };
|
||||
enum EWait { PRE, POST, NONE };
|
||||
enum ELast { NOTLAST, LAST };
|
||||
|
||||
#if USE_CONT == 1
|
||||
#define CM CONT
|
||||
#else
|
||||
#define CM NOCONT
|
||||
#endif
|
||||
#define WM PRE
|
||||
|
||||
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
|
||||
public:
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(0) | (b & 0xFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
};
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;wait(); }
|
||||
static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
writeWord( (b & (1 << BIT)) != 0);
|
||||
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
save_spi_state();
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
setSPIRate();
|
||||
enable_pins();
|
||||
}
|
||||
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
disable_pins();
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
restore_spi_state();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <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();
|
||||
}
|
||||
|
||||
void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
// Setup the pixel controller
|
||||
if((FLAGS & FLAG_START_BIT) == 0) {
|
||||
//If no start bit stupiditiy, write out as many 16-bit blocks as we can
|
||||
while(pixels.has(2)) {
|
||||
// Load and write out the first two bytes
|
||||
if(WM == NONE) { wait1(); }
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
|
||||
// Load and write out the next two bytes (step dithering, advance data in between since we
|
||||
// cross pixels here)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0()));
|
||||
|
||||
// Load and write out the next two bytes
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2()));
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
if(pixels.has(1)) {
|
||||
if(WM == NONE) { wait1(); }
|
||||
// write out the rest as alternating 16/8-bit blocks (likely to be just one)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
} else if(FLAGS & FLAG_START_BIT) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
writeWord( 0x100 | D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
|
||||
// restore ctar1
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
release();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_K66_H
|
||||
#define __INC_LED_SYSDEFS_ARM_K66_H
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 192000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
extern volatile uint32_t systick_millis_count;
|
||||
# define MS_COUNTER systick_millis_count
|
||||
|
||||
|
||||
// Default to using PROGMEM, since TEENSY3 provides it
|
||||
// even though all it does is ignore it. Just being
|
||||
// conservative here in case TEENSY3 changes.
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
# FastLED Platform: Teensy LC (KL26)
|
||||
|
||||
Teensy LC (MKL26Z64) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_kl26.h`: Aggregator; includes pin/SPI/clockless.
|
||||
- `fastpin_arm_kl26.h`: Pin helpers.
|
||||
- `fastspi_arm_kl26.h`: SPI output backend.
|
||||
- `clockless_arm_kl26.h`: Clockless driver for LC.
|
||||
- `ws2812serial_controller.h` (via k20 include): Serial WS2812 controller reuse.
|
||||
- `led_sysdefs_arm_kl26.h`: System defines for KL26.
|
||||
|
||||
Notes:
|
||||
- LC has tighter timing/memory limits; keep critical sections minimal.
|
||||
- Verify `FASTLED_USE_PROGMEM` and interrupt policy per toolchain; defaults may differ between cores.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `1` on LC cores.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK`.
|
||||
- **`FASTLED_SPI_BYTE_ONLY`**: Present in sysdefs indicating SPI byte granularity optimization.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_KL26
|
||||
#define __INC_CLOCKLESS_ARM_KL26
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "eorder.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
#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 = 280>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
if(!clocks) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
clocks = showRGBInternal(pixels);
|
||||
}
|
||||
long microsTaken = CLKS_TO_MICROS(clocks * ((T1 + T2 + T3) * 24));
|
||||
MS_COUNTER += (microsTaken / 1000);
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mColorAdjustment.premixed[0];
|
||||
data.s[1] = pixels.mColorAdjustment.premixed[1];
|
||||
data.s[2] = pixels.mColorAdjustment.premixed[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
return showLedData<4,8,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
// return 0; // 0x00FFFFFF - _VAL;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_CLOCKLESS_ARM_KL26
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef __INC_FASTLED_ARM_KL26_H
|
||||
#define __INC_FASTLED_ARM_KL26_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_kl26.h"
|
||||
#include "fastspi_arm_kl26.h"
|
||||
#include "clockless_arm_kl26.h"
|
||||
#include "../k20/ws2812serial_controller.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef __FASTPIN_ARM_KL26_H
|
||||
#define __FASTPIN_ARM_KL26_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be sloightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for teensy LC style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
// Macros for kl26 pin access/definition
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE reg32_t r() { return T; } \
|
||||
template<int BIT> static FASTLED_FORCE_INLINE ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(FGPIO ## L ## _PDOR); _RD32(FGPIO ## L ## _PSOR); _RD32(FGPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(FGPIO ## L ## _PDIR); _RD32(FGPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(FGPIO ## L ## _PDOR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(FGPIO ## L ## _PDOR), _R(FGPIO ## L ## _PSOR), _R(FGPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(FGPIO ## L ## _PDIR), _R(FGPIO ## L ## _PDDR)> {}; \
|
||||
/* template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; */
|
||||
|
||||
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(FASTLED_TEENSYLC) && defined(CORE_TEENSY)
|
||||
|
||||
#define MAX_PIN 26
|
||||
_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 1, A);
|
||||
_FL_DEFPIN(4, 2, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D);
|
||||
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
|
||||
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
|
||||
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
|
||||
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
|
||||
_FL_DEFPIN(24, 20, E); _FL_DEFPIN(25, 21, E); _FL_DEFPIN(26, 30, E);
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
// #define SPI1 (*(SPI_t *)0x4002D000)
|
||||
|
||||
#define SPI2_DATA 0
|
||||
#define SPI2_CLOCK 20
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_K20
|
||||
@@ -0,0 +1,252 @@
|
||||
#ifndef __INC_FASTSPI_ARM_KL26_H
|
||||
#define __INC_FASTSPI_ARM_KL26_h
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template <int VAL> void getScalars(uint8_t & sppr, uint8_t & spr) {
|
||||
if(VAL > 4096) { sppr=7; spr=8; }
|
||||
else if(VAL > 3584) { sppr=6; spr=8; }
|
||||
else if(VAL > 3072) { sppr=5; spr=8; }
|
||||
else if(VAL > 2560) { sppr=4; spr=8; }
|
||||
else if(VAL > 2048) { sppr=7; spr=7; }
|
||||
else if(VAL > 2048) { sppr=3; spr=8; }
|
||||
else if(VAL > 1792) { sppr=6; spr=7; }
|
||||
else if(VAL > 1536) { sppr=5; spr=7; }
|
||||
else if(VAL > 1536) { sppr=2; spr=8; }
|
||||
else if(VAL > 1280) { sppr=4; spr=7; }
|
||||
else if(VAL > 1024) { sppr=7; spr=6; }
|
||||
else if(VAL > 1024) { sppr=3; spr=7; }
|
||||
else if(VAL > 1024) { sppr=1; spr=8; }
|
||||
else if(VAL > 896) { sppr=6; spr=6; }
|
||||
else if(VAL > 768) { sppr=5; spr=6; }
|
||||
else if(VAL > 768) { sppr=2; spr=7; }
|
||||
else if(VAL > 640) { sppr=4; spr=6; }
|
||||
else if(VAL > 512) { sppr=7; spr=5; }
|
||||
else if(VAL > 512) { sppr=3; spr=6; }
|
||||
else if(VAL > 512) { sppr=1; spr=7; }
|
||||
else if(VAL > 512) { sppr=0; spr=8; }
|
||||
else if(VAL > 448) { sppr=6; spr=5; }
|
||||
else if(VAL > 384) { sppr=5; spr=5; }
|
||||
else if(VAL > 384) { sppr=2; spr=6; }
|
||||
else if(VAL > 320) { sppr=4; spr=5; }
|
||||
else if(VAL > 256) { sppr=7; spr=4; }
|
||||
else if(VAL > 256) { sppr=3; spr=5; }
|
||||
else if(VAL > 256) { sppr=1; spr=6; }
|
||||
else if(VAL > 256) { sppr=0; spr=7; }
|
||||
else if(VAL > 224) { sppr=6; spr=4; }
|
||||
else if(VAL > 192) { sppr=5; spr=4; }
|
||||
else if(VAL > 192) { sppr=2; spr=5; }
|
||||
else if(VAL > 160) { sppr=4; spr=4; }
|
||||
else if(VAL > 128) { sppr=7; spr=3; }
|
||||
else if(VAL > 128) { sppr=3; spr=4; }
|
||||
else if(VAL > 128) { sppr=1; spr=5; }
|
||||
else if(VAL > 128) { sppr=0; spr=6; }
|
||||
else if(VAL > 112) { sppr=6; spr=3; }
|
||||
else if(VAL > 96) { sppr=5; spr=3; }
|
||||
else if(VAL > 96) { sppr=2; spr=4; }
|
||||
else if(VAL > 80) { sppr=4; spr=3; }
|
||||
else if(VAL > 64) { sppr=7; spr=2; }
|
||||
else if(VAL > 64) { sppr=3; spr=3; }
|
||||
else if(VAL > 64) { sppr=1; spr=4; }
|
||||
else if(VAL > 64) { sppr=0; spr=5; }
|
||||
else if(VAL > 56) { sppr=6; spr=2; }
|
||||
else if(VAL > 48) { sppr=5; spr=2; }
|
||||
else if(VAL > 48) { sppr=2; spr=3; }
|
||||
else if(VAL > 40) { sppr=4; spr=2; }
|
||||
else if(VAL > 32) { sppr=7; spr=1; }
|
||||
else if(VAL > 32) { sppr=3; spr=2; }
|
||||
else if(VAL > 32) { sppr=1; spr=3; }
|
||||
else if(VAL > 32) { sppr=0; spr=4; }
|
||||
else if(VAL > 28) { sppr=6; spr=1; }
|
||||
else if(VAL > 24) { sppr=5; spr=1; }
|
||||
else if(VAL > 24) { sppr=2; spr=2; }
|
||||
else if(VAL > 20) { sppr=4; spr=1; }
|
||||
else if(VAL > 16) { sppr=7; spr=0; }
|
||||
else if(VAL > 16) { sppr=3; spr=1; }
|
||||
else if(VAL > 16) { sppr=1; spr=2; }
|
||||
else if(VAL > 16) { sppr=0; spr=3; }
|
||||
else if(VAL > 14) { sppr=6; spr=0; }
|
||||
else if(VAL > 12) { sppr=5; spr=0; }
|
||||
else if(VAL > 12) { sppr=2; spr=1; }
|
||||
else if(VAL > 10) { sppr=4; spr=0; }
|
||||
else if(VAL > 8) { sppr=3; spr=0; }
|
||||
else if(VAL > 8) { sppr=1; spr=1; }
|
||||
else if(VAL > 8) { sppr=0; spr=2; }
|
||||
else if(VAL > 6) { sppr=2; spr=0; }
|
||||
else if(VAL > 4) { sppr=1; spr=0; }
|
||||
else if(VAL > 4) { sppr=0; spr=1; }
|
||||
else /* if(VAL > 2) */ { sppr=0; spr=0; }
|
||||
}
|
||||
|
||||
|
||||
#define SPIX (*(KINETISL_SPI_t*)pSPIX)
|
||||
#define ARM_HARDWARE_SPI
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
|
||||
class ARMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
static inline void enable_pins(void) __attribute__((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 0: CORE_PIN0_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 1: CORE_PIN1_CONFIG = PORT_PCR_MUX(5); break;
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 8: CORE_PIN8_CONFIG = PORT_PCR_MUX(5); break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 12: CORE_PIN12_CONFIG = PORT_PCR_MUX(5); break;
|
||||
case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void disable_pins(void) __attribute((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 0: CORE_PIN0_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 1: CORE_PIN1_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 8: CORE_PIN8_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 12: CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 21: CORE_PIN21_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 20: CORE_PIN20_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
}
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
uint8_t sppr, spr;
|
||||
getScalars<_SPI_CLOCK_DIVIDER>(sppr, spr);
|
||||
|
||||
// Set the speed
|
||||
SPIX.BR = SPI_BR_SPPR(sppr) | SPI_BR_SPR(spr);
|
||||
|
||||
// Also, force 8 bit transfers (don't want to juggle 8/16 since that flushes the world)
|
||||
SPIX.C2 = 0;
|
||||
SPIX.C1 |= SPI_C1_SPE;
|
||||
}
|
||||
|
||||
public:
|
||||
ARMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// Enable the SPI clocks
|
||||
uint32_t sim4 = SIM_SCGC4;
|
||||
if ((pSPIX == 0x40076000) && !(sim4 & SIM_SCGC4_SPI0)) {
|
||||
SIM_SCGC4 = sim4 | SIM_SCGC4_SPI0;
|
||||
}
|
||||
|
||||
if ( (pSPIX == 0x40077000) && !(sim4 & SIM_SCGC4_SPI1)) {
|
||||
SIM_SCGC4 = sim4 | SIM_SCGC4_SPI1;
|
||||
}
|
||||
|
||||
SPIX.C1 = SPI_C1_MSTR | SPI_C1_SPE;
|
||||
SPIX.C2 = 0;
|
||||
SPIX.BR = SPI_BR_SPPR(1) | SPI_BR_SPR(0);
|
||||
}
|
||||
|
||||
// latch the CS select
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
setSPIRate();
|
||||
enable_pins();
|
||||
}
|
||||
|
||||
|
||||
// release the CS select
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
disable_pins();
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
}
|
||||
|
||||
// Wait for the world to be clear
|
||||
static void wait() __attribute__((always_inline)) { while(!(SPIX.S & SPI_S_SPTEF)); }
|
||||
|
||||
// wait until all queued up data has been written
|
||||
void waitFully() { wait(); }
|
||||
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.DL = b; }
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w & 0xFF); }
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
setSPIRate();
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
|
||||
setSPIRate();
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
int len = pixels.mLen;
|
||||
|
||||
select();
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
} else {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_KL26_H
|
||||
#define __INC_LED_SYSDEFS_ARM_KL26_H
|
||||
|
||||
#define FASTLED_TEENSYLC
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
#define FASTLED_ARM_M0_PLUS
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
#define FASTLED_SPI_BYTE_ONLY
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 96000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Define VARIANT_MCK for timing calculations
|
||||
#ifndef VARIANT_MCK
|
||||
#define VARIANT_MCK F_CPU
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
extern volatile uint32_t systick_millis_count;
|
||||
# define MS_COUNTER systick_millis_count
|
||||
|
||||
// Default to using PROGMEM since TEENSYLC provides it
|
||||
// even though all it does is ignore it. Just being
|
||||
// conservative here in case TEENSYLC changes.
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
// Include required FastLED headers
|
||||
#include "fl/namespace.h"
|
||||
#include "eorder.h"
|
||||
#include "fastled_delay.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// ARM Cortex-M33 DWT (Data Watchpoint and Trace) registers for cycle-accurate timing
|
||||
#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control
|
||||
#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
|
||||
#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register
|
||||
#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
|
||||
#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register
|
||||
|
||||
// Enable clockless LED support for MGM240
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
/// @brief ARM Cortex-M33 clockless LED controller for MGM240
|
||||
///
|
||||
/// This implementation provides cycle-accurate timing for driving clockless LEDs
|
||||
/// using the ARM DWT (Data Watchpoint and Trace) unit. The controller is generic
|
||||
/// and supports any LED chipset through the T1/T2/T3 timing parameters.
|
||||
///
|
||||
/// Key features:
|
||||
/// - DWT-based cycle-accurate timing (no compiler-dependent delays)
|
||||
/// - FreeRTOS task scheduler safety
|
||||
/// - Atomic GPIO operations using Silicon Labs DOUTSET/DOUTCLR registers
|
||||
/// - Support for all FastLED chipsets: WS2812, SK6812, WS2815, etc.
|
||||
/// - Interrupt-aware operation with configurable thresholds
|
||||
///
|
||||
/// @tparam DATA_PIN Arduino pin number for LED data line
|
||||
/// @tparam T1 High time for '1' bit in CPU cycles
|
||||
/// @tparam T2 High time for '0' bit in CPU cycles
|
||||
/// @tparam T3 Low time for both bits in CPU cycles
|
||||
/// @tparam RGB_ORDER Color ordering (e.g., GRB for WS2812)
|
||||
/// @tparam XTRA0 Extra bits per color channel (0-4 typically)
|
||||
/// Adds additional bits beyond the standard 8 bits per color channel.
|
||||
/// Used by some LED chipsets or for timing adjustments:
|
||||
/// - 0: Standard 8 bits per channel (most chipsets: WS2812, SK6812)
|
||||
/// - 1-4: Extra bits for special chipsets or timing fine-tuning
|
||||
/// Total bits per channel = 8 + XTRA0
|
||||
/// @tparam FLIP Bit order flip flag
|
||||
/// @tparam WAIT_TIME Minimum wait time between updates (microseconds)
|
||||
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
/// @brief Initialize the LED controller
|
||||
/// Sets up the data pin as output and caches pin mask and port address
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
/// @brief Get maximum refresh rate in Hz
|
||||
/// @return Maximum safe refresh rate (400 Hz for most applications)
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
/// @brief Output pixel data to LED strip
|
||||
/// @param pixels Pixel controller containing RGB data and scaling
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
// If timing was interrupted, wait and retry once
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
/// @brief Write multiple bits using cycle-accurate timing
|
||||
/// @tparam BITS Number of bits to write (typically 8+XTRA0)
|
||||
/// When XTRA0 > 0, writes additional bits beyond the standard 8 per channel.
|
||||
/// Extra bits are sent as '0' bits for timing or protocol requirements.
|
||||
/// @param next_mark Reference to next timing mark (DWT cycle count)
|
||||
/// @param port GPIO port pointer for fast bit manipulation
|
||||
/// @param hi Port value with data pin high
|
||||
/// @param lo Port value with data pin low
|
||||
/// @param b Reference to byte containing bits to send (MSB first)
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Internal RGB data output with DWT cycle-accurate timing
|
||||
/// @param pixels Pixel controller with RGB data
|
||||
/// @return DWT cycle count when completed (0 if interrupted)
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Enable ARM DWT cycle counter for precise timing
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
FASTLED_REGISTER data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
FASTLED_REGISTER data_t hi = *port | FastPin<DATA_PIN>::mask();
|
||||
FASTLED_REGISTER data_t lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte (R/G/B + XTRA0 extra bits), read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte (R/G/B + XTRA0 extra bits), read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte (R/G/B + XTRA0 extra bits), read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "platforms/arm/mgm240/led_sysdefs_arm_mgm240.h"
|
||||
#include "platforms/arm/mgm240/fastpin_arm_mgm240.h"
|
||||
#include "platforms/arm/mgm240/clockless_arm_mgm240.h"
|
||||
@@ -0,0 +1,25 @@
|
||||
// Only compile MGM240 code when building for MGM240 targets
|
||||
#if defined(ARDUINO_ARCH_SILABS)
|
||||
|
||||
#include "fastpin_arm_mgm240.h"
|
||||
|
||||
// Include Silicon Labs EMLIB GPIO for direct register access
|
||||
#include "em_gpio.h"
|
||||
#include "em_cmu.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Initialize GPIO clock (needed for Silicon Labs devices)
|
||||
void _mgm240_gpio_init() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
CMU_ClockEnable(cmuClock_GPIO, true);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // MGM240 target check
|
||||
@@ -0,0 +1,192 @@
|
||||
/// @file fastpin_arm_mgm240.h
|
||||
/// @brief FastPin implementation for Silicon Labs MGM240 ARM Cortex-M33
|
||||
///
|
||||
/// This implementation provides hardware-accelerated GPIO operations for the
|
||||
/// MGM240SD22VNA microcontroller using Silicon Labs EMLIB.
|
||||
///
|
||||
/// Key features:
|
||||
/// - Atomic GPIO operations using DOUTSET/DOUTCLR registers
|
||||
/// - Race condition-free pin manipulation in interrupt environments
|
||||
/// - Direct Silicon Labs EMLIB integration for optimal performance
|
||||
/// - Template-based compile-time optimization
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Include Silicon Labs EMLIB GPIO for direct register access
|
||||
#include "em_gpio.h"
|
||||
#include "em_cmu.h"
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// Forward declaration of base FastPin template
|
||||
template<uint8_t PIN> class FastPin;
|
||||
|
||||
/// Initialize GPIO clock (required for Silicon Labs EFM32/EFR32 devices)
|
||||
void _mgm240_gpio_init();
|
||||
|
||||
/// @brief GPIO port accessor structures
|
||||
/// These provide compile-time port resolution for template-based FastPin operations
|
||||
struct __generated_struct_GPIO_PORT_A {
|
||||
static constexpr GPIO_Port_TypeDef port() { return gpioPortA; }
|
||||
};
|
||||
|
||||
struct __generated_struct_GPIO_PORT_B {
|
||||
static constexpr GPIO_Port_TypeDef port() { return gpioPortB; }
|
||||
};
|
||||
|
||||
struct __generated_struct_GPIO_PORT_C {
|
||||
static constexpr GPIO_Port_TypeDef port() { return gpioPortC; }
|
||||
};
|
||||
|
||||
struct __generated_struct_GPIO_PORT_D {
|
||||
static constexpr GPIO_Port_TypeDef port() { return gpioPortD; }
|
||||
};
|
||||
|
||||
/// @brief Hardware pin template for MGM240 GPIO operations
|
||||
/// @tparam _MASK Bit mask for the pin within the port (1 << pin_number)
|
||||
/// @tparam _PORT_STRUCT Port accessor structure (e.g., __generated_struct_GPIO_PORT_A)
|
||||
/// @tparam _PORT_NUMBER Port number (0=A, 1=B, 2=C, 3=D)
|
||||
/// @tparam _PIN_NUMBER Pin number within the port (0-7 typically)
|
||||
template<uint32_t _MASK, typename _PORT_STRUCT, uint8_t _PORT_NUMBER, uint8_t _PIN_NUMBER> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
/// Configure pin as push-pull output
|
||||
FASTLED_FORCE_INLINE static void setOutput() {
|
||||
_mgm240_gpio_init();
|
||||
GPIO_PinModeSet(_PORT_STRUCT::port(), _PIN_NUMBER, gpioModePushPull, 0);
|
||||
}
|
||||
|
||||
/// Configure pin as input
|
||||
FASTLED_FORCE_INLINE static void setInput() {
|
||||
_mgm240_gpio_init();
|
||||
GPIO_PinModeSet(_PORT_STRUCT::port(), _PIN_NUMBER, gpioModeInput, 0);
|
||||
}
|
||||
|
||||
/// Set pin output high
|
||||
FASTLED_FORCE_INLINE static void hi() {
|
||||
GPIO_PinOutSet(_PORT_STRUCT::port(), _PIN_NUMBER);
|
||||
}
|
||||
|
||||
/// Set pin output low
|
||||
FASTLED_FORCE_INLINE static void lo() {
|
||||
GPIO_PinOutClear(_PORT_STRUCT::port(), _PIN_NUMBER);
|
||||
}
|
||||
|
||||
/// Set pin output based on value (non-zero = high, zero = low)
|
||||
FASTLED_FORCE_INLINE static void set(port_t val) {
|
||||
if(val) hi(); else lo();
|
||||
}
|
||||
|
||||
/// Generate a brief pulse by toggling twice
|
||||
FASTLED_FORCE_INLINE static void strobe() { toggle(); toggle(); }
|
||||
|
||||
/// Toggle pin output state
|
||||
FASTLED_FORCE_INLINE static void toggle() {
|
||||
GPIO_PinOutToggle(_PORT_STRUCT::port(), _PIN_NUMBER);
|
||||
}
|
||||
|
||||
/// Set pin high (port parameter ignored for compatibility)
|
||||
FASTLED_FORCE_INLINE static void hi(port_ptr_t port) { FL_UNUSED(port); hi(); }
|
||||
|
||||
/// Set pin low (port parameter ignored for compatibility)
|
||||
FASTLED_FORCE_INLINE static void lo(port_ptr_t port) { FL_UNUSED(port); lo(); }
|
||||
|
||||
/// Get port value with this pin set high
|
||||
FASTLED_FORCE_INLINE static port_t hival() {
|
||||
return GPIO_PortOutGet(_PORT_STRUCT::port()) | _MASK;
|
||||
}
|
||||
|
||||
/// Get port value with this pin set low
|
||||
FASTLED_FORCE_INLINE static port_t loval() {
|
||||
return GPIO_PortOutGet(_PORT_STRUCT::port()) & ~_MASK;
|
||||
}
|
||||
|
||||
/// Get pointer to GPIO port output register (for direct port manipulation)
|
||||
FASTLED_FORCE_INLINE static port_ptr_t port() {
|
||||
return &(GPIO->P[_PORT_STRUCT::port()].DOUT);
|
||||
}
|
||||
|
||||
/// Get pointer to atomic SET register for race-free high operations
|
||||
FASTLED_FORCE_INLINE static port_ptr_t sport() {
|
||||
return &(GPIO->P[_PORT_STRUCT::port()].DOUTSET);
|
||||
}
|
||||
|
||||
/// Get pointer to atomic CLEAR register for race-free low operations
|
||||
FASTLED_FORCE_INLINE static port_ptr_t cport() {
|
||||
return &(GPIO->P[_PORT_STRUCT::port()].DOUTCLR);
|
||||
}
|
||||
|
||||
/// Fast port write operation (used by timing-critical code)
|
||||
FASTLED_FORCE_INLINE static void fastset(port_ptr_t port, port_t val) {
|
||||
*port = val;
|
||||
}
|
||||
|
||||
/// Get the bit mask for this pin within its port
|
||||
FASTLED_FORCE_INLINE static port_t mask() { return _MASK; }
|
||||
|
||||
/// Read pin input state
|
||||
FASTLED_FORCE_INLINE static bool isset() {
|
||||
return GPIO_PinInGet(_PORT_STRUCT::port(), _PIN_NUMBER) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Pin definition macro for MGM240 FastPin specializations
|
||||
/// Creates a template specialization of FastPin for a specific Arduino pin number
|
||||
/// @param PIN Arduino pin number (0-25)
|
||||
/// @param BIT Pin number within the port (0-7)
|
||||
/// @param PORT_LETTER Port letter (A, B, C, D)
|
||||
/// @param MASK Bit mask for the pin (1 << BIT)
|
||||
#define _FL_DEFPIN(PIN, BIT, PORT_LETTER, MASK) template<> class FastPin<PIN> : public _ARMPIN<MASK, __generated_struct_GPIO_PORT_##PORT_LETTER, PORT_NUM_##PORT_LETTER, BIT> {};
|
||||
|
||||
// Define port numbers for template parameters
|
||||
#define PORT_NUM_A 0
|
||||
#define PORT_NUM_B 1
|
||||
#define PORT_NUM_C 2
|
||||
#define PORT_NUM_D 3
|
||||
|
||||
// Pin mappings for Arduino Nano Matter (MGM240SD22VNA)
|
||||
// Based on Arduino Nano form factor - verify against hardware documentation
|
||||
|
||||
// Digital pins 0-13 (Arduino Nano standard layout)
|
||||
_FL_DEFPIN(0, 0, A, (1 << 0)); // D0/RX - PA00
|
||||
_FL_DEFPIN(1, 1, A, (1 << 1)); // D1/TX - PA01
|
||||
_FL_DEFPIN(2, 2, A, (1 << 2)); // D2 - PA02
|
||||
_FL_DEFPIN(3, 3, A, (1 << 3)); // D3/PWM - PA03
|
||||
_FL_DEFPIN(4, 4, A, (1 << 4)); // D4 - PA04
|
||||
_FL_DEFPIN(5, 5, A, (1 << 5)); // D5/PWM - PA05
|
||||
_FL_DEFPIN(6, 6, A, (1 << 6)); // D6/PWM - PA06
|
||||
_FL_DEFPIN(7, 7, A, (1 << 7)); // D7 - PA07
|
||||
_FL_DEFPIN(8, 0, B, (1 << 0)); // D8 - PB00
|
||||
_FL_DEFPIN(9, 1, B, (1 << 1)); // D9/PWM - PB01
|
||||
_FL_DEFPIN(10, 2, B, (1 << 2)); // D10/SS - PB02
|
||||
_FL_DEFPIN(11, 3, B, (1 << 3)); // D11/MOSI - PB03
|
||||
_FL_DEFPIN(12, 4, B, (1 << 4)); // D12/MISO - PB04
|
||||
_FL_DEFPIN(13, 5, B, (1 << 5)); // D13/SCK/LED - PB05
|
||||
|
||||
// Analog pins A0-A7 (mapped to port C)
|
||||
_FL_DEFPIN(14, 0, C, (1 << 0)); // A0 - PC00
|
||||
_FL_DEFPIN(15, 1, C, (1 << 1)); // A1 - PC01
|
||||
_FL_DEFPIN(16, 2, C, (1 << 2)); // A2 - PC02
|
||||
_FL_DEFPIN(17, 3, C, (1 << 3)); // A3 - PC03
|
||||
_FL_DEFPIN(18, 4, C, (1 << 4)); // A4/SDA - PC04
|
||||
_FL_DEFPIN(19, 5, C, (1 << 5)); // A5/SCL - PC05
|
||||
_FL_DEFPIN(20, 6, C, (1 << 6)); // A6 - PC06
|
||||
_FL_DEFPIN(21, 7, C, (1 << 7)); // A7 - PC07
|
||||
|
||||
// Additional GPIO pins for extended functionality (port D)
|
||||
_FL_DEFPIN(22, 0, D, (1 << 0)); // D22 - PD00
|
||||
_FL_DEFPIN(23, 1, D, (1 << 1)); // D23 - PD01
|
||||
_FL_DEFPIN(24, 2, D, (1 << 2)); // D24 - PD02
|
||||
_FL_DEFPIN(25, 3, D, (1 << 3)); // D25 - PD03
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
/// @file led_sysdefs_arm_mgm240.h
|
||||
/// @brief System definitions for Silicon Labs MGM240 ARM Cortex-M33 microcontroller
|
||||
///
|
||||
/// This file provides platform-specific definitions for the MGM240SD22VNA
|
||||
/// microcontroller used in the SparkFun Thing Plus Matter board.
|
||||
///
|
||||
/// Key specifications:
|
||||
/// - ARM Cortex-M33 @ 39MHz
|
||||
/// - 256KB RAM, 1.5MB Flash
|
||||
/// - Silicon Labs EFM32/EFR32 GPIO architecture
|
||||
/// - FreeRTOS compatibility with automatic detection
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Include Silicon Labs EMLIB GPIO for direct register access
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
/// ARM platform identification
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
/// Use ARM Cortex-M3 compatibility mode for FastLED
|
||||
/// (Cortex-M33 is backward compatible with M3 instruction set)
|
||||
#define FASTLED_ARM_M3
|
||||
|
||||
/// Enable interrupt-aware timing for accurate LED control
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
/// @brief FreeRTOS-compatible critical section macros
|
||||
/// Automatically detects FreeRTOS presence and uses task-safe critical sections.
|
||||
/// Falls back to bare metal interrupt disable/enable when FreeRTOS is not available.
|
||||
#ifdef __has_include
|
||||
#if __has_include("FreeRTOS.h")
|
||||
#include "FreeRTOS.h"
|
||||
/// Enter critical section (FreeRTOS task-safe)
|
||||
#define cli() taskENTER_CRITICAL()
|
||||
/// Exit critical section (FreeRTOS task-safe)
|
||||
#define sei() taskEXIT_CRITICAL()
|
||||
#else
|
||||
/// Enter critical section (bare metal)
|
||||
#define cli() __disable_irq()
|
||||
/// Exit critical section (bare metal)
|
||||
#define sei() __enable_irq()
|
||||
#endif
|
||||
#else
|
||||
// Fallback for compilers without __has_include
|
||||
#define cli() __disable_irq()
|
||||
#define sei() __enable_irq()
|
||||
#endif
|
||||
|
||||
/// @brief CPU frequency for MGM240SD22VNA
|
||||
/// Default system clock frequency used for timing calculations.
|
||||
/// This value is critical for accurate LED protocol timing.
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 39000000L ///< 39 MHz default system clock
|
||||
#endif
|
||||
|
||||
/// ARM platforms don't use PROGMEM (flash storage macros)
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
/// @brief Type definitions for ARM register access
|
||||
typedef volatile uint32_t RwReg; ///< Read-write register (32-bit)
|
||||
typedef volatile uint32_t RoReg; ///< Read-only register (32-bit)
|
||||
|
||||
/// @brief Arduino compatibility macros
|
||||
/// These provide fallback pin manipulation functions when hardware FastPin is not available.
|
||||
/// The MGM240 platform uses hardware FastPin, so these are rarely used.
|
||||
#ifndef digitalPinToBitMask
|
||||
#define digitalPinToBitMask(P) (1 << (P))
|
||||
#endif
|
||||
|
||||
#ifndef digitalPinToPort
|
||||
#define digitalPinToPort(P) ((P) / 8)
|
||||
#endif
|
||||
|
||||
/// @brief Legacy Arduino port register emulation
|
||||
/// These provide generic ARM memory-mapped register access for Arduino compatibility.
|
||||
/// The MGM240 platform primarily uses Silicon Labs EMLIB GPIO functions instead.
|
||||
#ifndef portOutputRegister
|
||||
#define portOutputRegister(P) ((volatile uint32_t*)(0x40000000 + (P) * 0x400))
|
||||
#endif
|
||||
|
||||
#ifndef portInputRegister
|
||||
#define portInputRegister(P) ((volatile uint32_t*)(0x40000000 + (P) * 0x400))
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
# FastLED Platform: Teensy 4.x (i.MX RT1062)
|
||||
|
||||
Teensy 4.0/4.1 (IMXRT1062) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_mxrt1062.h`: Aggregator; includes pin/SPI/clockless and helpers.
|
||||
- `fastpin_arm_mxrt1062.h`: Pin helpers.
|
||||
- `fastspi_arm_mxrt1062.h`: SPI backend.
|
||||
- `clockless_arm_mxrt1062.h`: Single-lane clockless driver.
|
||||
- `block_clockless_arm_mxrt1062.h`: Block/multi-lane clockless.
|
||||
- `octows2811_controller.h`: OctoWS2811 integration.
|
||||
- `led_sysdefs_arm_mxrt1062.h`: System defines for RT1062.
|
||||
|
||||
Notes:
|
||||
- Very high CPU frequency; DWT-based timing and interrupt thresholds are critical for stability.
|
||||
- OctoWS2811 and SmartMatrix can offload large parallel outputs; ensure pin mappings and DMA settings match board wiring.
|
||||
@@ -0,0 +1,214 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H
|
||||
#define __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the teensy4
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY4)
|
||||
|
||||
#define __FL_T4_MASK ((1<<(LANES))-1)
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
class FlexibleInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, __FL_T4_MASK> {
|
||||
uint8_t m_bitOffsets[16];
|
||||
uint8_t m_nActualLanes;
|
||||
uint8_t m_nLowBit;
|
||||
uint8_t m_nHighBit;
|
||||
uint32_t m_nWriteMask;
|
||||
uint8_t m_nOutBlocks;
|
||||
uint32_t m_offsets[3];
|
||||
uint32_t MS_COUNTER;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * m_nActualLanes; }
|
||||
|
||||
// For each pin, if we've hit our lane count, break, otherwise set the pin to output,
|
||||
// store the bit offset in our offset array, add this pin to the write mask, and if this
|
||||
// pin ends a block sequence, then break out of the switch as well
|
||||
#define _BLOCK_PIN(P) case P: { \
|
||||
if(m_nActualLanes == LANES) break; \
|
||||
FastPin<P>::setOutput(); \
|
||||
m_bitOffsets[m_nActualLanes++] = FastPin<P>::pinbit(); \
|
||||
m_nWriteMask |= FastPin<P>::mask(); \
|
||||
if( P == 27 || P == 7 || P == 30) break; \
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
// pre-initialize
|
||||
fl::memfill(m_bitOffsets,0,16);
|
||||
m_nActualLanes = 0;
|
||||
m_nLowBit = 33;
|
||||
m_nHighBit = 0;
|
||||
m_nWriteMask = 0;
|
||||
MS_COUNTER = 0;
|
||||
|
||||
// setup the bits and data tracking for parallel output
|
||||
switch(FIRST_PIN) {
|
||||
// GPIO6 block output
|
||||
_BLOCK_PIN( 1);
|
||||
_BLOCK_PIN( 0);
|
||||
_BLOCK_PIN(24);
|
||||
_BLOCK_PIN(25);
|
||||
_BLOCK_PIN(19);
|
||||
_BLOCK_PIN(18);
|
||||
_BLOCK_PIN(14);
|
||||
_BLOCK_PIN(15);
|
||||
_BLOCK_PIN(17);
|
||||
_BLOCK_PIN(16);
|
||||
_BLOCK_PIN(22);
|
||||
_BLOCK_PIN(23);
|
||||
_BLOCK_PIN(20);
|
||||
_BLOCK_PIN(21);
|
||||
_BLOCK_PIN(26);
|
||||
_BLOCK_PIN(27);
|
||||
// GPIO7 block output
|
||||
_BLOCK_PIN(10);
|
||||
_BLOCK_PIN(12);
|
||||
_BLOCK_PIN(11);
|
||||
_BLOCK_PIN(13);
|
||||
_BLOCK_PIN( 6);
|
||||
_BLOCK_PIN( 9);
|
||||
_BLOCK_PIN(32);
|
||||
_BLOCK_PIN( 8);
|
||||
_BLOCK_PIN( 7);
|
||||
// GPIO 37 block output
|
||||
_BLOCK_PIN(37);
|
||||
_BLOCK_PIN(36);
|
||||
_BLOCK_PIN(35);
|
||||
_BLOCK_PIN(34);
|
||||
_BLOCK_PIN(39);
|
||||
_BLOCK_PIN(38);
|
||||
_BLOCK_PIN(28);
|
||||
_BLOCK_PIN(31);
|
||||
_BLOCK_PIN(30);
|
||||
}
|
||||
|
||||
for(int i = 0; i < m_nActualLanes; ++i) {
|
||||
if(m_bitOffsets[i] < m_nLowBit) { m_nLowBit = m_bitOffsets[i]; }
|
||||
if(m_bitOffsets[i] > m_nHighBit) { m_nHighBit = m_bitOffsets[i]; }
|
||||
}
|
||||
|
||||
m_nOutBlocks = (m_nHighBit + 8)/8;
|
||||
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, __FL_T4_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#else
|
||||
showRGBInternal(pixels);
|
||||
#endif
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[32];
|
||||
uint8_t bg[4][8];
|
||||
uint16_t shorts[16];
|
||||
uint32_t raw[8];
|
||||
} _outlines;
|
||||
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER _outlines & b, PixelController<RGB_ORDER, LANES, __FL_T4_MASK> &pixels) {
|
||||
_outlines b2;
|
||||
transpose8x1(b.bg[3], b2.bg[3]);
|
||||
transpose8x1(b.bg[2], b2.bg[2]);
|
||||
transpose8x1(b.bg[1], b2.bg[1]);
|
||||
transpose8x1(b.bg[0], b2.bg[0]);
|
||||
|
||||
FASTLED_REGISTER uint8_t d = pixels.template getd<PX>(pixels);
|
||||
FASTLED_REGISTER uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
int x = 0;
|
||||
for(uint32_t i = 8; i > 0;) {
|
||||
--i;
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
*FastPin<FIRST_PIN>::sport() = m_nWriteMask;
|
||||
next_mark = ARM_DWT_CYCCNT + m_offsets[0];
|
||||
|
||||
uint32_t out = (b2.bg[3][i] << 24) | (b2.bg[2][i] << 16) | (b2.bg[1][i] << 8) | b2.bg[0][i];
|
||||
|
||||
out = ((~out) & m_nWriteMask);
|
||||
while((next_mark - ARM_DWT_CYCCNT) > m_offsets[1]);
|
||||
*FastPin<FIRST_PIN>::cport() = out;
|
||||
|
||||
out = m_nWriteMask;
|
||||
while((next_mark - ARM_DWT_CYCCNT) > m_offsets[2]);
|
||||
*FastPin<FIRST_PIN>::cport() = out;
|
||||
|
||||
// Read and store up to two bytes
|
||||
if (x < m_nActualLanes) {
|
||||
b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels, x, d, scale);
|
||||
++x;
|
||||
if (x < m_nActualLanes) {
|
||||
b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels, x, d, scale);
|
||||
++x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, __FL_T4_MASK> &allpixels) {
|
||||
allpixels.preStepFirstByteDithering();
|
||||
_outlines b0;
|
||||
uint32_t start = ARM_DWT_CYCCNT;
|
||||
|
||||
for(int i = 0; i < m_nActualLanes; ++i) {
|
||||
b0.bytes[m_bitOffsets[i]] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
|
||||
m_offsets[0] = _FASTLED_NS_TO_DWT(T1+T2+T3);
|
||||
m_offsets[1] = _FASTLED_NS_TO_DWT(T2+T3);
|
||||
m_offsets[2] = _FASTLED_NS_TO_DWT(T3);
|
||||
uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD));
|
||||
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + m_offsets[0];
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
allpixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; }
|
||||
}
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
}
|
||||
|
||||
sei();
|
||||
|
||||
return ARM_DWT_CYCCNT - start;
|
||||
}
|
||||
};
|
||||
|
||||
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, int NUM_LANES, EOrder RGB_ORDER=GRB>
|
||||
class __FIBCC : public FlexibleInlineBlockClocklessController<NUM_LANES,DATA_PIN,CHIPSET<DATA_PIN,RGB_ORDER>::__T1(),CHIPSET<DATA_PIN,RGB_ORDER>::__T2(),CHIPSET<DATA_PIN,RGB_ORDER>::__T3(),RGB_ORDER,CHIPSET<DATA_PIN,RGB_ORDER>::__XTRA0(),CHIPSET<DATA_PIN,RGB_ORDER>::__FLIP(),CHIPSET<DATA_PIN,RGB_ORDER>::__WAIT_TIME()> {};
|
||||
|
||||
#define __FASTLED_HAS_FIBCC 1
|
||||
|
||||
#endif //defined(FASTLED_TEENSY4)
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_MXRT1062_H
|
||||
#define __INC_CLOCKLESS_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the teensy4
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY4)
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
#define _FASTLED_NS_TO_DWT(_NS) (((F_CPU_ACTUAL>>16)*(_NS)) / (1000000000UL>>16))
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
uint32_t off[3];
|
||||
|
||||
public:
|
||||
static constexpr int __DATA_PIN() { return DATA_PIN; }
|
||||
static constexpr int __T1() { return T1; }
|
||||
static constexpr int __T2() { return T2; }
|
||||
static constexpr int __T3() { return T3; }
|
||||
static constexpr EOrder __RGB_ORDER() { return RGB_ORDER; }
|
||||
static constexpr int __XTRA0() { return XTRA0; }
|
||||
static constexpr bool __FLIP() { return FLIP; }
|
||||
static constexpr int __WAIT_TIME() { return WAIT_TIME; }
|
||||
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
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 void writeBits(FASTLED_REGISTER uint32_t & next_mark, FASTLED_REGISTER uint32_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + off[0];
|
||||
FastPin<DATA_PIN>::hi();
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[1]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + off[0];
|
||||
FastPin<DATA_PIN>::hi();
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[1]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
uint32_t start = ARM_DWT_CYCCNT;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint32_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
|
||||
off[0] = _FASTLED_NS_TO_DWT(T1+T2+T3);
|
||||
off[1] = _FASTLED_NS_TO_DWT(T2+T3);
|
||||
off[2] = _FASTLED_NS_TO_DWT(T3);
|
||||
|
||||
uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD)*1000);
|
||||
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + off[0];
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; }
|
||||
}
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT - start;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef __INC_FASTLED_ARM_MXRT1062_H
|
||||
#define __INC_FASTLED_ARM_MXRT1062_H
|
||||
|
||||
#include "fastpin_arm_mxrt1062.h"
|
||||
#include "fastspi_arm_mxrt1062.h"
|
||||
#include "octows2811_controller.h"
|
||||
#include "../k20/ws2812serial_controller.h"
|
||||
#include "../k20/smartmatrix_t3.h"
|
||||
#include "clockless_arm_mxrt1062.h"
|
||||
#include "block_clockless_arm_mxrt1062.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef __FASTPIN_ARM_MXRT1062_H
|
||||
#define __FASTPIN_ARM_MXRT1062_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for teensy 4.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. It calls through to pinMode for setting input/output on pins
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _BIT, uint32_t _MASK, typename _GPIO_DR, typename _GPIO_DR_SET, typename _GPIO_DR_CLEAR, typename _GPIO_DR_TOGGLE> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _GPIO_DR_SET::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _GPIO_DR_CLEAR::r() = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _GPIO_DR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _GPIO_DR_TOGGLE::r() = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO_DR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO_DR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO_DR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO_DR_SET::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO_DR_CLEAR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
inline static uint32_t pinbit() __attribute__ ((always_inline)) { return _BIT; }
|
||||
};
|
||||
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE reg32_t r() { return T; } };
|
||||
#define _FL_IO(L) _RD32(GPIO ## L ## _DR); _RD32(GPIO ## L ## _DR_SET); _RD32(GPIO ## L ## _DR_CLEAR); _RD32(GPIO ## L ## _DR_TOGGLE); _FL_DEFINE_PORT(L, _R(GPIO ## L ## _DR));
|
||||
|
||||
// From the teensy core - it looks like there's the "default set" of port registers at GPIO1-5 - but then there
|
||||
// are a mirrored set for GPIO1-4 at GPIO6-9, which in the teensy core is referred to as "fast" - while the pin definitiosn
|
||||
// at https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=193716&viewfull=1#post193716
|
||||
// refer to GPIO1-4, we're going to use GPIO6-9 in the definitions below because the fast registers are what
|
||||
// the teensy core is using internally
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1U << BIT, _R(GPIO ## L ## _DR), _R(GPIO ## L ## _DR_SET), _R(GPIO ## L ## _DR_CLEAR), _R(GPIO ## L ## _DR_TOGGLE)> {};
|
||||
|
||||
#if defined(FASTLED_TEENSY4) && defined(CORE_TEENSY)
|
||||
_FL_IO(1); _FL_IO(2); _FL_IO(3); _FL_IO(4); _FL_IO(5);
|
||||
_FL_IO(6); _FL_IO(7); _FL_IO(8); _FL_IO(9);
|
||||
|
||||
#define MAX_PIN 39
|
||||
_FL_DEFPIN( 0, 3,6); _FL_DEFPIN( 1, 2,6); _FL_DEFPIN( 2, 4,9); _FL_DEFPIN( 3, 5,9);
|
||||
_FL_DEFPIN( 4, 6,9); _FL_DEFPIN( 5, 8,9); _FL_DEFPIN( 6,10,7); _FL_DEFPIN( 7,17,7);
|
||||
_FL_DEFPIN( 8,16,7); _FL_DEFPIN( 9,11,7); _FL_DEFPIN(10, 0,7); _FL_DEFPIN(11, 2,7);
|
||||
_FL_DEFPIN(12, 1,7); _FL_DEFPIN(13, 3,7); _FL_DEFPIN(14,18,6); _FL_DEFPIN(15,19,6);
|
||||
_FL_DEFPIN(16,23,6); _FL_DEFPIN(17,22,6); _FL_DEFPIN(18,17,6); _FL_DEFPIN(19,16,6);
|
||||
_FL_DEFPIN(20,26,6); _FL_DEFPIN(21,27,6); _FL_DEFPIN(22,24,6); _FL_DEFPIN(23,25,6);
|
||||
_FL_DEFPIN(24,12,6); _FL_DEFPIN(25,13,6); _FL_DEFPIN(26,30,6); _FL_DEFPIN(27,31,6);
|
||||
_FL_DEFPIN(28,18,8); _FL_DEFPIN(29,31,9); _FL_DEFPIN(30,23,8); _FL_DEFPIN(31,22,8);
|
||||
_FL_DEFPIN(32,12,7); _FL_DEFPIN(33, 7,9); _FL_DEFPIN(34,15,8); _FL_DEFPIN(35,14,8);
|
||||
_FL_DEFPIN(36,13,8); _FL_DEFPIN(37,12,8); _FL_DEFPIN(38,17,8); _FL_DEFPIN(39,16,8);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
|
||||
#define SPI1_DATA 26
|
||||
#define SPI1_CLOCK 27
|
||||
|
||||
#define SPI2_DATA 35
|
||||
#define SPI2_CLOCK 37
|
||||
|
||||
#endif // defined FASTLED_TEENSY4
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINSs
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
#ifndef __INC_FASTSPI_ARM_MXRT1062_H
|
||||
#define __INC_FASTSPI_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined (FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI)
|
||||
#include <SPI.h>
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_RATE, SPIClass & _SPIObject, int _SPI_INDEX>
|
||||
class Teensy4HardwareSPIOutput {
|
||||
Selectable *m_pSelect = nullptr;
|
||||
uint32_t m_bitCount = 0;
|
||||
uint32_t m_bitData = 0;
|
||||
inline IMXRT_LPSPI_t & port() __attribute__((always_inline)) {
|
||||
switch(_SPI_INDEX) {
|
||||
case 0:
|
||||
return IMXRT_LPSPI4_S;
|
||||
case 1:
|
||||
return IMXRT_LPSPI3_S;
|
||||
case 2:
|
||||
return IMXRT_LPSPI1_S;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Teensy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;}
|
||||
Teensy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;}
|
||||
|
||||
// set the object representing the selectable -- ignore for now
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() { _SPIObject.begin(); }
|
||||
|
||||
// latch the CS select
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
// begin the SPI transaction
|
||||
_SPIObject.beginTransaction(SPISettings(_SPI_CLOCK_RATE, MSBFIRST, SPI_MODE0));
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
}
|
||||
|
||||
// release the CS select
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
_SPIObject.endTransaction();
|
||||
}
|
||||
|
||||
// wait until all queued up data has been written
|
||||
static void waitFully() { /* TODO */ }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register) -
|
||||
void inline writeByte(uint8_t b) __attribute__((always_inline)) {
|
||||
if(m_bitCount == 0) {
|
||||
_SPIObject.transfer(b);
|
||||
} else {
|
||||
// There's been a bit of data written, add that to the output as well
|
||||
uint32_t outData = (m_bitData << 8) | b;
|
||||
uint32_t tcr = port().TCR;
|
||||
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8+m_bitCount) - 1); // turn on 9 bit mode
|
||||
port().TDR = outData; // output 9 bit data.
|
||||
while ((port().RSR & LPSPI_RSR_RXEMPTY)) ; // wait while the RSR fifo is empty...
|
||||
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8) - 1); // turn back on 8 bit mode
|
||||
port().RDR;
|
||||
m_bitCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
void inline writeWord(uint16_t w) __attribute__((always_inline)) {
|
||||
writeByte(((w>>8) & 0xFF));
|
||||
_SPIObject.transfer(w & 0xFF);
|
||||
}
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { _SPIObject.transfer(value); }
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select(); writeBytesValueRaw(value, len); release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
template <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 void writeBit(uint8_t b) {
|
||||
m_bitData = (m_bitData<<1) | ((b&(1<<BIT)) != 0);
|
||||
// If this is the 8th bit we've collected, just write it out raw
|
||||
FASTLED_REGISTER uint32_t bc = m_bitCount;
|
||||
bc = (bc + 1) & 0x07;
|
||||
if (!bc) {
|
||||
m_bitCount = 0;
|
||||
_SPIObject.transfer(m_bitData);
|
||||
}
|
||||
m_bitCount = bc;
|
||||
}
|
||||
|
||||
// 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, void* context = NULL) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_MXRT1062_H
|
||||
#define __INC_LED_SYSDEFS_ARM_MXRT1062_H
|
||||
|
||||
#define FASTLED_TEENSY4
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 96000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint32_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint32_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
// extern volatile uint32_t systick_millis_count;
|
||||
// # define MS_COUNTER systick_millis_count
|
||||
|
||||
// Teensy4 provides progmem
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef __INC_OCTOWS2811_CONTROLLER_H
|
||||
#define __INC_OCTOWS2811_CONTROLLER_H
|
||||
|
||||
#ifdef USE_OCTOWS2811
|
||||
|
||||
#include "OctoWS2811.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template<EOrder RGB_ORDER = GRB, uint8_t CHIP = WS2811_800kHz>
|
||||
class COctoWS2811Controller : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
|
||||
OctoWS2811 *pocto;
|
||||
uint8_t *drawbuffer,*framebuffer;
|
||||
|
||||
void _init(int nLeds) {
|
||||
if(pocto == NULL) {
|
||||
drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3);
|
||||
framebuffer = (uint8_t*)malloc(nLeds * 8 * 3);
|
||||
|
||||
// byte ordering is handled in show by the pixel controller
|
||||
int config = WS2811_RGB;
|
||||
config |= CHIP;
|
||||
|
||||
pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config);
|
||||
|
||||
pocto->begin();
|
||||
}
|
||||
}
|
||||
public:
|
||||
COctoWS2811Controller() { pocto = NULL; }
|
||||
virtual int size() { return CLEDController::size() * 8; }
|
||||
|
||||
virtual void init() { /* do nothing yet */ }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> &pixels) {
|
||||
uint32_t size = pixels.size();
|
||||
uint32_t sizeTimes8 = 8U * size;
|
||||
_init(size);
|
||||
|
||||
uint32_t index = 0;
|
||||
while (pixels.has(1)) {
|
||||
for (int lane = 0; lane < 8; lane++) {
|
||||
uint8_t r = pixels.loadAndScale0(lane);
|
||||
uint8_t g = pixels.loadAndScale1(lane);
|
||||
uint8_t b = pixels.loadAndScale2(lane);
|
||||
pocto->setPixel(index, r, g, b);
|
||||
index += size;
|
||||
}
|
||||
index -= sizeTimes8;
|
||||
index++;
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
pocto->show();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
# FastLED Platform: nRF51
|
||||
|
||||
Nordic nRF51 family support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_nrf51.h`: Aggregator; includes pin/SPI/clockless.
|
||||
- `fastpin_arm_nrf51.h`: Pin helpers.
|
||||
- `fastspi_arm_nrf51.h`: SPI backend.
|
||||
- `clockless_arm_nrf51.h`: Clockless driver for nRF51.
|
||||
- `led_sysdefs_arm_nrf51.h`: System defines for nRF51.
|
||||
|
||||
Notes:
|
||||
- Lower-clock Cortex-M0; carefully budget interrupt windows when using clockless.
|
||||
- Prefer fewer parallel lanes or shorter strips with clockless to maintain timing margins.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0`.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`.
|
||||
- **`FASTLED_ALL_PINS_HARDWARE_SPI`**: Enabled by default when not forcing software SPI.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_NRF51
|
||||
#define __INC_CLOCKLESS_ARM_NRF51
|
||||
|
||||
#if defined(NRF51)
|
||||
|
||||
#include <nrf51_bitfields.h>
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS==1)
|
||||
#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0;
|
||||
#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; }
|
||||
#define INNER_SEI sei();
|
||||
#else
|
||||
#define SEI_CHK
|
||||
#define CLI_CHK
|
||||
#define INNER_SEI delaycycles<1>();
|
||||
#endif
|
||||
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 75>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mColorAdjustment.premixed[0];
|
||||
data.s[1] = pixels.mColorAdjustment.premixed[1];
|
||||
data.s[2] = pixels.mColorAdjustment.premixed[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
|
||||
// timer mode w/prescaler of 0
|
||||
LED_TIMER->MODE = TIMER_MODE_MODE_Timer;
|
||||
LED_TIMER->PRESCALER = 0;
|
||||
LED_TIMER->EVENTS_COMPARE[0] = 0;
|
||||
LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
|
||||
LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
|
||||
LED_TIMER->TASKS_START = 1;
|
||||
|
||||
int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
|
||||
LED_TIMER->TASKS_STOP = 1;
|
||||
return ret; // 0x00FFFFFF - _VAL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // NRF51
|
||||
#endif // __INC_CLOCKLESS_ARM_NRF51
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __INC_FASTLED_ARM_NRF51_H
|
||||
#define __INC_FASTLED_ARM_NRF51_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_nrf51.h"
|
||||
#include "fastspi_arm_nrf51.h"
|
||||
#include "clockless_arm_nrf51.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef __FASTPIN_ARM_NRF51_H
|
||||
#define __FASTPIN_ARM_NRF51_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
#if defined(NRF51)
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
#if 0
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _DIRSET, typename _DIRCLR, typename _OUTSET, typename _OUTCLR, typename _OUT> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { _DIRSET::r() = _MASK; }
|
||||
inline static void setInput() { _DIRCLR::r() = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define ADDR(X) *(volatile uint32_t*)X
|
||||
#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset))
|
||||
#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518)
|
||||
#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C)
|
||||
#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508)
|
||||
#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C)
|
||||
#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504)
|
||||
|
||||
#define _RD32_NRF(T) struct __gen_struct_ ## T { static FASTLED_FORCE_INLINE reg32_t r() { return T; }};
|
||||
|
||||
_RD32_NRF(NR_DIRSET);
|
||||
_RD32_NRF(NR_DIRCLR);
|
||||
_RD32_NRF(NR_OUTSET);
|
||||
_RD32_NRF(NR_OUTCLR);
|
||||
_RD32_NRF(NR_OUT);
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN, \
|
||||
_R(NR_DIRSET), _R(NR_DIRCLR), _R(NR_OUTSET), _R(NR_OUTCLR), _R(NR_OUT)> {};
|
||||
#else
|
||||
|
||||
typedef struct { /*!< GPIO Structure */
|
||||
// __I uint32_t RESERVED0[321];
|
||||
__IO uint32_t OUT; /*!< Write GPIO port. */
|
||||
__IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */
|
||||
__IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */
|
||||
__I uint32_t IN; /*!< Read GPIO port. */
|
||||
__IO uint32_t DIR; /*!< Direction of GPIO pins. */
|
||||
__IO uint32_t DIRSET; /*!< DIR set register. */
|
||||
__IO uint32_t DIRCLR; /*!< DIR clear register. */
|
||||
__I uint32_t RESERVED1[120];
|
||||
__IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */
|
||||
} FL_NRF_GPIO_Type;
|
||||
|
||||
#define FL_NRF_GPIO_BASE 0x50000504UL
|
||||
#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE)
|
||||
|
||||
template<uint8_t PIN, uint32_t _MASK> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; }
|
||||
inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
|
||||
inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; }
|
||||
};
|
||||
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN> {};
|
||||
#endif
|
||||
|
||||
// Actual pin definitions
|
||||
#define MAX_PIN 31
|
||||
_FL_DEFPIN(0); _FL_DEFPIN(1); _FL_DEFPIN(2); _FL_DEFPIN(3);
|
||||
_FL_DEFPIN(4); _FL_DEFPIN(5); _FL_DEFPIN(6); _FL_DEFPIN(7);
|
||||
_FL_DEFPIN(8); _FL_DEFPIN(9); _FL_DEFPIN(10); _FL_DEFPIN(11);
|
||||
_FL_DEFPIN(12); _FL_DEFPIN(13); _FL_DEFPIN(14); _FL_DEFPIN(15);
|
||||
_FL_DEFPIN(16); _FL_DEFPIN(17); _FL_DEFPIN(18); _FL_DEFPIN(19);
|
||||
_FL_DEFPIN(20); _FL_DEFPIN(21); _FL_DEFPIN(22); _FL_DEFPIN(23);
|
||||
_FL_DEFPIN(24); _FL_DEFPIN(25); _FL_DEFPIN(26); _FL_DEFPIN(27);
|
||||
_FL_DEFPIN(28); _FL_DEFPIN(29); _FL_DEFPIN(30); _FL_DEFPIN(31);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
#ifndef __INC_FASTSPI_NRF_H
|
||||
#define __INC_FASTSPI_NRF_H
|
||||
|
||||
#ifdef NRF51
|
||||
|
||||
#ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
||||
|
||||
// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
|
||||
// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
|
||||
// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class NRF51SPIOutput {
|
||||
|
||||
struct saveData {
|
||||
uint32_t sck;
|
||||
uint32_t mosi;
|
||||
uint32_t miso;
|
||||
uint32_t freq;
|
||||
uint32_t enable;
|
||||
} mSavedData;
|
||||
|
||||
void saveSPIData() {
|
||||
mSavedData.sck = NRF_SPI0->PSELSCK;
|
||||
mSavedData.mosi = NRF_SPI0->PSELMOSI;
|
||||
mSavedData.miso = NRF_SPI0->PSELMISO;
|
||||
mSavedData.freq = NRF_SPI0->FREQUENCY;
|
||||
mSavedData.enable = NRF_SPI0->ENABLE;
|
||||
}
|
||||
|
||||
void restoreSPIData() {
|
||||
NRF_SPI0->PSELSCK = mSavedData.sck;
|
||||
NRF_SPI0->PSELMOSI = mSavedData.mosi;
|
||||
NRF_SPI0->PSELMISO = mSavedData.miso;
|
||||
NRF_SPI0->FREQUENCY = mSavedData.freq;
|
||||
mSavedData.enable = NRF_SPI0->ENABLE;
|
||||
}
|
||||
|
||||
public:
|
||||
NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
|
||||
NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
|
||||
|
||||
// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
NRF_SPI0->PSELSCK = _CLOCK_PIN;
|
||||
NRF_SPI0->PSELMOSI = _DATA_PIN;
|
||||
NRF_SPI0->PSELMISO = 0xFFFFFFFF;
|
||||
NRF_SPI0->FREQUENCY = 0x80000000;
|
||||
NRF_SPI0->ENABLE = 1;
|
||||
NRF_SPI0->EVENTS_READY = 0;
|
||||
}
|
||||
|
||||
// latch the CS select
|
||||
void select() { saveSPIData(); init(); }
|
||||
|
||||
// release the CS select
|
||||
void release() { shouldWait(); restoreSPIData(); }
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) {
|
||||
// static bool sWait=false;
|
||||
// bool oldWait = sWait;
|
||||
// sWait = wait;
|
||||
// never going to bother with waiting since we're always running the spi clock at max speed on the rfduino
|
||||
// TODO: When we set clock rate, implement/fix waiting properly, otherwise the world hangs up
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait until all queued up data has been written
|
||||
static void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
|
||||
static void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); NRF_SPI0->TXD = b; NRF_SPI0->INTENCLR; shouldWait(true); }
|
||||
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); }
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } }
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template<class D> void writeBytes(uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(uint8_t *data, int len) {
|
||||
writeBytes<DATA_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();
|
||||
NRF_SPI0->ENABLE = 0;
|
||||
if(b & 1<<BIT) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
NRF_SPI0->ENABLE = 1;
|
||||
}
|
||||
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef __LED_SYSDEFS_ARM_NRF51
|
||||
#define __LED_SYSDEFS_ARM_NRF51
|
||||
|
||||
#ifndef NRF51
|
||||
#define NRF51
|
||||
#endif
|
||||
|
||||
#define LED_TIMER NRF_TIMER1
|
||||
#define FASTLED_NO_PINMAP
|
||||
#define FASTLED_HAS_CLOCKLESS
|
||||
|
||||
#define FASTLED_SPI_BYTE_ONLY
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
#define FASTLED_ARM_M0
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 16000000
|
||||
#endif
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include <nrf51.h>
|
||||
#include <core_cm0.h>
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
typedef uint32_t prog_uint32_t;
|
||||
typedef uint8_t boolean;
|
||||
|
||||
#define PROGMEM
|
||||
#define NO_PROGMEM
|
||||
#define NEED_CXX_BITS
|
||||
|
||||
// Default to NOT using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
# FastLED Platform: nRF52
|
||||
|
||||
Nordic nRF52 family support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_nrf52.h`: Aggregator; includes pin/SPI/clockless and sysdefs.
|
||||
- `fastpin_arm_nrf52.h`, `fastpin_arm_nrf52_variants.h`: Pin helpers/variants.
|
||||
- `fastspi_arm_nrf52.h`: SPI backend.
|
||||
- `clockless_arm_nrf52.h`: Clockless driver.
|
||||
- `arbiter_nrf52.h`: PWM arbitration utility (selects/guards PWM instances for drivers).
|
||||
- `led_sysdefs_arm_nrf52.h`: System defines for nRF52.
|
||||
|
||||
Notes:
|
||||
- Requires `CLOCKLESS_FREQUENCY` definition in many setups; PWM resources may be shared and must be arbitrated.
|
||||
- `arbiter_nrf52.h` exposes a small API to acquire/release PWM instances safely across users; ensure ISR handlers are lightweight.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0` (flat memory model).
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`.
|
||||
- **`FASTLED_ALL_PINS_HARDWARE_SPI`**: Enabled by default unless forcing software SPI.
|
||||
- **`FASTLED_NRF52_SPIM`**: Select SPIM instance (e.g., `NRF_SPIM0`).
|
||||
- **`FASTLED_NRF52_ENABLE_PWM_INSTANCE0`**: Enable PWM instance used by clockless.
|
||||
- **`FASTLED_NRF52_NEVER_INLINE`**: Controls inlining attribute via `FASTLED_NRF52_INLINE_ATTRIBUTE`.
|
||||
- **`FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING`**: Limit pixels per string in PWM-encoded path (default 144).
|
||||
- **`FASTLED_NRF52_PWM_ID`**: Select PWM instance index used.
|
||||
- **`FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING`**: Suppress pin-map warnings for unverified boards.
|
||||
|
||||
Define before including `FastLED.h`.
|
||||
@@ -0,0 +1,114 @@
|
||||
#ifndef __INC_ARBITER_NRF52
|
||||
#define __INC_ARBITER_NRF52
|
||||
|
||||
#if defined(NRF52_SERIES)
|
||||
|
||||
#include "led_sysdefs_arm_nrf52.h"
|
||||
|
||||
//FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
typedef void (*FASTLED_NRF52_PWM_INTERRUPT_HANDLER)();
|
||||
|
||||
// a trick learned from other embedded projects ..
|
||||
// use the enum as an index to a statically-allocated array
|
||||
// to store unique information for that instance.
|
||||
// also provides a count of how many instances were enabled.
|
||||
//
|
||||
// See led_sysdefs_arm_nrf52.h for selection....
|
||||
//
|
||||
typedef enum _FASTLED_NRF52_ENABLED_PWM_INSTANCE {
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
|
||||
FASTLED_NRF52_PWM0_INSTANCE_IDX,
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
|
||||
FASTLED_NRF52_PWM1_INSTANCE_IDX,
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
|
||||
FASTLED_NRF52_PWM2_INSTANCE_IDX,
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
|
||||
FASTLED_NRF52_PWM3_INSTANCE_IDX,
|
||||
#endif
|
||||
FASTLED_NRF52_PWM_INSTANCE_COUNT
|
||||
} FASTLED_NRF52_ENABLED_PWM_INSTANCES;
|
||||
|
||||
static_assert(FASTLED_NRF52_PWM_INSTANCE_COUNT > 0, "Instance count must be greater than zero -- define FASTLED_NRF52_ENABLE_PWM_INSTNACE[n] (replace `[n]` with digit)");
|
||||
|
||||
template <uint32_t _PWM_ID>
|
||||
class PWM_Arbiter {
|
||||
private:
|
||||
static_assert(_PWM_ID < 32, "PWM_ID over 31 breaks current arbitration bitmask");
|
||||
//const uint32_t _ACQUIRE_MASK = (1u << _PWM_ID) ;
|
||||
//const uint32_t _CLEAR_MASK = ~((uint32_t)(1u << _PWM_ID));
|
||||
static uint32_t s_PwmInUse;
|
||||
static NRF_PWM_Type * const s_PWM;
|
||||
static IRQn_Type const s_PWM_IRQ;
|
||||
static FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile s_Isr;
|
||||
|
||||
public:
|
||||
static void isr_handler() {
|
||||
return s_Isr();
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static bool isAcquired() {
|
||||
return (0u != (s_PwmInUse & 1u)); // _ACQUIRE_MASK
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void acquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) {
|
||||
while (!tryAcquire(isr));
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) {
|
||||
uint32_t oldValue = __sync_fetch_and_or(&s_PwmInUse, 1u); // _ACQUIRE_MASK
|
||||
if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
|
||||
s_Isr = isr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseFromIsr() {
|
||||
uint32_t oldValue = __sync_fetch_and_and(&s_PwmInUse, ~1u); // _CLEAR_MASK
|
||||
if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
|
||||
// TODO: This should never be true... indicates was not held.
|
||||
// Assert here?
|
||||
(void)oldValue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static NRF_PWM_Type * getPWM() {
|
||||
return s_PWM;
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static IRQn_Type getIRQn() { return s_PWM_IRQ; }
|
||||
};
|
||||
template <uint32_t _PWM_ID> NRF_PWM_Type * const PWM_Arbiter<_PWM_ID>::s_PWM =
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
|
||||
(_PWM_ID == 0 ? NRF_PWM0 :
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
|
||||
(_PWM_ID == 1 ? NRF_PWM1 :
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
|
||||
(_PWM_ID == 2 ? NRF_PWM2 :
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
|
||||
(_PWM_ID == 3 ? NRF_PWM3 :
|
||||
#endif
|
||||
(NRF_PWM_Type*)-1
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
|
||||
)
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
|
||||
)
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
|
||||
)
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
|
||||
)
|
||||
#endif
|
||||
;
|
||||
template <uint32_t _PWM_ID> IRQn_Type const PWM_Arbiter<_PWM_ID>::s_PWM_IRQ = ((IRQn_Type)((uint8_t)((uint32_t)(s_PWM) >> 12)));
|
||||
template <uint32_t _PWM_ID> uint32_t PWM_Arbiter<_PWM_ID>::s_PwmInUse = 0;
|
||||
template <uint32_t _PWM_ID> FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile PWM_Arbiter<_PWM_ID>::s_Isr = NULL;
|
||||
|
||||
//FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // NRF52_SERIES
|
||||
#endif // __INC_ARBITER_NRF52
|
||||
@@ -0,0 +1,390 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_NRF52
|
||||
#define __INC_CLOCKLESS_ARM_NRF52
|
||||
|
||||
#if defined(NRF52_SERIES)
|
||||
|
||||
|
||||
//FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
#define FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING 144 // TODO: Figure out how to safely let this be calller-defined....
|
||||
|
||||
// nRF52810 has a single PWM peripheral (PWM0)
|
||||
// nRF52832 has three PWM peripherals (PWM0, PWM1, PWM2)
|
||||
// nRF52840 has four PWM peripherals (PWM0, PWM1, PWM2, PWM3)
|
||||
// NOTE: Update platforms.cpp in root of FastLED library if this changes
|
||||
#define FASTLED_NRF52_PWM_ID 0
|
||||
|
||||
extern uint32_t isrCount;
|
||||
|
||||
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER = RGB, int _XTRA0 = 0, bool _FLIP = false, int _WAIT_TIME_MICROSECONDS = 10>
|
||||
class ClocklessController : public CPixelLEDController<_RGB_ORDER> {
|
||||
static_assert(FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING > 0, "Maximum string length must be positive value (FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING)");
|
||||
static_assert(_T1 > 0 , "negative values are not allowed");
|
||||
static_assert(_T2 > 0 , "negative values are not allowed");
|
||||
static_assert(_T3 > 0 , "negative values are not allowed");
|
||||
static_assert(_T1 < (0x8000u-2u), "_T1 must fit in 15 bits");
|
||||
static_assert(_T2 < (0x8000u-2u), "_T2 must fit in 15 bits");
|
||||
static_assert(_T3 < (0x8000u-2u), "_T3 must fit in 15 bits");
|
||||
static_assert(_T1 < (0x8000u-2u), "_T0H must fit in 15 bits");
|
||||
static_assert(_T1+_T2 < (0x8000u-2u), "_T1H must fit in 15 bits");
|
||||
static_assert(_T1+_T2+_T3 < (0x8000u-2u), "_TOP must fit in 15 bits");
|
||||
static_assert(_T1+_T2+_T3 <= PWM_COUNTERTOP_COUNTERTOP_Msk, "_TOP too large for peripheral");
|
||||
|
||||
private:
|
||||
static const bool _INITIALIZE_PIN_HIGH = (_FLIP ? 1 : 0);
|
||||
static const uint16_t _POLARITY_BIT = (_FLIP ? 0 : 0x8000);
|
||||
|
||||
static const uint8_t _BITS_PER_PIXEL = (8 + _XTRA0) * 3; // NOTE: 3 means RGB only...
|
||||
static const uint16_t _PWM_BUFFER_COUNT = (_BITS_PER_PIXEL * FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING);
|
||||
static const uint8_t _T0H = ((uint16_t)(_T1 ));
|
||||
static const uint8_t _T1H = ((uint16_t)(_T1+_T2 ));
|
||||
static const uint8_t _TOP = ((uint16_t)(_T1+_T2+_T3));
|
||||
|
||||
// may as well be static, as can only attach one LED string per _DATA_PIN....
|
||||
static uint16_t s_SequenceBuffer[_PWM_BUFFER_COUNT];
|
||||
static uint16_t s_SequenceBufferValidElements;
|
||||
static volatile uint32_t s_SequenceBufferInUse;
|
||||
static CMinWait<_WAIT_TIME_MICROSECONDS> mWait; // ensure data has time to latch
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePinState() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
if (_INITIALIZE_PIN_HIGH) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePwmInstance(NRF_PWM_Type * pwm) {
|
||||
|
||||
// Pins must be set before enabling the peripheral
|
||||
pwm->PSEL.OUT[0] = FastPin<_DATA_PIN>::nrf_pin();
|
||||
pwm->PSEL.OUT[1] = NRF_PWM_PIN_NOT_CONNECTED;
|
||||
pwm->PSEL.OUT[2] = NRF_PWM_PIN_NOT_CONNECTED;
|
||||
pwm->PSEL.OUT[3] = NRF_PWM_PIN_NOT_CONNECTED;
|
||||
nrf_pwm_enable(pwm);
|
||||
nrf_pwm_configure(pwm, NRF_PWM_CLK_16MHz, NRF_PWM_MODE_UP, _TOP);
|
||||
nrf_pwm_decoder_set(pwm, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO);
|
||||
|
||||
// clear any prior shorts / interrupt enable bits
|
||||
nrf_pwm_shorts_set(pwm, 0);
|
||||
nrf_pwm_int_set(pwm, 0);
|
||||
// clear all prior events
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_STOPPED);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED0);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED1);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND0);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND1);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_PWMPERIODEND);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_LOOPSDONE);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_ConfigurePwmSequence(NRF_PWM_Type * pwm) {
|
||||
// config is easy, using SEQ0, no loops...
|
||||
nrf_pwm_sequence_t sequenceConfig;
|
||||
sequenceConfig.values.p_common = &(s_SequenceBuffer[0]);
|
||||
sequenceConfig.length = s_SequenceBufferValidElements;
|
||||
sequenceConfig.repeats = 0; // send the data once, and only once
|
||||
sequenceConfig.end_delay = 0; // no extra delay at the end of SEQ[0] / SEQ[1]
|
||||
nrf_pwm_sequence_set(pwm, 0, &sequenceConfig);
|
||||
nrf_pwm_sequence_set(pwm, 1, &sequenceConfig);
|
||||
nrf_pwm_loop_set(pwm, 0);
|
||||
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_EnableInterruptsAndShortcuts(NRF_PWM_Type * pwm) {
|
||||
IRQn_Type irqn = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getIRQn();
|
||||
// TODO: check API results...
|
||||
uint32_t result;
|
||||
|
||||
result = sd_nvic_SetPriority(irqn, configMAX_SYSCALL_INTERRUPT_PRIORITY);
|
||||
(void)result;
|
||||
result = sd_nvic_EnableIRQ(irqn);
|
||||
(void)result;
|
||||
|
||||
// shortcuts prevent (up to) 4-cycle delay from interrupt handler to next action
|
||||
uint32_t shortsToEnable = 0;
|
||||
shortsToEnable |= NRF_PWM_SHORT_SEQEND0_STOP_MASK; ///< SEQEND[0] --> STOP task.
|
||||
shortsToEnable |= NRF_PWM_SHORT_SEQEND1_STOP_MASK; ///< SEQEND[1] --> STOP task.
|
||||
//shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK; ///< LOOPSDONE --> SEQSTART[0] task.
|
||||
//shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK; ///< LOOPSDONE --> SEQSTART[1] task.
|
||||
shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_STOP_MASK; ///< LOOPSDONE --> STOP task.
|
||||
nrf_pwm_shorts_set(pwm, shortsToEnable);
|
||||
|
||||
// mark which events should cause interrupts...
|
||||
uint32_t interruptsToEnable = 0;
|
||||
interruptsToEnable |= NRF_PWM_INT_SEQEND0_MASK;
|
||||
interruptsToEnable |= NRF_PWM_INT_SEQEND1_MASK;
|
||||
interruptsToEnable |= NRF_PWM_INT_LOOPSDONE_MASK;
|
||||
interruptsToEnable |= NRF_PWM_INT_STOPPED_MASK;
|
||||
nrf_pwm_int_set(pwm, interruptsToEnable);
|
||||
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_StartTask(NRF_PWM_Type * pwm) {
|
||||
nrf_pwm_task_trigger(pwm, NRF_PWM_TASK_SEQSTART0);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void spinAcquireSequenceBuffer() {
|
||||
while (!tryAcquireSequenceBuffer());
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquireSequenceBuffer() {
|
||||
return __sync_bool_compare_and_swap(&s_SequenceBufferInUse, 0, 1);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseSequenceBuffer() {
|
||||
uint32_t tmp = __sync_val_compare_and_swap(&s_SequenceBufferInUse, 1, 0);
|
||||
if (tmp != 1) {
|
||||
// TODO: Error / Assert / log ?
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void isr_handler() {
|
||||
NRF_PWM_Type * pwm = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getPWM();
|
||||
IRQn_Type irqn = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getIRQn();
|
||||
|
||||
// Currently, only use SEQUENCE 0, so only event
|
||||
// of consequence is LOOPSDONE ...
|
||||
if (nrf_pwm_event_check(pwm,NRF_PWM_EVENT_STOPPED)) {
|
||||
nrf_pwm_event_clear(pwm,NRF_PWM_EVENT_STOPPED);
|
||||
|
||||
// update the minimum time to next call
|
||||
mWait.mark();
|
||||
// mark the sequence as no longer in use -- pointer, comparator, exchange value
|
||||
releaseSequenceBuffer();
|
||||
// prevent further interrupts from PWM events
|
||||
nrf_pwm_int_set(pwm, 0);
|
||||
// disable PWM interrupts - None of the PWM IRQs are shared
|
||||
// with other peripherals, avoiding complexity of shared IRQs.
|
||||
sd_nvic_DisableIRQ(irqn);
|
||||
// disable the PWM instance
|
||||
nrf_pwm_disable(pwm);
|
||||
// may take up to 4 cycles for writes to propagate (APB bus @ 16MHz)
|
||||
asm __volatile__ ( "NOP; NOP; NOP; NOP;" );
|
||||
// release the PWM arbiter to be re-used by another LED string
|
||||
PWM_Arbiter<FASTLED_NRF52_PWM_ID>::releaseFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void init() {
|
||||
FASTLED_NRF52_DEBUGPRINT("Clockless Timings:\n");
|
||||
FASTLED_NRF52_DEBUGPRINT(" T0H == %d", _T0H);
|
||||
FASTLED_NRF52_DEBUGPRINT(" T1H == %d", _T1H);
|
||||
FASTLED_NRF52_DEBUGPRINT(" TOP == %d\n", _TOP);
|
||||
// to avoid pin initialization from causing first LED to have invalid color,
|
||||
// call mWait.mark() to ensure data latches before color data gets sent.
|
||||
startPwmPlayback_InitializePinState();
|
||||
mWait.mark();
|
||||
|
||||
}
|
||||
virtual uint16_t getMaxRefreshRate() const { return 800; }
|
||||
|
||||
virtual void showPixels(PixelController<_RGB_ORDER> & pixels) {
|
||||
// wait for the only sequence buffer to become available
|
||||
spinAcquireSequenceBuffer();
|
||||
prepareSequenceBuffers(pixels);
|
||||
// ensure any prior data had time to latch
|
||||
mWait.wait();
|
||||
startPwmPlayback(s_SequenceBufferValidElements);
|
||||
return;
|
||||
}
|
||||
|
||||
template<uint8_t _BIT>
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void WriteBitToSequence(uint8_t byte, uint16_t * e) {
|
||||
*e = _POLARITY_BIT | (((byte & (1u << _BIT)) == 0) ? _T0H : _T1H);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void prepareSequenceBuffers(PixelController<_RGB_ORDER> & pixels) {
|
||||
s_SequenceBufferValidElements = 0;
|
||||
int32_t remainingSequenceElements = _PWM_BUFFER_COUNT;
|
||||
uint16_t * e = s_SequenceBuffer;
|
||||
uint32_t size_needed = pixels.size(); // count of pixels
|
||||
size_needed *= (8 + _XTRA0); // bits per pixel
|
||||
size_needed *= 2; // each bit takes two bytes
|
||||
|
||||
if (size_needed > _PWM_BUFFER_COUNT) {
|
||||
// TODO: assert()?
|
||||
return;
|
||||
}
|
||||
|
||||
while (pixels.has(1) && (remainingSequenceElements >= _BITS_PER_PIXEL)) {
|
||||
uint8_t b0 = pixels.loadAndScale0();
|
||||
WriteBitToSequence<7>(b0, e); ++e;
|
||||
WriteBitToSequence<6>(b0, e); ++e;
|
||||
WriteBitToSequence<5>(b0, e); ++e;
|
||||
WriteBitToSequence<4>(b0, e); ++e;
|
||||
WriteBitToSequence<3>(b0, e); ++e;
|
||||
WriteBitToSequence<2>(b0, e); ++e;
|
||||
WriteBitToSequence<1>(b0, e); ++e;
|
||||
WriteBitToSequence<0>(b0, e); ++e;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; ++i) {
|
||||
WriteBitToSequence<0>(0,e); ++e;
|
||||
}
|
||||
}
|
||||
uint8_t b1 = pixels.loadAndScale1();
|
||||
WriteBitToSequence<7>(b1, e); ++e;
|
||||
WriteBitToSequence<6>(b1, e); ++e;
|
||||
WriteBitToSequence<5>(b1, e); ++e;
|
||||
WriteBitToSequence<4>(b1, e); ++e;
|
||||
WriteBitToSequence<3>(b1, e); ++e;
|
||||
WriteBitToSequence<2>(b1, e); ++e;
|
||||
WriteBitToSequence<1>(b1, e); ++e;
|
||||
WriteBitToSequence<0>(b1, e); ++e;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; ++i) {
|
||||
WriteBitToSequence<0>(0,e); ++e;
|
||||
}
|
||||
}
|
||||
uint8_t b2 = pixels.loadAndScale2();
|
||||
WriteBitToSequence<7>(b2, e); ++e;
|
||||
WriteBitToSequence<6>(b2, e); ++e;
|
||||
WriteBitToSequence<5>(b2, e); ++e;
|
||||
WriteBitToSequence<4>(b2, e); ++e;
|
||||
WriteBitToSequence<3>(b2, e); ++e;
|
||||
WriteBitToSequence<2>(b2, e); ++e;
|
||||
WriteBitToSequence<1>(b2, e); ++e;
|
||||
WriteBitToSequence<0>(b2, e); ++e;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; ++i) {
|
||||
WriteBitToSequence<0>(0,e); ++e;
|
||||
}
|
||||
}
|
||||
|
||||
// advance pixel and sequence pointers
|
||||
s_SequenceBufferValidElements += _BITS_PER_PIXEL;
|
||||
remainingSequenceElements -= _BITS_PER_PIXEL;
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback(uint16_t bytesToSend) {
|
||||
PWM_Arbiter<FASTLED_NRF52_PWM_ID>::acquire(isr_handler);
|
||||
NRF_PWM_Type * pwm = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getPWM();
|
||||
|
||||
// mark the sequence as being in-use
|
||||
__sync_fetch_and_or(&s_SequenceBufferInUse, 1);
|
||||
|
||||
startPwmPlayback_InitializePinState();
|
||||
startPwmPlayback_InitializePwmInstance(pwm);
|
||||
startPwmPlayback_ConfigurePwmSequence(pwm);
|
||||
startPwmPlayback_EnableInterruptsAndShortcuts(pwm);
|
||||
startPwmPlayback_StartTask(pwm);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t* getRawSequenceBuffer() { return s_SequenceBuffer; }
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getRawSequenceBufferSize() { return _PWM_BUFFER_COUNT; }
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getSequenceBufferInUse() { return s_SequenceBufferInUse; }
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawSequenceBuffer(uint16_t bytesToSend) {
|
||||
mWait.wait(); // ensure min time between updates
|
||||
startPwmPlayback(bytesToSend);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawBytes(uint8_t * arrayOfBytes, uint16_t bytesToSend) {
|
||||
// wait for sequence buffer to be available
|
||||
while (s_SequenceBufferInUse != 0);
|
||||
|
||||
s_SequenceBufferValidElements = 0;
|
||||
int32_t remainingSequenceElements = _PWM_BUFFER_COUNT;
|
||||
uint16_t * e = s_SequenceBuffer;
|
||||
uint8_t * nextByte = arrayOfBytes;
|
||||
for (uint16_t bytesRemain = bytesToSend;
|
||||
(remainingSequenceElements >= 8) && (bytesRemain > 0);
|
||||
--bytesRemain,
|
||||
remainingSequenceElements -= 8,
|
||||
s_SequenceBufferValidElements += 8
|
||||
) {
|
||||
uint8_t b = *nextByte;
|
||||
WriteBitToSequence<7,false>(b, e); ++e;
|
||||
WriteBitToSequence<6,false>(b, e); ++e;
|
||||
WriteBitToSequence<5,false>(b, e); ++e;
|
||||
WriteBitToSequence<4,false>(b, e); ++e;
|
||||
WriteBitToSequence<3,false>(b, e); ++e;
|
||||
WriteBitToSequence<2,false>(b, e); ++e;
|
||||
WriteBitToSequence<1,false>(b, e); ++e;
|
||||
WriteBitToSequence<0,false>(b, e); ++e;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; ++i) {
|
||||
WriteBitToSequence<0,_FLIP>(0,e); ++e;
|
||||
}
|
||||
}
|
||||
}
|
||||
mWait.wait(); // ensure min time between updates
|
||||
|
||||
startPwmPlayback(s_SequenceBufferValidElements);
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
};
|
||||
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferValidElements = 0;
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
uint32_t volatile ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferInUse = 0;
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBuffer[_PWM_BUFFER_COUNT];
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
CMinWait<_WAIT_TIME_MICROSECONDS> ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::mWait;
|
||||
|
||||
/* nrf_pwm solution
|
||||
//
|
||||
// When the nRF52 softdevice (e.g., BLE) is enabled, the CPU can be pre-empted
|
||||
// at any time for radio interrupts. These interrupts cannot be disabled.
|
||||
// The problem is, even simple BLE advertising interrupts may take **`348μs`**
|
||||
// (per softdevice 1.40, see http://infocenter.nordicsemi.com/pdf/S140_SDS_v1.3.pdf)
|
||||
//
|
||||
// The nRF52 chips have a decent Easy-DMA-enabled PWM peripheral.
|
||||
//
|
||||
// The major downside:
|
||||
// [] The PWM peripheral has a fixed input buffer size at 16 bits per clock cycle.
|
||||
// (each clockless protocol bit == 2 bytes)
|
||||
//
|
||||
// The major upsides include:
|
||||
// [] Fully asynchronous, freeing CPU for other tasks
|
||||
// [] Softdevice interrupts do not affect PWM clocked output (reliable clocking)
|
||||
//
|
||||
// The initial solution generally does the following for showPixels():
|
||||
// [] wait for a sequence buffer to become available
|
||||
// [] prepare the entire LED string's sequence (see `prepareSequenceBuffers()`)
|
||||
// [] ensures minimum wait time from prior sequence's end
|
||||
//
|
||||
// Options after initial solution working:
|
||||
// []
|
||||
|
||||
// TODO: Double-buffers, so one can be doing DMA while the second
|
||||
// buffer is being prepared.
|
||||
// TODO: Pool of buffers, so can keep N-1 active in DMA, while
|
||||
// preparing data in the final buffer?
|
||||
// Write another class similar to PWM_Arbiter, only for
|
||||
// tracking use of sequence buffers?
|
||||
// TODO: Use volatile variable to track buffers that the
|
||||
// prior DMA operation is finished with, so can fill
|
||||
// in those buffers with newly-prepared data...
|
||||
// apis to send the pre-generated buffer. This would be essentially asynchronous,
|
||||
// and result in efficient run time if the pixels are either (a) static, or
|
||||
// (b) cycle through a limited number of options whose converted results can
|
||||
// be cached and re-used. While simple, this method takes lots of extra RAM...
|
||||
// 16 bits for every full clock (high/low) cycle.
|
||||
//
|
||||
// Clockless chips typically send 24 bits (3x 8-bit) per pixel.
|
||||
// One odd clockless chip sends 36 bits (3x 12-bit) per pixel.
|
||||
// Each bit requires a 16-bit sequence entry for the PWM peripheral.
|
||||
// This gives approximately:
|
||||
// 24 bpp 36 bpp
|
||||
// ==========================================
|
||||
// 1 pixel 48 bytes 72 bytes
|
||||
// 32 pixels 1,536 bytes 2,304 bytes
|
||||
// 64 pixels 3,072 bytes 4,608 bytes
|
||||
//
|
||||
//
|
||||
// UPDATE: this is the method I'm choosing, to get _SOMETHING_
|
||||
// clockless working... 3k RAM for 64 pixels is acceptable
|
||||
// for a first release, as it allows re-use of FASTLED
|
||||
// color correction, dithering, etc. ....
|
||||
*/
|
||||
|
||||
//FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // NRF52_SERIES
|
||||
#endif // __INC_CLOCKLESS_ARM_NRF52
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef __INC_FASTLED_ARM_NRF52_H
|
||||
#define __INC_FASTLED_ARM_NRF52_H
|
||||
|
||||
#include "led_sysdefs_arm_nrf52.h"
|
||||
#include "arbiter_nrf52.h"
|
||||
#include "fastpin_arm_nrf52.h"
|
||||
#include "fastspi_arm_nrf52.h"
|
||||
#include "clockless_arm_nrf52.h"
|
||||
|
||||
#endif // #ifndef __INC_FASTLED_ARM_NRF52_H
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
#ifndef __FASTPIN_ARM_NRF52_H
|
||||
#define __FASTPIN_ARM_NRF52_H
|
||||
|
||||
/*
|
||||
//
|
||||
// Background:
|
||||
// ===========
|
||||
// the nRF52 has more than 32 ports, and thus must support
|
||||
// two distinct GPIO port registers.
|
||||
//
|
||||
// For the nRF52 series, the structure to control the port is
|
||||
// `NRF_GPIO_Type`, with separate addresses mapped for set, clear, etc.
|
||||
// The two ports are defined as NRF_P0 and NRF_P1.
|
||||
// An example declaration for the ports is:
|
||||
// #define NRF_P0_BASE 0x50000000UL
|
||||
// #define NRF_P1_BASE 0x50000300UL
|
||||
// #define NRF_P0 ((NRF_GPIO_Type*)NRF_P0_BASE)
|
||||
// #define NRF_P1 ((NRF_GPIO_Type*)NRF_P1_BASE)
|
||||
//
|
||||
// Therefore, ideally, the _FL_DEFPIN() macro would simply
|
||||
// conditionally pass either NRF_P0 or NRF_P1 to the underlying
|
||||
// FastPin<> template class class.
|
||||
//
|
||||
// The "pin" provided to the FastLED<> template (and which
|
||||
// the _FL_DEFPIN() macro specializes for valid pins) is NOT
|
||||
// the microcontroller port.pin, but the Arduino digital pin.
|
||||
// Some boards have an identity mapping (e.g., nRF52832 Feather)
|
||||
// but most do not. Therefore, the _FL_DEFPIN() macro
|
||||
// must translate the Arduino pin to the mcu port.pin.
|
||||
//
|
||||
//
|
||||
// Difficulties:
|
||||
// =============
|
||||
// The goal is to avoid any such lookups, using compile-time
|
||||
// optimized functions for speed, in line with FastLED's
|
||||
// overall design goals. This means constexpr, compile-time
|
||||
// and aggressive inlining of functions....
|
||||
//
|
||||
// Right away, this precludes the use of g_ADigitalPinMap,
|
||||
// which is not constexpr, and thus not available for
|
||||
// preprocessor/compile-time optimizations. Therefore,
|
||||
// we have to specialize FastPin<uint8_t PIN>, given a
|
||||
// compile-time value for PIN, into at least a PORT and
|
||||
// a BITMASK for the port.
|
||||
//
|
||||
// Arduino compiles using C++11 for at least Feather nRF52840 Express.
|
||||
// C++11 is very restrictive about template parameters.
|
||||
// Template parameters can only be:
|
||||
// 1. a type (as most people expect)
|
||||
// 2. a template
|
||||
// 3. a constexpr native integer type
|
||||
//
|
||||
// Therefore, attempts to use `NRF_GPIO_Type *` as a
|
||||
// template parameter will fail....
|
||||
//
|
||||
// Solution:
|
||||
// =========
|
||||
// The solution chosen is to define a unique structure for each port,
|
||||
// whose SOLE purpose is to have a static inline function that
|
||||
// returns the `NRF_GPIO_Type *` that is needed.
|
||||
//
|
||||
// Thus, while it's illegal to pass `NRF_P0` as a template
|
||||
// parameter, it's perfectly legal to pass `__generated_struct_NRF_P0`,
|
||||
// and have the template call a well-known `static inline` function
|
||||
// that returns `NRF_P0` ... which is itself a compile-time constant.
|
||||
//
|
||||
// Note that additional magic can be applied that will automatically
|
||||
// generate the structures. If you want to add that to this platform,
|
||||
// check out the KL26 platform files for a starting point.
|
||||
//
|
||||
*/
|
||||
|
||||
// manually define two structures, to avoid fighting with preprocessor macros
|
||||
struct __generated_struct_NRF_P0 {
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() {
|
||||
return NRF_P0_BASE;
|
||||
}
|
||||
};
|
||||
// Not all NRF52 chips have two ports. Only define if P1 is present.
|
||||
#if defined(NRF_P1_BASE)
|
||||
struct __generated_struct_NRF_P1 {
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() {
|
||||
return NRF_P1_BASE;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
// The actual class template can then use a typename, for what is essentially a constexpr NRF_GPIO_Type*
|
||||
template <uint32_t _MASK, typename _PORT, uint8_t _PORT_NUMBER, uint8_t _PIN_NUMBER> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void setOutput() {
|
||||
// OK for this to be more than one instruction, as unusual to quickly switch input/output modes
|
||||
nrf_gpio_cfg(
|
||||
nrf_pin(),
|
||||
NRF_GPIO_PIN_DIR_OUTPUT, // set pin as output
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering
|
||||
NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled
|
||||
NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds
|
||||
NRF_GPIO_PIN_NOSENSE // pin sense level disabled
|
||||
);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void setInput() {
|
||||
// OK for this to be more than one instruction, as unusual to quickly switch input/output modes
|
||||
nrf_gpio_cfg(
|
||||
nrf_pin(),
|
||||
NRF_GPIO_PIN_DIR_INPUT, // set pin as input
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering
|
||||
NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled
|
||||
NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds
|
||||
NRF_GPIO_PIN_NOSENSE // pin sense level disabled
|
||||
);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void hi() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTSET = _MASK; } // sets _MASK in the SET OUTPUT register (output set high)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void lo() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTCLR = _MASK; } // sets _MASK in the CLEAR OUTPUT register (output set low)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void toggle() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT ^= _MASK; } // toggles _MASK bits in the OUTPUT GPIO port directly
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void strobe() { toggle(); toggle(); } // BUGBUG -- Is this used by FastLED? Without knowing (for example) SPI Speed?
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t hival() { return (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT | _MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 1
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t loval() { return (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT & ~_MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 0
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t port() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT); } // gets raw pointer to OUTPUT GPIO port
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t cport() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTCLR); } // gets raw pointer to SET DIRECTION GPIO port
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t sport() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTSET); } // gets raw pointer to CLEAR DIRECTION GPIO port
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t mask() { return _MASK; } // gets the value of _MASK
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void hi (FASTLED_REGISTER port_ptr_t port) { hi(); } // sets _MASK in the SET OUTPUT register (output set high)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void lo (FASTLED_REGISTER port_ptr_t port) { lo(); } // sets _MASK in the CLEAR OUTPUT register (output set low)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void set(FASTLED_REGISTER port_t val ) { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT = val; } // sets entire port's value (optimization used by FastLED)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) { *port = val; }
|
||||
constexpr static uint32_t nrf_pin2() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); }
|
||||
constexpr static bool LowSpeedOnlyRecommended() {
|
||||
// Caller must always determine if high speed use if allowed on a given pin,
|
||||
// because it depends on more than just the chip packaging ... it depends on entire board (and even system) design.
|
||||
return false; // choosing default to be FALSE, to allow users to ATTEMPT to use high-speed on pins where support is not known
|
||||
}
|
||||
// Expose the nrf pin (port/pin combined), port, and pin as properties (e.g., for setting up SPI)
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint32_t nrf_pin() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); }
|
||||
};
|
||||
|
||||
|
||||
template <uint32_t _MASK, typename _PORT, uint8_t _PORT_NUMBER, uint8_t _PIN_NUMBER>
|
||||
class _INVALID_ARMPIN: public _ARMPIN<_MASK, _PORT, _PORT_NUMBER, _PIN_NUMBER> {
|
||||
public:
|
||||
_INVALID_ARMPIN() {
|
||||
Serial.print("For whatever reason pin "); Serial.print(_PIN_NUMBER);
|
||||
Serial.println(" has been marked as invalid. Please use a different pin.");
|
||||
Serial.println("Pausing execution for 2 seconds.");
|
||||
delay(2000); // Give time for the message to be printed.
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// BOARD_PIN can be either the pin portion of a port.pin, or the combined NRF_GPIO_PIN_MAP() number.
|
||||
// For example both the following two defines refer to P1.15 (pin 47) as Arduino pin 3:
|
||||
// _FL_DEFPIN(3, 15, 1);
|
||||
// _FL_DEFPIN(3, 47, 1);
|
||||
//
|
||||
// Similarly, the following defines are all equivalent:
|
||||
// _DEFPIN_ARM_IDENTITY_P1(47);
|
||||
// _FL_DEFPIN(47, 15, 1);
|
||||
// _FL_DEFPIN(47, 47, 1);
|
||||
//
|
||||
|
||||
#define _FL_DEF_INVALID_PIN(ARDUINO_PIN, BOARD_PIN, BOARD_PORT) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _INVALID_ARMPIN< \
|
||||
0, \
|
||||
__generated_struct_NRF_P0, \
|
||||
0, \
|
||||
0 \
|
||||
> \
|
||||
{}
|
||||
|
||||
#define _FL_DEFPIN(ARDUINO_PIN, BOARD_PIN, BOARD_PORT) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _ARMPIN< \
|
||||
1u << (BOARD_PIN & 31u), \
|
||||
__generated_struct_NRF_P ## BOARD_PORT, \
|
||||
(BOARD_PIN / 32), \
|
||||
BOARD_PIN & 31u \
|
||||
> \
|
||||
{}
|
||||
|
||||
#define _DEFPIN_ARM_IDENTITY_P0(ARDUINO_PIN) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _ARMPIN< \
|
||||
1u << (ARDUINO_PIN & 31u), \
|
||||
__generated_struct_NRF_P0, \
|
||||
0, \
|
||||
(ARDUINO_PIN & 31u) + 0 \
|
||||
> \
|
||||
{}
|
||||
|
||||
#define _DEFPIN_ARM_IDENTITY_P1(ARDUINO_PIN) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _ARMPIN< \
|
||||
1u << (ARDUINO_PIN & 31u), \
|
||||
__generated_struct_NRF_P1, \
|
||||
1, \
|
||||
(ARDUINO_PIN & 31u) + 32 \
|
||||
> \
|
||||
{}
|
||||
|
||||
// The actual pin definitions are in a separate header file...
|
||||
#include "fastpin_arm_nrf52_variants.h"
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif // #ifndef __FASTPIN_ARM_NRF52_H
|
||||
@@ -0,0 +1,965 @@
|
||||
#ifndef __FASTPIN_ARM_NRF52_VARIANTS_H
|
||||
#define __FASTPIN_ARM_NRF52_VARIANTS_H
|
||||
|
||||
// use this to determine if found variant or not (avoid multiple boards at once)
|
||||
#undef __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
|
||||
// Adafruit Bluefruit nRF52832 Feather
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52832_FEATHER)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Adafruit Bluefruit nRF52832 Feather is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // xtal 1
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // xtal 2
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // a0
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // a1
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // a2
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // a3
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // TXD
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // GPIO #7
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // RXD
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // NFC1
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // NFC2
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // GPIO #11
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // SCK
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // MOSI
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // MISO
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // GPIO #15
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // GPIO #16
|
||||
_DEFPIN_ARM_IDENTITY_P0(17); // LED #1 (red)
|
||||
_DEFPIN_ARM_IDENTITY_P0(18); // SWO
|
||||
_DEFPIN_ARM_IDENTITY_P0(19); // LED #2 (blue)
|
||||
_DEFPIN_ARM_IDENTITY_P0(20); // DFU
|
||||
// _DEFPIN_ARM_IDENTITY_P0(21); // Reset -- not valid to use for FastLED?
|
||||
// _DEFPIN_ARM_IDENTITY_P0(22); // Factory Reset -- not vaild to use for FastLED?
|
||||
// _DEFPIN_ARM_IDENTITY_P0(23); // N/A
|
||||
// _DEFPIN_ARM_IDENTITY_P0(24); // N/A
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // SDA
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // SCL
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // GPIO #27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // A4
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // A5
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // A6
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // A7
|
||||
#endif // defined (ARDUINO_NRF52832_FEATHER)
|
||||
|
||||
// Adafruit Circuit Playground Bluefruit
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_CIRCUITPLAY)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
// This board is a bit of a mess ... as it defines
|
||||
// multiple arduino pins to map to a single Port/Pin
|
||||
// combination.
|
||||
|
||||
// Use PIN_NEOPIXEL (D8) for the ten built-in neopixels
|
||||
_FL_DEFPIN( 8, 13, 0); // P0.13 -- D8 / Neopixels
|
||||
|
||||
// PIN_A0 is connect to an amplifier, and thus *might*
|
||||
// not be suitable for use with FastLED.
|
||||
// Do not enable this pin until can confirm
|
||||
// signal integrity from this pin.
|
||||
//
|
||||
// NOTE: it might also be possible if first disable
|
||||
// the amp using D11 ("speaker shutdown" pin)
|
||||
//
|
||||
// _FL_DEFPIN(14, 26, 0); // P0.26 -- A0 / D12 / Audio Out
|
||||
_FL_DEFPIN(15, 2, 0); // P0.02 -- A1 / D6
|
||||
_FL_DEFPIN(16, 29, 0); // P0.29 -- A2 / D9
|
||||
_FL_DEFPIN(17, 3, 0); // P0.03 -- A3 / D10
|
||||
_FL_DEFPIN(18, 4, 0); // P0.04 -- A4 / D3 / SCL
|
||||
_FL_DEFPIN(19, 5, 0); // P0.05 -- A5 / D2 / SDA
|
||||
_FL_DEFPIN(20, 30, 0); // P0.30 -- A6 / D0 / UART RX
|
||||
_FL_DEFPIN(21, 14, 0); // P0.14 -- AREF / D1 / UART TX
|
||||
|
||||
#endif
|
||||
|
||||
// Adafruit Bluefruit nRF52840 Feather Express
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_FEATHER)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
// Arduino pins 0..7
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 -- UART TX
|
||||
//_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 -- UART RX
|
||||
_FL_DEFPIN( 2, 10, 0); // D2 is P0.10 -- NFC2
|
||||
_FL_DEFPIN( 3, 47, 1); // D3 is P1.15 -- PIN_LED1 (red)
|
||||
_FL_DEFPIN( 4, 42, 1); // D4 is P1.10 -- PIN_LED2 (blue)
|
||||
_FL_DEFPIN( 5, 40, 1); // D5 is P1.08 -- SPI/SS
|
||||
_FL_DEFPIN( 6, 7, 0); // D6 is P0.07
|
||||
_FL_DEFPIN( 7, 34, 1); // D7 is P1.02 -- PIN_DFU (UIButton)
|
||||
|
||||
// Arduino pins 8..15
|
||||
_FL_DEFPIN( 8, 16, 0); // D8 is P0.16 -- PIN_NEOPIXEL
|
||||
_FL_DEFPIN( 9, 26, 0); // D9 is P0.26
|
||||
_FL_DEFPIN(10, 27, 0); // D10 is P0.27
|
||||
_FL_DEFPIN(11, 6, 0); // D11 is P0.06
|
||||
_FL_DEFPIN(12, 8, 0); // D12 is P0.08
|
||||
_FL_DEFPIN(13, 41, 1); // D13 is P1.09
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0
|
||||
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1
|
||||
|
||||
// Arduino pins 16..23
|
||||
_FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2
|
||||
_FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5
|
||||
//_FL_DEFPIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery!
|
||||
//_FL_DEFPIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF
|
||||
_FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA
|
||||
_FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL
|
||||
|
||||
// Arduino pins 24..31
|
||||
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO
|
||||
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI
|
||||
_FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK
|
||||
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK
|
||||
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS
|
||||
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0
|
||||
//_FL_DEFPIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1
|
||||
//_FL_DEFPIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2
|
||||
|
||||
// Arduino pins 32..34
|
||||
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3
|
||||
//_FL_DEFPIN(33, 9, 0); // D33 is NFC1, only accessible via test point
|
||||
#endif // defined (ARDUINO_NRF52840_FEATHER)
|
||||
|
||||
|
||||
// Adafruit Bluefruit nRF52840 Feather Sense
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_FEATHER_SENSE)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
// Arduino pins 0..7
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 -- UART TX
|
||||
_FL_DEF_INVALID_PIN( 1, 24, 0); // D1 is P0.24 -- UART RX
|
||||
_FL_DEFPIN( 2, 10, 0); // D2 is P0.10 -- NFC2
|
||||
_FL_DEFPIN( 3, 43, 1); // D3 is P1.11
|
||||
_FL_DEFPIN( 4, 42, 1); // D4 is P1.10 -- PIN_LED2 (blue)
|
||||
_FL_DEFPIN( 5, 40, 1); // D5 is P1.08 -- SPI/SS
|
||||
_FL_DEFPIN( 6, 7, 0); // D6 is P0.07
|
||||
_FL_DEFPIN( 7, 34, 1); // D7 is P1.02 -- PIN_DFU (UIButton)
|
||||
|
||||
// Arduino pins 8..15
|
||||
_FL_DEFPIN( 8, 16, 0); // D8 is P0.16 -- PIN_NEOPIXEL
|
||||
_FL_DEFPIN( 9, 26, 0); // D9 is P0.26
|
||||
_FL_DEFPIN(10, 27, 0); // D10 is P0.27
|
||||
_FL_DEFPIN(11, 6, 0); // D11 is P0.06
|
||||
_FL_DEFPIN(12, 8, 0); // D12 is P0.08
|
||||
_FL_DEFPIN(13, 41, 1); // D13 is P1.09 -- PIN_LED1 (red)
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0
|
||||
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1
|
||||
|
||||
// Arduino pins 16..23
|
||||
_FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2
|
||||
_FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5
|
||||
_FL_DEF_INVALID_PIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery!
|
||||
_FL_DEF_INVALID_PIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF
|
||||
_FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA
|
||||
_FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL
|
||||
|
||||
// Arduino pins 24..31
|
||||
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO
|
||||
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI
|
||||
_FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK
|
||||
_FL_DEF_INVALID_PIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK
|
||||
_FL_DEF_INVALID_PIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS
|
||||
_FL_DEF_INVALID_PIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0
|
||||
_FL_DEF_INVALID_PIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1
|
||||
_FL_DEF_INVALID_PIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2
|
||||
|
||||
// Arduino pins 32..34
|
||||
//_FL_DEF_INVALID_PIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3
|
||||
//_FL_DEF_INVALID_PIN(33, 9, 0); // D33 is NFC1, only accessible via test point
|
||||
#endif // defined (ARDUINO_NRF52840_FEATHER_SENSE)
|
||||
|
||||
// Adafruit Bluefruit nRF52840 Metro Express
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_METRO)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Adafruit Bluefruit nRF52840 Metro Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART TX)
|
||||
_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART RX)
|
||||
_FL_DEFPIN( 2, 10, 1); // D2 is P1.10
|
||||
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
|
||||
_FL_DEFPIN( 4, 11, 1); // D4 is P1.11
|
||||
_FL_DEFPIN( 5, 12, 1); // D5 is P1.12
|
||||
_FL_DEFPIN( 6, 14, 1); // D6 is P1.14
|
||||
_FL_DEFPIN( 7, 26, 0); // D7 is P0.26
|
||||
_FL_DEFPIN( 8, 27, 0); // D8 is P0.27
|
||||
_FL_DEFPIN( 9, 12, 0); // D9 is P0.12
|
||||
_FL_DEFPIN(10, 6, 0); // D10 is P0.06
|
||||
_FL_DEFPIN(11, 8, 0); // D11 is P0.08
|
||||
_FL_DEFPIN(12, 9, 1); // D12 is P1.09
|
||||
_FL_DEFPIN(13, 14, 0); // D13 is P0.14
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0)
|
||||
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 30, 0); // D17 is P0.30 (A3)
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4)
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5)
|
||||
_FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A6, battery)
|
||||
_FL_DEFPIN(21, 31, 0); // D21 is P0.31 (A7, ARef)
|
||||
_FL_DEFPIN(22, 15, 0); // D22 is P0.15 (SDA)
|
||||
_FL_DEFPIN(23, 16, 0); // D23 is P0.16 (SCL)
|
||||
_FL_DEFPIN(24, 11, 0); // D24 is P0.11 (SPI MISO)
|
||||
_FL_DEFPIN(25, 8, 1); // D25 is P1.08 (SPI MOSI)
|
||||
_FL_DEFPIN(26, 7, 0); // D26 is P0.07 (SPI SCK )
|
||||
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 (QSPI CLK )
|
||||
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 (QSPI CS )
|
||||
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 (QSPI Data 0)
|
||||
//_FL_DEFPIN(30, 23, 0); // D30 is P0.23 (QSPI Data 1)
|
||||
//_FL_DEFPIN(31, 22, 0); // D31 is P0.22 (QSPI Data 2)
|
||||
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI Data 3)
|
||||
_FL_DEFPIN(33, 13, 1); // D33 is P1.13 LED1
|
||||
_FL_DEFPIN(34, 15, 1); // D34 is P1.15 LED2
|
||||
_FL_DEFPIN(35, 13, 0); // D35 is P0.13 NeoPixel
|
||||
_FL_DEFPIN(36, 0, 1); // D36 is P1.02 Switch
|
||||
_FL_DEFPIN(37, 0, 1); // D37 is P1.00 SWO/DFU
|
||||
_FL_DEFPIN(38, 9, 0); // D38 is P0.09 NFC1
|
||||
_FL_DEFPIN(39, 10, 0); // D39 is P0.10 NFC2
|
||||
#endif // defined (ARDUINO_NRF52840_METRO)
|
||||
|
||||
// Adafruit Bluefruit on nRF52840DK PCA10056
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_PCA10056)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
#if defined(USE_ARDUINO_PIN_NUMBERING)
|
||||
#error "Define of `USE_ARDUINO_PIN_NUMBERING` has known errors in pin mapping -- select different mapping"
|
||||
#elif defined(FASTLED_NRF52_USE_ARDUINO_UNO_R3_HEADER_PIN_NUMBERING)
|
||||
/* The following allows defining and using the FastPin<> templates,
|
||||
using the Arduino UNO R3 connector pin definitions.
|
||||
*/
|
||||
_FL_DEFPIN( 0, 1, 1); // D0 is P1.01
|
||||
_FL_DEFPIN( 1, 2, 1); // D1 is P1.02
|
||||
_FL_DEFPIN( 2, 3, 1); // D2 is P1.03
|
||||
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
|
||||
_FL_DEFPIN( 4, 5, 1); // D4 is P1.05
|
||||
_FL_DEFPIN( 5, 6, 1); // D5 is P1.06
|
||||
_FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option)
|
||||
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option)
|
||||
_FL_DEFPIN( 8, 10, 1); // D8 is P1.10
|
||||
_FL_DEFPIN( 9, 11, 1); // D9 is P1.11
|
||||
_FL_DEFPIN(10, 12, 1); // D10 is P1.12
|
||||
_FL_DEFPIN(11, 13, 1); // D11 is P1.13
|
||||
_FL_DEFPIN(12, 14, 1); // D12 is P1.14
|
||||
_FL_DEFPIN(13, 15, 1); // D13 is P1.15
|
||||
// Arduino UNO uses pins D14..D19 to map to header pins A0..A5
|
||||
// AREF has no equivalent digital pin map on Arduino, would be P0.02
|
||||
_FL_DEFPIN(14, 3, 0); // D14 / A0 is P0.03
|
||||
_FL_DEFPIN(15, 4, 0); // D15 / A1 is P0.04
|
||||
_FL_DEFPIN(16, 28, 0); // D16 / A2 is P0.28
|
||||
_FL_DEFPIN(17, 29, 0); // D17 / A3 is P0.29
|
||||
// Cannot determine which pin on PCA10056 would be intended solely from UNO R3 digital pin number
|
||||
//_FL_DEFPIN(18, 30, 0); // D18 could be one of two pins: A4 would be P0.30, SDA would be P0.26
|
||||
//_FL_DEFPIN(19, 31, 0); // D19 could be one of two pins: A5 would be P0.31, SCL would be P0.27
|
||||
#elif defined(FASTLED_NRF52_USE_ARDUINO_MEGA_2560_REV3_HEADER_PIN_NUMBERING)
|
||||
/* The following allows defining and using the FastPin<> templates,
|
||||
using the Arduino UNO R3 connector pin definitions.
|
||||
*/
|
||||
_FL_DEFPIN( 0, 1, 1); // D0 is P1.01
|
||||
_FL_DEFPIN( 1, 2, 1); // D1 is P1.02
|
||||
_FL_DEFPIN( 2, 3, 1); // D2 is P1.03
|
||||
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
|
||||
_FL_DEFPIN( 4, 5, 1); // D4 is P1.05
|
||||
_FL_DEFPIN( 5, 6, 1); // D5 is P1.06
|
||||
_FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option)
|
||||
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option)
|
||||
_FL_DEFPIN( 8, 10, 1); // D8 is P1.10
|
||||
_FL_DEFPIN( 9, 11, 1); // D9 is P1.11
|
||||
_FL_DEFPIN(10, 12, 1); // D10 is P1.12
|
||||
_FL_DEFPIN(11, 13, 1); // D11 is P1.13
|
||||
_FL_DEFPIN(12, 14, 1); // D12 is P1.14
|
||||
_FL_DEFPIN(13, 15, 1); // D13 is P1.15
|
||||
|
||||
// Arduino MEGA 2560 has additional digital pins on lower digital header
|
||||
_FL_DEFPIN(14, 10, 0); // D14 is P0.10
|
||||
_FL_DEFPIN(15, 9, 0); // D15 is P0.09
|
||||
_FL_DEFPIN(16, 8, 0); // D16 is P0.08
|
||||
_FL_DEFPIN(17, 7, 0); // D17 is P0.07
|
||||
_FL_DEFPIN(18, 6, 0); // D14 is P0.06
|
||||
_FL_DEFPIN(19, 5, 0); // D15 is P0.05
|
||||
// Cannot determine which pin on PCA10056 would be intended solely from UNO MEGA 2560 digital pin number
|
||||
//_FL_DEFPIN(20, 1, 0); // D20 could be one of two pins: D20 on lower header would be P0.01, SDA would be P0.26
|
||||
//_FL_DEFPIN(21, 0, 0); // D21 could be one of two pins: D21 on lower header would be P0.00, SCL would be P0.27
|
||||
|
||||
// Arduino MEGA 2560 has D22-D53 exposed on perpendicular two-row header
|
||||
// PCA10056 has support for D22-D38 via a 2x19 header at that location (D39 is GND on PCA10056)
|
||||
_FL_DEFPIN(22, 11, 0); // D22 is P0.11
|
||||
_FL_DEFPIN(23, 12, 0); // D23 is P0.12
|
||||
_FL_DEFPIN(24, 13, 0); // D24 is P0.13
|
||||
_FL_DEFPIN(25, 14, 0); // D25 is P0.14
|
||||
_FL_DEFPIN(26, 15, 0); // D26 is P0.15
|
||||
_FL_DEFPIN(27, 16, 0); // D27 is P0.16
|
||||
// _FL_DEFPIN(28, 17, 0); // D28 is P0.17 (QSPI !CS )
|
||||
// _FL_DEFPIN(29, 18, 0); // D29 is P0.18 (RESET)
|
||||
// _FL_DEFPIN(30, 19, 0); // D30 is P0.19 (QSPI CLK)
|
||||
// _FL_DEFPIN(31, 20, 0); // D31 is P0.20 (QSPI DIO0)
|
||||
// _FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI DIO1)
|
||||
// _FL_DEFPIN(33, 22, 0); // D33 is P0.22 (QSPI DIO2)
|
||||
// _FL_DEFPIN(34, 23, 0); // D34 is P0.23 (QSPI DIO3)
|
||||
_FL_DEFPIN(35, 24, 0); // D35 is P0.24
|
||||
_FL_DEFPIN(36, 25, 0); // D36 is P0.25
|
||||
_FL_DEFPIN(37, 0, 1); // D37 is P1.00
|
||||
_FL_DEFPIN(38, 9, 1); // D38 is P1.09
|
||||
// _FL_DEFPIN(39, , 0); // D39 is P0.
|
||||
|
||||
|
||||
// Arduino MEGA 2560 uses pins D54..D59 to map to header pins A0..A5
|
||||
// (it also has D60..D69 for A6..A15, which have no corresponding header on PCA10056)
|
||||
// AREF has no equivalent digital pin map on Arduino, would be P0.02
|
||||
_FL_DEFPIN(54, 3, 0); // D54 / A0 is P0.03
|
||||
_FL_DEFPIN(55, 4, 0); // D55 / A1 is P0.04
|
||||
_FL_DEFPIN(56, 28, 0); // D56 / A2 is P0.28
|
||||
_FL_DEFPIN(57, 29, 0); // D57 / A3 is P0.29
|
||||
_FL_DEFPIN(58, 30, 0); // D58 / A4 is P0.30
|
||||
_FL_DEFPIN(59, 31, 0); // D59 / A5 is P0.31
|
||||
|
||||
#else // identity mapping of arduino pin to port/pin
|
||||
/* 48 pins, defined using natural mapping in Adafruit's variant.cpp (!) */
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 (XL1 .. ensure SB4 bridged, SB2 cut)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (XL2 .. ensure SB3 bridged, SB1 cut)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (AIN0)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (AIN1)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (AIN2 / UART CTS option)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (AIN3 / UART RTS)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (UART TxD)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (TRACECLK / UART CTS default)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (UART RxD)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09 (NFC1)
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10 (NFC2)
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (TRACEDATA2 / BUTTON1 default)
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (TRACEDATA1 / BUTTON2 default)
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED1)
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (LED2)
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15 (LED3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16 (LED4)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (QSPI !CS )
|
||||
//_DEFPIN_ARM_IDENTITY_P0(18); // P0.18 (RESET)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (QSPI CLK )
|
||||
//_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (QSPI DIO0)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (QSPI DIO1)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (QSPI DIO2)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (QSPI DIO3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (BUTTON3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (BUTTON4)
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (AIN4)
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (AIN5)
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (AIN6)
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (AIN7)
|
||||
_DEFPIN_ARM_IDENTITY_P0(32); // P1.00 (SWO / TRACEDATA0)
|
||||
_DEFPIN_ARM_IDENTITY_P0(33); // P1.01
|
||||
_DEFPIN_ARM_IDENTITY_P0(34); // P1.02
|
||||
_DEFPIN_ARM_IDENTITY_P0(35); // P1.03
|
||||
_DEFPIN_ARM_IDENTITY_P0(36); // P1.04
|
||||
_DEFPIN_ARM_IDENTITY_P0(37); // P1.05
|
||||
_DEFPIN_ARM_IDENTITY_P0(38); // P1.06
|
||||
_DEFPIN_ARM_IDENTITY_P0(39); // P1.07 (BUTTON1 option)
|
||||
_DEFPIN_ARM_IDENTITY_P0(40); // P1.08 (BUTTON2 option)
|
||||
_DEFPIN_ARM_IDENTITY_P0(41); // P1.09 (TRACEDATA3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(42); // P1.10
|
||||
_DEFPIN_ARM_IDENTITY_P0(43); // P1.11
|
||||
_DEFPIN_ARM_IDENTITY_P0(44); // P1.12
|
||||
_DEFPIN_ARM_IDENTITY_P0(45); // P1.13
|
||||
_DEFPIN_ARM_IDENTITY_P0(46); // P1.14
|
||||
_DEFPIN_ARM_IDENTITY_P0(47); // P1.15
|
||||
#endif
|
||||
#endif // defined (ARDUINO_NRF52840_PCA10056)
|
||||
|
||||
// Adafruit ItsyBitsy nRF52840 Express
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52_ITSYBITSY)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Adafruit ItsyBitsy nRF52840 Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
|
||||
// [D0 .. D13] (digital)
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART RX)
|
||||
_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART TX)
|
||||
_FL_DEFPIN( 2, 2, 1); // D2 is P1.02
|
||||
_FL_DEFPIN( 3, 6, 0); // D3 is P0.06 LED
|
||||
_FL_DEFPIN( 4, 29, 0); // D4 is P0.29 UIButton
|
||||
_FL_DEFPIN( 5, 27, 0); // D5 is P0.27
|
||||
_FL_DEFPIN( 6, 9, 1); // D6 is P1.09 (DotStar Clock)
|
||||
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08
|
||||
_FL_DEFPIN( 8, 8, 0); // D8 is P0.08 (DotStar Data)
|
||||
_FL_DEFPIN( 9, 7, 0); // D9 is P0.07
|
||||
_FL_DEFPIN(10, 5, 0); // D10 is P0.05
|
||||
_FL_DEFPIN(11, 26, 0); // D11 is P0.26
|
||||
_FL_DEFPIN(12, 11, 0); // D12 is P0.11
|
||||
_FL_DEFPIN(13, 12, 0); // D13 is P0.12
|
||||
|
||||
// [D14 .. D20] (analog [A0 .. A6])
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0)
|
||||
_FL_DEFPIN(15, 30, 0); // D15 is P0.30 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 31, 0); // D17 is P0.31 (A3)
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4)
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5)
|
||||
_FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6/D10)
|
||||
|
||||
// [D21 .. D22] (I2C)
|
||||
_FL_DEFPIN(21, 16, 0); // D21 is P0.16 (SDA)
|
||||
_FL_DEFPIN(22, 14, 0); // D22 is P0.14 (SCL)
|
||||
|
||||
// [D23 .. D25] (SPI)
|
||||
_FL_DEFPIN(23, 20, 0); // D23 is P0.20 (SPI MISO)
|
||||
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 (SPI MOSI)
|
||||
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 (SPI SCK )
|
||||
|
||||
// [D26 .. D31] (QSPI)
|
||||
_FL_DEFPIN(26, 19, 0); // D26 is P0.19 (QSPI CLK)
|
||||
_FL_DEFPIN(27, 23, 0); // D27 is P0.23 (QSPI CS)
|
||||
_FL_DEFPIN(28, 21, 0); // D28 is P0.21 (QSPI Data 0)
|
||||
_FL_DEFPIN(29, 22, 0); // D29 is P0.22 (QSPI Data 1)
|
||||
_FL_DEFPIN(30, 0, 1); // D30 is P1.00 (QSPI Data 2)
|
||||
_FL_DEFPIN(31, 17, 0); // D31 is P0.17 (QSPI Data 3)
|
||||
|
||||
#endif // defined (ARDUINO_NRF52_ITSYBITSY)
|
||||
|
||||
// Electronut labs bluey
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/bluey/variant.cpp
|
||||
#if defined(ARDUINO_ELECTRONUT_BLUEY)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Electronut labs bluey is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_FL_DEFPIN( 0, 26, 0); // D0 is P0.26
|
||||
_FL_DEFPIN( 1, 27, 0); // D1 is P0.27
|
||||
_FL_DEFPIN( 2, 22, 0); // D2 is P0.22 (SPI SS )
|
||||
_FL_DEFPIN( 3, 23, 0); // D3 is P0.23 (SPI MOSI)
|
||||
_FL_DEFPIN( 4, 24, 0); // D4 is P0.24 (SPI MISO, also A3)
|
||||
_FL_DEFPIN( 5, 25, 0); // D5 is P0.25 (SPI SCK )
|
||||
_FL_DEFPIN( 6, 16, 0); // D6 is P0.16 (UIButton)
|
||||
_FL_DEFPIN( 7, 19, 0); // D7 is P0.19 (R)
|
||||
_FL_DEFPIN( 8, 18, 0); // D8 is P0.18 (G)
|
||||
_FL_DEFPIN( 9, 17, 0); // D9 is P0.17 (B)
|
||||
_FL_DEFPIN(10, 11, 0); // D10 is P0.11 (SCL)
|
||||
_FL_DEFPIN(11, 12, 0); // D11 is P0.12 (DRDYn)
|
||||
_FL_DEFPIN(12, 13, 0); // D12 is P0.13 (SDA)
|
||||
_FL_DEFPIN(13, 14, 0); // D13 is P0.17 (INT)
|
||||
_FL_DEFPIN(14, 15, 0); // D14 is P0.15 (INT1)
|
||||
_FL_DEFPIN(15, 20, 0); // D15 is P0.20 (INT2)
|
||||
_FL_DEFPIN(16, 2, 0); // D16 is P0.02 (A0)
|
||||
_FL_DEFPIN(17, 3, 0); // D17 is P0.03 (A1)
|
||||
_FL_DEFPIN(18, 4, 0); // D18 is P0.04 (A2)
|
||||
_FL_DEFPIN(19, 24, 0); // D19 is P0.24 (A3, also D4/SPI MISO) -- is this right?
|
||||
_FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A4)
|
||||
_FL_DEFPIN(21, 30, 0); // D21 is P0.30 (A5)
|
||||
_FL_DEFPIN(22, 31, 0); // D22 is P0.31 (A6)
|
||||
_FL_DEFPIN(23, 8, 0); // D23 is P0.08 (RX)
|
||||
_FL_DEFPIN(24, 6, 0); // D24 is P0.06 (TX)
|
||||
_FL_DEFPIN(25, 5, 0); // D25 is P0.05 (RTS)
|
||||
_FL_DEFPIN(26, 7, 0); // D26 is P0.07 (CTS)
|
||||
#endif // defined(ARDUINO_ELECTRONUT_BLUEY)
|
||||
|
||||
// Electronut labs hackaBLE
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE/variant.cpp
|
||||
#if defined(ARDUINO_ELECTRONUT_HACKABLE)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Electronut labs hackaBLE is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_FL_DEFPIN( 0, 14, 0); // D0 is P0.14 (RX)
|
||||
_FL_DEFPIN( 1, 13, 0); // D1 is P0.13 (TX)
|
||||
_FL_DEFPIN( 2, 12, 0); // D2 is P0.12
|
||||
_FL_DEFPIN( 3, 11, 0); // D3 is P0.11 (SPI MOSI)
|
||||
_FL_DEFPIN( 4, 8, 0); // D4 is P0.08 (SPI MISO)
|
||||
_FL_DEFPIN( 5, 7, 0); // D5 is P0.07 (SPI SCK )
|
||||
_FL_DEFPIN( 6, 6, 0); // D6 is P0.06
|
||||
_FL_DEFPIN( 7, 27, 0); // D7 is P0.27
|
||||
_FL_DEFPIN( 8, 26, 0); // D8 is P0.26
|
||||
_FL_DEFPIN( 9, 25, 0); // D9 is P0.25
|
||||
_FL_DEFPIN(10, 5, 0); // D10 is P0.05 (A3)
|
||||
_FL_DEFPIN(11, 4, 0); // D11 is P0.04 (A2)
|
||||
_FL_DEFPIN(12, 3, 0); // D12 is P0.03 (A1)
|
||||
_FL_DEFPIN(13, 2, 0); // D13 is P0.02 (A0 / AREF)
|
||||
_FL_DEFPIN(14, 23, 0); // D14 is P0.23
|
||||
_FL_DEFPIN(15, 22, 0); // D15 is P0.22
|
||||
_FL_DEFPIN(16, 18, 0); // D16 is P0.18
|
||||
_FL_DEFPIN(17, 16, 0); // D17 is P0.16
|
||||
_FL_DEFPIN(18, 15, 0); // D18 is P0.15
|
||||
_FL_DEFPIN(19, 24, 0); // D19 is P0.24
|
||||
_FL_DEFPIN(20, 28, 0); // D20 is P0.28 (A4)
|
||||
_FL_DEFPIN(21, 29, 0); // D21 is P0.29 (A5)
|
||||
_FL_DEFPIN(22, 30, 0); // D22 is P0.30 (A6)
|
||||
_FL_DEFPIN(23, 31, 0); // D23 is P0.31 (A7)
|
||||
_FL_DEFPIN(24, 19, 0); // D24 is P0.19 (RED LED)
|
||||
_FL_DEFPIN(25, 20, 0); // D25 is P0.20 (GREEN LED)
|
||||
_FL_DEFPIN(26, 17, 0); // D26 is P0.17 (BLUE LED)
|
||||
#endif // defined(ARDUINO_ELECTRONUT_HACKABLE)
|
||||
|
||||
// Electronut labs hackaBLE_v2
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE_v2/variant.cpp
|
||||
// (32 pins, natural mapping)
|
||||
#if defined(ARDUINO_ELECTRONUT_hackaBLE_v2)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Electronut labs hackaBLE_v2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A0 / SDA / AREF)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A1 / SCL )
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A2)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A3)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (RX)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (TX)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (SPI MISO)
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (SPI MOSI)
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (SPI SCK )
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (SPI SS )
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16
|
||||
_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (BLUE LED)
|
||||
_DEFPIN_ARM_IDENTITY_P0(18); // P0.18
|
||||
_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (RED LED)
|
||||
_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (GREEN LED)
|
||||
// _DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (RESET)
|
||||
_DEFPIN_ARM_IDENTITY_P0(22); // P0.22
|
||||
_DEFPIN_ARM_IDENTITY_P0(23); // P0.23
|
||||
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (A4)
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (A5)
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (A6)
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (A7)
|
||||
#endif // defined(ARDUINO_ELECTRONUT_hackaBLE_v2)
|
||||
|
||||
// RedBear Blend 2
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_Blend2/variant.cpp
|
||||
#if defined(ARDUINO_RB_BLEND_2)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "RedBear Blend 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_FL_DEFPIN( 0, 11, 0); // D0 is P0.11
|
||||
_FL_DEFPIN( 1, 12, 0); // D1 is P0.12
|
||||
_FL_DEFPIN( 2, 13, 0); // D2 is P0.13
|
||||
_FL_DEFPIN( 3, 14, 0); // D3 is P0.14
|
||||
_FL_DEFPIN( 4, 15, 0); // D4 is P0.15
|
||||
_FL_DEFPIN( 5, 16, 0); // D5 is P0.16
|
||||
_FL_DEFPIN( 6, 17, 0); // D6 is P0.17
|
||||
_FL_DEFPIN( 7, 18, 0); // D7 is P0.18
|
||||
_FL_DEFPIN( 8, 19, 0); // D8 is P0.19
|
||||
_FL_DEFPIN( 9, 20, 0); // D9 is P0.20
|
||||
_FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS )
|
||||
_FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI)
|
||||
_FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO)
|
||||
_FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED)
|
||||
_FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0)
|
||||
_FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3)
|
||||
_FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4)
|
||||
_FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5)
|
||||
_FL_DEFPIN(20, 26, 0); // D20 is P0.26 (SDA)
|
||||
_FL_DEFPIN(21, 27, 0); // D21 is P0.27 (SCL)
|
||||
_FL_DEFPIN(22, 8, 0); // D22 is P0.08 (RX)
|
||||
_FL_DEFPIN(23, 6, 0); // D23 is P0.06 (TX)
|
||||
_FL_DEFPIN(24, 2, 0); // D24 is P0.02 (AREF)
|
||||
#endif // defined(ARDUINO_RB_BLEND_2)
|
||||
|
||||
// RedBear BLE Nano 2
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_BLENano2/variant.cpp
|
||||
#if defined(ARDUINO_RB_BLE_NANO_2)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "RedBear BLE Nano 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_FL_DEFPIN( 0, 30, 0); // D0 is P0.30 (A0 / RX)
|
||||
_FL_DEFPIN( 1, 29, 0); // D1 is P0.29 (A1 / TX)
|
||||
_FL_DEFPIN( 2, 28, 0); // D2 is P0.28 (A2 / SDA)
|
||||
_FL_DEFPIN( 3, 2, 0); // D3 is P0.02 (A3 / SCL)
|
||||
_FL_DEFPIN( 4, 5, 0); // D4 is P0.05 (A4)
|
||||
_FL_DEFPIN( 5, 4, 0); // D5 is P0.04 (A5)
|
||||
_FL_DEFPIN( 6, 3, 0); // D6 is P0.03 (SPI SS )
|
||||
_FL_DEFPIN( 7, 6, 0); // D7 is P0.06 (SPI MOSI)
|
||||
_FL_DEFPIN( 8, 7, 0); // D8 is P0.07 (SPI MISO)
|
||||
_FL_DEFPIN( 9, 8, 0); // D9 is P0.08 (SPI SCK )
|
||||
// _FL_DEFPIN(10, 21, 0); // D10 is P0.21 (RESET)
|
||||
_FL_DEFPIN(13, 11, 0); // D11 is P0.11 (LED)
|
||||
#endif // defined(ARDUINO_RB_BLE_NANO_2)
|
||||
|
||||
// Nordic Semiconductor nRF52 DK
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/nRF52DK/variant.cpp
|
||||
#if defined(ARDUINO_NRF52_DK)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Nordic Semiconductor nRF52 DK is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
_FL_DEFPIN( 0, 11, 0); // D0 is P0.11
|
||||
_FL_DEFPIN( 1, 12, 0); // D1 is P0.12
|
||||
_FL_DEFPIN( 2, 13, 0); // D2 is P0.13 (BUTTON1)
|
||||
_FL_DEFPIN( 3, 14, 0); // D3 is P0.14 (BUTTON2)
|
||||
_FL_DEFPIN( 4, 15, 0); // D4 is P0.15 (BUTTON3)
|
||||
_FL_DEFPIN( 5, 16, 0); // D5 is P0.16 (BUTTON4)
|
||||
_FL_DEFPIN( 6, 17, 0); // D6 is P0.17 (LED1)
|
||||
_FL_DEFPIN( 7, 18, 0); // D7 is P0.18 (LED2)
|
||||
_FL_DEFPIN( 8, 19, 0); // D8 is P0.19 (LED3)
|
||||
_FL_DEFPIN( 9, 20, 0); // D9 is P0.20 (LED4)
|
||||
_FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS )
|
||||
_FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI)
|
||||
_FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO)
|
||||
_FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED)
|
||||
_FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0)
|
||||
_FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3)
|
||||
_FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4)
|
||||
_FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5)
|
||||
_FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6)
|
||||
_FL_DEFPIN(21, 2, 0); // D21 is P0.02 (A7 / AREF)
|
||||
_FL_DEFPIN(22, 26, 0); // D22 is P0.26 (SDA)
|
||||
_FL_DEFPIN(23, 27, 0); // D23 is P0.27 (SCL)
|
||||
_FL_DEFPIN(24, 8, 0); // D24 is P0.08 (RX)
|
||||
_FL_DEFPIN(25, 6, 0); // D25 is P0.06 (TX)
|
||||
#endif // defined(ARDUINO_NRF52_DK)
|
||||
|
||||
// Taida Century nRF52 mini board
|
||||
// https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/Taida_Century_nRF52_minidev/variant.cpp
|
||||
#if defined(ARDUINO_STCT_NRF52_minidev)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Taida Century nRF52 mini board is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
//_FL_DEFPIN( 0, 25, 0); // D0 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 1, 26, 0); // D1 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 2, 27, 0); // D2 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 3, 28, 0); // D3 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 4, 29, 0); // D4 is P0.xx (Not connected, near radio!)
|
||||
//_FL_DEFPIN( 5, 30, 0); // D5 is P0.xx (LED1, near radio!)
|
||||
//_FL_DEFPIN( 6, 31, 0); // D6 is P0.xx (LED2, near radio!)
|
||||
_FL_DEFPIN( 7, 2, 0); // D7 is P0.xx (SDA)
|
||||
_FL_DEFPIN( 8, 3, 0); // D8 is P0.xx (SCL)
|
||||
_FL_DEFPIN( 9, 4, 0); // D9 is P0.xx (BUTTON1 / NFC1)
|
||||
_FL_DEFPIN(10, 5, 0); // D10 is P0.xx
|
||||
//_FL_DEFPIN(11, 0, 0); // D11 is P0.xx (Not connected)
|
||||
//_FL_DEFPIN(12, 1, 0); // D12 is P0.xx (Not connected)
|
||||
_FL_DEFPIN(13, 6, 0); // D13 is P0.xx
|
||||
_FL_DEFPIN(14, 7, 0); // D14 is P0.xx
|
||||
_FL_DEFPIN(15, 8, 0); // D15 is P0.xx
|
||||
//_FL_DEFPIN(16, 9, 0); // D16 is P0.xx (Not connected)
|
||||
//_FL_DEFPIN(17, 10, 0); // D17 is P0.xx (NFC2, Not connected)
|
||||
_FL_DEFPIN(18, 11, 0); // D18 is P0.xx (RXD)
|
||||
_FL_DEFPIN(19, 12, 0); // D19 is P0.xx (TXD)
|
||||
_FL_DEFPIN(20, 13, 0); // D20 is P0.xx (SPI SS )
|
||||
_FL_DEFPIN(21, 14, 0); // D21 is P0.xx (SPI MISO)
|
||||
_FL_DEFPIN(22, 15, 0); // D22 is P0.xx (SPI MOSI)
|
||||
_FL_DEFPIN(23, 16, 0); // D23 is P0.xx (SPI SCK )
|
||||
_FL_DEFPIN(24, 17, 0); // D24 is P0.xx (A0)
|
||||
_FL_DEFPIN(25, 18, 0); // D25 is P0.xx (A1)
|
||||
_FL_DEFPIN(26, 19, 0); // D26 is P0.xx (A2)
|
||||
_FL_DEFPIN(27, 20, 0); // D27 is P0.xx (A3)
|
||||
//_FL_DEFPIN(28, 22, 0); // D28 is P0.xx (A4, near radio!)
|
||||
//_FL_DEFPIN(29, 23, 0); // D29 is P0.xx (A5, near radio!)
|
||||
_FL_DEFPIN(30, 24, 0); // D30 is P0.xx
|
||||
// _FL_DEFPIN(31, 21, 0); // D31 is P0.21 (RESET)
|
||||
#endif // defined(ARDUINO_STCT_NRF52_minidev)
|
||||
|
||||
|
||||
#if defined(ARDUINO_Seeed_XIAO_nRF52840_Sense)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "ARDUINO_Seeed_XIAO_nRF52840_Sense board is a semi-untested board -- please let us know if this pin mapping works so we can disable this warning: https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
|
||||
// Arduino pins 0..7
|
||||
_FL_DEFPIN( 0, 2, 0); // D0 is P0.02
|
||||
_FL_DEFPIN( 1, 3, 0); // D1 is P0.03
|
||||
_FL_DEFPIN( 2, 28, 0); // D2 is P0.28
|
||||
_FL_DEFPIN( 3, 29, 0); // D3 is P0.29
|
||||
_FL_DEFPIN( 4, 4, 0); // D4 is P0.4
|
||||
_FL_DEFPIN( 5, 5, 0); // D5 is P0.5
|
||||
_FL_DEFPIN( 6, 43, 1); // D6 is P1.43
|
||||
_FL_DEFPIN( 7, 44, 1); // D7 is P1.44
|
||||
|
||||
// Arduino pins 8..15
|
||||
_FL_DEFPIN( 8, 45, 1); // D8 is P1.45
|
||||
_FL_DEFPIN( 9, 46, 1); // D9 is P1.46
|
||||
_FL_DEFPIN(10, 47, 1); // D10 is P1.47
|
||||
|
||||
_FL_DEF_INVALID_PIN(11, 26, 0); // D11 is P0.26 (LED RED)
|
||||
_FL_DEF_INVALID_PIN(12, 6, 0); // D12 is P0.06 (LED BLUE)
|
||||
_FL_DEF_INVALID_PIN(13, 30, 0); // D13 is P0.30 (LED GREEN)
|
||||
_FL_DEF_INVALID_PIN(14, 14, 0); // D14 is P0.14 (READ_BAT)
|
||||
|
||||
_FL_DEF_INVALID_PIN(22, 13, 0); // D22 is P0.13 (HICHG)
|
||||
_FL_DEF_INVALID_PIN(23, 17, 0); // D23 is P0.17 (~CHG)
|
||||
|
||||
|
||||
_FL_DEF_INVALID_PIN(24, 21, 0); // D24 is P0.21 (QSPI_SCK)
|
||||
_FL_DEF_INVALID_PIN(25, 25, 0); // D25 is P0.25 (QSPI_CSN)
|
||||
_FL_DEF_INVALID_PIN(26, 20, 0); // D26 is P0.20 (QSPI_SIO_0 DI)
|
||||
_FL_DEF_INVALID_PIN(27, 24, 0); // D27 is P0.24 (QSPI_SIO_1 DO)
|
||||
_FL_DEF_INVALID_PIN(28, 22, 0); // D28 is P0.22 (QSPI_SIO_2 WP)
|
||||
_FL_DEF_INVALID_PIN(29, 23, 0); // D29 is P0.23 (QSPI_SIO_3 HOLD)
|
||||
|
||||
// NFC
|
||||
_FL_DEF_INVALID_PIN(30, 9, 0); // D30 is P0.09 (NFC1)
|
||||
_FL_DEF_INVALID_PIN(31, 10, 0); // D31 is P0.10 (NFC2)
|
||||
|
||||
// VBAT
|
||||
_FL_DEF_INVALID_PIN(32, 31, 0); // D32 is P0.31 (VBAT)
|
||||
#endif // defined(ARDUINO_Seeed_XIAO_nRF52840_Sense)
|
||||
|
||||
#if defined(ARDUINO_Seeed_XIAO_nRF52840)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "ARDUINO_Seeed_XIAO_nRF52840 board is semi tested, pins 0-15 have been provided by the community -- if everything works well let us know at https://github.com/FastLED/FastLED/issues so we can suppress this warning"
|
||||
#endif
|
||||
|
||||
// Arduino pins 0..7
|
||||
_FL_DEFPIN( 0, 2, 0); // D0 is P0.02
|
||||
_FL_DEFPIN( 1, 3, 0); // D1 is P0.03
|
||||
_FL_DEFPIN( 2, 28, 0); // D2 is P0.28
|
||||
_FL_DEFPIN( 3, 29, 0); // D3 is P0.29
|
||||
_FL_DEFPIN( 4, 4, 0); // D4 is P0.4
|
||||
_FL_DEFPIN( 5, 5, 0); // D5 is P0.5
|
||||
_FL_DEFPIN( 6, 43, 1); // D6 is P1.43
|
||||
_FL_DEFPIN( 7, 44, 1); // D7 is P1.44
|
||||
|
||||
// Arduino pins 8..15
|
||||
_FL_DEFPIN( 8, 45, 1); // D8 is P1.45
|
||||
_FL_DEFPIN( 9, 46, 1); // D9 is P1.46
|
||||
_FL_DEFPIN(10, 47, 1); // D10 is P1.47
|
||||
|
||||
_FL_DEF_INVALID_PIN(11, 26, 0); // D11 is P0.26 (LED RED)
|
||||
_FL_DEF_INVALID_PIN(12, 6, 0); // D12 is P0.06 (LED BLUE)
|
||||
_FL_DEF_INVALID_PIN(13, 30, 0); // D13 is P0.30 (LED GREEN)
|
||||
_FL_DEF_INVALID_PIN(14, 14, 0); // D14 is P0.14 (READ_BAT)
|
||||
|
||||
_FL_DEF_INVALID_PIN(22, 13, 0); // D22 is P0.13 (HICHG)
|
||||
_FL_DEF_INVALID_PIN(23, 17, 0); // D23 is P0.17 (~CHG)
|
||||
|
||||
|
||||
_FL_DEF_INVALID_PIN(24, 21, 0); // D24 is P0.21 (QSPI_SCK)
|
||||
_FL_DEF_INVALID_PIN(25, 25, 0); // D25 is P0.25 (QSPI_CSN)
|
||||
_FL_DEF_INVALID_PIN(26, 20, 0); // D26 is P0.20 (QSPI_SIO_0 DI)
|
||||
_FL_DEF_INVALID_PIN(27, 24, 0); // D27 is P0.24 (QSPI_SIO_1 DO)
|
||||
_FL_DEF_INVALID_PIN(28, 22, 0); // D28 is P0.22 (QSPI_SIO_2 WP)
|
||||
_FL_DEF_INVALID_PIN(29, 23, 0); // D29 is P0.23 (QSPI_SIO_3 HOLD)
|
||||
|
||||
// NFC
|
||||
_FL_DEF_INVALID_PIN(30, 9, 0); // D30 is P0.09 (NFC1)
|
||||
_FL_DEF_INVALID_PIN(31, 10, 0); // D31 is P0.10 (NFC2)
|
||||
|
||||
// VBAT
|
||||
_FL_DEF_INVALID_PIN(32, 31, 0); // D32 is P0.31 (VBAT)
|
||||
|
||||
|
||||
// _FL_DEFPIN(11, 6, 0); // D11 is P0.06
|
||||
// _FL_DEFPIN(12, 8, 0); // D12 is P0.08
|
||||
// _FL_DEFPIN(13, 41, 1); // D13 is P1.09 -- PIN_LED1 (red)
|
||||
// _FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0
|
||||
// _FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1
|
||||
|
||||
// // Arduino pins 16..23
|
||||
// _FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2
|
||||
// _FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3
|
||||
// _FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4
|
||||
// _FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5
|
||||
// _FL_DEF_INVALID_PIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery!
|
||||
// _FL_DEF_INVALID_PIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF
|
||||
// _FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA
|
||||
// _FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL
|
||||
|
||||
// // Arduino pins 24..31
|
||||
// _FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO
|
||||
// _FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI
|
||||
// _FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK
|
||||
// _FL_DEF_INVALID_PIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK
|
||||
// _FL_DEF_INVALID_PIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS
|
||||
// _FL_DEF_INVALID_PIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0
|
||||
// _FL_DEF_INVALID_PIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1
|
||||
// _FL_DEF_INVALID_PIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2
|
||||
|
||||
// Arduino pins 32..34
|
||||
//_FL_DEF_INVALID_PIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3
|
||||
//_FL_DEF_INVALID_PIN(33, 9, 0); // D33 is NFC1, only accessible via test point
|
||||
#endif // defined(ARDUINO_STCT_NRF52_minidev)
|
||||
|
||||
|
||||
|
||||
|
||||
// Generic nRF52832
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/boards.txt
|
||||
#if defined(ARDUINO_GENERIC) && ( defined(NRF52832_XXAA) || defined(NRF52832_XXAB) )
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#if !defined(FASTLED_NRF52_SUPPRESS_UNTESTED_BOARD_WARNING)
|
||||
#warning "Using `generic` NRF52832 board is an untested configuration -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
#endif
|
||||
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 ( UART RX
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (A0, UART TX)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A1)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A2)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A3)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A4)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (A5)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED)
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16
|
||||
_DEFPIN_ARM_IDENTITY_P0(17); // P0.17
|
||||
_DEFPIN_ARM_IDENTITY_P0(18); // P0.18
|
||||
_DEFPIN_ARM_IDENTITY_P0(19); // P0.19
|
||||
_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (I2C SDA)
|
||||
_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (I2C SCL)
|
||||
_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (SPI MISO)
|
||||
_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (SPI MOSI)
|
||||
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (SPI SCK )
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (SPI SS )
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31
|
||||
#endif // defined(ARDUINO_GENERIC)
|
||||
|
||||
|
||||
// Adafruit Bluefruit nRF52840 Feather Express
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined(NRF52840_XXAA) && !defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#warning "Unknown nRF52840 variant -- please report to FastLED developers what your board is."
|
||||
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
|
||||
// Arduino pins 0..7
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 -- UART TX
|
||||
//_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 -- UART RX
|
||||
_FL_DEFPIN( 2, 10, 0); // D2 is P0.10 -- NFC2
|
||||
_FL_DEFPIN( 3, 47, 1); // D3 is P1.15 -- PIN_LED1 (red)
|
||||
_FL_DEFPIN( 4, 42, 1); // D4 is P1.10 -- PIN_LED2 (blue)
|
||||
_FL_DEFPIN( 5, 40, 1); // D5 is P1.08 -- SPI/SS
|
||||
_FL_DEFPIN( 6, 7, 0); // D6 is P0.07
|
||||
_FL_DEFPIN( 7, 34, 1); // D7 is P1.02 -- PIN_DFU (UIButton)
|
||||
|
||||
// Arduino pins 8..15
|
||||
_FL_DEFPIN( 8, 16, 0); // D8 is P0.16 -- PIN_NEOPIXEL
|
||||
_FL_DEFPIN( 9, 26, 0); // D9 is P0.26
|
||||
_FL_DEFPIN(10, 27, 0); // D10 is P0.27
|
||||
_FL_DEFPIN(11, 6, 0); // D11 is P0.06
|
||||
_FL_DEFPIN(12, 8, 0); // D12 is P0.08
|
||||
_FL_DEFPIN(13, 41, 1); // D13 is P1.09
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0
|
||||
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1
|
||||
|
||||
// Arduino pins 16..23
|
||||
_FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2
|
||||
_FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5
|
||||
//_FL_DEFPIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery!
|
||||
//_FL_DEFPIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF
|
||||
_FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA
|
||||
_FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL
|
||||
|
||||
// Arduino pins 24..31
|
||||
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO
|
||||
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI
|
||||
_FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK
|
||||
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK
|
||||
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS
|
||||
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0
|
||||
//_FL_DEFPIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1
|
||||
//_FL_DEFPIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2
|
||||
|
||||
// Arduino pins 32..34
|
||||
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3
|
||||
//_FL_DEFPIN(33, 9, 0); // D33 is NFC1, only accessible via test point
|
||||
#endif // defined (NRF52840_XXAA)
|
||||
|
||||
#endif // __FASTPIN_ARM_NRF52_VARIANTS_H
|
||||
@@ -0,0 +1,340 @@
|
||||
#ifndef __FASTSPI_ARM_NRF52_H
|
||||
#define __FASTSPI_ARM_NRF52_H
|
||||
|
||||
|
||||
#ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
|
||||
#include <nrf_spim.h>
|
||||
|
||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
||||
|
||||
|
||||
// NRF52810 has SPIM0: Frequencies from 125kbps to 8Mbps
|
||||
// NRF52832 adds SPIM1, SPIM2 (same frequencies)
|
||||
// NRF52840 adds SPIM3 (same frequencies), adds SPIM3 that can be @ up to 32Mbps frequency(!)
|
||||
#if !defined(FASTLED_NRF52_SPIM)
|
||||
#define FASTLED_NRF52_SPIM NRF_SPIM0
|
||||
#endif
|
||||
|
||||
/* This class is slightly simpler than fastpin, as it can rely on fastpin
|
||||
* to handle the mapping to the underlying PN.XX board-level pins...
|
||||
*/
|
||||
|
||||
/// SPI_CLOCK_DIVIDER is number of CPU clock cycles per SPI transmission bit?
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class NRF52SPIOutput {
|
||||
private:
|
||||
// static variables -- always using same SPIM instance
|
||||
static bool s_InUse;
|
||||
static bool s_NeedToWait; // a data transfer was started, and completion event was not cleared.
|
||||
|
||||
/*
|
||||
// TODO -- Workaround nRF52840 errata #198, which relates to
|
||||
// contention between SPIM3 and CPU over AHB.
|
||||
// The workaround is to ensure the SPIM TX buffer
|
||||
// is on a different / dedicated RAM block.
|
||||
// This also avoids AHB contention generally, so
|
||||
// should be applied to all supported boards.
|
||||
//
|
||||
// But... how to allocate m_Buffer[] to be at a
|
||||
// specific memory range? Also, might need to
|
||||
// avoid use of single-transaction writeBytes()
|
||||
// as cannot control where that memory lies....
|
||||
*/
|
||||
static uint8_t s_BufferIndex;
|
||||
static uint8_t s_Buffer[2][2]; // 2x two-byte buffers, allows one buffer currently being sent, and a second one being prepped to send.
|
||||
|
||||
// This allows saving the configuration of the SPIM instance
|
||||
// upon select(), and restoring the configuration upon release().
|
||||
struct spim_config {
|
||||
uint32_t inten;
|
||||
uint32_t shorts;
|
||||
uint32_t sck_pin;
|
||||
uint32_t mosi_pin;
|
||||
uint32_t miso_pin;
|
||||
uint32_t frequency;
|
||||
// data pointers, RX/TX counts not saved as would only hide bugs
|
||||
uint32_t config; // mode & bit order
|
||||
uint32_t orc;
|
||||
|
||||
#if false // additional configuration to save/restore for SPIM3
|
||||
uint32_t csn_pin;
|
||||
uint32_t csn_polarity; // CSNPOL
|
||||
uint32_t csn_duration; // IFTIMING.CSNDUR
|
||||
uint32_t rx_delay; // IFTIMING.RXDELAY
|
||||
uint32_t dcx_pin; // PSELDCX
|
||||
uint32_t dcx_config; // DCXCNT
|
||||
#endif
|
||||
|
||||
} m_SpiSavedConfig;
|
||||
void saveSpimConfig() {
|
||||
m_SpiSavedConfig.inten = FASTLED_NRF52_SPIM->INTENSET;
|
||||
m_SpiSavedConfig.shorts = FASTLED_NRF52_SPIM->SHORTS;
|
||||
m_SpiSavedConfig.sck_pin = FASTLED_NRF52_SPIM->PSEL.SCK;
|
||||
m_SpiSavedConfig.mosi_pin = FASTLED_NRF52_SPIM->PSEL.MOSI;
|
||||
m_SpiSavedConfig.miso_pin = FASTLED_NRF52_SPIM->PSEL.MISO;
|
||||
m_SpiSavedConfig.frequency = FASTLED_NRF52_SPIM->FREQUENCY;
|
||||
m_SpiSavedConfig.config = FASTLED_NRF52_SPIM->CONFIG;
|
||||
m_SpiSavedConfig.orc = FASTLED_NRF52_SPIM->ORC;
|
||||
|
||||
#if false // additional configuration to save/restore for SPIM3
|
||||
m_SpiSavedConfig.csn_pin = FASTLED_NRF52_SPIM->PSEL.CSN;
|
||||
m_SpiSavedConfig.csn_polarity = FASTLED_NRF52_SPIM->CSNPOL;
|
||||
m_SpiSavedConfig.csn_duration = FASTLED_NRF52_SPIM->IFTIMING.CSNDUR;
|
||||
m_SpiSavedConfig.dcx_pin = FASTLED_NRF52_SPIM->PSELDCX;
|
||||
m_SpiSavedConfig.dcx_config = FASTLED_NRF52_SPIM->DCXCNT;
|
||||
#endif
|
||||
}
|
||||
void restoreSpimConfig() {
|
||||
// 0. ASSERT() the SPIM instance is not enabled
|
||||
|
||||
FASTLED_NRF52_SPIM->INTENCLR = 0xFFFFFFFF;
|
||||
FASTLED_NRF52_SPIM->INTENSET = m_SpiSavedConfig.inten;
|
||||
FASTLED_NRF52_SPIM->SHORTS = m_SpiSavedConfig.shorts;
|
||||
FASTLED_NRF52_SPIM->PSEL.SCK = m_SpiSavedConfig.sck_pin;
|
||||
FASTLED_NRF52_SPIM->PSEL.MOSI = m_SpiSavedConfig.mosi_pin;
|
||||
FASTLED_NRF52_SPIM->PSEL.MISO = m_SpiSavedConfig.miso_pin;
|
||||
FASTLED_NRF52_SPIM->FREQUENCY = m_SpiSavedConfig.frequency;
|
||||
FASTLED_NRF52_SPIM->CONFIG = m_SpiSavedConfig.config;
|
||||
FASTLED_NRF52_SPIM->ORC = m_SpiSavedConfig.orc;
|
||||
|
||||
#if false // additional configuration to save/restore for SPIM3
|
||||
FASTLED_NRF52_SPIM->PSEL.CSN = m_SpiSavedConfig.csn_pin;
|
||||
FASTLED_NRF52_SPIM->CSNPOL = m_SpiSavedConfig.csn_polarity;
|
||||
FASTLED_NRF52_SPIM->IFTIMING.CSNDUR = m_SpiSavedConfig.csn_duration;
|
||||
FASTLED_NRF52_SPIM->PSELDCX = m_SpiSavedConfig.dcx_pin;
|
||||
FASTLED_NRF52_SPIM->DCXCNT = m_SpiSavedConfig.dcx_config;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
NRF52SPIOutput() {}
|
||||
|
||||
// Low frequency GPIO is for signals with a frequency up to 10 kHz. Lowest speed SPIM is 125kbps.
|
||||
static_assert(!FastPin<_DATA_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified");
|
||||
static_assert(!FastPin<_CLOCK_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified");
|
||||
|
||||
/// initialize the SPI subssytem
|
||||
void init() {
|
||||
// 0. ASSERT() the SPIM instance is not enabled / in use
|
||||
//ASSERT(m_SPIM->ENABLE != (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos));
|
||||
|
||||
// 1. set pins to output/H0H1 drive/etc.
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// 2. Configure SPIMx
|
||||
nrf_spim_configure(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_MODE_0,
|
||||
NRF_SPIM_BIT_ORDER_MSB_FIRST
|
||||
);
|
||||
nrf_spim_frequency_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_FREQ_4M // BUGBUG -- use _SPI_CLOCK_DIVIDER to determine frequency
|
||||
);
|
||||
nrf_spim_pins_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
FastPin<_CLOCK_PIN>::nrf_pin(),
|
||||
FastPin<_DATA_PIN>::nrf_pin(),
|
||||
NRF_SPIM_PIN_NOT_CONNECTED
|
||||
);
|
||||
|
||||
// 4. Ensure events are cleared
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END);
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED);
|
||||
|
||||
// 5. Enable the SPIM instance
|
||||
nrf_spim_enable(FASTLED_NRF52_SPIM);
|
||||
}
|
||||
|
||||
/// latch the CS select
|
||||
void select() {
|
||||
//ASSERT(!s_InUse);
|
||||
saveSpimConfig();
|
||||
s_InUse = true;
|
||||
init();
|
||||
}
|
||||
|
||||
/// release the CS select
|
||||
void release() {
|
||||
//ASSERT(s_InUse);
|
||||
waitFully();
|
||||
s_InUse = false;
|
||||
restoreSpimConfig();
|
||||
}
|
||||
|
||||
/// wait until all queued up data has been written
|
||||
static void waitFully() {
|
||||
if (!s_NeedToWait) return;
|
||||
// else, need to wait for END event
|
||||
while(!FASTLED_NRF52_SPIM->EVENTS_END) {};
|
||||
s_NeedToWait = 0;
|
||||
// only use two events in this code...
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END);
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED);
|
||||
return;
|
||||
}
|
||||
// wait only until we can add a new transaction into the registers
|
||||
// (caller must still waitFully() before actually starting this next transaction)
|
||||
static void wait() {
|
||||
if (!s_NeedToWait) return;
|
||||
while (!FASTLED_NRF52_SPIM->EVENTS_STARTED) {};
|
||||
// leave the event set here... caller must waitFully() and start next transaction
|
||||
return;
|
||||
}
|
||||
|
||||
/// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) {
|
||||
wait();
|
||||
// cannot use pointer to stack, so copy to m_buffer[]
|
||||
uint8_t i = (s_BufferIndex ? 1u : 0u);
|
||||
s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap
|
||||
|
||||
s_Buffer[i][0u] = b; // cannot use the stack location, so copy to a more permanent buffer...
|
||||
nrf_spim_tx_buffer_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
&(s_Buffer[i][0u]),
|
||||
1
|
||||
);
|
||||
|
||||
waitFully();
|
||||
nrf_spim_task_trigger(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_TASK_START
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) {
|
||||
wait();
|
||||
// cannot use pointer to stack, so copy to m_buffer[]
|
||||
uint8_t i = (s_BufferIndex ? 1u : 0u);
|
||||
s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap
|
||||
|
||||
s_Buffer[i][0u] = (w >> 8u); // cannot use the stack location, so copy to a more permanent buffer...
|
||||
s_Buffer[i][1u] = (w & 0xFFu); // cannot use the stack location, so copy to a more permanent buffer...
|
||||
nrf_spim_tx_buffer_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
&(s_Buffer[i][0u]),
|
||||
2
|
||||
);
|
||||
|
||||
waitFully();
|
||||
nrf_spim_task_trigger(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_TASK_START
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while (len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
/// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
writeBytesValueRaw(value, len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
/// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
void writeBytes(uint8_t *data, int len) {
|
||||
// This is a special-case, with no adjustment of the bytes... write them directly...
|
||||
select();
|
||||
wait();
|
||||
nrf_spim_tx_buffer_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
data,
|
||||
len
|
||||
);
|
||||
waitFully();
|
||||
nrf_spim_task_trigger(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_TASK_START
|
||||
);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
/// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template<class D> void writeBytes(uint8_t *data, int len) {
|
||||
uint8_t * end = data + len;
|
||||
select();
|
||||
wait();
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
/// specialization for DATA_NOP ...
|
||||
//template<DATA_NOP> void writeBytes(uint8_t * data, int len) {
|
||||
// writeBytes(data, len);
|
||||
//}
|
||||
|
||||
/// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
// SPIM instance must be finished transmitting and then disabled
|
||||
waitFully();
|
||||
nrf_spim_disable(FASTLED_NRF52_SPIM);
|
||||
// set the data pin to appropriate state
|
||||
if (b & (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
// delay 1/2 cycle per SPI bit
|
||||
delaycycles<_SPI_CLOCK_DIVIDER/2>();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
delaycycles<_SPI_CLOCK_DIVIDER/2>();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
// re-enable the SPIM instance
|
||||
nrf_spim_enable(FASTLED_NRF52_SPIM);
|
||||
}
|
||||
|
||||
/// write out pixel data from the given PixelController object, including select, release, and waiting
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
// TODO: If user indicates a pre-allocated double-buffer,
|
||||
// then process all the pixels at once into that buffer,
|
||||
// then use the non-templated WriteBytes(data, len) function
|
||||
// to write the entire buffer as a single SPI transaction.
|
||||
while (pixels.has(1)) {
|
||||
if (FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
// Static member definition and initialization using templates.
|
||||
// see https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template#answer-3229919
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_InUse = false;
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_NeedToWait = false;
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_BufferIndex = 0;
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_Buffer[2][2] = {{0,0},{0,0}};
|
||||
|
||||
#endif // #ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
|
||||
|
||||
|
||||
#endif // #ifndef __FASTPIN_ARM_NRF52_H
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef __LED_SYSDEFS_ARM_NRF52
|
||||
#define __LED_SYSDEFS_ARM_NRF52
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 64000000 // the NRF52 series has a 64MHz CPU
|
||||
#endif
|
||||
|
||||
// even though CPU is at 64MHz, use the 8MHz-defined timings because...
|
||||
// PWM module runs at 16MHz
|
||||
// SPI0..2 runs at 8MHz
|
||||
#define CLOCKLESS_FREQUENCY 16000000 // the NRF52 has EasyDMA for PWM module at 16MHz
|
||||
|
||||
#ifndef F_TIMER
|
||||
#define F_TIMER 16000000 // the NRF52 timer is 16MHz, even though CPU is 64MHz
|
||||
#endif
|
||||
|
||||
#if !defined(FASTLED_USE_PROGMEM)
|
||||
#define FASTLED_USE_PROGMEM 0 // nRF52 series have flat memory model
|
||||
#endif
|
||||
|
||||
#if !defined(FASTLED_ALLOW_INTERRUPTS)
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
// Use PWM instance 0
|
||||
// See clockless_arm_nrf52.h and (in root of library) platforms.cpp
|
||||
#define FASTLED_NRF52_ENABLE_PWM_INSTANCE0
|
||||
|
||||
#if defined(FASTLED_NRF52_NEVER_INLINE)
|
||||
#define FASTLED_NRF52_INLINE_ATTRIBUTE FASTLED_FORCE_INLINE
|
||||
#else
|
||||
#define FASTLED_NRF52_INLINE_ATTRIBUTE FASTLED_FORCE_INLINE
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include <nrf.h>
|
||||
#include <nrf_spim.h> // for FastSPI
|
||||
#include <nrf_pwm.h> // for Clockless
|
||||
#include <nrf_nvic.h> // for Clockless / anything else using interrupts
|
||||
typedef __I uint32_t RoReg;
|
||||
typedef __IO uint32_t RwReg;
|
||||
|
||||
#define cli() __disable_irq()
|
||||
#define sei() __enable_irq()
|
||||
|
||||
#define FASTLED_NRF52_DEBUGPRINT(format, ...)\
|
||||
// do { FastLED_NRF52_DebugPrint(format, ##__VA_ARGS__); } while(0);
|
||||
|
||||
#endif // __LED_SYSDEFS_ARM_NRF52
|
||||
@@ -0,0 +1,21 @@
|
||||
# FastLED Platform: Renesas (UNO R4)
|
||||
|
||||
Renesas RA4M1 (Arduino UNO R4) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_renesas.h`: Aggregator; includes pin and clockless.
|
||||
- `fastpin_arm_renesas.h`: Pin helpers.
|
||||
- `clockless_arm_renesas.h`: Clockless driver.
|
||||
- `led_sysdef_arm_renesas.h`: System defines for Renesas.
|
||||
|
||||
Notes:
|
||||
- Interrupt policy and F_CPU definitions affect timing; keep critical sections tight.
|
||||
- Check UNO R4 core defines to ensure `FASTLED_USE_PROGMEM=0` and interrupt macros map to platform equivalents.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0`.
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Default `1`. Enables `FASTLED_ACCURATE_CLOCK` when interrupts are allowed.
|
||||
- **`FASTLED_NO_PINMAP`**: Indicates no PROGMEM pin maps are used.
|
||||
|
||||
Place defines before including `FastLED.h`.
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_RENESAS
|
||||
#define __INC_CLOCKLESS_ARM_RENESAS
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for RA4M1 (Cortex M4)
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control
|
||||
#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
|
||||
#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register
|
||||
#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
|
||||
#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register
|
||||
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
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;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
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 data_ptr_t port, FASTLED_REGISTER data_t hi, FASTLED_REGISTER data_t lo, FASTLED_REGISTER uint8_t & b) {
|
||||
for(FASTLED_REGISTER uint32_t i = BITS-1; i > 0; --i) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(4*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(4*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(4*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(4*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
FASTLED_REGISTER data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
FASTLED_REGISTER data_t hi = *port | FastPin<DATA_PIN>::mask();
|
||||
FASTLED_REGISTER data_t lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
FASTLED_REGISTER uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef __INC_FASTLED_ARM_RENESAS_H
|
||||
#define __INC_FASTLED_ARM_RENESAS_H
|
||||
|
||||
#include "fastpin_arm_renesas.h"
|
||||
#include "../../fastspi_ardunio_core.h"
|
||||
#include "clockless_arm_renesas.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifndef __INC_FASTPIN_ARM_RENESAS_H
|
||||
#define __INC_FASTPIN_ARM_RENESAS_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
|
||||
|
||||
#include "bsp_api.h"
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, bsp_io_port_pin_t bspPin, uint32_t _PORT> class _ARMPIN {
|
||||
public:
|
||||
|
||||
typedef volatile uint16_t * port_ptr_t;
|
||||
typedef uint16_t port_t;
|
||||
|
||||
#define PORT ((R_PORT0_Type*)(_PORT))
|
||||
#define digitalBspPinToPort(P) (P >> 8)
|
||||
#define digitalBspPinToBitMask(P) (1 << (P & 0xFF))
|
||||
|
||||
#if 0
|
||||
inline static void setOutput() {
|
||||
if(_BIT<8) {
|
||||
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
|
||||
} else {
|
||||
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
|
||||
}
|
||||
}
|
||||
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
#endif
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { PORT->POSR = digitalBspPinToBitMask(bspPin); }
|
||||
inline static void lo() __attribute__ ((always_inline)) { PORT->PORR = digitalBspPinToBitMask(bspPin); }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { PORT->PODR = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { PORT->PODR & digitalBspPinToBitMask(bspPin) ? lo() : hi(); }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return PORT->PODR | digitalBspPinToBitMask(bspPin); }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return PORT->PODR & ~digitalBspPinToBitMask(bspPin); }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->PODR; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->POSR; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->PORR; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return digitalBspPinToBitMask(bspPin); }
|
||||
|
||||
};
|
||||
|
||||
#define _FL_DEFPIN(PIN, bspPin, PORT) template<> class FastPin<PIN> : public _ARMPIN<PIN, bspPin, PORT> {};
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(ARDUINO_UNOR4_WIFI)
|
||||
|
||||
#define MAX_PIN 21
|
||||
// D0-D13
|
||||
_FL_DEFPIN( 0, BSP_IO_PORT_03_PIN_01, R_PORT3_BASE ); _FL_DEFPIN( 1, BSP_IO_PORT_03_PIN_02, R_PORT3_BASE ); _FL_DEFPIN( 2, BSP_IO_PORT_01_PIN_04, R_PORT1_BASE );
|
||||
_FL_DEFPIN( 3, BSP_IO_PORT_01_PIN_05, R_PORT1_BASE ); _FL_DEFPIN( 4, BSP_IO_PORT_01_PIN_06, R_PORT1_BASE ); _FL_DEFPIN( 5, BSP_IO_PORT_01_PIN_07, R_PORT1_BASE );
|
||||
_FL_DEFPIN( 6, BSP_IO_PORT_01_PIN_11, R_PORT1_BASE ); _FL_DEFPIN( 7, BSP_IO_PORT_01_PIN_12, R_PORT1_BASE ); _FL_DEFPIN( 8, BSP_IO_PORT_03_PIN_04, R_PORT3_BASE );
|
||||
_FL_DEFPIN( 9, BSP_IO_PORT_03_PIN_03, R_PORT3_BASE ); _FL_DEFPIN(10, BSP_IO_PORT_01_PIN_03, R_PORT1_BASE ); _FL_DEFPIN(11, BSP_IO_PORT_04_PIN_11, R_PORT4_BASE );
|
||||
_FL_DEFPIN(12, BSP_IO_PORT_04_PIN_10, R_PORT4_BASE ); _FL_DEFPIN(13, BSP_IO_PORT_01_PIN_02, R_PORT1_BASE );
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, BSP_IO_PORT_00_PIN_14, R_PORT0_BASE ); _FL_DEFPIN(15, BSP_IO_PORT_00_PIN_00, R_PORT0_BASE ); _FL_DEFPIN(16, BSP_IO_PORT_00_PIN_01, R_PORT0_BASE );
|
||||
_FL_DEFPIN(17, BSP_IO_PORT_00_PIN_02, R_PORT0_BASE ); _FL_DEFPIN(18, BSP_IO_PORT_01_PIN_01, R_PORT1_BASE ); _FL_DEFPIN(19, BSP_IO_PORT_01_PIN_00, R_PORT1_BASE );
|
||||
|
||||
#elif defined(ARDUINO_UNOR4_MINIMA)
|
||||
|
||||
#define MAX_PIN 21
|
||||
// D0-D13
|
||||
_FL_DEFPIN( 0, BSP_IO_PORT_03_PIN_01, R_PORT3_BASE ); _FL_DEFPIN( 1, BSP_IO_PORT_03_PIN_02, R_PORT3_BASE ); _FL_DEFPIN( 2, BSP_IO_PORT_01_PIN_05, R_PORT1_BASE );
|
||||
_FL_DEFPIN( 3, BSP_IO_PORT_01_PIN_04, R_PORT1_BASE ); _FL_DEFPIN( 4, BSP_IO_PORT_01_PIN_03, R_PORT1_BASE ); _FL_DEFPIN( 5, BSP_IO_PORT_01_PIN_02, R_PORT1_BASE );
|
||||
_FL_DEFPIN( 6, BSP_IO_PORT_01_PIN_06, R_PORT1_BASE ); _FL_DEFPIN( 7, BSP_IO_PORT_01_PIN_07, R_PORT1_BASE ); _FL_DEFPIN( 8, BSP_IO_PORT_03_PIN_04, R_PORT3_BASE );
|
||||
_FL_DEFPIN( 9, BSP_IO_PORT_03_PIN_03, R_PORT3_BASE ); _FL_DEFPIN(10, BSP_IO_PORT_01_PIN_12, R_PORT1_BASE ); _FL_DEFPIN(11, BSP_IO_PORT_01_PIN_09, R_PORT1_BASE );
|
||||
_FL_DEFPIN(12, BSP_IO_PORT_01_PIN_10, R_PORT1_BASE ); _FL_DEFPIN(13, BSP_IO_PORT_01_PIN_11, R_PORT1_BASE );
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, BSP_IO_PORT_00_PIN_14, R_PORT0_BASE ); _FL_DEFPIN(15, BSP_IO_PORT_00_PIN_00, R_PORT0_BASE ); _FL_DEFPIN(16, BSP_IO_PORT_00_PIN_01, R_PORT0_BASE );
|
||||
_FL_DEFPIN(17, BSP_IO_PORT_00_PIN_02, R_PORT0_BASE ); _FL_DEFPIN(18, BSP_IO_PORT_01_PIN_01, R_PORT1_BASE ); _FL_DEFPIN(19, BSP_IO_PORT_01_PIN_00, R_PORT1_BASE );
|
||||
|
||||
#elif defined(ARDUINO_THINGPLUS_RA6M5)
|
||||
|
||||
#define MAX_PIN 24
|
||||
// D0-D06
|
||||
_FL_DEFPIN( 0, BSP_IO_PORT_01_PIN_12, R_PORT1_BASE ); _FL_DEFPIN( 1, BSP_IO_PORT_04_PIN_06, R_PORT4_BASE ); _FL_DEFPIN( 2, BSP_IO_PORT_04_PIN_05, R_PORT4_BASE );
|
||||
_FL_DEFPIN( 3, BSP_IO_PORT_04_PIN_04, R_PORT4_BASE ); _FL_DEFPIN( 4, BSP_IO_PORT_04_PIN_03, R_PORT4_BASE ); _FL_DEFPIN( 5, BSP_IO_PORT_04_PIN_02, R_PORT4_BASE );
|
||||
_FL_DEFPIN( 6, BSP_IO_PORT_02_PIN_07, R_PORT2_BASE );
|
||||
|
||||
// D07-D12 (A0-A5)
|
||||
_FL_DEFPIN( 7, BSP_IO_PORT_00_PIN_14, R_PORT0_BASE ); _FL_DEFPIN( 8, BSP_IO_PORT_00_PIN_15, R_PORT0_BASE ); _FL_DEFPIN( 9, BSP_IO_PORT_05_PIN_05, R_PORT5_BASE );
|
||||
_FL_DEFPIN(10, BSP_IO_PORT_05_PIN_04, R_PORT5_BASE ); _FL_DEFPIN(11, BSP_IO_PORT_05_PIN_03, R_PORT5_BASE ); _FL_DEFPIN(12, BSP_IO_PORT_05_PIN_02, R_PORT5_BASE );
|
||||
|
||||
// D13-D21
|
||||
_FL_DEFPIN(13, BSP_IO_PORT_01_PIN_05, R_PORT1_BASE ); _FL_DEFPIN(14, BSP_IO_PORT_01_PIN_06, R_PORT1_BASE ); _FL_DEFPIN(15, BSP_IO_PORT_04_PIN_01, R_PORT4_BASE );
|
||||
_FL_DEFPIN(16, BSP_IO_PORT_04_PIN_00, R_PORT4_BASE ); _FL_DEFPIN(17, BSP_IO_PORT_01_PIN_10, R_PORT1_BASE ); _FL_DEFPIN(18, BSP_IO_PORT_01_PIN_09, R_PORT1_BASE );
|
||||
_FL_DEFPIN(19, BSP_IO_PORT_01_PIN_11, R_PORT1_BASE ); _FL_DEFPIN(20, BSP_IO_PORT_04_PIN_09, R_PORT4_BASE ); _FL_DEFPIN(21, BSP_IO_PORT_04_PIN_08, R_PORT4_BASE );
|
||||
|
||||
// D30-31
|
||||
_FL_DEFPIN(30, BSP_IO_PORT_03_PIN_04, R_PORT3_BASE ); _FL_DEFPIN(31, BSP_IO_PORT_04_PIN_15, R_PORT4_BASE );
|
||||
|
||||
#elif defined(ARDUINO_ARCH_RENESAS_PORTENTA)
|
||||
|
||||
#define MAX_PIN 22
|
||||
// D0-D14
|
||||
_FL_DEFPIN( 0, BSP_IO_PORT_01_PIN_05, R_PORT1_BASE ); _FL_DEFPIN( 1, BSP_IO_PORT_01_PIN_06, R_PORT1_BASE ); _FL_DEFPIN( 2, BSP_IO_PORT_01_PIN_01, R_PORT1_BASE );
|
||||
_FL_DEFPIN( 3, BSP_IO_PORT_03_PIN_03, R_PORT3_BASE ); _FL_DEFPIN( 4, BSP_IO_PORT_04_PIN_01, R_PORT4_BASE ); _FL_DEFPIN( 5, BSP_IO_PORT_02_PIN_10, R_PORT2_BASE );
|
||||
_FL_DEFPIN( 6, BSP_IO_PORT_06_PIN_01, R_PORT6_BASE ); _FL_DEFPIN( 7, BSP_IO_PORT_04_PIN_02, R_PORT4_BASE ); _FL_DEFPIN( 8, BSP_IO_PORT_09_PIN_00, R_PORT9_BASE );
|
||||
_FL_DEFPIN( 9, BSP_IO_PORT_02_PIN_04, R_PORT2_BASE ); _FL_DEFPIN(10, BSP_IO_PORT_03_PIN_15, R_PORT3_BASE ); _FL_DEFPIN(11, BSP_IO_PORT_04_PIN_07, R_PORT4_BASE );
|
||||
_FL_DEFPIN(12, BSP_IO_PORT_04_PIN_08, R_PORT4_BASE ); _FL_DEFPIN(13, BSP_IO_PORT_01_PIN_10, R_PORT1_BASE ); _FL_DEFPIN(14, BSP_IO_PORT_06_PIN_02, R_PORT6_BASE )
|
||||
// A0-A5
|
||||
_FL_DEFPIN(15, BSP_IO_PORT_00_PIN_06, R_PORT0_BASE ); _FL_DEFPIN(16, BSP_IO_PORT_00_PIN_05, R_PORT0_BASE ); _FL_DEFPIN(17, BSP_IO_PORT_00_PIN_04, R_PORT0_BASE );
|
||||
_FL_DEFPIN(18, BSP_IO_PORT_00_PIN_02, R_PORT0_BASE ); _FL_DEFPIN(19, BSP_IO_PORT_01_PIN_01, R_PORT1_BASE ); _FL_DEFPIN(20, BSP_IO_PORT_00_PIN_15, R_PORT0_BASE );
|
||||
_FL_DEFPIN(21, BSP_IO_PORT_00_PIN_14, R_PORT0_BASE );_FL_DEFPIN(22, BSP_IO_PORT_00_PIN_00, R_PORT0_BASE );
|
||||
#endif
|
||||
|
||||
#define SPI_DATA 12
|
||||
#define SPI_CLOCK 13
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_RENESAS_H
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_RENESAS_H
|
||||
#define __INC_LED_SYSDEFS_ARM_RENESAS_H
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#define FASTLED_NO_PINMAP
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
# FastLED Platform: RP2040
|
||||
|
||||
Raspberry Pi Pico (RP2040) support.
|
||||
|
||||
## Files (quick pass)
|
||||
- `fastled_arm_rp2040.h`: Aggregator; includes pin and clockless.
|
||||
- `fastpin_arm_rp2040.h`: Pin helpers.
|
||||
- `clockless_arm_rp2040.h`: Clockless driver using PIO.
|
||||
- `pio_asm.h`, `pio_gen.h`: PIO assembly and program generator for T1/T2/T3‑tuned clockless output.
|
||||
- `led_sysdefs_arm_rp2040.h`: System defines for RP2040.
|
||||
|
||||
Notes:
|
||||
- Uses PIO program assembled at runtime; ensure T1/T2/T3 match LED timing.
|
||||
- `clockless_arm_rp2040.h` configures wrap targets and delays via `pio_gen.h`; changes to timing require regenerating the program.
|
||||
|
||||
## Optional feature defines
|
||||
|
||||
- **`FASTLED_ALLOW_INTERRUPTS`**: Allow ISRs during show. Default `1`.
|
||||
- **`FASTLED_ACCURATE_CLOCK`**: Enabled when interrupts are allowed to maintain timing math accuracy.
|
||||
- **`FASTLED_USE_PROGMEM`**: Default `0` (flat memory model).
|
||||
- **Clockless driver selection/tuning**
|
||||
- **`FASTLED_RP2040_CLOCKLESS_PIO`**: Use PIO engine for clockless. Default `1`.
|
||||
- **`FASTLED_RP2040_CLOCKLESS_IRQ_SHARED`**: Share IRQ usage between PIO and other subsystems. Default `1`.
|
||||
- **`FASTLED_RP2040_CLOCKLESS_M0_FALLBACK`**: Fallback to a Cortex‑M0 timing loop if PIO is disabled/unavailable. Default `0`.
|
||||
|
||||
Define these before including `FastLED.h` in your sketch.
|
||||
@@ -0,0 +1,335 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_RP2040
|
||||
#define __INC_CLOCKLESS_ARM_RP2040
|
||||
|
||||
#include "hardware/structs/sio.h"
|
||||
|
||||
#if FASTLED_RP2040_CLOCKLESS_M0_FALLBACK || !FASTLED_RP2040_CLOCKLESS_PIO
|
||||
#include "../common/m0clockless.h"
|
||||
#endif
|
||||
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/dma.h"
|
||||
// compiler throws a warning about comparison that is always true
|
||||
// silence that so users don't see it
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#include "hardware/pio.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "pio_gen.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This clockless implementation uses RP2040's PIO feature to perform
|
||||
* non-blocking transfers to LEDs with very little memory overhead.
|
||||
* (allocates one buffer of equal size to the data to be sent)
|
||||
*
|
||||
* The SDK-provided claims system is used so that resources can used without
|
||||
* interfering with other code that behaves well and uses claims.
|
||||
*
|
||||
* Resource usage is 4 instructions of program memory on the first PIO instance
|
||||
* with an unclaimed state machine, said unclaimed PIO state machine, and one
|
||||
* DMA channel per instance of ClocklessController.
|
||||
* Additionally, one interrupt handler for DMA_IRQ_0 (configurable as shared or
|
||||
* exclusive via FASTLED_RP2040_CLOCKLESS_IRQ_SHARED) is used regardless of how
|
||||
* many instances are created.
|
||||
*
|
||||
* The DMA handler is likely the only significant risk in terms of conflicts,
|
||||
* and users can adapt other code to use DMA_IRQ_1 and/or adopt shared handlers
|
||||
* to avoid this becoming an issue.
|
||||
*/
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
static CMinWait<0> *dma_chan_waits[NUM_DMA_CHANNELS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
static inline void __isr clockless_dma_complete_handler() {
|
||||
for (unsigned int i = 0; i < NUM_DMA_CHANNELS; i++) {
|
||||
// if dma triggered for this channel and it's been used (has a CMinWait)
|
||||
if ((dma_hw->ints0 & (1 << i)) && dma_chan_waits[i]) {
|
||||
dma_hw->ints0 = (1 << i); // clear/ack IRQ
|
||||
dma_chan_waits[i]->mark(); // mark the wait
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static bool clockless_isr_installed = false;
|
||||
#endif
|
||||
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 280>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
int dma_channel = -1;
|
||||
void *dma_buf = nullptr;
|
||||
size_t dma_buf_size = 0;
|
||||
|
||||
float pio_clock_multiplier;
|
||||
int T1_mult, T2_mult, T3_mult;
|
||||
|
||||
// increase wait time by time taken to send 4 words (to flush PIO TX buffer)
|
||||
CMinWait<WAIT_TIME + ( ((T1 + T2 + T3) * 32 * 4) / (CLOCKLESS_FREQUENCY / 1000000) )> mWait;
|
||||
|
||||
// start a DMA transfer to the PIO state machine from addr (transfer count 32 bit words)
|
||||
static void do_dma_transfer(int channel, const void *addr, uint count) {
|
||||
dma_channel_set_read_addr(channel, addr, false);
|
||||
dma_channel_set_trans_count(channel, count, true);
|
||||
}
|
||||
|
||||
// writes bits to an in-memory buffer (to DMA from)
|
||||
// pico has enough memory to not really care about using a buffer for DMA
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static int writeBitsToBuf(int32_t *out_buf, unsigned int bitpos, uint8_t b) {
|
||||
// not really optimised and I haven't checked output assembly, but this should take ~50 cycles worst case
|
||||
// (and on average substantially fewer -- LEDs without XTRA0 should never trigger the second half of the function)
|
||||
|
||||
// position of word that takes highest bits (first word used)
|
||||
int wordpos_1 = bitpos >> 5; // bitpos / 32;
|
||||
|
||||
// number of bits from the byte that fit into first word
|
||||
int bitcnt_1 = 32 - (bitpos & 0b11111); // bitpos % 32;
|
||||
// shift required to place byte within the word
|
||||
int bitshift_1 = bitcnt_1 - 8;
|
||||
// mask for output bits that are taken from input
|
||||
// int32_t bitmask_1 = 0xFF << bitshift_1;
|
||||
int32_t bitmask_1 = ((1 << BITS) - 1) << (bitshift_1 - (BITS-8));
|
||||
|
||||
out_buf[wordpos_1] = (out_buf[wordpos_1] & ~bitmask_1) | ((b << bitshift_1) & bitmask_1);
|
||||
|
||||
if (bitcnt_1 >= BITS) return BITS; // fast case for entire byte fitting in word
|
||||
|
||||
// number of bits from the byte to place into second word
|
||||
int bitcnt_2 = 8 - bitcnt_1;
|
||||
// shift required to place byte within the word
|
||||
int bitshift_2 = 32 - bitcnt_2;
|
||||
// mask for output bits that are taken from input
|
||||
// int32_t bitmask_2 = ((1 << bitcnt_2) - 1) << bitshift_2;
|
||||
int32_t bitmask_2 = ((1 << (bitcnt_2 + (BITS-8))) - 1) << (bitshift_2 - (BITS-8)); // fixed XTRA0
|
||||
|
||||
out_buf[wordpos_1 + 1] = (out_buf[wordpos_1 + 1] & ~bitmask_2) | ((b << bitshift_2) & bitmask_2);
|
||||
|
||||
return BITS;
|
||||
}
|
||||
#else
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
#endif
|
||||
public:
|
||||
virtual void init() {
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
if (dma_channel != -1) return; // maybe init was called twice somehow? not sure if possible
|
||||
#endif
|
||||
|
||||
// start by configuring pin as output for blocking fallback
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
// convert from input timebase to one that the PIO program can handle
|
||||
int max_t = T1 > T2 ? T1 : T2;
|
||||
max_t = T3 > max_t ? T3 : max_t;
|
||||
|
||||
if (max_t > CLOCKLESS_PIO_MAX_TIME_PERIOD) {
|
||||
pio_clock_multiplier = (float)CLOCKLESS_PIO_MAX_TIME_PERIOD / max_t;
|
||||
T1_mult = pio_clock_multiplier * T1;
|
||||
T2_mult = pio_clock_multiplier * T2;
|
||||
T3_mult = pio_clock_multiplier * T3;
|
||||
}
|
||||
else {
|
||||
pio_clock_multiplier = 1.f;
|
||||
T1_mult = T1;
|
||||
T2_mult = T2;
|
||||
T3_mult = T3;
|
||||
}
|
||||
|
||||
PIO pio;
|
||||
int sm;
|
||||
int offset = -1;
|
||||
|
||||
#if defined(PICO_RP2040)
|
||||
// find an unclaimed PIO state machine and upload the clockless program if possible
|
||||
// there's two PIO instances, each with four state machines, so this should usually work out fine
|
||||
const PIO pios[NUM_PIOS] = { pio0, pio1 };
|
||||
#elif defined(PICO_RP2350)
|
||||
// RP2350 features three PIO instances!
|
||||
const PIO pios[NUM_PIOS] = { pio0, pio1, pio2 };
|
||||
#endif
|
||||
// iterate over PIO instances
|
||||
for (unsigned int i = 0; i < NUM_PIOS; i++) {
|
||||
pio = pios[i];
|
||||
sm = pio_claim_unused_sm(pio, false); // claim a state machine
|
||||
if (sm == -1) continue; // skip this PIO if no unused sm
|
||||
|
||||
offset = add_clockless_pio_program(pio, T1_mult, T2_mult, T3_mult);
|
||||
if (offset == -1) {
|
||||
pio_sm_unclaim(pio, sm); // unclaim the state machine and skip this PIO
|
||||
continue; // if program couldn't be added
|
||||
}
|
||||
|
||||
break; // found pio and sm that work
|
||||
}
|
||||
if (offset == -1) return; // couldn't find good pio and sm
|
||||
|
||||
|
||||
// claim an unused DMA channel (there's 12 in total,, so this should also usually work out fine)
|
||||
dma_channel = dma_claim_unused_channel(false);
|
||||
if (dma_channel == -1) return; // no free DMA channel
|
||||
|
||||
|
||||
// setup PIO state machine
|
||||
pio_gpio_init(pio, DATA_PIN);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, DATA_PIN, 1, true);
|
||||
|
||||
pio_sm_config c = clockless_pio_program_get_default_config(offset);
|
||||
sm_config_set_set_pins(&c, DATA_PIN, 1);
|
||||
sm_config_set_out_pins(&c, DATA_PIN, 1);
|
||||
sm_config_set_out_shift(&c, false, true, 32);
|
||||
|
||||
// uncommenting this makes the FIFO 8 words long,
|
||||
// which seems like it won't actually benefit us
|
||||
// sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
float div = clock_get_hz(clk_sys) / (pio_clock_multiplier * CLOCKLESS_FREQUENCY);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
|
||||
|
||||
// setup DMA
|
||||
dma_channel_config channel_config = dma_channel_get_default_config(dma_channel);
|
||||
channel_config_set_dreq(&channel_config, pio_get_dreq(pio, sm, true));
|
||||
dma_channel_configure(dma_channel,
|
||||
&channel_config,
|
||||
&pio->txf[sm],
|
||||
NULL, // address set when making transfer
|
||||
1, // count set when making transfer
|
||||
false); // don't trigger now
|
||||
|
||||
|
||||
// setup DMA complete interrupt handler to update mWait time after transfer
|
||||
|
||||
// store a pointer to mWait of this instance to a global array for the interrupt handler
|
||||
// kinda dirty hack here to cast to CMinWait<0>*, but only mark is used, which isn't affected by the template var WAIT
|
||||
dma_chan_waits[dma_channel] = (CMinWait<0>*)&mWait;
|
||||
|
||||
if (!clockless_isr_installed) {
|
||||
#if FASTLED_RP2040_CLOCKLESS_IRQ_SHARED
|
||||
irq_add_shared_handler(DMA_IRQ_0, clockless_dma_complete_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
#else
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, clockless_dma_complete_handler);
|
||||
#endif
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
clockless_isr_installed = true;
|
||||
}
|
||||
dma_channel_set_irq0_enabled(dma_channel, true);
|
||||
#endif // FASTLED_RP2040_CLOCKLESS_PIO
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
if (dma_channel == -1) { // setup failed, so fall back to a blocking implementation
|
||||
#if FASTLED_RP2040_CLOCKLESS_M0_FALLBACK
|
||||
showRGBBlocking(pixels);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// wait for past transfer to finish
|
||||
// call when previous pixels are done will run without blocking,
|
||||
// call when previous pixels are still being transmitted should block until complete
|
||||
|
||||
// a potential improvement here would be to prepare data for the output before waiting,
|
||||
// but that would require a smarter DMA buffer system
|
||||
// (currently, the gap between LEDs is greater than 50us due to the time taken)
|
||||
if (dma_channel_is_busy(dma_channel)) {
|
||||
dma_channel_wait_for_finish_blocking(dma_channel);
|
||||
}
|
||||
mWait.wait();
|
||||
|
||||
showRGBInternal(pixels);
|
||||
#else
|
||||
mWait.wait();
|
||||
showRGBBlocking(pixels);
|
||||
mWait.mark();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FASTLED_RP2040_CLOCKLESS_PIO
|
||||
void showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
size_t req_buf_size = (pixels.mLen * 3 * (8+XTRA0) + 31) / 32;
|
||||
|
||||
// (re)allocate DMA buffer if not large enough to hold req_buf_size 32-bit words
|
||||
// pico has enough memory to not really care about using a buffer for DMA
|
||||
// just give up on failure
|
||||
if (dma_buf_size < req_buf_size) {
|
||||
if (dma_buf != nullptr)
|
||||
free(dma_buf);
|
||||
|
||||
dma_buf = malloc(req_buf_size * 4);
|
||||
if (dma_buf == nullptr) {
|
||||
dma_buf_size = 0;
|
||||
return;
|
||||
}
|
||||
dma_buf_size = req_buf_size;
|
||||
|
||||
// fill with zeroes to ensure XTRA0s are really zero without needing extra work
|
||||
memset(dma_buf, 0, dma_buf_size * 4);
|
||||
}
|
||||
|
||||
unsigned int bitpos = 0;
|
||||
|
||||
pixels.preStepFirstByteDithering();
|
||||
uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
|
||||
// Write first byte, read next byte
|
||||
bitpos += writeBitsToBuf<8+XTRA0>((int32_t*)(dma_buf), bitpos, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
bitpos += writeBitsToBuf<8+XTRA0>((int32_t*)(dma_buf), bitpos, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
bitpos += writeBitsToBuf<8+XTRA0>((int32_t*)(dma_buf), bitpos, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
};
|
||||
|
||||
do_dma_transfer(dma_channel, dma_buf, req_buf_size);
|
||||
}
|
||||
#endif // FASTLED_RP2040_CLOCKLESS_PIO
|
||||
|
||||
#if FASTLED_RP2040_CLOCKLESS_M0_FALLBACK
|
||||
void showRGBBlocking(PixelController<RGB_ORDER> pixels) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mColorAdjustment.premixed[0];
|
||||
data.s[1] = pixels.mColorAdjustment.premixed[1];
|
||||
data.s[2] = pixels.mColorAdjustment.premixed[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typedef FastPin<DATA_PIN> pin;
|
||||
volatile uint32_t *portBase = &sio_hw->gpio_out;
|
||||
const int portSetOff = (uint32_t)&sio_hw->gpio_set - (uint32_t)&sio_hw->gpio_out;
|
||||
const int portClrOff = (uint32_t)&sio_hw->gpio_clr - (uint32_t)&sio_hw->gpio_out;
|
||||
|
||||
cli();
|
||||
showLedData<portSetOff, portClrOff, T1, T2, T3, RGB_ORDER, WAIT_TIME>(portBase, pin::mask(), pixels.mData, pixels.mLen, &data);
|
||||
sei();
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_CLOCKLESS_ARM_RP2040
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef __INC_FASTLED_ARM_RP2040_H
|
||||
#define __INC_FASTLED_ARM_RP2040_H
|
||||
|
||||
// Include the rp2040 headers
|
||||
#include "fastpin_arm_rp2040.h"
|
||||
#include "clockless_arm_rp2040.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef __FASTPIN_ARM_RP2040_H
|
||||
#define __FASTPIN_ARM_RP2040_H
|
||||
|
||||
#include "pico.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/structs/sio.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be sloightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
// warning: set and fastset are not thread-safe! use with caution!
|
||||
template<uint PIN, uint32_t _MASK> class _RP2040PIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { gpio_set_function(PIN, GPIO_FUNC_SIO); sio_hw->gpio_oe_set = _MASK; }
|
||||
inline static void setInput() { gpio_set_function(PIN, GPIO_FUNC_SIO); sio_hw->gpio_oe_clr = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { sio_hw->gpio_set = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { sio_hw->gpio_clr = _MASK; }
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { sio_hw->gpio_out = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { sio_hw->gpio_togl = _MASK; }
|
||||
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return sio_hw->gpio_out | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return sio_hw->gpio_out & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &sio_hw->gpio_out; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &sio_hw->gpio_set; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &sio_hw->gpio_clr; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
// Use 64-bit literal for pin mask calculation to support RP2350B pins 32-47
|
||||
// The template parameter is still uint32_t to maintain register compatibility
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _RP2040PIN<PIN, (uint32_t)(1ULL << PIN)> {};
|
||||
|
||||
// Set MAX_PIN based on platform capability
|
||||
#if defined(PICO_RP2350)
|
||||
// RP2350B has up to 48 pins (0-47), RP2350A has 30 pins (0-29)
|
||||
// We support up to 47 for RP2350 variants
|
||||
#define MAX_PIN 47
|
||||
#else
|
||||
// RP2040 has 30 pins (0-29)
|
||||
#define MAX_PIN 29
|
||||
#endif
|
||||
|
||||
// Define pins 0-29 for all RP2040/RP2350 variants
|
||||
_FL_DEFPIN(0); _FL_DEFPIN(1); _FL_DEFPIN(2); _FL_DEFPIN(3);
|
||||
_FL_DEFPIN(4); _FL_DEFPIN(5); _FL_DEFPIN(6); _FL_DEFPIN(7);
|
||||
_FL_DEFPIN(8); _FL_DEFPIN(9); _FL_DEFPIN(10); _FL_DEFPIN(11);
|
||||
_FL_DEFPIN(12); _FL_DEFPIN(13); _FL_DEFPIN(14); _FL_DEFPIN(15);
|
||||
_FL_DEFPIN(16); _FL_DEFPIN(17); _FL_DEFPIN(18); _FL_DEFPIN(19);
|
||||
_FL_DEFPIN(20); _FL_DEFPIN(21); _FL_DEFPIN(22); _FL_DEFPIN(23);
|
||||
_FL_DEFPIN(24); _FL_DEFPIN(25); _FL_DEFPIN(26); _FL_DEFPIN(27);
|
||||
_FL_DEFPIN(28); _FL_DEFPIN(29);
|
||||
|
||||
// Define additional pins 30-47 for RP2350 variants with extended GPIO
|
||||
#if defined(PICO_RP2350)
|
||||
_FL_DEFPIN(30); _FL_DEFPIN(31); _FL_DEFPIN(32); _FL_DEFPIN(33);
|
||||
_FL_DEFPIN(34); _FL_DEFPIN(35); _FL_DEFPIN(36); _FL_DEFPIN(37);
|
||||
_FL_DEFPIN(38); _FL_DEFPIN(39); _FL_DEFPIN(40); _FL_DEFPIN(41);
|
||||
_FL_DEFPIN(42); _FL_DEFPIN(43); _FL_DEFPIN(44); _FL_DEFPIN(45);
|
||||
_FL_DEFPIN(46); _FL_DEFPIN(47);
|
||||
#endif
|
||||
|
||||
#ifdef PICO_DEFAULT_SPI_TX_PIN
|
||||
#define SPI_DATA PICO_DEFAULT_SPI_TX_PIN
|
||||
#else
|
||||
#define SPI_DATA 19
|
||||
#endif
|
||||
|
||||
#ifdef PICO_DEFAULT_SPI_SCK_PIN
|
||||
#define SPI_CLOCK PICO_DEFAULT_SPI_SCK_PIN
|
||||
#else
|
||||
#define SPI_CLOCK 18
|
||||
#endif
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __FASTPIN_ARM_RP2040_H
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_RP2040_H
|
||||
#define __INC_LED_SYSDEFS_ARM_RP2040_H
|
||||
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
|
||||
#include "hardware/sync.h"
|
||||
|
||||
// Explicitly include Arduino.h here so any framework-specific defines take
|
||||
// priority.
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h> // ok include
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ARM
|
||||
#error "FASTLED_ARM must be defined before including this header. Ensure platforms/arm/is_arm.h is included first."
|
||||
#endif
|
||||
#define FASTLED_ARM_M0_PLUS
|
||||
|
||||
// TODO: PORT SPI TO HW
|
||||
//#define FASTLED_SPI_BYTE_ONLY
|
||||
#define FASTLED_FORCE_SOFTWARE_SPI
|
||||
// Force FAST_SPI_INTERRUPTS_WRITE_PINS on becuase two cores running
|
||||
// simultaneously could lead to data races on GPIO.
|
||||
// This could potentially be optimised by adding a mask to FastPin's set and
|
||||
// fastset, but for now it's probably safe to call that out of scope.
|
||||
#ifndef FAST_SPI_INTERRUPTS_WRITE_PINS
|
||||
#define FAST_SPI_INTERRUPTS_WRITE_PINS 1
|
||||
#endif
|
||||
|
||||
#define FASTLED_NO_PINMAP
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
|
||||
// #define F_CPU clock_get_hz(clk_sys) // can't use runtime function call
|
||||
// is the boot-time value in another var already for any platforms?
|
||||
// it doesn't seem to be, so hardcode the sdk default of 125 MHz
|
||||
#ifndef F_CPU
|
||||
#ifdef VARIANT_MCK
|
||||
#define F_CPU VARIANT_MCK
|
||||
#else
|
||||
#define F_CPU 125000000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef VARIANT_MCK
|
||||
#define VARIANT_MCK F_CPU
|
||||
#endif
|
||||
|
||||
// 8MHz for PIO
|
||||
// #define CLOCKLESS_FREQUENCY 8000000
|
||||
#define CLOCKLESS_FREQUENCY F_CPU
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
// not sure if this is wanted? but it probably is
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// Default to no PROGMEM
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
// Default to non-blocking PIO-based implemnetation
|
||||
#ifndef FASTLED_RP2040_CLOCKLESS_PIO
|
||||
#define FASTLED_RP2040_CLOCKLESS_PIO 1
|
||||
#endif
|
||||
|
||||
// Default to shared interrupt handler for clockless DMA
|
||||
#ifndef FASTLED_RP2040_CLOCKLESS_IRQ_SHARED
|
||||
#define FASTLED_RP2040_CLOCKLESS_IRQ_SHARED 1
|
||||
#endif
|
||||
|
||||
// Default to disabling M0 assembly clockless implementation
|
||||
#ifndef FASTLED_RP2040_CLOCKLESS_M0_FALLBACK
|
||||
#define FASTLED_RP2040_CLOCKLESS_M0_FALLBACK 0
|
||||
#endif
|
||||
|
||||
// SPI pin defs for old SDK ver
|
||||
#ifndef PICO_DEFAULT_SPI
|
||||
#define PICO_DEFAULT_SPI 0
|
||||
#endif
|
||||
#ifndef PICO_DEFAULT_SPI_SCK_PIN
|
||||
#define PICO_DEFAULT_SPI_SCK_PIN 18
|
||||
#endif
|
||||
#ifndef PICO_DEFAULT_SPI_TX_PIN
|
||||
#define PICO_DEFAULT_SPI_TX_PIN 19
|
||||
#endif
|
||||
#ifndef PICO_DEFAULT_SPI_RX_PIN
|
||||
#define PICO_DEFAULT_SPI_RX_PIN 16
|
||||
#endif
|
||||
#ifndef PICO_DEFAULT_SPI_CSN_PIN
|
||||
#define PICO_DEFAULT_SPI_CSN_PIN 17
|
||||
#endif
|
||||
|
||||
#if !defined(cli) && !defined(sei)
|
||||
static uint32_t saved_interrupt_status;
|
||||
#define cli() (saved_interrupt_status = save_and_disable_interrupts())
|
||||
#define sei() (restore_interrupts(saved_interrupt_status))
|
||||
#endif
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
#endif // __INC_LED_SYSDEFS_ARM_RP2040_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user