Update to 2.0.0

Also known as the @vortigont release! Hooray!
This commit is contained in:
mrfaptastic 2021-02-10 15:49:19 +00:00
parent 6ea2280ff3
commit 0749fa4193
43 changed files with 2702 additions and 863 deletions

File diff suppressed because it is too large Load diff

View file

@ -12,12 +12,18 @@
*/
//#define USE_GFX_ROOT 1
/*
* Do NOT build additional methods optimized for fast drawing,
* i.e. Adafruits drawFastHLine, drawFastVLine, etc...
*/
//#define NO_FAST_FUNCTIONS
/* Physical / Chained HUB75(s) RGB pixel WIDTH and HEIGHT.
*
* This library has only been tested with a 64 pixel (wide) and 32 (high) RGB panel.
* Theoretically, if you want to chain two of these horizontally to make a 128x32 panel
* you can do so with the cable and then set the MATRIX_WIDTH to '128'.
* This library has been tested with a 64x32 and 64x64 RGB panels.
* If you want to chain two or more of these horizontally to make a 128x32 panel
* you can do so with the cable and then set the CHAIN_LENGTH to '2'.
*
* Also, if you use a 64x64 panel, then set the MATRIX_HEIGHT to '64' and an E_PIN; it will work!
*
@ -26,19 +32,14 @@
*/
#ifndef MATRIX_WIDTH
#define MATRIX_WIDTH 64 // Single panel of 64 pixel width
// #define MATRIX_WIDTH 64*4 // Example of chaining four (4) panels. Need to multiply the width.
#endif
#ifndef MATRIX_HEIGHT
#define MATRIX_HEIGHT 32 // CHANGE THIS VALUE to 64 IF USING 64px HIGH panel(s) with E PIN
#endif
/* Best to keep these values as is. */
#ifndef PIXEL_COLOR_DEPTH_BITS
#define PIXEL_COLOR_DEPTH_BITS 8 // 8bit per RGB color = 24 bit/per pixel, reduce to save RAM
#endif
#ifndef MATRIX_ROWS_IN_PARALLEL
#define MATRIX_ROWS_IN_PARALLEL 2 // Don't change this unless you know what you're doing
#ifndef CHAIN_LENGTH
#define CHAIN_LENGTH 1 // Number of modules chained together, i.e. 4 panels chained result in virtualmatrix 64x4=256 px long
#endif
/* ESP32 Default Pin definition. You can change this, but best if you keep it as is and provide custom pin mappings
@ -65,24 +66,29 @@
// given we only map to 14 physical output wires/bits, we waste 2 bits.
/***************************************************************************************/
/* Do not change. */
/* Do not change definitions below unless you pretty sure you know what you are doing! */
#ifndef ESP32_I2S_CLOCK_SPEED
#define ESP32_I2S_CLOCK_SPEED (10000000UL) // @ 10Mhz
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
// RGB Panel Constants / Calculated Values
#ifndef MATRIX_ROWS_IN_PARALLEL
#define MATRIX_ROWS_IN_PARALLEL 2
#endif
#include "esp_heap_caps.h"
#include "esp32_i2s_parallel.h"
// 8bit per RGB color = 24 bit/per pixel,
// might be reduced to save RAM but it corrupts colors badly (curently broken)
#ifndef PIXEL_COLOR_DEPTH_BITS
#define PIXEL_COLOR_DEPTH_BITS 8
#endif
#ifdef USE_GFX_ROOT
#include "GFX.h" // Adafruit GFX core class -> https://github.com/mrfaptastic/GFX_Root
#else
#include "Adafruit_GFX.h" // Adafruit class with all the other stuff
#endif
#define COLOR_CHANNELS_PER_PIXEL 3
/***************************************************************************************/
/* Do not change. */
/* Definitions below shold NOT be ever changed without rewriting libraly logic */
#define ESP32_I2S_DMA_MODE I2S_PARALLEL_BITS_16 // Pump 16 bits out in parallel
#define ESP32_I2S_DMA_STORAGE_TYPE uint16_t // one uint16_t at a time.
#define CLKS_DURING_LATCH 0 // Not (yet) used.
// Panel Upper half RGB (numbering according to order in DMA gpio_bus configuration)
#define BITS_RGB1_OFFSET 0 // Start point of RGB_X1 bits
@ -108,44 +114,67 @@
#define BIT_D (1<<11)
#define BIT_E (1<<12)
#define BITMASK_RGB1_CLEAR (0xfff8) // inverted bitmask for R1G1B1 bit in pixel vector
#define BITMASK_RGB2_CLEAR (0xffc7) // inverted bitmask for R2G2B2 bit in pixel vector
#define BITMASK_RGB12_CLEAR (0xffc0) // inverted bitmask for R1G1B1R2G2B2 bit in pixel vector
#define BITMASK_CTRL_CLEAR (0xe03f) // inverted bitmask for control bits ABCDE,LAT,OE in pixel vector
// BitMasks are pre-computed based on the above #define's for performance.
#define BITMASK_RGB1_CLEAR (0b1111111111111000) // inverted bitmask for R1G1B1 bit in pixel vector
#define BITMASK_RGB2_CLEAR (0b1111111111000111) // inverted bitmask for R2G2B2 bit in pixel vector
#define BITMASK_RGB12_CLEAR (0b1111111111000000) // inverted bitmask for R1G1B1R2G2B2 bit in pixel vector
#define BITMASK_CTRL_CLEAR (0b1110000000111111) // inverted bitmask for control bits ABCDE,LAT,OE in pixel vector
#define BITMASK_OE_CLEAR (0b1111111101111111) // inverted bitmask for control bit OE in pixel vector
// How many clock cycles to blank OE before/after LAT signal change, default is 1 clock
#define DEFAULT_LAT_BLANKING 1
// Max clock cycles to blank OE before/after LAT signal change
#define MAX_LAT_BLANKING 4
// RGB Panel Constants / Calculated Values
#define COLOR_CHANNELS_PER_PIXEL 3
#define PIXELS_PER_ROW MATRIX_WIDTH // number of all pixels in a row of chained modules
//#define PIXEL_COLOR_DEPTH_BITS (MATRIX_COLOR_DEPTH/COLOR_CHANNELS_PER_PIXEL) // = 8
#define ROWS_PER_FRAME (MATRIX_HEIGHT/MATRIX_ROWS_IN_PARALLEL) // = 16
/***************************************************************************************/
/* Keep this as is. Do not change. */
#define ESP32_I2S_DMA_MODE I2S_PARALLEL_BITS_16 // Pump 16 bits out in parallel
#define ESP32_I2S_DMA_STORAGE_TYPE uint16_t // one uint16_t at a time.
#ifndef ESP32_I2S_CLOCK_SPEED
#define ESP32_I2S_CLOCK_SPEED (10000000UL) // @ 10Mhz
//#define ESP32_I2S_CLOCK_SPEED (20000000UL) // @ 20Mhz
// Lib includes
#include <vector>
#include <memory>
#include "esp_heap_caps.h"
#include "esp32_i2s_parallel.h"
#ifdef USE_GFX_ROOT
#include "GFX.h" // Adafruit GFX core class -> https://github.com/mrfaptastic/GFX_Root
#elif !defined NO_GFX
#include "Adafruit_GFX.h" // Adafruit class with all the other stuff
#endif
#define CLKS_DURING_LATCH 0 // Not used.
/***************************************************************************************/
/***************************************************************************************/
/* rowBitStruct
* Note: sizeof(data) must be multiple of 32 bits, as ESP32 DMA linked list buffer address pointer
* must be word-aligned.
/** @brief - Structure holds raw DMA data to drive TWO full rows of pixels spaning through all chained modules
* Note: sizeof(data) must be multiple of 32 bits, as ESP32 DMA linked list buffer address pointer must be word-aligned
*/
struct rowBitStruct {
ESP32_I2S_DMA_STORAGE_TYPE data[PIXELS_PER_ROW + CLKS_DURING_LATCH];
// This evaluates to just data[64] really.. an array of 64 uint16_t's
const size_t width;
const uint8_t color_depth;
const bool double_buff;
ESP32_I2S_DMA_STORAGE_TYPE *data;
/** @brief - returns size of row of data vectorfor a SINGLE buff
* size (in bytes) of a vector holding full DMA data for a row of pixels with _dpth color bits
* a SINGLE buffer only size is accounted, when using double buffers it actually takes twice as much space
* but returned size is for a half of double-buffer
*
* default - returns full data vector size for a SINGLE buff
*
*/
size_t size(uint8_t _dpth=0 ) { if (!_dpth) _dpth = color_depth; return width * _dpth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE); };
/** @brief - returns pointer to the row's data vector begining at pixel[0] for _dpth color bit
* default - returns pointer to the data vector's head
* NOTE: this call might be very slow in loops. Due to poor instruction caching in esp32 it might be required a reread from flash
* every loop cycle, better use inlined #define instead in such cases
*/
ESP32_I2S_DMA_STORAGE_TYPE* getDataPtr(const uint8_t _dpth=0, const bool buff_id=0) { return &(data[_dpth*width + buff_id*(width*color_depth)]); };
// constructor - allocates DMA-capable memory to hold the struct data
rowBitStruct(const size_t _width, const uint8_t _depth, const bool _dbuff) : width(_width), color_depth(_depth), double_buff(_dbuff) {
data = (ESP32_I2S_DMA_STORAGE_TYPE *)heap_caps_malloc( size()+size()*double_buff, MALLOC_CAP_DMA);
}
~rowBitStruct() { delete data;}
};
/* rowColorDepthStruct
* Duplicates of row bit structure, but for each color 'depth'ness.
*/
struct rowColorDepthStruct {
rowBitStruct rowbits[PIXEL_COLOR_DEPTH_BITS];
};
/* frameStruct
* Note: A 'frameStruct' contains ALL the data for a full-frame (i.e. BOTH 2x16-row frames are
@ -155,7 +184,8 @@ struct rowColorDepthStruct {
* memory per row (per rowColorDepthStruct) instead.
*/
struct frameStruct {
rowColorDepthStruct rowdata[ROWS_PER_FRAME];
uint8_t rows=0; // number of rows held in current frame, not used actually, just to keep the idea of struct
std::vector<std::shared_ptr<rowBitStruct> > rowBits;
};
typedef struct RGB24 {
@ -170,69 +200,147 @@ typedef struct RGB24 {
uint8_t blue;
} RGB24;
/**
* Enumeration of hardware-specific chips
* used to drive matrix modules
*/
enum shift_driver {SHIFT=0, FM6124, FM6126A, ICN2038S};
/***************************************************************************************/
// Used by val2PWM
//C/p'ed from https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/
// Example calculator: https://gist.github.com/mathiasvr/19ce1d7b6caeab230934080ae1f1380e
const uint8_t lumConvTab[]={
// need to make sure this would end up in RAM for fastest access
static const uint8_t DRAM_ATTR lumConvTab[]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 28, 29, 30, 30, 31, 31, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 86, 87, 88, 90, 91, 92, 93, 95, 96, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110, 112, 113, 115, 116, 118, 120, 121, 123, 124, 126, 128, 129, 131, 133, 134, 136, 138, 139, 141, 143, 145, 146, 148, 150, 152, 154, 156, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 192, 194, 196, 198, 200, 203, 205, 207, 209, 212, 214, 216, 218, 221, 223, 226, 228, 230, 233, 235, 238, 240, 243, 245, 248, 250, 253, 255, 255};
/** @brief - configuration values for HUB75_I2S driver
* This structure holds configuration vars that are used as
* an initialization values when creating an instance of MatrixPanel_I2S_DMA object.
* All params have it's default values.
*/
struct HUB75_I2S_CFG {
/**
* Enumeration of hardware-specific chips
* used to drive matrix modules
*/
enum shift_driver {SHIFT=0, FM6124, FM6126A, ICN2038S};
/**
* I2S clock speed selector
*/
enum clk_speed {HZ_10M=10000000, HZ_13340K=13340000, HZ_16M=16000000, HZ_20M=20000000, HZ_26670K=26670000};
// Structure Variables
// physical width of a single matrix panel module (in pixels, usually it is 64 ;) )
uint16_t mx_width;
// physical height of a single matrix panel module (in pixels, usually amost always it is either 32 or 64)
uint16_t mx_height;
// number of chained panels regardless of the topology, default 1 - a single matrix module
uint16_t chain_length;
/**
* GPIO pins mapping
*/
struct i2s_pins{
int8_t r1, g1, b1, r2, g2, b2, a, b, c, d, e, lat, oe, clk;
} gpio;
// Matrix driver chip type - default is a plain shift register
shift_driver driver;
// I2S clock speed
clk_speed i2sspeed;
// use DMA double buffer (twice as much RAM required)
bool double_buff;
// How many clock cycles to blank OE before/after LAT signal change, default is 1 clock
uint8_t latch_blanking;
// struct constructor
HUB75_I2S_CFG (
uint16_t _w = MATRIX_WIDTH,
uint16_t _h = MATRIX_HEIGHT,
uint16_t _chain = CHAIN_LENGTH,
i2s_pins _pinmap = {
R1_PIN_DEFAULT, G1_PIN_DEFAULT, B1_PIN_DEFAULT, R2_PIN_DEFAULT, G2_PIN_DEFAULT, B2_PIN_DEFAULT,
A_PIN_DEFAULT, B_PIN_DEFAULT, C_PIN_DEFAULT, D_PIN_DEFAULT, E_PIN_DEFAULT,
LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT },
shift_driver _drv = SHIFT,
bool _dbuff = false,
clk_speed _i2sspeed = HZ_10M,
uint16_t _latblk = 1
) : mx_width(_w),
mx_height(_h),
chain_length(_chain),
gpio(_pinmap),
driver(_drv), i2sspeed(_i2sspeed),
double_buff(_dbuff),
latch_blanking(_latblk) {}
}; // end of structure HUB75_I2S_CFG
/***************************************************************************************/
#ifdef USE_GFX_ROOT
class MatrixPanel_I2S_DMA : public GFX {
#elif !defined NO_GFX
class MatrixPanel_I2S_DMA : public Adafruit_GFX {
#else
class MatrixPanel_I2S_DMA : public Adafruit_GFX {
class MatrixPanel_I2S_DMA {
#endif
// ------- PUBLIC -------
public:
/**
* MatrixPanel_I2S_DMA
*
* default predefined values are used for matrix configuraton
*
*/
MatrixPanel_I2S_DMA()
#ifdef USE_GFX_ROOT
: GFX(MATRIX_WIDTH, MATRIX_HEIGHT)
#elif !defined NO_GFX
: Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT)
#endif
{}
/**
* MatrixPanel_I2S_DMA
*
* @param {bool} _double_buffer : Double buffer is disabled by default. Enable only if you know what you're doing. Manual switching required with flipDMABuffer() and showDMABuffer()
* @param {HUB75_I2S_CFG} opts : structure with matrix configuration
*
*/
MatrixPanel_I2S_DMA(bool _double_buffer = false)
MatrixPanel_I2S_DMA(const HUB75_I2S_CFG& opts) :
#ifdef USE_GFX_ROOT
: GFX(MATRIX_WIDTH, MATRIX_HEIGHT), double_buffering_enabled(_double_buffer) {
#else
: Adafruit_GFX(MATRIX_WIDTH, MATRIX_HEIGHT), double_buffering_enabled(_double_buffer) {
GFX(opts.mx_width*opts.chain_length, opts.mx_height),
#elif !defined NO_GFX
Adafruit_GFX(opts.mx_width*opts.chain_length, opts.mx_height),
#endif
m_cfg(opts) {}
}
/* Propagate the DMA pin configuration, or use compiler defaults */
bool begin(int dma_r1_pin = R1_PIN_DEFAULT , int dma_g1_pin = G1_PIN_DEFAULT, int dma_b1_pin = B1_PIN_DEFAULT , int dma_r2_pin = R2_PIN_DEFAULT , int dma_g2_pin = G2_PIN_DEFAULT , int dma_b2_pin = B2_PIN_DEFAULT , int dma_a_pin = A_PIN_DEFAULT , int dma_b_pin = B_PIN_DEFAULT , int dma_c_pin = C_PIN_DEFAULT , int dma_d_pin = D_PIN_DEFAULT , int dma_e_pin = E_PIN_DEFAULT , int dma_lat_pin = LAT_PIN_DEFAULT, int dma_oe_pin = OE_PIN_DEFAULT , int dma_clk_pin = CLK_PIN_DEFAULT, const shift_driver _drv=SHIFT)
{
/* Propagate the DMA pin configuration, allocate DMA buffs and start data ouput, initialy blank */
bool begin(){
// Change 'if' to '1' to enable, 0 to not include this Serial output in compiled program
#if SERIAL_DEBUG
Serial.printf("Using pin %d for the R1_PIN\n", dma_r1_pin);
Serial.printf("Using pin %d for the G1_PIN\n", dma_g1_pin);
Serial.printf("Using pin %d for the B1_PIN\n", dma_b1_pin);
Serial.printf("Using pin %d for the R2_PIN\n", dma_r2_pin);
Serial.printf("Using pin %d for the G2_PIN\n", dma_g2_pin);
Serial.printf("Using pin %d for the B2_PIN\n", dma_b2_pin);
Serial.printf("Using pin %d for the A_PIN\n", dma_a_pin);
Serial.printf("Using pin %d for the B_PIN\n", dma_b_pin);
Serial.printf("Using pin %d for the C_PIN\n", dma_c_pin);
Serial.printf("Using pin %d for the D_PIN\n", dma_d_pin);
Serial.printf("Using pin %d for the E_PIN\n", dma_e_pin);
Serial.printf("Using pin %d for the LAT_PIN\n", dma_lat_pin);
Serial.printf("Using pin %d for the OE_PIN\n", dma_oe_pin);
Serial.printf("Using pin %d for the CLK_PIN\n", dma_clk_pin);
Serial.printf_P(PSTR("Using pin %d for the R1_PIN\n"), m_cfg.gpio.r1);
Serial.printf_P(PSTR("Using pin %d for the G1_PIN\n"), m_cfg.gpio.g1);
Serial.printf_P(PSTR("Using pin %d for the B1_PIN\n"), m_cfg.gpio.b1);
Serial.printf_P(PSTR("Using pin %d for the R2_PIN\n"), m_cfg.gpio.r2);
Serial.printf_P(PSTR("Using pin %d for the G2_PIN\n"), m_cfg.gpio.g2);
Serial.printf_P(PSTR("Using pin %d for the B2_PIN\n"), m_cfg.gpio.b2);
Serial.printf_P(PSTR("Using pin %d for the A_PIN\n"), m_cfg.gpio.a);
Serial.printf_P(PSTR("Using pin %d for the B_PIN\n"), m_cfg.gpio.b);
Serial.printf_P(PSTR("Using pin %d for the C_PIN\n"), m_cfg.gpio.c);
Serial.printf_P(PSTR("Using pin %d for the D_PIN\n"), m_cfg.gpio.d);
Serial.printf_P(PSTR("Using pin %d for the E_PIN\n"), m_cfg.gpio.e);
Serial.printf_P(PSTR("Using pin %d for the LAT_PIN\n"), m_cfg.gpio.lat);
Serial.printf_P(PSTR("Using pin %d for the OE_PIN\n"), m_cfg.gpio.oe);
Serial.printf_P(PSTR("Using pin %d for the CLK_PIN\n"), m_cfg.gpio.clk);
#endif
// initialize some sppecific panel drivers
if (_drv)
shiftDriver(_drv, dma_r1_pin, dma_g1_pin, dma_b1_pin, dma_r2_pin, dma_g2_pin, dma_b2_pin, dma_a_pin, dma_b_pin, dma_c_pin, dma_d_pin, dma_e_pin, dma_lat_pin, dma_oe_pin, dma_clk_pin);
if (m_cfg.driver)
shiftDriver(m_cfg);
/* As DMA buffers are dynamically allocated, we must allocated in begin()
* Ref: https://github.com/espressif/arduino-esp32/issues/831
@ -242,32 +350,76 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
// Flush the DMA buffers prior to configuring DMA - Avoid visual artefacts on boot.
clearScreen(); // Must fill the DMA buffer with the initial output bit sequence or the panel will display garbage
if (double_buffering_enabled){
flipDMABuffer(); // flip to backbuffer 1
clearScreen(); // Must fill the DMA buffer with the initial output bit sequence or the panel will display garbage
flipDMABuffer(); // backbuffer 0
}
// Setup the ESP32 DMA Engine. Sprite_TM built this stuff.
configureDMA(dma_r1_pin, dma_g1_pin, dma_b1_pin, dma_r2_pin, dma_g2_pin, dma_b2_pin, dma_a_pin, dma_b_pin, dma_c_pin, dma_d_pin, dma_e_pin, dma_lat_pin, dma_oe_pin, dma_clk_pin ); //DMA and I2S configuration and setup
configureDMA(m_cfg); //DMA and I2S configuration and setup
showDMABuffer(); // show backbuf_id of 0
#if SERIAL_DEBUG
if (!everything_OK)
Serial.println("MatrixPanel_I2S_DMA::begin() failed.");
if (!initialized)
Serial.println(F("MatrixPanel_I2S_DMA::begin() failed."));
#endif
return everything_OK;
return initialized;
}
/*
* overload for compatibility
*/
bool begin(int r1, int g1 = G1_PIN_DEFAULT, int b1 = B1_PIN_DEFAULT, int r2 = R2_PIN_DEFAULT, int g2 = G2_PIN_DEFAULT, int b2 = B2_PIN_DEFAULT, int a = A_PIN_DEFAULT, int b = B_PIN_DEFAULT, int c = C_PIN_DEFAULT, int d = D_PIN_DEFAULT, int e = E_PIN_DEFAULT, int lat = LAT_PIN_DEFAULT, int oe = OE_PIN_DEFAULT, int clk = CLK_PIN_DEFAULT);
// TODO: Disable/Enable auto buffer flipping (useful for lots of drawPixel usage)...
// Draw pixels
// Adafruit's BASIC DRAW API
virtual void drawPixel(int16_t x, int16_t y, uint16_t color); // overwrite adafruit implementation
virtual void fillScreen(uint16_t color); // overwrite adafruit implementation
void clearScreen();
/**
* clears DMA buffers and reinitializes control bits
* to just clear the screen to black fillScreen(0) works faster
*/
void clearScreen();
#ifndef NO_FAST_FUNCTIONS
/**
* @brief - override Adafruit's FastVLine
* this works faster than multiple consecutive pixel by pixel drawPixel() call
*/
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){
uint8_t r, g, b;
color565to888(color, r, g, b);
vlineDMA(x, y, h, r, g, b);
}
// rgb888 overload
virtual inline void drawFastVLine(int16_t x, int16_t y, int16_t h, uint8_t r, uint8_t g, uint8_t b){ vlineDMA(x, y, h, r, g, b); };
/**
* @brief - override Adafruit's FastHLine
* this works faster than multiple consecutive pixel by pixel drawPixel() call
*/
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){
uint8_t r, g, b;
color565to888(color, r, g, b);
hlineDMA(x, y, w, r, g, b);
}
// rgb888 overload
virtual inline void drawFastHLine(int16_t x, int16_t y, int16_t w, uint8_t r, uint8_t g, uint8_t b){ hlineDMA(x, y, w, r, g, b); };
/**
* @brief - override Adafruit's fillRect
* this works much faster than mulltiple consecutive per-pixel drawPixel() calls
*/
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){
uint8_t r, g, b;
color565to888(color, r, g, b);
fillRectDMA(x, y, w, h, r, g, b);
}
// rgb888 overload
virtual inline void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b){fillRectDMA(x, y, w, h, r, g, b);}
#endif
void fillScreenRGB888(uint8_t r, uint8_t g, uint8_t b);
void drawPixelRGB565(int16_t x, int16_t y, uint16_t color);
void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b);
@ -275,23 +427,31 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
void drawIcon (int *ico, int16_t x, int16_t y, int16_t cols, int16_t rows);
// Color 444 is a 4 bit scale, so 0 to 15, color 565 takes a 0-255 bit value, so scale up by 255/15 (i.e. 17)!
uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return color565(r*17,g*17,b*17); }
static uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return color565(r*17,g*17,b*17); }
// Converts RGB888 to RGB565
uint16_t color565(uint8_t r, uint8_t g, uint8_t b); // This is what is used by Adafruit GFX!
static uint16_t color565(uint8_t r, uint8_t g, uint8_t b); // This is what is used by Adafruit GFX!
// Converts RGB333 to RGB565
uint16_t color333(uint8_t r, uint8_t g, uint8_t b); // This is what is used by Adafruit GFX! Not sure why they have a capital 'C' for this particular function.
static uint16_t color333(uint8_t r, uint8_t g, uint8_t b); // This is what is used by Adafruit GFX! Not sure why they have a capital 'C' for this particular function.
/**
* @brief - convert RGB565 to RGB888
* @param uint16_t color - RGB565 input color
* @param uint8_t &r, &g, &b - refs to variables where converted colors would be emplaced
*/
static void color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b);
inline void flipDMABuffer()
{
if ( !double_buffering_enabled) return;
if ( !m_cfg.double_buff) return;
// Flip to other buffer as the backbuffer. i.e. Graphic changes happen to this buffer (but aren't displayed until showDMABuffer())
back_buffer_id ^= 1;
#if SERIAL_DEBUG
Serial.printf("Set back buffer to: %d\n", back_buffer_id);
Serial.printf_P(PSTR("Set back buffer to: %d\n"), back_buffer_id);
#endif
// Wait before we allow any writing to the buffer. Stop flicker.
@ -301,10 +461,10 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
inline void showDMABuffer()
{
if (!double_buffering_enabled) return;
if (!m_cfg.double_buff) return;
#if SERIAL_DEBUG
Serial.printf("Showtime for buffer: %d\n", back_buffer_id);
Serial.printf_P(PSTR("Showtime for buffer: %d\n"), back_buffer_id);
#endif
i2s_parallel_flip_to_buffer(&I2S1, back_buffer_id);
@ -317,8 +477,12 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
{
// Change to set the brightness of the display, range of 1 to matrixWidth (i.e. 1 - 64)
brightness = b;
if (fastmode) // in 'fast' mode we should always reset DMA buffer to update OE bits that controls brightness
clearScreen(); // and YES, it WILL flicker. you've been warned :)
if (!initialized)
return;
brtCtrlOE(b);
if (m_cfg.double_buff)
brtCtrlOE(b, 1);
}
/**
@ -328,7 +492,7 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
*/
void setBrightness8(const uint8_t b)
{
setPanelBrightness(b * MATRIX_WIDTH / 256);
setPanelBrightness(b * PIXELS_PER_ROW / 256);
}
inline void setMinRefreshRate(int rr)
@ -336,76 +500,132 @@ class MatrixPanel_I2S_DMA : public Adafruit_GFX {
min_refresh_rate = rr;
}
/**
* Controls fast-update mode, when only RGB bits are upated in DMA buffer
* @param mode bool - set/clear fastmode. Will take effect on newly updated pixels only
* @return fastmode status
*/
bool setFastMode(const bool mode){
fastmode = mode;
return fastmode;
};
/**
* Controls fast-update mode, when only RGB bits are upated in DMA buffer
* @param void - returns current fastmode status
*/
bool setFastMode(){return fastmode;};
int calculated_refresh_rate = 0;
// ------- PRIVATE -------
private:
/* Pixel data is organized from LSB to MSB sequentially by row, from row 0 to row matrixHeight/matrixRowsInParallel
* (two rows of pixels are refreshed in parallel)
* Memory is allocated (malloc'd) by the row, and not in one massive chunk, for flexibility.
*/
rowColorDepthStruct *matrix_row_framebuffer_malloc[ROWS_PER_FRAME];
// ESP 32 DMA Linked List descriptor
int desccount = 0;
lldesc_t * dmadesc_a = {0};
lldesc_t * dmadesc_b = {0};
// ESP32-HUB75-MatrixPanel-I2S-DMA functioning
bool everything_OK = false;
bool double_buffering_enabled = false;// Do we use double buffer mode? Your project code will have to manually flip between both.
int back_buffer_id = 0; // If using double buffer, which one is NOT active (ie. being displayed) to write too?
int brightness = 32; // If you get ghosting... reduce brightness level. 60 seems to be the limit before ghosting on a 64 pixel wide physical panel for some panels.
int min_refresh_rate = 99; // Probably best to leave as is unless you want to experiment. Framerate has an impact on brightness and also power draw - voltage ripple.
int lsbMsbTransitionBit = 0; // For possible color depth calculations
/**
* this var controls how DMA buffers are updated
* if set to false (default) - full recalculation performed, including address line bits, OE, LAT
* if set to true, only RGB1, RGB2 bits are updated.
* Could be toggled any time, see Notes regarding #define GO_FOR_SPEED 1 above
* @brief - Sets how many clock cycles to blank OE before/after LAT signal change
* @param uint8_t pulses - clocks before/after OE
* default is DEFAULT_LAT_BLANKING
* Max is MAX_LAT_BLANKING
* @returns - new value for m_cfg.latch_blanking
*/
#ifdef GO_FOR_SPEED
bool fastmode = true;
#else
bool fastmode = false;
#endif
uint8_t setLatBlanking(uint8_t pulses);
/* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */
bool allocateDMAmemory();
/**
* Get a class configuration struct
*
*/
const HUB75_I2S_CFG& getCfg() const {return m_cfg;};
// ------- PROTECTED -------
// those might be useful for child classes, like VirtualMatrixPanel
protected:
/**
* @brief - clears and reinitializes color/control data in DMA buffs
* When allocated, DMA buffs might be dirtry, so we need to blank it and initialize ABCDE,LAT,OE control bits.
* Those control bits are constants during the entire DMA sweep and never changed when updating just pixel color data
* so we could set it once on DMA buffs initialization and forget.
* This effectively clears buffers to blank BLACK and makes it ready to display output.
* (Brightness control via OE bit manipulation is another case)
*/
void clearFrameBuffer(bool _buff_id = 0);
/* Setup the DMA Link List chain and initiate the ESP32 DMA engine */
void configureDMA(int r1_pin, int g1_pin, int b1_pin, int r2_pin, int g2_pin, int b2_pin, int a_pin, int b_pin, int c_pin, int d_pin, int e_pin, int lat_pin, int oe_pin, int clk_pin); // Get everything setup. Refer to the .c file
/* Update a specific pixel in the DMA buffer to a colour */
void updateMatrixDMABuffer(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t blue);
/* Update the entire DMA buffer (aka. The RGB Panel) a certain colour (wipe the screen basically) */
void updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue);
#ifndef NO_FAST_FUNCTIONS
/**
* @brief - update DMA buff drawing horizontal line at specified coordinates
* @param x_ccord - line start coordinate x
* @param y_ccord - line start coordinate y
* @param l - line length
* @param r,g,b, - RGB888 color
*/
void hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue);
/**
* @brief - update DMA buff drawing horizontal line at specified coordinates
* @param x_ccord - line start coordinate x
* @param y_ccord - line start coordinate y
* @param l - line length
* @param r,g,b, - RGB888 color
*/
void vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue);
/**
* @brief - update DMA buff drawing a rectangular at specified coordinates
* uses Fast H/V line draw internally, works faster than mulltiple consecutive pixel by pixel calls to updateMatrixDMABuffer()
* @param int16_t x, int16_t y - coordinates of a top-left corner
* @param int16_t w, int16_t h - width and height of a rectangular, min is 1 px
* @param uint8_t r - RGB888 color
* @param uint8_t g - RGB888 color
* @param uint8_t b - RGB888 color
*/
void fillRectDMA(int16_t x_coord, int16_t y_coord, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b);
#endif
// ------- PRIVATE -------
private:
// Matrix i2s settings
HUB75_I2S_CFG m_cfg;
/* ESP32-HUB75-MatrixPanel-I2S-DMA functioning constants
* we can't change those once object instance initialized it's DMA structs
*/
const uint8_t ROWS_PER_FRAME = m_cfg.mx_height / MATRIX_ROWS_IN_PARALLEL; // RPF - rows per frame, either 16 or 32 depending on matrix module
const uint16_t PIXELS_PER_ROW = m_cfg.mx_width * m_cfg.chain_length; // number of pixels in a single row of all chained matrix modules (WIDTH of a combined matrix chain)
// Other private variables
bool initialized = false;
int back_buffer_id = 0; // If using double buffer, which one is NOT active (ie. being displayed) to write too?
int brightness = 32; // If you get ghosting... reduce brightness level. 60 seems to be the limit before ghosting on a 64 pixel wide physical panel for some panels.
int min_refresh_rate = 99; // Probably best to leave as is unless you want to experiment. Framerate has an impact on brightness and also power draw - voltage ripple.
int lsbMsbTransitionBit = 0; // For possible color depth calculations
// *** DMA FRAMEBUFFER structures
// ESP 32 DMA Linked List descriptor
int desccount = 0;
lldesc_t * dmadesc_a = {0};
lldesc_t * dmadesc_b = {0};
/* Pixel data is organized from LSB to MSB sequentially by row, from row 0 to row matrixHeight/matrixRowsInParallel
* (two rows of pixels are refreshed in parallel)
* Memory is allocated (malloc'd) by the row, and not in one massive chunk, for flexibility.
* The whole DMA framebuffer is just a vector of pointers to structs with ESP32_I2S_DMA_STORAGE_TYPE arrays
* Since it's dimensions is unknown prior to class initialization, we just decrale it here as empty struct and will do all allocations later.
* Refer to rowBitStruct to get the idea of it's internal structure
*/
frameStruct dma_buff;
/* Calculate the memory available for DMA use, do some other stuff, and allocate accordingly */
bool allocateDMAmemory();
/* Setup the DMA Link List chain and initiate the ESP32 DMA engine */
void configureDMA(const HUB75_I2S_CFG& opts);
/**
* pre-init procedures for specific drivers
*
*/
void shiftDriver(const shift_driver _drv, const int dma_r1_pin, const int dma_g1_pin, const int dma_b1_pin, const int dma_r2_pin, const int dma_g2_pin, const int dma_b2_pin, const int dma_a_pin, const int dma_b_pin, const int dma_c_pin, const int dma_d_pin, const int dma_e_pin, const int dma_lat_pin, const int dma_oe_pin, const int dma_clk_pin);
void shiftDriver(const HUB75_I2S_CFG& opts);
/**
* @brief - reset OE bits in DMA buffer in a way to control brightness
* @param brt - brightness level from 0 to row_width
* @param _buff_id - buffer id to control
*/
void brtCtrlOE(int brt, const bool _buff_id=0);
}; // end Class header
@ -454,13 +674,6 @@ inline void MatrixPanel_I2S_DMA::drawPixelRGB24(int16_t x, int16_t y, RGB24 colo
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
//https://github.com/squix78/ILI9341Buffer/blob/master/ILI9341_SPI.cpp
inline uint16_t MatrixPanel_I2S_DMA::color565(uint8_t r, uint8_t g, uint8_t b) {
/*
Serial.printf("Got r value of %d\n", r);
Serial.printf("Got g value of %d\n", g);
Serial.printf("Got b value of %d\n", b);
*/
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
@ -497,5 +710,16 @@ inline void MatrixPanel_I2S_DMA::drawIcon (int *ico, int16_t x, int16_t y, int16
}
}
/**
* @brief - convert RGB565 to RGB888
* @param uint16_t color - RGB565 input color
* @param uint8_t &r, &g, &b - refs to variables where converted colors would be emplaced
*/
inline void MatrixPanel_I2S_DMA::color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b){
r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6;
g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6;
b = (((color & 0x1F) * 527) + 23) >> 6;
}
#endif

View file

@ -10,8 +10,9 @@
*******************************************************************/
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
#include <Fonts/FreeSansBold12pt7b.h>
#ifndef NO_GFX
#include <Fonts/FreeSansBold12pt7b.h>
#endif
struct VirtualCoords {
int16_t x;
@ -21,8 +22,10 @@ struct VirtualCoords {
#ifdef USE_GFX_ROOT
class VirtualMatrixPanel : public GFX
#else
#elif !defined NO_GFX
class VirtualMatrixPanel : public Adafruit_GFX
#else
class VirtualMatrixPanel
#endif
{
@ -38,11 +41,10 @@ class VirtualMatrixPanel : public Adafruit_GFX
MatrixPanel_I2S_DMA *display;
#ifdef USE_GFX_ROOT
VirtualMatrixPanel(MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int panelX, int panelY, bool serpentine_chain = true, bool top_down_chain = false)
#ifdef USE_GFX_ROOT
: GFX(vmodule_cols*panelX, vmodule_rows*panelY)
#else
VirtualMatrixPanel(MatrixPanel_I2S_DMA &disp, int vmodule_rows, int vmodule_cols, int panelX, int panelY, bool serpentine_chain = true, bool top_down_chain = false )
#elif !defined NO_GFX
: Adafruit_GFX(vmodule_cols*panelX, vmodule_rows*panelY)
#endif
{
@ -128,8 +130,12 @@ inline VirtualCoords VirtualMatrixPanel::getCoords(int16_t x, int16_t y) {
// Reverse co-ordinates if panel chain from ESP starts from the TOP RIGHT
if (_chain_top_down)
{
coords.x = (this->display->width()-1) - coords.x;
coords.y = (this->display->height()-1) - coords.y;
const HUB75_I2S_CFG _cfg = this->display->getCfg();
coords.x = (_cfg.mx_width * _cfg.chain_length - 1) - coords.x;
coords.y = (_cfg.mx_height-1) - coords.y;
//coords.x = (this->display->getCfg().mx_width-1) - coords.x;
//coords.y = (this->display->getCfg().mx_height-1) - coords.y;
}
//Serial.print("Mapping to x: "); Serial.print(coords.x, DEC); Serial.print(", y: "); Serial.println(coords.y, DEC);
@ -169,6 +175,7 @@ inline void VirtualMatrixPanel::drawPixelRGB24(int16_t x, int16_t y, RGB24 color
this->display->drawPixelRGB24(coords.x, coords.y, color);
}
#ifndef NO_GFX
inline void VirtualMatrixPanel::drawDisplayTest()
{
this->display->setFont(&FreeSansBold12pt7b);
@ -183,6 +190,7 @@ inline void VirtualMatrixPanel::drawDisplayTest()
}
}
#endif
// need to recreate this one, as it wouldnt work to just map where it starts.
inline void VirtualMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) {

View file

@ -1,7 +1,5 @@
The MIT License (MIT)
Copyright (c) 2015 Pixelmatix
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

View file

@ -6,14 +6,23 @@ This ESP32 Arduino library for HUB75 / HUB75E connector type 64x32 RGB LED 1/16
As a result, this library can theoretically provide ~16-24 bit colour, at various brightness levels without noticeable flicker.
## Panels Supported
* 64x32 pixel 1/16 Scan LED Matrix 'Indoor' Panel, such as this [typical RGB panel available for purchase](https://www.aliexpress.com/item/256-128mm-64-32-pixels-1-16-Scan-Indoor-3in1-SMD2121-RGB-full-color-P4-led/32810362851.html).
* 64x64 pixel 1/32 Scan LED Matrix 'Indoor' Panel (experimental).
* [FM6126](FM6126A.md) / ICN2038S panels based on [this example](/examples/FM6126Panel) will also work with the correct initialisation.
Ones interested in internals of such matrixes could find [this article](https://www.sparkfun.com/news/2650) useful.
## Panels Supported
* 64x32 (width x height) pixel 1/16 Scan LED Matrix 'Indoor' Panel, such as this [typical RGB panel available for purchase](https://www.aliexpress.com/item/256-128mm-64-32-pixels-1-16-Scan-Indoor-3in1-SMD2121-RGB-full-color-P4-led/32810362851.html).
* 64x64 pixel 1/32 Scan LED Matrix 'Indoor' Panel (experimental).
* 32x16 pixel 1/4 Scan LED Matrix 'Indoor' Panel using an ingenious workaround as demonstrated in [this example](/examples/32x16_1_4_ScanPanel).
* Any of the above panel resolution / scan rates based on [FM6126](FM6126A.md) / ICN2038S chips. Refer to [this example](/examples/PatternPlasma) on how to use!
## Panel driver chips known to be working well
* ICND2012
* RUC7258
* FM6126A AKA ICN2038S, FM6124 (if specified properly)
## Panels Not Supported
* 1/4, 1/8 Scan LED Matrix Panels are not supported, please use an alternative library if you bought one of these.
* Panels with a resolution of less than 64x32pixels and/or scan rate != 1/32 or 1/16
* 1/8 Scan LED Matrix Panels are not supported, please use an alternative library if you bought one of these.
## Update for 16x32 Panels
* there is a virtual panel available to work with 16x32 panels (see: [examples/16x32 Panel](/32x16_1_4_ScanPanel). This Panel includes drawing lines and rectanges, text and scrolling text
# Installation
@ -39,7 +48,7 @@ By default the pin mapping is as follows (defaults defined in ESP32-HUB75-Matrix
+-----------+
```
However, if you want to change this, simply provide the wanted pin mapping as part of the display.begin() call. For example, in your sketch have something like the following:
However, if you want to change this, simply provide the wanted pin mapping as part of the class initialization structure. For example, in your sketch have something like the following:
```
// Change these to whatever suits
@ -61,8 +70,14 @@ However, if you want to change this, simply provide the wanted pin mapping as pa
#define CLK_PIN 16
display.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
HUB75_I2S_CFG mxconfig(
64, // Module width
32, // Module height
2, // chain length
_pins, // pin mapping
);
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
```
The panel must be powered by 5V AC adapter with enough current capacity. (Current varies due to how many LED are turned on at the same time. To drive all the LEDs, you need 5V4A adapter.)
@ -79,8 +94,7 @@ MatrixPanel_I2S_DMA matrix;
void setup()
{
// MUST DO THIS FIRST!
matrix.begin(); // Use default pins supplied within ESP32-HUB75-MatrixPanel-I2S-DMA.h
// matrix.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // or custom pins
matrix.begin(); // Use default values for matrix dimentions and pins supplied within ESP32-HUB75-MatrixPanel-I2S-DMA.h
// Draw a single white pixel
matrix.drawPixel(0,0, matrix.color565(255,255,255)); // can do this after .begin() only
@ -92,6 +106,20 @@ void loop()
```
### Build-time options
Although Arduino IDE does not seem to offer any way of specifying compile-time options for external libs there are other IDE's (like PlatformIO/Eclipse) that could use that. This lib supports the following compile-time defines
**USE_GFX_ROOT** - Use lightweight version of AdafuitGFX, without Adafruit BusIO extensions
**NO_GFX** - Build without AdafuitGFX, only native methods supported based on manipulating DMA buffer. I.e. no methods of drawing circles/shapes, typing text or using fonts!!!
This might save some resources for applications using it's own internal graphics buffer or works solely with per-pixel manipulation. For example Aurora effects can work fine w/o AdafruitGFX.
**NO_FAST_FUNCTIONS** - do not build auxiliary speed-optimized functions. Those are used to speed-up operations like drawing straight lines or rectangles. Otherwise lines/shapes are drawn using drawPixel() method. The trade-off for speed is RAM/code-size, take it or leave it ;)
## Can I use with a larger panel (i.e. 64x64px square panel)?
If you want to use with a 64x64 pixel panel (typically a HUB75*E* panel) you MUST configure a valid *E_PIN* to your ESP32 and connect it to the E pin of the HUB75 panel! Hence the 'E' in 'HUB75E'
@ -115,9 +143,9 @@ Resolutions beyond 128x128 are likely to result in crashes due to memory constra
## Panel Brightness
By default you should not need to change / set the brightness setting as the default value (16) is sufficent for most purposes. Brightness can be changed by calling `setPanelBrightness(XX)` and then `clearScreen()`.
By default you should not need to change / set the brightness setting as the default value (16) is sufficient for most purposes. Brightness can be changed by calling `setPanelBrightness(int XX)` or `setBrightness8(uint8_t XX)`.
The value to pass 'setPanelBrightness' must be a value less than MATRIX_WIDTH. For example for a single 64x32 LED Matrix Module, a value less than 64. However, if you set the brightness too high, you may experience ghosting.
The value to pass 'setPanelBrightness' must be a value less than MATRIX_CHAIN_WIDTH in pixels. For example for a single 64x32 LED Matrix Module, a value must be less than 64. However, if you set the brightness too high, you may experience ghosting.
Also you may use method `setPanelBrightness8(x)`, where x is a uint8_t value between 0-255. Library will recalculate required brightness level depending on matrix width (mostly useful with FastLED-based sketches).
@ -126,13 +154,11 @@ Example:
```
void setup() {
Serial.begin(115200);
matrix.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
matrix.begin(); // setup the LED matrix
matrix.setPanelBrightness(16); // Set the brightness. 32 or lower ideal for a single 64x32 LED Matrix Panel.
matrix.clearScreen(); // You must clear the screen after changing brightness level for it to take effect.
// or another way
matrix.setPanelBrightness(192); // Set the brightness to about 3/4 (192/256) of maximum.
matrix.clearScreen(); // You must clear the screen after changing brightness level for it to take effect.
matrix.setPanelBrightness8(192); // Set the brightness to about 3/4 or 75% (192/256) of maximum.
}
```
@ -140,6 +166,18 @@ Summary: setPanelBrightness(xx) value can be any number from 0 (display off) to
![It's better in real life](image.jpg)
## Latch blanking
If you face issues with image ghosting when pixels has clones with horizontal offset, than you try to change Latch blanking value. Latch blanking controls
for how many clock pulses matrix output is disabled via EO signal before/after toggling LAT signal. It hides row bits transitioning and different panels may
require longer times for proper operation. Default value is 1 clock before/after LAT row transition. This could be controlled with `MatrixPanel_I2S_DMA::setLatBlanking(uint8_t v)`. v could be between 1 to 4, default is 1, larger values won't give any benefit other than reducing brightness.
An example:
```
matrix.setLatBlanking(2);
```
## Power, Power and Power!
Having a good power supply is CRITICAL, and it is highly recommended, for chains of LED Panels to have a 2000uf capacitor soldered to the back of each LED Panel across the [GND and VCC pins](https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/39#issuecomment-720780463), otherwise you WILL run into issues with 'flashy' graphics whereby a large amount of LEDs are turned on and off in succession (due to current/power draw peaks and troughs).

View file

@ -24,11 +24,9 @@
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "soc/i2s_struct.h"
#include "soc/i2s_reg.h"
#include "driver/periph_ctrl.h"
#include "soc/io_mux_reg.h"
#include "rom/lldesc.h"
//#include "esp_heap_caps.h"
#include "esp32_i2s_parallel.h"
@ -208,18 +206,17 @@ void i2s_parallel_setup_without_malloc(i2s_dev_t *dev, const i2s_parallel_config
dev->fifo_conf.val=0;
dev->fifo_conf.rx_fifo_mod_force_en=1;
dev->fifo_conf.tx_fifo_mod_force_en=1;
//dev->fifo_conf.tx_fifo_mod=1;
dev->fifo_conf.tx_fifo_mod=1;
dev->fifo_conf.tx_fifo_mod=1; // 16-bit sigle channel mode
dev->fifo_conf.rx_data_num=32; //Thresholds.
dev->fifo_conf.tx_data_num=32;
dev->fifo_conf.dscr_en=1;
dev->fifo_conf.dscr_en=1; // FIFO will pump the data from DMA
dev->conf1.val=0;
dev->conf1.tx_stop_en=0;
dev->conf1.tx_pcm_bypass=1;
dev->conf_chan.val=0;
dev->conf_chan.tx_chan_mod=1;
dev->conf_chan.tx_chan_mod=1; // Mono
dev->conf_chan.rx_chan_mod=1;
//Invert ws to be active-low... ToDo: make this configurable
@ -241,18 +238,19 @@ void i2s_parallel_setup_without_malloc(i2s_dev_t *dev, const i2s_parallel_config
st->dmadesc_b = cfg->lldesc_b;
//Reset FIFO/DMA -> needed? Doesn't dma_reset/fifo_reset do this?
/*
dev->lc_conf.in_rst=1; dev->lc_conf.out_rst=1; dev->lc_conf.ahbm_rst=1; dev->lc_conf.ahbm_fifo_rst=1;
dev->lc_conf.in_rst=0; dev->lc_conf.out_rst=0; dev->lc_conf.ahbm_rst=0; dev->lc_conf.ahbm_fifo_rst=0;
dev->conf.tx_reset=1; dev->conf.tx_fifo_reset=1; dev->conf.rx_fifo_reset=1;
dev->conf.tx_reset=0; dev->conf.tx_fifo_reset=0; dev->conf.rx_fifo_reset=0;
*/
// setup I2S Interrupt
SET_PERI_REG_BITS(I2S_INT_ENA_REG(1), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S);
// allocate a level 1 intterupt: lowest priority, as ISR isn't urgent and may take a long time to complete
esp_intr_alloc(ETS_I2S1_INTR_SOURCE, (int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1), i2s_isr, NULL, NULL);
//Start dma on front buffer (buffer a)
dev->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN;
dev->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN;
dev->out_link.addr=((uint32_t)(&st->dmadesc_a[0]));
dev->out_link.start=1;
dev->conf.tx_start=1;

View file

@ -1,7 +1,7 @@
#ifndef I2S_PARALLEL_H
#define I2S_PARALLEL_H
#if defined(ESP32)
#if defined(ESP32) || defined(IDF_VER)
#include <stdint.h>
@ -9,8 +9,14 @@
extern "C" {
#endif
#include "driver/gpio.h"
#include "soc/i2s_struct.h"
#include "rom/lldesc.h"
#if defined(IDF_VER)
#include "esp32/rom/lldesc.h"
#elif defined(ESP32)
#include "rom/lldesc.h"
#endif
#define DMA_MAX (4096-4)
//#define DMA_MAX (512)

View file

@ -1,9 +1,23 @@
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
MatrixPanel_I2S_DMA dma_display;
// Example sketch which shows how to display some patterns
// on a 64x32 LED matrix
//
// Or use an Alternative non-DMA library, i.e:
//#include <P3RGB64x32MatrixPanel.h>
//P3RGB64x32MatrixPanel display;
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
/*
* Below is an example of the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. Matrix Width and Height will need to be confirmed as compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths.
*
*/
MatrixPanel_I2S_DMA dma_display;
// Or use an Alternative non-DMA library, i.e:
//#include <P3RGB64x32MatrixPanel.h>
//P3RGB64x32MatrixPanel display;
void setup() {
@ -94,26 +108,6 @@ void setup() {
dma_display.setTextColor(dma_display.color444(15,0,8));
dma_display.println("*");
delay(2000);
/*
for (int i = 15; i > 0; i--)
{
// fade out
dma_display.fillScreen(dma_display.color565(0, 0, i*17));
delay(250);
}
for (int i = 15; i > 0; i--)
{
// draw a blue circle
dma_display.drawCircle(10, 10, 10, dma_display.color565(i*17, 0, 0));
delay(250);
}
*/
// whew!
}
void loop() {

View file

@ -0,0 +1,207 @@
/*
* Portions of this code are adapted from Aurora: https://github.com/pixelmatix/aurora
* Copyright (c) 2014 Jason Coon
*
* Portions of this code are adapted from LedEffects Plasma by Robert Atkins: https://bitbucket.org/ratkins/ledeffects/src/26ed3c51912af6fac5f1304629c7b4ab7ac8ca4b/Plasma.cpp?at=default
* Copyright (c) 2013 Robert Atkins
*
* 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:
*
* The above copyright notice and 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.
*/
// HUB75E pinout
// R1 | G1
// B1 | GND
// R2 | G2
// B2 | E
// A | B
// C | D
// CLK| LAT
// OE | GND
/* Default library pin configuration for the reference
you can redefine only ones you need later on object creation
#define R1 25
#define G1 26
#define BL1 27
#define R2 14
#define G2 12
#define BL2 13
#define CH_A 23
#define CH_B 19
#define CH_C 5
#define CH_D 17
#define CH_E -1 // assign to any available pin if using two panels or 64x64 panels with 1/32 scan
#define CLK 16
#define LAT 4
#define OE 15
*/
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include <FastLED.h>
// Configure for your panel(s) as appropriate!
#define PANEL_WIDTH 64
#define PANEL_HEIGHT 64 // Panel height of 64 will required PIN_E to be defined.
#define PANELS_NUMBER 2 // Number of chained panels, if just a single panel, obviously set to 1
#define PIN_E 32
#define PANE_WIDTH PANEL_WIDTH * PANELS_NUMBER
#define PANE_HEIGHT PANEL_HEIGHT
// placeholder for the matrix object
MatrixPanel_I2S_DMA *dma_display = nullptr;
uint16_t time_counter = 0, cycles = 0, fps = 0;
unsigned long fps_timer;
CRGB currentColor;
CRGBPalette16 palettes[] = {HeatColors_p, LavaColors_p, RainbowColors_p, RainbowStripeColors_p, CloudColors_p};
CRGBPalette16 currentPalette = palettes[0];
CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlendType blendType = LINEARBLEND) {
return ColorFromPalette(currentPalette, index, brightness, blendType);
}
void setup() {
Serial.begin(115200);
Serial.println(F("*****************************************************"));
Serial.println(F("* ESP32-HUB75-MatrixPanel-I2S-DMA DEMO *"));
Serial.println(F("*****************************************************"));
/*
The configuration for MatrixPanel_I2S_DMA object is held in HUB75_I2S_CFG structure,
pls refer to the lib header file for full details.
All options has it's predefined default values. So we can create a new structure and redefine only the options we need
// those are the defaults
mxconfig.mx_width = 64; // physical width of a single matrix panel module (in pixels, usually it is always 64 ;) )
mxconfig.mx_height = 32; // physical height of a single matrix panel module (in pixels, usually amost always it is either 32 or 64)
mxconfig.chain_length = 1; // number of chained panels regardless of the topology, default 1 - a single matrix module
mxconfig.gpio.r1 = R1; // pin mappings
mxconfig.gpio.g1 = G1;
mxconfig.gpio.b1 = B1; // etc
mxconfig.driver = HUB75_I2S_CFG::SHIFT; // shift reg driver, default is plain shift register
mxconfig.double_buff = false; // use double buffer (twice amount of RAM required)
mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M;// I2S clock speed, better leave as-is unless you want to experiment
*/
/*
For example we have two 64x64 panels chained, so we need to customize our setup like this
*/
HUB75_I2S_CFG mxconfig;
mxconfig.mx_height = PANEL_HEIGHT; // we have 64 pix heigh panels
mxconfig.chain_length = PANELS_NUMBER; // we have 2 panels chained
mxconfig.gpio.e = PIN_E; // we MUST assign pin e to some free pin on a board to drive 64 pix height panels with 1/32 scan
//mxconfig.driver = HUB75_I2S_CFG::FM6126A; // in case that we use panels based on FM6126A chip, we can change that
/*
//Another way of creating config structure
//Custom pin mapping for all pins
HUB75_I2S_CFG::i2s_pins _pins={R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK};
HUB75_I2S_CFG mxconfig(
64, // width
64, // height
4, // chain length
_pins, // pin mapping
HUB75_I2S_CFG::FM6126A // driver chip
);
*/
// OK, now we can create our matrix object
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
// let's adjust default brightness to about 75%
dma_display->setBrightness8(96); // range is 0-255, 0 - 0%, 255 - 100%
// Allocate memory and start DMA display
if( not dma_display->begin() )
Serial.println("****** !KABOOM! I2S memory allocation failed ***********");
// well, hope we are OK, let's draw some colors first :)
Serial.println("Fill screen: RED");
dma_display->fillScreenRGB888(255, 0, 0);
delay(1000);
Serial.println("Fill screen: GREEN");
dma_display->fillScreenRGB888(0, 255, 0);
delay(1000);
Serial.println("Fill screen: BLUE");
dma_display->fillScreenRGB888(0, 0, 255);
delay(1000);
Serial.println("Fill screen: Neutral White");
dma_display->fillScreenRGB888(64, 64, 64);
delay(1000);
Serial.println("Fill screen: black");
dma_display->fillScreenRGB888(0, 0, 0);
delay(1000);
// Set current FastLED palette
currentPalette = RainbowColors_p;
Serial.println("Starting plasma effect...");
fps_timer = millis();
}
void loop() {
for (int x = 0; x < PANE_WIDTH; x++) {
for (int y = 0; y < PANE_HEIGHT; y++) {
int16_t v = 0;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
v += cos16(y * (128 - wibble) + time_counter);
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8) + 127); //, brightness, currentBlendType);
dma_display->drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}
++time_counter;
++cycles;
++fps;
if (cycles >= 1024) {
time_counter = 0;
cycles = 0;
currentPalette = palettes[random(0,sizeof(palettes)/sizeof(palettes[0]))];
}
// print FPS rate every 5 seconds
// Note: this is NOT a matrix refresh rate, it's the number of data frames being drawn to the DMA buffer per second
if (fps_timer + 5000 < millis()){
Serial.printf_P(PSTR("Effect fps: %d\n"), fps/5);
fps_timer = millis();
fps = 0;
}
} // end loop

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -3,47 +3,23 @@
#include <Arduino.h>
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
MatrixPanel_I2S_DMA dma_display;
#include <FastLED.h>
////////////////////////////////////////////////////////////////////
// Reset Panel
// FM6126 support is still experimental
//
// pinout for ESP38 38pin module
// http://arduinoinfo.mywikis.net/wiki/Esp32#KS0413_keyestudio_ESP32_Core_Board
//
// HUB75E pinout
// R1 | G1
// B1 | GND
// R2 | G2
// B2 | E
// A | B
// C | D
// CLK| LAT
// OE | GND
// Output resolution and panel chain length configuration
#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 PANEL_CHAIN 1 // Total number of panels chained one to another
#define R1 25
#define G1 26
#define BL1 27
#define R2 14 // 21 SDA
#define G2 12 // 22 SDL
#define BL2 13
#define CH_A 23
#define CH_B 19
#define CH_C 5
#define CH_D 17
#define CH_E -1 // assign to any available pin if using two panels or 64x64 panels with 1/32 scan (i.e. 32 works fine)
#define CLK 16
#define LAT 4
#define OE 15
// End of default setup for RGB Matrix 64x32 panel
// placeholder for the matrix object
MatrixPanel_I2S_DMA *dma_display = nullptr;
///////////////////////////////////////////////////////////////
// FastLED variables for pattern output
uint16_t time_counter = 0, cycles = 0, fps = 0;
unsigned long fps_timer;
@ -57,38 +33,44 @@ CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlend
}
void setup(){
/*
The configuration for MatrixPanel_I2S_DMA object is held in HUB75_I2S_CFG structure,
All options has it's predefined default values. So we can create a new structure and redefine only the options we need
Please refer to the '2_PatternPlasma.ino' example for detailed example of how to use the MatrixPanel_I2S_DMA configuration
if you need to change the pin mappings etc.
*/
HUB75_I2S_CFG mxconfig(
PANEL_RES_X, // module width
PANEL_RES_Y, // module height
PANEL_CHAIN // Chain length
);
mxconfig.driver = HUB75_I2S_CFG::FM6126A; // in case that we use panels based on FM6126A chip, we can set it here before creating MatrixPanel_I2S_DMA object
// OK, now we can create our matrix object
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
// If you experience ghosting, you will need to reduce the brightness level, not all RGB Matrix
// Panels are the same - some seem to display ghosting artefacts at lower brightness levels.
// In the setup() function do something like:
// SETS THE BRIGHTNESS HERE. MAX value is MATRIX_WIDTH, 2/3 OR LOWER IDEAL, default is about 50%
// dma_display.setPanelBrightness(30);
/* another way to change brightness is to use
* dma_display.setPanelBrightness8(uint8_t brt); // were brt is within range 0-255
* it will recalculate to consider matrix width automatically
*/
//dma_display.setPanelBrightness8(180);
/**
* this demo runs pretty fine in fast-mode which gives much better fps on large matrixes (>128x64)
* see comments in the lib header on what does that means
*/
dma_display.setFastMode(true);
/**
* Run display on our matrix, be sure to specify 'FM6126A' as last parametr to the begin(),
* it would reset 6126 registers and enables the matrix
*/
dma_display.begin(R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK, FM6126A);
// let's adjust default brightness to about 75%
dma_display->setBrightness8(96); // range is 0-255, 0 - 0%, 255 - 100%
// Allocate memory and start DMA display
if( not dma_display->begin() )
Serial.println("****** !KABOOM! Insufficient memory - allocation failed ***********");
fps_timer = millis();
}
void loop(){
for (int x = 0; x < dma_display.width(); x++) {
for (int y = 0; y < dma_display.height(); y++) {
for (int x = 0; x < dma_display->width(); x++) {
for (int y = 0; y < dma_display->height(); y++) {
int16_t v = 0;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
@ -96,7 +78,7 @@ void loop(){
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8) + 127); //, brightness, currentBlendType);
dma_display.drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
dma_display->drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}

View file

@ -25,7 +25,18 @@
// ----------------------------
/*
* Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths within the sketch 'setup()'.
*
*/
MatrixPanel_I2S_DMA dma_display;
AnimatedGIF gif;
File f;
int x_offset, y_offset;

View file

@ -22,6 +22,16 @@
/* -------------------------- Class Initialisation -------------------------- */
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
/*
* Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths within the sketch 'setup()'.
*
*/
MatrixPanel_I2S_DMA matrix;
#include <FastLED.h>

View file

@ -27,7 +27,15 @@
#define CLK_PIN 16
// Display
/*
* Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths within the sketch 'setup()'.
*
*/
MatrixPanel_I2S_DMA display; // RGB Panel
// Wifi Logo, generated using LCD Image Converter: http://www.riuson.com/lcd-image-converter

View file

@ -3,19 +3,9 @@
Steps to use
-----------
1) In ESP32-HUB75-MatrixPanel-I2S-DMA.h:
- Set the MATRIX_HEIGHT to be the y resolution of a physical chained
panels in the line (as panels each must be of the same dimensions).
i.e. If you are chaining 32px 'high' panels, then set MATRIX_HEIGHT to 32.
- Set the MATRIX_WIDTH to be the sum of the x resolution of all the physical
chained panels (i.e. If you have 4 (four) x (64px w x 32px h) panels, then 64x4 = 256)
i.e. The total pixel 'width' of all the chained panels.
2) In the sketch (i.e. this example):
1) In the sketch (i.e. this example):
- Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y.
- Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, PANEL_CHAIN.
There are comments beside them explaining what they are in more detail.
- Other than where the matrix is defined and matrix.begin in the setup, you
should now be using the virtual display for everything (drawing pixels, writing text etc).
@ -39,25 +29,6 @@
*****************************************************************************/
//#define USE_CUSTOM_PINS // uncomment to use custom pins, then provide below
#define A_PIN 26
#define B_PIN 4
#define C_PIN 27
#define D_PIN 2
#define E_PIN 21
#define R1_PIN 5
#define R2_PIN 19
#define G1_PIN 17
#define G2_PIN 16
#define B1_PIN 18
#define B2_PIN 25
#define CLK_PIN 14
#define LAT_PIN 15
#define OE_PIN 13
/******************************************************************************
* VIRTUAL DISPLAY / MATRIX PANEL CHAINING CONFIGURATION
@ -168,14 +139,16 @@
#define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
#define PANEL_CHAIN NUM_ROWS*NUM_COLS // total number of panels chained one to another
/******************************************************************************
* Create physical DMA panel class AND virtual (chained) display class.
******************************************************************************/
// library includes
#include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
MatrixPanel_I2S_DMA dma_display;
VirtualMatrixPanel virtualDisp(dma_display, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true);
// placeholder for the matrix object
MatrixPanel_I2S_DMA *dma_display = nullptr;
// placeholder for the virtual display object
VirtualMatrixPanel *virtualDisp = nullptr;
/******************************************************************************
@ -190,29 +163,47 @@ void setup() {
Serial.println(" HELLO !");
Serial.println("*****************************************************");
#ifdef USE_CUSTOM_PINS
dma_display.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
#else
dma_display.begin();
#endif
/******************************************************************************
* Create physical DMA panel class AND virtual (chained) display class.
******************************************************************************/
/*
The configuration for MatrixPanel_I2S_DMA object is held in HUB75_I2S_CFG structure,
All options has it's predefined default values. So we can create a new structure and redefine only the options we need
Please refer to the '2_PatternPlasma.ino' example for detailed example of how to use the MatrixPanel_I2S_DMA configuration
*/
HUB75_I2S_CFG mxconfig(
PANEL_RES_X, // module width
PANEL_RES_Y, // module height
PANEL_CHAIN // chain length
);
//mxconfig.driver = HUB75_I2S_CFG::FM6126A; // in case that we use panels based on FM6126A chip, we can set it here before creating MatrixPanel_I2S_DMA object
// Sanity checks
if (NUM_ROWS <= 1) {
Serial.println(F("There is no reason to use the VirtualDisplay class for a single horizontal chain and row!"));
}
if (dma_display.width() != NUM_ROWS*NUM_COLS*PANEL_RES_X )
{
Serial.println(F("\r\nERROR: MATRIX_WIDTH and/or MATRIX_HEIGHT in 'ESP32-HUB75-MatrixPanel-I2S-DMA.h'\r\nis not configured correctly for the requested VirtualMatrixPanel dimensions!\r\n"));
Serial.printf("WIDTH according dma_display is %d, but should be %d. Is your NUM_ROWS and NUM_COLS correct?\r\n", dma_display.width(), NUM_ROWS*NUM_COLS*PANEL_RES_X);
return;
}
// OK, now we can create our matrix object
dma_display = new MatrixPanel_I2S_DMA(mxconfig);
// let's adjust default brightness to about 75%
dma_display->setBrightness8(96); // range is 0-255, 0 - 0%, 255 - 100%
// Allocate memory and start DMA display
if( not dma_display->begin() )
Serial.println("****** !KABOOM! I2S memory allocation failed ***********");
// create VirtualDisplay object based on our newly created dma_display object
virtualDisp = new VirtualMatrixPanel((*dma_display), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true);
// So far so good, so continue
virtualDisp.fillScreen(virtualDisp.color444(0, 0, 0));
virtualDisp.drawDisplayTest(); // draw text numbering on each screen to check connectivity
virtualDisp->fillScreen(virtualDisp->color444(0, 0, 0));
virtualDisp->drawDisplayTest(); // draw text numbering on each screen to check connectivity
delay(3000);
@ -225,20 +216,17 @@ void setup() {
Serial.println("| (ESP) | |");
Serial.println("+--------+---------+");
virtualDisp.setFont(&FreeSansBold12pt7b);
virtualDisp.setTextColor(virtualDisp.color565(0, 0, 255));
virtualDisp.setTextSize(2);
virtualDisp.setCursor(10, virtualDisp.height()-20);
virtualDisp->setFont(&FreeSansBold12pt7b);
virtualDisp->setTextColor(virtualDisp->color565(0, 0, 255));
virtualDisp->setTextSize(2);
virtualDisp->setCursor(10, virtualDisp->height()-20);
// Red text inside red rect (2 pix in from edge)
virtualDisp.print("1234") ;
virtualDisp.drawRect(1,1, virtualDisp.width()-2, virtualDisp.height()-2, virtualDisp.color565(255,0,0));
virtualDisp->print("1234");
virtualDisp->drawRect(1,1, virtualDisp->width()-2, virtualDisp->height()-2, virtualDisp->color565(255,0,0));
// White line from top left to bottom right
virtualDisp.drawLine(0,0, virtualDisp.width()-1, virtualDisp.height()-1, virtualDisp.color565(255,255,255));
virtualDisp->drawLine(0,0, virtualDisp->width()-1, virtualDisp->height()-1, virtualDisp->color565(255,255,255));
}
void loop() {

View file

@ -1,35 +1,33 @@
## Chained Panels example - Chaining individual LED matrix panels to make a larger panel ##
This is the PatternPlasma Demo adopted for use with multiple
displays arranged in a non standard order
This is the PatternPlasma Demo adopted for use with multiple LED Matrix Panel displays arranged in a non standard order (i.e. a grid) to make a bigger display.
### What is a non standard order? ###
### What do we mean by 'non standard order'? ###
When you connect multiple panels together, the library treats the
multiple panels as one big panel arranged horizontally. Arranging
the displays like this would be a standard order.
When you link / chain multiple panels together, the ESP32-HUB75-MatrixPanel-I2S-DMA library treats as one wide horizontal panel. This would be a 'standard' (default) order.
Non-standard order is essentially the creation of a non-horizontal only 'virtual' display that you can draw to in the same way you would
the matrix, but with VirtualDisplay library looking after the pixel mapping to the physical chained panels.
Non-standard order is essentially the creation of a non-horizontal-only display that you can draw to in the same way you would any other display, with VirtualDisplay library looking after the pixel mapping to the physical chained panels.
For example: You bought four (4) 64x32px panels, and wanted to use them to create a 128x64pixel display. You would use the VirtualMatrixPanel class.
[Refer to this document](VirtualMatrixPanel.pdf) for an explanation and refer to this example on how to use.
[Refer to this document](VirtualDisplay.pdf) for the guide on how to use.
### Steps to Use ###
1) In ESP32-HUB75-MatrixPanel-I2S-DMA.h:
1. [Refer to this document](VirtualMatrixPanel.pdf) for an explanation and refer to this example on how to use.
- Set the MATRIX_HEIGHT to be the y resolution of the physical chained panels in a line (if the panels are 32 x 16, set it to be 16)
- Set the MATRIX_WIDTH to be the sum of the x resolution of all the physical chained panels (i.e. If you have 4 x (32px w x 16px h) panels, 32x4 = 128)
2. In your Arduino sketch, configure these defines accordingly:
```
#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.
2) In the sketch:
#define NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
```
3. In your Arduino sketch, use the 'VirtualMatrixPanel' class instance (virtualDisp) to draw to the display (i.e. drawPixel), instead of the underling MatrixPanel_I2S_DMA class instance (dma_display).
- Set values for NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y. There are comments beside them
explaining what they are in more detail.
- Other than where the matrix is defined and matrix.begin in the setup, you should now be using the virtual display
for everything (drawing pixels, writing text etc). You can do a find and replace of all calls if it's an existing sketch
(just make sure you don't replace the definition and the matrix.begin)
- If the sketch makes use of MATRIX_HEIGHT or MATRIX_WIDTH, these will need to be replaced with the width and height
of your virtual screen. Either make new defines and use that, or you can use virtualDisp.width() or .height()
#### Thanks to ####
* Brian Lough for the Virtual to Real pixel co-ordinate code.

Binary file not shown.

Binary file not shown.

View file

@ -1,38 +1,42 @@
/* ------------------------- CUSTOM GPIO PIN MAPPING ------------------------- */
// Kosso's ESP32 Matrix PCB pins
#define R1_PIN 2
#define G1_PIN 15
#define B1_PIN 4
#define R2_PIN 16
#define G2_PIN 13
#define B2_PIN 17
#define A_PIN 5
#define B_PIN 18
#define C_PIN 19
#define D_PIN 27
#define E_PIN -1
#define LAT_PIN 21
#define OE_PIN 23
#define CLK_PIN 22
#include <Arduino.h>
/* Default library pin configuration for the reference
you can redefine only ones you need later on object creation
#define R1 25
#define G1 26
#define BL1 27
#define R2 14
#define G2 12
#define BL2 13
#define CH_A 23
#define CH_B 19
#define CH_C 5
#define CH_D 17
#define CH_E -1 // assign to any available pin if using two panels or 64x64 panels with 1/32 scan
#define CLK 16
#define LAT 4
#define OE 15
*/
/* -------------------------- Display Config Initialisation -------------------- */
// MATRIX_WIDTH and MATRIX_HEIGHT *must* be changed in ESP32-HUB75-MatrixPanel-I2S-DMA.h
// If you are using Platform IO (you should), pass MATRIX_WIDTH and MATRIX_HEIGHT as a compile time option.
// Refer to: https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/48#issuecomment-749402379
//This will not work here -> #define MATRIX_WIDTH 128 // Overall matrix dimensions if laid out end-to-end.
//This will not work here -> #define MATRIX_HEIGHT 32
// Assume we have four 64x32 panels daizy-chained and ESP32 attached to the bottom right corner
#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 NUM_ROWS 2 // Number of rows of chained INDIVIDUAL PANELS
#define NUM_COLS 1 // Number of INDIVIDUAL PANELS per ROW
#define NUM_COLS 2 // Number of INDIVIDUAL PANELS per ROW
#define PANEL_CHAIN NUM_ROWS*NUM_COLS // total number of panels chained one to another
// Virtual Panl dimensions
#define VPANEL_W 64 // Kosso: All Pattern files have had the MATRIX_WIDTH and MATRIX_HEIGHT replaced by these.
#define VPANEL_H 64 //
// Change this to your needs, for details on VirtualPanel pls see ChainedPanels example
#define SERPENT false
#define TOPDOWN false
// Virtual Panl dimensions - our combined panel would be a square 4x4 modules with a combined resolution of 128x128 pixels
#define VPANEL_W PANEL_RES_X*NUM_COLS // Kosso: All Pattern files have had the MATRIX_WIDTH and MATRIX_HEIGHT replaced by these.
#define VPANEL_H PANEL_RES_Y*NUM_ROWS //
// Kosso added: Button with debounce
#define BTN_PIN 0 // Pattern advance. Using EPS32 Boot button.
@ -50,32 +54,33 @@ int lastPattern = 0;
/* -------------------------- Class Initialisation -------------------------- */
//#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
//MatrixPanel_I2S_DMA matrix;
#include <ESP32-VirtualMatrixPanel-I2S-DMA.h>
MatrixPanel_I2S_DMA matrix;
// Added support for 'Chained' virtual panel configurations.
VirtualMatrixPanel virtualDisp(matrix, NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, true);
#include <FastLED.h> // Used for some mathematics calculations and effects.
// placeholder for the matrix object
MatrixPanel_I2S_DMA *matrix = nullptr;
// placeholder for the virtual display object
VirtualMatrixPanel *virtualDisp = nullptr;
// Aurora related
#include "Effects.h"
Effects effects;
#include "Drawable.h"
#include "Playlist.h"
//#include "Geometry.h"
#include "Patterns.h"
Patterns patterns;
/* -------------------------- Some variables -------------------------- */
unsigned long ms_current = 0;
unsigned long ms_previous = 0;
unsigned long ms_animation_max_duration = 60000; // 10 seconds
unsigned long ms_animation_max_duration = 20000; // 10 seconds
unsigned long next_frame = 0;
void listPatterns();
void setup()
{
// Setup serial interface
@ -87,19 +92,37 @@ void setup()
// For saving last pattern index. TO reboot with same.
preferences.begin("RGBMATRIX", false);
lastPattern = preferences.getInt("lastPattern", 0);
matrix.setPanelBrightness(30);
matrix.setMinRefreshRate(200);
matrix.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
virtualDisp.fillScreen(virtualDisp.color444(0, 0, 0));
// Configure your matrix setup here
HUB75_I2S_CFG mxconfig(PANEL_RES_X, PANEL_RES_Y, PANEL_CHAIN);
// custom pin mapping (if required)
//HUB75_I2S_CFG::i2s_pins _pins={R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK};
//mxconfig.gpio = _pins;
// in case that we use panels based on FM6126A chip, we can change that
//mxconfig.driver = HUB75_I2S_CFG::FM6126A;
// FM6126A panels could be cloked at 20MHz with no visual artefacts
// mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_20M;
// OK, now we can create our matrix object
matrix = new MatrixPanel_I2S_DMA(mxconfig);
// let's adjust default brightness to about 75%
matrix->setBrightness8(96); // range is 0-255, 0 - 0%, 255 - 100%
// Allocate memory and start DMA display
if( not matrix->begin() )
Serial.println("****** !KABOOM! I2S memory allocation failed ***********");
// create VirtualDisplay object based on our newly created dma_display object
virtualDisp = new VirtualMatrixPanel((*matrix), NUM_ROWS, NUM_COLS, PANEL_RES_X, PANEL_RES_Y, SERPENT, TOPDOWN);
Serial.println("**************** Starting Aurora Effects Demo ****************");
Serial.println("MATRIX_WIDTH " + String(MATRIX_WIDTH));
Serial.println("MATRIX_HEIGHT " + String(MATRIX_HEIGHT));
Serial.print("MATRIX_WIDTH: "); Serial.println(PANEL_RES_X*PANEL_CHAIN);
Serial.print("MATRIX_HEIGHT: "); Serial.println(PANEL_RES_Y);
#ifdef VPANEL_W
Serial.println("VIRTUAL PANEL WIDTH " + String(VPANEL_W));
@ -169,7 +192,7 @@ void loop()
if ( (ms_current - ms_previous) > ms_animation_max_duration )
{
// patternAdvance();
patternAdvance();
// just auto-change the palette
effects.RandomPalette();
ms_previous = ms_current;

View file

@ -38,7 +38,7 @@ public:
// a single frame should be drawn as fast as possible, without any delay or blocking
// return how many millisecond delay is requested before the next call to drawFrame()
virtual unsigned int drawFrame() {
matrix.fillScreen(0);
matrix->fillScreen(0);
//backgroundLayer.fillScreen({ 0, 0, 0 });
return 0;
};

View file

@ -112,8 +112,6 @@ uint8_t noisesmoothing;
class Effects {
public:
CRGB *leds;
//CRGB leds[NUM_LEDS];
//CRGB leds2[NUM_LEDS]; // Faptastic: getting rid of this and any dependant effects or algos. to save memory 24*64*32 bytes of ram (50k).
Effects(){
// we do dynamic allocation for leds buffer, otherwise esp32 toolchain can't link static arrays of such a big size for 256+ matrixes
@ -127,7 +125,6 @@ public:
}
ClearFrame();
matrix.clearScreen();
}
~Effects(){
free(leds);
@ -175,7 +172,7 @@ public:
for (int x=0; x<VPANEL_W; ++x){
//Serial.printf("Flushing x, y coord %d, %d\n", x, y);
uint16_t _pixel = XY16(x,y);
virtualDisp.drawPixelRGB888( x, y, leds[_pixel].r, leds[_pixel].g, leds[_pixel].b);
virtualDisp->drawPixelRGB888( x, y, leds[_pixel].r, leds[_pixel].g, leds[_pixel].b);
} // end loop to copy fast led to the dma matrix
}
}

View file

@ -36,7 +36,7 @@ class PatternInvadersSmall : public Drawable {
}
void start() {
matrix.fillScreen(0);
matrix->fillScreen(0);
}
unsigned int drawFrame() {
@ -80,7 +80,7 @@ class PatternInvadersMedium : public Drawable {
}
void start() {
matrix.fillScreen(0);
matrix->fillScreen(0);
}
unsigned int drawFrame() {
@ -92,10 +92,10 @@ class PatternInvadersMedium : public Drawable {
if (random(0, 2) == 1) color = color1;
matrix.fillRect(x + (i * 2), y + (j * 2), x + (i * 2 + 1), y + (j * 2 + 1), color);
matrix->fillRect(x + (i * 2), y + (j * 2), x + (i * 2 + 1), y + (j * 2 + 1), color);
if (i < 2)
matrix.fillRect(x + (8 - i * 2), y + (j * 2), x + (9 - i * 2), y + (j * 2 + 1), color);
matrix->fillRect(x + (8 - i * 2), y + (j * 2), x + (9 - i * 2), y + (j * 2 + 1), color);
}
}
@ -122,11 +122,11 @@ class PatternInvadersLarge : public Drawable {
}
void start() {
matrix.fillScreen(0);
matrix->fillScreen(0);
}
unsigned int drawFrame() {
matrix.fillScreen(0);
matrix->fillScreen(0);
CRGB color1 = effects.ColorFromCurrentPalette(random(0, 255));
@ -138,10 +138,10 @@ class PatternInvadersLarge : public Drawable {
color = color1;
}
matrix.fillRect(1 + x * 6, 1 + y * 6, 5 + x * 6, 5 + y * 6, color);
matrix->fillRect(1 + x * 6, 1 + y * 6, 5 + x * 6, 5 + y * 6, color);
if (x < 2)
matrix.fillRect(1 + (4 - x) * 6, 1 + y * 6, 5 + (4 - x) * 6, 5 + y * 6, color);
matrix->fillRect(1 + (4 - x) * 6, 1 + y * 6, 5 + (4 - x) * 6, 5 + y * 6, color);
}
}

View file

@ -12,7 +12,7 @@ class PatternTest : public Drawable {
unsigned int drawFrame() {
matrix.fillScreen(matrix.color565(128, 0, 0));
matrix->fillScreen(matrix->color565(128, 0, 0));
return 1000;
}
};

View file

@ -1,5 +1,15 @@
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
/*
* Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths within the sketch 'setup()'.
*
*/
MatrixPanel_I2S_DMA display(true); // Note the TRUE -> Turns of secondary buffer - "double buffering"!
// Double buffering is not enabled by default with the library.

View file

@ -33,8 +33,18 @@
#include "Layer.h" // Layer Library
#include "Fonts/FreeSansBold9pt7b.h" // include adafruit font
// Object creation
/*
* Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths within the sketch 'setup()'.
*
*/
MatrixPanel_I2S_DMA dma_display; // Create HUB75 DMA object
// Create FastLED based graphic 'layers'
Layer bgLayer(dma_display); // Create background Layer
Layer textLayer(dma_display); // Create foreground Layer

View file

@ -0,0 +1,223 @@
/*************************************************************************
* Contributor: https://github.com/mrRobot62
*
* Description:
*
* The underlying implementation of the ESP32-HUB75-MatrixPanel-I2S-DMA only
* supports output to 1/16 or 1/32 scan panels (two scan parallel scan lines)
* this is fixed and cannot be changed.
*
* However, it is possible to connect 1/4 scan panels to this same library and
* 'trick' the output to work correctly on these panels by way of adjusting the
* pixel co-ordinates that are 'sent' to the ESP32-HUB75-MatrixPanel-I2S-DMA
* library (in this example, it is the 'dmaOutput' class).
*
* This is done by way of the 'QuarterScanMatrixPanel.h' class that sends
* adjusted x,y co-ordinates to the underlying ESP32-HUB75-MatrixPanel-I2S-DMA
* library's drawPixel routine.
*
* Refer to the 'getCoords' function within 'QuarterScanMatrixPanel.h'
*
**************************************************************************/
// uncomment to use custom pins, then provide below
#define USE_CUSTOM_PINS
/* Pin 1,3,5,7,9,11,13,15 */
#define R1_PIN 25
#define B1_PIN 27
#define R2_PIN 14
#define B2_PIN 13
#define A_PIN 23
#define C_PIN 5
#define CLK_PIN 16
#define OE_PIN 15
/* Pin 2,6,10,12,14 */
#define G1_PIN 26
#define G2_PIN 12
#define B_PIN 19
#define D_PIN 17
#define LAT_PIN 4
#define E_PIN -1 // required for 1/32 scan panels
#include "QuarterScanMatrixPanel.h" // Virtual Display to re-map co-ordinates such that they draw correctly on a32x16 1/4 Scan panel
#include <Wire.h>
/*
* Below is an is the 'legacy' way of initialising the MatrixPanel_I2S_DMA class.
* i.e. MATRIX_WIDTH and MATRIX_HEIGHT are modified by compile-time directives.
* By default the library assumes a single 64x32 pixel panel is connected.
*
* Refer to the example '2_PatternPlasma' on the new / correct way to setup this library
* for different resolutions / panel chain lengths within the sketch 'setup()'.
*
*/
MatrixPanel_I2S_DMA dmaOutput;
// Create virtual 1/2 to 1/4 scan pixel co-ordinate mapping class.
QuarterScanMatrixPanel display(dmaOutput);
#include <FastLED.h>
int time_counter = 0;
int cycles = 0;
CRGBPalette16 currentPalette;
CRGB currentColor;
CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlendType blendType = LINEARBLEND) {
return ColorFromPalette(currentPalette, index, brightness, blendType);
}
typedef struct Matrix {
uint8_t x;
uint8_t y;
} Matrix;
Matrix matrix;
void testSimpleChars(uint16_t timeout) {
/** drawChar() **/
Serial.println("draw chars with drawChar()");
display.fillScreen(display.color444(0,0,0));
uint16_t myFGColor = display.color565(180,0,0);
uint16_t myBGColor = display.color565(0,50,0);
display.fillScreen(display.color444(0,0,0));
display.drawChar(0,0,'X',myFGColor, myFGColor,1);
display.drawChar(16,1,'Y',myFGColor, myBGColor,1);
display.drawChar(3,9,'Z',myFGColor, myFGColor,1);
display.drawChar(16,9,'4',display.color565(0,220,0), myBGColor,1);
delay(timeout);
}
void testSimpleCharString(uint16_t timeout) {
uint8_t x,y,w,h;
w = 6; h=8;
x = 0; y=0;
display.fillScreen(display.color444(0,0,0));
display.setTextFGColor(display.color565(0,60,180));
display.setCursor(x,y); display.write('L');
display.setCursor(x+w,y); display.write('u');
display.setCursor(x+(2*w),y); display.write('n');
display.setCursor(x+(3*w),y); display.write('a');
display.setTextFGColor(display.color565(180,60,140));
display.setCursor(x+(4*w),y); display.write('X');
delay(timeout);
}
void testTextString(uint16_t timeout) {
display.fillScreen(display.color444(0,0,0));
display.setTextFGColor(display.color565(0,60,255));
display.setCursor(0,5);
display.write("HURRA");
delay(timeout);
}
void testWrapChar(const char c, uint16_t speed, uint16_t timeout) {
display.setTextWrap(true);
for (uint8_t i = 32; i > 0; i--) {
display.fillScreen(display.color444(0,0,0));
display.setCursor(i, 5);
display.write(c);
delay(speed);
}
delay(timeout);
}
void testScrollingChar(const char c, uint16_t speed, uint16_t timeout) {
Serial.println("Scrolling Char");
uint16_t myFGColor = display.color565(180,0,0);
uint16_t myBGColor = display.color565(60,120,0);
display.fillScreen(display.color444(0,0,0));
display.setTextWrap(true);
// from right to left with wrap
display.scrollChar(31,5,c, myFGColor, myFGColor, 1, speed);
// left out with wrap
delay(500);
display.scrollChar(0,5,c, myBGColor, myBGColor, 1, speed);
delay(timeout);
}
void testScrollingText(const char *str, uint16_t speed, uint16_t timeout) {
Serial.println("Scrolling Text as loop");
// pre config
uint16_t red = display.color565(255,0,100);
uint16_t blue100 = display.color565(0,0,100);
uint16_t black = display.color565(0,0,0);
uint16_t green = display.color565(0,255,0);
uint16_t green150 = display.color565(0,150,0);
display.fillScreen(display.color565(0,0,0));
display.setCursor(31,5);
display.setScrollDir(1);
/** black background **/
display.setTextFGColor(green150);
display.scrollText("** Welcome **", speed);
display.fillScreen(black);
delay(timeout / 2) ;
/** scrolling with colored background */
display.fillRect(0,4,VP_WIDTH,8,blue100);
// scrolling, using default pixels size = length of string (not used parameter pixels)
display.setTextFGColor(red);
display.setTextBGColor(blue100);
display.scrollText(str, speed);
delay(timeout / 2) ;
// same as above but now from left to right
display.setScrollDir(0);
display.setTextFGColor(blue100);
display.setTextBGColor(red);
display.fillRect(0,4,VP_WIDTH,8,red);
display.scrollText(str, speed, 0);
delay(timeout);
display.fillScreen(black);
display.setTextFGColor(red);
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("*****************************************************");
Serial.println(" dmaOutput 32x16 !");
Serial.println("*****************************************************");
#ifdef USE_CUSTOM_PINS
dmaOutput.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
#else
display.begin(true); // init buffers
#endif
// fill the screen with 'black'
display.fillScreen(display.color444(0, 0, 0));
// Set current FastLED palette
currentPalette = RainbowColors_p;
// display.fillScreen(display.color565(0, 0, 0));
}
void loop() {
display.fillScreen(display.color444(0, 0, 0));
//testSimpleChars(1500);
//testSimpleCharString (1500);
testTextString(2000);
// length = 16 bytes without \0
//testWrapChar('A', 250, 1500);
//testScrollingChar('X', 250, 2000);
testScrollingText("Scrolling 16x32", 100, 2000);
} // end loop

View file

@ -0,0 +1,449 @@
/*
Patch class for 32x16 RGB Matrix panels
reimplement all functions which use x,y coordinates
*/
#ifndef ESP_HUB75_32x16MatrixPanel
#define ESP_HUB75_32x16MatrixPanel
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
#include <Fonts/FreeSansBold12pt7b.h>
#include "glcdfont.c"
struct VirtualCoords {
int16_t x;
int16_t y;
};
#define VP_WIDTH 32
#define VP_HEIGHT 16
#define DEFAULT_FONT_W 5
#define DEFAULT_FONT_H 7
#define PIXEL_SPACE 1 // space between chars in a string
/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i) \
(((i) & 0x80ll) ? '1' : '0'), \
(((i) & 0x40ll) ? '1' : '0'), \
(((i) & 0x20ll) ? '1' : '0'), \
(((i) & 0x10ll) ? '1' : '0'), \
(((i) & 0x08ll) ? '1' : '0'), \
(((i) & 0x04ll) ? '1' : '0'), \
(((i) & 0x02ll) ? '1' : '0'), \
(((i) & 0x01ll) ? '1' : '0')
#define PRINTF_BINARY_PATTERN_INT16 \
PRINTF_BINARY_PATTERN_INT8 PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
PRINTF_BYTE_TO_BINARY_INT8((i) >> 8), PRINTF_BYTE_TO_BINARY_INT8(i)
/* --- end macros --- */
class QuarterScanMatrixPanel : public Adafruit_GFX
{
public:
MatrixPanel_I2S_DMA *display;
QuarterScanMatrixPanel(MatrixPanel_I2S_DMA &disp) : Adafruit_GFX(64, 32)
{
this->display = &disp;
size_x = size_y = 1 ;
wrap = false;
cursor_x = cursor_y = 0;
dir = 1;
loop = true;
}
VirtualCoords getCoords(int16_t x, int16_t y);
int16_t getVirtualX(int16_t x) {
VirtualCoords coords = getCoords(x, 0);
return coords.x;
}
int16_t getVirtualY(int16_t y) {
VirtualCoords coords = getCoords(0,y);
return coords.y;
}
// int16_t getVirtualY(int16_t y) {return getCoords(0,y).y;}
/** extende function to draw lines/rects/... **/
virtual uint8_t width() {return VP_WIDTH;};
virtual uint8_t height() {return VP_HEIGHT;};
virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
virtual void drawHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color);
virtual void drawVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color);
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size);
virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size_x, uint8_t size_y);
virtual void scrollChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint16_t dir, uint16_t speed);
virtual void drawString(int16_t x, int16_t y, unsigned char* c, uint16_t color, uint16_t bg);
virtual size_t write(unsigned char c); // write a character on current cursor postion
virtual size_t write(const char *str); // write a character array (string) on curreont cursor postion
virtual void setTextWrap(bool w);
virtual void setCursor (int16_t x, int16_t y);
void setTextFGColor(uint16_t color) {textFGColor = color;};
void setTextBGColor(uint16_t color) {textBGColor = color;};
void setTextSize(uint8_t x, uint8_t y) {size_x = x; size_y = y;}; // magnification, default = 1
void setScrollDir(uint8_t d = 1) { dir = (d != 1) ? 0 : 1;}; // set scroll dir default = 1
void setScroolLoop (bool b = true) { loop = b;} ; // scroll text in a loop, default true
void scrollText(const char *str, uint16_t speed, uint16_t pixels);
/**------------------------------------------**/
// equivalent methods of the matrix library so it can be just swapped out.
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
virtual void fillScreen(uint16_t color); // overwrite adafruit implementation
void clearScreen() { fillScreen(0); }
void drawPixelRGB565(int16_t x, int16_t y, uint16_t color);
void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b);
void drawPixelRGB24(int16_t x, int16_t y, RGB24 color);
void drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows);
uint16_t color444(uint8_t r, uint8_t g, uint8_t b) {
return display->color444(r, g, b);
}
uint16_t color565(uint8_t r, uint8_t g, uint8_t b) {
return display->color565(r, g, b);
}
uint16_t color333(uint8_t r, uint8_t g, uint8_t b) {
return display->color333(r, g, b);
}
void flipDMABuffer() { display->flipDMABuffer(); }
void showDMABuffer() { display->showDMABuffer(); }
void drawDisplayTest();
protected:
int16_t cursor_x, cursor_y; // Cursor position
uint8_t size_x, size_y; // Font size Multiplikator default = 1 => 5x7 Font (5widht,7Height)
uint16_t textFGColor, textBGColor;
bool wrap ; // < If set, 'wrap' text at right edge of display
uint8_t dir ; // used for scrolling text direction
bool loop ; // used for scrolling text in a loop
private:
VirtualCoords coords;
}; // end Class header
/***************************************************************************************
@brief scroll text from right to left or vice versa on current cursor position
please note, this function is not interruptable.
@param *c pointer to \0 terminated string
@param pixels number of pixels to scroll, if 0, than scroll complete text
@param speed velocity of scrolling in ms
***************************************************************************************/
void QuarterScanMatrixPanel::scrollText(const char *str,uint16_t speed, uint16_t pixels = 0) {
// first we put all columns of every char inside str into a big array of lines
// than we move through this arry and draw line per line and move this line
// one position to dir
const uint8_t xSize = 6;
uint16_t len = strlen(str);
uint8_t array[len * xSize]; // size of array number of chars * width of char
//uint16_t lenArray = sizeof(array)/sizeof(array[0]);
uint16_t aPtr = 0;
//
// generate array
char c = *str;
// Serial.printf("size *str (%d), size array: (%d) \n", len, lenArray);
while (c) {
// Serial.printf("** %c ** \n", c);
// read font line per line. A line is a column inside a char
for (int8_t i = 0; i < 5; i++) {
uint8_t line = pgm_read_byte(&font[c * 5 + i]);
array[aPtr++] = line;
// Serial.printf("%d - Line " PRINTF_BINARY_PATTERN_INT8 "\n", i, PRINTF_BYTE_TO_BINARY_INT8(line) );
}
str++;
c = *str;
array[aPtr++] = 0x00; // line with 0 (space between chars)
}
array[aPtr++] = 0x00; // line with 0 (space between chars)
/*
Serial.printf("---------------------------- \n");
for (aPtr=0; aPtr < (len*xSize); aPtr++) {
Serial.printf("%d - Line " PRINTF_BINARY_PATTERN_INT8 "\n", aPtr, PRINTF_BYTE_TO_BINARY_INT8(array[aPtr]) );
}
*/
int16_t x,y,lastX, p;
lastX = (dir) ? VP_WIDTH : 0;
x = cursor_x;
y = cursor_y;
Serial.printf("X: %d, Y: %d \n", x,y);
p=0;
pixels = (pixels) ? pixels : len * xSize;
while (p <= pixels) {
// remove last pixel positions
fillRect(x,y,5,7,textBGColor);
// set new pixel position
x = (dir) ? lastX - p : lastX + p - pixels;
// iterator through our array
for (uint8_t i=0; i < (len*xSize); i++) {
uint8_t line = array[i];
//Serial.printf("%d:%d : " PRINTF_BINARY_PATTERN_INT8 "\n", x, i, PRINTF_BYTE_TO_BINARY_INT8(line) );
// read line and shift from right to left
// start with bit 0 (top of char) to 7(bottom)
for (uint8_t j=0; j < 8; j++, line>>=1) {
if (line & 1) {
// got 1, if x + i outside panel ignore pixel
if (x + i >= 0 && x + i < VP_WIDTH) {
drawPixel(x + i, y + j, textFGColor);
}
}
else {
// got 0
if (x + i >= 0 && x + i < VP_WIDTH) {
drawPixel(x + i, y + j, textBGColor);
}
} // if
} // for j
} // for i
p++;
delay(speed);
} // while
}
inline size_t QuarterScanMatrixPanel::write(const char *str) {
uint8_t x, y;
x=cursor_x;
y=cursor_y;
char c = *str;
while (c) {
//Serial.printf("%c ", c);
write(c);
str++;
c = *str;
x = x + ((DEFAULT_FONT_W + PIXEL_SPACE) * size_x);
setCursor(x,y);
}
Serial.printf("\n");
return 1;
}
inline size_t QuarterScanMatrixPanel::write(unsigned char c) {
Serial.printf("\twrite(%d, %d, %c)\n", cursor_x, cursor_y, c);
drawChar(cursor_x, cursor_y, c, textFGColor, textBGColor, size_x, size_y);
return 1;
}
void QuarterScanMatrixPanel::setTextWrap(bool w) {this->display->setTextWrap(w);}
void QuarterScanMatrixPanel::setCursor(int16_t x, int16_t y) {
cursor_x = x;
cursor_y = y;
}
/* - new for 16x32 panels - */
inline void QuarterScanMatrixPanel::drawLine(int16_t x, int16_t y, int16_t x1, int16_t y1, uint16_t color)
{
int16_t a,b;
for (a=x; a <= x1; a++) {
for (b=y; b <= y1; b++) {
drawPixel(a,b,color);
}
}
}
inline void QuarterScanMatrixPanel::drawHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
drawLine(x,y,x+w,y, color);
}
inline void QuarterScanMatrixPanel::drawVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
drawLine(x,y,x,y+h, color);
}
inline void QuarterScanMatrixPanel::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
for (int16_t i = x; i < x + w; i++) {
drawVLine(i, y, h, color);
}
}
void QuarterScanMatrixPanel::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size)
{
drawChar(x,y,c,color, bg, size, size);
}
inline void QuarterScanMatrixPanel::scrollChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint16_t dir, uint16_t speed){
if ((x >= VP_WIDTH) ||
(y >= VP_HEIGHT) ||
((x + 6 * size_x-1) < 0) ||
((y + 8 * size_y-1) <0))
return;
setTextWrap(true);
// text wrap is only for the right end of the panel, to scroll soft out of the left of panel
// algorithm should wrap the character from left to right
// loop s = scroll-loop, scrolls char 5 pixels into dir
uint8_t lastX = x;
for (int8_t s = 0; s < 6; s++) {
// loop i : width of a character
Serial.printf("X:%d ", x);
// clear current position
fillRect(x,y,5,7,0);
x = lastX - s;
for (int8_t i = 0; i < 5; i++) {
// first line is the firste vertical part of a character and 8bits long
// last bit is everytime 0
// we read 5 lines with 8 bit (5x7 char + 8bit with zeros)
// Example : char A (90deg cw)
// 01111100
// 00010010
// 00010001
// 00010010
// 01111100
uint8_t line = pgm_read_byte(&font[c * 5 + i]);
// shift from right to left bit per bit
// loop j = height of a character
// loop through a colunm of currenc character
Serial.printf("i:%d ", i);
// ignore all pixels outside panel
if (x+i >= VP_WIDTH) continue;
for (int8_t j=0; j < 8; j++, line >>= 1) {
if (line & 1) {
Serial.printf
(" ON %d", x+i);
// we read 1
if (x >= 0) {
drawPixel(x+i, y+j, color);
}
else if (x+i >= 0) {
drawPixel(x+i, y+j, color);
}
}
else if (bg != color) {
// we read 0
Serial.printf(" OFF %d", x+i);
if (x >= 0) {
drawPixel(x+i, y+j, bg);
}
else if (x+i >= 0) {
drawPixel(x+i, y+j, bg);
}
}
}
}
Serial.printf("\n");
delay(speed);
}
}
inline void QuarterScanMatrixPanel::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size_x, uint8_t size_y)
{
//Serial.printf("unmapped : drawChar(%d, %d, %c) \n",x, y, c);
// note: remapping to 16x32 coordinats is done inside drawPixel() or fillRect
if ((x >= VP_WIDTH) ||
(y >= VP_HEIGHT) ||
((x + 6 * size_x-1) < 0) ||
((y + 8 * size_y-1) <0))
return;
//Serial.printf("Font-Array : %d \n", sizeof(font));
for (int8_t i = 0; i < 5; i++) {
uint8_t line = pgm_read_byte(&font[c * 5 + i]);
//Serial.printf("%d - Line " PRINTF_BINARY_PATTERN_INT8 "\n", i, PRINTF_BYTE_TO_BINARY_INT8(line) );
for (int8_t j = 0; j < 8; j++, line >>= 1) {
if (line & 1) {
if (size_x == 1 && size_y == 1)
//Serial.printf("");
drawPixel(x + i, y + j, color);
else
// remark: it's important to call function with orgininal coordinates for x/y
fillRect(x + i * size_x, y + j * size_y, size_x, size_y,
color);
} else if (bg != color) {
if (size_x == 1 && size_y == 1)
drawPixel(x + i, y + j, bg);
else
// remark: it's important to call function with orgininal coordinates for x/y
fillRect(x + i * size_x, y + j * size_y, size_x, size_y, bg);
}
}
}
}
inline void QuarterScanMatrixPanel::drawString(int16_t x, int16_t y, unsigned char* c, uint16_t color, uint16_t bg) {
}
inline VirtualCoords QuarterScanMatrixPanel::getCoords(int16_t x, int16_t y)
{
const int y_remap[] = { 0,1,8,9,4,5,12,13,16,17,24,25,22,23,30,31 };
if (y > VP_HEIGHT)
y = VP_HEIGHT;
if (x > VP_WIDTH)
x = VP_WIDTH;
coords.x = x + VP_WIDTH;
coords.y = y_remap[y];
return coords;
}
/* -------------------------*/
inline void QuarterScanMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color)
{
VirtualCoords coords = getCoords(x, y);
this->display->drawPixel(coords.x, coords.y, color);
}
inline void QuarterScanMatrixPanel::fillScreen(uint16_t color) // adafruit virtual void override
{
// No need to map this.
this->display->fillScreen(color);
}
inline void QuarterScanMatrixPanel::drawPixelRGB565(int16_t x, int16_t y, uint16_t color)
{
VirtualCoords coords = getCoords(x, y);
this->display->drawPixelRGB565( coords.x, coords.y, color);
}
inline void QuarterScanMatrixPanel::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b)
{
VirtualCoords coords = getCoords(x, y);
this->display->drawPixelRGB888( coords.x, coords.y, r, g, b);
}
inline void QuarterScanMatrixPanel::drawPixelRGB24(int16_t x, int16_t y, RGB24 color)
{
VirtualCoords coords = getCoords(x, y);
this->display->drawPixelRGB24(coords.x, coords.y, color);
}
// need to recreate this one, as it wouldnt work to just map where it starts.
inline void QuarterScanMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) { }
#endif

View file

@ -0,0 +1,78 @@
# Using this library with 32x16 1/4 Scan Panels
## Problem
ESP32-HUB75-MatrixPanel-I2S-DMA library will not display output correctly with 1/4 scan panels such [as this](https://de.aliexpress.com/item/33017477904.html?spm=a2g0o.detail.1000023.16.1fedd556Yw52Zi&algo_pvid=4329f1c0-04d2-43d9-bdfd-7d4ee95e6b40&algo_expid=4329f1c0-04d2-43d9-bdfd-7d4ee95e6b40-52&btsid=9a8bf2b5-334b-45ea-a849-063d7461362e&ws_ab_test=searchweb0_0,searchweb201602_10,searchweb201603_60%5BAliExpress%2016x32%5D).
## Solution
It is possible to connect 1/4 scan panels to this library and 'trick' the output to work correctly on these panels by way of adjusting the pixel co-ordinates that are 'sent' to the ESP32-HUB75-MatrixPanel-I2S-DMA library (in this example, it is the 'dmaOutput' class).
Creation of a 'QuarterScanMatrixPanel.h' class which sends an adjusted x,y co-ordinates to the underlying ESP32-HUB75-MatrixPanel-I2S-DMA library's drawPixel routine, to trick the output to look pixel perfect. Refer to the 'getCoords' function within 'QuarterScanMatrixPanel.h'
## Limitations
* Only one font (glcd - standard font) is implemented. This font can't be resized.
## New functions (and adapted function) in this QuarterScanMatrixPanel class
### drawLine
`void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)`
Parameters should be self explained. x0/y0 upper left corner, x1/y1 lower right corner
### drawHLine
`void drawHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color)`
Draw a fast horizontal line with length `w`. Starting at `x0/y0`
### drawVLine
`void drawVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color)`
Draw a fast vertical line with length `h` Starting at `x0/y0`
### fillRect
`void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)`
Draw a rectangle starting at `x/y` with width `w` and height `h`in `color`
### drawChar (5x7) Standard font
`drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size)`
Draw a char at position x/y in `color` with a background color `bg`
`size` is ignored.
### writeChar (5x7)
`size_t write(unsigned char c)`
Write a char at current cursor position with current foreground and background color.
### writeString (5x7)
`size_t write(const char *c)`
Write a string at current cursor position with current foreground and background color.
You have to use `setCursor(x,y)` and `setTextFGColor() / setTextBGColor()`
### drawString (5x7)
`void drawString(int16_t x, int16_t y, unsigned char* c, uint16_t color, uint16_t bg)`
Draw String at postion x/y wit foreground `color` and background `bg`
Example: `display.drawString(0,5,"**Welcome**",display.color565(0,60,255));`
### void setScrollDir(uint8_t d = 1)
Set scrolling direction 0=left to right, 1= right to left (default)
### scrollText
`void scrollText(const char *str, uint16_t speed, uint16_t pixels)`
Scroll text `str` into `setScrollDir`. Speed indicates how fast in ms per pixel, pixels are the number pixes which should be scrolled, if not set or 0, than pixels is calculates by size of `*str`
### drawPixel(int16_t x, int16_t y, uint16_t color)
Draw a pixel at x/y in `color`. This function is the atomic function for all above drawing functions
### clearScreen() (all pixels off (black))
Same as `fillScreen(0)`
## Example videos:
https://user-images.githubusercontent.com/949032/104838449-4aae5600-58bb-11eb-802f-a358b49a9315.mp4
https://user-images.githubusercontent.com/949032/104366906-5647f880-551a-11eb-9792-a6f8276629e6.mp4

View file

@ -0,0 +1,16 @@
# Test Patterns
Simple solid colors, gradients and test line patterns, could be used to test matrixes for proper operation, flickering and estimate fillrate timings.
Should be build and uploaded as a [platformio](https://platformio.org/) project
To build with Arduino's framework use
```
pio run -t upload
```
To build using ESP32 IDF with arduino's component use
```
pio run -t upload -e idfarduino
```

View file

@ -0,0 +1,42 @@
[platformio]
default_envs = esp32
[env]
framework = arduino
platform = espressif32
board_build.filesystem = littlefs
board = wemos_d1_mini32
lib_deps =
FastLED
build_flags =
; -DSERIAL_DEBUG=1
upload_speed = 921600
monitor_speed = 115200
monitor_filters = esp32_exception_decoder
[env:esp32]
build_flags =
${env.build_flags}
-DUSE_FASTLINES
-DNO_GFX
lib_deps =
${env.lib_deps}
; use dev version of the lib
https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA.git#dev
[env:idfarduino]
platform = espressif32
platform_packages =
; use a special branch
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#idf-release/v4.0
framework = arduino, espidf
build_flags =
${env.build_flags}
-DARDUINO=200
-DESP32
-DUSE_FASTLINES
-DNO_GFX
lib_deps =
${env.lib_deps}
https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA.git#dev

View file

@ -0,0 +1,6 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
idf_component_register(SRCS ${app_sources})

View file

@ -0,0 +1,382 @@
// How to use this library with a FM6126 panel, thanks goes to:
// https://github.com/hzeller/rpi-rgb-led-matrix/issues/746
// IDF
#if defined(IDF_VER)
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gpio.h>
#include "sdkconfig.h"
#endif
#include <Arduino.h>
#include "xtensa/core-macros.h"
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include "main.h"
// HUB75E pinout
// R1 | G1
// B1 | GND
// R2 | G2
// B2 | E
// A | B
// C | D
// CLK| LAT
// OE | GND
#define R1 25
#define G1 26
#define BL1 27
#define R2 14 // 21 SDA
#define G2 12 // 22 SDL
#define BL2 13
#define CH_A 23
#define CH_B 19
#define CH_C 5
#define CH_D 17
#define CH_E 32 // assign to any available pin if using two panels or 64x64 panels with 1/32 scan
#define CLK 16
#define LAT 4
#define OE 15
#define M_WIDTH 256
#define M_HEIGHT 64
#define PATTERN_DELAY 2000
#define NUM_LEDS M_WIDTH*M_HEIGHT
//#define USE_FASTLINES
MatrixPanel_I2S_DMA *matrix = nullptr;
uint16_t time_counter = 0, cycles = 0, fps = 0;
unsigned long fps_timer;
//
CRGB *ledbuff;
//
unsigned long t1, t2, s1=0, s2=0, s3=0;
uint32_t ccount1, ccount2;
uint8_t color1 = 0, color2 = 0, color3 = 0;
uint16_t x,y;
void setup(){
Serial.begin(BAUD_RATE);
Serial.println("Starting pattern test...");
HUB75_I2S_CFG::i2s_pins _pins={R1, G1, BL1, R2, G2, BL2, CH_A, CH_B, CH_C, CH_D, CH_E, LAT, OE, CLK};
HUB75_I2S_CFG mxconfig(64, 64, 4, _pins, HUB75_I2S_CFG::FM6126A);
mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_20M;
matrix = new MatrixPanel_I2S_DMA(mxconfig);
matrix->begin();
matrix->setBrightness8(255);
//matrix->setLatBlanking(2);
ledbuff = (CRGB *)malloc(NUM_LEDS * sizeof(CRGB)); // allocate buffer for some tests
buffclear(ledbuff);
}
void loop(){
Serial.printf("Cycle: %d\n", ++cycles);
Serial.print("Estimating clearScreen() - ");
ccount1 = XTHAL_GET_CCOUNT();
matrix->clearScreen();
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
Serial.printf("%d ticks\n", ccount1);
delay(PATTERN_DELAY);
/*
// Power supply tester
// slowly fills matrix with white, stressing PSU
for (int y=0; y!=M_HEIGHT; ++y){
for (int x=0; x!=M_WIDTH; ++x){
matrix->drawPixelRGB888(x, y, 255,255,255);
//matrix->drawPixelRGB888(x, y-1, 255,0,0); // pls, be gentle :)
delay(10);
}
}
delay(5000);
*/
// simple solid colors
Serial.println("Fill screen: RED");
matrix->fillScreenRGB888(255, 0, 0);
delay(PATTERN_DELAY);
Serial.println("Fill screen: GREEN");
matrix->fillScreenRGB888(0, 255, 0);
delay(PATTERN_DELAY);
Serial.println("Fill screen: BLUE");
matrix->fillScreenRGB888(0, 0, 255);
delay(PATTERN_DELAY);
for (uint8_t i=5; i; --i){
Serial.print("Estimating single drawPixelRGB888(r, g, b) ticks: ");
color1 = random8();
ccount1 = XTHAL_GET_CCOUNT();
matrix->drawPixelRGB888(i,i, color1, color1, color1);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
Serial.printf("%d ticks\n", ccount1);
}
// Clearing CRGB ledbuff
Serial.print("Estimating ledbuff clear time: ");
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
buffclear(ledbuff);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n\n", t2, ccount1);
// Bare fillscreen(r, g, b)
Serial.print("Estimating fillscreenRGB888(r, g, b) time: ");
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
matrix->fillScreenRGB888(64, 64, 64); // white
ccount2 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
s1+=t2;
Serial.printf("%lu us, avg: %lu, ccnt: %d\n", t2, s1/cycles, ccount2);
delay(PATTERN_DELAY);
Serial.print("Estimating full-screen fillrate with looped drawPixelRGB888(): ");
y = M_HEIGHT;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
--y;
uint16_t x = M_WIDTH;
do {
--x;
matrix->drawPixelRGB888( x, y, 0, 0, 0);
} while(x);
} while(y);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
// created random color gradient in ledbuff
uint8_t color1 = 0;
uint8_t color2 = random8();
uint8_t color3 = 0;
for (uint16_t i = 0; i<NUM_LEDS; ++i){
ledbuff[i].r=color1++;
ledbuff[i].g=color2;
if (i%M_WIDTH==0)
color3+=255/M_HEIGHT;
ledbuff[i].b=color3;
}
//
//
Serial.print("Estimating ledbuff-to-matrix fillrate with drawPixelRGB888(), time: ");
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
mxfill(ledbuff);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
s2+=t2;
Serial.printf("%lu us, avg: %lu, %d ticks:\n", t2, s2/cycles, ccount1);
delay(PATTERN_DELAY);
//
#ifdef USE_FASTLINES
// Fillrate for fillRect() function
Serial.print("Estimating fullscreen fillrate with fillRect() time: ");
t1 = micros();
matrix->fillRect(0, 0, M_WIDTH, M_HEIGHT, 0, 224, 0);
t2 = micros()-t1;
Serial.printf("%lu us\n", t2);
delay(PATTERN_DELAY);
Serial.print("Chessboard with fillRect(): "); // шахматка
matrix->fillScreen(0);
x =0, y = 0;
color1 = random8();
color2 = random8();
color3 = random8();
bool toggle=0;
t1 = micros();
do {
do{
matrix->fillRect(x, y, 8, 8, color1, color2, color3);
x+=16;
}while(x < M_WIDTH);
y+=8;
toggle = !toggle;
x = toggle ? 8 : 0;
}while(y < M_HEIGHT);
t2 = micros()-t1;
Serial.printf("%lu us\n", t2);
delay(PATTERN_DELAY);
#endif
// ======== V-Lines ==========
Serial.println("Estimating V-lines with drawPixelRGB888(): "); //
matrix->fillScreen(0);
color1 = random8();
color2 = random8();
x = y = 0;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
y=0;
do{
matrix->drawPixelRGB888(x, y, color1, color2, color3);
} while(++y != M_HEIGHT);
x+=2;
} while(x != M_WIDTH);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
delay(PATTERN_DELAY);
#ifdef USE_FASTLINES
Serial.println("Estimating V-lines with vlineDMA(): "); //
matrix->fillScreen(0);
color2 = random8();
x = y = 0;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
matrix->drawFastVLine(x, y, M_HEIGHT, color1, color2, color3);
x+=2;
} while(x != M_WIDTH);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
delay(PATTERN_DELAY);
Serial.println("Estimating V-lines with fillRect(): "); //
matrix->fillScreen(0);
color1 = random8();
color2 = random8();
x = y = 0;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
matrix->fillRect(x, y, 1, M_HEIGHT, color1, color2, color3);
x+=2;
} while(x != M_WIDTH);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
delay(PATTERN_DELAY);
#endif
// ======== H-Lines ==========
Serial.println("Estimating H-lines with drawPixelRGB888(): "); //
matrix->fillScreen(0);
color2 = random8();
x = y = 0;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
x=0;
do{
matrix->drawPixelRGB888(x, y, color1, color2, color3);
} while(++x != M_WIDTH);
y+=2;
} while(y != M_HEIGHT);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
delay(PATTERN_DELAY);
#ifdef USE_FASTLINES
Serial.println("Estimating H-lines with hlineDMA(): ");
matrix->fillScreen(0);
color2 = random8();
color3 = random8();
x = y = 0;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
matrix->drawFastHLine(x, y, M_WIDTH, color1, color2, color3);
y+=2;
} while(y != M_HEIGHT);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
delay(PATTERN_DELAY);
Serial.println("Estimating H-lines with fillRect(): "); //
matrix->fillScreen(0);
color2 = random8();
color3 = random8();
x = y = 0;
t1 = micros();
ccount1 = XTHAL_GET_CCOUNT();
do {
matrix->fillRect(x, y, M_WIDTH, 1, color1, color2, color3);
y+=2;
} while(y != M_HEIGHT);
ccount1 = XTHAL_GET_CCOUNT() - ccount1;
t2 = micros()-t1;
Serial.printf("%lu us, %u ticks\n", t2, ccount1);
delay(PATTERN_DELAY);
#endif
Serial.println("\n====\n");
// take a rest for a while
delay(10000);
}
void buffclear(CRGB *buf){
memset(buf, 0x00, NUM_LEDS * sizeof(CRGB)); // flush buffer to black
}
void IRAM_ATTR mxfill(CRGB *leds){
uint16_t y = M_HEIGHT;
do {
--y;
uint16_t x = M_WIDTH;
do {
--x;
uint16_t _pixel = y * M_WIDTH + x;
matrix->drawPixelRGB888( x, y, leds[_pixel].r, leds[_pixel].g, leds[_pixel].b);
} while(x);
} while(y);
}
//
/**
* The one for 256+ matrixes
* otherwise this:
* for (uint8_t i = 0; i < MATRIX_WIDTH; i++) {}
* turns into an infinite loop
*/
uint16_t XY16( uint16_t x, uint16_t y)
{
if (x<M_WIDTH && y < M_HEIGHT){
return (y * M_WIDTH) + x; // everything offset by one to capute out of bounds stuff - never displayed by ShowFrame()
} else {
return 0;
}
}

View file

@ -0,0 +1,8 @@
#include <FastLED.h>
#define BAUD_RATE 115200 // serial debug port baud rate
void buffclear(CRGB *buf);
uint16_t XY16( uint16_t x, uint16_t y);
void mxfill(CRGB *leds);

View file

@ -1,109 +0,0 @@
/*
* Portions of this code are adapted from Aurora: https://github.com/pixelmatix/aurora
* Copyright (c) 2014 Jason Coon
*
* Portions of this code are adapted from LedEffects Plasma by Robert Atkins: https://bitbucket.org/ratkins/ledeffects/src/26ed3c51912af6fac5f1304629c7b4ab7ac8ca4b/Plasma.cpp?at=default
* Copyright (c) 2013 Robert Atkins
*
* 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:
*
* The above copyright notice and 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.
*/
//#define USE_CUSTOM_PINS // uncomment to use custom pins, then provide below
#define A_PIN 26
#define B_PIN 4
#define C_PIN 27
#define D_PIN 2
#define E_PIN 21
#define R1_PIN 5
#define R2_PIN 19
#define G1_PIN 17
#define G2_PIN 16
#define B1_PIN 18
#define B2_PIN 25
#define CLK_PIN 14
#define LAT_PIN 15
#define OE_PIN 13
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
MatrixPanel_I2S_DMA dma_display;
#include <FastLED.h>
int time_counter = 0;
int cycles = 0;
CRGBPalette16 currentPalette;
CRGB currentColor;
CRGB ColorFromCurrentPalette(uint8_t index = 0, uint8_t brightness = 255, TBlendType blendType = LINEARBLEND) {
return ColorFromPalette(currentPalette, index, brightness, blendType);
}
void setup() {
Serial.begin(115200);
Serial.println("*****************************************************");
Serial.println(" HELLO !");
Serial.println("*****************************************************");
#ifdef USE_CUSTOM_PINS
dma_display.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix
#else
dma_display.begin();
#endif
// fill the screen with 'black'
dma_display.fillScreen(dma_display.color444(0, 0, 0));
// Set current FastLED palette
currentPalette = RainbowColors_p;
}
void loop() {
for (int x = 0; x < dma_display.width(); x++) {
for (int y = 0; y < dma_display.height(); y++) {
int16_t v = 0;
uint8_t wibble = sin8(time_counter);
v += sin16(x * wibble * 3 + time_counter);
v += cos16(y * (128 - wibble) + time_counter);
v += sin16(y * x * cos8(-time_counter) / 8);
currentColor = ColorFromPalette(currentPalette, (v >> 8) + 127); //, brightness, currentBlendType);
dma_display.drawPixelRGB888(x, y, currentColor.r, currentColor.g, currentColor.b);
}
}
time_counter += 1;
cycles++;
if (cycles >= 2048) {
time_counter = 0;
cycles = 0;
}
} // end loop

14
examples/README.md Normal file
View file

@ -0,0 +1,14 @@
| Example Name |Description |
|--|--|
|1_SimpleTestShapes |Example for new starters - how to display basic shapes. |
|2_PatternPlasma |Example for new starters - how to display a cool plasma pattern. |
|3_FM6126Panel |Example for new starters - how to initialise FM6126/FM6126A panels with this library.
|AnimatedGIFPanel |Using Larry Bank's GIF Decoder to display animated GIFs. |
|AuroraDemo |Simple example demonstrating various animated effects. |
|BitmapIcons |Simple example of how to display a bitmap image to the display. |
|ChainedPanels |Popular example on how to use the 'VirtualDisplay' class to chain multiple LED Matrix Panels to form a much bigger display! Refer to the README within this example's folder! |
|ChainedPanelsAuroraDemo |As above, but showing a large trippy plasma animation. |
|DoubleBufferSwap |Advanced example of using a back-buffer (double buffering). Not useful for 99% of use cases. |
|GraphicsLayer |Advanced example of using an off-screen 'layer' library before writing to the LED Matrix Panels. |
|P6_32x16_1_4_ScanPanel |An advanced example ('hack') on how to use this library on 32w x 16h 1/4 Scan LED Matrix Panel. |
|PIO_TestPatterns |Non-Arduino example of how to display basic shapes. |

View file

@ -7,19 +7,21 @@ I've done this while optimizing loops and bit logic along with testing compiler
So the testbed is:
- Matrix modules: 4 x FM6126A based 64x64 modules chained in 256x64
A simple sketch:
A testpatters sketch:
- allocating single DMA buffs for 256x64
- allocating (NUM_LEDS*3) bytes for CRGB buffer
- measuring microseconds for the following calls:
- clearScreen() - full blanking
- fillScreenRGB888() with monochrome/gray colors
- fill screen using drawPixel()
- filling some gradient into CRGB buff
- painting CRGB buff into DMA buff with looped drawPixelRGB888()
- drawing lines
|Pattern |Reference|Ref+SPEED|Optimized|Optimized+SPEED|
|--|--|--|--|--|
|fillScreenRGB888()|14570|14570|13400 (8.5% faster)|5520 (164% faster)|
|CRGB buff fill|760|760|760|760|
|updateMatrixDMABuffer(CRGB)|7700|32080|55780 (38% faster)|33500(+4.2% slower)|
||clearScreen()|drawPixelRGB888(), ticks|fillScreen()|fillScreen with a drawPixel()|fillRect() over Maxrix|V-line with drawPixel|fast-V-line|H-line with drawPixel|fast-H-line|
|--|--|--|--|--|--|--|--|--|--|
|v1.2.4|1503113 ticks|9244 non-cached, 675 cached|1719 us, 412272 t|47149 us, 11315418 ticks|-|24505 us, 5880209 ticks|-|24200 us|-|
|FastLines|1503113 ticks|1350 non-cached, 405 cached|1677 us, 401198 t|28511 us, 6841440 ticks|10395 us|14462 us, 3469605 ticks|10391 us, 2492743 ticks|14575 us|5180 us, 1242041 ticks|
to be continued...

View file

@ -1,51 +0,0 @@
# DMA FRAME BUFFER MEMORY ALLOCATION
With version 1.2.2 of the library onwards, the framebuffer is split over multiple 'blocks' (mallocs) as a workaround to the fact the typical Arduino sketch can have a fragmented memory map whereby a single contiguous memory allocation may not be available to fit the entire display.
For example when `heap_caps_print_heap_info(MALLOC_CAP_DMA)` is executed to print information about the available memory blocks that are DMA capable (which we need for a DMA-enabled pixel framebuffer), you may see something like this:
```
Heap summary for capabilities 0x00000008:
At 0x3ffbdb28 len 52 free 4 allocated 0 min_free 4
largest_free_block 4 alloc_blocks 0 free_blocks 1 total_blocks 1
At 0x3ffb8000 len 6688 free 5256 allocated 1208 min_free 5256
largest_free_block 5256 alloc_blocks 11 free_blocks 1 total_blocks 12
At 0x3ffb0000 len 25480 free 17172 allocated 8228 min_free 17172
largest_free_block 17172 alloc_blocks 2 free_blocks 1 total_blocks 3
At 0x3ffae6e0 len 6192 free 6092 allocated 36 min_free 6092
largest_free_block 6092 alloc_blocks 1 free_blocks 1 total_blocks 2
At 0x3ffaff10 len 240 free 0 allocated 120 min_free 0
largest_free_block 0 alloc_blocks 5 free_blocks 1 total_blocks 6
At 0x3ffb6388 len 7288 free 0 allocated 6920 min_free 0
largest_free_block 0 alloc_blocks 21 free_blocks 0 total_blocks 21
At 0x3ffb9a20 len 16648 free 6300 allocated 9680 min_free 348
largest_free_block 4980 alloc_blocks 38 free_blocks 4 total_blocks 42
At 0x3ffc1818 len 124904 free 124856 allocated 0 min_free 124856
largest_free_block 124856 alloc_blocks 0 free_blocks 1 total_blocks 1
At 0x3ffe0440 len 15072 free 15024 allocated 0 min_free 15024
largest_free_block 15024 alloc_blocks 0 free_blocks 1 total_blocks 1
At 0x3ffe4350 len 113840 free 113792 allocated 0 min_free 113792
largest_free_block 113792 alloc_blocks 0 free_blocks 1 total_blocks 1
Totals:
free 288496 allocated 26192 min_free 282544 largest_free_block 124856
```
So what? Well if you look closely, you will probably see two lines (blocks) like this:
```
At 0x3ffc1818 len 124904 free 124856 allocated 0 min_free 124856
largest_free_block 124856 alloc_blocks 0 free_blocks 1 total_blocks 1
...
At 0x3ffe4350 len 113840 free 113792 allocated 0 min_free 113792
largest_free_block 113792 alloc_blocks 0 free_blocks 1 total_blocks 1
```
What this means is there are two blocks of DMA capable memory that are about 100kB each.
The previous versions of the library were only able to use the largest single block of DMA capable memory. The library now is able to use non-contiguous blocks of memory.
Given it's possible to display 128x32 with double buffering in approx. 100kB of RAM. If we use two memory blocks for example, theoretically we should be able to display 256x64 or greater resolution (with double buffering disabled).
# Caveats
Experimentation will be required as available memory is highly dependant on other stuff you have in your sketch. It is best to include and use the 'ESP32-HUB75-MatrixPanel-I2S-DMA' library as early as possible in your code and analyse the serial output of `heap_caps_print_heap_info(MALLOC_CAP_DMA)` to see what DMA memory blocks are available.

View file

@ -10,7 +10,7 @@
"name": "Faptastic",
"url": "https://github.com/mrfaptastic/"
},
"version": "1.2.4",
"version": "2.0.0",
"frameworks": "arduino",
"platforms": "esp32",
"examples": [

View file

@ -1,5 +1,5 @@
name=ESP32 HUB75 LED MATRIX PANEL DMA Display
version=1.2.4
version=2.0.0
author=Faptastic
maintainer=Faptastic
sentence=Experimental DMA based LED Matrix HUB75 Library