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

Compare commits

...

2 Commits

Author SHA1 Message Date
Asbelos
dd16e0da97 Notes 2024-09-07 13:00:26 +01:00
Asbelos
e823db3d24 Neopixel change to 8,8,8 2024-09-07 11:16:30 +01:00
6 changed files with 160 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;
};

61
Release_Notes/NeoPixel.md Normal file
View File

@ -0,0 +1,61 @@
NeoPixel support
The IO_NeoPixel.h driver supports the adafruit neopixel seesaw board. It turns each pixel into an individual VPIN which can be given a colour and turned on or off using the new <o> command or the NEOPIXEL Exrail macro. Exrail SIGNALS can also drive a single pixel signal or multiple separate pixels.
1. Defining the hardware driver:
Add a driver definition in myAutomation.h for each adafruit I2C driver.
HAL(neoPixel, firstVpin, numberOfPixels [, mode [, i2caddress])
Where mode is selected from the various pixel string types which have varying
colour order or refresh frequency. For MOST strings this mode will be NEO_GRB but for others refer to the comments in IO_NeoPixel.h
If omitted the node and i2caddress default to NEO_GRB, 0x60.
HAL(NeoPixel,1000,20)
This is a NeoPixel driver defaulting to I2C aqddress 0x60 for a GRB pixel string. Pixels are given vpin numbers from 1000 to 1019.
HAL(NeoPixel,1020,20,NEO_GRB,0x61)
This is a NeoPixel driver on i2c address 0x61
2. Setting pixels from the < > commands.
By default, each pixel in the string is created as white but switched off.
Each pixel has a vpin starting from the first vpin in the HAL definitions.
<o vpin> switches pixel on (same as <z vpin>) e.g. <o 1005>
<o -vpin> switches pixel off (same as <z -vpin>) e.g. <o -1003>
(the z commands work on pixels the same as other gpio pins.)
<o [-]vpin count> switches on/off count pixels starting at vpin. e.g <o 1000 5>
Note: it IS acceptable to switch across 2 strings of pixels if they are contiguous vpin ranges. It is also interesting that this command doesnt care if the vpins are NeoPixel or any other type, so it can be used to switch a range of other pin types.
<o [-]vpin red green blue [count]> sets the colour and on/off status of a pin or pins. Each colour is 0..255 e.g. <o 1005 255 255 0> sets pin 1005 to bright yellow and ON, <0 -1006 0 0 255 10> sets pins 1006 to 1015 (10 pins) to bright blue but OFF.
Note: If you set a pin to a colour, you can turn it on and off without having to reset the colour every time. This is something the adafruit seesaw library can't do and is just one of several reasons why we dont use it.
3. Setting pixels from EXRAIL
The new NEOPIXEL macro provides the same functionality as the <o [-]vpin red green blue [count]> command above.
NEOPIXEL([-]vpin, red, green, blue [,count])
Setting pixels on or off (without colour change) can be done with SET/RESET [currently there is no set range facility but that may be added as a general exrail thing... watch this space]
Because the pixels obey set/reset, the BLINK command can also be used to control blinking a pixel.
4. EXRAIL pixel signals.
There are two types possible, a mast with separate fixed colour pixels for each aspect, or a mast with one multiple colour pixel for all aspects.
For separate pixels, the colours should be established at startup and a normal SIGNALH macro used.
AUTOSTART
SIGNALH(1010,1011,1012)
NEOPIXEL(1010,255,0,0)
NEOPIXEL(1011,128,128,0)
NEOPIXEL(1012,0,255,0)
RED(1010) // force signal state otherwise all 3 lights will be on
DONE
For signals with 1 pixel, the NEOPIXEL_SIGNAL macro will create a signal
NEOPIXEL_SIGNAL(vpin,redfx,amberfx,greenfx)
*** This is experimental and may change****
In order to fit the existing signal code, the fx colours above are restricted to the red and green pixel values (ie no blue channel)
The fx values above can be created by the NeoRG macro so a bright red would be NeoRG(255,0) bright green Ng(0,255) and amber something like NeoRG(128,128)