From 6b817ad23e05a30c14b2de127d327375f787bcdd Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Wed, 6 Dec 2023 12:40:19 -0600 Subject: [PATCH] v1.6: Add support for 1000 LEDS, Add "dirty" support to remove update flicker. --- gpio/ws2812b_tester/README.md | 5 ++ gpio/ws2812b_tester/app.c | 20 ++++---- gpio/ws2812b_tester/application.fam | 2 +- gpio/ws2812b_tester/led_driver.c | 78 ++++++++--------------------- gpio/ws2812b_tester/led_driver.h | 5 +- 5 files changed, 40 insertions(+), 70 deletions(-) diff --git a/gpio/ws2812b_tester/README.md b/gpio/ws2812b_tester/README.md index 845888b..4368713 100644 --- a/gpio/ws2812b_tester/README.md +++ b/gpio/ws2812b_tester/README.md @@ -21,3 +21,8 @@ This application has three submenu items: # About The "About" menu item contains information about the application. + +# Updates +- Version 1.6 + - Added support for up to 1000 LEDs (max set in led_driver.h) + - Added "dirty flag" to get rid of flicker when not updating the LEDs diff --git a/gpio/ws2812b_tester/app.c b/gpio/ws2812b_tester/app.c index 7d88511..2fecbe1 100644 --- a/gpio/ws2812b_tester/app.c +++ b/gpio/ws2812b_tester/app.c @@ -52,6 +52,7 @@ typedef struct { Widget* widget_about; // The about screen FuriTimer* timer; // Timer for automatic updating the LEDs LedTesterModel* model; // The model + LedDriver* led_driver; // The LED driver } LedTesterApp; // Hack so that we can access the application object from a variable_item_list on_enter/exit callback. @@ -170,7 +171,7 @@ static const char* setting_led_count_config_label = "LED Count"; static uint16_t setting_led_count_values[] = {1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512, 1024}; static char* setting_led_count_names[] = - {"1", "2", "3", "4", "5", "6", "7", "8", "16", "32", "64", "128", "256"}; + {"1", "2", "3", "4", "5", "6", "7", "8", "16", "32", "64", "128", "256", "512", "1024"}; static uint8_t setting_led_count_default_index = 3; // 4 LEDs static void led_tester_setting_led_count_change(VariableItem* item) { LedTesterApp* app = variable_item_get_context(item); @@ -334,20 +335,14 @@ static bool led_tester_custom_event_callback(void* context, uint32_t event) { ((rgb[i] & 0xFF) * app->model->led_max_brightness / 100); } - LedDriver* led_driver = led_driver_alloc(256, pin); - - // Set all LEDs to off - for(size_t i = 0; i < 256; i++) { - led_driver_set_led(led_driver, i, 0); - } + led_driver_set_pin(app->led_driver, pin); // Set the LEDs to the pattern for(size_t i = 0; i < app->model->led_count; i++) { - led_driver_set_led(led_driver, i, rgb[i % 4]); + led_driver_set_led(app->led_driver, i, rgb[i % 4]); } - led_driver_transmit(led_driver); - led_driver_free(led_driver); + led_driver_transmit(app->led_driver, false); return true; } @@ -437,6 +432,8 @@ static LedTesterApp* led_tester_app_alloc() { variable_item_set_current_value_index(item, setting_led_pin_index); variable_item_set_current_value_text(item, setting_led_pin_names[setting_led_pin_index]); app->model->led_pin_index = setting_led_pin_index; + app->led_driver = + led_driver_alloc(MAX_LED_COUNT, setting_led_pin_values[setting_led_pin_index]); // Count item = variable_item_list_add( @@ -518,7 +515,7 @@ static LedTesterApp* led_tester_app_alloc() { 0, 128, 64, - "This is a WS2812B LED tester\nVersion 1.5\nConnect WS2812B LED data\nwire to GPIO pin on Flipper.\n\nThe 3V3 pin has a 1200mA\nmax current (~4 watts). The\n5V pin has a 1000mA max\ncurrent (5 watts).\n\nauthors: @codeallnight and\nZ3BRO!\n\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison\n\n"); + "This is a WS2812B LED tester\nVersion 1.6\nConnect WS2812B LED data\nwire to GPIO pin on Flipper.\n\nThe 3V3 pin has a 1200mA\nmax current (~4 watts). The\n5V pin has a 1000mA max\ncurrent (5 watts).\n\nauthors: @codeallnight and\nZ3BRO!\n\nhttps://discord.com/invite/NsjCvqwPAd\nhttps://youtube.com/@MrDerekJamison\n\n"); view_set_previous_callback( widget_get_view(app->widget_about), led_tester_navigation_submenu_callback); view_dispatcher_add_view( @@ -533,6 +530,7 @@ static LedTesterApp* led_tester_app_alloc() { * @param app The led tester application object. */ static void led_tester_app_free(LedTesterApp* app) { + led_driver_free(app->led_driver); view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewAbout); widget_free(app->widget_about); view_dispatcher_remove_view(app->view_dispatcher, LedTesterViewLeds); diff --git a/gpio/ws2812b_tester/application.fam b/gpio/ws2812b_tester/application.fam index 01f276d..05fdb63 100644 --- a/gpio/ws2812b_tester/application.fam +++ b/gpio/ws2812b_tester/application.fam @@ -3,7 +3,7 @@ App( name="WS2812B LED Tester App", apptype=FlipperAppType.EXTERNAL, entry_point="ws2812b_led_tester_app", - fap_version=(1, 5), + fap_version=(1, 6), stack_size=4 * 1024, requires=[ "gui", diff --git a/gpio/ws2812b_tester/led_driver.c b/gpio/ws2812b_tester/led_driver.c index 7c600d2..a73e264 100644 --- a/gpio/ws2812b_tester/led_driver.c +++ b/gpio/ws2812b_tester/led_driver.c @@ -10,7 +10,6 @@ #include "led_driver.h" -#define MAX_LED_COUNT 256 // We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED) #define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24) // We use a setinel value to figure out when the timer is complete. @@ -37,6 +36,7 @@ struct LedDriver { uint8_t timer_buffer[LED_DRIVER_BUFFER_SIZE + 2]; uint32_t write_pos; uint32_t read_pos; + bool dirty; uint32_t count_leds; uint32_t* led_data; @@ -91,6 +91,7 @@ LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) { led_driver_init_dma_gpio_update(led_driver, gpio); led_driver_init_dma_led_transition_timer(led_driver); led_driver->led_data = malloc(MAX_LED_COUNT * sizeof(uint32_t)); + led_driver->dirty = true; led_driver->count_leds = count_leds; @@ -104,6 +105,15 @@ void led_driver_free(LedDriver* led_driver) { free(led_driver); } +void led_driver_set_pin(LedDriver* led_driver, const GpioPin* gpio) { + if(led_driver->gpio == gpio) { + return; + } + + led_driver_init_dma_gpio_update(led_driver, gpio); + led_driver->dirty = true; +} + uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) { furi_assert(led_driver); if(index >= led_driver->count_leds) { @@ -112,6 +122,8 @@ uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrgg uint32_t previous = led_driver->led_data[index]; led_driver->led_data[index] = rrggbb; + led_driver->dirty |= previous != rrggbb; + return previous; } @@ -219,12 +231,18 @@ static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) { } } -void led_driver_transmit(LedDriver* led_driver) { +void led_driver_transmit(LedDriver* led_driver, bool transmit_if_clean) { furi_assert(led_driver); furi_assert(!led_driver->read_pos); furi_assert(!led_driver->write_pos); + if(!transmit_if_clean && !led_driver->dirty) { + FURI_LOG_D("LED_DRIVER", "Skipping transmit"); + return; + } + FURI_LOG_D("LED_DRIVER", "Transmit"); + furi_hal_gpio_init(led_driver->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_write(led_driver->gpio, false); @@ -258,59 +276,5 @@ void led_driver_transmit(LedDriver* led_driver) { memset(led_driver->timer_buffer, LED_DRIVER_TIMER_SETINEL, LED_DRIVER_BUFFER_SIZE); led_driver->read_pos = 0; led_driver->write_pos = 0; + led_driver->dirty = false; } - -/* -int32_t main_led_test(void* _p) { - UNUSED(_p); - - uint16_t num_leds = MAX_LED_COUNT; - LedDriver* led_driver = led_driver_alloc(num_leds, &gpio_ext_pc3); - - uint32_t* data[80]; - for(int i = 0; i < 80; i++) { - data[i] = malloc(16 * 16 * sizeof(uint32_t)); - } - - for(int j = 0; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[0][j] = red << 16 | green << 8 | blue; - } - data[0][0] = 0x000F00; - - for(int i = 1; i < 80; i++) { - for(int j = 1; j < num_leds; j++) { - uint8_t red = rand() % 2; - uint8_t green = rand() % 4; - uint8_t blue = rand() % 4; - data[i][j] = red << 16 | green << 8 | blue; - data[i][j] = data[i - 1][j - 1]; - } - data[i][0] = data[i - 1][num_leds - 1]; - // for(int j = 0; j < num_leds; j++) { - // if(data[i - 1][j] == 0x000F00) { - // data[i][j] = 0x000F00; - // } - // } - data[i][rand() % num_leds] = 0x000F00; - } - - int counter = 0; - while(true) { - uint32_t i = counter++ % 80; - for(int j = 0; j < num_leds; j++) { - led_driver_set_led(led_driver, j, data[i][j]); - } - led_driver_transmit(led_driver); - furi_delay_ms(20); - } - - for(int i = 0; i < 80; i++) { - free(data[i]); - } - - return 0; -} -*/ \ No newline at end of file diff --git a/gpio/ws2812b_tester/led_driver.h b/gpio/ws2812b_tester/led_driver.h index a2c1a76..ed03d61 100644 --- a/gpio/ws2812b_tester/led_driver.h +++ b/gpio/ws2812b_tester/led_driver.h @@ -1,10 +1,13 @@ #include #include +#define MAX_LED_COUNT 1024 + typedef struct LedDriver LedDriver; LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio); void led_driver_free(LedDriver* led_driver); +void led_driver_set_pin(LedDriver* led_driver, const GpioPin* gpio); uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb); uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index); -void led_driver_transmit(LedDriver* led_driver); +void led_driver_transmit(LedDriver* led_driver, bool transmit_if_clean);