Skip to content

Commit

Permalink
[FL-3774] Fix 5V on GPIO (#4103)
Browse files Browse the repository at this point in the history
* Move OTG controls to the power service
* Accessor: add missing power service import
* Power: add is_otg_enabled to info and properly handle OTG enable with VBUS voltage present
* Power: method naming
* Power: add backward compatibility with old-style use of furi_hal_power
* Scripts: lower MIN_GAP_PAGES to 1
* SubGhz: fix incorrect logging tag
* SubGhz: delegate OTG management to power service
* Power: fix condition race, various improvements

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
  • Loading branch information
Astrrra and skotopes authored Feb 20, 2025
1 parent 3698fc8 commit 2817666
Show file tree
Hide file tree
Showing 24 changed files with 169 additions and 60 deletions.
7 changes: 5 additions & 2 deletions applications/debug/accessor/accessor_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <stdarg.h>
#include <power/power_service/power.h>

void AccessorApp::run(void) {
AccessorEvent event;
Expand Down Expand Up @@ -35,16 +36,18 @@ AccessorApp::AccessorApp()
: text_store{0} {
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
expansion = static_cast<Expansion*>(furi_record_open(RECORD_EXPANSION));
power = static_cast<Power*>(furi_record_open(RECORD_POWER));
onewire_host = onewire_host_alloc(&gpio_ibutton);
expansion_disable(expansion);
furi_hal_power_enable_otg();
power_enable_otg(power, true);
}

AccessorApp::~AccessorApp() {
furi_hal_power_disable_otg();
power_enable_otg(power, false);
expansion_enable(expansion);
furi_record_close(RECORD_EXPANSION);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_POWER);
onewire_host_free(onewire_host);
}

Expand Down
2 changes: 2 additions & 0 deletions applications/debug/accessor/accessor_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <one_wire/one_wire_host.h>
#include <notification/notification_messages.h>
#include <expansion/expansion.h>
#include <power/power_service/power.h>

class AccessorApp {
public:
Expand Down Expand Up @@ -53,4 +54,5 @@ class AccessorApp {

NotificationApp* notification;
Expansion* expansion;
Power* power;
};
10 changes: 7 additions & 3 deletions applications/examples/example_thermo/example_thermo.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <one_wire/maxim_crc.h>
#include <one_wire/one_wire_host.h>

#include <furi_hal_power.h>
#include <power/power_service/power.h>

#define UPDATE_PERIOD_MS 1000UL
#define TEXT_STORE_SIZE 64U
Expand Down Expand Up @@ -76,6 +76,7 @@ typedef struct {
FuriThread* reader_thread;
FuriMessageQueue* event_queue;
OneWireHost* onewire;
Power* power;
float temp_celsius;
bool has_device;
} ExampleThermoContext;
Expand Down Expand Up @@ -273,7 +274,7 @@ static void example_thermo_input_callback(InputEvent* event, void* ctx) {
/* Starts the reader thread and handles the input */
static void example_thermo_run(ExampleThermoContext* context) {
/* Enable power on external pins */
furi_hal_power_enable_otg();
power_enable_otg(context->power, true);

/* Configure the hardware in host mode */
onewire_host_start(context->onewire);
Expand Down Expand Up @@ -309,7 +310,7 @@ static void example_thermo_run(ExampleThermoContext* context) {
onewire_host_stop(context->onewire);

/* Disable power on external pins */
furi_hal_power_disable_otg();
power_enable_otg(context->power, false);
}

/******************** Initialisation & startup *****************************/
Expand All @@ -334,6 +335,8 @@ static ExampleThermoContext* example_thermo_context_alloc(void) {

context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN);

context->power = furi_record_open(RECORD_POWER);

return context;
}

Expand All @@ -348,6 +351,7 @@ static void example_thermo_context_free(ExampleThermoContext* context) {
view_port_free(context->view_port);

furi_record_close(RECORD_GUI);
furi_record_close(RECORD_POWER);
}

/* The application's entry point. Execution starts from here. */
Expand Down
3 changes: 3 additions & 0 deletions applications/main/gpio/gpio_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ GpioApp* gpio_app_alloc(void) {
app->gui = furi_record_open(RECORD_GUI);
app->gpio_items = gpio_items_alloc();

app->power = furi_record_open(RECORD_POWER);

app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
Expand Down Expand Up @@ -100,6 +102,7 @@ void gpio_app_free(GpioApp* app) {
// Close records
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_POWER);

expansion_enable(app->expansion);
furi_record_close(RECORD_EXPANSION);
Expand Down
2 changes: 2 additions & 0 deletions applications/main/gpio/gpio_app_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "scenes/gpio_scene.h"
#include "gpio_custom_event.h"
#include "usb_uart_bridge.h"
#include <power/power_service/power.h>

#include <gui/gui.h>
#include <gui/view_dispatcher.h>
Expand All @@ -27,6 +28,7 @@ struct GpioApp {
SceneManager* scene_manager;
Widget* widget;
DialogEx* dialog;
Power* power;

VariableItemList* var_item_list;
VariableItem* var_item_flow;
Expand Down
6 changes: 3 additions & 3 deletions applications/main/gpio/scenes/gpio_scene_start.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void gpio_scene_start_on_enter(void* context) {
GpioOtgSettingsNum,
gpio_scene_start_var_list_change_callback,
app);
if(furi_hal_power_is_otg_enabled()) {
if(power_is_otg_enabled(app->power)) {
variable_item_set_current_value_index(item, GpioOtgOn);
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]);
} else {
Expand All @@ -80,9 +80,9 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {

if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioStartEventOtgOn) {
furi_hal_power_enable_otg();
power_enable_otg(app->power, true);
} else if(event.event == GpioStartEventOtgOff) {
furi_hal_power_disable_otg();
power_enable_otg(app->power, false);
} else if(event.event == GpioStartEventManualControl) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
Expand Down
12 changes: 6 additions & 6 deletions applications/main/infrared/infrared_app.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "infrared_app_i.h"

#include <furi_hal_power.h>
#include <power/power_service/power.h>

#include <string.h>
#include <toolbox/path.h>
Expand Down Expand Up @@ -501,12 +501,12 @@ void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin) {
}

void infrared_enable_otg(InfraredApp* infrared, bool enable) {
if(enable) {
furi_hal_power_enable_otg();
} else {
furi_hal_power_disable_otg();
}
Power* power = furi_record_open(RECORD_POWER);

power_enable_otg(power, enable);
infrared->app_state.is_otg_enabled = enable;

furi_record_close(RECORD_POWER);
}

static void infrared_load_settings(InfraredApp* infrared) {
Expand Down
9 changes: 7 additions & 2 deletions applications/main/onewire/onewire_cli.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <furi.h>
#include <furi_hal.h>

#include <power/power_service/power.h>

#include <cli/cli.h>
#include <toolbox/args.h>

Expand All @@ -26,13 +28,14 @@ static void onewire_cli_print_usage(void) {
static void onewire_cli_search(Cli* cli) {
UNUSED(cli);
OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton);
Power* power = furi_record_open(RECORD_POWER);
uint8_t address[8];
bool done = false;

printf("Search started\r\n");

onewire_host_start(onewire);
furi_hal_power_enable_otg();
power_enable_otg(power, true);

while(!done) {
if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
Expand All @@ -49,8 +52,10 @@ static void onewire_cli_search(Cli* cli) {
furi_delay_ms(100);
}

furi_hal_power_disable_otg();
power_enable_otg(power, false);

onewire_host_free(onewire);
furi_record_close(RECORD_POWER);
}

void onewire_cli(Cli* cli, FuriString* args, void* context) {
Expand Down
21 changes: 8 additions & 13 deletions applications/main/subghz/helpers/subghz_txrx.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,22 @@
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>

#include <power/power_service/power.h>

#define TAG "SubGhz"

static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
UNUSED(instance);
uint8_t attempts = 5;
while(--attempts > 0) {
if(furi_hal_power_enable_otg()) break;
}
if(attempts == 0) {
if(furi_hal_power_get_usb_voltage() < 4.5f) {
FURI_LOG_E(
TAG,
"Error power otg enable. BQ2589 check otg fault = %d",
furi_hal_power_check_otg_fault() ? 1 : 0);
}
}
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
}

static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) {
UNUSED(instance);
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
}

SubGhzTxRx* subghz_txrx_alloc(void) {
Expand Down
19 changes: 6 additions & 13 deletions applications/main/subghz/subghz_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,15 @@
#define TAG "SubGhzCli"

static void subghz_cli_radio_device_power_on(void) {
uint8_t attempts = 5;
while(--attempts > 0) {
if(furi_hal_power_enable_otg()) break;
}
if(attempts == 0) {
if(furi_hal_power_get_usb_voltage() < 4.5f) {
FURI_LOG_E(
"TAG",
"Error power otg enable. BQ2589 check otg fault = %d",
furi_hal_power_check_otg_fault() ? 1 : 0);
}
}
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
}

static void subghz_cli_radio_device_power_off(void) {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
}

static SubGhzEnvironment* subghz_cli_environment_init(void) {
Expand Down
2 changes: 2 additions & 0 deletions applications/main/subghz/subghz_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#include "rpc/rpc_app.h"

#include <power/power_service/power.h>

#include "helpers/subghz_threshold_rssi.h"

#include "helpers/subghz_txrx.h"
Expand Down
10 changes: 8 additions & 2 deletions applications/services/expansion/expansion_worker.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "expansion_worker.h"

#include <power/power_service/power.h>
#include <furi_hal_power.h>

#include <furi_hal_serial.h>
#include <furi_hal_serial_control.h>

Expand Down Expand Up @@ -250,9 +252,13 @@ static bool expansion_worker_handle_state_connected(
if(!expansion_worker_rpc_session_open(instance)) break;
instance->state = ExpansionWorkerStateRpcActive;
} else if(command == ExpansionFrameControlCommandEnableOtg) {
furi_hal_power_enable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, true);
furi_record_close(RECORD_POWER);
} else if(command == ExpansionFrameControlCommandDisableOtg) {
furi_hal_power_disable_otg();
Power* power = furi_record_open(RECORD_POWER);
power_enable_otg(power, false);
furi_record_close(RECORD_POWER);
} else {
break;
}
Expand Down
7 changes: 5 additions & 2 deletions applications/services/power/power_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) {

void power_cli_5v(Cli* cli, FuriString* args) {
UNUSED(cli);
Power* power = furi_record_open(RECORD_POWER);
if(!furi_string_cmp(args, "0")) {
furi_hal_power_disable_otg();
power_enable_otg(power, false);
} else if(!furi_string_cmp(args, "1")) {
furi_hal_power_enable_otg();
power_enable_otg(power, true);
} else {
cli_print_usage("power_otg", "<1|0>", furi_string_get_cstr(args));
}

furi_record_close(RECORD_POWER);
}

void power_cli_3v3(Cli* cli, FuriString* args) {
Expand Down
40 changes: 37 additions & 3 deletions applications/services/power/power_service/power.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ static bool power_update_info(Power* power) {
.is_charging = furi_hal_power_is_charging(),
.gauge_is_ok = furi_hal_power_gauge_is_ok(),
.is_shutdown_requested = furi_hal_power_is_shutdown_requested(),
.is_otg_enabled = furi_hal_power_is_otg_enabled(),
.charge = furi_hal_power_get_pct(),
.health = furi_hal_power_get_bat_health_pct(),
.capacity_remaining = furi_hal_power_get_battery_remaining_capacity(),
Expand Down Expand Up @@ -216,6 +217,30 @@ static void power_message_callback(FuriEventLoopObject* object, void* context) {
case PowerMessageTypeShowBatteryLowWarning:
power->show_battery_low_warning = *msg.bool_param;
break;
case PowerMessageTypeSwitchOTG:
power->is_otg_requested = *msg.bool_param;
if(power->is_otg_requested) {
// Only try to enable if VBUS voltage is low, otherwise charger will refuse
if(power->info.voltage_vbus < 4.5f) {
size_t retries = 5;
while(retries-- > 0) {
if(furi_hal_power_enable_otg()) {
break;
}
}
if(!retries) {
FURI_LOG_W(TAG, "Failed to enable OTG, will try later");
}
} else {
FURI_LOG_W(
TAG,
"Postponing OTG enable: VBUS(%0.1f) >= 4.5v",
(double)power->info.voltage_vbus);
}
} else {
furi_hal_power_disable_otg();
}
break;
default:
furi_crash();
}
Expand All @@ -241,9 +266,18 @@ static void power_tick_callback(void* context) {
if(need_refresh) {
view_port_update(power->battery_view_port);
}
// Check OTG status and disable it in case of fault
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_check_otg_status();
// Check OTG status, disable in case of a fault
if(furi_hal_power_check_otg_fault()) {
FURI_LOG_E(TAG, "OTG fault detected, disabling OTG");
furi_hal_power_disable_otg();
power->is_otg_requested = false;
}

// Change OTG state if needed (i.e. after disconnecting USB power)
if(power->is_otg_requested &&
(!power->info.is_otg_enabled && power->info.voltage_vbus < 4.5f)) {
FURI_LOG_D(TAG, "OTG requested but not enabled, enabling OTG");
furi_hal_power_enable_otg();
}
}

Expand Down
Loading

0 comments on commit 2817666

Please sign in to comment.