From 47ae15c4ecd1419a17445130da0fad427960c683 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 16 Jul 2024 16:55:40 -0700 Subject: [PATCH 1/6] Reduce complexity, no functional changes this commit. No need for handling both RGB and GRB (or other pixel-specific color ordering). Updates are all internal to `rgb.c`. * Rename `CPIXEL_COLOR_RGB` --> `CPIXEL_COLOR` * Delete `CPIXEL_COLOR_GRB` * Remove corresponding C11 _Generic() macros * Remove `RGB_MAX_BRIGHT` --- pirate/rgb.c | 214 ++++++++++++++++----------------------------------- 1 file changed, 66 insertions(+), 148 deletions(-) diff --git a/pirate/rgb.c b/pirate/rgb.c index e69e1158..24b421bf 100644 --- a/pirate/rgb.c +++ b/pirate/rgb.c @@ -1,4 +1,5 @@ #include +#include #include "pico/stdlib.h" #include "pirate.h" #include "hardware/pio.h" @@ -7,35 +8,24 @@ #include "system_config.h" #include "pirate/rgb.h" -#define RGB_MAX_BRIGHT 32 +// Total count of RGB pixels +#define COUNT_OF_PIXELS RGB_LEN // Externally to this file, colors are always handled as uint32_t values // which stores the color in 0x00RRGGBB format. -// Internally to this file, the color is stored in the CPIXEL_COLOR_xxx format. -// This reduces type-confusion that results in incorrect colors. +// Internally to this file, the color is stored in the CPIXEL_COLOR format. +// The only location where the order of bytes changes is when actually +// pushing the bytes to PIO ... to match the color format expected by the +// particular type of pixels used: See update_pixels(). -typedef struct _CPIXEL_COLOR_RGB { - // pad to 32-bits b/c pio_sm_put_blocking() expects a uint32_t - // This also ensures the compiler can optimize away conversion +typedef struct _CPIXEL_COLOR { + // pad to 32-bits to ensure compiler can optimize away conversion // from constexpr uint32_t colors (0x00RRGGBB format) into this - // type into a noop (same underlying structure) - uint8_t _unused; - union { - uint8_t red; - uint8_t r; - }; - union { - uint8_t green; - uint8_t g; - }; + // type into a noop (same underlying data in little-endian) union { uint8_t blue; uint8_t b; }; -} CPIXEL_COLOR_RGB; - -typedef struct _CPIXEL_COLOR_GRB { - uint8_t _unused; union { uint8_t green; uint8_t g; @@ -44,75 +34,30 @@ typedef struct _CPIXEL_COLOR_GRB { uint8_t red; uint8_t r; }; - union { - uint8_t blue; - uint8_t b; - }; -} CPIXEL_COLOR_GRB; + uint8_t _unused; +} CPIXEL_COLOR; // C23 would allow this to be `constexpr` // here, `static const` relies on compiler to optimize this away -static const CPIXEL_COLOR_RGB RGBCOLOR_BLACK = { .r=0x00, .g=0x00, .b=0x00 }; +static const CPIXEL_COLOR PIXEL_COLOR_BLACK = { .r=0x00, .g=0x00, .b=0x00 }; -static CPIXEL_COLOR_RGB color_rgb(uint8_t r, uint8_t g, uint8_t b) { - CPIXEL_COLOR_RGB c = { .r = r, .g = g, .b = b }; - return c; -} -static CPIXEL_COLOR_GRB color_grb(uint8_t g, uint8_t r, uint8_t b) { - CPIXEL_COLOR_GRB c = { .g = g, .r = r, .b = b }; +static CPIXEL_COLOR color_from_rgb(uint8_t r, uint8_t g, uint8_t b) { + CPIXEL_COLOR c = { .r = r, .g = g, .b = b }; return c; } -static CPIXEL_COLOR_RGB rgb_from_rgb(CPIXEL_COLOR_RGB c) { return c; } -static CPIXEL_COLOR_GRB grb_from_grb(CPIXEL_COLOR_GRB c) { return c; } -static CPIXEL_COLOR_RGB rgb_from_grb(CPIXEL_COLOR_GRB c) { - CPIXEL_COLOR_RGB r = { .r = c.r, .g = c.g, .b = c.b }; - return r; -} -static CPIXEL_COLOR_GRB grb_from_rgb(CPIXEL_COLOR_RGB c) { - CPIXEL_COLOR_GRB r = { .g = c.g, .r = c.r, .b = c.b }; - return r; -} -static CPIXEL_COLOR_RGB rgb_from_uint32(uint32_t c) { - CPIXEL_COLOR_RGB r = { +static CPIXEL_COLOR color_from_uint32(uint32_t c) { + CPIXEL_COLOR result = { ._unused = (c >> 24) & 0xff, // carry the input data, to allow compiler to optimization this to a noop .r = (c >> 16) & 0xff, .g = (c >> 8) & 0xff, .b = (c >> 0) & 0xff, }; - return r; -} -static CPIXEL_COLOR_GRB grb_from_uint32(uint32_t c) { - CPIXEL_COLOR_GRB r = { .g = (c >> 8) & 0xff, .r = (c >> 16) & 0xff, .b = c & 0xff }; - return r; -} -static uint32_t rgb_as_uint32(CPIXEL_COLOR_RGB c) { - return (c.r << 16) | (c.g << 8) | c.b; + return result; } -static uint32_t grb_as_uint32(CPIXEL_COLOR_GRB c) { +static uint32_t color_as_uint32(CPIXEL_COLOR c) { return (c.r << 16) | (c.g << 8) | c.b; } -// Use C11 `_Generic` to auto-select the correct conversion function -// based on the color type ... simplifies writing correct code. -#define rgb_from_color(COLOR) \ - _Generic((COLOR), \ - CPIXEL_COLOR_RGB: rgb_from_rgb, \ - CPIXEL_COLOR_GRB: rgb_from_grb, \ - )(COLOR) -#define grb_from_color(COLOR) \ - _Generic((COLOR), \ - CPIXEL_COLOR_RGB: grb_from_rgb, \ - CPIXEL_COLOR_GRB: grb_from_grb, \ - )(COLOR) -#define uint32_from_color(COLOR) \ - _Generic((COLOR), \ - CPIXEL_COLOR_RGB: rgb_as_uint32, \ - CPIXEL_COLOR_GRB: grb_as_uint32, \ - )(COLOR) - - - - // Note that both the layout and overall count of pixels // has changed between revisions. As a result, the count @@ -135,8 +80,6 @@ static uint32_t grb_as_uint32(CPIXEL_COLOR_GRB c) { // Also add a new constant, COUNT_OF_PIXELS, to define the number // pixels on each revision of board. -// Total count of RGB pixels -#define COUNT_OF_PIXELS RGB_LEN #if BP5_REV <= 9 // Sadly, C still doesn't recognize the following format as constexpr enough for static_assert() //static const uint8_t COUNT_OF_PIXELS = 16u; @@ -252,7 +195,7 @@ static_assert(COUNT_OF_PIXELS < sizeof(uint32_t)*8, "Too many pixels for pixel m static_assert((PIXEL_MASK_UPPER & PIXEL_MASK_SIDE) == 0, "Pixel cannot be both upper and side"); static_assert((PIXEL_MASK_UPPER | PIXEL_MASK_SIDE) == PIXEL_MASK_ALL, "Pixel must be either upper or side"); -CPIXEL_COLOR_RGB pixels[COUNT_OF_PIXELS]; // store as RGB ... as it's the common format +CPIXEL_COLOR pixels[COUNT_OF_PIXELS]; // store as RGB ... as it's the common format static inline void update_pixels(void) { // TODO: define symbolic constant for which state machine (no magic numbers!) @@ -261,7 +204,7 @@ static inline void update_pixels(void) { // little-endian, so 0x00GGRRBB is stored as 0xBB 0xRR 0xGG 0x00 // Shifting it left by 8 bits will give bytes 0x00 0xBB 0xRR 0xGG // which allows the PIO to unshift the bytes in the correct order - CPIXEL_COLOR_RGB c = pixels[i]; + CPIXEL_COLOR c = pixels[i]; c.r = c.r / system_config.led_brightness_divisor; c.g = c.g / system_config.led_brightness_divisor; c.b = c.b / system_config.led_brightness_divisor; @@ -278,58 +221,32 @@ static inline void update_pixels(void) { * The colours are a transition r -> g -> b -> back to r * Inspired by the Adafruit examples. */ -CPIXEL_COLOR_RGB color_wheel(uint8_t pos) { - CPIXEL_COLOR_RGB result; +CPIXEL_COLOR color_wheel(uint8_t pos) { + uint8_t r, g, b; + pos = 255 - pos; if(pos < 85) { - result = color_rgb(255 - (pos*3), 0, (pos*3)); + r = 255u - (pos * 3u); + g = 0u; + b = pos * 3; } else if(pos < 170) { pos -= 85; - result = color_rgb(0, (pos*3), 255 - (pos*3)); + r = 0u; + g = pos * 3u; + b = 255u - (pos * 3u); } else { pos -= 170; - result = color_rgb((pos*3), 255 - (pos*3), 0); - } - return result; -} - -/* - * Put a value 0 to 255 in to get a color value. - * The colours are a transition r -> g -> b -> back to r - * Inspired by the Adafruit examples. - * The ..._div() version of the function divides each of - * the R/G/B values by the system config's led brightness. - */ -CPIXEL_COLOR_RGB color_wheel_div(uint8_t pos) { - pos = 255u - pos; - uint8_t r,g,b; - if(pos < 85u) { - r=((uint32_t)(255u - pos * 3u)); - g=((uint32_t)(0u) ); - b=(pos * 3u); - } else if(pos < 170u) { - pos -= 85u; - r=((uint32_t)(0u) ); - g=((uint32_t)(pos * 3u) ); - b=(255u - pos * 3u); - } else { - pos -= 170u; - r=((uint32_t)(pos * 3u)); - g=((uint32_t)(255u - pos * 3u) ); - b=(0u); + r = pos * 3u; + g = 255u - (pos * 3u); + b = 0u; } - CPIXEL_COLOR_RGB result = - color_rgb(r, g, b); - return result; + return color_from_rgb(r, g, b); } -// BUGBUG -- Many of the callers convert an RGB color to GRB before passing -// it to this function. This means the color parameter is GRB (not RGB)? -// TODO: define RGB and GRB structures, and use them for clarity rather than uint32_t -void assign_pixel_rgb_color(uint32_t index_mask, CPIXEL_COLOR_RGB rgb_color){ - for (int i = 0; i < RGB_LEN; i++){ +void assign_pixel_color(uint32_t index_mask, CPIXEL_COLOR pixel_color){ + for (int i = 0; i < COUNT_OF_PIXELS; i++){ if (index_mask & (1u << i)) { - pixels[i] = rgb_color; + pixels[i] = pixel_color; } } } @@ -358,7 +275,7 @@ void assign_pixel_rgb_color(uint32_t index_mask, CPIXEL_COLOR_RGB rgb_color){ bool rgb_master( const uint32_t *groups, uint8_t group_count, - CPIXEL_COLOR_RGB (*color_wheel)(uint8_t color), + CPIXEL_COLOR (*color_wheel)(uint8_t color), uint8_t color_count, uint8_t color_increment, uint8_t cycles, @@ -379,8 +296,8 @@ bool rgb_master( uint32_t tmp_color_idx = color_idx + (i*color_increment); tmp_color_idx %= color_count; // ensures safe to cast to uint8_t - CPIXEL_COLOR_RGB rgb_color = color_wheel((uint8_t)tmp_color_idx); - assign_pixel_rgb_color(groups[i], rgb_color); + CPIXEL_COLOR rgb_color = color_wheel((uint8_t)tmp_color_idx); + assign_pixel_color(groups[i], rgb_color); } update_pixels(); ++color_idx; @@ -404,10 +321,12 @@ bool rgb_gentle_glow(void) { static const uint16_t animation_cycles = 240 * 16 + 9 * 9; // approximately same time as scanner static uint16_t cycle_count = 0; - CPIXEL_COLOR_RGB top_color = { .r = 15, .g = 15, .b = 15 }; - CPIXEL_COLOR_RGB side_color = { .r = 60, .g = 60, .b = 60 }; - assign_pixel_rgb_color(PIXEL_MASK_UPPER, top_color ); - assign_pixel_rgb_color(PIXEL_MASK_SIDE, side_color); + // clang-format off + CPIXEL_COLOR top_color = { .r = 15, .g = 15, .b = 15 }; + CPIXEL_COLOR side_color = { .r = 60, .g = 60, .b = 60 }; + assign_pixel_color(PIXEL_MASK_UPPER, top_color ); + assign_pixel_color(PIXEL_MASK_SIDE, side_color); + // clang-format on update_pixels(); ++cycle_count; @@ -419,7 +338,7 @@ bool rgb_gentle_glow(void) { } bool rgb_scanner(void) { - // TODO: decode this animation and add notes on what it's intended result is + static_assert(count_of(groups_center_left) < (sizeof(uint16_t)*8), "uint16_t too small to hold count_of(groups_center_left) elements"); // pixel_bitmask has a single bit set, which serves two purposes: @@ -431,10 +350,10 @@ bool rgb_scanner(void) { static uint8_t frame_delay_count = 0u; static uint8_t color_idx = 0; - static const CPIXEL_COLOR_RGB background_pixel_color = { .r = 0x20, .g = 0x20, .b = 0x20 }; + static const CPIXEL_COLOR background_pixel_color = { .r = 0x20, .g = 0x20, .b = 0x20 }; // each loop of the animation, use the next color in this sequence. // when all the colors have been used, the animation is complete. - const CPIXEL_COLOR_RGB colors[]={ + const CPIXEL_COLOR colors[]={ { .r = 0xFF, .g = 0x00, .b = 0x00 }, { .r = 0xD5, .g = 0x2A, .b = 0x00 }, { .r = 0xAB, .g = 0x55, .b = 0x00 }, @@ -464,12 +383,12 @@ bool rgb_scanner(void) { // generate the next frame of the animation for (int i = 0; i < count_of(groups_center_left); i++) { - CPIXEL_COLOR_RGB color = background_pixel_color; + CPIXEL_COLOR color = background_pixel_color; // does this pixel get the non-background color? if (pixel_bitmask & (1u << i)) { color = colors[color_idx]; } - assign_pixel_rgb_color(groups_center_left[i], color); + assign_pixel_color(groups_center_left[i], color); } update_pixels(); @@ -496,7 +415,7 @@ bool rgb_scanner(void) { return false; } -bool rgb_timer_callback(struct repeating_timer *t){ +bool pixel_timer_callback(struct repeating_timer *t){ static uint8_t mode=2; uint32_t color_grb; @@ -509,25 +428,25 @@ bool rgb_timer_callback(struct repeating_timer *t){ // clang-format off switch(mode) { case LED_EFFECT_DISABLED: - assign_pixel_rgb_color(PIXEL_MASK_ALL, RGBCOLOR_BLACK); + assign_pixel_color(PIXEL_MASK_ALL, PIXEL_COLOR_BLACK); update_pixels(); break; case LED_EFFECT_SOLID:; // semicolon is required for ... reasons - CPIXEL_COLOR_RGB color = rgb_from_uint32(system_config.led_color); - assign_pixel_rgb_color(PIXEL_MASK_ALL, color); + CPIXEL_COLOR color = color_from_uint32(system_config.led_color); + assign_pixel_color(PIXEL_MASK_ALL, color); update_pixels(); break; case LED_EFFECT_ANGLE_WIPE: - next = rgb_master(groups_top_left, count_of(groups_top_left), &color_wheel_div, 0xff, (0xff/count_of(groups_top_left) ), 5, 10); + next = rgb_master(groups_top_left, count_of(groups_top_left), &color_wheel, 0xff, (0xff/count_of(groups_top_left) ), 5, 10); break; case LED_EFFECT_CENTER_WIPE: - next = rgb_master(groups_center_left, count_of(groups_center_left), &color_wheel_div, 0xff, (0xff/count_of(groups_center_left) ), 5, 10); + next = rgb_master(groups_center_left, count_of(groups_center_left), &color_wheel, 0xff, (0xff/count_of(groups_center_left) ), 5, 10); break; case LED_EFFECT_CLOCKWISE_WIPE: - next = rgb_master(groups_center_clockwise, count_of(groups_center_clockwise), &color_wheel_div, 0xff, (0xff/count_of(groups_center_clockwise)), 5, 10); + next = rgb_master(groups_center_clockwise, count_of(groups_center_clockwise), &color_wheel, 0xff, (0xff/count_of(groups_center_clockwise)), 5, 10); break; case LED_EFFECT_TOP_SIDE_WIPE: - next = rgb_master(groups_top_down, count_of(groups_top_down), &color_wheel_div, 0xff, ( 30), 5, 10); + next = rgb_master(groups_top_down, count_of(groups_top_down), &color_wheel, 0xff, ( 30), 5, 10); break; case LED_EFFECT_SCANNER: next = rgb_scanner(); @@ -558,7 +477,7 @@ void rgb_irq_enable(bool enable){ static bool enabled=false; if(enable && !enabled) { - add_repeating_timer_ms(-10, rgb_timer_callback, NULL, &rgb_timer); + add_repeating_timer_ms(-10, pixel_timer_callback, NULL, &rgb_timer); enabled=true; } else if(!enable && enabled) @@ -580,7 +499,7 @@ void rgb_init(void) ws2812_program_init(pio, sm, offset, RGB_CDO, 800000, false); for (int i = 0; i < COUNT_OF_PIXELS; i++){ - pixels[i] = RGBCOLOR_BLACK; + pixels[i] = PIXEL_COLOR_BLACK; } // Create a repeating timer that calls repeating_timer_callback. @@ -593,8 +512,8 @@ void rgb_init(void) void rgb_set_all(uint8_t r, uint8_t g, uint8_t b){ - CPIXEL_COLOR_RGB color = { .r = r, .g = g, .b = b }; - assign_pixel_rgb_color(PIXEL_MASK_ALL, color); + CPIXEL_COLOR color = { .r = r, .g = g, .b = b }; + assign_pixel_color(PIXEL_MASK_ALL, color); update_pixels(); } @@ -604,9 +523,8 @@ void rgb_put(uint32_t color) { // first set each pixel to off for (int i = 0; i < COUNT_OF_PIXELS; i++){ - pixels[i] = RGBCOLOR_BLACK; + pixels[i] = PIXEL_COLOR_BLACK; } - CPIXEL_COLOR_RGB rgb = rgb_from_uint32(color); - pixels[DEMO_LED] = rgb; + pixels[DEMO_LED] = color_from_uint32(color); update_pixels(); -}; \ No newline at end of file +}; From a02e8aa879ba69e30bcbcce167e6503bd9d43334 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 16 Jul 2024 17:13:47 -0700 Subject: [PATCH 2/6] RGB: No functional changes. Add mappings for both Rev10 and Rev8 boards: * coordinates map the pixels into a 256x256 grid * angles map the pixels into an angle based on 1/256th-of-a-circle units --- pirate/rgb.c | 377 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 238 insertions(+), 139 deletions(-) diff --git a/pirate/rgb.c b/pirate/rgb.c index 24b421bf..eecd4ef7 100644 --- a/pirate/rgb.c +++ b/pirate/rgb.c @@ -8,8 +8,130 @@ #include "system_config.h" #include "pirate/rgb.h" -// Total count of RGB pixels -#define COUNT_OF_PIXELS RGB_LEN + +// REV10 REV8 +// +// 11 10 9 8 7 10 9 8 7 6 +// 12 +-------+ 6 11 +-------+ 5 +// 13 | | 5 12 | | 4 +// USB | OLED | [] USB | OLED | [] +// 14 | | 4 13 | | 3 +// 15 +-------+ 3 14 +-------+ 2 +// 16 17 0 1 2 15 x x 0 1 +// +#define COUNT_OF_PIXELS RGB_LEN // 18 for Rev10, 16 for Rev8 + +#pragma region // 8-bit scaled pixel coordinates and angle256 + /// @brief Scaled coordinates in range [0..255] + typedef struct _coordin8 { + uint8_t x; + uint8_t y; + } coordin8_t; + + + /// @brief Each pixel's coordinate in a 256x256 grid, as + /// extracted from the PCB layout and Pick'n'Place data. + /// @details The grid is oriented with the origin (0,0) at the + /// upper left, similar to a PC screen. Orient the PCB + /// so the USB port is on the left, and the + /// plank connector is on the right. + /// + /// y + /// x +---------------> +x + /// | 11 10 ... + /// | 12 + /// | ... + /// V + /// +y + /// + static const coordin8_t pixel_coordin8[] = { + // // SIDE POSITION FACING + #if BP5_REV >= 10 + { .x = 127, .y = 255, }, // bottom center out + #endif + { .x = 165, .y = 255, }, // bottom right side + { .x = 202, .y = 255, }, // bottom right out + { .x = 255, .y = 202, }, // right bottom out + { .x = 255, .y = 171, }, // right bottom side (by plank connector) + { .x = 255, .y = 84, }, // right top side (by plank connector) + { .x = 255, .y = 52, }, // right top out + { .x = 202, .y = 0, }, // top right out + { .x = 165, .y = 0, }, // top right side + { .x = 127, .y = 0, }, // top center out + { .x = 90, .y = 0, }, // top left side + { .x = 52, .y = 0, }, // top left out + { .x = 0, .y = 52, }, // left top out + { .x = 0, .y = 84, }, // left top side (by USB port) + { .x = 0, .y = 171, }, // left bottom side (by USB port) + { .x = 0, .y = 202, }, // left bottom out + { .x = 52, .y = 255, }, // bottom left out + #if BP5_REV >= 10 + { .x = 90, .y = 255, }, // bottom left side + #endif + }; + + /// @brief Angular position in 1/256th-circle units, as + /// extracted from the PCB layout and Pick'n'Place data. + /// From the center of the PCB, the zero angle is + /// directly towards the center of the plank connector, + /// with angles increasing in the anti-clockwise direction. + static const uint8_t pixel_angle256[] = { + #if BP5_REV >= 10 + 192, + #endif + 204, + 214, + 234, + 243, + 13, + 22, + 42, + 52, + 64, + 76, + 86, + 106, + 115, + 141, + 150, + 170, + #if BP5_REV >= 10 + 180, + #endif + }; + + static_assert(count_of(pixel_coordin8) == COUNT_OF_PIXELS); + static_assert(count_of(pixel_angle256) == COUNT_OF_PIXELS); + + // Sadly, C still refuses to allow the following format in static_assert(), saying it's not constant. + //static const uint32_t PIXEL_MASK_UPPER = 0b0....1; + //static const uint32_t PIXEL_MASK_SIDE = 0b1....0; + + // clang-format off + #if BP5_REV <= 9 + // Pixels that shine orthogonal to OLED: idx 1,2, 5,6, 8, 10,11, 14,15, + #define PIXEL_MASK_UPPER ( 0b1100110101100110 ) + // Pixels that shine orthogonal to OLED: idx 0, 3,4, 7, 9, 12,13, + #define PIXEL_MASK_SIDE ( 0b0011001010011001 ) + #else + // Pixels that shine in direction of OLED: idx 0, 2,3, 6,7, 9, 11,12, 15,16 + #define PIXEL_MASK_UPPER (0b011001101011001101) + // Pixels that shine orthogonal to OLED: idx 1, 4,5, 8, 10, 13,14, 17 + #define PIXEL_MASK_SIDE (0b100110010100110010) + #endif + // clang-format on + + static const uint32_t groups_top_down[] = { + PIXEL_MASK_UPPER, + PIXEL_MASK_SIDE, + }; // MSb is last led in string... + #define PIXEL_MASK_ALL ((1u << COUNT_OF_PIXELS) - 1) + static_assert(COUNT_OF_PIXELS < (sizeof(uint32_t)*8)-1, "Too many pixels for pixel mask definition to be valid"); + static_assert((PIXEL_MASK_UPPER & PIXEL_MASK_SIDE) == 0, "Pixel cannot be both upper and side"); + static_assert((PIXEL_MASK_UPPER | PIXEL_MASK_SIDE) == PIXEL_MASK_ALL, "Pixel must be either upper or side"); + + +#pragma endregion // Externally to this file, colors are always handled as uint32_t values // which stores the color in 0x00RRGGBB format. @@ -37,8 +159,10 @@ typedef struct _CPIXEL_COLOR { uint8_t _unused; } CPIXEL_COLOR; +CPIXEL_COLOR pixels[COUNT_OF_PIXELS]; // store as RGB ... as it's the common format + // C23 would allow this to be `constexpr` -// here, `static const` relies on compiler to optimize this away +// here, `static const` relies on compiler to optimize this away to a literal `((uint32_t)0u)` static const CPIXEL_COLOR PIXEL_COLOR_BLACK = { .r=0x00, .g=0x00, .b=0x00 }; static CPIXEL_COLOR color_from_rgb(uint8_t r, uint8_t g, uint8_t b) { @@ -59,143 +183,118 @@ static uint32_t color_as_uint32(CPIXEL_COLOR c) { } -// Note that both the layout and overall count of pixels -// has changed between revisions. As a result, the count -// of elements for any of these arrays may differ. -// -// groups_top_left[]: -// defines led_bitmasks that generally start at the top left corner of the device, -// and continue in a diagnol pattern. Generally setup in pairs, although some groups -// may set three pixels at a time. -// -// groups_center_left[]: -// tbd -// -// groups_center_clockwise[]: -// tbd -// -// groups_top_down[]: -// tbd -// -// Also add a new constant, COUNT_OF_PIXELS, to define the number -// pixels on each revision of board. - -#if BP5_REV <= 9 - // Sadly, C still doesn't recognize the following format as constexpr enough for static_assert() - //static const uint8_t COUNT_OF_PIXELS = 16u; - //static const uint32_t PIXEL_MASK_UPPER = 0b011001101011001101; - //static const uint32_t PIXEL_MASK_SIDE = 0b100110010100110010; - - static_assert(COUNT_OF_PIXELS < sizeof(uint32_t)*8, "Too many pixels for pixel mask definition to be valid"); - // Pixels that shine in direction of OLED: idx 0, 3,4, 7, 9, 12,13, - #define PIXEL_MASK_UPPER (0b0011001010011001) - // Pixels that shine orthogonal to OLED: idx 1,2, 5,6, 8, 10,11, 14,15, - #define PIXEL_MASK_SIDE (0b1100110101100110) - - static const uint32_t groups_top_left[] = { - // clang-format off - ((1u << 1) | (1u << 2) ), - ((1u << 0) | (1u << 3) ), - ((1u << 15) | (1u << 4) | (1u << 5)), // pair up 4/5 - ((1u << 14) | (1u << 6) | (1u << 7)), // pair up 6/7 - ((1u << 13) | (1u << 8) ), - ((1u << 12) | (1u << 9) ), - ((1u << 11) | (1u << 10) ), - // clang-format on - }; - static const uint32_t groups_center_left[] = { - // clang-format off - ((1u << 3) | (1u << 4) ), - ((1u << 2) | (1u << 5) ), - ((1u << 1) | (1u << 6) ), - ((1u << 0) | (1u << 7) | (1u << 8) | (1 << 9)), // pair 7, 8, and 9? - ((1u << 10) | (1u << 15) ), - ((1u << 11) | (1u << 14) ), - ((1u << 12) | (1u << 13) ), - // clang-format on - }; - static const uint32_t groups_center_clockwise[] = { - // Generally, pixel 15 and 10 are single, others are just pairs of top/bottom pixels - // Here's upper/side masks split into the groupings: - // PIXEL_MASK_UPPER (0b 0 01 10 0 10 10 01 10 01) - // PIXEL_MASK_SIDE (0b 1 10 01 1 01 01 10 01 10) - // clang-format off - ((1u << 13) | (1u << 14)), - ((1u << 15) ), - ((1u << 0) | (1u << 1)), - ((1u << 2) | (1u << 3)), - ((1u << 4) | (1u << 5)), - ((1u << 6) | (1u << 7)), - ((1u << 8) | (1u << 9)), - ((1u << 10) ), - ((1u << 11) | (1u << 12)), - // clang-format on - }; -#elif BP5_REV >= 10 - // Sadly, C still doesn't recognize the following format as constexpr enough for static_assert() - // and thus we must resort to preprocessor macros (still). - //static const uint32_t PIXEL_MASK_UPPER = 0b011001101011001101; - //static const uint32_t PIXEL_MASK_SIDE = 0b100110010100110010; - - // Pixels that shine in direction of OLED: idx 0, 2,3, 6,7, 9, 11,12, 15,16 - #define PIXEL_MASK_UPPER (0b011001101011001101) - // Pixels that shine orthogonal to OLED: idx 1, 4,5, 8, 10, 13,14, 17 - #define PIXEL_MASK_SIDE (0b100110010100110010) - - - static const uint32_t groups_top_left[] = { - // clang-format off - ( (1u << 2) | (1u << 3)), - ( (1u << 1) | (1u << 4)), - ((1u << 17) | (1u << 0) | (1u << 5)), - ((1u << 16) | (1u << 6)), - ((1u << 15) | (1u << 7)), - ((1u << 14) | (1u << 9) | (1u << 8)), - ((1u << 13) | (1u << 10) ), - ((1u << 12) | (1u << 11) ), - // clang-format on - }; - static const uint32_t groups_center_left[] = { - // clang-format off - ((1u << 4) | (1u << 5)), - ((1u << 3) | (1u << 6)), - ((1u << 2) | (1u << 7)), - ((1u << 1) | (1u << 8)), - ((1u << 0) | (1u << 9)), - ((1u << 17) | (1u << 10)), - ((1u << 16) | (1u << 11)), - ((1u << 15) | (1u << 12)), - ((1u << 14) | (1u << 13)), - // clang-format on - }; - static const uint32_t groups_center_clockwise[] = { - // clang-format off - ((1u << 14) | (1u << 15)), - ((1u << 16) ), - ((1u << 17) | (1u << 0)), - ((1u << 1) | (1u << 2)), - ((1u << 3) | (1u << 4)), - ((1u << 5) | (1u << 6)), - ((1u << 7) ), - ((1u << 8) | (1u << 9)), - ((1u << 10) | (1u << 11)), - ((1u << 12) | (1u << 13)), - // clang-format on - }; -#endif - -static const uint32_t groups_top_down[] = { - PIXEL_MASK_UPPER, - PIXEL_MASK_SIDE, -}; // MSb is last led in string... - -#define PIXEL_MASK_ALL ((1u << COUNT_OF_PIXELS) - 1) - -static_assert(COUNT_OF_PIXELS < sizeof(uint32_t)*8, "Too many pixels for pixel mask definition to be valid"); -static_assert((PIXEL_MASK_UPPER & PIXEL_MASK_SIDE) == 0, "Pixel cannot be both upper and side"); -static_assert((PIXEL_MASK_UPPER | PIXEL_MASK_SIDE) == PIXEL_MASK_ALL, "Pixel must be either upper or side"); +#pragma region // Legacy pixel animation groups + + // Note that both the layout and overall count of pixels + // has changed between revisions. As a result, the count + // of elements for any of these arrays may differ. + // + // groups_top_left[]: + // generally start at the top left corner of the device, + // and continue in a diagnol wipe to bottom right. + // + // groups_center_left[]: + // generally start at left center two pixels (by USB port), + // each of which flows towards the plank connector (in opposite directions) + // + // groups_center_clockwise[]: + // Similar to a clock, rotating around the device clockwise. + // + // groups_top_down[]: + // All the pixels facing upwards as one group, and all the pixels + // facing the sides as a second group. + + #if BP5_REV <= 9 + static const uint32_t groups_top_left[] = { + // clang-format off + ((1u << 1) | (1u << 2) ), + ((1u << 0) | (1u << 3) ), + ((1u << 15) | (1u << 4) | (1u << 5)), // pair up 4/5 + ((1u << 14) | (1u << 6) | (1u << 7)), // pair up 6/7 + ((1u << 13) | (1u << 8) ), + ((1u << 12) | (1u << 9) ), + ((1u << 11) | (1u << 10) ), + // clang-format on + }; + static const uint32_t groups_center_left[] = { + // clang-format off + ((1u << 3) | (1u << 4) ), + ((1u << 2) | (1u << 5) ), + ((1u << 1) | (1u << 6) ), + ((1u << 0) | (1u << 7) | (1u << 8) | (1 << 9)), + ((1u << 10) | (1u << 15) ), + ((1u << 11) | (1u << 14) ), + ((1u << 12) | (1u << 13) ), + // clang-format on + }; + static const uint32_t groups_center_clockwise[] = { + // clang-format off + ((1u << 13) | (1u << 14)), + ((1u << 15) ), + ((1u << 0) | (1u << 1)), + ((1u << 2) | (1u << 3)), + ((1u << 4) | (1u << 5)), + ((1u << 6) | (1u << 7)), + ((1u << 8) | (1u << 9)), + ((1u << 10) ), + ((1u << 11) | (1u << 12)), + // clang-format on + }; + #elif BP5_REV >= 10 + static const uint32_t groups_top_left[] = { + // TODO: use grid mappings instead + // e.g., for iteration target from 255..0 + // (x+y)/2 == iteration + // clang-format off + ( (1u << 2) | (1u << 3)), + ( (1u << 1) | (1u << 4)), + ((1u << 17) | (1u << 0) | (1u << 5)), + ((1u << 16) | (1u << 6)), + ((1u << 15) | (1u << 7)), + ((1u << 14) | (1u << 9) | (1u << 8)), + ((1u << 13) | (1u << 10) ), + ((1u << 12) | (1u << 11) ), + // clang-format on + }; + static const uint32_t groups_center_left[] = { + // TODO: use angular mappings instead + // e.g., for iteration target from 255..0 + // // convert to angular range: [0..127] based on absolute offset from angle 0 + // uint8_t pix_a = (a256 > 127u) ? 256u - a256 : a256; + // // scale it up to range [0..255] + // pix_a *= 2u; + // clang-format off + ((1u << 4) | (1u << 5)), + ((1u << 3) | (1u << 6)), + ((1u << 2) | (1u << 7)), + ((1u << 1) | (1u << 8)), + ((1u << 0) | (1u << 9)), + ((1u << 17) | (1u << 10)), + ((1u << 16) | (1u << 11)), + ((1u << 15) | (1u << 12)), + ((1u << 14) | (1u << 13)), + // clang-format on + }; + static const uint32_t groups_center_clockwise[] = { + // TODO: use angular mappings instead + // e.g., for iteration target from 255..0 + // uint8_t pix_a = 256u - a256; + // clang-format off + ((1u << 14) | (1u << 15)), + ((1u << 16) ), + ((1u << 17) | (1u << 0)), + ((1u << 1) | (1u << 2)), + ((1u << 3) | (1u << 4)), + ((1u << 5) | (1u << 6)), + ((1u << 7) ), + ((1u << 8) | (1u << 9)), + ((1u << 10) | (1u << 11)), + ((1u << 12) | (1u << 13)), + // clang-format on + }; + #endif +#pragma endregion // Legacy pixel animation groups -CPIXEL_COLOR pixels[COUNT_OF_PIXELS]; // store as RGB ... as it's the common format static inline void update_pixels(void) { // TODO: define symbolic constant for which state machine (no magic numbers!) From a5dbe316f6618129d6c556017f36a2d20ff41e63 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 16 Jul 2024 17:16:24 -0700 Subject: [PATCH 3/6] RGB: Comment only update --- pirate/rgb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pirate/rgb.c b/pirate/rgb.c index eecd4ef7..b24cb609 100644 --- a/pirate/rgb.c +++ b/pirate/rgb.c @@ -297,7 +297,6 @@ static uint32_t color_as_uint32(CPIXEL_COLOR c) { static inline void update_pixels(void) { - // TODO: define symbolic constant for which state machine (no magic numbers!) for (int i = 0; i < COUNT_OF_PIXELS; i++) { // little-endian, so 0x00GGRRBB is stored as 0xBB 0xRR 0xGG 0x00 @@ -311,6 +310,10 @@ static inline void update_pixels(void) { (c.g << 24) | (c.r << 16) | (c.b << 8) ; + + // TODO: define symbolic constant for which PIO / state machine (no magic numbers!) + // e.g., #define WS2812_PIO pio1 + // e.g., #define WS2812_SM 3 pio_sm_put_blocking(pio1, 3, toSend); } } From 1cf27eb7bd31d4ed3788e2e2c148271209570e82 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 16 Jul 2024 17:38:11 -0700 Subject: [PATCH 4/6] RGB: Increase use of `static` to all functions except exported API --- pirate/rgb.c | 19 +++++++++++++------ pirate/rgb.h | 1 - 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pirate/rgb.c b/pirate/rgb.c index b24cb609..92345ba5 100644 --- a/pirate/rgb.c +++ b/pirate/rgb.c @@ -183,6 +183,8 @@ static uint32_t color_as_uint32(CPIXEL_COLOR c) { } + + #pragma region // Legacy pixel animation groups // Note that both the layout and overall count of pixels @@ -323,7 +325,7 @@ static inline void update_pixels(void) { * The colours are a transition r -> g -> b -> back to r * Inspired by the Adafruit examples. */ -CPIXEL_COLOR color_wheel(uint8_t pos) { +static CPIXEL_COLOR color_wheel(uint8_t pos) { uint8_t r, g, b; pos = 255 - pos; @@ -345,7 +347,7 @@ CPIXEL_COLOR color_wheel(uint8_t pos) { return color_from_rgb(r, g, b); } -void assign_pixel_color(uint32_t index_mask, CPIXEL_COLOR pixel_color){ +static void assign_pixel_color(uint32_t index_mask, CPIXEL_COLOR pixel_color){ for (int i = 0; i < COUNT_OF_PIXELS; i++){ if (index_mask & (1u << i)) { pixels[i] = pixel_color; @@ -374,7 +376,7 @@ void assign_pixel_color(uint32_t index_mask, CPIXEL_COLOR pixel_color){ // HACKHACK -- to ensure a known starting state (reset the static variables) // can call with group_count=0, cycles=0 // TODO: Rename `color_wheel` to more appropriate, generic name -bool rgb_master( +static bool rgb_master( const uint32_t *groups, uint8_t group_count, CPIXEL_COLOR (*color_wheel)(uint8_t color), @@ -419,7 +421,7 @@ bool rgb_master( struct repeating_timer rgb_timer; -bool rgb_gentle_glow(void) { +static bool rgb_gentle_glow(void) { static const uint16_t animation_cycles = 240 * 16 + 9 * 9; // approximately same time as scanner static uint16_t cycle_count = 0; @@ -439,7 +441,7 @@ bool rgb_gentle_glow(void) { return false; } -bool rgb_scanner(void) { +static bool rgb_scanner(void) { static_assert(count_of(groups_center_left) < (sizeof(uint16_t)*8), "uint16_t too small to hold count_of(groups_center_left) elements"); @@ -517,7 +519,7 @@ bool rgb_scanner(void) { return false; } -bool pixel_timer_callback(struct repeating_timer *t){ +static bool pixel_timer_callback(struct repeating_timer *t){ static uint8_t mode=2; uint32_t color_grb; @@ -575,6 +577,11 @@ bool pixel_timer_callback(struct repeating_timer *t){ } + +// ================================================================================ +// Exported functions (Pixel API) follows: + + void rgb_irq_enable(bool enable){ static bool enabled=false; if(enable && !enabled) diff --git a/pirate/rgb.h b/pirate/rgb.h index 99c9ae84..fccb4675 100644 --- a/pirate/rgb.h +++ b/pirate/rgb.h @@ -21,7 +21,6 @@ static_assert(MAX_LED_EFFECT-1 == LED_EFFECT_PARTY_MODE, "LED_EFFECT_PARTY_MODE // TODO: review and provide a more useful client-focused API. // TODO: adjust to use RGB color type instead of uint32_t. void rgb_init(void); -uint32_t rgb_update(uint32_t mode); void rgb_put(uint32_t color); void rgb_irq_enable(bool enable); void rgb_set_all(uint8_t r, uint8_t g, uint8_t b); From 0b34a437d5ba27da14b8eddfffb4f0399a93df7d Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 16 Jul 2024 18:25:28 -0700 Subject: [PATCH 5/6] RGB: Add two new animation examples Not enabled yet, but commented-out as replacements for scanner effect --- pirate/rgb.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 3 deletions(-) diff --git a/pirate/rgb.c b/pirate/rgb.c index 92345ba5..5a423b2d 100644 --- a/pirate/rgb.c +++ b/pirate/rgb.c @@ -181,7 +181,14 @@ static CPIXEL_COLOR color_from_uint32(uint32_t c) { static uint32_t color_as_uint32(CPIXEL_COLOR c) { return (c.r << 16) | (c.g << 8) | c.b; } - +static CPIXEL_COLOR reduce_brightness(CPIXEL_COLOR c, uint8_t numerator, uint8_t divisor) { + CPIXEL_COLOR r = { + .r = c.r * numerator / divisor, + .g = c.g * numerator / divisor, + .b = c.b * numerator / divisor, + }; + return r; +} @@ -421,7 +428,7 @@ static bool rgb_master( struct repeating_timer rgb_timer; -static bool rgb_gentle_glow(void) { +static bool animation_gentle_glow(void) { static const uint16_t animation_cycles = 240 * 16 + 9 * 9; // approximately same time as scanner static uint16_t cycle_count = 0; @@ -441,6 +448,132 @@ static bool rgb_gentle_glow(void) { return false; } +/// @brief angular wipe from upper left to bottom right, with diffusion / blur +/// @param color The color to use for the wipe, or RGBCOLOR_BLACK to use angle-based rainbow +/// @return true when full animation has run its course +static bool animation_angular_wipe(CPIXEL_COLOR color) { + static const uint16_t value_diffusion = 40u; + static const uint16_t starting_value = 0u; + static const uint16_t ending_value = value_diffusion*4u + 256u; + static const uint16_t default_frame_delay = 1u; + + static uint8_t frame_delay_count = 0; + static uint16_t current_value = starting_value; + + // delay with each frame showing for multiple callbacks + if (frame_delay_count != 0) { + --frame_delay_count; + return false; + } + + // ending condition + if (current_value == ending_value) { + frame_delay_count = default_frame_delay * 4; + current_value = starting_value; + return true; + } + + // what color to use for this value? + if (color.r == 0 && color.g == 0 && color.b == 0) { + color = color_wheel(current_value); + } + + // always loop through each pixel + for (int i = 0; i < count_of(pixel_coordin8); ++i) { + // does it get color for this value? + + // TODO: allow arbitrary angle for the wipe. + // For now, hard-coded to (X+Y)/2 for wipe + // from upper left to lower right corner ... + uint16_t pix_v = (((uint16_t)pixel_coordin8[i].x) + ((uint16_t)pixel_coordin8[i].y)) / 2u; + + // shift from [0..255] to [2*value_diffusion .. 255 + 4*value_diffusion] + // this allows smooth entry and exit diffusion effects + pix_v += 2u * value_diffusion; + + // diff is not a true cartesion distance, + // but good enough for current purposes. + uint16_t diff = abs(current_value - pix_v); + + // until migrate the FastLED color mixing, + // background must be black for blur / diffusion effects + if (diff < value_diffusion) { + static_assert(value_diffusion < 256u, "invalid brightness calculations when value_diffusion is too large (limited to uint8_t)"); + CPIXEL_COLOR dimmed_color = reduce_brightness(color, value_diffusion - diff, value_diffusion); + assign_pixel_color(1u << i, dimmed_color); + } else { + assign_pixel_color(1u << i, PIXEL_COLOR_BLACK); + } + } + update_pixels(); + + // same delay for all frames + frame_delay_count = default_frame_delay; + current_value++; // mod(256) is implicit + return false; +} + +/// @brief starting at noon/up, color pixels in an anti-clockwise direction +/// @param color color to use for the pixels, or RGBCOLOR_BLACK to use angle-based rainbow +/// @return true when rull animation has run its course +static bool animation_anticlockwise_scan(CPIXEL_COLOR color) { + static const uint8_t starting_angle256 = 64u; + static const uint8_t angle_diffusion = 20u; + static const uint8_t default_frame_delay = 3u; + + static bool first_time_through = true; + static uint8_t frame_delay_count = 0; + static uint8_t current_angle256 = starting_angle256; + + // delay with each frame showing for multiple callbacks + if (frame_delay_count != 0) { + --frame_delay_count; + return false; + } + + // ending condition + if (current_angle256 == starting_angle256) { + if (first_time_through) { + first_time_through = false; + } else { + first_time_through = true; + return true; + } + } + + // what color to use for this angle? + if (color.r == 0 && color.g == 0 && color.b == 0) { + color = color_wheel(current_angle256); + } + + // always loop through each pixel + for (int i = 0; i < count_of(pixel_angle256); ++i) { + // does it get color at this angle? + uint8_t pixel_a = pixel_angle256[i]; + uint8_t angle_diff = abs(current_angle256 - pixel_a); + if (angle_diff < angle_diffusion) { + CPIXEL_COLOR reduced = reduce_brightness(color, angle_diffusion - angle_diff, angle_diffusion); + assign_pixel_color(1u << i, reduced); + } else { + assign_pixel_color(1u << i, PIXEL_COLOR_BLACK); + } + } + update_pixels(); + + // same delay for all frames + frame_delay_count = default_frame_delay; + current_angle256++; // mod(256) is implicit + return false; +} + +/// @brief starting at two pixels near USB port, scan edges towards plank connector +/// @param color color to use for the pixels, or RGBCOLOR_BLACK to use angle-based rainbow +/// @return true when rull animation has run its course +static bool animation_scanner(CPIXEL_COLOR color) { + // TODO: implement this function + return true; +} + static bool rgb_scanner(void) { static_assert(count_of(groups_center_left) < (sizeof(uint16_t)*8), "uint16_t too small to hold count_of(groups_center_left) elements"); @@ -553,10 +686,13 @@ static bool pixel_timer_callback(struct repeating_timer *t){ next = rgb_master(groups_top_down, count_of(groups_top_down), &color_wheel, 0xff, ( 30), 5, 10); break; case LED_EFFECT_SCANNER: + //static const CPIXEL_COLOR scanner_color = { .r = 0x80, .g = 0x20, .b = 0x20 }; + //next = animation_angular_wipe(scanner_color); + //next = animation_anticlockwise_scan(scanner_color); next = rgb_scanner(); break; case LED_EFFECT_GENTLE_GLOW: - next = rgb_gentle_glow(); + next = animation_gentle_glow(); break; case LED_EFFECT_PARTY_MODE: assert(!"Party mode should never be value of the *local* variable!"); From 6a431a1da6386b20b94a25aa5da0a324d2f05238 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 17 Jul 2024 06:25:32 -0700 Subject: [PATCH 6/6] RGB: comments only Add same self-documenting comments to `pixel_angle256` as are provided for `pixel_coordin8`. --- pirate/rgb.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/pirate/rgb.c b/pirate/rgb.c index 5a423b2d..8f28d485 100644 --- a/pirate/rgb.c +++ b/pirate/rgb.c @@ -46,6 +46,7 @@ /// static const coordin8_t pixel_coordin8[] = { // // SIDE POSITION FACING + // clang-format off #if BP5_REV >= 10 { .x = 127, .y = 255, }, // bottom center out #endif @@ -68,6 +69,7 @@ #if BP5_REV >= 10 { .x = 90, .y = 255, }, // bottom left side #endif + // clang-format on }; /// @brief Angular position in 1/256th-circle units, as @@ -76,28 +78,31 @@ /// directly towards the center of the plank connector, /// with angles increasing in the anti-clockwise direction. static const uint8_t pixel_angle256[] = { + // // SIDE POSITION FACING + // clang-format off #if BP5_REV >= 10 - 192, + 192, // bottom center out #endif - 204, - 214, - 234, - 243, - 13, - 22, - 42, - 52, - 64, - 76, - 86, - 106, - 115, - 141, - 150, - 170, + 204, // bottom right side + 214, // bottom right out + 234, // right bottom out + 243, // right bottom side (by plank connector) + 13, // right top side (by plank connector) + 22, // right top out + 42, // top right out + 52, // top right side + 64, // top center out + 76, // top left side + 86, // top left out + 106, // left top out + 115, // left top side (by USB port) + 141, // left bottom side (by USB port) + 150, // left bottom out + 170, // bottom left out #if BP5_REV >= 10 - 180, + 180, // bottom left side #endif + // clang-format on }; static_assert(count_of(pixel_coordin8) == COUNT_OF_PIXELS);