@@ -68,15 +68,14 @@ constexpr uint16_t OE_CLEAR_MASK = ~(1 << OE_BIT);
6868// ESP32 I2S TX FIFO position adjustment
6969// In 16-bit parallel mode with tx_fifo_mod=1, the FIFO outputs 16-bit words in swapped pairs.
7070// The FIFO reads 32-bit words from memory and outputs them as two 16-bit chunks in reversed order.
71- // This compensates by storing pixels at swapped positions in the buffer .
72- // ESP32-S2 has different FIFO ordering and doesn't need this adjustment.
71+ // XOR with 1 swaps odd/even pairs (0↔1, 2↔3, etc.). ESP32-S2 doesn't need adjustment .
72+ HUB75_CONST inline constexpr uint16_t fifo_adjust_x ( uint16_t x) {
7373#if defined(CONFIG_IDF_TARGET_ESP32)
74- HUB75_CONST inline constexpr uint16_t fifo_adjust_x ( uint16_t x) { return (x & 1U ) ? (x - 1 ) : (x + 1 ); }
74+ return x ^ 1 ;
7575#else
76- HUB75_CONST inline constexpr uint16_t fifo_adjust_x (uint16_t x) {
77- return x; // No adjustment for ESP32-S2
78- }
76+ return x;
7977#endif
78+ }
8079
8180// ============================================================================
8281// Constructor / Destructor
@@ -622,11 +621,11 @@ void I2sDma::initialize_buffer_internal(RowBitPlaneBuffer *buffers) {
622621
623622 // Fill all pixels with control bits (RGB=0, row address, OE=HIGH)
624623 for (uint16_t x = 0 ; x < dma_width_; x++) {
625- buf[x ] = (addr_for_buffer << ADDR_SHIFT) | (1 << OE_BIT);
624+ buf[fifo_adjust_x (x) ] = (addr_for_buffer << ADDR_SHIFT) | (1 << OE_BIT);
626625 }
627626
628627 // Set LAT bit on last pixel
629- buf[dma_width_ - 1 ] |= (1 << LAT_BIT);
628+ buf[fifo_adjust_x ( dma_width_ - 1 ) ] |= (1 << LAT_BIT);
630629 }
631630 }
632631}
@@ -660,7 +659,7 @@ void I2sDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t brig
660659 uint16_t *buf = (uint16_t *) (buffers[row].data + (bit * dma_width_ * 2 ));
661660 // Blank all pixels: set OE bit HIGH
662661 for (int x = 0 ; x < dma_width_; x++) {
663- buf[x ] |= (1 << OE_BIT);
662+ buf[fifo_adjust_x (x) ] |= (1 << OE_BIT);
664663 }
665664 }
666665 }
@@ -697,27 +696,27 @@ void I2sDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t brig
697696 for (int x = 0 ; x < dma_width_; x++) {
698697 if (x >= x_min && x < x_max) {
699698 // Enable display: clear OE bit
700- buf[x ] &= OE_CLEAR_MASK;
699+ buf[fifo_adjust_x (x) ] &= OE_CLEAR_MASK;
701700 } else {
702701 // Keep blanked: set OE bit
703- buf[x ] |= (1 << OE_BIT);
702+ buf[fifo_adjust_x (x) ] |= (1 << OE_BIT);
704703 }
705704 }
706705
707706 // CRITICAL: Latch blanking to prevent ghosting
708707 const int last_pixel = dma_width_ - 1 ;
709708
710709 // Blank LAT pixel itself
711- buf[last_pixel] |= (1 << OE_BIT);
710+ buf[fifo_adjust_x ( last_pixel) ] |= (1 << OE_BIT);
712711
713712 // Blank latch_blanking pixels BEFORE LAT
714713 for (int i = 1 ; i <= latch_blanking && (last_pixel - i) >= 0 ; i++) {
715- buf[last_pixel - i] |= (1 << OE_BIT);
714+ buf[fifo_adjust_x ( last_pixel - i) ] |= (1 << OE_BIT);
716715 }
717716
718717 // Blank latch_blanking pixels at START of buffer
719718 for (int i = 0 ; i < latch_blanking && i < dma_width_; i++) {
720- buf[i ] |= (1 << OE_BIT);
719+ buf[fifo_adjust_x (i) ] |= (1 << OE_BIT);
721720 }
722721 }
723722 }
@@ -900,7 +899,7 @@ HUB75_IRAM void I2sDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_t
900899 auto transformed =
901900 transform_coordinate (px, py, needs_layout_remap_, needs_scan_remap_, layout_, scan_wiring_, panel_width_,
902901 panel_height_, layout_rows_, layout_cols_, dma_width_, num_rows_);
903- px = fifo_adjust_x (transformed.x ); // Apply I2S FIFO position adjustment for ESP32
902+ px = fifo_adjust_x (transformed.x );
904903 const uint16_t row = transformed.row ;
905904 const bool is_lower = transformed.is_lower ;
906905
@@ -965,7 +964,7 @@ void I2sDma::clear() {
965964
966965 for (uint16_t x = 0 ; x < dma_width_; x++) {
967966 // Clear RGB bits, preserve control bits
968- buf[x ] &= ~RGB_MASK;
967+ buf[fifo_adjust_x (x) ] &= ~RGB_MASK;
969968 }
970969 }
971970 }
0 commit comments