From cfb1a0d01c620b40cf85f8e198e00a838722a202 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 9 Jun 2022 10:33:46 +0300 Subject: [PATCH] Hardware LED blinking (#1303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Hardware LED blinking notification messages * Blink: fix crash on exit, reset blinking on exit * Lib: remove unused UNUSED Co-authored-by: あく --- applications/debug_tools/blink_test.c | 55 ++++++--- applications/notification/notification.h | 12 ++ applications/notification/notification_app.c | 23 ++++ .../notification/notification_messages.c | 54 +++++++++ .../notification/notification_messages.h | 13 +++ firmware/targets/f7/furi_hal/furi_hal_light.c | 63 ++++++++--- .../targets/f7/furi_hal/furi_hal_resources.h | 8 +- .../targets/furi_hal_include/furi_hal_light.h | 19 ++++ lib/drivers/lp5562.c | 104 +++++++++++++++--- lib/drivers/lp5562.h | 23 +++- 10 files changed, 322 insertions(+), 52 deletions(-) diff --git a/applications/debug_tools/blink_test.c b/applications/debug_tools/blink_test.c index 2efaa4da..68139a43 100644 --- a/applications/debug_tools/blink_test.c +++ b/applications/debug_tools/blink_test.c @@ -1,3 +1,4 @@ +#include "furi/common_defines.h" #include #include @@ -6,8 +7,6 @@ #include -#define BLINK_COLOR_COUNT 7 - typedef enum { BlinkEventTypeTick, BlinkEventTypeInput, @@ -18,6 +17,42 @@ typedef struct { InputEvent input; } BlinkEvent; +static const NotificationSequence blink_test_sequence_hw_blink_start_red = { + &message_blink_start_10, + &message_blink_set_color_red, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence blink_test_sequence_hw_blink_green = { + &message_blink_set_color_green, + NULL, +}; + +static const NotificationSequence blink_test_sequence_hw_blink_blue = { + &message_blink_set_color_blue, + NULL, +}; + +static const NotificationSequence blink_test_sequence_hw_blink_stop = { + &message_blink_stop, + NULL, +}; + +static const NotificationSequence* blink_test_colors[] = { + &sequence_blink_red_100, + &sequence_blink_green_100, + &sequence_blink_blue_100, + &sequence_blink_yellow_100, + &sequence_blink_cyan_100, + &sequence_blink_magenta_100, + &sequence_blink_white_100, + &blink_test_sequence_hw_blink_start_red, + &blink_test_sequence_hw_blink_green, + &blink_test_sequence_hw_blink_blue, + &blink_test_sequence_hw_blink_stop, +}; + static void blink_test_update(void* ctx) { furi_assert(ctx); osMessageQueueId_t event_queue = ctx; @@ -58,16 +93,6 @@ int32_t blink_test_app(void* p) { NotificationApp* notifications = furi_record_open("notification"); - const NotificationSequence* colors[BLINK_COLOR_COUNT] = { - &sequence_blink_red_100, - &sequence_blink_green_100, - &sequence_blink_blue_100, - &sequence_blink_yellow_100, - &sequence_blink_cyan_100, - &sequence_blink_magenta_100, - &sequence_blink_white_100, - }; - uint8_t state = 0; BlinkEvent event; @@ -78,14 +103,16 @@ int32_t blink_test_app(void* p) { break; } } else { - notification_message(notifications, colors[state]); + notification_message(notifications, blink_test_colors[state]); state++; - if(state >= BLINK_COLOR_COUNT) { + if(state >= COUNT_OF(blink_test_colors)) { state = 0; } } } + notification_message(notifications, &blink_test_sequence_hw_blink_stop); + osTimerDelete(timer); gui_remove_view_port(gui, view_port); diff --git a/applications/notification/notification.h b/applications/notification/notification.h index 4cb9dc5e..14ca0ac2 100644 --- a/applications/notification/notification.h +++ b/applications/notification/notification.h @@ -1,6 +1,7 @@ #pragma once #include "stdint.h" #include "stdbool.h" +#include #ifdef __cplusplus extern "C" { @@ -30,9 +31,16 @@ typedef struct { float display_brightness; } NotificationMessageDataForcedSettings; +typedef struct { + uint16_t on_time; + uint16_t period; + Light color; +} NotificationMessageDataLedBlink; + typedef union { NotificationMessageDataSound sound; NotificationMessageDataLed led; + NotificationMessageDataLedBlink led_blink; NotificationMessageDataVibro vibro; NotificationMessageDataDelay delay; NotificationMessageDataForcedSettings forced_settings; @@ -48,6 +56,10 @@ typedef enum { NotificationMessageTypeLedGreen, NotificationMessageTypeLedBlue, + NotificationMessageTypeLedBlinkStart, + NotificationMessageTypeLedBlinkStop, + NotificationMessageTypeLedBlinkColor, + NotificationMessageTypeDelay, NotificationMessageTypeLedDisplayBacklight, diff --git a/applications/notification/notification_app.c b/applications/notification/notification_app.c index 7db4986d..687672c9 100644 --- a/applications/notification/notification_app.c +++ b/applications/notification/notification_app.c @@ -1,3 +1,4 @@ +#include "furi_hal_light.h" #include #include #include @@ -17,6 +18,7 @@ static const uint8_t reset_blue_mask = 1 << 2; static const uint8_t reset_vibro_mask = 1 << 3; static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; +static const uint8_t reset_blink_mask = 1 << 6; void notification_vibro_on(); void notification_vibro_off(); @@ -100,6 +102,9 @@ void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_m if(reset_mask & reset_blue_mask) { notification_reset_notification_led_layer(&app->led[2]); } + if(reset_mask & reset_blink_mask) { + furi_hal_light_blink_stop(); + } if(reset_mask & reset_vibro_mask) { notification_vibro_off(); } @@ -229,6 +234,24 @@ void notification_process_notification_message( app->led[2].value_last[LayerNotification] = led_values[2]; reset_mask |= reset_blue_mask; break; + case NotificationMessageTypeLedBlinkStart: + // store and send on delay or after seq + led_active = true; + furi_hal_light_blink_start( + notification_message->data.led_blink.color, + app->settings.led_brightness * 255, + notification_message->data.led_blink.on_time, + notification_message->data.led_blink.period); + reset_mask |= reset_blink_mask; + break; + case NotificationMessageTypeLedBlinkColor: + led_active = true; + furi_hal_light_blink_set_color(notification_message->data.led_blink.color); + break; + case NotificationMessageTypeLedBlinkStop: + furi_hal_light_blink_stop(); + reset_mask &= ~reset_blink_mask; + break; case NotificationMessageTypeVibro: if(notification_message->data.vibro.on) { if(vibro_setting) notification_vibro_on(); diff --git a/applications/notification/notification_messages.c b/applications/notification/notification_messages.c index 5a95556e..b14c08e3 100644 --- a/applications/notification/notification_messages.c +++ b/applications/notification/notification_messages.c @@ -1,3 +1,4 @@ +#include "furi_hal_resources.h" #include "notification.h" #include "notification_messages_notes.h" #include @@ -60,6 +61,59 @@ const NotificationMessage message_blue_0 = { .data.led.value = 0x00, }; +const NotificationMessage message_blink_start_10 = { + .type = NotificationMessageTypeLedBlinkStart, + .data.led_blink.color = 0, + .data.led_blink.on_time = 10, + .data.led_blink.period = 100, +}; + +const NotificationMessage message_blink_start_100 = { + .type = NotificationMessageTypeLedBlinkStart, + .data.led_blink.color = 0, + .data.led_blink.on_time = 100, + .data.led_blink.period = 1000, +}; + +const NotificationMessage message_blink_stop = { + .type = NotificationMessageTypeLedBlinkStop, +}; + +const NotificationMessage message_blink_set_color_red = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightRed, +}; + +const NotificationMessage message_blink_set_color_green = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightGreen, +}; + +const NotificationMessage message_blink_set_color_blue = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightBlue, +}; + +const NotificationMessage message_blink_set_color_cyan = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightBlue | LightGreen, +}; + +const NotificationMessage message_blink_set_color_magenta = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightBlue | LightRed, +}; + +const NotificationMessage message_blink_set_color_yellow = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightGreen | LightRed, +}; + +const NotificationMessage message_blink_set_color_white = { + .type = NotificationMessageTypeLedBlinkColor, + .data.led_blink.color = LightRed | LightGreen | LightBlue, +}; + // Delay const NotificationMessage message_delay_1 = { .type = NotificationMessageTypeDelay, diff --git a/applications/notification/notification_messages.h b/applications/notification/notification_messages.h index c1ab3407..a0ef654e 100644 --- a/applications/notification/notification_messages.h +++ b/applications/notification/notification_messages.h @@ -24,6 +24,19 @@ extern const NotificationMessage message_red_0; extern const NotificationMessage message_green_0; extern const NotificationMessage message_blue_0; +// Led hardware blink control +extern const NotificationMessage message_blink_start_10; +extern const NotificationMessage message_blink_start_100; +extern const NotificationMessage message_blink_stop; + +extern const NotificationMessage message_blink_set_color_red; +extern const NotificationMessage message_blink_set_color_green; +extern const NotificationMessage message_blink_set_color_blue; +extern const NotificationMessage message_blink_set_color_cyan; +extern const NotificationMessage message_blink_set_color_magenta; +extern const NotificationMessage message_blink_set_color_yellow; +extern const NotificationMessage message_blink_set_color_white; + // Delay extern const NotificationMessage message_delay_1; extern const NotificationMessage message_delay_10; diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 864b3c6e..5ff02f4a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -1,6 +1,9 @@ +#include "furi/common_defines.h" +#include "furi_hal_resources.h" #include #include #include +#include #define LED_CURRENT_RED 50 #define LED_CURRENT_GREEN 50 @@ -29,29 +32,63 @@ void furi_hal_light_init() { } void furi_hal_light_set(Light light, uint8_t value) { - uint8_t prev = 0; furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - switch(light) { - case LightRed: + if(light & LightRed) { lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); - break; - case LightGreen: + } + if(light & LightGreen) { lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); - break; - case LightBlue: + } + if(light & LightBlue) { lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); - break; - case LightBacklight: - prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); + } + if(light & LightBacklight) { + uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); lp5562_execute_ramp( &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); - break; - default: - break; } furi_hal_i2c_release(&furi_hal_i2c_handle_power); } +void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + lp5562_set_channel_src( + &furi_hal_i2c_handle_power, + LP5562ChannelRed | LP5562ChannelGreen | LP5562ChannelBlue, + LP5562Direct); + LP5562Channel led_ch = 0; + if(light & LightRed) led_ch |= LP5562ChannelRed; + if(light & LightGreen) led_ch |= LP5562ChannelGreen; + if(light & LightBlue) led_ch |= LP5562ChannelBlue; + lp5562_execute_blink( + &furi_hal_i2c_handle_power, LP5562Engine2, led_ch, on_time, period, brightness); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +void furi_hal_light_blink_stop() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + lp5562_set_channel_src( + &furi_hal_i2c_handle_power, + LP5562ChannelRed | LP5562ChannelGreen | LP5562ChannelBlue, + LP5562Direct); + lp5562_stop_program(&furi_hal_i2c_handle_power, LP5562Engine2); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +void furi_hal_light_blink_set_color(Light light) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + LP5562Channel led_ch = 0; + lp5562_set_channel_src( + &furi_hal_i2c_handle_power, + LP5562ChannelRed | LP5562ChannelGreen | LP5562ChannelBlue, + LP5562Direct); + if(light & LightRed) led_ch |= LP5562ChannelRed; + if(light & LightGreen) led_ch |= LP5562ChannelGreen; + if(light & LightBlue) led_ch |= LP5562ChannelBlue; + lp5562_set_channel_src(&furi_hal_i2c_handle_power, led_ch, LP5562Engine2); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + void furi_hal_light_sequence(const char* sequence) { do { if(*sequence == 'R') { diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index f12edcb3..e9d19827 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -24,10 +24,10 @@ typedef enum { /* Light */ typedef enum { - LightRed, - LightGreen, - LightBlue, - LightBacklight, + LightRed = (1 << 0), + LightGreen = (1 << 1), + LightBlue = (1 << 2), + LightBacklight = (1 << 3), } Light; typedef struct { diff --git a/firmware/targets/furi_hal_include/furi_hal_light.h b/firmware/targets/furi_hal_include/furi_hal_light.h index e3d244c3..0e06bd42 100644 --- a/firmware/targets/furi_hal_include/furi_hal_light.h +++ b/firmware/targets/furi_hal_include/furi_hal_light.h @@ -24,6 +24,25 @@ void furi_hal_light_init(); */ void furi_hal_light_set(Light light, uint8_t value); +/** Start hardware LED blinking mode + * + * @param light Light + * @param brightness light brightness [0-255] + * @param on_time LED on time in ms + * @param period LED blink period in ms + */ +void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period); + +/** Stop hardware LED blinking mode + */ +void furi_hal_light_blink_stop(); + +/** Set color in hardware LED blinking mode + * + * @param light Light + */ +void furi_hal_light_blink_set_color(Light light); + /** Execute sequence * * @param sequence Sequence to execute diff --git a/lib/drivers/lp5562.c b/lib/drivers/lp5562.c index 34b67ff6..7eef2a83 100644 --- a/lib/drivers/lp5562.c +++ b/lib/drivers/lp5562.c @@ -1,4 +1,5 @@ #include "lp5562.h" +#include "furi/common_defines.h" #include "lp5562_reg.h" #include @@ -79,27 +80,32 @@ uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel chan return value; } -static void - lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src) { +void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src) { uint8_t reg_val = 0; uint8_t bit_offset = 0; - if(channel == LP5562ChannelRed) { - bit_offset = 4; - } else if(channel == LP5562ChannelGreen) { - bit_offset = 2; - } else if(channel == LP5562ChannelBlue) { - bit_offset = 0; - } else if(channel == LP5562ChannelWhite) { - bit_offset = 6; - } else { - return; - } + do { + if(channel & LP5562ChannelRed) { + bit_offset = 4; + channel &= ~LP5562ChannelRed; + } else if(channel & LP5562ChannelGreen) { + bit_offset = 2; + channel &= ~LP5562ChannelGreen; + } else if(channel & LP5562ChannelBlue) { + bit_offset = 0; + channel &= ~LP5562ChannelBlue; + } else if(channel & LP5562ChannelWhite) { + bit_offset = 6; + channel &= ~LP5562ChannelWhite; + } else { + return; + } - furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x70, ®_val, LP5562_I2C_TIMEOUT); - reg_val &= ~(0x3 << bit_offset); - reg_val |= ((src & 0x03) << bit_offset); - furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT); + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x70, ®_val, LP5562_I2C_TIMEOUT); + reg_val &= ~(0x3 << bit_offset); + reg_val |= ((src & 0x03) << bit_offset); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT); + } while(channel); } void lp5562_execute_program( @@ -151,6 +157,19 @@ void lp5562_execute_program( furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, enable_reg, LP5562_I2C_TIMEOUT); } +void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) { + if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return; + uint8_t reg_val = 0; + uint8_t bit_offset = 0; + + // Engine configuration + bit_offset = (3 - eng) * 2; + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, ®_val, LP5562_I2C_TIMEOUT); + reg_val &= ~(0x3 << bit_offset); + reg_val |= (0x00 << bit_offset); // Disabled + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT); +} + void lp5562_execute_ramp( FuriHalI2cBusHandle* handle, LP5562Engine eng, @@ -194,3 +213,54 @@ void lp5562_execute_ramp( // Write end value to register lp5562_set_channel_value(handle, ch, val_end); } + +void lp5562_execute_blink( + FuriHalI2cBusHandle* handle, + LP5562Engine eng, + LP5562Channel ch, + uint16_t on_time, + uint16_t period, + uint8_t brightness) { + // Temporary switch to constant value from register + lp5562_set_channel_src(handle, ch, LP5562Direct); + + // Prepare command sequence + uint16_t program[16]; + uint16_t time_step = 0; + uint8_t prescaller = 0; + + program[0] = 0x4000 | brightness; // Set PWM + + time_step = on_time * 2; + if(time_step > 0x3F) { + time_step /= 32; + prescaller = 1; + } else { + prescaller = 0; + } + if(time_step == 0) { + time_step = 1; + } else if(time_step > 0x3F) + time_step = 0x3F; + program[1] = (prescaller << 14) | (time_step << 8); // Delay + + program[2] = 0x4000 | 0; // Set PWM + + time_step = (period - on_time) * 2; + if(time_step > 0x3F) { + time_step /= 32; + prescaller = 1; + } else { + prescaller = 0; + } + if(time_step == 0) { + time_step = 1; + } else if(time_step > 0x3F) + time_step = 0x3F; + program[3] = (prescaller << 14) | (time_step << 8); // Delay + + program[4] = 0x0000; // Go to start + + // Execute program + lp5562_execute_program(handle, eng, ch, program); +} diff --git a/lib/drivers/lp5562.h b/lib/drivers/lp5562.h index 33790d00..f5ebeeae 100644 --- a/lib/drivers/lp5562.h +++ b/lib/drivers/lp5562.h @@ -6,10 +6,10 @@ /** Channel types */ typedef enum { - LP5562ChannelRed, - LP5562ChannelGreen, - LP5562ChannelBlue, - LP5562ChannelWhite, + LP5562ChannelRed = (1 << 0), + LP5562ChannelGreen = (1 << 1), + LP5562ChannelBlue = (1 << 2), + LP5562ChannelWhite = (1 << 3), } LP5562Channel; typedef enum { @@ -37,6 +37,9 @@ void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel /** Get channel PWM value */ uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel); +/** Set channel source */ +void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src); + /** Execute program sequence */ void lp5562_execute_program( FuriHalI2cBusHandle* handle, @@ -44,6 +47,9 @@ void lp5562_execute_program( LP5562Channel ch, uint16_t* program); +/** Stop program sequence */ +void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng); + /** Execute ramp program sequence */ void lp5562_execute_ramp( FuriHalI2cBusHandle* handle, @@ -52,3 +58,12 @@ void lp5562_execute_ramp( uint8_t val_start, uint8_t val_end, uint16_t time); + +/** Start blink program sequence */ +void lp5562_execute_blink( + FuriHalI2cBusHandle* handle, + LP5562Engine eng, + LP5562Channel ch, + uint16_t on_time, + uint16_t period, + uint8_t brightness);