2020-11-28 09:39:35 +01:00
# include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
2018-10-23 02:00:47 +02:00
2022-11-17 01:45:40 +01:00
# if defined(SPIRAM_DMA_BUFFER)
2023-03-18 21:12:45 +01:00
// Sprite_TM saves the day again...
// https://www.esp32.com/viewtopic.php?f=2&t=30584
# include "rom/cache.h"
# endif
2022-11-17 01:45:40 +01:00
2023-03-18 21:12:45 +01:00
/* This replicates same function in rowBitStruct, but due to induced inlining it might be MUCH faster
* when used in tight loops while method from struct could be flushed out of instruction cache between
2022-10-26 01:50:32 +02:00
* loop cycles do NOT forget about buff_id param if using this .
2021-02-10 16:49:19 +01:00
*/
2023-03-18 21:12:45 +01:00
// #define getRowDataPtr(row, _dpth, buff_id) &(dma_buff.rowBits[row]->data[_dpth * dma_buff.rowBits[row]->width + buff_id*(dma_buff.rowBits[row]->width * dma_buff.rowBits[row]->colour_depth)])
// BufferID is now ignored, seperate global pointer pointer!
# define getRowDataPtr(row, _dpth, buff_id) &(fb->rowBits[row]->data[_dpth * fb->rowBits[row]->width])
2022-10-26 01:50:32 +02:00
2022-11-07 01:56:44 +01:00
/* We need to update the correct uint16_t in the rowBitStruct array, that gets sent out in parallel
* 16 bit parallel mode - Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
* Irrelevant for ESP32 - S2 the way the FIFO ordering works is different - refer to page 679 of S2 technical reference manual
*/
2023-03-18 21:12:45 +01:00
# if defined(ESP32_THE_ORIG)
# define ESP32_TX_FIFO_POSITION_ADJUST(x_coord) (((x_coord)&1U) ? (x_coord - 1) : (x_coord + 1))
# else
# define ESP32_TX_FIFO_POSITION_ADJUST(x_coord) x_coord
# endif
2022-10-26 17:49:49 +02:00
2023-03-18 21:12:45 +01:00
/* This library is designed to take an 8 bit / 1 byte value (0-255) for each R G B colour sub-pixel.
2023-01-28 22:54:09 +01:00
* The PIXEL_COLOR_DEPTH_BITS should always be ' 8 ' as a result .
2022-11-07 01:56:44 +01:00
* 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 .
*/
2023-03-18 21:12:45 +01:00
// #if PIXEL_COLOR_DEPTH_BITS > 12
// #error "Color depth bits cannot be greater than 12."
// #elif PIXEL_COLOR_DEPTH_BITS < 2
// #error "Color depth bits cannot be less than 2."
// #endif
2023-03-10 11:46:17 +01:00
2023-03-18 21:12:45 +01:00
// #define MASK_OFFSET (16 - PIXEL_COLOR_DEPTH_BITS)
// #define PIXEL_COLOR_MASK_BIT(color_depth_index) (1 << (color_depth_index + MASK_OFFSET))
# define PIXEL_COLOR_MASK_BIT(color_depth_index, mask_offset) (1 << (color_depth_index + mask_offset))
// static constexpr uint8_t const MASK_OFFSET = 8-PIXEL_COLOUR_DEPTH_BITS;
2022-11-07 01:56:44 +01:00
/*
2023-01-28 22:54:09 +01:00
# if PIXEL_COLOR_DEPTH_BITS < 8
2022-11-07 01:56:44 +01:00
uint8_t mask = ( 1 < < ( colour_depth_idx + MASK_OFFSET ) ) ; // expect 24 bit colour (8 bits per RGB subpixel)
# else
2023-01-28 22:54:09 +01:00
uint8_t mask = ( 1 < < ( colour_depth_idx ) ) ; // expect 24 bit colour (8 bits per RGB subpixel)
2023-03-18 21:12:45 +01:00
# endif
2022-11-07 01:56:44 +01:00
*/
2022-10-26 17:49:49 +02:00
2020-11-28 10:45:30 +01:00
bool MatrixPanel_I2S_DMA : : allocateDMAmemory ( )
2018-10-23 02:00:47 +02:00
{
2022-10-23 17:32:29 +02:00
2023-03-18 21:12:45 +01:00
ESP_LOGI ( " I2S-DMA " , " Free heap: %d " , heap_caps_get_free_size ( MALLOC_CAP_INTERNAL ) ) ;
ESP_LOGI ( " I2S-DMA " , " Free SPIRAM: %d " , heap_caps_get_free_size ( MALLOC_CAP_SPIRAM ) ) ;
2022-10-23 17:32:29 +02:00
2022-09-30 04:17:19 +02:00
// Alright, theoretically we should be OK, so let us do this, so
// lets allocate a chunk of memory for each row (a row could span multiple panels if chaining is in place)
2023-03-18 21:12:45 +01:00
ESP_LOGI ( " I2S-DMA " , " allocating rowBitStructs with pixel_color_depth_bits of %d " , m_cfg . getPixelColorDepthBits ( ) ) ;
// iterate through number of rows, allocate memory for each
size_t allocated_fb_memory = 0 ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
int fbs_required = ( m_cfg . double_buff ) ? 2 : 1 ;
for ( int fb = 0 ; fb < fbs_required ; fb + + )
{
frame_buffer [ fb ] . rowBits . reserve ( ROWS_PER_FRAME ) ;
for ( int malloc_num = 0 ; malloc_num < ROWS_PER_FRAME ; + + malloc_num )
2022-01-03 19:43:52 +01:00
{
2023-03-18 21:12:45 +01:00
auto ptr = std : : make_shared < rowBitStruct > ( PIXELS_PER_ROW , m_cfg . getPixelColorDepthBits ( ) , m_cfg . double_buff ) ;
2020-08-13 13:16:17 +02:00
2023-03-18 21:12:45 +01:00
if ( ptr - > data = = nullptr )
{
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 ) ;
2022-11-07 01:56:44 +01:00
2023-03-18 21:12:45 +01:00
return false ;
// TODO: should we release all previous rowBitStructs here???
}
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
allocated_fb_memory + = ptr - > size ( ) ;
frame_buffer [ fb ] . rowBits . emplace_back ( ptr ) ; // save new rowBitStruct into rows vector
+ + frame_buffer [ fb ] . rows ;
2018-10-23 02:00:47 +02:00
}
2023-03-18 21:12:45 +01:00
}
ESP_LOGI ( " I2S-DMA " , " Allocating %d bytes memory for DMA BCM framebuffer(s). " , allocated_fb_memory ) ;
2022-10-05 23:04:41 +02:00
2023-03-18 21:12:45 +01:00
// calculate the lowest LSBMSB_TRANSITION_BIT value that will fit in memory that will meet or exceed the configured refresh rate
# if !defined(FORCE_COLOR_DEPTH)
2023-01-22 14:22:36 +01:00
2023-03-18 21:12:45 +01:00
ESP_LOGI ( " I2S-DMA " , " Minimum visual refresh rate (scan rate from panel top to bottom) requested: %d Hz " , m_cfg . min_refresh_rate ) ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
while ( 1 )
{
int psPerClock = 1000000000000UL / m_cfg . i2sspeed ;
int nsPerLatch = ( ( PIXELS_PER_ROW + CLKS_DURING_LATCH ) * psPerClock ) / 1000 ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
// add time to shift out LSBs + LSB-MSB transition bit - this ignores fractions...
int nsPerRow = m_cfg . getPixelColorDepthBits ( ) * nsPerLatch ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
// add time to shift out MSBs
for ( int i = lsbMsbTransitionBit + 1 ; i < m_cfg . getPixelColorDepthBits ( ) ; i + + )
nsPerRow + = ( 1 < < ( i - lsbMsbTransitionBit - 1 ) ) * ( m_cfg . getPixelColorDepthBits ( ) - i ) * nsPerLatch ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
int nsPerFrame = nsPerRow * ROWS_PER_FRAME ;
int actualRefreshRate = 1000000000UL / ( nsPerFrame ) ;
calculated_refresh_rate = actualRefreshRate ;
ESP_LOGW ( " I2S-DMA " , " lsbMsbTransitionBit of %d gives %d Hz refresh rate. " , lsbMsbTransitionBit , actualRefreshRate ) ;
if ( actualRefreshRate > m_cfg . min_refresh_rate )
break ;
if ( lsbMsbTransitionBit < m_cfg . getPixelColorDepthBits ( ) - 1 )
lsbMsbTransitionBit + + ;
else
break ;
}
if ( lsbMsbTransitionBit > 0 )
{
ESP_LOGW ( " I2S-DMA " , " lsbMsbTransitionBit of %d used to achieve refresh rate of %d Hz. Percieved colour depth to the eye may be reduced. " , lsbMsbTransitionBit , m_cfg . min_refresh_rate ) ;
}
2018-10-23 02:00:47 +02:00
2023-03-10 11:46:17 +01:00
ESP_LOGI ( " I2S-DMA " , " DMA has pixel_color_depth_bits of %d " , m_cfg . getPixelColorDepthBits ( ) - lsbMsbTransitionBit ) ;
2022-10-05 23:04:41 +02:00
# endif
2018-10-23 02:00:47 +02:00
2020-08-02 23:03:16 +02:00
/***
* Step 2 a : lsbMsbTransition bit is now finalised - recalculate the DMA descriptor count required , which is used for
* memory allocation of the DMA linked list memory structure .
2023-03-18 21:12:45 +01:00
*/
int numDMAdescriptorsPerRow = 1 ;
for ( int i = lsbMsbTransitionBit + 1 ; i < m_cfg . getPixelColorDepthBits ( ) ; i + + )
{
numDMAdescriptorsPerRow + = ( 1 < < ( i - lsbMsbTransitionBit - 1 ) ) ;
}
2022-10-05 22:47:16 +02:00
2023-03-18 21:12:45 +01:00
ESP_LOGI ( " I2S-DMA " , " Recalculated number of DMA descriptors per row: %d " , numDMAdescriptorsPerRow ) ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
// Refer to 'DMA_LL_PAYLOAD_SPLIT' code in configureDMA() below to understand why this exists.
// numDMAdescriptorsPerRow is also used to calculate descount which is super important in i2s_parallel_config_t SoC DMA setup.
if ( frame_buffer [ 0 ] . rowBits [ 0 ] - > size ( ) > DMA_MAX )
{
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
ESP_LOGW ( " I2S-DMA " , " rowBits struct is too large to fit in one DMA transfer payload, splitting required. Adding %d DMA descriptors \n " , m_cfg . getPixelColorDepthBits ( ) - 1 ) ;
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
numDMAdescriptorsPerRow + = m_cfg . getPixelColorDepthBits ( ) - 1 ;
// Note: If numDMAdescriptorsPerRow is even just one descriptor too large, DMA linked list will not correctly loop.
}
2020-08-02 23:03:16 +02:00
2020-07-29 09:47:54 +02:00
/***
* Step 3 : Allocate memory for DMA linked list , linking up each framebuffer row in sequence for GPIO output .
2023-03-18 21:12:45 +01:00
*/
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
// malloc the DMA linked list descriptors that i2s_parallel will need
desccount = numDMAdescriptorsPerRow * ROWS_PER_FRAME ;
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
if ( m_cfg . double_buff )
{
dma_bus . enable_double_dma_desc ( ) ;
}
2022-09-30 04:17:19 +02:00
2023-03-18 21:12:45 +01:00
dma_bus . allocate_dma_desc_memory ( desccount ) ;
2020-07-29 11:44:38 +02:00
2023-03-18 21:12:45 +01:00
// point FB we can write to, to 0 / dmadesc_a
fb = & frame_buffer [ 0 ] ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
// Just os we know
initialized = true ;
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
return true ;
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
} // end allocateDMAmemory()
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
void MatrixPanel_I2S_DMA : : configureDMA ( const HUB75_I2S_CFG & _cfg )
2020-07-29 09:47:54 +02:00
{
2023-03-18 21:12:45 +01:00
// lldesc_t *previous_dmadesc_a = 0;
// lldesc_t *previous_dmadesc_b = 0;
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 = m_cfg . getPixelColorDepthBits ( ) ;
if ( frame_buffer [ 0 ] . rowBits [ 0 ] - > size ( ) > DMA_MAX )
{
num_dma_payload_colour_depths = 1 ;
}
// Fill DMA linked lists for both frames (as in, halves of the HUB75 panel) and if double buffering is enabled, link it up for both buffers.
for ( int row = 0 ; row < ROWS_PER_FRAME ; row + + )
{
// first set of data is LSB through MSB, single pass (IF TOTAL SIZE < DMA_MAX) - all colour bits are displayed once, which takes care of everything below and including LSBMSB_TRANSITION_BIT
// NOTE: size must be less than DMA_MAX - worst case for library: 16-bpp with 256 pixels per row would exceed this, need to break into two
// link_dma_desc(&dmadesc_a[current_dmadescriptor_offset], previous_dmadesc_a, dma_buff.rowBits[row]->getDataPtr(), dma_buff.rowBits[row]->size(num_dma_payload_colour_depths));
// previous_dmadesc_a = &dmadesc_a[current_dmadescriptor_offset];
dma_bus . create_dma_desc_link ( frame_buffer [ 0 ] . rowBits [ row ] - > getDataPtr ( 0 , 0 ) , frame_buffer [ 0 ] . rowBits [ row ] - > size ( 1 ) , false ) ;
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
if ( m_cfg . double_buff )
{
dma_bus . create_dma_desc_link ( frame_buffer [ 1 ] . rowBits [ row ] - > getDataPtr ( 0 , 1 ) , frame_buffer [ 1 ] . rowBits [ row ] - > size ( 1 ) , true ) ;
2020-08-02 23:03:16 +02:00
}
2023-03-18 21:12:45 +01:00
current_dmadescriptor_offset + + ;
// If the number of pixels per row is too great for the size of a DMA payload, so we need to split what we were going to send above.
if ( frame_buffer [ 0 ] . rowBits [ 0 ] - > size ( ) > DMA_MAX )
2022-10-05 22:47:16 +02:00
{
2022-09-30 04:17:19 +02:00
2023-03-18 21:12:45 +01:00
for ( int cd = 1 ; cd < m_cfg . getPixelColorDepthBits ( ) ; cd + + )
{
dma_bus . create_dma_desc_link ( frame_buffer [ 0 ] . rowBits [ row ] - > getDataPtr ( cd , 0 ) , frame_buffer [ 0 ] . rowBits [ row ] - > size ( 1 ) , false ) ;
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
if ( m_cfg . double_buff )
2022-10-05 22:47:16 +02:00
{
2023-03-18 21:12:45 +01:00
dma_bus . create_dma_desc_link ( frame_buffer [ 1 ] . rowBits [ row ] - > getDataPtr ( cd , 1 ) , frame_buffer [ 1 ] . rowBits [ row ] - > size ( 1 ) , true ) ;
2022-10-05 22:47:16 +02:00
}
2020-08-02 23:03:16 +02:00
2020-07-29 09:47:54 +02:00
current_dmadescriptor_offset + + ;
2023-03-18 21:12:45 +01:00
} // additional linked list items
} // row depth struct
for ( int i = lsbMsbTransitionBit + 1 ; i < m_cfg . getPixelColorDepthBits ( ) ; 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)
// we need 2^(i - LSBMSB_TRANSITION_BIT - 1) == 1 << (i - LSBMSB_TRANSITION_BIT - 1) passes from i to MSB
for ( int k = 0 ; k < ( 1 < < ( i - lsbMsbTransitionBit - 1 ) ) ; k + + )
{
dma_bus . create_dma_desc_link ( frame_buffer [ 0 ] . rowBits [ row ] - > getDataPtr ( i , 0 ) , frame_buffer [ 0 ] . rowBits [ row ] - > size ( 1 ) , false ) ;
if ( m_cfg . double_buff )
2020-08-02 23:13:04 +02:00
{
2023-03-18 21:12:45 +01:00
dma_bus . create_dma_desc_link ( frame_buffer [ 1 ] . rowBits [ row ] - > getDataPtr ( i , 1 ) , frame_buffer [ 1 ] . rowBits [ row ] - > size ( 1 ) , true ) ;
}
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
current_dmadescriptor_offset + + ;
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
} // end colour depth ^ 2 linked list
} // end colour depth loop
} // end frame rows
ESP_LOGI ( " I2S-DMA " , " %d DMA descriptors linked to buffer data. " , current_dmadescriptor_offset ) ;
//
// Setup DMA and Output to GPIO
//
auto bus_cfg = dma_bus . config ( ) ; // バス設定用の構造体を取得します。
bus_cfg . bus_freq = m_cfg . i2sspeed ;
bus_cfg . pin_wr = m_cfg . gpio . clk ; // WR を接続しているピン番号
bus_cfg . pin_d0 = m_cfg . gpio . r1 ;
bus_cfg . pin_d1 = m_cfg . gpio . g1 ;
bus_cfg . pin_d2 = m_cfg . gpio . b1 ;
bus_cfg . pin_d3 = m_cfg . gpio . r2 ;
bus_cfg . pin_d4 = m_cfg . gpio . g2 ;
bus_cfg . pin_d5 = m_cfg . gpio . b2 ;
bus_cfg . pin_d6 = m_cfg . gpio . lat ;
bus_cfg . pin_d7 = m_cfg . gpio . oe ;
bus_cfg . pin_d8 = m_cfg . gpio . a ;
bus_cfg . pin_d9 = m_cfg . gpio . b ;
bus_cfg . pin_d10 = m_cfg . gpio . c ;
bus_cfg . pin_d11 = m_cfg . gpio . d ;
bus_cfg . pin_d12 = m_cfg . gpio . e ;
bus_cfg . pin_d13 = - 1 ;
bus_cfg . pin_d14 = - 1 ;
bus_cfg . pin_d15 = - 1 ;
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
# if defined(SPIRAM_DMA_BUFFER)
bus_cfg . psram_clk_override = true ;
# endif
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
dma_bus . config ( bus_cfg ) ;
2020-08-02 23:03:16 +02:00
2023-03-18 21:12:45 +01:00
dma_bus . init ( ) ;
dma_bus . dma_transfer_start ( ) ;
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
flipDMABuffer ( ) ; // display back buffer 0, draw to 1, ignored if double buffering isn't enabled.
// i2s_parallel_send_dma(ESP32_I2S_DEVICE, &dmadesc_a[0]);
ESP_LOGI ( " I2S-DMA " , " DMA setup completed " ) ;
} // end initMatrixDMABuff
2020-07-29 09:47:54 +02:00
2020-08-13 13:16:17 +02:00
/* There are 'bits' set in the frameStruct that we simply don't need to set every single time we change a pixel / DMA buffer co-ordinate.
2022-01-03 19:43:52 +01:00
* For example , the bits that determine the address lines , we don ' t need to set these every time . Once they ' re in place , and assuming we
2021-10-16 16:00:43 +02:00
* don ' t accidentally clear them , then we don ' t need to set them again .
2020-08-13 13:16:17 +02:00
* So to save processing , we strip this logic out to the absolute bare minimum , which is toggling only the R , G , B pixels ( bits ) per co - ord .
*
* Critical dependency : That ' updateMatrixDMABuffer ( uint8_t red , uint8_t green , uint8_t blue ) ' has been run at least once over the
* entire frameBuffer to ensure all the non R , G , B bitmasks are in place ( i . e . like OE , Address Lines etc . )
2020-08-13 16:56:21 +02:00
*
* Note : If you change the brightness with setBrightness ( ) you MUST then clearScreen ( ) and repaint / flush the entire framebuffer .
2020-08-13 13:16:17 +02:00
*/
2020-07-29 09:47:54 +02:00
2021-02-10 16:49:19 +01:00
/** @brief - Update pixel at specific co-ordinate in the DMA buffer
* this is the main method used to update DMA buffer on pixel - by - pixel level so it must be fast , real fast !
* Let ' s put it into IRAM to avoid situations when it could be flushed out of instruction cache
* and had to be read from spi - flash over and over again .
* Yes , it is always a tradeoff between memory / speed / size , but compared to DMA - buffer size is not a big deal
2023-03-18 21:12:45 +01:00
*
2022-10-26 17:49:49 +02:00
* Note : Cannot pass a negative co - ord as it makes no sense in the DMA bit array lookup .
2021-02-10 16:49:19 +01:00
*/
2022-10-26 17:49:49 +02:00
void IRAM_ATTR MatrixPanel_I2S_DMA : : updateMatrixDMABuffer ( uint16_t x_coord , uint16_t y_coord , uint8_t red , uint8_t green , uint8_t blue )
2018-10-23 02:00:47 +02:00
{
2023-03-18 21:12:45 +01:00
if ( ! initialized )
return ;
2020-08-13 00:40:44 +02:00
2020-12-07 10:25:29 +01:00
/* 1) Check that the co-ordinates are within range, or it'll break everything big time.
2023-03-18 21:12:45 +01:00
* Valid co - ordinates are from 0 to ( MATRIX_XXXX - 1 )
*/
if ( x_coord > = PIXELS_PER_ROW | | y_coord > = m_cfg . mx_height )
{
2020-12-07 10:25:29 +01:00
return ;
}
2023-03-18 21:12:45 +01:00
/* 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 colour is exactly ' 1 '
2020-12-07 10:25:29 +01:00
* https : //ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/
2023-03-18 21:12:45 +01:00
*/
uint16_t red16 , green16 , blue16 ;
2021-02-20 16:26:20 +01:00
# ifndef NO_CIE1931
2023-03-18 21:12:45 +01:00
red16 = lumConvTab [ red ] ;
green16 = lumConvTab [ green ] ;
blue16 = lumConvTab [ blue ] ;
2023-02-08 15:33:14 +01:00
# else
2023-03-18 21:12:45 +01:00
red16 = red < < 8 ;
green16 = green < < 8 ;
blue16 = blue < < 8 ;
2021-02-20 16:26:20 +01:00
# endif
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
/* When using the drawPixel, we are obviously only changing the value of one x,y position,
* however , the two - scan panels paint TWO lines at the same time
* and this reflects the parallel in - DMA - memory data structure of uint16_t ' s that are getting
* pumped out at high speed .
*
* So we need to ensure we persist the bits ( 8 of them ) of the uint16_t for the row we aren ' t changing .
*
* The DMA buffer order has also been reversed ( refer to the last code in this function )
* so we have to check for this and check the correct position of the MATRIX_DATA_STORAGE_TYPE
* data .
*/
x_coord = ESP32_TX_FIFO_POSITION_ADJUST ( x_coord ) ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
uint16_t _colourbitclear = BITMASK_RGB1_CLEAR , _colourbitoffset = 0 ;
2020-07-29 09:47:54 +02:00
2023-03-18 21:12:45 +01:00
if ( y_coord > = ROWS_PER_FRAME )
{ // if we are drawing to the bottom part of the panel
_colourbitoffset = BITS_RGB2_OFFSET ;
_colourbitclear = BITMASK_RGB2_CLEAR ;
y_coord - = ROWS_PER_FRAME ;
}
2018-10-23 02:00:47 +02:00
2023-03-18 21:12:45 +01:00
// Iterating through colour depth bits, which we assume are 8 bits per RGB subpixel (24bpp)
uint8_t colour_depth_idx = m_cfg . getPixelColorDepthBits ( ) ;
do
{
- - colour_depth_idx ;
uint16_t mask = PIXEL_COLOR_MASK_BIT ( colour_depth_idx , MASK_OFFSET ) ;
uint16_t RGB_output_bits = 0 ;
2020-12-07 10:25:29 +01:00
2023-03-18 21:12:45 +01:00
/* Per the .h file, the order of the output RGB bits is:
* BIT_B2 , BIT_G2 , BIT_R2 , BIT_B1 , BIT_G1 , BIT_R1 */
RGB_output_bits | = ( bool ) ( blue16 & mask ) ; // --B
RGB_output_bits < < = 1 ;
RGB_output_bits | = ( bool ) ( green16 & mask ) ; // -BG
RGB_output_bits < < = 1 ;
RGB_output_bits | = ( bool ) ( red16 & mask ) ; // BGR
RGB_output_bits < < = _colourbitoffset ; // shift colour bits to the required position
2020-12-07 10:25:29 +01:00
2023-03-18 21:12:45 +01:00
// Get the contents at this address,
// 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 ) ;
2020-12-07 10:25:29 +01:00
2023-03-18 21:12:45 +01:00
// We need to update the correct uint16_t word in the rowBitStruct array pointing to a specific pixel at X - coordinate
p [ x_coord ] & = _colourbitclear ; // reset RGB bits
p [ x_coord ] | = RGB_output_bits ; // set new RGB bits
2019-01-10 00:51:27 +01:00
2023-03-18 21:12:45 +01:00
# if defined(SPIRAM_DMA_BUFFER)
Cache_WriteBack_Addr ( ( uint32_t ) & p [ x_coord ] , sizeof ( ESP32_I2S_DMA_STORAGE_TYPE ) ) ;
# endif
2022-11-17 01:45:40 +01:00
2023-03-18 21:12:45 +01:00
} while ( colour_depth_idx ) ; // end of colour depth loop (8)
2020-07-29 11:44:38 +02:00
} // updateMatrixDMABuffer (specific co-ords change)
2018-10-23 02:00:47 +02:00
2019-01-03 00:09:32 +01:00
/* Update the entire buffer with a single specific colour - quicker */
2021-08-18 17:33:59 +02:00
void MatrixPanel_I2S_DMA : : updateMatrixDMABuffer ( uint8_t red , uint8_t green , uint8_t blue )
2019-01-03 00:09:32 +01:00
{
2023-03-18 21:12:45 +01:00
if ( ! initialized )
return ;
/* https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/ */
uint16_t red16 , green16 , blue16 ;
2021-02-20 16:26:20 +01:00
# ifndef NO_CIE1931
2023-03-18 21:12:45 +01:00
red16 = lumConvTab [ red ] ;
green16 = lumConvTab [ green ] ;
blue16 = lumConvTab [ blue ] ;
2023-02-08 15:33:14 +01:00
# else
2023-03-18 21:12:45 +01:00
red16 = red < < 8 ;
green16 = green < < 8 ;
blue16 = blue < < 8 ;
2021-02-20 16:26:20 +01:00
# endif
2020-07-29 11:44:38 +02:00
2023-03-18 21:12:45 +01:00
for ( uint8_t colour_depth_idx = 0 ; colour_depth_idx < m_cfg . getPixelColorDepthBits ( ) ; colour_depth_idx + + ) // colour depth - 8 iterations
2020-12-07 10:25:29 +01:00
{
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
2023-03-18 21:12:45 +01:00
uint16_t RGB_output_bits = 0 ;
2022-11-07 01:56:44 +01:00
2023-03-11 11:51:41 +01:00
uint16_t mask = PIXEL_COLOR_MASK_BIT ( colour_depth_idx , MASK_OFFSET ) ;
2022-01-03 19:43:52 +01:00
/* Per the .h file, the order of the output RGB bits is:
* BIT_B2 , BIT_G2 , BIT_R2 , BIT_B1 , BIT_G1 , BIT_R1 */
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( blue16 & mask ) ; // --B
2020-12-08 09:10:35 +01:00
RGB_output_bits < < = 1 ;
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( green16 & mask ) ; // -BG
2020-12-08 09:10:35 +01:00
RGB_output_bits < < = 1 ;
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( red16 & mask ) ; // BGR
2022-01-03 19:43:52 +01:00
// Duplicate and shift across so we have have 6 populated bits of RGB1 and RGB2 pin values suitable for DMA buffer
2023-03-18 21:12:45 +01:00
RGB_output_bits | = RGB_output_bits < < BITS_RGB2_OFFSET ; // BGRBGR
// Serial.printf("Fill with: 0x%#06x\n", RGB_output_bits);
2020-12-07 10:25:29 +01:00
// iterate rows
2023-03-18 21:12:45 +01:00
int matrix_frame_parallel_row = fb - > rowBits . size ( ) ;
do
{
2021-02-10 16:49:19 +01:00
- - matrix_frame_parallel_row ;
// The destination for the pixel row bitstream
2022-10-26 01:50:32 +02:00
ESP32_I2S_DMA_STORAGE_TYPE * p = getRowDataPtr ( matrix_frame_parallel_row , colour_depth_idx , back_buffer_id ) ;
2020-12-07 10:25:29 +01:00
// iterate pixels in a row
2023-03-18 21:12:45 +01:00
int x_coord = fb - > rowBits [ matrix_frame_parallel_row ] - > width ;
do
{
2021-02-10 16:49:19 +01:00
- - x_coord ;
2023-03-18 21:12:45 +01:00
p [ x_coord ] & = BITMASK_RGB12_CLEAR ; // reset colour bits
p [ x_coord ] | = RGB_output_bits ; // set new colour bits
2022-11-17 01:45:40 +01:00
2023-03-18 21:12:45 +01:00
# if defined(SPIRAM_DMA_BUFFER)
Cache_WriteBack_Addr ( ( uint32_t ) & p [ x_coord ] , sizeof ( ESP32_I2S_DMA_STORAGE_TYPE ) ) ;
# endif
2022-11-17 01:45:40 +01:00
2023-03-18 21:12:45 +01:00
} while ( x_coord ) ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
} while ( matrix_frame_parallel_row ) ; // end row iteration
} // colour depth loop (8)
2020-11-28 01:19:20 +01:00
} // updateMatrixDMABuffer (full frame paint)
2021-02-10 16:49:19 +01:00
/**
2022-10-05 22:47:16 +02:00
* @ brief - clears and reinitializes colour / control data in DMA buffs
2021-10-16 16:00:43 +02:00
* When allocated , DMA buffs might be dirty , so we need to blank it and initialize ABCDE , LAT , OE control bits .
2022-10-05 22:47:16 +02:00
* Those control bits are constants during the entire DMA sweep and never changed when updating just pixel colour data
2023-03-18 21:12:45 +01:00
* so we could set it once on DMA buffs initialization and forget .
2021-02-10 16:49:19 +01:00
* This effectively clears buffers to blank BLACK and makes it ready to display output .
2023-01-08 01:16:29 +01:00
* ( Brightness control via OE bit manipulation is another case ) - this must be done as well seperately !
2021-02-10 16:49:19 +01:00
*/
2023-03-18 21:12:45 +01:00
void MatrixPanel_I2S_DMA : : clearFrameBuffer ( bool _buff_id )
{
2021-02-10 16:49:19 +01:00
if ( ! initialized )
return ;
// we start with iterating all rows in dma_buff structure
2023-03-18 21:12:45 +01:00
int row_idx = fb - > rowBits . size ( ) ;
do
{
2021-02-10 16:49:19 +01:00
- - row_idx ;
2023-03-18 21:12:45 +01:00
ESP32_I2S_DMA_STORAGE_TYPE * row = fb - > rowBits [ row_idx ] - > getDataPtr ( 0 , _buff_id ) ; // set pointer to the HEAD of a buffer holding data for the entire matrix row
2021-02-10 16:49:19 +01:00
ESP32_I2S_DMA_STORAGE_TYPE abcde = ( ESP32_I2S_DMA_STORAGE_TYPE ) row_idx ;
2023-03-18 21:12:45 +01:00
abcde < < = BITS_ADDR_OFFSET ; // shift row y-coord to match ABCDE bits in vector from 8 to 12
2021-02-10 16:49:19 +01:00
2022-10-05 22:47:16 +02:00
// get last pixel index in a row of all colourdepths
2023-03-18 21:12:45 +01:00
int x_pixel = fb - > rowBits [ row_idx ] - > width * fb - > rowBits [ row_idx ] - > colour_depth ;
// Serial.printf(" from pixel %d, ", x_pixel);
2021-02-10 16:49:19 +01:00
2022-10-05 22:47:16 +02:00
// fill all x_pixels except colour_index[0] (LSB) ones, this also clears all colour data to 0's black
2023-03-18 21:12:45 +01:00
do
{
2021-02-10 16:49:19 +01:00
- - x_pixel ;
2023-03-18 21:12:45 +01:00
if ( m_cfg . driver = = HUB75_I2S_CFG : : SM5266P )
{
// modifications here for row shift register type SM5266P
2022-01-03 19:43:52 +01:00
// https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164
2023-03-18 21:12:45 +01:00
row [ x_pixel ] = abcde & ( 0x18 < < BITS_ADDR_OFFSET ) ; // mask out the bottom 3 bits which are the clk di bk inputs
}
else
{
2022-10-26 17:49:49 +02:00
row [ ESP32_TX_FIFO_POSITION_ADJUST ( x_pixel ) ] = abcde ;
2022-01-03 19:43:52 +01:00
}
2023-03-13 19:27:39 +01:00
2023-03-18 21:12:45 +01:00
} while ( x_pixel ! = fb - > rowBits [ row_idx ] - > width & & x_pixel ) ;
2021-02-10 16:49:19 +01:00
2022-10-05 22:47:16 +02:00
// colour_index[0] (LSB) x_pixels must be "marked" with a previous's row address, 'cause it is used to display
2021-02-10 16:49:19 +01:00
// previous row while we pump in LSB's for a new row
2023-03-18 21:12:45 +01:00
abcde = ( ( ESP32_I2S_DMA_STORAGE_TYPE ) row_idx - 1 ) < < BITS_ADDR_OFFSET ;
do
{
2021-02-10 16:49:19 +01:00
- - x_pixel ;
2023-03-18 21:12:45 +01:00
if ( m_cfg . driver = = HUB75_I2S_CFG : : SM5266P )
{
// modifications here for row shift register type SM5266P
2022-01-03 19:43:52 +01:00
// https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164
2023-03-18 21:12:45 +01:00
row [ x_pixel ] = abcde & ( 0x18 < < BITS_ADDR_OFFSET ) ; // mask out the bottom 3 bits which are the clk di bk inputs
}
else
{
2022-10-26 17:49:49 +02:00
row [ ESP32_TX_FIFO_POSITION_ADJUST ( x_pixel ) ] = abcde ;
2023-03-18 21:12:45 +01:00
}
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
} while ( x_pixel ) ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
// modifications here for row shift register type SM5266P
// https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/issues/164
if ( m_cfg . driver = = HUB75_I2S_CFG : : SM5266P )
{
uint16_t serialCount ;
uint16_t latch ;
x_pixel = fb - > rowBits [ row_idx ] - > width - 16 ; // come back 8*2 pixels to allow for 8 writes
serialCount = 8 ;
do
{
serialCount - - ;
latch = row [ x_pixel ] | ( ( ( ( ( ESP32_I2S_DMA_STORAGE_TYPE ) row_idx ) % 8 ) = = serialCount ) < < 1 ) < < BITS_ADDR_OFFSET ; // data on 'B'
row [ x_pixel + + ] = latch | ( 0x05 < < BITS_ADDR_OFFSET ) ; // clock high on 'A'and BK high for update
row [ x_pixel + + ] = latch | ( 0x04 < < BITS_ADDR_OFFSET ) ; // clock low on 'A'and BK high for update
} while ( serialCount ) ;
} // end SM5266P
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
// 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 = fb - > rowBits [ row_idx ] - > colour_depth ;
do
{
2022-10-05 22:47:16 +02:00
- - colouridx ;
2021-02-10 16:49:19 +01:00
2023-01-28 22:54:09 +01:00
// switch pointer to a row for a specific colour index
2023-03-18 21:12:45 +01:00
row = fb - > rowBits [ row_idx ] - > getDataPtr ( colouridx , _buff_id ) ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
row [ ESP32_TX_FIFO_POSITION_ADJUST ( fb - > rowBits [ row_idx ] - > width - 1 ) ] | = BIT_LAT ; // -1 pixel to compensate array index starting at 0
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
// ESP32_TX_FIFO_POSITION_ADJUST(dma_buff.rowBits[row_idx]->width - 1)
2021-02-10 16:49:19 +01:00
// need to disable OE before/after latch to hide row transition
// Should be one clock or more before latch, otherwise can get ghosting
uint8_t _blank = m_cfg . latch_blanking ;
2023-03-18 21:12:45 +01:00
do
{
2021-02-10 16:49:19 +01:00
- - _blank ;
2023-01-08 00:59:12 +01:00
2023-03-18 21:12:45 +01:00
row [ ESP32_TX_FIFO_POSITION_ADJUST ( 0 + _blank ) ] | = BIT_OE ; // disable output
row [ ESP32_TX_FIFO_POSITION_ADJUST ( fb - > rowBits [ row_idx ] - > width - 1 ) ] | = BIT_OE ; // disable output
row [ ESP32_TX_FIFO_POSITION_ADJUST ( fb - > rowBits [ row_idx ] - > width - _blank - 1 ) ] | = BIT_OE ; // (LAT pulse is (width-2) -1 pixel to compensate array index starting at 0
2022-10-26 17:49:49 +02:00
2021-02-10 16:49:19 +01:00
} while ( _blank ) ;
2023-03-18 21:12:45 +01:00
} while ( colouridx ) ;
2022-11-17 01:45:40 +01:00
2023-03-18 21:12:45 +01:00
# if defined(SPIRAM_DMA_BUFFER)
Cache_WriteBack_Addr ( ( uint32_t ) row , sizeof ( ESP32_I2S_DMA_STORAGE_TYPE ) * ( ( fb - > rowBits [ row_idx ] - > width * fb - > rowBits [ row_idx ] - > colour_depth ) - 1 ) ) ;
# endif
2022-11-17 01:45:40 +01:00
2023-03-18 21:12:45 +01:00
} while ( row_idx ) ;
2023-01-08 00:59:12 +01:00
}
/**
* @ brief - reset OE bits in DMA buffer in a way to control brightness
* @ param brt - brightness level from 0 to 255 - NOT MATRIX_WIDTH
* @ param _buff_id - buffer id to control
*/
2023-03-18 21:12:45 +01:00
void MatrixPanel_I2S_DMA : : brtCtrlOEv2 ( uint8_t brt , const int _buff_id )
{
2023-01-08 00:59:12 +01:00
if ( ! initialized )
return ;
2023-03-18 21:12:45 +01:00
uint8_t _blank = m_cfg . latch_blanking ; // don't want to inadvertantly blast over this
uint8_t _depth = fb - > rowBits [ 0 ] - > colour_depth ;
uint16_t _width = fb - > rowBits [ 0 ] - > width ;
2023-01-08 00:59:12 +01:00
// start with iterating all rows in dma_buff structure
2023-03-18 21:12:45 +01:00
int row_idx = fb - > rowBits . size ( ) ;
do
{
2023-01-08 00:59:12 +01:00
- - row_idx ;
// let's set OE control bits for specific pixels in each color_index subrows
2023-02-08 15:33:14 +01:00
uint8_t colouridx = _depth ;
2023-03-18 21:12:45 +01:00
do
{
2023-01-08 00:59:12 +01:00
- - colouridx ;
2023-03-18 21:12:45 +01:00
char bitplane = ( 2 * _depth - colouridx ) % _depth ;
char bitshift = ( _depth - lsbMsbTransitionBit - 1 ) > > 1 ;
char rightshift = std : : max ( bitplane - bitshift - 2 , 0 ) ;
2023-02-08 15:33:14 +01:00
// calculate the OE disable period by brightness, and also blanking
2023-03-18 21:12:45 +01:00
int brightness_in_x_pixels = ( ( _width - _blank ) * brt ) > > ( 7 + rightshift ) ;
brightness_in_x_pixels = ( brightness_in_x_pixels > > 1 ) | ( brightness_in_x_pixels & 1 ) ;
2023-01-08 00:59:12 +01:00
// switch pointer to a row for a specific color index
2023-03-18 21:12:45 +01:00
ESP32_I2S_DMA_STORAGE_TYPE * row = fb - > rowBits [ row_idx ] - > getDataPtr ( colouridx , _buff_id ) ;
2023-01-08 00:59:12 +01:00
2023-02-08 15:33:14 +01:00
// define range of Output Enable on the center of the row
2023-03-18 21:12:45 +01:00
int x_coord_max = ( _width + brightness_in_x_pixels + 1 ) > > 1 ;
int x_coord_min = ( _width - brightness_in_x_pixels + 0 ) > > 1 ;
2023-02-08 15:33:14 +01:00
int x_coord = _width ;
2023-03-18 21:12:45 +01:00
do
{
2023-01-08 00:59:12 +01:00
- - x_coord ;
2023-03-18 21:12:45 +01:00
2023-02-08 15:33:14 +01:00
// (the check is already including "blanking" )
2023-03-18 21:12:45 +01:00
if ( x_coord > = x_coord_min & & x_coord < x_coord_max )
{
row [ ESP32_TX_FIFO_POSITION_ADJUST ( x_coord ) ] & = BITMASK_OE_CLEAR ;
}
else
{
row [ ESP32_TX_FIFO_POSITION_ADJUST ( x_coord ) ] | = BIT_OE ; // Disable output after this point.
}
} while ( x_coord ) ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
} while ( colouridx ) ;
// switch pointer to a row for a specific colour index
# if defined(SPIRAM_DMA_BUFFER)
ESP32_I2S_DMA_STORAGE_TYPE * row_hack = fb - > rowBits [ row_idx ] - > getDataPtr ( colouridx , _buff_id ) ;
Cache_WriteBack_Addr ( ( uint32_t ) row_hack , sizeof ( ESP32_I2S_DMA_STORAGE_TYPE ) * ( ( fb - > rowBits [ row_idx ] - > width * fb - > rowBits [ row_idx ] - > colour_depth ) - 1 ) ) ;
# endif
} while ( row_idx ) ;
}
2021-02-10 16:49:19 +01:00
/*
* overload for compatibility
*/
2023-03-18 21:12:45 +01:00
bool MatrixPanel_I2S_DMA : : begin ( int r1 , int g1 , int b1 , int r2 , int g2 , int b2 , int a , int b , int c , int d , int e , int lat , int oe , int clk )
{
if ( initialized )
return true ;
2021-02-11 00:28:40 +01:00
// RGB
2023-03-18 21:12:45 +01:00
m_cfg . gpio . r1 = r1 ;
m_cfg . gpio . g1 = g1 ;
m_cfg . gpio . b1 = b1 ;
m_cfg . gpio . r2 = r2 ;
m_cfg . gpio . g2 = g2 ;
m_cfg . gpio . b2 = b2 ;
2021-02-11 00:28:40 +01:00
// Line Select
2023-03-18 21:12:45 +01:00
m_cfg . gpio . a = a ;
m_cfg . gpio . b = b ;
m_cfg . gpio . c = c ;
m_cfg . gpio . d = d ;
m_cfg . gpio . e = e ;
2021-02-11 00:28:40 +01:00
// Clock & Control
2023-03-18 21:12:45 +01:00
m_cfg . gpio . lat = lat ;
m_cfg . gpio . oe = oe ;
m_cfg . gpio . clk = clk ;
2021-02-10 16:49:19 +01:00
return begin ( ) ;
}
2021-08-18 16:28:56 +02:00
2023-03-18 21:12:45 +01:00
bool MatrixPanel_I2S_DMA : : begin ( const HUB75_I2S_CFG & cfg )
{
if ( initialized )
return true ;
2023-03-11 11:51:41 +01:00
2023-03-18 21:12:45 +01:00
if ( ! setCfg ( cfg ) )
return false ;
2023-03-11 11:51:41 +01:00
return begin ( ) ;
2023-03-18 21:12:45 +01:00
}
2021-02-10 16:49:19 +01:00
/**
* @ 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
*/
2023-03-18 21:12:45 +01:00
uint8_t MatrixPanel_I2S_DMA : : setLatBlanking ( uint8_t pulses )
{
2021-02-10 16:49:19 +01:00
if ( pulses > MAX_LAT_BLANKING )
pulses = MAX_LAT_BLANKING ;
if ( ! pulses )
pulses = DEFAULT_LAT_BLANKING ;
m_cfg . latch_blanking = pulses ;
2023-03-18 21:12:45 +01:00
2023-01-08 00:59:12 +01:00
// remove brightness var for now.
2023-03-18 21:12:45 +01:00
// setPanelBrightness(brightness); // set brightness to reset OE bits to the values matching new LAT blanking setting
2021-02-10 16:49:19 +01:00
return m_cfg . latch_blanking ;
}
# ifndef NO_FAST_FUNCTIONS
/**
* @ brief - update DMA buff drawing horizontal line at specified coordinates
2023-01-08 00:59:12 +01:00
* @ param x_coord - line start coordinate x
* @ param y_coord - line start coordinate y
2021-02-10 16:49:19 +01:00
* @ param l - line length
2023-01-28 22:54:09 +01:00
* @ param r , g , b , - RGB888 colour
2021-02-10 16:49:19 +01:00
*/
2023-03-18 21:12:45 +01:00
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 )
2021-02-10 16:49:19 +01:00
return ;
2023-03-18 21:12:45 +01:00
if ( ( x_coord + l ) < 1 | | y_coord < 0 | | l < 1 | | x_coord > = PIXELS_PER_ROW | | y_coord > = m_cfg . mx_height )
2021-02-10 16:49:19 +01:00
return ;
2023-03-18 21:12:45 +01:00
l = x_coord < 0 ? l + x_coord : l ;
2023-03-09 12:25:43 +01:00
x_coord = x_coord < 0 ? 0 : x_coord ;
2023-03-18 21:12:45 +01:00
l = ( ( x_coord + l ) > = PIXELS_PER_ROW ) ? ( PIXELS_PER_ROW - x_coord ) : l ;
2021-08-16 15:34:46 +02:00
2023-03-18 21:12:45 +01:00
// if (x_coord+l > PIXELS_PER_ROW)
// l = PIXELS_PER_ROW - x_coord + 1; // reset width to end of row
2021-02-10 16:49:19 +01:00
/* LED Brightness Compensation */
2023-03-18 21:12:45 +01:00
uint16_t red16 , green16 , blue16 ;
2021-02-20 16:26:20 +01:00
# ifndef NO_CIE1931
2023-03-18 21:12:45 +01:00
red16 = lumConvTab [ red ] ;
green16 = lumConvTab [ green ] ;
blue16 = lumConvTab [ blue ] ;
2023-02-08 15:33:14 +01:00
# else
2023-03-18 21:12:45 +01:00
red16 = red < < 8 ;
green16 = green < < 8 ;
blue16 = blue < < 8 ;
2021-02-20 16:26:20 +01:00
# endif
2021-02-10 16:49:19 +01:00
2022-10-05 22:47:16 +02:00
uint16_t _colourbitclear = BITMASK_RGB1_CLEAR , _colourbitoffset = 0 ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
if ( y_coord > = ROWS_PER_FRAME )
{ // if we are drawing to the bottom part of the panel
2022-10-05 22:47:16 +02:00
_colourbitoffset = BITS_RGB2_OFFSET ;
2023-03-18 21:12:45 +01:00
_colourbitclear = BITMASK_RGB2_CLEAR ;
2021-02-10 16:49:19 +01:00
y_coord - = ROWS_PER_FRAME ;
}
2023-01-28 22:54:09 +01:00
// Iterating through colour depth bits (8 iterations)
2023-03-10 11:46:17 +01:00
uint8_t colour_depth_idx = m_cfg . getPixelColorDepthBits ( ) ;
2023-03-18 21:12:45 +01:00
do
{
2022-10-05 22:47:16 +02:00
- - colour_depth_idx ;
2021-02-10 16:49:19 +01:00
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
uint16_t RGB_output_bits = 0 ;
2023-03-18 21:12:45 +01:00
// uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST);
2023-01-28 22:54:09 +01:00
// #if PIXEL_COLOR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
2022-11-07 01:56:44 +01:00
// #else
2023-01-28 22:54:09 +01:00
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel)
2023-03-18 21:12:45 +01:00
// #endif
2023-03-11 11:51:41 +01:00
uint16_t mask = PIXEL_COLOR_MASK_BIT ( colour_depth_idx , MASK_OFFSET ) ;
2021-02-10 16:49:19 +01:00
/* Per the .h file, the order of the output RGB bits is:
2023-03-18 21:12:45 +01:00
* BIT_B2 , BIT_G2 , BIT_R2 , BIT_B1 , BIT_G1 , BIT_R1 */
RGB_output_bits | = ( bool ) ( blue16 & mask ) ; // --B
2021-02-10 16:49:19 +01:00
RGB_output_bits < < = 1 ;
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( green16 & mask ) ; // -BG
2021-02-10 16:49:19 +01:00
RGB_output_bits < < = 1 ;
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( red16 & mask ) ; // BGR
RGB_output_bits < < = _colourbitoffset ; // shift color bits to the required position
2021-02-10 16:49:19 +01:00
// Get the contents at this address,
2023-01-28 22:54:09 +01:00
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
2023-03-18 21:12:45 +01:00
ESP32_I2S_DMA_STORAGE_TYPE * p = fb - > rowBits [ y_coord ] - > getDataPtr ( colour_depth_idx , back_buffer_id ) ;
2021-02-10 16:49:19 +01:00
// inlined version works slower here, dunno why :(
2022-10-05 22:47:16 +02:00
// ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(y_coord, colour_depth_idx, back_buffer_id);
2021-02-10 16:49:19 +01:00
int16_t _l = l ;
2023-03-18 21:12:45 +01:00
do
{ // iterate pixels in a row
int16_t _x = x_coord + - - _l ;
/*
# if defined(ESP32_THE_ORIG)
// Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
uint16_t & v = p [ _x & 1U ? - - _x : + + _x ] ;
# else
// ESP 32 doesn't need byte flipping for TX FIFO.
uint16_t & v = p [ _x ] ;
# endif
*/
uint16_t & v = p [ ESP32_TX_FIFO_POSITION_ADJUST ( _x ) ] ;
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 colour depth loop (8)
2021-02-10 16:49:19 +01:00
} // hlineDMA()
/**
* @ brief - update DMA buff drawing vertical line at specified coordinates
2023-01-08 00:59:12 +01:00
* @ param x_coord - line start coordinate x
* @ param y_coord - line start coordinate y
2021-02-10 16:49:19 +01:00
* @ param l - line length
2023-01-28 22:54:09 +01:00
* @ param r , g , b , - RGB888 colour
2021-02-10 16:49:19 +01:00
*/
2023-03-18 21:12:45 +01:00
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 )
2021-02-10 16:49:19 +01:00
return ;
2023-03-18 21:12:45 +01:00
if ( x_coord < 0 | | ( y_coord + l ) < 1 | | l < 1 | | x_coord > = PIXELS_PER_ROW | | y_coord > = m_cfg . mx_height )
2021-02-10 16:49:19 +01:00
return ;
2023-03-18 21:12:45 +01:00
l = y_coord < 0 ? l + y_coord : l ;
2023-03-09 12:25:43 +01:00
y_coord = y_coord < 0 ? 0 : y_coord ;
2021-08-16 15:18:36 +02:00
// check for a length that goes beyond the height of the screen! Array out of bounds dma memory changes = screwed output #163
2023-03-18 21:12:45 +01:00
l = ( ( y_coord + l ) > = m_cfg . mx_height ) ? ( m_cfg . mx_height - y_coord ) : l ;
// if (y_coord + l > m_cfg.mx_height)
2021-08-16 15:18:36 +02:00
/// l = m_cfg.mx_height - y_coord + 1; // reset width to end of col
2021-02-10 16:49:19 +01:00
/* LED Brightness Compensation */
2023-03-18 21:12:45 +01:00
uint16_t red16 , green16 , blue16 ;
2021-02-20 16:26:20 +01:00
# ifndef NO_CIE1931
2023-03-18 21:12:45 +01:00
red16 = lumConvTab [ red ] ;
green16 = lumConvTab [ green ] ;
blue16 = lumConvTab [ blue ] ;
2023-02-08 15:33:14 +01:00
# else
2023-03-18 21:12:45 +01:00
red16 = red < < 8 ;
green16 = green < < 8 ;
blue16 = blue < < 8 ;
2021-02-20 16:26:20 +01:00
# endif
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
/*
# if defined(ESP32_THE_ORIG)
// Save the calculated value to the bitplane memory in reverse order to account for I2S Tx FIFO mode1 ordering
x_coord & 1U ? - - x_coord : + + x_coord ;
# endif
*/
2022-11-07 01:56:44 +01:00
x_coord = ESP32_TX_FIFO_POSITION_ADJUST ( x_coord ) ;
2021-02-10 16:49:19 +01:00
2023-03-10 11:46:17 +01:00
uint8_t colour_depth_idx = m_cfg . getPixelColorDepthBits ( ) ;
2023-03-18 21:12:45 +01:00
do
{ // Iterating through colour depth bits (8 iterations)
2022-10-05 22:47:16 +02:00
- - colour_depth_idx ;
2021-02-10 16:49:19 +01:00
// let's precalculate RGB1 and RGB2 bits than flood it over the entire DMA buffer
2023-03-18 21:12:45 +01:00
// uint8_t mask = (1 << colour_depth_idx COLOR_DEPTH_LESS_THAN_8BIT_ADJUST);
2023-01-28 22:54:09 +01:00
// #if PIXEL_COLOR_DEPTH_BITS < 8
// uint8_t mask = (1 << (colour_depth_idx+MASK_OFFSET)); // expect 24 bit colour (8 bits per RGB subpixel)
2022-11-07 01:56:44 +01:00
// #else
2023-01-28 22:54:09 +01:00
// uint8_t mask = (1 << (colour_depth_idx)); // expect 24 bit colour (8 bits per RGB subpixel)
2023-03-18 21:12:45 +01:00
// #endif
2022-11-07 01:56:44 +01:00
2023-03-11 11:51:41 +01:00
uint16_t mask = PIXEL_COLOR_MASK_BIT ( colour_depth_idx , MASK_OFFSET ) ;
2021-02-10 16:49:19 +01:00
uint16_t RGB_output_bits = 0 ;
/* Per the .h file, the order of the output RGB bits is:
2023-03-18 21:12:45 +01:00
* BIT_B2 , BIT_G2 , BIT_R2 , BIT_B1 , BIT_G1 , BIT_R1 */
RGB_output_bits | = ( bool ) ( blue16 & mask ) ; // --B
2021-02-10 16:49:19 +01:00
RGB_output_bits < < = 1 ;
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( green16 & mask ) ; // -BG
2021-02-10 16:49:19 +01:00
RGB_output_bits < < = 1 ;
2023-03-18 21:12:45 +01:00
RGB_output_bits | = ( bool ) ( red16 & mask ) ; // BGR
2021-02-10 16:49:19 +01:00
int16_t _l = 0 , _y = y_coord ;
2022-10-05 22:47:16 +02:00
uint16_t _colourbitclear = BITMASK_RGB1_CLEAR ;
2023-03-18 21:12:45 +01:00
do
{ // iterate pixels in a column
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
if ( _y > = ROWS_PER_FRAME )
{ // if y-coord overlapped bottom-half panel
2021-02-10 16:49:19 +01:00
_y - = ROWS_PER_FRAME ;
2023-03-18 21:12:45 +01:00
_colourbitclear = BITMASK_RGB2_CLEAR ;
2021-02-10 16:49:19 +01:00
RGB_output_bits < < = BITS_RGB2_OFFSET ;
}
// Get the contents at this address,
2023-01-28 22:54:09 +01:00
// it would represent a vector pointing to the full row of pixels for the specified colour depth bit at Y coordinate
2023-03-18 21:12:45 +01:00
// ESP32_I2S_DMA_STORAGE_TYPE *p = getRowDataPtr(_y, colour_depth_idx, back_buffer_id);
ESP32_I2S_DMA_STORAGE_TYPE * p = fb - > rowBits [ _y ] - > getDataPtr ( colour_depth_idx , back_buffer_id ) ;
2021-02-10 16:49:19 +01:00
2023-03-18 21:12:45 +01:00
p [ x_coord ] & = _colourbitclear ; // reset RGB bits
p [ x_coord ] | = RGB_output_bits ; // set new RGB bits
2021-02-10 16:49:19 +01:00
+ + _y ;
2023-03-18 21:12:45 +01:00
} while ( + + _l ! = l ) ; // iterate pixels in a col
} while ( colour_depth_idx ) ; // end of colour depth loop (8)
2021-02-10 16:49:19 +01:00
} // vlineDMA()
/**
* @ brief - update DMA buff drawing a rectangular at specified coordinates
2021-10-16 16:00:43 +02:00
* this works much faster than multiple consecutive per - pixel calls to updateMatrixDMABuffer ( )
2021-02-10 16:49:19 +01:00
* @ 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
2023-01-28 22:54:09 +01:00
* @ param uint8_t r - RGB888 colour
* @ param uint8_t g - RGB888 colour
* @ param uint8_t b - RGB888 colour
2021-02-10 16:49:19 +01:00
*/
2023-03-18 21:12:45 +01:00
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 )
{
2021-02-10 16:49:19 +01:00
// h-lines are >2 times faster than v-lines
// so will use it only for tall rects with h >2w
2023-03-18 21:12:45 +01:00
if ( h > 2 * w )
{
2021-02-10 16:49:19 +01:00
// draw using v-lines
2023-03-18 21:12:45 +01:00
do
{
2021-02-10 16:49:19 +01:00
- - w ;
2023-03-18 21:12:45 +01:00
vlineDMA ( x + w , y , h , r , g , b ) ;
} while ( w ) ;
}
else
{
2021-02-10 16:49:19 +01:00
// draw using h-lines
2023-03-18 21:12:45 +01:00
do
{
2021-02-10 16:49:19 +01:00
- - h ;
2023-03-18 21:12:45 +01:00
hlineDMA ( x , y + h , w , r , g , b ) ;
} while ( h ) ;
2020-12-07 10:25:29 +01:00
}
2021-02-10 16:49:19 +01:00
}
2023-03-18 21:12:45 +01:00
# endif // NO_FAST_FUNCTIONS