It's 'COLOR'

This commit is contained in:
mrfaptastic 2023-01-28 21:54:09 +00:00
parent ace8d938ee
commit c66b592e9f
3 changed files with 91 additions and 92 deletions

View file

@ -24,11 +24,11 @@ build_flags =
| **USE_GFX_ROOT** | Use [lightweight](https://github.com/mrfaptastic/Adafruit_GFX_Lite) version of AdafuitGFX, without Adafruit BusIO extensions | You **must** install [Adafruit_GFX_Lite](https://github.com/mrfaptastic/Adafruit_GFX_Lite) library instead of original AdafruitGFX|
| **NO_GFX** | Build without AdafuitGFX API, 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 working solely with per-pixel manipulation. | Use this if you rely on FastLED, Neomatrix or any other API. For example [Aurora](/examples/AuroraDemo/) 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 ;) | If you are not using AdafruitGFX than you probably do not need this either|
|**NO_CIE1931**|Do not use LED brightness [compensation](https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/) described in [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space). Normally library would adjust every pixel's RGB888 so that luminance (or brightness control) for the corresponding LED's would appear 'linear' to the human's eye. I.e. a white dot with rgb(128,128,128) would seem to be at 50% brightness between rgb(0,0,0) and rgb(255,255,255). Normally you would like to keep this enabled by default. Not only it makes brightness control "linear", it also makes colors more vivid, otherwise it looks brighter but 'bleached'.|You might want to turn it off in some special cases like: <ul><li>Using some other overlay lib for intermediate calculations that makes it's own compensation, like FastLED's [dimming functions](http://fastled.io/docs/3.1/group___dimming.html).<li>running at low color depth's - it **might** (or might not) look better in shadows, darker gradients w/o compensation, try it<li>you run for as bright output as possible, no matter what (make sure you have proper powering)<li>you run for speed/save resources at all costs</ul> |
| **FORCE_COLOUR_DEPTH** |In some cases the library may reduce colour fidelity to increase the refresh rate (i.e. reduce visible flicker). This is most likely to occur with a large chain of panels. However, if you want to force pure 24bpp colour, at the expense of likely noticeable flicker, then set this defined. |Not required in 99% of cases.
|**NO_CIE1931**|Do not use LED brightness [compensation](https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/) described in [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space). Normally library would adjust every pixel's RGB888 so that luminance (or brightness control) for the corresponding LED's would appear 'linear' to the human's eye. I.e. a white dot with rgb(128,128,128) would seem to be at 50% brightness between rgb(0,0,0) and rgb(255,255,255). Normally you would like to keep this enabled by default. Not only it makes brightness control "linear", it also makes colours more vivid, otherwise it looks brighter but 'bleached'.|You might want to turn it off in some special cases like: <ul><li>Using some other overlay lib for intermediate calculations that makes it's own compensation, like FastLED's [dimming functions](http://fastled.io/docs/3.1/group___dimming.html).<li>running at low colour depth's - it **might** (or might not) look better in shadows, darker gradients w/o compensation, try it<li>you run for as bright output as possible, no matter what (make sure you have proper powering)<li>you run for speed/save resources at all costs</ul> |
| **FORCE_COLOR_DEPTH** |In some cases the library may reduce colour fidelity to increase the refresh rate (i.e. reduce visible flicker). This is most likely to occur with a large chain of panels. However, if you want to force pure 24bpp colour, at the expense of likely noticeable flicker, then set this defined. |Not required in 99% of cases.
## Build-time variables
| Flag | Description | Note |
| :------------ |---------------|-----|
| **PIXEL_COLOR_DEPTH_BITS=8** | Color depth per color per pixel in range 2-8. More bit's - more natural color. But on the other hand every additional bit:<ul><li>eats ~2.5 bits of DMA memory per pixel<li>reduces matrix refresh rate in power of two due to nature of [BCM](http://www.batsocks.co.uk/readme/art_bcm_5.htm)</ul> | For large chains of panels (i.e. 6 x 64x64 panels) you WILL need to reduce the colour depth, or likely run out of memory. Default is 8 bits per colour per pixel, i.e. TrueColor 24 bit RGB. <br><br>For higher resolutions, from 64x64 and above it is not possible to provide full 24 bits colour without significant flickering OR reducing dynamic range in shadows. In that case using 5-6 bits at high res make very small difference to the humans eye actually. Refer to the [I2S memcalc](i2s_memcalc.md) for more details.
| **PIXEL_COLOR_DEPTH_BITS=8** | Colour depth per pixel in range 2-8. More bit's - more natural colour. But on the other hand every additional bit:<ul><li>eats ~2.5 bits of DMA memory per pixel<li>reduces matrix refresh rate in power of two due to nature of [BCM](http://www.batsocks.co.uk/readme/art_bcm_5.htm)</ul> | For large chains of panels (i.e. 6 x 64x64 panels) you WILL need to reduce the colour depth, or likely run out of memory. Default is 8 bits per colour per pixel, i.e. True colour 24 bit RGB. <br><br>For higher resolutions, from 64x64 and above it is not possible to provide full 24 bits colour without significant flickering OR reducing dynamic range in shadows. In that case using 5-6 bits at high res make very small difference to the humans eye actually. Refer to the [I2S memcalc](i2s_memcalc.md) for more details.

View file

@ -23,29 +23,29 @@
#endif
/* This library is designed to take an 8 bit / 1 byte value (0-255) for each R G B colour sub-pixel.
* The PIXEL_COLOUR_DEPTH_BITS should always be '8' as a result.
* The PIXEL_COLOR_DEPTH_BITS should always be '8' as a result.
* However, if the library is to be used with lower colour depth (i.e. 6 bit colour), then we need to ensure the 8-bit value passed to the colour masking
* is adjusted accordingly to ensure the LSB's are shifted left to MSB, by the difference. Otherwise the colours will be all screwed up.
*/
#if PIXEL_COLOUR_DEPTH_BITS > 8
#error "Color depth bits cannot be greater than 8."
#elif PIXEL_COLOUR_DEPTH_BITS < 2
#if PIXEL_COLOR_DEPTH_BITS > 8
#error "Colour depth bits cannot be greater than 8."
#elif PIXEL_COLOR_DEPTH_BITS < 2
#error "Colour depth bits cannot be less than 2."
#endif
#if PIXEL_COLOUR_DEPTH_BITS != 8
#define MASK_OFFSET (8 - PIXEL_COLOUR_DEPTH_BITS)
#if PIXEL_COLOR_DEPTH_BITS != 8
#define MASK_OFFSET (8 - PIXEL_COLOR_DEPTH_BITS)
#define PIXEL_COLOUR_MASK_BIT(colour_depth_index) (1 << (colour_depth_index + MASK_OFFSET))
//static constexpr uint8_t const MASK_OFFSET = 8-PIXEL_COLOUR_DEPTH_BITS;
//static constexpr uint8_t const MASK_OFFSET = 8-PIXEL_COLOR_DEPTH_BITS;
#else
#define PIXEL_COLOUR_MASK_BIT(colour_depth_index) (1 << (colour_depth_index))
#endif
/*
#if PIXEL_COLOUR_DEPTH_BITS < 8
#if PIXEL_COLOR_DEPTH_BITS < 8
uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
#else
uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel)
uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel)
#endif
*/
@ -65,11 +65,11 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
size_t allocated_fb_memory = 0;
for (int malloc_num =0; malloc_num < ROWS_PER_FRAME; ++malloc_num)
{
auto ptr = std::make_shared<rowBitStruct>(PIXELS_PER_ROW, PIXEL_COLOUR_DEPTH_BITS, m_cfg.double_buff);
auto ptr = std::make_shared<rowBitStruct>(PIXELS_PER_ROW, PIXEL_COLOR_DEPTH_BITS, m_cfg.double_buff);
if (ptr->data == nullptr)
{
ESP_LOGE("I2S-DMA", "CRITICAL ERROR: Not enough memory for requested colour depth! Please reduce PIXEL_COLOUR_DEPTH_BITS value.\r\n");
ESP_LOGE("I2S-DMA", "CRITICAL ERROR: Not enough memory for requested colour depth! Please reduce PIXEL_COLOR_DEPTH_BITS value.\r\n");
ESP_LOGE("I2S-DMA", "Could not allocate rowBitStruct %d!.\r\n", malloc_num);
return false;
@ -83,7 +83,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
ESP_LOGI("I2S-DMA", "Allocating %d bytes memory for DMA BCM framebuffer(s).", allocated_fb_memory);
// calculate the lowest LSBMSB_TRANSITION_BIT value that will fit in memory that will meet or exceed the configured refresh rate
#if !defined(FORCE_COLOUR_DEPTH)
#if !defined(FORCE_COLOR_DEPTH)
ESP_LOGI("I2S-DMA", "Minimum visual refresh rate (scan rate from panel top to bottom) requested: %d Hz", m_cfg.min_refresh_rate);
@ -92,11 +92,11 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
int nsPerLatch = ((PIXELS_PER_ROW + CLKS_DURING_LATCH) * psPerClock) / 1000;
// add time to shift out LSBs + LSB-MSB transition bit - this ignores fractions...
int nsPerRow = PIXEL_COLOUR_DEPTH_BITS * nsPerLatch;
int nsPerRow = PIXEL_COLOR_DEPTH_BITS * nsPerLatch;
// add time to shift out MSBs
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOUR_DEPTH_BITS; i++)
nsPerRow += (1<<(i - lsbMsbTransitionBit - 1)) * (PIXEL_COLOUR_DEPTH_BITS - i) * nsPerLatch;
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOR_DEPTH_BITS; i++)
nsPerRow += (1<<(i - lsbMsbTransitionBit - 1)) * (PIXEL_COLOR_DEPTH_BITS - i) * nsPerLatch;
int nsPerFrame = nsPerRow * ROWS_PER_FRAME;
int actualRefreshRate = 1000000000UL/(nsPerFrame);
@ -107,7 +107,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
if (actualRefreshRate > m_cfg.min_refresh_rate)
break;
if(lsbMsbTransitionBit < PIXEL_COLOUR_DEPTH_BITS - 1)
if(lsbMsbTransitionBit < PIXEL_COLOR_DEPTH_BITS - 1)
lsbMsbTransitionBit++;
else
break;
@ -126,7 +126,7 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
* memory allocation of the DMA linked list memory structure.
*/
int numDMAdescriptorsPerRow = 1;
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOUR_DEPTH_BITS; i++) {
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOR_DEPTH_BITS; i++) {
numDMAdescriptorsPerRow += (1<<(i - lsbMsbTransitionBit - 1));
}
@ -137,9 +137,9 @@ bool MatrixPanel_I2S_DMA::allocateDMAmemory()
if ( dma_buff.rowBits[0]->size() > DMA_MAX )
{
ESP_LOGW("I2S-DMA", "rowColorDepthStruct struct is too large, split DMA payload required. Adding %d DMA descriptors\n", PIXEL_COLOUR_DEPTH_BITS-1);
ESP_LOGW("I2S-DMA", "rowBits struct is too large to fit in one DMA transfer payload, splitting required. Adding %d DMA descriptors\n", PIXEL_COLOR_DEPTH_BITS-1);
numDMAdescriptorsPerRow += PIXEL_COLOUR_DEPTH_BITS-1;
numDMAdescriptorsPerRow += PIXEL_COLOR_DEPTH_BITS-1;
// Note: If numDMAdescriptorsPerRow is even just one descriptor too large, DMA linked list will not correctly loop.
}
@ -173,7 +173,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
int current_dmadescriptor_offset = 0;
// HACK: If we need to split the payload in 1/2 so that it doesn't breach DMA_MAX, lets do it by the colour_depth.
int num_dma_payload_colour_depths = PIXEL_COLOUR_DEPTH_BITS;
int num_dma_payload_colour_depths = PIXEL_COLOR_DEPTH_BITS;
if ( dma_buff.rowBits[0]->size() > DMA_MAX ) {
num_dma_payload_colour_depths = 1;
}
@ -199,7 +199,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
if ( dma_buff.rowBits[0]->size() > DMA_MAX )
{
for (int cd = 1; cd < PIXEL_COLOUR_DEPTH_BITS; cd++)
for (int cd = 1; cd < PIXEL_COLOR_DEPTH_BITS; cd++)
{
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(cd, 0), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths), false);
@ -213,7 +213,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
} // row depth struct
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOUR_DEPTH_BITS; i++)
for(int i=lsbMsbTransitionBit + 1; i<PIXEL_COLOR_DEPTH_BITS; i++)
{
// binary time division setup: we need 2 of bit (LSBMSB_TRANSITION_BIT + 1) four of (LSBMSB_TRANSITION_BIT + 2), etc
// because we sweep through to MSB each time, it divides the number of times we have to sweep in half (saving linked list RAM)
@ -221,10 +221,10 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
for(int k=0; k < (1<<(i - lsbMsbTransitionBit - 1)); k++)
{
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 0), dma_buff.rowBits[row]->size(PIXEL_COLOUR_DEPTH_BITS - i), false);
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 0), dma_buff.rowBits[row]->size(PIXEL_COLOR_DEPTH_BITS - i), false);
if (m_cfg.double_buff) {
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 1), dma_buff.rowBits[row]->size(PIXEL_COLOUR_DEPTH_BITS - i), true );
dma_bus.create_dma_desc_link(dma_buff.rowBits[row]->getDataPtr(i, 1), dma_buff.rowBits[row]->size(PIXEL_COLOR_DEPTH_BITS - i), true );
}
current_dmadescriptor_offset++;
@ -311,7 +311,7 @@ void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint
/* LED Brightness Compensation. Because if we do a basic "red & mask" for example,
* we'll NEVER send the dimmest possible colour, due to binary skew.
* i.e. It's almost impossible for colour_depth_idx of 0 to be sent out to the MATRIX unless the 'value' of a color is exactly '1'
* i.e. It's almost impossible for colour_depth_idx of 0 to be sent out to the MATRIX unless the 'value' of a colour is exactly '1'
* https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/
*/
#ifndef NO_CIE1931
@ -351,17 +351,10 @@ void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint
}
// Iterating through colour depth bits, which we assume are 8 bits per RGB subpixel (24bpp)
uint8_t colour_depth_idx = PIXEL_COLOUR_DEPTH_BITS;
uint8_t colour_depth_idx = PIXEL_COLOR_DEPTH_BITS;
do {
--colour_depth_idx;
/*
// uint8_t mask = (1 << (colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST)); // expect 24 bit colour (8 bits per RGB subpixel)
#if PIXEL_COLOUR_DEPTH_BITS < 8
uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
#else
uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel)
#endif
*/
uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx);
uint16_t RGB_output_bits = 0;
@ -376,7 +369,7 @@ void IRAM_ATTR MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint16_t x_coord, uint
// Get the contents at this address,
// it would represent a vector pointing to the full row of pixels for the specified color depth bit at Y coordinate
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx, back_buffer_id);
@ -404,13 +397,13 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint
blue = lumConvTab[blue];
#endif
for(uint8_t colour_depth_idx=0; colour_depth_idx<PIXEL_COLOUR_DEPTH_BITS; colour_depth_idx++) // color depth - 8 iterations
for(uint8_t colour_depth_idx=0; colour_depth_idx<PIXEL_COLOR_DEPTH_BITS; colour_depth_idx++) // colour depth - 8 iterations
{
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
uint16_t RGB_output_bits = 0;
// uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST); // 24 bit colour
// #if PIXEL_COLOUR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel)
// #if PIXEL_COLOR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
// #else
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel)
// #endif
@ -530,13 +523,13 @@ void MatrixPanel_I2S_DMA::clearFrameBuffer(bool _buff_id){
} // end SM5266P
// let's set LAT/OE control bits for specific pixels in each color_index subrows
// let's set LAT/OE control bits for specific pixels in each colour_index subrows
// Need to consider the original ESP32's (WROOM) DMA TX FIFO reordering of bytes...
uint8_t colouridx = dma_buff.rowBits[row_idx]->colour_depth;
do {
--colouridx;
// switch pointer to a row for a specific color index
// switch pointer to a row for a specific colour index
row = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
/*
@ -617,12 +610,12 @@ void MatrixPanel_I2S_DMA::brtCtrlOE(int brt, const bool _buff_id){
do {
--row_idx;
// let's set OE control bits for specific pixels in each color_index subrows
// let's set OE control bits for specific pixels in each colour_index subrows
uint8_t colouridx = dma_buff.rowBits[row_idx]->colour_depth;
do {
--colouridx;
// switch pointer to a row for a specific color index
// switch pointer to a row for a specific colour index
ESP32_I2S_DMA_STORAGE_TYPE* row = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
int x_coord = dma_buff.rowBits[row_idx]->width;
@ -668,7 +661,7 @@ void MatrixPanel_I2S_DMA::brtCtrlOE(int brt, const bool _buff_id){
} while(colouridx);
// switch pointer to a row for a specific color index
// switch pointer to a row for a specific colour index
#if defined(SPIRAM_DMA_BUFFER)
ESP32_I2S_DMA_STORAGE_TYPE* row_hack = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
Cache_WriteBack_Addr((uint32_t)row_hack, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((dma_buff.rowBits[row_idx]->width * dma_buff.rowBits[row_idx]->colour_depth)-1)) ;
@ -698,10 +691,10 @@ void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id) {
do {
--row_idx;
// let's set OE control bits for specific pixels in each color_index subrows
// let's set OE control bits for specific pixels in each colour_index subrows
uint8_t colouridx = dma_buff.rowBits[row_idx]->colour_depth;
do {
// Multiply brightness according to index of bitplane (color index subrow)
// Multiply brightness according to index of bitplane (colour index subrow)
// in respect of accumulating LED intensities with Binary-Coded Modulation:
// bitplane 0 is 1/1 of total brightness; bitplane 1 is 1/2; bitplane 2 is 1/4, etc
// accumulating all of them together means we will get only ~1/4 of the total brightness.
@ -717,7 +710,7 @@ void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id) {
int brightness_in_x_pixels = (PIXELS_PER_ROW * brt) >> (8 + rightshift);
--colouridx;
// switch pointer to a row for a specific color index
// switch pointer to a row for a specific colour index
ESP32_I2S_DMA_STORAGE_TYPE* row = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
int x_coord = dma_buff.rowBits[row_idx]->width;
@ -769,7 +762,7 @@ void MatrixPanel_I2S_DMA::brtCtrlOEv2(uint8_t brt, const int _buff_id) {
} while(colouridx);
// switch pointer to a row for a specific color index
// switch pointer to a row for a specific colour index
#if defined(SPIRAM_DMA_BUFFER)
ESP32_I2S_DMA_STORAGE_TYPE* row_hack = dma_buff.rowBits[row_idx]->getDataPtr(colouridx, _buff_id);
Cache_WriteBack_Addr((uint32_t)row_hack, sizeof(ESP32_I2S_DMA_STORAGE_TYPE) * ((dma_buff.rowBits[row_idx]->width * dma_buff.rowBits[row_idx]->colour_depth)-1)) ;
@ -828,7 +821,7 @@ uint8_t MatrixPanel_I2S_DMA::setLatBlanking(uint8_t pulses){
* @param x_coord - line start coordinate x
* @param y_coord - line start coordinate y
* @param l - line length
* @param r,g,b, - RGB888 color
* @param r,g,b, - RGB888 colour
*/
void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue){
if ( !initialized )
@ -858,18 +851,18 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
y_coord -= ROWS_PER_FRAME;
}
// Iterating through color depth bits (8 iterations)
uint8_t colour_depth_idx = PIXEL_COLOUR_DEPTH_BITS;
// Iterating through colour depth bits (8 iterations)
uint8_t colour_depth_idx = PIXEL_COLOR_DEPTH_BITS;
do {
--colour_depth_idx;
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
uint16_t RGB_output_bits = 0;
// uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST);
// #if PIXEL_COLOUR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel)
// #if PIXEL_COLOR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
// #else
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel)
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel)
// #endif
uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx);
@ -880,10 +873,10 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
RGB_output_bits |= (bool)(green & mask); // -BG
RGB_output_bits <<= 1;
RGB_output_bits |= (bool)(red & mask); // BGR
RGB_output_bits <<= _colourbitoffset; // shift color bits to the required position
RGB_output_bits <<= _colourbitoffset; // shift colour bits to the required position
// Get the contents at this address,
// it would represent a vector pointing to the full row of pixels for the specified color depth bit at Y coordinate
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
ESP32_I2S_DMA_STORAGE_TYPE *p = dma_buff.rowBits[y_coord]->getDataPtr(colour_depth_idx, back_buffer_id);
// inlined version works slower here, dunno why :(
// ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx, back_buffer_id);
@ -903,10 +896,10 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
*/
uint16_t &v = p[ESP32_TX_FIFO_POSITION_ADJUST(_x)];
v &= _colourbitclear; // reset color bits
v |= RGB_output_bits; // set new color bits
v &= _colourbitclear; // reset colour bits
v |= RGB_output_bits; // set new colour bits
} while(_l); // iterate pixels in a row
} while(colour_depth_idx); // end of color depth loop (8)
} while(colour_depth_idx); // end of colour depth loop (8)
} // hlineDMA()
@ -915,7 +908,7 @@ void MatrixPanel_I2S_DMA::hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
* @param x_coord - line start coordinate x
* @param y_coord - line start coordinate y
* @param l - line length
* @param r,g,b, - RGB888 color
* @param r,g,b, - RGB888 colour
*/
void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue){
if ( !initialized )
@ -944,16 +937,16 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
*/
x_coord = ESP32_TX_FIFO_POSITION_ADJUST(x_coord);
uint8_t colour_depth_idx = PIXEL_COLOUR_DEPTH_BITS;
do { // Iterating through color depth bits (8 iterations)
uint8_t colour_depth_idx = PIXEL_COLOR_DEPTH_BITS;
do { // Iterating through colour depth bits (8 iterations)
--colour_depth_idx;
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
// uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST);
// #if PIXEL_COLOUR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit color (8 bits per RGB subpixel)
// #if PIXEL_COLOR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
// #else
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit color (8 bits per RGB subpixel)
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel)
// #endif
uint8_t mask = PIXEL_COLOUR_MASK_BIT(colour_depth_idx);
@ -978,7 +971,7 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
}
// Get the contents at this address,
// it would represent a vector pointing to the full row of pixels for the specified color depth bit at Y coordinate
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
//ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(_y, colour_depth_idx, back_buffer_id);
ESP32_I2S_DMA_STORAGE_TYPE *p = dma_buff.rowBits[_y]->getDataPtr(colour_depth_idx, back_buffer_id);
@ -986,7 +979,7 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
p[x_coord] |= RGB_output_bits; // set new RGB bits
++_y;
} while(++_l!=l); // iterate pixels in a col
} while(colour_depth_idx); // end of color depth loop (8)
} while(colour_depth_idx); // end of colour depth loop (8)
} // vlineDMA()
@ -995,9 +988,9 @@ void MatrixPanel_I2S_DMA::vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l,
* this works much faster than multiple consecutive per-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
* @param uint8_t r - RGB888 colour
* @param uint8_t g - RGB888 colour
* @param uint8_t b - RGB888 colour
*/
void MatrixPanel_I2S_DMA::fillRectDMA(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b){

View file

@ -64,12 +64,17 @@
#define MATRIX_ROWS_IN_PARALLEL 2
#endif
// 8bit per RGB color = 24 bit/per pixel,
// 8bit per RGB colour = 24 bit/per pixel,
// might be reduced to save DMA RAM
#ifndef PIXEL_COLOUR_DEPTH_BITS
#define PIXEL_COLOUR_DEPTH_BITS 8
#ifdef PIXEL_COLOUR_DEPTH_BITS
#define PIXEL_COLOR_DEPTH_BITS PIXEL_COLOUR_DEPTH_BITS
#endif
#ifndef PIXEL_COLOR_DEPTH_BITS
#define PIXEL_COLOR_DEPTH_BITS 8
#endif
/***************************************************************************************/
/* Definitions below should NOT be ever changed without rewriting library logic */
#define ESP32_I2S_DMA_STORAGE_TYPE uint16_t // DMA output of one uint16_t at a time.
@ -124,7 +129,7 @@ struct rowBitStruct {
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
* size (in bytes) of a vector holding full DMA data for a row of pixels with _dpth colour 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
*
@ -133,7 +138,7 @@ struct rowBitStruct {
*/
size_t size(uint8_t _dpth=0 ) { if (!_dpth) _dpth = colour_depth; return width * _dpth * sizeof(ESP32_I2S_DMA_STORAGE_TYPE); };
/** @brief - returns pointer to the row's data vector beginning at pixel[0] for _dpth color bit
/** @brief - returns pointer to the row's data vector beginning at pixel[0] for _dpth colour 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
@ -170,7 +175,7 @@ struct rowBitStruct {
* are contained in parallel within the one uint16_t that is sent in parallel to the HUB75).
*
* This structure isn't actually allocated in one memory block anymore, as the library now allocates
* memory per row (per rowColorDepthStruct) instead.
* memory per row (per rowBits) instead.
*/
struct frameStruct {
uint8_t rows=0; // number of rows held in current frame, not used actually, just to keep the idea of struct
@ -267,7 +272,8 @@ struct HUB75_I2S_CFG {
mx_height(_h),
chain_length(_chain),
gpio(_pinmap),
driver(_drv), i2sspeed(_i2sspeed),
driver(_drv),
i2sspeed(_i2sspeed),
double_buff(_dbuff),
latch_blanking(_latblk),
clkphase(_clockphase),
@ -452,7 +458,7 @@ class MatrixPanel_I2S_DMA {
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)!
// Colour 444 is a 4 bit scale, so 0 to 15, colour 565 takes a 0-255 bit value, so scale up by 255/15 (i.e. 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
@ -463,7 +469,7 @@ class MatrixPanel_I2S_DMA {
/**
* @brief - convert RGB565 to RGB888
* @param uint16_t color - RGB565 input color
* @param uint16_t colour - RGB565 input colour
* @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);
@ -602,9 +608,9 @@ class MatrixPanel_I2S_DMA {
Bus_Parallel16 dma_bus;
/**
* @brief - clears and reinitializes color/control data in DMA buffs
* @brief - clears and reinitializes colour/control data in DMA buffs
* When allocated, DMA buffs might be dirty, 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
* Those control bits are constants during the entire DMA sweep and never changed when updating just pixel colour 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)
@ -618,7 +624,7 @@ class MatrixPanel_I2S_DMA {
void updateMatrixDMABuffer(uint8_t red, uint8_t green, uint8_t blue);
/**
* wipes DMA buffer(s) and reset all color/service bits
* wipes DMA buffer(s) and reset all colour/service bits
*/
inline void resetbuffers(){
@ -639,7 +645,7 @@ class MatrixPanel_I2S_DMA {
* @param x_ccord - line start coordinate x
* @param y_ccord - line start coordinate y
* @param l - line length
* @param r,g,b, - RGB888 color
* @param r,g,b, - RGB888 colour
*/
void hlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue);
@ -648,7 +654,7 @@ class MatrixPanel_I2S_DMA {
* @param x_ccord - line start coordinate x
* @param y_ccord - line start coordinate y
* @param l - line length
* @param r,g,b, - RGB888 color
* @param r,g,b, - RGB888 colour
*/
void vlineDMA(int16_t x_coord, int16_t y_coord, int16_t l, uint8_t red, uint8_t green, uint8_t blue);
@ -657,9 +663,9 @@ class MatrixPanel_I2S_DMA {
* uses Fast H/V line draw internally, works faster than multiple 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
* @param uint8_t r - RGB888 colour
* @param uint8_t g - RGB888 colour
* @param uint8_t b - RGB888 colour
*/
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
@ -743,7 +749,7 @@ class MatrixPanel_I2S_DMA {
/**
* @brief - convert RGB565 to RGB888
* @param uint16_t color - RGB565 input color
* @param uint16_t colour - RGB565 input colour
* @param uint8_t &r, &g, &b - refs to variables where converted colours would be emplaced
*/
inline void MatrixPanel_I2S_DMA::color565to888(const uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b){
@ -792,7 +798,7 @@ inline void MatrixPanel_I2S_DMA::fillScreen(CRGB color)
#endif
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
// Pass 8-bit (each) R,G,B, get back 16-bit packed colour
//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) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
@ -861,7 +867,7 @@ inline void MatrixPanel_I2S_DMA::drawIcon (int *ico, int16_t x, int16_t y, int16
only one line is showed at a time, and the display looks like every pixel is driven at the same time.
Now, the RGB inputs for these types of displays are digital, meaning each red, green and blue subpixel can only be on or off. This leads to a
color palette of 8 pixels, not enough to display nice pictures. To get around this, we use binary code modulation.
colour palette of 8 pixels, not enough to display nice pictures. To get around this, we use binary code modulation.
Binary code modulation is somewhat like PWM, but easier to implement in our case. First, we define the time we would refresh the display without
binary code modulation as the 'frame time'. For, say, a four-bit binary code modulation, the frame time is divided into 15 ticks of equal length.