diff --git a/lib/lib_display/LedControl/src/LedMatrix.cpp b/lib/lib_display/LedControl/src/LedMatrix.cpp new file mode 100644 index 000000000..427ae8c52 --- /dev/null +++ b/lib/lib_display/LedControl/src/LedMatrix.cpp @@ -0,0 +1,186 @@ +/* + * LedMatrix.h - Extends the Library LedControl for multiple LED dot matrix modules, based on MAX7219/MAX7221 + * Copyright (c) 2021 Michael Beuss + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * This permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "LedMatrix.h" + +// public +LedMatrix::LedMatrix(int dataPin, int clkPin, int csPin, unsigned int colums, unsigned int rows) +{ + if (colums * rows > MATRIX_MAX_MODULES) + { + // dimension exeeds maximum buffer size + if (colums >= MATRIX_MAX_MODULES) + { + colums = MATRIX_MAX_MODULES; + rows = 1; + } + else + { + rows = MATRIX_MAX_MODULES / colums; + } + } + + modulesPerRow = colums; + modulesPerCol = rows; + width = colums * 8; + height = rows * 8; + modules = colums * rows; + moduleOrientation = ORIENTATION_UPSIDE_DOWN; + ledControl = new LedControl(dataPin, clkPin, csPin, modules); + shutdown(false); // false: on, true: off + clear(); + setIntensity(7); +} + +bool LedMatrix::clear(void) +{ + for (int i = 0; i < MATRIX_BUFFER_SIZE; i++) + { + buffer[i] = 0; + } + for (int addr = 0; addr < modules; addr++) + { + ledControl->clearDisplay(addr); + } + return true; +} + +bool LedMatrix::setIntensity(byte dim) +{ + for (int addr = 0; addr < modules; addr++) + { + ledControl->setIntensity(addr, dim); // 1..15 + } + return true; +} + +bool LedMatrix::setOrientation(ModuleOrientation orientation) +{ + moduleOrientation = orientation; + return true; +} + +bool LedMatrix::setPixel(int x, int y, bool on) +{ + if (x >= width || y >= height) + return false; + + int modul_col = x / 8; // x pos divided by 8 is the index of the modul to the right + int buffer_pos = modul_col + y * width / 8; + byte buffer_byte = 8 >> (x % 8); + if (on) + { + buffer[buffer_pos] |= buffer_byte; // set bit + } + else + { + buffer[buffer_pos] &= ~buffer_byte; // reset bit + } + refreshByteOfBuffer(buffer_pos); + return true; +} + +void LedMatrix::test() +{ + const static byte testMatrix[] PROGMEM = { + B00000010, B00111100, B00111100, B00001000, + B00000110, B01000010, B01000010, B00010000, + B00001010, B00000010, B00000010, B00100000, + B00000010, B00000100, B00001100, B01000100, + B00000010, B00011000, B00000010, B01111110, + B00000010, B00100000, B01000010, B00000100, + B00000000, B11111110, B00111100, B00000100, + B00000000, B00000000, B00000000, B00000000, + }; + for (int i = 0; i < 32; i++) + { + buffer[i] = testMatrix[i]; + } + refresh(); +} + +// private +bool LedMatrix::shutdown(bool b) +{ + for (int addr = 0; addr < modules; addr++) + { + ledControl->shutdown(addr, b); // b: false: on, true: off + } + return true; +} + +void LedMatrix::refresh() +{ + for (int i = 0; i < modulesPerRow * height; i++) + { + refreshByteOfBuffer(i); + } +} + +void LedMatrix::refreshByteOfBuffer(int i) +{ + int line = i / modulesPerRow; + int addr = line / 8 + i % modulesPerRow; + byte b = buffer[i]; + if (moduleOrientation == ORIENTATION_NORMAL || moduleOrientation == ORIENTATION_UPSIDE_DOWN) + { + int rowOfAddr = 0; + if (moduleOrientation == ORIENTATION_NORMAL) + { + rowOfAddr = line % 8; // ORIENTATION_NORMAL + } + else + { + rowOfAddr = 7 - line % 8; // ORIENTATION_UPSIDE_DOWN + b = revereBitorder(b); + } + ledControl->setRow(addr, rowOfAddr, b); + } + else + { + // ORIENTATION_TURN_RIGHT or ORIENTATION_TURN_LEFT + int colOfAddr = 0; + if (moduleOrientation == ORIENTATION_TURN_LEFT) + { + colOfAddr = line % 8; // ORIENTATION_TURN_LEFT + } + else + { + colOfAddr = 7 - line % 8; // ORIENTATION_TURN_RIGHT + b = revereBitorder(b); + } + ledControl->setColumn(addr, colOfAddr, b); + } +} + +byte LedMatrix::revereBitorder (byte b) +{ + const static byte lookup[] PROGMEM = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, + }; + return (lookup[b & 0b1111] << 4) | lookup[b >> 4]; +} diff --git a/lib/lib_display/LedControl/src/LedMatrix.h b/lib/lib_display/LedControl/src/LedMatrix.h new file mode 100644 index 000000000..f439e5d05 --- /dev/null +++ b/lib/lib_display/LedControl/src/LedMatrix.h @@ -0,0 +1,124 @@ +/* + * LedMatrix.h - Extends the Library LedControl for multiple LED dot matrix modules, based on MAX7219/MAX7221 + * Copyright (c) 2021 Michael Beuss + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * This permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LedMatrix_h +#define LedMatrix_h + +#include + +#define MATRIX_MAX_MODULES 32 +#define MATRIX_BUFFER_SIZE MATRIX_MAX_MODULES * 8 // 8 bytes per modul. One byte represents 8 LEDs. + + +/** + * @brief LedMatric controls multiple 8x8 LED dot matrx modules. + * All modules in rows and clolums together build a common matrix. + * + */ +class LedMatrix +{ + public: + enum ModuleOrientation // orientation of 8x8 LED dot matrix mdules (first module is top left) + { + ORIENTATION_NORMAL, // first pixel is on top left, no turn is necessary + ORIENTATION_TURN_RIGHT, // first pixel is on bottom left, for correction it has to turn 90° right + ORIENTATION_UPSIDE_DOWN, // first pixel is on bottom right, fpr correction it has to turn 180° + ORIENTATION_TURN_LEFT, // first pixel is on top right, for correction is has to turn 90° left. + }; + + private: + unsigned int modulesPerRow; + unsigned int modulesPerCol; + unsigned int width; // matrix width [pixel] + unsigned int height; // matrix height [pixel] + unsigned int modules; // number of 8x8 mudules + ModuleOrientation moduleOrientation; + byte buffer[MATRIX_BUFFER_SIZE]; + LedControl* ledControl; + + + public: + /** + * @brief Construct a new Led Matrix object + * + * @param colums of 8x8 LED dot matrix modules + * @param rows of 8x8 LED dot matrix modules + */ + LedMatrix(int dataPin, int clkPin, int csPin, unsigned int colums, unsigned int rows); + + /** + * @brief Set all LEDs off. + * + */ + bool clear(void); + + /** + * @brief Set the brightness of the display + * + * @param dim 0..15 + */ + bool setIntensity(byte dim); + + /** + * @brief Switches the display on or off + * + * @param on true: on false: off + */ + void power( bool on ); + + /** + * @brief Set the orientation of the 8x8 LED dot matrix module + * + * @param orientation + */ + bool setOrientation(ModuleOrientation orientation); + + /** + * @brief Set the Pixel object + * + * @param x horizontal position from left + * @param y vertical position from top + * @param on true: on, false: off + */ + bool setPixel( int x, int y, bool on); + void test(); + + private: + bool shutdown(bool b); + + /** + * @brief sends the changed content of the buffer to the display + * + */ + void refresh(); + + void refreshByteOfBuffer( int i); + + byte revereBitorder (byte b); + +}; + +#endif //LedMatrix_h \ No newline at end of file diff --git a/tasmota/xdsp_19_max7219_matrix.ino b/tasmota/xdsp_19_max7219_matrix.ino index 97c20f00b..da3177f59 100644 --- a/tasmota/xdsp_19_max7219_matrix.ino +++ b/tasmota/xdsp_19_max7219_matrix.ino @@ -133,70 +133,45 @@ and setting it to 3 alternates between time and date. #define XDSP_19 19 #define CMD_MAX_LEN 55 +#include -#include - -LedControl *max7219_Matrix; +LedMatrix *max7219_Matrix; bool max2791Matrix_init_done = false; byte modulesPerRow = 4; - +byte modulesPerCol = 1; bool MAX7291Matrix_init(void) { if (!PinUsed(GPIO_MAX7219DIN) || !PinUsed(GPIO_MAX7219CLK) || !PinUsed(GPIO_MAX7219CS)) { - AddLog(LOG_LEVEL_INFO, PSTR("DSP: MAX7291Matrix_init GPIO pins missing DIN, CLK or CS")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: MAX7291Matrix_init GPIO pins missing DIN:%d, CLK:%d, CS:%d"), Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS) ); return false; // ensure necessariy pins are configurated } Settings->display_model = XDSP_19; + if (Settings->display_width) // [pixel] + { + modulesPerRow = (Settings->display_width - 1) / 8 + 1; + } + Settings->display_width = 8 * modulesPerRow; Settings->display_cols[0] = Settings->display_width; - Settings->display_height = 1; + if (Settings->display_height) // [pixel] + { + modulesPerCol = (Settings->display_height - 1) / 8 + 1; + } + Settings->display_height = 8 * modulesPerCol; Settings->display_rows = Settings->display_height; - max7219_Matrix = new LedControl(Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS), modulesPerRow); - MAX7291Matrix_On(true); - MAX7291Matrix_Clear(); - MAX7291Matrix_Dim(); - AddLog(LOG_LEVEL_INFO, PSTR("DSP: MAX7291Matrix_init")); + Settings->display_cols[1] = Settings->display_height; + max7219_Matrix = new LedMatrix(Pin(GPIO_MAX7219DIN), Pin(GPIO_MAX7219CLK), Pin(GPIO_MAX7219CS), modulesPerRow, modulesPerCol); + int intensity = GetDisplayDimmer16(); // 0..15 + max7219_Matrix->setIntensity(intensity); + int orientation = Settings->display_rotate; + max7219_Matrix->setOrientation((LedMatrix::ModuleOrientation)orientation ); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: MAX7291Matrix_init %dx%d modules, orientation: %d, intensity: %d"), modulesPerRow , modulesPerCol, orientation, intensity); max2791Matrix_init_done = true; - max7219_Matrix->setLed(0, 3, 3, true); - AddLog(LOG_LEVEL_INFO, PSTR("DSP: setLed 3,3")); - return true; -} - -bool MAX7291Matrix_On( bool on ) -{ - for (int addr = 0; addr < modulesPerRow; addr++) - { - max7219_Matrix->shutdown (addr, !on); // false: on, true: off - } - AddLog(LOG_LEVEL_INFO, PSTR("MTX: On %d"), on); - return true; - -} - -bool MAX7291Matrix_Dim(void) -{ - int dim = GetDisplayDimmer16(); - for (int addr = 0; addr < modulesPerRow; addr++) - { - max7219_Matrix->setIntensity(addr, dim); // 1..15 - } - AddLog(LOG_LEVEL_INFO, PSTR("DSP: Dim %d"), dim); - return true; -} - -// /*********************************************************************************************\ -// * Clears the display -// * Command: DisplayClear -// \*********************************************************************************************/ -bool MAX7291Matrix_Clear(void) -{ - for(int addr=0; addrclearDisplay(addr); - } - AddLog(LOG_LEVEL_INFO, PSTR("DSP: Clear")); + max7219_Matrix->test(); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: display test")); return true; } @@ -208,17 +183,18 @@ bool MAX7291Matrix_Text() { char sString[CMD_MAX_LEN + 1]; subStr(sString, XdrvMailbox.data, ",", 1); - MAX7291Matrix_Clear; + max7219_Matrix->clear(); AddLog(LOG_LEVEL_DEBUG, PSTR("MTX: sString %s"), sString); // test uint8_t length = strlen(sString); - //if(length > modulesPerRow) - length = modulesPerRow; + //if(length > modulesPerRow) + length = modulesPerRow; - for(int addr = 0; addrsetLed(addr, 0, 1, true); - AddLog(LOG_LEVEL_DEBUG, PSTR("MTX: setLed() %d, 0, 1)"), addr); + for (int addr = 0; addr < length; addr++) + { + max7219_Matrix->setPixel(addr, addr, true); + AddLog(LOG_LEVEL_DEBUG, PSTR("MTX: setPixel() %d, %d)"), addr, addr); } return true; @@ -226,50 +202,48 @@ bool MAX7291Matrix_Text() bool Xdsp19(uint8_t function) { - bool result = false; - + bool result = false; if (FUNC_DISPLAY_INIT_DRIVER == function) { result = MAX7291Matrix_init(); } - else{ - //if (max2791Matrix_init_done && (XDSP_19 == Settings->display_model)) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_CLEAR: - result = MAX7291Matrix_Clear(); - break; - case FUNC_DISPLAY_DIM: - result = MAX7291Matrix_Dim(); - break; - case FUNC_DISPLAY_SEVENSEG_TEXT: - case FUNC_DISPLAY_SEVENSEG_TEXTNC: - case FUNC_DISPLAY_NUMBER: - case FUNC_DISPLAY_NUMBERNC: - case FUNC_DISPLAY_FLOAT: - case FUNC_DISPLAY_FLOATNC: - case FUNC_DISPLAY_RAW: - case FUNC_DISPLAY_LEVEL: - case FUNC_DISPLAY_SCROLLTEXT: - case FUNC_DISPLAY_CLOCK: - case FUNC_DISPLAY_DRAW_STRING: - result = MAX7291Matrix_Text(); - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: - case FUNC_DISPLAY_EVERY_SECOND: - // ignore - return false; - default: - result = false; + else if (max7219_Matrix && (XDSP_19 == Settings->display_model)) + { + switch (function) + { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_CLEAR: + result = max7219_Matrix->clear(); + break; + case FUNC_DISPLAY_DIM: + result = max7219_Matrix->setIntensity(GetDisplayDimmer16()); + break; + case FUNC_DISPLAY_SEVENSEG_TEXT: + case FUNC_DISPLAY_SEVENSEG_TEXTNC: + case FUNC_DISPLAY_NUMBER: + case FUNC_DISPLAY_NUMBERNC: + case FUNC_DISPLAY_FLOAT: + case FUNC_DISPLAY_FLOATNC: + case FUNC_DISPLAY_RAW: + case FUNC_DISPLAY_LEVEL: + case FUNC_DISPLAY_SCROLLTEXT: + case FUNC_DISPLAY_CLOCK: + case FUNC_DISPLAY_DRAW_STRING: + result = MAX7291Matrix_Text(); + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: + case FUNC_DISPLAY_EVERY_SECOND: + // ignore + return false; + default: + result = false; } } return result; } - - #endif // USE_DISPLAY_MAX7219_MATRIX #endif // USE_DISPLAY