===== MAIN BIT-BANGING FUNCTION ===== This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then gcc will use register Y for the this pointer. By making it static, Y register becomes available for general use. FUNCTION PURPOSE: Outputs RGB pixel data to WS2812/WS2811 LEDs using precisely timed bit-banging While outputting each byte (
| 675 | // 2. Output byte 1 (e.g., G) while preparing byte 2 (e.g., B) |
| 676 | // 3. Output byte 2 (e.g., B) while preparing byte 0 of next pixel (e.g., next R) |
| 677 | static void /*__attribute__((optimize("O0")))*/ /*__attribute__ ((always_inline))*/ showRGBInternal(PixelController<RGB_ORDER> & pixels) { |
| 678 | u8 *data = (u8*)pixels.mData; // Pointer to pixel array |
| 679 | data_ptr_t port = FastPin<DATA_PIN>::port(); // GPIO port register address |
| 680 | data_t mask = FastPin<DATA_PIN>::mask(); // Pin bit mask |
| 681 | u8 scale_base = 0; // Scratch variable for scaling operations |
| 682 | |
| 683 | // FASTLED_REGISTER uint8_t *end = data + nLeds; |
| 684 | |
| 685 | // Compute port values for HIGH and LOW states |
| 686 | data_t hi = *port | mask; // Set data pin bit to 1 (HIGH), preserve other pins |
| 687 | data_t lo = *port & ~mask; // Clear data pin bit to 0 (LOW), preserve other pins |
| 688 | *port = lo; // Initialize line to LOW state |
| 689 | |
| 690 | // DOUBLE-BUFFER BYTES: b0=currently outputting, b1=next being prepared |
| 691 | u8 b0 = 0; // The byte currently being written out bit-by-bit |
| 692 | u8 b1 = 0; // The byte currently being loaded, dithered, and scaled for next output |
| 693 | |
| 694 | // Setup the pixel controller |
| 695 | pixels.preStepFirstByteDithering(); |
| 696 | |
| 697 | // ===== EXTRACT VARIABLES FOR ASM ACCESS ===== |
| 698 | // Pull all needed values into local variables so inline asm can reference them via ASM_VARS |
| 699 | // These are mapped to AVR registers through the ASM_VARS constraint list |
| 700 | |
| 701 | // advanceBy: Stride to next pixel (typically 3 for RGB, 4 for RGBW, negative for reverse order) |
| 702 | // Cast to int16 for sign extension (handles negative strides correctly) |
| 703 | i16 advanceBy = pixels.advanceBy(); |
| 704 | u16 count = pixels.mLen; // Number of pixels to output |
| 705 | |
| 706 | // s0, s1, s2: Scale factors for each color channel (brightness/color correction) |
| 707 | // Range: 0-255 (0=off, 255=full brightness) |
| 708 | // RO() handles RGB_ORDER remapping (e.g., RGB vs GRB vs BGR) |
| 709 | u8 s0 = pixels.mColorAdjustment.premixed.raw[RO(0)]; |
| 710 | u8 s1 = pixels.mColorAdjustment.premixed.raw[RO(1)]; |
| 711 | u8 s2 = pixels.mColorAdjustment.premixed.raw[RO(2)]; |
| 712 | #if (FASTLED_SCALE8_FIXED==1) |
| 713 | // Scale8 fix: increment scale factors so 255 wraps to 0 |
| 714 | // Assembly code detects s==0 as special case to return unscaled value |
| 715 | // This makes scale8(x, 255) return x instead of (x*255)/256 |
| 716 | s0++; s1++; s2++; |
| 717 | #endif |
| 718 | |
| 719 | // d0, d1, d2: Current dither values for each channel |
| 720 | // Dithering reduces color banding by adding controlled temporal noise |
| 721 | u8 d0 = pixels.d[RO(0)]; |
| 722 | u8 d1 = pixels.d[RO(1)]; |
| 723 | u8 d2 = pixels.d[RO(2)]; |
| 724 | |
| 725 | // e0, e1, e2: Dither step values (d is updated by: d = e - d after each pixel) |
| 726 | u8 e0 = pixels.e[RO(0)]; |
| 727 | u8 e1 = pixels.e[RO(1)]; |
| 728 | u8 e2 = pixels.e[RO(2)]; |
| 729 | |
| 730 | u8 loopvar=0; // Scratch variable for delay loops |
| 731 | |
| 732 | // ===== FIRST BYTE INITIALIZATION ===== |
| 733 | // Load and scale the very first byte (byte 0 of first pixel) before entering main loop |
| 734 | // This has to be done in asm to keep gcc from messing up the asm code further down |
no test coverage detected