/** * @file VirtualMatrixPanel.ino * @brief Example of using the VirtualMatrixPanel_T template class. * * The VirtualMatrixPanel_T class can be used for two purposes: * * 1) Create a much larger display out of a number of physical LED panels * chained in a Serpentine or Zig-Zag manner; * * 2) Provide a way to deal with weird individual physical panels that do not have a * simple linear X, Y pixel mapping. For example, 1/4 scan panels, or outdoor panels. * * 1) and 2) can be combined and utilsied together. * * There are THREE examples contained within this library. What example gets built depends * on the value of the "#define EXAMPLE_NUMBER X" value. Where X = Example number. * * Example 1: STANDARD 1/2 Scan (i.e. 1/16, 1/32) LED matrix panels, 64x32 pixels each, * in a grid of 2x2 panels, chained in a Serpentine manner. * * Example 2: Non-Standard 1/4 Scan (i.e. Four-Scan 1/8) outdoor LED matrix panels, 64x32 pixels each, * in a grid of 2x2 panels, chained in a Serpentine manner. * * Example 3: A single non-standard 1/4 Scan (i.e. Four-Scan 1/8) outdoor LED matrix panel, 64x32 pixels. */ #include #include // Select example to compile! #define EXAMPLE_NUMBER 1 //#define EXAMPLE_NUMBER 2 //#define EXAMPLE_NUMBER 3 /** * Configuration of the LED matrix panels number and individual pixel resolution. **/ #define PANEL_RES_X 64 // Number of pixels wide of each INDIVIDUAL panel module. #define PANEL_RES_Y 32 // Number of pixels tall of each INDIVIDUAL panel module. #define VDISP_NUM_ROWS 2 // Number of rows of individual LED panels #define VDISP_NUM_COLS 2 // Number of individual LED panels per row #define PANEL_CHAIN_LEN (VDISP_NUM_ROWS*VDISP_NUM_COLS) // Don't change /** * Configuration of the approach used to chain all the individual panels together. * Refer to the documentation or check the enum 'PANEL_CHAIN_TYPE' in VirtualMatrixPanel_T.hpp for options. **/ #define PANEL_CHAIN_TYPE CHAIN_TOP_RIGHT_DOWN /** * Optional config for the per-panel pixel mapping, for non-standard panels. * i.e. 1/4 scan panels, or outdoor panels. They're a pain in the a-- and all * have their own weird pixel mapping that is not linear. * * This is used for Examples 2 and 3. * **/ #define PANEL_SCAN_TYPE FOUR_SCAN_32PX_HIGH /** * Mandatory declaration of the dma_display. DO NOT CHANGE **/ MatrixPanel_I2S_DMA *dma_display = nullptr; /** * Template instantiation for the VirtualMatrixPanel_T class, depending on use-case. **/ #if EXAMPLE_NUMBER == 1 // --- Example 1: STANDARD 1/2 Scan --- // Declare a pointer to the specific instantiation: VirtualMatrixPanel_T* virtualDisp = nullptr; #endif #if EXAMPLE_NUMBER == 2 // --- Example 2: Non-Standard 1/4 Scan (Four-Scan 1/8) --- // Use an existing library user-contributed Scan Type pixel mapping using MyScanTypeMapping = ScanTypeMapping; // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class VirtualMatrixPanel_T* virtualDisp = nullptr; #endif #if EXAMPLE_NUMBER == 3 // --- Example 3: Single non-standard 1/4 Scan (Four-Scan 1/8) --- // Use an existing library user-contributed Scan Type pixel mapping using MyScanTypeMapping = ScanTypeMapping; // Create a pointer to the specific instantiation of the VirtualMatrixPanel_T class VirtualMatrixPanel_T* virtualDisp = nullptr; #endif // Bonus non-existnat example. Create your own per-panel custom pixel mapping! #if EXAMPLE_NUMBER == 4 // --- Custom Scan–Type Pixel Mapping --- // This policy adds a fixed offset to the coordinates. struct CustomScanTypeMapping { static constexpr VirtualCoords apply(VirtualCoords coords, int virt_y, int panel_pixel_base) { // For demonstration, add a fixed offset of +5 to x and +3 to y. coords.x += 5; coords.y += 3; return coords; } }; #endif void setup() { Serial.begin(115200); delay(2000); #if EXAMPLE_NUMBER == 3 /** * HACK ALERT! * For 1/4 scan panels (namely outdoor panels), electrically the pixels are connected in a chain that is * twice the physical panel's pixel width, and half the pixel height. As such, we need to configure * the underlying DMA library to match the same. Then we use the VirtualMatrixPanel_T class to map the * physical pixels to the virtual pixels. */ HUB75_I2S_CFG mxconfig( PANEL_RES_X*2, // DO NOT CHANGE THIS PANEL_RES_Y/2, // DO NOT CHANGE THIS 1 // A Single panel ); #elif EXAMPLE_NUMBER == 2 /** * HACK ALERT! * For 1/4 scan panels (namely outdoor panels), electrically the pixels are connected in a chain that is * twice the physical panel's pixel width, and half the pixel height. As such, we need to configure * the underlying DMA library to match the same. Then we use the VirtualMatrixPanel_T class to map the * physical pixels to the virtual pixels. */ HUB75_I2S_CFG mxconfig( PANEL_RES_X*2, // DO NOT CHANGE THIS PANEL_RES_Y/2, // DO NOT CHANGE THIS PANEL_CHAIN_LEN ); #else // Standard panel type natively supported by this library (Example 1) HUB75_I2S_CFG mxconfig( PANEL_RES_X, PANEL_RES_Y, PANEL_CHAIN_LEN ); #endif mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M; mxconfig.clkphase = false; //mxconfig.driver = HUB75_I2S_CFG::FM6126A; /** * Setup physical DMA LED display output. */ dma_display = new MatrixPanel_I2S_DMA(mxconfig); dma_display->begin(); dma_display->setBrightness8(128); //0-255 dma_display->clearScreen(); /** * Setup the VirtualMatrixPanel_T class to map the virtual pixels to the physical pixels. */ #if EXAMPLE_NUMBER == 1 virtualDisp = new VirtualMatrixPanel_T(VDISP_NUM_ROWS, VDISP_NUM_COLS, PANEL_RES_X, PANEL_RES_Y); #elif EXAMPLE_NUMBER == 2 virtualDisp = new VirtualMatrixPanel_T(VDISP_NUM_ROWS, VDISP_NUM_COLS, PANEL_RES_X, PANEL_RES_Y); #elif EXAMPLE_NUMBER == 3 virtualDisp = new VirtualMatrixPanel_T(1, 1, PANEL_RES_X, PANEL_RES_Y); // Single 1/4 scan panel #endif // Pass a reference to the DMA display to the VirtualMatrixPanel_T class virtualDisp->setDisplay(*dma_display); for (int y = 0; y < virtualDisp->height(); y++) { for (int x = 0; x < virtualDisp->width(); x++) { uint16_t color = virtualDisp->color565(96, 0, 0); // red if (x == 0) color = virtualDisp->color565(0, 255, 0); // g if (x == (virtualDisp->width()-1)) color = virtualDisp->color565(0, 0, 255); // b virtualDisp->drawPixel(x, y, color); delay(2); } } delay(3000); virtualDisp->clearScreen(); virtualDisp->drawDisplayTest(); // re draw text numbering on each screen to check connectivity } void loop() { // Do nothing here. delay (100); }