[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.
This commit is contained in:
		
							
								
								
									
										133
									
								
								firmware/targets/f6/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								firmware/targets/f6/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							@@ -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
									
								
								firmware/targets/f6/furi-hal/furi-hal-i2c-config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								firmware/targets/f6/furi-hal/furi-hal-i2c-config.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
									
								
								firmware/targets/f6/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								firmware/targets/f6/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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_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();
 | 
			
		||||
    FURI_LOG_I(TAG, "Init OK");
 | 
			
		||||
    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);
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
 | 
			
		||||
    float ret = 0.0f;
 | 
			
		||||
 | 
			
		||||
    if (ic == FuriHalPowerICCharger) {
 | 
			
		||||
        // Linear approximation, +/- 5 C
 | 
			
		||||
        ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)/1000) / 0.6f;
 | 
			
		||||
    } else if (ic == FuriHalPowerICFuelGauge) {
 | 
			
		||||
        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) {
 | 
			
		||||
    if (ic == FuriHalPowerICCharger) {
 | 
			
		||||
        // Linear approximation, +/- 5 C
 | 
			
		||||
        return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
 | 
			
		||||
    } else if (ic == FuriHalPowerICFuelGauge) {
 | 
			
		||||
        return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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};
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
								
								firmware/targets/f7/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								firmware/targets/f7/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							@@ -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
									
								
								firmware/targets/f7/furi-hal/furi-hal-i2c-config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								firmware/targets/f7/furi-hal/furi-hal-i2c-config.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
									
								
								firmware/targets/f7/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								firmware/targets/f7/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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_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();
 | 
			
		||||
    FURI_LOG_I(TAG, "Init OK");
 | 
			
		||||
    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);
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
 | 
			
		||||
    float ret = 0.0f;
 | 
			
		||||
 | 
			
		||||
    if (ic == FuriHalPowerICCharger) {
 | 
			
		||||
        // Linear approximation, +/- 5 C
 | 
			
		||||
        ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)/1000) / 0.6f;
 | 
			
		||||
    } else if (ic == FuriHalPowerICFuelGauge) {
 | 
			
		||||
        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) {
 | 
			
		||||
    if (ic == FuriHalPowerICCharger) {
 | 
			
		||||
        // Linear approximation, +/- 5 C
 | 
			
		||||
        return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
 | 
			
		||||
    } else if (ic == FuriHalPowerICFuelGauge) {
 | 
			
		||||
        return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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};
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 */
 | 
			
		||||
		Reference in New Issue
	
	Block a user