v1.6: Add support for 1000 LEDS, Add "dirty" support to remove update flicker.

This commit is contained in:
Derek Jamison 2023-12-06 12:40:19 -06:00
parent 606abe7887
commit 6b817ad23e
5 changed files with 40 additions and 70 deletions

View File

@ -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

View File

@ -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);

View File

@ -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",

View File

@ -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;
}
*/

View File

@ -1,10 +1,13 @@
#include <furi.h>
#include <furi_hal.h>
#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);