From d4787e859ee0b1992d645b6f57fb145246c94334 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Fri, 21 Jan 2022 19:55:44 +0300 Subject: [PATCH] [FL-1506, FL-2197] Power, USB, LED driver improvements (#966) * Power, USB, LED driver improvements * u2f hid descriptor fix * variable_item_list: value alignment fix * InputTypeRepeat handling in menu/submenu/var_item_list * lp5562: fix bugs on 400khz i2c * Scripts: lint in parallel. * FuriHal: rename some USB structure to match naming convention. Drivers: update magic values in LP5562. Co-authored-by: Aleksandr Kutuzov --- applications/bad_usb/bad_usb_app.c | 2 +- applications/debug_tools/usb_mouse.c | 2 +- applications/debug_tools/usb_test.c | 5 + applications/gpio/usb_uart_bridge.c | 2 +- applications/gui/modules/file_select.c | 2 +- applications/gui/modules/menu.c | 8 + applications/gui/modules/submenu.c | 8 + applications/gui/modules/variable_item_list.c | 29 ++- applications/power_observer/power_observer.c | 55 +++++- applications/u2f/u2f_hid.c | 2 +- bootloader/targets/f6/furi_hal/furi_hal_i2c.c | 69 +++++++ bootloader/targets/f6/furi_hal/furi_hal_i2c.h | 106 +++++++++++ bootloader/targets/f7/furi_hal/furi_hal_i2c.c | 69 +++++++ bootloader/targets/f7/furi_hal/furi_hal_i2c.h | 106 +++++++++++ firmware/targets/f6/furi_hal/furi_hal_light.c | 7 +- firmware/targets/f6/furi_hal/furi_hal_power.c | 8 + firmware/targets/f6/furi_hal/furi_hal_usb.c | 93 +++++++--- .../targets/f6/furi_hal/furi_hal_usb_cdc.c | 10 +- .../targets/f6/furi_hal/furi_hal_usb_hid.c | 6 +- .../targets/f6/furi_hal/furi_hal_usb_u2f.c | 13 +- firmware/targets/f7/furi_hal/furi_hal_light.c | 7 +- firmware/targets/f7/furi_hal/furi_hal_power.c | 8 + firmware/targets/f7/furi_hal/furi_hal_usb.c | 93 +++++++--- .../targets/f7/furi_hal/furi_hal_usb_cdc.c | 10 +- .../targets/f7/furi_hal/furi_hal_usb_hid.c | 6 +- .../targets/f7/furi_hal/furi_hal_usb_u2f.c | 13 +- .../targets/furi_hal_include/furi_hal_power.h | 4 + .../targets/furi_hal_include/furi_hal_usb.h | 35 +++- lib/drivers/bq25896.c | 94 ++++++---- lib/drivers/bq25896.h | 3 + lib/drivers/bq27220.c | 41 ++--- lib/drivers/lp5562.c | 170 ++++++++++++++++-- lib/drivers/lp5562.h | 28 ++- lib/drivers/lp5562_reg.h | 10 ++ scripts/lint.py | 26 ++- 35 files changed, 968 insertions(+), 182 deletions(-) diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c index 58af3d7c..4f94598e 100644 --- a/applications/bad_usb/bad_usb_app.c +++ b/applications/bad_usb/bad_usb_app.c @@ -71,7 +71,7 @@ void bad_usb_app_free(BadUsbApp* app) { } int32_t bad_usb_app(void* p) { - UsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_set_config(&usb_hid); BadUsbApp* bad_usb_app = bad_usb_app_alloc(); diff --git a/applications/debug_tools/usb_mouse.c b/applications/debug_tools/usb_mouse.c index edbd1e4a..9321b806 100644 --- a/applications/debug_tools/usb_mouse.c +++ b/applications/debug_tools/usb_mouse.c @@ -41,7 +41,7 @@ int32_t usb_mouse_app(void* p) { furi_check(event_queue); ViewPort* view_port = view_port_alloc(); - UsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_set_config(&usb_hid); view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL); diff --git a/applications/debug_tools/usb_test.c b/applications/debug_tools/usb_test.c index 4f390d6a..ff32198a 100644 --- a/applications/debug_tools/usb_test.c +++ b/applications/debug_tools/usb_test.c @@ -15,6 +15,7 @@ typedef struct { typedef enum { UsbTestSubmenuIndexEnable, UsbTestSubmenuIndexDisable, + UsbTestSubmenuIndexRestart, UsbTestSubmenuIndexVcpSingle, UsbTestSubmenuIndexVcpDual, UsbTestSubmenuIndexHid, @@ -28,6 +29,8 @@ void usb_test_submenu_callback(void* context, uint32_t index) { furi_hal_usb_enable(); } else if(index == UsbTestSubmenuIndexDisable) { furi_hal_usb_disable(); + } else if(index == UsbTestSubmenuIndexRestart) { + furi_hal_usb_reinit(); } else if(index == UsbTestSubmenuIndexVcpSingle) { furi_hal_usb_set_config(&usb_cdc_single); } else if(index == UsbTestSubmenuIndexVcpDual) { @@ -60,6 +63,8 @@ UsbTestApp* usb_test_app_alloc() { app->submenu, "Enable", UsbTestSubmenuIndexEnable, usb_test_submenu_callback, app); submenu_add_item( app->submenu, "Disable", UsbTestSubmenuIndexDisable, usb_test_submenu_callback, app); + submenu_add_item( + app->submenu, "Restart", UsbTestSubmenuIndexRestart, usb_test_submenu_callback, app); submenu_add_item( app->submenu, "Single VCP", UsbTestSubmenuIndexVcpSingle, usb_test_submenu_callback, app); submenu_add_item( diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index 03706b8d..c9085af7 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -154,7 +154,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_set_context(usb_uart->tx_thread, usb_uart); furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); - UsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c index 449f3194..78ecd241 100644 --- a/applications/gui/modules/file_select.c +++ b/applications/gui/modules/file_select.c @@ -77,7 +77,7 @@ static bool file_select_input_callback(InputEvent* event, void* context) { FileSelect* file_select = (FileSelect*)context; bool consumed = false; - if(event->type == InputTypeShort) { + if((event->type == InputTypeShort) | (event->type == InputTypeRepeat)) { if(!file_select->init_completed) { if(!file_select_init_inner(file_select)) { file_select->callback(false, file_select->context); diff --git a/applications/gui/modules/menu.c b/applications/gui/modules/menu.c index 2b551d55..6df2b94a 100644 --- a/applications/gui/modules/menu.c +++ b/applications/gui/modules/menu.c @@ -87,6 +87,14 @@ static bool menu_input_callback(InputEvent* event, void* context) { consumed = true; menu_process_ok(menu); } + } else if(event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + consumed = true; + menu_process_up(menu); + } else if(event->key == InputKeyDown) { + consumed = true; + menu_process_down(menu); + } } return consumed; diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c index 60a1b092..9d2f5f44 100644 --- a/applications/gui/modules/submenu.c +++ b/applications/gui/modules/submenu.c @@ -106,6 +106,14 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) { default: break; } + } else if(event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + consumed = true; + submenu_process_up(submenu); + } else if(event->key == InputKeyDown) { + consumed = true; + submenu_process_down(submenu); + } } return consumed; diff --git a/applications/gui/modules/variable_item_list.c b/applications/gui/modules/variable_item_list.c index f5226afd..b8d3a45d 100644 --- a/applications/gui/modules/variable_item_list.c +++ b/applications/gui/modules/variable_item_list.c @@ -71,7 +71,13 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, 73, item_text_y, "<"); } - canvas_draw_str(canvas, 80, item_text_y, string_get_cstr(item->current_value_text)); + canvas_draw_str_aligned( + canvas, + (115 + 73) / 2 + 1, + item_text_y, + AlignCenter, + AlignBottom, + string_get_cstr(item->current_value_text)); if(item->current_value_index < (item->values_count - 1)) { canvas_draw_str(canvas, 115, item_text_y, ">"); @@ -147,6 +153,27 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context) default: break; } + } else if(event->type == InputTypeRepeat) { + switch(event->key) { + case InputKeyUp: + consumed = true; + variable_item_list_process_up(variable_item_list); + break; + case InputKeyDown: + consumed = true; + variable_item_list_process_down(variable_item_list); + break; + case InputKeyLeft: + consumed = true; + variable_item_list_process_left(variable_item_list); + break; + case InputKeyRight: + consumed = true; + variable_item_list_process_right(variable_item_list); + break; + default: + break; + } } return consumed; diff --git a/applications/power_observer/power_observer.c b/applications/power_observer/power_observer.c index 9f494d07..b32447f0 100644 --- a/applications/power_observer/power_observer.c +++ b/applications/power_observer/power_observer.c @@ -2,6 +2,11 @@ #include #include +typedef struct { + osThreadId_t thread; + +} PowerObserverSrv; + const NotificationMessage message_green_110 = { .type = NotificationMessageTypeLedGreen, .data.led.value = 110, @@ -14,20 +19,58 @@ static const NotificationSequence sequence_overconsumption = { NULL, }; +typedef enum { + EventReset = (1 << 0), + EventRequest = (1 << 1), +} UsbEvent; + +static void usb_state_callback(FuriHalUsbStateEvent state, void* context) { + PowerObserverSrv* srv = (PowerObserverSrv*)(context); + if(state == FuriHalUsbStateEventReset) { + osThreadFlagsSet(srv->thread, EventReset); + } else if(state == FuriHalUsbStateEventDescriptorRequest) { + osThreadFlagsSet(srv->thread, EventRequest); + } +} + int32_t power_observer_srv(void* p) { NotificationApp* notifications = furi_record_open("notification"); + PowerObserverSrv* srv = furi_alloc(sizeof(PowerObserverSrv)); + srv->thread = osThreadGetId(); const float overconsumption_limit = 0.03f; + bool usb_request_pending = false; + uint8_t usb_wait_time = 0; + + furi_hal_usb_set_state_callback(usb_state_callback, srv); while(true) { - float current = -furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); - - if(current >= overconsumption_limit) { - notification_message_block(notifications, &sequence_overconsumption); + uint32_t flags = osThreadFlagsWait(EventReset | EventRequest, osFlagsWaitAny, 500); + if((flags & osFlagsError) == 0) { + if(flags & EventReset) { + usb_request_pending = true; + usb_wait_time = 0; + } + if(flags & EventRequest) { + usb_request_pending = false; + } + } else if(usb_request_pending) { + usb_wait_time++; + if(usb_wait_time > 4) { + furi_hal_usb_reinit(); + usb_request_pending = false; + } } - delay(1000); + float current = -furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); + if(current > overconsumption_limit) { + notification_message_block(notifications, &sequence_overconsumption); + } + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_check_otg_status(); + } } + free(srv); return 0; -} \ No newline at end of file +} diff --git a/applications/u2f/u2f_hid.c b/applications/u2f/u2f_hid.c index 7cc44dbe..1ddfecaf 100644 --- a/applications/u2f/u2f_hid.c +++ b/applications/u2f/u2f_hid.c @@ -190,7 +190,7 @@ static int32_t u2f_hid_worker(void* context) { FURI_LOG_D(WORKER_TAG, "Init"); - UsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_set_config(&usb_hid_u2f); u2f_hid->lock_timer = osTimerNew(u2f_hid_lock_timeout_callback, osTimerOnce, u2f_hid, NULL); diff --git a/bootloader/targets/f6/furi_hal/furi_hal_i2c.c b/bootloader/targets/f6/furi_hal/furi_hal_i2c.c index c918264a..210f96d6 100644 --- a/bootloader/targets/f6/furi_hal/furi_hal_i2c.c +++ b/bootloader/targets/f6/furi_hal/furi_hal_i2c.c @@ -134,3 +134,72 @@ bool furi_hal_i2c_trx( return false; } } + +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout) { + assert(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); +} + +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout) { + assert(handle); + + uint8_t reg_data[2]; + bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); + *data = (reg_data[0] << 8) | (reg_data[1]); + + return ret; +} + +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout) { + assert(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); +} + +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout) { + assert(handle); + + uint8_t tx_data[2]; + tx_data[0] = reg_addr; + tx_data[1] = data; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); +} + +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout) { + assert(handle); + + uint8_t tx_data[3]; + tx_data[0] = reg_addr; + tx_data[1] = (data >> 8) & 0xFF; + tx_data[2] = data & 0xFF; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); +} diff --git a/bootloader/targets/f6/furi_hal/furi_hal_i2c.h b/bootloader/targets/f6/furi_hal/furi_hal_i2c.h index 35269ec5..c2ad1728 100644 --- a/bootloader/targets/f6/furi_hal/furi_hal_i2c.h +++ b/bootloader/targets/f6/furi_hal/furi_hal_i2c.h @@ -84,6 +84,112 @@ bool furi_hal_i2c_trx( const uint8_t rx_size, uint32_t timeout); +/** Perform I2C device register read (8-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data pointer to register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout); + +/** Perform I2C device register read (16-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data pointer to register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout); + +/** Perform I2C device memory read + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr memory start address + * @param data pointer to data buffer + * @param len size of data buffer + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout); + +/** Perform I2C device register write (8-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout); + +/** Perform I2C device register write (16-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout); + +/** Perform I2C device memory + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr memory start address + * @param data pointer to data buffer + * @param len size of data buffer + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout); + #ifdef __cplusplus } #endif diff --git a/bootloader/targets/f7/furi_hal/furi_hal_i2c.c b/bootloader/targets/f7/furi_hal/furi_hal_i2c.c index c918264a..210f96d6 100644 --- a/bootloader/targets/f7/furi_hal/furi_hal_i2c.c +++ b/bootloader/targets/f7/furi_hal/furi_hal_i2c.c @@ -134,3 +134,72 @@ bool furi_hal_i2c_trx( return false; } } + +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout) { + assert(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); +} + +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout) { + assert(handle); + + uint8_t reg_data[2]; + bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); + *data = (reg_data[0] << 8) | (reg_data[1]); + + return ret; +} + +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout) { + assert(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); +} + +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout) { + assert(handle); + + uint8_t tx_data[2]; + tx_data[0] = reg_addr; + tx_data[1] = data; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); +} + +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout) { + assert(handle); + + uint8_t tx_data[3]; + tx_data[0] = reg_addr; + tx_data[1] = (data >> 8) & 0xFF; + tx_data[2] = data & 0xFF; + + return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); +} diff --git a/bootloader/targets/f7/furi_hal/furi_hal_i2c.h b/bootloader/targets/f7/furi_hal/furi_hal_i2c.h index 35269ec5..c2ad1728 100644 --- a/bootloader/targets/f7/furi_hal/furi_hal_i2c.h +++ b/bootloader/targets/f7/furi_hal/furi_hal_i2c.h @@ -84,6 +84,112 @@ bool furi_hal_i2c_trx( const uint8_t rx_size, uint32_t timeout); +/** Perform I2C device register read (8-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data pointer to register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout); + +/** Perform I2C device register read (16-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data pointer to register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout); + +/** Perform I2C device memory read + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr memory start address + * @param data pointer to data buffer + * @param len size of data buffer + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout); + +/** Perform I2C device register write (8-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout); + +/** Perform I2C device register write (16-bit) + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr register address + * @param data register value + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout); + +/** Perform I2C device memory + * + * @param handle pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr memory start address + * @param data pointer to data buffer + * @param len size of data buffer + * @param timeout timeout in ticks + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + uint8_t len, + uint32_t timeout); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f6/furi_hal/furi_hal_light.c b/firmware/targets/f6/furi_hal/furi_hal_light.c index 6aba7efb..1bd1adad 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_light.c +++ b/firmware/targets/f6/furi_hal/furi_hal_light.c @@ -28,6 +28,7 @@ void furi_hal_light_init() { } void furi_hal_light_set(Light light, uint8_t value) { + uint8_t prev = 0; furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); switch(light) { case LightRed: @@ -40,10 +41,12 @@ void furi_hal_light_set(Light light, uint8_t value) { lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); break; case LightBacklight: - lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value); + prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); + lp5562_execute_ramp( + &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); break; default: break; } furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} \ No newline at end of file +} diff --git a/firmware/targets/f6/furi_hal/furi_hal_power.c b/firmware/targets/f6/furi_hal/furi_hal_power.c index a574ebcc..1dbdb3dd 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_power.c +++ b/firmware/targets/f6/furi_hal/furi_hal_power.c @@ -216,6 +216,13 @@ bool furi_hal_power_is_otg_enabled() { return ret; } +void furi_hal_power_check_otg_status() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power)) + bq25896_disable_otg(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + uint32_t furi_hal_power_get_battery_remaining_capacity() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power); @@ -297,6 +304,7 @@ void furi_hal_power_dump_state() { BQ27220_ERROR) { printf("Failed to get bq27220 status. Communication error.\r\n"); } else { + // Operation status register printf( "bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n", operation_status.CALMD, diff --git a/firmware/targets/f6/furi_hal/furi_hal_usb.c b/firmware/targets/f6/furi_hal/furi_hal_usb.c index 59b37bbf..881081b6 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f6/furi_hal/furi_hal_usb.c @@ -9,15 +9,19 @@ #define USB_RECONNECT_DELAY 500 -static UsbInterface* usb_if_cur; -static UsbInterface* usb_if_next; +static FuriHalUsbInterface* usb_if_cur; +static FuriHalUsbInterface* usb_if_next; static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); static uint32_t ubuf[0x20]; usbd_device udev; +static FuriHalUsbStateCallback callback; +static void* cb_ctx; + static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length); +static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); @@ -25,6 +29,7 @@ struct UsbCfg { osTimerId_t reconnect_tmr; bool enabled; bool connected; + bool mode_changing; } usb_config; static void furi_hal_usb_tmr_cb(void* context); @@ -48,6 +53,7 @@ void furi_hal_usb_init(void) { usbd_reg_descr(&udev, usb_descriptor_get); usbd_reg_event(&udev, usbd_evt_susp, susp_evt); usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); + // Reset callback will be enabled after first mode change to avoid getting false reset events usb_config.enabled = false; usb_config.reconnect_tmr = NULL; @@ -57,28 +63,49 @@ void furi_hal_usb_init(void) { FURI_LOG_I(TAG, "Init OK"); } -void furi_hal_usb_set_config(UsbInterface* new_if) { - if(new_if != usb_if_cur) { - if(usb_config.enabled) { - usb_if_next = new_if; - if(usb_config.reconnect_tmr == NULL) - usb_config.reconnect_tmr = - osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); - furi_hal_usb_disable(); - osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); - } else { - if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); - if(new_if != NULL) { - new_if->init(&udev, new_if); - FURI_LOG_I(TAG, "USB mode change"); - usb_config.enabled = true; - usb_if_cur = new_if; - } +void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { + if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage + usb_config.mode_changing = true; + usb_if_next = new_if; + if(usb_config.reconnect_tmr == NULL) + usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); + furi_hal_usb_disable(); + usb_config.mode_changing = true; + osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); + } else if( + (usb_config.mode_changing) && + (usb_if_next != new_if)) { // Last interface mode change wasn't completed + osTimerStop(usb_config.reconnect_tmr); + usb_if_next = new_if; + osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); + } else { // Interface mode change - second stage + if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); + if(new_if != NULL) { + new_if->init(&udev, new_if); + usbd_reg_event(&udev, usbd_evt_reset, reset_evt); + FURI_LOG_I(TAG, "USB Mode change done"); + usb_config.enabled = true; + usb_if_cur = new_if; + usb_config.mode_changing = false; } } } -UsbInterface* furi_hal_usb_get_config() { +void furi_hal_usb_reinit() { + // Temporary disable callback to avoid getting false reset events + usbd_reg_event(&udev, usbd_evt_reset, NULL); + FURI_LOG_I(TAG, "USB Reinit"); + furi_hal_usb_disable(); + usbd_enable(&udev, false); + usbd_enable(&udev, true); + if(usb_config.reconnect_tmr == NULL) + usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); + usb_config.mode_changing = true; + usb_if_next = usb_if_cur; + osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); +} + +FuriHalUsbInterface* furi_hal_usb_get_config() { return usb_if_cur; } @@ -113,6 +140,9 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ switch(dtype) { case USB_DTYPE_DEVICE: + if(callback != NULL) { + callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx); + } desc = usb_if_cur->dev_descr; break; case USB_DTYPE_CONFIGURATION: @@ -122,11 +152,11 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ case USB_DTYPE_STRING: if(dnumber == UsbDevLang) { desc = &dev_lang_desc; - } else if(dnumber == UsbDevManuf) { + } else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) { desc = usb_if_cur->str_manuf_descr; - } else if(dnumber == UsbDevProduct) { + } else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) { desc = usb_if_cur->str_prod_descr; - } else if(dnumber == UsbDevSerial) { + } else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) { desc = usb_if_cur->str_serial_descr; } else return usbd_fail; @@ -144,11 +174,25 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ return usbd_ack; } +void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { + callback = cb; + cb_ctx = ctx; +} + +static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { + if(callback != NULL) { + callback(FuriHalUsbStateEventReset, cb_ctx); + } +} + static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { if((usb_if_cur != NULL) && (usb_config.connected == true)) { usb_config.connected = false; usb_if_cur->suspend(&udev); } + if(callback != NULL) { + callback(FuriHalUsbStateEventSuspend, cb_ctx); + } } static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { @@ -156,4 +200,7 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { usb_config.connected = true; usb_if_cur->wakeup(&udev); } + if(callback != NULL) { + callback(FuriHalUsbStateEventWakeup, cb_ctx); + } } diff --git a/firmware/targets/f6/furi_hal/furi_hal_usb_cdc.c b/firmware/targets/f6/furi_hal/furi_hal_usb_cdc.c index 26fd40c3..d7fcbc58 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_usb_cdc.c +++ b/firmware/targets/f6/furi_hal/furi_hal_usb_cdc.c @@ -385,7 +385,7 @@ static const struct CdcConfigDescriptorDual static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; static uint8_t cdc_ctrl_line_state[IF_NUM_MAX]; -static void cdc_init(usbd_device* dev, UsbInterface* intf); +static void cdc_init(usbd_device* dev, FuriHalUsbInterface* intf); static void cdc_deinit(usbd_device* dev); static void cdc_on_wakeup(usbd_device* dev); static void cdc_on_suspend(usbd_device* dev); @@ -393,12 +393,12 @@ static void cdc_on_suspend(usbd_device* dev); static usbd_respond cdc_ep_config(usbd_device* dev, uint8_t cfg); static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); static usbd_device* usb_dev; -static UsbInterface* cdc_if_cur = NULL; +static FuriHalUsbInterface* cdc_if_cur = NULL; static bool connected = false; static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; static void* cb_ctx[IF_NUM_MAX]; -UsbInterface usb_cdc_single = { +FuriHalUsbInterface usb_cdc_single = { .init = cdc_init, .deinit = cdc_deinit, .wakeup = cdc_on_wakeup, @@ -413,7 +413,7 @@ UsbInterface usb_cdc_single = { .cfg_descr = (void*)&cdc_cfg_desc_single, }; -UsbInterface usb_cdc_dual = { +FuriHalUsbInterface usb_cdc_dual = { .init = cdc_init, .deinit = cdc_deinit, .wakeup = cdc_on_wakeup, @@ -428,7 +428,7 @@ UsbInterface usb_cdc_dual = { .cfg_descr = (void*)&cdc_cfg_desc_dual, }; -static void cdc_init(usbd_device* dev, UsbInterface* intf) { +static void cdc_init(usbd_device* dev, FuriHalUsbInterface* intf) { usb_dev = dev; cdc_if_cur = intf; diff --git a/firmware/targets/f6/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f6/furi_hal/furi_hal_usb_hid.c index 098acc73..6f94fb33 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f6/furi_hal/furi_hal_usb_hid.c @@ -236,7 +236,7 @@ static struct HidReport { struct HidReportConsumer consumer; } __attribute__((packed)) hid_report; -static void hid_init(usbd_device* dev, UsbInterface* intf); +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf); static void hid_deinit(usbd_device* dev); static void hid_on_wakeup(usbd_device* dev); static void hid_on_suspend(usbd_device* dev); @@ -348,7 +348,7 @@ bool furi_hal_hid_consumer_key_release(uint16_t button) { return hid_send_report(ReportIdConsumer); } -UsbInterface usb_hid = { +FuriHalUsbInterface usb_hid = { .init = hid_init, .deinit = hid_deinit, .wakeup = hid_on_wakeup, @@ -363,7 +363,7 @@ UsbInterface usb_hid = { .cfg_descr = (void*)&hid_cfg_desc, }; -static void hid_init(usbd_device* dev, UsbInterface* intf) { +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf) { if(hid_semaphore == NULL) hid_semaphore = osSemaphoreNew(1, 1, NULL); usb_dev = dev; hid_report.keyboard.report_id = ReportIdKeyboard; diff --git a/firmware/targets/f6/furi_hal/furi_hal_usb_u2f.c b/firmware/targets/f6/furi_hal/furi_hal_usb_u2f.c index a3bb145e..4ee3d712 100644 --- a/firmware/targets/f6/furi_hal/furi_hal_usb_u2f.c +++ b/firmware/targets/f6/furi_hal/furi_hal_usb_u2f.c @@ -48,8 +48,7 @@ static const uint8_t hid_u2f_report_desc[] = { }; static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); -static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("U2F Token test"); -static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("TODO: serial"); +static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("U2F Token"); /* Device descriptor */ static const struct usb_device_descriptor hid_u2f_device_desc = { @@ -65,7 +64,7 @@ static const struct usb_device_descriptor hid_u2f_device_desc = { .bcdDevice = VERSION_BCD(1, 0, 0), .iManufacturer = UsbDevManuf, .iProduct = UsbDevProduct, - .iSerialNumber = UsbDevSerial, + .iSerialNumber = 0, .bNumConfigurations = 1, }; @@ -138,7 +137,7 @@ static const struct HidConfigDescriptor hid_u2f_cfg_desc = { }, }; -static void hid_u2f_init(usbd_device* dev, UsbInterface* intf); +static void hid_u2f_init(usbd_device* dev, FuriHalUsbInterface* intf); static void hid_u2f_deinit(usbd_device* dev); static void hid_u2f_on_wakeup(usbd_device* dev); static void hid_u2f_on_suspend(usbd_device* dev); @@ -171,7 +170,7 @@ void furi_hal_hid_u2f_set_callback(HidU2fCallback cb, void* ctx) { } } -UsbInterface usb_hid_u2f = { +FuriHalUsbInterface usb_hid_u2f = { .init = hid_u2f_init, .deinit = hid_u2f_deinit, .wakeup = hid_u2f_on_wakeup, @@ -181,12 +180,12 @@ UsbInterface usb_hid_u2f = { .str_manuf_descr = (void*)&dev_manuf_desc, .str_prod_descr = (void*)&dev_prod_desc, - .str_serial_descr = (void*)&dev_serial_desc, + .str_serial_descr = NULL, .cfg_descr = (void*)&hid_u2f_cfg_desc, }; -static void hid_u2f_init(usbd_device* dev, UsbInterface* intf) { +static void hid_u2f_init(usbd_device* dev, FuriHalUsbInterface* intf) { if(hid_u2f_semaphore == NULL) hid_u2f_semaphore = osSemaphoreNew(1, 1, NULL); usb_dev = dev; diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 6aba7efb..1bd1adad 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -28,6 +28,7 @@ void furi_hal_light_init() { } void furi_hal_light_set(Light light, uint8_t value) { + uint8_t prev = 0; furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); switch(light) { case LightRed: @@ -40,10 +41,12 @@ void furi_hal_light_set(Light light, uint8_t value) { lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); break; case LightBacklight: - lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite, value); + prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); + lp5562_execute_ramp( + &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); break; default: break; } furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index a574ebcc..1dbdb3dd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -216,6 +216,13 @@ bool furi_hal_power_is_otg_enabled() { return ret; } +void furi_hal_power_check_otg_status() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power)) + bq25896_disable_otg(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + uint32_t furi_hal_power_get_battery_remaining_capacity() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power); @@ -297,6 +304,7 @@ void furi_hal_power_dump_state() { BQ27220_ERROR) { printf("Failed to get bq27220 status. Communication error.\r\n"); } else { + // Operation status register printf( "bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n", operation_status.CALMD, diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index 59b37bbf..881081b6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -9,15 +9,19 @@ #define USB_RECONNECT_DELAY 500 -static UsbInterface* usb_if_cur; -static UsbInterface* usb_if_next; +static FuriHalUsbInterface* usb_if_cur; +static FuriHalUsbInterface* usb_if_next; static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); static uint32_t ubuf[0x20]; usbd_device udev; +static FuriHalUsbStateCallback callback; +static void* cb_ctx; + static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length); +static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); @@ -25,6 +29,7 @@ struct UsbCfg { osTimerId_t reconnect_tmr; bool enabled; bool connected; + bool mode_changing; } usb_config; static void furi_hal_usb_tmr_cb(void* context); @@ -48,6 +53,7 @@ void furi_hal_usb_init(void) { usbd_reg_descr(&udev, usb_descriptor_get); usbd_reg_event(&udev, usbd_evt_susp, susp_evt); usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); + // Reset callback will be enabled after first mode change to avoid getting false reset events usb_config.enabled = false; usb_config.reconnect_tmr = NULL; @@ -57,28 +63,49 @@ void furi_hal_usb_init(void) { FURI_LOG_I(TAG, "Init OK"); } -void furi_hal_usb_set_config(UsbInterface* new_if) { - if(new_if != usb_if_cur) { - if(usb_config.enabled) { - usb_if_next = new_if; - if(usb_config.reconnect_tmr == NULL) - usb_config.reconnect_tmr = - osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); - furi_hal_usb_disable(); - osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); - } else { - if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); - if(new_if != NULL) { - new_if->init(&udev, new_if); - FURI_LOG_I(TAG, "USB mode change"); - usb_config.enabled = true; - usb_if_cur = new_if; - } +void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { + if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage + usb_config.mode_changing = true; + usb_if_next = new_if; + if(usb_config.reconnect_tmr == NULL) + usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); + furi_hal_usb_disable(); + usb_config.mode_changing = true; + osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); + } else if( + (usb_config.mode_changing) && + (usb_if_next != new_if)) { // Last interface mode change wasn't completed + osTimerStop(usb_config.reconnect_tmr); + usb_if_next = new_if; + osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); + } else { // Interface mode change - second stage + if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); + if(new_if != NULL) { + new_if->init(&udev, new_if); + usbd_reg_event(&udev, usbd_evt_reset, reset_evt); + FURI_LOG_I(TAG, "USB Mode change done"); + usb_config.enabled = true; + usb_if_cur = new_if; + usb_config.mode_changing = false; } } } -UsbInterface* furi_hal_usb_get_config() { +void furi_hal_usb_reinit() { + // Temporary disable callback to avoid getting false reset events + usbd_reg_event(&udev, usbd_evt_reset, NULL); + FURI_LOG_I(TAG, "USB Reinit"); + furi_hal_usb_disable(); + usbd_enable(&udev, false); + usbd_enable(&udev, true); + if(usb_config.reconnect_tmr == NULL) + usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); + usb_config.mode_changing = true; + usb_if_next = usb_if_cur; + osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); +} + +FuriHalUsbInterface* furi_hal_usb_get_config() { return usb_if_cur; } @@ -113,6 +140,9 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ switch(dtype) { case USB_DTYPE_DEVICE: + if(callback != NULL) { + callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx); + } desc = usb_if_cur->dev_descr; break; case USB_DTYPE_CONFIGURATION: @@ -122,11 +152,11 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ case USB_DTYPE_STRING: if(dnumber == UsbDevLang) { desc = &dev_lang_desc; - } else if(dnumber == UsbDevManuf) { + } else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) { desc = usb_if_cur->str_manuf_descr; - } else if(dnumber == UsbDevProduct) { + } else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) { desc = usb_if_cur->str_prod_descr; - } else if(dnumber == UsbDevSerial) { + } else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) { desc = usb_if_cur->str_serial_descr; } else return usbd_fail; @@ -144,11 +174,25 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ return usbd_ack; } +void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { + callback = cb; + cb_ctx = ctx; +} + +static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { + if(callback != NULL) { + callback(FuriHalUsbStateEventReset, cb_ctx); + } +} + static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { if((usb_if_cur != NULL) && (usb_config.connected == true)) { usb_config.connected = false; usb_if_cur->suspend(&udev); } + if(callback != NULL) { + callback(FuriHalUsbStateEventSuspend, cb_ctx); + } } static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { @@ -156,4 +200,7 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { usb_config.connected = true; usb_if_cur->wakeup(&udev); } + if(callback != NULL) { + callback(FuriHalUsbStateEventWakeup, cb_ctx); + } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c index 26fd40c3..d7fcbc58 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -385,7 +385,7 @@ static const struct CdcConfigDescriptorDual static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; static uint8_t cdc_ctrl_line_state[IF_NUM_MAX]; -static void cdc_init(usbd_device* dev, UsbInterface* intf); +static void cdc_init(usbd_device* dev, FuriHalUsbInterface* intf); static void cdc_deinit(usbd_device* dev); static void cdc_on_wakeup(usbd_device* dev); static void cdc_on_suspend(usbd_device* dev); @@ -393,12 +393,12 @@ static void cdc_on_suspend(usbd_device* dev); static usbd_respond cdc_ep_config(usbd_device* dev, uint8_t cfg); static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); static usbd_device* usb_dev; -static UsbInterface* cdc_if_cur = NULL; +static FuriHalUsbInterface* cdc_if_cur = NULL; static bool connected = false; static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; static void* cb_ctx[IF_NUM_MAX]; -UsbInterface usb_cdc_single = { +FuriHalUsbInterface usb_cdc_single = { .init = cdc_init, .deinit = cdc_deinit, .wakeup = cdc_on_wakeup, @@ -413,7 +413,7 @@ UsbInterface usb_cdc_single = { .cfg_descr = (void*)&cdc_cfg_desc_single, }; -UsbInterface usb_cdc_dual = { +FuriHalUsbInterface usb_cdc_dual = { .init = cdc_init, .deinit = cdc_deinit, .wakeup = cdc_on_wakeup, @@ -428,7 +428,7 @@ UsbInterface usb_cdc_dual = { .cfg_descr = (void*)&cdc_cfg_desc_dual, }; -static void cdc_init(usbd_device* dev, UsbInterface* intf) { +static void cdc_init(usbd_device* dev, FuriHalUsbInterface* intf) { usb_dev = dev; cdc_if_cur = intf; diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index 098acc73..6f94fb33 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -236,7 +236,7 @@ static struct HidReport { struct HidReportConsumer consumer; } __attribute__((packed)) hid_report; -static void hid_init(usbd_device* dev, UsbInterface* intf); +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf); static void hid_deinit(usbd_device* dev); static void hid_on_wakeup(usbd_device* dev); static void hid_on_suspend(usbd_device* dev); @@ -348,7 +348,7 @@ bool furi_hal_hid_consumer_key_release(uint16_t button) { return hid_send_report(ReportIdConsumer); } -UsbInterface usb_hid = { +FuriHalUsbInterface usb_hid = { .init = hid_init, .deinit = hid_deinit, .wakeup = hid_on_wakeup, @@ -363,7 +363,7 @@ UsbInterface usb_hid = { .cfg_descr = (void*)&hid_cfg_desc, }; -static void hid_init(usbd_device* dev, UsbInterface* intf) { +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf) { if(hid_semaphore == NULL) hid_semaphore = osSemaphoreNew(1, 1, NULL); usb_dev = dev; hid_report.keyboard.report_id = ReportIdKeyboard; diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c index a3bb145e..4ee3d712 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c @@ -48,8 +48,7 @@ static const uint8_t hid_u2f_report_desc[] = { }; static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); -static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("U2F Token test"); -static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("TODO: serial"); +static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("U2F Token"); /* Device descriptor */ static const struct usb_device_descriptor hid_u2f_device_desc = { @@ -65,7 +64,7 @@ static const struct usb_device_descriptor hid_u2f_device_desc = { .bcdDevice = VERSION_BCD(1, 0, 0), .iManufacturer = UsbDevManuf, .iProduct = UsbDevProduct, - .iSerialNumber = UsbDevSerial, + .iSerialNumber = 0, .bNumConfigurations = 1, }; @@ -138,7 +137,7 @@ static const struct HidConfigDescriptor hid_u2f_cfg_desc = { }, }; -static void hid_u2f_init(usbd_device* dev, UsbInterface* intf); +static void hid_u2f_init(usbd_device* dev, FuriHalUsbInterface* intf); static void hid_u2f_deinit(usbd_device* dev); static void hid_u2f_on_wakeup(usbd_device* dev); static void hid_u2f_on_suspend(usbd_device* dev); @@ -171,7 +170,7 @@ void furi_hal_hid_u2f_set_callback(HidU2fCallback cb, void* ctx) { } } -UsbInterface usb_hid_u2f = { +FuriHalUsbInterface usb_hid_u2f = { .init = hid_u2f_init, .deinit = hid_u2f_deinit, .wakeup = hid_u2f_on_wakeup, @@ -181,12 +180,12 @@ UsbInterface usb_hid_u2f = { .str_manuf_descr = (void*)&dev_manuf_desc, .str_prod_descr = (void*)&dev_prod_desc, - .str_serial_descr = (void*)&dev_serial_desc, + .str_serial_descr = NULL, .cfg_descr = (void*)&hid_u2f_cfg_desc, }; -static void hid_u2f_init(usbd_device* dev, UsbInterface* intf) { +static void hid_u2f_init(usbd_device* dev, FuriHalUsbInterface* intf) { if(hid_u2f_semaphore == NULL) hid_u2f_semaphore = osSemaphoreNew(1, 1, NULL); usb_dev = dev; diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 62387059..3f5a0020 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -91,6 +91,10 @@ void furi_hal_power_enable_otg(); */ void furi_hal_power_disable_otg(); +/** Check OTG status and disable it if falt happened + */ +void furi_hal_power_check_otg_status(); + /** Get OTG status * * @return true if enabled diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/firmware/targets/furi_hal_include/furi_hal_usb.h index b4f001e8..9ab9b642 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb.h @@ -2,10 +2,10 @@ #include "usb.h" -typedef struct UsbInterface UsbInterface; +typedef struct FuriHalUsbInterface FuriHalUsbInterface; -struct UsbInterface { - void (*init)(usbd_device* dev, UsbInterface* intf); +struct FuriHalUsbInterface { + void (*init)(usbd_device* dev, FuriHalUsbInterface* intf); void (*deinit)(usbd_device* dev); void (*wakeup)(usbd_device* dev); void (*suspend)(usbd_device* dev); @@ -20,10 +20,19 @@ struct UsbInterface { }; /** USB device interface modes */ -extern UsbInterface usb_cdc_single; -extern UsbInterface usb_cdc_dual; -extern UsbInterface usb_hid; -extern UsbInterface usb_hid_u2f; +extern FuriHalUsbInterface usb_cdc_single; +extern FuriHalUsbInterface usb_cdc_dual; +extern FuriHalUsbInterface usb_hid; +extern FuriHalUsbInterface usb_hid_u2f; + +typedef enum { + FuriHalUsbStateEventReset, + FuriHalUsbStateEventWakeup, + FuriHalUsbStateEventSuspend, + FuriHalUsbStateEventDescriptorRequest, +} FuriHalUsbStateEvent; + +typedef void (*FuriHalUsbStateCallback)(FuriHalUsbStateEvent state, void* context); /** USB device low-level initialization */ @@ -33,13 +42,13 @@ void furi_hal_usb_init(); * * @param mode new USB device mode */ -void furi_hal_usb_set_config(UsbInterface* new_if); +void furi_hal_usb_set_config(FuriHalUsbInterface* new_if); /** Get USB device configuration * * @return current USB device mode */ -UsbInterface* furi_hal_usb_get_config(); +FuriHalUsbInterface* furi_hal_usb_get_config(); /** Disable USB device */ @@ -48,3 +57,11 @@ void furi_hal_usb_disable(); /** Enable USB device */ void furi_hal_usb_enable(); + +/** Set USB state callback + */ +void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx); + +/** Restart USB device + */ +void furi_hal_usb_reinit(); diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 3f923b97..73135d93 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -10,19 +10,6 @@ uint8_t bit_reverse(uint8_t b) { return b; } -bool bq25896_read(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data, size_t size) { - return furi_hal_i2c_trx(handle, BQ25896_ADDRESS, &address, 1, data, size, BQ25896_I2C_TIMEOUT); -} - -bool bq25896_read_reg(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data) { - return bq25896_read(handle, address, data, 1); -} - -bool bq25896_write_reg(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data) { - uint8_t buffer[2] = {address, *data}; - return furi_hal_i2c_tx(handle, BQ25896_ADDRESS, buffer, 2, BQ25896_I2C_TIMEOUT); -} - typedef struct { REG00 r00; REG01 r01; @@ -51,60 +38,101 @@ static bq25896_regs_t bq25896_regs; void bq25896_init(FuriHalI2cBusHandle* handle) { bq25896_regs.r14.REG_RST = 1; - bq25896_write_reg(handle, 0x14, (uint8_t*)&bq25896_regs.r14); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x14, *(uint8_t*)&bq25896_regs.r14, BQ25896_I2C_TIMEOUT); // Readout all registers - bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs)); + furi_hal_i2c_read_mem( + handle, + BQ25896_ADDRESS, + 0x00, + (uint8_t*)&bq25896_regs, + sizeof(bq25896_regs), + BQ25896_I2C_TIMEOUT); // Poll ADC forever bq25896_regs.r02.CONV_START = 1; bq25896_regs.r02.CONV_RATE = 1; - bq25896_write_reg(handle, 0x02, (uint8_t*)&bq25896_regs.r02); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x02, *(uint8_t*)&bq25896_regs.r02, BQ25896_I2C_TIMEOUT); bq25896_regs.r07.WATCHDOG = WatchdogDisable; - bq25896_write_reg(handle, 0x07, (uint8_t*)&bq25896_regs.r07); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x07, *(uint8_t*)&bq25896_regs.r07, BQ25896_I2C_TIMEOUT); - bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs)); + // OTG power configuration + bq25896_regs.r0A.BOOSTV = 0x8; // BOOST Voltage: 5.062V + bq25896_regs.r0A.BOOST_LIM = BOOST_LIM_1400; // BOOST Current limit: 1.4A + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT); + + furi_hal_i2c_read_mem( + handle, + BQ25896_ADDRESS, + 0x00, + (uint8_t*)&bq25896_regs, + sizeof(bq25896_regs), + BQ25896_I2C_TIMEOUT); } void bq25896_poweroff(FuriHalI2cBusHandle* handle) { bq25896_regs.r09.BATFET_DIS = 1; - bq25896_write_reg(handle, 0x09, (uint8_t*)&bq25896_regs.r09); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x09, *(uint8_t*)&bq25896_regs.r09, BQ25896_I2C_TIMEOUT); } bool bq25896_is_charging(FuriHalI2cBusHandle* handle) { - bq25896_read(handle, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs)); - bq25896_read_reg(handle, 0x0B, (uint8_t*)&bq25896_regs.r0B); + furi_hal_i2c_read_mem( + handle, + BQ25896_ADDRESS, + 0x00, + (uint8_t*)&bq25896_regs, + sizeof(bq25896_regs), + BQ25896_I2C_TIMEOUT); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x0B, (uint8_t*)&bq25896_regs.r0B, BQ25896_I2C_TIMEOUT); return bq25896_regs.r0B.CHRG_STAT != ChrgStatNo; } void bq25896_enable_charging(FuriHalI2cBusHandle* handle) { bq25896_regs.r03.CHG_CONFIG = 1; - bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } void bq25896_disable_charging(FuriHalI2cBusHandle* handle) { bq25896_regs.r03.CHG_CONFIG = 0; - bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } void bq25896_enable_otg(FuriHalI2cBusHandle* handle) { bq25896_regs.r03.OTG_CONFIG = 1; - bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } void bq25896_disable_otg(FuriHalI2cBusHandle* handle) { bq25896_regs.r03.OTG_CONFIG = 0; - bq25896_write_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03); + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); } bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) { - bq25896_read_reg(handle, 0x03, (uint8_t*)&bq25896_regs.r03); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x03, (uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT); return bq25896_regs.r03.OTG_CONFIG; } +bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle) { + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT); + return bq25896_regs.r0C.BOOST_FAULT; +} + uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) { - bq25896_read_reg(handle, 0x11, (uint8_t*)&bq25896_regs.r11); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x11, (uint8_t*)&bq25896_regs.r11, BQ25896_I2C_TIMEOUT); if(bq25896_regs.r11.VBUS_GD) { return (uint16_t)bq25896_regs.r11.VBUSV * 100 + 2600; } else { @@ -113,21 +141,25 @@ uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) { } uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle) { - bq25896_read_reg(handle, 0x0F, (uint8_t*)&bq25896_regs.r0F); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x0F, (uint8_t*)&bq25896_regs.r0F, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r0F.SYSV * 20 + 2304; } uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle) { - bq25896_read_reg(handle, 0x0E, (uint8_t*)&bq25896_regs.r0E); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x0E, (uint8_t*)&bq25896_regs.r0E, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r0E.BATV * 20 + 2304; } uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle) { - bq25896_read_reg(handle, 0x12, (uint8_t*)&bq25896_regs.r12); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x12, (uint8_t*)&bq25896_regs.r12, BQ25896_I2C_TIMEOUT); return (uint16_t)bq25896_regs.r12.ICHGR * 50; } uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle) { - bq25896_read_reg(handle, 0x10, (uint8_t*)&bq25896_regs.r10); + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x10, (uint8_t*)&bq25896_regs.r10, BQ25896_I2C_TIMEOUT); return (uint32_t)bq25896_regs.r10.TSPCT * 465 + 21000; } diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index 5019a816..39d343c3 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -28,6 +28,9 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle); /** Is otg enabled */ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); +/** Check OTG BOOST Fault status */ +bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle); + /** Get VBUS Voltage in mV */ uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/bq27220.c b/lib/drivers/bq27220.c index 0dcae180..bcdf4535 100644 --- a/lib/drivers/bq27220.c +++ b/lib/drivers/bq27220.c @@ -8,24 +8,17 @@ #define TAG "Gauge" uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { - uint8_t buffer[2] = {address}; - uint16_t ret = 0; + uint16_t buf = 0; - if(furi_hal_i2c_trx(handle, BQ27220_ADDRESS, buffer, 1, buffer, 2, BQ27220_I2C_TIMEOUT)) { - ret = *(uint16_t*)buffer; - } + furi_hal_i2c_read_mem( + handle, BQ27220_ADDRESS, address, (uint8_t*)&buf, 2, BQ27220_I2C_TIMEOUT); - return ret; + return buf; } bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { - bool ret = false; - uint8_t buffer[3]; - - buffer[0] = CommandControl; - buffer[1] = control & 0xFF; - buffer[2] = (control >> 8) & 0xFF; - ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT); + bool ret = furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandControl, (uint8_t*)&control, 2, BQ27220_I2C_TIMEOUT); return ret; } @@ -40,22 +33,22 @@ uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { bool bq27220_set_parameter_u16(FuriHalI2cBusHandle* handle, uint16_t address, uint16_t value) { bool ret; - uint8_t buffer[5]; + uint8_t buffer[4]; - buffer[0] = CommandSelectSubclass; - buffer[1] = address & 0xFF; - buffer[2] = (address >> 8) & 0xFF; - buffer[3] = (value >> 8) & 0xFF; - buffer[4] = value & 0xFF; - ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 5, BQ27220_I2C_TIMEOUT); + buffer[0] = address & 0xFF; + buffer[1] = (address >> 8) & 0xFF; + buffer[2] = (value >> 8) & 0xFF; + buffer[3] = value & 0xFF; + ret = furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 4, BQ27220_I2C_TIMEOUT); delay_us(10000); uint8_t checksum = bq27220_get_checksum(&buffer[1], 4); - buffer[0] = CommandMACDataSum; - buffer[1] = checksum; - buffer[2] = 6; - ret = furi_hal_i2c_tx(handle, BQ27220_ADDRESS, buffer, 3, BQ27220_I2C_TIMEOUT); + buffer[0] = checksum; + buffer[1] = 6; + ret = furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT); delay_us(10000); return ret; diff --git a/lib/drivers/lp5562.c b/lib/drivers/lp5562.c index 8535f3ac..82554365 100644 --- a/lib/drivers/lp5562.c +++ b/lib/drivers/lp5562.c @@ -1,21 +1,17 @@ #include "lp5562.h" #include "lp5562_reg.h" +#include #include -static bool lp5562_write_reg(FuriHalI2cBusHandle* handle, uint8_t address, uint8_t* data) { - uint8_t buffer[2] = {address, *data}; - return furi_hal_i2c_tx(handle, LP5562_ADDRESS, buffer, 2, LP5562_I2C_TIMEOUT); -} - void lp5562_reset(FuriHalI2cBusHandle* handle) { Reg0D_Reset reg = {.value = 0xFF}; - lp5562_write_reg(handle, 0x0D, (uint8_t*)®); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)®, LP5562_I2C_TIMEOUT); } void lp5562_configure(FuriHalI2cBusHandle* handle) { Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true}; - lp5562_write_reg(handle, 0x08, (uint8_t*)&config); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x08, *(uint8_t*)&config, LP5562_I2C_TIMEOUT); Reg70_LedMap map = { .red = EngSelectI2C, @@ -23,42 +19,178 @@ void lp5562_configure(FuriHalI2cBusHandle* handle) { .blue = EngSelectI2C, .white = EngSelectI2C, }; - lp5562_write_reg(handle, 0x70, (uint8_t*)&map); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, *(uint8_t*)&map, LP5562_I2C_TIMEOUT); } void lp5562_enable(FuriHalI2cBusHandle* handle) { Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true}; - lp5562_write_reg(handle, 0x00, (uint8_t*)®); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, *(uint8_t*)®, LP5562_I2C_TIMEOUT); + //>488μs delay is required after writing to 0x00 register, otherwise program engine will not work + delay_us(500); } void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) { uint8_t reg_no; if(channel == LP5562ChannelRed) { - reg_no = 0x07; + reg_no = LP5562_CHANNEL_RED_CURRENT_REGISTER; } else if(channel == LP5562ChannelGreen) { - reg_no = 0x06; + reg_no = LP5562_CHANNEL_GREEN_CURRENT_REGISTER; } else if(channel == LP5562ChannelBlue) { - reg_no = 0x05; + reg_no = LP5562_CHANNEL_BLUE_CURRENT_REGISTER; } else if(channel == LP5562ChannelWhite) { - reg_no = 0x0F; + reg_no = LP5562_CHANNEL_WHITE_CURRENT_REGISTER; } else { return; } - lp5562_write_reg(handle, reg_no, &value); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT); } void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) { uint8_t reg_no; if(channel == LP5562ChannelRed) { - reg_no = 0x04; + reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER; } else if(channel == LP5562ChannelGreen) { - reg_no = 0x03; + reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER; } else if(channel == LP5562ChannelBlue) { - reg_no = 0x02; + reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER; } else if(channel == LP5562ChannelWhite) { - reg_no = 0x0E; + reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER; } else { return; } - lp5562_write_reg(handle, reg_no, &value); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT); +} + +uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel) { + uint8_t reg_no; + uint8_t value; + if(channel == LP5562ChannelRed) { + reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER; + } else if(channel == LP5562ChannelGreen) { + reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER; + } else if(channel == LP5562ChannelBlue) { + reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER; + } else if(channel == LP5562ChannelWhite) { + reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER; + } else { + return 0; + } + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, reg_no, &value, LP5562_I2C_TIMEOUT); + return value; +} + +static void + lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src) { + uint8_t reg_val = 0; + uint8_t bit_offset = 0; + + if(channel == LP5562ChannelRed) { + bit_offset = 4; + } else if(channel == LP5562ChannelGreen) { + bit_offset = 2; + } else if(channel == LP5562ChannelBlue) { + bit_offset = 0; + } else if(channel == LP5562ChannelWhite) { + bit_offset = 6; + } else { + return; + } + + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x70, ®_val, LP5562_I2C_TIMEOUT); + reg_val &= ~(0x3 << bit_offset); + reg_val |= ((src & 0x03) << bit_offset); + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT); +} + +void lp5562_execute_program( + FuriHalI2cBusHandle* handle, + LP5562Engine eng, + LP5562Channel ch, + uint16_t* program) { + if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return; + uint8_t reg_val = 0; + uint8_t bit_offset = 0; + uint8_t enable_reg = 0; + + // Read old value of enable register + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x00, &enable_reg, LP5562_I2C_TIMEOUT); + + // Engine configuration + bit_offset = (3 - eng) * 2; + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, ®_val, LP5562_I2C_TIMEOUT); + reg_val &= ~(0x3 << bit_offset); + reg_val |= (0x01 << bit_offset); // load + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT); + delay_us(100); + + // Program load + for(uint8_t i = 0; i < 16; i++) { + // Program words are big-endian, so reverse byte order before loading + program[i] = __REV16(program[i]); + } + furi_hal_i2c_write_mem( + handle, + LP5562_ADDRESS, + 0x10 + (0x20 * (eng - 1)), + (uint8_t*)program, + 16 * 2, + LP5562_I2C_TIMEOUT); + + // Program start + bit_offset = (3 - eng) * 2; + furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, ®_val, LP5562_I2C_TIMEOUT); + reg_val &= ~(0x3 << bit_offset); + reg_val |= (0x02 << bit_offset); // run + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT); + + // Switch output to Execution Engine + lp5562_set_channel_src(handle, ch, eng); + + enable_reg &= ~(0x3 << bit_offset); + enable_reg |= (0x02 << bit_offset); // run + furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, enable_reg, LP5562_I2C_TIMEOUT); +} + +void lp5562_execute_ramp( + FuriHalI2cBusHandle* handle, + LP5562Engine eng, + LP5562Channel ch, + uint8_t val_start, + uint8_t val_end, + uint16_t time) { + if(val_start == val_end) return; + + // Temporary switch to constant value from register + lp5562_set_channel_src(handle, ch, LP5562Direct); + + // Prepare command sequence + uint16_t program[16]; + uint8_t diff = (val_end > val_start) ? (val_end - val_start) : (val_start - val_end); + uint16_t time_step = time * 2 / diff; + uint8_t prescaller = 0; + if(time_step > 0x3F) { + time_step /= 32; + prescaller = 1; + } + + if(time_step == 0) { + time_step = 1; + } else if(time_step > 0x3F) + time_step = 0x3F; + + program[0] = 0x4000 | val_start; // Set PWM + if(val_end > val_start) { + program[1] = (prescaller << 14) | (time_step << 8) | ((diff / 2) & 0x7F); // Ramp Up + } else { + program[1] = (prescaller << 14) | (time_step << 8) | 0x80 | + ((diff / 2) & 0x7F); // Ramp Down + } + program[2] = 0xA001 | ((2 - 1) << 7); // Loop to step 1, repeat twice to get full 8-bit scale + program[3] = 0xC000; // End + + // Execute program + lp5562_execute_program(handle, eng, LP5562ChannelWhite, program); + + // Write end value to register + lp5562_set_channel_value(handle, ch, val_end); } diff --git a/lib/drivers/lp5562.h b/lib/drivers/lp5562.h index 536ef899..33790d00 100644 --- a/lib/drivers/lp5562.h +++ b/lib/drivers/lp5562.h @@ -12,6 +12,13 @@ typedef enum { LP5562ChannelWhite, } LP5562Channel; +typedef enum { + LP5562Direct = 0, + LP5562Engine1 = 1, + LP5562Engine2 = 2, + LP5562Engine3 = 3, +} LP5562Engine; + /** Initialize Driver */ void lp5562_reset(FuriHalI2cBusHandle* handle); @@ -24,5 +31,24 @@ void lp5562_enable(FuriHalI2cBusHandle* handle); /** Set channel current */ void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value); -/** Set channel current */ +/** Set channel PWM value */ void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value); + +/** Get channel PWM value */ +uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel); + +/** Execute program sequence */ +void lp5562_execute_program( + FuriHalI2cBusHandle* handle, + LP5562Engine eng, + LP5562Channel ch, + uint16_t* program); + +/** Execute ramp program sequence */ +void lp5562_execute_ramp( + FuriHalI2cBusHandle* handle, + LP5562Engine eng, + LP5562Channel ch, + uint8_t val_start, + uint8_t val_end, + uint16_t time); diff --git a/lib/drivers/lp5562_reg.h b/lib/drivers/lp5562_reg.h index 2f2c1a52..e65cc2af 100644 --- a/lib/drivers/lp5562_reg.h +++ b/lib/drivers/lp5562_reg.h @@ -7,6 +7,16 @@ #define LP5562_ADDRESS 0x60 #define LP5562_I2C_TIMEOUT 50 +#define LP5562_CHANNEL_RED_CURRENT_REGISTER 0x07 +#define LP5562_CHANNEL_GREEN_CURRENT_REGISTER 0x06 +#define LP5562_CHANNEL_BLUE_CURRENT_REGISTER 0x05 +#define LP5562_CHANNEL_WHITE_CURRENT_REGISTER 0x0F + +#define LP5562_CHANNEL_RED_VALUE_REGISTER 0x04 +#define LP5562_CHANNEL_GREEN_VALUE_REGISTER 0x03 +#define LP5562_CHANNEL_BLUE_VALUE_REGISTER 0x02 +#define LP5562_CHANNEL_WHITE_VALUE_REGISTER 0x0E + typedef enum { EngExecHold = 0b00, EngExecStep = 0b01, diff --git a/scripts/lint.py b/scripts/lint.py index 1be0c837..6537b548 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -4,6 +4,7 @@ import os import re import shutil import subprocess +import multiprocessing from flipper.app import App @@ -60,16 +61,29 @@ class Main(App): output.append(os.path.join(dirpath, filename)) return output + @staticmethod + def _format_source(task): + try: + subprocess.check_call(task) + return True + except subprocess.CalledProcessError as e: + return False + def _format_sources(self, sources: list, dry_run: bool = False): args = ["clang-format", "--Werror", "--style=file", "-i"] if dry_run: args.append("--dry-run") - args += sources - try: - subprocess.check_call(args) - return True - except subprocess.CalledProcessError as e: - return False + + files_per_task = 69 + tasks = [] + while len(sources) > 0: + tasks.append(args + sources[:files_per_task]) + sources = sources[files_per_task:] + + pool = multiprocessing.Pool() + results = pool.map(self._format_source, tasks) + + return not False in results def _fix_filename(self, filename: str): return filename.replace("-", "_")