/// The LED strip DI (data input) line should be on port D6 (Digital pin 6 on
/// Arduino Uno). If you need to change the port, change declarations below
/// , for example "ws2812b<D,6> myWs2812" to "ws2812b<B,4> myWs2812"
/// if you wanted to use port B4.
#include "FAB_LED.h"
#include <SPI.h>
#include <SD.h>
const int effect = 10;
const int chipSelect = 10;
//uint8_t pos;
unsigned int pos;
unsigned int posf;
unsigned int i;
/// @brief This parameter says how many LEDs will be lit up in your strip.
//const uint8_t numPixels = 255;
const int numPixels = 128;
/// @brief This says how bright LEDs will be (max is 255)
const uint8_t maxBrightness = 255;
char* playfile;
/// @brief Declare the protocol for your LED strip. Data is wired to port D6,
/// and the clock to port D5, for APA102 only.
//apa104<D,6> LEDstrip;
//apa106<D,6> LEDstrip;
//apa102<D,6,D,5> LEDstrip;
/// @brief Array holding the pixel data.
/// Note you have multiple formats available, to support any LED type. If you
/// use the wrong type, FAB_LED will do the conversion transparently for you.
hbgr pixels[numPixels] = {};
//grb pixels[numPixels] = {};
//grbw pixels[numPixels] = {}
int n = 0;
void setup()
{
Serial.begin(115200);
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
//pinMode(6, OUTPUT);
// RGB initializes to zero, but for APA102, h is set to full brightness.
// Depending on which class you use for your pixels, comment the h or w field
// accordingly.
for (pos = 0; pos < numPixels; pos++) {
pixels[pos].h = 0xFF; // hgrb has h field
pixels[pos].g = 0;
pixels[pos].b = 0;
pixels[pos].r = 0;
//pixels[pos].w = 0; // grbw has w field
}
LEDstrip.refresh(); // Hack: needed for apa102 to display last pixels
// Clear display
LEDstrip.sendPixels(numPixels,pixels);
LEDstrip.refresh(); // Hack: needed for apa102 to display last pixels
}
void loop()
{
int m = 0;
switch(n) {
case 0:
playfile = "001.bmp";
break;
case 1:
playfile = "002.bmp";
break;
case 2:
playfile = "003.bmp";
break;
case 3:
playfile = "004.bmp";
break;
case 4:
playfile = "005.bmp";
break;
case 5:
playfile = "006.bmp";
break;
case 6:
playfile = "007.bmp";
break;
case 7:
playfile = "008.bmp";
break;
case 8:
playfile = "009.bmp";
break;
case 9:
playfile = "010.bmp";
break;
case 10:
playfile = "011.bmp";
break;
case 11:
playfile = "012.bmp";
break;
case 12:
playfile = "013.bmp";
break;
case 13:
playfile = "014.bmp";
break;
case 14:
playfile = "015.bmp";
break;
case 15:
playfile = "016.bmp";
break;
case 16:
playfile = "017.bmp";
break;
case 17:
playfile = "018.bmp";
break;
case 18:
playfile = "019.bmp";
break;
case 19:
playfile = "020.bmp";
break;
default:
playfile = "001.bmp";
}
File dataFile = SD.open(playfile);
if (dataFile) {
long start1 = millis();
while (dataFile.available()) {
if(m<1) {
for (i = 0; i < 54 ; i++) {
dataFile.read();
m++;
}
}
for (pos = 0; pos < numPixels ; pos++) {
byte b = dataFile.read();
pixels[pos].b = b;
//Serial.print(b);
//Serial.print(" ");
byte g = dataFile.read();
pixels[pos].g = g;
//Serial.print(g);
//Serial.print(" ");
byte r = dataFile.read();
pixels[pos].r = r;
//Serial.println(r);
//Serial.print(pos);
//Serial.print(" ");
//Serial.println();
}
//n++;
LEDstrip.sendPixels(numPixels, pixels);
LEDstrip.refresh();
//delay(10);
}
dataFile.close();
long stopp = millis();
long tim = stopp-start1;
Serial.println();
Serial.print(tim);
Serial.println("ms 128 LED");
//while(true) {}
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.bin");
}
n++;
if (n>(effect)) n = 0;
Serial.print("N ");
Serial.print(n);
delay(100);
}
A FAB_LED.h:
[code=c]
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Fast Addressable LED Library
// Copyright (c)2015 Dan Truong
//
// Licence: limited use is authorized for the purpose of beta testing
//
// The goal of this library is to be very fast, very sm and take advantage
// of the relaxed LED timings to ow other work besides bit banging while the
// LEDs are being updated.
// The library ows cing multiple times the same function to repeat
// patterns, and ows inline color palette management to save on memory space
// usage. The palette conversion is done inline as the pixels are being painted.
//
// The bit-banging is being done using GCC built-ins to avoid contrived ASM
// language code blocks support. It should work for any arduino platform of
// 16MHz or more to function.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#ifndef FAB_LED_H
#define FAB_LED_H
#include <stdint.h>
#include <Arduino.h>
// This code is sensitive to optimizations if you want to cascade function cs,
// so make the IDE compile at -O2 instead of -Os (size).
#pragma GCC optimize ("-O2")
// static_assert is a built-in C++ 11 assert that Arduino does not seem to support
#ifndef static_assert
#define STATIC_ASSERT4(COND,MSG,LIN) typedef char assert_##MSG##_##LIN[(!!(COND))*2-1]
#define STATIC_ASSERT3(X,M,L) STATIC_ASSERT4(X, M, L)
#define STATIC_ASSERT2(X,M,L) STATIC_ASSERT3(X,M,L)
#define STATIC_ASSERT(X,M) STATIC_ASSERT2(X,M,__LINE__)
#else
#define SA2TXT2(x) # x
#define SA2TXT(x) SA2TXT2(x)
#define STATIC_ASSERT(X,M) static_assert(X, SA2TXT(M))
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief typed pixel structures for every LED protocol supported.
/// These simplify access to a pixel byte array, or even to send typed pixels
/// to the LED strip, as the programmer directly access pixel colors by name.
////////////////////////////////////////////////////////////////////////////////
// Pixel colors byte order, 0 and 7 invalid.
#define PT_RGB 0b00100000
#define PT_GRB 0b01000000
#define PT_BGR 0b10000000
//#define PT_RBG 0b11000000
//#define PT_GBR 0b10100000
//#define PT_BRG 0b01100000
#define PT_COL 0b11100000 // Mask for 3-byte colors
#define PT_IS_SAME_COLOR(v1, v2) ((v1) & T_COL == (v2) & T_COL)
// Extra pixels bytes, 0 none, 2 invalid.
#define PT_XXXW
0b00000001 // Postfix white pixel brightness (sk6812)
#define PT_BXXX 0b00000010 // APA 102 prefix brightness pixel 0b111bbbbb
#define PT_XBYT 0b00000011 // Mask for extra byte(s)
#define PT_HAS_WHITE(v) ((v) & PT_XBYT == PT_XXXW)
#define PT_HAS_BRIGHT(v) ((v) & PT_XBYT == PT_BXXX)
#define PT_IS_3B(v) ((v) & PT_XVAL == 0)
#define PT_IS_4B(v) ((v) & PT_XVAL != 0)
// apa102, apa106 native color order
typedef struct {
static const uint8_t type = PT_RGB;
{ uint8_t r; uint8_t red; };
{ uint8_t g; uint8_t green; };
{ uint8_t b; uint8_t blue; };
} rgb;
// apa104, ws2812 native color order
typedef struct {
static const uint8_t type = PT_GRB;
{ uint8_t g; uint8_t green; };
{ uint8_t r; uint8_t red; };
{ uint8_t b; uint8_t blue; };
} grb;
typedef struct {
static const uint8_t type = PT_BGR;
{ uint8_t b; uint8_t blue; };
{ uint8_t g; uint8_t green; };
{ uint8_t r; uint8_t red; };
} bgr;
typedef struct {
static const uint8_t type = PT_RGB | PT_XXXW;
{ uint8_t r; uint8_t red; };
{ uint8_t g; uint8_t green; };
{ uint8_t b; uint8_t blue; };
{ uint8_t w; uint8_t white; };
} rgbw;
typedef struct {
static const uint8_t type = PT_GRB | PT_XXXW;
{ uint8_t g; uint8_t green; };
{ uint8_t r; uint8_t red; };
{ uint8_t b; uint8_t blue; };
{ uint8_t w; uint8_t white; };
} grbw;
// apa102 native color order
typedef struct {
static const uint8_t type = PT_BGR | PT_BXXX;
{ uint8_t B; uint8_t brightness;
uint8_t w; uint8_t white;
uint8_t h; uint8_t header; };
{ uint8_t b; uint8_t blue; };
{ uint8_t g; uint8_t green; };
{ uint8_t r; uint8_t red; };
} hbgr;
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper macro for palette index encoding into a char * array when
/// using an 8bit or less per pixel with a uint8_t type.
////////////////////////////////////////////////////////////////////////////////
/// @brief computes the size of a uint8_t array that uses pixels encoded with a
/// palette. For example: uint8_t buffer[ARRAY_SIZE(128, 2)]; is an array of 128
/// pixels encoded with 2 bits per pixel (4 colors).
#define ARRAY_SIZE(numPixels, bitsPerPixel) (((numPixels)+7) / 8 * (bitsPerPixel))
/// @brief Encode a pixel's color
/// For example, for encoding colors on 2 bits (4 colors, 0,1,2,3, in the palette),
/// Encoding color #3, using 2 bits per pixels, in the buffer for pixel i:
/// SET_PIXEL(buffer, i, 2, 3);
#define SET_PIXEL(array, index, bitsPerPixel, color) \
_SET_PIXEL((array), (index), (bitsPerPixel), (color))
/// @brief extract a pixel's palette color index a pixel array
/// For example, colorIndex = GET_PIXEL(buffer, i, 2);
#define GET_PIXEL(array, index, bitsPerPixel) \
_GET_PIXEL((array), (index), (bitsPerPixel))
// Internal definitions
#define _SET_PIXEL(array, index, bitsPerPixel, color) \
array[index/(8/bitsPerPixel)] = ( \
array[index/(8/bitsPerPixel)] & \
~(((1<<bitsPerPixel)-1) << ((index * bitsPerPixel)%8)) \
) | color << ((index * bitsPerPixel)%8)
#define _GET_PIXEL(array, index, bitsPerPixel) ( \
array[index/(8/bitsPerPixel)] >>(index*bitsPerPixel)%8 \
) & ((1<<bitsPerPixel)-1)
////////////////////////////////////////////////////////////////////////////////
/// @brief Conversion between cycles and nano seconds
////////////////////////////////////////////////////////////////////////////////
#define NS_PER_SEC 1000000000ULL
#define CYCLES_PER_SEC ((uint64_t) (F_CPU))
#define CYCLES(time_ns) (((CYCLES_PER_SEC * (time_ns)) + NS_PER_SEC - 1ULL) / NS_PER_SEC)
#define NANOSECONDS(cycles) (((cycles) * NS_PER_SEC + CYCLES_PER_SEC-1) / CYCLES_PER_SEC)
////////////////////////////////////////////////////////////////////////////////
/// @brief definitions for class template specializations
////////////////////////////////////////////////////////////////////////////////
/// @brief AVR ports are referenced A through F. For example ws2812b<D,6>
enum avrLedStripPort {
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = 6
};
/// @brief Pixel type declares the byte order for every color of the LED strip
/// when using 24/32 bit typed pixels
enum pixelFormat {
NONE = 0, // Defaults to 3 bytes per pixel of unspecified order
RGB = 1,
GRB = 2,
BGR = 3,
RGBW = 4,
GRBW = 5,
HBGR = 6
};
#define PIXEL_FORMAT_4B RGBW
/// @brief Type of low-level method to send data for the LED strip (see sendBytes)
enum ledProtocol {
ONE_PORT_BITBANG = 1, // Any LED with single data line
TWO_PORT_SPLIT_BITBANG = 2, // Same, but update 2 ports in parel, sending 1/2 the array to one port, and the other 1/2 to the other
TWO_PORT_INTLV_BITBANG = 3, // Same, but update 2 ports in parel, interleaving the pixels of the array
EIGHT_PORT_BITBANG = 4, // Experimental
ONE_PORT_PWM = 5, // Not implemented
ONE_PORT_UART = 6, // Not implemented
SPI_BITBANG = 7, // APA-102 and any LED with data and clock line
SPI_HARDWARE = 8 // Not implemented
};
#define PROTOCOL_SPI SPI_BITBANG
////////////////////////////////////////////////////////////////////////////////
/// @brief Hack to survive undefined port on Arduino
/// avoid compilation errors for arduinos that are missing some of the 4 ports
/// by defining the unknown ports to be any of the known ones. This quiets
/// the compiler, and that fluff code optimizes away.
////////////////////////////////////////////////////////////////////////////////
// Define a DUMMY_PORT_ID that will be used to patch unknown ports to map to
// the first existing port we find. Undefined ports won't be used but this
// lets the compiler work without complaining.
#if defined(PORTD)
#define DUMMY_PORT PORTD
#define DUMMY_DDR DDRD
#elif defined(PORTB)
#define DUMMY_PORT PORTB
#define DUMMY_DDR DDRB
#elif defined(PORTA)
#define DUMMY_PORT PORTA
#define DUMMY_DDR DDRA
#elif defined(PORTC)
#define DUMMY_PORT PORTC
#define DUMMY_DDR DDRC
#endif // PORT
// If any of the ports we support does not exist, re-map it to the dummy port.
#ifdef DUMMY_PORT
#ifndef PORTA
#define PORTA DUMMY_PORT
#define DDRA DUMMY_DDR
#endif // PORTA
#ifndef PORTB
#define PORTB DUMMY_PORT
#define DDRB DUMMY_DDR
#endif // PORTB
#ifndef PORTC
#define PORTC DUMMY_PORT
#define DDRC DUMMY_DDR
#endif // PORTC
#ifndef PORTD
#define PORTD DUMMY_PORT
#define DDRD DUMMY_DDR
#endif // PORTD
#ifndef PORTE
#define PORTE DUMMY_PORT
#define DDRE DUMMY_DDR
#endif // PORTE
#ifndef PORTF
#define PORTF DUMMY_PORT
#define DDRF DUMMY_DDR
#endif // PORTF
#endif // DUMMY_PORT
////////////////////////////////////////////////////////////////////////////////
#ifdef ARDUINO_ARCH_AVR
////////////////////////////////////////////////////////////////////////////////
/// @brief Arduino AVR low level macros
////////////////////////////////////////////////////////////////////////////////
/// Port Data Direction control Register address
#define AVR_DDR(id) _AVR_DDR((id))
#define _AVR_DDR(id) ((id==A) ? DDRA : (id==B) ? DDRB : (id==C) ? DDRC : \
(id==D) ? DDRD : (id==E) ? DDRE : DDRF)
#define SET_DDR_HIGH( portId, portPin) AVR_DDR(portId) |= 1U << portPin
#define FAB_DDR(portId, val) AVR_DDR(portId) = val
/// Port address & pin level manipulation
#define AVR_PORT(id) _AVR_PORT((id))
#define _AVR_PORT(id) ((id==A) ? PORTA : (id==B) ? PORTB : (id==C) ? PORTC : \
(id==D) ? PORTD : (id==E) ? PORTE : PORTF)
#define FAB_PORT(portId, val) AVR_PORT(portId) = val
// Note: gcc converts these bit manipulations to sbi and cbi instructions
#define SET_PORT_HIGH(portId, portPin) AVR_PORT(portId) |= 1U << portPin
#define SET_PORT_LOW( portId, portPin) AVR_PORT(portId) &= ~(1U << portPin);
/// Method to optimy delay N cycles with nops for bitBang.
#define DELAY_CYCLES(count) if (count > 0) __builtin_avr_delay_cycles(count);
// Number of cycles sbi and cbi instructions take when using SET macros
const int sbiCycles = 2;
const int cbiCycles = 2;
#define DISABLE_INTERRUPTS {uint8_t oldSREG = SREG; __builtin_avr_cli()
#define RESTORE_INTERRUPTS SREG = oldSREG; }
////////////////////////////////////////////////////////////////////////////////
#elif defined(__arm__)
////////////////////////////////////////////////////////////////////////////////
/// @brief ARM0 - ARM3 low level macros
/// @note: Ports have no letter, just port pins. ws2812b<D,6> will ignore D and
/// just route to pin 6.
///
/// Register information:
/// DWT_CYCCNT: Cycle count register
////////////////////////////////////////////////////////////////////////////////
/// @todo THE debug() function template resolution does not work on non optimized teensy
//#define DISABLE_DEBUG_METHOD
#define SET_DDR_HIGH( portId, portPin)
#define FAB_DDR( portId, val)
#define FAB_PORT(portId, val)
#define SET_PORT_HIGH(portId, pinId) digitalWriteFast(pinId, 1)
#define SET_PORT_LOW( portId, pinId) digitalWriteFast(pinId, 0)
/// Delay N cycles using cycles register
#define DELAY_CYCLES(count) {int till = count + ARM_DWT_CYCCNT; while (ARM_DWT_CYCCNT < till);}
// Number of cycles sbi and cbi instructions take
const int sbiCycles = 2;
const int cbiCycles = 2;
#define DISABLE_INTERRUPTS {uint8_t oldSREG = SREG; cli()
#define RESTORE_INTERRUPTS SREG = oldSREG; }
//mov r0, #COUNT
//L:
//subs r0, r0, #1
//bnz L
#define MACRO_CMB( A , B) A##B
#define M_RPT(__N, __macro) MACRO_CMB(M_RPT, __N)(__macro)
#define M_RPT0(__macro)
#define M_RPT1(__macro) M_RPT0(__macro) __macro(0)
#define M_RPT2(__macro) M_RPT1(__macro) __macro(1)
//...
#define MY_NOP(__N) __asm ("nop");
#define delay150cycles M_RPT(150, MY_NOP);
////////////////////////////////////////////////////////////////////////////////
#error "Unsupported Tensilica (ARC)Architecture"
#else
////////////////////////////////////////////////////////////////////////////////
/// @brief unknown processor architecture
////////////////////////////////////////////////////////////////////////////////
#error "Unsupported Architecture"
////////////////////////////////////////////////////////////////////////////////
#endif // CPU ARCHITECTURE
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Base class defining LED strip operations owed.
////////////////////////////////////////////////////////////////////////////////
static const uint8_t blank[3] = {128,128,128};
#define FAB_TDEF int16_t high1, \
int16_t low1, \
int16_t high0, \
int16_t low0, \
uint32_t minMsRefresh, \
avrLedStripPort dataPortId, \
uint8_t dataPortPin, \
avrLedStripPort clockPortId,\
uint8_t clockPortPin, \
pixelFormat colors, \
ledProtocol protocol
#define FAB_TVAR high1, low1, high0, low0, minMsRefresh, dataPortId, dataPortPin, clockPortId, clockPortPin, colors, protocol
/// @brief Class to drive LED strips. Relies on custom sendBytes() method to push data to LEDs
//template<
// int16_t high1, // Number of cycles high for logical one
// int16_t low1, // Number of cycles low for logical one
// int16_t high0, // Number of cycles high for logical zero
// int16_t low0, // Number of cycles low for logical zero
// uint32_t minMsRefresh, // Minimum milliseconds to wait to reset LED strip data stream
// avrLedStripPort dataPortId, // AVR port the LED strip is attached to
// uint8_t dataPortPin // AVR port bit the LED strip is attached to
//>
template <FAB_TDEF>
class avrBitbangLedStrip
{
static const uint8_t bytesPerPixel = (colors < PIXEL_FORMAT_4B) ? 3 : 4;
public:
////////////////////////////////////////////////////////////////////////
/// @brief Constructor: Set ed dataPortId.dataPortPin to digital output
////////////////////////////////////////////////////////////////////////
avrBitbangLedStrip();
////////////////////////////////////////////////////////////////////////
~avrBitbangLedStrip() { };
////////////////////////////////////////////////////////////////////////
/// @brief Prints to console the configuration
/// @note You must implement the print routines (see example)
////////////////////////////////////////////////////////////////////////
template <void printChar(const char *),void printInt(uint32_t)>
#ifndef DISABLE_DEBUG_METHOD
static inline void debug(void);
#endif
////////////////////////////////////////////////////////////////////////
/// @brief Sends N bytes to the LED using bit-banging.
///
/// @param[in] count Number of bytes sent
/// @param[in] array Array of bytes to send
///
/// @warning
/// The cer must handle interupts!
/// If interupts are not off, the timer interupt will happen, and it takes
/// too long and will cause a LED strip reset.
///
/// @note
/// __attribute__ ((always_inline)) forces gcc to inline this function in
/// the cer, to optimize there and avoid function c overhead. The
/// timing is critical at 16MHz.
/// @note
/// __builtin_avr_delay_cycles() is a gcc built-in that will generate ASM
/// to implement the exact number of cycle delays specified using the most
/// compact instruction loop, and is portable.
/// @note
/// The AVR port bit-set math will be changed by gcc to a sbi/cbi in ASM
////////////////////////////////////////////////////////////////////////
static inline void
sendBytes(const uint16_t count, const uint8_t * array)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Sends N uint32_t in a row set to zero or 0xFFFFFFFF, to build
/// a frame for each pixel, and for a whole strip, SPI protocol only
////////////////////////////////////////////////////////////////////////
static inline void
spiSoftwareSendFrame(const uint16_t count, bool high)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @bried Implements sendBytes for the 1-port SPI protocol
////////////////////////////////////////////////////////////////////////
static inline void
spiSoftwareSendBytes(const uint16_t count, const uint8_t * array)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Implements sendBytes for the 1-ports protocol
////////////////////////////////////////////////////////////////////////
static inline void
onePortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Implements sendBytes for the 2-ports protocol
////////////////////////////////////////////////////////////////////////
static inline void
twoPortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Implements sendBytes for the 8-ports protocol
////////////////////////////////////////////////////////////////////////
static inline void
eightPortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Clears the LED strip
/// @param[in] numPixels Number of pixels to erase
////////////////////////////////////////////////////////////////////////
static inline void clear( const uint16_t numPixels)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Sets the LED strip to a grey value
/// @param[in] numPixels Number of pixels to erase
/// @param[in] value Brightness of the grey [0..255]
////////////////////////////////////////////////////////////////////////
static inline void grey(
const uint16_t numPixels,
const uint8_t value)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Sends an array of 32bit words encoding 0x00bbrrgg to the LEDs
/// This is the standard encoding for most libraries and wastes 25% of
/// the SRAM.
///
/// @param[in] numPixels Number of pixels to write
/// @param[in] array Array of 1 word per pixels in native order
/// (most significant byte is ignored)
////////////////////////////////////////////////////////////////////////
static inline void sendPixels(
const uint16_t numPixels,
const uint32_t * pixelArray)
__attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Sends 3-byte pixels to the LED strip.
/// This function should be the most common used method.
///
/// @note for SK6812, which uses 4 bytes per pixels, this method will send
/// 4 bytes per pixel and expect an array containing 4 bytes per pixel.
/// @param[in] numPixels Number of pixels to write
/// @param[in] array Array of 3-bytes per pixels in native order
/// (for example GBR for WS2812B LED strips)
////////////////////////////////////////////////////////////////////////
static inline void sendPixels(
const uint16_t numPixels,
const uint8_t * array) __attribute__ ((always_inline));
static inline void sendPixels(
const uint16_t numPixels,
const rgbw * array) __attribute__ ((always_inline));
static inline void sendPixels(
const uint16_t numPixels,
const grbw * array) __attribute__ ((always_inline));
static inline void sendPixels(
const uint16_t numPixels,
const hbgr * array) __attribute__ ((always_inline));
static inline void sendPixels(
const uint16_t numPixels,
const rgb * array) __attribute__ ((always_inline));
static inline void sendPixels(
const uint16_t numPixels,
const grb * array) __attribute__ ((always_inline));
static inline void sendPixels(
const uint16_t numPixels,
const bgr * array) __attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Sends an array of bits encoded with a palette to the LEDs.
/// This is a compact method to store patterns, compressed 3x to 24x,
/// but with the overhead of a palette array.
///
/// @param[in] count Size of the array in bytes
/// @param[in] array Array of bitsPerPixel bits per pixels, where the
/// number of bits is 1,2, 4 or 8.
/// @param[in] palette Palette array with one entry per color used.
///
/// 1bit -> 6B palette
/// 2bits -> 12B palette
/// 4bits -> 48B palette
/// 8bits -> 768B palette
///
/// @note bitsPerPixel is a template constant to ow the compiler to
/// optimize the bit-masking code.
////////////////////////////////////////////////////////////////////////
template <const uint8_t bitsPerPixel, class pixelColors> // @note does not support uint32_t uint8_t.
static inline void sendPixels (
const uint16_t count,
const uint8_t * pixelArray,
const uint8_t * reds,
const uint8_t * greens,
const uint8_t * blues) __attribute__ ((always_inline));
template <const uint8_t bitsPerPixel>
static inline void sendPixels (
const uint16_t count,
const uint8_t * pixelArray,
const uint8_t * palette) __attribute__ ((always_inline));
template <const uint8_t bitsPerPixel, class paletteColors>
static inline void sendPixels (
const uint16_t count,
const uint8_t * pixelArray,
const paletteColors * palette) __attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Send an array that is remapped to a physical LED strip
/// of a different layout than the natural layout of the pixel array
/// @param[in] pixelMap An array mapping a physical pixel to an offset
/// in the array pixelMap[pix] = index
/// Note: the array size is not used as it is expected that it will be
/// at least as big as the largest index in the pixel map.
////////////////////////////////////////////////////////////////////////
template <class pixelType>
static inline void sendPixelsRemap(
const uint16_t numPixels,
const uint16_t * pixelMap,
const pixelType * array) __attribute__ ((always_inline));
template <const uint8_t bitsPerPixel, class pixelType>
static inline void sendPixelsRemap (
const uint16_t numPixels,
const uint16_t * pixelMap,
const uint8_t * pixelArray,
const pixelType * palette) __attribute__ ((always_inline));
template <const uint8_t bitsPerPixel, class pixelType>
static inline void sendPixelsRemap (
const uint8_t numPixels,
const uint8_t * pixelMap,
const uint8_t * pixelArray,
const pixelType * palette) __attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Sends an array of 3 pixels per 16bit words to the LEDs
/// This yelds 32K colors, and saves 33% RAM without using a palette.
///
/// the SRAM.
/// @brief Sends an array of 16-bit words to the LEDs. Each word encodes
/// one pixel with 64 levels (5 bits)
//
/// @note brightness is a template constant that controls the unused 3
/// bits of the color. Its value is zero to 2. Often set to 0 when
/// LED strip is too bright.
////////////////////////////////////////////////////////////////////////
template <uint8_t brightness>
static inline void sendPixels(int count, uint16_t * pixelArray) __attribute__ ((always_inline));
////////////////////////////////////////////////////////////////////////
/// @brief Waits long enough to trigger a LED strip reset
////////////////////////////////////////////////////////////////////////
static inline void refresh() {
if (protocol == SPI_BITBANG) {
// SPI: Reset LED strip to accept a refresh with 32bit start frame
// set to zero, and an end frame set to 0 or 0xFF (we use zero)
// of 16B supporting up to 256 pixels
spiSoftwareSendFrame(4+16, false);
} else {
// 1-wire: Delay next pixels to cause a refresh
delay(minMsRefresh);
}
}
};
////////////////////////////////////////////////////////////////////////////////
/// Class methods definition
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
template<FAB_TDEF>
avrBitbangLedStrip<FAB_TVAR>::avrBitbangLedStrip()
{
// Digital out pin mode
// bitSet(portDDR, dataPortPin);
// DDR? |= 1U << dataPortPin;
switch (protocol) {
case TWO_PORT_SPLIT_BITBANG:
case TWO_PORT_INTLV_BITBANG:
// Init two ports as out, set to low state
SET_DDR_HIGH(dataPortId, dataPortPin);
SET_DDR_HIGH(clockPortId, clockPortPin);
SET_PORT_LOW(dataPortId, dataPortPin);
SET_PORT_LOW(clockPortId, clockPortPin);
break;
case EIGHT_PORT_BITBANG:
FAB_DDR(dataPortId, 0xFF); // pins out
FAB_PORT(dataPortId, 0x00); // pins low
break;
case SPI_BITBANG:
// Init both ports as out
SET_DDR_HIGH(dataPortId, dataPortPin);
SET_DDR_HIGH(clockPortId, clockPortPin);
// SPI: Reset LED strip to accept a refresh
spiSoftwareSendFrame(16, false);
break;
default:
// Init data port as out, set to low state
SET_DDR_HIGH(dataPortId, dataPortPin);
SET_PORT_LOW(dataPortId, dataPortPin);
break;
}
};
#ifndef DISABLE_DEBUG_METHOD
template<FAB_TDEF>
template <void printChar(const char *),void printInt(uint32_t)>
inline void
avrBitbangLedStrip<FAB_TVAR>::debug(void)
{
printChar("\nclass avrBitbangLedStrip<...>\n");
printInt(CYCLES_PER_SEC/1000000);
printChar("MHz CPU, ");
printInt(NANOSECONDS(1000));
printChar(" picoseconds per cycle\n");
printChar("ONE HIGH=");
printInt(high1);
printChar(" LOW=");
printInt(low1);
printChar(" cycles\n");
printChar("ZERO HIGH=");
printInt(high0);
printChar(" LOW=");
printInt(low0);
printChar(" cycles\n");
switch (colors) {
case NONE: printChar("NONE"); break;
case RGB: printChar("RGB"); break;
case GRB: printChar("GRB"); break;
case BGR: printChar("BGR"); break;
case RGBW: printChar("RGBW"); break;
case GRBW: printChar("GRBW"); break;
case HBGR: printChar("HBGR"); break;
default: printChar("ERROR!"); break;
}
printChar(" REFRESH MSEC=");
printInt(minMsRefresh);
printChar("\nDATA_PORT ");
switch(dataPortId) {
case A:
printChar("A.");
break;
case B:
printChar("B.");
break;
case C:
printChar("C.");
break;
case D:
printChar("D.");
break;
case E:
printChar("E.");
break;
case F:
printChar("F.");
break;
default:
printChar("UNKNOWN.");
}
printInt(dataPortPin);
printChar(", ");
if (protocol >= PROTOCOL_SPI) {
printChar("CLOCK_PORT ");
switch(clockPortId) {
case A:
printChar("A.");
break;
case B:
printChar("B.");
break;
case C:
printChar("C.");
break;
case D:
printChar("D.");
break;
case E:
printChar("E.");
break;
case F:
printChar("F.");
break;
default:
printChar("UNKNOWN.");
}
printInt(clockPortPin);
printChar(", ");
}
switch(protocol) {
case ONE_PORT_BITBANG:
printChar("ONE-PORT (bitbang)");
break;
case TWO_PORT_SPLIT_BITBANG:
printChar("TWO-PORT-SPLIT (bitbang)");
break;
case TWO_PORT_INTLV_BITBANG:
printChar("TWO-PORT-INTERLEAVED (bitbang)");
break;
case EIGHT_PORT_BITBANG:
printChar("HEIGHT-PORT (bitbang)");
break;
case ONE_PORT_PWM:
printChar("ONE-PORT (PWM)");
break;
case ONE_PORT_UART:
printChar("ONE-PORT (UART)");
break;
case SPI_BITBANG:
printChar("SPI (bitbang)");
break;
case SPI_HARDWARE:
printChar("SPI (hardware)");
break;
default:
printChar("PROTOCOL UNKNOWN");
}
printChar("\n");
}
#endif
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendBytes(const uint16_t count, const uint8_t * array)
{
switch (protocol) {
case ONE_PORT_BITBANG:
onePortSoftwareSendBytes(count, array);
break;
case TWO_PORT_SPLIT_BITBANG:
case TWO_PORT_INTLV_BITBANG:
// Note: the function will detect and handle modes I and S
twoPortSoftwareSendBytes(count, array);
break;
case EIGHT_PORT_BITBANG:
eightPortSoftwareSendBytes(count, array);
break;
case SPI_BITBANG:
spiSoftwareSendBytes(count, array);
break;
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::spiSoftwareSendFrame(const uint16_t count, bool high)
{
if (high) {
SET_PORT_HIGH(dataPortId, dataPortPin);
} else {
SET_PORT_LOW(dataPortId, dataPortPin);
}
// for(uint32_t c = 0; c < 32 * count; c++) {
for(uint32_t c = 0; c < 8 * count; c++) {
SET_PORT_LOW(clockPortId, clockPortPin);
SET_PORT_HIGH(clockPortId, clockPortPin);
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::spiSoftwareSendBytes(const uint16_t count, const uint8_t * array)
{
for(uint16_t cnt = 0; cnt < count; ++cnt) {
const uint8_t val = array[cnt];
// If LED strip is defined as 3 byte type (default) then hard code the first
// byte to 0xFF, aka max brightness.
// This hard-codes the APA-102 protocol here so it is a bit hacky.
if ((colors >= PIXEL_FORMAT_4B) && ((cnt % 3) == 0)) {
// spiSoftwareSendFrame(1, true);
}
// To send a bit to SPI, set its value, then transtion clock low-high
for(int8_t b=7; b>=0; b--) {
const bool bit = (val>>b) & 0x1;
SET_PORT_LOW(clockPortId, clockPortPin);
if (bit) {
SET_PORT_HIGH(dataPortId, dataPortPin);
} else {
SET_PORT_LOW(dataPortId, dataPortPin);
}
SET_PORT_HIGH(clockPortId, clockPortPin);
}
}
}
/// @brief sends the array split across two ports each having half the LED strip to illuminate.
/// To achieve this, we repurpose the clock port used for SPI as a second data port.
/// We support two protocols:
/// TWO_PORT_SPLIT_BITBANG: The array is split into 2 halves sent each sent to one of the ports
/// TWO_PORT_INTLV_BITBANG: The array is interleaved and each pixel of 3 byte is sent to the next port
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::twoPortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
{
// Number of bytes per pixel
const uint16_t bpp = (colors < PIXEL_FORMAT_4B) ? 3 : 4;
// If split mode, we send a block of half size
const uint16_t blockSize = (protocol == TWO_PORT_SPLIT_BITBANG) ? count/2 : count;
// Stride is two for interlacing to jump pixels going to the other port
const uint8_t stride = (protocol == TWO_PORT_SPLIT_BITBANG) ? 1 : 2;
const uint8_t increment = stride * bpp;
// Loop to scan pixels, potentiy skipping every other pixel, or scanning 1/2 the pixels
// based on the display protocol used.
for(uint16_t pix = 0; pix < blockSize; pix += increment) {
// Loop to send 3 or 4 bytes of a pixel to the same port
for(uint16_t pos = pix; pos < pix+bpp; pos++) {
for(int8_t bit = 7; bit >= 0; bit--) {
const uint8_t mask = 1 << bit;
volatile bool isbitDhigh = array[pos] & mask;
volatile bool isbitChigh = (protocol == TWO_PORT_SPLIT_BITBANG) ?
array[pos + blockSize] & mask : // split: pixel is blockSize away.
array[pos + bpp] & mask; // interleaved: pixel is next one.
if (isbitDhigh) SET_PORT_HIGH(dataPortId, dataPortPin);
if (isbitChigh) SET_PORT_HIGH(clockPortId, clockPortPin);
if (!isbitDhigh) {
SET_PORT_HIGH(dataPortId, dataPortPin);
DELAY_CYCLES(high0 - sbiCycles);
SET_PORT_LOW(dataPortId, dataPortPin);
}
if (!isbitChigh) {
SET_PORT_HIGH(clockPortId, clockPortPin);
DELAY_CYCLES(high0 - sbiCycles);
SET_PORT_LOW(clockPortId, clockPortPin);
}
DELAY_CYCLES(high1 - 2*sbiCycles);
SET_PORT_LOW(dataPortId, dataPortPin);
SET_PORT_LOW(clockPortId, clockPortPin);
DELAY_CYCLES(low1 - 2*cbiCycles);
}
}
}
}
#if 0
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::twoPortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
{
const uint16_t blockSize = count/2;
for(uint16_t pos = 0; pos < blockSize; pos++) {
for(int8_t bit = 7; bit >= 0; bit--) {
const uint8_t mask = 1 << bit;
const bool isbitDhigh = array[0*blockSize + pos] & mask;
const bool isbitChigh = array[1*blockSize + pos] & mask;
if (isbitDhigh) SET_PORT_HIGH(dataPortId, dataPortPin);
if (isbitChigh) SET_PORT_HIGH(clockPortId, clockPortPin);
if (!isbitDhigh) {
SET_PORT_HIGH(dataPortId, dataPortPin);
//DELAY_CYCLES(high0 - sbiCycles);
DELAY_CYCLES(0);
SET_PORT_LOW(dataPortId, dataPortPin);
}
if (!isbitChigh) {
SET_PORT_HIGH(clockPortId, clockPortPin);
//DELAY_CYCLES(high0 - sbiCycles);
DELAY_CYCLES(0);
SET_PORT_LOW(clockPortId, clockPortPin);
}
DELAY_CYCLES(0);
SET_PORT_LOW(dataPortId, dataPortPin);
SET_PORT_LOW(clockPortId, clockPortPin);
DELAY_CYCLES(0);
}
}
}
#endif
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::eightPortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
{
const uint16_t bpp = (colors < PIXEL_FORMAT_4B) ? 3 : 4;
const uint16_t blockSize __asm__("r14") = count / (clockPortPin - dataPortPin + 1) / bpp * bpp;
// Buffer one byte of each port in register
uint8_t r0 = 0; // __asm__("r2") = 0;
uint8_t r1 = 0; // __asm__("r3") = 0;
uint8_t r2 = 0; // __asm__("r4") = 0;
uint8_t r3 = 0; // __asm__("r5") = 0;
uint8_t r4 = 0; // __asm__("r6") = 0;
uint8_t r5 = 0; // __asm__("r7") = 0;
uint8_t r6 = 0; // __asm__("r8") = 0;
uint8_t r7 = 0; // __asm__("r9") = 0;
// Since bits for each port are sent delayed by one cycle to
// implement the memory access pipeline, we must leave the
// ports not yet in use set to LOW level. Once the pipeline
// is initialized fully onMask == 0xFF, and we always set it
// to high to send a zero or a one.
uint8_t onMask __asm__("r10") = 0;
for(uint16_t c __asm__("r12") = 0; c < blockSize ; ++c) {
for(int8_t b __asm__("r13") = 7; b >= 0; --b) {
uint8_t bitmask __asm__("r10") = 0;
// Load ONE byte per iteration
// Skip end condition check to reduce CPU cycles.
// It is expected that the LED strip won't have extra pixels.
switch (b) {
case 0:
// By checking staticy if rX is read memory, we
// avoid changing it, and the compiler will optimize out
// the code including bitmask setting. On 16MHz Uno, we can
// only set bitmask for 6 ports before the strip resets.
if (0 == dataPortPin) {
onMask |= 1;
r0 = array[c];
}
if (r0 & 0b10000000) bitmask |= 1;
if (r1 & 0b01000000) bitmask |= 2;
if (r2 & 0b00100000) bitmask |= 4;
if (r3 & 0b00010000) bitmask |= 8;
if (r4 & 0b00001000) bitmask |= 16;
if (r5 & 0b00000100) bitmask |= 32;
if (r6 & 0b00000010) bitmask |= 64;
if (r7 & 0b00000001) bitmask |= 128;
break;
case 1:
if ((1 >= dataPortPin) && (1 <= clockPortPin)) {
onMask |= 2;
r1 = array[c + (1-dataPortPin) * blockSize];
}
if (r0 & 0b00000001) bitmask |= 1;
if (r1 & 0b10000000) bitmask |= 2;
if (r2 & 0b01000000) bitmask |= 4;
if (r3 & 0b00100000) bitmask |= 8;
if (r4 & 0b00010000) bitmask |= 16;
if (r5 & 0b00001000) bitmask |= 32;
if (r6 & 0b00000100) bitmask |= 64;
if (r7 & 0b00000010) bitmask |= 128;
break;
case 2:
if ((2 >= dataPortPin) && (2 <= clockPortPin)) {
onMask |= 4;
r2 = array[c + (2-dataPortPin) * blockSize];
}
if (r0 & 0b00000010) bitmask |= 1;
if (r1 & 0b00000001) bitmask |= 2;
if (r2 & 0b10000000) bitmask |= 4;
if (r3 & 0b01000000) bitmask |= 8;
if (r4 & 0b00100000) bitmask |= 16;
if (r5 & 0b00010000) bitmask |= 32;
if (r6 & 0b00001000) bitmask |= 64;
if (r7 & 0b00000100) bitmask |= 128;
break;
case 3:
if ((3 >= dataPortPin) && (3 <= clockPortPin)) {
onMask |= 8;
r3 = array[c + (3-dataPortPin) * blockSize];
}
if (r0 & 0b00000100) bitmask |= 1;
if (r1 & 0b00000010) bitmask |= 2;
if (r2 & 0b00000001) bitmask |= 4;
if (r3 & 0b10000000) bitmask |= 8;
if (r4 & 0b01000000) bitmask |= 16;
if (r5 & 0b00100000) bitmask |= 32;
if (r6 & 0b00010000) bitmask |= 64;
if (r7 & 0b00001000) bitmask |= 128;
break;
case 4:
if ((4 >= dataPortPin) && (4 <= clockPortPin)) {
onMask |= 16;
r4 = array[c + (4-dataPortPin) * blockSize];
}
if (r0 & 0b00001000) bitmask |= 1;
if (r1 & 0b00000100) bitmask |= 2;
if (r2 & 0b00000010) bitmask |= 4;
if (r3 & 0b00000001) bitmask |= 8;
if (r4 & 0b10000000) bitmask |= 16;
if (r5 & 0b01000000) bitmask |= 32;
if (r6 & 0b00100000) bitmask |= 64;
if (r7 & 0b00010000) bitmask |= 128;
break;
case 5:
if ((5 >= dataPortPin) && (5 <= clockPortPin)) {
onMask |= 32;
r5 = array[c + (5-dataPortPin) * blockSize];
}
if (r0 & 0b00010000) bitmask |= 1;
if (r1 & 0b00001000) bitmask |= 2;
if (r2 & 0b00000100) bitmask |= 4;
if (r3 & 0b00000010) bitmask |= 8;
if (r4 & 0b00000001) bitmask |= 16;
if (r5 & 0b10000000) bitmask |= 32;
if (r6 & 0b01000000) bitmask |= 64;
if (r7 & 0b00100000) bitmask |= 128;
break;
case 6:
if ((6 >= dataPortPin) && (6 <= clockPortPin)) {
onMask |= 64;
r6 = array[c + (6-dataPortPin) * blockSize];
}
if (r0 & 0b00100000) bitmask |= 1;
if (r1 & 0b00010000) bitmask |= 2;
if (r2 & 0b00001000) bitmask |= 4;
if (r3 & 0b00000100) bitmask |= 8;
if (r4 & 0b00000010) bitmask |= 16;
if (r5 & 0b00000001) bitmask |= 32;
if (r6 & 0b10000000) bitmask |= 64;
if (r7 & 0b01000000) bitmask |= 128;
break;
case 7:
if ((7 >= dataPortPin) && (7 <= clockPortPin)) {
onMask |= 128;
r7 = array[c + (7-dataPortPin) * blockSize];
}
if (r0 & 0b01000000) bitmask |= 1;
if (r1 & 0b00100000) bitmask |= 2;
if (r2 & 0b00010000) bitmask |= 4;
if (r3 & 0b00001000) bitmask |= 8;
if (r4 & 0b00000100) bitmask |= 16;
if (r5 & 0b00000010) bitmask |= 32;
if (r6 & 0b00000001) bitmask |= 64;
if (r7 & 0b10000000) bitmask |= 128;
break;
}
// Set HIGH, set LOW zeros, set LOW zeros and ones.
FAB_PORT(dataPortId, onMask);
DELAY_CYCLES(high0 - sbiCycles);
FAB_PORT(dataPortId, bitmask);
DELAY_CYCLES( high1 - high0 + sbiCycles);
FAB_PORT(dataPortId, 0x00);
// Estimate overhead of math above to ~ 20 cycles
DELAY_CYCLES(low0 - cbiCycles - 20);
}
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::onePortSoftwareSendBytes(const uint16_t count, const uint8_t * array)
{
for(uint16_t c=0; c < count; c++) {
const uint8_t val = array[c];
for(int8_t b=7; b>=0; b--) {
const bool bit = (val>>b) & 0x1;
if (bit) {
// Send a ONE
// HIGH with ASM sbi (2 words, 2 cycles)
SET_PORT_HIGH(dataPortId, dataPortPin);
// Wait exact number of cycles specified
DELAY_CYCLES(high1 - sbiCycles);
// LOW with ASM cbi (2 words, 2 cycles)
SET_PORT_LOW(dataPortId, dataPortPin);
// Wait exact number of cycles specified
DELAY_CYCLES(low1 - cbiCycles);
} else {
// Send a ZERO
// HIGH with ASM sbi (2 words, 2 cycles)
SET_PORT_HIGH(dataPortId, dataPortPin);
// Wait exact number of cycles specified
DELAY_CYCLES(high0 - sbiCycles);
// LOW with ASM cbi (2 words, 2 cycles)
SET_PORT_LOW(dataPortId, dataPortPin);
// Wait exact number of cycles specified
DELAY_CYCLES(low0 - cbiCycles);
}
}
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::clear(const uint16_t numPixels)
{
if (protocol == SPI_BITBANG) {
// SPI: Send start frame, Clean numPixels, and Reset LED strip
spiSoftwareSendFrame(1 + numPixels + (numPixels+1)/2, 0);
} else {
// 1-wire: Delay next pixels to cause a refresh
const uint8_t array[4] = {0,0,0,0};
DISABLE_INTERRUPTS;
for( uint16_t i = 0; i < numPixels; i++) {
sendBytes(bytesPerPixel, array);
}
RESTORE_INTERRUPTS;
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::grey(const uint16_t numPixels, const uint8_t value)
{
const uint8_t array[4] = {value, value, value, value};
DISABLE_INTERRUPTS;
for( uint16_t i = 0; i < numPixels; i++) {
sendBytes(bytesPerPixel, array);
}
RESTORE_INTERRUPTS;
}
// 3B raw input array
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const uint8_t * array)
{
DISABLE_INTERRUPTS;
sendBytes(numPixels * bytesPerPixel, array);
RESTORE_INTERRUPTS;
}
// Since colors is a constant, the switch case will convert to 4 sendBytes max.
#define SEND_REMAPPED_PIXELS(numPixels, array, sendWhite) \
DISABLE_INTERRUPTS; \
for (uint16_t i = 0; i < numPixels; i++) { \
switch (colors) { \
case RGB: \
sendBytes(1, &array[i].r); \
sendBytes(1, &array[i].g); \
sendBytes(1, &array[i].b); \
break; \
case GRB: \
sendBytes(1, &array[i].g); \
sendBytes(1, &array[i].r); \
sendBytes(1, &array[i].b); \
break; \
case BGR: \
sendBytes(1, &array[i].b); \
sendBytes(1, &array[i].g); \
sendBytes(1, &array[i].r); \
break; \
case RGBW: \
sendBytes(1, &array[i].r); \
sendBytes(1, &array[i].g); \
sendBytes(1, &array[i].b); \
sendWhite; \
break; \
case GRBW: \
sendBytes(1, &array[i].g); \
sendBytes(1, &array[i].r); \
sendBytes(1, &array[i].b); \
sendWhite; \
break; \
case HBGR: \
sendWhite; \
sendBytes(1, &array[i].b); \
sendBytes(1, &array[i].g); \
sendBytes(1, &array[i].r); \
break; \
default: \
break; \
} \
} \
RESTORE_INTERRUPTS;
#define sendWhiteMacro sendBytes(1, &array[i].w)
#define SEND_REMAPPED_PIXELS_4B(numPixels, array) SEND_REMAPPED_PIXELS(numPixels, array, sendWhiteMacro)
#define SEND_REMAPPED_PIXELS_3B(numPixels, array) SEND_REMAPPED_PIXELS(numPixels, array, )
// 4B struct input arrays
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const rgbw * array)
{
if (colors == RGBW || colors == NONE) {
sendPixels(numPixels, (const uint8_t *) array);
} else if (colors == RGB) {
// Output array is same order but 3B, send as 32bit, which will be converted
sendPixels(numPixels, (const uint32_t *) array);
} else {
// Handle input array of different format than LED strip
SEND_REMAPPED_PIXELS_4B(numPixels, array);
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const grbw * array)
{
if (colors == GRBW || colors == NONE) {
sendPixels(numPixels, (const uint8_t *) array);
} else if (colors == GRB) {
sendPixels(numPixels, (const uint32_t *) array);
} else {
// Handle input array of different format than LED strip
SEND_REMAPPED_PIXELS_4B(numPixels, array);
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const hbgr * array)
{
if (colors == HBGR || colors == NONE) {
sendPixels(numPixels, (const uint8_t *) array);
} else if (colors == BGR) {
sendPixels(numPixels, (const uint32_t *) array);
} else {
// Handle input array of different format than LED strip
SEND_REMAPPED_PIXELS_4B(numPixels, array);
}
}
// 3B struct input arrays
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const rgb * array)
{
if (colors == RGB || colors == NONE) {
// Input array is native format. No conversion.
sendPixels(numPixels, (const uint8_t *) array);
} else {
// 4B, or 3B pixel array with different byte order, must be converted.
SEND_REMAPPED_PIXELS_3B(numPixels, array);
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const grb * array)
{
if (colors == GRB || colors == NONE) {
sendPixels(numPixels, (const uint8_t *) array);
} else {
SEND_REMAPPED_PIXELS_3B(numPixels, array);
}
}
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const bgr * array)
{
if (colors == BGR || colors == NONE) {
sendPixels(numPixels, (const uint8_t *) array);
} else {
SEND_REMAPPED_PIXELS_3B(numPixels, array);
}
}
// 4B raw input array
template<FAB_TDEF>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
const uint16_t numPixels,
const uint32_t * pixelArray)
{
DISABLE_INTERRUPTS;
if (colors >= PIXEL_FORMAT_4B) {
// 4 byte per pixel array, send bytes.
sendBytes((const uint16_t) numPixels * bytesPerPixel, (const uint8_t *) pixelArray);
} else {
// 3 byte per pixel array, send 3 out of 4 bytes.
for (int i=0; i< numPixels; i++) {
uint8_t * bytes = (uint8_t *) & pixelArray[i];
// For LED strips using 3 bytes per color, drop a byte.
sendBytes(bytesPerPixel, bytes);
}
}
RESTORE_INTERRUPTS;
}
// Palette input arrays
template<FAB_TDEF>
template <const uint8_t bitsPerPixel, class T>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels (
const uint16_t count,
const uint8_t * pixelArray,
const uint8_t * reds,
const uint8_t * greens,
const uint8_t * blues)
{
// Debug: Support simple palettes 2, 4, 16 or 256 colors
STATIC_ASSERT( bitsPerPixel == 1 || bitsPerPixel == 2 ||
bitsPerPixel == 4 || bitsPerPixel == 8,
Unsupported_palette_size);
// 1,2 4 and 8 bit bitmasks. Note 3,5,6 don't make sense
// and 5bits is handled separately with uint16_t type.
const uint8_t andMask = (bitsPerPixel ==1) ? 0x01 :
(bitsPerPixel == 2) ? 0x03 :
(bitsPerPixel == 4) ? 0x0F :
(bitsPerPixel == 8) ? 0xFF :
0x00;
DISABLE_INTERRUPTS;
// Send each byte as 1 to 4 pixels
uint16_t offset;
offset = 0;
uint16_t index;
index = 0;
while (1) {
uint8_t elem;
elem = pixelArray[offset++];
for (uint8_t j = 0; j < 8/bitsPerPixel; j++) {
if (index++ >= count) {
goto end;
}
const uint8_t colorIndex = elem & andMask;
T pixel;
pixel.r = reds[colorIndex];
pixel.g = greens[colorIndex];
pixel.b = blues[colorIndex];
// @note support w in future
sendPixels(1, &pixel);
elem >>= bitsPerPixel;
}
}
end:
RESTORE_INTERRUPTS;
}
// Palette input arrays
template<FAB_TDEF>
template <const uint8_t bitsPerPixel>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels (
const uint16_t count,
const uint8_t * pixelArray,
const uint8_t * palette)
{
// Debug: Support simple palettes 2, 4, 16 or 256 colors
STATIC_ASSERT( bitsPerPixel == 1 || bitsPerPixel == 2 ||
bitsPerPixel == 4 || bitsPerPixel == 8,
Unsupported_palette_size);
// 1,2 4 and 8 bit bitmasks. Note 3,5,6 don't make sense
// and 5bits is handled separately with uint16_t type.
const uint8_t andMask = (bitsPerPixel ==1) ? 0x01 :
(bitsPerPixel == 2) ? 0x03 :
(bitsPerPixel == 4) ? 0x0F :
(bitsPerPixel == 8) ? 0xFF :
0x00;
DISABLE_INTERRUPTS;
// Send each byte as 1 to 4 pixels
uint16_t offset;
offset = 0;
uint16_t index;
index = 0;
while (1) {
uint8_t elem;
elem = pixelArray[offset++];
for (uint8_t j = 0; j < 8/bitsPerPixel; j++) {
if (index++ >= count) {
goto end;
}
const uint8_t colorIndex = elem & andMask;
sendBytes(bytesPerPixel, &palette[bytesPerPixel*colorIndex]);
elem >>= bitsPerPixel;
}
}
end:
RESTORE_INTERRUPTS;
}
template<FAB_TDEF>
template <const uint8_t bitsPerPixel, class T>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels (
const uint16_t count,
const uint8_t * pixelArray,
const T * palette)
{
// Debug: Support simple palettes 2, 4, 16 or 256 colors
STATIC_ASSERT( bitsPerPixel == 1 || bitsPerPixel == 2 ||
bitsPerPixel == 4 || bitsPerPixel == 8,
Unsupported_palette_size);
// 1,2 4 and 8 bit bitmasks. Note 3,5,6 don't make sense
// and 5bits is handled separately with uint16_t type.
const uint8_t andMask = (bitsPerPixel ==1) ? 0x01 :
(bitsPerPixel == 2) ? 0x03 :
(bitsPerPixel == 4) ? 0x0F :
(bitsPerPixel == 8) ? 0xFF :
0x00;
DISABLE_INTERRUPTS;
// Send each byte as 1 to 4 pixels
uint16_t offset;
offset = 0;
uint16_t index;
index = 0;
while (1) {
uint8_t elem;
elem = pixelArray[offset++];
for (uint8_t j = 0; j < 8/bitsPerPixel; j++) {
if (index++ >= count) {
goto end;
}
const uint8_t colorIndex = elem & andMask;
sendBytes(bytesPerPixel, &palette[bytesPerPixel*colorIndex]);
elem >>= bitsPerPixel;
}
}
end:
RESTORE_INTERRUPTS;
}
template<FAB_TDEF>
template <class pixelType>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixelsRemap(
const uint16_t numPixels,
const uint16_t * pixelMap,
const pixelType * array)
{
// The uint8_t raw type actuy does not hold the whole pixel, it needs 3 bytes.
const uint16_t size = (sizeof(pixelType) == 1) ? bytesPerPixel : 1;
DISABLE_INTERRUPTS;
for (uint16_t i = 0; i < numPixels; i += 1) {
const uint16_t ri = pixelMap[i];
sendPixels((uint16_t) 1, &array[size * ri]);
}
RESTORE_INTERRUPTS;
}
// @note The 8bit per pixel works but 2bits per pixel does not at 16MHz.
template<FAB_TDEF>
template <const uint8_t bitsPerPixel, class pixelType>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixelsRemap (
const uint16_t numPixels,
const uint16_t * pixelMap,
const uint8_t * pixelArray,
const pixelType * palette)
{
// Support only palettes with 2, 4, 16 or 256 colors
STATIC_ASSERT( bitsPerPixel == 1 || bitsPerPixel == 2 ||
bitsPerPixel == 4 || bitsPerPixel == 8,
Unsupported_palette_size);
const uint16_t size = (sizeof(pixelType) == 1) ? bytesPerPixel : 1;
DISABLE_INTERRUPTS;
for (uint16_t i = 0; i < numPixels; i++) {
// Remapped i: index in array of the next pixel to push.
const uint16_t ri = pixelMap[i];
// Extract color index via bitmasks
const uint8_t colorIndex = GET_PIXEL(pixelArray, ri, bitsPerPixel);
// Get the color/pixel to send
const pixelType * pixel = &palette[size * colorIndex];
sendPixels(1, pixel);
}
RESTORE_INTERRUPTS;
}
template<FAB_TDEF>
template <const uint8_t bitsPerPixel, class pixelType>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixelsRemap (
const uint8_t numPixels,
const uint8_t * pixelMap,
const uint8_t * pixelArray,
const pixelType * palette)
{
// Support only palettes with 2, 4, 16 or 256 colors
STATIC_ASSERT( bitsPerPixel == 1 || bitsPerPixel == 2 ||
bitsPerPixel == 4 || bitsPerPixel == 8,
Unsupported_palette_size);
const uint16_t size = (sizeof(pixelType) == 1) ? bytesPerPixel : 1;
DISABLE_INTERRUPTS;
for (uint8_t i = 0; i < numPixels; i++) {
// Remapped i: index in array of the next pixel to push.
const uint8_t ri = pixelMap[i];
// Extract color index via bitmasks
const uint8_t colorIndex = GET_PIXEL(pixelArray, ri, bitsPerPixel);
// Get the color/pixel to send
const pixelType * pixel = &palette[size * colorIndex];
sendPixels(1, pixel);
}
RESTORE_INTERRUPTS;
}
/// @todo Rewrite this to support R5G6B5, R4G4B4W4 and a variable brightness.
template<FAB_TDEF>
template <uint8_t brightness>
inline void
avrBitbangLedStrip<FAB_TVAR>::sendPixels(
int count,
uint16_t * pixelArray)
{
const uint8_t mask5 = ((1 << (5 - brightness))-1) || ((1 << brightness) - 1);
const uint8_t mask10 = ((1 << (10 - brightness))-1) || ((1 << brightness) - 1);
uint8_t bytes[4];
// Debug: Support brightness 0..3
STATIC_ASSERT(brightness < 3, Unsupported_brightness_level);
DISABLE_INTERRUPTS;
bytes[3] = 0;
for (int i = 0; i < count; i++) {
const uint16_t elem = pixelArray[i];
bytes[0] = (uint8_t) elem << brightness;
bytes[1] = (elem >> (5 - brightness)) & mask5;
bytes[2] = (elem >> (10 - brightness)) & mask10;
sendBytes(bytesPerPixel, bytes);
}
RESTORE_INTERRUPTS;
}
////////////////////////////////////////////////////////////////////////////////
// Implementation classes for LED strip
// Defines the actual LED timings
// WS2811 2811B 2812B 2812S 2801 LEDs use similar protocols and timings
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// WS2812B (default, mainstream)
////////////////////////////////////////////////////////////////////////////////
#if 0
// These are the less agressive bitbanging timings
#define WS2812B_1H_CY CYCLES
(650) // 650ns-950ns _----------__
#define WS2812B_1L_CY CYCLES
(125) // 250ns-550ns . . .
#define WS2812B_0H_CY CYCLES
(125) // 250ns-550ns _-----_______
#define WS2812B_0L_CY CYCLES
(650) // 650ns-950ns . . .
#define WS2812B_MS_REFRESH
50 // 50,000ns Minimum sleep time to reset LED strip
#define WS2812B_NS_RF
2000000 // Max refresh rate for pixels to light up 2msec (LED PWM is 500Hz)
#else
// These are the more agressive bitbanging timings, note 0 and 1 have different durations
#define WS2812B_1H_CY CYCLES
(500) // 6 7 10 _----------__
#define WS2812B_1L_CY CYCLES
(125) // 2 2 4 . . .
#define WS2812B_0H_CY CYCLES
(125) // 2 2 5 _-----_______
#define WS2812B_0L_CY CYCLES
(188) // 2 2 7 . . .
#define WS2812B_MS_REFRESH
20 // Minimum sleep time (low) to reset LED strip
#define WS2812B_NS_RF
2000000 // Max refresh rate for pixels to light up 2msec (LED PWM is 500Hz)
#endif
WS2812B_0L_CY,
WS2812B_MS_REFRESH, dataPortId, dataPortBit, A,
0, GRB, ONE_PORT_BITBANG
template<avrLedStripPort dataPortId, uint8_t dataPortBit>
{
public:
};
////////////////////////////////////////////////////////////////////////////////
// WS2812BS - Bitbang the pixels to two ports in parel.
// The pixel array is split in two. Each port displays a half.
////////////////////////////////////////////////////////////////////////////////
WS2812B_0L_CY,
WS2812B_MS_REFRESH, dataPortId1, dataPortBit1, dataPortId2, dataPortBit2, GRB, TWO_PORT_SPLIT_BITBANG
template<avrLedStripPort dataPortId1, uint8_t dataPortBit1,avrLedStripPort dataPortId2, uint8_t dataPortBit2>
class
ws2812bs : public avrBitbangLedStrip<FAB_TVAR_
WS2812BS>
{
public:
};
////////////////////////////////////////////////////////////////////////////////
// WS2812B8S - Bitbang the pixels to eight ports in parel.
// The pixel array is split in 8. Each port displays a portion.
/////////