Porting latest BusHub75Matrix from MoonModules - Mostly authored by Frank - MIT licence granted for this copy
This commit is contained in:
@@ -23,6 +23,27 @@
|
||||
|
||||
extern bool cctICused;
|
||||
|
||||
void setBitInArray(uint8_t* byteArray, size_t position, bool value) { // set bit - with error handling for nullptr
|
||||
//if (byteArray == nullptr) return;
|
||||
size_t byteIndex = position / 8;
|
||||
unsigned bitIndex = position % 8;
|
||||
if (value)
|
||||
byteArray[byteIndex] |= (1 << bitIndex);
|
||||
else
|
||||
byteArray[byteIndex] &= ~(1 << bitIndex);
|
||||
}
|
||||
|
||||
size_t getBitArrayBytes(size_t num_bits) { // number of bytes needed for an array with num_bits bits
|
||||
return (num_bits + 7) / 8;
|
||||
}
|
||||
|
||||
void setBitArray(uint8_t* byteArray, size_t numBits, bool value) { // set all bits to same value
|
||||
if (byteArray == nullptr) return;
|
||||
size_t len = getBitArrayBytes(numBits);
|
||||
if (value) memset(byteArray, 0xFF, len);
|
||||
else memset(byteArray, 0x00, len);
|
||||
}
|
||||
|
||||
//colors.cpp
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
|
||||
@@ -790,14 +811,45 @@ void BusNetwork::cleanup(void) {
|
||||
|
||||
BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
|
||||
|
||||
mxconfig.double_buff = true; // <------------- Turn on double buffer
|
||||
_valid = false;
|
||||
mxconfig.double_buff = false; // default to off, known to cause issue with some effects but needs more memory
|
||||
// mxconfig.double_buff = true; // <------------- Turn on double buffer
|
||||
// mxconfig.driver = HUB75_I2S_CFG::ICN2038S; // experimental - use specific shift register driver
|
||||
//mxconfig.latch_blanking = 3;
|
||||
// mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; // experimental - 5MHZ should be enugh, but colours looks slightly better at 10MHz
|
||||
//mxconfig.min_refresh_rate = 90;
|
||||
//mxconfig.min_refresh_rate = 120;
|
||||
|
||||
switch(bc.type) {
|
||||
case TYPE_HUB75MATRIX_HS:
|
||||
fourScanPanel = nullptr;
|
||||
|
||||
if(bc.type == TYPE_HUB75MATRIX_HS) {
|
||||
mxconfig.mx_width = bc.pins[0];
|
||||
mxconfig.mx_height = bc.pins[1];
|
||||
break;
|
||||
|
||||
}
|
||||
else if(bc.type == TYPE_HUB75MATRIX_QS) {
|
||||
fourScanPanel = new VirtualMatrixPanel((*display), 1, 1, bc.pins[0], bc.pins[1]);
|
||||
fourScanPanel->setRotation(0);
|
||||
switch(bc.pins[1]) {
|
||||
case 16:
|
||||
fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_16PX_HIGH);
|
||||
break;
|
||||
case 32:
|
||||
fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_32PX_HIGH);
|
||||
break;
|
||||
case 64:
|
||||
fourScanPanel->setPhysicalPanelScanRate(FOUR_SCAN_64PX_HIGH);
|
||||
break;
|
||||
default:
|
||||
DEBUG_PRINTLN("Unsupported height");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTLN("Unknown type");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[2], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory
|
||||
|
||||
@@ -882,29 +934,159 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
|
||||
// let's adjust default brightness
|
||||
display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100%
|
||||
|
||||
delay(24); // experimental
|
||||
// Allocate memory and start DMA display
|
||||
if( not display->begin() ) {
|
||||
DEBUG_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTLN("MatrixPanel_I2S_DMA begin ok");
|
||||
delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle
|
||||
_valid = true;
|
||||
display->clearScreen(); // initially clear the screen buffer
|
||||
DEBUG_PRINTLN("MatrixPanel_I2S_DMA clear ok");
|
||||
|
||||
if (_ledBuffer) free(_ledBuffer); // should not happen
|
||||
if (_ledsDirty) free(_ledsDirty); // should not happen
|
||||
DEBUG_PRINTLN("MatrixPanel_I2S_DMA allocate memory");
|
||||
_ledsDirty = (byte*) malloc(getBitArrayBytes(_len)); // create LEDs dirty bits
|
||||
DEBUG_PRINTLN("MatrixPanel_I2S_DMA allocate memory ok");
|
||||
|
||||
if (_ledsDirty == nullptr) {
|
||||
display->stopDMAoutput();
|
||||
delete display; display = nullptr;
|
||||
_valid = false;
|
||||
DEBUG_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for dirty bits!"));
|
||||
return; // fail is we cannot get memory for the buffer
|
||||
}
|
||||
setBitArray(_ledsDirty, _len, false); // reset dirty bits
|
||||
|
||||
if (mxconfig.double_buff == false) {
|
||||
_ledBuffer = (CRGB*) calloc(_len, sizeof(CRGB)); // create LEDs buffer (initialized to BLACK)
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_PRINTLN("MatrixPanel_I2S_DMA started");
|
||||
|
||||
if (_valid) {
|
||||
_panelWidth = fourScanPanel ? fourScanPanel->width() : display->width(); // cache width - it will never change
|
||||
}
|
||||
|
||||
DEBUG_PRINT(F("MatrixPanel_I2S_DMA "));
|
||||
DEBUG_PRINTF("%sstarted, width=%u, %u pixels.\n", _valid? "":"not ", _panelWidth, _len);
|
||||
|
||||
if (mxconfig.double_buff == true) DEBUG_PRINTLN(F("MatrixPanel_I2S_DMA driver native double-buffering enabled."));
|
||||
if (_ledBuffer != nullptr) DEBUG_PRINTLN(F("MatrixPanel_I2S_DMA LEDS buffer enabled."));
|
||||
if (_ledsDirty != nullptr) DEBUG_PRINTLN(F("MatrixPanel_I2S_DMA LEDS dirty bit optimization enabled."));
|
||||
if ((_ledBuffer != nullptr) || (_ledsDirty != nullptr)) {
|
||||
DEBUG_PRINT(F("MatrixPanel_I2S_DMA LEDS buffer uses "));
|
||||
DEBUG_PRINT((_ledBuffer? _len*sizeof(CRGB) :0) + (_ledsDirty? getBitArrayBytes(_len) :0));
|
||||
DEBUG_PRINTLN(F(" bytes."));
|
||||
}
|
||||
}
|
||||
|
||||
void BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
r = R(c);
|
||||
g = G(c);
|
||||
b = B(c);
|
||||
x = pix % display->width();
|
||||
y = floor(pix / display->width());
|
||||
display->drawPixelRGB888(x, y, r, g, b);
|
||||
void __attribute__((hot)) BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (!_valid || pix >= _len) return;
|
||||
// if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
|
||||
if (_ledBuffer) {
|
||||
CRGB fastled_col = CRGB(c);
|
||||
if (_ledBuffer[pix] != fastled_col) {
|
||||
_ledBuffer[pix] = fastled_col;
|
||||
setBitInArray(_ledsDirty, pix, true); // flag pixel as "dirty"
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((c == BLACK) && (getBitFromArray(_ledsDirty, pix) == false)) return; // ignore black if pixel is already black
|
||||
setBitInArray(_ledsDirty, pix, c != BLACK); // dirty = true means "color is not BLACK"
|
||||
|
||||
#ifndef NO_CIE1931
|
||||
c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction
|
||||
#endif
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
|
||||
if(fourScanPanel != nullptr) {
|
||||
int width = _panelWidth;
|
||||
int x = pix % width;
|
||||
int y = pix / width;
|
||||
fourScanPanel->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
} else {
|
||||
int width = _panelWidth;
|
||||
int x = pix % width;
|
||||
int y = pix / width;
|
||||
display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t BusHub75Matrix::getPixelColor(uint16_t pix) const {
|
||||
if (!_valid || pix >= _len) return BLACK;
|
||||
if (_ledBuffer)
|
||||
return uint32_t(_ledBuffer[pix].scale8(_bri)) & 0x00FFFFFF; // scale8() is needed to mimic NeoPixelBus, which returns scaled-down colours
|
||||
else
|
||||
return getBitFromArray(_ledsDirty, pix) ? DARKGREY: BLACK; // just a hack - we only know if the pixel is black or not
|
||||
}
|
||||
|
||||
void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) {
|
||||
this->display->setBrightness(b);
|
||||
_bri = b;
|
||||
if (_bri > 238) _bri=238;
|
||||
display->setBrightness(_bri);
|
||||
}
|
||||
|
||||
void __attribute__((hot)) BusHub75Matrix::show(void) {
|
||||
if (!_valid) return;
|
||||
display->setBrightness(_bri);
|
||||
|
||||
if (_ledBuffer) {
|
||||
// write out buffered LEDs
|
||||
bool isFourScan = (fourScanPanel != nullptr);
|
||||
//if (isFourScan) fourScanPanel->setRotation(0);
|
||||
unsigned height = isFourScan ? fourScanPanel->height() : display->height();
|
||||
unsigned width = _panelWidth;
|
||||
|
||||
//while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker.
|
||||
|
||||
size_t pix = 0; // running pixel index
|
||||
for (int y=0; y<height; y++) for (int x=0; x<width; x++) {
|
||||
if (getBitFromArray(_ledsDirty, pix) == true) { // only repaint the "dirty" pixels
|
||||
uint32_t c = uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; // get RGB color, removing FastLED "alpha" component
|
||||
#ifndef NO_CIE1931
|
||||
c = unGamma24(c); // to use the driver linear brightness feature, we first need to undo WLED gamma correction
|
||||
#endif
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
if (isFourScan) fourScanPanel->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
else display->drawPixelRGB888(int16_t(x), int16_t(y), r, g, b);
|
||||
}
|
||||
pix ++;
|
||||
}
|
||||
setBitArray(_ledsDirty, _len, false); // buffer shown - reset all dirty bits
|
||||
}
|
||||
|
||||
if(mxconfig.double_buff) {
|
||||
display->flipDMABuffer(); // Show the back buffer, set current output buffer to the back (i.e. no longer being sent to LED panels)
|
||||
// while(!previousBufferFree) delay(1); // experimental - Wait before we allow any writing to the buffer. Stop flicker.
|
||||
display->clearScreen(); // Now clear the back-buffer
|
||||
setBitArray(_ledsDirty, _len, false); // dislay buffer is blank - reset all dirty bits
|
||||
}
|
||||
}
|
||||
|
||||
void BusHub75Matrix::cleanup() {
|
||||
if (display && _valid) display->stopDMAoutput(); // terminate DMA driver (display goes black)
|
||||
_valid = false;
|
||||
_panelWidth = 0;
|
||||
deallocatePins();
|
||||
DEBUG_PRINTLN("HUB75 output ended.");
|
||||
|
||||
//if (fourScanPanel != nullptr) delete fourScanPanel; // warning: deleting object of polymorphic class type 'VirtualMatrixPanel' which has non-virtual destructor might cause undefined behavior
|
||||
delete display;
|
||||
display = nullptr;
|
||||
fourScanPanel = nullptr;
|
||||
if (_ledBuffer != nullptr) free(_ledBuffer); _ledBuffer = nullptr;
|
||||
if (_ledsDirty != nullptr) free(_ledsDirty); _ledsDirty = nullptr;
|
||||
}
|
||||
|
||||
void BusHub75Matrix::deallocatePins() {
|
||||
@@ -931,10 +1113,16 @@ void BusHub75Matrix::deallocatePins() {
|
||||
std::vector<LEDType> BusHub75Matrix::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_HUB75MATRIX_HS, "H", PSTR("HUB75 (Half Scan)")},
|
||||
// {TYPE_HUB75MATRIX_QS, "H", PSTR("HUB75 (Quarter Scan)")},
|
||||
{TYPE_HUB75MATRIX_QS, "H", PSTR("HUB75 (Quarter Scan)")},
|
||||
};
|
||||
}
|
||||
|
||||
uint8_t BusHub75Matrix::getPins(uint8_t* pinArray) const {
|
||||
pinArray[0] = mxconfig.mx_width;
|
||||
pinArray[1] = mxconfig.mx_height;
|
||||
pinArray[2] = mxconfig.chain_length;
|
||||
return 3;
|
||||
}
|
||||
|
||||
#endif
|
||||
// ***************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user