From e823db3d244919f5529d22c71de8f4de834e21dd Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 7 Sep 2024 11:16:30 +0100 Subject: [PATCH] Neopixel change to 8,8,8 --- DCCEXParser.cpp | 18 ++++---- EXRAIL2.cpp | 20 ++++++--- EXRAIL2MacroReset.h | 3 +- EXRAILMacros.h | 13 +++--- IO_NeoPixel.h | 106 ++++++++++++++++++++++++++++---------------- 5 files changed, 99 insertions(+), 61 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 3ec5710..0d70895 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -405,16 +405,18 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) return; } if (params==2) { // - for (auto pix=vpin;pix<=vpin+p[1];pix++) IODevice::write(pix,setON); + for (auto pix=vpin;pix - uint16_t colourcode=((p[1] & 0x1F)<<11) | - ((p[2] & 0x1F)<<6) | - ((p[3] & 0x1F)<<1); - if (setON) colourcode |= 0x0001; - // driver treats count 0 as 1 - for (auto pix=vpin;pix<=vpin+p[4];pix++) IODevice::writeAnalogue(pix,colourcode,0,0); + if (params==4 || params==5) { // + auto count=p[4]?p[4]:1; + if (p[1]<0 || p[1]>0xFF) break; + if (p[2]<0 || p[2]>0xFF) break; + if (p[3]<0 || p[3]>0xFF) break; + // strange parameter mangling... see IO_NeoPixel.h NeoPixel::_writeAnalogue + int colour_RG=(p[1]<<8) | p[2]; + uint16_t colour_B=p[3]; + for (auto pix=vpin;pix0?operand:-operand; + auto count=getOperand(3); + for (auto pix=vpin;pix0,getOperand(2)); + } break; #ifndef IO_NO_HAL @@ -1199,11 +1205,11 @@ int16_t RMFT2::getSignalSlot(int16_t id) { } if (sigtype== NEOPIXEL_SIGNAL_FLAG) { - // redpin,amberpin,greenpin are the 3 rgbs - VPIN colour=redpin; - if (rag==SIGNAL_AMBER) colour=amberpin; - if (rag==SIGNAL_GREEN) colour=greenpin; - IODevice::writeAnalogue(sigid, colour); + // redpin,amberpin,greenpin are the 3 RG values but with no blue permitted. . (code limitation hack) + int colour_RG=redpin; + if (rag==SIGNAL_AMBER) colour_RG=amberpin; + if (rag==SIGNAL_GREEN) colour_RG=greenpin; + IODevice::writeAnalogue(sigid, colour_RG,true,0); return; } diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 73ff0d1..1f4afc8 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -272,8 +272,7 @@ #define LCN(msg) #define MESSAGE(msg) #define MOVETT(id,steps,activity) -#define NEOPIXEL(id,colour) -#define NEOPIXEL_OFF(id,colour) +#define NEOPIXEL(id,r,g,b,count...) #define NEOPIXEL_SIGNAL(sigid,redcolour,ambercolour,greencolour) #define ACON(eventid) #define ACOF(eventid) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 4b2852a..c0e1931 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -71,8 +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) ) +// NEOPIXEL RG generator for NEOPIXEL_SIGNAL +#define NeoRG(red,green) ((red & 0xff)<<8) | (green & 0xff) // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" @@ -435,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(id,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, ambercolour, greencolour, #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) id,0,0,0, @@ -558,8 +558,11 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define LCN(msg) PRINT(msg) #define MESSAGE(msg) PRINT(msg) #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), -#define NEOPIXEL(id,colour) OPCODE_NEOPIXEL,V(id),OPCODE_PAD,V(colour| NEOPIXEL_FLAG_ON), -#define NEOPIXEL_OFF(id,colour) OPCODE_NEOPIXEL,V(id),OPCODE_PAD,V(colour& ^NEOPIXEL_FLAG_ON), +#define NEOPIXEL(id,r,g,b,count...) OPCODE_NEOPIXEL,V(id),\ + OPCODE_PAD,V(((r & 0xff)<<8) | (g & 0xff)),\ + OPCODE_PAD,V((b & 0xff)),\ + OPCODE_PAD,V(#count[0]?(count+0):1), + #define NEOPIXEL_SIGNAL(sigid,redcolour,ambercolour,greencolour) #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), diff --git a/IO_NeoPixel.h b/IO_NeoPixel.h index 9212d6e..297e97f 100644 --- a/IO_NeoPixel.h +++ b/IO_NeoPixel.h @@ -133,7 +133,6 @@ public: } private: - static const uint16_t NEOPIXEL_ON_FLAG=0x0001; static const byte SEESAW_NEOPIXEL_BASE=0x0E; static const byte SEESAW_NEOPIXEL_STATUS = 0x00; @@ -152,20 +151,35 @@ private: _firstVpin = firstVpin; _nPins=nPins; _I2CAddress = i2cAddress; - _brightness=2; // TODO 0,1,2,3 + + // calculate the offsets into the seesaw buffer for each colour depending + // on the pixel strip type passed in mode. + _redOffset=4+(mode >> 4 & 0x03); _greenOffset=4+(mode >> 2 & 0x03); _blueOffset=4+(mode & 0x03); if (4+(mode >>6 & 0x03) == _redOffset) _bytesPerPixel=3; else _bytesPerPixel=4; // string has a white byte. + _kHz800=(mode & NEO_KHZ400)==0; _showPendimg=false; - // In dccex there are only 2 bytes per pixel - pixelBuffer=(uint16_t *) calloc(_nPins,sizeof(uint16_t)); // all pixels off + + // Each pixel requires 3 bytes RGB memory. + // Although the driver device can remember this, it cant do off/on without + // forgetting what the on colour was! + pixelBuffer=(RGB *) malloc(_nPins*sizeof(RGB)); + stateBuffer=(byte *) calloc((_nPins+7)/8,sizeof(byte)); // all pixels off + if (pixelBuffer==nullptr || stateBuffer==nullptr) { + DIAG(F("NeoPixel I2C:%s not enough RAM"), _I2CAddress.toString()); + return; + } + // preset all pins to white so a digital on/off will do something even if no colour set. + memset(pixelBuffer,0xFF,_nPins*sizeof(RGB)); addDevice(this); } void _begin() { + // Initialise Neopixel device I2CManager.begin(); if (!I2CManager.exists(_I2CAddress)) { @@ -198,45 +212,41 @@ private: _showPendimg=false; } - // read back pixel colour (rarely needed I suspect) - int _readAnalogue(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - auto pin=vpin-_firstVpin; - return pixelBuffer[pin]; - } - + // read back pixel on/off int _read(VPIN vpin) override { if (_deviceState == DEVSTATE_FAILED) return 0; - auto pin=vpin-_firstVpin; - return pixelBuffer[pin] & NEOPIXEL_ON_FLAG; + return isPixelOn(vpin-_firstVpin); } // Write digital value. Sets pixel on or off void _write(VPIN vpin, int value) override { if (_deviceState == DEVSTATE_FAILED) return; - auto pin=vpin-_firstVpin; + auto pixel=vpin-_firstVpin; if (value) { - if (pixelBuffer[pin] & NEOPIXEL_ON_FLAG) return; - pixelBuffer[pin] |= NEOPIXEL_ON_FLAG; + if (isPixelOn(pixel)) return; + setPixelOn(pixel); } else { // set off - if (!(pixelBuffer[pin] & NEOPIXEL_ON_FLAG)) return; - pixelBuffer[pin] &= (~NEOPIXEL_ON_FLAG); + if (!isPixelOn(pixel)) return; + setPixelOff(pixel); } - transmit(pin); + transmit(pixel); } - // Write analogue (integer) value - void _writeAnalogue(VPIN vpin, int colour, uint8_t ignore1, uint16_t ignore2) override { - (void) ignore1; - (void) ignore2; + // Write analogue value. + // The convoluted parameter mashing here is to allow passing the RGB and on/off + // information through the generic HAL _writeAnalog interface which was originally + // designed for servos and short integers + void _writeAnalogue(VPIN vpin, int colour_RG, uint8_t onoff, uint16_t colour_B) override { if (_deviceState == DEVSTATE_FAILED) return; - auto newColour=(uint16_t)colour; - auto pin=vpin-_firstVpin; - if (pixelBuffer[pin]==newColour) return; - pixelBuffer[pin]=newColour; - transmit(pin); + RGB newColour={(byte)((colour_RG>>8) & 0xFF), (byte)(colour_RG & 0xFF), (byte)(colour_B & 0xFF)}; + auto pixel=vpin-_firstVpin; + if (pixelBuffer[pixel]==newColour && isPixelOn(pixel)==(bool)onoff) return; // no change + + if (onoff) setPixelOn(pixel); else setPixelOff(pixel); + pixelBuffer[pixel]=newColour; + transmit(pixel); } // Display device information and status. @@ -247,6 +257,12 @@ private: _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("")); } + + + bool isPixelOn(int16_t pixel) {return stateBuffer[pixel/8] & (0x80>>(pixel%8));} + void setPixelOn(int16_t pixel) {stateBuffer[pixel/8] |= (0x80>>(pixel%8));} + void setPixelOff(int16_t pixel) {stateBuffer[pixel/8] &= ~(0x80>>(pixel%8));} + // Helper function for error handling void reportError(uint8_t status, bool fail=true) { DIAG(F("NeoPixel I2C:%s Error:%d (%S)"), _I2CAddress.toString(), @@ -256,30 +272,42 @@ private: } - void transmit(uint16_t pin, bool show=true) { + void transmit(uint16_t pixel, bool show=true) { byte buffer[]={SEESAW_NEOPIXEL_BASE,SEESAW_NEOPIXEL_BUF,0x00,0x00,0x00,0x00,0x00}; - uint16_t offset= pin * _bytesPerPixel; + uint16_t offset= pixel * _bytesPerPixel; buffer[2]=(byte)(offset>>8); buffer[3]=(byte)(offset & 0xFF); - auto colour=pixelBuffer[pin]; - if (colour & NEOPIXEL_ON_FLAG) { - buffer[_redOffset]=(colour>>11 & 0x1F) <<_brightness; - buffer[_greenOffset]=(colour>>6 & 0x1F) <<_brightness; - buffer[_blueOffset]=(colour>>1 & 0x1F) <<_brightness; - } // else leave buffer black + + if (isPixelOn(pixel)) { + auto colour=pixelBuffer[pixel]; + buffer[_redOffset]=colour.red; + buffer[_greenOffset]=colour.green; + buffer[_blueOffset]=colour.blue; + } // else leave buffer black (in buffer preset to zeros above) // Transmit pixel to driver I2CManager.write(_I2CAddress,buffer,4 +_bytesPerPixel); _showPendimg=true; } - uint16_t* pixelBuffer = nullptr; - byte _brightness; + struct RGB { + byte red; + byte green; + byte blue; + bool operator==(const RGB& other) const { + return red == other.red && green == other.green && blue == other.blue; + } + }; + + RGB* pixelBuffer = nullptr; + byte* stateBuffer = nullptr; // 1 bit per pixel + bool _showPendimg; + + // mapping of RGB onto pixel buffer for seesaw. byte _bytesPerPixel; byte _redOffset; byte _greenOffset; byte _blueOffset; - bool _showPendimg; bool _kHz800; };