From 530b77bbab4605a3fdfdec735ef01d0b56ef543c Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 3 Sep 2024 15:04:40 +0100 Subject: [PATCH] NEOPIXEL driver and macros --- EXRAILMacros.h | 4 +- IO_NeoPixel.h | 140 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 128 insertions(+), 16 deletions(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 347654e..4b2852a 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -71,6 +71,8 @@ //const byte TRACK_POWER_0=0, TRACK_POWER_OFF=0; //const byte TRACK_POWER_1=1, TRACK_POWER_ON=1; +// NEOPIXEL RGB generator +#define NeoRGB(red,green,blue) (((red & 0x1F)<<11) | ((green & 0x1F)<<6) | ((blue & 0x1F)<<1) ) // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" @@ -433,7 +435,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) { #undef DCCX_SIGNAL #define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) id | RMFT2::DCCX_SIGNAL_FLAG,redAspect,amberAspect,greenAspect, #undef NEOPIXEL_SIGNAL -#define NEOPIXEL_SIGNAL(sigid,redcolour,ambercolour,greencolour) id | RMFT2::NEOPIXEL_SIGNAL_FLAG,redcolour | NEOPIXEL_FLAG_ON, ambercolour | NEOPIXEL_FLAG_ON, greencolour | NEOPIXEL_FLAG_ON, +#define NEOPIXEL_SIGNAL(id,redcolour,ambercolour,greencolour) id | RMFT2::NEOPIXEL_SIGNAL_FLAG,redcolour | NEOPIXEL_FLAG_ON, ambercolour | NEOPIXEL_FLAG_ON, greencolour | NEOPIXEL_FLAG_ON, #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) id,0,0,0, diff --git a/IO_NeoPixel.h b/IO_NeoPixel.h index 25ed79d..64921cd 100644 --- a/IO_NeoPixel.h +++ b/IO_NeoPixel.h @@ -25,8 +25,8 @@ * To create NEOPIXEL devices, these are defined in myAutomation.h: * (Note the device driver is included by default) * -* HAL(NEOPIXEL,first vpin, number of pixels, i2c address) -* e.g. HAL(NEOPIXEL,1000,64,0x60) +* HAL(NEOPIXEL,first vpin, number of pixels,mode, i2c address) +* e.g. HAL(NEOPIXEL,1000,64,NEO_RGB,0x60) * This gives each pixel in the chain an individual vpin * The number of pixels must match the physical pixels in the chain. * @@ -44,25 +44,116 @@ #include "DIAG.h" #include "FSH.h" + +// The following macros to define the Neopixel String type +// have been copied from the Adafruit Seesaw Library under the +// terms of the GPL. +// Credit to: https://github.com/adafruit/Adafruit_Seesaw + +// The order of primary colors in the NeoPixel data stream can vary +// among device types, manufacturers and even different revisions of +// the same item. The third parameter to the seesaw_NeoPixel +// constructor encodes the per-pixel byte offsets of the red, green +// and blue primaries (plus white, if present) in the data stream -- +// the following #defines provide an easier-to-use named version for +// each permutation. e.g. NEO_GRB indicates a NeoPixel-compatible +// device expecting three bytes per pixel, with the first byte +// containing the green value, second containing red and third +// containing blue. The in-memory representation of a chain of +// NeoPixels is the same as the data-stream order; no re-ordering of +// bytes is required when issuing data to the chain. + +// Bits 5,4 of this value are the offset (0-3) from the first byte of +// a pixel to the location of the red color byte. Bits 3,2 are the +// green offset and 1,0 are the blue offset. If it is an RGBW-type +// device (supporting a white primary in addition to R,G,B), bits 7,6 +// are the offset to the white byte...otherwise, bits 7,6 are set to +// the same value as 5,4 (red) to indicate an RGB (not RGBW) device. +// i.e. binary representation: +// 0bWWRRGGBB for RGBW devices +// 0bRRRRGGBB for RGB + +// RGB NeoPixel permutations; white and red offsets are always same +// Offset: W R G B +#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) +#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) +#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) +#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) +#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) +#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) + +// RGBW NeoPixel permutations; all 4 offsets are distinct +// Offset: W R G B +#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) +#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) +#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) +#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) +#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) +#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) + +#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) +#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) +#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) +#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) +#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) +#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) + +#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) +#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) +#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) +#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) +#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) +#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) + +#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) +#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) +#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) +#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) +#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) +#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) + +// If 400 KHz support is enabled, the third parameter to the constructor +// requires a 16-bit value (in order to select 400 vs 800 KHz speed). +// If only 800 KHz is enabled (as is default on ATtiny), an 8-bit value +// is sufficient to encode pixel color order, saving some space. + +#define NEO_KHZ800 0x0000 // 800 KHz datastream +#define NEO_KHZ400 0x0100 // 400 KHz datastream + ///////////////////////////////////////////////////////////////////////////////////////////////////// /* * IODevice subclass for NeoPixel. */ + class NeoPixel : public IODevice { public: - - static void create(VPIN vpin, int nPins, I2CAddress i2cAddress) { - if (checkNoOverlap(vpin, nPins, i2cAddress)) new NeoPixel(vpin, nPins, i2cAddress); + static void create(VPIN vpin, int nPins, uint16_t mode, I2CAddress i2cAddress) { + if (checkNoOverlap(vpin, nPins, mode, i2cAddress)) new NeoPixel(vpin, nPins, mode, i2cAddress); } private: static const uint16_t NEOPIXEL_ON_FLAG=0x0001; + static const byte SEESAW_NEOPIXEL_BASE=0x0E; + static const byte SEESAW_NEOPIXEL_STATUS = 0x00; + static const byte SEESAW_NEOPIXEL_PIN = 0x01; + static const byte SEESAW_NEOPIXEL_SPEED = 0x02; + static const byte SEESAW_NEOPIXEL_BUF_LENGTH = 0x03; + static const byte SEESAW_NEOPIXEL_BUF=0x04; + static const byte SEESAW_NEOPIXEL_SHOW=0x05; + // Constructor - NeoPixel(VPIN firstVpin, int nPins, I2CAddress i2cAddress) { + NeoPixel(VPIN firstVpin, int nPins, uint16_t mode, I2CAddress i2cAddress) { _firstVpin = firstVpin; _I2CAddress = i2cAddress; + _brightness=2; // 0,1,2,3 + _redOffset=mode >> 4 & 0x03; + _greenOffset=mode >> 2 & 0x03; + _blueOffset=mode & 0x03; + if ((mode >>6 & 0x03) == _redOffset) _bytesPerPixel=3; + else _bytesPerPixel=4; // string has a white byte. + _kHz800=(mode & NEO_KHZ400)==0; addDevice(this); } @@ -75,7 +166,12 @@ private: return; } - // TODO - initialize the neopixel board + byte setbuffer[] = {SEESAW_NEOPIXEL_BASE, SEESAW_NEOPIXEL_BUF_LENGTH, + (byte)(_bytesPerPixel >> 8), (byte)(_bytesPerPixel & 0xFF)}; + I2CManager.write(_I2CAddress, setbuffer, sizeof(setbuffer)); + byte speedBuffer[]={SEESAW_NEOPIXEL_BASE, SEESAW_NEOPIXEL_SPEED,_kHz800}; + I2CManager.write(_I2CAddress, speedBuffer, sizeof(speedBuffer)); + pixelBuffer=(uint16_t *) calloc(_nPins,sizeof(uint16_t)); // all pixels off _display(); @@ -139,18 +235,32 @@ private: _deviceState = DEVSTATE_FAILED; } + void transmit(uint16_t pin) { - auto colour=pixelBuffer[pin]; + + byte buffer[8]={SEESAW_NEOPIXEL_BASE,SEESAW_NEOPIXEL_BUF,0x00,0x00,0x00,0x00,0x00}; + uint16_t offset= pin * _bytesPerPixel; + buffer[2]=(byte)(offset>>8); + buffer[3]=(byte)(offset &0xFF); + auto colour=pixelBuffer[pin]; if (colour & NEOPIXEL_ON_FLAG) { - // TODO convert 5,5,5 to RGB - } - else { - // TODO set black - } - // TODO transmit pixel to driver + buffer[_redOffset]=(colour>>11 & 0x1F) <<_brightness; + buffer[_greenOffset]=(colour>>6 & 0x1F) <<_brightness; + buffer[_blueOffset]=(colour>>1 & 0x1F) <<_brightness; + } // else leave buffer black + + // Transmit pixel to driver + I2CManager.write(_I2CAddress,buffer,4 +_bytesPerPixel); + buffer[1]=SEESAW_NEOPIXEL_SHOW; + I2CManager.write(_I2CAddress,buffer,2); } uint16_t* pixelBuffer = nullptr; - + byte _brightness; + byte _bytesPerPixel; + byte _redOffset; + byte _greenOffset; + byte _blueOffset; + bool _kHz800; }; #endif