[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:
		
							
								
								
									
										149
									
								
								bootloader/targets/f6/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								bootloader/targets/f6/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								bootloader/targets/f6/furi-hal/furi-hal-i2c-config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								bootloader/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 | ||||
							
								
								
									
										51
									
								
								bootloader/targets/f6/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								bootloader/targets/f6/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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}; | ||||
|     furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit); | ||||
| } | ||||
|  | ||||
|     LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); | ||||
| void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { | ||||
|     handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); | ||||
|  | ||||
|     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); | ||||
|     assert(handle->bus->current_handle == NULL); | ||||
|  | ||||
|     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|     handle->bus->current_handle = handle; | ||||
|  | ||||
|     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); | ||||
|     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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
| @@ -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}; | ||||
|   | ||||
| @@ -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
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c-config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c-config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								bootloader/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 | ||||
							
								
								
									
										51
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c-types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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}; | ||||
|     furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit); | ||||
| } | ||||
|  | ||||
|     LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); | ||||
| void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { | ||||
|     handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); | ||||
|  | ||||
|     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); | ||||
|     assert(handle->bus->current_handle == NULL); | ||||
|  | ||||
|     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|     handle->bus->current_handle = handle; | ||||
|  | ||||
|     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); | ||||
|     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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
| @@ -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}; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user