[FL-3774] Fix 5V on GPIO (#4103)

* 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>
This commit is contained in:
Astra
2025-02-20 12:37:52 +09:00
committed by GitHub
parent 3698fc8d02
commit 2817666eb9
24 changed files with 169 additions and 60 deletions

View File

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

View File

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

View File

@@ -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(),
@@ -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();
}
@@ -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();
}
}

View File

@@ -39,6 +39,7 @@ typedef struct {
bool gauge_is_ok;
bool is_charging;
bool is_shutdown_requested;
bool is_otg_enabled;
float current_charger;
float current_gauge;
@@ -96,6 +97,19 @@ bool power_is_battery_healthy(Power* power);
*/
void power_enable_low_battery_level_notification(Power* power, bool enable);
/** Enable or disable OTG
*
* @param power Power instance
* @param enable true - enable, false - disable
*/
void power_enable_otg(Power* power, bool enable);
/** Check OTG status
*
* @return true if OTG is requested
*/
bool power_is_otg_enabled(Power* power);
#ifdef __cplusplus
}
#endif

View File

@@ -70,3 +70,22 @@ void power_enable_low_battery_level_notification(Power* power, bool enable) {
furi_check(
furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);
}
void power_enable_otg(Power* power, bool enable) {
furi_check(power);
PowerMessage msg = {
.type = PowerMessageTypeSwitchOTG,
.bool_param = &enable,
.lock = api_lock_alloc_locked(),
};
furi_check(
furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk);
api_lock_wait_unlock_and_free(msg.lock);
}
bool power_is_otg_enabled(Power* power) {
furi_check(power);
return power->is_otg_requested;
}

View File

@@ -33,6 +33,7 @@ struct Power {
bool battery_low;
bool show_battery_low_warning;
bool is_otg_requested;
uint8_t battery_level;
uint8_t power_off_timeout;
};
@@ -48,6 +49,7 @@ typedef enum {
PowerMessageTypeGetInfo,
PowerMessageTypeIsBatteryHealthy,
PowerMessageTypeShowBatteryLowWarning,
PowerMessageTypeSwitchOTG,
} PowerMessageType;
typedef struct {

View File

@@ -4,6 +4,7 @@
#include <furi_hal_gpio.h>
#include <furi_hal_power.h>
#include <furi_hal_resources.h>
#include <power/power_service/power.h>
static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) {
switch(rpc_pin) {
@@ -218,12 +219,16 @@ void rpc_system_gpio_set_otg_mode(const PB_Main* request, void* context) {
const PB_Gpio_GpioOtgMode mode = request->content.gpio_set_otg_mode.mode;
Power* power = furi_record_open(RECORD_POWER);
if(mode == PB_Gpio_GpioOtgMode_OFF) {
furi_hal_power_disable_otg();
power_enable_otg(power, false);
} else {
furi_hal_power_enable_otg();
power_enable_otg(power, true);
}
furi_record_close(RECORD_POWER);
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
}