1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-22 15:46:14 +01:00

Neopixel change to 8,8,8

This commit is contained in:
Asbelos 2024-09-07 11:16:30 +01:00
parent d3d6cc97fb
commit e823db3d24
5 changed files with 99 additions and 61 deletions

View File

@ -405,16 +405,18 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
return;
}
if (params==2) { // <o [-]vpin count>
for (auto pix=vpin;pix<=vpin+p[1];pix++) IODevice::write(pix,setON);
for (auto pix=vpin;pix<vpin+p[1];pix++) IODevice::write(pix,setON);
return;
}
if (params==4 || params==5) { // <z [-]vpin r g b [count]>
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) { // <z [-]vpin r g b [count]>
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;pix<vpin+count;pix++) IODevice::writeAnalogue(pix,colour_RG,setON,colour_B);
return;
}
}

View File

@ -998,8 +998,14 @@ void RMFT2::loop2() {
}
break;
case OPCODE_NEOPIXEL: // OPCODE_NEOPIXEL,V(vpin),OPCODE_PAD,V(rgbcolour)
IODevice::writeAnalogue(operand,getOperand(1));
case OPCODE_NEOPIXEL:
// OPCODE_NEOPIXEL,V([-]vpin),OPCODE_PAD,V(colour_RG),OPCODE_PAD,V(colour_B),OPCODE_PAD,V(count)
{
VPIN vpin=operand>0?operand:-operand;
auto count=getOperand(3);
for (auto pix=vpin;pix<vpin+count;pix++)
IODevice::writeAnalogue(pix,getOperand(1),operand>0,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;
}

View File

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

View File

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

View File

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