Browse Source

[FL-1911] FuriHal: i2c refactoring (#847)

* Project: fix release build, replace asserts with checks.
* FuriHal: i2c refactoring, new bus access model, flexible bus gpio configuration.
* FuriHal: set i2c pins to high on detach.
* FuriHal: more i2c bus events, put bus under reset when not used, move bus enable/disable routine to bus handler.
* FuriHal: fix i2c deadlock in power api, add external i2c handle.
あく 4 years ago
parent
commit
cf591ef7eb
45 changed files with 1690 additions and 786 deletions
  1. 2 2
      applications/storage/storage-external-api.c
  2. 1 1
      applications/tests/rpc/rpc_test.c
  3. 149 0
      bootloader/targets/f6/furi-hal/furi-hal-i2c-config.c
  4. 31 0
      bootloader/targets/f6/furi-hal/furi-hal-i2c-config.h
  5. 51 0
      bootloader/targets/f6/furi-hal/furi-hal-i2c-types.h
  6. 47 48
      bootloader/targets/f6/furi-hal/furi-hal-i2c.c
  7. 55 9
      bootloader/targets/f6/furi-hal/furi-hal-i2c.h
  8. 21 15
      bootloader/targets/f6/furi-hal/furi-hal-light.c
  9. 3 0
      bootloader/targets/f6/furi-hal/furi-hal-resources.c
  10. 3 12
      bootloader/targets/f6/furi-hal/furi-hal-resources.h
  11. 149 0
      bootloader/targets/f7/furi-hal/furi-hal-i2c-config.c
  12. 31 0
      bootloader/targets/f7/furi-hal/furi-hal-i2c-config.h
  13. 51 0
      bootloader/targets/f7/furi-hal/furi-hal-i2c-types.h
  14. 47 48
      bootloader/targets/f7/furi-hal/furi-hal-i2c.c
  15. 55 9
      bootloader/targets/f7/furi-hal/furi-hal-i2c.h
  16. 21 15
      bootloader/targets/f7/furi-hal/furi-hal-light.c
  17. 3 0
      bootloader/targets/f7/furi-hal/furi-hal-resources.c
  18. 3 12
      bootloader/targets/f7/furi-hal/furi-hal-resources.h
  19. 133 0
      firmware/targets/f6/furi-hal/furi-hal-i2c-config.c
  20. 31 0
      firmware/targets/f6/furi-hal/furi-hal-i2c-config.h
  21. 49 0
      firmware/targets/f6/furi-hal/furi-hal-i2c-types.h
  22. 47 60
      firmware/targets/f6/furi-hal/furi-hal-i2c.c
  23. 30 27
      firmware/targets/f6/furi-hal/furi-hal-light.c
  24. 94 37
      firmware/targets/f6/furi-hal/furi-hal-power.c
  25. 3 0
      firmware/targets/f6/furi-hal/furi-hal-resources.c
  26. 3 18
      firmware/targets/f6/furi-hal/furi-hal-resources.h
  27. 1 1
      firmware/targets/f6/furi-hal/furi-hal-vcp.c
  28. 133 0
      firmware/targets/f7/furi-hal/furi-hal-i2c-config.c
  29. 31 0
      firmware/targets/f7/furi-hal/furi-hal-i2c-config.h
  30. 49 0
      firmware/targets/f7/furi-hal/furi-hal-i2c-types.h
  31. 47 60
      firmware/targets/f7/furi-hal/furi-hal-i2c.c
  32. 30 27
      firmware/targets/f7/furi-hal/furi-hal-light.c
  33. 94 37
      firmware/targets/f7/furi-hal/furi-hal-power.c
  34. 3 0
      firmware/targets/f7/furi-hal/furi-hal-resources.c
  35. 3 18
      firmware/targets/f7/furi-hal/furi-hal-resources.h
  36. 0 108
      firmware/targets/f7/furi-hal/furi-hal-spi.h
  37. 1 1
      firmware/targets/f7/furi-hal/furi-hal-vcp.c
  38. 19 31
      firmware/targets/furi-hal-include/furi-hal-i2c.h
  39. 1 2
      firmware/targets/furi-hal-include/furi-hal-spi.h
  40. 37 50
      lib/drivers/bq25896.c
  41. 14 13
      lib/drivers/bq25896.h
  42. 82 89
      lib/drivers/bq27220.c
  43. 13 12
      lib/drivers/bq27220.h
  44. 13 19
      lib/drivers/lp5562.c
  45. 6 5
      lib/drivers/lp5562.h

+ 2 - 2
applications/storage/storage-external-api.c

@@ -427,7 +427,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) {
 
             string_init_printf(fullname, "%s/%s", string_get_cstr(cur_dir), name);
             FS_Error error = storage_common_remove(storage, string_get_cstr(fullname));
-            furi_assert(error == FSE_OK);
+            furi_check(error == FSE_OK);
             string_clear(fullname);
         }
         storage_dir_close(dir);
@@ -438,7 +438,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) {
         }
 
         FS_Error error = storage_common_remove(storage, string_get_cstr(cur_dir));
-        furi_assert(error == FSE_OK);
+        furi_check(error == FSE_OK);
 
         if(string_cmp(cur_dir, path)) {
             size_t last_char = string_search_rchar(cur_dir, '/');

+ 1 - 1
applications/tests/rpc/rpc_test.c

@@ -115,7 +115,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) {
                 clean_directory(fs_api, fullname);
             }
             FS_Error error = storage_common_remove(fs_api, fullname);
-            furi_assert(error == FSE_OK);
+            furi_check(error == FSE_OK);
             free(fullname);
         }
         free(name);

+ 149 - 0
bootloader/targets/f6/furi-hal/furi-hal-i2c-config.c

@@ -0,0 +1,149 @@
+#include "furi-hal-i2c-config.h"
+#include <furi-hal-resources.h>
+#include <furi-hal-version.h>
+
+#include <stm32wbxx_ll_rcc.h>
+#include <stm32wbxx_ll_bus.h>
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Standard Mode @100kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100 0x10707DBC
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Fast Mode @400kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400 0x00602173
+
+static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if(event == FuriHalI2cBusEventInit) {
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+        bus->current_handle = NULL;
+    } else if(event == FuriHalI2cBusEventDeinit) {
+    } else if(event == FuriHalI2cBusEventLock) {
+    } else if(event == FuriHalI2cBusEventUnlock) {
+    } else if(event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1);
+    } else if(event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_power = {
+    .i2c = I2C1,
+    .current_handle = NULL,
+    .callback = furi_hal_i2c_bus_power_event,
+};
+
+static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if(event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3);
+    } else if(event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_external = {
+    .i2c = I2C3,
+    .current_handle = NULL,
+    .callback = furi_hal_i2c_bus_external_event,
+};
+
+void furi_hal_i2c_bus_handle_power_event(
+    FuriHalI2cBusHandle* handle,
+    FuriHalI2cBusHandleEvent event) {
+    if(event == FuriHalI2cBusHandleEventActivate) {
+        LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_sda,
+            GpioModeAltFunctionOpenDrain,
+            GpioPullNo,
+            GpioSpeedLow,
+            GpioAltFn4I2C1);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_scl,
+            GpioModeAltFunctionOpenDrain,
+            GpioPullNo,
+            GpioSpeedLow,
+            GpioAltFn4I2C1);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        if(furi_hal_version_get_hw_version() > 10) {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400;
+        } else {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        }
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if(event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_i2c_power_sda, 1);
+        hal_gpio_write(&gpio_i2c_power_scl, 1);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_sda, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_scl, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_power = {
+    .bus = &furi_hal_i2c_bus_power,
+    .callback = furi_hal_i2c_bus_handle_power_event,
+};
+
+void furi_hal_i2c_bus_handle_external_event(
+    FuriHalI2cBusHandle* handle,
+    FuriHalI2cBusHandleEvent event) {
+    if(event == FuriHalI2cBusHandleEventActivate) {
+        hal_gpio_init_ex(
+            &gpio_ext_pc0, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+        hal_gpio_init_ex(
+            &gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if(event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_ext_pc0, 1);
+        hal_gpio_write(&gpio_ext_pc1, 1);
+        hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_external = {
+    .bus = &furi_hal_i2c_bus_external,
+    .callback = furi_hal_i2c_bus_handle_external_event,
+};

+ 31 - 0
bootloader/targets/f6/furi-hal/furi-hal-i2c-config.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include <furi-hal-i2c-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal(power) i2c bus, I2C1, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_power;
+
+/** External i2c bus, I2C3, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_external;
+
+/** Handle for internal(power) i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PA9(SCL) / PA10(SDA), float on release
+ * Params: 400khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_power;
+
+/** Handle for external i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PC0(SCL) / PC1(SDA), float on release
+ * Params: 100khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_external;
+
+#ifdef __cplusplus
+}
+#endif

+ 51 - 0
bootloader/targets/f6/furi-hal/furi-hal-i2c-types.h

@@ -0,0 +1,51 @@
+#pragma once
+
+#include <stm32wbxx_ll_i2c.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct FuriHalI2cBus FuriHalI2cBus;
+typedef struct FuriHalI2cBusHandle FuriHalI2cBusHandle;
+
+/** FuriHal i2c bus states */
+typedef enum {
+    FuriHalI2cBusEventInit, /**< Bus initialization event, called on system start */
+    FuriHalI2cBusEventDeinit, /**< Bus deinitialization event, called on system stop */
+    FuriHalI2cBusEventLock, /**< Bus lock event, called before activation */
+    FuriHalI2cBusEventUnlock, /**< Bus unlock event, called after deactivation */
+    FuriHalI2cBusEventActivate, /**< Bus activation event, called before handle activation */
+    FuriHalI2cBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */
+} FuriHalI2cBusEvent;
+
+/** FuriHal i2c bus event callback */
+typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEvent event);
+
+/** FuriHal i2c bus */
+struct FuriHalI2cBus {
+    I2C_TypeDef* i2c;
+    FuriHalI2cBusHandle* current_handle;
+    FuriHalI2cBusEventCallback callback;
+};
+
+/** FuriHal i2c handle states */
+typedef enum {
+    FuriHalI2cBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */
+    FuriHalI2cBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */
+} FuriHalI2cBusHandleEvent;
+
+/** FuriHal i2c handle event callback */
+typedef void (*FuriHalI2cBusHandleEventCallback)(
+    FuriHalI2cBusHandle* handle,
+    FuriHalI2cBusHandleEvent event);
+
+/** FuriHal i2c handle */
+struct FuriHalI2cBusHandle {
+    FuriHalI2cBus* bus;
+    FuriHalI2cBusHandleEventCallback callback;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 47 - 48
bootloader/targets/f6/furi-hal/furi-hal-i2c.c

@@ -1,66 +1,64 @@
 #include <furi-hal-i2c.h>
+#include <furi-hal-version.h>
 
-#include <stm32wbxx_ll_bus.h>
 #include <stm32wbxx_ll_i2c.h>
-#include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_gpio.h>
 #include <stm32wbxx_ll_cortex.h>
 
+#include <assert.h>
+
 void furi_hal_i2c_init() {
-    LL_I2C_InitTypeDef I2C_InitStruct = {0};
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-
-    LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
-
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
-    GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
-    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
-
-    I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
-    I2C_InitStruct.Timing = POWER_I2C_TIMINGS;
-    I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
-    I2C_InitStruct.DigitalFilter = 0;
-    I2C_InitStruct.OwnAddress1 = 0;
-    I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
-    I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
-    LL_I2C_Init(I2C1, &I2C_InitStruct);
-    LL_I2C_EnableAutoEndMode(I2C1);
-    LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
-    LL_I2C_DisableOwnAddress2(I2C1);
-    LL_I2C_DisableGeneralCall(I2C1);
-    LL_I2C_EnableClockStretching(I2C1);
+    furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit);
+}
+
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) {
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
+
+    assert(handle->bus->current_handle == NULL);
+
+    handle->bus->current_handle = handle;
+
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate);
+
+    handle->callback(handle, FuriHalI2cBusHandleEventActivate);
+}
+
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) {
+    assert(handle->bus->current_handle == handle);
+
+    handle->callback(handle, FuriHalI2cBusHandleEventDeactivate);
+
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate);
+
+    handle->bus->current_handle = NULL;
+
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
 }
 
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    assert(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_WRITE);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_TXIS(instance)) {
-            LL_I2C_TransmitData8(instance, (*data));
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) {
+            LL_I2C_TransmitData8(handle->bus->i2c, (*data));
             data++;
             size--;
             time_left = timeout;
@@ -74,34 +72,35 @@ bool furi_hal_i2c_tx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    assert(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_READ);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_RXNE(instance)) {
-            *data = LL_I2C_ReceiveData8(instance);
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) {
+            *data = LL_I2C_ReceiveData8(handle->bus->i2c);
             data++;
             size--;
             time_left = timeout;
@@ -115,21 +114,21 @@ bool furi_hal_i2c_rx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* tx_data,
     uint8_t tx_size,
     uint8_t* rx_data,
     uint8_t rx_size,
     uint32_t timeout) {
-    if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
-       furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
+    if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) &&
+       furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) {
         return true;
     } else {
         return false;

+ 55 - 9
bootloader/targets/f6/furi-hal/furi-hal-i2c.h

@@ -1,31 +1,82 @@
+/**
+ * @file furi-hal-i2c.h
+ * I2C HAL API
+ */
+
 #pragma once
 
 #include <stdbool.h>
 #include <stdint.h>
-#include <furi-hal-resources.h>
+#include <furi-hal-i2c-config.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/** Init I2C
+ */
 void furi_hal_i2c_init();
 
+/** Acquire i2c bus handle
+ *
+ * @return     Instance of FuriHalI2cBus
+ */
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle);
+
+/** Release i2c bus handle
+ *
+ * @param      bus   instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire`
+ */
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle);
+
+/** Perform I2C tx transfer
+ *
+ * @param      instance  I2C_TypeDef instance
+ * @param      address   I2C slave address
+ * @param      data      pointer to data buffer
+ * @param      size      size of data buffer
+ * @param      timeout   timeout in ticks
+ *
+ * @return     true on successful transfer, false otherwise
+ */
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     const uint8_t* data,
     const uint8_t size,
     uint32_t timeout);
 
+/** Perform I2C rx transfer
+ *
+ * @param      instance  I2C_TypeDef instance
+ * @param      address   I2C slave address
+ * @param      data      pointer to data buffer
+ * @param      size      size of data buffer
+ * @param      timeout   timeout in ticks
+ *
+ * @return     true on successful transfer, false otherwise
+ */
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     uint8_t* data,
     const uint8_t size,
     uint32_t timeout);
 
+/** Perform I2C tx and rx transfers
+ *
+ * @param      instance  I2C_TypeDef instance
+ * @param      address   I2C slave address
+ * @param      tx_data   pointer to tx data buffer
+ * @param      tx_size   size of tx data buffer
+ * @param      rx_data   pointer to rx data buffer
+ * @param      rx_size   size of rx data buffer
+ * @param      timeout   timeout in ticks
+ *
+ * @return     true on successful transfer, false otherwise
+ */
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     const uint8_t* tx_data,
     const uint8_t tx_size,
@@ -33,11 +84,6 @@ bool furi_hal_i2c_trx(
     const uint8_t rx_size,
     uint32_t timeout);
 
-#define with_furi_hal_i2c(type, pointer, function_body)       \
-    {                                                         \
-        *pointer = ({ type __fn__ function_body __fn__; })(); \
-    }
-
 #ifdef __cplusplus
 }
 #endif

+ 21 - 15
bootloader/targets/f6/furi-hal/furi-hal-light.c

@@ -7,37 +7,43 @@
 #define LED_CURRENT_WHITE 150
 
 void furi_hal_light_init() {
-    lp5562_reset();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
 
-    lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
-    lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
-    lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
-    lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
+    lp5562_reset(&furi_hal_i2c_handle_power);
 
-    lp5562_set_channel_value(LP5562ChannelRed, 0x00);
-    lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
-    lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
-    lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelRed, LED_CURRENT_RED);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelGreen, LED_CURRENT_GREEN);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelBlue, LED_CURRENT_BLUE);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelWhite, LED_CURRENT_WHITE);
 
-    lp5562_enable();
-    lp5562_configure();
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, 0x00);
+
+    lp5562_enable(&furi_hal_i2c_handle_power);
+    lp5562_configure(&furi_hal_i2c_handle_power);
+
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_light_set(Light light, uint8_t value) {
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     switch(light) {
     case LightRed:
-        lp5562_set_channel_value(LP5562ChannelRed, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value);
         break;
     case LightGreen:
-        lp5562_set_channel_value(LP5562ChannelGreen, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value);
         break;
     case LightBlue:
-        lp5562_set_channel_value(LP5562ChannelBlue, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
         break;
     case LightBacklight:
-        lp5562_set_channel_value(LP5562ChannelWhite, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value);
         break;
     default:
         break;
     }
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }

+ 3 - 0
bootloader/targets/f6/furi-hal/furi-hal-resources.c

@@ -39,3 +39,6 @@ const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
 
 const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
 const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
+
+const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10};
+const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9};

+ 3 - 12
bootloader/targets/f6/furi-hal/furi-hal-resources.h

@@ -8,18 +8,6 @@
 extern "C" {
 #endif
 
-#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
-#define POWER_I2C_SCL_GPIO_Port GPIOA
-#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
-#define POWER_I2C_SDA_GPIO_Port GPIOA
-
-#define POWER_I2C I2C1
-/* Timing register value is computed with the STM32CubeMX Tool,
-  * Fast Mode @100kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define POWER_I2C_TIMINGS 0x10707DBC
-
 /* Input Keys */
 typedef enum {
     InputKeyUp,
@@ -77,6 +65,9 @@ extern const GpioPin gpio_irda_tx;
 extern const GpioPin gpio_usart_tx;
 extern const GpioPin gpio_usart_rx;
 
+extern const GpioPin gpio_i2c_power_sda;
+extern const GpioPin gpio_i2c_power_scl;
+
 #ifdef __cplusplus
 }
 #endif

+ 149 - 0
bootloader/targets/f7/furi-hal/furi-hal-i2c-config.c

@@ -0,0 +1,149 @@
+#include "furi-hal-i2c-config.h"
+#include <furi-hal-resources.h>
+#include <furi-hal-version.h>
+
+#include <stm32wbxx_ll_rcc.h>
+#include <stm32wbxx_ll_bus.h>
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Standard Mode @100kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100 0x10707DBC
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Fast Mode @400kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400 0x00602173
+
+static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if(event == FuriHalI2cBusEventInit) {
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+        bus->current_handle = NULL;
+    } else if(event == FuriHalI2cBusEventDeinit) {
+    } else if(event == FuriHalI2cBusEventLock) {
+    } else if(event == FuriHalI2cBusEventUnlock) {
+    } else if(event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1);
+    } else if(event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_power = {
+    .i2c = I2C1,
+    .current_handle = NULL,
+    .callback = furi_hal_i2c_bus_power_event,
+};
+
+static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if(event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3);
+    } else if(event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_external = {
+    .i2c = I2C3,
+    .current_handle = NULL,
+    .callback = furi_hal_i2c_bus_external_event,
+};
+
+void furi_hal_i2c_bus_handle_power_event(
+    FuriHalI2cBusHandle* handle,
+    FuriHalI2cBusHandleEvent event) {
+    if(event == FuriHalI2cBusHandleEventActivate) {
+        LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_sda,
+            GpioModeAltFunctionOpenDrain,
+            GpioPullNo,
+            GpioSpeedLow,
+            GpioAltFn4I2C1);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_scl,
+            GpioModeAltFunctionOpenDrain,
+            GpioPullNo,
+            GpioSpeedLow,
+            GpioAltFn4I2C1);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        if(furi_hal_version_get_hw_version() > 10) {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400;
+        } else {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        }
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if(event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_i2c_power_sda, 1);
+        hal_gpio_write(&gpio_i2c_power_scl, 1);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_sda, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(
+            &gpio_i2c_power_scl, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_power = {
+    .bus = &furi_hal_i2c_bus_power,
+    .callback = furi_hal_i2c_bus_handle_power_event,
+};
+
+void furi_hal_i2c_bus_handle_external_event(
+    FuriHalI2cBusHandle* handle,
+    FuriHalI2cBusHandleEvent event) {
+    if(event == FuriHalI2cBusHandleEventActivate) {
+        hal_gpio_init_ex(
+            &gpio_ext_pc0, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+        hal_gpio_init_ex(
+            &gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if(event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_ext_pc0, 1);
+        hal_gpio_write(&gpio_ext_pc1, 1);
+        hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_external = {
+    .bus = &furi_hal_i2c_bus_external,
+    .callback = furi_hal_i2c_bus_handle_external_event,
+};

+ 31 - 0
bootloader/targets/f7/furi-hal/furi-hal-i2c-config.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include <furi-hal-i2c-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal(power) i2c bus, I2C1, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_power;
+
+/** External i2c bus, I2C3, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_external;
+
+/** Handle for internal(power) i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PA9(SCL) / PA10(SDA), float on release
+ * Params: 400khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_power;
+
+/** Handle for external i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PC0(SCL) / PC1(SDA), float on release
+ * Params: 100khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_external;
+
+#ifdef __cplusplus
+}
+#endif

+ 51 - 0
bootloader/targets/f7/furi-hal/furi-hal-i2c-types.h

@@ -0,0 +1,51 @@
+#pragma once
+
+#include <stm32wbxx_ll_i2c.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct FuriHalI2cBus FuriHalI2cBus;
+typedef struct FuriHalI2cBusHandle FuriHalI2cBusHandle;
+
+/** FuriHal i2c bus states */
+typedef enum {
+    FuriHalI2cBusEventInit, /**< Bus initialization event, called on system start */
+    FuriHalI2cBusEventDeinit, /**< Bus deinitialization event, called on system stop */
+    FuriHalI2cBusEventLock, /**< Bus lock event, called before activation */
+    FuriHalI2cBusEventUnlock, /**< Bus unlock event, called after deactivation */
+    FuriHalI2cBusEventActivate, /**< Bus activation event, called before handle activation */
+    FuriHalI2cBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */
+} FuriHalI2cBusEvent;
+
+/** FuriHal i2c bus event callback */
+typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEvent event);
+
+/** FuriHal i2c bus */
+struct FuriHalI2cBus {
+    I2C_TypeDef* i2c;
+    FuriHalI2cBusHandle* current_handle;
+    FuriHalI2cBusEventCallback callback;
+};
+
+/** FuriHal i2c handle states */
+typedef enum {
+    FuriHalI2cBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */
+    FuriHalI2cBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */
+} FuriHalI2cBusHandleEvent;
+
+/** FuriHal i2c handle event callback */
+typedef void (*FuriHalI2cBusHandleEventCallback)(
+    FuriHalI2cBusHandle* handle,
+    FuriHalI2cBusHandleEvent event);
+
+/** FuriHal i2c handle */
+struct FuriHalI2cBusHandle {
+    FuriHalI2cBus* bus;
+    FuriHalI2cBusHandleEventCallback callback;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 47 - 48
bootloader/targets/f7/furi-hal/furi-hal-i2c.c

@@ -1,66 +1,64 @@
 #include <furi-hal-i2c.h>
+#include <furi-hal-version.h>
 
-#include <stm32wbxx_ll_bus.h>
 #include <stm32wbxx_ll_i2c.h>
-#include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_gpio.h>
 #include <stm32wbxx_ll_cortex.h>
 
+#include <assert.h>
+
 void furi_hal_i2c_init() {
-    LL_I2C_InitTypeDef I2C_InitStruct = {0};
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-
-    LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
-
-    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
-    GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
-    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
-
-    I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
-    I2C_InitStruct.Timing = POWER_I2C_TIMINGS;
-    I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
-    I2C_InitStruct.DigitalFilter = 0;
-    I2C_InitStruct.OwnAddress1 = 0;
-    I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
-    I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
-    LL_I2C_Init(I2C1, &I2C_InitStruct);
-    LL_I2C_EnableAutoEndMode(I2C1);
-    LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
-    LL_I2C_DisableOwnAddress2(I2C1);
-    LL_I2C_DisableGeneralCall(I2C1);
-    LL_I2C_EnableClockStretching(I2C1);
+    furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit);
+}
+
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) {
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
+
+    assert(handle->bus->current_handle == NULL);
+
+    handle->bus->current_handle = handle;
+
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate);
+
+    handle->callback(handle, FuriHalI2cBusHandleEventActivate);
+}
+
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) {
+    assert(handle->bus->current_handle == handle);
+
+    handle->callback(handle, FuriHalI2cBusHandleEventDeactivate);
+
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate);
+
+    handle->bus->current_handle = NULL;
+
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
 }
 
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    assert(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_WRITE);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_TXIS(instance)) {
-            LL_I2C_TransmitData8(instance, (*data));
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) {
+            LL_I2C_TransmitData8(handle->bus->i2c, (*data));
             data++;
             size--;
             time_left = timeout;
@@ -74,34 +72,35 @@ bool furi_hal_i2c_tx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    assert(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_READ);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_RXNE(instance)) {
-            *data = LL_I2C_ReceiveData8(instance);
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) {
+            *data = LL_I2C_ReceiveData8(handle->bus->i2c);
             data++;
             size--;
             time_left = timeout;
@@ -115,21 +114,21 @@ bool furi_hal_i2c_rx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* tx_data,
     uint8_t tx_size,
     uint8_t* rx_data,
     uint8_t rx_size,
     uint32_t timeout) {
-    if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
-       furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
+    if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) &&
+       furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) {
         return true;
     } else {
         return false;

+ 55 - 9
bootloader/targets/f7/furi-hal/furi-hal-i2c.h

@@ -1,31 +1,82 @@
+/**
+ * @file furi-hal-i2c.h
+ * I2C HAL API
+ */
+
 #pragma once
 
 #include <stdbool.h>
 #include <stdint.h>
-#include <furi-hal-resources.h>
+#include <furi-hal-i2c-config.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/** Init I2C
+ */
 void furi_hal_i2c_init();
 
+/** Acquire i2c bus handle
+ *
+ * @return     Instance of FuriHalI2cBus
+ */
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle);
+
+/** Release i2c bus handle
+ *
+ * @param      bus   instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire`
+ */
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle);
+
+/** Perform I2C tx transfer
+ *
+ * @param      instance  I2C_TypeDef instance
+ * @param      address   I2C slave address
+ * @param      data      pointer to data buffer
+ * @param      size      size of data buffer
+ * @param      timeout   timeout in ticks
+ *
+ * @return     true on successful transfer, false otherwise
+ */
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     const uint8_t* data,
     const uint8_t size,
     uint32_t timeout);
 
+/** Perform I2C rx transfer
+ *
+ * @param      instance  I2C_TypeDef instance
+ * @param      address   I2C slave address
+ * @param      data      pointer to data buffer
+ * @param      size      size of data buffer
+ * @param      timeout   timeout in ticks
+ *
+ * @return     true on successful transfer, false otherwise
+ */
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     uint8_t* data,
     const uint8_t size,
     uint32_t timeout);
 
+/** Perform I2C tx and rx transfers
+ *
+ * @param      instance  I2C_TypeDef instance
+ * @param      address   I2C slave address
+ * @param      tx_data   pointer to tx data buffer
+ * @param      tx_size   size of tx data buffer
+ * @param      rx_data   pointer to rx data buffer
+ * @param      rx_size   size of rx data buffer
+ * @param      timeout   timeout in ticks
+ *
+ * @return     true on successful transfer, false otherwise
+ */
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     const uint8_t* tx_data,
     const uint8_t tx_size,
@@ -33,11 +84,6 @@ bool furi_hal_i2c_trx(
     const uint8_t rx_size,
     uint32_t timeout);
 
-#define with_furi_hal_i2c(type, pointer, function_body)       \
-    {                                                         \
-        *pointer = ({ type __fn__ function_body __fn__; })(); \
-    }
-
 #ifdef __cplusplus
 }
 #endif

+ 21 - 15
bootloader/targets/f7/furi-hal/furi-hal-light.c

@@ -7,37 +7,43 @@
 #define LED_CURRENT_WHITE 150
 
 void furi_hal_light_init() {
-    lp5562_reset();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
 
-    lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
-    lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
-    lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
-    lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
+    lp5562_reset(&furi_hal_i2c_handle_power);
 
-    lp5562_set_channel_value(LP5562ChannelRed, 0x00);
-    lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
-    lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
-    lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelRed, LED_CURRENT_RED);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelGreen, LED_CURRENT_GREEN);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelBlue, LED_CURRENT_BLUE);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelWhite, LED_CURRENT_WHITE);
 
-    lp5562_enable();
-    lp5562_configure();
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, 0x00);
+
+    lp5562_enable(&furi_hal_i2c_handle_power);
+    lp5562_configure(&furi_hal_i2c_handle_power);
+
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_light_set(Light light, uint8_t value) {
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     switch(light) {
     case LightRed:
-        lp5562_set_channel_value(LP5562ChannelRed, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value);
         break;
     case LightGreen:
-        lp5562_set_channel_value(LP5562ChannelGreen, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value);
         break;
     case LightBlue:
-        lp5562_set_channel_value(LP5562ChannelBlue, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
         break;
     case LightBacklight:
-        lp5562_set_channel_value(LP5562ChannelWhite, value);
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value);
         break;
     default:
         break;
     }
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }

+ 3 - 0
bootloader/targets/f7/furi-hal/furi-hal-resources.c

@@ -39,3 +39,6 @@ const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
 
 const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
 const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
+
+const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10};
+const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9};

+ 3 - 12
bootloader/targets/f7/furi-hal/furi-hal-resources.h

@@ -8,18 +8,6 @@
 extern "C" {
 #endif
 
-#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
-#define POWER_I2C_SCL_GPIO_Port GPIOA
-#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
-#define POWER_I2C_SDA_GPIO_Port GPIOA
-
-#define POWER_I2C I2C1
-/* Timing register value is computed with the STM32CubeMX Tool,
-  * Fast Mode @100kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define POWER_I2C_TIMINGS 0x10707DBC
-
 /* Input Keys */
 typedef enum {
     InputKeyUp,
@@ -77,6 +65,9 @@ extern const GpioPin gpio_irda_tx;
 extern const GpioPin gpio_usart_tx;
 extern const GpioPin gpio_usart_rx;
 
+extern const GpioPin gpio_i2c_power_sda;
+extern const GpioPin gpio_i2c_power_scl;
+
 #ifdef __cplusplus
 }
 #endif

+ 133 - 0
firmware/targets/f6/furi-hal/furi-hal-i2c-config.c

@@ -0,0 +1,133 @@
+#include "furi-hal-i2c-config.h"
+#include <furi-hal-resources.h>
+#include <furi-hal-version.h>
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Standard Mode @100kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100 0x10707DBC
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Fast Mode @400kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400 0x00602173
+
+osMutexId_t furi_hal_i2c_bus_power_mutex = NULL;
+
+static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if (event == FuriHalI2cBusEventInit) {
+        furi_hal_i2c_bus_power_mutex = osMutexNew(NULL);
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+        bus->current_handle = NULL;
+    } else if (event == FuriHalI2cBusEventDeinit) {
+        osMutexDelete(furi_hal_i2c_bus_power_mutex);
+    } else if (event == FuriHalI2cBusEventLock) {
+        furi_check(osMutexAcquire(furi_hal_i2c_bus_power_mutex, osWaitForever) == osOK);
+    } else if (event == FuriHalI2cBusEventUnlock) {
+        furi_check(osMutexRelease(furi_hal_i2c_bus_power_mutex) == osOK);
+    } else if (event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1);
+    } else if (event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_power = {
+    .i2c=I2C1,
+    .callback=furi_hal_i2c_bus_power_event,
+};
+
+osMutexId_t furi_hal_i2c_bus_external_mutex = NULL;
+
+static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if (event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3);
+    } else if (event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_external = {
+    .i2c=I2C3,
+    .callback=furi_hal_i2c_bus_external_event,
+};
+
+void furi_hal_i2c_bus_handle_power_event(FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event) {
+    if (event == FuriHalI2cBusHandleEventActivate) {
+        hal_gpio_init_ex(&gpio_i2c_power_sda, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C1);
+        hal_gpio_init_ex(&gpio_i2c_power_scl, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C1);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        if (furi_hal_version_get_hw_version() > 10) {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400;
+        } else {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        }
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if (event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_i2c_power_sda, 1);
+        hal_gpio_write(&gpio_i2c_power_scl, 1);
+        hal_gpio_init_ex(&gpio_i2c_power_sda, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(&gpio_i2c_power_scl, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_power = {
+    .bus = &furi_hal_i2c_bus_power,
+    .callback = furi_hal_i2c_bus_handle_power_event,
+};
+
+void furi_hal_i2c_bus_handle_external_event(FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event) {
+    if (event == FuriHalI2cBusHandleEventActivate) {
+        hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+        hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if (event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_ext_pc0, 1);
+        hal_gpio_write(&gpio_ext_pc1, 1);
+        hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_external = {
+    .bus = &furi_hal_i2c_bus_external,
+    .callback = furi_hal_i2c_bus_handle_external_event,
+};

+ 31 - 0
firmware/targets/f6/furi-hal/furi-hal-i2c-config.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include <furi-hal-i2c-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal(power) i2c bus, I2C1, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_power;
+
+/** External i2c bus, I2C3, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_external;
+
+/** Handle for internal(power) i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PA9(SCL) / PA10(SDA), float on release
+ * Params: 400khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_power;
+
+/** Handle for external i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PC0(SCL) / PC1(SDA), float on release
+ * Params: 100khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_external;
+
+#ifdef __cplusplus
+}
+#endif

+ 49 - 0
firmware/targets/f6/furi-hal/furi-hal-i2c-types.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include <stm32wbxx_ll_i2c.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct FuriHalI2cBus FuriHalI2cBus;
+typedef struct FuriHalI2cBusHandle FuriHalI2cBusHandle;
+
+/** FuriHal i2c bus states */
+typedef enum {
+    FuriHalI2cBusEventInit, /**< Bus initialization event, called on system start */
+    FuriHalI2cBusEventDeinit, /**< Bus deinitialization event, called on system stop */
+    FuriHalI2cBusEventLock, /**< Bus lock event, called before activation */
+    FuriHalI2cBusEventUnlock, /**< Bus unlock event, called after deactivation */
+    FuriHalI2cBusEventActivate, /**< Bus activation event, called before handle activation */
+    FuriHalI2cBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */
+} FuriHalI2cBusEvent;
+
+/** FuriHal i2c bus event callback */
+typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEvent event);
+
+/** FuriHal i2c bus */
+struct FuriHalI2cBus {
+    I2C_TypeDef* i2c;
+    FuriHalI2cBusHandle* current_handle;
+    FuriHalI2cBusEventCallback callback;
+};
+
+/** FuriHal i2c handle states */
+typedef enum {
+    FuriHalI2cBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */
+    FuriHalI2cBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */
+} FuriHalI2cBusHandleEvent;
+
+/** FuriHal i2c handle event callback */
+typedef void (*FuriHalI2cBusHandleEventCallback)(FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event);
+
+/** FuriHal i2c handle */
+struct FuriHalI2cBusHandle {
+    FuriHalI2cBus* bus;
+    FuriHalI2cBusHandleEventCallback callback;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 47 - 60
firmware/targets/f6/furi-hal/furi-hal-i2c.c

@@ -8,68 +8,62 @@
 
 #define TAG "FuriHalI2C"
 
-osMutexId_t furi_hal_i2c_mutex = NULL;
-
 void furi_hal_i2c_init() {
-    furi_hal_i2c_mutex = osMutexNew(NULL);
-    furi_check(furi_hal_i2c_mutex);
-
-    LL_I2C_InitTypeDef I2C_InitStruct = {0};
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-
-    LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
-
-    GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
-    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-    I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
-    I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
-    I2C_InitStruct.DigitalFilter = 0;
-    I2C_InitStruct.OwnAddress1 = 0;
-    I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
-    I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
-    if (furi_hal_version_get_hw_version() > 10) {
-        I2C_InitStruct.Timing = POWER_I2C_TIMINGS_400;
-    } else {
-        I2C_InitStruct.Timing = POWER_I2C_TIMINGS_100;
-    }
-    LL_I2C_Init(POWER_I2C, &I2C_InitStruct);
-    LL_I2C_EnableAutoEndMode(POWER_I2C);
-    LL_I2C_SetOwnAddress2(POWER_I2C, 0, LL_I2C_OWNADDRESS2_NOMASK);
-    LL_I2C_DisableOwnAddress2(POWER_I2C);
-    LL_I2C_DisableGeneralCall(POWER_I2C);
-    LL_I2C_EnableClockStretching(POWER_I2C);
+    furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit);
+    furi_hal_i2c_bus_external.callback(&furi_hal_i2c_bus_external, FuriHalI2cBusEventInit);
     FURI_LOG_I(TAG, "Init OK");
 }
 
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) {
+    // Lock bus access
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
+    // Ensuree that no active handle set
+    furi_check(handle->bus->current_handle == NULL);
+    // Set current handle
+    handle->bus->current_handle = handle;
+    // Activate bus
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate);
+    // Activate handle
+    handle->callback(handle, FuriHalI2cBusHandleEventActivate);
+}
+
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) {
+    // Ensure that current handle is our handle
+    furi_check(handle->bus->current_handle == handle);
+    // Deactivate handle
+    handle->callback(handle, FuriHalI2cBusHandleEventDeactivate);
+    // Deactivate bus
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate);
+    // Reset current handle
+    handle->bus->current_handle = NULL;
+    // Unlock bus
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
+}
+
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    furi_check(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_WRITE);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_TXIS(instance)) {
-            LL_I2C_TransmitData8(instance, (*data));
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) {
+            LL_I2C_TransmitData8(handle->bus->i2c, (*data));
             data++;
             size--;
             time_left = timeout;
@@ -83,34 +77,35 @@ bool furi_hal_i2c_tx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    furi_check(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_READ);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_RXNE(instance)) {
-            *data = LL_I2C_ReceiveData8(instance);
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) {
+            *data = LL_I2C_ReceiveData8(handle->bus->i2c);
             data++;
             size--;
             time_left = timeout;
@@ -124,31 +119,23 @@ bool furi_hal_i2c_rx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* tx_data,
     uint8_t tx_size,
     uint8_t* rx_data,
     uint8_t rx_size,
     uint32_t timeout) {
-    if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
-       furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
+    if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) &&
+       furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) {
         return true;
     } else {
         return false;
     }
 }
-
-void furi_hal_i2c_lock() {
-    furi_check(osMutexAcquire(furi_hal_i2c_mutex, osWaitForever) == osOK);
-}
-
-void furi_hal_i2c_unlock() {
-    furi_check(osMutexRelease(furi_hal_i2c_mutex) == osOK);
-}

+ 30 - 27
firmware/targets/f6/furi-hal/furi-hal-light.c

@@ -1,46 +1,49 @@
 #include <furi-hal-light.h>
 #include <lp5562.h>
 
-#define TAG "FuriHalLight"
-
-#define LED_CURRENT_RED     50
-#define LED_CURRENT_GREEN   50
-#define LED_CURRENT_BLUE    50
-#define LED_CURRENT_WHITE   150
+#define LED_CURRENT_RED 50
+#define LED_CURRENT_GREEN 50
+#define LED_CURRENT_BLUE 50
+#define LED_CURRENT_WHITE 150
 
 void furi_hal_light_init() {
-    lp5562_reset();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+
+    lp5562_reset(&furi_hal_i2c_handle_power);
+
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelRed, LED_CURRENT_RED);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelGreen, LED_CURRENT_GREEN);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelBlue, LED_CURRENT_BLUE);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelWhite, LED_CURRENT_WHITE);
 
-    lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
-    lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
-    lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
-    lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, 0x00);
 
-    lp5562_set_channel_value(LP5562ChannelRed, 0x00);
-    lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
-    lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
-    lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
+    lp5562_enable(&furi_hal_i2c_handle_power);
+    lp5562_configure(&furi_hal_i2c_handle_power);
 
-    lp5562_enable();
-    lp5562_configure();
-    FURI_LOG_I(TAG, "Init OK");
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_light_set(Light light, uint8_t value) {
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     switch(light) {
-        case LightRed:
-            lp5562_set_channel_value(LP5562ChannelRed, value);
+    case LightRed:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value);
         break;
-        case LightGreen:
-            lp5562_set_channel_value(LP5562ChannelGreen, value);
+    case LightGreen:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value);
         break;
-        case LightBlue:
-            lp5562_set_channel_value(LP5562ChannelBlue, value);
+    case LightBlue:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
         break;
-        case LightBacklight:
-            lp5562_set_channel_value(LP5562ChannelWhite, value);
+    case LightBacklight:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value);
         break;
-        default:
+    default:
         break;
     }
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }

+ 94 - 37
firmware/targets/f6/furi-hal/furi-hal-power.c

@@ -74,8 +74,12 @@ void HAL_RCC_CSSCallback(void) {
 void furi_hal_power_init() {
     LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
     LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
-    bq27220_init(&cedv);
-    bq25896_init();
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq27220_init(&furi_hal_i2c_handle_power, &cedv);
+    bq25896_init(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
     FURI_LOG_I(TAG, "Init OK");
 }
 
@@ -161,19 +165,30 @@ void furi_hal_power_sleep() {
 
 
 uint8_t furi_hal_power_get_pct() {
-    return bq27220_get_state_of_charge();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 uint8_t furi_hal_power_get_bat_health_pct() {
-    return bq27220_get_state_of_health();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 bool furi_hal_power_is_charging() {
-    return bq25896_is_charging();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 void furi_hal_power_off() {
-    bq25896_poweroff();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq25896_poweroff(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_power_reset() {
@@ -181,66 +196,102 @@ void furi_hal_power_reset() {
 }
 
 void furi_hal_power_enable_otg() {
-    bq25896_enable_otg();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq25896_enable_otg(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_power_disable_otg() {
-    bq25896_disable_otg();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq25896_disable_otg(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 bool furi_hal_power_is_otg_enabled() {
-    return bq25896_is_otg_enabled();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 uint32_t furi_hal_power_get_battery_remaining_capacity() {
-    return bq27220_get_remaining_capacity();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 uint32_t furi_hal_power_get_battery_full_capacity() {
-    return bq27220_get_full_charge_capacity();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
+    float ret = 0.0f;
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     if (ic == FuriHalPowerICCharger) {
-        return (float)bq25896_get_vbat_voltage() / 1000.0f;
+        ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
     } else if (ic == FuriHalPowerICFuelGauge) {
-        return (float)bq27220_get_voltage() / 1000.0f;
-    } else {
-        return 0.0f;
+        ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
     }
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
+    return ret;
 }
 
 float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
+    float ret = 0.0f;
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     if (ic == FuriHalPowerICCharger) {
-        return (float)bq25896_get_vbat_current() / 1000.0f;
+        ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f;
     } else if (ic == FuriHalPowerICFuelGauge) {
-        return (float)bq27220_get_current() / 1000.0f;
-    } else {
-        return 0.0f;
-    }
+        ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f;
+    } 
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
+    return ret;
 }
 
-float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
+static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
+    float ret = 0.0f;
+
     if (ic == FuriHalPowerICCharger) {
         // Linear approximation, +/- 5 C
-        return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
+        ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)/1000) / 0.6f;
     } else if (ic == FuriHalPowerICFuelGauge) {
-        return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
-    } else {
-        return 0.0f;
+        ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f;
     }
-    
+
+    return ret;
+}
+
+float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    float ret = furi_hal_power_get_battery_temperature_internal(ic);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
+    return ret;
 }
 
 float furi_hal_power_get_usb_voltage(){
-    return (float)bq25896_get_vbus_voltage() / 1000.0f;
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 void furi_hal_power_dump_state() {
     BatteryStatus battery_status;
     OperationStatus operation_status;
-    if (bq27220_get_battery_status(&battery_status) == BQ27220_ERROR
-        || bq27220_get_operation_status(&operation_status) == BQ27220_ERROR) {
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+
+    if (bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR
+        || bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) == BQ27220_ERROR) {
         printf("Failed to get bq27220 status. Communication error.\r\n");
     } else {
         printf(
@@ -266,21 +317,23 @@ void furi_hal_power_dump_state() {
         // Voltage and current info
         printf(
             "bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n",
-            bq27220_get_full_charge_capacity(), bq27220_get_design_capacity(), bq27220_get_remaining_capacity(),
-            bq27220_get_state_of_charge(), bq27220_get_state_of_health()
+            bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power), bq27220_get_design_capacity(&furi_hal_i2c_handle_power), bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power),
+            bq27220_get_state_of_charge(&furi_hal_i2c_handle_power), bq27220_get_state_of_health(&furi_hal_i2c_handle_power)
         );
         printf(
             "bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
-            bq27220_get_voltage(), bq27220_get_current(), (int)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge)
+            bq27220_get_voltage(&furi_hal_i2c_handle_power), bq27220_get_current(&furi_hal_i2c_handle_power), (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge)
         );
     }
 
     printf(
         "bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n",
-        bq25896_get_vbus_voltage(), bq25896_get_vsys_voltage(),
-        bq25896_get_vbat_voltage(), bq25896_get_vbat_current(),
-        bq25896_get_ntc_mpct()
+        bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power), bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power),
+        bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power), bq25896_get_vbat_current(&furi_hal_i2c_handle_power),
+        bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)
     );
+
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_power_enable_external_3_3v(){
@@ -298,7 +351,9 @@ void furi_hal_power_suppress_charge_enter() {
     xTaskResumeAll();
 
     if (disable_charging) {
-        bq25896_disable_charging();
+        furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+        bq25896_disable_charging(&furi_hal_i2c_handle_power);
+        furi_hal_i2c_release(&furi_hal_i2c_handle_power);
     }
 }
 
@@ -309,6 +364,8 @@ void furi_hal_power_suppress_charge_exit() {
     xTaskResumeAll();
 
     if (enable_charging) {
-        bq25896_enable_charging();
+        furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+        bq25896_enable_charging(&furi_hal_i2c_handle_power);
+        furi_hal_i2c_release(&furi_hal_i2c_handle_power);
     }
 }

+ 3 - 0
firmware/targets/f6/furi-hal/furi-hal-resources.c

@@ -54,3 +54,6 @@ const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
 
 const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
 const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
+
+const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10};
+const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9};

+ 3 - 18
firmware/targets/f6/furi-hal/furi-hal-resources.h

@@ -10,24 +10,6 @@
 extern "C" {
 #endif
 
-#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
-#define POWER_I2C_SCL_GPIO_Port GPIOA
-#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
-#define POWER_I2C_SDA_GPIO_Port GPIOA
-
-#define POWER_I2C I2C1
-/** Timing register value is computed with the STM32CubeMX Tool,
-  * Standard Mode @100kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define POWER_I2C_TIMINGS_100 0x10707DBC
-
-/** Timing register value is computed with the STM32CubeMX Tool,
-  * Fast Mode @400kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define POWER_I2C_TIMINGS_400 0x00602173
-
 /* Input Related Constants */
 #define INPUT_DEBOUNCE_TICKS 20
 
@@ -98,6 +80,9 @@ extern const GpioPin gpio_irda_tx;
 
 extern const GpioPin gpio_usart_tx;
 extern const GpioPin gpio_usart_rx;
+extern const GpioPin gpio_i2c_power_sda;
+extern const GpioPin gpio_i2c_power_scl;
+
 
 #ifdef __cplusplus
 }

+ 1 - 1
firmware/targets/f6/furi-hal/furi-hal-vcp.c

@@ -280,7 +280,7 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) {
 
 static void vcp_on_cdc_rx(void* context) {
     uint32_t ret = osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtRx);
-    furi_assert((ret & osFlagsError) == 0);
+    furi_check((ret & osFlagsError) == 0);
 }
 
 static void vcp_on_cdc_tx_complete(void* context) {

+ 133 - 0
firmware/targets/f7/furi-hal/furi-hal-i2c-config.c

@@ -0,0 +1,133 @@
+#include "furi-hal-i2c-config.h"
+#include <furi-hal-resources.h>
+#include <furi-hal-version.h>
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Standard Mode @100kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100 0x10707DBC
+
+/** Timing register value is computed with the STM32CubeMX Tool,
+  * Fast Mode @400kHz with I2CCLK = 64 MHz,
+  * rise time = 0ns, fall time = 0ns
+  */
+#define FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400 0x00602173
+
+osMutexId_t furi_hal_i2c_bus_power_mutex = NULL;
+
+static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if (event == FuriHalI2cBusEventInit) {
+        furi_hal_i2c_bus_power_mutex = osMutexNew(NULL);
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+        bus->current_handle = NULL;
+    } else if (event == FuriHalI2cBusEventDeinit) {
+        osMutexDelete(furi_hal_i2c_bus_power_mutex);
+    } else if (event == FuriHalI2cBusEventLock) {
+        furi_check(osMutexAcquire(furi_hal_i2c_bus_power_mutex, osWaitForever) == osOK);
+    } else if (event == FuriHalI2cBusEventUnlock) {
+        furi_check(osMutexRelease(furi_hal_i2c_bus_power_mutex) == osOK);
+    } else if (event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1);
+    } else if (event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_power = {
+    .i2c=I2C1,
+    .callback=furi_hal_i2c_bus_power_event,
+};
+
+osMutexId_t furi_hal_i2c_bus_external_mutex = NULL;
+
+static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) {
+    if (event == FuriHalI2cBusEventActivate) {
+        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3);
+        LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1);
+        LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3);
+    } else if (event == FuriHalI2cBusEventDeactivate) {
+        LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3);
+    }
+}
+
+FuriHalI2cBus furi_hal_i2c_bus_external = {
+    .i2c=I2C3,
+    .callback=furi_hal_i2c_bus_external_event,
+};
+
+void furi_hal_i2c_bus_handle_power_event(FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event) {
+    if (event == FuriHalI2cBusHandleEventActivate) {
+        hal_gpio_init_ex(&gpio_i2c_power_sda, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C1);
+        hal_gpio_init_ex(&gpio_i2c_power_scl, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C1);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        if (furi_hal_version_get_hw_version() > 10) {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_400;
+        } else {
+            I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        }
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if (event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_i2c_power_sda, 1);
+        hal_gpio_write(&gpio_i2c_power_scl, 1);
+        hal_gpio_init_ex(&gpio_i2c_power_sda, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(&gpio_i2c_power_scl, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_power = {
+    .bus = &furi_hal_i2c_bus_power,
+    .callback = furi_hal_i2c_bus_handle_power_event,
+};
+
+void furi_hal_i2c_bus_handle_external_event(FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event) {
+    if (event == FuriHalI2cBusHandleEventActivate) {
+        hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+        hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3);
+
+        LL_I2C_InitTypeDef I2C_InitStruct = {0};
+        I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
+        I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
+        I2C_InitStruct.DigitalFilter = 0;
+        I2C_InitStruct.OwnAddress1 = 0;
+        I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
+        I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
+        I2C_InitStruct.Timing = FURI_HAL_I2C_CONFIG_POWER_I2C_TIMINGS_100;
+        LL_I2C_Init(handle->bus->i2c, &I2C_InitStruct);
+
+        LL_I2C_EnableAutoEndMode(handle->bus->i2c);
+        LL_I2C_SetOwnAddress2(handle->bus->i2c, 0, LL_I2C_OWNADDRESS2_NOMASK);
+        LL_I2C_DisableOwnAddress2(handle->bus->i2c);
+        LL_I2C_DisableGeneralCall(handle->bus->i2c);
+        LL_I2C_EnableClockStretching(handle->bus->i2c);
+        LL_I2C_Enable(handle->bus->i2c);
+    } else if (event == FuriHalI2cBusHandleEventDeactivate) {
+        LL_I2C_Disable(handle->bus->i2c);
+        hal_gpio_write(&gpio_ext_pc0, 1);
+        hal_gpio_write(&gpio_ext_pc1, 1);
+        hal_gpio_init_ex(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+        hal_gpio_init_ex(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow, GpioAltFnUnused);
+    }
+}
+
+FuriHalI2cBusHandle furi_hal_i2c_handle_external = {
+    .bus = &furi_hal_i2c_bus_external,
+    .callback = furi_hal_i2c_bus_handle_external_event,
+};

+ 31 - 0
firmware/targets/f7/furi-hal/furi-hal-i2c-config.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include <furi-hal-i2c-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal(power) i2c bus, I2C1, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_power;
+
+/** External i2c bus, I2C3, under reset when not used */
+extern FuriHalI2cBus furi_hal_i2c_bus_external;
+
+/** Handle for internal(power) i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PA9(SCL) / PA10(SDA), float on release
+ * Params: 400khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_power;
+
+/** Handle for external i2c bus
+ * Bus: furi_hal_i2c_bus_external
+ * Pins: PC0(SCL) / PC1(SDA), float on release
+ * Params: 100khz
+ */
+extern FuriHalI2cBusHandle furi_hal_i2c_handle_external;
+
+#ifdef __cplusplus
+}
+#endif

+ 49 - 0
firmware/targets/f7/furi-hal/furi-hal-i2c-types.h

@@ -0,0 +1,49 @@
+#pragma once
+
+#include <stm32wbxx_ll_i2c.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct FuriHalI2cBus FuriHalI2cBus;
+typedef struct FuriHalI2cBusHandle FuriHalI2cBusHandle;
+
+/** FuriHal i2c bus states */
+typedef enum {
+    FuriHalI2cBusEventInit, /**< Bus initialization event, called on system start */
+    FuriHalI2cBusEventDeinit, /**< Bus deinitialization event, called on system stop */
+    FuriHalI2cBusEventLock, /**< Bus lock event, called before activation */
+    FuriHalI2cBusEventUnlock, /**< Bus unlock event, called after deactivation */
+    FuriHalI2cBusEventActivate, /**< Bus activation event, called before handle activation */
+    FuriHalI2cBusEventDeactivate, /**< Bus deactivation event, called after handle deactivation  */
+} FuriHalI2cBusEvent;
+
+/** FuriHal i2c bus event callback */
+typedef void (*FuriHalI2cBusEventCallback)(FuriHalI2cBus* bus, FuriHalI2cBusEvent event);
+
+/** FuriHal i2c bus */
+struct FuriHalI2cBus {
+    I2C_TypeDef* i2c;
+    FuriHalI2cBusHandle* current_handle;
+    FuriHalI2cBusEventCallback callback;
+};
+
+/** FuriHal i2c handle states */
+typedef enum {
+    FuriHalI2cBusHandleEventActivate, /**< Handle activate: connect gpio and apply bus config */
+    FuriHalI2cBusHandleEventDeactivate, /**< Handle deactivate: disconnect gpio and reset bus config */
+} FuriHalI2cBusHandleEvent;
+
+/** FuriHal i2c handle event callback */
+typedef void (*FuriHalI2cBusHandleEventCallback)(FuriHalI2cBusHandle* handle, FuriHalI2cBusHandleEvent event);
+
+/** FuriHal i2c handle */
+struct FuriHalI2cBusHandle {
+    FuriHalI2cBus* bus;
+    FuriHalI2cBusHandleEventCallback callback;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 47 - 60
firmware/targets/f7/furi-hal/furi-hal-i2c.c

@@ -8,68 +8,62 @@
 
 #define TAG "FuriHalI2C"
 
-osMutexId_t furi_hal_i2c_mutex = NULL;
-
 void furi_hal_i2c_init() {
-    furi_hal_i2c_mutex = osMutexNew(NULL);
-    furi_check(furi_hal_i2c_mutex);
-
-    LL_I2C_InitTypeDef I2C_InitStruct = {0};
-    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
-
-    LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
-
-    GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
-    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
-    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
-    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
-    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
-    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-    I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
-    I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
-    I2C_InitStruct.DigitalFilter = 0;
-    I2C_InitStruct.OwnAddress1 = 0;
-    I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
-    I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
-    if (furi_hal_version_get_hw_version() > 10) {
-        I2C_InitStruct.Timing = POWER_I2C_TIMINGS_400;
-    } else {
-        I2C_InitStruct.Timing = POWER_I2C_TIMINGS_100;
-    }
-    LL_I2C_Init(POWER_I2C, &I2C_InitStruct);
-    LL_I2C_EnableAutoEndMode(POWER_I2C);
-    LL_I2C_SetOwnAddress2(POWER_I2C, 0, LL_I2C_OWNADDRESS2_NOMASK);
-    LL_I2C_DisableOwnAddress2(POWER_I2C);
-    LL_I2C_DisableGeneralCall(POWER_I2C);
-    LL_I2C_EnableClockStretching(POWER_I2C);
+    furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit);
+    furi_hal_i2c_bus_external.callback(&furi_hal_i2c_bus_external, FuriHalI2cBusEventInit);
     FURI_LOG_I(TAG, "Init OK");
 }
 
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) {
+    // Lock bus access
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
+    // Ensuree that no active handle set
+    furi_check(handle->bus->current_handle == NULL);
+    // Set current handle
+    handle->bus->current_handle = handle;
+    // Activate bus
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate);
+    // Activate handle
+    handle->callback(handle, FuriHalI2cBusHandleEventActivate);
+}
+
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) {
+    // Ensure that current handle is our handle
+    furi_check(handle->bus->current_handle == handle);
+    // Deactivate handle
+    handle->callback(handle, FuriHalI2cBusHandleEventDeactivate);
+    // Deactivate bus
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate);
+    // Reset current handle
+    handle->bus->current_handle = NULL;
+    // Unlock bus
+    handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
+}
+
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    furi_check(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_WRITE);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_TXIS(instance)) {
-            LL_I2C_TransmitData8(instance, (*data));
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) {
+            LL_I2C_TransmitData8(handle->bus->i2c, (*data));
             data++;
             size--;
             time_left = timeout;
@@ -83,34 +77,35 @@ bool furi_hal_i2c_tx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     uint8_t* data,
     uint8_t size,
     uint32_t timeout) {
+    furi_check(handle->bus->current_handle == handle);
     uint32_t time_left = timeout;
     bool ret = true;
 
-    while(LL_I2C_IsActiveFlag_BUSY(instance))
+    while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c))
         ;
 
     LL_I2C_HandleTransfer(
-        instance,
+        handle->bus->i2c,
         address,
         LL_I2C_ADDRSLAVE_7BIT,
         size,
         LL_I2C_MODE_AUTOEND,
         LL_I2C_GENERATE_START_READ);
 
-    while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
-        if(LL_I2C_IsActiveFlag_RXNE(instance)) {
-            *data = LL_I2C_ReceiveData8(instance);
+    while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) {
+        if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) {
+            *data = LL_I2C_ReceiveData8(handle->bus->i2c);
             data++;
             size--;
             time_left = timeout;
@@ -124,31 +119,23 @@ bool furi_hal_i2c_rx(
         }
     }
 
-    LL_I2C_ClearFlag_STOP(instance);
+    LL_I2C_ClearFlag_STOP(handle->bus->i2c);
 
     return ret;
 }
 
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     uint8_t address,
     const uint8_t* tx_data,
     uint8_t tx_size,
     uint8_t* rx_data,
     uint8_t rx_size,
     uint32_t timeout) {
-    if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
-       furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
+    if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) &&
+       furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) {
         return true;
     } else {
         return false;
     }
 }
-
-void furi_hal_i2c_lock() {
-    furi_check(osMutexAcquire(furi_hal_i2c_mutex, osWaitForever) == osOK);
-}
-
-void furi_hal_i2c_unlock() {
-    furi_check(osMutexRelease(furi_hal_i2c_mutex) == osOK);
-}

+ 30 - 27
firmware/targets/f7/furi-hal/furi-hal-light.c

@@ -1,46 +1,49 @@
 #include <furi-hal-light.h>
 #include <lp5562.h>
 
-#define TAG "FuriHalLight"
-
-#define LED_CURRENT_RED     50
-#define LED_CURRENT_GREEN   50
-#define LED_CURRENT_BLUE    50
-#define LED_CURRENT_WHITE   150
+#define LED_CURRENT_RED 50
+#define LED_CURRENT_GREEN 50
+#define LED_CURRENT_BLUE 50
+#define LED_CURRENT_WHITE 150
 
 void furi_hal_light_init() {
-    lp5562_reset();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+
+    lp5562_reset(&furi_hal_i2c_handle_power);
+
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelRed, LED_CURRENT_RED);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelGreen, LED_CURRENT_GREEN);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelBlue, LED_CURRENT_BLUE);
+    lp5562_set_channel_current(&furi_hal_i2c_handle_power, LP5562ChannelWhite, LED_CURRENT_WHITE);
 
-    lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
-    lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
-    lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
-    lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, 0x00);
+    lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, 0x00);
 
-    lp5562_set_channel_value(LP5562ChannelRed, 0x00);
-    lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
-    lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
-    lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
+    lp5562_enable(&furi_hal_i2c_handle_power);
+    lp5562_configure(&furi_hal_i2c_handle_power);
 
-    lp5562_enable();
-    lp5562_configure();
-    FURI_LOG_I(TAG, "Init OK");
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_light_set(Light light, uint8_t value) {
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     switch(light) {
-        case LightRed:
-            lp5562_set_channel_value(LP5562ChannelRed, value);
+    case LightRed:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value);
         break;
-        case LightGreen:
-            lp5562_set_channel_value(LP5562ChannelGreen, value);
+    case LightGreen:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value);
         break;
-        case LightBlue:
-            lp5562_set_channel_value(LP5562ChannelBlue, value);
+    case LightBlue:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value);
         break;
-        case LightBacklight:
-            lp5562_set_channel_value(LP5562ChannelWhite, value);
+    case LightBacklight:
+        lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value);
         break;
-        default:
+    default:
         break;
     }
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }

+ 94 - 37
firmware/targets/f7/furi-hal/furi-hal-power.c

@@ -74,8 +74,12 @@ void HAL_RCC_CSSCallback(void) {
 void furi_hal_power_init() {
     LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
     LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
-    bq27220_init(&cedv);
-    bq25896_init();
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq27220_init(&furi_hal_i2c_handle_power, &cedv);
+    bq25896_init(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
     FURI_LOG_I(TAG, "Init OK");
 }
 
@@ -161,19 +165,30 @@ void furi_hal_power_sleep() {
 
 
 uint8_t furi_hal_power_get_pct() {
-    return bq27220_get_state_of_charge();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 uint8_t furi_hal_power_get_bat_health_pct() {
-    return bq27220_get_state_of_health();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 bool furi_hal_power_is_charging() {
-    return bq25896_is_charging();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 void furi_hal_power_off() {
-    bq25896_poweroff();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq25896_poweroff(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_power_reset() {
@@ -181,66 +196,102 @@ void furi_hal_power_reset() {
 }
 
 void furi_hal_power_enable_otg() {
-    bq25896_enable_otg();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq25896_enable_otg(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_power_disable_otg() {
-    bq25896_disable_otg();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bq25896_disable_otg(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 bool furi_hal_power_is_otg_enabled() {
-    return bq25896_is_otg_enabled();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 uint32_t furi_hal_power_get_battery_remaining_capacity() {
-    return bq27220_get_remaining_capacity();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 uint32_t furi_hal_power_get_battery_full_capacity() {
-    return bq27220_get_full_charge_capacity();
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
+    float ret = 0.0f;
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     if (ic == FuriHalPowerICCharger) {
-        return (float)bq25896_get_vbat_voltage() / 1000.0f;
+        ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
     } else if (ic == FuriHalPowerICFuelGauge) {
-        return (float)bq27220_get_voltage() / 1000.0f;
-    } else {
-        return 0.0f;
+        ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
     }
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
+    return ret;
 }
 
 float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
+    float ret = 0.0f;
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     if (ic == FuriHalPowerICCharger) {
-        return (float)bq25896_get_vbat_current() / 1000.0f;
+        ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f;
     } else if (ic == FuriHalPowerICFuelGauge) {
-        return (float)bq27220_get_current() / 1000.0f;
-    } else {
-        return 0.0f;
-    }
+        ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f;
+    } 
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
+    return ret;
 }
 
-float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
+static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
+    float ret = 0.0f;
+
     if (ic == FuriHalPowerICCharger) {
         // Linear approximation, +/- 5 C
-        return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
+        ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)/1000) / 0.6f;
     } else if (ic == FuriHalPowerICFuelGauge) {
-        return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
-    } else {
-        return 0.0f;
+        ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f;
     }
-    
+
+    return ret;
+}
+
+float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    float ret = furi_hal_power_get_battery_temperature_internal(ic);
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+
+    return ret;
 }
 
 float furi_hal_power_get_usb_voltage(){
-    return (float)bq25896_get_vbus_voltage() / 1000.0f;
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+    float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
+    return ret;
 }
 
 void furi_hal_power_dump_state() {
     BatteryStatus battery_status;
     OperationStatus operation_status;
-    if (bq27220_get_battery_status(&battery_status) == BQ27220_ERROR
-        || bq27220_get_operation_status(&operation_status) == BQ27220_ERROR) {
+
+    furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+
+    if (bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR
+        || bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) == BQ27220_ERROR) {
         printf("Failed to get bq27220 status. Communication error.\r\n");
     } else {
         printf(
@@ -266,21 +317,23 @@ void furi_hal_power_dump_state() {
         // Voltage and current info
         printf(
             "bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n",
-            bq27220_get_full_charge_capacity(), bq27220_get_design_capacity(), bq27220_get_remaining_capacity(),
-            bq27220_get_state_of_charge(), bq27220_get_state_of_health()
+            bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power), bq27220_get_design_capacity(&furi_hal_i2c_handle_power), bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power),
+            bq27220_get_state_of_charge(&furi_hal_i2c_handle_power), bq27220_get_state_of_health(&furi_hal_i2c_handle_power)
         );
         printf(
             "bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
-            bq27220_get_voltage(), bq27220_get_current(), (int)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge)
+            bq27220_get_voltage(&furi_hal_i2c_handle_power), bq27220_get_current(&furi_hal_i2c_handle_power), (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge)
         );
     }
 
     printf(
         "bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n",
-        bq25896_get_vbus_voltage(), bq25896_get_vsys_voltage(),
-        bq25896_get_vbat_voltage(), bq25896_get_vbat_current(),
-        bq25896_get_ntc_mpct()
+        bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power), bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power),
+        bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power), bq25896_get_vbat_current(&furi_hal_i2c_handle_power),
+        bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)
     );
+
+    furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 }
 
 void furi_hal_power_enable_external_3_3v(){
@@ -298,7 +351,9 @@ void furi_hal_power_suppress_charge_enter() {
     xTaskResumeAll();
 
     if (disable_charging) {
-        bq25896_disable_charging();
+        furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+        bq25896_disable_charging(&furi_hal_i2c_handle_power);
+        furi_hal_i2c_release(&furi_hal_i2c_handle_power);
     }
 }
 
@@ -309,6 +364,8 @@ void furi_hal_power_suppress_charge_exit() {
     xTaskResumeAll();
 
     if (enable_charging) {
-        bq25896_enable_charging();
+        furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
+        bq25896_enable_charging(&furi_hal_i2c_handle_power);
+        furi_hal_i2c_release(&furi_hal_i2c_handle_power);
     }
 }

+ 3 - 0
firmware/targets/f7/furi-hal/furi-hal-resources.c

@@ -55,3 +55,6 @@ const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
 
 const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
 const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
+
+const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10};
+const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9};

+ 3 - 18
firmware/targets/f7/furi-hal/furi-hal-resources.h

@@ -10,24 +10,6 @@
 extern "C" {
 #endif
 
-#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
-#define POWER_I2C_SCL_GPIO_Port GPIOA
-#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
-#define POWER_I2C_SDA_GPIO_Port GPIOA
-
-#define POWER_I2C I2C1
-/** Timing register value is computed with the STM32CubeMX Tool,
-  * Standard Mode @100kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define POWER_I2C_TIMINGS_100 0x10707DBC
-
-/** Timing register value is computed with the STM32CubeMX Tool,
-  * Fast Mode @400kHz with I2CCLK = 64 MHz,
-  * rise time = 0ns, fall time = 0ns
-  */
-#define POWER_I2C_TIMINGS_400 0x00602173
-
 /* Input Related Constants */
 #define INPUT_DEBOUNCE_TICKS 20
 
@@ -99,6 +81,9 @@ extern const GpioPin gpio_irda_tx;
 
 extern const GpioPin gpio_usart_tx;
 extern const GpioPin gpio_usart_rx;
+extern const GpioPin gpio_i2c_power_sda;
+extern const GpioPin gpio_i2c_power_scl;
+
 
 #ifdef __cplusplus
 }

+ 0 - 108
firmware/targets/f7/furi-hal/furi-hal-spi.h

@@ -1,108 +0,0 @@
-#pragma once
-#include "main.h"
-#include "furi-hal-spi-config.h"
-#include <furi-hal-gpio.h>
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Init SPI API
- */
-void furi_hal_spi_init();
-
-/* Bus Level API */
-
-/** Lock SPI bus
- * Takes bus mutex, if used
- */
-void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus);
-
-/** Unlock SPI bus
- * Releases BUS mutex, if used
- */
-void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus);
-
-/**
- * Configure SPI bus
- * @param bus - spi bus handler
- * @param config - spi configuration structure
- */
-void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config);
-
-/** SPI Receive
- * @param bus - spi bus handler
- * @param buffer - receive buffer
- * @param size - transaction size
- * @param timeout - bus operation timeout in ms
- */
-bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
-
-/** SPI Transmit
- * @param bus - spi bus handler
- * @param buffer - transmit buffer
- * @param size - transaction size
- * @param timeout - bus operation timeout in ms
- */
-bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
-
-/** SPI Transmit and Receive
- * @param bus - spi bus handlere
- * @param tx_buffer - device handle
- * @param rx_buffer - device handle
- * @param size - transaction size
- * @param timeout - bus operation timeout in ms
- */
-bool furi_hal_spi_bus_trx(const FuriHalSpiBus* bus, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
-
-/* Device Level API */
-
-/** Reconfigure SPI bus for device
- * @param device - device description
- */
-void furi_hal_spi_device_configure(const FuriHalSpiDevice* device);
-
-/** Get Device handle
- * And lock access to the corresponding SPI BUS
- * @param device_id - device identifier
- * @return device handle
- */
-const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id);
-
-/** Return Device handle
- * And unlock access to the corresponding SPI BUS
- * @param device - device handle
- */
-void furi_hal_spi_device_return(const FuriHalSpiDevice* device);
-
-/** SPI Recieve
- * @param device - device handle
- * @param buffer - receive buffer
- * @param size - transaction size
- * @param timeout - bus operation timeout in ms
- */
-bool furi_hal_spi_device_rx(const FuriHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout);
-
-/** SPI Transmit
- * @param device - device handle
- * @param buffer - transmit buffer
- * @param size - transaction size
- * @param timeout - bus operation timeout in ms
- */
-bool furi_hal_spi_device_tx(const FuriHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout);
-
-/** SPI Transmit and Receive
- * @param device - device handle
- * @param tx_buffer - device handle
- * @param rx_buffer - device handle
- * @param size - transaction size
- * @param timeout - bus operation timeout in ms
- */
-bool furi_hal_spi_device_trx(const FuriHalSpiDevice* device, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
-
-
-#ifdef __cplusplus
-}
-#endif

+ 1 - 1
firmware/targets/f7/furi-hal/furi-hal-vcp.c

@@ -280,7 +280,7 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) {
 
 static void vcp_on_cdc_rx(void* context) {
     uint32_t ret = osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtRx);
-    furi_assert((ret & osFlagsError) == 0);
+    furi_check((ret & osFlagsError) == 0);
 }
 
 static void vcp_on_cdc_tx_complete(void* context) {

+ 19 - 31
firmware/targets/furi-hal-include/furi-hal-i2c.h

@@ -7,7 +7,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
-#include <furi-hal-resources.h>
+#include <furi-hal-i2c-config.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -17,18 +17,30 @@ extern "C" {
  */
 void furi_hal_i2c_init();
 
+/** Acquire i2c bus handle
+ *
+ * @return     Instance of FuriHalI2cBus
+ */
+void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle);
+
+/** Release i2c bus handle
+ *
+ * @param      bus   instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire`
+ */
+void furi_hal_i2c_release(FuriHalI2cBusHandle* handle);
+
 /** Perform I2C tx transfer
  *
  * @param      instance  I2C_TypeDef instance
  * @param      address   I2C slave address
  * @param      data      pointer to data buffer
  * @param      size      size of data buffer
- * @param      timeout   timeout in CPU ticks
+ * @param      timeout   timeout in ticks
  *
  * @return     true on successful transfer, false otherwise
  */
 bool furi_hal_i2c_tx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     const uint8_t* data,
     const uint8_t size,
@@ -40,12 +52,12 @@ bool furi_hal_i2c_tx(
  * @param      address   I2C slave address
  * @param      data      pointer to data buffer
  * @param      size      size of data buffer
- * @param      timeout   timeout in CPU ticks
+ * @param      timeout   timeout in ticks
  *
  * @return     true on successful transfer, false otherwise
  */
 bool furi_hal_i2c_rx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     uint8_t* data,
     const uint8_t size,
@@ -59,12 +71,12 @@ bool furi_hal_i2c_rx(
  * @param      tx_size   size of tx data buffer
  * @param      rx_data   pointer to rx data buffer
  * @param      rx_size   size of rx data buffer
- * @param      timeout   timeout in CPU ticks
+ * @param      timeout   timeout in ticks
  *
  * @return     true on successful transfer, false otherwise
  */
 bool furi_hal_i2c_trx(
-    I2C_TypeDef* instance,
+    FuriHalI2cBusHandle* handle,
     const uint8_t address,
     const uint8_t* tx_data,
     const uint8_t tx_size,
@@ -72,30 +84,6 @@ bool furi_hal_i2c_trx(
     const uint8_t rx_size,
     uint32_t timeout);
 
-/** Acquire I2C mutex
- */
-void furi_hal_i2c_lock();
-
-/** Release I2C mutex
- */
-void furi_hal_i2c_unlock();
-
-/** With clause for I2C peripheral
- *
- * @param      type           type of function_body
- * @param      pointer        pointer to return of function_body
- * @param      function_body  a (){} lambda declaration, executed with I2C mutex
- *                            acquired
- *
- * @return     Nothing
- */
-#define with_furi_hal_i2c(type, pointer, function_body)        \
-    {                                                         \
-        furi_hal_i2c_lock();                                   \
-        *pointer = ({ type __fn__ function_body __fn__; })(); \
-        furi_hal_i2c_unlock();                                 \
-    }
-
 #ifdef __cplusplus
 }
 #endif

+ 1 - 2
firmware/targets/f6/furi-hal/furi-hal-spi.h → firmware/targets/furi-hal-include/furi-hal-spi.h

@@ -25,8 +25,7 @@ void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus);
  */
 void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus);
 
-/**
- * Configure SPI bus
+/** Configure SPI bus
  * @param bus - spi bus handler
  * @param config - spi configuration structure
  */

+ 37 - 50
lib/drivers/bq25896.c

@@ -1,7 +1,6 @@
 #include "bq25896.h"
 #include "bq25896_reg.h"
 
-#include <furi-hal-i2c.h>
 #include <stddef.h>
 
 uint8_t bit_reverse(uint8_t b) {
@@ -11,29 +10,17 @@ uint8_t bit_reverse(uint8_t b) {
     return b;
 }
 
-bool bq25896_read(uint8_t address, uint8_t* data, size_t size) {
-    bool ret;
-    with_furi_hal_i2c(
-        bool, &ret, () {
-            return furi_hal_i2c_trx(
-                POWER_I2C, BQ25896_ADDRESS, &address, 1, data, size, BQ25896_I2C_TIMEOUT);
-        });
-    return ret;
+bool bq25896_read(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data, size_t size) {
+    return furi_hal_i2c_trx(handle, BQ25896_ADDRESS, &address, 1, data, size, BQ25896_I2C_TIMEOUT);
 }
 
-bool bq25896_read_reg(uint8_t address, uint8_t* data) {
-    bq25896_read(address, data, 1);
-    return true;
+bool bq25896_read_reg(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data) {
+    return bq25896_read(handle, address, data, 1);
 }
 
-bool bq25896_write_reg(uint8_t address, uint8_t* data) {
+bool bq25896_write_reg(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data) {
     uint8_t buffer[2] = {address, *data};
-    bool ret;
-    with_furi_hal_i2c(
-        bool, &ret, () {
-            return furi_hal_i2c_tx(POWER_I2C, BQ25896_ADDRESS, buffer, 2, BQ25896_I2C_TIMEOUT);
-        });
-    return ret;
+    return furi_hal_i2c_tx(handle, BQ25896_ADDRESS, buffer, 2, BQ25896_I2C_TIMEOUT);
 }
 
 typedef struct {
@@ -62,62 +49,62 @@ typedef struct {
 
 static bq25896_regs_t bq25896_regs;
 
-void bq25896_init() {
+void bq25896_init(FuriHalI2cBusHandle* handle) {
     bq25896_regs.r14.REG_RST = 1;
-    bq25896_write_reg(0x14, (uint8_t*)&bq25896_regs.r14);
+    bq25896_write_reg(handle, 0x14, (uint8_t*)&bq25896_regs.r14);
 
     // Readout all registers
-    bq25896_read(0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
+    bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
 
     // Poll ADC forever
     bq25896_regs.r02.CONV_START = 1;
     bq25896_regs.r02.CONV_RATE = 1;
-    bq25896_write_reg(0x02, (uint8_t*)&bq25896_regs.r02);
+    bq25896_write_reg(handle, 0x02, (uint8_t*)&bq25896_regs.r02);
 
     bq25896_regs.r07.WATCHDOG = WatchdogDisable;
-    bq25896_write_reg(0x07, (uint8_t*)&bq25896_regs.r07);
+    bq25896_write_reg(handle, 0x07, (uint8_t*)&bq25896_regs.r07);
 
-    bq25896_read(0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
+    bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
 }
 
-void bq25896_poweroff() {
+void bq25896_poweroff(FuriHalI2cBusHandle* handle) {
     bq25896_regs.r09.BATFET_DIS = 1;
-    bq25896_write_reg(0x09, (uint8_t*)&bq25896_regs.r09);
+    bq25896_write_reg(handle, 0x09, (uint8_t*)&bq25896_regs.r09);
 }
 
-bool bq25896_is_charging() {
-    bq25896_read(0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
-    bq25896_read_reg(0x0B, (uint8_t*)&bq25896_regs.r0B);
+bool bq25896_is_charging(FuriHalI2cBusHandle* handle) {
+    bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs));
+    bq25896_read_reg(handle, 0x0B, (uint8_t*)&bq25896_regs.r0B);
     return bq25896_regs.r0B.CHRG_STAT != ChrgStatNo;
 }
 
-void bq25896_enable_charging() {
+void bq25896_enable_charging(FuriHalI2cBusHandle* handle) {
     bq25896_regs.r03.CHG_CONFIG = 1;
-    bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+    bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03);
 }
 
-void bq25896_disable_charging() {
+void bq25896_disable_charging(FuriHalI2cBusHandle* handle) {
     bq25896_regs.r03.CHG_CONFIG = 0;
-    bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+    bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03);
 }
 
-void bq25896_enable_otg() {
+void bq25896_enable_otg(FuriHalI2cBusHandle* handle) {
     bq25896_regs.r03.OTG_CONFIG = 1;
-    bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+    bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03);
 }
 
-void bq25896_disable_otg() {
+void bq25896_disable_otg(FuriHalI2cBusHandle* handle) {
     bq25896_regs.r03.OTG_CONFIG = 0;
-    bq25896_write_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+    bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03);
 }
 
-bool bq25896_is_otg_enabled() {
-    bq25896_read_reg(0x03, (uint8_t*)&bq25896_regs.r03);
+bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) {
+    bq25896_read_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03);
     return bq25896_regs.r03.OTG_CONFIG;
 }
 
-uint16_t bq25896_get_vbus_voltage() {
-    bq25896_read_reg(0x11, (uint8_t*)&bq25896_regs.r11);
+uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) {
+    bq25896_read_reg(handle, 0x11, (uint8_t*)&bq25896_regs.r11);
     if(bq25896_regs.r11.VBUS_GD) {
         return (uint16_t)bq25896_regs.r11.VBUSV * 100 + 2600;
     } else {
@@ -125,22 +112,22 @@ uint16_t bq25896_get_vbus_voltage() {
     }
 }
 
-uint16_t bq25896_get_vsys_voltage() {
-    bq25896_read_reg(0x0F, (uint8_t*)&bq25896_regs.r0F);
+uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle) {
+    bq25896_read_reg(handle, 0x0F, (uint8_t*)&bq25896_regs.r0F);
     return (uint16_t)bq25896_regs.r0F.SYSV * 20 + 2304;
 }
 
-uint16_t bq25896_get_vbat_voltage() {
-    bq25896_read_reg(0x0E, (uint8_t*)&bq25896_regs.r0E);
+uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle) {
+    bq25896_read_reg(handle, 0x0E, (uint8_t*)&bq25896_regs.r0E);
     return (uint16_t)bq25896_regs.r0E.BATV * 20 + 2304;
 }
 
-uint16_t bq25896_get_vbat_current() {
-    bq25896_read_reg(0x12, (uint8_t*)&bq25896_regs.r12);
+uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle) {
+    bq25896_read_reg(handle, 0x12, (uint8_t*)&bq25896_regs.r12);
     return (uint16_t)bq25896_regs.r12.ICHGR * 50;
 }
 
-uint32_t bq25896_get_ntc_mpct() {
-    bq25896_read_reg(0x10, (uint8_t*)&bq25896_regs.r10);
+uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle) {
+    bq25896_read_reg(handle, 0x10, (uint8_t*)&bq25896_regs.r10);
     return (uint32_t)bq25896_regs.r10.TSPCT * 465 + 21000;
 }

+ 14 - 13
lib/drivers/bq25896.h

@@ -2,42 +2,43 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include <furi-hal-i2c.h>
 
 /** Initialize Driver */
-void bq25896_init();
+void bq25896_init(FuriHalI2cBusHandle* handle);
 
 /** Send device into shipping mode */
-void bq25896_poweroff();
+void bq25896_poweroff(FuriHalI2cBusHandle* handle);
 
 /** Is currently charging */
-bool bq25896_is_charging();
+bool bq25896_is_charging(FuriHalI2cBusHandle* handle);
 
 /** Enable charging */
-void bq25896_enable_charging();
+void bq25896_enable_charging(FuriHalI2cBusHandle* handle);
 
 /** Disable charging */
-void bq25896_disable_charging();
+void bq25896_disable_charging(FuriHalI2cBusHandle* handle);
 
 /** Enable otg */
-void bq25896_enable_otg();
+void bq25896_enable_otg(FuriHalI2cBusHandle* handle);
 
 /** Disable otg */
-void bq25896_disable_otg();
+void bq25896_disable_otg(FuriHalI2cBusHandle* handle);
 
 /** Is otg enabled */
-bool bq25896_is_otg_enabled();
+bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle);
 
 /** Get VBUS Voltage in mV */
-uint16_t bq25896_get_vbus_voltage();
+uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle);
 
 /** Get VSYS Voltage in mV */
-uint16_t bq25896_get_vsys_voltage();
+uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle);
 
 /** Get VBAT Voltage in mV */
-uint16_t bq25896_get_vbat_voltage();
+uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle);
 
 /** Get VBAT current in mA */
-uint16_t bq25896_get_vbat_current();
+uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle);
 
 /** Get NTC voltage in mpct of REGN */
-uint32_t bq25896_get_ntc_mpct();
+uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle);

+ 82 - 89
lib/drivers/bq27220.c

@@ -1,37 +1,32 @@
 #include "bq27220.h"
 #include "bq27220_reg.h"
 
-#include <furi-hal-i2c.h>
 #include <furi-hal-delay.h>
+#include <furi/log.h>
 #include <stdbool.h>
 
 #define TAG "Gauge"
 
-uint16_t bq27220_read_word(uint8_t address) {
+uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) {
     uint8_t buffer[2] = {address};
-    uint16_t ret;
-    with_furi_hal_i2c(
-        uint16_t, &ret, () {
-            if(furi_hal_i2c_trx(
-                   POWER_I2C, BQ27220_ADDRESS, buffer, 1, buffer, 2, BQ27220_I2C_TIMEOUT)) {
-                return *(uint16_t*)buffer;
-            } else {
-                return 0;
-            }
-        });
+    uint16_t ret = 0;
+
+    if(furi_hal_i2c_trx(handle, BQ27220_ADDRESS, buffer, 1, buffer, 2, BQ27220_I2C_TIMEOUT)) {
+        ret = *(uint16_t*)buffer;
+    }
+
     return ret;
 }
 
-bool bq27220_control(uint16_t control) {
-    bool ret;
-    with_furi_hal_i2c(
-        bool, &ret, () {
-            uint8_t buffer[3];
-            buffer[0] = CommandControl;
-            buffer[1] = control & 0xFF;
-            buffer[2] = (control >> 8) & 0xFF;
-            return furi_hal_i2c_tx(POWER_I2C, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT);
-        });
+bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) {
+    bool ret = false;
+    uint8_t buffer[3];
+
+    buffer[0] = CommandControl;
+    buffer[1] = control & 0xFF;
+    buffer[2] = (control >> 8) & 0xFF;
+    ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT);
+
     return ret;
 }
 
@@ -43,75 +38,73 @@ uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) {
     return 0xFF - ret;
 }
 
-bool bq27220_set_parameter_u16(uint16_t address, uint16_t value) {
+bool bq27220_set_parameter_u16(FuriHalI2cBusHandle* handle, uint16_t address, uint16_t value) {
     bool ret;
     uint8_t buffer[5];
-    with_furi_hal_i2c(
-        bool, &ret, () {
-            buffer[0] = CommandSelectSubclass;
-            buffer[1] = address & 0xFF;
-            buffer[2] = (address >> 8) & 0xFF;
-            buffer[3] = (value >> 8) & 0xFF;
-            buffer[4] = value & 0xFF;
-            return furi_hal_i2c_tx(POWER_I2C, BQ27220_ADDRESS, buffer, 5, BQ27220_I2C_TIMEOUT);
-        });
+
+    buffer[0] = CommandSelectSubclass;
+    buffer[1] = address & 0xFF;
+    buffer[2] = (address >> 8) & 0xFF;
+    buffer[3] = (value >> 8) & 0xFF;
+    buffer[4] = value & 0xFF;
+    ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 5, BQ27220_I2C_TIMEOUT);
+
     delay_us(10000);
+
     uint8_t checksum = bq27220_get_checksum(&buffer[1], 4);
-    with_furi_hal_i2c(
-        bool, &ret, () {
-            buffer[0] = CommandMACDataSum;
-            buffer[1] = checksum;
-            buffer[2] = 6;
-            return furi_hal_i2c_tx(POWER_I2C, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT);
-        });
+    buffer[0] = CommandMACDataSum;
+    buffer[1] = checksum;
+    buffer[2] = 6;
+    ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT);
+
     delay_us(10000);
     return ret;
 }
 
-bool bq27220_init(const ParamCEDV* cedv) {
+bool bq27220_init(FuriHalI2cBusHandle* handle, const ParamCEDV* cedv) {
     uint32_t timeout = 100;
-    uint16_t design_cap = bq27220_get_design_capacity();
+    uint16_t design_cap = bq27220_get_design_capacity(handle);
     if(cedv->design_cap == design_cap) {
         FURI_LOG_I(TAG, "Skip battery profile update");
         return true;
     }
     FURI_LOG_I(TAG, "Start updating battery profile");
     OperationStatus status = {};
-    if(!bq27220_control(Control_ENTER_CFG_UPDATE)) {
+    if(!bq27220_control(handle, Control_ENTER_CFG_UPDATE)) {
         FURI_LOG_E(TAG, "Can't configure update");
         return false;
     };
 
     while((status.CFGUPDATE != 1) && (timeout-- > 0)) {
-        bq27220_get_operation_status(&status);
+        bq27220_get_operation_status(handle, &status);
     }
-    bq27220_set_parameter_u16(AddressGaugingConfig, cedv->cedv_conf.gauge_conf_raw);
-    bq27220_set_parameter_u16(AddressFullChargeCapacity, cedv->full_charge_cap);
-    bq27220_set_parameter_u16(AddressDesignCapacity, cedv->design_cap);
-    bq27220_set_parameter_u16(AddressEMF, cedv->EMF);
-    bq27220_set_parameter_u16(AddressC0, cedv->C0);
-    bq27220_set_parameter_u16(AddressR0, cedv->R0);
-    bq27220_set_parameter_u16(AddressT0, cedv->T0);
-    bq27220_set_parameter_u16(AddressR1, cedv->R1);
-    bq27220_set_parameter_u16(AddressTC, (cedv->TC) << 8 | cedv->C1);
-    bq27220_set_parameter_u16(AddressStartDOD0, cedv->DOD0);
-    bq27220_set_parameter_u16(AddressStartDOD10, cedv->DOD10);
-    bq27220_set_parameter_u16(AddressStartDOD20, cedv->DOD20);
-    bq27220_set_parameter_u16(AddressStartDOD30, cedv->DOD30);
-    bq27220_set_parameter_u16(AddressStartDOD40, cedv->DOD40);
-    bq27220_set_parameter_u16(AddressStartDOD50, cedv->DOD40);
-    bq27220_set_parameter_u16(AddressStartDOD60, cedv->DOD60);
-    bq27220_set_parameter_u16(AddressStartDOD70, cedv->DOD70);
-    bq27220_set_parameter_u16(AddressStartDOD80, cedv->DOD80);
-    bq27220_set_parameter_u16(AddressStartDOD90, cedv->DOD90);
-    bq27220_set_parameter_u16(AddressStartDOD100, cedv->DOD100);
-    bq27220_set_parameter_u16(AddressEDV0, cedv->EDV0);
-    bq27220_set_parameter_u16(AddressEDV1, cedv->EDV1);
-    bq27220_set_parameter_u16(AddressEDV2, cedv->EDV2);
-
-    bq27220_control(Control_EXIT_CFG_UPDATE);
+    bq27220_set_parameter_u16(handle, AddressGaugingConfig, cedv->cedv_conf.gauge_conf_raw);
+    bq27220_set_parameter_u16(handle, AddressFullChargeCapacity, cedv->full_charge_cap);
+    bq27220_set_parameter_u16(handle, AddressDesignCapacity, cedv->design_cap);
+    bq27220_set_parameter_u16(handle, AddressEMF, cedv->EMF);
+    bq27220_set_parameter_u16(handle, AddressC0, cedv->C0);
+    bq27220_set_parameter_u16(handle, AddressR0, cedv->R0);
+    bq27220_set_parameter_u16(handle, AddressT0, cedv->T0);
+    bq27220_set_parameter_u16(handle, AddressR1, cedv->R1);
+    bq27220_set_parameter_u16(handle, AddressTC, (cedv->TC) << 8 | cedv->C1);
+    bq27220_set_parameter_u16(handle, AddressStartDOD0, cedv->DOD0);
+    bq27220_set_parameter_u16(handle, AddressStartDOD10, cedv->DOD10);
+    bq27220_set_parameter_u16(handle, AddressStartDOD20, cedv->DOD20);
+    bq27220_set_parameter_u16(handle, AddressStartDOD30, cedv->DOD30);
+    bq27220_set_parameter_u16(handle, AddressStartDOD40, cedv->DOD40);
+    bq27220_set_parameter_u16(handle, AddressStartDOD50, cedv->DOD40);
+    bq27220_set_parameter_u16(handle, AddressStartDOD60, cedv->DOD60);
+    bq27220_set_parameter_u16(handle, AddressStartDOD70, cedv->DOD70);
+    bq27220_set_parameter_u16(handle, AddressStartDOD80, cedv->DOD80);
+    bq27220_set_parameter_u16(handle, AddressStartDOD90, cedv->DOD90);
+    bq27220_set_parameter_u16(handle, AddressStartDOD100, cedv->DOD100);
+    bq27220_set_parameter_u16(handle, AddressEDV0, cedv->EDV0);
+    bq27220_set_parameter_u16(handle, AddressEDV1, cedv->EDV1);
+    bq27220_set_parameter_u16(handle, AddressEDV2, cedv->EDV2);
+
+    bq27220_control(handle, Control_EXIT_CFG_UPDATE);
     delay_us(10000);
-    design_cap = bq27220_get_design_capacity();
+    design_cap = bq27220_get_design_capacity(handle);
     if(cedv->design_cap == design_cap) {
         FURI_LOG_I(TAG, "Battery profile update success");
         return true;
@@ -121,16 +114,16 @@ bool bq27220_init(const ParamCEDV* cedv) {
     }
 }
 
-uint16_t bq27220_get_voltage() {
-    return bq27220_read_word(CommandVoltage);
+uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandVoltage);
 }
 
-int16_t bq27220_get_current() {
-    return bq27220_read_word(CommandCurrent);
+int16_t bq27220_get_current(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandCurrent);
 }
 
-uint8_t bq27220_get_battery_status(BatteryStatus* battery_status) {
-    uint16_t data = bq27220_read_word(CommandBatteryStatus);
+uint8_t bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status) {
+    uint16_t data = bq27220_read_word(handle, CommandBatteryStatus);
     if(data == BQ27220_ERROR) {
         return BQ27220_ERROR;
     } else {
@@ -139,8 +132,8 @@ uint8_t bq27220_get_battery_status(BatteryStatus* battery_status) {
     }
 }
 
-uint8_t bq27220_get_operation_status(OperationStatus* operation_status) {
-    uint16_t data = bq27220_read_word(CommandOperationStatus);
+uint8_t bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status) {
+    uint16_t data = bq27220_read_word(handle, CommandOperationStatus);
     if(data == BQ27220_ERROR) {
         return BQ27220_ERROR;
     } else {
@@ -149,26 +142,26 @@ uint8_t bq27220_get_operation_status(OperationStatus* operation_status) {
     }
 }
 
-uint16_t bq27220_get_temperature() {
-    return bq27220_read_word(CommandTemperature);
+uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandTemperature);
 }
 
-uint16_t bq27220_get_full_charge_capacity() {
-    return bq27220_read_word(CommandFullChargeCapacity);
+uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandFullChargeCapacity);
 }
 
-uint16_t bq27220_get_design_capacity() {
-    return bq27220_read_word(CommandDesignCapacity);
+uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandDesignCapacity);
 }
 
-uint16_t bq27220_get_remaining_capacity() {
-    return bq27220_read_word(CommandRemainingCapacity);
+uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandRemainingCapacity);
 }
 
-uint16_t bq27220_get_state_of_charge() {
-    return bq27220_read_word(CommandStateOfCharge);
+uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandStateOfCharge);
 }
 
-uint16_t bq27220_get_state_of_health() {
-    return bq27220_read_word(CommandStateOfHealth);
+uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle) {
+    return bq27220_read_word(handle, CommandStateOfHealth);
 }

+ 13 - 12
lib/drivers/bq27220.h

@@ -2,6 +2,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <furi-hal-i2c.h>
 
 #define BQ27220_ERROR 0x0
 #define BQ27220_SUCCESS 0x1
@@ -95,36 +96,36 @@ typedef struct {
 /** Initialize Driver
  * @return true on success, false otherwise
  */
-bool bq27220_init(const ParamCEDV* cedv);
+bool bq27220_init(FuriHalI2cBusHandle* handle, const ParamCEDV* cedv);
 
 /** Get battery voltage in mV or error */
-uint16_t bq27220_get_voltage();
+uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle);
 
 /** Get current in mA or error*/
-int16_t bq27220_get_current();
+int16_t bq27220_get_current(FuriHalI2cBusHandle* handle);
 
 /** Get battery status */
-uint8_t bq27220_get_battery_status(BatteryStatus* battery_status);
+uint8_t bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status);
 
 /** Get operation status */
-uint8_t bq27220_get_operation_status(OperationStatus* operation_status);
+uint8_t bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status);
 
 /** Get temperature in units of 0.1°K */
-uint16_t bq27220_get_temperature();
+uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle);
 
 /** Get compensated full charge capacity in in mAh */
-uint16_t bq27220_get_full_charge_capacity();
+uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle);
 
 /** Get design capacity in mAh */
-uint16_t bq27220_get_design_capacity();
+uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle);
 
 /** Get remaining capacity in in mAh */
-uint16_t bq27220_get_remaining_capacity();
+uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle);
 
 /** Get predicted remaining battery capacity in percents */
-uint16_t bq27220_get_state_of_charge();
+uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle);
 
 /** Get ratio of full charge capacity over design capacity in percents */
-uint16_t bq27220_get_state_of_health();
+uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle);
 
-void bq27220_change_design_capacity(uint16_t capacity);
+void bq27220_change_design_capacity(FuriHalI2cBusHandle* handle, uint16_t capacity);

+ 13 - 19
lib/drivers/lp5562.c

@@ -1,27 +1,21 @@
 #include "lp5562.h"
 #include "lp5562_reg.h"
 
-#include <furi-hal-i2c.h>
 #include <stdio.h>
 
-bool lp5562_write_reg(uint8_t address, uint8_t* data) {
+static bool lp5562_write_reg(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data) {
     uint8_t buffer[2] = {address, *data};
-    bool ret;
-    with_furi_hal_i2c(
-        bool, &ret, () {
-            return furi_hal_i2c_tx(POWER_I2C, LP5562_ADDRESS, buffer, 2, LP5562_I2C_TIMEOUT);
-        });
-    return ret;
+    return furi_hal_i2c_tx(handle, LP5562_ADDRESS, buffer, 2, LP5562_I2C_TIMEOUT);
 }
 
-void lp5562_reset() {
+void lp5562_reset(FuriHalI2cBusHandle* handle) {
     Reg0D_Reset reg = {.value = 0xFF};
-    lp5562_write_reg(0x0D, (uint8_t*)&reg);
+    lp5562_write_reg(handle, 0x0D, (uint8_t*)&reg);
 }
 
-void lp5562_configure() {
+void lp5562_configure(FuriHalI2cBusHandle* handle) {
     Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true};
-    lp5562_write_reg(0x08, (uint8_t*)&config);
+    lp5562_write_reg(handle, 0x08, (uint8_t*)&config);
 
     Reg70_LedMap map = {
         .red = EngSelectI2C,
@@ -29,15 +23,15 @@ void lp5562_configure() {
         .blue = EngSelectI2C,
         .white = EngSelectI2C,
     };
-    lp5562_write_reg(0x70, (uint8_t*)&map);
+    lp5562_write_reg(handle, 0x70, (uint8_t*)&map);
 }
 
-void lp5562_enable() {
+void lp5562_enable(FuriHalI2cBusHandle* handle) {
     Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true};
-    lp5562_write_reg(0x00, (uint8_t*)&reg);
+    lp5562_write_reg(handle, 0x00, (uint8_t*)&reg);
 }
 
-void lp5562_set_channel_current(LP5562Channel channel, uint8_t value) {
+void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
     uint8_t reg_no;
     if(channel == LP5562ChannelRed) {
         reg_no = 0x07;
@@ -50,10 +44,10 @@ void lp5562_set_channel_current(LP5562Channel channel, uint8_t value) {
     } else {
         return;
     }
-    lp5562_write_reg(reg_no, &value);
+    lp5562_write_reg(handle, reg_no, &value);
 }
 
-void lp5562_set_channel_value(LP5562Channel channel, uint8_t value) {
+void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
     uint8_t reg_no;
     if(channel == LP5562ChannelRed) {
         reg_no = 0x04;
@@ -66,5 +60,5 @@ void lp5562_set_channel_value(LP5562Channel channel, uint8_t value) {
     } else {
         return;
     }
-    lp5562_write_reg(reg_no, &value);
+    lp5562_write_reg(handle, reg_no, &value);
 }

+ 6 - 5
lib/drivers/lp5562.h

@@ -2,6 +2,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <furi-hal-i2c.h>
 
 /** Channel types */
 typedef enum {
@@ -12,16 +13,16 @@ typedef enum {
 } LP5562Channel;
 
 /** Initialize Driver */
-void lp5562_reset();
+void lp5562_reset(FuriHalI2cBusHandle* handle);
 
 /** Configure Driver */
-void lp5562_configure();
+void lp5562_configure(FuriHalI2cBusHandle* handle);
 
 /** Enable Driver */
-void lp5562_enable();
+void lp5562_enable(FuriHalI2cBusHandle* handle);
 
 /** Set channel current */
-void lp5562_set_channel_current(LP5562Channel channel, uint8_t value);
+void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value);
 
 /** Set channel current */
-void lp5562_set_channel_value(LP5562Channel channel, uint8_t value);
+void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value);